Merge branch 'hackathon' into Som

This commit is contained in:
knownotunknown
2024-02-17 17:07:06 -06:00
11 changed files with 313 additions and 97 deletions

View File

@@ -24,6 +24,7 @@
"clsx": "^2.1.0", "clsx": "^2.1.0",
"highcharts": "^11.3.0", "highcharts": "^11.3.0",
"highcharts-react-official": "^3.2.1", "highcharts-react-official": "^3.2.1",
"html2canvas": "^1.4.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-devtools-core": "^5.0.0", "react-devtools-core": "^5.0.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

34
pnpm-lock.yaml generated
View File

@@ -34,6 +34,9 @@ dependencies:
highcharts-react-official: highcharts-react-official:
specifier: ^3.2.1 specifier: ^3.2.1
version: 3.2.1(highcharts@11.3.0)(react@18.2.0) version: 3.2.1(highcharts@11.3.0)(react@18.2.0)
html2canvas:
specifier: ^1.4.1
version: 1.4.1
react: react:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
@@ -5853,6 +5856,11 @@ packages:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true dev: true
/base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
dev: false
/base64-js@1.5.1: /base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true dev: true
@@ -6545,6 +6553,12 @@ packages:
postcss: 8.4.35 postcss: 8.4.35
dev: true dev: true
/css-line-break@2.1.0:
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
dependencies:
utrie: 1.0.2
dev: false
/css-select@5.1.0: /css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
dependencies: dependencies:
@@ -8649,6 +8663,14 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/html2canvas@1.4.1:
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
engines: {node: '>=8.0.0'}
dependencies:
css-line-break: 2.1.0
text-segmentation: 1.0.3
dev: false
/htmlparser2@8.0.2: /htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
dependencies: dependencies:
@@ -12408,6 +12430,12 @@ packages:
minimatch: 3.1.2 minimatch: 3.1.2
dev: true dev: true
/text-segmentation@1.0.3:
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
dependencies:
utrie: 1.0.2
dev: false
/text-table@0.2.0: /text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true dev: true
@@ -12950,6 +12978,12 @@ packages:
engines: {node: '>= 0.4.0'} engines: {node: '>= 0.4.0'}
dev: true dev: true
/utrie@1.0.2:
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
dependencies:
base64-arraybuffer: 1.0.2
dev: false
/uuid@9.0.1: /uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true hasBin: true

View File

@@ -0,0 +1,101 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { Course, Status } from '@shared/types/Course';
import Instructor from '@shared/types/Instructor';
import { CalendarBottomBar } from '@views/components/common/CalendarBottomBar/CalendarBottomBar';
import { getCourseColors } from '../../shared/util/colors';
const exampleGovCourse: Course = new Course({
courseName: 'Nope',
creditHours: 3,
department: 'GOV',
description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'],
fullName: 'GOV 312L Something something',
instructionMode: 'Online',
instructors: [
new Instructor({
firstName: 'Bevo',
lastName: 'Barrymore',
fullName: 'Bevo Barrymore',
}),
],
isReserved: false,
number: '312L',
schedule: {
meetings: [],
},
semester: {
code: '12345',
season: 'Spring',
year: 2024,
},
status: Status.OPEN,
uniqueId: 12345,
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
});
const examplePsyCourse: Course = new Course({
courseName: 'Nope Again',
creditHours: 3,
department: 'PSY',
description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'],
fullName: 'PSY 317L Yada yada',
instructionMode: 'Online',
instructors: [
new Instructor({
firstName: 'Bevo',
lastName: 'Etz',
fullName: 'Bevo Etz',
}),
],
isReserved: false,
number: '317L',
schedule: {
meetings: [],
},
semester: {
code: '12346',
season: 'Spring',
year: 2024,
},
status: Status.CLOSED,
uniqueId: 12346,
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
});
const meta = {
title: 'Components/Common/CalendarBottomBar',
component: CalendarBottomBar,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {},
} satisfies Meta<typeof CalendarBottomBar>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
courses: [
{
colors: getCourseColors('pink', 200),
courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} ${exampleGovCourse.instructors[0].lastName}`,
status: exampleGovCourse.status,
},
{
colors: getCourseColors('slate', 500),
courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} ${examplePsyCourse.instructors[0].lastName}`,
status: examplePsyCourse.status,
},
],
},
render: props => (
<div className='outline-red outline w-292.5!'>
<CalendarBottomBar {...props} />
</div>
),
};

View File

@@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
import CalendarHeader from '@views/components/common/CalendarHeader/CalenderHeader'; import CalendarHeader from '@views/components/common/CalendarHeader/CalenderHeader';
const meta = { const meta = {
title: 'Components/CalendarHeader', title: 'Components/Common/CalendarHeader',
component: CalendarHeader, component: CalendarHeader,
parameters: { parameters: {
layout: 'centered', layout: 'centered',

View File

@@ -17,7 +17,7 @@ export default function ImportantLinks({ className }: Props) {
<Text variant='h3'>Important Links</Text> <Text variant='h3'>Important Links</Text>
<a <a
href='https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/' href='https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/'
className='text-ut-burntorange flex items-center gap-0.5' className='flex items-center gap-0.5 text-ut-burntorange'
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
> >
@@ -26,7 +26,7 @@ export default function ImportantLinks({ className }: Props) {
</a> </a>
<a <a
href='https://utdirect.utexas.edu/apps/registrar/course_schedule/20236/' href='https://utdirect.utexas.edu/apps/registrar/course_schedule/20236/'
className='text-ut-burntorange flex items-center gap-0.5' className='flex items-center gap-0.5 text-ut-burntorange'
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
> >
@@ -35,7 +35,7 @@ export default function ImportantLinks({ className }: Props) {
</a> </a>
<a <a
href='https://utdirect.utexas.edu/registrar/ris.WBX' href='https://utdirect.utexas.edu/registrar/ris.WBX'
className='text-ut-burntorange flex items-center gap-0.5' className='flex items-center gap-0.5 text-ut-burntorange'
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
> >
@@ -44,7 +44,7 @@ export default function ImportantLinks({ className }: Props) {
</a> </a>
<a <a
href='https://utdirect.utexas.edu/registration/chooseSemester.WBX' href='https://utdirect.utexas.edu/registration/chooseSemester.WBX'
className='text-ut-burntorange flex items-center gap-0.5' className='flex items-center gap-0.5 text-ut-burntorange'
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
> >
@@ -53,7 +53,7 @@ export default function ImportantLinks({ className }: Props) {
</a> </a>
<a <a
href='https://utdirect.utexas.edu/apps/degree/audits/' href='https://utdirect.utexas.edu/apps/degree/audits/'
className='text-ut-burntorange flex items-center gap-0.5' className='flex items-center gap-0.5 text-ut-burntorange'
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
> >

View File

@@ -0,0 +1,44 @@
import React from 'react';
import clsx from 'clsx';
import Text from '../Text/Text';
import CalendarCourseBlock, { CalendarCourseCellProps } from '../CalendarCourseCell/CalendarCourseCell';
import { Button } from '../Button/Button';
import ImageIcon from '~icons/material-symbols/image';
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
type CalendarBottomBarProps = {
courses: CalendarCourseCellProps[];
};
/**
*
*/
export const CalendarBottomBar = ({ courses }: CalendarBottomBarProps): JSX.Element => {
if (courses.length === -1) console.log('foo'); // dumb line to make eslint happy
return (
<div className='w-full flex py-1.25'>
<div className='flex flex-grow items-center gap-3.75 pl-7.5 pr-2.5'>
<Text variant='h4'>Async. and Other:</Text>
<div className='h-14 inline-flex gap-2.5'>
{courses.map(course => (
<CalendarCourseBlock
courseDeptAndInstr={course.courseDeptAndInstr}
status={course.status}
colors={course.colors}
key={course.courseDeptAndInstr}
className={clsx(course.className, 'w-35!')}
/>
))}
</div>
</div>
<div className='flex items-center pl-2.5 pr-7.5'>
<Button variant='single' color='ut-black' icon={CalendarMonthIcon}>
Save as .CAL
</Button>
<Button variant='single' color='ut-black' icon={ImageIcon}>
Save as .PNG
</Button>
</div>
</div>
);
};

View File

@@ -12,6 +12,7 @@ export interface CalendarCourseCellProps {
timeAndLocation?: string; timeAndLocation?: string;
status: Status; status: Status;
colors: CourseColors; colors: CourseColors;
className?: string;
} }
const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
@@ -19,6 +20,7 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
timeAndLocation, timeAndLocation,
status, status,
colors, colors,
className,
}: CalendarCourseCellProps) => { }: CalendarCourseCellProps) => {
let rightIcon: React.ReactNode | null = null; let rightIcon: React.ReactNode | null = null;
if (status === Status.WAITLISTED) { if (status === Status.WAITLISTED) {
@@ -34,7 +36,7 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
return ( return (
<div <div
className={`w-full flex justify-center rounded p-2 ${fontColor}`} className={clsx('w-full flex justify-center rounded p-2', fontColor, className)}
style={{ style={{
backgroundColor: colors.primaryColor, backgroundColor: colors.primaryColor,
}} }}

View File

@@ -1,12 +1,14 @@
import React, { useState } from 'react'; import React, { useState, useRef } from 'react';
import html2canvas from 'html2canvas';
import { DAY_MAP } from 'src/shared/types/CourseMeeting'; import { DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule'; import { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule';
import calIcon from 'src/assets/icons/cal.svg';
import pngIcon from 'src/assets/icons/png.svg';
import CalendarCell from '../CalendarGridCell/CalendarGridCell'; import CalendarCell from '../CalendarGridCell/CalendarGridCell';
import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell'; import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell';
import styles from './CalendarGrid.module.scss'; import styles from './CalendarGrid.module.scss';
import calIcon from 'src/assets/icons/cal.svg';
// import calIcon from 'src/assets/icons/cal.svg'; import pngIcon from 'src/assets/icons/png.svg';
// import pngIcon from 'src/assets/icons/png.svg';
const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key)); const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key));
const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8); const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8);
@@ -36,11 +38,25 @@ interface Props {
*/ */
function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Props>): JSX.Element { function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Props>): JSX.Element {
const [iterator, setIterator] = useState<number>(0); const [iterator, setIterator] = useState<number>(0);
const calendarRef = useRef(null); // Create a ref for the calendar grid
const saveAsPNG = () => {
if (calendarRef.current) {
html2canvas(calendarRef.current).then(canvas => {
// Create an a element to trigger download
const a = document.createElement('a');
a.href = canvas.toDataURL('image/png');
a.download = 'calendar.png';
a.click();
});
}
};
return ( return (
<div className={styles.calendar}> <div className={styles.calendar}>
<div className={styles.dayLabelContainer} /> <div className={styles.dayLabelContainer} />
{/* Displaying the rest of the calendar */} {/* Displaying the rest of the calendar */}
<div className={styles.timeAndGrid}> <div ref={calendarRef} className={styles.timeAndGrid}>
{/* <div className={styles.timeColumn}> {/* <div className={styles.timeColumn}>
<div className={styles.timeBlock}></div> <div className={styles.timeBlock}></div>
{hoursOfDay.map((hour) => ( {hoursOfDay.map((hour) => (
@@ -81,18 +97,18 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Pr
</div> </div>
</div> </div>
{/* <div className={styles.buttonContainer}> <div className={styles.buttonContainer}>
<div className={styles.divider}></div> <div className={styles.divider} /> {/* First divider */}
<button className={styles.calendarButton}> <button className={styles.calendarButton}>
<img src={calIcon} className={styles.buttonIcon} alt="CAL" /> <img src={calIcon} className={styles.buttonIcon} alt='CAL' />
Save as .CAL Save as .CAL
</button> </button>
<div className={styles.divider}></div> <div className={styles.divider} /> {/* Second divider */}
<button className={styles.calendarButton}> <button onClick={saveAsPNG} className={styles.calendarButton}>
<img src={pngIcon} className={styles.buttonIcon} alt="PNG" /> <img src={pngIcon} className={styles.buttonIcon} alt='PNG' />
Save as .PNG Save as .PNG
</button> </button>
</div> */} </div>
</div> </div>
); );
} }

View File

@@ -12,40 +12,41 @@ import ScheduleTotalHoursAndCourses from '../ScheduleTotalHoursAndCourses/Schedu
import CourseStatus from '../CourseStatus/CourseStatus'; import CourseStatus from '../CourseStatus/CourseStatus';
const CalendarHeader = () => ( const CalendarHeader = () => (
<div <div className='min-h-79px min-w-672px flex px-0 py-15'>
style={{ <div className='flex flex-row gap-20'>
display: 'flex', <div className='flex gap-10'>
minWidth: '672px', <div className='flex gap-1'>
minHeight: '79px',
padding: '15px 0px',
justifyContent: 'space-between',
alignItems: 'center',
alignContent: 'center',
rowGap: '10px',
alignSelf: 'stretch',
flexWrap: 'wrap',
}}
>
<Button variant='single' icon={MenuIcon} color='ut-gray' /> <Button variant='single' icon={MenuIcon} color='ut-gray' />
<div className='flex items-center'>
<div style={{ display: 'flex', alignItems: 'center' }}>
<LogoIcon style={{ marginRight: '5px' }} /> <LogoIcon style={{ marginRight: '5px' }} />
<Text>Your Logo Text</Text> <div className='flex flex-col gap-1 whitespace-nowrap'>
<Text className='leading-trim text-cap font-roboto text-base text-ut-burntorange font-medium'>
UT Registration
</Text>
<Text className='leading-trim text-cap font-roboto text-base text-ut-orange font-medium'>
Plus
</Text>
</div> </div>
</div>
</div>
<div className='flex flex-col'>
<ScheduleTotalHoursAndCourses scheduleName='SCHEDULE' totalHours={22} totalCourses={8} /> <ScheduleTotalHoursAndCourses scheduleName='SCHEDULE' totalHours={22} totalCourses={8} />
DATA UPDATED ON: 12:00 AM 02/01/2024
</div>
</div>
<div className='flex flex-row items-center space-x-8'>
<div className='flex flex-row space-x-4'>
<CourseStatus size='small' status={Status.WAITLISTED} /> <CourseStatus size='small' status={Status.WAITLISTED} />
<CourseStatus size='small' status={Status.CLOSED} /> <CourseStatus size='small' status={Status.CLOSED} />
<CourseStatus size='small' status={Status.CANCELLED} /> <CourseStatus size='small' status={Status.CANCELLED} />
<div style={{ display: 'flex' }}>
<Button variant='outline' icon={UndoIcon} color='ut-black' />
<Button variant='outline' icon={RedoIcon} color='ut-black' />
</div> </div>
<div className='flex flex-row'>
<Button variant='outline' icon={SettingsIcon} color='ut-black' /> <Button variant='single' icon={UndoIcon} color='ut-black' />
<Button variant='single' icon={RedoIcon} color='ut-black' />
<Button variant='single' icon={SettingsIcon} color='ut-black' />
</div>
</div>
</div>
<Divider type='solid' /> <Divider type='solid' />
</div> </div>
); );

View File

@@ -21,7 +21,7 @@ export default function ScheduleTotalHoursAndCourses({
totalCourses, totalCourses,
}: ScheduleTotalHoursAndCoursesProps): JSX.Element { }: ScheduleTotalHoursAndCoursesProps): JSX.Element {
return ( return (
<div className='min-w-64 flex flex-wrap content-center items-baseline gap-2 uppercase'> <div className='min-w-64 flex whitespace-nowrap content-center items-baseline gap-2 uppercase'>
<Text className='text-[#BF5700]' variant='h1' as='span'> <Text className='text-[#BF5700]' variant='h1' as='span'>
{`${scheduleName}: `} {`${scheduleName}: `}
</Text> </Text>

View File

@@ -1,7 +1,7 @@
import { CalendarCourseCellProps } from 'src/views/components/common/CalendarCourseCell/CalendarCourseCell'; import { CalendarCourseCellProps } from 'src/views/components/common/CalendarCourseCell/CalendarCourseCell';
import useSchedules from './useSchedules'; import useSchedules from './useSchedules';
const dayToNumber = { const dayToNumber: { [day: string]: number } = {
Monday: 0, Monday: 0,
Tuesday: 1, Tuesday: 1,
Wednesday: 2, Wednesday: 2,
@@ -15,18 +15,26 @@ interface CalendarGridPoint {
endIndex: number; endIndex: number;
} }
/**
* Return type of useFlattenedCourseSchedule
*/
export interface CalendarGridCourse { export interface CalendarGridCourse {
calendarGridPoint?: CalendarGridPoint; calendarGridPoint: CalendarGridPoint;
componentProps: CalendarCourseCellProps; componentProps: CalendarCourseCellProps;
} }
const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30); const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30);
export function useFlattenedCourseSchedule() { /**
* Get the active schedule, and convert it to be render-able into a calendar.
* @returns CalendarGridCourse
*/
export function useFlattenedCourseSchedule(): CalendarGridCourse[] {
const [activeSchedule] = useSchedules(); const [activeSchedule] = useSchedules();
const { courses } = activeSchedule; const { courses } = activeSchedule;
const out = courses.flatMap(course => { return courses
.flatMap(course => {
const { const {
status, status,
department, department,
@@ -39,10 +47,16 @@ export function useFlattenedCourseSchedule() {
// asynch, online course // asynch, online course
return [ return [
{ {
calendarGridPoint: {
dayIndex: 0,
startIndex: 0,
endIndex: 0,
},
componentProps: { componentProps: {
courseDeptAndInstr, courseDeptAndInstr,
status, status,
colors: { colors: {
// TODO: figure out colors - these are defaults
primaryColor: 'ut-gray', primaryColor: 'ut-gray',
secondaryColor: 'ut-gray', secondaryColor: 'ut-gray',
}, },
@@ -50,36 +64,39 @@ export function useFlattenedCourseSchedule() {
}, },
]; ];
} }
// in-person
return meetings.flatMap(meeting => { return meetings.flatMap(meeting => {
const { days, startTime, endTime, location } = meeting; const { days, startTime, endTime, location } = meeting;
const time = meeting.getTimeString({ separator: '-', capitalize: true }); const time = meeting.getTimeString({ separator: '-', capitalize: true });
const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`; const timeAndLocation = `${time} - ${location ? location.building : 'WB'}`;
return days.map(d => { return days.map(d => ({
const dayIndex = dayToNumber[d]; calendarGridPoint: {
const startIndex = convertMinutesToIndex(startTime); dayIndex: dayToNumber[d],
const endIndex = convertMinutesToIndex(endTime); startIndex: convertMinutesToIndex(startTime),
const calendarGridPoint: CalendarGridPoint = { endIndex: convertMinutesToIndex(endTime),
dayIndex, },
startIndex,
endIndex,
};
return {
calendarGridPoint,
componentProps: { componentProps: {
courseDeptAndInstr, courseDeptAndInstr,
timeAndLocation, timeAndLocation,
status, status,
colors: { colors: {
// TODO: figure out colors - these are defaults
primaryColor: 'ut-orange', primaryColor: 'ut-orange',
secondaryColor: '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;
}); });
});
});
return out;
} }