Merge branch 'hackathon' into Som
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
"clsx": "^2.1.0",
|
||||
"highcharts": "^11.3.0",
|
||||
"highcharts-react-official": "^3.2.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"react": "^18.2.0",
|
||||
"react-devtools-core": "^5.0.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@@ -34,6 +34,9 @@ dependencies:
|
||||
highcharts-react-official:
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1(highcharts@11.3.0)(react@18.2.0)
|
||||
html2canvas:
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
@@ -5853,6 +5856,11 @@ packages:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
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:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
dev: true
|
||||
@@ -6545,6 +6553,12 @@ packages:
|
||||
postcss: 8.4.35
|
||||
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:
|
||||
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
|
||||
dependencies:
|
||||
@@ -8649,6 +8663,14 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
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:
|
||||
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
||||
dependencies:
|
||||
@@ -12408,6 +12430,12 @@ packages:
|
||||
minimatch: 3.1.2
|
||||
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:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
dev: true
|
||||
@@ -12950,6 +12978,12 @@ packages:
|
||||
engines: {node: '>= 0.4.0'}
|
||||
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:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
hasBin: true
|
||||
|
||||
101
src/stories/components/CalendarBottomBar.stories.tsx
Normal file
101
src/stories/components/CalendarBottomBar.stories.tsx
Normal 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>
|
||||
),
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import CalendarHeader from '@views/components/common/CalendarHeader/CalenderHeader';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/CalendarHeader',
|
||||
title: 'Components/Common/CalendarHeader',
|
||||
component: CalendarHeader,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function ImportantLinks({ className }: Props) {
|
||||
<Text variant='h3'>Important Links</Text>
|
||||
<a
|
||||
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'
|
||||
rel='noreferrer'
|
||||
>
|
||||
@@ -26,7 +26,7 @@ export default function ImportantLinks({ className }: Props) {
|
||||
</a>
|
||||
<a
|
||||
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'
|
||||
rel='noreferrer'
|
||||
>
|
||||
@@ -35,7 +35,7 @@ export default function ImportantLinks({ className }: Props) {
|
||||
</a>
|
||||
<a
|
||||
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'
|
||||
rel='noreferrer'
|
||||
>
|
||||
@@ -44,7 +44,7 @@ export default function ImportantLinks({ className }: Props) {
|
||||
</a>
|
||||
<a
|
||||
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'
|
||||
rel='noreferrer'
|
||||
>
|
||||
@@ -53,7 +53,7 @@ export default function ImportantLinks({ className }: Props) {
|
||||
</a>
|
||||
<a
|
||||
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'
|
||||
rel='noreferrer'
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ export interface CalendarCourseCellProps {
|
||||
timeAndLocation?: string;
|
||||
status: Status;
|
||||
colors: CourseColors;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
|
||||
@@ -19,6 +20,7 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
|
||||
timeAndLocation,
|
||||
status,
|
||||
colors,
|
||||
className,
|
||||
}: CalendarCourseCellProps) => {
|
||||
let rightIcon: React.ReactNode | null = null;
|
||||
if (status === Status.WAITLISTED) {
|
||||
@@ -34,7 +36,7 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`w-full flex justify-center rounded p-2 ${fontColor}`}
|
||||
className={clsx('w-full flex justify-center rounded p-2', fontColor, className)}
|
||||
style={{
|
||||
backgroundColor: colors.primaryColor,
|
||||
}}
|
||||
|
||||
@@ -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 { 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 CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell';
|
||||
import styles from './CalendarGrid.module.scss';
|
||||
|
||||
// import calIcon from 'src/assets/icons/cal.svg';
|
||||
// import pngIcon from 'src/assets/icons/png.svg';
|
||||
import calIcon from 'src/assets/icons/cal.svg';
|
||||
import pngIcon from 'src/assets/icons/png.svg';
|
||||
|
||||
const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key));
|
||||
const hoursOfDay = Array.from({ length: 14 }, (_, index) => index + 8);
|
||||
@@ -34,13 +36,27 @@ interface Props {
|
||||
* Grid of CalendarGridCell components forming the user's course schedule calendar view
|
||||
* @param 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 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 (
|
||||
<div className={styles.calendar}>
|
||||
<div className={styles.dayLabelContainer} />
|
||||
{/* Displaying the rest of the calendar */}
|
||||
<div className={styles.timeAndGrid}>
|
||||
<div ref={calendarRef} className={styles.timeAndGrid}>
|
||||
{/* <div className={styles.timeColumn}>
|
||||
<div className={styles.timeBlock}></div>
|
||||
{hoursOfDay.map((hour) => (
|
||||
@@ -81,18 +97,18 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Pr
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <div className={styles.buttonContainer}>
|
||||
<div className={styles.divider}></div>
|
||||
<div className={styles.buttonContainer}>
|
||||
<div className={styles.divider} /> {/* First divider */}
|
||||
<button className={styles.calendarButton}>
|
||||
<img src={calIcon} className={styles.buttonIcon} alt="CAL" />
|
||||
<img src={calIcon} className={styles.buttonIcon} alt='CAL' />
|
||||
Save as .CAL
|
||||
</button>
|
||||
<div className={styles.divider}></div>
|
||||
<button className={styles.calendarButton}>
|
||||
<img src={pngIcon} className={styles.buttonIcon} alt="PNG" />
|
||||
<div className={styles.divider} /> {/* Second divider */}
|
||||
<button onClick={saveAsPNG} className={styles.calendarButton}>
|
||||
<img src={pngIcon} className={styles.buttonIcon} alt='PNG' />
|
||||
Save as .PNG
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,40 +12,41 @@ import ScheduleTotalHoursAndCourses from '../ScheduleTotalHoursAndCourses/Schedu
|
||||
import CourseStatus from '../CourseStatus/CourseStatus';
|
||||
|
||||
const CalendarHeader = () => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
minWidth: '672px',
|
||||
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' />
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<LogoIcon style={{ marginRight: '5px' }} />
|
||||
<Text>Your Logo Text</Text>
|
||||
<div className='min-h-79px min-w-672px flex px-0 py-15'>
|
||||
<div className='flex flex-row gap-20'>
|
||||
<div className='flex gap-10'>
|
||||
<div className='flex gap-1'>
|
||||
<Button variant='single' icon={MenuIcon} color='ut-gray' />
|
||||
<div className='flex items-center'>
|
||||
<LogoIcon style={{ marginRight: '5px' }} />
|
||||
<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 className='flex flex-col'>
|
||||
<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.CLOSED} />
|
||||
<CourseStatus size='small' status={Status.CANCELLED} />
|
||||
</div>
|
||||
<div className='flex flex-row'>
|
||||
<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>
|
||||
|
||||
<ScheduleTotalHoursAndCourses scheduleName='SCHEDULE' totalHours={22} totalCourses={8} />
|
||||
|
||||
<CourseStatus size='small' status={Status.WAITLISTED} />
|
||||
<CourseStatus size='small' status={Status.CLOSED} />
|
||||
<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>
|
||||
|
||||
<Button variant='outline' icon={SettingsIcon} color='ut-black' />
|
||||
|
||||
<Divider type='solid' />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function ScheduleTotalHoursAndCourses({
|
||||
totalCourses,
|
||||
}: ScheduleTotalHoursAndCoursesProps): JSX.Element {
|
||||
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'>
|
||||
{`${scheduleName}: `}
|
||||
</Text>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CalendarCourseCellProps } from 'src/views/components/common/CalendarCourseCell/CalendarCourseCell';
|
||||
import useSchedules from './useSchedules';
|
||||
|
||||
const dayToNumber = {
|
||||
const dayToNumber: { [day: string]: number } = {
|
||||
Monday: 0,
|
||||
Tuesday: 1,
|
||||
Wednesday: 2,
|
||||
@@ -15,71 +15,88 @@ interface CalendarGridPoint {
|
||||
endIndex: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return type of useFlattenedCourseSchedule
|
||||
*/
|
||||
export interface CalendarGridCourse {
|
||||
calendarGridPoint?: CalendarGridPoint;
|
||||
calendarGridPoint: CalendarGridPoint;
|
||||
componentProps: CalendarCourseCellProps;
|
||||
}
|
||||
|
||||
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 { courses } = activeSchedule;
|
||||
|
||||
const out = courses.flatMap(course => {
|
||||
const {
|
||||
status,
|
||||
department,
|
||||
instructors,
|
||||
schedule: { meetings },
|
||||
} = course;
|
||||
const courseDeptAndInstr = `${department} ${instructors[0].lastName}`;
|
||||
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 [
|
||||
{
|
||||
componentProps: {
|
||||
courseDeptAndInstr,
|
||||
status,
|
||||
colors: {
|
||||
primaryColor: 'ut-gray',
|
||||
secondaryColor: 'ut-gray',
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
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 => {
|
||||
const dayIndex = dayToNumber[d];
|
||||
const startIndex = convertMinutesToIndex(startTime);
|
||||
const endIndex = convertMinutesToIndex(endTime);
|
||||
const calendarGridPoint: CalendarGridPoint = {
|
||||
dayIndex,
|
||||
startIndex,
|
||||
endIndex,
|
||||
};
|
||||
// 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 {
|
||||
calendarGridPoint,
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user