From 92f220bb2d978a532a14793bbe23dec407315801 Mon Sep 17 00:00:00 2001 From: Faris Ashai Date: Sat, 30 Mar 2024 13:48:15 -0700 Subject: [PATCH 1/9] Admin Page Authentication --- src/pages/admin/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/admin/index.tsx b/src/pages/admin/index.tsx index c11b2181..231463ad 100644 --- a/src/pages/admin/index.tsx +++ b/src/pages/admin/index.tsx @@ -29,7 +29,11 @@ const AdminPage = ({ user: { accessType }, preview }: AdminProps) => { margin: '1rem 0', }} > - Manage Events + {PermissionService.canManageEvents.includes(accessType) ? ( + Manage Events + ) : ( + 'Restricted Access' + )}
Store From 4ec0d8b4758deb79b6697e4e5a286087415a1657 Mon Sep 17 00:00:00 2001 From: Faris Ashai Date: Sat, 30 Mar 2024 13:49:07 -0700 Subject: [PATCH 2/9] Progress Link to Leaderboard --- src/components/profile/UserProgress/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/profile/UserProgress/index.tsx b/src/components/profile/UserProgress/index.tsx index c6306f31..af900dcf 100644 --- a/src/components/profile/UserProgress/index.tsx +++ b/src/components/profile/UserProgress/index.tsx @@ -1,6 +1,8 @@ import { Typography, type Variant } from '@/components/common'; +import { config } from '@/lib'; import { type PublicProfile } from '@/lib/types/apiResponses'; import { getLevel, getUserRank } from '@/lib/utils'; +import Link from 'next/link'; import { useEffect, useState } from 'react'; import styles from './style.module.scss'; @@ -31,7 +33,7 @@ export const UserProgress = ({ currentRank === nextLevelRank ? `Level ${getLevel(points + 100)}` : nextLevelRank; return ( -
+ {isSignedInUser ? 'Your' : `${firstName}'s`} Progress @@ -56,6 +58,6 @@ export const UserProgress = ({ {100 - (points % 100)} more points to level up to {nextLevelText}
- + ); }; From 1aa700925a5a0194df9ef98b2991fdcd49564aac Mon Sep 17 00:00:00 2001 From: Faris Ashai Date: Sat, 30 Mar 2024 14:19:54 -0700 Subject: [PATCH 3/9] Event Page Query Params --- src/components/events/EventCarousel/index.tsx | 12 ++++++- src/pages/events.tsx | 35 +++++++++++++++---- src/pages/index.tsx | 4 +++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/components/events/EventCarousel/index.tsx b/src/components/events/EventCarousel/index.tsx index 27a319e4..e5371844 100644 --- a/src/components/events/EventCarousel/index.tsx +++ b/src/components/events/EventCarousel/index.tsx @@ -7,6 +7,11 @@ import type { ReactNode } from 'react'; import EventCard from '../EventCard'; import styles from './style.module.scss'; +export type FilterEventOptions = { + community?: 'all' | 'general' | 'ai' | 'cyber' | 'design' | 'hack'; + date?: 'all-time' | 'past-year' | 'past-month' | 'past-week' | 'upcoming'; + attended?: 'all' | 'attended' | 'not-attended'; +}; interface EventCarouselProps { title: string | ReactNode; titleClassName?: string; @@ -14,6 +19,7 @@ interface EventCarouselProps { attendances: PublicAttendance[]; placeholder: string; className?: string; + linkFilters?: FilterEventOptions; } const EventCarousel = ({ @@ -23,6 +29,7 @@ const EventCarousel = ({ attendances, placeholder, className = '', + linkFilters = {}, }: EventCarouselProps) => { return (
@@ -32,7 +39,10 @@ const EventCarousel = ({ {title}
- + See all events > diff --git a/src/pages/events.tsx b/src/pages/events.tsx index 27c978f5..9590c702 100644 --- a/src/pages/events.tsx +++ b/src/pages/events.tsx @@ -9,11 +9,17 @@ 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'; +import { useRouter } from 'next/router'; +import { useEffect, useMemo, useState } from 'react'; interface EventsPageProps { events: PublicEvent[]; attendances: PublicAttendance[]; + initialFilters: { + community: string; + date: string; + attended: string; + }; } interface FilterOptions { @@ -59,13 +65,24 @@ const filterEvent = ( }; const ROWS_PER_PAGE = 25; -const EventsPage = ({ events, attendances }: EventsPageProps) => { +const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => { const [page, setPage] = useState(0); - const [communityFilter, setCommunityFilter] = useState('all'); - const [dateFilter, setDateFilter] = useState('upcoming'); - const [attendedFilter, setAttendedFilter] = useState('all'); + const [communityFilter, setCommunityFilter] = useState(initialFilters.community); + const [dateFilter, setDateFilter] = useState(initialFilters.date); + const [attendedFilter, setAttendedFilter] = useState(initialFilters.attended); const [query, setQuery] = useState(''); + const router = useRouter(); + + useEffect(() => { + const validState = + initialFilters.community === communityFilter && + initialFilters.date === dateFilter && + initialFilters.attended === attendedFilter; + if (!validState) router.reload(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router, initialFilters]); + const years = useMemo(getYears, []); const filteredEvents = events.filter(e => @@ -171,7 +188,7 @@ const EventsPage = ({ events, attendances }: EventsPageProps) => { export default EventsPage; -const getServerSidePropsFunc: GetServerSideProps = async ({ req, res }) => { +const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) => { const authToken = CookieService.getServerCookie(CookieType.ACCESS_TOKEN, { req, res }); const getEventsPromise = EventAPI.getAllEvents(); @@ -179,7 +196,11 @@ const getServerSidePropsFunc: GetServerSideProps = async ({ req, res }) => { const [events, attendances] = await Promise.all([getEventsPromise, getAttendancesPromise]); - return { props: { events, attendances } }; + const { community = 'all', date = 'all-time', attended = 'all' } = query; + + const initialFilters = { community, date, attended }; + + return { props: { events, attendances, initialFilters } }; }; export const getServerSideProps = withAccessType( diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 78ad5475..46c97606 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -150,6 +150,9 @@ const PortalHomePage = ({ Date: Sat, 30 Mar 2024 14:54:25 -0700 Subject: [PATCH 4/9] Revert "Progress Link to Leaderboard" This reverts commit 4ec0d8b4758deb79b6697e4e5a286087415a1657. --- src/components/profile/UserProgress/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/profile/UserProgress/index.tsx b/src/components/profile/UserProgress/index.tsx index af900dcf..c6306f31 100644 --- a/src/components/profile/UserProgress/index.tsx +++ b/src/components/profile/UserProgress/index.tsx @@ -1,8 +1,6 @@ import { Typography, type Variant } from '@/components/common'; -import { config } from '@/lib'; import { type PublicProfile } from '@/lib/types/apiResponses'; import { getLevel, getUserRank } from '@/lib/utils'; -import Link from 'next/link'; import { useEffect, useState } from 'react'; import styles from './style.module.scss'; @@ -33,7 +31,7 @@ export const UserProgress = ({ currentRank === nextLevelRank ? `Level ${getLevel(points + 100)}` : nextLevelRank; return ( - +
{isSignedInUser ? 'Your' : `${firstName}'s`} Progress @@ -58,6 +56,6 @@ export const UserProgress = ({ {100 - (points % 100)} more points to level up to {nextLevelText}
- + ); }; From 0f5ad1dd0e7856f122318dab4d699f1cbbc69a65 Mon Sep 17 00:00:00 2001 From: Faris Ashai Date: Sat, 30 Mar 2024 14:54:40 -0700 Subject: [PATCH 5/9] Formatting Fix --- src/pages/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 46c97606..d9fb5f5c 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -150,9 +150,7 @@ const PortalHomePage = ({ Date: Sat, 30 Mar 2024 14:57:04 -0700 Subject: [PATCH 6/9] Fix link area --- src/components/profile/UserProfilePage/index.tsx | 5 +++-- src/pages/index.tsx | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/profile/UserProfilePage/index.tsx b/src/components/profile/UserProfilePage/index.tsx index 1a7e086b..11b61a08 100644 --- a/src/components/profile/UserProfilePage/index.tsx +++ b/src/components/profile/UserProfilePage/index.tsx @@ -9,6 +9,7 @@ import { copy, fixUrl, getProfilePicture } from '@/lib/utils'; import GradCapIcon from '@/public/assets/icons/grad-cap-icon.svg'; import LeaderboardIcon from '@/public/assets/icons/leaderboard-icon.svg'; import MajorIcon from '@/public/assets/icons/major-icon.svg'; +import Link from 'next/link'; import styles from './style.module.scss'; export interface UserProfilePageProps { @@ -71,7 +72,7 @@ export const UserProfilePage = ({ ) : null} -
+ -
+
diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d9fb5f5c..c0e2ac2b 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -144,9 +144,9 @@ const PortalHomePage = ({
-
+ -
+ Date: Sat, 30 Mar 2024 15:02:07 -0700 Subject: [PATCH 7/9] Trailing ? and prop name --- src/components/events/EventCarousel/index.tsx | 8 +++++--- src/pages/index.tsx | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/events/EventCarousel/index.tsx b/src/components/events/EventCarousel/index.tsx index e5371844..cf1fafb0 100644 --- a/src/components/events/EventCarousel/index.tsx +++ b/src/components/events/EventCarousel/index.tsx @@ -19,7 +19,7 @@ interface EventCarouselProps { attendances: PublicAttendance[]; placeholder: string; className?: string; - linkFilters?: FilterEventOptions; + initialEventFilters?: FilterEventOptions; } const EventCarousel = ({ @@ -29,8 +29,10 @@ const EventCarousel = ({ attendances, placeholder, className = '', - linkFilters = {}, + initialEventFilters = {}, }: EventCarouselProps) => { + const eventQueryParams = new URLSearchParams(initialEventFilters).toString(); + return (
@@ -41,7 +43,7 @@ const EventCarousel = ({
See all events > diff --git a/src/pages/index.tsx b/src/pages/index.tsx index c0e2ac2b..7705dca5 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -150,7 +150,7 @@ const PortalHomePage = ({ Date: Sat, 30 Mar 2024 15:10:45 -0700 Subject: [PATCH 8/9] border color should match outline on hover --- src/components/events/EventCard/style.module.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/events/EventCard/style.module.scss b/src/components/events/EventCard/style.module.scss index c9e47298..cebf99b3 100644 --- a/src/components/events/EventCard/style.module.scss +++ b/src/components/events/EventCard/style.module.scss @@ -24,22 +24,27 @@ transform: scale(0.975); &[data-community='General'] { + border-color: vars.$blue-4; outline-color: vars.$blue-4; } &[data-community='Ai'] { + border-color: vars.$scarlet-4; outline-color: vars.$scarlet-4; } &[data-community='Hack'] { + border-color: vars.$orange-4; outline-color: vars.$orange-4; } &[data-community='Cyber'] { + border-color: vars.$cyan-4; outline-color: vars.$cyan-4; } &[data-community='Design'] { + border-color: vars.$pink-4; outline-color: vars.$pink-4; } } From d5ab6c3523fbbec3aff46db74e132161b55531ec Mon Sep 17 00:00:00 2001 From: Faris Ashai Date: Sat, 30 Mar 2024 16:26:46 -0700 Subject: [PATCH 9/9] Sync URL and field state to share filtered views --- src/components/events/EventCarousel/index.tsx | 14 +-- src/lib/types/client.ts | 6 ++ src/pages/events.tsx | 101 ++++++++++++++---- src/pages/index.tsx | 4 +- 4 files changed, 91 insertions(+), 34 deletions(-) diff --git a/src/components/events/EventCarousel/index.tsx b/src/components/events/EventCarousel/index.tsx index cf1fafb0..f016e248 100644 --- a/src/components/events/EventCarousel/index.tsx +++ b/src/components/events/EventCarousel/index.tsx @@ -1,17 +1,13 @@ import { Carousel, Typography } from '@/components/common'; import { config } from '@/lib'; import { PublicAttendance, PublicEvent } from '@/lib/types/apiResponses'; +import { type FilterEventOptions } from '@/lib/types/client'; import DiamondFriends from '@/public/assets/graphics/portal/diamond-friends.svg'; import Link from 'next/link'; import type { ReactNode } from 'react'; import EventCard from '../EventCard'; import styles from './style.module.scss'; -export type FilterEventOptions = { - community?: 'all' | 'general' | 'ai' | 'cyber' | 'design' | 'hack'; - date?: 'all-time' | 'past-year' | 'past-month' | 'past-week' | 'upcoming'; - attended?: 'all' | 'attended' | 'not-attended'; -}; interface EventCarouselProps { title: string | ReactNode; titleClassName?: string; @@ -29,10 +25,8 @@ const EventCarousel = ({ attendances, placeholder, className = '', - initialEventFilters = {}, + initialEventFilters, }: EventCarouselProps) => { - const eventQueryParams = new URLSearchParams(initialEventFilters).toString(); - return (
@@ -43,7 +37,9 @@ const EventCarousel = ({
See all events > diff --git a/src/lib/types/client.ts b/src/lib/types/client.ts index a3a749a5..783f72dc 100644 --- a/src/lib/types/client.ts +++ b/src/lib/types/client.ts @@ -13,3 +13,9 @@ export interface ClientCartItem extends Omit a.event.uuid === event.uuid); @@ -67,21 +74,65 @@ const filterEvent = ( const ROWS_PER_PAGE = 25; const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => { const [page, setPage] = useState(0); - const [communityFilter, setCommunityFilter] = useState(initialFilters.community); - const [dateFilter, setDateFilter] = useState(initialFilters.date); - const [attendedFilter, setAttendedFilter] = useState(initialFilters.attended); + const [communityFilter, setCommunityFilter] = useState(initialFilters.community); + const [dateFilter, setDateFilter] = useState(initialFilters.date); + const [attendedFilter, setAttendedFilter] = useState(initialFilters.attended); const [query, setQuery] = useState(''); const router = useRouter(); useEffect(() => { - const validState = - initialFilters.community === communityFilter && - initialFilters.date === dateFilter && - initialFilters.attended === attendedFilter; - if (!validState) router.reload(); + const urlSynced = + router.query.attended === attendedFilter && + router.query.date === dateFilter && + router.query.community === communityFilter; + + if (!urlSynced) { + if (Object.keys(router.query).length === 0) { + // If url is /events, we can set to default instead of looking for query parameters + setCommunityFilter(DEFAULT_FILTER_STATE.community); + setDateFilter(DEFAULT_FILTER_STATE.date); + setAttendedFilter(DEFAULT_FILTER_STATE.attended); + return; + } + + // If the URL state has directly changed, we should update the client filter state to match + setAttendedFilter(router.query.attended as string); + setDateFilter(router.query.date as string); + setCommunityFilter(router.query.community as string); + } + + // If we add the filter state as a depeandency here, changing the URL params will enter an infinite callback loop + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [router]); + + useEffect(() => { + const urlSynced = + router.query.attended === attendedFilter && + router.query.date === dateFilter && + router.query.community === communityFilter; + + if (!urlSynced) { + if ( + attendedFilter === DEFAULT_FILTER_STATE.attended && + dateFilter === DEFAULT_FILTER_STATE.date && + communityFilter === DEFAULT_FILTER_STATE.community + ) + // We can leave the URL alone in the default state of /events + return; + + // If the client state of the filters has changed, we should update the URL to match + router.replace( + `${config.eventsRoute}?${new URLSearchParams({ + attended: attendedFilter, + community: communityFilter, + date: dateFilter, + })}` + ); + } + // If we add the router as a depeandency here, changing the URL params will enter an infinite callback loop // eslint-disable-next-line react-hooks/exhaustive-deps - }, [router, initialFilters]); + }, [dateFilter, attendedFilter, communityFilter]); const years = useMemo(getYears, []); @@ -141,10 +192,10 @@ const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => ariaLabel="Filter events by time" options={[ { value: 'upcoming', label: 'Upcoming' }, - { value: 'past-week', label: 'Past week' }, - { value: 'past-month', label: 'Past month' }, - { value: 'past-year', label: 'Past year' }, - { value: 'all-time', label: 'All time' }, + { value: 'past-week', label: 'Past Week' }, + { value: 'past-month', label: 'Past Month' }, + { value: 'past-year', label: 'Past Year' }, + { value: 'all-time', label: 'All Time' }, DIVIDER, ...years, ]} @@ -161,9 +212,9 @@ const EventsPage = ({ events, attendances, initialFilters }: EventsPageProps) => name="timeOptions" ariaLabel="Filter events by attendance" options={[ - { value: 'all', label: 'Any attendance' }, + { value: 'any', label: 'Any Attendance' }, { value: 'attended', label: 'Attended' }, - { value: 'not-attended', label: 'Not attended' }, + { value: 'not-attended', label: 'Not Attended' }, ]} value={attendedFilter} onChange={v => { @@ -196,7 +247,11 @@ const getServerSidePropsFunc: GetServerSideProps = async ({ req, res, query }) = const [events, attendances] = await Promise.all([getEventsPromise, getAttendancesPromise]); - const { community = 'all', date = 'all-time', attended = 'all' } = query; + const { + community = DEFAULT_FILTER_STATE.community, + date = DEFAULT_FILTER_STATE.date, + attended = DEFAULT_FILTER_STATE.attended, + } = query as FilterEventOptions; const initialFilters = { community, date, attended }; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 7705dca5..4dbb2227 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -150,7 +150,7 @@ const PortalHomePage = ({