Skip to content

Commit

Permalink
Merge pull request #801 from asuc-octo/gql-courses
Browse files Browse the repository at this point in the history
Gql courses
  • Loading branch information
mathhulk authored Mar 8, 2025
2 parents edde7ec + 0d9b587 commit a71f732
Show file tree
Hide file tree
Showing 25 changed files with 382 additions and 220 deletions.
2 changes: 1 addition & 1 deletion apps/backend/src/modules/class/typedefs/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export default gql`
date: String!
startTime: String!
endTime: String!
location: String!
location: String
type: ExamType!
}
Expand Down
131 changes: 84 additions & 47 deletions apps/frontend/src/app/Catalog/Dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Dispatch, SetStateAction, useMemo } from "react";
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useMemo,
useState,
} from "react";

import { useApolloClient } from "@apollo/client";
import {
ArrowSeparateVertical,
BookmarkSolid,
Expand All @@ -14,8 +22,11 @@ import { Button, Container, IconButton, Tooltip } from "@repo/theme";
import { DropdownMenu } from "@repo/theme";

import Carousel from "@/components/Carousel";
import ClassCard from "@/components/ClassCard";
import ClassDrawer from "@/components/ClassDrawer";
import { useReadUser } from "@/hooks/api";
import { ITerm } from "@/lib/api";
import { IClass, ITerm, READ_CLASS, ReadClassResponse } from "@/lib/api";
import { sortByTermDescending } from "@/lib/classes";
import { getRecentClasses } from "@/lib/recent-classes";

import styles from "./Dashboard.module.scss";
Expand All @@ -36,18 +47,57 @@ export default function Dashboard({
setOpen,
}: DashboardProps) {
const navigate = useNavigate();
const { data: user, loading: userLoading } = useReadUser();
const client = useApolloClient();

const { data: user } = useReadUser();

const recentClasses = useMemo(
const bookmarkedClasses = useMemo(
() =>
getRecentClasses().filter(
(recentClass) =>
recentClass.semester === term.semester &&
recentClass.year === term.year
user?.bookmarkedClasses.filter(
(bookmarkedClass) =>
bookmarkedClass.year === term.year &&
bookmarkedClass.semester === term.semester
),
[term]
[term, user]
);

const [recentClasses, setRecentClasses] = useState<IClass[]>([]);

const initialize = useCallback(async () => {
const recentClasses = getRecentClasses();

const responses = await Promise.all(
recentClasses.map(async (recentClass) => {
const { subject, year, semester, courseNumber, number } = recentClass;

try {
const response = await client.query<ReadClassResponse>({
query: READ_CLASS,
variables: {
subject,
year,
semester,
courseNumber,
number,
},
});

return response.data.class;
} catch {
// TODO: Handle errors

return;
}
})
);

setRecentClasses(responses.filter((response) => !!response));
}, [client]);

useEffect(() => {
initialize();
}, [initialize]);

return (
<div className={styles.root}>
<Container size="sm">
Expand All @@ -59,12 +109,16 @@ export default function Dashboard({
Switch terms
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content sideOffset={5}>
<DropdownMenu.Content sideOffset={5} style={{ maxHeight: 200 }}>
{terms
.filter(
({ year, semester }) =>
year !== term.year || semester !== term.semester
({ year, semester }, index) =>
index ===
terms.findIndex(
(term) => term.semester === semester && term.year === year
)
)
.toSorted(sortByTermDescending)
.map(({ year, semester }) => {
return (
<DropdownMenu.Item
Expand Down Expand Up @@ -103,51 +157,34 @@ export default function Dashboard({
Icon={<BookmarkSolid />}
to="/account"
>
{userLoading || !user ? (
{/* TODO: Better placeholder states */}
{!bookmarkedClasses ? (
<div className={styles.card}>
<div className={styles.error}>Sign in to bookmark classes</div>
</div>
) : user?.bookmarkedClasses.length == 0 ? (
) : bookmarkedClasses.length === 0 ? (
<div className={styles.card}>
<div className={styles.error}>No bookmarked classes</div>
</div>
) : (
user?.bookmarkedClasses
.filter(
(bookmarkedClass) =>
bookmarkedClass.year === term.year &&
bookmarkedClass.semester === term.semester
)
.map((bookmarkedClass, i) => {
return (
<Carousel.Class
key={i}
subject={bookmarkedClass.subject}
year={bookmarkedClass.year}
semester={bookmarkedClass.semester}
courseNumber={bookmarkedClass.courseNumber}
number={bookmarkedClass.number}
/>
);
})
bookmarkedClasses.map((bookmarkedClass, index) => (
<ClassDrawer {...bookmarkedClass}>
<Carousel.Item key={index}>
<ClassCard class={bookmarkedClass} />
</Carousel.Item>
</ClassDrawer>
))
)}
</Carousel.Root>
{recentClasses.length !== 0 && (
{recentClasses.length > 0 && (
<Carousel.Root title="Recently viewed" Icon={<Search />}>
{recentClasses.map(
({ subject, year, semester, courseNumber, number }, i) => {
return (
<Carousel.Class
key={i}
subject={subject}
year={year}
semester={semester}
courseNumber={courseNumber}
number={number}
/>
);
}
)}
{recentClasses.map((recentClass, index) => (
<ClassDrawer {...recentClass}>
<Carousel.Item key={index}>
<ClassCard class={recentClass} />
</Carousel.Item>
</ClassDrawer>
))}
</Carousel.Root>
)}
</Container>
Expand Down
8 changes: 4 additions & 4 deletions apps/frontend/src/app/Schedule/Editor/SideBar/Class/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ export default function Class({
/> */}
<Capacity
enrolledCount={
_class.primarySection.enrollment.latest.enrolledCount
_class.primarySection.enrollment?.latest.enrolledCount
}
maxEnroll={_class.primarySection.enrollment.latest.maxEnroll}
maxEnroll={_class.primarySection.enrollment?.latest.maxEnroll}
waitlistedCount={
_class.primarySection.enrollment.latest.waitlistedCount
_class.primarySection.enrollment?.latest.waitlistedCount
}
maxWaitlist={
_class.primarySection.enrollment.latest.maxWaitlist
_class.primarySection.enrollment?.latest.maxWaitlist
}
/>
<Units unitsMin={_class.unitsMin} unitsMax={_class.unitsMax} />
Expand Down
53 changes: 32 additions & 21 deletions apps/frontend/src/components/Capacity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { Tooltip } from "radix-ui";

import styles from "./Capacity.module.scss";

const getColor = (count: number, capacity: number) => {
const getColor = (count?: number, capacity?: number) => {
if (typeof count !== "number" || typeof capacity !== "number")
return "var(--paragraph-color)";

const percentage = count / capacity;

return percentage >= 0.75
Expand All @@ -16,10 +19,10 @@ const getColor = (count: number, capacity: number) => {
};

interface CapacityProps {
enrolledCount: number;
maxEnroll: number;
waitlistedCount: number;
maxWaitlist: number;
enrolledCount?: number;
maxEnroll?: number;
waitlistedCount?: number;
maxWaitlist?: number;
}

export default function Capacity({
Expand All @@ -44,9 +47,11 @@ export default function Capacity({
<div className={styles.trigger}>
<User />
<p className={styles.text}>
<span style={{ color }}>{enrolledCount.toLocaleString()}</span>
<span style={{ color }}>
{enrolledCount?.toLocaleString() ?? " - "}
</span>
&nbsp;/&nbsp;
{maxEnroll.toLocaleString()}
{maxEnroll?.toLocaleString() ?? " - "}
</p>
</div>
</Tooltip.Trigger>
Expand All @@ -62,18 +67,22 @@ export default function Capacity({
<div className={styles.row}>
<p className={styles.key}>Enrolled</p>
<p>
<span style={{ color }}>{enrolledCount.toLocaleString()}</span>
<span style={{ color }}>
{enrolledCount?.toLocaleString() ?? " - "}
</span>
&nbsp;/&nbsp;
<span className={styles.value}>
{maxEnroll.toLocaleString()}
{maxEnroll?.toLocaleString() ?? " - "}
</span>
&nbsp;(
<span style={{ color }}>
{maxEnroll === 0
? 0
: Math.round(
(enrolledCount / maxEnroll) * 100
).toLocaleString()}
{enrolledCount === undefined || maxEnroll === undefined
? "N/A"
: maxEnroll === 0
? 0
: Math.round(
(enrolledCount / maxEnroll) * 100
).toLocaleString()}
%
</span>
)
Expand All @@ -84,17 +93,19 @@ export default function Capacity({
<p className={styles.key}>Waitlisted</p>
<p>
<span style={{ color: waitlistColor }}>
{waitlistedCount.toLocaleString()}
{waitlistedCount?.toLocaleString() ?? " - "}
</span>
&nbsp;/&nbsp;
{maxWaitlist.toLocaleString()}
{maxWaitlist?.toLocaleString() ?? " - "}
&nbsp;(
<span style={{ color: waitlistColor }}>
{maxWaitlist === 0
? 0
: Math.round(
(waitlistedCount / maxWaitlist) * 100
).toLocaleString()}
{waitlistedCount === undefined || maxWaitlist === undefined
? "N/A"
: maxWaitlist === 0
? 0
: Math.round(
(waitlistedCount / maxWaitlist) * 100
).toLocaleString()}
%
</span>
)
Expand Down
6 changes: 6 additions & 0 deletions apps/frontend/src/components/Carousel/Carousel.module.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.item {
flex-shrink: 0;
width: 288px;
}

.root {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -54,6 +59,7 @@
gap: 16px;
scrollbar-width: none;
scroll-behavior: smooth;
position: relative;
}
}

Expand Down
Loading

0 comments on commit a71f732

Please sign in to comment.