Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add core curriculum chips to injected popup #372

Merged
3 changes: 3 additions & 0 deletions src/shared/types/Course.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export class Course {
scrapedAt!: number;
/** The colors of the course when displayed */
colors: CourseColors;
/** The core curriculum requirements the course satisfies */
core: string[];

constructor(course: Serialized<Course>) {
Object.assign(this, course);
Expand All @@ -88,6 +90,7 @@ export class Course {
this.scrapedAt = Date.now();
}
this.colors = course.colors ? structuredClone(course.colors) : getCourseColors('emerald', 500);
this.core = course.core ?? [];
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/stories/components/ConflictsWithWarning.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const ExampleCourse: Course = new Course({
'Taught as a Web-based course.',
],
flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online',
instructors: [
Expand Down Expand Up @@ -60,6 +61,7 @@ export const ExampleCourse2: Course = new Course({
'May be counted toward the Independent Inquiry flag requirement.',
],
flags: ['Independent Inquiry'],
core: ['Natural Science and Technology, Part II'],
fullName: 'C S 439 PRINCIPLES OF COMPUTER SYSTEMS',
instructionMode: 'In Person',
instructors: [
Expand Down
1 change: 1 addition & 0 deletions src/stories/components/List.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const generateCourses = (count: number): Course[] => {
'Taught as a Web-based course.',
],
flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online',
instructors: [
Expand Down
1 change: 1 addition & 0 deletions src/stories/components/PopupCourseBlock.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const ExampleCourse: Course = new Course({
'Taught as a Web-based course.',
],
flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online',
instructors: [
Expand Down
2 changes: 2 additions & 0 deletions src/stories/components/calendar/CalendarBottomBar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const exampleGovCourse: Course = new Course({
department: 'GOV',
description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'],
core: ['American and Texas Government'],
fullName: 'GOV 312L Something something',
instructionMode: 'Online',
instructors: [
Expand Down Expand Up @@ -43,6 +44,7 @@ const examplePsyCourse: Course = new Course({
department: 'PSY',
description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'],
core: ['Social and Behavioral Sciences'],
fullName: 'PSY 317L Yada yada',
instructionMode: 'Online',
scrapedAt: Date.now(),
Expand Down
3 changes: 3 additions & 0 deletions src/stories/injected/mocked.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const exampleCourse: Course = new Course({
'Taught as a Web-based course.',
],
flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online',
scrapedAt: Date.now(),
Expand Down Expand Up @@ -99,6 +100,7 @@ export const bevoCourse: Course = new Course({
},
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
flags: ['Independent Inquiry', 'Writing'],
core: ['Humanities'],
instructionMode: 'In Person',
semester: {
code: '12345',
Expand Down Expand Up @@ -154,6 +156,7 @@ export const mikeScottCS314Course: Course = new Course({
},
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/50825/',
flags: ['Writing', 'Independent Inquiry'],
core: ['Natural Science and Technology, Part II'],
instructionMode: 'In Person',
semester: {
code: '12345',
Expand Down
2 changes: 1 addition & 1 deletion src/views/components/common/Chip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface Props {
}

/**
* A reusable chip component that follows the design system of the extension.
* A reusable chip component for flagged courses that follows the design system of the extension.
tildezero marked this conversation as resolved.
Show resolved Hide resolved
* @returns
*/
export function Chip({ label }: React.PropsWithChildren<Props>): JSX.Element {
Expand Down
46 changes: 46 additions & 0 deletions src/views/components/common/CoreChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Text from '@views/components/common/Text/Text';
import React from 'react';

/**
* A type that represents the core curriculum aspects that a course can satisfy.
*/
export type Core = 'ID' | 'C1' | 'HU' | 'GO' | 'HI' | 'SB' | 'MA' | 'N1' | 'N2' | 'VP';
export const coreMap = {
'First-Year Signature Course': 'ID',
'English Composition': 'C1',
Humanities: 'HU',
'American and Texas Government': 'GO',
'U.S. History': 'HI',
'Social and Behavioral Sciences': 'SB',
'Natural Science and Technology, Part I': 'N1',
'Natural Science and Technology, Part II': 'N2',
Mathematics: 'MA',
'Visual and Performing Arts': 'VP',
} as const satisfies Record<string, Core>;

interface Props {
label: Core;
}

/**
* A reusable chip component for core curriculum satisfying courses that follows the design system of the extension.
* @returns
*/
export function CoreChip({ label }: React.PropsWithChildren<Props>): JSX.Element {
const coreName = Object.entries(coreMap).find(([full, short]) => short === label)?.[0] ?? label;

return (
<Text
as='div'
variant='h4'
className='min-w-5 inline-flex items-center justify-center gap-2.5 rounded-lg px-1.5 py-0.5 text-center'
style={{
backgroundColor: '#005F86',
color: '#FFFFFF',
}}
title={`[CORE CURRICULUM]: ${coreName}`}
>
{label}
</Text>
);
}
tildezero marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type Instructor from '@shared/types/Instructor';
import type { UserSchedule } from '@shared/types/UserSchedule';
import { Button } from '@views/components/common/Button';
import { Chip, flagMap } from '@views/components/common/Chip';
import { CoreChip, coreMap } from '@views/components/common/CoreChip';
import Divider from '@views/components/common/Divider';
import Link from '@views/components/common/Link';
import Text from '@views/components/common/Text/Text';
Expand Down Expand Up @@ -49,7 +50,7 @@ const capitalizeString = (str: string) => str.charAt(0).toUpperCase() + str.slic
* @returns {JSX.Element} The rendered component.
*/
export default function HeadingAndActions({ course, activeSchedule, onClose }: HeadingAndActionProps): JSX.Element {
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course;
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule, core } = course;
const courseAdded = activeSchedule.courses.some(ourCourse => ourCourse.uniqueId === uniqueId);
const formattedUniqueId = uniqueId.toString().padStart(5, '0');
const isInCalendar = useCalendar();
Expand Down Expand Up @@ -154,6 +155,13 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
label={flagMap[flag as keyof typeof flagMap]}
/>
))}
{core?.length > 0 &&
core?.map((coreVal: string) => (
tildezero marked this conversation as resolved.
Show resolved Hide resolved
<CoreChip
key={coreMap[coreVal as keyof typeof coreMap]}
label={coreMap[coreVal as keyof typeof coreMap]}
/>
))}
</div>
</div>
<div className='mt-1 flex flex-col'>
Expand Down
17 changes: 17 additions & 0 deletions src/views/lib/CourseCatalogScraper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const TableDataSelector = {
SCHEDULE_HOURS: 'td[data-th="Hour"]>span',
SCHEDULE_LOCATION: 'td[data-th="Room"]>span',
FLAGS: 'td[data-th="Flags"] ul li',
CORE_CURRICULUM: 'td[data-th="Core"] ul li',
} as const satisfies Record<string, string>;

type TableDataSelectorType = (typeof TableDataSelector)[keyof typeof TableDataSelector];
Expand Down Expand Up @@ -99,6 +100,7 @@ export class CourseCatalogScraper {
semester: this.getSemester(),
scrapedAt: Date.now(),
colors: getCourseColors('emerald', 500),
core: this.getCore(row),
});
courses.push({
element: row,
Expand Down Expand Up @@ -337,6 +339,21 @@ export class CourseCatalogScraper {
return Array.from(lis).map(li => li.textContent || '');
}

/**
* Get the list of core curriculum requirements the course satisfies
* @param row
* @returns an array of core curriculum codes
*/
getCore(row: HTMLTableRowElement): string[] {
const lis = row.querySelectorAll(TableDataSelector.CORE_CURRICULUM);
return (
Array.from(lis)
// ut schedule is weird and puts a blank core curriculum element even if there aren't any core requirements so filter those out
.filter(li => li.getAttribute('title') !== ' core curriculum requirement')
.map(li => li.textContent || '')
);
}

/**
* This will scrape all the time information from the course catalog table row and return it as a CourseSchedule object, which represents all of the meeting timiestimes/places of the course.
* @param row the row of the course catalog table
Expand Down
Loading