From d3f64ec79eb64ebe72ae28991127dbe521823842 Mon Sep 17 00:00:00 2001 From: Samuel Gunter Date: Mon, 25 Mar 2024 19:41:33 -0500 Subject: [PATCH 1/2] feat: actually sum for duplicate semesters (different uniques) (#202) --- src/views/lib/database/queryDistribution.ts | 27 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/views/lib/database/queryDistribution.ts b/src/views/lib/database/queryDistribution.ts index 6406e1ba..2ce8f14a 100644 --- a/src/views/lib/database/queryDistribution.ts +++ b/src/views/lib/database/queryDistribution.ts @@ -118,10 +118,29 @@ export async function querySemesterDistribution(course: Course, semester: Semest throw new NoDataError(course); } - let row: Required = {} as Required; - res.columns.forEach((col, i) => { - row[col as keyof CourseSQLRow] = res.values[0]![i]! as never; - }); + const row: Required = {} as Required; + for (let i = 0; i < res.columns.length; i++) { + const col = res.columns[i] as keyof CourseSQLRow; + switch (col) { + case 'A': + case 'A_Minus': + case 'B_Plus': + case 'B': + case 'B_Minus': + case 'C_Plus': + case 'C': + case 'C_Minus': + case 'D_Plus': + case 'D': + case 'D_Minus': + case 'F': + case 'Other': + row[col] = res.values.reduce((acc, cur) => acc + (cur[i] as number), 0) as never; + break; + default: + row[col] = res.columns[i]![0]! as never; + } + } return { A: row.A, From 227de53e8453804f0791f269dae64ec388136390 Mon Sep 17 00:00:00 2001 From: Samuel Gunter Date: Fri, 29 Mar 2024 00:01:23 -0500 Subject: [PATCH 2/2] feat: show async courses in the bottom bar (#204) * feat: show async courses in the bottom bar * fix: hide "Async/Other" header when there are no async courses, off-by-n error (where n is the number async courses) * refactor: move types closer to map instead of weird "as boolean" * refactor: move satisfies to return type --- .../calendar/CalendarBottomBar.stories.tsx | 27 +++++++---- .../calendar/CalendarGrid.stories.tsx | 7 +++ src/views/components/calendar/Calendar.tsx | 2 +- .../components/calendar/CalendarBottomBar.tsx | 33 ++++++++----- .../components/calendar/CalendarGrid.tsx | 48 ++++++++++--------- src/views/hooks/useFlattenedCourseSchedule.ts | 29 +++++------ 6 files changed, 84 insertions(+), 62 deletions(-) diff --git a/src/stories/components/calendar/CalendarBottomBar.stories.tsx b/src/stories/components/calendar/CalendarBottomBar.stories.tsx index 91c5303f..3695ff92 100644 --- a/src/stories/components/calendar/CalendarBottomBar.stories.tsx +++ b/src/stories/components/calendar/CalendarBottomBar.stories.tsx @@ -84,18 +84,29 @@ type Story = StoryObj; export const Default: Story = { args: { - courses: [ + courseCells: [ { - colors: getCourseColors('pink', 200), - courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} – ${exampleGovCourse.instructors[0]!.lastName}`, - status: exampleGovCourse.status, + async: true, + calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 }, + componentProps: { + colors: getCourseColors('pink', 200), + courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} – ${exampleGovCourse.instructors[0]!.lastName}`, + status: exampleGovCourse.status, + }, + course: exampleGovCourse, }, { - colors: getCourseColors('slate', 500), - courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} – ${examplePsyCourse.instructors[0]!.lastName}`, - status: examplePsyCourse.status, + async: true, + calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 }, + componentProps: { + colors: getCourseColors('slate', 500), + courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} – ${examplePsyCourse.instructors[0]!.lastName}`, + status: examplePsyCourse.status, + }, + course: examplePsyCourse, }, ], + setCourse: () => {}, }, render: props => (
@@ -105,7 +116,7 @@ export const Default: Story = { }; export const Empty: Story = { args: { - courses: [], + courseCells: [], }, render: props => (
diff --git a/src/stories/components/calendar/CalendarGrid.stories.tsx b/src/stories/components/calendar/CalendarGrid.stories.tsx index 86d74364..7eb3b8e4 100644 --- a/src/stories/components/calendar/CalendarGrid.stories.tsx +++ b/src/stories/components/calendar/CalendarGrid.stories.tsx @@ -35,6 +35,7 @@ const testData: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, course: ExampleCourse, + async: false, }, { calendarGridPoint: { @@ -49,6 +50,7 @@ const testData: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, course: ExampleCourse, + async: false, }, { calendarGridPoint: { @@ -63,6 +65,7 @@ const testData: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, course: ExampleCourse, + async: false, }, { calendarGridPoint: { @@ -77,6 +80,7 @@ const testData: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, course: ExampleCourse, + async: false, }, { calendarGridPoint: { @@ -91,6 +95,7 @@ const testData: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, course: ExampleCourse, + async: false, }, { calendarGridPoint: { @@ -105,6 +110,7 @@ const testData: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, course: ExampleCourse, + async: false, }, { calendarGridPoint: { @@ -119,6 +125,7 @@ const testData: CalendarGridCourse[] = [ colors: getCourseColors('emerald', 500), }, course: ExampleCourse, + async: false, }, ]; diff --git a/src/views/components/calendar/Calendar.tsx b/src/views/components/calendar/Calendar.tsx index e47f5e61..0633e680 100644 --- a/src/views/components/calendar/Calendar.tsx +++ b/src/views/components/calendar/Calendar.tsx @@ -87,7 +87,7 @@ export default function Calendar(): JSX.Element {
- +
diff --git a/src/views/components/calendar/CalendarBottomBar.tsx b/src/views/components/calendar/CalendarBottomBar.tsx index 8438e410..588c7de1 100644 --- a/src/views/components/calendar/CalendarBottomBar.tsx +++ b/src/views/components/calendar/CalendarBottomBar.tsx @@ -1,18 +1,20 @@ +import type { Course } from '@shared/types/Course'; import { saveAsCal, saveCalAsPng } from '@views/components/calendar/utils'; import { Button } from '@views/components/common/Button'; import Divider from '@views/components/common/Divider'; import Text from '@views/components/common/Text/Text'; +import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule'; import clsx from 'clsx'; import React from 'react'; import CalendarMonthIcon from '~icons/material-symbols/calendar-month'; import ImageIcon from '~icons/material-symbols/image'; -import type { CalendarCourseCellProps } from './CalendarCourseCell'; import CalendarCourseBlock from './CalendarCourseCell'; type CalendarBottomBarProps = { - courses?: CalendarCourseCellProps[]; + courseCells?: CalendarGridCourse[]; + setCourse: React.Dispatch>; }; /** @@ -21,8 +23,9 @@ type CalendarBottomBarProps = { * @param {Object[]} courses - The list of courses to display in the calendar. * @returns {JSX.Element} The rendered bottom bar component. */ -export default function CalendarBottomBar({ courses }: CalendarBottomBarProps): JSX.Element { - const displayCourses = courses && courses.length > 0; +export default function CalendarBottomBar({ courseCells, setCourse }: CalendarBottomBarProps): JSX.Element { + const asyncCourseCells = courseCells?.filter(block => block.async); + const displayCourses = asyncCourseCells && asyncCourseCells.length > 0; return (
@@ -35,15 +38,19 @@ export default function CalendarBottomBar({ courses }: CalendarBottomBarProps): <> Async/Other:
- {courses.map(({ courseDeptAndInstr, status, colors, className }) => ( - - ))} + {asyncCourseCells.map(block => { + const { courseDeptAndInstr, status, colors, className } = block.componentProps; + return ( + setCourse(block.course)} + /> + ); + })}
)} diff --git a/src/views/components/calendar/CalendarGrid.tsx b/src/views/components/calendar/CalendarGrid.tsx index 8ca31cf8..b4e3af86 100644 --- a/src/views/components/calendar/CalendarGrid.tsx +++ b/src/views/components/calendar/CalendarGrid.tsx @@ -123,28 +123,30 @@ function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseC }); }); - return courseCells.map((block, i) => { - const { courseDeptAndInstr, timeAndLocation, status } = courseCells[i]!.componentProps; + return courseCells + .filter(block => !block.async) + .map(block => { + const { courseDeptAndInstr, timeAndLocation, status } = block.componentProps; - return ( -
- setCourse(block.course)} - /> -
- ); - }); + return ( +
+ setCourse(block.course)} + /> +
+ ); + }); } diff --git a/src/views/hooks/useFlattenedCourseSchedule.ts b/src/views/hooks/useFlattenedCourseSchedule.ts index 99ed5bbb..87613a8e 100644 --- a/src/views/hooks/useFlattenedCourseSchedule.ts +++ b/src/views/hooks/useFlattenedCourseSchedule.ts @@ -1,7 +1,5 @@ -import type { HexColor } from '@shared/types/Color'; import type { Course, StatusType } from '@shared/types/Course'; import type { CourseMeeting } from '@shared/types/CourseMeeting'; -import { colors } from '@shared/types/ThemeColors'; import type { UserSchedule } from '@shared/types/UserSchedule'; import type { CalendarCourseCellProps } from '@views/components/calendar/CalendarCourseCell'; @@ -34,6 +32,7 @@ export interface CalendarGridCourse { calendarGridPoint: CalendarGridPoint; componentProps: CalendarCourseCellProps; course: Course; + async: boolean; gridColumnStart?: number; gridColumnEnd?: number; totalColumns?: number; @@ -103,25 +102,23 @@ function processAsyncCourses({ courseDeptAndInstr: string; status: StatusType; course: Course; -}) { +}): CalendarGridCourse[] { return [ { calendarGridPoint: { - dayIndex: 0, - startIndex: 0, - endIndex: 0, + dayIndex: -1, + startIndex: -1, + endIndex: -1, }, componentProps: { courseDeptAndInstr, status, - colors: { - primaryColor: colors.ut.gray as HexColor, - secondaryColor: colors.ut.gray as HexColor, - }, + colors: course.colors, }, course, + async: true, }, - ] satisfies CalendarGridCourse[]; + ]; } /** @@ -132,7 +129,7 @@ function processInPersonMeetings( courseDeptAndInstr: string, status: StatusType, course: Course -) { +): CalendarGridCourse[] { const { days, startTime, endTime, location } = meeting; const midnightIndex = 1440; const normalizingTimeFactor = 720; @@ -151,13 +148,11 @@ function processInPersonMeetings( courseDeptAndInstr, timeAndLocation, status, - colors: { - primaryColor: colors.ut.orange as HexColor, - secondaryColor: colors.ut.orange as HexColor, - }, + colors: course.colors, }, course, - })) satisfies CalendarGridCourse[]; + async: false, + })); } /**