Compare commits

..

116 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
ec63b5e903 chore: update Prettier to v3.6.2
Co-authored-by: Razboy20 <29903962+Razboy20@users.noreply.github.com>
2025-11-17 20:02:45 +00:00
copilot-swe-agent[bot]
3ec42bf207 chore: update ESLint to v9 and migrate to flat config format
Co-authored-by: Razboy20 <29903962+Razboy20@users.noreply.github.com>
2025-11-17 20:01:00 +00:00
copilot-swe-agent[bot]
c4193d52d3 Initial plan 2025-11-17 19:49:35 +00:00
Vincent Situ
6a67a32e4f feat: allow bypassing the 10-schedule limit (#675)
* feat: allow bypassing the 10-schedule limit

* feat: option to bypass 10 schedules- code done, not tested

* feat(ui): option to bypass 10 schedules

* feat(ui): button to lead to settings after reaching max limit

* Delete manifest.json

* Remove cross-env from build:watch script

* Remove cross-env and update caniuse-lite version

Removed cross-env dependency and updated caniuse-lite version.

* chore: lint

---------

Co-authored-by: Derek <derex1987@gmail.com>
2025-11-10 13:39:36 -06:00
Uthman
549c52a39f feat: add drag-and-drop import for schedules (#661)
* testing

* feat: add drag-and-drop support to FileUpload (#446)

* chore: remove testing comment

* chore: fix lint issues

* chore: format FileUpload.tsx with prettier

---------

Co-authored-by: Uthman Ogungbo <uthmanogungbo@Uthmans-MacBook-Pro.local>
Co-authored-by: Uthman Ogungbo <uthmanogungbo@wireless-10-148-166-229.public.utexas.edu>
Co-authored-by: Derek <derex1987@gmail.com>
2025-11-06 23:03:55 -06:00
doprz
36007b9d1a docs: add CODE_OF_CONDUCT.md (#651)
Added a Code of Conduct document to outline community guidelines and expectations.

Co-authored-by: Derek <derex1987@gmail.com>
2025-11-05 16:58:16 -06:00
cee5f0284f feat: condense resourceLinks course schedule (#676)
* chore: remove summer 2025 schedule list item

* feat: compress course schedules into one link

* Revise ResourceLinks with Spring '26 schedule

Updated course schedule links for Spring '26.
2025-11-05 16:38:00 -06:00
dependabot[bot]
e8a8b8e1ae chore(deps): bump the npm_and_yarn group across 1 directory with 4 updates (#639)
Bumps the npm_and_yarn group with 4 updates in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite), [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers), [brace-expansion](https://github.com/juliangruber/brace-expansion) and [undici](https://github.com/nodejs/undici).


Updates `vite` from 5.4.14 to 5.4.20
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.20/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.20/packages/vite)

Updates `@babel/helpers` from 7.26.9 to 7.28.4
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.28.4/packages/babel-helpers)

Updates `brace-expansion` from 1.1.11 to 1.1.12
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12)

Updates `undici` from 6.21.1 to 6.22.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.21.1...v6.22.0)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 5.4.20
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: "@babel/helpers"
  dependency-version: 7.28.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: undici
  dependency-version: 6.22.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 11:36:53 -05:00
doprz
c21cbd77f0 feat(release): v2.2.2 2025-10-13 11:20:01 -05:00
99a035e29d chore: remove summer 2025 schedule list item (#637) 2025-10-13 02:25:27 -05:00
doprz
64baa6d290 refactor(nix): dev shells (#634) 2025-10-12 22:47:47 -05:00
Samuel Gunter
46fe591fa7 fix: whitespace wrapping in semester warning (#629) 2025-10-07 18:28:41 -05:00
doprz
8f7e1bc0af feat(env): add SENTRY env vars 2025-10-07 16:11:14 -05:00
doprz
9fc1098ef7 chore(env): add .env.example 2025-10-07 16:00:40 -05:00
Warith Rahman
ae094416fc chore(ui): added spring 2026 course schedule (#628) 2025-10-07 15:26:15 -05:00
ishita778
2e7dac1e3e feat: show warning for courses of different semesters (#570)
* chore: removed extra space at calendar footer

* chore: fixed eslint issues

* chore: changed return type to react node

* chore: displaycourses true fixes and checks fixed

* chore: prettier fix

* feat: not working same semester course issue

* feat: modifying components to use the new hook

* feat: small fixes

* fix: remove comments and spaces

* fix: dialog error solved

* fix: add to new schedule

* fix: prettier

* fix: delete unnecessary custom hook and p[rettier

* fix: checks all passing

* fix: added requested changes

* fix: added new conditions

* fix: description fixed

* style: fix Roboto Flex not being used as font in dialog

* fix: made requested changes

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-08-27 13:41:36 -05:00
doprz
7bea23a655 refactor: nix flake (#625)
* chore(nix): update flake

* chore(nix): remove just and update comment

* chore(nix): update node version + volta config

* refactor: nix flake
2025-08-25 11:33:44 -07:00
Samuel Gunter
3d28869e92 chore: spring 2025 grades (#624) 2025-08-18 00:29:59 -05:00
doprz
f0f1f0b365 chore: bump node and pnpm version (#620)
* chore(nix): update flake

* chore(nix): remove just and update comment

* chore(nix): update node version + volta config
2025-08-12 14:00:45 -05:00
be861b823c feat: search result shading (#617)
* feat: site support kws

* feat: function

* feat: stuff before bedtime

* feat: shading function

* feat: shading

* feat: shading the table children

* chore: fix lint issues

* feat: dependency array

* feat: remove

* feat: remove temp console log
2025-08-07 13:28:56 -05:00
Samuel Gunter
95de8df372 fix: fix or ignore various eslint warning (#609) 2025-07-16 07:54:40 -07:00
5994ded8be feat: export schedule button add to calendar (#594)
* feat: export schedule button add to calendar add to util too

* docs: hypen bruh

* chore: lowercase

* style: filecode icon

* chore: unused import

* refactor: use export json deleted old function

* chore: linting

* chore: remove useless import

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-06-17 11:57:48 -07:00
doprz
7b401add15 feat: add nix flake (#593)
Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-06-08 23:16:43 -05:00
Samuel Gunter
2d92dd47f0 feat: support summer grades, fix summer course parser (#596)
* feat: support summer grades, fix summer course parser

* chore: lint

* docs: mention summer terms in Course::number description

* feat: Course::getNumberWithoutTerm, strip summer term indicator when displaying grades

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-06-08 21:10:05 -07:00
Aiyaz Mostofa
eb8141ee8c fix: limit height of schedule list dropdown in the extension popup (#543)
* fix: limit height of schedule list dropdown in the extension popup

* fix: limit the entire dropdown to 200px, not just the schedule list

* fix: use flexbox for dropdown and wedge scrollbar inside margin

* fix: use DisclosurePanel in schedule dropdown, do Uno class sorting

---------

Co-authored-by: Ethan Lanting <ethanlanting@gmail.com>
Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-06-08 20:54:36 -07:00
sjalkote
2a50f5580d feat: automatically select new or duplicated schedules (#583) (#589)
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-06-08 20:39:46 -07:00
doprz
65bfb1d129 docs: add pnpm and update versions (#597) 2025-06-08 15:57:12 -05:00
doprz
234f3d627d feat(release): v2.2.1 2025-06-03 19:46:36 -05:00
Ethan Lanting
be1dccfcb9 feat: add dining app promo (#598)
* feat: add DiningAppPromo component and integrate it into Calendar

* feat: update WhatsNewPopup with new features and app download link

* fix: remove outdated links

* chore: run lint

* chore: run prettier

* feat: enhance DiningAppPromo with close button and integrate user preference for promo visibility

* chore: run lint

* chore: run check types

* fix: correct promo visibility logic in Calendar component

* feat: centralize app store URLs in appUrls.ts

* chore: run lint

* feat: integrate UT Dining promo image

* chore: run lint

* fix: update logo in WhatsNew popup to use LD icon

* fix: convert URLs to URL objects for consistency

* fix: update LD icon in WhatsNew popup to new version

* fix: update description for Coffee Shops feature to clarify operating times

* fix: rename promo state and storage key to showUTDiningPromo for clarity

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-05-28 20:13:45 -05:00
Razboy20
454e5e807a fix: hide sentry instrumentation on debug builds (#604)
* fix: hide sentry instrumentation on debug builds

* fix: amend documentation to reference zip:to-public rather than zip
2025-05-27 23:35:11 -05:00
Razboy20
29d20d5c5a chore: setup dependabot configuration (#603)
* chore: setup dependabot configuration
2025-05-27 22:55:11 -05:00
doprz
e29546c727 chore: update issue templates 2025-05-27 21:55:36 -05:00
doprz
5a89be6238 chore: update issue templates 2025-05-27 21:53:42 -05:00
Samuel Gunter
cfb5faa09b fix: course columns on calendar (#587)
* fix: course columns on calendar

* test: new tests for course cell columns, extract calls to help function

* fix: gracefully handle async courses

* refactor: split course cell column logic into multiple functions, add comments, and derive fields

* chore: comments, for-index to for-of

* chore: fix typo

* fix: don't use sentry in storybook contexts

* fix: silence, brand

* refactor: don't rely on calculating columns, find as you go

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-05-06 16:07:27 -05:00
37471efb74 feat: inside jokes005 (#590)
* feat: moved some of my lines together removed one of mine and added jack black

* feat: just ctrl x ctrl v a few lines

* Update insideJokes.tsx
2025-04-13 21:33:52 -05:00
doprz
7a4f40a765 feat(release): v2.2.0 2025-04-06 18:11:27 -05:00
doprz
d11d55db66 chore: update exec list (#580)
* chore: update exec list

* chore: update roles

* chore: update h2

* fix: key

* chore: caps
2025-04-05 14:33:44 -05:00
Samuel Gunter
76b6aa7c15 fix: include logo in screenshot, fix screenshots on small/zoomed windows (#579)
* fix: include logo in screenshot

* fix: screenshots on small/zoomed windows, screenshots with no async/other

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
2025-04-05 11:21:37 -05:00
Samuel Gunter
70d4fecad6 feat: recruitment banner for designer (#578) 2025-04-04 09:56:37 -05:00
Samuel Gunter
c3fa91752c chore: bump migration dialog message number, remove unused UpdateText component (#577) 2025-04-02 10:00:05 -05:00
Ethan Lanting
7c2beef193 feat: auto create empty schedule when deleted all schedules (#552)
* feat: enhance schedule deletion to create a new schedule if none remain

* feat: set active index to new schedule if only one exists

* chore: run lint

* feat: enhance schedule deletion to create a new schedule if none remain

* feat: set active index to new schedule if only one exists

* chore: run lint

* feat: reset schedules on update, refactor invariant to within deleteSchedule

* chore: pnpm lint

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
2025-04-01 13:24:08 -05:00
Samuel Gunter
630d0d80d2 chore: professionalism in jokes (#572) 2025-03-26 14:16:06 -05:00
Samuel Gunter
695743104c feat: persist sidebar toggle state (#569) 2025-03-26 13:50:34 -05:00
Samuel Gunter
d014244b28 chore: pin volta pnpm version (#573) 2025-03-25 23:00:34 -05:00
Samuel Gunter
5cd56259f7 test: fix flaky sleep test (#571) 2025-03-24 02:28:41 -05:00
Samuel Gunter
fa9f78b46e feat: sticky calendar header and days (#568)
* feat: sticky calendar days

* feat: partial height borders for day labels

* feat: make calendar header actually sticky

* fix: remove unneeded gap

* refactor: add preston as co-author

Co-authored-by: Preston-Cook <preston.l.cook@gmail.com>

* fix: z-index issues with export sub-buttons

---------

Co-authored-by: Preston-Cook <preston.l.cook@gmail.com>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-03-23 19:49:11 -05:00
Samuel Gunter
4a5f67f0fd fix: ics calendar export dates (#535)
* feat: academicCalendars object

* feat: seemingly working start, end, and until dates

* feat: seemingly working everything

* style: removed unnecessary deps, reorganized code

* style: code comments yay

* chore: old version of pnpm?

* ci: force github actions to rerun

* feat: list instructors in ics string, basic tests

* feat: testable code for ICS, tests for ICS, filter excluded dates

* style: eslint autofix

* test: check for graceful handling of errors in ICS

* fix: actually use scheduleToIcsString

* chore: eslint didn't include a space where it should've

* fix: ensure tz everywhere

* refactor: move string util to string util file

* feat: em dash in calendar event title

* feat: academic calendars 22-23 and 23-24

* fix: en dash instead of em dash
2025-03-22 22:55:16 -05:00
Yash Kukrecha
3bed9cc27f fix(schedule): truncate long schedule names in popup (#564) 2025-03-22 21:10:21 -05:00
ishita778
0dcae25b93 chore: removed extra space at calendar footer (#557)
* chore: removed extra space at calendar footer

* chore: fixed eslint issues

* chore: changed return type to react node

* chore: displaycourses true fixes and checks fixed

* chore: prettier fix
2025-03-18 04:00:46 -05:00
beastgwert
ca734dcd39 feat: rework start time to checkboxes (#553)
* feat: replace dropdown with checkbox

* refactor: remove console logs

* refactor: eslint happy

* refactor: change daysValue from string to array

* style: match course schedule page styling

* style: remove label font-normal

* style: finalize course schedule page style match

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-03-15 22:37:51 -05:00
Samuel Gunter
9448072112 feat: ensure unique splash text on schedule change (#554) 2025-03-10 18:57:54 -05:00
ishita778
b1e98ca9d7 chore: Modify the schedule creation prompt (#550)
* chore: modify the schedule creation prompt

* chore: changed border color to offwhite

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-03-08 21:01:46 -06:00
Abdulrahman Alshahrani
f036d409e6 feat: implement a What's New prompt (#539)
* feat: create whats new initial component

* feat: create initial whats-new hook

* feat: create whats-new story

* feat: add button to open dialog in storybook

* feat: complete popup ui

* feat: add check for new updates or installs

* fix: fix linter issues

* fix: use proper features and add video

* fix: properly fetch version from manifest

* feat: add a link to open the popup

* fix: update spacing and features' content

* fix: update UTRP Map name

* fix: increase icon size and display version correctly

* feat: update the features video

* fix: update offwhite color

* fix: color typo

* fix: fixing colors again

* feat: use numbers instead of boolean

* fix: typo in import

* feat: add type safety to features array

* feat: cdn video url

* fix: delete mp4 video

* feat: handle video failure to load

* fix: make border outline tight to video

* feat: make design responsive

* fix: make features array readonly

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Derek Chen <derex1987@gmail.com>
2025-03-08 15:41:09 -06:00
ishita778
5493c63f18 chore: Change the highlight color on-hover over grade distribution bars to off-white/50 (#547)
* chore: opacity changed to 50 percent

* chore: fix pnpm lock file

* chore: pnpm checks pass attempt

* chore: pnpm lock checks should work

* fix: opacity 50 and pnpm version

* chore: pnpm lock file fix
2025-03-07 13:31:23 -06:00
ishita778
6c3139bf0f fix: merge course labels across pages (#541)
* fix: merge course labels across pages

* fix: merge same course

* fix: all checks pass

* fix: updated addrows function

* fix: prettier check

* fix: all checks

* fix: all checks pass

* fix: moved query tbody outside

* fix: uses row element

* fix: checks pass now

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-03-05 15:08:33 -06:00
Krish Patel
28ebb69612 style: update delete schedule prompt (#546)
Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
2025-03-05 14:15:59 -06:00
Samuel Gunter
008cb40cb8 style: move input click style to central place (#532)
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-03-05 14:05:34 -06:00
doprz
195d3a219a chore(release): v2.1.1 2025-03-02 21:01:18 -06:00
doprz
c6452c4f2b feat: add isDeveloper 2025-03-02 20:47:31 -06:00
doprz
3dbacf0d65 chore: pin node@20.9.0 (#540) 2025-03-01 12:54:31 -06:00
doprz
56a82baf40 chore: update lock file and set engine to use pnpm v10 (#536)
* chore: update lock file and set engine to use pnpm v10.5.2

* chore: set pnpm version to v10.x.x

* chore(ci): use pnpm v10

* chore: update Dockerfile to use pnpm@latest-10

* chore(ci): use pnpm/action-setup@v4
2025-02-28 22:54:46 -06:00
ishita778
20d210245b chore: rename unscheduled text at calendar footer (#538) 2025-02-28 11:46:19 -06:00
ishita778
21cbcf677a chore: changed text to data source (#537) 2025-02-28 11:07:19 -06:00
Samuel Gunter
8fb355d03b chore(cal): update course schedule links for fall and summer 2025 (#534) 2025-02-27 23:37:38 -06:00
doprz
218477404f feat: map page (#390)
* feat: add boilerplate

* feat: add working paths

* feat: improve building selection controls and add week schedule

Signed-off-by: doprz <52579214+doprz@users.noreply.github.com>

* fix: sort week schedule

Signed-off-by: doprz <52579214+doprz@users.noreply.github.com>

* feat(testing): improve pathfinding

* Revert "feat(testing): improve pathfinding"

This reverts commit 87998cedbf.

* feat: add pathfinding with building selection controls

Signed-off-by: doprz <52579214+doprz@users.noreply.github.com>

* feat: improve path finding algorithm thresholds

* feat: add DaySelector, PathStats, and WeekSchedule components

* feat: add working stats and daily schedule

* chore: refactor everything

* feat: add linear walkway node generation

* feat: add bezier curve walkway node generation

* feat: add circular walkway node generation

* docs: add docs

* feat: add individual path selection and bump version

* fix: tsdoc and updated components/utils

* chore(deps): update deps

* feat: add UTRP Map and Debug Page to Settings > Developer Mode

* chore: fix pr review comments

* chore: add showDebugNodes

* chore: add all buildings around the UT tower

* chore: add stadium POIs

* chore: add east mall buildings

* chore: update DaySelector to use proper button styling

* chore: add university ave walkway

* feat: add zoom, pan, and dev controls functionality

- Fix SVG Overlay Alignment
- Use SVG for map
- Add Dev Controls
- Fix day selector position
- Update the SVG's `preserveAspectRatio` attribute to `xMidYMid` meet to
ensure proper scaling
- Use `useCallback` for event handlers to prevent unnecessary re-renders
- Remove old PNG map

* feat: add dynamic rendering"

* feat: add dynamicRendering dev toggle and fullscreen support

* chore: update deps

* chore: disable viewport svg overlay culling if dynamic rendering is off

* chore: update pnpm-lock.yaml

* chore: add north mall buildings

* chore: add buildings next to JES

* refactor: map components into individual files

* fix: missing import

---------

Signed-off-by: doprz <52579214+doprz@users.noreply.github.com>
2025-02-27 19:44:03 -06:00
Ethan Lanting
766c0bc1b4 fix: import schedule file upload button (#515)
* fix: add active scale style and accept file prop

* chore: improve type safety using MIMEType

* fix: update FileUpload component to support multiple MIME types in accept prop

* chore: run lint

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-02-24 20:17:49 -06:00
Samuel Gunter
4a8d0666c2 chore: update grades as of fall 2024 (#508) 2025-02-21 16:09:53 -06:00
doprz
8fac10a743 chore(release): v2.1.0 2025-02-19 19:39:25 -06:00
doprz
1aa4e8c5fb feat(build): add vite-build-logger (#507)
* feat(build): add vite-build-logger

* chore: fix types

* chore: fix logic

---------

Co-authored-by: Preston-Cook <preston.l.cook@gmail.com>
2025-02-15 00:05:56 -06:00
Preston Cook
ee4c6ce699 feat(ui): update popup and course blocks (#506)
* feat(ui): add time and location to popup

* feat(ui): memoize meeting times

* feat(ui): remove resizing

* feat(ui): add no select to copy course id button

* feat(ui): complete update to popup and course blocks

* chore: update settings page

* chore: fix types

* fix(ui): update spacing, padding, and remove last updated section

* chore: fix type issues

* fix(ui): update borders to offwhite/50

* fix(ui): apply proper offwhite styling

* fix(ui): add unique key to async courses in bottom bar
2025-02-13 18:07:05 -06:00
Ethan Lanting
b171f01d01 feat(ui): Modify Calendar Footer design and Unscheduled courses (#503)
* feat: update styling and text for unscheduled courses in footer

* refactor: correct offwhite theme colors

* fix(ui): adjust em dash spacing, footer bottom padding, and vertically align course text

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-02-13 13:09:40 -06:00
Preston Cook
846070ebb5 fix(ui): stop import button dropdown from squishing (#504) 2025-02-07 20:52:03 -06:00
Preston Cook
9c766c2695 feat(ui): calendar header redesign (#479)
* feat(ui): calendar header redesign (WIP)

* feat(ui): calendar header redesign (WIP)

* chore: use path alias

* feat(ui): calendar header redesign

* implement Isaiah changes

* refactor to prevent unnecessary recreations of resize observer

* clean up resize observer and remove unnecessary checks

* remove offwhite border from toolbar

* merge

* complete toolbar

* update screenreader functionality

* ensure truncation works

* merge

* finish new toolbar

* remove unused screen size hook and .mjs file

* add in export button with options

* add static size for export button dropdown to prevent shrinking on smaller viewports

* change schedule section min width to prevent shrinkage

* change text for schedule section to match small caps

* fix truncating issues with small caps

* remove hidden overflow

* add padding

* add min height for hader

* reserve scrollbar width

* tmp

* add sticky positioning to header

* fix inert prop issue

* remove pnpm lock file

* fix scrollbar appearing too early

* fix vertical stickiness

* fix(ui): fix header spacing

* fix(ui): update total hours and courses to be h4

* fix(ui): reduce top spacing on header

* fix(ui): remove header top padding

* fix(ui): stop bottom scrollbar from shifting layout

* feat: add functionality to header and fix screenshot spacing

* feat: add functionality to header and fix screenshot spacing

* fix(ui): allow scrollbar in header and adjust padding to compensate for reserved space

* fix(ui): make export options container hug children

* fix(ui): add offwhite border

* chore: add back lock file from main branch :)

* feat(ui): add reduced motion for accessability

* feat(ui): change right scrollbar on calendar grid to be hidden when not necessary

* chore: make all buttons except export invisible

* chore: remove all buttons except export and adjust hardcoded pixel widths for responsiveness

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
2025-02-05 23:53:16 -06:00
Preston Cook
4752f5860a fix(ui): fix longstanding drag-and-drop duplication issue (#502)
* fix(ui): fixed color switching on list reordering

* chore: remove lock file

* chore: add back lock file

* feat(ui): fix color duplication issue and prevent scrolling beyond parent

* feat(ui): add to storybook

* fix(ui): remove white background while dragging

* chore: remove dnd pangea from package.json

* chore: rebuild lock file

* chore: remove nested li element issue

* fix(ui): allow grabbing cursor while dragging

* fix(ui): address chromatic errors

* fix(ui): address chromatic errors

* fix(ui): address linting issues and pass tests

* fix(ui): create hook for modifying the cursor globally

* chore: add check for storybook env

* chore: add back unused import to AddAllButton

* fix: make cursor grabbing hook more explicit

* chore: move sortable list item into sortable list file

* fix: remove isStorybook prop from ScheduleListItem

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-02-04 17:28:54 -06:00
Ethan Lanting
c2328e461e feat(ui): color picker final touches (#491)
* fix: update CourseCellColorPicker.tsx background to white

* feat: add color picker to CalendarCourseCell component

* feat: add color picker functionality to update course colors

* fix: type issues with storybook components

* feat: add useColorPicker hook, isValidHexColor and updateCourseColors utilities

* refactor: color picker logic and UI components

* refactor: update useFlattenedCourseSchedule hook to include courseID property

* refactor: update storybook calendar components with updated props

* refactor: update color picker ui logic to account for position of cell

* fix: revert back to error handling for invalid rgb

* refactor: update jsdocs

* refactor: integrate ColorPickerContext into Calendar components and update props

* refactor: integrate ColorPickerContext into Calendar components and update related props

* refactor: change JSDocs comments and remove unused color inversion state

* refactor: update story components

* feat: add functionality for selecting secondary course colors

* refactor: enhance HexColorEditor to dynamically adjust tag icon color based on preview color

* refactor: simplify JSDoc comment in useColorPicker hook

* fix: revert Button component

* refactor: update CalendarCourseCell component positioning and styling

* fix: correct types in color.ts

* feat: add getDarkerShade function to compute darker shades of hex colors

* feat: add shadow to color picker button

* fix: update button size in ColorPatch component

* feat: implement debounced input for hex color editor and add useDebounce hook

* chore: utilize the logical and && operator instead of the ternary operator

* fix: imports and palette icon

* refactor: remove unused import

* fix: bug when course add fails with custom colors

* chore: run lint

* chore: run check-types

* feat: add HSL color type and conversion functions

* refactor: rename colorway to theme

* fix: hide color picker on screenshot

* fix: undo important syntax

* refactor: rename SomeFunction to DebouncedCallback

* refactor: remove inner function

* refactor: update return type to DebouncedCallback

* fix: adjust sizes for hash and palette button

* feat: create tests for hexToHSL and isValidHexColor

* refactor: update parameter type to use HexColor

* fix: increase size of palette button

* fix: update dependency array for hex code debounce

* fix: change colorPickerRef element ref

* feat: add Roboto Mono font

* fix: update input class to use monospace font

* feat: add getLighterShade function

* chore: run prettier and lint

* feat: synchronize local hex code with hexCode prop changes

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-29 20:32:06 -06:00
doprz
aa29bcf9fe feat: update SWE list 2025-01-27 19:39:59 -06:00
Preston Cook
f0b257aa12 feat(ui): add schedule list icons (#500) 2025-01-25 20:10:40 -06:00
Abdulrahman Alshahrani
93733e3721 feat: add open calendar button (#457)
* feat: add open calendar button

* fix: replace function to open calendar page

* fix: make open calendar function a one-liner

* fix: import correct calendar icon

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-24 21:27:53 -06:00
Ethan L
501f506677 feat(ui): course unique number copy button (#490)
* feat: add copy icon for course unique ID

* feat: update Button component to support event parameter in onClick handler

* feat: add copy functionality for course unique ID

* refactor: use Text component instead of span

* fix: remove duplicate course number

* fix: remove unnecessary event forwarding

* fix: remove unnecessary boolean type

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

* fix: remove double space

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

* refactor: reduce clipboard copy delay and use formatted unique ID

* feat: add copy animation to dialog

---------

Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: nikshitak <nikkikurva@gmail.com>
2025-01-21 00:02:00 -06:00
Samuel Gunter
009de62828 feat(settings): add option to always open calendar in new tab (#488)
* feat(settings): add option to always open calendar in new tab

* fix: await setting

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-20 23:18:00 -06:00
Samuel Gunter
b74c698866 feat(settings): allow disabling of auto-loading courses (#489) 2025-01-20 23:04:14 -06:00
cd05e5e7fc feat: validate login passed to background and implemented into add all injected button (#443)
* docs: proper captializing

* docs: confirmed that the for loop is entered but on first startup something else

* feat: turns out - validate login status - not in background - pass background now

* feat: kronk.gif

* Update UserScheduleMessages.ts

* Update addCourseByURL.ts

* chore: format

* chore: chore

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-20 18:42:15 -06:00
doprz
f425510e11 docs: add repobeats and star history (#487)
* docs: add repobeats and star history

* chore: update alt
2025-01-20 18:21:32 -06:00
Ethan
1f635d2515 feat(ui): course color picker (#382)
* fix: update CourseCellColorPicker.tsx background to white

* feat: add color picker to CalendarCourseCell component

* feat: add color picker functionality to update course colors

* fix: type issues with storybook components

* feat: add useColorPicker hook, isValidHexColor and updateCourseColors utilities

* refactor: color picker logic and UI components

* refactor: update useFlattenedCourseSchedule hook to include courseID property

* refactor: update storybook calendar components with updated props

* refactor: update color picker ui logic to account for position of cell

* fix: revert back to error handling for invalid rgb

* refactor: update jsdocs

* refactor: integrate ColorPickerContext into Calendar components and update props

* refactor: integrate ColorPickerContext into Calendar components and update related props

* refactor: change JSDocs comments and remove unused color inversion state

* refactor: update story components

* feat: add functionality for selecting secondary course colors

* refactor: enhance HexColorEditor to dynamically adjust tag icon color based on preview color

* refactor: simplify JSDoc comment in useColorPicker hook

* fix: revert Button component

* refactor: update CalendarCourseCell component positioning and styling

* fix: correct types in color.ts

* feat: add getDarkerShade function to compute darker shades of hex colors

* feat: add shadow to color picker button

* fix: update button size in ColorPatch component

* feat: implement debounced input for hex color editor and add useDebounce hook

* chore: utilize the logical and && operator instead of the ternary operator

* fix: imports and palette icon

* refactor: remove unused import

* fix: bug when course add fails with custom colors

* chore: run lint

* chore: run check-types

* feat: add HSL color type and conversion functions

* refactor: rename colorway to theme

* fix: hide color picker on screenshot

* fix: undo important syntax

* refactor: rename SomeFunction to DebouncedCallback

* refactor: remove inner function

* refactor: update return type to DebouncedCallback

* fix: adjust sizes for hash and palette button

* feat: create tests for hexToHSL and isValidHexColor

* refactor: update parameter type to use HexColor

* fix: increase size of palette button

* fix: update dependency array for hex code debounce

* fix: change colorPickerRef element ref

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-20 17:48:52 -06:00
Abdulrahman Alshahrani
a61bddf0e8 feat(ui): redesign grade distribution tooltip (#485)
* feat(ui): redesign grade distribution tooltip

* chore: prettier

* fix: use theme color for graph background

---------

Co-authored-by: Derek Chen <derex1987@gmail.com>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-20 13:03:06 -06:00
Ethan
843cb5b443 feat(ui): calendar sidebar redesign (#464)
* feat: update calendar sidebar, footer, and header with Figma design

* chore: run lint

* feat: update header with Figma design

* chore: run lint

* chore: remove unused vars

* chore: fix types

* fix: adjust sidebar minimum width

* fix: update LogoIcon layout to ensure text is always displayed

* feat: add spacing constants

* fix: add sidebar styling with spacing system and sticky header

* fix: update spacing constants to use rem units

* refactor: replace padding with spacing system and colors with UTRP theme

* refactor: rename ImportantLinks to ResourceLinks

* refactor: simplify CalendarHeader button component by using icon prop

* feat: add sidebar open and close transition

* refactor: rename unused var

* fix: update social icon color

* feat: improve layout and spacing in calendar components

* refactor: remove unused GearSix icon and options handler

* feat: update calendar components with new icons and improved spacing

* fix: correct class name

* refactor: organize social links into array and update link styling

* refactor: remove unused import

* fix: adjust gap spacing in radio button

* fix: update divider component to use theme offwhite1

* fix: increase size of outward arrow icon

* feat: add getSpacingInPx function to convert rem to pixels

* fix: update gap spacing in CalendarSchedules component to use spacing system

* fix: rollback footer social icons to original icons

* fix: update Calendar styles to use theme offwhite1 and adjust padding to account for scrollbar

* fix: update LargeLogo component to use gap-spacing-3

* fix: update button variants to 'minimal' and adjust styles for consistency

* fix: adjust padding in Calendar component for better layout consistency

* fix: increase size of arrow icon

* fix: add shrink-0 to radio buttons
2025-01-19 18:34:20 -06:00
Aaron Chen
52347fd56d feat(ui): change red text when instructor not found (#483)
* feat(ui): change red text when instructor not found

* feat(ui): fix wrong &apos; usage

* feat(ui): changed/shortened text

* feat(ui): update text, get rid of formatting

* feat(ui): removed unintentional H character from testing

* feat(ui): add space

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-18 22:58:30 -06:00
Aaron Chen
a20332e53d feat(ui): added shadows to popup buttons and course blocks (#378)
* feat(ui): added shadows to popup buttons and course blocks

* feat(ui): keep shadow state while dragging course block

* feat(ui): fixed transition "flash" after dragging

* feat(ui): fix linting/styling

* Update src/views/components/PopupMain.tsx

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

* fix(ui): change old icons to the new ones from main

* fix(ui): show flag icon instead of plus icon for feedback

* chore: fix button variants after merge

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Samuel Gunter <29130894+Samathingamajig@users.noreply.github.com>
Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
2025-01-08 02:20:10 -06:00
Preston Cook
0aa469af81 feat(ui): update button variants following figma (#482)
* feat(ui): update button variants following figma

* feat(ui): separate size prop to allow for regular and small sized button variants

* update type to no longer include minimal-small

* update uno css config to use new spacing system

* add variants and sizes to file upload; update button and file upload stories

* add mini button variant and update small button

* specify width on icon-only regular variant

* update plus buttons to be mini sizes

* remove redundant classnames

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-07 14:59:15 -06:00
Preston Cook
0d73b13b28 feat(ui): update theme colors #466 (#473)
* feat(ui): update theme colors

* remove kebab-casing from all colors

* remove ut-concrete and replace concrete with ut-gray throughout app

* feat(ui): update staticutrpblack to staticblack

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-02 20:23:40 -06:00
Long Phan
24256798ba fix: 4th attempt for: now able to delete schedule even if active (#435)
* fix: 2nd attempt for: now able to delete schedule even if active

* fix: 3rd attempt for: now able to delete schedule even if active

* fix: 4th attempt for: now able to delete schedule even if active

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-02 12:31:41 -06:00
Ethan
e61ab565c3 feat: add spacing system (#474)
* feat: add spacing system

* refactor: update spacing naming

* chore: add leading 0

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2025-01-01 22:12:00 -06:00
Samuel Gunter
9cbcfa4720 chore: migrate to new scss import syntax (#478)
Co-authored-by: Long Phan <75595656+Wizardbacon13@users.noreply.github.com>
2024-12-31 20:43:40 -06:00
doprz
c712fc741e chore: remove typo (#476)
Remove "hello" from "I Understand" button
2024-12-31 15:17:00 -06:00
Abdulrahman Alshahrani
ca5e4c13d3 feat: add 'new search' link to the course catalog page (#456)
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-12-31 13:44:16 -06:00
Preston Cook
37bd7e79d9 feat(ui): change icons to phosphor-icons #467 (#469)
* change icons to phosphor-icons

* feat(ui): change icons to phosphor-icons

* feat(ui): change icons to phosphor-icons

* feat(ui): correct icon sizes, weights, and colors

* feat(ui): change arrow-up-right sizes to 16px
2024-12-31 13:36:08 -06:00
Ethan
918f4e419c feat: update text styles (#468)
Fixes #465
2024-12-28 14:21:58 -06:00
3abb11734a refactor: change a file name (#447)
* refactor: file upload component

* refactor: changed name of a function and added a new line
2024-11-23 00:14:49 -06:00
doprz
a5e921fd75 feat(build): add Docker support (#322)
* feat(build): add Docker support

* fix: pnpm patches

* chore: update readme

* chore: refactor Docker section into separate markdown file

* chore: remove polling and host 0.0.0.0

* feat: add .dockerignore

* feat: update .dockerignore
2024-11-21 22:41:52 -06:00
Warith Rahman
7dd93690d6 fix: place hours and courses under schedule name (#388)
* fix(header): bottom-aligned the schedule name + hours/courses in calendar

* feat: updated font style in header and dropdown

* fix: new hour/course styling per discussion

i love having 80 different ideas, hopefully after this there's like maybe a tiny commit left to do before this is done..

* feat(header): added icons to cal header

WORK IN PROGRESS

* style: updated layout of header and main popup

* fix: updated font and spacing for header and popup

* fix: updated cal + popup style details
- schedule title is now in normal casing w/ colon removed
- last updated on is now entirely deleted from everywhere
- hour and course numbers now h3 in calendar ONLY

* refactor: changed size for calendar header

* refactor: changed ut-black to theme-black

* refactor: remove hiding on small window sizes

* refactor: reduced spacing in popup

* refactor: updated unocss for small-caps configuration

* style: changed variant + className order

* fix: readded update time text

* style: auto formatter, unused imports, capitalization

---------

Co-authored-by: Razboy20 <razboy20@gmail.com>
Co-authored-by: Samuel Gunter <sgunter@utexas.edu>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-11-21 20:56:56 -06:00
7dbffc6eef feat: export/import functionality (backup/restore/share with friends) + a new input component (#433)
* feat: export schedule function to be added to handler

* feat: use UserScheduleStore and return json

* feat: download functionality

* feat: oh wow we already have a blob download util that is very very nice

* feat: return empty json if none found

* feat: import function completion

* feat: file uploading done

* feat: new input component-stories made-settings input replaced with component

* feat: attempt 1 to hook settings.tsx to importSchedule

* feat: it works horray aka using right Course constructor it works

* chore: fix jsdoc

* chore: comments and debug style

* docs: extra comment

* feat: name of schedule more user friendly

* feat: reworked how schedule is passed and check for file being schedule

* feat: color is kept on import

* fix: add sendResponse to exportSchedule

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-11-21 12:55:48 -06:00
Samuel Gunter
8b922082a7 fix: instructor formatting errors (#425)
* fix: instructor formatting errors

* refactor: simplify logic in Instructor toString, remove unused formatters

* refactor: remove "unnecessary" else's after returns

I think it looks worse like this but whatever
2024-11-19 19:28:38 -06:00
Sean Sy
19e3838df2 Merge pull request #440
* style(ui): changed tick marks to match x-axis
2024-11-19 11:28:38 -06:00
25e3ba1ed4 docs: remove redundant sentence in readme (#439)
* Update README.md

Removed redundant line

* chore: add newline

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-11-17 23:02:44 -06:00
ali v
598bafe67f fix: transition added (#381) 2024-11-16 19:37:42 -06:00
ali v
ebcc0aa76a fix: updated text when time/location not provided (#289)
* fix: updated text when time/location not provided

* fix(issue): fixed text when time/loc isn't provided

* chore: lint and format

* fix: created location&time component

* fix: renamed component & restructured component

* chore: fix lint

* fix: variety of issues within DisplayMeetingInfo component

* fix: fixed lint error

* fix: ran prettier

* chore: locationInfo usage

* chore: cleanup

* fix: removed periods and added online info

* fix: wrapped in div to remove space

* fix: space now shows properly

* fix: spacing fixed

* chore: remove types from jsdoc

* fix: extra arg

* chore: fix lint

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
2024-11-16 15:11:30 -06:00
doprz
e987fbbe8e feat: add eslint-plugin-tsdoc (#430)
* feat: add eslint-plugin-tsdoc

* feat(doc): update current jsdoc to tsdoc specification

* chore: update deps

* feat: update warn to error for jsdoc and tsdoc

* chore(doc): lint
2024-11-16 00:20:36 -06:00
c41467c617 feat: injected button - add all courses from MyUT AND passing URL to handler (#291)
* feat: first button attempt

* feat: fetching each course code

* feat: adding courses function from there but idk where to get the active schedule from

* docs: todo

* feat: retrieved active schedule

* feat: button tactics

* feat: add support for my.utexas.edu

* feat: inject button into MyUT

* feat: refactor code to render components dynamically based on site

* feat: scrape course ids from MyUT and remove duplicates

* feat: site support links for classlist

* feat: add utility function to add course by URL

* feat: support additional case for course cal

* feat: duplicates

* chore: cleanup

* feat: temporary checkpoint

* feat: reroute to use new add course by url

* feat: linking to new function, cleaning up, adding messaging for course url add

* chore: unused import

* feat: relinking addCourse function to the button fingers crossed

* feat: we did it!

* chore: remove comment

* chore: cleanup cleanup

* feat: tried to handle the async stuff because of that small bug but nothing fixed. doesnt hurt tho

* feat: i have fixed it holy kevinnn

* chore: delete unused file and organization

* chore: removed unused log

* feat: better log for course add

* chore: refactor via data destructuring

* chore: pass component as prop via React.ComponentType

---------

Co-authored-by: Ethan Lanting <ethanlanting@gmail.com>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-11-15 19:07:37 -06:00
Abdulrahman Alshahrani
9ad32390d1 feat: add explanation to grade distribution (#325)
* feat: add 'About the data' link to grade distribution chart

* chore: change the About the data text style/variant to small

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-11-15 14:54:19 -06:00
Tony Dinh
0d51cae4c8 feat: modify Course Block text style and time and location text (#409) 2024-11-15 13:43:41 -06:00
Aaron Chen
91fa78e2d0 fix(ui): reduce left side grade distribution margin/padding (#427)
* fix(ui): align left margin of grade distribution

* fix(ui): remove accidental linewidth change (not relevant to this pr)
2024-11-15 13:13:37 -06:00
doprz
b13e217a5b chore: remove extra package 2024-11-14 20:16:38 -06:00
Kabir Ramzan
b732a80eaa feat: add CacheStore for GitHub stats and use names instead of usernames (#405)
* feat: add CacheStore for GitHub stats and use names instead of usernames

* fix: only cache name for successful API queries

* chore: linting

* chore: refactor any with unknown and add jsdocs

* fix: use cached endpoint to avoid rate limiting issues

* fix: code style

* chore: add type assertion

* fix: use correct type

* Revert "fix: use correct type"

This reverts commit 74956c12f3.

* fix: use correct type

* fix: use URL Web API

* fix: add CONTRIBUTORS_API_ROUTE constant

---------

Co-authored-by: Derek Chen <derex1987@gmail.com>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-11-13 12:01:20 -06:00
doprz
46c76b1703 feat: add 404 page (#426)
* feat: add 404 page

* chore: lint and format

* fix: remove extra line
2024-11-12 21:57:59 -06:00
doprz
db04bbb52e feat(build): refactor gulpfile to use gulp-execa (#323)
* feat(build): refactor gulpfile to use gulp-execa

* chore: fix PR comments

* chore(deps): update deps
2024-11-10 19:23:19 -06:00
Isaiah David Rodriguez
e425daf041 chore: update issue templates (#327)
* Update issue templates

* feat: refactor bug and feature issue templates

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
2024-11-05 18:32:58 -06:00
200 changed files with 23025 additions and 6453 deletions

242
.dockerignore Normal file
View File

@@ -0,0 +1,242 @@
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node,react,storybookjs
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,node,react,storybookjs
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-te
node_modulesst
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
### react ###
.DS_*
**/*.backup.*
**/*.back.*
*.sublime*
psd
thumb
sketch
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node,react,storybookjs
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
package-lock.json
storybook-static/
package/
# Version control
.git
.gitignore
.gitattributes
# Dependencies
.pnpm-store
!pnpm-lock.yaml
# Testing
coverage
.nyc_output
# OS files
.DS_Store
Thumbs.db
# Docker
Dockerfile
.dockerignore
docker-compose*
# Documentation
README.md
CHANGELOG.md
DOCKER_DEV_SETUP.md
docs/

View File

@@ -7,3 +7,6 @@ insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
indent_size = 4 indent_size = 4
indent_style = space indent_style = space
[*.nix]
indent_size = 2

3
.env.example Normal file
View File

@@ -0,0 +1,3 @@
SENTRY_ORG=longhorn-developers
SENTRY_PROJECT=ut-registration-plus
SENTRY_AUTH_TOKEN=

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

View File

@@ -1,96 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories and management
node_modules/
jspm_packages/
package.json
package-lock.json
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# next.js build output
.next
# Webpack-built output
/dist
# Extension archives
/build
# VsCode
.vscode/*
!.vscode/launch.json
!.vscode/tasks.json
# macOS
.DS_Store
# Terraform
.terraform
# development dependencies
.dev/vue-devtools
.dev/browser-profiles
# IntelliJ
.idea
# Sylelint IntelliJ Plugin Requirement
.stylelintrc.json
# Local environment settings
.env.local
*.svg
config
.eslintrc.js
!.storybook

View File

@@ -1,230 +0,0 @@
module.exports = {
root: true,
env: {
browser: true,
es6: true,
node: true,
webextensions: true,
},
ignorePatterns: ['*.html', 'tsconfig.json'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:storybook/recommended',
'airbnb-base',
'airbnb/rules/react',
'airbnb-typescript',
'@unocss',
'prettier',
],
plugins: [
'import',
'import-essentials',
'jsdoc',
'react-prefer-function-component',
'@typescript-eslint',
'simple-import-sort',
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
debugger: true,
browser: true,
context: true,
JSX: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
ecmaVersion: 2022,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
modules: true,
experimentalObjectRestSpread: true,
},
},
settings: {
react: {
version: 'detect',
},
jsdoc: {
mode: 'typescript',
},
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
},
},
rules: {
'prefer-const': [
'off',
{
destructuring: 'any',
ignoreReadBeforeAssign: false,
},
],
'no-plusplus': 'off',
'no-inner-declarations': 'off',
'sort-imports': 'off',
'no-case-declarations': 'off',
'no-unreachable': 'warn',
'no-constant-condition': 'error',
'space-before-function-paren': 'off',
'no-undef': 'off',
'no-return-await': 'off',
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/no-shadow': ['off'],
'@typescript-eslint/no-use-before-define': ['off'],
'class-methods-use-this': 'off',
'react-hooks/exhaustive-deps': 'warn',
'@typescript-eslint/lines-between-class-members': 'off',
'no-param-reassign': [
'error',
{
props: false,
},
],
'no-console': 'off',
'consistent-return': 'off',
'react/destructuring-assignment': 'off',
'import/prefer-default-export': 'off',
'no-promise-executor-return': 'off',
'import/no-cycle': 'off',
'import/no-extraneous-dependencies': 'off',
'react/jsx-props-no-spreading': 'off',
'react/jsx-no-useless-fragment': [
'error',
{
allowExpressions: true,
},
],
'keyword-spacing': [
'error',
{
before: true,
after: true,
},
],
'no-continue': 'off',
'space-before-blocks': [
'error',
{
functions: 'always',
keywords: 'always',
classes: 'always',
},
],
'react/jsx-filename-extension': [
1,
{
extensions: ['.tsx'],
},
],
'react/no-deprecated': 'warn',
'react/prop-types': 'off',
'react-prefer-function-component/react-prefer-function-component': [
'warn',
{
allowComponentDidCatch: false,
},
],
'react/function-component-definition': 'off',
'react/button-has-type': 'off',
'jsdoc/require-param-type': 'off',
'jsdoc/require-returns-type': 'off',
'jsdoc/newline-after-description': 'off',
'react/require-default-props': 'off',
'jsdoc/require-jsdoc': [
'warn',
{
enableFixer: false,
publicOnly: true,
checkConstructors: false,
require: {
ArrowFunctionExpression: true,
ClassDeclaration: true,
ClassExpression: true,
FunctionExpression: true,
},
contexts: [
'MethodDefinition:not([key.name="componentDidMount"]):not([key.name="render"])',
'ArrowFunctionExpression',
'ClassDeclaration',
'ClassExpression',
'ClassProperty:not([key.name="state"]):not([key.name="componentDidMount"])',
'FunctionDeclaration',
'FunctionExpression',
'TSDeclareFunction',
'TSEnumDeclaration',
'TSInterfaceDeclaration',
'TSMethodSignature',
'TSModuleDeclaration',
'TSTypeAliasDeclaration',
],
},
],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/space-before-function-paren': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-interface': 'warn',
'import/no-restricted-paths': [
'error',
{
zones: [
{
target: './src/background',
from: './src/views',
message:
'You cannot import into the `background` directory from the `views` directory (i.e. content script files) because it will break the build!',
},
{
target: './src/views',
from: './src/background',
message:
'You cannot import into the `views` directory from the `background` directory (i.e. background script files) because it will break the build!',
},
{
target: './src/shared',
from: './',
except: ['./src/shared', './node_modules'],
message: 'You cannot import into `shared` from an external directory.',
},
],
},
],
'import/extensions': 'off',
'no-restricted-syntax': [
'error',
'ForInStatement',
'LabeledStatement',
'WithStatement',
{
selector: 'TSEnumDeclaration',
message: "Don't declare enums",
},
],
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'import-essentials/restrict-import-depth': 'error',
'import-essentials/check-path-alias': 'error',
},
};

59
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,59 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG] "
labels: ''
assignees: ''
---
**Pre-submission Checklist**
<!-- Please check all applicable boxes before submitting your issue -->
- [ ] I have searched existing issues to make sure this is not a duplicate
- [ ] I have cleared my browser cache and confirmed the issue persists
- [ ] I have checked this issue affects the latest version of the extension
- [ ] I have disabled other extensions to ensure this isn't a conflict issue
- [ ] I have included all the information requested below
**Bug Description**
<!-- A clear and concise description of what the bug is -->
**Steps To Reproduce**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected Behavior**
<!-- A clear and concise description of what you expected to happen -->
**Current Behavior**
<!-- A clear and concise description of what actually happened -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem -->
**Debug Information**
- UTRP Extension Version: [e.g. 1.2.0]
- Browser Info: [e.g. Chrome 120.0.0]
- OS: [e.g. Windows 11, macOS Sonoma]
**Console Logs**
<!-- Please check the browser console (F12) and paste any relevant error messages -->
```
// Paste console logs here
```
**Additional Context**
<!-- Add any other context about the problem here -->

View File

@@ -0,0 +1,34 @@
---
name: Feature Request
about: Suggest an idea for this project
title: "[FEATURE] "
labels: feature
assignees: ''
---
**Pre-submission Checklist**
<!-- Please check all applicable boxes before submitting your feature request -->
- [ ] I have searched existing issues to ensure this feature hasn't been requested
- [ ] I have searched closed issues to check if this was previously rejected/implemented
- [ ] I have checked the project roadmap (if available) for planned similar features
- [ ] I have reviewed the documentation to confirm this feature doesn't exist
- [ ] I have completed all sections below with detailed information
**Feature Description**
<!-- A clear and concise description of the feature you'd like to see -->
**Proposed Solution**
<!-- A clear and concise description of what you want to happen -->
**UI/UX Considerations**
<!-- If this feature involves UI changes, please describe the visual aspects -->
**Technical Implementation Details**
<!-- If you have specific technical suggestions, list them here -->

View File

@@ -0,0 +1,12 @@
---
name: Updating Build Dependencies
about: Updating Build Dependencies
title: ''
labels: build, dependencies
assignees: doprz, Razboy20
---
- [ ] Updated Nix Flake
- [ ] Update Dockerfile
- [ ] Update Docs

21
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
day: 'monday'
time: '09:00'
timezone: 'America/Chicago'
groups:
minor-and-patch-updates:
update-types:
- 'minor'
- 'patch'
major-updates:
update-types:
- 'major'
ignore:
- dependency-name: '@crxjs/vite-plugin'
- dependency-name: '@unocss/vite'

View File

@@ -13,9 +13,9 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v3 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
@@ -32,9 +32,9 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v3 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install

View File

@@ -13,9 +13,9 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v3 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install

View File

@@ -11,9 +11,9 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v3 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install

View File

@@ -13,9 +13,9 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Setup pnpm - name: Setup pnpm
uses: pnpm/action-setup@v3 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install

2
.gitignore vendored
View File

@@ -211,3 +211,5 @@ sketch
package-lock.json package-lock.json
storybook-static/ storybook-static/
package/ package/
.direnv/

View File

@@ -1,287 +1,390 @@
## [2.2.2](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.2.1...v2.2.2) (2025-10-13)
### Features
* add nix flake ([#593](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/593)) ([7b401ad](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7b401add1565ff401bad99745ff9e53b9a7f899f))
* automatically select new or duplicated schedules ([#583](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/583)) ([#589](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/589)) ([2a50f55](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2a50f5580d3dbeb0d66546c23cf29bbb37d80da2))
* **env:** add SENTRY env vars ([8f7e1bc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8f7e1bc0af6336549068e02b80df21d4e8f4ef9c))
* export schedule button add to calendar ([#594](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/594)) ([5994ded](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5994ded8be876cb55174d27d3fdb0832b21a0ff9))
* search result shading ([#617](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/617)) ([be861b8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/be861b823cb2cb7f6f4a1f266351eec3fc1c2f99))
* show warning for courses of different semesters ([#570](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/570)) ([2e7dac1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2e7dac1e3eba757231ac07ac966231c08c703a16))
* support summer grades, fix summer course parser ([#596](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/596)) ([2d92dd4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2d92dd47f00a44b7d48e92a8ffba94480e4e73f9))
### Bug Fixes
* fix or ignore various eslint warning ([#609](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/609)) ([95de8df](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/95de8df37243b6d59625df515a60442f11b7a9d3))
* limit height of schedule list dropdown in the extension popup ([#543](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/543)) ([eb8141e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/eb8141ee8c3d32bce901457178d50781b78f86dd))
* whitespace wrapping in semester warning ([#629](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/629)) ([46fe591](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/46fe591fa72ef017eea7cfb8aa37d12d8f223926))
## [2.2.1](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.2.0...v2.2.1) (2025-06-04)
### Features
* add dining app promo ([#598](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/598)) ([be1dccf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/be1dccfcb9d052c6b291b50cc53418d6bb645beb))
* inside jokes005 ([#590](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/590)) ([37471ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/37471efb740c7a5828cf3b54bac70954694359d7))
* **release:** v2.2.1 ([234f3d6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/234f3d627d603adf8555b4d0e93106d198918169))
### Bug Fixes
* course columns on calendar ([#587](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/587)) ([cfb5faa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cfb5faa09bb0788e270d100f1f36536a53bcff75))
* hide sentry instrumentation on debug builds ([#604](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/604)) ([454e5e8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/454e5e807af29ae0384cc3a3b8b691df5edc69d1))
## [2.2.0](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.1.1...v2.2.0) (2025-04-06)
### Features
* auto create empty schedule when deleted all schedules ([#552](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/552)) ([7c2beef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7c2beef1930fbc887e8ec1aea789016b3150cd21))
* ensure unique splash text on schedule change ([#554](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/554)) ([9448072](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/94480721124e052426c1f3236e8605c7088df79c))
* implement a What's New prompt ([#539](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/539)) ([f036d40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f036d409e60a39fd1d3cb2f0db53a6056615f336))
* persist sidebar toggle state ([#569](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/569)) ([6957431](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/695743104c57951ba1957258c60c843f8fae793f))
* recruitment banner for designer ([#578](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/578)) ([70d4fec](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/70d4fecad61ec3cd3ba839de302fd851e075d073))
* **release:** v2.2.0 ([7a4f40a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a4f40a765d704bf32a3b515d695916ed84f9397))
* rework start time to checkboxes ([#553](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/553)) ([ca734dc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ca734dcd39a433cfd2e930ea04adeba959b32c36))
* sticky calendar header and days ([#568](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/568)) ([fa9f78b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fa9f78b46e3a2270a44d4cc0691195a7c695cb93))
### Bug Fixes
* ics calendar export dates ([#535](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/535)) ([4a5f67f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4a5f67f0fda9f0ef57f821e4b7a55d63f099f579))
* include logo in screenshot, fix screenshots on small/zoomed windows ([#579](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/579)) ([76b6aa7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/76b6aa7c150299dfcfa4b3dc00ce2de32f90f75c))
* merge course labels across pages ([#541](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/541)) ([6c3139b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6c3139bf0f324c9a7be826b6c24e8bf142fc53b1))
* **schedule:** truncate long schedule names in popup ([#564](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/564)) ([3bed9cc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3bed9cc27febfe795af0766a913c4845e74cc2da))
## [2.1.1](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.1.0...v2.1.1) (2025-03-03)
### Features
* add isDeveloper ([c6452c4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c6452c4f2b174487e6f51ad546ca1c3f8b4dbc1f))
* map page ([#390](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/390)) ([2184774](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/218477404fdeacda7b39cd233e4e1e65995935d4))
### Bug Fixes
* import schedule file upload button ([#515](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/515)) ([766c0bc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/766c0bc1b4a75f8146a922cb2eca8871032c1dc9))
## [2.1.0](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.2...v2.1.0) (2025-02-20)
### Features
* add 'new search' link to the course catalog page ([#456](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/456)) ([ca5e4c1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ca5e4c13d31aeb603660972536712ea161c6f870))
* add 404 page ([#426](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/426)) ([46c76b1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/46c76b1703ea2344c3330c4cfa82560663be1c4c))
* add CacheStore for GitHub stats and use names instead of usernames ([#405](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/405)) ([b732a80](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b732a80eaa6f6cbf5b627ffc31ab321de3a8e8f4))
* add eslint-plugin-tsdoc ([#430](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/430)) ([e987fbb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e987fbbe8e733de6767d62811c2d4d6eaccf2d24))
* add explanation to grade distribution ([#325](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/325)) ([9ad3239](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9ad32390d13e1d413ca01b1bc45d02c695bc23dd))
* add open calendar button ([#457](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/457)) ([93733e3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93733e37214f00543479e77209ca03864776a1a6))
* add spacing system ([#474](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/474)) ([e61ab56](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e61ab565c35a197609d205adcaac9c7ffe0fc6da))
* **build:** add Docker support ([#322](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/322)) ([a5e921f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a5e921fd75109a50f17d05c9682e73ba246f3dd1))
* **build:** add vite-build-logger ([#507](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/507)) ([1aa4e8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1aa4e8c5fb2d1aea308e4cccea3e2818a9c946ec))
* **build:** refactor gulpfile to use gulp-execa ([#323](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/323)) ([db04bbb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/db04bbb52e4ab19730dd66f417e68ab013ce2f79))
* export/import functionality (backup/restore/share with friends) + a new input component ([#433](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/433)) ([7dbffc6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7dbffc6eef346747042f1596da627ad0a2fcae1a))
* injected button - add all courses from MyUT AND passing URL to handler ([#291](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/291)) ([c41467c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c41467c6176d31f70cd3ecdcf56eeb73696c6c23))
* modify Course Block text style and time and location text ([#409](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/409)) ([0d51cae](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0d51cae4c8b27201b09c0898f4d7a6e7abb3c100))
* **settings:** add option to always open calendar in new tab ([#488](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/488)) ([009de62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/009de628285ce9c6571e492f8e3f52cdeeed4459))
* **settings:** allow disabling of auto-loading courses ([#489](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/489)) ([b74c698](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b74c698866c1074ce2236ede549c94555667e6a5))
* **ui:** add schedule list icons ([#500](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/500)) ([f0b257a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f0b257aa124a8fff0bb31e3396715aecb09948d5))
* **ui:** added shadows to popup buttons and course blocks ([#378](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/378)) ([a20332e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a20332e53da9bda873fabb8395afe6ff6303799c))
* **ui:** calendar header redesign ([#479](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/479)) ([9c766c2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9c766c26959673389e372c3a440c1a73d5887b1d))
* **ui:** calendar sidebar redesign ([#464](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/464)) ([843cb5b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/843cb5b4430885527592feee33656dfae50c95c2))
* **ui:** change icons to phosphor-icons [#467](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/467) ([#469](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/469)) ([37bd7e7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/37bd7e79d9202d55c7d0f566518526b0aed53c68))
* **ui:** change red text when instructor not found ([#483](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/483)) ([52347fd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/52347fd56dff95190915d9f375dbebe1383e76c8))
* **ui:** color picker final touches ([#491](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/491)) ([c2328e4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c2328e461ea0a846f399a22bc525540da2796dcd))
* **ui:** course color picker ([#382](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/382)) ([1f635d2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1f635d2515fc403ad7f08fc3a244a17d262e3f7b))
* **ui:** course unique number copy button ([#490](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/490)) ([501f506](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/501f50667725aa1515391869b133908b8383d868))
* **ui:** Modify Calendar Footer design and Unscheduled courses ([#503](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/503)) ([b171f01](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b171f01d01f47c75e7df5e5c8e4e7b15b7397c52))
* **ui:** redesign grade distribution tooltip ([#485](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/485)) ([a61bddf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a61bddf0e8a76c89d4963d9e580fb063309bee92))
* **ui:** update button variants following figma ([#482](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/482)) ([0aa469a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0aa469af81e7912a6b26ee1b80c5920f677b5fbd))
* **ui:** update popup and course blocks ([#506](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/506)) ([ee4c6ce](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ee4c6ce6999ad35821b9be5d657790f2dee017b3))
* **ui:** update theme colors [#466](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/466) ([#473](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/473)) ([0d73b13](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0d73b13b288bf8b18444f26455da24c2f4acedf6))
* update SWE list ([aa29bcf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aa29bcf9fe4bad7812336d55d7575a6032aea91c))
* update text styles ([#468](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/468)) ([918f4e4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/918f4e419cf5fa71bda1112597e9a373daca69ea)), closes [#465](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/465)
* validate login passed to background and implemented into add all injected button ([#443](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/443)) ([cd05e5e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cd05e5e7fcaa02dab30f230c178f189d8052a7c9))
### Bug Fixes
* 4th attempt for: now able to delete schedule even if active ([#435](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/435)) ([2425679](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/24256798ba7b877dd170d77ce4970b13b8a69a68))
* instructor formatting errors ([#425](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/425)) ([8b92208](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b922082a79ebdbf50a09f37ecb9dfea1be6e1f3))
* place hours and courses under schedule name ([#388](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/388)) ([7dd9369](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7dd93690d6dbd206cb50442f42ec85d6bbfc1da8))
* transition added ([#381](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/381)) ([598bafe](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/598bafe67f47ad57b3801a425e61f86093ba2be8))
* **ui:** fix longstanding drag-and-drop duplication issue ([#502](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/502)) ([4752f58](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4752f5860a96e08a1177c0ddf57a0fa269d89072))
* **ui:** reduce left side grade distribution margin/padding ([#427](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/427)) ([91fa78e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/91fa78e2d0532a73e24ecc013d2ba6c0f62c1fcd))
* **ui:** stop import button dropdown from squishing ([#504](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/504)) ([846070e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/846070ebb5e13e0fc0df94666dbce75194100049))
* updated text when time/location not provided ([#289](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/289)) ([ebcc0aa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ebcc0aa76a89f0d1a9e90cfc50d70f017c9bed42))
## [2.0.2](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.1...v2.0.2) (2024-11-05) ## [2.0.2](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/v2.0.1...v2.0.2) (2024-11-05)
### Features ### 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 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 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)) * 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)) * 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)) * 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) * **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 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)) * 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)) * 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 ### 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) * 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)) * 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) * 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)) * 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) * 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)) * 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)) * 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:** 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:** 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:** 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:** 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)) * **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
- spring 2024 instructors db ([#317](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/317)) ([79dd29c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/79dd29cfc9e849b09e7d91bd0eed51c1c93b3352)) * spring 2024 instructors db ([#317](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/317)) ([79dd29c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/79dd29cfc9e849b09e7d91bd0eed51c1c93b3352))
### Bug Fixes ### Bug Fixes
- add a little error checking to settings ([#315](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/315)) ([e261641](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e261641e5985d8bd5047d8a0be5d1caae844e40f)) * add a little error checking to settings ([#315](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/315)) ([e261641](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e261641e5985d8bd5047d8a0be5d1caae844e40f))
- gulp zip ([#314](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/314)) ([05f00b2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/05f00b23d26b90f564710db4364426e90c8d6831)) * gulp zip ([#314](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/314)) ([05f00b2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/05f00b23d26b90f564710db4364426e90c8d6831))
- migration loop ([aeff5e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aeff5e09a238503293c3882d97d40270da1e4883)) * migration loop ([aeff5e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aeff5e09a238503293c3882d97d40270da1e4883))
- show calendar in active window ([#312](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/312)) ([ceba38b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ceba38b1ac74ec9e6630222183bd466a8d12c27d)) * show calendar in active window ([#312](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/312)) ([ceba38b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ceba38b1ac74ec9e6630222183bd466a8d12c27d))
## [2.0.0](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/b4e8c7589e53f1064d70703459cc6d66fae1b04c...v2.0.0) (2024-10-15) ## [2.0.0](https://github.com/Longhorn-Developers/UT-Registration-Plus/compare/b4e8c7589e53f1064d70703459cc6d66fae1b04c...v2.0.0) (2024-10-15)
### Features ### Features
- abhinavchadaga/course-catalog-popup ([#128](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/128)) ([745f9dd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/745f9dd6fb96ccc0eace7189b87abf9a38f03828)) * abhinavchadaga/course-catalog-popup ([#128](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/128)) ([745f9dd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/745f9dd6fb96ccc0eace7189b87abf9a38f03828))
- abhinavchadaga/reusable-popup-prompt ([#148](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/148)) ([44af9e1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/44af9e16e4bf3b92c9a4fa0e779197f4f9ecb237)) * abhinavchadaga/reusable-popup-prompt ([#148](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/148)) ([44af9e1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/44af9e16e4bf3b92c9a4fa0e779197f4f9ecb237))
- actually sum for duplicate semesters (different uniques) ([#202](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/202)) ([d3f64ec](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d3f64ec79eb64ebe72ae28991127dbe521823842)) * actually sum for duplicate semesters (different uniques) ([#202](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/202)) ([d3f64ec](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d3f64ec79eb64ebe72ae28991127dbe521823842))
- add button to the rows, use new ConflictsWithWarning component ([bec2649](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bec2649fc1244bffb5cb03809db62cc0ea477260)) * add button to the rows, use new ConflictsWithWarning component ([bec2649](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bec2649fc1244bffb5cb03809db62cc0ea477260))
- add buttons with icons in tailwind ([93b65ac](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93b65ac2edef3fc728305cc51d1a32ecf5073274)) * add buttons with icons in tailwind ([93b65ac](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93b65ac2edef3fc728305cc51d1a32ecf5073274))
- add cal save buttons (no functionality) ([53e7c7f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/53e7c7fedaf1f268d2d7d06ff1a5df9e2af2306d)) * add cal save buttons (no functionality) ([53e7c7f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/53e7c7fedaf1f268d2d7d06ff1a5df9e2af2306d))
- add Calendar Component ([e0212d5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e0212d510928e5e95d3ba75e391c34714f1bb035)) * add Calendar Component ([e0212d5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e0212d510928e5e95d3ba75e391c34714f1bb035))
- add Calendar schedules component (clicking on storybook not working) ([d1a336e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d1a336e903f09b04b89f11ba7ede2f7d26e0ac3c)) * add Calendar schedules component (clicking on storybook not working) ([d1a336e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d1a336e903f09b04b89f11ba7ede2f7d26e0ac3c))
- add CalendarHeader and its Storybook, need to resize ([23276e5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/23276e5c7c907261b1d02e9006d8021d99b43984)) * add CalendarHeader and its Storybook, need to resize ([23276e5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/23276e5c7c907261b1d02e9006d8021d99b43984))
- add check-path-alias custom ESLint rule ([#123](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/123)) ([208103d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/208103d7081abcf5b31bf36335320aaf7d213464)) * add check-path-alias custom ESLint rule ([#123](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/123)) ([208103d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/208103d7081abcf5b31bf36335320aaf7d213464))
- add chrome.storage api mocks for storybook use ([#141](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/141)) ([3839bff](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3839bff29ef6f0ecfad1e975e685df9a64d96c92)) * add chrome.storage api mocks for storybook use ([#141](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/141)) ([3839bff](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3839bff29ef6f0ecfad1e975e685df9a64d96c92))
- add CSS for calendarCell div ([6cdcf49](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6cdcf4930d9868c0ed45820f66102d89a644cfb6)) * add CSS for calendarCell div ([6cdcf49](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6cdcf4930d9868c0ed45820f66102d89a644cfb6))
- add CSS for hourLine div ([0ba6153](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0ba61534cbfc83408cb64eb1145ecacef1ee8f04)) * add CSS for hourLine div ([0ba6153](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0ba61534cbfc83408cb64eb1145ecacef1ee8f04))
- add CSS for timeLabelContainer div ([56f6456](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/56f6456ce85f77edb9d6fa64ecb82a8388feec41)) * add CSS for timeLabelContainer div ([56f6456](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/56f6456ce85f77edb9d6fa64ecb82a8388feec41))
- add custom ESLint rule restrict-import-depth ([#110](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/110)) ([8c069b7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8c069b7ad339ed8db97bb5e3e6e88ab8d332489b)) * add custom ESLint rule restrict-import-depth ([#110](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/110)) ([8c069b7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8c069b7ad339ed8db97bb5e3e6e88ab8d332489b))
- add downloadBlob util ([2af351e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2af351efa85264da5abeee27fbd091d4ce637041)) * add downloadBlob util ([2af351e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2af351efa85264da5abeee27fbd091d4ce637041))
- add empty settings component - waiting on design ([9d0f210](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9d0f2105481cc3d5affdb4bd69d428379ea92de3)) * add empty settings component - waiting on design ([9d0f210](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9d0f2105481cc3d5affdb4bd69d428379ea92de3))
- add ImportantLinks Component ([5dbee6f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5dbee6f0c39e620c3b65a5e0156fd59ec8217ca7)) * add ImportantLinks Component ([5dbee6f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5dbee6f0c39e620c3b65a5e0156fd59ec8217ca7))
- add List component ([e6b4049](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e6b40494031ca88894b2f918bb18448b70124481)) * add List component ([e6b4049](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e6b40494031ca88894b2f918bb18448b70124481))
- add MIMEType ([0c76052](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c760524781556cf089f1885dc4ff3fd567bfc2b)) * add MIMEType ([0c76052](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c760524781556cf089f1885dc4ff3fd567bfc2b))
- add new db powered by UT_Grade_Parser ([#163](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/163)) ([60d1f48](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/60d1f48bd95132f0517258f017a7644d8aff5101)) * add new db powered by UT_Grade_Parser ([#163](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/163)) ([60d1f48](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/60d1f48bd95132f0517258f017a7644d8aff5101))
- add react-loading-skeleton package ([#244](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/244)) ([b4dd91a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4dd91ad2595d9583a60530c2c2ff6d829d2d5e5)) * add react-loading-skeleton package ([#244](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/244)) ([b4dd91a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4dd91ad2595d9583a60530c2c2ff6d829d2d5e5))
- add skeleton loader for course description + distribution ([#267](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/267)) ([c2cab40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c2cab407f3a07c1d3073e936c9d23cb1cecc9cb9)) * add skeleton loader for course description + distribution ([#267](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/267)) ([c2cab40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c2cab407f3a07c1d3073e936c9d23cb1cecc9cb9))
- add story for CalendarGridCell ([fd91c3b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fd91c3b12ef5670563e689d190be75ef1f695d1c)) * add story for CalendarGridCell ([fd91c3b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fd91c3b12ef5670563e689d190be75ef1f695d1c))
- add Storybook story ([cb3cb5d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cb3cb5d5fc18a68ca5a825f97b808b535aa1fe29)) * add Storybook story ([cb3cb5d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cb3cb5d5fc18a68ca5a825f97b808b535aa1fe29))
- add tailwind version of Button component ([28d93b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/28d93b3c25e1cbca3baea992a299d626ca823570)) * add tailwind version of Button component ([28d93b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/28d93b3c25e1cbca3baea992a299d626ca823570))
- add tickmarks to day div ([8b9cb06](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b9cb065c28b5dd38b7e1681839a81f55691a3c6)) * add tickmarks to day div ([8b9cb06](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b9cb065c28b5dd38b7e1681839a81f55691a3c6))
- add time column to Calendar Component ([9d68211](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9d6821127e7ff9bf63cef659932ee7408de4c969)) * add time column to Calendar Component ([9d68211](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9d6821127e7ff9bf63cef659932ee7408de4c969))
- add timeAndGrid div ([95e0544](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/95e0544b7301d382439b74038fb1b3e25dfe6d30)) * add timeAndGrid div ([95e0544](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/95e0544b7301d382439b74038fb1b3e25dfe6d30))
- added flag ;-; ([#195](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/195)) ([9b4d61c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9b4d61c2b0d8806b5d4a10296c1cc1266ac4229f)) * added flag ;-; ([#195](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/195)) ([9b4d61c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9b4d61c2b0d8806b5d4a10296c1cc1266ac4229f))
- added scrapedAt property ([#149](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/149)) ([8e181b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e181b3010dfe25ea1e4b4ba181cbe9944d6e92e)) * added scrapedAt property ([#149](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/149)) ([8e181b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e181b3010dfe25ea1e4b4ba181cbe9944d6e92e))
- additional changes to [#201](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/201) ([#224](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/224)) ([bc354f3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bc354f3798a02e6ebc3c66a8b5eee553c25a19d4)) * additional changes to [#201](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/201) ([#224](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/224)) ([bc354f3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bc354f3798a02e6ebc3c66a8b5eee553c25a19d4))
- aesthetically pleasing squishier course blocks when compressed ([#232](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/232)) ([438c82b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/438c82bfb5db0f570b261d15368a05447939b0fb)) * aesthetically pleasing squishier course blocks when compressed ([#232](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/232)) ([438c82b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/438c82bfb5db0f570b261d15368a05447939b0fb))
- align day labels and add to grid ([1bb6191](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1bb6191244f40220a9af66cced4fe8f0c1e546a0)) * align day labels and add to grid ([1bb6191](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1bb6191244f40220a9af66cced4fe8f0c1e546a0))
- alignment on calendar and header ([#109](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/109)) ([07ec5ab](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/07ec5abc3e1b40b801b7c977d21399b31222a4d3)) * alignment on calendar and header ([#109](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/109)) ([07ec5ab](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/07ec5abc3e1b40b801b7c977d21399b31222a4d3))
- async course adding and async/other course block adjustments ([#273](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/273)) ([668c8d0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/668c8d00756a5e3e3daaade46380967c74eec1ae)) * async course adding and async/other course block adjustments ([#273](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/273)) ([668c8d0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/668c8d00756a5e3e3daaade46380967c74eec1ae))
- async text hiding on Calendar's Bottom Bar when there are no async courses ([#152](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/152)) ([0dff122](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0dff12232c29415fa7bc5ac3771393a406ab738d)) * async text hiding on Calendar's Bottom Bar when there are no async courses ([#152](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/152)) ([0dff122](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0dff12232c29415fa7bc5ac3771393a406ab738d))
- basic CalendarCourseMeeting component laid out - missing Text and Right Icon ([da9e7aa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/da9e7aac4135afa5184e4949c36a288ef6ab62a7)) * basic CalendarCourseMeeting component laid out - missing Text and Right Icon ([da9e7aa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/da9e7aac4135afa5184e4949c36a288ef6ab62a7))
- Best Practices ([#102](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/102)) ([5eb7be2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5eb7be246cd2a1db27c7ca777d81074ae7bb3b82)) * Best Practices ([#102](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/102)) ([5eb7be2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5eb7be246cd2a1db27c7ca777d81074ae7bb3b82))
- beta builds ([#187](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/187)) ([8a5e12e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8a5e12ec63539ae7199f0f1561f642cb9cf19302)) * beta builds ([#187](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/187)) ([8a5e12e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8a5e12ec63539ae7199f0f1561f642cb9cf19302))
- better discord icon ([#205](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/205)) ([a3f5e0f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a3f5e0f27ffdfcb4e3ddd47140979aca379f9f42)) * better discord icon ([#205](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/205)) ([a3f5e0f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a3f5e0f27ffdfcb4e3ddd47140979aca379f9f42))
- bold red refresh error ([#220](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/220)) ([229a8a2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/229a8a29e8d04dfbe15de200ceffc53b4f758413)) * bold red refresh error ([#220](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/220)) ([229a8a2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/229a8a29e8d04dfbe15de200ceffc53b4f758413))
- bottom bar for the calendar page ([#91](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/91)) ([0f730d6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0f730d6c50462c9f7d0428ebb379742305212ef6)) * bottom bar for the calendar page ([#91](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/91)) ([0f730d6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0f730d6c50462c9f7d0428ebb379742305212ef6))
- build without errors ([babc925](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/babc925967b01f1064b8d8e067e7bc792dd57b0a)) * build without errors ([babc925](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/babc925967b01f1064b8d8e067e7bc792dd57b0a))
- calendar components 3rd attempt at merging ([#60](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/60)) ([4faca8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4faca8c43befe19c9b05ff4f3afba420e016bb59)) * calendar components 3rd attempt at merging ([#60](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/60)) ([4faca8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4faca8c43befe19c9b05ff4f3afba420e016bb59))
- Calendar Components 3rd Attempt at Merging ([#60](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/60)) ([ab2cd68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ab2cd688fae616742b39c0637291a65e508e23bd)) * Calendar Components 3rd Attempt at Merging ([#60](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/60)) ([ab2cd68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ab2cd688fae616742b39c0637291a65e508e23bd))
- calendar course block component ([#75](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/75)) ([00e0019](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/00e00197efb807d420196f2b030fc4dc71e9cd42)) * calendar course block component ([#75](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/75)) ([00e0019](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/00e00197efb807d420196f2b030fc4dc71e9cd42))
- calendar course block component ([#75](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/75)) ([a41cb3e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a41cb3ed8711eff9a2fbbe435e867c073904f452)) * calendar course block component ([#75](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/75)) ([a41cb3e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a41cb3ed8711eff9a2fbbe435e867c073904f452))
- calendar grid and grid cells ([#81](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/81)) ([bfeb239](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bfeb2398aacf9a1fba892737b6ef0286acbc326f)) * calendar grid and grid cells ([#81](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/81)) ([bfeb239](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bfeb2398aacf9a1fba892737b6ef0286acbc326f))
- Calendar Grid and Grid Cells ([#81](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/81)) ([dd2f696](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dd2f696f8de2c0b291e917ef4d4cb2171b76712c)) * Calendar Grid and Grid Cells ([#81](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/81)) ([dd2f696](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dd2f696f8de2c0b291e917ef4d4cb2171b76712c))
- calendar header formatting and data displaying ([#160](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/160)) ([5cce1c7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5cce1c79fc2b50843426ed290a1b53849375e4eb)) * calendar header formatting and data displaying ([#160](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/160)) ([5cce1c7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5cce1c79fc2b50843426ed290a1b53849375e4eb))
- calendar matchings ([#173](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/173)) ([791a42b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/791a42bcd49478024b6f1a237a2d2a9f6211749e)) * calendar matchings ([#173](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/173)) ([791a42b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/791a42bcd49478024b6f1a237a2d2a9f6211749e))
- Calendar Schedule component finished, fix: list didn't allow updates when adding a new schedule ([#115](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/115)) ([a99a557](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a99a55788a0b9791ff048c8bbbaad5bada47c41f)) * Calendar Schedule component finished, fix: list didn't allow updates when adding a new schedule ([#115](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/115)) ([a99a557](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a99a55788a0b9791ff048c8bbbaad5bada47c41f))
- calendar-course-cell-color-picker ([#157](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/157)) ([df18491](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/df1849180de2c793c10e3a49d2c862d236456ec2)) * calendar-course-cell-color-picker ([#157](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/157)) ([df18491](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/df1849180de2c793c10e3a49d2c862d236456ec2))
- can open tabs, updated injected popup heading. basically done ([35fab34](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/35fab3444582b436f63c7b16a26ebd5989c4bf3e)) * can open tabs, updated injected popup heading. basically done ([35fab34](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/35fab3444582b436f63c7b16a26ebd5989c4bf3e))
- change Chip to tailwind css. Fixed eslint for ConflictsWithWarning ([3568b8e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3568b8eb5ee9ce3fe17f77f5641c0583bacc17a4)) * change Chip to tailwind css. Fixed eslint for ConflictsWithWarning ([3568b8e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3568b8eb5ee9ce3fe17f77f5641c0583bacc17a4))
- check-path-alias autofix ([#124](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/124)) ([265652c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/265652c4200a56b1ae62e6e94e0609fb4604c188)) * check-path-alias autofix ([#124](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/124)) ([265652c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/265652c4200a56b1ae62e6e94e0609fb4604c188))
- chrome extension works ([35f3c72](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/35f3c7225049d760c6061ed827aa796514291e62)) * chrome extension works ([35f3c72](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/35f3c7225049d760c6061ed827aa796514291e62))
- color palette for calendar ([#118](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/118)) ([471e55d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/471e55dcea1ae439658d00ed41570e2f218f0c3d)) * color palette for calendar ([#118](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/118)) ([471e55d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/471e55dcea1ae439658d00ed41570e2f218f0c3d))
- Conventional Commits ([#103](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/103)) ([fe599df](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fe599dfe7514ead880596d3b75c8370bde796e09)) * Conventional Commits ([#103](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/103)) ([fe599df](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fe599dfe7514ead880596d3b75c8370bde796e09))
- convert all LabelsAndDetails Components to Tailwind ([9e0f9df](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9e0f9df9de1a5c99657ddcef3b2eb101dd825b84)) * convert all LabelsAndDetails Components to Tailwind ([9e0f9df](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9e0f9df9de1a5c99657ddcef3b2eb101dd825b84))
- course color generation ([#179](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/179)) ([5ed81e4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5ed81e4be99a8b7f3c68a3ba70358e4cbe5cc613)) * course color generation ([#179](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/179)) ([5ed81e4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5ed81e4be99a8b7f3c68a3ba70358e4cbe5cc613))
- course colors ([#175](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/175)) ([dc77cc2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dc77cc27da7ab521e290a8c9ba810573e677b6b8)) * course colors ([#175](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/175)) ([dc77cc2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dc77cc27da7ab521e290a8c9ba810573e677b6b8))
- Course Flag Tooltips ([#178](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/178)) ([ba2bc60](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ba2bc60add2b87ccd4e9894c454d7a8d8382f0dd)) * Course Flag Tooltips ([#178](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/178)) ([ba2bc60](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ba2bc60add2b87ccd4e9894c454d7a8d8382f0dd))
- course-catalog-injected-popup ([#98](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/98)) ([89d03f4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/89d03f4244d324915cadadaf75a827221525d43f)) * course-catalog-injected-popup ([#98](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/98)) ([89d03f4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/89d03f4244d324915cadadaf75a827221525d43f))
- CourseStatus Component implemented ([#83](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/83)) ([58d7df4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/58d7df499ca9a8dfd4dc3aa47bc9d76837f21bf7)) * CourseStatus Component implemented ([#83](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/83)) ([58d7df4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/58d7df499ca9a8dfd4dc3aa47bc9d76837f21bf7))
- CourseStatus Component implemented ([#83](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/83)) ([fa1d737](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fa1d7374bcafbe19673dc31f33b3f8075e45f37a)) * CourseStatus Component implemented ([#83](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/83)) ([fa1d737](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/fa1d7374bcafbe19673dc31f33b3f8075e45f37a))
- create empty Popup story ([ad83ba4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ad83ba4cdc67f86096d0cbc2cb6659aa9be98a0b)) * create empty Popup story ([ad83ba4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ad83ba4cdc67f86096d0cbc2cb6659aa9be98a0b))
- Create icon helper ([#77](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/77)) ([ccea0f4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ccea0f4bd1e8d66dc0717d92c9e970040d9fbe9e)) * Create icon helper ([#77](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/77)) ([ccea0f4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ccea0f4bd1e8d66dc0717d92c9e970040d9fbe9e))
- Create icon helper ([#77](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/77)) ([1b51d65](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1b51d65c89a4b544f4f2a9c60106e14300f9a3b0)) * Create icon helper ([#77](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/77)) ([1b51d65](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1b51d65c89a4b544f4f2a9c60106e14300f9a3b0))
- Derek vinson/calendar header ([#94](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/94)) ([12d09b5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/12d09b54cbf840993d17323b96cf274ec24c67ce)) * Derek vinson/calendar header ([#94](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/94)) ([12d09b5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/12d09b54cbf840993d17323b96cf274ec24c67ce))
- Derek/disable updating ([#239](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/239)) ([0ab83ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0ab83efd47d75fb6c278994c7dc8a6d22b4b6b83)) * Derek/disable updating ([#239](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/239)) ([0ab83ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0ab83efd47d75fb6c278994c7dc8a6d22b4b6b83))
- Derek/export png ([#95](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/95)) ([58c2b46](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/58c2b4634a9d065ca156d27c4af69a504b7c2d2d)) * Derek/export png ([#95](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/95)) ([58c2b46](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/58c2b4634a9d065ca156d27c4af69a504b7c2d2d))
- DialogProvider component ([#198](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/198)) ([d1b921a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d1b921a5b000693d7f3dabaf84d8b9580c361941)) * DialogProvider component ([#198](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/198)) ([d1b921a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d1b921a5b000693d7f3dabaf84d8b9580c361941))
- dividers in calendar bottom bar ([#120](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/120)) ([eba5d9f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/eba5d9f508576d2648bd2501e5d5aaff32592566)) * dividers in calendar bottom bar ([#120](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/120)) ([eba5d9f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/eba5d9f508576d2648bd2501e5d5aaff32592566))
- **docs:** add extra acknowledgements ([e2cbfa3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e2cbfa3f48cb0b10a43134cf9784a44b13aa542d)) * **docs:** add extra acknowledgements ([e2cbfa3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e2cbfa3f48cb0b10a43134cf9784a44b13aa542d))
- drag only on vertical axis ([34a6449](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/34a644952995d4aedd18d11d327bd8589f5b2610)) * drag only on vertical axis ([34a6449](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/34a644952995d4aedd18d11d327bd8589f5b2610))
- early iteration of non-virtual list ([677aa62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/677aa624d7fb2bbb922aaf8806abe979c3399e4e)) * early iteration of non-virtual list ([677aa62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/677aa624d7fb2bbb922aaf8806abe979c3399e4e))
- enable TS strict mode ([#168](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/168)) ([efed1c0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/efed1c0edbf93987c551952a03c9c4b3c461d819)) * enable TS strict mode ([#168](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/168)) ([efed1c0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/efed1c0edbf93987c551952a03c9c4b3c461d819))
- experimental toggle for icons, left off for now ([#237](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/237)) ([f34dd95](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f34dd95d7765322b6cecb2e817a46e0549d929a5)) * experimental toggle for icons, left off for now ([#237](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/237)) ([f34dd95](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f34dd95d7765322b6cecb2e817a46e0549d929a5))
- fall 2023 grades ([#226](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/226)) ([863d980](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/863d980b2d1c706b1fc648f1a0de2a31515e339e)) * fall 2023 grades ([#226](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/226)) ([863d980](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/863d980b2d1c706b1fc648f1a0de2a31515e339e))
- finally fix grid JSX.Element generation ([e49fc29](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e49fc295ba40a37f9bf59686adbd8b4f523cd9c6)) * finally fix grid JSX.Element generation ([e49fc29](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e49fc295ba40a37f9bf59686adbd8b4f523cd9c6))
- finish ScheduleTotalHoursAndCourses ([12f680d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/12f680d7e9aef826281c8f769825408dc6f9f9de)) * finish ScheduleTotalHoursAndCourses ([12f680d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/12f680d7e9aef826281c8f769825408dc6f9f9de))
- fix icons on PopupMain and convert to tailwind ([#108](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/108)) ([87799d8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/87799d8f02aa5986025982f768639afdb1b0267a)) * fix icons on PopupMain and convert to tailwind ([#108](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/108)) ([87799d8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/87799d8f02aa5986025982f768639afdb1b0267a))
- fix save as button dividers ([#153](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/153)) ([7986549](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7986549fdd40834a85bedbfd0862cdb21c9ae657)) * fix save as button dividers ([#153](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/153)) ([7986549](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7986549fdd40834a85bedbfd0862cdb21c9ae657))
- grades by professor ([#225](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/225)) ([78d749a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/78d749a8a96f63c32663b464e5b663f932c28ed0)) * grades by professor ([#225](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/225)) ([78d749a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/78d749a8a96f63c32663b464e5b663f932c28ed0))
- html2canvas -> htmlToImage and fix derick's bugs ([bda0282](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bda02826b14b96fd65a6f24218950e510c5aa3d8)) * html2canvas -> htmlToImage and fix derick's bugs ([bda0282](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bda02826b14b96fd65a6f24218950e510c5aa3d8))
- icon added successfully ([3b588c2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3b588c20394523ddf90456e1ca58546e5b820c74)) * icon added successfully ([3b588c2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3b588c20394523ddf90456e1ca58546e5b820c74))
- implement Chip component ([23e881f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/23e881f14cc80100d8f8deef6a25daaa72d83f07)) * implement Chip component ([23e881f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/23e881f14cc80100d8f8deef6a25daaa72d83f07))
- implement flatten course schedule helper function ([e54f488](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e54f488b170d5a9af646c1ac82e8b33579fef61d)) * implement flatten course schedule helper function ([e54f488](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e54f488b170d5a9af646c1ac82e8b33579fef61d))
- implemented ConflictsWithWarning ([93f3a30](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93f3a307b4acce6cc55fc411f1ea2d7576006b00)) * implemented ConflictsWithWarning ([93f3a30](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/93f3a307b4acce6cc55fc411f1ea2d7576006b00))
- implemented InfoCard ([21b6430](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/21b643000d884442e025534802be6bb1f6ba68e2)) * implemented InfoCard ([21b6430](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/21b643000d884442e025534802be6bb1f6ba68e2))
- Initial Splash Text Commit ([#208](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/208)) ([0534f60](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0534f607a51f1dd0905f6f45f08519cba65ea05a)) * Initial Splash Text Commit ([#208](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/208)) ([0534f60](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0534f607a51f1dd0905f6f45f08519cba65ea05a))
- limit schedules to 10 ([#272](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/272)) ([290b841](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/290b8415170bca631e5d71b08d88db80b02163f0)) * limit schedules to 10 ([#272](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/272)) ([290b841](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/290b8415170bca631e5d71b08d88db80b02163f0))
- link to your registered courses ([#228](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/228)) ([f83e012](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f83e012d62e268d95dce19a30e54e34f3779142b)) * link to your registered courses ([#228](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/228)) ([f83e012](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f83e012d62e268d95dce19a30e54e34f3779142b))
- list reordering ([#154](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/154)) ([038ebaa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/038ebaa2689c14bc9906afb542ef62c21cb13177)) * list reordering ([#154](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/154)) ([038ebaa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/038ebaa2689c14bc9906afb542ef62c21cb13177))
- listed versioning for beta builds ([#192](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/192)) ([0c42979](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c429794230a91c4fd949715b7eea210ac18fee3)) * listed versioning for beta builds ([#192](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/192)) ([0c42979](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c429794230a91c4fd949715b7eea210ac18fee3))
- lowercase instructor! ([#268](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/268)) ([9ec05ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9ec05ef764b2451c05e2d084d92074fb1e984268)) * lowercase instructor! ([#268](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/268)) ([9ec05ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9ec05ef764b2451c05e2d084d92074fb1e984268))
- made List more extensible ([cd34601](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cd34601379d5cd2b53d967afbc954178834823b9)) * made List more extensible ([cd34601](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cd34601379d5cd2b53d967afbc954178834823b9))
- match calendar designs & add functionality ([#176](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/176)) ([8027c3d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8027c3d1bf228053acbad3e232b030c9ddbaca6a)) * match calendar designs & add functionality ([#176](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/176)) ([8027c3d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8027c3d1bf228053acbad3e232b030c9ddbaca6a))
- migrate styles to TailwindCSS ([7e2f5ea](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7e2f5eaed7d782bc886545f7241e0891d55cfae0)) * migrate styles to TailwindCSS ([7e2f5ea](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7e2f5eaed7d782bc886545f7241e0891d55cfae0))
- migration update showing ([#293](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/293)) ([aede681](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aede681d4bb5b01723eb9d68527f4e64991adfab)) * migration update showing ([#293](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/293)) ([aede681](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aede681d4bb5b01723eb9d68527f4e64991adfab))
- missed one chip toggle there ([#245](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/245)) ([5ca24da](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5ca24dab82509d34cf43af4359940476159d052f)) * missed one chip toggle there ([#245](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/245)) ([5ca24da](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5ca24dab82509d34cf43af4359940476159d052f))
- newer grades and parameterized queries ([#238](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/238)) ([75ad416](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/75ad4167b6639bbcf58e2dc6dafc80ed4656a899)) * newer grades and parameterized queries ([#238](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/238)) ([75ad416](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/75ad4167b6639bbcf58e2dc6dafc80ed4656a899))
- one single exclamation mark did all that ([#235](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/235)) ([be87e41](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/be87e4181430e0df41683be5c9d0a1ce992b2c61)) * one single exclamation mark did all that ([#235](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/235)) ([be87e41](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/be87e4181430e0df41683be5c9d0a1ce992b2c61))
- open an injected course page on course block click in popup main ([#146](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/146)) ([2709484](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/27094846f70feb8d83c8d464ab0fa8dcd99b3e71)) * open an injected course page on course block click in popup main ([#146](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/146)) ([2709484](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/27094846f70feb8d83c8d464ab0fa8dcd99b3e71))
- pad unique ids to 5 digits ([#170](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/170)) ([b4ad687](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4ad6870bfc88f68bdd7f87fe1d94c23d0a02b95)) * pad unique ids to 5 digits ([#170](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/170)) ([b4ad687](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4ad6870bfc88f68bdd7f87fe1d94c23d0a02b95))
- parallelize initializeDB.ts promises ([9f1dcc6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9f1dcc667df9857e3d0469fe7112a9f985aad490)) * parallelize initializeDB.ts promises ([9f1dcc6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9f1dcc667df9857e3d0469fe7112a9f985aad490))
- popout icon for ccip in calendar ([#221](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/221)) ([6812d68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6812d685d08902f7b3d9d2e5293350c8170c6f06)) * popout icon for ccip in calendar ([#221](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/221)) ([6812d68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6812d685d08902f7b3d9d2e5293350c8170c6f06))
- popup schedule select ([#126](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/126)) ([7f2a589](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7f2a5893d4dd82a0f077dca6dfd28e79f929766f)) * popup schedule select ([#126](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/126)) ([7f2a589](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7f2a5893d4dd82a0f077dca6dfd28e79f929766f))
- PopupCourseBlock Component ([#79](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/79)) ([9accd17](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9accd17bd418f8c9334e80b0f7e38673092ba208)) * PopupCourseBlock Component ([#79](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/79)) ([9accd17](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9accd17bd418f8c9334e80b0f7e38673092ba208))
- PopupCourseBlock Component ([#79](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/79)) ([f045b40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f045b400a56569f7353eb00ecd0d081b68660fc5)) * PopupCourseBlock Component ([#79](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/79)) ([f045b40](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f045b400a56569f7353eb00ecd0d081b68660fc5))
- proper injected styles ([#164](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/164)) ([e919e96](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e919e96c53bc9bec7db3d09693d1ff0684b56e87)) * proper injected styles ([#164](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/164)) ([e919e96](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e919e96c53bc9bec7db3d09693d1ff0684b56e87))
- readme animation ([#281](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/281)) ([da6d86c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/da6d86c785f92f70f52ef0b4cab3dc14e0bd9790)) * readme animation ([#281](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/281)) ([da6d86c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/da6d86c785f92f70f52ef0b4cab3dc14e0bd9790))
- refactor all components in common ([e544312](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e5443122b422266a126de497b475020e298b2f1c)) * refactor all components in common ([e544312](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e5443122b422266a126de497b475020e298b2f1c))
- refactor all components in injected ([0c44849](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c44849e15333c3a5cbdf8972eb31ada0e830b0a)) * refactor all components in injected ([0c44849](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c44849e15333c3a5cbdf8972eb31ada0e830b0a))
- refactor calendar ([28f1924](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/28f192472b7f244253b6a0b5339491b9740880f7)) * refactor calendar ([28f1924](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/28f192472b7f244253b6a0b5339491b9740880f7))
- Refactor database initialization code ([5e98f45](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5e98f45210346f0e7bb69af5cf25e336c76416ca)) * Refactor database initialization code ([5e98f45](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5e98f45210346f0e7bb69af5cf25e336c76416ca))
- release notes ([#283](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/283)) ([bd17e33](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bd17e3353792df70ca2e33c8b7a32b5fb9acc7d2)) * release notes ([#283](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/283)) ([bd17e33](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bd17e3353792df70ca2e33c8b7a32b5fb9acc7d2))
- report issue popup ([#261](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/261)) ([65ff6bf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/65ff6bfbbfc6b696621628e448865155d0405f7f)) * report issue popup ([#261](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/261)) ([65ff6bf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/65ff6bfbbfc6b696621628e448865155d0405f7f))
- rerouted directory to syllabus when click on professor name ([#211](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/211)) ([8959e0d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8959e0d9f7fdd82125f391f1ec51aa390ba9450d)) * rerouted directory to syllabus when click on professor name ([#211](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/211)) ([8959e0d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8959e0d9f7fdd82125f391f1ec51aa390ba9450d))
- sam's jokes ([#278](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/278)) ([895cd31](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/895cd31d8eb40e7e99fc0969b543e95bd070f6b7)) * sam's jokes ([#278](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/278)) ([895cd31](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/895cd31d8eb40e7e99fc0969b543e95bd070f6b7))
- save as PNG functionality ([ad18fbd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ad18fbd16235be0c75121b527867fac708670300)) * save as PNG functionality ([ad18fbd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ad18fbd16235be0c75121b527867fac708670300))
- schedule list item action menu ([#230](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/230)) ([15fc369](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/15fc3699cfda3ad56e5a262bd37dca166e2685dd)) * schedule list item action menu ([#230](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/230)) ([15fc369](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/15fc3699cfda3ad56e5a262bd37dca166e2685dd))
- screenshot whole page, hide certain elements, screenshot fixed size ([#180](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/180)) ([7d4c5d7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7d4c5d7be8b266b5f3caf2135c4f3fecb96d75be)) * screenshot whole page, hide certain elements, screenshot fixed size ([#180](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/180)) ([7d4c5d7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7d4c5d7be8b266b5f3caf2135c4f3fecb96d75be))
- settings page ([#260](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/260)) ([7a5c3a2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a5c3a2e62c3a726735f9fe921f9e08d4092d0f9)) * settings page ([#260](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/260)) ([7a5c3a2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a5c3a2e62c3a726735f9fe921f9e08d4092d0f9))
- show async courses in the bottom bar ([#204](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/204)) ([227de53](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/227de53e8453804f0791f269dae64ec388136390)) * show async courses in the bottom bar ([#204](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/204)) ([227de53](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/227de53e8453804f0791f269dae64ec388136390))
- some small changes for colors and font ([#201](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/201)) ([bae1da4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bae1da43d3373e12d593682e9fb69e19557b5749)) * some small changes for colors and font ([#201](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/201)) ([bae1da4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bae1da43d3373e12d593682e9fb69e19557b5749))
- splash text additions before v2 release ([#296](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/296)) ([e774f31](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e774f316e3f92c03e79274a55f1f729178c9a14a)) * splash text additions before v2 release ([#296](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/296)) ([e774f31](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e774f316e3f92c03e79274a55f1f729178c9a14a))
- splash text has arrived! ([#246](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/246)) ([9971435](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9971435716dec11e954dc26c14bf7d1505563d0d)) * splash text has arrived! ([#246](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/246)) ([9971435](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9971435716dec11e954dc26c14bf7d1505563d0d))
- Storybook for Vite ([#52](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/52)) ([9cc299c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9cc299ced6132644a5c2375b95a8a16a3482601b)) * Storybook for Vite ([#52](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/52)) ([9cc299c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9cc299ced6132644a5c2375b95a8a16a3482601b))
- swe title updates ([#310](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/310)) ([4629626](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4629626a31934c491758ddca874e5018d9c22e57)) * swe title updates ([#310](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/310)) ([4629626](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4629626a31934c491758ddca874e5018d9c22e57))
- switch button ([#229](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/229)) ([abae7a5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/abae7a5c22ab944262d66ae897298b008a03c5f1)) * switch button ([#229](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/229)) ([abae7a5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/abae7a5c22ab944262d66ae897298b008a03c5f1))
- temporary removal of waitlist etc ([#236](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/236)) ([d424ccc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d424ccce49ba05305b24cb61aeb4ffeada5bab33)) * temporary removal of waitlist etc ([#236](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/236)) ([d424ccc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d424ccce49ba05305b24cb61aeb4ffeada5bab33))
- UnoCSS (TailwindCSS) (Storybook only) ([#61](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/61)) ([85c7f78](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/85c7f7817c58f13f6a4e8bec13a45f6412ad87db)) * UnoCSS (TailwindCSS) (Storybook only) ([#61](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/61)) ([85c7f78](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/85c7f7817c58f13f6a4e8bec13a45f6412ad87db))
- UnoCSS (TailwindCSS) (Storybook only) ([#61](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/61)) ([6521a4b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6521a4b2a90af06e182c779f2fc7cb1a3b1e55c1)) * UnoCSS (TailwindCSS) (Storybook only) ([#61](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/61)) ([6521a4b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6521a4b2a90af06e182c779f2fc7cb1a3b1e55c1))
- unplugin-icons ([#62](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/62)) ([2d67b12](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2d67b1218ff852c59901b77909615c5f54794a67)) * unplugin-icons ([#62](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/62)) ([2d67b12](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2d67b1218ff852c59901b77909615c5f54794a67))
- unplugin-icons ([#62](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/62)) ([945e09b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/945e09b49e4ffa9bd4b02442f6567bb99831923e)) * unplugin-icons ([#62](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/62)) ([945e09b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/945e09b49e4ffa9bd4b02442f6567bb99831923e))
- update admin titles ([4cf8c3f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4cf8c3f9645cd8fda07244413cdc406105b12fec)) * update admin titles ([4cf8c3f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4cf8c3f9645cd8fda07244413cdc406105b12fec))
- update badge count when schedule changes ([#150](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/150)) ([a5e9e3c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a5e9e3c2145d61d8cc5788eb50fa19718e6d13bf)) * update badge count when schedule changes ([#150](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/150)) ([a5e9e3c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a5e9e3c2145d61d8cc5788eb50fa19718e6d13bf))
- update Button to v2 design ([863521f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/863521fb3bb268131bd3f369064ae10a442b4fbc)) * update Button to v2 design ([863521f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/863521fb3bb268131bd3f369064ae10a442b4fbc))
- update dialog component to headlessui ([#159](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/159)) ([442be8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/442be8cbee06ec403467b100d8d5300dd4a7ea72)) * update dialog component to headlessui ([#159](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/159)) ([442be8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/442be8cbee06ec403467b100d8d5300dd4a7ea72))
- update with TailwindCSS ([f3a8a7d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f3a8a7db560a6b28f96a2e976c8ed33af97fe77a)) * update with TailwindCSS ([f3a8a7d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f3a8a7db560a6b28f96a2e976c8ed33af97fe77a))
- updated calendar page and recruitment banner links ([#219](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/219)) ([a2303ee](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a2303ee35f10aba5fa7ff5e307d0092a39f18830)) * updated calendar page and recruitment banner links ([#219](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/219)) ([a2303ee](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a2303ee35f10aba5fa7ff5e307d0092a39f18830))
- updated divider component ([#99](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/99)) ([8ab60c9](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8ab60c9f018143df594e7bfd0d321666289cc28c)) * updated divider component ([#99](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/99)) ([8ab60c9](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8ab60c9f018143df594e7bfd0d321666289cc28c))
- updated Text component to latest design specification ([#70](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/70)) ([8b8433d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b8433deaf8fe66f917fd66fb56cb0a748bc6e6e)) * updated Text component to latest design specification ([#70](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/70)) ([8b8433d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8b8433deaf8fe66f917fd66fb56cb0a748bc6e6e))
- updated Text component to latest design specification ([#70](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/70)) ([bb727f7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bb727f70bea5a4c2ff5dba7e9b4428c0c84de7b2)) * updated Text component to latest design specification ([#70](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/70)) ([bb727f7](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bb727f70bea5a4c2ff5dba7e9b4428c0c84de7b2))
- updating joke styling and updating jokes array ([#277](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/277)) ([0da27e2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0da27e2c46e9fc32efb80808715053c1f9f5165a)) * updating joke styling and updating jokes array ([#277](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/277)) ([0da27e2](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0da27e2c46e9fc32efb80808715053c1f9f5165a))
- use display: grid for calendarGrid ([b535a6e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b535a6eb32ac1091c8b5309ca3cfb6bab0b62526)) * use display: grid for calendarGrid ([b535a6e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b535a6eb32ac1091c8b5309ca3cfb6bab0b62526))
- use downloadBlob util ([#186](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/186)) ([2dfb10e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2dfb10e57b51a08fcde2dc6638a81b5ec9bbc7ab)) * use downloadBlob util ([#186](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/186)) ([2dfb10e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2dfb10e57b51a08fcde2dc6638a81b5ec9bbc7ab))
- use filter() instead of pop() ([063349d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/063349d96d0ad008fa0dc1f0a8c7a27cf3f108ce)) * use filter() instead of pop() ([063349d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/063349d96d0ad008fa0dc1f0a8c7a27cf3f108ce))
- use React-icons ([8df9ea5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8df9ea55a9f0bcb04c90314cb311bba9d1ebf2e3)) * use React-icons ([8df9ea5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8df9ea55a9f0bcb04c90314cb311bba9d1ebf2e3))
- UTRP v2 migration ([#292](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/292)) ([d22237d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d22237d561ddf6a2f8ed699ca4c11088a8b408e8)) * UTRP v2 migration ([#292](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/292)) ([d22237d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d22237d561ddf6a2f8ed699ca4c11088a8b408e8))
- working PNG and CAL downloads ([#119](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/119)) ([d9ee23c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d9ee23c5bbe27240a0e2849aae2eae81d7960bb5)) * working PNG and CAL downloads ([#119](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/119)) ([d9ee23c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d9ee23c5bbe27240a0e2849aae2eae81d7960bb5))
### Bug Fixes ### Bug Fixes
- add margin-top: -10px to p ([18406b0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/18406b0c94604b1b524de03bf97e78cffe7a5bbf)) * add margin-top: -10px to p ([18406b0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/18406b0c94604b1b524de03bf97e78cffe7a5bbf))
- added room number to course popup ([#231](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/231)) ([9eaff24](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9eaff24cbb6d78d7f28521a07c32630a90a96f82)) * added room number to course popup ([#231](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/231)) ([9eaff24](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9eaff24cbb6d78d7f28521a07c32630a90a96f82))
- align timeBlock div ([4dc8957](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4dc8957c459d0ee57e40c67fa61194ace832327e)) * align timeBlock div ([4dc8957](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4dc8957c459d0ee57e40c67fa61194ace832327e))
- async course display size ([#181](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/181)) ([949bbb0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/949bbb0835bfb55a81f0800850aa084b4ed1bfc4)) * async course display size ([#181](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/181)) ([949bbb0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/949bbb0835bfb55a81f0800850aa084b4ed1bfc4))
- broken close bracket ([b34aacb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b34aacb06772af6f83d1f7bafe828ba43a10db85)) * broken close bracket ([b34aacb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b34aacb06772af6f83d1f7bafe828ba43a10db85))
- broken file ([92462cf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/92462cf0dffa222d75965885ee0e63181bce9890)) * broken file ([92462cf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/92462cf0dffa222d75965885ee0e63181bce9890))
- bugs ([2a01506](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2a0150600f26af5b7394f878e13c4d19611f3f64)) * bugs ([2a01506](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2a0150600f26af5b7394f878e13c4d19611f3f64))
- build ([60ab140](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/60ab140c556ec56eca78b1f4e280dfb6e9dbb538)) * build ([60ab140](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/60ab140c556ec56eca78b1f4e280dfb6e9dbb538))
- build errors and merge in Casey's branch (driodiwb) ([39947b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/39947b3694f42f9d5ad2f21d044585aa3d12c407)) * build errors and merge in Casey's branch (driodiwb) ([39947b3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/39947b3694f42f9d5ad2f21d044585aa3d12c407))
- build errors and restructure Calendar page ([c6a48dd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c6a48dd3f6025fe531c18e12b8730683d63f67ed)) * build errors and restructure Calendar page ([c6a48dd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c6a48dd3f6025fe531c18e12b8730683d63f67ed))
- calendar course cell colors ([74be880](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/74be880f9d2dc340ba48416429711fb28915baa2)) * calendar course cell colors ([74be880](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/74be880f9d2dc340ba48416429711fb28915baa2))
- calendar storybook issue ([#125](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/125)) ([0c5bec8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c5bec8002022cb4702f3879f43fcd32b9fa6b5a)) * calendar storybook issue ([#125](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/125)) ([0c5bec8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0c5bec8002022cb4702f3879f43fcd32b9fa6b5a))
- Calendar View/Scaling Issues ([#144](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/144)) ([4c61ebd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4c61ebd3fc93240903f1224f9dd3057a23d30046)) * Calendar View/Scaling Issues ([#144](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/144)) ([4c61ebd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4c61ebd3fc93240903f1224f9dd3057a23d30046))
- CalendarCourseCell spacing ([13c69ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/13c69ef862f0214dfa6c9368fc3463519aa8f2f0)) * CalendarCourseCell spacing ([13c69ef](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/13c69ef862f0214dfa6c9368fc3463519aa8f2f0))
- change Chromatic action to build current branch, not base branch ([#100](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/100)) ([e73c9fe](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e73c9fe417169e73c395cde1aeb841eee8add579)) * change Chromatic action to build current branch, not base branch ([#100](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/100)) ([e73c9fe](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e73c9fe417169e73c395cde1aeb841eee8add579))
- change material icons to material symbols ([#71](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/71)) ([52e34cb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/52e34cb830ff1f12659703776bd3d9a24815ee00)) * change material icons to material symbols ([#71](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/71)) ([52e34cb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/52e34cb830ff1f12659703776bd3d9a24815ee00))
- chromatic build ([11303da](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/11303daebcc1e4de340463e6266082dc0920582d)) * chromatic build ([11303da](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/11303daebcc1e4de340463e6266082dc0920582d))
- chromatic builds on PRs ([#140](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/140)) ([78a6939](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/78a69399292767a1f8eac66e4c86cbcb8aad019d)) * chromatic builds on PRs ([#140](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/140)) ([78a6939](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/78a69399292767a1f8eac66e4c86cbcb8aad019d))
- chromatic storybook - CourseCatalogInjectedPopup ([#106](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/106)) ([ced2997](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ced29975b20a2a5beac51ea875627a2b1042e5c2)) * chromatic storybook - CourseCatalogInjectedPopup ([#106](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/106)) ([ced2997](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ced29975b20a2a5beac51ea875627a2b1042e5c2))
- clean up [#173](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/173) ([#174](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/174)) ([afa634f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/afa634f0853dc16cf313c129434d49bf132388d0)) * clean up [#173](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/173) ([#174](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/174)) ([afa634f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/afa634f0853dc16cf313c129434d49bf132388d0))
- cleanup imports ([#112](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/112)) ([b17c3fa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b17c3fae6d9a8fdb2f6de84dc28d00bc31e7bcc8)) * cleanup imports ([#112](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/112)) ([b17c3fa](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b17c3fae6d9a8fdb2f6de84dc28d00bc31e7bcc8))
- conflict row bug ([#130](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/130)) ([a8ea3bc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a8ea3bc683f88fc33191a8cacf649be83f6758f8)) * conflict row bug ([#130](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/130)) ([a8ea3bc](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a8ea3bc683f88fc33191a8cacf649be83f6758f8))
- ConflictsWithWarning ([1599e48](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1599e48d75d4877b3515c943120d83b59d084a55)) * ConflictsWithWarning ([1599e48](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1599e48d75d4877b3515c943120d83b59d084a55))
- correct parsing of noon ([#155](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/155)) ([91f62e1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/91f62e1943633eaef7dc8d437e6a7397456a9123)) * correct parsing of noon ([#155](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/155)) ([91f62e1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/91f62e1943633eaef7dc8d437e6a7397456a9123))
- db with proper insertion order ([8e79d6a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e79d6a6a87210e0a30584c0c9ceb60bd3a33665)) * db with proper insertion order ([8e79d6a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e79d6a6a87210e0a30584c0c9ceb60bd3a33665))
- delete storybook timestamp file ([f93a98e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f93a98e46ae76e9b1474c8143bfc3f5076e8b91f)) * delete storybook timestamp file ([f93a98e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f93a98e46ae76e9b1474c8143bfc3f5076e8b91f))
- dialog movement ([#227](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/227)) ([bc5d68c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bc5d68ce18f818c285ef7f5eda03f499365ee2e8)) * dialog movement ([#227](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/227)) ([bc5d68c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bc5d68ce18f818c285ef7f5eda03f499365ee2e8))
- disabled [object Object] hover message on dialog popups ([#284](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/284)) ([dcc1d81](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dcc1d81a74922ba27ad2a6859f978f3f20136178)) * disabled [object Object] hover message on dialog popups ([#284](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/284)) ([dcc1d81](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dcc1d81a74922ba27ad2a6859f978f3f20136178))
- divider usage in HeaderAndActions ([#113](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/113)) ([84e8320](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/84e8320e8fce5961877a5745e89419a722055ed5)) * divider usage in HeaderAndActions ([#113](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/113)) ([84e8320](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/84e8320e8fce5961877a5745e89419a722055ed5))
- doesn't autoload on pages that don't have pages to load ([#270](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/270)) ([88eeb62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/88eeb620aec52d6e4172cc908843339412ade90b)) * doesn't autoload on pages that don't have pages to load ([#270](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/270)) ([88eeb62](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/88eeb620aec52d6e4172cc908843339412ade90b))
- don't crash on cultural diversity flag ([#196](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/196)) ([5b1e451](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5b1e4513e29203a54ed9f4913c227d6c45d2e67a)) * don't crash on cultural diversity flag ([#196](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/196)) ([5b1e451](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5b1e4513e29203a54ed9f4913c227d6c45d2e67a))
- eslint and remove React-beautiful-dnd ([6af805b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6af805ba3a7b04b7e5705d3d597b5ace686d61e0)) * eslint and remove React-beautiful-dnd ([6af805b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6af805ba3a7b04b7e5705d3d597b5ace686d61e0))
- extra space ([0f43796](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0f43796cd864acd012fc1242cbdf34844df62406)) * extra space ([0f43796](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0f43796cd864acd012fc1242cbdf34844df62406))
- Fix popup drag hitbox ([#216](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/216)) ([7a40008](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a40008c1e3846f09ac4eeadc4a50f810ffc0d90)) * Fix popup drag hitbox ([#216](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/216)) ([7a40008](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7a40008c1e3846f09ac4eeadc4a50f810ffc0d90))
- fixed bug where activeSchedule doesn't update correctly ([#158](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/158)) ([a409090](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a409090b904686b76a5db52af980e81108b97082)) * fixed bug where activeSchedule doesn't update correctly ([#158](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/158)) ([a409090](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a409090b904686b76a5db52af980e81108b97082))
- fixed bug with course cells after 12 PM extending past midnight ([#122](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/122)) ([f22a3cd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f22a3cd7c0df138ca6ecd4b4a4efaa7ce845c3dd)) * fixed bug with course cells after 12 PM extending past midnight ([#122](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/122)) ([f22a3cd](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/f22a3cd7c0df138ca6ecd4b4a4efaa7ce845c3dd))
- fixed issues involving course meeting objects not being recognized as course meeting objects ([#132](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/132)) ([3406e9a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3406e9a0e2644218fc06cbd0b05046df5339264c)) * fixed issues involving course meeting objects not being recognized as course meeting objects ([#132](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/132)) ([3406e9a](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3406e9a0e2644218fc06cbd0b05046df5339264c))
- Fixed typescript error ([#161](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/161)) ([df7a7c6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/df7a7c65d695cb5c5a825ac9f5ecc48438735b65)) * Fixed typescript error ([#161](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/161)) ([df7a7c6](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/df7a7c65d695cb5c5a825ac9f5ecc48438735b65))
- grade dist when no instructor ([#269](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/269)) ([6a363ae](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6a363aeb5ccb1c11e15db317b34c4f04a3c9aafc)) * grade dist when no instructor ([#269](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/269)) ([6a363ae](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6a363aeb5ccb1c11e15db317b34c4f04a3c9aafc))
- grid JSX.Element generation ([b691bf3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b691bf3231d5002a631687f26c7409e311237662)) * grid JSX.Element generation ([b691bf3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b691bf3231d5002a631687f26c7409e311237662))
- icon library resolution ([#74](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/74)) ([bb3b313](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bb3b313fd272bba12f99270c912e046df5358fed)) * icon library resolution ([#74](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/74)) ([bb3b313](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bb3b313fd272bba12f99270c912e046df5358fed))
- idk why that comment was there ([#177](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/177)) ([c5fc621](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c5fc6219e1540e1fb889d8bedc7296190103a56b)) * idk why that comment was there ([#177](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/177)) ([c5fc621](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c5fc6219e1540e1fb889d8bedc7296190103a56b))
- import error ([152bc45](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/152bc4577685605ae95f39d9e670b5449e32e0e9)) * import error ([152bc45](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/152bc4577685605ae95f39d9e670b5449e32e0e9))
- improper list data propagation ([#240](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/240)) ([149fda3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/149fda3b721dcc3d8ffa6c2398c28e39dcb62aaa)) * improper list data propagation ([#240](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/240)) ([149fda3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/149fda3b721dcc3d8ffa6c2398c28e39dcb62aaa))
- improve dialog handling and error management in list items ([#257](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/257)) ([1942508](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1942508d8d11f51b62806894ae13162b6404753b)) * improve dialog handling and error management in list items ([#257](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/257)) ([1942508](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/1942508d8d11f51b62806894ae13162b6404753b))
- injection not working from som/elie commit ([#145](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/145)) ([591687e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/591687eee8731b3649a5f8c1357a416eb2992a93)) * injection not working from som/elie commit ([#145](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/145)) ([591687e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/591687eee8731b3649a5f8c1357a416eb2992a93))
- list component fixed ([#162](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/162)) ([5714ed1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5714ed16d72c5442f58366ac51f40cc32ea406ab)) * list component fixed ([#162](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/162)) ([5714ed1](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5714ed16d72c5442f58366ac51f40cc32ea406ab))
- made list draggable only by handle ([cbb190b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cbb190bf4b3ad694c87b650489e1fca5aa6b7826)) * made list draggable only by handle ([cbb190b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cbb190bf4b3ad694c87b650489e1fca5aa6b7826))
- margins on list component ([cc71389](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cc7138949cf77d1bcf1520633d669e939540575e)) * margins on list component ([cc71389](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/cc7138949cf77d1bcf1520633d669e939540575e))
- non-determinstic options page generation ([#137](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/137)) ([d700110](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d70011016a45f5de62b06839a31c634637024f4a)) * non-determinstic options page generation ([#137](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/137)) ([d700110](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d70011016a45f5de62b06839a31c634637024f4a))
- non-virtual dnd ([837fddf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/837fddf8048350b0b25a58bbd16c1957fbad6edb)) * non-virtual dnd ([837fddf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/837fddf8048350b0b25a58bbd16c1957fbad6edb))
- old icon removed in .tsx ([4d387e8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4d387e8063314c7c6d7452b746728e2cf85a1787)) * old icon removed in .tsx ([4d387e8](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4d387e8063314c7c6d7452b746728e2cf85a1787))
- only show button hover effects when not disabled ([8e3aa7e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e3aa7ef3380836fd36a12698b1f6982e110c0f7)) * only show button hover effects when not disabled ([8e3aa7e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/8e3aa7ef3380836fd36a12698b1f6982e110c0f7))
- openTabFromContentScript TS issue ([3a48859](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3a48859dddc0e85649ff6a949e9afd22c962825d)) * openTabFromContentScript TS issue ([3a48859](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3a48859dddc0e85649ff6a949e9afd22c962825d))
- options page ([#131](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/131)) ([dc100b5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dc100b5d3ac1db5f1dc011ea2536c286f56fa55b)) * options page ([#131](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/131)) ([dc100b5](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/dc100b5d3ac1db5f1dc011ea2536c286f56fa55b))
- reactivity ([#188](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/188)) ([4f4f34e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4f4f34e281e8aabcdbf8b06652d05bca3554b11f)) * reactivity ([#188](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/188)) ([4f4f34e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/4f4f34e281e8aabcdbf8b06652d05bca3554b11f))
- README.md ([a30fecf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a30fecf8ec76ecae03be9bb4d813803b9ffdb490)) * README.md ([a30fecf](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/a30fecf8ec76ecae03be9bb4d813803b9ffdb490))
- README.md ([b27b21b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b27b21bbfcc8fbdc130c3fa85776479451745b7b)) * README.md ([b27b21b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b27b21bbfcc8fbdc130c3fa85776479451745b7b))
- refactor AST parsing for custom ESLint rule ([62f0851](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/62f0851406bf82d3d0481fed40462bdd30ffcf1b)) * refactor AST parsing for custom ESLint rule ([62f0851](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/62f0851406bf82d3d0481fed40462bdd30ffcf1b))
- remote react-window fully (from List component) ([#114](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/114)) ([5f1c023](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5f1c0231e483bc571ef944bbc0b950ab20d31028)) * remote react-window fully (from List component) ([#114](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/114)) ([5f1c023](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/5f1c0231e483bc571ef944bbc0b950ab20d31028))
- remove course name from the syllabi lookup ([#200](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/200)) ([bcb5a8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bcb5a8c469c4b5c1909880ab6789d67d04487ed2)) * remove course name from the syllabi lookup ([#200](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/200)) ([bcb5a8c](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/bcb5a8c469c4b5c1909880ab6789d67d04487ed2))
- remove extra spacing ([#121](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/121)) ([6ba8b68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6ba8b68654dc182bd2a084f93b0f4f7d5c132862)) * remove extra spacing ([#121](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/121)) ([6ba8b68](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/6ba8b68654dc182bd2a084f93b0f4f7d5c132862))
- remove white space after duplicate schedule regex matching ([#286](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/286)) ([d73615e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d73615e28129ee7d78604c6b295ec572b11be46e)) * remove white space after duplicate schedule regex matching ([#286](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/286)) ([d73615e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/d73615e28129ee7d78604c6b295ec572b11be46e))
- rename to course block and fix line height for styling ([b602b0b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b602b0b895416a26214e934f9650a8ec0a10711e)) * rename to course block and fix line height for styling ([b602b0b](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b602b0b895416a26214e934f9650a8ec0a10711e))
- revert "chore: add default story" ([aef8c3d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aef8c3d9873c6df79f6a54ab4c669ee1849b5ea5)) * revert "chore: add default story" ([aef8c3d](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/aef8c3d9873c6df79f6a54ab4c669ee1849b5ea5))
- revert "rename to course block and fix line height for styling" ([0273a23](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0273a239139c0f3c0ad773ed8ab1cd4ad8d22fa3)) * revert "rename to course block and fix line height for styling" ([0273a23](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/0273a239139c0f3c0ad773ed8ab1cd4ad8d22fa3))
- revert CalendarGrid and CalendarGridCell back to SCSS from Tailwind ([56306ab](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/56306ab9440baf5ca1158f95d788de7ba42d73d8)) * revert CalendarGrid and CalendarGridCell back to SCSS from Tailwind ([56306ab](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/56306ab9440baf5ca1158f95d788de7ba42d73d8))
- revert last commit ([27d945f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/27d945f57c76a1c2f31fee9f98c324fab6183bb6)) * revert last commit ([27d945f](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/27d945f57c76a1c2f31fee9f98c324fab6183bb6))
- Schedule Switching Bugs ([#138](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/138)) ([c51e688](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c51e6881d186e8bef056e483e2bca95585d5eac6)) * Schedule Switching Bugs ([#138](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/138)) ([c51e688](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/c51e6881d186e8bef056e483e2bca95585d5eac6))
- support classes with no location ([#242](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/242)) ([3ff06e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3ff06e043b112bc232c9c173e72b2ea99609cf7a)) * support classes with no location ([#242](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/242)) ([3ff06e0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/3ff06e043b112bc232c9c173e72b2ea99609cf7a))
- theme colors ([2f537b4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2f537b4f3e3946241f2e287d9ae075e5a2aa7087)) * theme colors ([2f537b4](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/2f537b4f3e3946241f2e287d9ae075e5a2aa7087))
- type issues by using correct import ([#111](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/111)) ([19fe070](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/19fe070491be9e2d40f12918561f6f6ac5921237)) * type issues by using correct import ([#111](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/111)) ([19fe070](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/19fe070491be9e2d40f12918561f6f6ac5921237))
- **ui:** unhid settings button ([#288](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/288)) ([db1eac3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/db1eac33a2786a3db87f0e6b3679233a85ab923f)) * **ui:** unhid settings button ([#288](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/288)) ([db1eac3](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/db1eac33a2786a3db87f0e6b3679233a85ab923f))
- undefined color case ([9aa78a0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9aa78a02a5d4f68daa062e4892875cf5f568306c)) * undefined color case ([9aa78a0](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/9aa78a02a5d4f68daa062e4892875cf5f568306c))
- unocss theme color namings ([79d7832](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/79d7832d0987b8a7c9650b984f72ffd702682423)) * unocss theme color namings ([79d7832](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/79d7832d0987b8a7c9650b984f72ffd702682423))
- update alignment ([7eb3113](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7eb3113adaa0917c0275576c3366c387bfe77440)) * update alignment ([7eb3113](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/7eb3113adaa0917c0275576c3366c387bfe77440))
- update daysOfWeek with new DAY_MAP keys ([ecdaadb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ecdaadb83eb21b144d07d573613fd8971f0a5ce2)) * update daysOfWeek with new DAY_MAP keys ([ecdaadb](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ecdaadb83eb21b144d07d573613fd8971f0a5ce2))
- visual overflow bug when editing schedule name ([#251](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/251)) ([e8d2c2e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e8d2c2e1429072fcc1173ee770f06b07e9ddda20)) * visual overflow bug when editing schedule name ([#251](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/251)) ([e8d2c2e](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/e8d2c2e1429072fcc1173ee770f06b07e9ddda20))
- vitest path alias bug ([ee37897](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ee37897df4f65cd2d285e2ad628be43aebb21ea1)) * vitest path alias bug ([ee37897](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/ee37897df4f65cd2d285e2ad628be43aebb21ea1))
### Reverts ### Reverts
- Revert "individual bug fix" ([b4e8c75](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4e8c7589e53f1064d70703459cc6d66fae1b04c)) * Revert "individual bug fix" ([b4e8c75](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/b4e8c7589e53f1064d70703459cc6d66fae1b04c))
- color palette for calendar ([#118](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/118)) ([51bbd65](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/51bbd6590bf284ce54644b014466c8b2c73b8925)) * color palette for calendar ([#118](https://github.com/Longhorn-Developers/UT-Registration-Plus/issues/118)) ([51bbd65](https://github.com/Longhorn-Developers/UT-Registration-Plus/commit/51bbd6590bf284ce54644b014466c8b2c73b8925))

32
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,32 @@
# UT Registration Plus Code of Conduct
Like the technical community as a whole, the UT Registration Plus team and community is made up of a mixture of professionals and volunteers from all over the world, working on every aspect of the mission - including mentorship, teaching, and connecting people.
Diversity is one of our huge strengths, but it can also lead to communication issues and unhappiness. To that end, we have a few ground rules that we ask people to adhere to. This code applies equally to founders, mentors and those seeking help and guidance.
This isnt an exhaustive list of things that you cant do. Rather, take it in the spirit in which its intended - a guide to make it easier to enrich all of us and the technical communities in which we participate.
This code of conduct applies to all spaces managed by the UT Registration Plus project or Longhorn Developers. This includes IRC, the mailing lists, the issue tracker, DSF events, and any other forums created by the project team which the community uses for communication. In addition, violations of this code outside these spaces may affect a person's ability to participate within them.
If you believe someone is violating the code of conduct, we ask that you report it by emailing [contact@longhorns.dev](mailto:contact@longhorns.dev). For more details please see our
- **Be friendly and patient.**
- **Be welcoming.** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
- **Be considerate.** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
- **Be respectful.** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. Its important to remember that a community where people feel uncomfortable or threatened is not a productive one. Members of the UT Registration Plus community should be respectful when dealing with other members as well as with people outside the UT Registration Plus community.
- **Be careful in the words that you choose.** We are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. This includes, but is not limited to:
- Violent threats or language directed against another person.
- Discriminatory jokes and language.
- Posting sexually explicit or violent material.
- Posting (or threatening to post) other people's personally identifying information ("doxing").
- Personal insults, especially those using racist or sexist terms.
- Unwelcome sexual attention.
- Advocating for, or encouraging, any of the above behavior.
- Repeated harassment of others. In general, if someone asks you to stop, then stop.
- **When we disagree, try to understand why.** Disagreements, both social and technical, happen all the time and UT Registration Plus is no exception. It is important that we resolve disagreements and differing views constructively. Remember that were different. The strength of UT Registration Plus comes from its varied community, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesnt mean that theyre wrong. Dont forget that it is human to err and blaming each other doesnt get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
Original text courtesy of the [Speak Up! project](http://web.archive.org/web/20141109123859/http://speakup.io/coc.html).
## Questions?
If you have questions, please see . If that doesn't answer your questions, feel free to [contact us](mailto:contact@longhorns.dev).

89
DOCKER_DEV_SETUP.md Normal file
View File

@@ -0,0 +1,89 @@
# Docker Dev Setup
## Prerequisites
- Docker installed on your machine
- Git (to clone the repository)
## Getting Started
1. Clone the repository:
```
git clone https://github.com/Longhorn-Developers/UT-Registration-Plus.git
cd UT-Registration-Plus
```
2. Build the Docker image:
```
docker build -t ut-registration-plus .
```
## Using Docker for Different Modes
The Docker setup supports three modes of operation: build, zip, and dev. You can choose the mode either by passing it as a command or by setting the `BUILD_MODE` environment variable.
### Build Mode (Default)
This mode builds the extension and places the output in the `dist` folder.
```
docker run -it --rm -v $(pwd)/dist:/extension/dist ut-registration-plus build
```
or
```
docker run -it --rm -v $(pwd)/dist:/extension/dist -e BUILD_MODE=build ut-registration-plus
```
### Zip Mode
This mode builds the extension and creates a zipped package in the `package` folder.
```
docker run -it --rm -v $(pwd)/package:/extension/package ut-registration-plus zip
```
or
```
docker run -it --rm -v $(pwd)/package:/extension/package -e BUILD_MODE=zip ut-registration-plus
```
### Development Mode with Hot Module Replacement (HMR)
This mode runs the extension in development mode with HMR support.
_Note_: This currently doesn't work.
```
docker run -it --rm -v $(pwd)/dist:/extension/dist -v $(pwd)/dist:/extension/dist -p 5173:5173 ut-registration-plus dev
```
or
```
docker run -it --rm -v $(pwd)/dist:/extension/dist -p 5173:5173 -e BUILD_MODE=dev ut-registration-plus
```
## Accessing the Built Extension
- For build mode, the extension files will be in the `dist` directory on your host machine.
- For zip mode, the zipped extension will be in the `package` directory on your host machine.
- For dev mode, the extension will be continuously built in the `dist` directory, and you can load it as an unpacked extension in Chrome.
## Docker Development Workflow
When working on the extension:
1. Run the container in dev mode
2. Make changes to your source code
3. The extension will automatically rebuild thanks to HMR
4. Reload the extension in Chrome to see your changes
## Troubleshooting
- If you encounter permission issues with the output directories, ensure that the directories exist on your host machine and have the correct permissions.
- For any other issues, please check the Docker logs or open an issue in the GitHub repository.

47
Dockerfile Normal file
View File

@@ -0,0 +1,47 @@
# Stage 1: Base image with Node.js and pnpm
FROM node:20.9.0-alpine AS base
# Install pnpm
RUN npm install -g pnpm@latest-10
# Set working directory
WORKDIR /app
# Copy package.json, pnpm-lock.yaml, and .nvmrc
COPY package.json pnpm-lock.yaml .nvmrc ./
# Copy patches directory if it exists
COPY patches ./patches
# Install dependencies, including applying patches
RUN pnpm install --frozen-lockfile
# Copy the rest of the source code
COPY . .
# Stage 2: Final stage
FROM base AS final
# Install zip utility and bash
RUN apk add --no-cache zip bash
# Set working directory
WORKDIR /extension
# Copy all files from base
COPY --from=base /app ./
# Copy the entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
# Make the entrypoint script executable
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Expose port for HMR
EXPOSE 5173
# Set the entrypoint to our new script
ENTRYPOINT ["docker-entrypoint.sh"]
# Set the default command (which can be overridden)
CMD ["build"]

View File

@@ -6,7 +6,7 @@
![Downloads](https://img.shields.io/chrome-web-store/d/hboadpjkoaieogjimneceaahlppnipaa) ![Downloads](https://img.shields.io/chrome-web-store/d/hboadpjkoaieogjimneceaahlppnipaa)
![License](https://img.shields.io/github/license/Longhorn-Developers/UT-Registration-Plus) ![License](https://img.shields.io/github/license/Longhorn-Developers/UT-Registration-Plus)
**UT Registration Plus (UTRP)** streamlines the process of registering for classes at UT Austin by reducing the chaos of juggling multiple tabs like Rate My Professor, Google Sheets, and the UT Course Schedule. With UTRP, you can simplify class selection and schedule management. We've all been there. 20 tabs of Rate My Professor, Google Spreadsheet, and the UT Course Schedule open and you still don't know what classes to take. UT Registration Plus (UTRP), tries to streamline most of the unnecessary steps and headaches of registering for classes at UT Austin. **UT Registration Plus (UTRP)** streamlines the process of registering for classes at UT Austin by reducing the chaos of juggling multiple tabs like Rate My Professor, Google Sheets, and the UT Course Schedule. With UTRP, you can simplify class selection and schedule management. We've all been there. 20 tabs of Rate My Professor, Google Spreadsheet, and the UT Course Schedule open and you still don't know what classes to take.
## Demo ## Demo
@@ -26,8 +26,9 @@
## Toolchain ## Toolchain
- React v20.9.0 (LTS) - React v20.9.0 (LTS)
- TypeScript - TypeScript v5.x
- Vite 5 - Vite v5.x
- pnpm v10.x
- UnoCSS - UnoCSS
- ESLint - ESLint
- Prettier - Prettier
@@ -58,7 +59,9 @@ Contributions are welcome and encouraged! To get started:
For significant changes, its recommended to open an issue first to discuss the proposed updates. For significant changes, its recommended to open an issue first to discuss the proposed updates.
## Development: Getting Started ## Development
### Getting Started
1. Clone this repository: `git clone https://github.com/Longhorn-Developers/UT-Registration-Plus.git` 1. Clone this repository: `git clone https://github.com/Longhorn-Developers/UT-Registration-Plus.git`
2. **Node Version**: This project requires the Node.js version specified in `.nvmrc`. Use [nvm](https://github.com/nvm-sh/nvm) to install and manage the correct version: 2. **Node Version**: This project requires the Node.js version specified in `.nvmrc`. Use [nvm](https://github.com/nvm-sh/nvm) to install and manage the correct version:
@@ -101,6 +104,10 @@ Use `BETA=true pnpm build` to generate a beta build.
</details> </details>
### Docker
This project includes a Dockerfile that allows you to build, zip, or run the extension in development mode using Docker. Refer to [Docker Dev Setup](./DOCKER_DEV_SETUP.md) to get started.
## Loading the Extension Manually ## Loading the Extension Manually
To load the extension manually in Chrome: To load the extension manually in Chrome:
@@ -178,8 +185,9 @@ We maintain a strict code of conduct. By contributing, you agree to adhere to th
Special thanks to the developers and contributors behind these amazing tools and libraries: Special thanks to the developers and contributors behind these amazing tools and libraries:
- React v20.9.0 (LTS) - React v20.9.0 (LTS)
- TypeScript - TypeScript v5.x
- Vite 5 - Vite v5.x
- pnpm v10.x
- UnoCSS - UnoCSS
- ESLint - ESLint
- Prettier - Prettier
@@ -194,3 +202,17 @@ Special thanks to the developers and contributors behind these amazing tools and
- [Figma Designs](https://www.figma.com/design/8tsCay2FRqctrdcZ3r9Ahw/UTRP) by @IsaDavRod - [Figma Designs](https://www.figma.com/design/8tsCay2FRqctrdcZ3r9Ahw/UTRP) by @IsaDavRod
- [Longhorn Developers](https://github.com/Longhorn-Developers) - established in 2024 - [Longhorn Developers](https://github.com/Longhorn-Developers) - established in 2024
- The UTRP devs, beta testers, and all supporters of the project! - The UTRP devs, beta testers, and all supporters of the project!
## Activity
![UT-Registration-Plus Activity](https://repobeats.axiom.co/api/embed/47930fa3916ac1b475cd63a05948c449eb5ad502.svg "UT-Registration-Plus Repobeats analytics image")
## Star History
<a href="https://star-history.com/#Longhorn-Developers/UT-Registration-Plus&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Longhorn-Developers/UT-Registration-Plus&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Longhorn-Developers/UT-Registration-Plus&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Longhorn-Developers/UT-Registration-Plus&type=Date" />
</picture>
</a>

46
docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail
# Define supported modes
SUPPORTED_MODES=("build" "zip" "dev")
# Function to display usage information
usage() {
echo "Usage: $0 [build|zip|dev]"
echo " build: Build the extension"
echo " zip: Build and zip the extension"
echo " dev: Run in development mode with HMR"
exit 1
}
# Check if BUILD_MODE is set, otherwise use the first argument
if [ -n "${BUILD_MODE:-}" ]; then
mode="$BUILD_MODE"
elif [ $# -eq 1 ]; then
mode="$1"
else
usage
fi
# Validate the mode
if [[ ! " ${SUPPORTED_MODES[*]} " =~ " ${mode} " ]]; then
echo "Error: Invalid mode '${mode}'" >&2
usage
fi
# Execute the appropriate command based on the mode
case "$mode" in
build)
echo "Building extension..."
exec pnpm run build
;;
zip)
echo "Building and zipping extension..."
exec pnpm run zip
;;
dev)
echo "Running in development mode with HMR..."
exec pnpm run dev
;;
esac

257
eslint.config.mjs Normal file
View File

@@ -0,0 +1,257 @@
import { fixupConfigRules } from '@eslint/compat';
import { FlatCompat } from '@eslint/eslintrc';
import js from '@eslint/js';
import importEssentials from 'eslint-plugin-import-essentials';
import jsdoc from 'eslint-plugin-jsdoc';
import reactPreferFunction from 'eslint-plugin-react-prefer-function-component';
import simpleImportSort from 'eslint-plugin-simple-import-sort';
import tsdoc from 'eslint-plugin-tsdoc';
import unocss from '@unocss/eslint-config/flat';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
export default [
{
ignores: ['*.html', 'tsconfig.json', 'dist/**', 'build/**', 'node_modules/**'],
},
js.configs.recommended,
...fixupConfigRules(
compat.extends(
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:storybook/recommended',
'airbnb-base',
'airbnb/rules/react',
'airbnb-typescript',
'prettier'
)
),
unocss,
{
plugins: {
'import-essentials': importEssentials,
jsdoc,
tsdoc,
'react-prefer-function-component': reactPreferFunction,
'simple-import-sort': simpleImportSort,
},
languageOptions: {
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
debugger: true,
browser: true,
context: true,
JSX: true,
},
ecmaVersion: 2022,
sourceType: 'module',
parserOptions: {
project: './tsconfig.json',
ecmaFeatures: {
jsx: true,
modules: true,
experimentalObjectRestSpread: true,
},
},
},
settings: {
react: {
version: 'detect',
},
jsdoc: {
mode: 'typescript',
},
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
},
},
rules: {
// Disable rules removed in @typescript-eslint v8
'@typescript-eslint/no-throw-literal': 'off',
'prefer-const': [
'off',
{
destructuring: 'any',
ignoreReadBeforeAssign: false,
},
],
'no-plusplus': 'off',
'no-inner-declarations': 'off',
'sort-imports': 'off',
'no-case-declarations': 'off',
'no-unreachable': 'warn',
'no-constant-condition': 'error',
'space-before-function-paren': 'off',
'no-undef': 'off',
'no-return-await': 'off',
'@typescript-eslint/return-await': 'off',
'@typescript-eslint/no-shadow': ['off'],
'@typescript-eslint/no-use-before-define': ['off'],
'class-methods-use-this': 'off',
'react-hooks/exhaustive-deps': 'warn',
'@typescript-eslint/lines-between-class-members': 'off',
'no-param-reassign': [
'error',
{
props: false,
},
],
'no-console': 'off',
'consistent-return': 'off',
'react/destructuring-assignment': 'off',
'import/prefer-default-export': 'off',
'no-promise-executor-return': 'off',
'import/no-cycle': 'off',
'import/no-extraneous-dependencies': 'off',
'react/jsx-props-no-spreading': 'off',
'react/jsx-no-useless-fragment': [
'error',
{
allowExpressions: true,
},
],
'keyword-spacing': [
'error',
{
before: true,
after: true,
},
],
'no-continue': 'off',
'space-before-blocks': [
'error',
{
functions: 'always',
keywords: 'always',
classes: 'always',
},
],
'react/jsx-filename-extension': [
1,
{
extensions: ['.tsx'],
},
],
'react/no-deprecated': 'warn',
'react/prop-types': 'off',
'react-prefer-function-component/react-prefer-function-component': [
'warn',
{
allowComponentDidCatch: false,
},
],
'react/function-component-definition': 'off',
'react/button-has-type': 'off',
'jsdoc/require-param-type': 'off',
'jsdoc/require-returns-type': 'off',
'jsdoc/newline-after-description': 'off',
'react/require-default-props': 'off',
'jsdoc/require-jsdoc': [
'error',
{
enableFixer: false,
publicOnly: true,
checkConstructors: false,
require: {
ArrowFunctionExpression: true,
ClassDeclaration: true,
ClassExpression: true,
FunctionExpression: true,
},
contexts: [
'MethodDefinition:not([key.name="componentDidMount"]):not([key.name="render"])',
'ArrowFunctionExpression',
'ClassDeclaration',
'ClassExpression',
'ClassProperty:not([key.name="state"]):not([key.name="componentDidMount"])',
'FunctionDeclaration',
'FunctionExpression',
'TSDeclareFunction',
'TSEnumDeclaration',
'TSInterfaceDeclaration',
'TSMethodSignature',
'TSModuleDeclaration',
'TSTypeAliasDeclaration',
],
},
],
'tsdoc/syntax': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/naming-convention': 'off',
'@typescript-eslint/space-before-function-paren': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-interface': 'warn',
'import/no-restricted-paths': [
'error',
{
zones: [
{
target: './src/background',
from: './src/views',
message:
'You cannot import into the `background` directory from the `views` directory (i.e. content script files) because it will break the build!',
},
{
target: './src/views',
from: './src/background',
message:
'You cannot import into the `views` directory from the `background` directory (i.e. background script files) because it will break the build!',
},
{
target: './src/shared',
from: './',
except: ['./src/shared', './node_modules'],
message: 'You cannot import into `shared` from an external directory.',
},
],
},
],
'import/extensions': 'off',
'no-restricted-syntax': [
'error',
'ForInStatement',
'LabeledStatement',
'WithStatement',
{
selector: 'TSEnumDeclaration',
message: "Don't declare enums",
},
],
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'import-essentials/restrict-import-depth': 'error',
'import-essentials/check-path-alias': 'error',
},
},
];

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1759831965,
"narHash": "sha256-vgPm2xjOmKdZ0xKA6yLXPJpjOtQPHfaZDRtH+47XEBo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c9b6fb798541223bbb396d287d16f43520250518",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

43
flake.nix Normal file
View File

@@ -0,0 +1,43 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = (import nixpkgs { inherit system; });
commonPackages = with pkgs; [
nodejs_20 # v20.19.5
pnpm_10 # v10.18.0
];
additionalPackages = with pkgs; [
bun
nodePackages.conventional-changelog-cli
sentry-cli
];
in
{
formatter = pkgs.nixfmt-rfc-style;
devShells.default = pkgs.mkShell {
name = "utrp-dev";
buildInputs = commonPackages;
};
devShells.full = pkgs.mkShell {
name = "utrp-dev-full";
buildInputs = commonPackages ++ additionalPackages;
};
}
);
}

View File

@@ -1,48 +1,97 @@
// (Thanks go to https://github.com/pnd280/complexity/blob/alpha/gulpfile.js) import chalk from 'chalk';
import cp from 'child_process';
import fs from 'fs'; import fs from 'fs';
import gulp from 'gulp'; import { dest, series, src } from 'gulp';
import gulpZip from 'gulp-zip'; import { exec } from 'gulp-execa';
import { createRequire } from 'module'; import zip from 'gulp-zip';
import path from 'path';
const DIST_DIR = 'dist';
const PACKAGE_DIR = 'package';
const DATABASE_DIR = path.join(DIST_DIR, 'database');
// Custom log functions
const log = message => console.log(chalk.blue(`[${new Date().toTimeString().split(' ')[0]}]`), chalk.white(message));
const logWarn = message =>
console.warn(
chalk.blue(`[${new Date().toTimeString().split(' ')[0]}]`),
chalk.yellow(' [WARN]'),
chalk.white(message)
);
const logError = message =>
console.error(
chalk.blue(`[${new Date().toTimeString().split(' ')[0]}]`),
chalk.red(' [ERROR]'),
chalk.white(message)
);
// Remove extra database folder
function removeExtraDatabaseDir(cb) {
fs.rmSync(DATABASE_DIR, { recursive: true, force: true });
log('Extra database directory removed.');
cb();
}
// Instrument with Sentry
// Make sure sentry is configured https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/typescript/#2-configure-sentry-cli // Make sure sentry is configured https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/typescript/#2-configure-sentry-cli
function instrumentWithSentry() { async function instrumentWithSentry(cb) {
return cp.exec('sentry-cli sourcemaps inject dist/ && sentry-cli sourcemaps upload dist/'); if (process.env.SENTRY_ENV && process.env.SENTRY_ENV !== 'development') {
await exec(`sentry-cli sourcemaps inject ${DIST_DIR}`);
await exec(`sentry-cli sourcemaps upload ${DIST_DIR}`);
log('Sentry instrumentation completed.');
} else {
logWarn('Skipping uploading/creating Sentry source maps. (development build)');
}
cb();
} }
// Zip the dist folder
function zipDist() { function zipDist() {
const require = createRequire(import.meta.url); const packageInfo = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
const manifest = require('./package.json'); const zipFileName = `${packageInfo.name.replace(/ /g, '-')}-${packageInfo.version}.zip`;
const zipFileName = `${manifest.name.replaceAll(' ', '-')}-${manifest.version}.zip`;
return gulp return src(`${DIST_DIR}/**`, {
.src('dist/**', { base: DIST_DIR,
encoding: false, encoding: false, // Disable encoding to handle binary files correctly
}) })
.pipe(gulpZip(zipFileName)) .pipe(zip(zipFileName))
.pipe(gulp.dest('package')); .pipe(dest(PACKAGE_DIR))
.on('end', () => log(`Zip file created: ${path.join(PACKAGE_DIR, zipFileName)}`));
} }
const zip = gulp.series(instrumentWithSentry, zipDist);
// Temp fix for CSP on Chrome 130 // Temp fix for CSP on Chrome 130
// Manually remove them because there is no option to disable use_dynamic_url on @crxjs/vite-plugin // Manually remove them because there is no option to disable use_dynamic_url on @crxjs/vite-plugin
function forceDisableUseDynamicUrl(done) { // Force disable use_dynamic_url in manifest.json
const require = createRequire(import.meta.url); function forceDisableUseDynamicUrl(cb) {
const manifest = require('./dist/manifest.json'); const manifestPath = path.join(DIST_DIR, 'manifest.json');
manifest.web_accessible_resources.forEach(resource => { if (!fs.existsSync(manifestPath)) {
delete resource.use_dynamic_url; logWarn('manifest.json not found. Skipping modification.');
}); return cb();
if (!fs.existsSync('./dist/manifest.json')) {
return done();
} }
fs.writeFileSync('./dist/manifest.json', JSON.stringify(manifest, null, 2)); const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
let modified = false;
done(); manifest.web_accessible_resources.forEach(resource => {
if (resource.use_dynamic_url) {
delete resource.use_dynamic_url;
modified = true;
}
});
if (modified) {
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
log('use_dynamic_url removed from manifest.json');
} else {
log('No use_dynamic_url found in manifest.json. No changes made.');
}
cb();
} }
export { forceDisableUseDynamicUrl, zip }; // Main build task
const zipProdBuild = series(removeExtraDatabaseDir, instrumentWithSentry, zipDist);
export { forceDisableUseDynamicUrl, zipProdBuild };

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.2", "version": "2.2.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",
@@ -9,12 +9,14 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"build:dev": "tsc && NODE_ENV='development' vite build --mode development",
"build:watch": "NODE_ENV='development' vite build --mode development -w", "build:watch": "NODE_ENV='development' vite build --mode development -w",
"zip": "PROD=true pnpm build && pnpm gulp zip", "zip": "pnpm build && pnpm gulp zipProdBuild",
"zip:to-publish": "SENTRY_ENV='production' pnpm 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 --report-unused-disable-directives",
"lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --fix", "lint:fix": "eslint src --report-unused-disable-directives --fix",
"check-types": "tsc --noEmit", "check-types": "tsc --noEmit",
"test": "vitest", "test": "vitest",
"test:ui": "vitest --ui", "test:ui": "vitest --ui",
@@ -27,113 +29,131 @@
"prepare": "husky" "prepare": "husky"
}, },
"dependencies": { "dependencies": {
"@headlessui/react": "^2.1.10", "@date-fns/tz": "^1.2.0",
"@hello-pangea/dnd": "^17.0.0", "@dnd-kit/core": "^6.3.1",
"@octokit/rest": "^21.0.2", "@dnd-kit/modifiers": "^9.0.0",
"@sentry/react": "^8.34.0", "@dnd-kit/sortable": "^10.0.0",
"@unocss/vite": "^0.63.4", "@dnd-kit/utilities": "^3.2.2",
"@vitejs/plugin-react": "^4.3.2", "@headlessui/react": "^2.2.0",
"@octokit/rest": "^21.1.1",
"@phosphor-icons/react": "^2.1.7",
"@sentry/react": "^8.55.0",
"@tanstack/react-query": "^5.69.0",
"@unocss/vite": "^0.63.6",
"@vitejs/plugin-react": "^4.3.4",
"chrome-extension-toolkit": "^0.0.54", "chrome-extension-toolkit": "^0.0.54",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"conventional-changelog": "^6.0.0", "conventional-changelog": "^6.0.0",
"date-fns": "^4.1.0",
"highcharts": "^11.4.8", "highcharts": "^11.4.8",
"highcharts-react-official": "^3.2.1", "highcharts-react-official": "^3.2.1",
"html-to-image": "^1.11.11", "html-to-image": "^1.11.13",
"husky": "^9.1.6", "husky": "^9.1.7",
"kc-dabr-wasm": "^0.1.2", "kc-dabr-wasm": "^0.1.2",
"nanoid": "^5.0.7", "nanoid": "^5.1.2",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-loading-skeleton": "^3.5.0", "react-loading-skeleton": "^3.5.0",
"react-markdown": "^9.0.1", "react-markdown": "^9.1.0",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.6.1",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.1",
"sass": "^1.79.5", "sass": "^1.85.1",
"simple-git": "^3.27.0", "simple-git": "^3.27.0",
"sql.js": "1.11.0" "sql.js": "1.11.0"
}, },
"devDependencies": { "devDependencies": {
"@chromatic-com/storybook": "^2.0.2", "@chromatic-com/storybook": "^2.0.2",
"@commitlint/cli": "^19.5.0", "@commitlint/cli": "^19.7.1",
"@commitlint/config-conventional": "^19.5.0", "@commitlint/config-conventional": "^19.7.1",
"@commitlint/types": "^19.5.0", "@commitlint/types": "^19.5.0",
"@crxjs/vite-plugin": "2.0.0-beta.21", "@crxjs/vite-plugin": "2.0.0-beta.21",
"@iconify-json/bi": "^1.2.1", "@eslint/compat": "^2.0.0",
"@iconify-json/iconoir": "^1.2.2", "@eslint/eslintrc": "^3.3.1",
"@iconify-json/material-symbols": "^1.2.4", "@eslint/js": "^9.39.1",
"@iconify-json/ri": "^1.2.1", "@iconify-json/bi": "^1.2.2",
"@iconify-json/ic": "^1.2.2",
"@iconify-json/iconoir": "^1.2.7",
"@iconify-json/material-symbols": "^1.2.14",
"@iconify-json/ri": "^1.2.5",
"@iconify-json/streamline": "^1.2.2",
"@semantic-release/exec": "^6.0.3", "@semantic-release/exec": "^6.0.3",
"@sentry/types": "^8.34.0", "@sentry/types": "^8.55.0",
"@storybook/addon-designs": "^8.0.3", "@storybook/addon-designs": "^8.2.0",
"@storybook/addon-essentials": "^8.3.5", "@storybook/addon-essentials": "^8.6.0",
"@storybook/addon-links": "^8.3.5", "@storybook/addon-links": "^8.6.0",
"@storybook/blocks": "^8.3.5", "@storybook/blocks": "^8.6.0",
"@storybook/react": "^8.3.5", "@storybook/react": "^8.6.0",
"@storybook/react-vite": "^8.3.5", "@storybook/react-vite": "^8.6.0",
"@storybook/test": "^8.3.5", "@storybook/test": "^8.6.0",
"@svgr/core": "^8.1.0", "@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0",
"@types/chrome": "^0.0.273", "@types/chrome": "^0.0.273",
"@types/conventional-changelog": "^3.1.5", "@types/conventional-changelog": "^3.1.5",
"@types/gulp": "^4.0.17", "@types/gulp": "^4.0.17",
"@types/gulp-zip": "^4.0.4", "@types/gulp-zip": "^4.0.4",
"@types/node": "^22.7.5", "@types/node": "^22.13.5",
"@types/prompts": "^2.4.9", "@types/prompts": "^2.4.9",
"@types/react": "^18.3.11", "@types/react": "^18.3.18",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.5",
"@types/react-syntax-highlighter": "^15.5.13", "@types/react-syntax-highlighter": "^15.5.13",
"@types/semantic-release": "^20.0.6", "@types/semantic-release": "^20.0.6",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@types/sql.js": "^1.4.9", "@types/sql.js": "^1.4.9",
"@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/eslint-plugin": "^8.47.0",
"@typescript-eslint/parser": "^7.18.0", "@typescript-eslint/parser": "^8.47.0",
"@unocss/eslint-config": "^0.63.4", "@unocss/eslint-config": "^0.63.6",
"@unocss/postcss": "^0.63.4", "@unocss/postcss": "^0.63.6",
"@unocss/preset-uno": "^0.63.4", "@unocss/preset-uno": "^0.63.6",
"@unocss/preset-web-fonts": "^0.63.4", "@unocss/preset-web-fonts": "^0.63.6",
"@unocss/reset": "^0.63.4", "@unocss/reset": "^0.63.6",
"@unocss/transformer-directives": "^0.63.4", "@unocss/transformer-directives": "^0.63.6",
"@unocss/transformer-variant-group": "^0.63.4", "@unocss/transformer-variant-group": "^0.63.6",
"@vitejs/plugin-react-swc": "^3.7.1", "@vitejs/plugin-react-swc": "^3.8.0",
"@vitest/coverage-v8": "^2.1.2", "@vitest/coverage-v8": "^2.1.9",
"@vitest/ui": "^2.1.2", "@vitest/ui": "^2.1.9",
"chalk": "^5.3.0", "chalk": "^5.4.1",
"chromatic": "^11.12.5", "chromatic": "^11.26.0",
"cssnano": "^7.0.6", "cssnano": "^7.0.6",
"cssnano-preset-advanced": "^7.0.6", "cssnano-preset-advanced": "^7.0.6",
"dotenv": "^16.4.5", "dotenv": "^16.4.7",
"es-module-lexer": "^1.5.4", "es-module-lexer": "^1.6.0",
"eslint": "^8.57.1", "eslint": "^9.39.1",
"eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^18.0.0", "eslint-config-airbnb-typescript": "^18.0.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^10.1.8",
"eslint-import-resolver-typescript": "^3.6.3", "eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-import-essentials": "^0.2.1", "eslint-plugin-import-essentials": "^0.2.1",
"eslint-plugin-jsdoc": "^50.3.2", "eslint-plugin-jsdoc": "^61.2.1",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.1", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-prefer-function-component": "^3.3.0", "eslint-plugin-react-prefer-function-component": "^3.4.0",
"eslint-plugin-react-refresh": "^0.4.12", "eslint-plugin-react-refresh": "^0.4.19",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-storybook": "^0.9.0", "eslint-plugin-storybook": "^10.0.8",
"eslint-plugin-tsdoc": "^0.3.0",
"gulp": "^5.0.0", "gulp": "^5.0.0",
"gulp-zip": "^6.0.0", "gulp-execa": "^7.0.1",
"gulp-zip": "^6.1.0",
"path": "^0.12.7", "path": "^0.12.7",
"postcss": "^8.4.47", "postcss": "^8.5.3",
"prettier": "^3.3.3", "prettier": "^3.6.2",
"react-dev-utils": "^12.0.1", "react-dev-utils": "^12.0.1",
"semantic-release": "^24.1.2", "semantic-release": "^24.2.3",
"storybook": "^8.3.5", "storybook": "^8.6.0",
"typescript": "^5.6.3", "typescript": "^5.7.3",
"unocss": "^0.63.4", "unocss": "^0.63.6",
"unocss-preset-primitives": "0.0.2-beta.1", "unocss-preset-primitives": "0.0.2-beta.1",
"unplugin-icons": "^0.19.3", "unplugin-icons": "^0.19.3",
"vite": "^5.4.8", "vite": "^5.4.20",
"vite-plugin-inspect": "^0.8.7", "vite-plugin-inspect": "^0.8.9",
"vitest": "^2.1.2" "vitest": "^2.1.9"
},
"engineStrict": true,
"engines": {
"pnpm": "^10"
}, },
"pnpm": { "pnpm": {
"patchedDependencies": { "patchedDependencies": {
@@ -143,5 +163,9 @@
"overrides": { "overrides": {
"es-module-lexer": "^1.5.4" "es-module-lexer": "^1.5.4"
} }
},
"volta": {
"node": "20.19.4",
"pnpm": "10.14.0"
} }
} }

9007
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -27,8 +27,8 @@ interface Props {
/** /**
* Generates a changelog using the conventional-changelog command. * Generates a changelog using the conventional-changelog command.
* *
* @returns {Promise<void>} A promise that resolves when the changelog is generated. * @returns A promise that resolves when the changelog is generated.
* @throws {Error} If there is an error generating the changelog. * @throws If there is an error generating the changelog.
*/ */
async function generateChangelog({ preset, outFile = 'CHANGELOG.md', releaseCount = 1 }: Props): Promise<void> { async function generateChangelog({ preset, outFile = 'CHANGELOG.md', releaseCount = 1 }: Props): Promise<void> {
try { try {

BIN
src/assets/LD-icon-new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

5075
src/assets/UT-Map.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 347 KiB

View File

@@ -8,15 +8,27 @@ const splashText: string[] = [
'The squirrels, they have mastered begging for food better than students. Impressive... but worrying.', 'The squirrels, they have mastered begging for food better than students. Impressive... but worrying.',
"Do you study often? Ha! What am I saying? Of course you don't.", "Do you study often? Ha! What am I saying? Of course you don't.",
"Hey, you, you're finally awake. You were trying to skip class right?", "Hey, you, you're finally awake. You were trying to skip class right?",
'Mmm... Brutalist architecture...',
'The course syllabus: more than meets the eye',
'Pain is temporary, GPA is forever.',
"You've Yee'd Your Last Haw.",
'lol everything is already waitlisted.',
'Could be worse. Could be A&M.',
"Should you major in Compsci? well, here's a better question. do you wanna have a bad time?",
'A pen and paper is old fashioned, but sometimes old ways are best',
'A heart is like bedrock, destroyable only by cheating',
'You may not rest now, there are Canvas assignments nearby',
'You are filled with DETERMINATION',
'60k+ users!',
'Almost Turing complete',
'#BF5700',
'The waitlist is a lie!',
'BEVO JOCKEY 🗣️🗣️🗣️',
'RIP Domino, you beloved campus feline.', 'RIP Domino, you beloved campus feline.',
"The year is 2055 and Welch still isn't finished.", "The year is 2055 and Welch still isn't finished.",
'Motivation dropping faster than ur GPA', 'Motivation dropping faster than ur GPA',
'No Work Happens On PCL 5th Floor.', 'No Work Happens On PCL 5th Floor.',
'I may be a sophomore in name, but my credit count screams freshman!', 'I may be a sophomore in name, but my credit count screams freshman!',
'Pain is temporary, GPA is forever.',
"You've Yee'd Your Last Haw.",
'lol everything is already waitlisted.',
'Could be worse. Could be A&M.',
// 'TeXAs iS BaCK GuYZ', // 'TeXAs iS BaCK GuYZ',
'mAke iT yOuR tExAS', 'mAke iT yOuR tExAS',
'change yOur slogan', 'change yOur slogan',
@@ -24,7 +36,7 @@ const splashText: string[] = [
'Does McCombs teach Parseltongue?', 'Does McCombs teach Parseltongue?',
'No Cruce Enfrente Del Bus.', 'No Cruce Enfrente Del Bus.',
'Omae Wa Mou Shindeiru...', 'Omae Wa Mou Shindeiru...',
'Every day another brick disappears from Speedway', "They say each day, another brick disappears from Speedway. No one's sure where to.",
'The GDC will annex the EER one day', 'The GDC will annex the EER one day',
'To hike to Kins or not to hike to Kins...', 'To hike to Kins or not to hike to Kins...',
"C'mon you Longhorns! You want to study forever?", "C'mon you Longhorns! You want to study forever?",
@@ -34,15 +46,11 @@ const splashText: string[] = [
"The Block of Butter incident of '22", "The Block of Butter incident of '22",
'Begun, the midterms have.', 'Begun, the midterms have.',
'You must construct additional schedules', 'You must construct additional schedules',
"Arrows of Christ vs Church of Scientology was the crossover we didn't know we needed",
'THE WALK SIGN IS ON TO CROSS GUADALUPE AND 21ST', 'THE WALK SIGN IS ON TO CROSS GUADALUPE AND 21ST',
'Pay attention. Might learn something.', 'Pay attention. Might learn something.',
'Long ago, apartment rates lived together in harmony. Then, everything changed when American Campus Communities Inc attacked.',
'Roll for Initiative!', 'Roll for Initiative!',
'The line at the on-campus Starbucks is longer than your course waitlist.', 'The line at the on-campus Starbucks is longer than your course waitlist.',
'The weather changes more often than your class schedule.', 'The weather changes more often than your class schedule.',
'Mmm... Brutalist architecture...',
'The course syllabus: more than meets the eye',
"'studying' often means refreshing Canvas every five minutes to see if the professor posted lecture slides.", "'studying' often means refreshing Canvas every five minutes to see if the professor posted lecture slides.",
"It's over Bevo! I have the high ground!", "It's over Bevo! I have the high ground!",
"I'll just skip this lecture and watch the recording later. What's the worst that could happen?", "I'll just skip this lecture and watch the recording later. What's the worst that could happen?",
@@ -59,23 +67,15 @@ const splashText: string[] = [
'follow @sghsri!', 'follow @sghsri!',
'Officially part of the SEC', 'Officially part of the SEC',
'Planner is now acquired by Plus', 'Planner is now acquired by Plus',
'Longhorn-Developers is the best UT Student Org', 'Longhorn Developers is the best UT Student Org',
'The Eiffel Tower is the UT Tower of Paris', 'The Eiffel Tower is the UT Tower of Paris',
'A pen and paper is old fashioned, but sometimes old ways are best', "He's a CS Major, but he showers regularly. 🧢",
'A heart is like bedrock, destroyable only by cheating',
'You may not rest now, there are Canvas assignments nearby',
'You are filled with DETERMINATION',
'60k+ users!',
'Almost Turing complete',
'#BF5700',
'The waitlist is a lie!',
`He's a CS Major, but he showers regularly. 🧢`,
'A CS major walks into a bar. The bar is empty because it is a CS major.', 'A CS major walks into a bar. The bar is empty because it is a CS major.',
'UT Registration Plus - The only thing that can make registration worse is not having it', 'UT Registration Plus - The only thing that can make registration worse is not having it',
'UT Registration Plus - We make registration slightly less painful. Slightly.', 'UT Registration Plus - We make registration slightly less painful. Slightly.',
'UT Registration Plus - Do you really want to figure out which professors will ruin your GPA by yourself?', 'UT Registration Plus - Do you really want to figure out which professors will ruin your GPA by yourself?',
'Ayo tf is a memory leak', "Ayo what's is a memory leak",
"lowkey we never thought we'd get this far, how tf are 60k of you people using this", "lowkey we never thought we'd get this far, how are 60k of you people using this",
"dang we're really out here making a splash", "dang we're really out here making a splash",
"We'd make a joke about A&M, but we're not sure they can read", "We'd make a joke about A&M, but we're not sure they can read",
"We've only caused one or two outages, we swear!", "We've only caused one or two outages, we swear!",
@@ -113,8 +113,7 @@ const splashText: string[] = [
"Stop trying to make UTRP happen, it's not going to happen!", "Stop trying to make UTRP happen, it's not going to happen!",
'Befriend the raccoons on campus', 'Befriend the raccoons on campus',
`It's ${new Date().toLocaleString('en-US', { month: 'long', day: 'numeric' })} and OU still sucks`, `It's ${new Date().toLocaleString('en-US', { month: 'long', day: 'numeric' })} and OU still sucks`,
'As seen on TV! ', 'As seen on TV!',
"Should you major in Compsci? well, here's a better question. do you wanna have a bad time?",
]; ];
export default splashText; export default splashText;

View File

@@ -8,8 +8,8 @@ const manifest = chrome.runtime.getManifest();
/** /**
* Handles editing the storage for a specific area. * Handles editing the storage for a specific area.
* *
* @param {string} areaName - The name of the storage area. * @param areaName - The name of the storage area.
* @returns {Function} - A function that accepts changes and sets them in the storage. * @returns A function that accepts changes and sets them in the storage.
*/ */
const handleEditStorage = (areaName: 'local' | 'sync' | 'session') => (changes: Record<string, unknown>) => { const handleEditStorage = (areaName: 'local' | 'sync' | 'session') => (changes: Record<string, unknown>) => {
chrome.storage[areaName].set(changes); chrome.storage[areaName].set(changes);

View File

@@ -18,11 +18,13 @@ const nameSuffix = isBeta ? ' (beta)' : mode === 'development' ? ' (dev)' : '';
const HOST_PERMISSIONS: string[] = [ const HOST_PERMISSIONS: string[] = [
'*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*', '*://*.utdirect.utexas.edu/apps/registrar/course_schedule/*',
'*://*.utdirect.utexas.edu/registration/classlist/*',
'*://*.utexas.collegescheduler.com/*', '*://*.utexas.collegescheduler.com/*',
'*://*.catalog.utexas.edu/ribbit/', '*://*.catalog.utexas.edu/ribbit/',
'*://*.registrar.utexas.edu/schedules/*', '*://*.registrar.utexas.edu/schedules/*',
'*://*.login.utexas.edu/login/*', '*://*.login.utexas.edu/login/*',
'https://utexas.bluera.com/*', 'https://utexas.bluera.com/*',
'*://my.utexas.edu/student/student/*',
]; ];
const manifest = defineManifest(async () => ({ const manifest = defineManifest(async () => ({

486
src/pages/404/Page404.tsx Normal file
View File

@@ -0,0 +1,486 @@
// @ts-nocheck
/* eslint-disable */
import React, { useEffect, useRef } from 'react';
/**
* Page404 component
*
* @returns The Page404 component
*/
export default function Page404(): JSX.Element {
const canvasRef = useRef(null);
const ASPECT_RATIO = 848 / 480;
useEffect(() => {
// Credit: Notch
function _0x58e5() {
let _0x33cb15 = [
'putImageData',
'sin',
'getContext',
'random',
'sqrt',
'3498792PzxeQW',
'2JZNlgj',
'1199520qBJBWq',
'8OebCGy',
'156395VBdeaj',
'79193hyrbkc',
'10iqsPJh',
'now',
'data',
'80uLyuyX',
'cos',
'1843331yKjgfa',
'createImageData',
'2266182vPDGCM',
'current',
'49758oxlYFR',
];
_0x58e5 = function () {
return _0x33cb15;
};
return _0x58e5();
}
let _0xdd3699 = _0x9cb4;
(function (_0x5cc1ea, _0x44f991) {
let _0x3b1280 = _0x9cb4;
let _0x500290 = _0x5cc1ea();
while ([]) {
try {
let _0xea56e3 =
(-parseInt(_0x3b1280(0x8b)) / (0x2344 + 0x63 * 0x59 + -0x45ae)) *
(parseInt(_0x3b1280(0x87)) / (0x2ec * -0x3 + -0x9 * 0x3ed + 0x2c1b)) +
(parseInt(_0x3b1280(0x95)) / (0x4 * -0x8b7 + -0x16 * -0x109 + 0x1 * 0xc19)) *
(-parseInt(_0x3b1280(0x8f)) / (0x17 * -0x8c + 0x1 * 0x1f0f + 0x1d * -0xa3)) +
-parseInt(_0x3b1280(0x8a)) / (-0x113 * 0x21 + -0x1 * 0xc20 + 0x2f98) +
-parseInt(_0x3b1280(0x88)) / (-0x125c + 0xd * 0x12d + 0x1 * 0x319) +
parseInt(_0x3b1280(0x91)) / (0x389 * -0x2 + 0x1 * 0xb9c + -0x483) +
(-parseInt(_0x3b1280(0x89)) / (0x4e7 + -0xcf * -0x1 + -0x2d7 * 0x2)) *
(-parseInt(_0x3b1280(0x93)) / (-0x1859 + 0x1366 + 0x4fc)) +
(parseInt(_0x3b1280(0x8c)) / (-0x1 * -0x22b2 + -0x438 * -0x5 + -0x1be * 0x20)) *
(parseInt(_0x3b1280(0x86)) / (0x29 * 0x29 + 0x1d09 * 0x1 + -0x238f));
if (_0xea56e3 === _0x44f991) break;
else _0x500290.push(_0x500290.shift());
} catch (_0x5847bb) {
_0x500290.push(_0x500290.shift());
}
}
})(_0x58e5, 0x3c063 + 0x13c * -0x305 + 0x2e43c);
const _0x5dce41 = canvasRef[_0xdd3699(0x94)];
const _0x16f39e = _0x5dce41[_0xdd3699(0x83)]('2d');
let _0x228047;
const _0x124180 = 0x2 * -0x7f7 + -0x70 * 0x1 + -0x602 * -0x3;
const _0x591e4f = 0xba7 * 0x1 + 0x1ebc + -0x2973;
let _0x39dbea = new Array(
(0x94f + -0x215 * -0x1 + -0xb24) *
(-0x1 * -0x1dda + -0x23e + -0x1b5c) *
(0xb9c + 0x1 * 0x1c57 + -0x27b3 * 0x1)
);
let _0x391e96 = new Array(
(-0x1e8f + -0x1 * -0x20f9 + -0x25a) *
(-0xce6 + 0x2a * 0x86 + -0x906) *
(0x1d57 * -0x1 + -0x1170 + 0x2eca) *
(0x1c2 + -0x11a9 + 0x1 * 0xff7)
);
function _0x3ebd0f() {
let _0x53ab34 = _0xdd3699;
for (
var _0x599786 = 0x1e5b + 0x7a * -0x3a + -0x2b6;
_0x599786 < -0x1 * 0x593 + 0x9 * -0x15 + 0x660;
_0x599786++
) {
let _0x5dd0b8 =
0x1043 +
0x401 +
-0x1345 -
((Math[_0x53ab34(0x84)]() * (-0xb10 + -0x173e + 0x22ae)) |
(0x1 * -0xf5b + -0x83 * -0xd + -0x8b4 * -0x1));
for (
var _0xebe028 = -0x1827 * -0x1 + 0x385 + 0x142 * -0x16;
_0xebe028 < (-0x77c + 0x11d * 0x1b + -0x1683) * (0x1a * 0x162 + 0x13 * -0xc3 + -0x1578);
_0xebe028++
) {
for (
var _0x4bc490 = 0x2c * 0x55 + -0xf1d + -0x3 * -0x2b;
_0x4bc490 < 0x3 * -0x9a5 + -0x1ee8 + 0x5 * 0xbfb;
_0x4bc490++
) {
let _0x36dcfc = -0xd60e5f * 0x1 + -0x3f232d + 0x1ab9dd6;
if (_0x599786 == -0x517 * -0x5 + -0x1b9b + -0x8b * -0x4)
_0x36dcfc = -0xcc5387 * 0x1 + -0xa89ad4 + -0xa6 * -0x303bf;
(_0x599786 != -0x9 * -0x4f + 0x53 * -0x22 + 0x843 ||
((Math[_0x53ab34(0x84)]() * (-0x11de + -0x1 * -0xbf1 + 0x5f0)) |
(0xf60 + -0x704 + -0x85c)) ==
0xb0b * -0x2 + -0x139b + 0x29b1) &&
(_0x5dd0b8 =
0x1 * 0x6d4 +
-0x9 * -0x1f2 +
-0x19 * 0xef -
((Math[_0x53ab34(0x84)]() * (-0x5 * 0x79d + -0x233 * -0xb + 0xe40)) |
(-0x4e4 + -0x165a + 0x1b3e)));
if (
_0x599786 == 0xe17 + 0x2a1 + -0x10b7 &&
_0xebe028 <
(((_0x4bc490 * _0x4bc490 * (-0x25c0 + -0x24 * 0x76 + -0x5 * -0xadf) +
_0x4bc490 * (0x2569 + -0x396 + 0x10c1 * -0x2)) >>
(-0x6d * 0x5 + 0x3b9 * 0x9 + 0x646 * -0x5)) &
(-0x249a + 0xec3 + 0x15da)) +
(-0x56 * 0x29 + -0x11 * -0x219 + -0x15d1)
)
_0x36dcfc = -0x1 * -0x3d6586 + 0x8bbbc5 + -0x5e770b;
else
_0x599786 == 0x9e + 0x29 * -0xa2 + 0x1955 &&
_0xebe028 <
(((_0x4bc490 * _0x4bc490 * (0x647 * -0x5 + -0x2359 + 0x42bf) +
_0x4bc490 * (-0xf * 0xb8 + -0x220c + 0x2d25)) >>
(0x13 * -0xc4 + -0x15fc + -0x3 * -0xc2e)) &
(-0x2483 + 0x13a9 + 0x59f * 0x3)) +
(0xca3 + -0x480 + -0x810) &&
(_0x5dd0b8 =
(_0x5dd0b8 * (-0xf2a * 0x1 + 0x4ab + 0xa81 * 0x1)) /
(-0x1 * 0x1e5d + -0x1f7a + -0x261 * -0x1a));
if (_0x599786 == 0xdad + 0xbe5 + 0xd * -0x1f7) {
_0x36dcfc = 0x30e9f6 * -0x2 + -0x3c841f + 0x105aa3c;
if (
_0x4bc490 > -0x1af + -0x2 * -0xf94 + -0x5 * 0x5e5 &&
_0x4bc490 < 0x1ed5 + 0x3c4 * 0x3 + -0x2a12 &&
((_0xebe028 > 0x164 + -0x1720 + 0x56f * 0x4 &&
_0xebe028 < 0x1514 + -0x127d * 0x1 + -0x288) ||
(_0xebe028 > 0x1b * 0x147 + -0x1415 * -0x1 + 0x1b39 * -0x2 &&
_0xebe028 < 0x2208 + -0x2408 + 0x22f * 0x1))
) {
_0x36dcfc = -0x1a7143 + 0x16bbd0e + -0x94b369;
let _0xf0bb95 = _0x4bc490 - (-0x20 * -0xce + -0x1222 * -0x1 + -0x2bdb);
var _0x436c14 =
(_0xebe028 & (0x1f03 + -0x1506 + -0x3e * 0x29)) -
(-0x1e7 + 0xef9 * 0x2 + -0x28c * 0xb);
if (_0xf0bb95 < -0x10 * 0xe0 + -0x207d + 0x3 * 0xf7f)
_0xf0bb95 = -0x8f * 0x2b + -0xd18 + 0x251e * 0x1 - _0xf0bb95;
if (_0x436c14 < 0x1f64 + -0x18d * -0x3 + -0x240b * 0x1)
_0x436c14 = -0x755 * 0x4 + -0xafb + 0x81 * 0x50 - _0x436c14;
if (_0x436c14 > _0xf0bb95) _0xf0bb95 = _0x436c14;
_0x5dd0b8 =
0x3b5 * 0x5 +
-0x1 * 0x1db7 +
0xbf2 -
((Math.random() * (-0xc45 + 0x257 * -0xb + 0x2622)) |
(-0xf29 * -0x1 + -0x948 + 0x1 * -0x5e1)) +
(_0xf0bb95 % (-0x1 * -0x13e1 + 0xa4e * -0x3 + 0x1c * 0x65)) *
(-0x939 + -0x1d87 + 0x26e0);
} else
((Math[_0x53ab34(0x84)]() * (-0x405 * 0x6 + -0x122a + 0x2a4a)) |
(0x7f7 + -0x1 * 0xc4f + -0x1 * -0x458)) ==
0x1 * -0x26b + -0x131 * 0xd + 0x2 * 0x8f4 &&
(_0x5dd0b8 =
(_0x5dd0b8 *
(0x102c +
-0x2f9 * 0x4 +
-0x3b2 -
(_0x4bc490 & (0x18a7 * -0x1 + 0x236e + 0x563 * -0x2)) *
(0x89 * 0x45 + 0xb38 + -0x2fc1))) /
(-0x1 * 0x156 + -0x4d * 0x65 + 0x201b * 0x1));
}
_0x599786 == -0x2 * -0x1087 + -0x3 * 0xcb3 + 0x9 * 0x90 &&
((_0x36dcfc = -0x100d2 * -0x9f + -0x659425 + 0x7b4bcc),
((_0x4bc490 +
(_0xebe028 >> (-0x12 * -0x205 + 0x1ca + -0x1 * 0x2622)) *
(-0xe67 + -0x1c85 * 0x1 + -0x30 * -0xe5)) %
(0x15c1 * -0x1 + 0x73f + 0xe8a * 0x1) ==
-0x1729 + -0x1 * 0x247 + 0x1970 ||
_0xebe028 % (0x14e9 * -0x1 + 0x21e1 + -0x1 * 0xcf4) ==
0x641 * 0x1 + -0x16fd + 0x4 * 0x42f) &&
(_0x36dcfc = 0x112d086 + -0x442565 + -0x47edf * 0x4));
_0x599786 == 0x6 * -0x2bb + -0x24 + 0x9d * 0x1b &&
(_0x36dcfc = -0x197 * 0x4ae9 + -0x22af5 * 0x1 + 0xb98463);
let _0xba7812 = _0x5dd0b8;
if (_0xebe028 >= -0x4 * -0x52 + 0x25d + -0x385)
_0xba7812 /= -0x1 * -0x497 + 0x1a3f + -0x7b5 * 0x4;
_0x599786 == -0x1d35 * -0x1 + -0x2445 + 0x718 &&
((_0x36dcfc = -0x981b * 0xc4 + -0x6c2f7 * -0x16 + -0x11 * -0x2db19),
((Math.random() * (-0x1 * 0x181a + -0x1700 + 0x2f1c)) | (-0x266 + 0x3 * 0x8b4 + -0x17b6)) ==
0xcc2 + -0xbc6 + -0xe * 0x12 &&
((_0x36dcfc = 0x2589 + -0xd * 0x27f + 0x3e * -0x15),
(_0xba7812 = 0x10ef + -0x68a + -0x966)));
let _0x1206cd =
(((((_0x36dcfc >> (0x2 * 0xfb3 + -0x118a + -0xdcc)) & (-0x8fc + -0x1a19 + 0x1 * 0x2414)) *
_0xba7812) /
(0xa6 * -0x25 + 0x112c + 0x7d1)) <<
(0x140c + -0xac0 + -0x1 * 0x93c)) |
(((((_0x36dcfc >> (-0x11 * -0x3e + 0x157d + -0x1993 * 0x1)) &
(-0x9 * 0x421 + -0x2102 + 0x472a * 0x1)) *
_0xba7812) /
(0x107b * -0x1 + -0x16fc + 0x2876)) <<
(-0x20e8 + -0x5e * -0x53 + 0x276)) |
(((_0x36dcfc & (0x581 * 0x3 + 0x1 * 0x977 + -0x18fb)) * _0xba7812) /
(-0xaa0 + 0x211 + 0x2 * 0x4c7));
_0x391e96[
_0x4bc490 +
_0xebe028 * (0x7 * -0x415 + 0x1b6e * -0x1 + 0x1f * 0x1cf) +
_0x599786 * (0x504 + -0x2075 + -0x97b * -0x3) * (0x25 * -0xa9 + 0x171d + -0x3 * -0x71)
] = _0x1206cd;
}
}
}
for (
var _0x4bc490 = -0x825 * -0x3 + 0x71a + -0x1b * 0x12b;
_0x4bc490 < 0x17f0 + -0x15a3 + -0x20d;
_0x4bc490++
) {
for (
var _0xebe028 = 0x53a + 0x7fe + -0x24 * 0x5e;
_0xebe028 < 0x1 * 0x1445 + 0x6d * 0x2 + -0xd * 0x19b;
_0xebe028++
) {
for (
let _0x2eee05 = 0x69 * 0x39 + -0x202 * 0xb + -0x14b;
_0x2eee05 < 0x97 * 0x27 + -0x261 + -0x1 * 0x1460;
_0x2eee05++
) {
var _0x599786 =
(_0x2eee05 << (-0x1 * -0x235e + 0x121 + -0x2473)) |
(_0xebe028 << (-0x7db + -0x80 * 0x23 + 0x1961)) |
_0x4bc490;
var _0x436c14 =
(_0xebe028 - (0xec6 + 0x6 * 0x199 + -0xbc * 0x21 + 0.5)) *
(0x1fca + 0x7 * 0xf9 + 0x2699 * -0x1 + 0.4);
let _0x198036 =
(_0x2eee05 - (0x1 * 0xecb + -0x24b8 + 0x160d + 0.5)) * (0xc * 0xda + -0x825 + -0x213 + 0.4);
_0x39dbea[_0x599786] =
(Math.random() * (0x1134 + -0x131 * -0x9 + -0x1bdd)) | (0x160c + -0x7 * 0x6b + -0x131f);
if (
Math[_0x53ab34(0x84)]() >
Math[_0x53ab34(0x85)](Math.sqrt(_0x436c14 * _0x436c14 + _0x198036 * _0x198036)) -
(0x15a9 + -0xe8f * -0x2 + 0x32c7 * -0x1 + 0.8)
)
_0x39dbea[_0x599786] = 0x1a62 + -0xaf * -0x1 + 0x1 * -0x1b11;
}
}
}
_0x228047 = _0x16f39e[_0x53ab34(0x92)](_0x124180, _0x591e4f);
for (
var _0x599786 = 0x26b7 + -0x1b66 * 0x1 + -0x1 * 0xb51;
_0x599786 < _0x124180 * _0x591e4f;
_0x599786++
) {
_0x228047[_0x53ab34(0x8e)][
_0x599786 * (-0x4 * -0x4c0 + 0x228a + -0x3586) + (-0x856 + -0x22 * 0x37 + 0x1 * 0xfa7)
] = -0x4e5 + 0x196a + -0x126 * 0x11;
}
setInterval(_0x5629d1, (0x26b5 + 0x1 * 0xf9b + 0x1934 * -0x2) / (0x1 * 0xca1 + -0xbb6 + -0x87));
}
function _0x5629d1() {
let _0x13c635 = _0xdd3699;
_0x5b7f43(),
_0x16f39e[_0x13c635(0x81)](
_0x228047,
0x9c + -0x1 * 0x23ab + 0x230f * 0x1,
-0x1c26 + 0x7bf + -0x6cd * -0x3
);
}
let _0x93f360 = 0x64 * 0x49 + 0x1e1e + -0x3aa2;
function _0x5b7f43() {
let _0x4626de = _0xdd3699;
let _0x400eca =
Math[_0x4626de(0x82)](
((Date.now() % (-0x1f15 * -0x1 + 0x1 * -0x44b9 + -0x265a * -0x2)) /
(-0x396f + 0x1 * -0x995 + 0x6a14)) *
Math.PI *
(0x2429 + 0x2360 + -0x4787)
) *
(-0x106 * -0x1d + -0x3e * -0xa0 + -0x446e + 0.4) +
Math.PI / (0x1b21 + -0x1 * -0xa9 + 0x379 * -0x8);
let _0xaac757 =
Math[_0x4626de(0x90)](
((Date[_0x4626de(0x8d)]() % (0xd * -0x351 + -0x3994 + 0x13f7 * 0x7)) /
(0x1ceb + -0x9ef * 0x1 + 0x1414)) *
Math.PI *
(0x1420 + 0x12b1 + -0x26cf)
) *
(-0x1 * -0x211a + -0x18ff + -0x81b * 0x1 + 0.4);
let _0x159021 = Math[_0x4626de(0x90)](_0xaac757);
let _0x11f9a2 = Math[_0x4626de(0x82)](_0xaac757);
let _0x1ac9ca = Math[_0x4626de(0x90)](_0x400eca);
let _0x49fc16 = Math[_0x4626de(0x82)](_0x400eca);
let _0x116a45 =
0x1b4f +
-0x1167 +
-0x9c8 +
0.5 +
((Date[_0x4626de(0x8d)]() % (-0x1 * -0xfaa + 0x169 * 0x1f + -0x7 * 0x2e7)) /
(-0x2831 + 0x1 * 0x171 + 0x4dd0)) *
(-0x911 + -0x26e2 + -0x1b * -0x1c9);
let _0x46282c = 0x1 * 0x238 + 0x252d + -0x2745 + 0.5;
let _0x1680c5 = -0x14ad + -0x187 * 0x15 + -0x30 * -0x11a + 0.5;
_0x93f360++;
for (let _0x132623 = -0x174b + 0x19 * -0xb7 + 0x292a; _0x132623 < _0x124180; _0x132623++) {
let _0x1859c2 = (_0x132623 - _0x124180 / (-0x3e5 * 0x8 + -0x1e7b + -0x3da5 * -0x1)) / _0x591e4f;
for (let _0x1a573d = 0x2440 + -0x262f + -0x1ef * -0x1; _0x1a573d < _0x591e4f; _0x1a573d++) {
let _0x58642d = (_0x1a573d - _0x591e4f / (0x14f4 + 0x24b * 0x11 + -0x3bed)) / _0x591e4f;
let _0x3109fa = -0x7ca + 0x5 * -0x503 + 0x349 * 0xa;
let _0x1df399 = _0x3109fa * _0x159021 + _0x58642d * _0x11f9a2;
let _0x8e77ec = _0x58642d * _0x159021 - _0x3109fa * _0x11f9a2;
let _0x5d0446 = _0x1859c2 * _0x1ac9ca + _0x1df399 * _0x49fc16;
let _0x14ed58 = _0x1df399 * _0x1ac9ca - _0x1859c2 * _0x49fc16;
let _0x13f1b0 = 0x1af9 + -0x3 * 0xc1b + 0x34 * 0x2e;
let _0x2062a9 = -0x6c * -0x21 + -0x13bb * 0x1 + 0x43 * 0x1a;
let _0x5c387a = 0x1124 + 0x862 * 0x2 + 0x9b * -0x38;
let _0x267dd3 = -0x986 + 0x5 * 0x1f5 + 0x5 * -0x7;
for (
let _0x3e178a = -0xf01 + 0xd * -0x59 + 0x93 * 0x22;
_0x3e178a < -0x16f6 + -0x204b * 0x1 + -0x36 * -0x106;
_0x3e178a++
) {
let _0x828a0c = _0x5d0446;
if (_0x3e178a == 0x411 * -0x7 + 0x2 * 0x80f + 0x11 * 0xba) _0x828a0c = _0x8e77ec;
if (_0x3e178a == -0x1e6d + -0xdb1 * -0x1 + 0x85f * 0x2) _0x828a0c = _0x14ed58;
let _0x57383c =
(0x18ab + 0x183f + -0x1 * 0x30e9) /
(_0x828a0c < 0x25 * -0xe3 + -0x2424 + 0x44f3 ? -_0x828a0c : _0x828a0c);
let _0x4b089b = _0x5d0446 * _0x57383c;
let _0x5b3085 = _0x8e77ec * _0x57383c;
let _0x1eaaad = _0x14ed58 * _0x57383c;
let _0x2a568a = _0x116a45 - (_0x116a45 | (-0xcdb + 0x1447 + -0x13 * 0x64));
if (_0x3e178a == 0x1 * -0x1048 + -0x7ea + -0x4d7 * -0x5)
_0x2a568a = _0x46282c - (_0x46282c | (-0x1db5 + 0x1 * -0x35 + -0x223 * -0xe));
if (_0x3e178a == 0x107e + 0x2c7 + -0x1343 * 0x1)
_0x2a568a = _0x1680c5 - (_0x1680c5 | (0x234d + -0x2368 + -0x3 * -0x9));
if (_0x828a0c > 0xe00 + 0xc9b * -0x1 + -0x165)
_0x2a568a = 0xb * -0x117 + 0x1 * -0x1e0b + 0x2a09 - _0x2a568a;
let _0x38c463 = _0x57383c * _0x2a568a;
let _0x227002 = _0x116a45 + _0x4b089b * _0x2a568a;
let _0x2aec99 = _0x46282c + _0x5b3085 * _0x2a568a;
let _0x32116b = _0x1680c5 + _0x1eaaad * _0x2a568a;
if (_0x828a0c < 0x180b + 0x19e3 * 0x1 + 0x391 * -0xe) {
if (_0x3e178a == 0x1313 * -0x2 + 0x1 * 0x2605 + -0x1 * -0x21) _0x227002--;
if (_0x3e178a == 0x3d * 0x15 + 0x114d + 0x164d * -0x1) _0x2aec99--;
if (_0x3e178a == -0x1c1 * -0x8 + -0x60c + -0x3fd * 0x2) _0x32116b--;
}
while (_0x38c463 < _0x267dd3) {
let _0x3db6a4 =
_0x39dbea[
((_0x32116b & (-0x3 * 0x1f3 + 0x1ef2 + -0x18da * 0x1)) <<
(-0x8d3 * 0x2 + -0x1f7 * 0x9 + 0x2361)) |
((_0x2aec99 & (-0x1a92 + 0x6b8 + 0x1419)) << (-0xe71 * 0x1 + -0x788 + 0x15ff)) |
(_0x227002 & (0x1897 + 0x2134 + -0x398c))
];
if (_0x3db6a4 > -0x18f6 + 0x513 * 0x2 + 0xed0) {
let _0x546fb5 =
((_0x227002 + _0x32116b) * (-0x861 + -0x16bd + 0x1f2e)) &
(0xd1c + -0x94 * -0x8 + -0x11ad);
let _0x1667c5 =
((_0x2aec99 * (-0x16f5 * -0x1 + -0x47 * -0x2f + 0x2a * -0xdb)) &
(-0x2469 + 0x156a * -0x1 + 0x39e2)) +
(-0x1 * 0x13f8 + 0x6df + -0x1 * -0xd29);
if (_0x3e178a == 0x1 * -0x1a87 + 0x1fdd + -0x555 * 0x1) {
(_0x546fb5 =
(_0x227002 * (-0x10d7 + 0x1 * 0x15ad + -0x2 * 0x263)) &
(-0x25ca * 0x1 + 0x8 * 0x278 + -0x1 * -0x1219)),
(_0x1667c5 =
(_0x32116b * (0x4d3 + 0x1c09 * -0x1 + 0x3 * 0x7c2)) &
(-0xf06 * 0x2 + -0x144f * -0x1 + -0x344 * -0x3));
if (_0x5b3085 < -0xa * 0xed + -0xd19 + 0x1 * 0x165b)
_0x1667c5 += -0xd48 + 0xf6c + 0xc * -0x2b;
}
let _0x5206c3 =
_0x391e96[
_0x546fb5 +
_0x1667c5 * (0x1c90 + 0x2 * -0x1297 + 0x8ae) +
_0x3db6a4 *
(0x208 * -0x13 + -0x2550 + 0x1 * 0x4ce8) *
(0xbc5 + -0x7b3 * -0x2 + -0x58 * 0x4f)
];
_0x5206c3 > 0x65a * -0x4 + 0xf9a + -0x5 * -0x1f6 &&
((_0x13f1b0 = _0x5206c3),
(_0x5c387a =
0x181c +
0x1449 +
-0x2b66 -
(((_0x38c463 / (-0x15 * -0x59 + -0x3 * -0x3c9 + 0x944 * -0x2)) *
(0x9cd * 0x1 + -0x15ce + 0x100 * 0xd)) |
(-0xc * -0x2a0 + 0x1 * -0x23bf + 0x43f))),
(_0x2062a9 =
((0x7a0 + 0x3f * 0x51 + 0x8 * -0x352) *
(-0xf60 +
0x21e9 +
-0xa * 0x1c1 -
((_0x3e178a + (0x1439 + -0x1049 + -0x1 * 0x3ee)) %
(-0xbd7 + -0x1 * -0x7d5 + 0x405)) *
(0x214 * -0x7 + 0x17 * 0x7 + 0xe1d * 0x1))) /
(-0x2709 + -0x6 * -0x312 + -0x39a * -0x6)),
(_0x267dd3 = _0x38c463));
}
(_0x227002 += _0x4b089b),
(_0x2aec99 += _0x5b3085),
(_0x32116b += _0x1eaaad),
(_0x38c463 += _0x57383c);
}
}
let _0x5cba48 =
(((_0x13f1b0 >> (0x8bb + 0x407 * -0x2 + -0x9d * 0x1)) & (-0xc6 + 0xb43 + -0x97e)) *
_0x2062a9 *
_0x5c387a) /
((-0x1df7 * -0x1 + -0x48b * 0x1 + -0x186d) * (-0x5 * -0x585 + -0x1497 + -0x603));
let _0x48b547 =
(((_0x13f1b0 >> (-0xa42 + -0x760 * 0x2 + 0x5 * 0x502)) &
(0xb31 + 0x1 * -0x538 + 0x31 * -0x1a)) *
_0x2062a9 *
_0x5c387a) /
((-0x1cff * -0x1 + -0xecd + -0xd33) * (-0x1 * 0x4d5 + 0x2440 + -0x2c * 0xb1));
let _0xdf8389 =
((_0x13f1b0 & (-0x247a + -0x4 * -0x9c2 + -0x39 * 0x7)) * _0x2062a9 * _0x5c387a) /
((0x1d5 * 0xa + -0x250a + -0x31 * -0x67) * (-0x7 * 0x25f + -0xae7 + 0x1c7f * 0x1));
(_0x228047[_0x4626de(0x8e)][
(_0x132623 + _0x1a573d * _0x124180) * (0x29 * -0xa9 + -0x94 * -0x2b + -0x239 * -0x1) +
(0x55d * 0x2 + 0xeed * 0x1 + -0xc7 * 0x21)
] = _0x5cba48),
(_0x228047[_0x4626de(0x8e)][
(_0x132623 + _0x1a573d * _0x124180) * (0x9 * -0x205 + -0xfb7 * 0x1 + -0x1c * -0x136) +
(-0x1 * -0x1e79 + 0x10cd + -0x2f45)
] = _0x48b547),
(_0x228047[_0x4626de(0x8e)][
(_0x132623 + _0x1a573d * _0x124180) * (0x1e2a + -0x21df + -0x1 * -0x3b9) +
(0x1e79 + 0x860 * -0x2 + 0x1 * -0xdb7)
] = _0xdf8389);
}
}
}
function _0x9cb4(_0x473675, _0x42315f) {
let _0x1c3f44 = _0x58e5();
return (
(_0x9cb4 = function (_0x2f27e0, _0x487e61) {
_0x2f27e0 -= -0x6c7 * -0x2 + 0x1 * -0x1b75 + -0x4 * -0x39a;
let _0x44ff4c = _0x1c3f44[_0x2f27e0];
return _0x44ff4c;
}),
_0x9cb4(_0x473675, _0x42315f)
);
}
_0x3ebd0f();
console.log('404 page loaded');
}, []);
return (
<div className='relative h-screen w-screen flex items-center justify-center overflow-hidden bg-white'>
{/* Canvas Container */}
<div className='relative h-full w-full'>
<canvas
ref={canvasRef}
className='absolute left-1/2 top-1/2 h-full w-full object-contain -translate-x-1/2 -translate-y-1/2'
style={{
maxWidth: '100vw',
maxHeight: '100vh',
aspectRatio: ASPECT_RATIO,
imageRendering: 'crisp-edges',
}}
/>
</div>
{/* 404 Text Overlay */}
<div className='absolute left-1/2 top-1/2 z-10 -translate-x-1/2 -translate-y-1/2'>
<h1 className='text-8xl text-white font-bold'>404 Page not found</h1>
</div>
</div>
);
}

27
src/pages/404/index.html Normal file
View File

@@ -0,0 +1,27 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>404</title>
</head>
<style type="text/css">
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
</style>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
</html>

8
src/pages/404/index.tsx Normal file
View File

@@ -0,0 +1,8 @@
import 'uno.css';
import React from 'react';
import { createRoot } from 'react-dom/client';
import Page404 from './Page404';
createRoot(document.getElementById('root')!).render(<Page404 />);

View File

@@ -1,4 +1,7 @@
import { ExtensionStore } from '@shared/storage/ExtensionStore'; import { ExtensionStore } from '@shared/storage/ExtensionStore';
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import createSchedule from '../lib/createSchedule';
/** /**
* Called when the extension is updated (or when the extension is reloaded in development mode) * Called when the extension is updated (or when the extension is reloaded in development mode)
@@ -8,4 +11,11 @@ export default async function onUpdate() {
version: chrome.runtime.getManifest().version, version: chrome.runtime.getManifest().version,
lastUpdate: Date.now(), lastUpdate: Date.now(),
}); });
const schedules = await UserScheduleStore.get('schedules');
// By invariant, there must always be at least one schedule
if (schedules.length === 0) {
createSchedule('Schedule 1');
}
} }

View File

@@ -2,6 +2,8 @@ import type { TabWithId } from '@background/util/openNewTab';
import openNewTab from '@background/util/openNewTab'; import openNewTab from '@background/util/openNewTab';
import { tabs } from '@shared/messages'; import { tabs } from '@shared/messages';
import type { CalendarBackgroundMessages } from '@shared/messages/CalendarMessages'; import type { CalendarBackgroundMessages } from '@shared/messages/CalendarMessages';
import { OptionsStore } from '@shared/storage/OptionsStore';
import { CRX_PAGES } from '@shared/types/CRXPages';
import type { MessageHandler } from 'chrome-extension-toolkit'; import type { MessageHandler } from 'chrome-extension-toolkit';
const getAllTabInfos = async () => { const getAllTabInfos = async () => {
@@ -21,13 +23,13 @@ const getAllTabInfos = async () => {
const calendarBackgroundHandler: MessageHandler<CalendarBackgroundMessages> = { const calendarBackgroundHandler: MessageHandler<CalendarBackgroundMessages> = {
async switchToCalendarTab({ data, sendResponse }) { async switchToCalendarTab({ data, sendResponse }) {
const { uniqueId } = data; const { uniqueId } = data;
const calendarUrl = chrome.runtime.getURL(`calendar.html`); const calendarUrl = chrome.runtime.getURL(CRX_PAGES.CALENDAR);
const allTabs = await getAllTabInfos(); const allTabs = await getAllTabInfos();
const openCalendarTabInfo = allTabs.find(tab => tab.url?.startsWith(calendarUrl)); const openCalendarTabInfo = allTabs.find(tab => tab.url?.startsWith(calendarUrl));
if (openCalendarTabInfo !== undefined) { if (openCalendarTabInfo !== undefined && !(await OptionsStore.get('alwaysOpenCalendarInNewTab'))) {
const tabid = openCalendarTabInfo.tab.id; const tabid = openCalendarTabInfo.tab.id;
await chrome.tabs.update(tabid, { active: true }); await chrome.tabs.update(tabid, { active: true });

View File

@@ -2,16 +2,18 @@ import addCourse from '@pages/background/lib/addCourse';
import clearCourses from '@pages/background/lib/clearCourses'; import clearCourses from '@pages/background/lib/clearCourses';
import createSchedule from '@pages/background/lib/createSchedule'; import createSchedule from '@pages/background/lib/createSchedule';
import deleteSchedule from '@pages/background/lib/deleteSchedule'; import deleteSchedule from '@pages/background/lib/deleteSchedule';
import exportSchedule from '@pages/background/lib/exportSchedule';
import removeCourse from '@pages/background/lib/removeCourse'; import removeCourse from '@pages/background/lib/removeCourse';
import renameSchedule from '@pages/background/lib/renameSchedule'; import renameSchedule from '@pages/background/lib/renameSchedule';
import switchSchedule from '@pages/background/lib/switchSchedule'; import switchSchedule from '@pages/background/lib/switchSchedule';
import type { UserScheduleMessages } from '@shared/messages/UserScheduleMessages'; import type { UserScheduleMessages } from '@shared/messages/UserScheduleMessages';
import { Course } from '@shared/types/Course'; import { Course } from '@shared/types/Course';
import { validateLoginStatus } from '@shared/util/checkLoginStatus';
import type { MessageHandler } from 'chrome-extension-toolkit'; import type { MessageHandler } from 'chrome-extension-toolkit';
const userScheduleHandler: MessageHandler<UserScheduleMessages> = { const userScheduleHandler: MessageHandler<UserScheduleMessages> = {
addCourse({ data, sendResponse }) { addCourse({ data, sendResponse }) {
addCourse(data.scheduleId, new Course(data.course)).then(sendResponse); addCourse(data.scheduleId, new Course(data.course), data.hasColor ?? false).then(sendResponse);
}, },
removeCourse({ data, sendResponse }) { removeCourse({ data, sendResponse }) {
removeCourse(data.scheduleId, new Course(data.course)).then(sendResponse); removeCourse(data.scheduleId, new Course(data.course)).then(sendResponse);
@@ -31,6 +33,21 @@ const userScheduleHandler: MessageHandler<UserScheduleMessages> = {
renameSchedule({ data, sendResponse }) { renameSchedule({ data, sendResponse }) {
renameSchedule(data.scheduleId, data.newName).then(sendResponse); renameSchedule(data.scheduleId, data.newName).then(sendResponse);
}, },
// proxy so we can add courses
addCourseByURL({ data: { url, method, body, response }, sendResponse }) {
fetch(url, {
method,
body,
})
.then(res => (response === 'json' ? res.json() : res.text()))
.then(sendResponse);
},
validateLoginStatus({ data, sendResponse }) {
validateLoginStatus(data.url).then(sendResponse);
},
exportSchedule({ data, sendResponse }) {
exportSchedule(data.scheduleId).then(sendResponse);
},
}; };
export default userScheduleHandler; export default userScheduleHandler;

View File

@@ -4,21 +4,26 @@ import { getUnusedColor } from '@shared/util/colors';
/** /**
* Adds a course to a user's schedule. * Adds a course to a user's schedule.
*
* @param scheduleId - The id of the schedule to add the course to. * @param scheduleId - The id of the schedule to add the course to.
* @param course - The course to add. * @param course - The course to add.
* @param hasColor - If the course block already has colors manually set
* @returns A promise that resolves to void. * @returns A promise that resolves to void.
* @throws An error if the schedule is not found. * @throws An error if the schedule is not found.
*/ */
export default async function addCourse(scheduleId: string, course: Course): Promise<void> { export default async function addCourse(scheduleId: string, course: Course, hasColor = false): Promise<void> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');
const activeSchedule = schedules.find(s => s.id === scheduleId); const activeSchedule = schedules.find(s => s.id === scheduleId);
if (!activeSchedule) { if (!activeSchedule) {
throw new Error('Schedule not found'); throw new Error('Schedule not found');
} }
course.colors = getUnusedColor(activeSchedule, course); if (!hasColor) {
course.colors = getUnusedColor(activeSchedule, course);
}
activeSchedule.courses.push(course); activeSchedule.courses.push(course);
activeSchedule.updatedAt = Date.now(); activeSchedule.updatedAt = Date.now();
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
console.log(`Course added: ${course.courseName} (ID: ${course.uniqueId})`);
} }

View File

@@ -0,0 +1,65 @@
import addCourse from '@pages/background/lib/addCourse';
import { background } from '@shared/messages';
import type { UserSchedule } from '@shared/types/UserSchedule';
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
import getCourseTableRows from '@views/lib/getCourseTableRows';
import { SiteSupport } from '@views/lib/getSiteSupport';
/**
* Adds a course to the active schedule by fetching course details from a provided URL.
* If no URL is provided, prompts the user to enter one.
* Sriram and Elie made this
*
* @param activeSchedule - The user's active schedule to which the course will be added.
* @param link - The URL from which to fetch the course details. If not provided, a prompt will ask for it.
*
* @returns A promise that resolves when the course has been added or the operation is cancelled.
*
* @throws an error if there is an issue with scraping the course details.
*/
export async function addCourseByURL(activeSchedule: UserSchedule, link?: string): Promise<void> {
// todo: Use a proper modal instead of a prompt
// eslint-disable-next-line no-param-reassign, no-alert
if (!link) link = prompt('Enter course link') || undefined;
// Exit if the user cancels the prompt
if (!link) {
return;
}
try {
let htmlText: string;
try {
htmlText = await background.addCourseByURL({
url: link,
method: 'GET',
response: 'text',
});
} catch (e) {
// eslint-disable-next-line no-alert
alert(`Failed to fetch url '${link}'`);
return;
}
const doc = new DOMParser().parseFromString(htmlText, 'text/html');
const scraper = new CourseCatalogScraper(SiteSupport.COURSE_CATALOG_DETAILS, doc, link);
const tableRows = getCourseTableRows(doc);
const scrapedCourses = scraper.scrape(tableRows, false);
if (scrapedCourses.length !== 1) return;
const description = scraper.getDescription(doc);
const row = scrapedCourses[0]!;
const course = row.course!;
course.description = description;
if (activeSchedule.courses.every(c => c.uniqueId !== course.uniqueId)) {
console.log('Adding course');
await addCourse(activeSchedule.id, course);
} else {
console.log('Course already exists');
}
} catch (error) {
console.error('Error scraping course:', error);
}
}

View File

@@ -5,8 +5,9 @@ import type { Serialized } from 'chrome-extension-toolkit';
/** /**
* Creates a new schedule with the given name * Creates a new schedule with the given name
* @param scheduleName the name of the schedule to create *
* @returns undefined if successful, otherwise an error message * @param scheduleName - The name of the schedule to create
* @returns Undefined if successful, otherwise an error message
*/ */
export default async function createSchedule(scheduleName: string) { export default async function createSchedule(scheduleName: string) {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');
@@ -35,5 +36,14 @@ export default async function createSchedule(scheduleName: string) {
schedules.push(newSchedule); schedules.push(newSchedule);
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
// Automatically switch to the new schedule
await UserScheduleStore.set('activeIndex', schedules.length - 1);
// If there is only one schedule, set the active index to the new schedule
if (schedules.length <= 1) {
await UserScheduleStore.set('activeIndex', 0);
}
return newSchedule.id; return newSchedule.id;
} }

View File

@@ -18,20 +18,23 @@ export default async function deleteSchedule(scheduleId: string): Promise<string
if (scheduleIndex === -1) { if (scheduleIndex === -1) {
throw new Error(`Schedule ${scheduleId} does not exist`); throw new Error(`Schedule ${scheduleId} does not exist`);
} }
if (scheduleIndex === activeIndex) {
throw new Error(`Cannot delete active schedule`);
}
if (scheduleIndex < activeIndex) {
await UserScheduleStore.set('activeIndex', activeIndex - 1);
}
schedules.splice(scheduleIndex, 1); schedules.splice(scheduleIndex, 1);
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
if (activeIndex >= schedules.length) { // By invariant, there must always be at least one schedule
await UserScheduleStore.set('activeIndex', schedules.length - 1); if (schedules.length === 0) {
createSchedule('Schedule 1');
} }
let newActiveIndex = activeIndex;
if (scheduleIndex < activeIndex) {
newActiveIndex = activeIndex - 1;
} else if (activeIndex >= schedules.length) {
newActiveIndex = schedules.length - 1;
}
await UserScheduleStore.set('activeIndex', newActiveIndex);
return undefined; return undefined;
} }

View File

@@ -5,8 +5,9 @@ import handleDuplicate from './handleDuplicate';
/** /**
* Creates a new schedule with the given name * Creates a new schedule with the given name
* @param scheduleName the name of the schedule to create *
* @returns undefined if successful, otherwise an error message * @param scheduleName - The name of the schedule to create
* @returns Undefined if successful, otherwise an error message
*/ */
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');
@@ -30,5 +31,9 @@ export default async function duplicateSchedule(scheduleId: string): Promise<str
} satisfies typeof schedule); } satisfies typeof schedule);
await UserScheduleStore.set('schedules', schedules); await UserScheduleStore.set('schedules', schedules);
// Automatically switch to the duplicated schedule
await UserScheduleStore.set('activeIndex', scheduleIndex + 1);
return undefined; return undefined;
} }

View File

@@ -0,0 +1,24 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/**
* Exports the provided schedule to a portable JSON
*
* @param scheduleId - The Id matching the to-be-exported schedule
* @returns JSON format of the provided schedule ID, empty if one was not found
*/
export default async function exportSchedule(scheduleId: string): Promise<string | undefined> {
try {
const storageData = await UserScheduleStore.get('schedules');
const selectedSchedule = storageData.find(s => s.id === scheduleId);
if (!selectedSchedule) {
console.warn(`Schedule ${scheduleId} does not exist`);
return JSON.stringify({});
}
console.log(selectedSchedule);
return JSON.stringify(selectedSchedule, null, 2);
} catch (error) {
console.error('Error getting storage data:', error);
}
}

View File

@@ -3,9 +3,10 @@ import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/** /**
* Duplicates a new schedule with the given name. * Duplicates a new schedule with the given name.
* Assumes that each schedule has a unique name. * Assumes that each schedule has a unique name.
* @param scheduleName the name of the schedule to handle duplication for *
* @param schedules the list of UserSchedules to find existing names * @param scheduleName - The name of the schedule to handle duplication for
* @returns the new name for the schedule, of the form `{baseName}({index})` * @param schedules - The list of UserSchedules to find existing names
* @returns The new name for the schedule, of the form `{baseName}({index})`
*/ */
export default async function handleDuplicate(scheduleName: string): Promise<string> { export default async function handleDuplicate(scheduleName: string): Promise<string> {
const schedules = await UserScheduleStore.get('schedules'); const schedules = await UserScheduleStore.get('schedules');

View File

@@ -0,0 +1,34 @@
import { Course } from '@shared/types/Course';
import type { UserSchedule } from '@shared/types/UserSchedule';
import type { Serialized } from 'chrome-extension-toolkit';
import addCourse from './addCourse';
import createSchedule from './createSchedule';
import switchSchedule from './switchSchedule';
function isValidSchedule(data: unknown): data is Serialized<UserSchedule> {
if (typeof data !== 'object' || data === null) return false;
const schedule = data as Record<string, unknown>;
return typeof schedule.id === 'string' && typeof schedule.name === 'string' && Array.isArray(schedule.courses);
}
/**
* Imports a user schedule from portable file, creating a new schedule for it
* @param scheduleData - Data to be parsed back into a course schedule
*/
export default async function importSchedule(scheduleData: unknown): Promise<void> {
if (isValidSchedule(scheduleData)) {
const newScheduleId = await createSchedule(scheduleData.name);
await switchSchedule(newScheduleId);
for (const c of scheduleData.courses) {
const course = new Course(c);
// eslint-disable-next-line no-await-in-loop
await addCourse(newScheduleId, course, true);
}
console.log('Course schedule successfully parsed!');
} else {
console.error('No schedule data provided for import');
}
}

View File

@@ -3,6 +3,7 @@ import Calendar from '@views/components/calendar/Calendar';
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider'; import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot'; import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import { MigrationDialog } from '@views/components/common/MigrationDialog'; import { MigrationDialog } from '@views/components/common/MigrationDialog';
import { WhatsNewDialog } from '@views/components/common/WhatsNewPopup';
import SentryProvider from '@views/contexts/SentryContext'; import SentryProvider from '@views/contexts/SentryContext';
import { MessageListener } from 'chrome-extension-toolkit'; import { MessageListener } from 'chrome-extension-toolkit';
import useKC_DABR_WASM from 'kc-dabr-wasm'; import useKC_DABR_WASM from 'kc-dabr-wasm';
@@ -34,6 +35,7 @@ export default function CalendarMain() {
<ExtensionRoot className='h-full w-full'> <ExtensionRoot className='h-full w-full'>
<DialogProvider> <DialogProvider>
<MigrationDialog /> <MigrationDialog />
<WhatsNewDialog />
<Calendar /> <Calendar />
</DialogProvider> </DialogProvider>
</ExtensionRoot> </ExtensionRoot>

View File

@@ -1,18 +1,37 @@
import CourseCatalogMain from '@views/components/CourseCatalogMain'; import CourseCatalogMain from '@views/components/CourseCatalogMain';
import InjectedButton from '@views/components/injected/AddAllButton';
import DaysCheckbox from '@views/components/injected/DaysCheckbox';
import ShadedResults from '@views/components/injected/SearchResultShader';
import getSiteSupport, { SiteSupport } from '@views/lib/getSiteSupport'; import getSiteSupport, { SiteSupport } from '@views/lib/getSiteSupport';
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
const support = getSiteSupport(window.location.href); const support = getSiteSupport(window.location.href);
if (support === SiteSupport.COURSE_CATALOG_DETAILS || support === SiteSupport.COURSE_CATALOG_LIST) { const renderComponent = (Component: React.ComponentType) => {
const container = document.createElement('div'); const container = document.createElement('div');
container.id = 'extension-root'; container.id = 'extension-root';
document.body.appendChild(container); document.body.appendChild(container);
createRoot(container).render( createRoot(container).render(
<React.StrictMode> <React.StrictMode>
<CourseCatalogMain support={support} /> <Component />
</React.StrictMode> </React.StrictMode>
); );
};
if (support === SiteSupport.COURSE_CATALOG_DETAILS || support === SiteSupport.COURSE_CATALOG_LIST) {
renderComponent(() => <CourseCatalogMain support={support} />);
}
if (support === SiteSupport.MY_UT) {
renderComponent(InjectedButton);
}
if (support === SiteSupport.COURSE_CATALOG_SEARCH) {
renderComponent(DaysCheckbox);
}
if (support === SiteSupport.COURSE_CATALOG_KWS) {
renderComponent(ShadedResults);
} }

19
src/pages/map/Map.tsx Normal file
View File

@@ -0,0 +1,19 @@
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
import Map from '@views/components/map/Map';
import useKC_DABR_WASM from 'kc-dabr-wasm';
import React from 'react';
/**
* Renders the map page for the UTRP (UT Registration Plus) extension.
*/
export default function MapPage() {
useKC_DABR_WASM();
return (
<ExtensionRoot>
<DialogProvider>
<Map />
</DialogProvider>
</ExtensionRoot>
);
}

16
src/pages/map/index.html Normal file
View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>UTRP Map</title>
</head>
<body style="min-height: 100vh; height: 0; margin: 0">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
</html>

6
src/pages/map/index.tsx Normal file
View File

@@ -0,0 +1,6 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import Map from './Map';
createRoot(document.getElementById('root')!).render(<Map />);

View File

@@ -9,7 +9,7 @@
body { body {
margin: 0; margin: 0;
padding: 0; padding: 0;
width: 400px; width: 430px;
height: 540px; height: 540px;
} }
</style> </style>

View File

@@ -2,7 +2,7 @@ interface CESMessage {
/** /**
* Opens the CES page for the specified instructor * Opens the CES page for the specified instructor
* *
* @param data first and last name of the instructor * @param data - First and last name of the instructor
*/ */
openCESPage: (data: { instructorFirstName: string; instructorLastName: string }) => chrome.tabs.Tab; openCESPage: (data: { instructorFirstName: string; instructorLastName: string }) => chrome.tabs.Tab;
} }

View File

@@ -4,17 +4,22 @@
export default interface TabManagementMessages { export default interface TabManagementMessages {
/** /**
* Opens a new tab with the given URL * Opens a new tab with the given URL
* @param data The URL to open *
* @param data - The URL to open
*/ */
openNewTab: (data: { url: string }) => chrome.tabs.Tab; openNewTab: (data: { url: string }) => chrome.tabs.Tab;
/** /**
* Gets the ID of the current tab (the tab that sent the message) * Gets the ID of the current tab (the tab that sent the message)
*
* @returns The ID of the current tab * @returns The ID of the current tab
*/ */
getTabId: () => number; getTabId: () => number;
/** /**
* Removes the tab with the given ID * Removes the tab with the given ID
* @param data The ID of the tab to remove *
* @param data - The ID of the tab to remove
* @returns The ID of the tab that was removed * @returns The ID of the tab that was removed
*/ */
removeTab: (data: { tabId: number }) => void; removeTab: (data: { tabId: number }) => void;

View File

@@ -6,42 +6,77 @@ import type { Course } from '@shared/types/Course';
export interface UserScheduleMessages { export interface UserScheduleMessages {
/** /**
* Add a course to a schedule * Add a course to a schedule
* @param data the schedule id and course to add *
* @param data - The schedule id and course to add
*/ */
addCourse: (data: { scheduleId: string; course: Course }) => void; addCourse: (data: { scheduleId: string; course: Course; hasColor?: boolean }) => void;
/**
* Adds a course by URL
*
* @param data - The URL of the course to add
* @returns Response of the requested course URL
*/
addCourseByURL: (data: { url: string; method: string; body?: string; response: 'json' | 'text' }) => string;
/** /**
* Remove a course from a schedule * Remove a course from a schedule
* @param data the schedule id and course to remove *
* @param data - The schedule id and course to remove
*/ */
removeCourse: (data: { scheduleId: string; course: Course }) => void; removeCourse: (data: { scheduleId: string; course: Course }) => void;
/** /**
* Clears all courses from a schedule * Clears all courses from a schedule
* @param data the id of the schedule to clear *
* @param data - The id of the schedule to clear
*/ */
clearCourses: (data: { scheduleId: string }) => void; clearCourses: (data: { scheduleId: string }) => void;
/** /**
* Switches the active schedule to the one specified * Switches the active schedule to the one specified
* @param data the id of the schedule to switch to *
* @param data - The id of the schedule to switch to
*/ */
switchSchedule: (data: { scheduleId: string }) => void; switchSchedule: (data: { scheduleId: string }) => void;
/** /**
* Creates a new schedule with the specified name * Creates a new schedule with the specified name
* @param data the name of the schedule to create *
* @returns undefined if successful, otherwise an error message * @param data - The name of the schedule to create
* @returns Undefined if successful, otherwise an error message
*/ */
createSchedule: (data: { scheduleName: string }) => string | undefined; createSchedule: (data: { scheduleName: string }) => string | undefined;
/** /**
* Deletes a schedule with the specified name * Deletes a schedule with the specified name
* @param data the id of the schedule to delete *
* @returns undefined if successful, otherwise an error message * @param data - The id of the schedule to delete
* @returns Undefined if successful, otherwise an error message
*/ */
deleteSchedule: (data: { scheduleId: string }) => string | undefined; deleteSchedule: (data: { scheduleId: string }) => string | undefined;
/** /**
* Renames a schedule with the specified name * Renames a schedule with the specified name
* @param data the id of the schedule to rename and the new name *
* @returns undefined if successful, otherwise an error message * @param data - The id of the schedule to rename and the new name
* @returns Undefined if successful, otherwise an error message
*/ */
renameSchedule: (data: { scheduleId: string; newName: string }) => string | undefined; renameSchedule: (data: { scheduleId: string; newName: string }) => string | undefined;
/**
* Checks the login status by making a request to the provided URL.
*
* @param data - The URL to check the login status against.
* @returns true if user was already logged into the provided URL, false otherwise
*/
validateLoginStatus: (data: { url: string }) => boolean;
/**
* Exports the current schedule to a JSON file for backing up and sharing
*
* @param data - Id of schedule that will be exported
* @returns
*/
exportSchedule: (data: { scheduleId: string }) => string | undefined;
} }

View File

@@ -0,0 +1,15 @@
import type { CachedData } from '@shared/types/CachedData';
import { createLocalStore, debugStore } from 'chrome-extension-toolkit';
interface ICacheStore {
github: Record<string, CachedData<unknown>>;
}
/**
* A store that is used for storing cached data such as GitHub contributors
*/
export const CacheStore = createLocalStore<ICacheStore>({
github: {},
});
debugStore({ cacheStore: CacheStore });

View File

@@ -4,6 +4,8 @@ import { createLocalStore, debugStore } from 'chrome-extension-toolkit';
* A store that is used to store data that is only relevant during development * A store that is used to store data that is only relevant during development
*/ */
interface IDevStore { interface IDevStore {
/** whether the user is a developer */
isDeveloper: boolean;
/** the tabId for the debug tab */ /** the tabId for the debug tab */
debugTabId?: number; debugTabId?: number;
/** whether the debug tab is visible */ /** whether the debug tab is visible */
@@ -17,6 +19,7 @@ interface IDevStore {
} }
export const DevStore = createLocalStore<IDevStore>({ export const DevStore = createLocalStore<IDevStore>({
isDeveloper: false,
debugTabId: undefined, debugTabId: undefined,
isTabReloading: true, isTabReloading: true,
wasDebugTabVisible: false, wasDebugTabVisible: false,

View File

@@ -8,11 +8,14 @@ interface IExtensionStore {
version: string; version: string;
/** When was the last update */ /** When was the last update */
lastUpdate: number; lastUpdate: number;
/** The last version of the "What's New" popup that was shown to the user */
lastWhatsNewPopupVersion: number;
} }
export const ExtensionStore = createLocalStore<IExtensionStore>({ export const ExtensionStore = createLocalStore<IExtensionStore>({
version: chrome.runtime.getManifest().version, version: chrome.runtime.getManifest().version,
lastUpdate: Date.now(), lastUpdate: Date.now(),
lastWhatsNewPopupVersion: 0,
}); });
debugStore({ ExtensionStore }); debugStore({ ExtensionStore });

View File

@@ -7,9 +7,6 @@ export interface IOptionsStore {
/** whether we should enable course status chips (indicator for waitlisted, cancelled, and closed courses) */ /** whether we should enable course status chips (indicator for waitlisted, cancelled, and closed courses) */
enableCourseStatusChips: boolean; enableCourseStatusChips: boolean;
/** whether we should enable course's time and location in the extension's popup */
enableTimeAndLocationInPopup: boolean;
/** whether we should automatically highlight conflicts on the course schedule page (adds a red strikethrough to courses that have conflicting times) */ /** whether we should automatically highlight conflicts on the course schedule page (adds a red strikethrough to courses that have conflicting times) */
enableHighlightConflicts: boolean; enableHighlightConflicts: boolean;
@@ -18,27 +15,45 @@ export interface IOptionsStore {
/** whether we should automatically refresh the data for the waitlist, course status, and other info with the latest data from UT's site */ /** whether we should automatically refresh the data for the waitlist, course status, and other info with the latest data from UT's site */
enableDataRefreshing: boolean; enableDataRefreshing: boolean;
/** whether we should open the calendar in a new tab; default is to focus an existing calendar tab */
alwaysOpenCalendarInNewTab: boolean;
/** whether the calendar sidebar should be shown when the calendar is opened */
showCalendarSidebar: boolean;
/** whether the promo should be shown */
showUTDiningPromo: boolean;
/** whether users are allowed to bypass the 10 schedule limit */
allowMoreSchedules: boolean;
} }
export const OptionsStore = createSyncStore<IOptionsStore>({ export const OptionsStore = createSyncStore<IOptionsStore>({
enableCourseStatusChips: false, enableCourseStatusChips: false,
enableTimeAndLocationInPopup: false,
enableHighlightConflicts: true, enableHighlightConflicts: true,
enableScrollToLoad: true, enableScrollToLoad: true,
enableDataRefreshing: true, enableDataRefreshing: false,
alwaysOpenCalendarInNewTab: false,
showCalendarSidebar: true,
showUTDiningPromo: true,
allowMoreSchedules: false,
}); });
/** /**
* Initializes the settings by retrieving the values from the OptionsStore. * Initializes the settings by retrieving the values from the OptionsStore.
* @returns {Promise<IOptionsStore>} A promise that resolves to an object satisfying the IOptionsStore interface. *
* @returns A promise that resolves to an object satisfying the IOptionsStore interface.
*/ */
export const initSettings = async () => export const initSettings = async () =>
({ ({
enableCourseStatusChips: await OptionsStore.get('enableCourseStatusChips'), enableCourseStatusChips: await OptionsStore.get('enableCourseStatusChips'),
enableTimeAndLocationInPopup: await OptionsStore.get('enableTimeAndLocationInPopup'),
enableHighlightConflicts: await OptionsStore.get('enableHighlightConflicts'), enableHighlightConflicts: await OptionsStore.get('enableHighlightConflicts'),
enableScrollToLoad: await OptionsStore.get('enableScrollToLoad'), enableScrollToLoad: await OptionsStore.get('enableScrollToLoad'),
enableDataRefreshing: await OptionsStore.get('enableDataRefreshing'), enableDataRefreshing: await OptionsStore.get('enableDataRefreshing'),
alwaysOpenCalendarInNewTab: await OptionsStore.get('alwaysOpenCalendarInNewTab'),
showCalendarSidebar: await OptionsStore.get('showCalendarSidebar'),
showUTDiningPromo: await OptionsStore.get('showUTDiningPromo'),
allowMoreSchedules: await OptionsStore.get('allowMoreSchedules'),
}) satisfies IOptionsStore; }) satisfies IOptionsStore;
// Clothing retailer right // Clothing retailer right

View File

@@ -5,6 +5,7 @@ export const CRX_PAGES = {
DEBUG: '/debug.html', DEBUG: '/debug.html',
CALENDAR: '/calendar.html', CALENDAR: '/calendar.html',
OPTIONS: '/options.html', OPTIONS: '/options.html',
MAP: '/map.html',
REPORT: '/report.html', REPORT: '/report.html',
} as const; } as const;

View File

@@ -0,0 +1,7 @@
/**
* Represents cached data with its fetch timestamp
*/
export type CachedData<T> = {
data: T;
dataFetched: number;
};

View File

@@ -25,3 +25,8 @@ export type sRGB = [r: number, g: number, b: number];
* Represents a Lab color value. * Represents a Lab color value.
*/ */
export type Lab = [l: number, a: number, b: number]; export type Lab = [l: number, a: number, b: number];
/**
* Represents a HSL color value.
*/
export type HSL = [h: number, s: number, l: number];

View File

@@ -44,7 +44,12 @@ export type Semester = {
export class Course { export class Course {
/** Every course has a uniqueId within UT's registrar system corresponding to each course section */ /** Every course has a uniqueId within UT's registrar system corresponding to each course section */
uniqueId!: number; uniqueId!: number;
/** This is the course number for a course, i.e CS 314 would be 314, MAL 306H would be 306H */ /**
* This is the course number for a course, i.e CS 314 would be 314, MAL 306H would be 306H.
* UT prefixes summer courses with f, s, n, or w:
* [f]irst term, [s]econd term, [n]ine week term, [w]hole term.
* So, the first term of PSY 301 over the summer would be 'f301'
*/
number!: string; number!: string;
/** The full name of the course, i.e. CS 314 Data Structures and Algorithms */ /** The full name of the course, i.e. CS 314 Data Structures and Algorithms */
fullName!: string; fullName!: string;
@@ -91,12 +96,53 @@ export class Course {
} }
this.colors = course.colors ? structuredClone(course.colors) : getCourseColors('emerald', 500); this.colors = course.colors ? structuredClone(course.colors) : getCourseColors('emerald', 500);
this.core = course.core ?? []; this.core = course.core ?? [];
if (course.semester.season === 'Summer') {
// A bug from and old version put the summer term in the course,
// so we need to handle that case
const { department, number } = Course.cleanSummerTerm(course.department, course.number);
this.department = department;
this.number = number;
}
}
/**
* Due to a bug in an older version, the summer term was included in the course department code,
* instead of the course number.
* UT prefixes summer courses with f, s, n, or w:
* [f]irst term, [s]econd term, [n]ine week term, [w]hole term
*
* @param department - The course department code, like 'C S'
* @param number - The course number, like '314H'
* @returns The properly formatted department and course number
* @example
* ```ts
* cleanSummerTerm('C S', '314H') // { department: 'C S', number: '314H' }
* cleanSummerTerm('P R', 'f378') // { department: 'P R', number: 'f378' }
* cleanSummerTerm('P R f', '378') // { department: 'P R', number: 'f378' }
* cleanSummerTerm('P S', 'n303') // { department: 'P S', number: 'n303' }
* cleanSummerTerm('P S n', '303') // { department: 'P S', number: 'n303' }
* ```
*/
static cleanSummerTerm(department: string, number: string): { department: string; number: string } {
// UT prefixes summer courses with f, s, n, or w:
// [f]irst term, [s]econd term, [n]ine week term, [w]hole term
const summerTerm = department.match(/[fsnw]$/);
if (!summerTerm) {
return { department, number };
}
return {
department: department.slice(0, -1).trim(),
number: summerTerm[0] + number,
};
} }
/** /**
* Gets a list of all the conflicts between this course and another course (i.e. if they have a meeting at the same time) * Gets a list of all the conflicts between this course and another course (i.e. if they have a meeting at the same time)
* @param other another course to compare this course to *
* @returns a list of all the conflicts between this course and the other course as a tuple of the two conflicting meetings * @param other - Another course to compare this course to
* @returns A list of all the conflicts between this course and the other course as a tuple of the two conflicting meetings
*/ */
getConflicts(other: Course): [CourseMeeting, CourseMeeting][] { getConflicts(other: Course): [CourseMeeting, CourseMeeting][] {
const conflicts: [CourseMeeting, CourseMeeting][] = []; const conflicts: [CourseMeeting, CourseMeeting][] = [];
@@ -110,6 +156,18 @@ export class Course {
return conflicts; return conflicts;
} }
/**
* @returns The course number without the summer term
* @example
* ```ts
* const c = new Course({ number: 'f301', ... });
* c.getNumberWithoutTerm() // '301'
* ```
*/
getNumberWithoutTerm(): string {
return this.number.replace(/^\D/, ''); // Remove nondigit at start, if it exists
}
} }
/** /**

View File

@@ -48,13 +48,13 @@ export class CourseMeeting {
/** /**
* Whether or not this meeting conflicts with another meeting * Whether or not this meeting conflicts with another meeting
* MWF 10:00 am - 11:00 am conflicts with a F 10:00 am - 10:30 am *
* @param meeting the meeting to check for conflicts with * @remarks MWF 10:00 am - 11:00 am conflicts with a F 10:00 am - 10:30 am
* @returns true if the given meeting conflicts with this meeting, false otherwise * @param meeting - The meeting to check for conflicts with
* @returns True if the given meeting conflicts with this meeting, false otherwise
*/ */
isConflicting(meeting: CourseMeeting): boolean { isConflicting({ days: otherDays, startTime: otherStartTime, endTime: otherEndTime }: CourseMeeting): boolean {
const { days, startTime, endTime } = this; const { days, startTime, endTime } = this;
const { days: otherDays, startTime: otherStartTime, endTime: otherEndTime } = meeting;
const hasDayConflict = days.some(day => otherDays.includes(day)); const hasDayConflict = days.some(day => otherDays.includes(day));
const hasTimeConflict = startTime < otherEndTime && endTime > otherStartTime; const hasTimeConflict = startTime < otherEndTime && endTime > otherStartTime;
@@ -64,17 +64,17 @@ export class CourseMeeting {
/** /**
* Return the string representation of the days of the week that this meeting is taught * Return the string representation of the days of the week that this meeting is taught
* @param options options for the string representation *
* @returns string representation of the days of the week that this meeting is taught * @param options - Options for the string representation
* @returns String representation of the days of the week that this meeting is taught
*/ */
getDaysString(options: DaysStringOptions): string { getDaysString({ format, separator }: DaysStringOptions): string {
let { format, separator } = options;
let { days } = this; let { days } = this;
if (format === 'short') { if (format === 'short') {
days = Object.keys(DAY_MAP).filter(day => days.includes(DAY_MAP[day as keyof typeof DAY_MAP])) as Day[]; days = Object.keys(DAY_MAP).filter(day => days.includes(DAY_MAP[day as keyof typeof DAY_MAP])) as Day[];
} }
if (separator === 'none') { if (!separator) {
return days.join(''); return days.join('');
} }
const listFormat = new Intl.ListFormat('en-US', { const listFormat = new Intl.ListFormat('en-US', {
@@ -86,10 +86,11 @@ export class CourseMeeting {
/** /**
* Return the string representation of the time range for the course * Return the string representation of the time range for the course
* @param options options for the string representation *
* @returns string representation of the time range for the course * @param options - Options for the string representation
* @returns String representation of the time range for the course
*/ */
getTimeString(options: TimeStringOptions): string { getTimeString({ separator = '-' }: TimeStringOptions): string {
const { startTime, endTime } = this; const { startTime, endTime } = this;
const startHour = Math.floor(startTime / 60); const startHour = Math.floor(startTime / 60);
const startMinute = startTime % 60; const startMinute = startTime % 60;
@@ -108,7 +109,7 @@ export class CourseMeeting {
} }
startTimeString += startMinute === 0 ? ':00' : `:${startMinute}`; startTimeString += startMinute === 0 ? ':00' : `:${startMinute}`;
startTimeString += startHour >= 12 ? ' p.m.' : ' a.m.'; startTimeString += startHour >= 12 ? 'pm' : 'am';
if (endHour === 0) { if (endHour === 0) {
endTimeString = '12'; endTimeString = '12';
@@ -117,15 +118,11 @@ export class CourseMeeting {
} else { } else {
endTimeString = `${endHour}`; endTimeString = `${endHour}`;
} }
endTimeString += endMinute === 0 ? ':00' : `:${endMinute}`; endTimeString += endMinute === 0 ? ':00' : `:${endMinute}`;
endTimeString += endHour >= 12 ? ' p.m.' : ' a.m.'; endTimeString += endHour >= 12 ? 'pm' : 'am';
if (options.capitalize) { return `${startTimeString} ${separator} ${endTimeString}`;
startTimeString = startTimeString.toUpperCase();
endTimeString = endTimeString.toUpperCase();
}
return `${startTimeString} ${options.separator} ${endTimeString}`;
} }
} }
@@ -135,8 +132,6 @@ export class CourseMeeting {
type TimeStringOptions = { type TimeStringOptions = {
/** the separator between the start and end times */ /** the separator between the start and end times */
separator: string; separator: string;
/** capitalizes the AM/PM */
capitalize?: boolean;
}; };
/** /**
@@ -156,5 +151,5 @@ type DaysStringOptions = {
* *
* 'narrow' = `Monday Wednesday Friday` * 'narrow' = `Monday Wednesday Friday`
*/ */
separator: Intl.ListFormatStyle | 'none'; separator?: Intl.ListFormatStyle;
}; };

View File

@@ -20,9 +20,10 @@ export class CourseSchedule {
/** /**
* Given a string representation of the meeting information for a class, parse it into a CourseMeeting object * Given a string representation of the meeting information for a class, parse it into a CourseMeeting object
* @param dayLine a string representation of the days of the week that the course is taught: MWF, TR, etc. *
* @param timeLine a string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc. * @param dayLine - A string representation of the days of the week that the course is taught: MWF, TR, etc.
* @param locLine a string representation of the location that the course is taught in: JGB 2.302, etc. * @param timeLine - A string representation of a time-range that the course is taught: 10:00 am - 11:00 am, 1:00 pm - 2:00 pm, etc.
* @param locLine - A string representation of the location that the course is taught in: JGB 2.302, etc.
* @returns CourseMeeting object representing the meeting information * @returns CourseMeeting object representing the meeting information
*/ */
static parse(dayLine: string, timeLine: string, locLine: string): CourseMeeting { static parse(dayLine: string, timeLine: string, locLine: string): CourseMeeting {

View File

@@ -16,57 +16,44 @@ export default class Instructor {
}); });
} }
/**
* Get the URL to the instructor's directory page on the UT Directory website
* @returns a URL string to the instructor's directory page
*/
getDirectoryUrl(): string {
const name = this.toString({
format: 'full_name',
case: 'capitalize',
});
const url = new URL('https://directory.utexas.edu/index.php');
url.searchParams.set('q', name);
url.searchParams.set('scope', 'faculty/staff');
url.searchParams.set('submit', 'Search');
return url.toString();
}
/** /**
* Get a string representation of the instructor * Get a string representation of the instructor
* @param options the options for how to format the instructor string *
* @returns a string representation of the instructor * @param options - The options for how to format the instructor string
* @returns A string representation of the instructor
*/ */
toString(options: InstructorFormatOptions): string { toString(options: InstructorFormatOptions): string {
const { firstName, lastName, fullName } = this; const { firstName, lastName, fullName } = this;
const { format, case: caseType } = options; const { format } = options;
const process = (str: string) => { switch (format) {
if (caseType === 'lowercase') { case 'first_last':
return str.toLowerCase(); if (firstName && lastName) {
} return `${capitalize(firstName)} ${capitalize(lastName)}`;
if (caseType === 'uppercase') { }
return str.toUpperCase();
}
return capitalize(str);
};
if (format === 'abbr' && firstName && lastName && firstName[0]) { if (lastName) {
return `${process(firstName[0])}. ${process(lastName)}`; return capitalize(lastName);
} }
if (format === 'full_name' && fullName) {
return process(fullName);
}
if (format === 'first_last' && firstName && lastName) {
return `${process(firstName)} ${process(lastName)}`;
}
if (format === 'last' && lastName) {
return process(lastName);
}
throw new Error(`Invalid Instructor String format: ${format}`); if (fullName) {
return fullName;
}
return '';
case 'last':
if (lastName) {
return capitalize(lastName);
}
if (fullName) {
return fullName;
}
return '';
default:
throw new Error(`Invalid Instructor String format: ${format}`);
}
} }
} }
@@ -75,9 +62,5 @@ export default class Instructor {
*/ */
type InstructorFormatOptions = { type InstructorFormatOptions = {
/** How do you want the names of the professors formatted */ /** How do you want the names of the professors formatted */
format: 'abbr' | 'first_last' | 'last' | 'full_name'; format: 'first_last' | 'last';
/**
* What the case of the string should be
*/
case: 'capitalize' | 'lowercase' | 'uppercase';
}; };

View File

@@ -17,4 +17,9 @@ const MIMEType = {
*/ */
export type MIMETypeKey = keyof typeof MIMEType; export type MIMETypeKey = keyof typeof MIMEType;
/**
* Represents a value of the MIMEType object
*/
export type MIMETypeValue = (typeof MIMEType)[MIMETypeKey];
export default MIMEType; export default MIMEType;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/**
* An object representing various spacing values used throughout the application.
* Each key corresponds to a specific spacing size based on the 4px grid system.
*/
export const spacing = {
'spacing-1': '0.125rem',
'spacing-2': '0.25rem',
'spacing-3': '0.5rem',
'spacing-4': '0.75rem',
'spacing-5': '1rem',
'spacing-6': '1.25rem',
'spacing-7': '1.5rem',
'spacing-8': '2rem',
} as const;
type SpacingKey = keyof typeof spacing;
/**
* Converts a spacing value from rem to pixels
* @param key - The spacing key to convert
* @returns The spacing value in pixels
*/
export function getSpacingInPx(key: SpacingKey): number {
const remValue = parseFloat(spacing[key]);
return remValue * 16; // 1rem = 16px
}

View File

@@ -14,11 +14,17 @@ export const colors = {
blue: '#005F86', blue: '#005F86',
gray: '#9CADB7', gray: '#9CADB7',
offwhite: '#D6D2C4', offwhite: '#D6D2C4',
concrete: '#95A5A6',
}, },
theme: { theme: {
red: '#D10000', red: '#D10000',
black: '#1A2024', black: '#1A2024',
offwhite: '#D6D2C4',
black1: '#333F4850',
black2: '#333F4820',
staticwhite: '#FFFFFF',
staticblack: '#1A2024',
majorgridline: '#D1D5DB',
minorgridline: '#F3F4F6',
}, },
} as const satisfies Record<string, Record<string, string>>; } as const satisfies Record<string, Record<string, string>>;
@@ -37,7 +43,7 @@ export const extendedColors = {
d: '#DC2626', d: '#DC2626',
dminus: '#B91C1C', dminus: '#B91C1C',
f: '#B91C1C', f: '#B91C1C',
other: '#6B7280', other: '#D6D3D1',
}, },
} as const; } as const;

View File

@@ -0,0 +1,57 @@
import { describe, expect, it } from 'vitest';
import { Course } from '../Course';
describe('Course::cleanSummerTerm', () => {
it("shouldn't affect already cleaned summer terms", () => {
const inputs = [
['C S', '314H'],
['P R', 'f378'],
['P S', 'f303'],
['WGS', 's301'],
['S W', 'n360K'],
['GOV', 'w312L'],
['J', 's311F'],
['J S', '311F'],
] as const;
const expected = [
{ department: 'C S', number: '314H' },
{ department: 'P R', number: 'f378' },
{ department: 'P S', number: 'f303' },
{ department: 'WGS', number: 's301' },
{ department: 'S W', number: 'n360K' },
{ department: 'GOV', number: 'w312L' },
{ department: 'J', number: 's311F' },
{ department: 'J S', number: '311F' },
];
const results = inputs.map(input => Course.cleanSummerTerm(input[0], input[1]));
expect(results).toEqual(expected);
});
it('should move summer term indicator to course number', () => {
const inputs = [
['P R f', '378'],
['P S f', '303'],
['WGS s', '301'],
['S W n', '360K'],
['GOV w', '312L'],
['J s', '311F'],
['J S', '311F'],
] as const;
const expected = [
{ department: 'P R', number: 'f378' },
{ department: 'P S', number: 'f303' },
{ department: 'WGS', number: 's301' },
{ department: 'S W', number: 'n360K' },
{ department: 'GOV', number: 'w312L' },
{ department: 'J', number: 's311F' },
{ department: 'J S', number: '311F' },
];
const results = inputs.map(input => Course.cleanSummerTerm(input[0], input[1]));
expect(results).toEqual(expected);
});
});

View File

@@ -0,0 +1,19 @@
/**
* This file contains URLs for external applications and resources.
* Centralizing these URLs makes it easier to track, update, and manage them in a single place.
*/
/**
* URL to the UT Dining app on the App Store
*/
export const UT_DINING_APP_STORE_URL = new URL('https://apps.apple.com/us/app/ut-dining/id6743042002').toString();
/**
* URL to the UT Dining app on the Google Play Store (currently not available)
*/
export const UT_DINING_GOOGLE_PLAY_URL = ''; // Placeholder for Google Play URL, Android app not available yet
/**
* URL to the promo image
*/
export const UT_DINING_PROMO_IMAGE_URL = new URL('https://cdn.longhorns.dev/ut-dining-advert1.png').toString();

View File

@@ -1,11 +1,11 @@
import type { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
import { theme } from 'unocss/preset-mini'; import { theme } from 'unocss/preset-mini';
import type { HexColor, Lab, RGB, sRGB } from '../types/Color'; import type { HexColor, HSL, Lab, RGB, sRGB } from '../types/Color';
import { isHexColor } from '../types/Color'; import { isHexColor } from '../types/Color';
import type { Course } from '../types/Course'; import type { Course } from '../types/Course';
import type { CourseColors, TWColorway, TWIndex } from '../types/ThemeColors'; import type { CourseColors, TWColorway, TWIndex } from '../types/ThemeColors';
import { colorwayIndexes } from '../types/ThemeColors'; import { colors, colorwayIndexes } from '../types/ThemeColors';
import type { UserSchedule } from '../types/UserSchedule'; import type { UserSchedule } from '../types/UserSchedule';
/** /**
@@ -26,6 +26,19 @@ export function hexToRGB(hex: HexColor): RGB | undefined {
return [parseInt(result[1]!, 16), parseInt(result[2]!, 16), parseInt(result[3]!, 16)]; return [parseInt(result[1]!, 16), parseInt(result[2]!, 16), parseInt(result[3]!, 16)];
} }
/**
* Checks if a given string is a valid hex color.
*
* A valid hex color is a string that starts with a '#' followed by either
* 3 or 6 hexadecimal characters (0-9, A-F, a-f).
*
* @param hex - The hex color string to validate.
* @returns True if the string is a valid hex color, false otherwise.
*/
export function isValidHexColor(hex: string): boolean {
return /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(hex);
}
export const useableColorways = Object.keys(theme.colors) export const useableColorways = Object.keys(theme.colors)
// check that the color is a colorway (is an object) // check that the color is a colorway (is an object)
.filter(color => typeof theme.colors[color as keyof typeof theme.colors] === 'object') .filter(color => typeof theme.colors[color as keyof typeof theme.colors] === 'object')
@@ -33,7 +46,9 @@ export const useableColorways = Object.keys(theme.colors)
/** /**
* Generate a Tailwind classname for the font color based on the background color * Generate a Tailwind classname for the font color based on the background color
* @param bgColor the hex color of the background *
* @param bgColor - The hex color of the background
* @returns The Tailwind classname for the font color
*/ */
export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' | 'text-theme-black' { export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' | 'text-theme-black' {
const coefficients = [0.2126729, 0.7151522, 0.072175] as const; const coefficients = [0.2126729, 0.7151522, 0.072175] as const;
@@ -54,9 +69,20 @@ export function pickFontColor(bgColor: HexColor): 'text-white' | 'text-black' |
return Ys < 0.365 ? 'text-black' : 'text-theme-black'; return Ys < 0.365 ? 'text-black' : 'text-theme-black';
} }
// Mapping of Tailwind CSS class names to their corresponding hex values
export const tailwindColorMap: Record<string, HexColor> = {
'text-white': '#FFFFFF',
'text-black': '#000000',
'text-theme-black': colors.theme.black,
};
/** /**
* Get primary and secondary colors from a Tailwind colorway * Get primary and secondary colors from a Tailwind colorway
* @param colorway the Tailwind colorway ex. "emerald" *
* @param colorway - The Tailwind colorway ex. "emerald"
* @param index - The index of the primary color in the colorway
* @param offset - The offset to get the secondary color
* @returns The primary and secondary colors
*/ */
export function getCourseColors(colorway: TWColorway, index?: number, offset: number = 300): CourseColors { export function getCourseColors(colorway: TWColorway, index?: number, offset: number = 300): CourseColors {
if (index === undefined) { if (index === undefined) {
@@ -76,10 +102,15 @@ export function getCourseColors(colorway: TWColorway, index?: number, offset: nu
* @param color - The hexadecimal color value. * @param color - The hexadecimal color value.
* @returns The Tailwind colorway. * @returns The Tailwind colorway.
*/ */
export function getColorwayFromColor(color: HexColor): TWColorway { export function getColorwayFromColor(color: HexColor): {
colorway: TWColorway;
index: TWIndex;
} {
for (const colorway of useableColorways) { for (const colorway of useableColorways) {
if (Object.values(theme.colors[colorway]).includes(color)) { const colorValues = Object.values(theme.colors[colorway]);
return colorway as TWColorway; const index = colorValues.indexOf(color);
if (index !== -1) {
return { colorway: colorway as TWColorway, index: (index * 100) as TWIndex };
} }
} }
@@ -115,10 +146,159 @@ export function getColorwayFromColor(color: HexColor): TWColorway {
return getColorwayFromColor(closestColor); return getColorwayFromColor(closestColor);
} }
/**
* Converts a hexadecimal color value to HSL (Hue, Saturation, Lightness) format
*
* @param hex - The hexadecimal color string
* @returns An array of [hue (0-360), saturation (0-100), lightness (0-100)]
* @throws If the hex color cannot be converted to RGB
*/
export const hexToHSL = (hex: HexColor): HSL => {
const rgb = hexToRGB(hex);
if (!rgb) {
throw new Error('hexToRGB returned undefined');
}
// Convert RGB to decimals
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
// Find min/max/delta
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const delta = max - min;
// Calculate HSL values
let h = 0;
let s = 0;
let l = (max + min) / 2;
if (delta !== 0) {
// Calculate saturation
s = delta / (1 - Math.abs(2 * l - 1));
// Calculate hue
if (max === r) {
h = ((g - b) / delta) % 6;
} else if (max === g) {
h = (b - r) / delta + 2;
} else {
h = (r - g) / delta + 4;
}
h *= 60;
}
// Normalize values
h = Math.round(h < 0 ? h + 360 : h);
s = Math.round(s * 100);
l = Math.round(l * 100);
return [h, s, l];
};
/**
* Converts an HSL color value to RGB format.
*
* @param hsl - The HSL color value
* @returns An RGB color value
*/
function hslToRGB([hue, saturation, lightness]: HSL): RGB {
// Convert percentages to decimals
const s = saturation / 100;
const l = lightness / 100;
// Calculate intermediate values
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));
const m = l - c / 2;
let r = 0;
let g = 0;
let b = 0;
// Determine RGB values based on hue
if (hue >= 0 && hue < 60) {
[r, g, b] = [c, x, 0];
} else if (hue >= 60 && hue < 120) {
[r, g, b] = [x, c, 0];
} else if (hue >= 120 && hue < 180) {
[r, g, b] = [0, c, x];
} else if (hue >= 180 && hue < 240) {
[r, g, b] = [0, x, c];
} else if (hue >= 240 && hue < 300) {
[r, g, b] = [x, 0, c];
} else {
[r, g, b] = [c, 0, x];
}
// Convert to 0-255 range and round
return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];
}
/**
* Returns a darker shade of the given hex color by reducing the lightness in HSL color space.
*
* @param color - The hexadecimal color value to darken.
* @param offset - The percentage to reduce the lightness by (default is 20).
* @returns The darker shade of the given hex color.
* @throws If the provided color is not a valid hex color.
*/
export function getDarkerShade(color: HexColor, offset: number = 20): HexColor {
const rgb = hexToRGB(color);
if (!rgb) {
throw new Error('color: Invalid hex.');
}
// Convert to HSL
const [h, s, l] = hexToHSL(color);
// Reduce lightness by offset percentage, ensuring it doesn't go below 0
const newL = Math.max(0, l - offset);
// Convert back to RGB
const newRGB = hslToRGB([h, s, newL]);
// Convert to hex
return `#${newRGB.map(c => Math.round(c).toString(16).padStart(2, '0')).join('')}`;
}
/**
* Returns a lighter shade of the given hex color by increasing the lightness in HSL color space.
*
* @param color - The hexadecimal color value to lighten.
* @param offset - The percentage to increase the lightness by (default is 20).
* @returns The lighter shade of the given hex color.
* @throws If the provided color is not a valid hex color.
*/
export function getLighterShade(color: HexColor, offset: number = 20): HexColor {
const rgb = hexToRGB(color);
if (!rgb) {
throw new Error('color: Invalid hex.');
}
// Convert to HSL
const [h, s, l] = hexToHSL(color);
// Increase lightness by offset percentage, ensuring it doesn't go above 100
const newL = Math.min(100, l + offset);
// Convert back to RGB
const newRGB = hslToRGB([h, s, newL]);
// Convert to hex
return `#${newRGB.map(c => Math.round(c).toString(16).padStart(2, '0')).join('')}`;
}
/** /**
* Get next unused color in a tailwind colorway for a given schedule * Get next unused color in a tailwind colorway for a given schedule
* @param schedule the schedule which the course is in *
* @param course the course to get the color for * @param schedule - The schedule which the course is in
* @param course - The course to get the color for
* @param index - The index of the primary color in the colorway
* @param offset - The offset to get the secondary color
* @returns The primary and secondary colors
*/ */
export function getUnusedColor( export function getUnusedColor(
schedule: Serialized<UserSchedule>, schedule: Serialized<UserSchedule>,
@@ -143,17 +323,28 @@ export function getUnusedColor(
const scheduleCourses = schedule.courses.map(c => ({ const scheduleCourses = schedule.courses.map(c => ({
...c, ...c,
colorway: getColorwayFromColor(c.colors.primaryColor), theme: (() => {
try {
return getColorwayFromColor(c.colors.primaryColor);
} catch (error) {
// Default to emerald colorway with index 500
return {
colorway: 'emerald' as TWColorway,
index: 500 as TWIndex,
};
}
})(),
})); }));
const usedColorways = new Set(scheduleCourses.map(c => c.colorway));
const usedColorways = new Set(scheduleCourses.map(c => c.theme.colorway));
const availableColorways = new Set(useableColorways.filter(c => !usedColorways.has(c))); const availableColorways = new Set(useableColorways.filter(c => !usedColorways.has(c)));
if (availableColorways.size > 0) { if (availableColorways.size > 0) {
let sameDepartment = scheduleCourses.filter(c => c.department === course.department); let sameDepartment = scheduleCourses.filter(c => c.department === course.department);
sameDepartment.sort((a, b) => { sameDepartment.sort((a, b) => {
const aIndex = useableColorways.indexOf(a.colorway); const aIndex = useableColorways.indexOf(a.theme.colorway);
const bIndex = useableColorways.indexOf(b.colorway); const bIndex = useableColorways.indexOf(b.theme.colorway);
return aIndex - bIndex; return aIndex - bIndex;
}); });
@@ -162,8 +353,8 @@ export function getUnusedColor(
// check to see if any adjacent colorways are available // check to see if any adjacent colorways are available
const centerCourse = sameDepartment[Math.floor(Math.random() * sameDepartment.length)]!; const centerCourse = sameDepartment[Math.floor(Math.random() * sameDepartment.length)]!;
let nextColorway = getNextColorway(centerCourse.colorway); let nextColorway = getNextColorway(centerCourse.theme.colorway);
let prevColorway = getPreviousColorway(centerCourse.colorway); let prevColorway = getPreviousColorway(centerCourse.theme.colorway);
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
@@ -221,8 +412,9 @@ function rgbToSrgb(rgb: RGB): sRGB {
/** /**
* Convert an RGB color to the OKLab color space * Convert an RGB color to the OKLab color space
* @param rgb the RGB color *
* @returns the color in the OKLab color space * @param rgb - The RGB color
* @returns The color in the OKLab color space
*/ */
function srgbToOKlab([r, g, b]: sRGB): Lab { function srgbToOKlab([r, g, b]: sRGB): Lab {
let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b; let l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;

View File

@@ -3,9 +3,10 @@ import MIMEType from '../types/MIMEType';
/** /**
* Downloads a blob by creating a temporary URL and triggering a download. * Downloads a blob by creating a temporary URL and triggering a download.
* @param blobPart The blob data to be downloaded. *
* @param type The MIME type of the blob. * @param blobPart - The blob data to be downloaded.
* @param fileName The name of the file to be downloaded. * @param type - The MIME type of the blob.
* @param fileName - The name of the file to be downloaded.
* @returns A promise that resolves when the download is successful, or rejects with an error if the download fails. * @returns A promise that resolves when the download is successful, or rejects with an error if the download fails.
*/ */
export function downloadBlob(blobPart: BlobPart, type: MIMETypeKey, fileName: string): Promise<void> { export function downloadBlob(blobPart: BlobPart, type: MIMETypeKey, fileName: string): Promise<void> {

View File

@@ -1,27 +1,29 @@
import { ClockUser, LockKey, Prohibit } from '@phosphor-icons/react';
import type { StatusType } from '@shared/types/Course'; import type { StatusType } from '@shared/types/Course';
import { Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import type { SVGProps } from 'react'; import type { SVGProps } from 'react';
import React from 'react'; import React from 'react';
import ClosedIcon from '~icons/material-symbols/lock'; interface StatusIconProps extends SVGProps<SVGSVGElement> {
import WaitlistIcon from '~icons/material-symbols/timelapse'; status: StatusType;
import CancelledIcon from '~icons/material-symbols/warning'; }
/** /**
* Get Icon component based on status * Get Icon component based on status
* @param props.status status *
* @returns the icon component * @param props - The props for the StatusIcon component
* @returns The rendered icon component
*/ */
export function StatusIcon(props: SVGProps<SVGSVGElement> & { status: StatusType }): JSX.Element | null { export function StatusIcon(props: StatusIconProps): JSX.Element | null {
const { status, ...rest } = props; const { status, ...rest } = props;
switch (status) { switch (status) {
case Status.WAITLISTED: case Status.WAITLISTED:
return <WaitlistIcon {...rest} />; return <ClockUser weight='fill' {...rest} />;
case Status.CLOSED: case Status.CLOSED:
return <ClosedIcon {...rest} />; return <LockKey weight='fill' {...rest} />;
case Status.CANCELLED: case Status.CANCELLED:
return <CancelledIcon {...rest} />; return <Prohibit weight='fill' {...rest} />;
default: default:
return null; return null;
} }

View File

@@ -3,7 +3,7 @@ import { customAlphabet } from 'nanoid';
/** /**
* Generate secure URL-friendly unique ID. * Generate secure URL-friendly unique ID.
* *
* @param size Size of the ID. The default size is 12. * @param size - Size of the ID. The default size is 12.
* @returns A random string. * @returns A random string.
*/ */
export const generateRandomId = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 12); export const generateRandomId = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 12);

View File

@@ -1,6 +1,7 @@
/** /**
* Given a string, returns a string with the first letter capitalized. * Given a string, returns a string with the first letter capitalized.
* @input The string to capitalize. *
* @param input - The string to capitalize.
*/ */
export function capitalize(input: string): string { export function capitalize(input: string): string {
let capitalized = ''; let capitalized = '';
@@ -25,8 +26,9 @@ export function capitalize(input: string): string {
/** /**
* Given a string, returns a string with the first letter capitalized. * Given a string, returns a string with the first letter capitalized.
* @param input capitalize the first letter of this string *
* @returns the string with the first letter capitalized * @param input - Capitalize the first letter of this string
* @returns The string with the first letter capitalized
*/ */
export function capitalizeFirstLetter(input: string): string { export function capitalizeFirstLetter(input: string): string {
return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase(); return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
@@ -34,8 +36,9 @@ export function capitalizeFirstLetter(input: string): string {
/** /**
* Cuts the input string to the specified length and adds an ellipsis if the string is longer than the specified length. * Cuts the input string to the specified length and adds an ellipsis if the string is longer than the specified length.
* @param input The string to ellipsify. *
* @param length The length of the string to return. * @param input - The string to ellipsify.
* @param length - The length of the string to return.
* @returns The ellipsified string. * @returns The ellipsified string.
*/ */
export const ellipsify = (input: string, chars: number): string => { export const ellipsify = (input: string, chars: number): string => {
@@ -45,3 +48,22 @@ export const ellipsify = (input: string, chars: number): string => {
} }
return ellipisifed; return ellipisifed;
}; };
/**
* Stringifies a list of items in English format.
*
* @param items - The list of items to stringify.
* @returns A string representation of the list in English format.
* @example
* englishStringifyList([]) // ''
* englishStringifyList(['Alice']) // 'Alice'
* englishStringifyList(['Alice', 'Bob']) // 'Alice and Bob'
* englishStringifyList(['Alice', 'Bob', 'Charlie']) // 'Alice, Bob, and Charlie'
*/
export const englishStringifyList = (items: readonly string[]): string => {
if (items.length === 0) return '';
if (items.length === 1) return items[0]!;
if (items.length === 2) return `${items[0]} and ${items[1]}`;
return `${items.slice(0, -1).join(', ')}, and ${items.at(-1)}`;
};

View File

@@ -0,0 +1,112 @@
import { getLighterShade, hexToHSL, isValidHexColor } from '@shared/util/colors';
import { describe, expect, it } from 'vitest';
describe('hexToHSL', () => {
it('should convert pure red to HSL', () => {
const result = hexToHSL('#FF0000');
expect(result).toEqual([0, 100, 50]);
});
it('should convert pure green to HSL', () => {
const result = hexToHSL('#00FF00');
expect(result).toEqual([120, 100, 50]);
});
it('should convert pure blue to HSL', () => {
const result = hexToHSL('#0000FF');
expect(result).toEqual([240, 100, 50]);
});
it('should convert white to HSL', () => {
const result = hexToHSL('#FFFFFF');
expect(result).toEqual([0, 0, 100]);
});
it('should convert black to HSL', () => {
const result = hexToHSL('#000000');
expect(result).toEqual([0, 0, 0]);
});
it('should convert UT burnt orange to HSL', () => {
const result = hexToHSL('#BF5700');
expect(result).toEqual([27, 100, 37]);
});
it('should convert gray to HSL', () => {
const result = hexToHSL('#808080');
expect(result).toEqual([0, 0, 50]);
});
it('should throw error for invalid hex color', () => {
expect(() => hexToHSL('#GGGGGG')).toThrow('hexToRGB returned undefined');
});
});
describe('isValidHexColor', () => {
it('should validate 6-digit hex colors with hash', () => {
expect(isValidHexColor('#000000')).toBe(true);
expect(isValidHexColor('#FFFFFF')).toBe(true);
expect(isValidHexColor('#BF5700')).toBe(true);
expect(isValidHexColor('#D6D2C4')).toBe(true);
});
it('should validate 6-digit hex colors without hash', () => {
expect(isValidHexColor('000000')).toBe(true);
expect(isValidHexColor('FFFFFF')).toBe(true);
expect(isValidHexColor('BF5700')).toBe(true);
});
it('should validate 3-digit hex colors with hash', () => {
expect(isValidHexColor('#000')).toBe(true);
expect(isValidHexColor('#FFF')).toBe(true);
expect(isValidHexColor('#F0F')).toBe(true);
});
it('should validate 3-digit hex colors without hash', () => {
expect(isValidHexColor('000')).toBe(true);
expect(isValidHexColor('FFF')).toBe(true);
expect(isValidHexColor('F0F')).toBe(true);
});
it('should reject invalid hex colors', () => {
expect(isValidHexColor('#')).toBe(false);
expect(isValidHexColor('#GGG')).toBe(false);
expect(isValidHexColor('#GGGGGG')).toBe(false);
expect(isValidHexColor('GGGGGG')).toBe(false);
expect(isValidHexColor('#12345')).toBe(false);
expect(isValidHexColor('#1234567')).toBe(false);
expect(isValidHexColor('not a color')).toBe(false);
expect(isValidHexColor('')).toBe(false);
});
});
describe('getLighterShade', () => {
it('should lighten a color by default offset (20%)', () => {
const result = getLighterShade('#BF5700');
expect(result).toBe('#ff8624');
});
it('should lighten black correctly', () => {
const result = getLighterShade('#000000');
expect(result).toBe('#333333');
});
it('should not exceed 100% lightness', () => {
const result = getLighterShade('#FFFFFF', 20);
expect(result).toBe('#ffffff');
});
it('should handle custom offset values', () => {
const result = getLighterShade('#BF5700', 40);
expect(result).toBe('#ffbe8a');
});
it('should maintain hue while increasing lightness', () => {
const result = getLighterShade('#00FF00', 20); // Pure green
expect(result.toLowerCase()).toBe('#66ff66');
});
it('should throw error for invalid hex color', () => {
expect(() => getLighterShade('#GGGGGG')).toThrow('color: Invalid hex.');
});
});

View File

@@ -1,4 +1,4 @@
import { capitalize, capitalizeFirstLetter, ellipsify } from '@shared/util/string'; import { capitalize, capitalizeFirstLetter, ellipsify, englishStringifyList } from '@shared/util/string';
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
// TODO: Fix `string.ts` and `string.test.ts` to make the tests pass // TODO: Fix `string.ts` and `string.test.ts` to make the tests pass
@@ -54,3 +54,49 @@ describe('ellipsify', () => {
expect(ellipsify('', 5)).toBe(''); expect(ellipsify('', 5)).toBe('');
}); });
}); });
describe('englishStringifyList', () => {
it('should handle an empty array', () => {
const data = [] satisfies string[];
const result = englishStringifyList(data);
const expected = '';
expect(result).toBe(expected);
});
it('should handle 1 element', () => {
const data = ['Alice'] satisfies string[];
const result = englishStringifyList(data);
const expected = 'Alice';
expect(result).toBe(expected);
});
it('should handle 2 elements', () => {
const data = ['Alice', 'Bob'] satisfies string[];
const result = englishStringifyList(data);
const expected = 'Alice and Bob';
expect(result).toBe(expected);
});
it('should handle 3 elements', () => {
const data = ['Alice', 'Bob', 'Charlie'] satisfies string[];
const result = englishStringifyList(data);
const expected = 'Alice, Bob, and Charlie';
expect(result).toBe(expected);
});
it('should handle n elements', () => {
const testcases = [
{ data: [], expected: '' },
{ data: ['foo'], expected: 'foo' },
{ data: ['foo', 'bar'], expected: 'foo and bar' },
{ data: ['foo', 'bar', 'baz'], expected: 'foo, bar, and baz' },
{ data: ['a', 'b', 'c', 'd'], expected: 'a, b, c, and d' },
{ data: 'abcdefghijk'.split(''), expected: 'a, b, c, d, e, f, g, h, i, j, and k' },
] satisfies { data: string[]; expected: string }[];
for (const { data, expected } of testcases) {
const result = englishStringifyList(data);
expect(result).toBe(expected);
}
});
});

View File

@@ -3,11 +3,13 @@ import { describe, expect, it } from 'vitest';
describe('sleep', () => { describe('sleep', () => {
it('should resolve after the specified number of milliseconds', async () => { it('should resolve after the specified number of milliseconds', async () => {
const start = Date.now(); const start = performance.now();
const milliseconds = 1000; const milliseconds = 1000;
await sleep(milliseconds); await sleep(milliseconds);
const end = Date.now(); const end = performance.now();
const elapsed = end - start; const elapsed = end - start;
expect(elapsed).toBeGreaterThanOrEqual(milliseconds); // Flaky test due to JS's lack of precision in setTimeout,
// so we allow for a 1ms difference
expect(elapsed).toBeGreaterThanOrEqual(milliseconds - 1);
}); });
}); });

View File

@@ -6,6 +6,7 @@ export const DAY = 24 * HOUR;
/** /**
* Pauses the execution for the specified number of milliseconds. * Pauses the execution for the specified number of milliseconds.
*
* @param milliseconds - The number of milliseconds to sleep. * @param milliseconds - The number of milliseconds to sleep.
* @returns A promise that resolves after the specified number of milliseconds. * @returns A promise that resolves after the specified number of milliseconds.
*/ */
@@ -14,8 +15,8 @@ export const sleep = (milliseconds: number): Promise<void> => new Promise(resolv
/** /**
* Checks to see if expired by the time first stored and the time frame that it is stored for * Checks to see if expired by the time first stored and the time frame that it is stored for
* *
* @param time time it was stored * @param time - time it was stored
* @param threshold time frame it can be stored for * @param threshold - time frame it can be stored for
* @return true if expired, false if the time frame is still in range * @returns true if expired, false if the time frame is still in range
*/ */
export const didExpire = (time: number, threshold: number): boolean => time + threshold <= Date.now(); export const didExpire = (time: number, threshold: number): boolean => time + threshold <= Date.now();

View File

@@ -1,16 +1,9 @@
import { CalendarDots, ChatText, FileText, ImageSquare, Minus, Plus, Smiley } from '@phosphor-icons/react';
import { colorsFlattened } from '@shared/util/themeColors'; import { colorsFlattened } from '@shared/util/themeColors';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@views/components/common/Button'; import { Button } from '@views/components/common/Button';
import React from 'react'; import React from 'react';
import AddIcon from '~icons/material-symbols/add';
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
import DescriptionIcon from '~icons/material-symbols/description';
import ImagePlaceholderIcon from '~icons/material-symbols/image';
import HappyFaceIcon from '~icons/material-symbols/mood';
import RemoveIcon from '~icons/material-symbols/remove';
import ReviewsIcon from '~icons/material-symbols/reviews';
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = { const meta = {
title: 'Components/Common/Button', title: 'Components/Common/Button',
@@ -24,7 +17,7 @@ const meta = {
// More on argTypes: https://storybook.js.org/docs/api/argtypes // More on argTypes: https://storybook.js.org/docs/api/argtypes
args: { args: {
children: 'Button', children: 'Button',
icon: ImagePlaceholderIcon, icon: ImageSquare,
}, },
argTypes: { argTypes: {
children: { control: 'text' }, children: { control: 'text' },
@@ -42,6 +35,62 @@ export const Default: Story = {
}, },
}; };
export const Small: Story = {
// @ts-ignore
args: {
children: '',
},
render: props => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
<div style={{ display: 'flex', gap: '15px' }}>
<Button {...props} variant='filled' color='ut-black' size='small'>
Button
</Button>
<Button {...props} variant='outline' color='ut-black' size='small'>
Button
</Button>
<Button {...props} variant='minimal' color='ut-black' size='small'>
Button
</Button>
</div>
<hr />
<div style={{ display: 'flex', gap: '15px' }}>
<Button {...props} icon={ImageSquare} variant='filled' color='ut-black' size='small' />
<Button {...props} icon={ImageSquare} variant='outline' color='ut-black' size='small' />
<Button {...props} icon={ImageSquare} variant='minimal' color='ut-black' size='small' />
</div>
</div>
),
};
export const Mini: Story = {
// @ts-ignore
args: {
children: '',
},
render: props => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
<div style={{ display: 'flex', gap: '15px' }}>
<Button {...props} variant='filled' color='ut-black' size='mini'>
Button
</Button>
<Button {...props} variant='outline' color='ut-black' size='mini'>
Button
</Button>
<Button {...props} variant='minimal' color='ut-black' size='mini'>
Button
</Button>
</div>
<hr />
<div style={{ display: 'flex', gap: '15px' }}>
<Button {...props} icon={ImageSquare} variant='filled' color='ut-black' size='mini' />
<Button {...props} icon={ImageSquare} variant='outline' color='ut-black' size='mini' />
<Button {...props} icon={ImageSquare} variant='minimal' color='ut-black' size='mini' />
</div>
</div>
),
};
export const Disabled: Story = { export const Disabled: Story = {
args: { args: {
variant: 'filled', variant: 'filled',
@@ -57,7 +106,7 @@ export const Grid: Story = {
<div style={{ display: 'flex', gap: '15px' }}> <div style={{ display: 'flex', gap: '15px' }}>
<Button {...props} variant='filled' color='ut-black' /> <Button {...props} variant='filled' color='ut-black' />
<Button {...props} variant='outline' color='ut-black' /> <Button {...props} variant='outline' color='ut-black' />
<Button {...props} variant='single' color='ut-black' /> <Button {...props} variant='minimal' color='ut-black' />
</div> </div>
<hr /> <hr />
@@ -65,7 +114,7 @@ export const Grid: Story = {
<div style={{ display: 'flex', gap: '15px' }}> <div style={{ display: 'flex', gap: '15px' }}>
<Button {...props} variant='filled' color='ut-black' disabled /> <Button {...props} variant='filled' color='ut-black' disabled />
<Button {...props} variant='outline' color='ut-black' disabled /> <Button {...props} variant='outline' color='ut-black' disabled />
<Button {...props} variant='single' color='ut-black' disabled /> <Button {...props} variant='minimal' color='ut-black' disabled />
</div> </div>
</div> </div>
), ),
@@ -89,12 +138,12 @@ export const PrettyColors: Story = {
<Button {...props} variant='outline' color={color}> <Button {...props} variant='outline' color={color}>
Button Button
</Button> </Button>
<Button {...props} variant='single' color={color}> <Button {...props} variant='minimal' color={color}>
Button Button
</Button> </Button>
<Button {...props} variant='filled' color={color} /> <Button {...props} variant='filled' color={color} />
<Button {...props} variant='outline' color={color} /> <Button {...props} variant='outline' color={color} />
<Button {...props} variant='single' color={color} /> <Button {...props} variant='minimal' color={color} />
</div> </div>
))} ))}
</div> </div>
@@ -106,10 +155,10 @@ export const PrettyColors: Story = {
export const CourseButtons: Story = { export const CourseButtons: Story = {
render: props => ( render: props => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px', alignItems: 'center' }}> <div style={{ display: 'flex', flexDirection: 'column', gap: '15px', alignItems: 'center' }}>
<Button {...props} variant='filled' color='ut-green' icon={AddIcon}> <Button {...props} variant='filled' color='ut-green' icon={Plus}>
Add Course Add Course
</Button> </Button>
<Button {...props} variant='filled' color='theme-red' icon={RemoveIcon}> <Button {...props} variant='filled' color='theme-red' icon={Minus}>
Remove Course Remove Course
</Button> </Button>
</div> </div>
@@ -129,17 +178,17 @@ export const CourseCatalogActionButtons: Story = {
}, },
render: props => ( render: props => (
<div style={{ display: 'flex', gap: '15px' }}> <div style={{ display: 'flex', gap: '15px' }}>
<Button {...props} variant='filled' color='ut-burntorange' icon={CalendarMonthIcon} /> <Button {...props} variant='filled' color='ut-burntorange' icon={CalendarDots} />
<Button {...props} variant='outline' color='ut-blue' icon={ReviewsIcon}> <Button {...props} variant='outline' color='ut-blue' icon={ChatText}>
RateMyProf RateMyProf
</Button> </Button>
<Button {...props} variant='outline' color='ut-teal' icon={HappyFaceIcon}> <Button {...props} variant='outline' color='ut-teal' icon={Smiley}>
CES CES
</Button> </Button>
<Button {...props} variant='outline' color='ut-orange' icon={DescriptionIcon}> <Button {...props} variant='outline' color='ut-orange' icon={FileText}>
Past Syllabi Past Syllabi
</Button> </Button>
<Button {...props} variant='filled' color='ut-green' icon={AddIcon}> <Button {...props} variant='filled' color='ut-green' icon={Plus}>
Add Course Add Course
</Button> </Button>
</div> </div>

View File

@@ -1,11 +1,10 @@
import { ArrowsVertical } from '@phosphor-icons/react';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@views/components/common/Button'; import { Button } from '@views/components/common/Button';
import DialogProvider, { usePrompt } from '@views/components/common/DialogProvider/DialogProvider'; import DialogProvider, { usePrompt } from '@views/components/common/DialogProvider/DialogProvider';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import React, { useState } from 'react'; import React, { useState } from 'react';
import MaterialSymbolsExpandAllRoundedIcon from '~icons/material-symbols/expand-all-rounded';
const meta = { const meta = {
title: 'Components/Common/DialogProvider', title: 'Components/Common/DialogProvider',
component: DialogProvider, component: DialogProvider,
@@ -46,7 +45,13 @@ const InnerComponent = () => {
}; };
return ( return (
<Button variant='filled' color='ut-burntorange' icon={MaterialSymbolsExpandAllRoundedIcon} onClick={myShow}> <Button
variant='filled'
color='ut-burntorange'
icon={ArrowsVertical}
iconProps={{ className: 'h-4 w-4' }}
onClick={myShow}
>
Open Dialog Open Dialog
</Button> </Button>
); );
@@ -86,7 +91,7 @@ const FiveDialogsInnerComponent = () => {
}; };
return ( return (
<Button variant='filled' color='ut-burntorange' icon={MaterialSymbolsExpandAllRoundedIcon} onClick={myShow}> <Button variant='filled' color='ut-burntorange' icon={ArrowsVertical} onClick={myShow}>
Open Dialogs Open Dialogs
</Button> </Button>
); );
@@ -121,7 +126,7 @@ const NestedDialogsInnerComponent = () => {
}; };
return ( return (
<Button variant='filled' color='ut-burntorange' icon={MaterialSymbolsExpandAllRoundedIcon} onClick={myShow}> <Button variant='filled' color='ut-burntorange' icon={ArrowsVertical} onClick={myShow}>
Open Next Dialog Open Next Dialog
</Button> </Button>
); );
@@ -161,7 +166,7 @@ const DialogWithOnCloseInnerComponent = () => {
<h1> <h1>
You closed the button below {timesClosed} {timesClosed === 1 ? 'time' : 'times'} You closed the button below {timesClosed} {timesClosed === 1 ? 'time' : 'times'}
</h1> </h1>
<Button variant='filled' color='ut-burntorange' icon={MaterialSymbolsExpandAllRoundedIcon} onClick={myShow}> <Button variant='filled' color='ut-burntorange' icon={ArrowsVertical} onClick={myShow}>
Open Dialog Open Dialog
</Button> </Button>
</> </>

View File

@@ -0,0 +1,14 @@
import type { Meta, StoryObj } from '@storybook/react';
import DiningAppPromo from '@views/components/calendar/DiningAppPromo';
export default {
title: 'Components/Calendar/DiningAppPromo',
component: DiningAppPromo,
parameters: {
layout: 'centered',
},
} satisfies Meta<typeof DiningAppPromo>;
type Story = StoryObj<typeof DiningAppPromo>;
export const Default: Story = {};

View File

@@ -1,14 +1,9 @@
import { CalendarDots, ChatText, FileText, Plus, Smiley } from '@phosphor-icons/react';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@views/components/common/Button'; import { Button } from '@views/components/common/Button';
import Divider from '@views/components/common/Divider'; import Divider from '@views/components/common/Divider';
import React from 'react'; import React from 'react';
import AddIcon from '~icons/material-symbols/add';
import CalendarMonthIcon from '~icons/material-symbols/calendar-month';
import DescriptionIcon from '~icons/material-symbols/description';
import HappyFaceIcon from '~icons/material-symbols/mood';
import ReviewsIcon from '~icons/material-symbols/reviews';
const meta = { const meta = {
title: 'Components/Common/Divider', title: 'Components/Common/Divider',
component: Divider, component: Divider,
@@ -59,18 +54,18 @@ export const CourseCatalogActionButtons: Story = {
}, },
render: props => ( render: props => (
<div className='flex items-center gap-3.75'> <div className='flex items-center gap-3.75'>
<Button variant='filled' color='ut-burntorange' icon={CalendarMonthIcon} /> <Button variant='filled' color='ut-burntorange' icon={CalendarDots} />
<Divider {...props} /> <Divider {...props} />
<Button variant='outline' color='ut-blue' icon={ReviewsIcon}> <Button variant='outline' color='ut-blue' icon={ChatText}>
RateMyProf RateMyProf
</Button> </Button>
<Button variant='outline' color='ut-teal' icon={HappyFaceIcon}> <Button variant='outline' color='ut-teal' icon={Smiley}>
CES CES
</Button> </Button>
<Button variant='outline' color='ut-orange' icon={DescriptionIcon}> <Button variant='outline' color='ut-orange' icon={FileText}>
Past Syllabi Past Syllabi
</Button> </Button>
<Button variant='filled' color='ut-green' icon={AddIcon}> <Button variant='filled' color='ut-green' icon={Plus}>
Add Course Add Course
</Button> </Button>
</div> </div>

View File

@@ -2,10 +2,10 @@ import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { UserSchedule } from '@shared/types/UserSchedule'; import { UserSchedule } from '@shared/types/UserSchedule';
import { generateRandomId } from '@shared/util/random'; import { generateRandomId } from '@shared/util/random';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import List from '@views/components/common/List';
import type { ScheduleDropdownProps } from '@views/components/common/ScheduleDropdown'; import type { ScheduleDropdownProps } from '@views/components/common/ScheduleDropdown';
import ScheduleDropdown from '@views/components/common/ScheduleDropdown'; import ScheduleDropdown from '@views/components/common/ScheduleDropdown';
import ScheduleListItem from '@views/components/common/ScheduleListItem'; import ScheduleListItem from '@views/components/common/ScheduleListItem';
import { SortableList } from '@views/components/common/SortableList';
import useSchedules, { getActiveSchedule, switchSchedule } from '@views/hooks/useSchedules'; import useSchedules, { getActiveSchedule, switchSchedule } from '@views/hooks/useSchedules';
import type { Serialized } from 'chrome-extension-toolkit'; import type { Serialized } from 'chrome-extension-toolkit';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
@@ -48,10 +48,10 @@ const meta: Meta<typeof ScheduleDropdown> = {
return ( return (
<div className='w-80'> <div className='w-80'>
<ScheduleDropdown {...args}> <ScheduleDropdown {...args}>
<List <SortableList
className='gap-spacing-3'
draggables={schedules} draggables={schedules}
itemKey={s => s.id} onChange={reordered => {
onReordered={reordered => {
const activeSchedule = getActiveSchedule(); const activeSchedule = getActiveSchedule();
const activeIndex = reordered.findIndex(s => s.id === activeSchedule.id); const activeIndex = reordered.findIndex(s => s.id === activeSchedule.id);
@@ -59,18 +59,10 @@ const meta: Meta<typeof ScheduleDropdown> = {
UserScheduleStore.set('schedules', reordered); UserScheduleStore.set('schedules', reordered);
UserScheduleStore.set('activeIndex', activeIndex); UserScheduleStore.set('activeIndex', activeIndex);
}} }}
gap={10} renderItem={schedule => (
> <ScheduleListItem schedule={schedule} onClick={() => switchSchedule(schedule.id)} />
{(schedule, handleProps) => (
<ScheduleListItem
schedule={schedule}
onClick={() => {
switchSchedule(schedule.id);
}}
dragHandleProps={handleProps}
/>
)} )}
</List> />
</ScheduleDropdown> </ScheduleDropdown>
</div> </div>
); );

View File

@@ -0,0 +1,159 @@
import { ImageSquare } from '@phosphor-icons/react';
import { colorsFlattened } from '@shared/util/themeColors';
import type { Meta, StoryObj } from '@storybook/react';
import FileUpload from '@views/components/common/FileUpload';
import React from 'react';
/**
* Stole this straight from Button.stories.tsx to test the input
*/
const meta = {
title: 'Components/Common/FileUpload',
component: FileUpload,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
args: {
children: 'Upload File',
icon: ImageSquare,
},
argTypes: {
children: { control: 'text' },
color: {
control: 'select',
options: Object.keys(colorsFlattened),
},
variant: {
control: 'select',
options: ['filled', 'outline', 'single'],
},
disabled: {
control: 'boolean',
},
onChange: { action: 'file selected' }, // action to show when file is selected
},
} satisfies Meta<typeof FileUpload>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
variant: 'filled',
color: 'ut-black', // Default theme color
},
};
export const Small: Story = {
// @ts-ignore
args: {
children: '',
},
render: props => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
<div style={{ display: 'flex', gap: '15px' }}>
<FileUpload {...props} variant='filled' color='ut-black' size='small'>
Upload File
</FileUpload>
<FileUpload {...props} variant='outline' color='ut-black' size='small'>
Upload File
</FileUpload>
<FileUpload {...props} variant='minimal' color='ut-black' size='small'>
Upload File
</FileUpload>
</div>
<hr />
<div style={{ display: 'flex', gap: '15px' }}>
<FileUpload {...props} icon={ImageSquare} variant='filled' color='ut-black' size='small' />
<FileUpload {...props} icon={ImageSquare} variant='outline' color='ut-black' size='small' />
<FileUpload {...props} icon={ImageSquare} variant='minimal' color='ut-black' size='small' />
</div>
</div>
),
};
export const Mini: Story = {
// @ts-ignore
args: {
children: '',
},
render: props => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
<div style={{ display: 'flex', gap: '15px' }}>
<FileUpload {...props} variant='filled' color='ut-black' size='mini'>
Button
</FileUpload>
<FileUpload {...props} variant='outline' color='ut-black' size='mini'>
Button
</FileUpload>
<FileUpload {...props} variant='minimal' color='ut-black' size='mini'>
Button
</FileUpload>
</div>
<hr />
<div style={{ display: 'flex', gap: '15px' }}>
<FileUpload {...props} icon={ImageSquare} variant='filled' color='ut-black' size='mini' />
<FileUpload {...props} icon={ImageSquare} variant='outline' color='ut-black' size='mini' />
<FileUpload {...props} icon={ImageSquare} variant='minimal' color='ut-black' size='mini' />
</div>
</div>
),
};
export const Disabled: Story = {
args: {
variant: 'filled',
color: 'ut-black',
disabled: true,
},
};
// @ts-ignore
export const Grid: Story = {
render: props => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
<div style={{ display: 'flex', gap: '15px' }}>
<FileUpload {...props} variant='filled' color='ut-black' />
<FileUpload {...props} variant='outline' color='ut-black' />
<FileUpload {...props} variant='minimal' color='ut-black' />
</div>
<hr />
<div style={{ display: 'flex', gap: '15px' }}>
<FileUpload {...props} variant='filled' color='ut-black' disabled />
<FileUpload {...props} variant='outline' color='ut-black' disabled />
<FileUpload {...props} variant='minimal' color='ut-black' disabled />
</div>
</div>
),
};
export const PrettyColors: Story = {
// @ts-ignore
args: {
children: '',
},
render: props => {
const colorsNames = Object.keys(colorsFlattened) as (keyof typeof colorsFlattened)[];
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
{colorsNames.map(color => (
<div style={{ display: 'flex', gap: '15px' }} key={color}>
<FileUpload {...props} variant='filled' color={color}>
Button
</FileUpload>
<FileUpload {...props} variant='outline' color={color}>
Button
</FileUpload>
<FileUpload {...props} variant='minimal' color={color}>
Button
</FileUpload>
</div>
))}
</div>
);
},
};

View File

@@ -56,10 +56,10 @@ export const AreYouSure: StoryObj<PromptDialogProps> = {
</Text> </Text>
), ),
children: [ children: [
<Button key='yes' variant='single' color='ut-burntorange'> <Button key='yes' variant='minimal' color='ut-burntorange'>
Yes Yes
</Button>, </Button>,
<Button key='no' variant='single' color='ut-black'> <Button key='no' variant='minimal' color='ut-black'>
No No
</Button>, </Button>,
], ],
@@ -76,7 +76,7 @@ export const YouHave10ActiveSchedules: StoryObj<PromptDialogProps> = {
</Text> </Text>
), ),
children: [ children: [
<Button key='yes' variant='single' color='ut-black'> <Button key='yes' variant='minimal' color='ut-black'>
I understand I understand
</Button>, </Button>,
], ],
@@ -94,10 +94,10 @@ export const WelcomeToUTRPV2: StoryObj<PromptDialogProps> = {
</Text> </Text>
), ),
children: [ children: [
<Button key='migrate' variant='single' color='ut-black'> <Button key='migrate' variant='minimal' color='ut-black'>
Don&apos;t Migrate Don&apos;t Migrate
</Button>, </Button>,
<Button key='start-fresh' variant='single' color='ut-burntorange'> <Button key='start-fresh' variant='minimal' color='ut-burntorange'>
Migrate Migrate
</Button>, </Button>,
], ],

View File

@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react';
import ResourceLinks from '@views/components/calendar/ResourceLinks';
const meta = {
title: 'Components/Common/ResourceLinks',
component: ResourceLinks,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {},
} satisfies Meta<typeof ResourceLinks>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {},
};

View File

@@ -1,16 +1,15 @@
import type { DraggableProvidedDragHandleProps } from '@hello-pangea/dnd';
import { Course, Status } from '@shared/types/Course'; import { Course, Status } from '@shared/types/Course';
import { CourseMeeting } from '@shared/types/CourseMeeting'; import { CourseMeeting } from '@shared/types/CourseMeeting';
import Instructor from '@shared/types/Instructor'; import Instructor from '@shared/types/Instructor';
import { tailwindColorways } from '@shared/util/storybook'; import { tailwindColorways } from '@shared/util/storybook';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import List from '@views/components/common/List';
import PopupCourseBlock from '@views/components/common/PopupCourseBlock'; import PopupCourseBlock from '@views/components/common/PopupCourseBlock';
import type { BaseItem } from '@views/components/common/SortableList';
import { SortableList } from '@views/components/common/SortableList';
import React from 'react'; import React from 'react';
const numberOfCourses = 5; const numberOfCourses = 5;
// TODO: move into utils
/** /**
* Generates an array of courses. * Generates an array of courses.
* *
@@ -73,36 +72,33 @@ const generateCourses = (count: number): Course[] => {
}; };
const exampleCourses = generateCourses(numberOfCourses); const exampleCourses = generateCourses(numberOfCourses);
const generateCourseBlocks = (course: Course, dragHandleProps: DraggableProvidedDragHandleProps) => (
<PopupCourseBlock key={course.uniqueId} course={course} colors={course.colors} dragHandleProps={dragHandleProps} /> type CourseWithId = { course: Course } & BaseItem;
);
const meta = { const meta = {
title: 'Components/Common/List', title: 'Components/Common/SortableList',
component: List, component: SortableList,
parameters: { parameters: {
layout: 'centered', layout: 'centered',
}, },
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { } satisfies Meta<typeof SortableList<CourseWithId>>;
gap: { control: 'number' },
},
} satisfies Meta<typeof List<Course>>;
export default meta; export default meta;
type Story = StoryObj<Meta<typeof List<Course>>>; type Story = StoryObj<Meta<typeof SortableList<CourseWithId>>>;
export const Default: Story = { export const Default: Story = {
args: { args: {
draggables: exampleCourses, draggables: exampleCourses.map(course => ({
children: generateCourseBlocks, id: course.uniqueId,
itemKey: item => item.uniqueId, course,
gap: 12, })),
onReordered: () => {}, onChange: () => {},
renderItem: ({ id, course }) => <PopupCourseBlock key={id} course={course} colors={course.colors} />,
}, },
render: args => ( render: args => (
<div className='w-sm'> <div className='h-3xl w-3xl transform-none'>
<List {...args} /> <SortableList {...args} />
</div> </div>
), ),
}; };

View File

@@ -1,32 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import type { UpdateTextProps } from '@views/components/common/UpdateText';
import UpdateText from '@views/components/common/UpdateText';
import React from 'react';
const meta = {
title: 'Components/Common/UpdateText',
component: UpdateText,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
courses: { control: 'object' },
},
} satisfies Meta<typeof UpdateText>;
export default meta;
type Story = StoryObj<typeof meta>;
const Template = (args: React.JSX.IntrinsicAttributes & UpdateTextProps) => <UpdateText {...args} />;
export const Default: Story = {
render: Template,
args: {
courses: ['12345', '23456', '34567', '45678', '56789'],
},
};
Default.args = {
courses: ['12345', '23456', '34567', '45678', '56789'],
};

View File

@@ -0,0 +1,38 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@views/components/common/Button';
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
import WhatsNewPopup from '@views/components/common/WhatsNewPopup';
import useWhatsNewPopUp from '@views/hooks/useWhatsNew';
import React from 'react';
const meta = {
title: 'Components/Common/WhatsNewPopup',
component: WhatsNewPopup,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'centered',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
} satisfies Meta<typeof WhatsNewPopup>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {
render: () => (
<DialogProvider>
<InnerComponent />
</DialogProvider>
),
};
const InnerComponent = () => {
const handleOnClick = useWhatsNewPopUp();
return (
<Button color='ut-burntorange' onClick={handleOnClick}>
Open Dialog
</Button>
);
};

View File

@@ -3,6 +3,7 @@ import Instructor from '@shared/types/Instructor';
import { getCourseColors } from '@shared/util/colors'; import { getCourseColors } from '@shared/util/colors';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import CalendarBottomBar from '@views/components/calendar/CalendarBottomBar'; import CalendarBottomBar from '@views/components/calendar/CalendarBottomBar';
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
import React from 'react'; import React from 'react';
const exampleGovCourse: Course = new Course({ const exampleGovCourse: Course = new Course({
@@ -91,9 +92,18 @@ export const Default: Story = {
async: true, async: true,
calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 }, calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 },
componentProps: { componentProps: {
colors: getCourseColors('pink', 200),
courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} ${exampleGovCourse.instructors[0]!.lastName}`, courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} ${exampleGovCourse.instructors[0]!.lastName}`,
status: exampleGovCourse.status, status: exampleGovCourse.status,
blockData: {
calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 },
componentProps: {
courseDeptAndInstr: `${exampleGovCourse.department} ${exampleGovCourse.number} ${exampleGovCourse.instructors[0]!.lastName}`,
status: exampleGovCourse.status,
blockData: {} as CalendarGridCourse,
},
course: exampleGovCourse,
async: true,
},
}, },
course: exampleGovCourse, course: exampleGovCourse,
}, },
@@ -101,9 +111,18 @@ export const Default: Story = {
async: true, async: true,
calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 }, calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 },
componentProps: { componentProps: {
colors: getCourseColors('slate', 500),
courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} ${examplePsyCourse.instructors[0]!.lastName}`, courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} ${examplePsyCourse.instructors[0]!.lastName}`,
status: examplePsyCourse.status, status: examplePsyCourse.status,
blockData: {
calendarGridPoint: { dayIndex: -1, endIndex: -1, startIndex: -1 },
componentProps: {
courseDeptAndInstr: `${examplePsyCourse.department} ${examplePsyCourse.number} ${examplePsyCourse.instructors[0]!.lastName}`,
status: examplePsyCourse.status,
blockData: {} as CalendarGridCourse,
},
course: examplePsyCourse,
async: true,
},
}, },
course: examplePsyCourse, course: examplePsyCourse,
}, },

View File

@@ -1,8 +1,8 @@
import { Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import { getCourseColors } from '@shared/util/colors';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import type { CalendarCourseCellProps } from '@views/components/calendar/CalendarCourseCell'; import type { CalendarCourseCellProps } from '@views/components/calendar/CalendarCourseCell';
import CalendarCourseCell from '@views/components/calendar/CalendarCourseCell'; import CalendarCourseCell from '@views/components/calendar/CalendarCourseCell';
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
import React from 'react'; import React from 'react';
import { ExampleCourse } from '../PopupCourseBlock.stories'; import { ExampleCourse } from '../PopupCourseBlock.stories';
@@ -19,7 +19,6 @@ const meta = {
className: { control: { type: 'text' } }, className: { control: { type: 'text' } },
status: { control: { type: 'select', options: Object.values(Status) } }, status: { control: { type: 'select', options: Object.values(Status) } },
timeAndLocation: { control: { type: 'text' } }, timeAndLocation: { control: { type: 'text' } },
colors: { control: { type: 'object' } },
}, },
render: (args: CalendarCourseCellProps) => ( render: (args: CalendarCourseCellProps) => (
<div className='w-45'> <div className='w-45'>
@@ -30,24 +29,71 @@ const meta = {
courseDeptAndInstr: ExampleCourse.department, courseDeptAndInstr: ExampleCourse.department,
className: ExampleCourse.number, className: ExampleCourse.number,
status: ExampleCourse.status, status: ExampleCourse.status,
timeAndLocation: ExampleCourse.schedule.meetings[0]!.getTimeString({ separator: '-' }), timeAndLocation: ExampleCourse.schedule.meetings[0]!.getTimeString({ separator: '' }),
colors: getCourseColors('emerald', 500),
}, },
} satisfies Meta<typeof CalendarCourseCell>; } satisfies Meta<typeof CalendarCourseCell>;
export default meta; export default meta;
type Story = StoryObj<typeof meta>; type Story = StoryObj<typeof meta>;
export const Default: Story = {}; export const Default: Story = {
args: {
courseDeptAndInstr: ExampleCourse.department,
className: ExampleCourse.number,
status: ExampleCourse.status,
timeAndLocation: ExampleCourse.schedule.meetings[0]!.getTimeString({ separator: '-' }),
blockData: {
calendarGridPoint: {
dayIndex: 4,
startIndex: 10,
endIndex: 11,
},
course: ExampleCourse,
async: false,
componentProps: {
courseDeptAndInstr: ExampleCourse.department,
status: ExampleCourse.status,
timeAndLocation: ExampleCourse.schedule.meetings[0]!.getTimeString({ separator: '-' }),
blockData: {} as CalendarGridCourse,
},
},
},
};
export const Variants: Story = { export const Variants: Story = {
render: props => ( args: {
<div className='grid grid-cols-2 h-40 max-w-60 w-90vw gap-x-4 gap-y-2'> courseDeptAndInstr: ExampleCourse.department,
<CalendarCourseCell {...props} colors={getCourseColors('green', 500)} /> className: ExampleCourse.number,
<CalendarCourseCell {...props} colors={getCourseColors('teal', 400)} /> status: ExampleCourse.status,
<CalendarCourseCell {...props} colors={getCourseColors('indigo', 400)} /> timeAndLocation: ExampleCourse.schedule.meetings[0]!.getTimeString({ separator: '-' }),
<CalendarCourseCell {...props} colors={getCourseColors('red', 500)} /> blockData: {
</div> calendarGridPoint: {
), dayIndex: 4,
startIndex: 10,
endIndex: 11,
},
course: ExampleCourse,
async: false,
componentProps: {
courseDeptAndInstr: ExampleCourse.department,
status: ExampleCourse.status,
timeAndLocation: ExampleCourse.schedule.meetings[0]!.getTimeString({ separator: '-' }),
blockData: {
calendarGridPoint: {
dayIndex: 4,
startIndex: 10,
endIndex: 11,
},
componentProps: {
courseDeptAndInstr: ExampleCourse.department,
status: ExampleCourse.status,
timeAndLocation: ExampleCourse.schedule.meetings[0]!.getTimeString({ separator: '-' }),
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
},
},
},
}; };

View File

@@ -1,5 +1,4 @@
import { Status } from '@shared/types/Course'; import { Status } from '@shared/types/Course';
import { getCourseColors } from '@shared/util/colors';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import CalendarGrid from '@views/components/calendar/CalendarGrid'; import CalendarGrid from '@views/components/calendar/CalendarGrid';
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule'; import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
@@ -32,7 +31,21 @@ const testData: CalendarGridCourse[] = [
courseDeptAndInstr: 'Course 1', courseDeptAndInstr: 'Course 1',
timeAndLocation: '9:00 AM - 10:00 AM, Room 101', timeAndLocation: '9:00 AM - 10:00 AM, Room 101',
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), blockData: {
calendarGridPoint: {
dayIndex: 4,
startIndex: 10,
endIndex: 11,
},
componentProps: {
courseDeptAndInstr: 'Course 1',
timeAndLocation: '9:00 AM - 10:00 AM, Room 101',
status: Status.OPEN,
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
}, },
course: ExampleCourse, course: ExampleCourse,
async: false, async: false,
@@ -47,7 +60,21 @@ const testData: CalendarGridCourse[] = [
courseDeptAndInstr: 'Course 1', courseDeptAndInstr: 'Course 1',
timeAndLocation: '9:00 AM - 10:00 AM, Room 101', timeAndLocation: '9:00 AM - 10:00 AM, Room 101',
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), blockData: {
calendarGridPoint: {
dayIndex: 2,
startIndex: 5,
endIndex: 6,
},
componentProps: {
courseDeptAndInstr: 'Course 1',
timeAndLocation: '9:00 AM - 10:00 AM, Room 101',
status: Status.OPEN,
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
}, },
course: ExampleCourse, course: ExampleCourse,
async: false, async: false,
@@ -62,7 +89,21 @@ const testData: CalendarGridCourse[] = [
courseDeptAndInstr: 'Course 2', courseDeptAndInstr: 'Course 2',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102', timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), blockData: {
calendarGridPoint: {
dayIndex: 1,
startIndex: 10,
endIndex: 12,
},
componentProps: {
courseDeptAndInstr: 'Course 2',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED,
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
}, },
course: ExampleCourse, course: ExampleCourse,
async: false, async: false,
@@ -77,7 +118,21 @@ const testData: CalendarGridCourse[] = [
courseDeptAndInstr: 'Course 1', courseDeptAndInstr: 'Course 1',
timeAndLocation: '9:00 AM - 10:00 AM, Room 101', timeAndLocation: '9:00 AM - 10:00 AM, Room 101',
status: Status.OPEN, status: Status.OPEN,
colors: getCourseColors('emerald', 500), blockData: {
calendarGridPoint: {
dayIndex: 4,
startIndex: 10,
endIndex: 11,
},
componentProps: {
courseDeptAndInstr: 'Course 1',
timeAndLocation: '9:00 AM - 10:00 AM, Room 101',
status: Status.OPEN,
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
}, },
course: ExampleCourse, course: ExampleCourse,
async: false, async: false,
@@ -92,7 +147,21 @@ const testData: CalendarGridCourse[] = [
courseDeptAndInstr: 'Course 2', courseDeptAndInstr: 'Course 2',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102', timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), blockData: {
calendarGridPoint: {
dayIndex: 1,
startIndex: 10,
endIndex: 12,
},
componentProps: {
courseDeptAndInstr: 'Course 2',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED,
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
}, },
course: ExampleCourse, course: ExampleCourse,
async: false, async: false,
@@ -107,7 +176,21 @@ const testData: CalendarGridCourse[] = [
courseDeptAndInstr: 'Course 3', courseDeptAndInstr: 'Course 3',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102', timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), blockData: {
calendarGridPoint: {
dayIndex: 1,
startIndex: 10,
endIndex: 12,
},
componentProps: {
courseDeptAndInstr: 'Course 3',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED,
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
}, },
course: ExampleCourse, course: ExampleCourse,
async: false, async: false,
@@ -122,7 +205,21 @@ const testData: CalendarGridCourse[] = [
courseDeptAndInstr: 'Course 4', courseDeptAndInstr: 'Course 4',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102', timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED, status: Status.CLOSED,
colors: getCourseColors('emerald', 500), blockData: {
calendarGridPoint: {
dayIndex: 1,
startIndex: 10,
endIndex: 12,
},
componentProps: {
courseDeptAndInstr: 'Course 4',
timeAndLocation: '10:00 AM - 11:00 AM, Room 102',
status: Status.CLOSED,
blockData: {} as CalendarGridCourse,
},
course: ExampleCourse,
async: false,
},
}, },
course: ExampleCourse, course: ExampleCourse,
async: false, async: false,

View File

@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import CalendarHeader from '@views/components/calendar/CalenderHeader'; import CalendarHeader from '@views/components/calendar/CalendarHeader/CalendarHeader';
const meta = { const meta = {
title: 'Components/Calendar/CalendarHeader', title: 'Components/Calendar/CalendarHeader',

Some files were not shown because too many files have changed in this diff Show More