diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index bede01947..af79d964e 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -46,5 +46,5 @@ jobs: uses: thollander/actions-comment-pull-request@v2 with: message: | - Vercel Preview URL :rocket: : ${{ steps.deploy.outputs.preview_url }} + Vercel Preview URL :rocket: : ${{ steps.deploy.outputs.preview_url }}/auth/login Neon branch :elephant: : https://console.neon.tech/app/projects/${{ secrets.NEON_PROJECT_ID }}/branches/${{ steps.create_branch.outputs.branch_id }} diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index d1d7c6691..b69fec308 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -1,13 +1,12 @@ -name: Check Formatting +name: Lint and Formatting Check on: - workflow_dispatch: - pull_request: + push: paths-ignore: - 'validator/**' jobs: - eslint: + main: name: Run ESLint + Prettier runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 46a3785b0..743a1c8eb 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ yarn-error.log* # See docs/ide-config.md for more information. .idea .vs/ +.vscode .editorconfig prisma/generated diff --git a/.vscode/workspace.code-workspace b/.vscode/workspace.code-workspace deleted file mode 100644 index c48d9735e..000000000 --- a/.vscode/workspace.code-workspace +++ /dev/null @@ -1,14 +0,0 @@ -{ - "folders": [ - { "path": "../", "name": "root" }, - { - "path": "../validator", - "name": "validator" - } - ], - "settings": { - "files.exclude": { - "validator/": true - } - } -} diff --git a/CODEOWNERS b/CODEOWNERS index dc4e5cddc..2a34331ac 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,6 +1,6 @@ # Request lead for file changes in root and workflows. -/* @akevinge -/.github/**/* @akevinge +/* @UTDNebula/pr-reviewers +/.github/**/* @UTDNebula/pr-reviewers # Request Planner PR Reviewers team for changes in to the following root directories. /.vscode/ @UTDNebula/pr-reviewers diff --git a/cypress.config.ts b/cypress.config.ts index f3fb7ca89..ed4c8bff3 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -23,7 +23,7 @@ export default defineConfig({ viewportHeight: 1080, viewportWidth: 1920, baseUrl: 'http://localhost:3000', - setupNodeEvents(on, config) { + setupNodeEvents(on) { on('after:run', async () => { await prisma.$disconnect(); }); diff --git a/next.config.js b/next.config.js index 6adf34c14..069934cf5 100644 --- a/next.config.js +++ b/next.config.js @@ -40,11 +40,12 @@ module.exports = async (phase) => { transform: '@mui/icons-material/{{member}}', }, }, - rewrites: async () => { + redirects: async () => { return [ { source: '/', - destination: '/index.html', + destination: 'https://www.utdnebula.com/projects/planner', + permanent: true, }, ]; }, diff --git a/package-lock.json b/package-lock.json index 6262874d3..b86b1742d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,7 +84,6 @@ "eslint-plugin-react": "^7.23.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.6.15", - "husky": "^8.0.0", "jest": "^29.6.1", "node-fetch": "^3.3.2", "postcss": "^8.4.31", @@ -16785,21 +16784,6 @@ "node": ">=10.17.0" } }, - "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, "node_modules/hyphen": { "version": "1.6.4", "license": "ISC" diff --git a/package.json b/package.json index e7deadd1e..f2fd72a0c 100644 --- a/package.json +++ b/package.json @@ -18,19 +18,12 @@ "cypress:open": "NODE_ENV=test start-server-and-test dev http://localhost:3000 \"cypress open --browser chrome --e2e\"", "cypress:run": "NODE_ENV=test start-server-and-test start http://127.0.0.1:3000 \"cypress run --browser chrome --config video=false --e2e\"", "test": "NODE_OPTIONS=--experimental-vm-modules jest", - "prepare": "husky install", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, "prisma": { "seed": "ts-node --transpile-only --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts" }, - "lint-staged": { - "**/*.{js,jsx,ts,tsx,json}": [ - "npx prettier --write", - "npx eslint --fix" - ] - }, "dependencies": { "@dnd-kit/core": "^6.0.6", "@mui/icons-material": "^5.3.1", @@ -108,7 +101,6 @@ "eslint-plugin-react": "^7.23.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.6.15", - "husky": "^8.0.0", "jest": "^29.6.1", "node-fetch": "^3.3.2", "postcss": "^8.4.31", diff --git a/public/demo.gif b/public/demo.gif deleted file mode 100644 index 19787953b..000000000 Binary files a/public/demo.gif and /dev/null differ diff --git a/public/graduation.jpg b/public/graduation.jpg deleted file mode 100644 index 1b7445f1a..000000000 Binary files a/public/graduation.jpg and /dev/null differ diff --git a/public/icons8-gold-medal-100.png b/public/icons8-gold-medal-100.png deleted file mode 100644 index 24f605e70..000000000 Binary files a/public/icons8-gold-medal-100.png and /dev/null differ diff --git a/public/icons8-lightning-bolt-100.png b/public/icons8-lightning-bolt-100.png deleted file mode 100644 index 2e6e3716b..000000000 Binary files a/public/icons8-lightning-bolt-100.png and /dev/null differ diff --git a/public/icons8-mail-96.png b/public/icons8-mail-96.png deleted file mode 100644 index e143899e7..000000000 Binary files a/public/icons8-mail-96.png and /dev/null differ diff --git a/public/icons8-menu-rounded-100.png b/public/icons8-menu-rounded-100.png deleted file mode 100644 index a77a3af16..000000000 Binary files a/public/icons8-menu-rounded-100.png and /dev/null differ diff --git a/public/icons8-pen-100.png b/public/icons8-pen-100.png deleted file mode 100644 index f049d0cd7..000000000 Binary files a/public/icons8-pen-100.png and /dev/null differ diff --git a/public/icons8-stopwatch-100.png b/public/icons8-stopwatch-100.png deleted file mode 100644 index b851819e4..000000000 Binary files a/public/icons8-stopwatch-100.png and /dev/null differ diff --git a/public/icons8-test-tube-100.png b/public/icons8-test-tube-100.png deleted file mode 100644 index 98ad714a0..000000000 Binary files a/public/icons8-test-tube-100.png and /dev/null differ diff --git a/public/icons8-upload-64.png b/public/icons8-upload-64.png deleted file mode 100644 index 53763fa1c..000000000 Binary files a/public/icons8-upload-64.png and /dev/null differ diff --git a/public/index.css b/public/index.css deleted file mode 100644 index 41e016284..000000000 --- a/public/index.css +++ /dev/null @@ -1,563 +0,0 @@ -:root { - --light-gray: #ddd; - --gray: #999; -} - -@font-face { - font-family: "Inter"; - src: url("./Inter-VariableFont_slnt,wght.ttf"); -} - -html, body { - min-height: 100vh; - scroll-behavior: smooth; -} - -body { - display: flex; - flex-direction: column; - font-family: "Inter", sans-serif; - margin: 0; - overflow-x: hidden; -} - -/* - Components - */ - -a { - text-decoration: none; -} - -.button { - border: 2px solid var(--light-gray); - border-radius: 1.5em; - padding: .6rem 1.1rem; - transition: background-color .3s; -} - -.button:hover { - background-color: var(--light-gray); -} - -/* - Header - */ - -header { - display: flex; - justify-content: space-between; - padding: 2rem 10vw; - z-index: 1; -} - -header ul { - list-style-type: none; - margin: 0; - padding: 0; -} - -header li { - display: inline-block; - margin: 0.75rem 1rem; -} - -header a { - color: var(--gray); - transition: color .2s; -} - -header a:hover { - color: #333; -} - -/* - Login button - */ - -.login { - background-color: #6266F9; - border-color: #6266F9; - color: #fff !important; -} - -.login:hover { - background-color: #474bb6; - color: #fff; -} - -/* - Footer - */ - -footer { - display: flex; - flex-direction: column; - align-items: center; - padding: 1rem 0; -} - -footer img { - margin-bottom: .5rem; -} - -footer p { - color: var(--gray); - font-size: .75rem; -} - -hr { - width: 80%; - background-color: var(--light-gray); - border: none; - height: 1px; -} - -/* - Middle stuff - */ - -main { - flex-grow: 1; -} - -/* - First slide - */ - -#intro { - display: flex; - min-height: calc(120vh - 6rem); - align-items: center; - overflow: hidden; - position: relative; -} - -#intro > div, #plan > div { - flex-grow: 1; - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 0 5vw 0 10vw; -} - -#intro > div { - position: absolute; - top: 30vh; -} - -#intro h1 { - font-size: 3rem; - margin: 0; - max-width: 30vw; -} - -#intro h2 { - color: var(--gray); - font-size: 1rem; - margin: 0; - text-transform: uppercase; -} - -#intro h3 { - color: var(--gray); - font-weight: normal; - font-size: 1rem; - max-width: 30vw; -} - -#intro img, #plan img { - box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; - border-radius: 20px 0 0 20px; -} - -#intro img { - position: absolute; - left: 42%; - top: 10%; - z-index: 0; - max-height: 90vh; -} - -button { - appearance: none; - background: none; - border: none; - outline: none; - font-weight: bold; - font-size: 1.1rem; - margin: 1rem 0; -} - -.buttons a { - color: #000; -} - -#menu { - display: none; -} - -#menu img { - border-radius: 50%; - height: 1.5rem; - padding: .75rem; - position: absolute; - top: 1.5rem; - right: calc(10vw - .75rem); - transition: background-color .2s; -} - -#menu img:hover { - background-color: var(--light-gray); -} - -@media screen and (max-width: 1200px) { - /* - Vertically align the image - */ - #intro { - flex-direction: column; - } - - #intro h1 { - font-size: 2.5rem; - max-width: 400px; - } - - #intro h3 { - max-width: 400px; - } - - #intro > div { - position: static; - padding: 10vh 10vw; - } - - #intro img { - position: static; - max-width: 90vw; - border-radius: 20px; - margin-bottom: 2rem; - } -} - -@media screen and (max-width: 900px) { - header ul { - display: none; - } - - #menu { - display: block; - } -} - -@media screen and (max-width: 650px) { - /* - Detach the image - */ - #intro > div { - flex-grow: 0; - } - - #intro img { - width: 150vw; - } -} - -/* - Second slide - */ - -section:nth-child(odd) { - background-color: #f4f5f6; -} - -#features, #team, #contact, #plan { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-height: calc(100vh - 4rem); - padding: 2rem 0; - text-align: center; -} - -#features h2, #team h2, #contact h2, #plan h2 { - font-size: 3rem; - margin: 0; -} - -#features h3 { - color: var(--gray); - font-weight: normal; - width: 30%; -} - -.features { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 10vw; - padding: 15vh 10vw; -} - -.feature { - align-items: center; - display: flex; - flex-direction: column; -} - -.feature div { - background-color: #eee; - border-radius: 100%; -} - -.feature img { - height: 4rem; - padding: .5rem .6rem; -} - -.feature p { - color: var(--gray); - margin: 0; - line-height: 1.5rem; -} - -@media screen and (max-width: 1200px) { - #features h2, #team h2, #contact h2, #plan h2 { - font-size: 2.5rem !important; - text-align: center !important; - } - - #features h3 { - width: 300px; - font-size: 1rem; - } - - .features { - grid-template-columns: 1fr; - padding: 5vh 0; - } - - .feature p { - font-size: .9rem; - max-width: 300px; - } -} - -/* - Third slide - */ - -#team ul { - list-style-type: none; - margin: 0; - padding: 0; -} - -#team li { - display: inline-block; - font-size: .9rem; - padding: .5rem; - margin: 5vh 0; -} - -#team li.login { - border-radius: 1.5em; - padding: .5rem 1rem; -} - -#team h2 { - margin: 2rem; -} - -.selectedTeam { - background-color: #3772ff; - border-color: #3772ff; - color: white !important; - border-radius: 1.5em; - padding: .5rem 1rem; -} - -.team { - display: grid; - grid-template-columns: repeat(3, 1fr); - width: 90%; - margin-top: 3vh; -} - -.member img { - height: 200px; - border-radius: 10px; - transition: box-shadow .2s; -} - -.member img:hover { - box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px; -} - -.member h4 { - margin: 1rem 0 0 0; -} - -.member h5 { - color: var(--gray); - margin: 1rem 0; -} - -@media screen and (max-width: 1200px) { - .team { - width: 100%; - display: flex; - overflow-x: scroll; - } - - .member:first-child { - margin-left: 25vw; - } - - .member:last-child { - margin-right: 25vw; - } - - .member { - margin: 0 5vw; - } -} - -/* - Plan slide - */ - -#plan { - flex-direction: row; - justify-content: space-between; -} - -#plan img { - max-height: 90vh; - transform: translateY(10vh); -} - -#plan h2 { - font-size: 4rem; - text-align: left; - max-width: 600px; - margin-bottom: 2rem; -} - -@media screen and (max-width: 1200px) { - #plan { - flex-direction: column; - justify-content: center; - min-height: 50vh; - } - - #plan > div { - align-items: center; - flex-grow: 0; - margin-bottom: 2rem; - } - - #plan img { - display: none; - } -} - -/* - Contact slide - */ - -#contact form { - background-color: #fff; - border-radius: 20px; - box-shadow: rgba(0, 0, 0, 0.1) 0px 20px 25px -5px, rgba(0, 0, 0, 0.04) 0px 10px 10px -5px; - display: flex; - margin: 3rem; - text-align: left; -} - -#contact img { - height: 2rem; -} - -#contact h3 { - color: var(--gray); - font-weight: normal; -} - -#contact p { - margin: 0; -} - -#contact form > div:first-child { - margin: 10vh 10vw; -} - -#contact form > div:last-child { - margin: 10vh 10vw 10vh 0; -} - -#contact hr { - display: none; -} - -.inputField { - display: flex; - flex-direction: column; -} - -.inputField label { - color: var(--gray); - font-weight: bold; - font-size: .8rem; - text-transform: uppercase; - margin: .5rem 0; -} - -.inputField input, .inputField textarea { - background-color: #eee; - border: none; - border-radius: 20px; - font-family: "Inter", sans-serif; - font-size: .9rem; - padding: 1rem; - resize: none; - width: 300px; - transition: background-color .3s; -} - -.inputField input:focus, .inputField textarea:focus { - outline: none; - background-color: #ddd; -} - -@media screen and (max-width: 800px) { - #contact form { - flex-direction: column; - } - - #contact form > div:first-child { - margin: 2rem 10vw; - } - - #contact hr { - display: block; - width: calc(100% - 20vw); - } - - #contact form > div:last-child { - margin: 2rem 10vw; - } - - #contact .button { - margin: 1rem 0 0 0; - } - - .inputField { - margin-bottom: 1rem; - } - - .inputField input, .inputField textarea { - width: 50vw; - } -} diff --git a/public/index.html b/public/index.html deleted file mode 100644 index a890f6150..000000000 --- a/public/index.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - Nebula Planner - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- planner logo - - -
-
-
-
-

Navigate your degree with ease

-

Launch Your Degree Plan with Nebula Planner

-

Blast off your academic journey to the moon with Nebula Labs Planner - the ultimate tool for customizing your four-year degree.

- -
- planner mockup -
-
-

Features

-

- Say goodbye to the stress and hassle of degree planning and hello to a smooth, organized path towards graduation with Nebula Planner. -

-
-
-
- medal -
-

Spreadsheet where?

-

- Bye-bye clunky spreadsheets, hello easy-peasy degree planning with simple click-and-drag action. You're welcome! -

-
-
-
- medal -
-

Slay your progress!

-

- Not sure where you are in your degree plan? That's ok. Tracking your degree plan is now a piece of cake with our degree tracker. No cap. -

-
-
-
- medal -
-

Save Time

-

- Nebula Planner streamlines academic planning, enabling you to map out your course requirements, track your progress, and adjust your plan as needed so you can focus on what you do best. -

-
-
- -
-
-

Meet the team

-
-
- Person -

Caleb Lim

-
Project Lead
-
-
- Person -

Aanos Mahmood

-
Design Lead
-
-
- Person -

Stephanie Li

-
Product Lead
-
-
- Person -

Hilary Nguyen

-
Designer, Product team
-
-
- Person -

Aravindan Kasiraman

-
Developer
-
-
- Person -

Jason Antwi-Appah

-
Developer
-
-
- Person -

Kevin Ge

-
Developer
-
-
- Person -

JC Garza

-
Developer
-
-
- Person -

Saidarsh Tukkadi

-
Developer
-
-
- Person -

Lokesh Yerneni

-
Developer
-
-
- Person -

Peyton Barre

-
Developer
-
-
- -
-
-
-

Explore the Possibilities with Nebula Planner!

- -
- Graduation -
-
-

Contact Us

-
-
- Mail -

Email

-

planner@utdnebula.com

-
-
-
-
- - -
-
- - -
- -
-
-
-
- - - - diff --git a/public/mockup.png b/public/mockup.png deleted file mode 100644 index d302e6338..000000000 Binary files a/public/mockup.png and /dev/null differ diff --git a/public/person.png b/public/person.png deleted file mode 100644 index ca906fa3b..000000000 Binary files a/public/person.png and /dev/null differ diff --git a/public/planner_logo.svg b/public/planner_logo.svg deleted file mode 100644 index b08977ee6..000000000 --- a/public/planner_logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/team/Aanos.jpeg b/public/team/Aanos.jpeg deleted file mode 100644 index 76180dfd3..000000000 Binary files a/public/team/Aanos.jpeg and /dev/null differ diff --git a/public/team/Aravindan.jpeg b/public/team/Aravindan.jpeg deleted file mode 100644 index c849ba684..000000000 Binary files a/public/team/Aravindan.jpeg and /dev/null differ diff --git a/public/team/Caleb.jpeg b/public/team/Caleb.jpeg deleted file mode 100644 index b3875c2c5..000000000 Binary files a/public/team/Caleb.jpeg and /dev/null differ diff --git a/public/team/JCGarza.jpeg b/public/team/JCGarza.jpeg deleted file mode 100644 index e2223a7ab..000000000 Binary files a/public/team/JCGarza.jpeg and /dev/null differ diff --git a/public/team/Jason.jpeg b/public/team/Jason.jpeg deleted file mode 100644 index 2f21b439f..000000000 Binary files a/public/team/Jason.jpeg and /dev/null differ diff --git a/public/team/Kevin.jpeg b/public/team/Kevin.jpeg deleted file mode 100644 index 002c85eb8..000000000 Binary files a/public/team/Kevin.jpeg and /dev/null differ diff --git a/public/team/Loki.jpeg b/public/team/Loki.jpeg deleted file mode 100644 index d6b100acb..000000000 Binary files a/public/team/Loki.jpeg and /dev/null differ diff --git a/public/team/Peyton.jpg b/public/team/Peyton.jpg deleted file mode 100644 index 724b1c38b..000000000 Binary files a/public/team/Peyton.jpg and /dev/null differ diff --git a/public/team/Saidarsh.jpeg b/public/team/Saidarsh.jpeg deleted file mode 100644 index dadafcdb1..000000000 Binary files a/public/team/Saidarsh.jpeg and /dev/null differ diff --git a/public/team/Solomon (1).jpeg b/public/team/Solomon (1).jpeg deleted file mode 100644 index e143477ec..000000000 Binary files a/public/team/Solomon (1).jpeg and /dev/null differ diff --git a/public/team/hilary.jpeg b/public/team/hilary.jpeg deleted file mode 100644 index f93a6186e..000000000 Binary files a/public/team/hilary.jpeg and /dev/null differ diff --git a/public/team/stephanie.jpg b/public/team/stephanie.jpg deleted file mode 100644 index be567362c..000000000 Binary files a/public/team/stephanie.jpg and /dev/null differ diff --git a/src/components/AutoCompleteMajor.tsx b/src/components/AutoCompleteMajor.tsx index cac1016d9..a12211033 100644 --- a/src/components/AutoCompleteMajor.tsx +++ b/src/components/AutoCompleteMajor.tsx @@ -1,24 +1,20 @@ import Autocomplete from '@mui/material/Autocomplete'; import Popper from '@mui/material/Popper'; import TextField from '@mui/material/TextField'; -import { FC, useCallback, useRef } from 'react'; +import React, { ComponentPropsWithoutRef, FC, useCallback, useRef } from 'react'; -interface AutoCompleteMajorProps extends React.ComponentPropsWithoutRef<'div'> { - onValueChange: (value: string) => void; +interface AutoCompleteMajorProps extends ComponentPropsWithoutRef<'div'> { + onValueChange?: (value: string) => void; onInputChange: (query: string) => void; options: string[]; - autoFocus?: boolean; placeholder?: string; - defaultValue?: string; } -const AutoCompleteMajor: FC> = ({ +const AutoCompleteMajor: FC> = ({ onValueChange, onInputChange, options, - autoFocus, placeholder = 'Major', - defaultValue = '', ...props }) => { const containerRef = useRef(null); @@ -51,7 +47,7 @@ const AutoCompleteMajor: FC onValueChange(value ?? '')} + onChange={(_, value) => typeof onValueChange !== 'undefined' && onValueChange(value ?? '')} onInputChange={(_, query) => { onInputChange(query); }} diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 70f1b6471..7ea75729c 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,4 +1,4 @@ -import React, { Children, FC } from 'react'; +import React, { Children, ComponentPropsWithoutRef, FC } from 'react'; import Spinner from './Spinner'; @@ -25,7 +25,7 @@ const fontClasses = { default: 'text-sm font-medium', }; -export interface ButtonProps extends React.ComponentPropsWithoutRef<'button'> { +export interface ButtonProps extends ComponentPropsWithoutRef<'button'> { color?: keyof typeof colorClasses; size?: keyof typeof sizeClasses; width?: keyof typeof widthClasses; diff --git a/src/components/common/ErrorMessage.tsx b/src/components/common/ErrorMessage.tsx index ddd730154..25e38ad52 100644 --- a/src/components/common/ErrorMessage.tsx +++ b/src/components/common/ErrorMessage.tsx @@ -15,7 +15,7 @@ const errorMessageStyle = { left: '35%', } as React.CSSProperties; -export default function ErrorMessage(error: any) { +export default function ErrorMessage(error: string) { return ( Something went wrong - {error}! diff --git a/src/components/common/WarningMessageModal.tsx b/src/components/common/WarningMessageModal.tsx deleted file mode 100644 index 7e2dce7e6..000000000 --- a/src/components/common/WarningMessageModal.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import CloseIcon from '@mui/icons-material/Close'; -import WarningIcon from '@mui/icons-material/Warning'; -import React from 'react'; - -export interface WarningMessageModalProps { - setWarning: (open: boolean) => void; - message: string; -} - -export default function WarningMessageModal(props: WarningMessageModalProps) { - const { setWarning, message } = props; - const [expand, setExpand] = React.useState(false); - - return ( -
- {expand ? ( -
-
{message}
- -
- ) : ( - - )} -
- ); -} diff --git a/src/components/home/Home.tsx b/src/components/home/Home.tsx index 984b4267e..767c6c3a8 100644 --- a/src/components/home/Home.tsx +++ b/src/components/home/Home.tsx @@ -1,7 +1,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import { Steps } from 'intro.js-react'; import { useRouter } from 'next/router'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import ChevronIcon from '@/icons/ChevronIcon'; import PlusIcon from '@/icons/PlusIcon'; @@ -34,7 +34,7 @@ export default function PlansPage(): JSX.Element { const router = useRouter(); const [showHomeOnboardingModal, setShowHomeOnboardingModal] = useState(false); - React.useEffect(() => { + useEffect(() => { setShowHomeOnboardingModal((userData && !userData.seenHomeOnboardingModal) ?? false); if (!isLoading && userData && !userData.onboardingComplete) { router.push('/app/onboarding'); diff --git a/src/components/home/Profile.tsx b/src/components/home/Profile.tsx index e1efd0132..dfae8b552 100644 --- a/src/components/home/Profile.tsx +++ b/src/components/home/Profile.tsx @@ -7,7 +7,7 @@ import InputLabel from '@mui/material/InputLabel'; import MenuItem from '@mui/material/MenuItem'; import Select, { SelectChangeEvent } from '@mui/material/Select'; import { signOut } from 'next-auth/react'; -import { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { toast } from 'react-toastify'; import { trpc } from '@/utils/trpc'; diff --git a/src/components/home/Sidebar.tsx b/src/components/home/Sidebar.tsx index b3c4cd1bd..d144d5538 100644 --- a/src/components/home/Sidebar.tsx +++ b/src/components/home/Sidebar.tsx @@ -4,8 +4,6 @@ import { signOut } from 'next-auth/react'; import { useEffect, useMemo, useState } from 'react'; import ChevronIcon from '@/icons/ChevronIcon'; -import ContactIcon from '@/icons/ContactIcon'; -import FeedbackIcon from '@/icons/FeedbackIcon'; import GlobalIcon from '@/icons/GlobalIcon'; import HomeIcon from '@/icons/HomeIcon'; import LogoIcon from '@/icons/LogoIcon'; @@ -37,17 +35,7 @@ export default function Sidebar({ isMobile }: { isMobile: boolean }): JSX.Elemen Icon: ProfileIcon, }, { - url: 'https://discord.gg/K5B727vEnV', - label: 'Contact Support', - Icon: ContactIcon, - }, - { - url: 'https://airtable.com/shrFg9MPi9BGguwPU', - label: 'Feedback Form', - Icon: FeedbackIcon, - }, - { - url: 'https://discord.gg/anrh9B2Z3w', + url: 'https://discord.utdnebula.com/', label: 'Join Our Discord', Icon: GlobalIcon, }, diff --git a/src/components/onboarding/AutoCompleteSearchBarOnboarding.tsx b/src/components/onboarding/AutoCompleteSearchBarOnboarding.tsx deleted file mode 100644 index 783c725c6..000000000 --- a/src/components/onboarding/AutoCompleteSearchBarOnboarding.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import SearchIcon from '@mui/icons-material/Search'; -import { IconButton } from '@mui/material'; -import Autocomplete from '@mui/material/Autocomplete'; -import Popper from '@mui/material/Popper'; -import TextField from '@mui/material/TextField'; -import { FC, useCallback, useRef } from 'react'; - -interface SearchBarProps extends React.ComponentPropsWithoutRef<'div'> { - onValueChange: (value: string) => void; - onInputChange: (query: string) => void; - options: string[]; - autoFocus?: boolean; - placeholder?: string; - defaultValue?: string; -} - -const SearchBar: FC> = ({ - onValueChange, - onInputChange, - options, - autoFocus, - placeholder = 'Course Code', - defaultValue = '', - ...props -}) => { - const containerRef = useRef(null); - - const CustomPopper = useCallback( - (props) => { - if (!containerRef.current) { - return
; - } - const { width } = containerRef.current.getBoundingClientRect(); - return ( - - ); - }, - [containerRef], - ); - - return ( -
-
- onValueChange(value ?? '')} - onInputChange={(_, query) => onInputChange(query)} - options={options} - fullWidth - PopperComponent={CustomPopper} - renderInput={(params) => { - return ( - - ); - }} - /> -
- - - -
-
- ); -}; - -export default SearchBar; diff --git a/src/components/onboarding/DropdownSelectOnboarding.tsx b/src/components/onboarding/DropdownSelectOnboarding.tsx deleted file mode 100644 index 98e67b97f..000000000 --- a/src/components/onboarding/DropdownSelectOnboarding.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import React, { useRef, useState } from 'react'; - -interface DropdownSelectProps { - id?: string; - value: T; - values: T[]; - onChange: (arg: T) => void; - getValue: (v: T) => T; - getDisplayedValue: (v: T) => string; -} - -const DropdownSelect = ({ - id, - value, - values, - onChange, - getValue, - getDisplayedValue, -}: DropdownSelectProps) => { - const [anchorEl, setAnchorEl] = useState(null); - const anchorRef = useRef(null); - - return ( -
setAnchorEl(anchorEl ? null : anchorRef.current)} - > - - -
-
- ); -}; - -export default DropdownSelect; diff --git a/src/components/onboarding/welcome.tsx b/src/components/onboarding/welcome.tsx index cd89fc75c..11c4a1d27 100644 --- a/src/components/onboarding/welcome.tsx +++ b/src/components/onboarding/welcome.tsx @@ -1,6 +1,6 @@ import { MenuItem, Select, SelectChangeEvent } from '@mui/material'; import Link from 'next/link'; -import React from 'react'; +import React, { useEffect } from 'react'; import AutoCompleteMajor from '@/components/AutoCompleteMajor'; import EmojiIcons from '@/icons/EmojiIcon'; @@ -18,16 +18,10 @@ export type WelcomeTypes = { export type WelcomeData = { handleChange: (updatedFields: Partial) => void; data: WelcomeTypes; - semesterOptions: { startSemesters: SemesterCode[]; endSemesters: SemesterCode[] }; handleValidate: (value: boolean) => void; }; -export default function Welcome({ - handleChange, - data, - handleValidate, - semesterOptions, -}: WelcomeData): JSX.Element { +export default function Welcome({ handleChange, data, handleValidate }: WelcomeData): JSX.Element { const { name, startSemester, endSemester }: WelcomeTypes = data; const setName = (event: SelectChangeEvent) => { @@ -42,7 +36,6 @@ export default function Welcome({ handleChange({ endSemester: sem }); }; - const [major, setMajor] = React.useState(''); const { majors, err } = useMajors(); const { results, updateQuery } = useSearch({ @@ -105,11 +98,11 @@ export default function Welcome({ handleValidate(isValid); }; - React.useEffect(() => { + useEffect(() => { checkValidate(); }, [data]); - React.useEffect(() => { + useEffect(() => { window.scrollTo(0, 0); }, []); @@ -117,7 +110,7 @@ export default function Welcome({ return ( <> Oops, we ran into an error! Please let us know on our{' '} - + discord {' '} to get it fixed as soon as possible. @@ -155,7 +148,6 @@ export default function Welcome({ setMajor(value)} onInputChange={(query: string) => updateQuery(query)} options={results.map((major: { filMajor: string }) => major.filMajor)} autoFocus diff --git a/src/components/planner/CourseInfoHoverCard.tsx b/src/components/planner/CourseInfoHoverCard.tsx index a4ea2a9c8..abafbd0ac 100644 --- a/src/components/planner/CourseInfoHoverCard.tsx +++ b/src/components/planner/CourseInfoHoverCard.tsx @@ -70,7 +70,7 @@ const CourseDescription = ({ description }: { description: string }) => { />{' '} @@ -238,7 +238,7 @@ function CourseSelectorContainer({
It seems like your major is no longer supported! Contact us to have it added. - +
diff --git a/src/components/planner/Sidebar/SidebarCourseItem.tsx b/src/components/planner/Sidebar/SidebarCourseItem.tsx index 6843cdcff..bfb03cb47 100644 --- a/src/components/planner/Sidebar/SidebarCourseItem.tsx +++ b/src/components/planner/Sidebar/SidebarCourseItem.tsx @@ -2,7 +2,14 @@ import { UniqueIdentifier, useDraggable } from '@dnd-kit/core'; import { CSS } from '@dnd-kit/utilities'; import CheckIcon from '@mui/icons-material/Check'; import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; -import React, { ComponentPropsWithoutRef, forwardRef, useState, useRef, useEffect } from 'react'; +import React, { + ComponentPropsWithoutRef, + forwardRef, + memo, + useState, + useRef, + useEffect, +} from 'react'; import { getSemesterHourFromCourseCode } from '@/utils/utilFunctions'; @@ -17,7 +24,7 @@ interface SidebarCourseItemProps extends ComponentPropsWithoutRef<'div'> { } /** UI Implementation of sidebar course */ /* eslint-disable react/prop-types */ -export const SidebarCourseItem = React.memo( +export const SidebarCourseItem = memo( forwardRef(function SidebarCourseItem( { course, isDragging, ...props }, ref, diff --git a/src/components/planner/Tiles/SemesterCourseItem.tsx b/src/components/planner/Tiles/SemesterCourseItem.tsx index dee3eb7c8..196eb9966 100644 --- a/src/components/planner/Tiles/SemesterCourseItem.tsx +++ b/src/components/planner/Tiles/SemesterCourseItem.tsx @@ -1,6 +1,6 @@ import { UniqueIdentifier, useDraggable } from '@dnd-kit/core'; import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; -import React, { ComponentPropsWithoutRef, FC, forwardRef, useState, useRef } from 'react'; +import React, { ComponentPropsWithoutRef, FC, forwardRef, memo, useState, useRef } from 'react'; import Skeleton from 'react-loading-skeleton'; import Checkbox from '@/components/Checkbox'; @@ -36,7 +36,7 @@ export interface SemesterCourseItemProps extends ComponentPropsWithoutRef<'div'> /** UI implementation of a semester course */ /* eslint-disable react/prop-types */ -export const MemoizedSemesterCourseItem = React.memo( +export const MemoizedSemesterCourseItem = memo( forwardRef(function SemesterCourseItem( { course, @@ -287,4 +287,4 @@ const DraggableSemesterCourseItem: FC = ({ ); }; -export default React.memo(DraggableSemesterCourseItem); +export default memo(DraggableSemesterCourseItem); diff --git a/src/components/planner/Tiles/SemesterTile.tsx b/src/components/planner/Tiles/SemesterTile.tsx index 823278f22..88a54438b 100644 --- a/src/components/planner/Tiles/SemesterTile.tsx +++ b/src/components/planner/Tiles/SemesterTile.tsx @@ -1,5 +1,5 @@ import { UniqueIdentifier, useDroppable } from '@dnd-kit/core'; -import React, { FC, forwardRef, useState, useRef, useImperativeHandle } from 'react'; +import React, { FC, forwardRef, memo, useState, useRef, useImperativeHandle } from 'react'; import ChevronIcon from '@/icons/ChevronIcon'; import LockIcon from '@/icons/LockIcon'; @@ -23,7 +23,7 @@ export interface SemesterTileProps { * Strictly UI implementation of a semester tile */ /* eslint-disable react/prop-types */ -export const MemoizedSemesterTile = React.memo( +export const MemoizedSemesterTile = memo( forwardRef(function SemesterTile( { semester, getDragId }, outerRef, @@ -223,4 +223,4 @@ const DroppableSemesterTile: FC = ({ ); }; -export default React.memo(DroppableSemesterTile); +export default memo(DroppableSemesterTile); diff --git a/src/components/planner/Toolbar/EditablePlanTitle.tsx b/src/components/planner/Toolbar/EditablePlanTitle.tsx index 490180581..03a10fe2e 100644 --- a/src/components/planner/Toolbar/EditablePlanTitle.tsx +++ b/src/components/planner/Toolbar/EditablePlanTitle.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { toast } from 'react-toastify'; import PencilIcon from '@/icons/PencilIcon'; @@ -11,8 +11,8 @@ export default function EditableMajorTitle({ initialTitle: string; planId: string; }) { - const [title, setTitle] = React.useState(initialTitle); - const [editTitle, setEditTitle] = React.useState(false); + const [title, setTitle] = useState(initialTitle); + const [editTitle, setEditTitle] = useState(false); const updatePlanName = trpc.plan.updatePlanTitle.useMutation(); diff --git a/src/components/planner/Toolbar/Toolbar.tsx b/src/components/planner/Toolbar/Toolbar.tsx index 279478e96..38cacffc3 100644 --- a/src/components/planner/Toolbar/Toolbar.tsx +++ b/src/components/planner/Toolbar/Toolbar.tsx @@ -49,7 +49,7 @@ const Toolbar: FC = ({ const { data: coursesData } = q; - const [{ loading, error, url }, update] = usePDF({ + const [{ error, url }, update] = usePDF({ document: ( { dataSet: T[]; - keys: any; + keys: string[]; threshold?: number; } diff --git a/src/components/search/search.ts b/src/components/search/search.ts index 57d2395c8..3e8b14176 100644 --- a/src/components/search/search.ts +++ b/src/components/search/search.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import { useEffect, useState } from 'react'; interface SearchParams { getData: () => Promise; @@ -23,14 +23,14 @@ const useSearch = ({ filterFn, constraints = [0, 20], }: SearchParams): SearchReturn => { - const [results, setResults] = React.useState([]); - const [err, setErr] = React.useState(); + const [results, setResults] = useState([]); + const [err, setErr] = useState(); const getResults = () => { return results; }; - React.useEffect(() => updateQuery(initialQuery), []); + useEffect(() => updateQuery(initialQuery), []); // TODO: Insert logc for filtering w/ chips // TODO: Update filtering code to deal with data from Nebula API diff --git a/src/components/template/CustomPlan.tsx b/src/components/template/CustomPlan.tsx index 4002e96f3..c27e4fe1d 100644 --- a/src/components/template/CustomPlan.tsx +++ b/src/components/template/CustomPlan.tsx @@ -290,7 +290,6 @@ export default function CustomPlan({ onDismiss }: { onDismiss: () => void }) { onValueChange={(value) => setMajor(value)} onInputChange={(query: string) => updateQuery(query)} options={results.map((major: { filMajor: string }) => major.filMajor)} - autoFocus >
@@ -433,7 +432,7 @@ export default function CustomPlan({ onDismiss }: { onDismiss: () => void }) { return ( <> Oops, we ran into an error! Please let us know on our{' '} - + discord {' '} to get it fixed as soon as possible. diff --git a/src/components/template/NewPlan.tsx b/src/components/template/NewPlan.tsx index 9f1f56b96..537d569c5 100644 --- a/src/components/template/NewPlan.tsx +++ b/src/components/template/NewPlan.tsx @@ -14,7 +14,7 @@ export default function CustomPlan({ onDismiss }: { onDismiss: () => void }) { const [major, setMajor] = useState(null); const [planNameError, setPlanNameError] = useState(false); const [majorError, setMajorError] = useState(false); - const { majors, err } = useMajors(); + const { majors } = useMajors(); const setErrors = () => { setPlanNameError(name === ''); setMajorError(major === null); @@ -106,7 +106,6 @@ export default function CustomPlan({ onDismiss }: { onDismiss: () => void }) { onValueChange={(value) => setMajor(value)} onInputChange={(query: string) => updateQuery(query)} options={results.map((major: { filMajor: string }) => major.filMajor)} - autoFocus >
diff --git a/src/components/template/TemplateView.tsx b/src/components/template/TemplateView.tsx index b9880a44e..c5f1470fd 100644 --- a/src/components/template/TemplateView.tsx +++ b/src/components/template/TemplateView.tsx @@ -1,6 +1,5 @@ import router from 'next/router'; -import { useState } from 'react'; //nprogress module -import React from 'react'; +import React, { useEffect, useState } from 'react'; //nprogress module import AutoCompleteMajor from '@/components/AutoCompleteMajor'; import { trpc } from '@/utils/trpc'; @@ -45,7 +44,7 @@ export default function TemplateView({ onDismiss }: { onDismiss: () => void }) { constraints: [0, 100], }); - React.useEffect(() => { + useEffect(() => { updateQuery(''); }, [isLoading]); @@ -130,7 +129,6 @@ export default function TemplateView({ onDismiss }: { onDismiss: () => void }) { onValueChange={(value) => setMajor(value)} onInputChange={(query: string) => updateQuery(query)} options={results.map((major: { filMajor: string }) => major.filMajor)} - autoFocus > diff --git a/src/icons/ContactIcon.tsx b/src/icons/ContactIcon.tsx deleted file mode 100644 index d8ac2e19d..000000000 --- a/src/icons/ContactIcon.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { FC, SVGProps } from 'react'; - -const ContactIcon: FC> = (props) => { - return ( - - - - ); -}; - -export default ContactIcon; diff --git a/src/icons/DuplicateIcon.tsx b/src/icons/DuplicateIcon.tsx index 72b6ed999..fae2ab75a 100644 --- a/src/icons/DuplicateIcon.tsx +++ b/src/icons/DuplicateIcon.tsx @@ -2,7 +2,14 @@ import { FC, SVGProps } from 'react'; const DuplicateIcon: FC> = (props) => { return ( - + > = (props) => { - return ( - - - - ); -}; - -export default FeedbackIcon; diff --git a/src/icons/FilterIcon.tsx b/src/icons/FilterIcon.tsx index b948a7456..83cc86d66 100644 --- a/src/icons/FilterIcon.tsx +++ b/src/icons/FilterIcon.tsx @@ -2,7 +2,14 @@ import { FC, SVGProps } from 'react'; const FilterIcon: FC> = (props) => { return ( - + > = (props) => { return ( - + ): JSX.Element { return ( - + > = (props) => { fill="inherit" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" + {...props} > diff --git a/src/icons/LogoutIcon.tsx b/src/icons/LogoutIcon.tsx index e4625d5f9..278f89ca5 100644 --- a/src/icons/LogoutIcon.tsx +++ b/src/icons/LogoutIcon.tsx @@ -2,7 +2,14 @@ import { SVGProps } from 'react'; export default function LogoutIcon(props: SVGProps): JSX.Element { return ( - + ): JSX.Element { return ( - + ): JSX.Element { return ( - + ): JSX.Element { return ( - + (initialOnboardingData); - const [isModifyLoading, setIsModifyLoading] = React.useState(false); + const [isModifyLoading, setIsModifyLoading] = useState(false); const handleOnboardingDataUpdate = (updatedFields: Partial) => { setOnboardingData({ ...onboardingData, ...updatedFields }); @@ -69,8 +69,6 @@ export default function OnboardingPage() { const [page, setPage] = useState(0); const [validate, setValidate] = useState([true, true, true]); - const [validNextPage, setValidNextPage] = useState(false); - const router = useRouter(); const validateForm = (value: boolean) => { @@ -100,7 +98,6 @@ export default function OnboardingPage() { key={0} handleChange={handleOnboardingDataUpdate as (updatedFields: Partial) => void} data={{ name, startSemester, endSemester }} - semesterOptions={{ startSemesters, endSemesters }} handleValidate={validateForm} />, ]; @@ -112,9 +109,7 @@ export default function OnboardingPage() { } }; - useEffect(() => { - setValidNextPage(validate[page]); - }); + const validNextPage = validate[page]; // TODO: Find better way to structure this glorified form. return ( diff --git a/src/pages/auth/login.tsx b/src/pages/auth/login.tsx index b03e94ef9..19b4c17c7 100644 --- a/src/pages/auth/login.tsx +++ b/src/pages/auth/login.tsx @@ -42,7 +42,7 @@ export function AuthPage(props: { const inputRef = useRef(null); // Fetch courses here and put in cache - const q = trpc.courses.publicGetAllCourses.useQuery(undefined, { + trpc.courses.publicGetAllCourses.useQuery(undefined, { staleTime: Infinity, cacheTime: Infinity, refetchOnWindowFocus: false, diff --git a/src/server/trpc/router/plan.ts b/src/server/trpc/router/plan.ts index 18279d88c..3371bb180 100644 --- a/src/server/trpc/router/plan.ts +++ b/src/server/trpc/router/plan.ts @@ -527,7 +527,8 @@ export const planRouter = router({ const bypasses = [...degreeRequirements.bypasses, requirement]; - const updatedDegreeRequirements = await ctx.prisma.degreeRequirements.update({ + //update degree plan requirements + await ctx.prisma.degreeRequirements.update({ where: { plan: { userId: ctx.session.user.id }, id: degreeRequirements.id, diff --git a/src/utils/useTaskQueue.ts b/src/utils/useTaskQueue.ts index f337de2a5..52c4bf7da 100644 --- a/src/utils/useTaskQueue.ts +++ b/src/utils/useTaskQueue.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import { useEffect, useRef } from 'react'; export type Task = { func: (args: T) => Promise; @@ -15,9 +15,9 @@ export function useTaskQueue(params: { shouldProcess: boolean }): { isProcessing: boolean; addTask: (task: Task) => void; } { - const queue = React.useRef({ isProcessing: false, tasks: [] }); + const queue = useRef({ isProcessing: false, tasks: [] }); - React.useEffect(() => { + useEffect(() => { if (!params.shouldProcess || queue.current.tasks.length === 0) return; if (queue.current.isProcessing) return; diff --git a/vercel-build-ignore.sh b/vercel-build-ignore.sh index 7965f5664..a56a24062 100644 --- a/vercel-build-ignore.sh +++ b/vercel-build-ignore.sh @@ -1,3 +1,4 @@ +# This cancels Vercel builds until manually started to avoid too many Neon branhces echo "VERCEL_GIT_COMMIT_REF: $VERCEL_GIT_COMMIT_REF" if [[ "$VERCEL_GIT_COMMIT_REF" == "develop" || "$VERCEL_GIT_COMMIT_REF" == "main" ]] ; then