From d5868a8f47a6bb751d41a0153c8cb9b8cb17f9da Mon Sep 17 00:00:00 2001 From: Sean Date: Sat, 13 Apr 2024 22:42:08 -0700 Subject: [PATCH 01/11] Add preview images to some pages --- src/components/common/SEO/index.tsx | 15 ++++++++++++--- src/pages/_app.tsx | 7 ++++++- src/pages/events/[uuid].tsx | 3 +++ src/pages/store/collection/[uuid].tsx | 3 ++- src/pages/store/item/[uuid].tsx | 2 ++ src/pages/u/[handle].tsx | 3 +++ 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/components/common/SEO/index.tsx b/src/components/common/SEO/index.tsx index 21d1ccba..48a6e6c2 100644 --- a/src/components/common/SEO/index.tsx +++ b/src/components/common/SEO/index.tsx @@ -8,9 +8,16 @@ const DESC = interface SEOProps { title?: string; description?: string; + previewImage?: string; + bigPreviewImage?: boolean; } -const SEO = ({ title, description = DESC }: SEOProps) => { +const SEO = ({ + title, + description = DESC, + previewImage = Logo.src, + bigPreviewImage = false, +}: SEOProps) => { const fullTitle = title ? `${title} | ${TITLE}` : TITLE; return ( @@ -28,11 +35,13 @@ const SEO = ({ title, description = DESC }: SEOProps) => { {/* type of content */} {/* actual website title */} - + {/* title to display for the specific link being shared */} {/* preview image */} - + + {/* make preview image large on Discord and other sites */} + {bigPreviewImage ? : null} {/* preview description text */} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 78a4e41d..114772d1 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -29,7 +29,12 @@ export default function MyApp({ Component, pageProps, router }: AppProps) { font-family: ${dmSans.style.fontFamily}, sans-serif; } `} - + diff --git a/src/pages/events/[uuid].tsx b/src/pages/events/[uuid].tsx index 15f210c6..7dea40b0 100644 --- a/src/pages/events/[uuid].tsx +++ b/src/pages/events/[uuid].tsx @@ -58,6 +58,9 @@ const getServerSidePropsFunc: GetServerSidePropsWithUser = async ({ params, req, return { props: { title: event.title, + description: event.description, + previewImage: event.thumbnail, + bigPreviewImage: true, token, event, attended: attendances.some(attendance => attendance.event.uuid === uuid), diff --git a/src/pages/store/collection/[uuid].tsx b/src/pages/store/collection/[uuid].tsx index efab6ae4..3ef8f4d8 100644 --- a/src/pages/store/collection/[uuid].tsx +++ b/src/pages/store/collection/[uuid].tsx @@ -6,7 +6,7 @@ import withAccessType from '@/lib/hoc/withAccessType'; import { CookieService, PermissionService } from '@/lib/services'; import { PrivateProfile, PublicMerchCollection } from '@/lib/types/apiResponses'; import { CookieType } from '@/lib/types/enums'; -import { getDefaultMerchItemPhoto } from '@/lib/utils'; +import { getDefaultMerchCollectionPhoto, getDefaultMerchItemPhoto } from '@/lib/utils'; import styles from '@/styles/pages/StoreCollectionPage.module.scss'; import { GetServerSideProps } from 'next'; import Image from 'next/image'; @@ -99,6 +99,7 @@ const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) props: { title: collection.title, description: collection.description, + previewImage: getDefaultMerchCollectionPhoto(collection), uuid, collection, previewPublic: preview === 'member', diff --git a/src/pages/store/item/[uuid].tsx b/src/pages/store/item/[uuid].tsx index 925eead4..0a959434 100644 --- a/src/pages/store/item/[uuid].tsx +++ b/src/pages/store/item/[uuid].tsx @@ -10,6 +10,7 @@ import { PublicMerchItemWithPurchaseLimits, } from '@/lib/types/apiResponses'; import { CookieType } from '@/lib/types/enums'; +import { getDefaultMerchItemPhoto } from '@/lib/utils'; import NoImage from '@/public/assets/graphics/cat404.png'; import styles from '@/styles/pages/StoreItemPage.module.scss'; import { GetServerSideProps } from 'next'; @@ -141,6 +142,7 @@ const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) props: { title: item.itemName, description: item.description, + previewImage: getDefaultMerchItemPhoto(item), uuid, item, previewPublic: preview === 'member', diff --git a/src/pages/u/[handle].tsx b/src/pages/u/[handle].tsx index bb9e66ad..d2e37563 100644 --- a/src/pages/u/[handle].tsx +++ b/src/pages/u/[handle].tsx @@ -9,6 +9,7 @@ import { UserAPI } from '@/lib/api'; import withAccessType from '@/lib/hoc/withAccessType'; import { CookieService, PermissionService } from '@/lib/services'; import { CookieType } from '@/lib/types/enums'; +import { getProfilePicture } from '@/lib/utils'; import type { GetServerSideProps } from 'next/types'; type UserHandlePageProps = UserHandleNotFoundProps | UserProfilePageProps; @@ -56,6 +57,8 @@ const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) return { props: { title: `${handleUser.firstName} ${handleUser.lastName}`, + description: handleUser.bio, + previewImage: getProfilePicture(handleUser), handleUser, isSignedInUser, signedInAttendances, From c543f0becd13c6b0bb991353c039898bcf2b6086 Mon Sep 17 00:00:00 2001 From: Sean Date: Sat, 13 Apr 2024 22:51:45 -0700 Subject: [PATCH 02/11] Include event time and location in site description --- src/components/common/SEO/index.tsx | 6 ++-- src/pages/events/[uuid].tsx | 56 ++++++++++++++--------------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/components/common/SEO/index.tsx b/src/components/common/SEO/index.tsx index 48a6e6c2..e3d7c85e 100644 --- a/src/components/common/SEO/index.tsx +++ b/src/components/common/SEO/index.tsx @@ -18,13 +18,11 @@ const SEO = ({ previewImage = Logo.src, bigPreviewImage = false, }: SEOProps) => { - const fullTitle = title ? `${title} | ${TITLE}` : TITLE; - return ( {/* google indexing data */} - {fullTitle} + {title ? `${title} | ${TITLE}` : TITLE} @@ -37,7 +35,7 @@ const SEO = ({ {/* actual website title */} {/* title to display for the specific link being shared */} - + {/* preview image */} {/* make preview image large on Discord and other sites */} diff --git a/src/pages/events/[uuid].tsx b/src/pages/events/[uuid].tsx index 7dea40b0..d34bfbe8 100644 --- a/src/pages/events/[uuid].tsx +++ b/src/pages/events/[uuid].tsx @@ -2,10 +2,11 @@ import { Typography } from '@/components/common'; import EventDetail from '@/components/events/EventDetail'; import { Feedback, FeedbackForm } from '@/components/feedback'; import { EventAPI, FeedbackAPI, UserAPI } from '@/lib/api'; -import withAccessType, { GetServerSidePropsWithUser } from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import { GetServerSidePropsWithUser } from '@/lib/hoc/withAccessType'; +import { CookieService } from '@/lib/services'; import type { PublicEvent, PublicFeedback } from '@/lib/types/apiResponses'; import { CookieType } from '@/lib/types/enums'; +import { formatEventDate } from '@/lib/utils'; import styles from '@/styles/pages/event.module.scss'; import { useMemo, useState } from 'react'; @@ -47,32 +48,31 @@ export default EventPage; const getServerSidePropsFunc: GetServerSidePropsWithUser = async ({ params, req, res, user }) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); + const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }) ?? null; - try { - const [event, attendances, [feedback = null]] = await Promise.all([ - EventAPI.getEvent(uuid, token), - UserAPI.getAttendancesForCurrentUser(token), - FeedbackAPI.getFeedback(token, { user: user.uuid, event: uuid }), - ]); - return { - props: { - title: event.title, - description: event.description, - previewImage: event.thumbnail, - bigPreviewImage: true, - token, - event, - attended: attendances.some(attendance => attendance.event.uuid === uuid), - feedback, - }, - }; - } catch { - return { notFound: true }; - } + // try { + const [event, attendances, [feedback = null]] = await Promise.all([ + EventAPI.getEvent(uuid, token), + user ? UserAPI.getAttendancesForCurrentUser(token) : [], + user ? FeedbackAPI.getFeedback(token, { user: user.uuid, event: uuid }) : [], + ]); + return { + props: { + title: event.title, + description: `${formatEventDate(event.start, event.end, true)} at ${event.location}\n\n${ + event.description + }`, + previewImage: event.cover, + bigPreviewImage: true, + token, + event, + attended: attendances.some(attendance => attendance.event.uuid === uuid), + feedback, + }, + }; + // } catch { + // return { notFound: true }; + // } }; -export const getServerSideProps = withAccessType( - getServerSidePropsFunc, - PermissionService.loggedInUser -); +export const getServerSideProps = getServerSidePropsFunc; From 8d6a69a315042f3ecacca5d59de488c8602768cb Mon Sep 17 00:00:00 2001 From: Sean Date: Sat, 13 Apr 2024 23:01:51 -0700 Subject: [PATCH 03/11] refactor: Make withAccessType provide authToken --- src/lib/hoc/withAccessType.ts | 8 +++---- src/pages/admin/event/create.tsx | 12 +++++------ src/pages/admin/event/edit/[uuid].tsx | 11 ++++------ src/pages/admin/store/pickup/[uuid].tsx | 10 ++++----- src/pages/admin/store/pickup/create.tsx | 9 +++----- src/pages/admin/store/pickup/edit/[uuid].tsx | 9 +++----- src/pages/admin/store/pickup/index.tsx | 9 +++----- src/pages/events.tsx | 10 +++------ src/pages/events/[uuid].tsx | 11 +++++----- src/pages/feedback.tsx | 10 ++++----- src/pages/index.tsx | 12 ++++++----- src/pages/leaderboard.tsx | 12 ++++------- src/pages/profile.tsx | 12 +++++------ src/pages/profile/edit.tsx | 14 +++++++------ src/pages/store/collection/[uuid].tsx | 13 +++++++----- src/pages/store/collection/[uuid]/edit.tsx | 10 +++------ src/pages/store/collection/new.tsx | 9 +++----- src/pages/store/index.tsx | 13 +++++++----- src/pages/store/item/[uuid].tsx | 13 +++++++----- src/pages/store/item/[uuid]/edit.tsx | 10 +++------ src/pages/store/item/new.tsx | 9 +++----- src/pages/store/orders.tsx | 14 +++++-------- src/pages/u/[handle].tsx | 22 +++++++++++--------- 23 files changed, 116 insertions(+), 146 deletions(-) diff --git a/src/lib/hoc/withAccessType.ts b/src/lib/hoc/withAccessType.ts index 1cee57c4..1f8b9fff 100644 --- a/src/lib/hoc/withAccessType.ts +++ b/src/lib/hoc/withAccessType.ts @@ -12,12 +12,12 @@ import type { } from 'next'; import { ParsedUrlQuery } from 'querystring'; -export type GetServerSidePropsWithUser< +export type GetServerSidePropsWithAuth< Props extends { [key: string]: any } = { [key: string]: any }, Params extends ParsedUrlQuery = ParsedUrlQuery, Preview extends PreviewData = PreviewData > = ( - context: GetServerSidePropsContext & { user: PrivateProfile } + context: GetServerSidePropsContext & { user: PrivateProfile; authToken: string } ) => Promise>; interface AccessTypeOptions { @@ -35,7 +35,7 @@ interface AccessTypeOptions { * @returns */ export default function withAccessType( - gssp: GetServerSidePropsWithUser, + gssp: GetServerSidePropsWithAuth, validAccessTypes: UserAccessType[], { redirectTo = config.loginRoute }: AccessTypeOptions = {} ): GetServerSideProps { @@ -98,7 +98,7 @@ export default function withAccessType( if (!validAccessTypes.includes(userAccessLevel)) return missingAccessRedirect; // If we haven't short-circuited, user has valid access. Show the page and add the user prop. - const originalReturnValue = await gssp({ ...context, user }); + const originalReturnValue = await gssp({ ...context, user, authToken: authTokenCookie }); // Insert the user object to the original return value if it doesn't exist already if ('props' in originalReturnValue) { const existingProps = await Promise.resolve(originalReturnValue.props); diff --git a/src/pages/admin/event/create.tsx b/src/pages/admin/event/create.tsx index ca3bf31a..5252fa9d 100644 --- a/src/pages/admin/event/create.tsx +++ b/src/pages/admin/event/create.tsx @@ -1,11 +1,10 @@ import { EventDetailsForm } from '@/components/admin/event'; import { config } from '@/lib'; import { EventAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PublicEvent } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; -import { GetServerSideProps, NextPage } from 'next'; +import { NextPage } from 'next'; interface CreateEventProps { defaultData: Partial; @@ -16,10 +15,9 @@ const CreateEventPage: NextPage = ({ defaultData }) => ( export default CreateEventPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authToken }) => { if (typeof query.duplicate === 'string') { - const defaultData = await EventAPI.getEvent(query.duplicate, token); + const defaultData = await EventAPI.getEvent(query.duplicate, authToken); return { props: { title: 'Create Event', defaultData }, }; diff --git a/src/pages/admin/event/edit/[uuid].tsx b/src/pages/admin/event/edit/[uuid].tsx index 7cb347ed..02fb3fcb 100644 --- a/src/pages/admin/event/edit/[uuid].tsx +++ b/src/pages/admin/event/edit/[uuid].tsx @@ -1,12 +1,10 @@ import { EventDetailsForm } from '@/components/admin/event'; import { config } from '@/lib'; import { EventAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PublicEvent } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import { DateTime } from 'luxon'; -import type { GetServerSideProps } from 'next/types'; interface EditEventProps { editEvent: PublicEvent; @@ -25,12 +23,11 @@ const EditEventPage = ({ editEvent }: EditEventProps) => ( export default EditEventPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ params, authToken }) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); try { - const editEvent = await EventAPI.getEvent(uuid, token); + const editEvent = await EventAPI.getEvent(uuid, authToken); return { props: { title: `Edit ${editEvent.title}`, editEvent }, }; diff --git a/src/pages/admin/store/pickup/[uuid].tsx b/src/pages/admin/store/pickup/[uuid].tsx index a3dcb862..2dcea7a8 100644 --- a/src/pages/admin/store/pickup/[uuid].tsx +++ b/src/pages/admin/store/pickup/[uuid].tsx @@ -11,13 +11,12 @@ import { Button, Typography } from '@/components/common'; import { EventCard } from '@/components/events'; import { StoreAPI } from '@/lib/api'; import config from '@/lib/config'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PublicOrderPickupEvent } from '@/lib/types/apiResponses'; -import { CookieType, OrderPickupEventStatus } from '@/lib/types/enums'; +import { OrderPickupEventStatus } from '@/lib/types/enums'; import { formatEventDate } from '@/lib/utils'; import styles from '@/styles/pages/StorePickupEventDetailsPage.module.scss'; -import { GetServerSideProps } from 'next'; import Link from 'next/link'; import router from 'next/router'; import { useState } from 'react'; @@ -121,9 +120,8 @@ const PickupEventDetailsPage = ({ pickupEvent, token }: PickupEventDetailsPagePr export default PickupEventDetailsPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ params, authToken: token }) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); try { const pickupEvent = await StoreAPI.getPickupEvent(token, uuid); if (pickupEvent.orders) diff --git a/src/pages/admin/store/pickup/create.tsx b/src/pages/admin/store/pickup/create.tsx index 155e30ad..a6db9e38 100644 --- a/src/pages/admin/store/pickup/create.tsx +++ b/src/pages/admin/store/pickup/create.tsx @@ -4,12 +4,10 @@ import AdminPickupEventForm from '@/components/admin/event/AdminPickupEvent/Admi import { Navbar } from '@/components/store'; import { config } from '@/lib'; import { EventAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PrivateProfile, PublicEvent } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StoreItemEditPage.module.scss'; -import { GetServerSideProps } from 'next'; interface CreatePickupEventPageProps { user: PrivateProfile; @@ -31,8 +29,7 @@ const CreatePickupEventPage = ({ export default CreatePickupEventPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res }) => { - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ authToken: token }) => { const futureEvents = await EventAPI.getAllFutureEvents(); return { props: { title: 'Create Pickup Event', token, futureEvents } }; }; diff --git a/src/pages/admin/store/pickup/edit/[uuid].tsx b/src/pages/admin/store/pickup/edit/[uuid].tsx index ce01bc60..6191d8b1 100644 --- a/src/pages/admin/store/pickup/edit/[uuid].tsx +++ b/src/pages/admin/store/pickup/edit/[uuid].tsx @@ -4,12 +4,10 @@ import AdminPickupEventForm from '@/components/admin/event/AdminPickupEvent/Admi import { Navbar } from '@/components/store'; import { config } from '@/lib'; import { EventAPI, StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PrivateProfile, PublicEvent, PublicOrderPickupEvent } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StoreItemEditPage.module.scss'; -import { GetServerSideProps } from 'next'; interface EditPickupEventProps { user: PrivateProfile; @@ -38,9 +36,8 @@ const EditPickupEventPage = ({ export default EditPickupEventPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ params, authToken: token }) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); const [futureEvents, pickupEvent] = await Promise.all([ EventAPI.getAllFutureEvents(), StoreAPI.getPickupEvent(token, uuid), diff --git a/src/pages/admin/store/pickup/index.tsx b/src/pages/admin/store/pickup/index.tsx index 881891e4..2c1543be 100644 --- a/src/pages/admin/store/pickup/index.tsx +++ b/src/pages/admin/store/pickup/index.tsx @@ -2,12 +2,10 @@ import { PickupEventCard } from '@/components/admin/store'; import { Typography } from '@/components/common'; import { config } from '@/lib'; import { StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import type { PublicOrderPickupEvent } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StorePickupEventPage.module.scss'; -import { GetServerSideProps } from 'next'; import router from 'next/router'; import { useState } from 'react'; @@ -66,8 +64,7 @@ const AdminPickupPage = ({ futurePickupEvents, pastPickupEvents }: AdminPickupPa export default AdminPickupPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res }) => { - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ authToken: token }) => { const futurePickupEventsPromise = StoreAPI.getFutureOrderPickupEvents(token); const pastPickupEventsPromise = StoreAPI.getPastOrderPickupEvents(token); const [futurePickupEvents, pastPickupEvents] = await Promise.all([ diff --git a/src/pages/events.tsx b/src/pages/events.tsx index 1e6a002b..26efee32 100644 --- a/src/pages/events.tsx +++ b/src/pages/events.tsx @@ -3,9 +3,9 @@ import { DIVIDER } from '@/components/common/Dropdown'; import { EventDisplay } from '@/components/events'; import { config } from '@/lib'; import { EventAPI, UserAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import useQueryState from '@/lib/hooks/useQueryState'; -import { CookieService, PermissionService } from '@/lib/services'; +import { PermissionService } from '@/lib/services'; import type { PublicAttendance, PublicEvent } from '@/lib/types/apiResponses'; import { FilterEventOptions, @@ -13,10 +13,8 @@ import { isValidCommunityFilter, isValidDateFilter, } from '@/lib/types/client'; -import { CookieType } from '@/lib/types/enums'; import { formatSearch, getDateRange, getYears } from '@/lib/utils'; import styles from '@/styles/pages/events.module.scss'; -import type { GetServerSideProps } from 'next'; import { useMemo, useState } from 'react'; interface EventsPageProps { @@ -216,9 +214,7 @@ const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => export default EventsPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const authToken = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); - +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authToken }) => { const getEventsPromise = EventAPI.getAllEvents(); const getAttendancesPromise = UserAPI.getAttendancesForCurrentUser(authToken); diff --git a/src/pages/events/[uuid].tsx b/src/pages/events/[uuid].tsx index d34bfbe8..45af6ca8 100644 --- a/src/pages/events/[uuid].tsx +++ b/src/pages/events/[uuid].tsx @@ -2,10 +2,8 @@ import { Typography } from '@/components/common'; import EventDetail from '@/components/events/EventDetail'; import { Feedback, FeedbackForm } from '@/components/feedback'; import { EventAPI, FeedbackAPI, UserAPI } from '@/lib/api'; -import { GetServerSidePropsWithUser } from '@/lib/hoc/withAccessType'; -import { CookieService } from '@/lib/services'; +import { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import type { PublicEvent, PublicFeedback } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import { formatEventDate } from '@/lib/utils'; import styles from '@/styles/pages/event.module.scss'; import { useMemo, useState } from 'react'; @@ -46,9 +44,12 @@ const EventPage = ({ token, event, attended, feedback: initFeedback }: EventPage export default EventPage; -const getServerSidePropsFunc: GetServerSidePropsWithUser = async ({ params, req, res, user }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ + params, + user, + authToken: token, +}) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }) ?? null; // try { const [event, attendances, [feedback = null]] = await Promise.all([ diff --git a/src/pages/feedback.tsx b/src/pages/feedback.tsx index 68dd6a95..8cad4af9 100644 --- a/src/pages/feedback.tsx +++ b/src/pages/feedback.tsx @@ -2,14 +2,13 @@ import { Dropdown, PaginationControls, Typography } from '@/components/common'; import { Feedback, feedbackTypeNames } from '@/components/feedback'; import { config } from '@/lib'; import { FeedbackAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import useQueryState from '@/lib/hooks/useQueryState'; -import { CookieService, PermissionService } from '@/lib/services'; +import { PermissionService } from '@/lib/services'; import type { PrivateProfile, PublicFeedback } from '@/lib/types/apiResponses'; -import { CookieType, FeedbackStatus, FeedbackType, UserAccessType } from '@/lib/types/enums'; +import { FeedbackStatus, FeedbackType, UserAccessType } from '@/lib/types/enums'; import { isEnum } from '@/lib/utils'; import styles from '@/styles/pages/feedback.module.scss'; -import type { GetServerSideProps } from 'next'; import Link from 'next/link'; import { useMemo, useState } from 'react'; @@ -176,8 +175,7 @@ const FeedbackPage = ({ user, feedback, token, initialFilters }: FeedbackPagePro export default FeedbackPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authToken: token }) => { const feedback = await FeedbackAPI.getFeedback(token); const { type, status, sort } = query; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 211e97b5..3221f691 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -3,7 +3,7 @@ import { CheckInModal, EventCarousel } from '@/components/events'; import { UserProgress } from '@/components/profile/UserProgress'; import { config, showToast } from '@/lib'; import { EventAPI, UserAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import { attendEvent } from '@/lib/managers/EventManager'; import { CookieService, PermissionService } from '@/lib/services'; import type { PrivateProfile, PublicAttendance, PublicEvent } from '@/lib/types/apiResponses'; @@ -12,7 +12,6 @@ import RaccoonGraphic from '@/public/assets/graphics/portal/raccoon-hero.svg'; import WavesGraphic from '@/public/assets/graphics/portal/waves.svg'; import CheckMark from '@/public/assets/icons/check-mark.svg'; import styles from '@/styles/pages/Home.module.scss'; -import { GetServerSideProps } from 'next'; import Link from 'next/link'; import { useEffect, useState } from 'react'; @@ -173,9 +172,12 @@ const PortalHomePage = ({ export default PortalHomePage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const authToken = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); - +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ + req, + res, + query, + authToken, +}) => { let checkInResponse = null; if (typeof query.code === 'string') { // If a check-in code is specified, first check in to that event. diff --git a/src/pages/leaderboard.tsx b/src/pages/leaderboard.tsx index f2354621..f215dbed 100644 --- a/src/pages/leaderboard.tsx +++ b/src/pages/leaderboard.tsx @@ -3,15 +3,13 @@ import { DIVIDER } from '@/components/common/Dropdown'; import { LeaderboardRow, TopThreeCard } from '@/components/leaderboard'; import { config } from '@/lib'; import { LeaderboardAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { SlidingLeaderboardQueryParams } from '@/lib/types/apiRequests'; import { PrivateProfile, PublicProfile } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import { getDateRange, getEndYear, getProfilePicture, getUserRank, getYears } from '@/lib/utils'; import MyPositionIcon from '@/public/assets/icons/my-position-icon.svg'; import styles from '@/styles/pages/leaderboard.module.scss'; -import { GetServerSideProps } from 'next'; import { useRouter } from 'next/router'; import { useMemo, useState } from 'react'; @@ -171,12 +169,10 @@ const LeaderboardPage = ({ sort, leaderboard, user: { uuid } }: LeaderboardProps export default LeaderboardPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const AUTH_TOKEN = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); - +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authToken }) => { const sort = typeof query.sort === 'string' ? query.sort : getEndYear() - 1; - const leaderboard = await LeaderboardAPI.getLeaderboard(AUTH_TOKEN, getLeaderboardRange(sort)); + const leaderboard = await LeaderboardAPI.getLeaderboard(authToken, getLeaderboardRange(sort)); return { props: { title: 'Leaderboard', sort, leaderboard }, diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 243e0a68..ffa9ab47 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -1,17 +1,15 @@ import { config } from '@/lib'; import { UserAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; -import { CookieType } from '@/lib/types/enums'; -import type { GetServerSideProps, NextPage } from 'next'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; +import type { NextPage } from 'next'; const UserProfilePage: NextPage = () => null; export default UserProfilePage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res }) => { - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); - const user = await UserAPI.getCurrentUserAndRefreshCookie(token, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ req, res, authToken }) => { + const user = await UserAPI.getCurrentUserAndRefreshCookie(authToken, { req, res }); return { redirect: { diff --git a/src/pages/profile/edit.tsx b/src/pages/profile/edit.tsx index 9dc5653c..0535e44d 100644 --- a/src/pages/profile/edit.tsx +++ b/src/pages/profile/edit.tsx @@ -11,7 +11,7 @@ import { config, showToast } from '@/lib'; import { AuthAPI, ResumeAPI, UserAPI } from '@/lib/api'; import majors from '@/lib/constants/majors'; import socialMediaTypes from '@/lib/constants/socialMediaTypes'; -import withAccessType from '@/lib/hoc/withAccessType'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import { CookieService, PermissionService } from '@/lib/services'; import { ExistingSocialMedia, SocialMedia } from '@/lib/types/apiRequests'; import { PrivateProfile } from '@/lib/types/apiResponses'; @@ -20,7 +20,6 @@ import { capitalize, fixUrl, getProfilePicture, reportError } from '@/lib/utils' import DownloadIcon from '@/public/assets/icons/download-icon.svg'; import DropdownIcon from '@/public/assets/icons/dropdown-arrow-1.svg'; import styles from '@/styles/pages/EditProfile.module.scss'; -import type { GetServerSideProps } from 'next'; import Link from 'next/link'; import { FormEvent, useEffect, useId, useMemo, useState } from 'react'; @@ -590,12 +589,15 @@ const EditProfilePage = ({ user: initUser, authToken }: EditProfileProps) => { export default EditProfilePage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res }) => { - const AUTH_TOKEN = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ + req, + res, + authToken, +}) => { // Ensure `user` is up-to-date - const user = await UserAPI.getFreshCurrentUserAndRefreshCookie(AUTH_TOKEN, { req, res }); + const user = await UserAPI.getFreshCurrentUserAndRefreshCookie(authToken, { req, res }); - return { props: { title: 'Edit Profile', authToken: AUTH_TOKEN, user } }; + return { props: { title: 'Edit Profile', authToken, user } }; }; export const getServerSideProps = withAccessType( diff --git a/src/pages/store/collection/[uuid].tsx b/src/pages/store/collection/[uuid].tsx index 3ef8f4d8..9bb5e498 100644 --- a/src/pages/store/collection/[uuid].tsx +++ b/src/pages/store/collection/[uuid].tsx @@ -2,13 +2,12 @@ import { Typography } from '@/components/common'; import { CreateButton, HiddenIcon, ItemCard, Navbar, StoreEditButton } from '@/components/store'; import { config } from '@/lib'; import { StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import { CookieService, PermissionService } from '@/lib/services'; import { PrivateProfile, PublicMerchCollection } from '@/lib/types/apiResponses'; import { CookieType } from '@/lib/types/enums'; import { getDefaultMerchCollectionPhoto, getDefaultMerchItemPhoto } from '@/lib/utils'; import styles from '@/styles/pages/StoreCollectionPage.module.scss'; -import { GetServerSideProps } from 'next'; import Image from 'next/image'; import { useMemo } from 'react'; @@ -89,12 +88,16 @@ const CollectionsPage = ({ export default CollectionsPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ + params, + req, + res, + authToken, +}) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); const preview = CookieService.getServerCookie(CookieType.USER_PREVIEW_ENABLED, { req, res }); try { - const collection = await StoreAPI.getCollection(token, uuid); + const collection = await StoreAPI.getCollection(authToken, uuid); return { props: { title: collection.title, diff --git a/src/pages/store/collection/[uuid]/edit.tsx b/src/pages/store/collection/[uuid]/edit.tsx index cffd53f7..e9a2283a 100644 --- a/src/pages/store/collection/[uuid]/edit.tsx +++ b/src/pages/store/collection/[uuid]/edit.tsx @@ -2,12 +2,10 @@ import { CollectionDetailsForm } from '@/components/admin/store'; import { Navbar } from '@/components/store'; import { StoreAPI } from '@/lib/api'; import config from '@/lib/config'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PrivateProfile, PublicMerchCollection } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StoreItemEditPage.module.scss'; -import { GetServerSideProps } from 'next'; interface CollectionEditPageProps { user: PrivateProfile; @@ -25,10 +23,8 @@ const CollectionEditPage = ({ user: { credits }, token, collection }: Collection export default CollectionEditPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ params, authToken: token }) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); - try { const collection = await StoreAPI.getCollection(token, uuid); return { props: { title: `Edit ${collection.title}`, token, collection } }; diff --git a/src/pages/store/collection/new.tsx b/src/pages/store/collection/new.tsx index 39479c45..11092a43 100644 --- a/src/pages/store/collection/new.tsx +++ b/src/pages/store/collection/new.tsx @@ -2,12 +2,10 @@ import { CollectionDetailsForm } from '@/components/admin/store'; import { Navbar } from '@/components/store'; import { config } from '@/lib'; import { StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PrivateProfile, PublicMerchCollection } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StoreItemEditPage.module.scss'; -import { GetServerSideProps } from 'next'; interface CreateCollectionPageProps { user: PrivateProfile; @@ -25,8 +23,7 @@ const CreateCollectionPage = ({ user: { credits }, token, item }: CreateCollecti export default CreateCollectionPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authToken: token }) => { const item = typeof query.duplicate === 'string' ? await StoreAPI.getCollection(token, query.duplicate) diff --git a/src/pages/store/index.tsx b/src/pages/store/index.tsx index daa11f47..811abfcd 100644 --- a/src/pages/store/index.tsx +++ b/src/pages/store/index.tsx @@ -10,13 +10,12 @@ import { } from '@/components/store'; import { config, showToast } from '@/lib'; import { StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import { CookieService, PermissionService } from '@/lib/services'; import { PrivateProfile, PublicMerchCollection } from '@/lib/types/apiResponses'; import { CookieType } from '@/lib/types/enums'; import { getDefaultMerchCollectionPhoto } from '@/lib/utils'; import styles from '@/styles/pages/StoreHomePage.module.scss'; -import { GetServerSideProps } from 'next'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { useState } from 'react'; @@ -142,11 +141,15 @@ const StoreHomePage = ({ export default StoreHomePage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const AUTH_TOKEN = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ + req, + res, + query, + authToken, +}) => { const preview = CookieService.getServerCookie(CookieType.USER_PREVIEW_ENABLED, { req, res }); - const collections = await StoreAPI.getAllCollections(AUTH_TOKEN); + const collections = await StoreAPI.getAllCollections(authToken); return { props: { diff --git a/src/pages/store/item/[uuid].tsx b/src/pages/store/item/[uuid].tsx index 0a959434..553516fa 100644 --- a/src/pages/store/item/[uuid].tsx +++ b/src/pages/store/item/[uuid].tsx @@ -2,7 +2,7 @@ import { Typography } from '@/components/common'; import { CartOptionsGroup, ItemHeader, Navbar, SizeSelector } from '@/components/store'; import { config, showToast } from '@/lib'; import { StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; import { CartService, CookieService, PermissionService } from '@/lib/services'; import { PrivateProfile, @@ -13,7 +13,6 @@ import { CookieType } from '@/lib/types/enums'; import { getDefaultMerchItemPhoto } from '@/lib/utils'; import NoImage from '@/public/assets/graphics/cat404.png'; import styles from '@/styles/pages/StoreItemPage.module.scss'; -import { GetServerSideProps } from 'next'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; import { useId, useMemo, useState } from 'react'; @@ -131,13 +130,17 @@ const StoreItemPage = ({ export default StoreItemPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ + params, + req, + res, + authToken, +}) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); const preview = CookieService.getServerCookie(CookieType.USER_PREVIEW_ENABLED, { req, res }); try { - const item = await StoreAPI.getItem(token, uuid); + const item = await StoreAPI.getItem(authToken, uuid); return { props: { title: item.itemName, diff --git a/src/pages/store/item/[uuid]/edit.tsx b/src/pages/store/item/[uuid]/edit.tsx index 8a3fb703..9d48d634 100644 --- a/src/pages/store/item/[uuid]/edit.tsx +++ b/src/pages/store/item/[uuid]/edit.tsx @@ -2,12 +2,10 @@ import { ItemDetailsForm } from '@/components/admin/store'; import { Navbar } from '@/components/store'; import { StoreAPI } from '@/lib/api'; import config from '@/lib/config'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PrivateProfile, PublicMerchCollection, PublicMerchItem } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StoreItemEditPage.module.scss'; -import { GetServerSideProps } from 'next'; interface ItemEditPageProps { user: PrivateProfile; @@ -26,10 +24,8 @@ const ItemEditPage = ({ user: { credits }, token, item, collections }: ItemEditP export default ItemEditPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ params, authToken: token }) => { const uuid = params?.uuid as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); - try { const [item, collections] = await Promise.all([ StoreAPI.getItem(token, uuid), diff --git a/src/pages/store/item/new.tsx b/src/pages/store/item/new.tsx index 6d3e11b1..4c122750 100644 --- a/src/pages/store/item/new.tsx +++ b/src/pages/store/item/new.tsx @@ -2,12 +2,10 @@ import { ItemDetailsForm } from '@/components/admin/store'; import { Navbar } from '@/components/store'; import { config } from '@/lib'; import { StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PrivateProfile, PublicMerchCollection, PublicMerchItem } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StoreItemEditPage.module.scss'; -import { GetServerSideProps } from 'next'; interface CreateItemPageProps { user: PrivateProfile; @@ -31,8 +29,7 @@ const CreateItemPage = ({ user: { credits }, token, item, collections }: CreateI export default CreateItemPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authToken: token }) => { const [item, collections] = await Promise.all([ typeof query.duplicate === 'string' ? StoreAPI.getItem(token, query.duplicate) diff --git a/src/pages/store/orders.tsx b/src/pages/store/orders.tsx index 9b07d017..681efeaf 100644 --- a/src/pages/store/orders.tsx +++ b/src/pages/store/orders.tsx @@ -1,12 +1,10 @@ import { Dropdown, Typography } from '@/components/common'; import { Navbar, OrdersDisplay } from '@/components/store'; import { StoreAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { PrivateProfile, PublicOrder, PublicOrderPickupEvent } from '@/lib/types/apiResponses'; -import { CookieType } from '@/lib/types/enums'; import styles from '@/styles/pages/StoreOrders.module.scss'; -import { GetServerSideProps } from 'next'; import { useState } from 'react'; interface OrderPageProps { @@ -68,11 +66,9 @@ const StoreOrderPage = ({ user: { credits }, orders, futurePickupEvents }: Order export default StoreOrderPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res }) => { - const AUTH_TOKEN = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); - - const ordersPromise = StoreAPI.getAllOrders(AUTH_TOKEN); - const futurePickupEventsPromise = StoreAPI.getFutureOrderPickupEvents(AUTH_TOKEN); +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ authToken }) => { + const ordersPromise = StoreAPI.getAllOrders(authToken); + const futurePickupEventsPromise = StoreAPI.getFutureOrderPickupEvents(authToken); const [orders, futurePickupEvents] = await Promise.all([ ordersPromise, diff --git a/src/pages/u/[handle].tsx b/src/pages/u/[handle].tsx index d2e37563..930c7f17 100644 --- a/src/pages/u/[handle].tsx +++ b/src/pages/u/[handle].tsx @@ -6,11 +6,9 @@ import { } from '@/components/profile'; import { config } from '@/lib'; import { UserAPI } from '@/lib/api'; -import withAccessType from '@/lib/hoc/withAccessType'; -import { CookieService, PermissionService } from '@/lib/services'; -import { CookieType } from '@/lib/types/enums'; +import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { PermissionService } from '@/lib/services'; import { getProfilePicture } from '@/lib/utils'; -import type { GetServerSideProps } from 'next/types'; type UserHandlePageProps = UserHandleNotFoundProps | UserProfilePageProps; @@ -28,15 +26,19 @@ const UserHandlePage = (props: UserHandlePageProps) => { export default UserHandlePage; -const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) => { +const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ + params, + req, + res, + authToken, +}) => { const handle = params?.handle as string; - const token = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); try { const [handleUser, user, signedInAttendances] = await Promise.all([ - UserAPI.getUserByHandle(token, handle).catch(() => null), - UserAPI.getCurrentUserAndRefreshCookie(token, { req, res }), - UserAPI.getAttendancesForCurrentUser(token), + UserAPI.getUserByHandle(authToken, handle).catch(() => null), + UserAPI.getCurrentUserAndRefreshCookie(authToken, { req, res }), + UserAPI.getAttendancesForCurrentUser(authToken), ]); // render UserHandleNotFoundPage when user with handle is not retrieved @@ -49,7 +51,7 @@ const getServerSidePropsFunc: GetServerSideProps = async ({ params, req, res }) let recentAttendances = signedInAttendances.slice(-10).reverse(); // Otherwise, fetch the viewed user's attendances. if (!isSignedInUser && handleUser.isAttendancePublic) - recentAttendances = (await UserAPI.getAttendancesForUserByUUID(token, handleUser.uuid)) + recentAttendances = (await UserAPI.getAttendancesForUserByUUID(authToken, handleUser.uuid)) .slice(-10) .reverse(); From 9abc321c3fc6299bc2397393bb2c73386ff606fe Mon Sep 17 00:00:00 2001 From: Sean Date: Sat, 13 Apr 2024 23:38:27 -0700 Subject: [PATCH 04/11] Make events pages work without auth --- src/lib/api/EventAPI.ts | 5 ++- src/lib/hoc/withAccessType.ts | 47 +++++++++++++++--------- src/pages/events.tsx | 30 ++++++++++----- src/pages/events/[uuid].tsx | 69 ++++++++++++++++++----------------- 4 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/lib/api/EventAPI.ts b/src/lib/api/EventAPI.ts index 0ba8ff21..131ff253 100644 --- a/src/lib/api/EventAPI.ts +++ b/src/lib/api/EventAPI.ts @@ -20,10 +20,11 @@ import axios from 'axios'; /** * Get a single event by UUID * @param uuid Search query uuid - * @param token Bearer token + * @param token Bearer token. Optional, but should be provided if you need to + * see attendance codes. * @returns Event info */ -export const getEvent = async (uuid: UUID, token: string): Promise => { +export const getEvent = async (uuid: UUID, token?: string): Promise => { const requestUrl = `${config.api.baseUrl}${config.api.endpoints.event.event}/${uuid}`; const response = await axios.get(requestUrl, { diff --git a/src/lib/hoc/withAccessType.ts b/src/lib/hoc/withAccessType.ts index 1f8b9fff..9e049e77 100644 --- a/src/lib/hoc/withAccessType.ts +++ b/src/lib/hoc/withAccessType.ts @@ -12,6 +12,33 @@ import type { } from 'next'; import { ParsedUrlQuery } from 'querystring'; +/** + * Tries to read the user object from the cookie. If the user object doesn't + * exist or is invalid, then it will try to fetch a new one with the auth token. + */ +export async function getCurrentUser( + { req, res }: Pick, + authToken: string +): Promise { + const userCookie = CookieService.getServerCookie(CookieType.USER, { req, res }); + let user: PrivateProfile | undefined; + + if (userCookie) { + // Standard flow will use the existing user cookie as src data unless it's corrupted or missing keys, then try to refresh user otherwise redirect on fail + try { + user = JSON.parse(userCookie); + } catch { + user = undefined; + } + } + + if (!user?.accessType) { + user = await UserAPI.getCurrentUserAndRefreshCookie(authToken, { req, res }); + } + + return user; +} + export type GetServerSidePropsWithAuth< Props extends { [key: string]: any } = { [key: string]: any }, Params extends ParsedUrlQuery = ParsedUrlQuery, @@ -42,7 +69,6 @@ export default function withAccessType( // Generate a new getServerSideProps function by taking the return value of the original function and appending the user prop onto it if the user cookie exists, otherwise force user to login page const modified: GetServerSideProps = async (context: GetServerSidePropsContext) => { const { req, res } = context; - const userCookie = CookieService.getServerCookie(CookieType.USER, { req, res }); const authTokenCookie = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); const { homeRoute, loginRoute } = config; @@ -73,23 +99,8 @@ export default function withAccessType( return loginRedirect; } - let user: PrivateProfile | undefined; - let userAccessLevel: UserAccessType | undefined; - - if (userCookie) { - // Standard flow will use the existing user cookie as src data unless it's corrupted or missing keys, then try to refresh user otherwise redirect on fail - try { - user = JSON.parse(userCookie); - userAccessLevel = user?.accessType; - } catch { - user = undefined; - } - } - - if (!user || !userAccessLevel) { - user = await UserAPI.getCurrentUserAndRefreshCookie(authTokenCookie, { req, res }); - userAccessLevel = user.accessType; - } + const user = await getCurrentUser({ req, res }, authTokenCookie); + const userAccessLevel = user.accessType; // This block should be impossible to hit assuming the portal API doesn't go down if (!userAccessLevel) throw new Error('User access level is not defined'); diff --git a/src/pages/events.tsx b/src/pages/events.tsx index 26efee32..f902b174 100644 --- a/src/pages/events.tsx +++ b/src/pages/events.tsx @@ -3,9 +3,9 @@ import { DIVIDER } from '@/components/common/Dropdown'; import { EventDisplay } from '@/components/events'; import { config } from '@/lib'; import { EventAPI, UserAPI } from '@/lib/api'; -import withAccessType, { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { getCurrentUser } from '@/lib/hoc/withAccessType'; import useQueryState from '@/lib/hooks/useQueryState'; -import { PermissionService } from '@/lib/services'; +import { CookieService } from '@/lib/services'; import type { PublicAttendance, PublicEvent } from '@/lib/types/apiResponses'; import { FilterEventOptions, @@ -13,8 +13,10 @@ import { isValidCommunityFilter, isValidDateFilter, } from '@/lib/types/client'; +import { CookieType } from '@/lib/types/enums'; import { formatSearch, getDateRange, getYears } from '@/lib/utils'; import styles from '@/styles/pages/events.module.scss'; +import { GetServerSideProps } from 'next'; import { useMemo, useState } from 'react'; interface EventsPageProps { @@ -214,9 +216,13 @@ const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => export default EventsPage; -const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authToken }) => { +export const getServerSideProps: GetServerSideProps = async ({ req, res, query }) => { + const authToken: string | null = + CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }) ?? null; + const user = authToken !== null ? await getCurrentUser({ req, res }, authToken) : null; + const getEventsPromise = EventAPI.getAllEvents(); - const getAttendancesPromise = UserAPI.getAttendancesForCurrentUser(authToken); + const getAttendancesPromise = authToken ? UserAPI.getAttendancesForCurrentUser(authToken) : []; const [events, attendances] = await Promise.all([getEventsPromise, getAttendancesPromise]); @@ -229,10 +235,14 @@ const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ query, authT const initialFilters = { community, date, attendance, search }; - return { props: { title: 'Events', events, attendances, initialFilters } }; + return { + props: { + title: 'Events', + events, + attendances, + initialFilters, + // For navbar + user, + }, + }; }; - -export const getServerSideProps = withAccessType( - getServerSidePropsFunc, - PermissionService.loggedInUser -); diff --git a/src/pages/events/[uuid].tsx b/src/pages/events/[uuid].tsx index 45af6ca8..b910232c 100644 --- a/src/pages/events/[uuid].tsx +++ b/src/pages/events/[uuid].tsx @@ -2,14 +2,17 @@ import { Typography } from '@/components/common'; import EventDetail from '@/components/events/EventDetail'; import { Feedback, FeedbackForm } from '@/components/feedback'; import { EventAPI, FeedbackAPI, UserAPI } from '@/lib/api'; -import { GetServerSidePropsWithAuth } from '@/lib/hoc/withAccessType'; +import { getCurrentUser } from '@/lib/hoc/withAccessType'; +import { CookieService } from '@/lib/services'; import type { PublicEvent, PublicFeedback } from '@/lib/types/apiResponses'; +import { CookieType } from '@/lib/types/enums'; import { formatEventDate } from '@/lib/utils'; import styles from '@/styles/pages/event.module.scss'; +import { GetServerSideProps } from 'next'; import { useMemo, useState } from 'react'; interface EventPageProps { - token: string; + token: string | null; event: PublicEvent; attended: boolean; feedback: PublicFeedback | null; @@ -28,7 +31,7 @@ const EventPage = ({ token, event, attended, feedback: initFeedback }: EventPage ); - } else if (started) { + } else if (started && token) { feedbackForm = ( ); @@ -44,36 +47,36 @@ const EventPage = ({ token, event, attended, feedback: initFeedback }: EventPage export default EventPage; -const getServerSidePropsFunc: GetServerSidePropsWithAuth = async ({ - params, - user, - authToken: token, -}) => { +export const getServerSideProps: GetServerSideProps = async ({ params, req, res }) => { const uuid = params?.uuid as string; - // try { - const [event, attendances, [feedback = null]] = await Promise.all([ - EventAPI.getEvent(uuid, token), - user ? UserAPI.getAttendancesForCurrentUser(token) : [], - user ? FeedbackAPI.getFeedback(token, { user: user.uuid, event: uuid }) : [], - ]); - return { - props: { - title: event.title, - description: `${formatEventDate(event.start, event.end, true)} at ${event.location}\n\n${ - event.description - }`, - previewImage: event.cover, - bigPreviewImage: true, - token, - event, - attended: attendances.some(attendance => attendance.event.uuid === uuid), - feedback, - }, - }; - // } catch { - // return { notFound: true }; - // } -}; + const token: string | null = + CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }) ?? null; + const user = token !== null ? await getCurrentUser({ req, res }, token) : null; -export const getServerSideProps = getServerSidePropsFunc; + try { + const [event, attendances, [feedback = null]] = await Promise.all([ + EventAPI.getEvent(uuid, token), + token ? UserAPI.getAttendancesForCurrentUser(token) : [], + user ? FeedbackAPI.getFeedback(token, { user: user.uuid, event: uuid }) : [], + ]); + return { + props: { + title: event.title, + description: `${formatEventDate(event.start, event.end, true)} at ${event.location}\n\n${ + event.description + }`, + previewImage: event.cover, + bigPreviewImage: true, + token, + event, + attended: attendances.some(attendance => attendance.event.uuid === uuid), + feedback, + // For navbar + user, + }, + }; + } catch { + return { notFound: true }; + } +}; From 395057bf553a8fa044e2ecb94576deca7d870d41 Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 14 Apr 2024 00:23:38 -0700 Subject: [PATCH 05/11] Login appeal component --- src/components/common/Login/index.tsx | 113 ++++++++++++++++++ src/components/common/LoginAppeal/index.tsx | 40 +++++++ .../common/LoginAppeal/style.module.scss | 56 +++++++++ .../common/LoginAppeal/style.module.scss.d.ts | 13 ++ .../common/VerticalForm/style.module.scss | 3 +- src/components/common/index.ts | 1 + src/lib/config.ts | 1 + src/pages/events.tsx | 12 +- src/pages/events/[uuid].tsx | 11 +- src/pages/login.tsx | 105 +--------------- 10 files changed, 250 insertions(+), 105 deletions(-) create mode 100644 src/components/common/Login/index.tsx create mode 100644 src/components/common/LoginAppeal/index.tsx create mode 100644 src/components/common/LoginAppeal/style.module.scss create mode 100644 src/components/common/LoginAppeal/style.module.scss.d.ts diff --git a/src/components/common/Login/index.tsx b/src/components/common/Login/index.tsx new file mode 100644 index 00000000..302d0391 --- /dev/null +++ b/src/components/common/Login/index.tsx @@ -0,0 +1,113 @@ +import { SignInButton, SignInFormItem, SignInTitle } from '@/components/auth'; +import VerticalForm from '@/components/common/VerticalForm'; +import { config, showToast } from '@/lib'; +import { resendEmailVerification } from '@/lib/api/AuthAPI'; +import { AuthManager } from '@/lib/managers'; +import { CookieService, ValidationService } from '@/lib/services'; +import { URL } from '@/lib/types'; +import { LoginRequest } from '@/lib/types/apiRequests'; +import { PrivateProfile } from '@/lib/types/apiResponses'; +import { UserState } from '@/lib/types/enums'; +import { reportError } from '@/lib/utils'; +import { useRouter } from 'next/router'; +import { SubmitHandler, useForm } from 'react-hook-form'; +import { AiOutlineMail } from 'react-icons/ai'; +import { VscLock } from 'react-icons/vsc'; + +interface LoginProps { + destination: URL; + full?: boolean; +} + +const Login = ({ destination, full }: LoginProps) => { + const router = useRouter(); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + + const onSubmit: SubmitHandler = ({ email, password }) => { + AuthManager.login({ + email, + password, + onSuccessCallback: (user: PrivateProfile) => { + if (user.state === UserState.PENDING) { + showToast('Account Not Verified', 'Click to resend a verification email', [ + { + text: 'Send Email', + onClick: async () => { + await resendEmailVerification(user.email); + showToast(`Verification email sent to ${user.email}!`); + }, + }, + ]); + + CookieService.clearClientCookies(); + + return; + } + if (user.state === UserState.BLOCKED) { + showToast('This account has been disabled', 'Email acm@ucsd.edu if you have questions.'); + + CookieService.clearClientCookies(); + return; + } + + router.push(destination); + }, + onFailCallback: error => { + reportError('Unable to login', error); + }, + }); + }; + + return ( + + {full ? : null} + } + element="input" + name="email" + type="email" + placeholder="Email (user@ucsd.edu)" + formRegister={register('email', { + validate: email => { + const validation = ValidationService.isValidEmail(email); + return validation.valid || validation.error; + }, + })} + error={errors.email} + /> + } + name="password" + element="input" + type="password" + placeholder="Password" + formRegister={register('password', { + required: 'Required', + })} + error={errors.password} + /> + + + {full ? ( + + ) : null} + + ); +}; + +export default Login; diff --git a/src/components/common/LoginAppeal/index.tsx b/src/components/common/LoginAppeal/index.tsx new file mode 100644 index 00000000..9ec09c30 --- /dev/null +++ b/src/components/common/LoginAppeal/index.tsx @@ -0,0 +1,40 @@ +import Login from '@/components/common/Login'; +import Typography from '@/components/common/Typography'; +import { config } from '@/lib'; +import Link from 'next/link'; +import { useRouter } from 'next/router'; +import { ReactNode } from 'react'; +import styles from './style.module.scss'; + +interface LoginAppealProps { + children: ReactNode; +} + +const LoginAppeal = ({ children }: LoginAppealProps) => { + const router = useRouter(); + + return ( +
+
+ + Join ACM + + + {children} + +
+
+
+ +

+ Don‘t have an account?{' '} + + Sign up + +

+
+
+ ); +}; + +export default LoginAppeal; diff --git a/src/components/common/LoginAppeal/style.module.scss b/src/components/common/LoginAppeal/style.module.scss new file mode 100644 index 00000000..7999d738 --- /dev/null +++ b/src/components/common/LoginAppeal/style.module.scss @@ -0,0 +1,56 @@ +@use 'src/styles/vars.scss' as vars; + +.wrapper { + background-color: var(--theme-elevated-background); + border: 1px solid var(--theme-elevated-stroke); + border-radius: 0.5rem; + display: flex; + gap: 2rem; + padding: 2rem; + + @media (max-width: vars.$breakpoint-md) { + flex-direction: column; + text-align: center; + } + + .aside { + display: flex; + flex-basis: 30rem; + flex-direction: column; + gap: 1rem; + justify-content: center; + margin-right: auto; + + @media (max-width: vars.$breakpoint-md) { + flex-basis: auto; + margin: 0 auto; + max-width: 30rem; + } + } + + .line { + border-left: 1px solid var(--theme-elevated-stroke); + @media (max-width: vars.$breakpoint-md) { + border-top: 1px solid var(--theme-elevated-stroke); + margin: 0 auto; + width: 10rem; + } + } + + .login { + display: flex; + flex-direction: column; + gap: 2rem; + margin: 0 auto; + max-width: 300px; + width: 100%; + + .signUp { + color: var(--theme-blue-text-button); + + &:hover { + text-decoration: underline; + } + } + } +} diff --git a/src/components/common/LoginAppeal/style.module.scss.d.ts b/src/components/common/LoginAppeal/style.module.scss.d.ts new file mode 100644 index 00000000..d622337e --- /dev/null +++ b/src/components/common/LoginAppeal/style.module.scss.d.ts @@ -0,0 +1,13 @@ +export type Styles = { + aside: string; + line: string; + login: string; + signUp: string; + wrapper: string; +}; + +export type ClassNames = keyof Styles; + +declare const styles: Styles; + +export default styles; diff --git a/src/components/common/VerticalForm/style.module.scss b/src/components/common/VerticalForm/style.module.scss index de172949..3607df1a 100644 --- a/src/components/common/VerticalForm/style.module.scss +++ b/src/components/common/VerticalForm/style.module.scss @@ -7,5 +7,6 @@ height: calc(vars.$min-content-height - 4rem); justify-content: center; margin: 0 auto; - width: 300px; + max-width: 300px; + width: 100%; } diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 473d5f31..a6e9570b 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -6,6 +6,7 @@ export { default as Dropdown } from './Dropdown'; export { default as EditButton } from './EditButton'; export { default as GifSafeImage } from './GifSafeImage'; export { default as LinkButton } from './LinkButton'; +export { default as LoginAppeal } from './LoginAppeal'; export { default as Modal } from './Modal'; export { default as PaginationControls } from './PaginationControls'; export { default as SEO } from './SEO'; diff --git a/src/lib/config.ts b/src/lib/config.ts index fa4a347a..9878ba1f 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -79,6 +79,7 @@ const config = { }, homeRoute: '/', eventsRoute: '/events', + registerRoute: '/register', loginRoute: '/login', logoutRoute: '/logout', leaderboardRoute: '/leaderboard', diff --git a/src/pages/events.tsx b/src/pages/events.tsx index f902b174..fe2bb726 100644 --- a/src/pages/events.tsx +++ b/src/pages/events.tsx @@ -1,4 +1,4 @@ -import { Dropdown, PaginationControls, Typography } from '@/components/common'; +import { Dropdown, LoginAppeal, PaginationControls, Typography } from '@/components/common'; import { DIVIDER } from '@/components/common/Dropdown'; import { EventDisplay } from '@/components/events'; import { config } from '@/lib'; @@ -23,6 +23,7 @@ interface EventsPageProps { events: PublicEvent[]; attendances: PublicAttendance[]; initialFilters: FilterEventOptions; + loggedOut: boolean; } interface FilterOptions { @@ -76,7 +77,7 @@ const DEFAULT_FILTER_STATE = { const ROWS_PER_PAGE = 25; -const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => { +const EventsPage = ({ events, attendances, initialFilters, loggedOut }: EventsPageProps) => { const [page, setPage] = useState(0); const years = useMemo(getYears, []); @@ -131,6 +132,12 @@ const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => return (
Events + {loggedOut ? ( + + Create an account to check into events, give feedback, earn points, and join a community + of thousands. + + ) : null}
+ Create an account to check into events, give feedback, earn points, and join a community of + thousands. + + ); + } else if (feedback) { feedbackForm = (
diff --git a/src/pages/login.tsx b/src/pages/login.tsx index f69c8873..46ca86f3 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -1,111 +1,16 @@ -import { SignInButton, SignInFormItem, SignInTitle } from '@/components/auth'; -import { VerticalForm } from '@/components/common'; -import { config, showToast } from '@/lib'; -import { resendEmailVerification } from '@/lib/api/AuthAPI'; -import { AuthManager } from '@/lib/managers'; -import { CookieService, ValidationService } from '@/lib/services'; +import Login from '@/components/common/Login'; +import { config } from '@/lib'; +import { CookieService } from '@/lib/services'; import { URL } from '@/lib/types'; -import type { LoginRequest } from '@/lib/types/apiRequests'; -import type { PrivateProfile } from '@/lib/types/apiResponses'; -import { CookieType, UserState } from '@/lib/types/enums'; -import { reportError } from '@/lib/utils'; +import { CookieType } from '@/lib/types/enums'; import type { GetServerSideProps, NextPage } from 'next'; -import { useRouter } from 'next/router'; -import { SubmitHandler, useForm } from 'react-hook-form'; -import { AiOutlineMail } from 'react-icons/ai'; -import { VscLock } from 'react-icons/vsc'; interface LoginProps { destination: URL; } const LoginPage: NextPage = ({ destination }) => { - const router = useRouter(); - - const { - register, - handleSubmit, - formState: { errors }, - } = useForm(); - - const onSubmit: SubmitHandler = ({ email, password }) => { - AuthManager.login({ - email, - password, - onSuccessCallback: (user: PrivateProfile) => { - if (user.state === UserState.PENDING) { - showToast('Account Not Verified', 'Click to resend a verification email', [ - { - text: 'Send Email', - onClick: async () => { - await resendEmailVerification(user.email); - showToast(`Verification email sent to ${user.email}!`); - }, - }, - ]); - - CookieService.clearClientCookies(); - - return; - } - if (user.state === UserState.BLOCKED) { - showToast('This account has been disabled', 'Email acm@ucsd.edu if you have questions.'); - - CookieService.clearClientCookies(); - return; - } - - router.push(destination); - }, - onFailCallback: error => { - reportError('Unable to login', error); - }, - }); - }; - - return ( - - - } - element="input" - name="email" - type="email" - placeholder="Email (user@ucsd.edu)" - formRegister={register('email', { - validate: email => { - const validation = ValidationService.isValidEmail(email); - return validation.valid || validation.error; - }, - })} - error={errors.email} - /> - } - name="password" - element="input" - type="password" - placeholder="Password" - formRegister={register('password', { - required: 'Required', - })} - error={errors.password} - /> - - - - - ); + return ; }; export default LoginPage; From d5f8341d510768f07731ee904924a49a4cd24aed Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 14 Apr 2024 00:38:45 -0700 Subject: [PATCH 06/11] Set theme color to ACM general blue This does not affect the theme color changes that are made to the theme-color thing after loading the page --- src/components/admin/store/DetailsForm/style.module.scss | 2 +- src/components/common/SEO/index.tsx | 2 ++ src/components/common/SEO/style.module.scss | 5 +++++ src/components/common/SEO/style.module.scss.d.ts | 9 +++++++++ src/pages/_document.tsx | 1 - 5 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/components/common/SEO/style.module.scss create mode 100644 src/components/common/SEO/style.module.scss.d.ts diff --git a/src/components/admin/store/DetailsForm/style.module.scss b/src/components/admin/store/DetailsForm/style.module.scss index ca9dcccb..a2a34a27 100644 --- a/src/components/admin/store/DetailsForm/style.module.scss +++ b/src/components/admin/store/DetailsForm/style.module.scss @@ -90,5 +90,5 @@ } :export { - #{defaultThemeColorHex}: vars.$light-primary-2; + #{defaultThemeColorHex}: $blue-5; } diff --git a/src/components/common/SEO/index.tsx b/src/components/common/SEO/index.tsx index e3d7c85e..0942122a 100644 --- a/src/components/common/SEO/index.tsx +++ b/src/components/common/SEO/index.tsx @@ -1,5 +1,6 @@ import Logo from '@/public/assets/acm-logos/general/light-mode.png'; import Head from 'next/head'; +import style from './style.module.scss'; const TITLE = 'ACM UCSD Membership Portal'; const DESC = @@ -42,6 +43,7 @@ const SEO = ({ {bigPreviewImage ? : null} {/* preview description text */} + ); }; diff --git a/src/components/common/SEO/style.module.scss b/src/components/common/SEO/style.module.scss new file mode 100644 index 00000000..0cde48e2 --- /dev/null +++ b/src/components/common/SEO/style.module.scss @@ -0,0 +1,5 @@ +@use 'src/styles/vars.scss' as vars; + +:export { + #{defaultThemeColorHex}: $blue-5; +} diff --git a/src/components/common/SEO/style.module.scss.d.ts b/src/components/common/SEO/style.module.scss.d.ts new file mode 100644 index 00000000..23048cec --- /dev/null +++ b/src/components/common/SEO/style.module.scss.d.ts @@ -0,0 +1,9 @@ +export type Styles = { + defaultThemeColorHex: string; +}; + +export type ClassNames = keyof Styles; + +declare const styles: Styles; + +export default styles; diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 18b0af18..8e95c972 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -6,7 +6,6 @@ export default function Document() { -
From 2049ab6120526497f31e4df08d7e1b71fdb350f4 Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 14 Apr 2024 00:40:47 -0700 Subject: [PATCH 07/11] fix scss blue variable --- src/components/admin/store/DetailsForm/style.module.scss | 2 +- src/components/common/SEO/style.module.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/admin/store/DetailsForm/style.module.scss b/src/components/admin/store/DetailsForm/style.module.scss index a2a34a27..b99e9a63 100644 --- a/src/components/admin/store/DetailsForm/style.module.scss +++ b/src/components/admin/store/DetailsForm/style.module.scss @@ -90,5 +90,5 @@ } :export { - #{defaultThemeColorHex}: $blue-5; + #{defaultThemeColorHex}: vars.$blue-5; } diff --git a/src/components/common/SEO/style.module.scss b/src/components/common/SEO/style.module.scss index 0cde48e2..4875a9d3 100644 --- a/src/components/common/SEO/style.module.scss +++ b/src/components/common/SEO/style.module.scss @@ -1,5 +1,5 @@ @use 'src/styles/vars.scss' as vars; :export { - #{defaultThemeColorHex}: $blue-5; + #{defaultThemeColorHex}: vars.$blue-5; } From 3ac4d1a63f66c8954158bc6a384d2eafcc2b2167 Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 17 Apr 2024 17:26:02 -0700 Subject: [PATCH 08/11] Hide attendance filter when logged out --- src/pages/events.tsx | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/pages/events.tsx b/src/pages/events.tsx index fe2bb726..fa27b915 100644 --- a/src/pages/events.tsx +++ b/src/pages/events.tsx @@ -191,22 +191,24 @@ const EventsPage = ({ events, attendances, initialFilters, loggedOut }: EventsPa />
-
- { - setStates('attendance', v); - setPage(0); - }} - /> -
+ {loggedOut ? null : ( +
+ { + setStates('attendance', v); + setPage(0); + }} + /> +
+ )}
From f37d2503b7aecaeb4cf4450b5fe0bfbebc994d6f Mon Sep 17 00:00:00 2001 From: Sean Date: Wed, 17 Apr 2024 17:53:04 -0700 Subject: [PATCH 09/11] Show feedback form immediately when checked into event --- src/pages/events/[uuid].tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/events/[uuid].tsx b/src/pages/events/[uuid].tsx index 2834e392..f7ed91b0 100644 --- a/src/pages/events/[uuid].tsx +++ b/src/pages/events/[uuid].tsx @@ -38,7 +38,10 @@ const EventPage = ({ token, event, attended, feedback: initFeedback }: EventPage
); - } else if (started && token) { + } else if ((started || attended) && token) { + // People can check in before the event starts, and the check-in modal + // prompts them to add feedback. If they click "Add feedback" before the + // event starts, it should still let them give feedback feedbackForm = ( ); From 777678a7bfe4c4455e8960edc4c8acb2eaef8156 Mon Sep 17 00:00:00 2001 From: Sean Date: Tue, 23 Apr 2024 15:29:28 -0700 Subject: [PATCH 10/11] refactor: Rename auth/SignIn components to common/VerticalForm because they're used for more than just signing in Cherry picked from e75f2c06d4a856fe7ea019751c525f5bca358d77; accidentally committed to wrong branch --- src/components/auth/index.ts | 3 -- .../VerticalFormButton}/index.tsx | 6 ++-- .../VerticalFormButton}/style.module.scss | 0 .../style.module.scss.d.ts | 0 .../VerticalFormItem}/index.tsx | 6 ++-- .../VerticalFormItem}/style.module.scss | 0 .../VerticalFormItem}/style.module.scss.d.ts | 0 .../VerticalFormTitle}/index.tsx | 6 ++-- .../VerticalFormTitle}/style.module.scss | 0 .../VerticalFormTitle}/style.module.scss.d.ts | 0 src/components/common/index.ts | 3 ++ .../profile/UserHandleNotFound/index.tsx | 5 ++-- src/pages/404.tsx | 5 ++-- src/pages/500.tsx | 5 ++-- src/pages/admin/attendance.tsx | 18 +++++++----- src/pages/admin/milestone.tsx | 16 +++++++---- src/pages/admin/points.tsx | 21 +++++++++----- src/pages/check-email.tsx | 5 ++-- src/pages/forgot-password.tsx | 14 ++++++---- src/pages/register.tsx | 28 +++++++++++-------- src/pages/reset-password/[accessCode].tsx | 16 +++++++---- src/pages/verify-email/[accessCode].tsx | 7 ++--- 22 files changed, 93 insertions(+), 71 deletions(-) delete mode 100644 src/components/auth/index.ts rename src/components/{auth/SignInButton => common/VerticalFormButton}/index.tsx (82%) rename src/components/{auth/SignInButton => common/VerticalFormButton}/style.module.scss (100%) rename src/components/{auth/SignInButton => common/VerticalFormButton}/style.module.scss.d.ts (100%) rename src/components/{auth/SignInFormItem => common/VerticalFormItem}/index.tsx (91%) rename src/components/{auth/SignInFormItem => common/VerticalFormItem}/style.module.scss (100%) rename src/components/{auth/SignInFormItem => common/VerticalFormItem}/style.module.scss.d.ts (100%) rename src/components/{auth/SignInTitle => common/VerticalFormTitle}/index.tsx (62%) rename src/components/{auth/SignInTitle => common/VerticalFormTitle}/style.module.scss (100%) rename src/components/{auth/SignInTitle => common/VerticalFormTitle}/style.module.scss.d.ts (100%) diff --git a/src/components/auth/index.ts b/src/components/auth/index.ts deleted file mode 100644 index e950b3b8..00000000 --- a/src/components/auth/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default as SignInButton } from './SignInButton'; -export { default as SignInFormItem } from './SignInFormItem'; -export { default as SignInTitle } from './SignInTitle'; diff --git a/src/components/auth/SignInButton/index.tsx b/src/components/common/VerticalFormButton/index.tsx similarity index 82% rename from src/components/auth/SignInButton/index.tsx rename to src/components/common/VerticalFormButton/index.tsx index 1d9b8b9d..a5b3f786 100644 --- a/src/components/auth/SignInButton/index.tsx +++ b/src/components/common/VerticalFormButton/index.tsx @@ -20,9 +20,9 @@ interface DisplayOptions { style?: CSSProperties; } -type SignInButtonProps = (LinkInterface | ButtonInterface) & DisplayOptions; +type VerticalFormButtonProps = (LinkInterface | ButtonInterface) & DisplayOptions; -const SignInButton = (props: SignInButtonProps) => { +const VerticalFormButton = (props: VerticalFormButtonProps) => { const { type, text, display, style = {} } = props; if (type === 'link') { @@ -45,4 +45,4 @@ const SignInButton = (props: SignInButtonProps) => { return null; }; -export default SignInButton; +export default VerticalFormButton; diff --git a/src/components/auth/SignInButton/style.module.scss b/src/components/common/VerticalFormButton/style.module.scss similarity index 100% rename from src/components/auth/SignInButton/style.module.scss rename to src/components/common/VerticalFormButton/style.module.scss diff --git a/src/components/auth/SignInButton/style.module.scss.d.ts b/src/components/common/VerticalFormButton/style.module.scss.d.ts similarity index 100% rename from src/components/auth/SignInButton/style.module.scss.d.ts rename to src/components/common/VerticalFormButton/style.module.scss.d.ts diff --git a/src/components/auth/SignInFormItem/index.tsx b/src/components/common/VerticalFormItem/index.tsx similarity index 91% rename from src/components/auth/SignInFormItem/index.tsx rename to src/components/common/VerticalFormItem/index.tsx index 79c2fe59..71ecb5b2 100644 --- a/src/components/auth/SignInFormItem/index.tsx +++ b/src/components/common/VerticalFormItem/index.tsx @@ -20,9 +20,9 @@ interface FormItemProps { inputHeight?: string; } -type SignInFormProps = FormItemProps & (InputTypeProps | SelectTypeProps); +type VerticalFormProps = FormItemProps & (InputTypeProps | SelectTypeProps); -const SignInFormItem = (props: SignInFormProps) => { +const VerticalFormItem = (props: VerticalFormProps) => { const { icon, placeholder, formRegister, element, error, inputHeight } = props; if (element === 'input') { @@ -74,4 +74,4 @@ const SignInFormItem = (props: SignInFormProps) => { return null; }; -export default SignInFormItem; +export default VerticalFormItem; diff --git a/src/components/auth/SignInFormItem/style.module.scss b/src/components/common/VerticalFormItem/style.module.scss similarity index 100% rename from src/components/auth/SignInFormItem/style.module.scss rename to src/components/common/VerticalFormItem/style.module.scss diff --git a/src/components/auth/SignInFormItem/style.module.scss.d.ts b/src/components/common/VerticalFormItem/style.module.scss.d.ts similarity index 100% rename from src/components/auth/SignInFormItem/style.module.scss.d.ts rename to src/components/common/VerticalFormItem/style.module.scss.d.ts diff --git a/src/components/auth/SignInTitle/index.tsx b/src/components/common/VerticalFormTitle/index.tsx similarity index 62% rename from src/components/auth/SignInTitle/index.tsx rename to src/components/common/VerticalFormTitle/index.tsx index e3a485d7..dc425272 100644 --- a/src/components/auth/SignInTitle/index.tsx +++ b/src/components/common/VerticalFormTitle/index.tsx @@ -1,11 +1,11 @@ import styles from './style.module.scss'; -interface SignInTitleProps { +interface VerticalFormTitleProps { text: string; description?: string; } -const SignInTitle = ({ text, description }: SignInTitleProps) => { +const VerticalFormTitle = ({ text, description }: VerticalFormTitleProps) => { return ( <>

{text}

@@ -14,4 +14,4 @@ const SignInTitle = ({ text, description }: SignInTitleProps) => { ); }; -export default SignInTitle; +export default VerticalFormTitle; diff --git a/src/components/auth/SignInTitle/style.module.scss b/src/components/common/VerticalFormTitle/style.module.scss similarity index 100% rename from src/components/auth/SignInTitle/style.module.scss rename to src/components/common/VerticalFormTitle/style.module.scss diff --git a/src/components/auth/SignInTitle/style.module.scss.d.ts b/src/components/common/VerticalFormTitle/style.module.scss.d.ts similarity index 100% rename from src/components/auth/SignInTitle/style.module.scss.d.ts rename to src/components/common/VerticalFormTitle/style.module.scss.d.ts diff --git a/src/components/common/index.ts b/src/components/common/index.ts index a6e9570b..ac0322a0 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -13,3 +13,6 @@ export { default as SEO } from './SEO'; export { default as Typography } from './Typography'; export type { Variant } from './Typography'; export { default as VerticalForm } from './VerticalForm'; +export { default as VerticalFormButton } from './VerticalFormButton'; +export { default as VerticalFormItem } from './VerticalFormItem'; +export { default as VerticalFormTitle } from './VerticalFormTitle'; diff --git a/src/components/profile/UserHandleNotFound/index.tsx b/src/components/profile/UserHandleNotFound/index.tsx index 4bc258d8..1d7b5e76 100644 --- a/src/components/profile/UserHandleNotFound/index.tsx +++ b/src/components/profile/UserHandleNotFound/index.tsx @@ -1,5 +1,4 @@ -import { SignInButton } from '@/components/auth'; -import { Typography, VerticalForm } from '@/components/common'; +import { Typography, VerticalForm, VerticalFormButton } from '@/components/common'; import Cat404 from '@/public/assets/graphics/cat404.png'; import Image from 'next/image'; @@ -13,6 +12,6 @@ export const UserHandleNotFound = ({ handle }: UserHandleNotFoundProps) => ( No user with handle ‘{handle}’ was found. Sad Cat - + ); diff --git a/src/pages/404.tsx b/src/pages/404.tsx index 3951b70e..f4c11058 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,5 +1,4 @@ -import { SignInButton } from '@/components/auth'; -import { VerticalForm } from '@/components/common'; +import { VerticalForm, VerticalFormButton } from '@/components/common'; import Cat404 from '@/public/assets/graphics/cat404.png'; import styles from '@/styles/pages/404.module.scss'; import Image from 'next/image'; @@ -10,7 +9,7 @@ const PageNotFound = () => {

Whoops, we ended up on the wrong page!

Sad Cat - +
); diff --git a/src/pages/500.tsx b/src/pages/500.tsx index 9c224123..c95c9143 100644 --- a/src/pages/500.tsx +++ b/src/pages/500.tsx @@ -1,5 +1,4 @@ -import { SignInButton } from '@/components/auth'; -import { VerticalForm } from '@/components/common'; +import { VerticalForm, VerticalFormButton } from '@/components/common'; import Cat404 from '@/public/assets/graphics/cat404.png'; import styles from '@/styles/pages/404.module.scss'; import Image from 'next/image'; @@ -17,7 +16,7 @@ const InternalServerError = () => { > Logging out will usually fix your issue. - + { return ( - - } element="input" name="email" @@ -44,7 +48,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.email} /> - } element="input" name="description" @@ -55,7 +59,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.description} /> - } name="points" element="input" @@ -66,7 +70,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.points} /> - { return ( - - } element="input" name="name" @@ -40,7 +44,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.name} /> - } name="points" element="input" @@ -51,7 +55,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.points} /> - { return ( - - + } element="input" name="email" @@ -41,7 +48,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.email} /> - } element="input" name="description" @@ -52,7 +59,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.description} /> - } name="points" element="input" @@ -63,7 +70,7 @@ const AwardPointsPage: NextPage = () => { })} error={errors.points} /> - { We've sent an email to {email} to verify your email address and activate your account. - { return ( - - + } element="input" name="email" @@ -49,7 +53,7 @@ const ForgotPassword: NextPage = () => { })} error={errors.email} /> - { }} onEnterPress={handleSubmit(onSubmit)} > - - + } name="firstName" type="text" @@ -67,7 +71,7 @@ const RegisterPage: NextPage = () => { })} inputHeight="1.5rem" /> - } name="lastName" type="text" @@ -79,7 +83,7 @@ const RegisterPage: NextPage = () => { })} inputHeight="1.5rem" /> - } name="email" type="email" @@ -94,7 +98,7 @@ const RegisterPage: NextPage = () => { })} inputHeight="1.5rem" /> - } name="password" type="password" @@ -109,7 +113,7 @@ const RegisterPage: NextPage = () => { })} inputHeight="1.5rem" /> - } name="confirmPassword" type="password" @@ -129,7 +133,7 @@ const RegisterPage: NextPage = () => { })} inputHeight="1.5rem" /> - } name="major" options={majors} @@ -139,7 +143,7 @@ const RegisterPage: NextPage = () => { formRegister={register('major')} inputHeight="1.25rem" /> - } name="major" element="select" @@ -151,13 +155,13 @@ const RegisterPage: NextPage = () => { })} inputHeight="1.25rem" /> - - { return ( - - + } type="password" element="input" @@ -62,7 +66,7 @@ const ResetPasswordPage = ({ code }: ResetPasswordProps) => { })} error={errors.newPassword} /> - } type="password" element="input" @@ -82,7 +86,7 @@ const ResetPasswordPage = ({ code }: ResetPasswordProps) => { })} error={errors.confirmPassword} /> - { }} >

Success! Your account has been verified.

- { }} >

Unable to verify your email! Please try again.

- Date: Tue, 23 Apr 2024 15:32:29 -0700 Subject: [PATCH 11/11] fix Login component --- src/components/common/Login/index.tsx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/common/Login/index.tsx b/src/components/common/Login/index.tsx index 302d0391..b6507a8b 100644 --- a/src/components/common/Login/index.tsx +++ b/src/components/common/Login/index.tsx @@ -1,5 +1,7 @@ -import { SignInButton, SignInFormItem, SignInTitle } from '@/components/auth'; import VerticalForm from '@/components/common/VerticalForm'; +import VerticalFormButton from '@/components/common/VerticalFormButton'; +import VerticalFormItem from '@/components/common/VerticalFormItem'; +import VerticalFormTitle from '@/components/common/VerticalFormTitle'; import { config, showToast } from '@/lib'; import { resendEmailVerification } from '@/lib/api/AuthAPI'; import { AuthManager } from '@/lib/managers'; @@ -65,8 +67,8 @@ const Login = ({ destination, full }: LoginProps) => { return ( - {full ? : null} - : null} + } element="input" name="email" @@ -80,7 +82,7 @@ const Login = ({ destination, full }: LoginProps) => { })} error={errors.email} /> - } name="password" element="input" @@ -91,20 +93,25 @@ const Login = ({ destination, full }: LoginProps) => { })} error={errors.password} /> - - {full ? ( - + ) : null} );