feat: calendar header formatting and data displaying (#160)

This commit is contained in:
Samuel Gunter
2024-03-13 21:38:40 -05:00
committed by GitHub
parent 61c1e88dcf
commit 5cce1c79fc
16 changed files with 57 additions and 10 deletions

View File

@@ -16,6 +16,7 @@ export default async function addCourse(scheduleName: string, course: Course): P
} }
activeSchedule.courses.push(course); activeSchedule.courses.push(course);
activeSchedule.updatedAt = Date.now();
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
} }

View File

@@ -12,5 +12,7 @@ export default async function clearCourses(scheduleName: string): Promise<void>
throw new Error(`Schedule ${scheduleName} does not exist`); throw new Error(`Schedule ${scheduleName} does not exist`);
} }
schedule.courses = []; schedule.courses = [];
schedule.updatedAt = Date.now();
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
} }

View File

@@ -15,6 +15,7 @@ export default async function createSchedule(scheduleName: string): Promise<stri
name: scheduleName, name: scheduleName,
courses: [], courses: [],
hours: 0, hours: 0,
updatedAt: Date.now(),
}); });
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);

View File

@@ -12,6 +12,7 @@ export default async function removeCourse(scheduleName: string, course: Course)
} }
activeSchedule.courses = activeSchedule.courses.filter(c => c.uniqueId !== course.uniqueId); activeSchedule.courses = activeSchedule.courses.filter(c => c.uniqueId !== course.uniqueId);
activeSchedule.updatedAt = Date.now();
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
} }

View File

@@ -17,6 +17,7 @@ export default async function renameSchedule(scheduleName: string, newName: stri
} }
schedules[scheduleIndex].name = newName; schedules[scheduleIndex].name = newName;
schedules[scheduleIndex].updatedAt = Date.now();
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
return undefined; return undefined;

View File

@@ -13,6 +13,7 @@ export default async function switchSchedule(scheduleName: string): Promise<void
if (scheduleIndex === -1) { if (scheduleIndex === -1) {
throw new Error(`Schedule ${scheduleName} does not exist`); throw new Error(`Schedule ${scheduleName} does not exist`);
} }
schedules[scheduleIndex].updatedAt = Date.now();
await UserScheduleStore.set('activeIndex', scheduleIndex); await UserScheduleStore.set('activeIndex', scheduleIndex);
} }

View File

@@ -15,6 +15,7 @@ export const UserScheduleStore = createLocalStore<IUserScheduleStore>({
courses: [], courses: [],
name: 'Schedule 1', name: 'Schedule 1',
hours: 0, hours: 0,
updatedAt: Date.now(),
}), }),
], ],
activeIndex: 0, activeIndex: 0,

View File

@@ -9,6 +9,8 @@ export class UserSchedule {
courses: Course[]; courses: Course[];
name: string; name: string;
hours: number; hours: number;
/** Unix timestamp of when the schedule was last updated */
updatedAt: number;
constructor(schedule: Serialized<UserSchedule>) { constructor(schedule: Serialized<UserSchedule>) {
this.courses = schedule.courses.map(c => new Course(c)); this.courses = schedule.courses.map(c => new Course(c));
@@ -17,6 +19,7 @@ export class UserSchedule {
for (const course of this.courses) { for (const course of this.courses) {
this.hours += course.creditHours; this.hours += course.creditHours;
} }
this.updatedAt = schedule.updatedAt;
} }
containsCourse(course: Course): boolean { containsCourse(course: Course): boolean {

View File

@@ -65,7 +65,8 @@ const schedules = [
}), }),
], ],
name: 'Main Schedule', name: 'Main Schedule',
hours: 0, // Add the missing 'hours' property hours: 0,
updatedAt: Date.now(),
}), }),
new UserSchedule({ new UserSchedule({
courses: [ courses: [
@@ -135,7 +136,8 @@ const schedules = [
}), }),
], ],
name: 'Backup #3', name: 'Backup #3',
hours: 0, // Add the missing 'hours' property hours: 0,
updatedAt: Date.now(),
}), }),
]; ];

View File

@@ -47,7 +47,7 @@ export const ClosedCourse: Story = {
...MikeScottCS314Course, ...MikeScottCS314Course,
status: Status.CLOSED, status: Status.CLOSED,
} as Course, } as Course,
activeSchedule: new UserSchedule({ courses: [], name: '', hours: 0 }), activeSchedule: new UserSchedule({ courses: [], name: '', hours: 0, updatedAt: Date.now() }),
}, },
}; };

View File

@@ -57,6 +57,7 @@ export const exampleSchedule: UserSchedule = new UserSchedule({
courses: [exampleCourse], courses: [exampleCourse],
name: 'Example Schedule', name: 'Example Schedule',
hours: 3, hours: 3,
updatedAt: Date.now(),
}); });
export const bevoCourse: Course = new Course({ export const bevoCourse: Course = new Course({
@@ -108,6 +109,7 @@ export const bevoScheule: UserSchedule = new UserSchedule({
courses: [bevoCourse], courses: [bevoCourse],
name: 'Bevo Schedule', name: 'Bevo Schedule',
hours: 3, hours: 3,
updatedAt: Date.now(),
}); });
export const MikeScottCS314Course: Course = new Course({ export const MikeScottCS314Course: Course = new Course({
@@ -160,4 +162,5 @@ export const MikeScottCS314Schedule: UserSchedule = new UserSchedule({
courses: [MikeScottCS314Course], courses: [MikeScottCS314Course],
name: 'Mike Scott CS314 Schedule', name: 'Mike Scott CS314 Schedule',
hours: 3, hours: 3,
updatedAt: Date.now(),
}); });

View File

@@ -6,6 +6,7 @@ import List from '@views/components/common/List/List';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions'; import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions';
import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules'; import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules';
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript'; import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
import clsx from 'clsx'; import clsx from 'clsx';
import React, { useState } from 'react'; import React, { useState } from 'react';
@@ -120,7 +121,7 @@ export default function PopupMain(): JSX.Element {
</div> </div>
<div className='inline-flex items-center self-center gap-1'> <div className='inline-flex items-center self-center gap-1'>
<Text variant='mini' className='text-ut-gray'> <Text variant='mini' className='text-ut-gray'>
DATA UPDATED ON: 12:00 AM 02/01/2024 LAST UPDATED: {getUpdatedAtDateTimeString(activeSchedule.updatedAt)}
</Text> </Text>
<button <button
className='h-4 w-4 bg-transparent p-0 btn' className='h-4 w-4 bg-transparent p-0 btn'

View File

@@ -5,6 +5,8 @@ import Divider from '@views/components/common/Divider/Divider';
import { LogoIcon } from '@views/components/common/LogoIcon'; import { LogoIcon } from '@views/components/common/LogoIcon';
import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses'; import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import useSchedules from '@views/hooks/useSchedules';
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript'; import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
import React from 'react'; import React from 'react';
@@ -27,9 +29,11 @@ const handleOpenOptions = async (): Promise<void> => {
* @returns The JSX element representing the calendar header. * @returns The JSX element representing the calendar header.
*/ */
export default function CalendarHeader(): JSX.Element { export default function CalendarHeader(): JSX.Element {
const [activeSchedule] = useSchedules();
return ( return (
<div className='min-h-79px min-w-672px w-full flex px-0 py-5'> <div className='min-h-79px min-w-672px w-full flex px-0 py-5'>
<div className='flex flex-row gap-20'> <div className='flex flex-row gap-20 w-full'>
<div className='flex gap-10'> <div className='flex gap-10'>
<div className='flex gap-1'> <div className='flex gap-1'>
<Button className='self-center' variant='single' icon={MenuIcon} color='ut-gray' /> <Button className='self-center' variant='single' icon={MenuIcon} color='ut-gray' />
@@ -43,13 +47,17 @@ export default function CalendarHeader(): JSX.Element {
</div> </div>
<Divider className='self-center' size='2.5rem' orientation='vertical' /> <Divider className='self-center' size='2.5rem' orientation='vertical' />
<div className='flex flex-col self-center'> <div className='flex flex-col self-center'>
<ScheduleTotalHoursAndCourses scheduleName='SCHEDULE' totalHours={22} totalCourses={8} /> <ScheduleTotalHoursAndCourses
scheduleName={activeSchedule.name}
totalHours={activeSchedule.hours}
totalCourses={activeSchedule.courses.length}
/>
<Text variant='h4' className='text-xs text-gray font-medium leading-normal'> <Text variant='h4' className='text-xs text-gray font-medium leading-normal'>
DATA UPDATED ON: 12:00 AM 02/01/2024 LAST UPDATED: {getUpdatedAtDateTimeString(activeSchedule.updatedAt)}
</Text> </Text>
</div> </div>
</div> </div>
<div className='flex flex-row items-center justify-end space-x-8'> <div className='flex flex-row items-center justify-end space-x-8 ml-auto'>
<div className='flex flex-row space-x-4'> <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} />

View File

@@ -26,9 +26,9 @@ export default function ScheduleTotalHoursAndCourses({
{`${scheduleName}: `} {`${scheduleName}: `}
</Text> </Text>
<Text variant='h3' as='div' className='flex flex-row items-center gap-2 text-theme-black'> <Text variant='h3' as='div' className='flex flex-row items-center gap-2 text-theme-black'>
{totalHours} HOURS {totalHours} {totalHours === 1 ? 'HOUR' : 'HOURS'}
<Text variant='h4' as='span' className='text-ut-black'> <Text variant='h4' as='span' className='text-ut-black'>
{totalCourses} courses {totalCourses} {totalCourses === 1 ? 'COURSE' : 'COURSES'}
</Text> </Text>
</Text> </Text>
</div> </div>

View File

@@ -61,6 +61,7 @@ export function useFlattenedCourseSchedule(): FlattenedCourseSchedule {
courses: [], courses: [],
name: 'Something may have went wrong', name: 'Something may have went wrong',
hours: 0, hours: 0,
updatedAt: Date.now(),
}), }),
} satisfies FlattenedCourseSchedule; } satisfies FlattenedCourseSchedule;
} }

View File

@@ -0,0 +1,21 @@
/**
*
* @param updatedAt {number} - The time in milliseconds since the epoch when the schedule was last updated.
* @returns {string} - DateTime formatted as HH:MM AM/PM MM/DD/YYYY
*/
export function getUpdatedAtDateTimeString(updatedAt: number): string {
const updatedAtDate = new Date(updatedAt);
const timeFormat = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric',
hour12: true,
}).format(updatedAtDate);
const dateFormat = new Intl.DateTimeFormat('en-US', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
}).format(updatedAtDate);
return `${timeFormat} ${dateFormat}`;
}