Compare commits

...

6 Commits

Author SHA1 Message Date
Casey Charleston
2c4dd36f27 feat: popup misaligned but clickable on calendar grid 2024-02-23 09:52:53 -06:00
Casey Charleston
5daccc8349 fix: injected course popup storybook 2024-02-21 23:51:12 -06:00
Casey Charleston
e4ce051086 Merging hackathon into hackathon 2024-02-21 23:28:32 -06:00
Casey Charleston
51b6799d73 dropdown and calendar schedules storybook 2024-02-17 18:35:17 -06:00
Casey Charleston
141f5cc70e merging 2024-02-17 18:00:06 -06:00
Casey Charleston
6883a0bc8d packages 2024-02-17 17:59:05 -06:00
19 changed files with 2162 additions and 915 deletions

View File

@@ -37,6 +37,7 @@
"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",
"react-window": "^1.8.10",
"sass": "^1.71.1", "sass": "^1.71.1",
"sql.js": "1.10.2", "sql.js": "1.10.2",
"styled-components": "^6.1.8", "styled-components": "^6.1.8",

2687
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,4 +21,4 @@ export class UserSchedule {
containsCourse(course: Course): boolean { containsCourse(course: Course): boolean {
return this.courses.some(c => c.uniqueId === course.uniqueId); return this.courses.some(c => c.uniqueId === course.uniqueId);
} }
} }

View File

@@ -1,7 +1,7 @@
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { UserSchedule } from '@shared/types/UserSchedule'; import { UserSchedule } from '@shared/types/UserSchedule';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
import React from 'react'; import React from 'react';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting'; import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule'; import { CourseSchedule } from 'src/shared/types/CourseSchedule';

View File

@@ -1,8 +1,13 @@
import { Meta, StoryObj } from '@storybook/react'; import { Course, Status } from '@shared/types/Course';
import CalendarGrid from 'src/views/components/calendar/CalendarGrid/CalendarGrid';
import { getCourseColors } from '@shared/util/colors'; import { getCourseColors } from '@shared/util/colors';
import { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule'; import type { Meta, StoryObj } from '@storybook/react';
import { Status } from '@shared/types/Course'; import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
import { Serialized } from 'chrome-extension-toolkit';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import CalendarGrid from 'src/views/components/calendar/CalendarGrid/CalendarGrid';
const meta = { const meta = {
title: 'Components/Calendar/CalendarGrid', title: 'Components/Calendar/CalendarGrid',
@@ -17,6 +22,57 @@ const meta = {
} satisfies Meta<typeof CalendarGrid>; } satisfies Meta<typeof CalendarGrid>;
export default meta; export default meta;
const exampleCourse: Course = new Course({
uniqueId: 50805,
number: '314',
fullName: 'C S 314 DATA STRUCTURES',
courseName: 'DATA STRUCTURES',
department: 'C S',
creditHours: 3,
status: Status.OPEN,
instructors: [
new Instructor({ fullName: 'SCOTT, MICHAEL', firstName: 'MICHAEL', lastName: 'SCOTT', middleInitial: 'D' }),
],
isReserved: true,
description: [
'Second part of a two-part sequence in programming. Introduction to specifications, simple unit testing, and debugging; building and using canonical data structures; algorithm analysis and reasoning techniques such as assertions and invariants.',
'Computer Science 314 and 314H may not both be counted.',
'BVO 311C and 312H may not both be counted.',
'Prerequisite: Computer Science 312 or 312H with a grade of at least C-.',
'May be counted toward the Quantitative Reasoning flag requirement.',
],
schedule: new CourseSchedule({
meetings: [
new CourseMeeting({
days: [DAY_MAP.T, DAY_MAP.TH],
startTime: 480,
endTime: 570,
location: { building: 'UTC', room: '123' },
}),
new CourseMeeting({
days: [DAY_MAP.TH],
startTime: 570,
endTime: 630,
location: { building: 'JES', room: '123' },
}),
],
}),
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
flags: ['Writing', 'Independent Inquiry'],
instructionMode: 'In Person',
semester: {
code: '12345',
year: 2024,
season: 'Spring',
},
});
const exampleSchedule = new UserSchedule({
name: 'Example Schedule',
courses: [exampleCourse],
hours: 3,
} as Serialized<UserSchedule>);
const testData: CalendarGridCourse[] = [ const testData: CalendarGridCourse[] = [
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -30,6 +86,11 @@ const testData: CalendarGridCourse[] = [
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -43,6 +104,11 @@ const testData: CalendarGridCourse[] = [
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -56,6 +122,11 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -69,6 +140,11 @@ const testData: CalendarGridCourse[] = [
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -82,6 +158,11 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -95,6 +176,11 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
{ {
calendarGridPoint: { calendarGridPoint: {
@@ -108,6 +194,11 @@ const testData: CalendarGridCourse[] = [
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
}, },
popupProps: {
course: exampleCourse,
activeSchedule: exampleSchedule,
onClose: () => {},
},
}, },
]; ];

View File

@@ -17,6 +17,7 @@ const meta = {
argTypes: { argTypes: {
dummySchedules: { control: 'object' }, dummySchedules: { control: 'object' },
dummyActiveIndex: { control: 'number' }, dummyActiveIndex: { control: 'number' },
}, },
render: (args: any) => ( render: (args: any) => (
<div> <div>
@@ -140,5 +141,6 @@ export const Default: Story = {
args: { args: {
dummySchedules: schedules, dummySchedules: schedules,
dummyActiveIndex: 0, dummyActiveIndex: 0,
}, },
}; };

View File

@@ -1,9 +1,10 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import type { Serialized } from 'chrome-extension-toolkit';
import { Course, Status } from 'src/shared/types/Course'; import { Course, Status } from 'src/shared/types/Course';
import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting'; import { CourseMeeting, DAY_MAP } from 'src/shared/types/CourseMeeting';
import { CourseSchedule } from 'src/shared/types/CourseSchedule'; import { CourseSchedule } from 'src/shared/types/CourseSchedule';
import Instructor from 'src/shared/types/Instructor'; import Instructor from 'src/shared/types/Instructor';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import CourseCatalogInjectedPopup from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup'; import CourseCatalogInjectedPopup from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
const exampleCourse: Course = new Course({ const exampleCourse: Course = new Course({
@@ -51,11 +52,19 @@ const exampleCourse: Course = new Course({
}, },
}); });
const exampleSchedule = new UserSchedule({
name: 'Example Schedule',
courses: [exampleCourse],
hours: 3,
} as Serialized<UserSchedule>);
const meta: Meta<typeof CourseCatalogInjectedPopup> = { const meta: Meta<typeof CourseCatalogInjectedPopup> = {
title: 'Components/Injected/CourseCatalogInjectedPopup', title: 'Components/Injected/CourseCatalogInjectedPopup',
component: CourseCatalogInjectedPopup, component: CourseCatalogInjectedPopup,
argTypes: { argTypes: {
onClose: { action: 'onClose' }, onClose: { action: 'onClose' },
activeSchedule: { control: 'object' },
course: { control: 'object' },
}, },
}; };
@@ -64,6 +73,7 @@ type Story = StoryObj<typeof CourseCatalogInjectedPopup>;
export const Default: Story = { export const Default: Story = {
args: { args: {
activeSchedule: exampleSchedule,
course: exampleCourse, course: exampleCourse,
}, },
}; };

View File

@@ -1,97 +1,154 @@
import React from 'react';
import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon
import { StatusIcon } from '@shared/util/icons'; import { StatusIcon } from '@shared/util/icons';
import React from 'react';
// import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon
import { Status } from 'src/shared/types/Course'; import { Status } from 'src/shared/types/Course';
import { test_colors } from 'src/stories/components/PopupCourseBlock.stories'; import { test_colors } from 'src/stories/components/PopupCourseBlock.stories';
import logoImage from '../../assets/logo.png'; // Adjust the path as necessary
import useSchedules from '../hooks/useSchedules';
import { openTabFromContentScript } from '../lib/openNewTabFromContentScript';
import Divider from './common/Divider/Divider';
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot'; import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
import List from './common/List/List'; // Ensure this path is correctly pointing to your List component
import PopupCourseBlock from './common/PopupCourseBlock/PopupCourseBlock'; import PopupCourseBlock from './common/PopupCourseBlock/PopupCourseBlock';
import Text from './common/Text/Text'; import Text from './common/Text/Text';
import Divider from './common/Divider/Divider';
import logoImage from '../../assets/logo.png'; // Adjust the path as necessary
import List from './common/List/List'; // Ensure this path is correctly pointing to your List component
import useSchedules from '../hooks/useSchedules';
import { handleOpenCalendar } from './injected/CourseCatalogInjectedPopup/HeadingAndActions'; import { handleOpenCalendar } from './injected/CourseCatalogInjectedPopup/HeadingAndActions';
import { openTabFromContentScript } from '../lib/openNewTabFromContentScript';
export default function PopupMain() { export default function PopupMain() {
const [activeSchedule] = useSchedules(); const [activeSchedule] = useSchedules();
const draggableElements = activeSchedule?.courses.map((course, i) => (
const draggableElements = activeSchedule?.courses.map((course, i) => ( <PopupCourseBlock key={course.uniqueId} course={course} colors={test_colors[i]} />
<PopupCourseBlock
key={course.uniqueId}
course={course}
colors={test_colors[i]}
/>
)); ));
const handleOpenOptions = async () => { // Not sure if it's bad practice to export this const handleOpenOptions = async () => {
// Not sure if it's bad practice to export this
const url = chrome.runtime.getURL('/src/pages/options/index.html'); const url = chrome.runtime.getURL('/src/pages/options/index.html');
await openTabFromContentScript(url); await openTabFromContentScript(url);
}; };
return ( return (
<ExtensionRoot> <ExtensionRoot>
<div className="mx-auto max-w-sm rounded-lg bg-white p-4 shadow-md"> <div className='mx-auto max-w-sm rounded-lg bg-white p-4 shadow-md'>
<div className="mb-2 flex items-center justify-between bg-white"> <div className='mb-2 flex items-center justify-between bg-white'>
<div className="flex items-center"> <div className='flex items-center'>
<img src={logoImage} alt="Logo" style={{ width: '40px', height: '40px', marginRight: '8px' }} /> <img src={logoImage} alt='Logo' style={{ width: '40px', height: '40px', marginRight: '8px' }} />
<div> <div>
<Text as="div" variant="h1-course" style={{ color: '#bf5700', fontSize: '1.3rem' }}>UT Registration</Text> <Text as='div' variant='h1-course' style={{ color: '#bf5700', fontSize: '1.3rem' }}>
<Text as="div" variant="h1-course" style={{ color: '#f8971f', fontSize: '1.3rem' }}>Plus</Text> UT Registration
</Text>
<Text as='div' variant='h1-course' style={{ color: '#f8971f', fontSize: '1.3rem' }}>
Plus
</Text>
</div> </div>
</div> </div>
<div className="flex items-center"> <div className='flex items-center'>
<button style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }} onClick={handleOpenCalendar}> <button
<FaCalendarAlt color="white" /> style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }}
onClick={handleOpenCalendar}
>
{/* <FaCalendarAlt color='white' /> */}
</button> </button>
<button style={{ backgroundColor: 'white', marginLeft: '10px', borderRadius: '8px', padding: '8px', boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }} <button
onClick = {handleOpenOptions}> style={{
<FaCog color="#C05621" /> backgroundColor: 'white',
marginLeft: '10px',
borderRadius: '8px',
padding: '8px',
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
}}
onClick={handleOpenOptions}
>
{/* <FaCog color='#C05621' /> */}
</button> </button>
</div> </div>
</div> </div>
<Divider color="#E2E8F0" type="solid" style={{ margin: '1rem 0' }} /> <Divider color='#E2E8F0' type='solid' style={{ margin: '1rem 0' }} />
<div className="mb-4 rounded-lg bg-white p-2 text-left shadow-inner" style={{ backgroundColor: 'white', border: '1px solid #FBD38D', borderRadius: '0.5rem' }}> <div
<Text as="div" variant="h2-course" style={{ color: '#DD6B20', fontSize: '1.2rem' }}>MAIN SCHEDULE:</Text> className='mb-4 rounded-lg bg-white p-2 text-left shadow-inner'
style={{ backgroundColor: 'white', border: '1px solid #FBD38D', borderRadius: '0.5rem' }}
>
<Text as='div' variant='h2-course' style={{ color: '#DD6B20', fontSize: '1.2rem' }}>
MAIN SCHEDULE:
</Text>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'start', color: '#333f48' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'start', color: '#333f48' }}>
<Text as="div" variant="h1" style={{ fontSize: '1.2rem', fontWeight: 'bold', marginRight: '0.5rem' }}>22 HOURS</Text> <Text
<Text as="div" variant="h2-course" style={{ fontSize: '1.2rem' }}>8 Courses</Text> as='div'
variant='h1'
style={{ fontSize: '1.2rem', fontWeight: 'bold', marginRight: '0.5rem' }}
>
22 HOURS
</Text>
<Text as='div' variant='h2-course' style={{ fontSize: '1.2rem' }}>
8 Courses
</Text>
</div> </div>
</div> </div>
{/* Integrate the List component here */} {/* Integrate the List component here */}
{activeSchedule ? <List {activeSchedule ? (
draggableElements={draggableElements} <List
itemHeight={100} // Adjust based on your content size draggableElements={draggableElements}
listHeight={500} // Adjust based on total height you want for the list itemHeight={100} // Adjust based on your content size
listWidth={350} // Adjust based on your layout/design listHeight={500} // Adjust based on total height you want for the list
gap={12} // Spacing between items listWidth={350} // Adjust based on your layout/design
/> : null} gap={12} // Spacing between items
<div className="mt-4 flex justify-between border-t border-gray-200 p-4 text-xs"> />
<div className="flex items-center"> ) : null}
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}> <div className='mt-4 flex justify-between border-t border-gray-200 p-4 text-xs'>
<StatusIcon status={Status.WAITLISTED} className="h-5 w-5 text-white" /> <div className='flex items-center'>
<div
style={{
backgroundColor: '#6B7280',
padding: '1px',
borderRadius: '4px',
marginRight: '3px',
marginLeft: '8px',
}}
>
<StatusIcon status={Status.WAITLISTED} className='h-5 w-5 text-white' />
</div> </div>
<Text as="span" variant="mini">WAITLISTED</Text> <Text as='span' variant='mini'>
WAITLISTED
</Text>
</div> </div>
<div className="flex items-center"> <div className='flex items-center'>
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}> <div
<StatusIcon status={Status.CLOSED} className="h-5 w-5 text-white" /> style={{
backgroundColor: '#6B7280',
padding: '1px',
borderRadius: '4px',
marginRight: '3px',
marginLeft: '8px',
}}
>
<StatusIcon status={Status.CLOSED} className='h-5 w-5 text-white' />
</div> </div>
<Text as="span" variant="mini">CLOSED</Text> <Text as='span' variant='mini'>
CLOSED
</Text>
</div> </div>
<div className="flex items-center"> <div className='flex items-center'>
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}> <div
<StatusIcon status={Status.CANCELLED} className="h-5 w-5 text-white" /> style={{
backgroundColor: '#6B7280',
padding: '1px',
borderRadius: '4px',
marginRight: '3px',
marginLeft: '8px',
}}
>
<StatusIcon status={Status.CANCELLED} className='h-5 w-5 text-white' />
</div> </div>
<Text as="span" variant="mini">CANCELLED</Text> <Text as='span' variant='mini'>
CANCELLED
</Text>
</div> </div>
</div> </div>
<div className="mt-2 text-center text-xs"> <div className='mt-2 text-center text-xs'>
<div className="inline-flex items-center justify-center"> <div className='inline-flex items-center justify-center'>
<Text as="div" variant="mini">DATA UPDATED ON: 12:00 AM 02/01/2024</Text> <Text as='div' variant='mini'>
<FaRedo className="ml-2 h-4 w-4 text-gray-600" /> DATA UPDATED ON: 12:00 AM 02/01/2024
</Text>
{/* <FaRedo className='ml-2 h-4 w-4 text-gray-600' /> */}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,13 @@
import { Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import { CourseColors, pickFontColor } from 'src/shared/util/colors'; import type { CourseColors } from 'src/shared/util/colors';
import { pickFontColor } from 'src/shared/util/colors';
import ClosedIcon from '~icons/material-symbols/lock'; import ClosedIcon from '~icons/material-symbols/lock';
import WaitlistIcon from '~icons/material-symbols/timelapse'; import WaitlistIcon from '~icons/material-symbols/timelapse';
import CancelledIcon from '~icons/material-symbols/warning'; import CancelledIcon from '~icons/material-symbols/warning';
import Text from '../../common/Text/Text'; import Text from '../../common/Text/Text';
export interface CalendarCourseCellProps { export interface CalendarCourseCellProps {
@@ -13,6 +16,7 @@ export interface CalendarCourseCellProps {
status: Status; status: Status;
colors: CourseColors; colors: CourseColors;
className?: string; className?: string;
popup?: any;
} }
const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
@@ -21,7 +25,9 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
status, status,
colors, colors,
className, className,
popup,
}: CalendarCourseCellProps) => { }: CalendarCourseCellProps) => {
const [showPopup, setShowPopup] = React.useState(false);
let rightIcon: React.ReactNode | null = null; let rightIcon: React.ReactNode | null = null;
if (status === Status.WAITLISTED) { if (status === Status.WAITLISTED) {
rightIcon = <WaitlistIcon className='h-5 w-5' />; rightIcon = <WaitlistIcon className='h-5 w-5' />;
@@ -31,15 +37,26 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
rightIcon = <CancelledIcon className='h-5 w-5' />; rightIcon = <CancelledIcon className='h-5 w-5' />;
} }
// popup.onClose = () => setShowPopup(false);
const handleClick = () => {
setShowPopup(true);
};
// whiteText based on secondaryColor // whiteText based on secondaryColor
const fontColor = pickFontColor(colors.primaryColor); const fontColor = pickFontColor(colors.primaryColor);
return ( return (
<div <div
className={clsx('h-full w-full flex justify-center rounded p-2 overflow-x-hidden', fontColor, className)} className={clsx(
'h-full w-full flex justify-center rounded p-2 overflow-x-hidden cursor-pointer',
fontColor,
className
)}
style={{ style={{
backgroundColor: colors.primaryColor, backgroundColor: colors.primaryColor,
}} }}
onClick={handleClick}
> >
<div className='flex flex-1 flex-col gap-1'> <div className='flex flex-1 flex-col gap-1'>
<Text <Text
@@ -66,6 +83,7 @@ const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
{rightIcon} {rightIcon}
</div> </div>
)} )}
<div>{showPopup ? popup : null}</div>
</div> </div>
); );
}; };

View File

@@ -1,14 +1,17 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useEffect, useRef, useState } from 'react';
// import html2canvas from 'html2canvas'; // 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 CourseCatalogInjectedPopup from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
import type { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule';
import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell';
/* 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';
*/ */
import CalendarCell from '../CalendarGridCell/CalendarGridCell'; import CalendarCell from '../CalendarGridCell/CalendarGridCell';
import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell';
import styles from './CalendarGrid.module.scss'; import styles from './CalendarGrid.module.scss';
/* 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);
const grid = []; const grid = [];
@@ -119,7 +122,7 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Pr
</div> </div>
))} ))}
{grid.map((row, rowIndex) => row)} {grid.map((row, rowIndex) => row)}
{courseCells ? <AccountForCourseConflicts courseCells={courseCells}/> : null} {courseCells ? <AccountForCourseConflicts courseCells={courseCells} /> : null}
{/* courseCells.map((block: CalendarGridCourse) => ( {/* courseCells.map((block: CalendarGridCourse) => (
<div <div
key={`${block}`} key={`${block}`}
@@ -203,6 +206,7 @@ function AccountForCourseConflicts({ courseCells }: AccountForCourseConflictsPro
timeAndLocation={block.componentProps.timeAndLocation} timeAndLocation={block.componentProps.timeAndLocation}
status={block.componentProps.status} status={block.componentProps.status}
colors={block.componentProps.colors} colors={block.componentProps.colors}
popup={<CourseCatalogInjectedPopup course={block.popupProps.course} activeSchedule={block.popupProps.activeSchedule} onClose={block.popupProps.onClose} />}
/> />
</div> </div>
)); ));

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import styles from './CalendarGridCell.module.scss'; import styles from './CalendarGridCell.module.scss';
interface Props { interface Props {

View File

@@ -1,5 +1,6 @@
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'; import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import React, { ReactElement, useCallback, useState } from 'react'; import type { ReactElement } from 'react';
import React, { useCallback, useState } from 'react';
import { areEqual } from 'react-window'; import { areEqual } from 'react-window';
/* /*

View File

@@ -1,5 +1,7 @@
import clsx from 'clsx'; import clsx from 'clsx';
import React, { PropsWithChildren, useCallback } from 'react'; import type { PropsWithChildren } from 'react';
import React, { useCallback } from 'react';
import styles from './Popup.module.scss'; import styles from './Popup.module.scss';
interface Props { interface Props {
@@ -19,8 +21,6 @@ export default function Popup({ onClose, children, className, style, testId, ove
const containerRef = React.useRef<HTMLDivElement>(null); const containerRef = React.useRef<HTMLDivElement>(null);
const bodyRef = React.useRef<HTMLDivElement>(null); const bodyRef = React.useRef<HTMLDivElement>(null);
const handleClickOutside = useCallback( const handleClickOutside = useCallback(
(event: MouseEvent) => { (event: MouseEvent) => {
if (!bodyRef.current) return; if (!bodyRef.current) return;

View File

@@ -1,12 +1,14 @@
import Popup from '@views/components/common/Popup/Popup'; import Popup from '@views/components/common/Popup/Popup';
import React from 'react'; import React from 'react';
import { Course } from 'src/shared/types/Course'; import type { Course } from 'src/shared/types/Course';
import { UserSchedule } from 'src/shared/types/UserSchedule'; import type { UserSchedule } from 'src/shared/types/UserSchedule';
import Description from './Description'; import Description from './Description';
import GradeDistribution from './GradeDistribution'; import GradeDistribution from './GradeDistribution';
import HeadingAndActions from './HeadingAndActions'; import HeadingAndActions from './HeadingAndActions';
interface CourseCatalogInjectedPopupProps { export interface CourseCatalogInjectedPopupProps {
course: Course; course: Course;
activeSchedule?: UserSchedule; activeSchedule?: UserSchedule;
onClose: () => void; onClose: () => void;

View File

@@ -1,5 +1,6 @@
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
import Text from '../../common/Text/Text'; import Text from '../../common/Text/Text';
interface DescriptionProps { interface DescriptionProps {

View File

@@ -3,8 +3,8 @@ import Text from '@views/components/common/Text/Text';
import Highcharts from 'highcharts'; import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official'; import HighchartsReact from 'highcharts-react-official';
import React from 'react'; import React from 'react';
import { Course } from 'src/shared/types/Course'; import type { Course } from 'src/shared/types/Course';
import { Distribution, LetterGrade } from 'src/shared/types/Distribution'; import type { Distribution, LetterGrade } from 'src/shared/types/Distribution';
import { colors } from 'src/shared/util/themeColors'; import { colors } from 'src/shared/util/themeColors';
import { import {
NoDataError, NoDataError,

View File

@@ -1,20 +1,21 @@
import React, { useState } from 'react';
import { Button } from '@views/components/common/Button/Button'; import { Button } from '@views/components/common/Button/Button';
import { Chip, flagMap } from '@views/components/common/Chip/Chip'; import { Chip, flagMap } from '@views/components/common/Chip/Chip';
import Divider from '@views/components/common/Divider/Divider'; import Divider from '@views/components/common/Divider/Divider';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import React, { useState } from 'react';
import addCourse from 'src/pages/background/lib/addCourse'; import addCourse from 'src/pages/background/lib/addCourse';
import removeCourse from 'src/pages/background/lib/removeCourse'; import removeCourse from 'src/pages/background/lib/removeCourse';
import type { Course } from 'src/shared/types/Course';
import type { UserSchedule } from 'src/shared/types/UserSchedule';
import { openTabFromContentScript } from 'src/views/lib/openNewTabFromContentScript'; import { openTabFromContentScript } from 'src/views/lib/openNewTabFromContentScript';
import { Course } from 'src/shared/types/Course';
import { UserSchedule } from 'src/shared/types/UserSchedule';
import Add from '~icons/material-symbols/add'; import Add from '~icons/material-symbols/add';
import Remove from '~icons/material-symbols/remove';
import CalendarMonth from '~icons/material-symbols/calendar-month'; import CalendarMonth from '~icons/material-symbols/calendar-month';
import CloseIcon from '~icons/material-symbols/close'; import CloseIcon from '~icons/material-symbols/close';
import Copy from '~icons/material-symbols/content-copy'; import Copy from '~icons/material-symbols/content-copy';
import Description from '~icons/material-symbols/description'; import Description from '~icons/material-symbols/description';
import Mood from '~icons/material-symbols/mood'; import Mood from '~icons/material-symbols/mood';
import Remove from '~icons/material-symbols/remove';
import Reviews from '~icons/material-symbols/reviews'; import Reviews from '~icons/material-symbols/reviews';
interface HeadingAndActionProps { interface HeadingAndActionProps {
@@ -26,7 +27,8 @@ interface HeadingAndActionProps {
onClose: () => void; onClose: () => void;
} }
export const handleOpenCalendar = async () => { // Not sure if it's bad practice to export this export const handleOpenCalendar = async () => {
// Not sure if it's bad practice to export this
const url = chrome.runtime.getURL('calendar.html'); const url = chrome.runtime.getURL('calendar.html');
await openTabFromContentScript(url); await openTabFromContentScript(url);
}; };
@@ -41,7 +43,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course; const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course;
const [courseAdded, setCourseAdded] = useState<boolean>( const [courseAdded, setCourseAdded] = useState<boolean>(
activeSchedule.courses.some(course => course.uniqueId === uniqueId) activeSchedule.courses.some(course => course.uniqueId === uniqueId)
); );
const instructorString = instructors const instructorString = instructors
.map(instructor => { .map(instructor => {
@@ -74,8 +76,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
const handleAddOrRemoveCourse = async () => { const handleAddOrRemoveCourse = async () => {
if (!courseAdded) { if (!courseAdded) {
await addCourse(activeSchedule.name, course); await addCourse(activeSchedule.name, course);
} } else {
else {
await removeCourse(activeSchedule.name, course); await removeCourse(activeSchedule.name, course);
} }
setCourseAdded(!courseAdded); setCourseAdded(!courseAdded);
@@ -137,7 +138,12 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
<Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}> <Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}>
Past Syllabi Past Syllabi
</Button> </Button>
<Button variant='filled' color={!courseAdded ? 'ut-green' : 'ut-red'} icon={!courseAdded ? Add : Remove} onClick={handleAddOrRemoveCourse}> <Button
variant='filled'
color={!courseAdded ? 'ut-green' : 'ut-red'}
icon={!courseAdded ? Add : Remove}
onClick={handleAddOrRemoveCourse}
>
{!courseAdded ? 'Add Course' : 'Remove Course'} {!courseAdded ? 'Add Course' : 'Remove Course'}
</Button> </Button>
</div> </div>

View File

@@ -1,6 +1,7 @@
import { Course } from '@shared/types/Course'; import type { Course } from '@shared/types/Course';
import { UserSchedule } from '@shared/types/UserSchedule'; import type { UserSchedule } from '@shared/types/UserSchedule';
import React from 'react'; import React from 'react';
import Popup from '../../common/Popup/Popup'; import Popup from '../../common/Popup/Popup';
import CourseDescription from './CourseDescription/CourseDescription'; import CourseDescription from './CourseDescription/CourseDescription';
import CourseHeader from './CourseHeader/CourseHeader'; import CourseHeader from './CourseHeader/CourseHeader';

View File

@@ -1,4 +1,5 @@
import type { CalendarCourseCellProps } from 'src/views/components/calendar/CalendarCourseCell/CalendarCourseCell'; import type { CalendarCourseCellProps } from 'src/views/components/calendar/CalendarCourseCell/CalendarCourseCell';
import type { CourseCatalogInjectedPopupProps } from 'src/views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
import useSchedules from './useSchedules'; import useSchedules from './useSchedules';
@@ -25,6 +26,7 @@ export interface CalendarGridCourse {
gridColumnStart?: number; gridColumnStart?: number;
gridColumnEnd?: number; gridColumnEnd?: number;
totalColumns?: number; totalColumns?: number;
popupProps: CourseCatalogInjectedPopupProps;
} }
export const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30); export const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30);
@@ -58,6 +60,7 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] {
}, },
componentProps: { componentProps: {
courseDeptAndInstr, courseDeptAndInstr,
timeAndLocation: 'Asynchronous',
status, status,
colors: { colors: {
// TODO: figure out colors - these are defaults // TODO: figure out colors - these are defaults
@@ -65,6 +68,11 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] {
secondaryColor: 'ut-gray', secondaryColor: 'ut-gray',
}, },
}, },
popupProps: {
course,
activeSchedule,
onClose: () => {},
},
}, },
]; ];
} }
@@ -91,6 +99,11 @@ export function useFlattenedCourseSchedule(): CalendarGridCourse[] {
secondaryColor: 'ut-orange', secondaryColor: 'ut-orange',
}, },
}, },
popupProps: {
course,
activeSchedule,
onClose: () => {}, // Add onClose property here
},
})); }));
}); });
}) })