From 7c0185b0cdbb4d3dc791082137888f5198e16432 Mon Sep 17 00:00:00 2001 From: ARtheboss Date: Tue, 14 Jan 2025 21:22:48 +0530 Subject: [PATCH 01/10] some dashboard updates --- .../Catalog/Dashboard/Dashboard.module.scss | 6 ++ .../src/app/Catalog/Dashboard/index.tsx | 80 +++++++++++++++---- apps/frontend/src/app/Catalog/index.tsx | 2 + .../Carousel/Carousel.module.scss | 0 .../Carousel/index.tsx | 0 .../CarouselClass/CarouselClass.module.scss | 65 +++++++++++++++ .../src/components/CarouselClass/index.tsx | 63 +++++++++++++++ apps/frontend/src/components/Class/index.tsx | 16 +++- .../components/ClassBrowser/Header/index.tsx | 1 + apps/frontend/src/lib/recents.ts | 30 +++++++ docker-compose.yml | 2 +- 11 files changed, 246 insertions(+), 19 deletions(-) rename apps/frontend/src/{app/Catalog/Dashboard => components}/Carousel/Carousel.module.scss (100%) rename apps/frontend/src/{app/Catalog/Dashboard => components}/Carousel/index.tsx (100%) create mode 100644 apps/frontend/src/components/CarouselClass/CarouselClass.module.scss create mode 100644 apps/frontend/src/components/CarouselClass/index.tsx create mode 100644 apps/frontend/src/lib/recents.ts diff --git a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss index 923f7f27f..608e65b95 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss +++ b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss @@ -8,6 +8,12 @@ border-radius: 8px; background-color: var(--foreground-color); flex-shrink: 0; + .error { + color: var(--paragraph-color); + text-align: center; + margin-top: 46px; + font-size: 14px; + } } .header { diff --git a/apps/frontend/src/app/Catalog/Dashboard/index.tsx b/apps/frontend/src/app/Catalog/Dashboard/index.tsx index c1f1255ab..13095d7bb 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/index.tsx +++ b/apps/frontend/src/app/Catalog/Dashboard/index.tsx @@ -3,7 +3,6 @@ import { Dispatch, SetStateAction } from "react"; import { ArrowSeparateVertical, BookmarkSolid, - Calendar, Collapse, Expand, Search, @@ -11,20 +10,59 @@ import { import { Button, Container, IconButton, Tooltip } from "@repo/theme"; -import Carousel from "./Carousel"; +import Carousel from "@/components/Carousel"; import styles from "./Dashboard.module.scss"; +import { useReadUser } from "@/hooks/api"; +import CarouselClass from "@/components/CarouselClass"; +import { ITerm } from "@/lib/api"; +import { getRecents } from "@/lib/recents"; interface DashboardProps { + term: ITerm | null | undefined, + onSelect: (subject: string, courseNumber: string, number: string) => void; expanded: boolean; setExpanded: Dispatch>; setOpen: Dispatch>; } +function getOrdinalSuffix(day: number): string { + if (day > 3 && day < 21) return 'th'; // applies to 11-20 + switch (day % 10) { + case 1: return 'st'; + case 2: return 'nd'; + case 3: return 'rd'; + default: return 'th'; + } +} + +function formatDate(date: Date): string { + const monthNames = [ + 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' + ]; + + const day = date.getDate(); + const month = monthNames[date.getMonth()]; + const ordinalSuffix = getOrdinalSuffix(day); + + return `${month} ${day}${ordinalSuffix}`; +} + + export default function Dashboard({ + term, + onSelect, expanded, setExpanded, setOpen, }: DashboardProps) { + + const { data: user, loading: userLoading } = useReadUser(); + + if (!term) return <> + + const recents = getRecents().filter((v) => v.semester == term.semester && v.year == term.year); + return (
@@ -45,23 +83,37 @@ export default function Dashboard({
-

Summer 2024

-

March 20th through August 9th

- }> -
-
-
-
- } to="/semesters"> +

{term.semester} {term.year}

+ {term.startDate && term.endDate ?

{formatDate(new Date(term.startDate))} through {formatDate(new Date(term.endDate!))}

: <> } + {/* } to="/semesters">
-
+
*/} } to="/account"> -
-
-
+ {(userLoading || !user) ?
+
Sign in to bookmark classes
+
: user?.bookmarkedClasses.length == 0 ? +
+
No bookmarked classes
+
: + user?.bookmarkedClasses.filter((c) => !term || (c.year == term.year && c.semester == term.semester)).map((c, i) => { + return (
+ +
) + }) + }
+ { recents.length == 0 ? + <> : + }> { + recents.filter((c) => !term || (c.year == term.year && c.semester == term.semester)).map((c, i) => { + return (
+ +
) + }) } +
+ } ); diff --git a/apps/frontend/src/app/Catalog/index.tsx b/apps/frontend/src/app/Catalog/index.tsx index 41a6b8f75..562cd8306 100644 --- a/apps/frontend/src/app/Catalog/index.tsx +++ b/apps/frontend/src/app/Catalog/index.tsx @@ -144,6 +144,8 @@ export default function Catalog() { /> ) : ( void; + year: number; + semester: Semester; + subject: string; + courseNumber: string; + number: string; +} + +export default function CarouselClass({ onSelect, year, semester, subject, courseNumber, number }: CarouselClassProps) { + + const { data, loading } = useReadClass( + year as number, + semester as Semester, + subject as string, + courseNumber as string, + number as string, + ); + + const _class = useMemo(() => data, [data]); + + return ( + loading || !_class ? <> : +
onSelect(subject, courseNumber, number)} + > +
+

+ {_class.subject} {_class.courseNumber} #{_class.number} +

+

{_class.title ?? _class.course.title}

+
+ + + +
+
+
+
+ +
+
+
+ ); +} diff --git a/apps/frontend/src/components/Class/index.tsx b/apps/frontend/src/components/Class/index.tsx index f5d639a6b..f563831c6 100644 --- a/apps/frontend/src/components/Class/index.tsx +++ b/apps/frontend/src/components/Class/index.tsx @@ -40,6 +40,7 @@ import { IClass, Semester } from "@/lib/api"; import { getExternalLink } from "@/lib/section"; import styles from "./Class.module.scss"; +import { addToRecent } from "@/lib/recents"; const Enrollment = lazy(() => import("./Enrollment")); const Grades = lazy(() => import("./Grades")); @@ -195,14 +196,13 @@ export default function Class({ const bookmarkedClasses = bookmarked ? user.bookmarkedClasses.filter( (bookmarkedClass) => - bookmarkedClass.subject === _class?.subject && + !(bookmarkedClass.subject === _class?.subject && bookmarkedClass.courseNumber === _class?.courseNumber && bookmarkedClass.number === _class?.number && bookmarkedClass.year === _class?.year && - bookmarkedClass.semester === _class?.semester + bookmarkedClass.semester === _class?.semester) ) : user.bookmarkedClasses.concat(_class); - await updateUser( { bookmarkedClasses: bookmarkedClasses.map((bookmarkedClass) => ({ @@ -232,6 +232,14 @@ export default function Class({ // TODO: Error state if (!course || !_class || !pin) { return <>; + } else { + addToRecent({ + subject: _class.subject, + year: _class.year, + semester: _class.semester, + courseNumber: _class.courseNumber, + number: _class.number + }) } return ( @@ -254,7 +262,7 @@ export default function Class({ [styles.active]: bookmarked, })} onClick={() => bookmark()} - disabled={userLoading} + // disabled={userLoading} // setting disabled to false still appears on my version > {bookmarked ? : } diff --git a/apps/frontend/src/components/ClassBrowser/Header/index.tsx b/apps/frontend/src/components/ClassBrowser/Header/index.tsx index 4e2aedc94..3fde0c63b 100644 --- a/apps/frontend/src/components/ClassBrowser/Header/index.tsx +++ b/apps/frontend/src/components/ClassBrowser/Header/index.tsx @@ -37,6 +37,7 @@ export default function Header() { placeholder={`Search ${semester} ${year} classes...`} onFocus={() => setExpanded(false)} autoFocus + autoComplete="off" />
{classes.length.toLocaleString()}
setExpanded(!expanded)}> diff --git a/apps/frontend/src/lib/recents.ts b/apps/frontend/src/lib/recents.ts new file mode 100644 index 000000000..df562bf0f --- /dev/null +++ b/apps/frontend/src/lib/recents.ts @@ -0,0 +1,30 @@ +import { Semester } from "./api" + +interface RecentClassData { + subject: string, + year: number, + semester: Semester, + courseNumber: string, + number: string +} + +export function addToRecent({ subject, year, semester, courseNumber, number }: RecentClassData) { + const recents = getRecents(); + const newRcd = { + subject: subject, + year: year, + semester: semester, + courseNumber: courseNumber, + number: number + } as RecentClassData + const alreadyExists = recents.reduce((v, rcd) => v || (newRcd.subject == rcd.subject && newRcd.year == rcd.year && newRcd.semester == rcd.semester && newRcd.courseNumber == rcd.courseNumber && newRcd.number == rcd.number), false) + if (alreadyExists) return; + recents.push(newRcd) + localStorage.setItem("recents-data", JSON.stringify(recents)) +} + +export function getRecents() { + const res = localStorage.getItem("recents-data"); + if (res) return JSON.parse(res) as RecentClassData[]; + return []; +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7b086f350..105288832 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: networks: - bt ports: - - 8080:8080 + - 3000:8080 restart: always volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf From 16ab448fc43961f09b5d5d27e8ac13732bd92ca2 Mon Sep 17 00:00:00 2001 From: ARtheboss Date: Tue, 14 Jan 2025 21:23:03 +0530 Subject: [PATCH 02/10] lint --- .../src/app/Catalog/Dashboard/index.tsx | 127 +++++++++++++----- .../src/components/CarouselClass/index.tsx | 67 +++++---- apps/frontend/src/components/Class/index.tsx | 18 +-- apps/frontend/src/lib/recents.ts | 61 +++++---- 4 files changed, 178 insertions(+), 95 deletions(-) diff --git a/apps/frontend/src/app/Catalog/Dashboard/index.tsx b/apps/frontend/src/app/Catalog/Dashboard/index.tsx index 13095d7bb..6402d3797 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/index.tsx +++ b/apps/frontend/src/app/Catalog/Dashboard/index.tsx @@ -11,14 +11,15 @@ import { import { Button, Container, IconButton, Tooltip } from "@repo/theme"; import Carousel from "@/components/Carousel"; -import styles from "./Dashboard.module.scss"; -import { useReadUser } from "@/hooks/api"; import CarouselClass from "@/components/CarouselClass"; +import { useReadUser } from "@/hooks/api"; import { ITerm } from "@/lib/api"; import { getRecents } from "@/lib/recents"; +import styles from "./Dashboard.module.scss"; + interface DashboardProps { - term: ITerm | null | undefined, + term: ITerm | null | undefined; onSelect: (subject: string, courseNumber: string, number: string) => void; expanded: boolean; setExpanded: Dispatch>; @@ -26,19 +27,33 @@ interface DashboardProps { } function getOrdinalSuffix(day: number): string { - if (day > 3 && day < 21) return 'th'; // applies to 11-20 + if (day > 3 && day < 21) return "th"; // applies to 11-20 switch (day % 10) { - case 1: return 'st'; - case 2: return 'nd'; - case 3: return 'rd'; - default: return 'th'; + case 1: + return "st"; + case 2: + return "nd"; + case 3: + return "rd"; + default: + return "th"; } } function formatDate(date: Date): string { const monthNames = [ - 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December' + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", ]; const day = date.getDate(); @@ -48,7 +63,6 @@ function formatDate(date: Date): string { return `${month} ${day}${ordinalSuffix}`; } - export default function Dashboard({ term, onSelect, @@ -56,12 +70,13 @@ export default function Dashboard({ setExpanded, setOpen, }: DashboardProps) { - const { data: user, loading: userLoading } = useReadUser(); - if (!term) return <> + if (!term) return <>; - const recents = getRecents().filter((v) => v.semester == term.semester && v.year == term.year); + const recents = getRecents().filter( + (v) => v.semester == term.semester && v.year == term.year + ); return (
@@ -83,37 +98,79 @@ export default function Dashboard({
-

{term.semester} {term.year}

- {term.startDate && term.endDate ?

{formatDate(new Date(term.startDate))} through {formatDate(new Date(term.endDate!))}

: <> } +

+ {term.semester} {term.year} +

+ {term.startDate && term.endDate ? ( +

+ {formatDate(new Date(term.startDate))} through{" "} + {formatDate(new Date(term.endDate!))} +

+ ) : ( + <> + )} {/* } to="/semesters">
*/} } to="/account"> - {(userLoading || !user) ?
-
Sign in to bookmark classes
-
: user?.bookmarkedClasses.length == 0 ? + {userLoading || !user ? ( +
+
Sign in to bookmark classes
+
+ ) : user?.bookmarkedClasses.length == 0 ? (
No bookmarked classes
-
: - user?.bookmarkedClasses.filter((c) => !term || (c.year == term.year && c.semester == term.semester)).map((c, i) => { - return (
- -
) - }) - } + + ) : ( + user?.bookmarkedClasses + .filter( + (c) => + !term || (c.year == term.year && c.semester == term.semester) + ) + .map((c, i) => { + return ( +
+ +
+ ); + }) + )}
- { recents.length == 0 ? - <> : - }> { - recents.filter((c) => !term || (c.year == term.year && c.semester == term.semester)).map((c, i) => { - return (
- -
) - }) } + {recents.length == 0 ? ( + <> + ) : ( + }> + {" "} + {recents + .filter( + (c) => + !term || (c.year == term.year && c.semester == term.semester) + ) + .map((c, i) => { + return ( +
+ +
+ ); + })}
- } + )} ); diff --git a/apps/frontend/src/components/CarouselClass/index.tsx b/apps/frontend/src/components/CarouselClass/index.tsx index a0b3e7e2c..38e42c10d 100644 --- a/apps/frontend/src/components/CarouselClass/index.tsx +++ b/apps/frontend/src/components/CarouselClass/index.tsx @@ -5,10 +5,10 @@ import { ArrowRight, NewTab, OpenInWindow } from "iconoir-react"; import AverageGrade from "@/components/AverageGrade"; import Capacity from "@/components/Capacity"; import Units from "@/components/Units"; +import { useReadClass } from "@/hooks/api"; import { IClass, Semester } from "@/lib/api"; import styles from "./CarouselClass.module.scss"; -import { useReadClass } from "@/hooks/api"; interface CarouselClassProps { onSelect: (subject: string, courseNumber: string, number: string) => void; @@ -19,45 +19,54 @@ interface CarouselClassProps { number: string; } -export default function CarouselClass({ onSelect, year, semester, subject, courseNumber, number }: CarouselClassProps) { - +export default function CarouselClass({ + onSelect, + year, + semester, + subject, + courseNumber, + number, +}: CarouselClassProps) { const { data, loading } = useReadClass( year as number, semester as Semester, subject as string, courseNumber as string, - number as string, + number as string ); const _class = useMemo(() => data, [data]); - return ( - loading || !_class ? <> : -
onSelect(subject, courseNumber, number)} - > -
-

- {_class.subject} {_class.courseNumber} #{_class.number} -

-

{_class.title ?? _class.course.title}

-
- - - -
+ return loading || !_class ? ( + <> + ) : ( +
onSelect(subject, courseNumber, number)} + > +
+

+ {_class.subject} {_class.courseNumber} #{_class.number} +

+

+ {_class.title ?? _class.course.title} +

+
+ + +
-
-
- -
+
+
+
+
+
); } diff --git a/apps/frontend/src/components/Class/index.tsx b/apps/frontend/src/components/Class/index.tsx index f563831c6..c61cc155f 100644 --- a/apps/frontend/src/components/Class/index.tsx +++ b/apps/frontend/src/components/Class/index.tsx @@ -37,10 +37,10 @@ import { useReadCourse, useReadUser, useUpdateUser } from "@/hooks/api"; import { useReadClass } from "@/hooks/api/classes/useReadClass"; import usePins from "@/hooks/usePins"; import { IClass, Semester } from "@/lib/api"; +import { addToRecent } from "@/lib/recents"; import { getExternalLink } from "@/lib/section"; import styles from "./Class.module.scss"; -import { addToRecent } from "@/lib/recents"; const Enrollment = lazy(() => import("./Enrollment")); const Grades = lazy(() => import("./Grades")); @@ -196,11 +196,13 @@ export default function Class({ const bookmarkedClasses = bookmarked ? user.bookmarkedClasses.filter( (bookmarkedClass) => - !(bookmarkedClass.subject === _class?.subject && - bookmarkedClass.courseNumber === _class?.courseNumber && - bookmarkedClass.number === _class?.number && - bookmarkedClass.year === _class?.year && - bookmarkedClass.semester === _class?.semester) + !( + bookmarkedClass.subject === _class?.subject && + bookmarkedClass.courseNumber === _class?.courseNumber && + bookmarkedClass.number === _class?.number && + bookmarkedClass.year === _class?.year && + bookmarkedClass.semester === _class?.semester + ) ) : user.bookmarkedClasses.concat(_class); await updateUser( @@ -238,8 +240,8 @@ export default function Class({ year: _class.year, semester: _class.semester, courseNumber: _class.courseNumber, - number: _class.number - }) + number: _class.number, + }); } return ( diff --git a/apps/frontend/src/lib/recents.ts b/apps/frontend/src/lib/recents.ts index df562bf0f..6c5d47d62 100644 --- a/apps/frontend/src/lib/recents.ts +++ b/apps/frontend/src/lib/recents.ts @@ -1,30 +1,45 @@ -import { Semester } from "./api" +import { Semester } from "./api"; interface RecentClassData { - subject: string, - year: number, - semester: Semester, - courseNumber: string, - number: string + subject: string; + year: number; + semester: Semester; + courseNumber: string; + number: string; } -export function addToRecent({ subject, year, semester, courseNumber, number }: RecentClassData) { - const recents = getRecents(); - const newRcd = { - subject: subject, - year: year, - semester: semester, - courseNumber: courseNumber, - number: number - } as RecentClassData - const alreadyExists = recents.reduce((v, rcd) => v || (newRcd.subject == rcd.subject && newRcd.year == rcd.year && newRcd.semester == rcd.semester && newRcd.courseNumber == rcd.courseNumber && newRcd.number == rcd.number), false) - if (alreadyExists) return; - recents.push(newRcd) - localStorage.setItem("recents-data", JSON.stringify(recents)) +export function addToRecent({ + subject, + year, + semester, + courseNumber, + number, +}: RecentClassData) { + const recents = getRecents(); + const newRcd = { + subject: subject, + year: year, + semester: semester, + courseNumber: courseNumber, + number: number, + } as RecentClassData; + const alreadyExists = recents.reduce( + (v, rcd) => + v || + (newRcd.subject == rcd.subject && + newRcd.year == rcd.year && + newRcd.semester == rcd.semester && + newRcd.courseNumber == rcd.courseNumber && + newRcd.number == rcd.number), + false + ); + if (alreadyExists) return; + recents.push(newRcd); + localStorage.setItem("recents-data", JSON.stringify(recents)); } export function getRecents() { - const res = localStorage.getItem("recents-data"); - if (res) return JSON.parse(res) as RecentClassData[]; - return []; -} \ No newline at end of file + const res = localStorage.getItem("recents-data"); + if (res) return JSON.parse(res) as RecentClassData[]; + return []; +} From 13fc52cbeabd3178478d9089aa12d6eb36ce1633 Mon Sep 17 00:00:00 2001 From: ARtheboss Date: Tue, 14 Jan 2025 21:24:24 +0530 Subject: [PATCH 03/10] fixed warnings --- apps/frontend/src/components/CarouselClass/index.tsx | 6 +++--- apps/frontend/src/components/Class/index.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/frontend/src/components/CarouselClass/index.tsx b/apps/frontend/src/components/CarouselClass/index.tsx index 38e42c10d..7120b37f0 100644 --- a/apps/frontend/src/components/CarouselClass/index.tsx +++ b/apps/frontend/src/components/CarouselClass/index.tsx @@ -1,12 +1,12 @@ -import { MouseEventHandler, forwardRef, useMemo } from "react"; +import { useMemo } from "react"; -import { ArrowRight, NewTab, OpenInWindow } from "iconoir-react"; +import { OpenInWindow } from "iconoir-react"; import AverageGrade from "@/components/AverageGrade"; import Capacity from "@/components/Capacity"; import Units from "@/components/Units"; import { useReadClass } from "@/hooks/api"; -import { IClass, Semester } from "@/lib/api"; +import { Semester } from "@/lib/api"; import styles from "./CarouselClass.module.scss"; diff --git a/apps/frontend/src/components/Class/index.tsx b/apps/frontend/src/components/Class/index.tsx index c61cc155f..f53338079 100644 --- a/apps/frontend/src/components/Class/index.tsx +++ b/apps/frontend/src/components/Class/index.tsx @@ -264,7 +264,7 @@ export default function Class({ [styles.active]: bookmarked, })} onClick={() => bookmark()} - // disabled={userLoading} // setting disabled to false still appears on my version + disabled={userLoading} // setting disabled to false still appears on my version > {bookmarked ? : } From 54f7fadc4f71bd7fcd88992fe76520092163c414 Mon Sep 17 00:00:00 2001 From: ARtheboss Date: Wed, 15 Jan 2025 21:02:56 +0530 Subject: [PATCH 04/10] basic catalog dashboard page --- .../Catalog/Dashboard/Dashboard.module.scss | 2 +- .../src/app/Catalog/Dashboard/index.tsx | 29 +++++- apps/frontend/src/app/Catalog/index.tsx | 9 +- .../DropdownMenu/DropdownMenu.module.scss | 91 +++++++++++++++++++ .../src/components/DropdownMenu/index.tsx | 49 ++++++++++ packages/theme/src/components/index.ts | 1 + 6 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 packages/theme/src/components/DropdownMenu/DropdownMenu.module.scss create mode 100644 packages/theme/src/components/DropdownMenu/index.tsx diff --git a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss index 608e65b95..332740760 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss +++ b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss @@ -54,4 +54,4 @@ color: var(--paragraph-color); margin-top: 8px; } -} +} \ No newline at end of file diff --git a/apps/frontend/src/app/Catalog/Dashboard/index.tsx b/apps/frontend/src/app/Catalog/Dashboard/index.tsx index 6402d3797..551a7f1ac 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/index.tsx +++ b/apps/frontend/src/app/Catalog/Dashboard/index.tsx @@ -15,11 +15,14 @@ import CarouselClass from "@/components/CarouselClass"; import { useReadUser } from "@/hooks/api"; import { ITerm } from "@/lib/api"; import { getRecents } from "@/lib/recents"; +import { DropdownMenu } from "@repo/theme"; import styles from "./Dashboard.module.scss"; interface DashboardProps { term: ITerm | null | undefined; + termList: ITerm[] | undefined; + changeTerm: React.Dispatch>; onSelect: (subject: string, courseNumber: string, number: string) => void; expanded: boolean; setExpanded: Dispatch>; @@ -65,6 +68,8 @@ function formatDate(date: Date): string { export default function Dashboard({ term, + termList, + changeTerm, onSelect, expanded, setExpanded, @@ -82,10 +87,26 @@ export default function Dashboard({
- + + + + + + {termList ? termList.filter( + ({ year, semester }, index) => + index === termList.findIndex((term) => term.semester === semester && term.year === year) + ).map((t) => { + return { changeTerm(t) }} + >{t.semester} {t.year} + }) : <>} + + +
- {termList ? termList.filter( - ({ year, semester }, index) => - index === termList.findIndex((term) => term.semester === semester && term.year === year) - ).map((t) => { - return { changeTerm(t) }} - >{t.semester} {t.year} - }) : <>} + {termList ? ( + termList + .filter( + ({ year, semester }, index) => + index === + termList.findIndex( + (term) => + term.semester === semester && term.year === year + ) + ) + .map((t) => { + return ( + { + changeTerm(t); + }} + > + {t.semester} {t.year} + + ); + }) + ) : ( + <> + )} diff --git a/apps/frontend/src/app/Catalog/index.tsx b/apps/frontend/src/app/Catalog/index.tsx index 0976e8993..7e686ef6f 100644 --- a/apps/frontend/src/app/Catalog/index.tsx +++ b/apps/frontend/src/app/Catalog/index.tsx @@ -45,7 +45,7 @@ export default function Catalog() { return parseInt(providedYear) || null; }, [providedYear]); - const [selectedTerm, changeTerm] = useState(null); + const [selectedTerm, changeTerm] = useState(null); const term = useMemo(() => { if (!terms) return null; @@ -62,7 +62,7 @@ export default function Catalog() { .find((term) => term.temporalPosition === TemporalPosition.Future); return ( - selectedTerm ?? + selectedTerm ?? terms?.find((term) => term.year === year && term.semester === semester) ?? currentTerm ?? nextTerm diff --git a/packages/theme/src/components/DropdownMenu/index.tsx b/packages/theme/src/components/DropdownMenu/index.tsx index e741549c9..7835880ea 100644 --- a/packages/theme/src/components/DropdownMenu/index.tsx +++ b/packages/theme/src/components/DropdownMenu/index.tsx @@ -20,18 +20,17 @@ const Content = forwardRef< ); }); -const Item = forwardRef< - HTMLDivElement, - ComponentProps ->(({ className, ...props }, forwardedRef) => { - return ( - - ) -}) +const Item = forwardRef>( + ({ className, ...props }, forwardedRef) => { + return ( + + ); + } +); export const DropdownMenu = { Root: Primitive.Root, From d1021cb88e9b739b266288c1b43c374bbcc8b64d Mon Sep 17 00:00:00 2001 From: ARtheboss <30683624+ARtheboss@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:07:17 +0530 Subject: [PATCH 06/10] Update docker-compose.yml --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 105288832..7b086f350 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,7 @@ services: networks: - bt ports: - - 3000:8080 + - 8080:8080 restart: always volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf From 17cf107118e27c62f1e2c6a7c7008b183b6a3c60 Mon Sep 17 00:00:00 2001 From: ARtheboss Date: Mon, 20 Jan 2025 17:42:05 -0800 Subject: [PATCH 07/10] carousel updates and change term fix --- .../src/app/Catalog/Dashboard/index.tsx | 60 +++---------------- apps/frontend/src/app/Catalog/index.tsx | 9 +-- .../components/Carousel/Carousel.module.scss | 2 + .../src/components/Carousel/index.tsx | 38 ++++++++++-- .../src/components/CarouselClass/index.tsx | 9 ++- apps/frontend/src/components/Class/index.tsx | 24 +++++--- apps/frontend/src/lib/recents.ts | 16 ++--- 7 files changed, 74 insertions(+), 84 deletions(-) diff --git a/apps/frontend/src/app/Catalog/Dashboard/index.tsx b/apps/frontend/src/app/Catalog/Dashboard/index.tsx index e46eabb6d..aa84a122f 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/index.tsx +++ b/apps/frontend/src/app/Catalog/Dashboard/index.tsx @@ -18,59 +18,20 @@ import { ITerm } from "@/lib/api"; import { getRecents } from "@/lib/recents"; import styles from "./Dashboard.module.scss"; +import { useNavigate } from "react-router-dom"; +import moment from "moment"; interface DashboardProps { term: ITerm | null | undefined; termList: ITerm[] | undefined; - changeTerm: React.Dispatch>; - onSelect: (subject: string, courseNumber: string, number: string) => void; expanded: boolean; setExpanded: Dispatch>; setOpen: Dispatch>; } -function getOrdinalSuffix(day: number): string { - if (day > 3 && day < 21) return "th"; // applies to 11-20 - switch (day % 10) { - case 1: - return "st"; - case 2: - return "nd"; - case 3: - return "rd"; - default: - return "th"; - } -} - -function formatDate(date: Date): string { - const monthNames = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ]; - - const day = date.getDate(); - const month = monthNames[date.getMonth()]; - const ordinalSuffix = getOrdinalSuffix(day); - - return `${month} ${day}${ordinalSuffix}`; -} - export default function Dashboard({ term, termList, - changeTerm, - onSelect, expanded, setExpanded, setOpen, @@ -83,6 +44,8 @@ export default function Dashboard({ (v) => v.semester == term.semester && v.year == term.year ); + const navigate = useNavigate(); + return (
@@ -109,9 +72,7 @@ export default function Dashboard({ return ( { - changeTerm(t); - }} + onClick={() => navigate(`/catalog/${t.year}/${t.semester}`)} > {t.semester} {t.year} @@ -140,8 +101,8 @@ export default function Dashboard({

{term.startDate && term.endDate ? (

- {formatDate(new Date(term.startDate))} through{" "} - {formatDate(new Date(term.endDate!))} + {moment(term.startDate).format("MMMM Do")} through{" "} + {moment(term.endDate).format("MMMM Do")}

) : ( <> @@ -170,7 +131,6 @@ export default function Dashboard({ return (
- {recents.length == 0 ? ( - <> - ) : ( + {recents.length !== 0 && ( }> {" "} {recents @@ -192,11 +150,11 @@ export default function Dashboard({ (c) => !term || (c.year == term.year && c.semester == term.semester) ) + .reverse() .map((c, i) => { return (
(null); - const term = useMemo(() => { if (!terms) return null; @@ -62,12 +60,11 @@ export default function Catalog() { .find((term) => term.temporalPosition === TemporalPosition.Future); return ( - selectedTerm ?? terms?.find((term) => term.year === year && term.semester === semester) ?? currentTerm ?? nextTerm ); - }, [terms, year, semester, selectedTerm]); + }, [terms, year, semester]); const subject = useMemo( () => providedSubject?.toUpperCase(), @@ -149,8 +146,6 @@ export default function Catalog() { (null); + const handleScroll = (event: UIEvent) => { const { scrollLeft, scrollWidth, clientWidth } = event.target as HTMLDivElement; @@ -27,6 +29,34 @@ export default function Carousel({ title, Icon, children, to }: CarouselProps) { setEnd(scrollLeft + clientWidth < scrollWidth); }; + const scrollLeft = () => { + // scroll to next card to the left + if (!containerRef.current) return; + const children = containerRef.current.children; + for (let i = children.length; i >= 0; i--) { + const element = children.item(i) as HTMLDivElement; + if (element && element.offsetLeft < containerRef.current.scrollLeft - 50) { + containerRef.current.scrollLeft = element.offsetLeft - (element.offsetWidth - element.clientWidth); + return; + } + } + containerRef.current.scrollLeft -= containerRef.current.offsetWidth / 3; + } + const scrollRight = () => { + // scroll to next card to the right + if (!containerRef.current) return; + const children = containerRef.current.children; + for (let i = 0; i < children.length; i++) { + const element = children.item(i) as HTMLDivElement; + if (element && element.offsetLeft > containerRef.current.scrollLeft + 50) { + containerRef.current.scrollLeft = element.offsetLeft - (element.offsetWidth - element.clientWidth); + return; + } + } + containerRef.current.scrollLeft += containerRef.current.offsetWidth / 3; + } + + return (
@@ -40,10 +70,10 @@ export default function Carousel({ title, Icon, children, to }: CarouselProps) { )} - + - +
@@ -53,7 +83,7 @@ export default function Carousel({ title, Icon, children, to }: CarouselProps) { [styles.end]: end, })} > -
+
{children}
diff --git a/apps/frontend/src/components/CarouselClass/index.tsx b/apps/frontend/src/components/CarouselClass/index.tsx index 7120b37f0..f29992870 100644 --- a/apps/frontend/src/components/CarouselClass/index.tsx +++ b/apps/frontend/src/components/CarouselClass/index.tsx @@ -9,9 +9,9 @@ import { useReadClass } from "@/hooks/api"; import { Semester } from "@/lib/api"; import styles from "./CarouselClass.module.scss"; +import { Link } from "react-router-dom"; interface CarouselClassProps { - onSelect: (subject: string, courseNumber: string, number: string) => void; year: number; semester: Semester; subject: string; @@ -20,7 +20,6 @@ interface CarouselClassProps { } export default function CarouselClass({ - onSelect, year, semester, subject, @@ -40,9 +39,9 @@ export default function CarouselClass({ return loading || !_class ? ( <> ) : ( -
onSelect(subject, courseNumber, number)} + to={`/catalog/${year}/${semester}/${subject}/${courseNumber}/${number}`} >

@@ -67,6 +66,6 @@ export default function CarouselClass({

-
+ ); } diff --git a/apps/frontend/src/components/Class/index.tsx b/apps/frontend/src/components/Class/index.tsx index f53338079..5a838b9ad 100644 --- a/apps/frontend/src/components/Class/index.tsx +++ b/apps/frontend/src/components/Class/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode, Suspense, lazy, useCallback, useMemo } from "react"; +import { ReactNode, Suspense, lazy, useCallback, useEffect, useMemo } from "react"; import { DialogClose } from "@radix-ui/react-dialog"; import * as Tabs from "@radix-ui/react-tabs"; @@ -226,15 +226,11 @@ export default function Class({ ); }, [_class, bookmarked, updateUser, user]); - // TODO: Loading state - if (loading || courseLoading) { - return <>; - } + useEffect(() => { + if (!_class) return; + + console.log(_class) - // TODO: Error state - if (!course || !_class || !pin) { - return <>; - } else { addToRecent({ subject: _class.subject, year: _class.year, @@ -242,6 +238,16 @@ export default function Class({ courseNumber: _class.courseNumber, number: _class.number, }); + }, [_class]); + + // TODO: Loading state + if (loading || courseLoading) { + return <>; + } + + // TODO: Error state + if (!course || !_class || !pin) { + return <>; } return ( diff --git a/apps/frontend/src/lib/recents.ts b/apps/frontend/src/lib/recents.ts index 6c5d47d62..1f4226dea 100644 --- a/apps/frontend/src/lib/recents.ts +++ b/apps/frontend/src/lib/recents.ts @@ -8,6 +8,8 @@ interface RecentClassData { number: string; } +const RECENTS_LIST_MAX_LENGTH = 10; + export function addToRecent({ subject, year, @@ -15,7 +17,7 @@ export function addToRecent({ courseNumber, number, }: RecentClassData) { - const recents = getRecents(); + let recents = getRecents(); const newRcd = { subject: subject, year: year, @@ -23,18 +25,16 @@ export function addToRecent({ courseNumber: courseNumber, number: number, } as RecentClassData; - const alreadyExists = recents.reduce( - (v, rcd) => - v || - (newRcd.subject == rcd.subject && + recents = recents.filter( + (rcd) => + !(newRcd.subject == rcd.subject && newRcd.year == rcd.year && newRcd.semester == rcd.semester && newRcd.courseNumber == rcd.courseNumber && - newRcd.number == rcd.number), - false + newRcd.number == rcd.number) ); - if (alreadyExists) return; recents.push(newRcd); + recents.splice(0, recents.length - RECENTS_LIST_MAX_LENGTH); localStorage.setItem("recents-data", JSON.stringify(recents)); } From a997d15fc3106dff77574b14a97dcf9f2d1a5992 Mon Sep 17 00:00:00 2001 From: ARtheboss Date: Mon, 20 Jan 2025 17:42:38 -0800 Subject: [PATCH 08/10] lint --- .../src/app/Catalog/Dashboard/index.tsx | 8 ++++--- .../src/components/Carousel/index.tsx | 23 ++++++++++++------- .../src/components/CarouselClass/index.tsx | 2 +- apps/frontend/src/components/Class/index.tsx | 11 +++++++-- apps/frontend/src/lib/recents.ts | 6 +++-- 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/apps/frontend/src/app/Catalog/Dashboard/index.tsx b/apps/frontend/src/app/Catalog/Dashboard/index.tsx index aa84a122f..0c9d6e397 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/index.tsx +++ b/apps/frontend/src/app/Catalog/Dashboard/index.tsx @@ -7,6 +7,8 @@ import { Expand, Search, } from "iconoir-react"; +import moment from "moment"; +import { useNavigate } from "react-router-dom"; import { Button, Container, IconButton, Tooltip } from "@repo/theme"; import { DropdownMenu } from "@repo/theme"; @@ -18,8 +20,6 @@ import { ITerm } from "@/lib/api"; import { getRecents } from "@/lib/recents"; import styles from "./Dashboard.module.scss"; -import { useNavigate } from "react-router-dom"; -import moment from "moment"; interface DashboardProps { term: ITerm | null | undefined; @@ -72,7 +72,9 @@ export default function Dashboard({ return ( navigate(`/catalog/${t.year}/${t.semester}`)} + onClick={() => + navigate(`/catalog/${t.year}/${t.semester}`) + } > {t.semester} {t.year} diff --git a/apps/frontend/src/components/Carousel/index.tsx b/apps/frontend/src/components/Carousel/index.tsx index ad19546ac..caede1b83 100644 --- a/apps/frontend/src/components/Carousel/index.tsx +++ b/apps/frontend/src/components/Carousel/index.tsx @@ -35,27 +35,34 @@ export default function Carousel({ title, Icon, children, to }: CarouselProps) { const children = containerRef.current.children; for (let i = children.length; i >= 0; i--) { const element = children.item(i) as HTMLDivElement; - if (element && element.offsetLeft < containerRef.current.scrollLeft - 50) { - containerRef.current.scrollLeft = element.offsetLeft - (element.offsetWidth - element.clientWidth); + if ( + element && + element.offsetLeft < containerRef.current.scrollLeft - 50 + ) { + containerRef.current.scrollLeft = + element.offsetLeft - (element.offsetWidth - element.clientWidth); return; } } containerRef.current.scrollLeft -= containerRef.current.offsetWidth / 3; - } + }; const scrollRight = () => { // scroll to next card to the right if (!containerRef.current) return; const children = containerRef.current.children; for (let i = 0; i < children.length; i++) { const element = children.item(i) as HTMLDivElement; - if (element && element.offsetLeft > containerRef.current.scrollLeft + 50) { - containerRef.current.scrollLeft = element.offsetLeft - (element.offsetWidth - element.clientWidth); + if ( + element && + element.offsetLeft > containerRef.current.scrollLeft + 50 + ) { + containerRef.current.scrollLeft = + element.offsetLeft - (element.offsetWidth - element.clientWidth); return; } } containerRef.current.scrollLeft += containerRef.current.offsetWidth / 3; - } - + }; return (
@@ -70,7 +77,7 @@ export default function Carousel({ title, Icon, children, to }: CarouselProps) { )} - + diff --git a/apps/frontend/src/components/CarouselClass/index.tsx b/apps/frontend/src/components/CarouselClass/index.tsx index f29992870..da34d48d1 100644 --- a/apps/frontend/src/components/CarouselClass/index.tsx +++ b/apps/frontend/src/components/CarouselClass/index.tsx @@ -1,6 +1,7 @@ import { useMemo } from "react"; import { OpenInWindow } from "iconoir-react"; +import { Link } from "react-router-dom"; import AverageGrade from "@/components/AverageGrade"; import Capacity from "@/components/Capacity"; @@ -9,7 +10,6 @@ import { useReadClass } from "@/hooks/api"; import { Semester } from "@/lib/api"; import styles from "./CarouselClass.module.scss"; -import { Link } from "react-router-dom"; interface CarouselClassProps { year: number; diff --git a/apps/frontend/src/components/Class/index.tsx b/apps/frontend/src/components/Class/index.tsx index 5a838b9ad..dfa5bcbe7 100644 --- a/apps/frontend/src/components/Class/index.tsx +++ b/apps/frontend/src/components/Class/index.tsx @@ -1,4 +1,11 @@ -import { ReactNode, Suspense, lazy, useCallback, useEffect, useMemo } from "react"; +import { + ReactNode, + Suspense, + lazy, + useCallback, + useEffect, + useMemo, +} from "react"; import { DialogClose } from "@radix-ui/react-dialog"; import * as Tabs from "@radix-ui/react-tabs"; @@ -229,7 +236,7 @@ export default function Class({ useEffect(() => { if (!_class) return; - console.log(_class) + console.log(_class); addToRecent({ subject: _class.subject, diff --git a/apps/frontend/src/lib/recents.ts b/apps/frontend/src/lib/recents.ts index 1f4226dea..143d8b56f 100644 --- a/apps/frontend/src/lib/recents.ts +++ b/apps/frontend/src/lib/recents.ts @@ -27,11 +27,13 @@ export function addToRecent({ } as RecentClassData; recents = recents.filter( (rcd) => - !(newRcd.subject == rcd.subject && + !( + newRcd.subject == rcd.subject && newRcd.year == rcd.year && newRcd.semester == rcd.semester && newRcd.courseNumber == rcd.courseNumber && - newRcd.number == rcd.number) + newRcd.number == rcd.number + ) ); recents.push(newRcd); recents.splice(0, recents.length - RECENTS_LIST_MAX_LENGTH); From b9c5ec08395d6f06e5504f3c3848f190795539cf Mon Sep 17 00:00:00 2001 From: Matthew Rowland Date: Sun, 26 Jan 2025 10:31:51 -0800 Subject: [PATCH 09/10] chore: Formatting --- .../Catalog/Dashboard/Dashboard.module.scss | 2 +- .../CarouselClass/CarouselClass.module.scss | 115 ++++++++-------- .../DropdownMenu/DropdownMenu.module.scss | 125 +++++++++--------- 3 files changed, 120 insertions(+), 122 deletions(-) diff --git a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss index 332740760..608e65b95 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss +++ b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss @@ -54,4 +54,4 @@ color: var(--paragraph-color); margin-top: 8px; } -} \ No newline at end of file +} diff --git a/apps/frontend/src/components/CarouselClass/CarouselClass.module.scss b/apps/frontend/src/components/CarouselClass/CarouselClass.module.scss index be4ac4f14..525425169 100644 --- a/apps/frontend/src/components/CarouselClass/CarouselClass.module.scss +++ b/apps/frontend/src/components/CarouselClass/CarouselClass.module.scss @@ -1,65 +1,64 @@ .root { - border: 1px solid var(--border-color); - border-radius: 8px; - flex-shrink: 0; - background-color: var(--foreground-color); - position: relative; - padding: 16px; + border: 1px solid var(--border-color); + border-radius: 8px; + flex-shrink: 0; + background-color: var(--foreground-color); + position: relative; + padding: 16px; + display: flex; + gap: 16px; + align-items: flex-start; + cursor: pointer; + + &:hover .column .icon { + color: var(--heading-color); + } + + .column { display: flex; - gap: 16px; - align-items: flex-start; - cursor: pointer; - - &:hover .column .icon { + flex-direction: column; + gap: 8px; + flex-shrink: 0; + + .icon { + color: var(--paragraph-color); + transition: all 100ms ease-in-out; + } + } + + .text { + flex-grow: 1; + font-size: 14px; + width: 80%; + + .heading { color: var(--heading-color); + margin-bottom: 8px; + line-height: 1; + font-weight: 660; + font-feature-settings: + "cv05" on, + "cv13" on, + "ss07" on, + "cv12" on, + "cv06" on; } - - .column { - display: flex; - flex-direction: column; - gap: 8px; - flex-shrink: 0; - - .icon { - color: var(--paragraph-color); - transition: all 100ms ease-in-out; - } + + .description { + color: var(--paragraph-color); + line-height: 1.5; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; } - - .text { - flex-grow: 1; - font-size: 14px; - width: 80%; - - .heading { - color: var(--heading-color); - margin-bottom: 8px; - line-height: 1; - font-weight: 660; - font-feature-settings: - "cv05" on, - "cv13" on, - "ss07" on, - "cv12" on, - "cv06" on; - } - - .description { - color: var(--paragraph-color); - line-height: 1.5; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - width: 100%; - } - - .row { - display: flex; - gap: 12px; - margin-top: 12px; - align-items: center; - float: bottom; - } + + .row { + display: flex; + gap: 12px; + margin-top: 12px; + align-items: center; + float: bottom; } } - \ No newline at end of file +} diff --git a/packages/theme/src/components/DropdownMenu/DropdownMenu.module.scss b/packages/theme/src/components/DropdownMenu/DropdownMenu.module.scss index 5d3f05476..29e204398 100644 --- a/packages/theme/src/components/DropdownMenu/DropdownMenu.module.scss +++ b/packages/theme/src/components/DropdownMenu/DropdownMenu.module.scss @@ -1,91 +1,90 @@ - .content { - position: fixed; - left: -75px; - z-index: 989; - min-width: 150px; - background-color: var(--foreground-color); - padding: 5px 0; - box-shadow: - 0px 10px 38px -10px rgba(22, 23, 24, 0.35), - 0px 10px 20px -15px rgba(22, 23, 24, 0.2); - animation-duration: 400ms; - animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); - will-change: transform, opacity; + position: fixed; + left: -75px; + z-index: 989; + min-width: 150px; + background-color: var(--foreground-color); + padding: 5px 0; + box-shadow: + 0px 10px 38px -10px rgba(22, 23, 24, 0.35), + 0px 10px 20px -15px rgba(22, 23, 24, 0.2); + animation-duration: 400ms; + animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); + will-change: transform, opacity; } .content[data-side="top"] { - animation-name: slideDownAndFade; + animation-name: slideDownAndFade; } .content[data-side="right"] { - animation-name: slideLeftAndFade; + animation-name: slideLeftAndFade; } .content[data-side="bottom"] { - animation-name: slideUpAndFade; + animation-name: slideUpAndFade; } -.content[data-side="left"]{ - animation-name: slideRightAndFade; +.content[data-side="left"] { + animation-name: slideRightAndFade; } .item { - font-size: 14px; - line-height: 1; - color: var(--paragraph-color); - border-radius: 3px; - display: flex; - align-items: center; - height: 30px; - padding: 0 20px; - position: relative; - user-select: none; - outline: none; + font-size: 14px; + line-height: 1; + color: var(--paragraph-color); + border-radius: 3px; + display: flex; + align-items: center; + height: 30px; + padding: 0 20px; + position: relative; + user-select: none; + outline: none; } .item:hover { - background-color: var(--button-active-color); + background-color: var(--button-active-color); } .item[data-highlighted] { - color: var(--heading-color); + color: var(--heading-color); } @keyframes slideUpAndFade { - from { - opacity: 0; - transform: translateY(2px); - } - to { - opacity: 1; - transform: translateY(0); - } + from { + opacity: 0; + transform: translateY(2px); + } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes slideRightAndFade { - from { - opacity: 0; - transform: translateX(-2px); - } - to { - opacity: 1; - transform: translateX(0); - } + from { + opacity: 0; + transform: translateX(-2px); + } + to { + opacity: 1; + transform: translateX(0); + } } @keyframes slideDownAndFade { - from { - opacity: 0; - transform: translateY(-2px); - } - to { - opacity: 1; - transform: translateY(0); - } + from { + opacity: 0; + transform: translateY(-2px); + } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes slideLeftAndFade { - from { - opacity: 0; - transform: translateX(2px); - } - to { - opacity: 1; - transform: translateX(0); - } + from { + opacity: 0; + transform: translateX(2px); + } + to { + opacity: 1; + transform: translateX(0); + } } From 43e9d0b87bc1f7cdcd2f8d107e91419df257b1e4 Mon Sep 17 00:00:00 2001 From: Matthew Rowland Date: Sun, 26 Jan 2025 11:38:15 -0800 Subject: [PATCH 10/10] chore: Clean up --- .../Catalog/Dashboard/Dashboard.module.scss | 1 + .../src/app/Catalog/Dashboard/index.tsx | 144 ++++++++---------- apps/frontend/src/app/Catalog/index.tsx | 4 +- .../components/Carousel/Carousel.module.scss | 2 +- .../Class/Class.module.scss} | 12 +- .../src/components/Carousel/Class/index.tsx | 76 +++++++++ .../src/components/Carousel/index.tsx | 12 +- .../src/components/CarouselClass/index.tsx | 71 --------- apps/frontend/src/components/Class/index.tsx | 10 +- apps/frontend/src/lib/recent-classes.ts | 58 +++++++ apps/frontend/src/lib/recents.ts | 47 ------ .../theme/src/components/Button/index.tsx | 4 +- .../theme/src/components/IconButton/index.tsx | 4 +- .../theme/src/components/MenuItem/index.tsx | 3 +- 14 files changed, 225 insertions(+), 223 deletions(-) rename apps/frontend/src/components/{CarouselClass/CarouselClass.module.scss => Carousel/Class/Class.module.scss} (83%) create mode 100644 apps/frontend/src/components/Carousel/Class/index.tsx delete mode 100644 apps/frontend/src/components/CarouselClass/index.tsx create mode 100644 apps/frontend/src/lib/recent-classes.ts delete mode 100644 apps/frontend/src/lib/recents.ts diff --git a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss index 608e65b95..7733df435 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss +++ b/apps/frontend/src/app/Catalog/Dashboard/Dashboard.module.scss @@ -8,6 +8,7 @@ border-radius: 8px; background-color: var(--foreground-color); flex-shrink: 0; + .error { color: var(--paragraph-color); text-align: center; diff --git a/apps/frontend/src/app/Catalog/Dashboard/index.tsx b/apps/frontend/src/app/Catalog/Dashboard/index.tsx index 0c9d6e397..6a9115c8e 100644 --- a/apps/frontend/src/app/Catalog/Dashboard/index.tsx +++ b/apps/frontend/src/app/Catalog/Dashboard/index.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction } from "react"; +import { Dispatch, SetStateAction, useMemo } from "react"; import { ArrowSeparateVertical, @@ -14,16 +14,15 @@ import { Button, Container, IconButton, Tooltip } from "@repo/theme"; import { DropdownMenu } from "@repo/theme"; import Carousel from "@/components/Carousel"; -import CarouselClass from "@/components/CarouselClass"; import { useReadUser } from "@/hooks/api"; import { ITerm } from "@/lib/api"; -import { getRecents } from "@/lib/recents"; +import { getRecentClasses } from "@/lib/recent-classes"; import styles from "./Dashboard.module.scss"; interface DashboardProps { - term: ITerm | null | undefined; - termList: ITerm[] | undefined; + term: ITerm; + terms: ITerm[]; expanded: boolean; setExpanded: Dispatch>; setOpen: Dispatch>; @@ -31,21 +30,24 @@ interface DashboardProps { export default function Dashboard({ term, - termList, + terms, expanded, setExpanded, setOpen, }: DashboardProps) { + const navigate = useNavigate(); const { data: user, loading: userLoading } = useReadUser(); - if (!term) return <>; - - const recents = getRecents().filter( - (v) => v.semester == term.semester && v.year == term.year + const recentClasses = useMemo( + () => + getRecentClasses().filter( + (recentClass) => + recentClass.semester === term.semester && + recentClass.year === term.year + ), + [term] ); - const navigate = useNavigate(); - return (
@@ -58,32 +60,21 @@ export default function Dashboard({ - {termList ? ( - termList - .filter( - ({ year, semester }, index) => - index === - termList.findIndex( - (term) => - term.semester === semester && term.year === year - ) - ) - .map((t) => { - return ( - - navigate(`/catalog/${t.year}/${t.semester}`) - } - > - {t.semester} {t.year} - - ); - }) - ) : ( - <> - )} - + {terms + .filter( + ({ year, semester }) => + year !== term.year || semester !== term.semester + ) + .map(({ year, semester }) => { + return ( + navigate(`/catalog/${year}/${semester}`)} + > + {semester} {year} + + ); + })}
@@ -101,20 +92,17 @@ export default function Dashboard({

{term.semester} {term.year}

- {term.startDate && term.endDate ? ( + {term.startDate && term.endDate && (

{moment(term.startDate).format("MMMM Do")} through{" "} {moment(term.endDate).format("MMMM Do")}

- ) : ( - <> )} - {/* } to="/semesters"> -
-
-
-
*/} - } to="/account"> + } + to="/account" + > {userLoading || !user ? (
Sign in to bookmark classes
@@ -126,47 +114,41 @@ export default function Dashboard({ ) : ( user?.bookmarkedClasses .filter( - (c) => - !term || (c.year == term.year && c.semester == term.semester) + (bookmarkedClass) => + bookmarkedClass.year === term.year && + bookmarkedClass.semester === term.semester ) - .map((c, i) => { + .map((bookmarkedClass, i) => { return ( -
- -
+ ); }) )} - - {recents.length !== 0 && ( - }> - {" "} - {recents - .filter( - (c) => - !term || (c.year == term.year && c.semester == term.semester) - ) - .reverse() - .map((c, i) => { + + {recentClasses.length !== 0 && ( + }> + {recentClasses.map( + ({ subject, year, semester, courseNumber, number }, i) => { return ( -
- -
+ ); - })} -
+ } + )} + )}
diff --git a/apps/frontend/src/app/Catalog/index.tsx b/apps/frontend/src/app/Catalog/index.tsx index 07f1d759e..77a2d6949 100644 --- a/apps/frontend/src/app/Catalog/index.tsx +++ b/apps/frontend/src/app/Catalog/index.tsx @@ -102,7 +102,7 @@ export default function Catalog() { } // TODO: Error state - if (!term) { + if (!terms || !term) { return <>; } @@ -145,7 +145,7 @@ export default function Catalog() { ) : ( data, [data]); + + return loading || !_class ? ( + <> + ) : ( + +
+
+

+ {_class.subject} {_class.courseNumber} #{_class.number} +

+

+ {_class.title ?? _class.course.title} +

+
+ + + +
+
+
+
+ +
+
+
+
+ ); +} diff --git a/apps/frontend/src/components/Carousel/index.tsx b/apps/frontend/src/components/Carousel/index.tsx index caede1b83..5c31476b6 100644 --- a/apps/frontend/src/components/Carousel/index.tsx +++ b/apps/frontend/src/components/Carousel/index.tsx @@ -7,15 +7,16 @@ import { Link, To } from "react-router-dom"; import { IconButton } from "@repo/theme"; import styles from "./Carousel.module.scss"; +import Class from "./Class"; -interface CarouselProps { +interface RootProps { title: string; Icon: ReactNode; children: ReactNode; to?: To; } -export default function Carousel({ title, Icon, children, to }: CarouselProps) { +function Root({ title, Icon, children, to }: RootProps) { const [start, setStart] = useState(false); const [end, setEnd] = useState(false); @@ -97,3 +98,10 @@ export default function Carousel({ title, Icon, children, to }: CarouselProps) {
); } + +const Carousel = { + Root, + Class, +}; + +export default Carousel; diff --git a/apps/frontend/src/components/CarouselClass/index.tsx b/apps/frontend/src/components/CarouselClass/index.tsx deleted file mode 100644 index da34d48d1..000000000 --- a/apps/frontend/src/components/CarouselClass/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useMemo } from "react"; - -import { OpenInWindow } from "iconoir-react"; -import { Link } from "react-router-dom"; - -import AverageGrade from "@/components/AverageGrade"; -import Capacity from "@/components/Capacity"; -import Units from "@/components/Units"; -import { useReadClass } from "@/hooks/api"; -import { Semester } from "@/lib/api"; - -import styles from "./CarouselClass.module.scss"; - -interface CarouselClassProps { - year: number; - semester: Semester; - subject: string; - courseNumber: string; - number: string; -} - -export default function CarouselClass({ - year, - semester, - subject, - courseNumber, - number, -}: CarouselClassProps) { - const { data, loading } = useReadClass( - year as number, - semester as Semester, - subject as string, - courseNumber as string, - number as string - ); - - const _class = useMemo(() => data, [data]); - - return loading || !_class ? ( - <> - ) : ( - -
-

- {_class.subject} {_class.courseNumber} #{_class.number} -

-

- {_class.title ?? _class.course.title} -

-
- - - -
-
-
-
- -
-
- - ); -} diff --git a/apps/frontend/src/components/Class/index.tsx b/apps/frontend/src/components/Class/index.tsx index dfa5bcbe7..a6e253550 100644 --- a/apps/frontend/src/components/Class/index.tsx +++ b/apps/frontend/src/components/Class/index.tsx @@ -44,7 +44,7 @@ import { useReadCourse, useReadUser, useUpdateUser } from "@/hooks/api"; import { useReadClass } from "@/hooks/api/classes/useReadClass"; import usePins from "@/hooks/usePins"; import { IClass, Semester } from "@/lib/api"; -import { addToRecent } from "@/lib/recents"; +import { addRecentClass } from "@/lib/recent-classes"; import { getExternalLink } from "@/lib/section"; import styles from "./Class.module.scss"; @@ -236,9 +236,7 @@ export default function Class({ useEffect(() => { if (!_class) return; - console.log(_class); - - addToRecent({ + addRecentClass({ subject: _class.subject, year: _class.year, semester: _class.semester, @@ -277,7 +275,7 @@ export default function Class({ [styles.active]: bookmarked, })} onClick={() => bookmark()} - disabled={userLoading} // setting disabled to false still appears on my version + disabled={userLoading} > {bookmarked ? : } @@ -361,7 +359,7 @@ export default function Class({ as={Link} to={{ ...location, - pathname: `/catalog/${year}/${semester}`, + pathname: `/catalog/${_class.year}/${_class.semester}`, }} onClick={() => onClose()} > diff --git a/apps/frontend/src/lib/recent-classes.ts b/apps/frontend/src/lib/recent-classes.ts new file mode 100644 index 000000000..0bec4d1b6 --- /dev/null +++ b/apps/frontend/src/lib/recent-classes.ts @@ -0,0 +1,58 @@ +import { Semester } from "./api"; + +interface RecentClassData { + subject: string; + year: number; + semester: Semester; + courseNumber: string; + number: string; +} + +const RECENT_CLASSES_KEY = "recent-classes"; + +const RECENT_CLASSES_MAX_LENGTH = 10; + +export function addRecentClass({ + subject, + year, + semester, + courseNumber, + number, +}: RecentClassData) { + let recentClasses = getRecentClasses(); + + recentClasses = recentClasses.filter( + (recentClass) => + !( + subject == recentClass.subject && + year == recentClass.year && + semester == recentClass.semester && + courseNumber == recentClass.courseNumber && + number == recentClass.number + ) + ); + + recentClasses.unshift({ + subject: subject, + year: year, + semester: semester, + courseNumber: courseNumber, + number: number, + }); + + recentClasses = recentClasses.slice(0, RECENT_CLASSES_MAX_LENGTH); + + const item = JSON.stringify(recentClasses); + localStorage.setItem(RECENT_CLASSES_KEY, item); +} + +export function getRecentClasses() { + try { + const item = localStorage.getItem(RECENT_CLASSES_KEY); + if (!item) return []; + + return JSON.parse(item) as RecentClassData[]; + } catch { + return []; + } +} diff --git a/apps/frontend/src/lib/recents.ts b/apps/frontend/src/lib/recents.ts deleted file mode 100644 index 143d8b56f..000000000 --- a/apps/frontend/src/lib/recents.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Semester } from "./api"; - -interface RecentClassData { - subject: string; - year: number; - semester: Semester; - courseNumber: string; - number: string; -} - -const RECENTS_LIST_MAX_LENGTH = 10; - -export function addToRecent({ - subject, - year, - semester, - courseNumber, - number, -}: RecentClassData) { - let recents = getRecents(); - const newRcd = { - subject: subject, - year: year, - semester: semester, - courseNumber: courseNumber, - number: number, - } as RecentClassData; - recents = recents.filter( - (rcd) => - !( - newRcd.subject == rcd.subject && - newRcd.year == rcd.year && - newRcd.semester == rcd.semester && - newRcd.courseNumber == rcd.courseNumber && - newRcd.number == rcd.number - ) - ); - recents.push(newRcd); - recents.splice(0, recents.length - RECENTS_LIST_MAX_LENGTH); - localStorage.setItem("recents-data", JSON.stringify(recents)); -} - -export function getRecents() { - const res = localStorage.getItem("recents-data"); - if (res) return JSON.parse(res) as RecentClassData[]; - return []; -} diff --git a/packages/theme/src/components/Button/index.tsx b/packages/theme/src/components/Button/index.tsx index eccd08137..e73038558 100644 --- a/packages/theme/src/components/Button/index.tsx +++ b/packages/theme/src/components/Button/index.tsx @@ -29,8 +29,8 @@ export const Button = forwardRef( {...props} ref={ref} data-variant={variant} - disabled={disabled} - data-disabled={disabled} + disabled={disabled || undefined} + data-disabled={disabled || undefined} className={classNames(styles.root, className)} > {children} diff --git a/packages/theme/src/components/IconButton/index.tsx b/packages/theme/src/components/IconButton/index.tsx index 3eed717cd..430399d8a 100644 --- a/packages/theme/src/components/IconButton/index.tsx +++ b/packages/theme/src/components/IconButton/index.tsx @@ -26,8 +26,8 @@ export const IconButton = forwardRef( ) => { return (