chore: lint-format-docs-tests-bugfixes (#105)
* docs: add jsdoc * feat: change enums to as const objects * chore(test): add themeColors.test.ts * fix: fix tests and bugs with strings.ts util * fix: path alias imports and tsconfig file bug * fix: remove --max-warnings 0
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
import { Course, ScrapedRow } from '@shared/types/Course';
|
||||
import type { Course, ScrapedRow } from '@shared/types/Course';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useKeyPress } from '../hooks/useKeyPress';
|
||||
import useSchedules from '../hooks/useSchedules';
|
||||
import { CourseCatalogScraper } from '../lib/CourseCatalogScraper';
|
||||
import getCourseTableRows from '../lib/getCourseTableRows';
|
||||
import { SiteSupport } from '../lib/getSiteSupport';
|
||||
import type { SiteSupport } from '../lib/getSiteSupport';
|
||||
import { populateSearchInputs } from '../lib/populateSearchInputs';
|
||||
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
|
||||
import AutoLoad from './injected/AutoLoad/AutoLoad';
|
||||
import CourseCatalogInjectedPopup from './injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
|
||||
import CoursePopup from './injected/CoursePopupOld/CoursePopup';
|
||||
import RecruitmentBanner from './injected/RecruitmentBanner/RecruitmentBanner';
|
||||
import TableHead from './injected/TableHead';
|
||||
import TableRow from './injected/TableRow/TableRow';
|
||||
import TableSubheading from './injected/TableSubheading/TableSubheading';
|
||||
import CourseCatalogInjectedPopup from './injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
|
||||
|
||||
interface Props {
|
||||
support: SiteSupport.COURSE_CATALOG_DETAILS | SiteSupport.COURSE_CATALOG_LIST;
|
||||
|
||||
@@ -1,97 +1,158 @@
|
||||
import logoImage from '@assets/logo.png'; // Adjust the path as necessary
|
||||
import { Status } from '@shared/types/Course';
|
||||
import { StatusIcon } from '@shared/util/icons';
|
||||
import Divider from '@views/components/common/Divider/Divider';
|
||||
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||
import List from '@views/components/common/List/List'; // Ensure this path is correctly pointing to your List component
|
||||
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions';
|
||||
import useSchedules from '@views/hooks/useSchedules';
|
||||
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
|
||||
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 { Status } from 'src/shared/types/Course';
|
||||
import { test_colors } from 'src/stories/components/PopupCourseBlock.stories';
|
||||
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
|
||||
import PopupCourseBlock from './common/PopupCourseBlock/PopupCourseBlock';
|
||||
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 { openTabFromContentScript } from '../lib/openNewTabFromContentScript';
|
||||
|
||||
import { TestColors } from 'src/stories/components/PopupCourseBlock.stories';
|
||||
|
||||
/**
|
||||
* Renders the main popup component.
|
||||
* This component displays the main schedule, courses, and options buttons.
|
||||
*/
|
||||
export default function PopupMain() {
|
||||
const [activeSchedule] = useSchedules();
|
||||
const [activeSchedule] = useSchedules();
|
||||
|
||||
|
||||
const draggableElements = activeSchedule?.courses.map((course, i) => (
|
||||
<PopupCourseBlock
|
||||
key={course.uniqueId}
|
||||
course={course}
|
||||
colors={test_colors[i]}
|
||||
/>
|
||||
const draggableElements = activeSchedule?.courses.map((course, i) => (
|
||||
<PopupCourseBlock key={course.uniqueId} course={course} colors={TestColors[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');
|
||||
await openTabFromContentScript(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<ExtensionRoot>
|
||||
<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="flex items-center">
|
||||
<img src={logoImage} alt="Logo" style={{ width: '40px', height: '40px', marginRight: '8px' }} />
|
||||
<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='flex items-center'>
|
||||
<img src={logoImage} alt='Logo' style={{ width: '40px', height: '40px', marginRight: '8px' }} />
|
||||
<div>
|
||||
<Text as="div" variant="h1-course" style={{ color: '#bf5700', fontSize: '1.3rem' }}>UT Registration</Text>
|
||||
<Text as="div" variant="h1-course" style={{ color: '#f8971f', fontSize: '1.3rem' }}>Plus</Text>
|
||||
<Text as='div' variant='h1-course' style={{ color: '#bf5700', fontSize: '1.3rem' }}>
|
||||
UT Registration
|
||||
</Text>
|
||||
<Text as='div' variant='h1-course' style={{ color: '#f8971f', fontSize: '1.3rem' }}>
|
||||
Plus
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<button style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }} onClick={handleOpenCalendar}>
|
||||
<FaCalendarAlt color="white" />
|
||||
<div className='flex items-center'>
|
||||
<button
|
||||
style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }}
|
||||
onClick={handleOpenCalendar}
|
||||
>
|
||||
<FaCalendarAlt color='white' />
|
||||
</button>
|
||||
<button style={{ backgroundColor: 'white', marginLeft: '10px', borderRadius: '8px', padding: '8px', boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }}
|
||||
onClick = {handleOpenOptions}>
|
||||
<FaCog color="#C05621" />
|
||||
<button
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
marginLeft: '10px',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
|
||||
}}
|
||||
onClick={handleOpenOptions}
|
||||
>
|
||||
<FaCog color='#C05621' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<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' }}>
|
||||
<Text as="div" variant="h2-course" style={{ color: '#DD6B20', fontSize: '1.2rem' }}>MAIN SCHEDULE:</Text>
|
||||
<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' }}
|
||||
>
|
||||
<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' }}>
|
||||
<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>
|
||||
<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>
|
||||
{/* Integrate the List component here */}
|
||||
{activeSchedule ? <List
|
||||
draggableElements={draggableElements}
|
||||
itemHeight={100} // Adjust based on your content size
|
||||
listHeight={500} // Adjust based on total height you want for the list
|
||||
listWidth={350} // Adjust based on your layout/design
|
||||
gap={12} // Spacing between items
|
||||
/> : null}
|
||||
<div className="mt-4 flex justify-between border-t border-gray-200 p-4 text-xs">
|
||||
<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" />
|
||||
{activeSchedule ? (
|
||||
<List
|
||||
draggableElements={draggableElements}
|
||||
itemHeight={100} // Adjust based on your content size
|
||||
listHeight={500} // Adjust based on total height you want for the list
|
||||
listWidth={350} // Adjust based on your layout/design
|
||||
gap={12} // Spacing between items
|
||||
/>
|
||||
) : null}
|
||||
<div className='mt-4 flex justify-between border-t border-gray-200 p-4 text-xs'>
|
||||
<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>
|
||||
<Text as="span" variant="mini">WAITLISTED</Text>
|
||||
<Text as='span' variant='mini'>
|
||||
WAITLISTED
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
||||
<StatusIcon status={Status.CLOSED} 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.CLOSED} className='h-5 w-5 text-white' />
|
||||
</div>
|
||||
<Text as="span" variant="mini">CLOSED</Text>
|
||||
<Text as='span' variant='mini'>
|
||||
CLOSED
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
||||
<StatusIcon status={Status.CANCELLED} 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.CANCELLED} className='h-5 w-5 text-white' />
|
||||
</div>
|
||||
<Text as="span" variant="mini">CANCELLED</Text>
|
||||
<Text as='span' variant='mini'>
|
||||
CANCELLED
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 text-center text-xs">
|
||||
<div className="inline-flex items-center justify-center">
|
||||
<Text as="div" variant="mini">DATA UPDATED ON: 12:00 AM 02/01/2024</Text>
|
||||
<FaRedo className="ml-2 h-4 w-4 text-gray-600" />
|
||||
<div className='mt-2 text-center text-xs'>
|
||||
<div className='inline-flex items-center justify-center'>
|
||||
<Text as='div' variant='mini'>
|
||||
DATA UPDATED ON: 12:00 AM 02/01/2024
|
||||
</Text>
|
||||
<FaRedo className='ml-2 h-4 w-4 text-gray-600' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import CalendarHeader from 'src/views/components/calendar/CalendarHeader/CalenderHeader';
|
||||
|
||||
import { CalendarBottomBar } from '../CalendarBottomBar/CalendarBottomBar';
|
||||
import CalendarGrid from '../CalendarGrid/CalendarGrid';
|
||||
import { CalendarSchedules } from '../CalendarSchedules/CalendarSchedules';
|
||||
import ImportantLinks from '../ImportantLinks';
|
||||
import CalendarGrid from '../CalendarGrid/CalendarGrid';
|
||||
|
||||
export const flags = ['WR', 'QR', 'GC', 'CD', 'E', 'II'];
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Text from '../../common/Text/Text';
|
||||
import CalendarCourseBlock, { CalendarCourseCellProps } from '../CalendarCourseCell/CalendarCourseCell';
|
||||
import { Button } from '../../common/Button/Button';
|
||||
import ImageIcon from '~icons/material-symbols/image';
|
||||
import React from 'react';
|
||||
|
||||
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
|
||||
import ImageIcon from '~icons/material-symbols/image';
|
||||
|
||||
import { Button } from '../../common/Button/Button';
|
||||
import Text from '../../common/Text/Text';
|
||||
import type { CalendarCourseCellProps } from '../CalendarCourseCell/CalendarCourseCell';
|
||||
import CalendarCourseBlock from '../CalendarCourseCell/CalendarCourseCell';
|
||||
|
||||
type CalendarBottomBarProps = {
|
||||
courses?: CalendarCourseCellProps[];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { CourseMeeting } from 'src/shared/types/CourseMeeting';
|
||||
import type { Course } from 'src/shared/types/Course';
|
||||
import type { CourseMeeting } from 'src/shared/types/CourseMeeting';
|
||||
|
||||
import styles from './CalendarCourseMeeting.module.scss';
|
||||
|
||||
/**
|
||||
@@ -26,6 +27,8 @@ export interface CalendarCourseMeetingProps {
|
||||
const CalendarCourseMeeting: React.FC<CalendarCourseMeetingProps> = ({
|
||||
course,
|
||||
meetingIdx,
|
||||
color,
|
||||
rightIcon,
|
||||
}: CalendarCourseMeetingProps) => {
|
||||
let meeting: CourseMeeting | null = meetingIdx !== undefined ? course.schedule.meetings[meetingIdx] : null;
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Status } from '@shared/types/Course';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import clsx from 'clsx';
|
||||
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 WaitlistIcon from '~icons/material-symbols/timelapse';
|
||||
import CancelledIcon from '~icons/material-symbols/warning';
|
||||
import Text from '../../common/Text/Text';
|
||||
|
||||
/**
|
||||
* Props for the CalendarCourseCell component.
|
||||
*/
|
||||
export interface CalendarCourseCellProps {
|
||||
courseDeptAndInstr: string;
|
||||
timeAndLocation?: string;
|
||||
@@ -15,6 +20,18 @@ export interface CalendarCourseCellProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a cell for a calendar course.
|
||||
*
|
||||
* @component
|
||||
* @param {CalendarCourseCellProps} props - The component props.
|
||||
* @param {string} props.courseDeptAndInstr - The course department and instructor.
|
||||
* @param {string} props.timeAndLocation - The time and location of the course.
|
||||
* @param {Status} props.status - The status of the course.
|
||||
* @param {Colors} props.colors - The colors for styling the cell.
|
||||
* @param {string} props.className - Additional CSS class name for the cell.
|
||||
* @returns {JSX.Element} The rendered component.
|
||||
*/
|
||||
const CalendarCourseCell: React.FC<CalendarCourseCellProps> = ({
|
||||
courseDeptAndInstr,
|
||||
timeAndLocation,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
// import html2canvas from 'html2canvas';
|
||||
import { DAY_MAP } from 'src/shared/types/CourseMeeting';
|
||||
import { CalendarGridCourse } from 'src/views/hooks/useFlattenedCourseSchedule';
|
||||
|
||||
import CalendarCourseCell from '../CalendarCourseCell/CalendarCourseCell';
|
||||
/* 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';
|
||||
|
||||
/* const daysOfWeek = Object.keys(DAY_MAP).filter(key => !['S', 'SU'].includes(key));
|
||||
@@ -107,7 +108,7 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Pr
|
||||
newGrid.push(row);
|
||||
}
|
||||
setGrid(newGrid);
|
||||
}, []);
|
||||
}, [hoursOfDay]);
|
||||
|
||||
return (
|
||||
<div className={styles.calendarGrid}>
|
||||
@@ -119,7 +120,7 @@ function CalendarGrid({ courseCells, saturdayClass }: React.PropsWithChildren<Pr
|
||||
</div>
|
||||
))}
|
||||
{grid.map((row, rowIndex) => row)}
|
||||
{courseCells ? <AccountForCourseConflicts courseCells={courseCells}/> : null}
|
||||
{courseCells ? <AccountForCourseConflicts courseCells={courseCells} /> : null}
|
||||
{/* courseCells.map((block: CalendarGridCourse) => (
|
||||
<div
|
||||
key={`${block}`}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import styles from './CalendarGridCell.module.scss';
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import React from 'react';
|
||||
import { Status } from '@shared/types/Course';
|
||||
import Divider from '../../common/Divider/Divider';
|
||||
import { Button } from '../../common/Button/Button';
|
||||
import Text from '../../common/Text/Text';
|
||||
import MenuIcon from '~icons/material-symbols/menu';
|
||||
import UndoIcon from '~icons/material-symbols/undo';
|
||||
import RedoIcon from '~icons/material-symbols/redo';
|
||||
import SettingsIcon from '~icons/material-symbols/settings';
|
||||
import ScheduleTotalHoursAndCourses from '../../common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses';
|
||||
import CourseStatus from '../../common/CourseStatus/CourseStatus';
|
||||
import { Button } from '@views/components/common/Button/Button';
|
||||
import CourseStatus from '@views/components/common/CourseStatus/CourseStatus';
|
||||
import Divider from '@views/components/common/Divider/Divider';
|
||||
import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses/ScheduleTotalHoursAndCourses';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import React from 'react';
|
||||
import calIcon from 'src/assets/logo.png';
|
||||
|
||||
import MenuIcon from '~icons/material-symbols/menu';
|
||||
import RedoIcon from '~icons/material-symbols/redo';
|
||||
import SettingsIcon from '~icons/material-symbols/settings';
|
||||
import UndoIcon from '~icons/material-symbols/undo';
|
||||
|
||||
/**
|
||||
* Renders the header component for the calendar.
|
||||
* @returns The CalendarHeader component.
|
||||
*/
|
||||
const CalendarHeader = () => (
|
||||
<div className='min-h-79px min-w-672px flex px-0 py-15'>
|
||||
<div className='flex flex-row gap-20'>
|
||||
@@ -18,9 +23,9 @@ const CalendarHeader = () => (
|
||||
<div className='flex gap-1'>
|
||||
<Button variant='single' icon={MenuIcon} color='ut-gray' />
|
||||
<div className='flex items-center'>
|
||||
<img src={calIcon} className='min-w-[48px] max-w-[48px]' alt='UT Registration Plus Logo' />
|
||||
<img src={calIcon} className='max-w-[48px] min-w-[48px]' alt='UT Registration Plus Logo' />
|
||||
<div className='flex flex-col whitespace-nowrap'>
|
||||
<Text className='leading-trim text-cap font-roboto text-base text-ut-burntorange font-medium'>
|
||||
<Text className='leading-trim font-roboto text-cap 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'>
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import List from '@views/components/common/List/List';
|
||||
import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import React, { useState } from 'react';
|
||||
import AddSchedule from '~icons/material-symbols/add';
|
||||
import List from '../../common/List/List';
|
||||
import ScheduleListItem from '../../common/ScheduleListItem/ScheduleListItem';
|
||||
import Text from '../../common/Text/Text';
|
||||
|
||||
import AddSchedule from '~icons/material-symbols/add';
|
||||
|
||||
/**
|
||||
* Props for the CalendarSchedules component.
|
||||
*/
|
||||
export type Props = {
|
||||
style?: React.CSSProperties;
|
||||
dummySchedules?: UserSchedule[];
|
||||
dummyActiveIndex?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a component that displays a list of schedules.
|
||||
*
|
||||
* @param props - The component props.
|
||||
* @returns The rendered component.
|
||||
*/
|
||||
export function CalendarSchedules(props: Props) {
|
||||
const [activeScheduleIndex, setActiveScheduleIndex] = useState(props.dummyActiveIndex || 0);
|
||||
const [schedules, setSchedules] = useState(props.dummySchedules || []);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Text from '../common/Text/Text';
|
||||
import React from 'react';
|
||||
|
||||
import OutwardArrowIcon from '~icons/material-symbols/arrow-outward';
|
||||
|
||||
import Text from '../common/Text/Text';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import type IconComponent from '~icons/material-symbols';
|
||||
import { ThemeColor, getThemeColorHexByName, getThemeColorRgbByName } from '../../../../shared/util/themeColors';
|
||||
|
||||
import type { ThemeColor } from '../../../../shared/util/themeColors';
|
||||
import { getThemeColorHexByName, getThemeColorRgbByName } from '../../../../shared/util/themeColors';
|
||||
import Text from '../Text/Text';
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './Card.module.scss';
|
||||
|
||||
/**
|
||||
* Props for the Card component.
|
||||
*/
|
||||
export type Props = {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import Text from '../Text/Text';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import type { Course } from 'src/shared/types/Course';
|
||||
|
||||
import Text from '../Text/Text';
|
||||
|
||||
/**
|
||||
@@ -28,9 +29,7 @@ export default function ConflictsWithWarning({ className, conflicts }: Conflicts
|
||||
>
|
||||
<div>Conflicts With:</div>
|
||||
{conflicts.map(course => (
|
||||
<div>
|
||||
{`${course.department} ${course.number} (${course.uniqueId})`}
|
||||
</div>
|
||||
<div>{`${course.department} ${course.number} (${course.uniqueId})`}</div>
|
||||
))}
|
||||
</Text>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Status } from '@shared/types/Course';
|
||||
import type { Status } from '@shared/types/Course';
|
||||
import { StatusIcon } from '@shared/util/icons';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import Text from '../Text/Text';
|
||||
|
||||
type SizeType = 'small' | 'mini';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Color } from '@views/styles/colors.module.scss';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Disclosure, Transition } from '@headlessui/react';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import List from '@views/components/common/List/List';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import React from 'react';
|
||||
import userScheduleHandler from 'src/pages/background/handler/userScheduleHandler';
|
||||
|
||||
import DropdownArrowDown from '~icons/material-symbols/arrow-drop-down';
|
||||
import DropdownArrowUp from '~icons/material-symbols/arrow-drop-up';
|
||||
import List from '../List/List';
|
||||
import Text from '../Text/Text';
|
||||
|
||||
/**
|
||||
* Props for the Dropdown component.
|
||||
*/
|
||||
export type Props = {
|
||||
style?: React.CSSProperties;
|
||||
// Dummy value solely for storybook
|
||||
@@ -62,7 +66,7 @@ export default function Dropdown(props: Props) {
|
||||
<Disclosure.Button>
|
||||
<div className='flex items-center border-none bg-white p-3 text-left'>
|
||||
<div className='flex-1'>
|
||||
<Text as='div' variant='h4' className='text-ut-burntorange mb-1 w-100%'>
|
||||
<Text as='div' variant='h4' className='mb-1 w-100% text-ut-burntorange'>
|
||||
MAIN SCHEDULE:
|
||||
</Text>
|
||||
<div>
|
||||
@@ -74,7 +78,7 @@ export default function Dropdown(props: Props) {
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<Text className='text-ut-burntorange text-2xl font-normal'>
|
||||
<Text className='text-2xl text-ut-burntorange font-normal'>
|
||||
{expanded ? <DropdownArrowDown /> : <DropdownArrowUp />}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import styles from './ExtensionRoot.module.scss';
|
||||
|
||||
import '@unocss/reset/tailwind-compat.css';
|
||||
import 'uno.css';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import styles from './ExtensionRoot.module.scss';
|
||||
|
||||
interface Props {
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { Color } from '@views/styles/colors.module.scss';
|
||||
import colors from '@views/styles/colors.module.scss';
|
||||
import type { Size } from '@views/styles/fonts.module.scss';
|
||||
import fonts from '@views/styles/fonts.module.scss';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import colors, { Color } from '@views/styles/colors.module.scss';
|
||||
import fonts, { Size } from '@views/styles/fonts.module.scss';
|
||||
|
||||
import styles from './Icon.module.scss';
|
||||
import { MaterialIconCode } from './MaterialIcons';
|
||||
import type { MaterialIconCode } from './MaterialIcons';
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -2194,4 +2194,7 @@ const icons = [
|
||||
'zoom_out_map',
|
||||
] as const;
|
||||
|
||||
export type MaterialIconCode = typeof icons[number];
|
||||
/**
|
||||
* Represents a type that corresponds to a material icon code.
|
||||
*/
|
||||
export type MaterialIconCode = (typeof icons)[number];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import Text from '../Text/Text';
|
||||
|
||||
interface Props {
|
||||
@@ -10,31 +11,35 @@ interface Props {
|
||||
* A maybe reusable InfoCard component that follows the design system of the extension.
|
||||
* @returns
|
||||
*/
|
||||
export function InfoCard({
|
||||
titleText,
|
||||
bodyText
|
||||
}: React.PropsWithChildren<Props>): JSX.Element {
|
||||
export function InfoCard({ titleText, bodyText }: React.PropsWithChildren<Props>): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className = 'w-50 flex flex-col items-start justify-center border rounded p-4'
|
||||
style = {{
|
||||
border: "1px solid #D6D2C4",
|
||||
background: "#FFF" // White
|
||||
}}>
|
||||
<div className="flex flex-col items-start self-stretch gap-1.5">
|
||||
<Text variant = "h4" as = 'span'
|
||||
style = {{
|
||||
color: '#F8971F', // Orange
|
||||
}}>
|
||||
{titleText}
|
||||
</Text>
|
||||
<Text variant = "small" as = 'span'
|
||||
style = {{
|
||||
color: '#333F48', // Black
|
||||
}}>
|
||||
{bodyText}
|
||||
</Text>
|
||||
</ div>
|
||||
className='w-50 flex flex-col items-start justify-center border rounded p-4'
|
||||
style={{
|
||||
border: '1px solid #D6D2C4',
|
||||
background: '#FFF', // White
|
||||
}}
|
||||
>
|
||||
<div className='flex flex-col items-start self-stretch gap-1.5'>
|
||||
<Text
|
||||
variant='h4'
|
||||
as='span'
|
||||
style={{
|
||||
color: '#F8971F', // Orange
|
||||
}}
|
||||
>
|
||||
{titleText}
|
||||
</Text>
|
||||
<Text
|
||||
variant='small'
|
||||
as='span'
|
||||
style={{
|
||||
color: '#333F48', // Black
|
||||
}}
|
||||
>
|
||||
{bodyText}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { background } from '@shared/messages';
|
||||
import clsx from 'clsx';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import Text, { TextProps } from '../Text/Text';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { TextProps } from '../Text/Text';
|
||||
import Text from '../Text/Text';
|
||||
import styles from './Link.module.scss';
|
||||
|
||||
type Props = Omit<TextProps, 'span'> & {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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';
|
||||
|
||||
interface Props {
|
||||
@@ -19,8 +21,6 @@ export default function Popup({ onClose, children, className, style, testId, ove
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
const bodyRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
|
||||
const handleClickOutside = useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (!bodyRef.current) return;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { Course, Status } from '@shared/types/Course';
|
||||
import { CourseColors, pickFontColor } from '@shared/util/colors';
|
||||
import type { Course } from '@shared/types/Course';
|
||||
import { Status } from '@shared/types/Course';
|
||||
import type { CourseColors } from '@shared/util/colors';
|
||||
import { pickFontColor } from '@shared/util/colors';
|
||||
import { StatusIcon } from '@shared/util/icons';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';
|
||||
|
||||
import Text from '../Text/Text';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';
|
||||
import Text from '../Text/Text';
|
||||
|
||||
import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';
|
||||
|
||||
/**
|
||||
* Props for the ScheduleListItem component.
|
||||
*/
|
||||
export type Props = {
|
||||
style?: React.CSSProperties;
|
||||
active?: boolean;
|
||||
@@ -18,11 +22,11 @@ export default function ScheduleListItem(props: Props) {
|
||||
console.log(props);
|
||||
return (
|
||||
<div style={{ ...props.style }} className='items-center'>
|
||||
<li className='text-ut-burntorange w-100% flex cursor-pointer items-center self-stretch justify-left'>
|
||||
<li className='w-100% flex cursor-pointer items-center self-stretch justify-left text-ut-burntorange'>
|
||||
<div className='group flex justify-center'>
|
||||
<div
|
||||
<div
|
||||
className='flex cursor-move items-center self-stretch rounded rounded-r-0'
|
||||
{...dragHandleProps}
|
||||
{...dragHandleProps}
|
||||
>
|
||||
<DragIndicatorIcon className='h-6 w-6 cursor-move text-zinc-300 btn-transition -ml-1.5 hover:text-zinc-400' />
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import Text from '../Text/Text';
|
||||
|
||||
/**
|
||||
@@ -21,7 +22,7 @@ export default function ScheduleTotalHoursAndCourses({
|
||||
totalCourses,
|
||||
}: ScheduleTotalHoursAndCoursesProps): JSX.Element {
|
||||
return (
|
||||
<div className='min-w-64 flex whitespace-nowrap content-center items-baseline gap-2 uppercase'>
|
||||
<div className='min-w-64 flex content-center items-baseline gap-2 whitespace-nowrap uppercase'>
|
||||
<Text className='text-[#BF5700]' variant='h1' as='span'>
|
||||
{`${scheduleName}: `}
|
||||
</Text>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './Spinner.module.scss';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './Text.module.scss';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { ScrapedRow } from '@shared/types/Course';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import type { ScrapedRow } from '@shared/types/Course';
|
||||
import useInfiniteScroll from '@views/hooks/useInfiniteScroll';
|
||||
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
|
||||
import { SiteSupport } from '@views/lib/getSiteSupport';
|
||||
@@ -9,6 +7,9 @@ import {
|
||||
loadNextCourseCatalogPage,
|
||||
removePaginationButtons,
|
||||
} from '@views/lib/loadNextCourseCatalogPage';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import styles from './AutoLoad.module.scss';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import Popup from '@views/components/common/Popup/Popup';
|
||||
import React from 'react';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
import type { Course } from 'src/shared/types/Course';
|
||||
import type { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
|
||||
import Description from './Description';
|
||||
import GradeDistribution from './GradeDistribution';
|
||||
import HeadingAndActions from './HeadingAndActions';
|
||||
@@ -12,6 +13,16 @@ interface CourseCatalogInjectedPopupProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* CourseCatalogInjectedPopup component displays a popup with course details.
|
||||
*
|
||||
* @component
|
||||
* @param {CourseCatalogInjectedPopupProps} props - The component props.
|
||||
* @param {Course} props.course - The course object containing course details.
|
||||
* @param {Schedule} props.activeSchedule - The active schedule object.
|
||||
* @param {Function} props.onClose - The function to close the popup.
|
||||
* @returns {JSX.Element} The CourseCatalogInjectedPopup component.
|
||||
*/
|
||||
const CourseCatalogInjectedPopup: React.FC<CourseCatalogInjectedPopupProps> = ({ course, activeSchedule, onClose }) => (
|
||||
<Popup overlay className='max-w-[780px] px-6' onClose={onClose}>
|
||||
<div className='flex flex-col'>
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import Text from '../../common/Text/Text';
|
||||
|
||||
interface DescriptionProps {
|
||||
lines: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the description component.
|
||||
*
|
||||
* @component
|
||||
* @param {DescriptionProps} props - The component props.
|
||||
* @param {string[]} props.lines - The lines of text to render.
|
||||
* @returns {JSX.Element} The rendered description component.
|
||||
*/
|
||||
const Description: React.FC<DescriptionProps> = ({ lines }: DescriptionProps) => {
|
||||
const keywords = ['prerequisite', 'restricted'];
|
||||
return (
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
import type { Course } from '@shared/types/Course';
|
||||
import type { Distribution, LetterGrade } from '@shared/types/Distribution';
|
||||
import { colors } from '@shared/util/themeColors';
|
||||
import Spinner from '@views/components/common/Spinner/Spinner';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import Highcharts from 'highcharts';
|
||||
import HighchartsReact from 'highcharts-react-official';
|
||||
import React from 'react';
|
||||
import { Course } from 'src/shared/types/Course';
|
||||
import { Distribution, LetterGrade } from 'src/shared/types/Distribution';
|
||||
import { colors } from 'src/shared/util/themeColors';
|
||||
import {
|
||||
NoDataError,
|
||||
queryAggregateDistribution,
|
||||
querySemesterDistribution,
|
||||
} from 'src/views/lib/database/queryDistribution';
|
||||
} from '@views/lib/database/queryDistribution';
|
||||
import Highcharts from 'highcharts';
|
||||
import HighchartsReact from 'highcharts-react-official';
|
||||
import React from 'react';
|
||||
|
||||
interface GradeDistributionProps {
|
||||
course: Course;
|
||||
}
|
||||
|
||||
enum DataStatus {
|
||||
LOADING = 'LOADING',
|
||||
FOUND = 'FOUND',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
ERROR = 'ERROR',
|
||||
}
|
||||
const DataStatus = {
|
||||
LOADING: 'LOADING',
|
||||
FOUND: 'FOUND',
|
||||
NOT_FOUND: 'NOT_FOUND',
|
||||
ERROR: 'ERROR',
|
||||
} as const;
|
||||
|
||||
const GRADE_COLORS: Record<LetterGrade, string> = {
|
||||
type DataStatusType = (typeof DataStatus)[keyof typeof DataStatus];
|
||||
|
||||
const GRADE_COLORS = {
|
||||
A: colors.gradeDistribution.a,
|
||||
'A-': colors.gradeDistribution.aminus,
|
||||
'B+': colors.gradeDistribution.bplus,
|
||||
@@ -36,12 +38,20 @@ const GRADE_COLORS: Record<LetterGrade, string> = {
|
||||
D: colors.gradeDistribution.d,
|
||||
'D-': colors.gradeDistribution.dminus,
|
||||
F: colors.gradeDistribution.f,
|
||||
};
|
||||
} as const satisfies Record<LetterGrade, string>;
|
||||
|
||||
/**
|
||||
* Renders the grade distribution chart for a specific course.
|
||||
*
|
||||
* @component
|
||||
* @param {GradeDistributionProps} props - The component props.
|
||||
* @param {Course} props.course - The course for which to display the grade distribution.
|
||||
* @returns {JSX.Element} The grade distribution chart component.
|
||||
*/
|
||||
const GradeDistribution: React.FC<GradeDistributionProps> = ({ course }) => {
|
||||
const [semester, setSemester] = React.useState('Aggregate');
|
||||
const [distributions, setDistributions] = React.useState<Record<string, Distribution>>({});
|
||||
const [status, setStatus] = React.useState(DataStatus.LOADING);
|
||||
const [status, setStatus] = React.useState<DataStatusType>(DataStatus.LOADING);
|
||||
const ref = React.useRef<HighchartsReact.RefObject>(null);
|
||||
|
||||
const chartData = React.useMemo(() => {
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from '@views/components/common/Button/Button';
|
||||
import { Chip, flagMap } from '@views/components/common/Chip/Chip';
|
||||
import Divider from '@views/components/common/Divider/Divider';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import React, { useState } from 'react';
|
||||
import addCourse from 'src/pages/background/lib/addCourse';
|
||||
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 { Course } from 'src/shared/types/Course';
|
||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||
|
||||
import Add from '~icons/material-symbols/add';
|
||||
import Remove from '~icons/material-symbols/remove';
|
||||
import CalendarMonth from '~icons/material-symbols/calendar-month';
|
||||
import CloseIcon from '~icons/material-symbols/close';
|
||||
import Copy from '~icons/material-symbols/content-copy';
|
||||
import Description from '~icons/material-symbols/description';
|
||||
import Mood from '~icons/material-symbols/mood';
|
||||
import Remove from '~icons/material-symbols/remove';
|
||||
import Reviews from '~icons/material-symbols/reviews';
|
||||
|
||||
interface HeadingAndActionProps {
|
||||
@@ -26,7 +27,11 @@ interface HeadingAndActionProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const handleOpenCalendar = async () => { // Not sure if it's bad practice to export this
|
||||
/**
|
||||
* Opens the calendar in a new tab.
|
||||
* @returns {Promise<void>} A promise that resolves when the tab is opened.
|
||||
*/
|
||||
export const handleOpenCalendar = async () => {
|
||||
const url = chrome.runtime.getURL('calendar.html');
|
||||
await openTabFromContentScript(url);
|
||||
};
|
||||
@@ -41,7 +46,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
|
||||
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course;
|
||||
const [courseAdded, setCourseAdded] = useState<boolean>(
|
||||
activeSchedule.courses.some(course => course.uniqueId === uniqueId)
|
||||
);
|
||||
);
|
||||
|
||||
const instructorString = instructors
|
||||
.map(instructor => {
|
||||
@@ -74,8 +79,7 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
|
||||
const handleAddOrRemoveCourse = async () => {
|
||||
if (!courseAdded) {
|
||||
await addCourse(activeSchedule.name, course);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
await removeCourse(activeSchedule.name, course);
|
||||
}
|
||||
setCourseAdded(!courseAdded);
|
||||
@@ -137,7 +141,12 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
|
||||
<Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}>
|
||||
Past Syllabi
|
||||
</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'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
import { Course } from '@shared/types/Course';
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import type { Course } from '@shared/types/Course';
|
||||
import Card from '@views/components/common/Card/Card';
|
||||
import Spinner from '@views/components/common/Spinner/Spinner';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
|
||||
import { SiteSupport } from '@views/lib/getSiteSupport';
|
||||
import Card from '../../../common/Card/Card';
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import styles from './CourseDescription.module.scss';
|
||||
|
||||
type Props = {
|
||||
course: Course;
|
||||
};
|
||||
|
||||
enum LoadStatus {
|
||||
LOADING = 'LOADING',
|
||||
DONE = 'DONE',
|
||||
ERROR = 'ERROR',
|
||||
}
|
||||
const LoadStatus = {
|
||||
LOADING: 'LOADING',
|
||||
DONE: 'DONE',
|
||||
ERROR: 'ERROR',
|
||||
} as const;
|
||||
|
||||
type LoadStatusType = (typeof LoadStatus)[keyof typeof LoadStatus];
|
||||
|
||||
/**
|
||||
* Renders the course description component.
|
||||
*
|
||||
* @param {Props} props - The component props.
|
||||
* @param {Course} props.course - The course object.
|
||||
* @returns {JSX.Element} The rendered course description component.
|
||||
*/
|
||||
export default function CourseDescription({ course }: Props) {
|
||||
const [description, setDescription] = useState<string[]>([]);
|
||||
const [status, setStatus] = useState<LoadStatus>(LoadStatus.LOADING);
|
||||
const [status, setStatus] = useState<LoadStatusType>(LoadStatus.LOADING);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDescription(course)
|
||||
@@ -71,11 +78,7 @@ function DescriptionLine({ line }: LineProps) {
|
||||
[styles.restriction]: lowerCaseLine.includes('restrict'),
|
||||
});
|
||||
|
||||
return (
|
||||
<Text className={className} /* size='medium' */>
|
||||
{line}
|
||||
</Text>
|
||||
);
|
||||
return <Text className={className} /* size='medium' */>{line}</Text>;
|
||||
}
|
||||
|
||||
async function fetchDescription(course: Course): Promise<string[]> {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { background } from '@shared/messages';
|
||||
import { Course } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import type { Course } from '@shared/types/Course';
|
||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import { Button } from '@views/components/common/Button/Button';
|
||||
import Card from '@views/components/common/Card/Card';
|
||||
import Icon from '@views/components/common/Icon/Icon';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './CourseButtons.module.scss';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { Course } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import React from 'react';
|
||||
import type { Course } from '@shared/types/Course';
|
||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import { Button } from '@views/components/common/Button/Button';
|
||||
import Card from '@views/components/common/Card/Card';
|
||||
import Icon from '@views/components/common/Icon/Icon';
|
||||
import Link from '@views/components/common/Link/Link';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import { Button } from 'src/views/components/common/Button/Button';
|
||||
import React from 'react';
|
||||
|
||||
import CloseIcon from '~icons/material-symbols/close';
|
||||
import CopyIcon from '~icons/material-symbols/content-copy';
|
||||
|
||||
import CourseButtons from './CourseButtons/CourseButtons';
|
||||
import styles from './CourseHeader.module.scss';
|
||||
import CopyIcon from '~icons/material-symbols/content-copy';
|
||||
import CloseIcon from '~icons/material-symbols/close';
|
||||
|
||||
type Props = {
|
||||
course: Course;
|
||||
@@ -40,7 +42,7 @@ export default function CourseHeader({ course, activeSchedule, onClose }: Props)
|
||||
<Button icon={CopyIcon} variant='single' className='mr-1 px-2' color='ut-burntorange'>
|
||||
{course.uniqueId}
|
||||
</Button>
|
||||
<button className='btn bg-transparent p-0'>
|
||||
<button className='bg-transparent p-0 btn'>
|
||||
<CloseIcon className='h-7 w-7' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Course } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import type { Course } from '@shared/types/Course';
|
||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import React from 'react';
|
||||
|
||||
import Popup from '../../common/Popup/Popup';
|
||||
import CourseDescription from './CourseDescription/CourseDescription';
|
||||
import CourseHeader from './CourseHeader/CourseHeader';
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { Course, Semester } from '@shared/types/Course';
|
||||
import { Distribution, LetterGrade } from '@shared/types/Distribution';
|
||||
import Highcharts from 'highcharts';
|
||||
import HighchartsReact from 'highcharts-react-official';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import type { Course, Semester } from '@shared/types/Course';
|
||||
import type { Distribution, LetterGrade } from '@shared/types/Distribution';
|
||||
import Card from '@views/components/common/Card/Card';
|
||||
import Icon from '@views/components/common/Icon/Icon';
|
||||
import Spinner from '@views/components/common/Spinner/Spinner';
|
||||
@@ -14,20 +10,26 @@ import {
|
||||
querySemesterDistribution,
|
||||
} from '@views/lib/database/queryDistribution';
|
||||
import colors from '@views/styles/colors.module.scss';
|
||||
import Highcharts from 'highcharts';
|
||||
import HighchartsReact from 'highcharts-react-official';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import styles from './GradeDistribution.module.scss';
|
||||
|
||||
enum DataStatus {
|
||||
LOADING = 'LOADING',
|
||||
FOUND = 'FOUND',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
ERROR = 'ERROR',
|
||||
}
|
||||
const DataStatus = {
|
||||
LOADING: 'LOADING',
|
||||
FOUND: 'FOUND',
|
||||
NOT_FOUND: 'NOT_FOUND',
|
||||
ERROR: 'ERROR',
|
||||
} as const;
|
||||
|
||||
type DataStatusType = (typeof DataStatus)[keyof typeof DataStatus];
|
||||
|
||||
interface Props {
|
||||
course: Course;
|
||||
}
|
||||
|
||||
const GRADE_COLORS: Record<LetterGrade, string> = {
|
||||
const GRADE_COLORS = {
|
||||
A: colors.turtle_pond,
|
||||
'A-': colors.turtle_pond,
|
||||
'B+': colors.cactus,
|
||||
@@ -40,7 +42,7 @@ const GRADE_COLORS: Record<LetterGrade, string> = {
|
||||
D: colors.tangerine,
|
||||
'D-': colors.tangerine,
|
||||
F: colors.speedway_brick,
|
||||
};
|
||||
} as const satisfies Record<LetterGrade, string>;
|
||||
|
||||
/**
|
||||
* A chart to fetch and display the grade distribution for a course
|
||||
@@ -51,7 +53,7 @@ export default function GradeDistribution({ course }: Props) {
|
||||
const [semesters, setSemesters] = useState<Semester[]>([]);
|
||||
const [selectedSemester, setSelectedSemester] = useState<Semester | null>(null);
|
||||
const [distribution, setDistribution] = useState<Distribution | null>(null);
|
||||
const [status, setStatus] = useState<DataStatus>(DataStatus.LOADING);
|
||||
const [status, setStatus] = useState<DataStatusType>(DataStatus.LOADING);
|
||||
|
||||
const [chartOptions, setChartOptions] = useState<Highcharts.Options>({
|
||||
title: {
|
||||
@@ -206,7 +208,7 @@ export default function GradeDistribution({ course }: Props) {
|
||||
<Text color='speedway_brick' /* size='medium' weight='semi_bold' */>
|
||||
There was an error fetching the grade distribution data
|
||||
</Text>
|
||||
<Icon color='speedway_brick' /* size='large' */ name='sentiment_dissatisfied' />
|
||||
<Icon color='speedway_brick' /* size='large' */ name='sentiment_dissatisfied' />
|
||||
</Card>
|
||||
)}
|
||||
{status === DataStatus.NOT_FOUND && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import Link from '../../common/Link/Link';
|
||||
import Text from '../../common/Text/Text';
|
||||
import styles from './RecruitmentBanner.module.scss';
|
||||
@@ -50,6 +51,10 @@ export default function RecruitmentBanner() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if recruitment can be done from the current department.
|
||||
* @returns {boolean} True if recruitment can be done from the current department, false otherwise.
|
||||
*/
|
||||
export function canRecruitFrom(): boolean {
|
||||
const params = ['fos_fl', 'fos_cn'];
|
||||
let department = '';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropsWithChildren, useEffect, useState } from 'react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Course, ScrapedRow } from '@shared/types/Course';
|
||||
import { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import type { Course, ScrapedRow } from '@shared/types/Course';
|
||||
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Button } from '../../common/Button/Button';
|
||||
import styles from './TableRow.module.scss';
|
||||
import ConflictsWithWarning from '../../common/ConflictsWithWarning/ConflictsWithWarning';
|
||||
|
||||
import AddIcon from '~icons/material-symbols/add-circle';
|
||||
|
||||
import { Button } from '../../common/Button/Button';
|
||||
import ConflictsWithWarning from '../../common/ConflictsWithWarning/ConflictsWithWarning';
|
||||
import styles from './TableRow.module.scss';
|
||||
|
||||
interface Props {
|
||||
isSelected: boolean;
|
||||
row: ScrapedRow;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ScrapedRow } from '@shared/types/Course';
|
||||
import type { ScrapedRow } from '@shared/types/Course';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import styles from './TableSubheading.module.scss';
|
||||
|
||||
interface Props {
|
||||
|
||||
Reference in New Issue
Block a user