Compare commits

...

25 Commits

Author SHA1 Message Date
doprz
93548627a6 chore: bump v2.0.2 2024-11-04 18:15:09 -06:00
suhas
638ee88c96 feat: bold course number in grade distribution chart, change text to ut-black (#406)
* feat: bold course number in grade distribution chart, change text to text-ut-black

* fix: use !important instead of manual style

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>

* fix: use tailwind classname instead of manual font weight

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2024-11-04 15:38:51 -06:00
adityamkk
50e88fa015 fix(ui): multiple instructors are formatted properly, displays last name only, and are capitalized in all course blocks (#342) (#403)
* fix(ui): updated multiple instructor name formatting in course blocks

* fix(ui): display instructor last names only in all course blocks

* refactor: use instructor toString when getting instructor full name

* refactor: toString in useFlattenedCourseSchedule without template literal

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>

* refactor: delete type hint and unnecessary comments

* fix(ui): instructor names semicolon delimiter in calendar course block

* fix(ui): removed 'unknown' when there are no instructors

* fix(ui): change - to &ndash

* fix(ui): changed - to ndash in hook

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2024-10-29 16:07:26 -05:00
adityamkk
b3ae91d8f3 fix(ui): placeholder text for no instructor course #400 (#402)
* fix(ui): placeholder text for no instructor course #400

* Update src/views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions.tsx

Co-authored-by: Samuel Gunter <sgunter@utexas.edu>

---------

Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
2024-10-29 12:12:51 -05:00
shehreenr
0077ae70d2 feat(ui): changed popup close icon to ut-black (#394)
* feat(ui): changed popup close icon to #333F48

* feat(ui): ran prettier

* feat(ui): changed hex code to ut-black

* feat(ui): change popup close button color

* feat(ui): got rid of extra space
2024-10-29 12:06:56 -05:00
Kabir Ramzan
94744e01b9 feat: use "copy of" for duplicated schedules and place them under the original schedule #358 (#397)
* feat: use "copy of" for duplicated schedules and place them under the original schedule #358

* fix: code style and consistency
2024-10-28 17:56:02 -05:00
Rishthegreat
8de88d6ad7 fix(ui): main popup now shows 0 for empty schedule (#395) 2024-10-27 17:11:51 -05:00
Razboy20
2d0804f90e fix: sentry issues (#389) 2024-10-23 14:15:47 -05:00
Samuel Gunter
d3577358d0 fix: typo in settings page (#386) 2024-10-22 23:23:06 -05:00
adityamkk
7346720894 fix(ui): duplicate schedule warning (#295)
* fix(ui): duplicate schedule warning

* fix(ui): duplicate schedule warning

* fix(ui): duplicate schedules

* fix(ui): schedule limit loophole refactored

* fix(ui): schedule bypass hooks

* fix(ui): useEnforceScheduleLimit hook created

* fix(ui): added useCallback to hook

* fix(ui): updated jsdoc comment on hook

* fix(ui): updated jsdoc comments on hook
2024-10-22 23:10:54 -05:00
Aditya Angajala
b00bf6c180 fix: change schedule total courses text color to UTRP black (#369) 2024-10-22 22:11:48 -05:00
Samuel Gunter
eb306787ae chore: fix lint warnings (#385) 2024-10-22 21:47:35 -05:00
Tony Dinh
643ea13207 feat: add more relevant links to the From the Team section (#380)
* feat: add more relevant links to the From the Team section

* feat: add CRXPages.ts

* chore: update to TS 4.9 satisfies

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-10-22 17:17:12 -05:00
suhas
6f1afc5b25 feat: add core curriculum chips to injected popup (#372)
* feat: add core curriculum chips to injected popup

* fix: add VP and MAcore indicators

* feat: core now defined in constructor

* fix: make core and flags closer together

* fix: stop empty core chip from showing when no core requirements

* fix: remove optional chaining for core chips

* feat: generalize Chip for both flags and core classes

* fix: change types for storybook and add new story for CoreChip

* fix: remove labelMap prop from Chip, chore: clean up imports

* feat: change tooltip for core curriculum requirement

---------

Co-authored-by: Derek Chen <derex1987@gmail.com>
2024-10-22 16:15:27 -05:00
Brianna-Flo
83d76f72da chore: One red color of #D10000 (#377)
* chore: change UTRP red color

* chore: change UTRP Red

* chore: change red update

* chore: utrp red, consistent capitalization

---------

Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
2024-10-22 16:00:14 -05:00
Preston Cook
768ac776ed fix: remove screenshot padding class for png download for #344 (#376)
Fixes #334
2024-10-22 00:11:28 -05:00
Brianna-Flo
9995b93d2a chore: change UTRP red color (#373) 2024-10-21 22:23:10 -05:00
Long Phan
4f609aeec7 fix: changed the font-weight of h1-course (#370)
Fixes #347
2024-10-21 22:20:21 -05:00
Parth
b6eccaca6a feat: Add linkedin social to calendar (#368)
* feat: update calendar footer with kibedjkib

* fix: made it prettier

* Update src/views/components/calendar/CalendarFooter.tsx

Co-authored-by: Samuel Gunter <sgunter@utexas.edu>

---------

Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
2024-10-21 22:16:55 -05:00
Warith Rahman
cef99c2d72 feat: update useful links (#367)
added and rearranged links on calendar sidebar
2024-10-21 19:13:11 -05:00
Yahya Kousa
86792eb56f fix(ui): add space before/after forward slash in "ASYNC/OTHER" text (#366)
* fix(ui): add space before/after forward slash in "ASYNC./OTHER" text

* fix(ui): add space before/after forward slash in "ASYNC./OTHER" text
2024-10-21 19:01:09 -05:00
Preston Cook
a715bbd933 fix: added descending sort for commits on contributor section in settings page (#365)
* fix: bug fix to address issue #363

* fix: bug fix to address issue #363

* fix: bug fix to sort LONGHORN_SWE constant

* fix: fix to sort LONGHORN_SWE constant

Resolves #363
2024-10-21 16:24:09 -05:00
Ethan
c2007ef090 fix: ensure input elements take full width of parent (#364) 2024-10-21 14:35:34 -05:00
doprz
b967240f8f feat: update senior swe admins (#326) 2024-10-18 22:17:47 -05:00
Samuel Gunter
839f9c6d6a feat: disable/some actions when no instructor (#319) 2024-10-17 12:03:57 -05:00
43 changed files with 408 additions and 156 deletions

View File

@@ -172,7 +172,14 @@ module.exports = {
}, },
], ],
'@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/naming-convention': 'off', '@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/space-before-function-paren': 'off', '@typescript-eslint/space-before-function-paren': 'off',
'@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/ban-ts-comment': 'off',

View File

@@ -2,6 +2,7 @@
interface ImportMetaEnv { interface ImportMetaEnv {
readonly VITE_PACKAGE_VERSION: string; readonly VITE_PACKAGE_VERSION: string;
readonly VITE_SENTRY_ENVIRONMENT: string;
readonly VITE_BETA_BUILD?: 'true'; readonly VITE_BETA_BUILD?: 'true';
} }

View File

@@ -1,3 +1,32 @@
## [2.0.2](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.1...v2.0.2) (2024-11-05)
### Features
- add core curriculum chips to injected popup ([#372](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/372)) ([6f1afc5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6f1afc5b25441c6a1fbfdf57b3c8b5b74e36f5a0))
- Add linkedin social to calendar ([#368](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/368)) ([b6eccac](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b6eccaca6a2cdba9b57d2f49f064ae8504bbd5cb))
- add more relevant links to the From the Team section ([#380](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/380)) ([643ea13](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/643ea1320798aabb7783d267f5e6fd7c00fc2e3f))
- bold course number in grade distribution chart, change text to ut-black ([#406](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/406)) ([638ee88](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/638ee88c96510a779c157b524903caaeffc9ef19))
- disable/some actions when no instructor ([#319](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/319)) ([839f9c6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/839f9c6d6afd4a1eae1a0bdf8893ab2e19b9fdff))
- **ui:** changed popup close icon to ut-black ([#394](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/394)) ([0077ae7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0077ae70d22f24549c4c3b243188d19adbfbac14)), closes [#333F48](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/333F48)
- update senior swe admins ([#326](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/326)) ([b967240](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b967240f8fbb7a790a78f4aa256f0a77a491abb8))
- update useful links ([#367](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/367)) ([cef99c2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cef99c2d72d3a2800f8a918d01cb116f8795d0c8))
- use "copy of" for duplicated schedules and place them under the original schedule [#358](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/358) ([#397](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/397)) ([94744e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/94744e01b94819fb4f5d64616ea56857b906c2dd))
### Bug Fixes
- added descending sort for commits on contributor section in settings page ([#365](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/365)) ([a715bbd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a715bbd933a87742e7bce3a44e8ba1bd419ad5eb)), closes [#363](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/363) [#363](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/363) [#363](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/363)
- change schedule total courses text color to UTRP black ([#369](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/369)) ([b00bf6c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b00bf6c180f1c6c3a61c5ef855e160ddf4af3ea4))
- changed the font-weight of h1-course ([#370](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/370)) ([4f609ae](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4f609aeec797c1f99f0a57e5aeef7b82756ea4bc)), closes [#347](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/347)
- ensure input elements take full width of parent ([#364](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/364)) ([c2007ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c2007ef090aab3bbfcb8bca1ebc476255d09cb90))
- remove screenshot padding class for png download for [#344](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/344) ([#376](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/376)) ([768ac77](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/768ac776ed4d5ca2113a032a93c2dc7432915aa1)), closes [#334](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/334)
- sentry issues ([#389](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/389)) ([2d0804f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2d0804f90e5d7a9ff83f7fd5c5acfdc7c1b1cc84))
- typo in settings page ([#386](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/386)) ([d357735](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d3577358d0d1fb60f2c776ae4b01e255fcf9109e))
- **ui:** add space before/after forward slash in "ASYNC/OTHER" text ([#366](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/366)) ([86792eb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/86792eb56f04b615f7d52b2f417b88f4cb9a82ec))
- **ui:** duplicate schedule warning ([#295](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/295)) ([7346720](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/73467208947e0116ce8538052ee75dea1d8038f9))
- **ui:** main popup now shows 0 for empty schedule ([#395](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/395)) ([8de88d6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8de88d6ad7d4c2b5c3aa08e1efc59f7226b40c6b))
- **ui:** multiple instructors are formatted properly, displays last name only, and are capitalized in all course blocks ([#342](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/342)) ([#403](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/403)) ([50e88fa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/50e88fa015e0290fbe0dab8a19f8fcdbc4dd02b0))
- **ui:** placeholder text for no instructor course [#400](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/400) ([#402](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/402)) ([b3ae91d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b3ae91d8f3cebb89e5e5cea7f1200d28326afb4d))
## [2.0.1](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.0...v2.0.1) (2024-10-17) ## [2.0.1](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.0...v2.0.1) (2024-10-17)
### Features ### Features

View File

@@ -1,7 +1,7 @@
{ {
"name": "ut-registration-plus", "name": "ut-registration-plus",
"displayName": "UT Registration Plus", "displayName": "UT Registration Plus",
"version": "2.0.1", "version": "2.0.2",
"description": "UT Registration Plus is a Chrome extension that allows students to easily register for classes.", "description": "UT Registration Plus is a Chrome extension that allows students to easily register for classes.",
"private": true, "private": true,
"homepage": "https://github.com/Longhorn-Developers/UT-Registration-Plus", "homepage": "https://github.com/Longhorn-Developers/UT-Registration-Plus",
@@ -10,7 +10,7 @@
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"build:watch": "NODE_ENV='development' vite build --mode development -w", "build:watch": "NODE_ENV='development' vite build --mode development -w",
"zip": "pnpm build && pnpm gulp zip", "zip": "PROD=true pnpm build && pnpm gulp zip",
"prettier": "prettier src --check", "prettier": "prettier src --check",
"prettier:fix": "prettier src --write", "prettier:fix": "prettier src --write",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives",

View File

@@ -32,7 +32,7 @@ chrome.runtime.onInstalled.addListener(details => {
}); });
// migration/login logic // migration/login logic
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { chrome.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
// console.log(changeInfo); // console.log(changeInfo);
if (changeInfo.url === 'https://utdirect.utexas.edu/apps/registrar/course_schedule/utrp_login/') { if (changeInfo.url === 'https://utdirect.utexas.edu/apps/registrar/course_schedule/utrp_login/') {
function openPopupAction() { function openPopupAction() {
@@ -59,15 +59,11 @@ messageListener.listen();
UserScheduleStore.listen('schedules', async schedules => { UserScheduleStore.listen('schedules', async schedules => {
const index = await UserScheduleStore.get('activeIndex'); const index = await UserScheduleStore.get('activeIndex');
const numCourses = schedules.newValue[index]?.courses?.length; const numCourses = schedules.newValue[index]?.courses?.length;
if (!numCourses) return; updateBadgeText(numCourses || 0);
updateBadgeText(numCourses);
}); });
UserScheduleStore.listen('activeIndex', async ({ newValue }) => { UserScheduleStore.listen('activeIndex', async ({ newValue }) => {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');
const numCourses = schedules[newValue]?.courses?.length; const numCourses = schedules[newValue]?.courses?.length;
if (!numCourses) return; updateBadgeText(numCourses || 0);
updateBadgeText(numCourses);
}); });

View File

@@ -10,15 +10,18 @@ import handleDuplicate from './handleDuplicate';
*/ */
export default async function duplicateSchedule(scheduleId: string): Promise<string | undefined> { export default async function duplicateSchedule(scheduleId: string): Promise<string | undefined> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');
const schedule = schedules.find(schedule => schedule.id === scheduleId); const scheduleIndex = schedules.findIndex(schedule => schedule.id === scheduleId);
if (schedule === undefined) { if (scheduleIndex === -1) {
throw new Error(`Schedule ${scheduleId} does not exist`); throw new Error(`Schedule ${scheduleId} does not exist`);
} }
const updatedName = await handleDuplicate(schedule.name); const schedule = schedules[scheduleIndex]!;
schedules.push({ const copyOfName = `Copy of ${schedule.name}`;
const updatedName = await handleDuplicate(copyOfName);
schedules.splice(scheduleIndex + 1, 0, {
id: generateRandomId(), id: generateRandomId(),
name: updatedName, name: updatedName,
courses: JSON.parse(JSON.stringify(schedule.courses)), courses: JSON.parse(JSON.stringify(schedule.courses)),

View File

@@ -0,0 +1,15 @@
/**
* An object containing the paths to various pages used in the CRX application.
*/
export const CRX_PAGES = {
DEBUG: '/debug.html',
CALENDAR: '/calendar.html',
OPTIONS: '/options.html',
REPORT: '/report.html',
} as const;
/**
* Represents a type that corresponds to the keys of the `CRX_PAGES` object.
* This type is used to ensure that only valid page keys are used within the application.
*/
export type CRX_Page = keyof typeof CRX_PAGES;

View File

@@ -79,6 +79,8 @@ export class Course {
scrapedAt!: number; scrapedAt!: number;
/** The colors of the course when displayed */ /** The colors of the course when displayed */
colors: CourseColors; colors: CourseColors;
/** The core curriculum requirements the course satisfies */
core: string[];
constructor(course: Serialized<Course>) { constructor(course: Serialized<Course>) {
Object.assign(this, course); Object.assign(this, course);
@@ -88,6 +90,7 @@ export class Course {
this.scrapedAt = Date.now(); this.scrapedAt = Date.now();
} }
this.colors = course.colors ? structuredClone(course.colors) : getCourseColors('emerald', 500); this.colors = course.colors ? structuredClone(course.colors) : getCourseColors('emerald', 500);
this.core = course.core ?? [];
} }
/** /**

View File

@@ -15,10 +15,9 @@ export const colors = {
gray: '#9CADB7', gray: '#9CADB7',
offwhite: '#D6D2C4', offwhite: '#D6D2C4',
concrete: '#95A5A6', concrete: '#95A5A6',
red: '#B91C1C', // Not sure if this should be here, but it's used for remove course, and add course is ut-green
}, },
theme: { theme: {
red: '#BF0000', red: '#D10000',
black: '#1A2024', black: '#1A2024',
}, },
} as const satisfies Record<string, Record<string, string>>; } as const satisfies Record<string, Record<string, string>>;

View File

@@ -15,7 +15,7 @@ import CancelledIcon from '~icons/material-symbols/warning';
export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: StatusType }): JSX.Element | null { export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: StatusType }): JSX.Element | null {
const { status, ...rest } = props; const { status, ...rest } = props;
switch (props.status) { switch (status) {
case Status.WAITLISTED: case Status.WAITLISTED:
return <WaitlistIcon {...rest} />; return <WaitlistIcon {...rest} />;
case Status.CLOSED: case Status.CLOSED:

View File

@@ -14,7 +14,7 @@ export const BADGE_LIMIT = 10;
*/ */
export default function updateBadgeText(value: number): void { export default function updateBadgeText(value: number): void {
let badgeText = ''; let badgeText = '';
if (value > 0) { if (value >= 0) {
if (value > BADGE_LIMIT) { if (value > BADGE_LIMIT) {
badgeText = `${BADGE_LIMIT}+`; badgeText = `${BADGE_LIMIT}+`;
} else { } else {

View File

@@ -16,8 +16,16 @@ export default meta;
type Story = StoryObj<typeof meta>; type Story = StoryObj<typeof meta>;
export const Default: Story = { export const FlagChip: Story = {
args: { args: {
label: 'QR', label: 'QR',
variant: 'flag',
},
};
export const CoreChip: Story = {
args: {
label: 'SB',
variant: 'core',
}, },
}; };

View File

@@ -17,6 +17,7 @@ export const ExampleCourse: Course = new Course({
'Taught as a Web-based course.', 'Taught as a Web-based course.',
], ],
flags: ['Quantitative Reasoning'], flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB', fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online', instructionMode: 'Online',
instructors: [ instructors: [
@@ -60,6 +61,7 @@ export const ExampleCourse2: Course = new Course({
'May be counted toward the Independent Inquiry flag requirement.', 'May be counted toward the Independent Inquiry flag requirement.',
], ],
flags: ['Independent Inquiry'], flags: ['Independent Inquiry'],
core: ['Natural Science and Technology, Part II'],
fullName: 'C S 439 PRINCIPLES OF COMPUTER SYSTEMS', fullName: 'C S 439 PRINCIPLES OF COMPUTER SYSTEMS',
instructionMode: 'In Person', instructionMode: 'In Person',
instructors: [ instructors: [

View File

@@ -34,6 +34,7 @@ const generateCourses = (count: number): Course[] => {
'Taught as a Web-based course.', 'Taught as a Web-based course.',
], ],
flags: ['Quantitative Reasoning'], flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB', fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online', instructionMode: 'Online',
instructors: [ instructors: [

View File

@@ -26,6 +26,7 @@ export const ExampleCourse: Course = new Course({
'Taught as a Web-based course.', 'Taught as a Web-based course.',
], ],
flags: ['Quantitative Reasoning'], flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB', fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online', instructionMode: 'Online',
instructors: [ instructors: [

View File

@@ -11,6 +11,7 @@ const exampleGovCourse: Course = new Course({
department: 'GOV', department: 'GOV',
description: ['nah', 'aint typing this', 'corndog'], description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'], flags: ['no flag for you >:)'],
core: ['American and Texas Government'],
fullName: 'GOV 312L Something something', fullName: 'GOV 312L Something something',
instructionMode: 'Online', instructionMode: 'Online',
instructors: [ instructors: [
@@ -43,6 +44,7 @@ const examplePsyCourse: Course = new Course({
department: 'PSY', department: 'PSY',
description: ['nah', 'aint typing this', 'corndog'], description: ['nah', 'aint typing this', 'corndog'],
flags: ['no flag for you >:)'], flags: ['no flag for you >:)'],
core: ['Social and Behavioral Sciences'],
fullName: 'PSY 317L Yada yada', fullName: 'PSY 317L Yada yada',
instructionMode: 'Online', instructionMode: 'Online',
scrapedAt: Date.now(), scrapedAt: Date.now(),

View File

@@ -16,6 +16,7 @@ export const exampleCourse: Course = new Course({
'Taught as a Web-based course.', 'Taught as a Web-based course.',
], ],
flags: ['Quantitative Reasoning'], flags: ['Quantitative Reasoning'],
core: ['Natural Science and Technology, Part I'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB', fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online', instructionMode: 'Online',
scrapedAt: Date.now(), scrapedAt: Date.now(),
@@ -99,6 +100,7 @@ export const bevoCourse: Course = new Course({
}, },
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/', url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
flags: ['Independent Inquiry', 'Writing'], flags: ['Independent Inquiry', 'Writing'],
core: ['Humanities'],
instructionMode: 'In Person', instructionMode: 'In Person',
semester: { semester: {
code: '12345', code: '12345',
@@ -154,6 +156,7 @@ export const mikeScottCS314Course: Course = new Course({
}, },
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/50825/', url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/50825/',
flags: ['Writing', 'Independent Inquiry'], flags: ['Writing', 'Independent Inquiry'],
core: ['Natural Science and Technology, Part II'],
instructionMode: 'In Person', instructionMode: 'In Person',
semester: { semester: {
code: '12345', code: '12345',

View File

@@ -7,6 +7,7 @@ import { openReportWindow } from '@shared/util/openReportWindow';
import Divider from '@views/components/common/Divider'; import Divider from '@views/components/common/Divider';
import List from '@views/components/common/List'; import List from '@views/components/common/List';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import { useEnforceScheduleLimit } from '@views/hooks/useEnforceScheduleLimit';
import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules'; import useSchedules, { getActiveSchedule, replaceSchedule, switchSchedule } from '@views/hooks/useSchedules';
import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString'; import { getUpdatedAtDateTimeString } from '@views/lib/getUpdatedAtDateTimeString';
import useKC_DABR_WASM from 'kc-dabr-wasm'; import useKC_DABR_WASM from 'kc-dabr-wasm';
@@ -59,9 +60,16 @@ export default function PopupMain(): JSX.Element {
}, []); }, []);
const [activeSchedule, schedules] = useSchedules(); const [activeSchedule, schedules] = useSchedules();
const [isRefreshing, setIsRefreshing] = useState(false); // const [isRefreshing, setIsRefreshing] = useState(false);
const [funny, setFunny] = useState<string>(''); const [funny, setFunny] = useState<string>('');
const enforceScheduleLimit = useEnforceScheduleLimit();
const handleAddSchedule = () => {
if (enforceScheduleLimit()) {
createSchedule('New Schedule');
}
};
useEffect(() => { useEffect(() => {
const randomIndex = Math.floor(Math.random() * splashText.length); const randomIndex = Math.floor(Math.random() * splashText.length);
setFunny( setFunny(
@@ -128,7 +136,7 @@ export default function PopupMain(): JSX.Element {
variant='filled' variant='filled'
color='ut-burntorange' color='ut-burntorange'
className='h-fit p-0 btn' className='h-fit p-0 btn'
onClick={() => createSchedule('New Schedule')} onClick={handleAddSchedule}
> >
<AddSchedule className='h-6 w-6' /> <AddSchedule className='h-6 w-6' />
</Button> </Button>

View File

@@ -81,29 +81,33 @@ export default function ReportIssueMain(): JSX.Element {
<label htmlFor='email' className='mb-1 block text-sm text-ut-black font-medium'> <label htmlFor='email' className='mb-1 block text-sm text-ut-black font-medium'>
Your @utexas.edu email Your @utexas.edu email
</label> </label>
<input <div className='flex'>
type='email' <input
id='email' type='email'
value={email} id='email'
onChange={e => setEmail(e.target.value)} value={email}
className='w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500' onChange={e => setEmail(e.target.value)}
placeholder='bevo@utexas.edu' className='w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500'
required placeholder='bevo@utexas.edu'
/> required
/>
</div>
</div> </div>
<div className='mb-4'> <div className='mb-4'>
<label htmlFor='feedback' className='mb-1 block text-sm text-ut-black font-medium'> <label htmlFor='feedback' className='mb-1 block text-sm text-ut-black font-medium'>
Your feedback Your feedback
</label> </label>
<textarea <div className='flex'>
id='feedback' <textarea
value={feedback} id='feedback'
onChange={e => setFeedback(e.target.value)} value={feedback}
className='h-24 w-full resize-none border border-gray-300 rounded-md px-3 py-2 text-sm font-sans focus:outline-none focus:ring-2 focus:ring-orange-500' onChange={e => setFeedback(e.target.value)}
placeholder='I wish UT Registration Plus could...' className='h-24 w-full resize-none border border-gray-300 rounded-md px-3 py-2 text-sm font-sans focus:outline-none focus:ring-2 focus:ring-orange-500'
required placeholder='I wish UT Registration Plus could...'
/> required
/>
</div>
</div> </div>
<Button <Button

View File

@@ -37,7 +37,7 @@ export default function CalendarBottomBar({ courseCells, setCourse }: CalendarBo
{displayCourses && ( {displayCourses && (
<> <>
<Text variant='p' className='text-ut-black'> <Text variant='p' className='text-ut-black'>
ASYNC/OTHER ASYNC / OTHER
</Text> </Text>
<Text variant='h4' className='text-gray-300'> <Text variant='h4' className='text-gray-300'>

View File

@@ -76,7 +76,7 @@ export default function CalendarCourseCell({
return ( return (
<div <div
className={clsx( className={clsx(
'h-full w-0 flex justify-center rounded p-x-2 p-y-1.2 cursor-pointer screenshot:p-1.5 hover:shadow-md transition-shadow-100 ease-out', 'h-full w-0 flex justify-center rounded p-x-2 p-y-1.2 cursor-pointer hover:shadow-md transition-shadow-100 ease-out',
{ {
'min-w-full': timeAndLocation, 'min-w-full': timeAndLocation,
'w-full': !timeAndLocation, 'w-full': !timeAndLocation,

View File

@@ -3,6 +3,7 @@ import React from 'react';
import DiscordIcon from '~icons/bi/discord'; import DiscordIcon from '~icons/bi/discord';
import GithubIcon from '~icons/ri/github-fill'; import GithubIcon from '~icons/ri/github-fill';
import InstagramIcon from '~icons/ri/instagram-line'; import InstagramIcon from '~icons/ri/instagram-line';
import LinkedinIcon from '~icons/ri/linkedin-box-fill';
import Link from '../common/Link'; import Link from '../common/Link';
@@ -23,6 +24,12 @@ export default function CalendarFooter(): JSX.Element {
<Link className='linkanimate' href='https://github.com/Longhorn-Developers'> <Link className='linkanimate' href='https://github.com/Longhorn-Developers'>
<GithubIcon className='h-6 w-6' /> <GithubIcon className='h-6 w-6' />
</Link> </Link>
<Link
className='linkanimate'
href='https://www.linkedin.com/company/longhorn-developers/posts/?feedView=all'
>
<LinkedinIcon className='h-6 w-6 -mx-0.75' />
</Link>
</div> </div>
<p className='text-2.5 text-ut-concrete font-light tracking-wide'> <p className='text-2.5 text-ut-concrete font-light tracking-wide'>
UT Registration Plus is a project under Longhorn Developers, a student-led organization aimed at UT Registration Plus is a project under Longhorn Developers, a student-led organization aimed at

View File

@@ -47,7 +47,7 @@ function makeGridRow(row: number, cols: number): JSX.Element {
*/ */
export default function CalendarGrid({ export default function CalendarGrid({
courseCells, courseCells,
saturdayClass, // TODO: implement/move away from props saturdayClass: _saturdayClass, // TODO: implement/move away from props
setCourse, setCourse,
}: React.PropsWithChildren<Props>): JSX.Element { }: React.PropsWithChildren<Props>): JSX.Element {
return ( return (

View File

@@ -4,13 +4,12 @@ import { Button } from '@views/components/common/Button';
import List from '@views/components/common/List'; import List from '@views/components/common/List';
import ScheduleListItem from '@views/components/common/ScheduleListItem'; import ScheduleListItem from '@views/components/common/ScheduleListItem';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import { useEnforceScheduleLimit } from '@views/hooks/useEnforceScheduleLimit';
import useSchedules, { getActiveSchedule, switchSchedule } from '@views/hooks/useSchedules'; import useSchedules, { getActiveSchedule, switchSchedule } from '@views/hooks/useSchedules';
import React from 'react'; import React from 'react';
import AddSchedule from '~icons/material-symbols/add'; import AddSchedule from '~icons/material-symbols/add';
import { usePrompt } from '../common/DialogProvider/DialogProvider';
/** /**
* Renders a component that displays a list of schedules. * Renders a component that displays a list of schedules.
* *
@@ -19,32 +18,12 @@ import { usePrompt } from '../common/DialogProvider/DialogProvider';
*/ */
export function CalendarSchedules() { export function CalendarSchedules() {
const [, schedules] = useSchedules(); const [, schedules] = useSchedules();
const showDialog = usePrompt();
const enforceScheduleLimit = useEnforceScheduleLimit();
const handleAddSchedule = () => { const handleAddSchedule = () => {
if (schedules.length >= 10) { if (enforceScheduleLimit()) {
showDialog({ createSchedule('New Schedule');
title: `You have 10 active schedules!`,
description: (
<>
To encourage organization,{' '}
<span className='text-ut-burntorange'>please consider removing some unused schedules</span> you
may have.
</>
),
// eslint-disable-next-line react/no-unstable-nested-components
buttons: close => (
<Button variant='filled' color='ut-burntorange' onClick={close}>
I Understand
</Button>
),
});
return;
} }
createSchedule('New Schedule');
}; };
return ( return (

View File

@@ -19,29 +19,29 @@ const links: LinkItem[] = [
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20252/', url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20252/',
}, },
{ {
text: "Fall '24 Course Schedule", text: 'Course Schedule Archives',
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20249/', url: 'https://registrar.utexas.edu/schedules/archive',
}, },
// { // {
// text: "Summer '24 Course Schedule", // text: "Summer '24 Course Schedule",
// url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20246/', // url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20246/',
// }, // },
{
text: "'24-'25 Academic Calendar",
url: 'https://registrar.utexas.edu/calendars/24-25',
},
{ {
text: 'Registration Info Sheet', text: 'Registration Info Sheet',
url: 'https://utdirect.utexas.edu/registrar/ris.WBX', url: 'https://utdirect.utexas.edu/registrar/ris.WBX',
}, },
{ {
text: 'Register For Courses', text: 'Register for Courses',
url: 'https://utdirect.utexas.edu/registration/chooseSemester.WBX', url: 'https://utdirect.utexas.edu/registration/chooseSemester.WBX',
}, },
{ {
text: 'Degree Audit', text: 'Degree Audit',
url: 'https://utdirect.utexas.edu/apps/degree/audits/', url: 'https://utdirect.utexas.edu/apps/degree/audits/',
}, },
{
text: 'My Registered Courses',
url: 'https://utdirect.utexas.edu/registration/classlist.WBX',
},
]; ];
/** /**

View File

@@ -1,3 +1,5 @@
import { CRX_PAGES } from '@shared/types/CRXPages';
import { openReportWindow } from '@shared/util/openReportWindow';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';
@@ -13,26 +15,41 @@ interface LinkItem {
url: string; url: string;
} }
const links: LinkItem[] = [ const links = [
// { {
// text: 'Feedback Form', text: 'Rate us on Chrome Web Store',
// url: '#', url: 'https://chromewebstore.google.com/detail/ut-registration-plus/hboadpjkoaieogjimneceaahlppnipaa',
// }, },
// { {
// text: 'Apply to Longhorn Developers', text: 'Send us Feedback & Ideas',
// url: '#', url: CRX_PAGES.REPORT,
// }, },
{ {
text: 'Become a Beta Tester', text: 'Become a Beta Tester',
url: 'https://forms.gle/Y9dmQAb1yzW5PRg48', url: 'https://forms.gle/Y9dmQAb1yzW5PRg48',
}, },
]; {
text: 'Credits Meet the team',
url: CRX_PAGES.OPTIONS,
},
{
text: 'Apply to Longhorn Developers',
url: 'https://forms.gle/cdkLKmFwPmvHmiBe9',
},
] as const satisfies LinkItem[];
/** /**
* The "From The Team" section of the calendar website * The "From The Team" section of the calendar website
* @returns * @returns
*/ */
export default function TeamLinks({ className }: Props): JSX.Element { export default function TeamLinks({ className }: Props): JSX.Element {
const handleClick = (link: LinkItem, event: React.MouseEvent) => {
if (link.url === CRX_PAGES.REPORT) {
event.preventDefault();
openReportWindow();
}
};
return ( return (
<article className={clsx(className, 'flex flex-col gap-2')}> <article className={clsx(className, 'flex flex-col gap-2')}>
<Text variant='h3'>From the Team</Text> <Text variant='h3'>From the Team</Text>
@@ -43,6 +60,7 @@ export default function TeamLinks({ className }: Props): JSX.Element {
className='flex items-center gap-0.5 text-ut-burntorange underline-offset-2 hover:underline' className='flex items-center gap-0.5 text-ut-burntorange underline-offset-2 hover:underline'
target='_blank' target='_blank'
rel='noreferrer' rel='noreferrer'
onClick={event => handleClick(link, event)}
> >
<Text variant='p'>{link.text}</Text> <Text variant='p'>{link.text}</Text>
<OutwardArrowIcon className='h-3 w-3' /> <OutwardArrowIcon className='h-3 w-3' />

View File

@@ -1,4 +1,5 @@
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import clsx from 'clsx';
import React from 'react'; import React from 'react';
/** /**
@@ -14,26 +15,60 @@ export const flagMap = {
'Independent Inquiry': 'II', 'Independent Inquiry': 'II',
} as const satisfies Record<string, Flag>; } as const satisfies Record<string, Flag>;
interface Props { /**
label: Flag; * 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>;
type Props =
| {
variant: 'core';
label: Core;
}
| {
variant: 'flag';
label: Flag;
};
/** /**
* A reusable chip component that follows the design system of the extension. * A reusable chip component that follows the design system of the extension.
* @returns * @returns
*/ */
export function Chip({ label }: React.PropsWithChildren<Props>): JSX.Element { export function Chip({ variant, label }: React.PropsWithChildren<Props>): JSX.Element {
const longFlagName = Object.entries(flagMap).find(([full, short]) => short === label)?.[0] ?? label; let labelMap;
switch (variant) {
case 'core':
labelMap = coreMap;
break;
case 'flag':
labelMap = flagMap;
break;
default:
labelMap = {};
}
const longName = Object.entries(labelMap).find(([_full, short]) => short === label)?.[0] ?? label;
return ( return (
<Text <Text
as='div' as='div'
variant='h4' variant='h4'
className='min-w-5 inline-flex items-center justify-center gap-2.5 rounded-lg px-1.5 py-0.5' className={clsx('min-w-5 inline-flex items-center justify-center gap-2.5 rounded-lg px-1.5 py-0.5', {
style={{ 'bg-ut-yellow text-black': variant === 'flag',
backgroundColor: '#FFD600', 'bg-ut-blue text-white': variant === 'core',
}} })}
title={`${longFlagName} flag`} title={variant === 'flag' ? `${longName} flag` : `${longName} core curriculum requirement`}
> >
{label} {label}
</Text> </Text>

View File

@@ -14,7 +14,7 @@ type Props = TextProps<'a'> & {
* A reusable Text component with props that build on top of the design system for the extension * A reusable Text component with props that build on top of the design system for the extension
*/ */
export default function Link(props: PropsWithChildren<Props>): JSX.Element { export default function Link(props: PropsWithChildren<Props>): JSX.Element {
let { className, href, ...passedProps } = props; const { className, href, ...passedProps } = props;
if (href && !props.onClick) { if (href && !props.onClick) {
passedProps.onClick = e => { passedProps.onClick = e => {
@@ -37,7 +37,7 @@ export default function Link(props: PropsWithChildren<Props>): JSX.Element {
'underline cursor-pointer': !isDisabled, 'underline cursor-pointer': !isDisabled,
'cursor-not-allowed color-ut-gray': isDisabled, 'cursor-not-allowed color-ut-gray': isDisabled,
}, },
props.className className
)} )}
/> />
); );

View File

@@ -47,7 +47,7 @@ function MigrationButtons({ close }: { close: () => void }): JSX.Element {
return ( return (
<> <>
{error && ( {error && (
<Text variant='p' className='text-ut-red'> <Text variant='p' className='text-theme-red'>
An error occurred while migrating your courses. Please try again later in settings. ( An error occurred while migrating your courses. Please try again later in settings. (
{error.substring(0, 8)}) {error.substring(0, 8)})
</Text> </Text>

View File

@@ -78,8 +78,9 @@ export default function PopupCourseBlock({
<DragIndicatorIcon className='h-6 w-6 text-white' /> <DragIndicatorIcon className='h-6 w-6 text-white' />
</div> </div>
<Text className={clsx('flex-1 py-3.5 truncate', fontColor)} variant='h1-course'> <Text className={clsx('flex-1 py-3.5 truncate', fontColor)} variant='h1-course'>
<span className='px-0.5 font-450'>{formattedUniqueId}</span> {course.department} {course.number} &ndash;{' '} <span className='px-0.5 font-450'>{formattedUniqueId}</span> {course.department} {course.number}
{course.instructors.length === 0 ? 'Unknown' : course.instructors.map(v => v.lastName)} {course.instructors.length > 0 ? <> &ndash; </> : ''}
{course.instructors.map(v => v.toString({ format: 'last', case: 'capitalize' })).join('; ')}
</Text> </Text>
{enableCourseStatusChips && course.status !== Status.OPEN && ( {enableCourseStatusChips && course.status !== Status.OPEN && (
<div <div

View File

@@ -4,6 +4,7 @@ import duplicateSchedule from '@pages/background/lib/duplicateSchedule';
import renameSchedule from '@pages/background/lib/renameSchedule'; import renameSchedule from '@pages/background/lib/renameSchedule';
import type { UserSchedule } from '@shared/types/UserSchedule'; import type { UserSchedule } from '@shared/types/UserSchedule';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import { useEnforceScheduleLimit } from '@views/hooks/useEnforceScheduleLimit';
import useSchedules from '@views/hooks/useSchedules'; import useSchedules from '@views/hooks/useSchedules';
import clsx from 'clsx'; import clsx from 'clsx';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
@@ -34,6 +35,12 @@ export default function ScheduleListItem({ schedule, dragHandleProps, onClick }:
const [editorValue, setEditorValue] = useState(schedule.name); const [editorValue, setEditorValue] = useState(schedule.name);
const showDialog = usePrompt(); const showDialog = usePrompt();
const enforceScheduleLimit = useEnforceScheduleLimit();
const handleDuplicateSchedule = (scheduleId: string) => {
if (enforceScheduleLimit()) {
duplicateSchedule(scheduleId);
}
};
const editorRef = React.useRef<HTMLInputElement>(null); const editorRef = React.useRef<HTMLInputElement>(null);
useEffect(() => { useEffect(() => {
@@ -92,7 +99,7 @@ export default function ScheduleListItem({ schedule, dragHandleProps, onClick }:
</Button> </Button>
<Button <Button
variant='filled' variant='filled'
color='ut-red' color='theme-red'
onClick={() => { onClick={() => {
close(); close();
deleteSchedule(schedule.id); deleteSchedule(schedule.id);
@@ -180,7 +187,7 @@ export default function ScheduleListItem({ schedule, dragHandleProps, onClick }:
<Text <Text
as='button' as='button'
variant='small' variant='small'
onClick={() => duplicateSchedule(schedule.id)} onClick={() => handleDuplicateSchedule(schedule.id)}
className='w-full rounded bg-transparent p-2 text-left data-[focus]:bg-gray-200/40' className='w-full rounded bg-transparent p-2 text-left data-[focus]:bg-gray-200/40'
> >
Duplicate Duplicate
@@ -191,7 +198,7 @@ export default function ScheduleListItem({ schedule, dragHandleProps, onClick }:
as='button' as='button'
variant='small' variant='small'
onClick={handleDelete} onClick={handleDelete}
className='w-full rounded bg-transparent p-2 text-left text-ut-red data-[focus]:bg-red-200/40' className='w-full rounded bg-transparent p-2 text-left text-theme-red data-[focus]:bg-red-200/40'
> >
Delete Delete
</Text> </Text>

View File

@@ -27,7 +27,7 @@ export default function ScheduleTotalHoursAndCourses({
</Text> </Text>
<Text variant='h3' as='div' className='flex flex-row items-center gap-2 text-theme-black'> <Text variant='h3' as='div' className='flex flex-row items-center gap-2 text-theme-black'>
{totalHours} {totalHours === 1 ? 'Hour' : 'Hours'} {totalHours} {totalHours === 1 ? 'Hour' : 'Hours'}
<Text variant='h4' as='span' className='hidden text-ut-black capitalize screenshot:inline sm:inline'> <Text variant='h4' as='span' className='hidden capitalize screenshot:inline sm:inline'>
{totalCourses} {totalCourses === 1 ? 'Course' : 'Courses'} {totalCourses} {totalCourses === 1 ? 'Course' : 'Courses'}
</Text> </Text>
</Text> </Text>

View File

@@ -59,7 +59,7 @@
.h1-course { .h1-course {
font-size: 1rem; font-size: 1rem;
font-weight: 600; font-weight: 550;
text-transform: capitalize; text-transform: capitalize;
letter-spacing: 0; letter-spacing: 0;
} }

View File

@@ -211,8 +211,11 @@ export default function GradeDistribution({ course }: GradeDistributionProps): J
{status === DataStatus.FOUND && ( {status === DataStatus.FOUND && (
<> <>
<div className='flex flex-wrap content-center items-center self-stretch justify-center gap-3'> <div className='flex flex-wrap content-center items-center self-stretch justify-center gap-3'>
<Text variant='small'> <Text variant='small' className='text-ut-black'>
Grade Distribution for {course.department} {course.number} Grade Distribution for{' '}
<Text variant='small' className='font-extrabold!' as='strong'>
{course.department} {course.number}
</Text>
</Text> </Text>
<select <select
className='border border rounded border-solid px-3 py-2' className='border border rounded border-solid px-3 py-2'

View File

@@ -3,7 +3,7 @@ import type { Course } from '@shared/types/Course';
import type Instructor from '@shared/types/Instructor'; import type Instructor from '@shared/types/Instructor';
import type { UserSchedule } from '@shared/types/UserSchedule'; import type { UserSchedule } from '@shared/types/UserSchedule';
import { Button } from '@views/components/common/Button'; import { Button } from '@views/components/common/Button';
import { Chip, flagMap } from '@views/components/common/Chip'; import { Chip, coreMap, flagMap } from '@views/components/common/Chip';
import Divider from '@views/components/common/Divider'; import Divider from '@views/components/common/Divider';
import Link from '@views/components/common/Link'; import Link from '@views/components/common/Link';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
@@ -49,16 +49,13 @@ const capitalizeString = (str: string) => str.charAt(0).toUpperCase() + str.slic
* @returns {JSX.Element} The rendered component. * @returns {JSX.Element} The rendered component.
*/ */
export default function HeadingAndActions({ course, activeSchedule, onClose }: HeadingAndActionProps): JSX.Element { 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 courseAdded = activeSchedule.courses.some(ourCourse => ourCourse.uniqueId === uniqueId);
const formattedUniqueId = uniqueId.toString().padStart(5, '0'); const formattedUniqueId = uniqueId.toString().padStart(5, '0');
const isInCalendar = useCalendar(); const isInCalendar = useCalendar();
const getInstructorFullName = (instructor: Instructor) => { const getInstructorFullName = (instructor: Instructor) =>
const { firstName = '', lastName = '' } = instructor; instructor.toString({ format: 'first_last', case: 'capitalize' });
if (firstName === '') return capitalizeString(lastName);
return `${capitalizeString(firstName)} ${capitalizeString(lastName)}`;
};
const getBuildingUrl = (building: string) => const getBuildingUrl = (building: string) =>
`https://utdirect.utexas.edu/apps/campus/buildings/nlogon/maps/UTM/${building}`; `https://utdirect.utexas.edu/apps/campus/buildings/nlogon/maps/UTM/${building}`;
@@ -95,6 +92,12 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
const url = `https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?year=&semester=&department=${department}&course_number=${courseNumber}&course_title=&unique=&instructor_first=${firstName}&instructor_last=${lastName}&course_type=In+Residence&search=Search`; const url = `https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?year=&semester=&department=${department}&course_number=${courseNumber}&course_title=&unique=&instructor_first=${firstName}&instructor_last=${lastName}&course_type=In+Residence&search=Search`;
openNewTab({ url }); openNewTab({ url });
} }
// Show the course's syllabi when no instructors listed
if (instructors.length === 0) {
const url = `https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?year=&semester=&department=${department}&course_number=${courseNumber}&course_title=&unique=&instructor_first=&instructor_last=&course_type=In+Residence&search=Search`;
openNewTab({ url });
}
}; };
const handleAddOrRemoveCourse = async () => { const handleAddOrRemoveCourse = async () => {
@@ -119,13 +122,13 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
<Button color='ut-burntorange' variant='single' icon={Copy} onClick={handleCopy}> <Button color='ut-burntorange' variant='single' icon={Copy} onClick={handleCopy}>
{formattedUniqueId} {formattedUniqueId}
</Button> </Button>
<button className='bg-transparent p-0 text-theme-black btn' onClick={onClose}> <button className='bg-transparent p-0 text-ut-black btn' onClick={onClose}>
<CloseIcon className='h-7 w-7' /> <CloseIcon className='h-7 w-7' />
</button> </button>
</div> </div>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
{instructors.length > 0 && ( {instructors.length > 0 ? (
<Text variant='h4' as='p' className='items-center justify-center'> <Text variant='h4' as='p'>
with{' '} with{' '}
{instructors {instructors
.map(instructor => ( .map(instructor => (
@@ -140,12 +143,24 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
)) ))
.flatMap((el, i) => (i === 0 ? [el] : [', ', el]))} .flatMap((el, i) => (i === 0 ? [el] : [', ', el]))}
</Text> </Text>
) : (
<Text variant='h4' as='p'>
(No instructor has been provided)
</Text>
)} )}
<div className='flex items-center gap-1'> <div className='flex items-center gap-1'>
{flags.map((flag: string) => ( {flags.map((flag: string) => (
<Chip <Chip
key={flagMap[flag as keyof typeof flagMap]} key={flagMap[flag as keyof typeof flagMap]}
label={flagMap[flag as keyof typeof flagMap]} label={flagMap[flag as keyof typeof flagMap]}
variant='flag'
/>
))}
{core.map((coreVal: string) => (
<Chip
key={coreMap[coreVal as keyof typeof coreMap]}
label={coreMap[coreVal as keyof typeof coreMap]}
variant='core'
/> />
))} ))}
</div> </div>
@@ -199,10 +214,22 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
}} }}
/> />
<Divider size='1.75rem' orientation='vertical' /> <Divider size='1.75rem' orientation='vertical' />
<Button variant='outline' color='ut-blue' icon={Reviews} onClick={handleOpenRateMyProf}> <Button
variant='outline'
color='ut-blue'
icon={Reviews}
onClick={handleOpenRateMyProf}
disabled={instructors.length === 0}
>
RateMyProf RateMyProf
</Button> </Button>
<Button variant='outline' color='ut-teal' icon={Mood} onClick={handleOpenCES}> <Button
variant='outline'
color='ut-teal'
icon={Mood}
onClick={handleOpenCES}
disabled={instructors.length === 0}
>
CES CES
</Button> </Button>
<Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}> <Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}>

View File

@@ -79,11 +79,11 @@ const useDevMode = (targetCount: number): [boolean, () => void] => {
* @returns The Settings component. * @returns The Settings component.
*/ */
export default function Settings(): JSX.Element { export default function Settings(): JSX.Element {
const [enableCourseStatusChips, setEnableCourseStatusChips] = useState<boolean>(false); const [_enableCourseStatusChips, setEnableCourseStatusChips] = useState<boolean>(false);
const [showTimeLocation, setShowTimeLocation] = useState<boolean>(false); const [_showTimeLocation, setShowTimeLocation] = useState<boolean>(false);
const [highlightConflicts, setHighlightConflicts] = useState<boolean>(false); const [highlightConflicts, setHighlightConflicts] = useState<boolean>(false);
const [loadAllCourses, setLoadAllCourses] = useState<boolean>(false); const [loadAllCourses, setLoadAllCourses] = useState<boolean>(false);
const [enableDataRefreshing, setEnableDataRefreshing] = useState<boolean>(false); const [_enableDataRefreshing, setEnableDataRefreshing] = useState<boolean>(false);
const showMigrationDialog = useMigrationDialog(); const showMigrationDialog = useMigrationDialog();
@@ -216,6 +216,7 @@ export default function Settings(): JSX.Element {
try { try {
response = await fetch(link); response = await fetch(link);
} catch (e) { } catch (e) {
// eslint-disable-next-line no-alert
alert(`Failed to fetch url '${link}'`); alert(`Failed to fetch url '${link}'`);
return; return;
} }
@@ -410,7 +411,7 @@ export default function Settings(): JSX.Element {
</div> </div>
<Button <Button
variant='outline' variant='outline'
color='ut-red' color='theme-red'
icon={DeleteForeverIcon} icon={DeleteForeverIcon}
onClick={handleEraseAll} onClick={handleEraseAll}
> >
@@ -427,7 +428,7 @@ export default function Settings(): JSX.Element {
</div> </div>
<Text <Text
variant='h2-course' variant='h2-course'
className={clsx('text-center text-ut-red !font-normal', { className={clsx('text-center text-theme-red !font-normal', {
'line-through': highlightConflicts, 'line-through': highlightConflicts,
})} })}
> >
@@ -489,7 +490,7 @@ export default function Settings(): JSX.Element {
<p className='text-xs text-ut-green'> <p className='text-xs text-ut-green'>
{githubStats.adminGitHubStats[admin.githubUsername]?.linesAdded} ++ {githubStats.adminGitHubStats[admin.githubUsername]?.linesAdded} ++
</p> </p>
<p className='text-xs text-ut-red'> <p className='text-xs text-theme-red'>
{githubStats.adminGitHubStats[admin.githubUsername]?.linesDeleted} -- {githubStats.adminGitHubStats[admin.githubUsername]?.linesDeleted} --
</p> </p>
</div> </div>
@@ -499,9 +500,13 @@ export default function Settings(): JSX.Element {
</div> </div>
</section> </section>
<section className='my-8'> <section className='my-8'>
<h2 className='mb-4 text-xl text-ut-black font-semibold'>UTRP CONTRIBUTERS</h2> <h2 className='mb-4 text-xl text-ut-black font-semibold'>UTRP CONTRIBUTORS</h2>
<div className='grid grid-cols-2 gap-4 2xl:grid-cols-4 md:grid-cols-3 xl:grid-cols-3'> <div className='grid grid-cols-2 gap-4 2xl:grid-cols-4 md:grid-cols-3 xl:grid-cols-3'>
{LONGHORN_DEVELOPERS_SWE.map(swe => ( {LONGHORN_DEVELOPERS_SWE.sort(
(a, b) =>
(githubStats?.userGitHubStats[b.githubUsername]?.commits ?? 0) -
(githubStats?.userGitHubStats[a.githubUsername]?.commits ?? 0)
).map(swe => (
<div <div
key={swe.githubUsername} key={swe.githubUsername}
className='border border-gray-300 rounded bg-ut-gray/10 p-4' className='border border-gray-300 rounded bg-ut-gray/10 p-4'
@@ -531,7 +536,7 @@ export default function Settings(): JSX.Element {
<p className='text-xs text-ut-green'> <p className='text-xs text-ut-green'>
{githubStats.userGitHubStats[swe.githubUsername]?.linesAdded} ++ {githubStats.userGitHubStats[swe.githubUsername]?.linesAdded} ++
</p> </p>
<p className='text-xs text-ut-red'> <p className='text-xs text-theme-red'>
{githubStats.userGitHubStats[swe.githubUsername]?.linesDeleted} -- {githubStats.userGitHubStats[swe.githubUsername]?.linesDeleted} --
</p> </p>
</div> </div>
@@ -546,6 +551,11 @@ export default function Settings(): JSX.Element {
admin => admin.githubUsername === username admin => admin.githubUsername === username
) && !LONGHORN_DEVELOPERS_SWE.some(swe => swe.githubUsername === username) ) && !LONGHORN_DEVELOPERS_SWE.some(swe => swe.githubUsername === username)
) )
.sort(
(a, b) =>
(githubStats.userGitHubStats[b]?.commits ?? 0) -
(githubStats.userGitHubStats[a]?.commits ?? 0)
)
.map(username => ( .map(username => (
<div <div
key={username} key={username}
@@ -574,7 +584,7 @@ export default function Settings(): JSX.Element {
<p className='text-xs text-ut-green'> <p className='text-xs text-ut-green'>
{githubStats.userGitHubStats[username]?.linesAdded} ++ {githubStats.userGitHubStats[username]?.linesAdded} ++
</p> </p>
<p className='text-xs text-ut-red'> <p className='text-xs text-theme-red'>
{githubStats.userGitHubStats[username]?.linesDeleted} -- {githubStats.userGitHubStats[username]?.linesDeleted} --
</p> </p>
</div> </div>

View File

@@ -65,8 +65,9 @@ export default function SentryProvider({
integrations, integrations,
transport: makeFetchTransport, transport: makeFetchTransport,
stackParser: defaultStackParser, stackParser: defaultStackParser,
// debug: true, debug: import.meta.env.DEV,
release: import.meta.env.VITE_PACKAGE_VERSION, release: import.meta.env.VITE_PACKAGE_VERSION,
environment: import.meta.env.VITE_SENTRY_ENVIRONMENT,
}; };
let client: Client; let client: Client;

View File

@@ -0,0 +1,45 @@
import useSchedules from '@views/hooks/useSchedules';
import React, { useCallback } from 'react';
import { Button } from '../components/common/Button';
import { usePrompt } from '../components/common/DialogProvider/DialogProvider';
const SCHEDULE_LIMIT = 10;
/**
* Hook that creates a function that enforces a maximum amount of schedules
*
* If a new schedule can be created without exceeding the limit, the function returns true
* Otherwise, display a prompt explaining the limit, and returns false
*
* @returns a function, () => boolean
*/
export function useEnforceScheduleLimit(): () => boolean {
const [, schedules] = useSchedules();
const showDialog = usePrompt();
return useCallback(() => {
if (schedules.length >= SCHEDULE_LIMIT) {
showDialog({
title: `You have ${SCHEDULE_LIMIT} active schedules!`,
description: (
<>
To encourage organization,{' '}
<span className='text-ut-burntorange'>please consider removing some unused schedules</span> you
may have.
</>
),
buttons: close => (
<Button variant='filled' color='ut-burntorange' onClick={close}>
I Understand
</Button>
),
});
return false;
}
return true;
}, [schedules, showDialog]);
}

View File

@@ -95,9 +95,11 @@ function extractCourseInfo(course: Course) {
let courseDeptAndInstr = `${course.department} ${course.number}`; let courseDeptAndInstr = `${course.department} ${course.number}`;
const mainInstructor = course.instructors[0]; if (course.instructors.length > 0) {
if (mainInstructor) { courseDeptAndInstr += ' \u2013 ';
courseDeptAndInstr += ` ${mainInstructor.toString({ format: 'first_last', case: 'capitalize' })}`; courseDeptAndInstr += course.instructors
.map(instructor => instructor.toString({ format: 'last', case: 'capitalize' }))
.join('; ');
} }
return { status, courseDeptAndInstr, meetings, course }; return { status, courseDeptAndInstr, meetings, course };

View File

@@ -19,10 +19,9 @@ const TableDataSelector = {
SCHEDULE_HOURS: 'td[data-th="Hour"]>span', SCHEDULE_HOURS: 'td[data-th="Hour"]>span',
SCHEDULE_LOCATION: 'td[data-th="Room"]>span', SCHEDULE_LOCATION: 'td[data-th="Room"]>span',
FLAGS: 'td[data-th="Flags"] ul li', FLAGS: 'td[data-th="Flags"] ul li',
CORE_CURRICULUM: 'td[data-th="Core"] ul li',
} as const satisfies Record<string, string>; } as const satisfies Record<string, string>;
type TableDataSelectorType = (typeof TableDataSelector)[keyof typeof TableDataSelector];
/** /**
* The selectors that we use to scrape the course details page for an individual course (https://utdirect.utexas.edu/apps/registrar/course_schedule/20239/52700/) * The selectors that we use to scrape the course details page for an individual course (https://utdirect.utexas.edu/apps/registrar/course_schedule/20239/52700/)
*/ */
@@ -31,8 +30,6 @@ const DetailsSelector = {
COURSE_DESCRIPTION: '#details p', COURSE_DESCRIPTION: '#details p',
} as const; } as const;
type DetailsSelectorType = (typeof DetailsSelector)[keyof typeof DetailsSelector];
/** /**
* A class that allows us to scrape information from UT's course catalog to create our internal representation of a course * A class that allows us to scrape information from UT's course catalog to create our internal representation of a course
*/ */
@@ -99,6 +96,7 @@ export class CourseCatalogScraper {
semester: this.getSemester(), semester: this.getSemester(),
scrapedAt: Date.now(), scrapedAt: Date.now(),
colors: getCourseColors('emerald', 500), colors: getCourseColors('emerald', 500),
core: this.getCore(row),
}); });
courses.push({ courses.push({
element: row, element: row,
@@ -337,6 +335,21 @@ export class CourseCatalogScraper {
return Array.from(lis).map(li => li.textContent || ''); 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. * 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 * @param row the row of the course catalog table

View File

@@ -43,11 +43,11 @@ export const LONGHORN_DEVELOPERS_ADMINS = [
{ name: 'Diego Perez', role: 'Staff Engineer', githubUsername: 'doprz' }, { name: 'Diego Perez', role: 'Staff Engineer', githubUsername: 'doprz' },
{ name: 'Lukas Zenick', role: 'Senior Software Engineer', githubUsername: 'Lukas-Zenick' }, { name: 'Lukas Zenick', role: 'Senior Software Engineer', githubUsername: 'Lukas-Zenick' },
{ name: 'Isaiah Rodriguez', role: 'Chief Operations and Design Officer', githubUsername: 'IsaDavRod' }, { name: 'Isaiah Rodriguez', role: 'Chief Operations and Design Officer', githubUsername: 'IsaDavRod' },
{ name: 'Samuel Gunter', role: 'Senior Software Engineer', githubUsername: 'Samathingamajig' },
{ name: 'Derek Chen', role: 'Senior Software Engineer', githubUsername: 'DereC4' },
] as const satisfies TeamMember[]; ] as const satisfies TeamMember[];
export const LONGHORN_DEVELOPERS_SWE = [ export const LONGHORN_DEVELOPERS_SWE = [
{ name: 'Samuel Gunter', role: 'Software Engineer', githubUsername: 'Samathingamajig' },
{ name: 'Derek Chen', role: 'Software Engineer', githubUsername: 'DereC4' },
{ name: 'Casey Charleston', role: 'Software Engineer', githubUsername: 'caseycharleston' }, { name: 'Casey Charleston', role: 'Software Engineer', githubUsername: 'caseycharleston' },
{ name: 'Vinson', role: 'Software Engineer', githubUsername: 'vinsonzheng499' }, { name: 'Vinson', role: 'Software Engineer', githubUsername: 'vinsonzheng499' },
{ name: 'Vivek', role: 'Software Engineer', githubUsername: 'vivek12311' }, { name: 'Vivek', role: 'Software Engineer', githubUsername: 'vivek12311' },

View File

@@ -10,7 +10,7 @@ $turquoise: #00a9b7;
$bluebonnet: #005f86; $bluebonnet: #005f86;
$shade: #9cadb7; $shade: #9cadb7;
$limestone: #d6d2c4; $limestone: #d6d2c4;
$speedway_brick: #af2e2d; $speedway_brick: #d10000;
//scss hover active focus color calculation //scss hover active focus color calculation

View File

@@ -22,6 +22,13 @@ if (isBeta) {
process.env.VITE_BETA_BUILD = 'true'; process.env.VITE_BETA_BUILD = 'true';
} }
process.env.VITE_PACKAGE_VERSION = packageJson.version; process.env.VITE_PACKAGE_VERSION = packageJson.version;
if (process.env.PROD) {
process.env.VITE_SENTRY_ENVIRONMENT = 'production';
} else if (isBeta) {
process.env.VITE_SENTRY_ENVIRONMENT = 'beta';
} else {
process.env.VITE_SENTRY_ENVIRONMENT = 'development';
}
export const preambleCode = ` export const preambleCode = `
import RefreshRuntime from "__BASE__@react-refresh" import RefreshRuntime from "__BASE__@react-refresh"
@@ -89,10 +96,13 @@ export default defineConfig({
apply: 'serve', apply: 'serve',
transform(code, id) { transform(code, id) {
if (id.endsWith('.tsx') || id.endsWith('.ts') || id.endsWith('?url')) { if (id.endsWith('.tsx') || id.endsWith('.ts') || id.endsWith('?url')) {
return code.replace( return {
/(['"])(\/public\/.*?)(['"])/g, code: code.replace(
(_, quote1, path, quote2) => `chrome.runtime.getURL(${quote1}${path}${quote2})` /(['"])(\/public\/.*?)(['"])/g,
); (_, quote1, path, quote2) => `chrome.runtime.getURL(${quote1}${path}${quote2})`
),
map: null,
};
} }
}, },
}, },
@@ -101,10 +111,13 @@ export default defineConfig({
apply: 'build', apply: 'build',
transform(code, id) { transform(code, id) {
if (id.endsWith('.tsx') || id.endsWith('.ts') || id.endsWith('?url')) { if (id.endsWith('.tsx') || id.endsWith('.ts') || id.endsWith('?url')) {
return code.replace( return {
/(['"])(__VITE_ASSET__.*?__)(['"])/g, code: code.replace(
(_, quote1, path, quote2) => `chrome.runtime.getURL(${quote1}${path}${quote2})` /(['"])(__VITE_ASSET__.*?__)(['"])/g,
); (_, quote1, path, quote2) => `chrome.runtime.getURL(${quote1}${path}${quote2})`
),
map: null,
};
} }
}, },
}, },
@@ -114,13 +127,16 @@ export default defineConfig({
enforce: 'post', enforce: 'post',
transform(code, id) { transform(code, id) {
if (process.env.NODE_ENV === 'development' && (id.endsWith('.css') || id.endsWith('.scss'))) { if (process.env.NODE_ENV === 'development' && (id.endsWith('.css') || id.endsWith('.scss'))) {
return code.replace( return {
/url\((.*?)\)/g, code: code.replace(
(_, path) => /url\((.*?)\)/g,
`url(\\"" + chrome.runtime.getURL(${path (_, path) =>
.replaceAll(`\\"`, `"`) `url(\\"" + chrome.runtime.getURL(${path
.replace(/public\//, '')}) + "\\")` .replaceAll(`\\"`, `"`)
); .replace(/public\//, '')}) + "\\")`
),
map: null,
};
} }
}, },
}, },
@@ -133,9 +149,8 @@ export default defineConfig({
/(__VITE_ASSET__.*?__)/g, /(__VITE_ASSET__.*?__)/g,
(_, path) => `chrome-extension://__MSG_@@extension_id__${path}` (_, path) => `chrome-extension://__MSG_@@extension_id__${path}`
); );
return transformedCode; return { code: transformedCode, map: null };
} }
return code;
}, },
}, },
renameFile('src/pages/debug/index.html', 'debug.html'), renameFile('src/pages/debug/index.html', 'debug.html'),
@@ -143,7 +158,7 @@ export default defineConfig({
renameFile('src/pages/calendar/index.html', 'calendar.html'), renameFile('src/pages/calendar/index.html', 'calendar.html'),
renameFile('src/pages/report/index.html', 'report.html'), renameFile('src/pages/report/index.html', 'report.html'),
vitePluginRunCommandOnDemand({ vitePluginRunCommandOnDemand({
afterServerStart: 'pnpm gulp forceDisableUseDynamicUrl', // afterServerStart: 'pnpm gulp forceDisableUseDynamicUrl',
closeBundle: 'pnpm gulp forceDisableUseDynamicUrl', closeBundle: 'pnpm gulp forceDisableUseDynamicUrl',
}), }),
], ],
@@ -206,4 +221,11 @@ export default defineConfig({
provider: 'v8', provider: 'v8',
}, },
}, },
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
},
},
},
}); });