-
- {course.courseName} ({course.department} {course.number})
+
+
+
+ {course.courseName}
-
- #{course.uniqueId}
-
+
+ {`(${course.department} ${course.number})`}
+
+
+
+
+
-
- {`with ${!course.instructors.length ? 'TBA' : ''}`}
- {course.instructors.map((instructor, index) => {
- const name = instructor.toString({
- format: 'first_last',
- case: 'capitalize',
- });
-
- const url = instructor.getDirectoryUrl();
- const numInstructors = course.instructors.length;
- const isLast = course.instructors.length > 1 && index === course.instructors.length - 1;
- return (
-
- {numInstructors > 1 && index === course.instructors.length - 1 ? '& ' : ''}
-
- {name}
-
- {numInstructors > 2 && !isLast ? ', ' : ''}
-
- );
- })}
-
- {course.schedule.meetings.map(meeting => (
-
-
- {meeting.getDaysString({
- format: 'long',
- separator: 'short',
- })}
-
- {' at '}
-
- {meeting.getTimeString({
- separator: 'to',
- capitalize: true,
- })}
-
- {' in '}
-
- {meeting.location?.building ?? 'TBA'}
-
+
+
+ with{' '}
+ {course.instructors.map(instructor => (
+ {instructor.lastName}
+ ))}
- ))}
+
+
+ //
+ //
+ //
+ //
+ // {course.courseName} ({course.department} {course.number}) blahhhhh
+ //
+ //
+ // #{course.uniqueId}
+ //
+ //
+ //
+ // {`with ${!course.instructors.length ? 'TBA' : ''}`}
+ // {course.instructors.map((instructor, index) => {
+ // const name = instructor.toString({
+ // format: 'first_last',
+ // case: 'capitalize',
+ // });
- {/* */}
-
+ // const url = instructor.getDirectoryUrl();
+ // const numInstructors = course.instructors.length;
+ // const isLast = course.instructors.length > 1 && index === course.instructors.length - 1;
+ // return (
+ //
+ // {numInstructors > 1 && index === course.instructors.length - 1 ? '& ' : ''}
+ //
+ // {name}
+ //
+ // {numInstructors > 2 && !isLast ? ', ' : ''}
+ //
+ // );
+ // })}
+ //
+ // {course.schedule.meetings.map(meeting => (
+ //
+ //
+ // {meeting.getDaysString({
+ // format: 'long',
+ // separator: 'short',
+ // })}
+ //
+ // {' at '}
+ //
+ // {meeting.getTimeString({
+ // separator: 'to',
+ // capitalize: true,
+ // })}
+ //
+ // {' in '}
+ //
+ // {meeting.location?.building ?? 'TBA'}
+ //
+ //
+ // ))}
+
+ //
+ //
);
}
diff --git a/src/views/components/injected/TableRow/TableRow.tsx b/src/views/components/injected/TableRow/TableRow.tsx
index e9c6796e..a6e82cb1 100644
--- a/src/views/components/injected/TableRow/TableRow.tsx
+++ b/src/views/components/injected/TableRow/TableRow.tsx
@@ -3,9 +3,9 @@ import { UserSchedule } from '@shared/types/UserSchedule';
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { Button } from '../../common/Button/Button';
-import Icon from '../../common/Icon/Icon';
-import Text from '../../common/Text/Text';
import styles from './TableRow.module.scss';
+import ConflictsWithWarning from '../../common/ConflictsWithWarning/ConflictsWithWarning';
+import AddIcon from '~icons/material-symbols/add-circle';
interface Props {
isSelected: boolean;
@@ -54,7 +54,7 @@ export default function TableRow({ row, isSelected, activeSchedule, onClick }: P
return () => {
element.classList.remove(styles.inActiveSchedule);
};
- }, [activeSchedule, element.classList]);
+ }, [activeSchedule, course, element.classList]);
useEffect(() => {
if (!activeSchedule || !course) {
@@ -84,20 +84,14 @@ export default function TableRow({ row, isSelected, activeSchedule, onClick }: P
return ReactDOM.createPortal(
<>
-
- {conflicts.length > 0 && (
-
-
- {conflicts.map(c => (
-
- {c.department} {c.number} ({c.uniqueId})
-
- ))}
-
-
- )}
+
+ {conflicts.length > 0 &&
}
>,
container
);
diff --git a/src/views/hooks/useFlattenedCourseSchedule.ts b/src/views/hooks/useFlattenedCourseSchedule.ts
new file mode 100644
index 00000000..da1a3bb3
--- /dev/null
+++ b/src/views/hooks/useFlattenedCourseSchedule.ts
@@ -0,0 +1,102 @@
+import { CalendarCourseCellProps } from 'src/views/components/common/CalendarCourseCell/CalendarCourseCell';
+import useSchedules from './useSchedules';
+
+const dayToNumber: { [day: string]: number } = {
+ Monday: 0,
+ Tuesday: 1,
+ Wednesday: 2,
+ Thursday: 3,
+ Friday: 4,
+};
+
+interface CalendarGridPoint {
+ dayIndex: number;
+ startIndex: number;
+ endIndex: number;
+}
+
+/**
+ * Return type of useFlattenedCourseSchedule
+ */
+export interface CalendarGridCourse {
+ calendarGridPoint: CalendarGridPoint;
+ componentProps: CalendarCourseCellProps;
+}
+
+const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30);
+
+/**
+ * Get the active schedule, and convert it to be render-able into a calendar.
+ * @returns CalendarGridCourse
+ */
+export function useFlattenedCourseSchedule(): CalendarGridCourse[] {
+ const [activeSchedule] = useSchedules();
+ const { courses } = activeSchedule;
+
+ return courses
+ .flatMap(course => {
+ const {
+ status,
+ department,
+ instructors,
+ schedule: { meetings },
+ } = course;
+ const courseDeptAndInstr = `${department} ${instructors[0].lastName}`;
+
+ if (meetings.length === 0) {
+ // asynch, online course
+ return [
+ {
+ calendarGridPoint: {
+ dayIndex: 0,
+ startIndex: 0,
+ endIndex: 0,
+ },
+ componentProps: {
+ courseDeptAndInstr,
+ status,
+ colors: {
+ // TODO: figure out colors - these are defaults
+ primaryColor: 'ut-gray',
+ secondaryColor: 'ut-gray',
+ },
+ },
+ },
+ ];
+ }
+
+ // in-person
+ return meetings.flatMap(meeting => {
+ const { days, startTime, endTime, location } = meeting;
+ const time = meeting.getTimeString({ separator: '-', capitalize: true });
+ const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`;
+
+ return days.map(d => ({
+ calendarGridPoint: {
+ dayIndex: dayToNumber[d],
+ startIndex: convertMinutesToIndex(startTime),
+ endIndex: convertMinutesToIndex(endTime),
+ },
+ componentProps: {
+ courseDeptAndInstr,
+ timeAndLocation,
+ status,
+ colors: {
+ // TODO: figure out colors - these are defaults
+ primaryColor: 'ut-orange',
+ secondaryColor: 'ut-orange',
+ },
+ },
+ }));
+ });
+ })
+ .sort((a: CalendarGridCourse, b: CalendarGridCourse) => {
+ if (a.calendarGridPoint.dayIndex !== b.calendarGridPoint.dayIndex) {
+ return a.calendarGridPoint.dayIndex - b.calendarGridPoint.dayIndex;
+ }
+ if (a.calendarGridPoint.startIndex !== b.calendarGridPoint.startIndex) {
+ return a.calendarGridPoint.startIndex - b.calendarGridPoint.startIndex;
+ }
+ return a.calendarGridPoint.endIndex - b.calendarGridPoint.endIndex;
+ });
+}
diff --git a/unocss.config.ts b/unocss.config.ts
index 605cad07..4cf5f36f 100644
--- a/unocss.config.ts
+++ b/unocss.config.ts
@@ -7,18 +7,9 @@ import { colors } from './src/shared/util/themeColors';
export default defineConfig({
rules: [
- ['btn-transition', { transition: 'color 180ms, border-color 150ms, background-color 150ms, transform 50ms' }],
[
- 'btn-shadow',
- {
- 'box-shadow': '0px 1px 3px 1px var(--shadow-color-15), 0px 1px 2px 0px var(--shadow-color-30);',
- },
- ],
- [
- 'btn-shade',
- {
- 'background-color': 'var(--bg-color-8)',
- },
+ 'btn-transition',
+ { transition: 'color 180ms, border-color 150ms, background-color 150ms, box-shadow 100ms, transform 50ms' },
],
[
'ring-offset-0',
@@ -29,7 +20,7 @@ export default defineConfig({
],
shortcuts: {
focusable: 'outline-none ring-blue-500/50 dark:ring-blue-400/60 ring-0 focus-visible:ring-4',
- btn: 'h-10 w-auto flex cursor-pointer justify-center items-center gap-2 rounded-1 px-4 py-0 text-4.5 btn-transition',
+ btn: 'h-10 w-auto flex cursor-pointer justify-center items-center gap-2 rounded-1 px-4 py-0 text-4.5 btn-transition btn-transition disabled:(cursor-not-allowed opacity-50) active:enabled:scale-96 focusable',
},
theme: {
easing: {