diff --git a/.github/workflows/aws-cd.yml b/.github/workflows/aws-cd.yml index 9f992a6ac..d176b8d9f 100644 --- a/.github/workflows/aws-cd.yml +++ b/.github/workflows/aws-cd.yml @@ -35,7 +35,7 @@ jobs: # Build a docker container and # push it to ECR so that it can # be deployed to ECS. - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f infrastructure/prod/Dockerfile.server . + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG --build-arg="COMMIT=$IMAGE_TAG" --build-arg="BUILD_TIMESTAMP=$(date +%s)" --build-arg="COMMIT_MESSAGE=$(git --no-pager show -s --format=%s)" -f infrastructure/prod/Dockerfile.server . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT @@ -49,7 +49,7 @@ jobs: # Build a docker container and # push it to ECR so that it can # be deployed to ECS. - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f infrastructure/prod/Dockerfile.app . + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG --build-arg="COMMIT=$IMAGE_TAG" --build-arg="BUILD_TIMESTAMP=$(date +%s)" --build-arg="COMMIT_MESSAGE=$(git --no-pager show -s --format=%s)" -f infrastructure/prod/Dockerfile.app . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT @@ -57,7 +57,7 @@ jobs: run: | # - create new revision for task definition with latest image. # - redeploy ECS services with the latest revision. - ./infrastructure/prod/redeploy.sh staging + ./infrastructure/prod/redeploy.sh staging local-head both - name: Logout of Amazon ECR if: always() diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dd35f955a..67045a751 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,9 +14,9 @@ jobs: node-version: 16 - uses: bahmutov/npm-install@v1 - run: yarn lint - run-api-v2-tests: + run-api-tests: runs-on: ubuntu-latest - name: API V2 Tests + name: API Tests steps: - name: Checkout code uses: actions/checkout@v3 @@ -26,7 +26,7 @@ jobs: node-version: "16" - run: yarn install --immutable - name: Run Jest Tests - run: yarn packages/api-v2 test + run: yarn packages/api test build-frontend-image: runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index d03cbc744..ea37e1e09 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,17 @@ GraduateNU aims to empower Northeastern students to customize their plan of stud 1. Make sure you run the dev postgres database using `yarn dev:db:up`. Run `yarn dev:db:down` when you're done. -2. Make a copy of the packages/api-v2/.env.development and name it .env.development.local. Fill in placeholder secrets. +2. Make a copy of the packages/api/.env.development and name it .env.development.local. Fill in placeholder secrets. -3. Make a copy of the packages/api-v2/.env.testing and name it .env.testing.local. for running BE tests +3. Make a copy of the packages/api/.env.testing and name it .env.testing.local. for running BE tests -4. If you haven't run migrations in a while or this is a fresh repo, then run the migrations for the the api using `yarn packages/api-v2 dev:migration:run`. See packages/API-V2/README for more info. +4. If you haven't run migrations in a while or this is a fresh repo, then run the migrations for the the api using `yarn packages/api dev:migration:run`. See packages/API/README for more info. -5. Then run the new version of the application by running `yarn dev:v2` at the root of the project. This starts up a NestJS server + a NextJS frontend + a Proxy. The proxy listens on port [3002](http://localhost:3002/), forwards /api requests to the NestJS server running on port 3001, and all other requests to the frontend running on port 3000. +5. Then run the new version of the application by running `yarn dev` at the root of the project. This starts up a NestJS server + a NextJS frontend + a Proxy. The proxy listens on port [3002](http://localhost:3002/), forwards /api requests to the NestJS server running on port 3001, and all other requests to the frontend running on port 3000. 6. Visit [http://localhost:3002](http://localhost:3002/) to view the app. -To run the two separately, visit the frontend and api-v2 packages(sub directories of the monorepo). +To run the two separately, visit the frontend and api packages(sub directories of the monorepo). ## Running the prod builds in prod docker configurations locally @@ -43,9 +43,9 @@ Example: `yarn packages/frontend lint` The workspaces within this monorepo are: -1. **frontend-v2**: A Next.js web UI. It is what users see when they visit our application. +1. **frontend**: A Next.js web UI. It is what users see when they visit our application. -2. **api-v2**: A Nest.js API reponsible for storing and managing Graduate's data. Our frontend leans on our api for data related services. +2. **api**: A Nest.js API reponsible for storing and managing Graduate's data. Our frontend leans on our api for data related services. 3. **api-client:** A typescript client responsible for providing a streamlined and typed interface to interact with our API. The frontend uses this client to send request to our API. diff --git a/infrastructure/develop/docker-compose.api.yml b/infrastructure/develop/docker-compose.api.yml index e8e9a4ff4..67d88355c 100644 --- a/infrastructure/develop/docker-compose.api.yml +++ b/infrastructure/develop/docker-compose.api.yml @@ -25,7 +25,7 @@ services: ports: - 4001:3001 env_file: - - "../../packages/api-v2/.env.development" + - "../../packages/api/.env.development" # env vars here override the env vars in the env_file above environment: # we are no longer connecting to the db on localhost, the host is the name of the container diff --git a/infrastructure/prod/Dockerfile.app b/infrastructure/prod/Dockerfile.app index 48deb0d53..a062fab5b 100644 --- a/infrastructure/prod/Dockerfile.app +++ b/infrastructure/prod/Dockerfile.app @@ -10,7 +10,7 @@ WORKDIR /app # package.json of root and of needed packages COPY package.json yarn.lock babel.config.js .yarnrc.yml ./ COPY .yarn .yarn -COPY packages/frontend-v2/package.json packages/frontend-v2/package.json +COPY packages/frontend/package.json packages/frontend/package.json COPY packages/api-client/package.json packages/api-client/package.json COPY packages/common/package.json packages/common/package.json @@ -18,13 +18,21 @@ COPY packages/common/package.json packages/common/package.json RUN yarn install > /dev/null # Get src files -COPY packages/frontend-v2 packages/frontend-v2 +COPY packages/frontend packages/frontend COPY packages/api-client packages/api-client COPY packages/common packages/common +ARG COMMIT +ARG BUILD_TIMESTAMP +ARG COMMIT_MESSAGE + +ENV NEXT_PUBLIC_COMMIT_HASH $COMMIT +ENV NEXT_PUBLIC_COMMIT_MESSAGE $COMMIT_MESSAGE +ENV NEXT_PUBLIC_BUILD_TIMESTAMP $BUILD_TIMESTAMP + RUN yarn packages/api-client build RUN yarn packages/common build -RUN yarn packages/frontend-v2 build +RUN yarn packages/frontend build # Production image FROM node:16-alpine AS runner @@ -36,9 +44,9 @@ RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules -COPY --from=builder --chown=nextjs:nodejs /app/packages/frontend-v2/public ./public -COPY --from=builder --chown=nextjs:nodejs /app/packages/frontend-v2/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/packages/frontend-v2/.next/static ./.next/static +COPY --from=builder --chown=nextjs:nodejs /app/packages/frontend/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/packages/frontend/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/packages/frontend/.next/static ./.next/static USER nextjs diff --git a/infrastructure/prod/Dockerfile.server b/infrastructure/prod/Dockerfile.server index 325d54ffc..7ae62147d 100644 --- a/infrastructure/prod/Dockerfile.server +++ b/infrastructure/prod/Dockerfile.server @@ -5,24 +5,31 @@ WORKDIR /server # package.json of root and of needed packages COPY package.json yarn.lock babel.config.js .yarnrc.yml ./ COPY .yarn .yarn -COPY packages/api-v2/package.json packages/api-v2/package.json +COPY packages/api/package.json packages/api/package.json COPY packages/common/package.json packages/common/package.json # Install at root level RUN yarn install > /dev/null # Get src files -COPY packages/api-v2 packages/api-v2 +COPY packages/api packages/api COPY packages/common packages/common # Build server and common dependency RUN yarn packages/common build -RUN yarn packages/api-v2 build +RUN yarn packages/api build FROM node:16-alpine AS runner WORKDIR /server +ARG COMMIT +ARG COMMIT_MESSAGE +ARG BUILD_TIMESTAMP + ENV NODE_ENV production +ENV COMMIT_HASH $COMMIT +ENV COMMIT_MESSAGE $COMMIT_MESSAGE +ENV BUILD_TIMESTAMP $BUILD_TIMESTAMP RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nestjs diff --git a/infrastructure/prod/README.md b/infrastructure/prod/README.md new file mode 100644 index 000000000..248e309f8 --- /dev/null +++ b/infrastructure/prod/README.md @@ -0,0 +1,36 @@ +# How to do a Manual Deploy (to staging or prod). + +Note: All of the `yarn` scripts in this guide are in the root `package.json`. + +**This will deploy the current files as they are in your local repo**. They will be tagged with the local HEAD commit. + +If you don't want this, you'll need to run modified calls rather than the package.json scripts directly. + +## Prereqs: + +- Install Docker +- Install the AWS CLI +- Have an AWS Access Key ID and AWS Secret Access Key pair. +- Run `aws configure`, and input those keys along with us-east-1 as the region. You can set the Default Output Format to json. + +## Steps: + +1. Run `yarn frontend:build` and/or `yarn backend:build` depending on what you want to build. +2. Run `yarn frontend:run` and/or `yarn backend:run` to test that the image runs (the backend won't be able to connect to the database which is expected, and the frontend will not connect to the backend). +3. You may have to run `yarn ecr:docker:auth` to reauthenticate Docker with AWS ECR. (There is no harm in running this command unnecessarily, so if you're unsure you can just run it). +4. If the images run successfully, run `yarn frontend:push` and/or `yarn backend:push` to push the built images to AWS ECR. +5. Run `./infrastructure/prod/redeploy.sh local-head ` + + `` is either `prod` or `staging`. + `` is one of `frontend`, `backend`, or `both`. + +# How to Manually Redeploy a Previously Deployed Commit + +If you know that a commit already has an image pushed to ECR (ex. the commit is in main, or you already pushed the image using the above steps) and you want to deploy that particular image to staging or prod, you can use the redeploy.sh script. + +1. Find the commit's hash (looks like this: 1445f9533c4ec9a7324c721f09fc5ccec1542d8d). +2. Run `./infrastructure/prod/redeploy.sh ` + + `` is either `prod` or `staging`. + `` is the hash of the commit you just found. + `` is one of `frontend`, `backend`, or `both`. diff --git a/infrastructure/prod/entrypoint.server.sh b/infrastructure/prod/entrypoint.server.sh index a2d9d4b22..afd35b35c 100644 --- a/infrastructure/prod/entrypoint.server.sh +++ b/infrastructure/prod/entrypoint.server.sh @@ -1,6 +1,7 @@ #!/bin/sh -cd packages/api-v2 +cd packages/api +echo "Running on commit: $COMMIT_HASH" yarn typeorm migration:run exec "$@" diff --git a/infrastructure/prod/get-ecr-image-name.sh b/infrastructure/prod/get-ecr-image-name.sh new file mode 100755 index 000000000..a8f946ff7 --- /dev/null +++ b/infrastructure/prod/get-ecr-image-name.sh @@ -0,0 +1,34 @@ +if [[ $# != 2 ]]; then + echo "Usage: get-ecr-image-name.sh " + exit 1 +fi + +# Accept a few different formats for the commit hash. +if [[ ${#1} = 40 ]]; then + ECR_IMAGE_COMMIT_HASH=$1 +elif [[ $1 = "latest-main" ]]; then + ECR_IMAGE_COMMIT_HASH=$(git ls-remote https://github.com/sandboxnu/graduatenu.git main | awk '{ print $1 }') +elif [[ $1 = "local-head" ]]; then + ECR_IMAGE_COMMIT_HASH=$(git rev-parse HEAD) +else + echo "ERROR: Invalid commit '$1'" + exit 1 +fi + +if [[ $2 = "frontend" ]]; then + REPO="graduatenu-node" +elif [[ $2 = "backend" ]]; then + REPO="graduatenu-rails" +else + echo "Please choose a service to create an image name for: 'frontend' or 'backend'" + exit 1 +fi + +AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) +AWS_DEFAULT_REGION="us-east-1" + +ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com" + +ECR_IMAGE_NAME="${ECR_REGISTRY}/${REPO}:${ECR_IMAGE_COMMIT_HASH}" + +echo $ECR_IMAGE_NAME diff --git a/infrastructure/prod/redeploy.sh b/infrastructure/prod/redeploy.sh index e0932d44f..e3c135af7 100755 --- a/infrastructure/prod/redeploy.sh +++ b/infrastructure/prod/redeploy.sh @@ -1,35 +1,65 @@ #!/bin/bash +if [[ $# != 3 ]]; then + echo "Usage: redeploy.sh " + exit 1 +fi + if [[ ! " prod staging " =~ " $1 " ]]; then echo "Please provide environment to use: prod or staging" exit 1 fi +# Accept a few different formats for the commit hash. +if [[ ${#2} = 40 ]]; then + ECR_IMAGE_COMMIT_HASH=$2 +elif [[ $2 = "latest-main" ]]; then + ECR_IMAGE_COMMIT_HASH=$(git ls-remote https://github.com/sandboxnu/graduatenu.git main | awk '{ print $1 }') +elif [[ $2 = "local-head" ]]; then + ECR_IMAGE_COMMIT_HASH=$(git rev-parse HEAD) +else + echo "ERROR: Invalid commit '$2'" + exit 1 +fi + +# Only deploy the service specified. +if [[ $3 = "frontend" ]]; then + DEPLOY_INDEXES=(1) +elif [[ $3 = "backend" ]]; then + DEPLOY_INDEXES=(0) +elif [[ $3 = "both" ]]; then + DEPLOY_INDEXES=(0 1) +else + echo "Please choose a service to deploy: 'frontend', 'backend', or 'both'" + exit 1 +fi + +echo "Deploying $3 repo(s) to $1 with commit "$2" ($ECR_IMAGE_COMMIT_HASH)" + AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) AWS_DEFAULT_REGION="us-east-1" REPOS=( "graduatenu-rails" "graduatenu-node" ) -LATEST_HASH=$(git ls-remote https://github.com/sandboxnu/graduatenu.git main | awk '{ print $1 }') ECS_CLUSTER="$1-graduatenu" TASK_FAMILIES=( "${ECS_CLUSTER}-api" "${ECS_CLUSTER}-web" ) SERVICES=( "${ECS_CLUSTER}-api" "${ECS_CLUSTER}-web" ) + # Disable aws from sending stdout to less export AWS_PAGER="" -echo "Redeploying services for cluster: ${ECS_CLUSTER} with last pushed image" - - -for i in "${!REPOS[@]}"; do +for service_index in "${!DEPLOY_INDEXES[@]}"; do + echo "Deploying ${REPOS[DEPLOY_INDEXES[service_index]]}..." + i=${DEPLOY_INDEXES[service_index]} # Last pushed image should always be tagged with the latest commit hash on main - ECR_IMAGE="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${REPOS[$i]}:${LATEST_HASH}" + ECR_IMAGE="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${REPOS[$i]}:${ECR_IMAGE_COMMIT_HASH}" TASK_FAMILY="${TASK_FAMILIES[$i]}" SERVICE="${SERVICES[$i]}" # fetch template for task definition TASK_DEFINITION=$(aws ecs describe-task-definition --task-definition "$TASK_FAMILY" --region "$AWS_DEFAULT_REGION") # update the template's image to use the latest ECR_IMAGE - NEW_TASK_DEFINTIION=$(echo $TASK_DEFINITION | jq --arg IMAGE "$ECR_IMAGE" '.taskDefinition | .containerDefinitions[0].image = $IMAGE | del(.taskDefinitionArn) | del(.revision) | del(.status) | del(.requiresAttributes) | del(.compatibilities) | del(.registeredAt) | del(.registeredBy)') + NEW_TASK_DEFINITION=$(echo $TASK_DEFINITION | jq --arg IMAGE "$ECR_IMAGE" '.taskDefinition | .containerDefinitions[0].image = $IMAGE | del(.taskDefinitionArn) | del(.revision) | del(.status) | del(.requiresAttributes) | del(.compatibilities) | del(.registeredAt) | del(.registeredBy)') # register the new revision for the task definition - NEW_TASK_INFO=$(aws ecs register-task-definition --region "$AWS_DEFAULT_REGION" --cli-input-json "$NEW_TASK_DEFINTIION") + NEW_TASK_INFO=$(aws ecs register-task-definition --region "$AWS_DEFAULT_REGION" --cli-input-json "$NEW_TASK_DEFINITION") NEW_REVISION=$(echo $NEW_TASK_INFO | jq '.taskDefinition.revision') # update the service to replace tasks with the latest revision using the latest image aws ecs update-service --cluster ${ECS_CLUSTER} \ diff --git a/package.json b/package.json index cde392aaa..cbcf165d8 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ ] }, "scripts": { - "dev:v2": "concurrently \"yarn foreach '@graduate/(common|api-v2|frontend-v2|api-client)' run dev\" \"yarn dev:proxy\"", - "prod:v2": "concurrently \"yarn foreach '@graduate/(common|api-v2|frontend-v2|api-client)' run prod\" \"yarn dev:proxy\"", + "dev": "concurrently \"yarn foreach '@graduate/(common|api|frontend|api-client)' run dev\" \"yarn dev:proxy\"", + "prod": "concurrently \"yarn foreach '@graduate/(common|api|frontend|api-client)' run prod\" \"yarn dev:proxy\"", "foreach": "yarn workspaces foreach --parallel --verbose --interlaced --include", "dev:proxy": "node infrastructure/develop/dev-proxy.js", "dev:db:up": "docker compose -f infrastructure/develop/docker-compose.db.yml up -d", @@ -22,6 +22,13 @@ "backend:docker:build": "docker compose -f infrastructure/develop/docker-compose.api.yml build", "backend:docker:run": "docker compose -f infrastructure/develop/docker-compose.api.yml up -d", "backend:docker:down": "docker compose -f infrastructure/develop/docker-compose.api.yml down", + "frontend:build": "docker build --platform linux/amd64 --build-arg=\"COMMIT=$(git rev-parse HEAD)\" --build-arg=\"BUILD_TIMESTAMP=$(date +%s)\" --build-arg=\"COMMIT_MESSAGE=$(git --no-pager show -s --format=%s)\" -t $(sh ./infrastructure/prod/get-ecr-image-name.sh local-head frontend) -f ./infrastructure/prod/Dockerfile.app .", + "frontend:run": "docker run -p 4000:3000 $(sh ./infrastructure/prod/get-ecr-image-name.sh local-head frontend)", + "frontend:push": "docker push $(sh ./infrastructure/prod/get-ecr-image-name.sh local-head frontend)", + "backend:build": "docker build --platform linux/amd64 --build-arg=\"COMMIT=$(git rev-parse HEAD)\" --build-arg=\"BUILD_TIMESTAMP=$(date +%s)\" --build-arg=\"COMMIT_MESSAGE=$(git --no-pager show -s --format=%s)\" -t $(sh ./infrastructure/prod/get-ecr-image-name.sh local-head backend) -f ./infrastructure/prod/Dockerfile.server .", + "backend:run": "docker run -p 4001:3001 $(sh ./infrastructure/prod/get-ecr-image-name.sh local-head backend)", + "backend:push": "docker push $(sh ./infrastructure/prod/get-ecr-image-name.sh local-head backend)", + "ecr:docker:auth": "aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com", "lint": "eslint packages/ --ext .ts,.tsx .", "tsc": "yarn workspaces foreach -v --exclude . run tsc", "g:babel": "cd $INIT_CWD && babel", @@ -31,6 +38,7 @@ "dependencies": { "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-decorators": "^7.17.8", + "@nestjs/throttler": "^5.0.1", "cross-env": "^7.0.3", "nodemailer": "^6.9.1" }, diff --git a/packages/api-client/index.ts b/packages/api-client/index.ts index 0dbf641b0..6ce17f3d8 100644 --- a/packages/api-client/index.ts +++ b/packages/api-client/index.ts @@ -20,6 +20,8 @@ import { ForgotPasswordDto, ResetPasswordDto, courseToString, + NUPathEnum, + GetMetaInfoResponse, } from "@graduate/common"; import { ClassConstructor, plainToInstance } from "class-transformer"; @@ -103,6 +105,11 @@ class APIClient { getSupportedMajors: (): Promise => this.req("GET", `/majors/supportedMajors`, GetSupportedMajorsResponse), }; + + meta = { + getInfo: (): Promise => + this.req("GET", "/meta/info", GetMetaInfoResponse), + }; } /** @@ -132,6 +139,7 @@ interface SearchClass { subject: string; prereqs?: INEUAndReq | INEUOrReq; coreqs?: INEUAndReq | INEUOrReq; + nupath?: NUPathEnum[]; maxCredits: number; minCredits: number; termId: string; @@ -189,6 +197,7 @@ function occurrenceToCourse(occurrence: SearchClass): ScheduleCourse2 { numCreditsMin: occurrence.minCredits, prereqs: occurrence.prereqs, coreqs: occurrence.coreqs, + nupaths: occurrence.nupath, id: null, }; } @@ -229,6 +238,7 @@ class SearchAPIClient { minCredits prereqs coreqs + nupath } } }`, @@ -269,6 +279,7 @@ class SearchAPIClient { maxCredits prereqs coreqs + nupath termId } } @@ -353,7 +364,7 @@ class SearchAPIClient { search(termId:"${termId}", query: "${searchQuery}", classIdRange: {min: ${minIndex}, max: ${maxIndex}}) { totalCount pageInfo { hasNextPage } - nodes { ... on ClassOccurrence { name subject maxCredits minCredits prereqs coreqs classId + nodes { ... on ClassOccurrence { name subject maxCredits minCredits prereqs coreqs nupath classId } } } @@ -371,6 +382,7 @@ class SearchAPIClient { subject: result.subject, prereqs: result.prereqs, coreqs: result.coreqs, + nupaths: result.nupath, numCreditsMin: result.minCredits, numCreditsMax: result.maxCredits, }; diff --git a/packages/api-v2/migrations/1678404621776-EmailConfirmation.ts b/packages/api-v2/migrations/1678404621776-EmailConfirmation.ts deleted file mode 100644 index 9e00c4a57..000000000 --- a/packages/api-v2/migrations/1678404621776-EmailConfirmation.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {MigrationInterface, QueryRunner} from "typeorm"; - -export class EmailConfirmation1678404621776 implements MigrationInterface { - name = 'EmailConfirmation1678404621776' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "student" ADD "isEmailConfirmed" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "student" DROP COLUMN "isEmailConfirmed"`); - } - -} diff --git a/packages/api-v2/migrations/1680386712510-OptionalMajorInPlan.ts b/packages/api-v2/migrations/1680386712510-OptionalMajorInPlan.ts deleted file mode 100644 index bfd6a6a11..000000000 --- a/packages/api-v2/migrations/1680386712510-OptionalMajorInPlan.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {MigrationInterface, QueryRunner} from "typeorm"; - -export class OptionalMajorInPlan1680386712510 implements MigrationInterface { - name = 'OptionalMajorInPlan1680386712510' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "plan" ALTER COLUMN "major" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "plan" ALTER COLUMN "catalogYear" DROP NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "plan" ALTER COLUMN "catalogYear" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "plan" ALTER COLUMN "major" SET NOT NULL`); - } - -} diff --git a/packages/api-v2/.env.development b/packages/api/.env.development similarity index 100% rename from packages/api-v2/.env.development rename to packages/api/.env.development diff --git a/packages/api-v2/.env.testing b/packages/api/.env.testing similarity index 100% rename from packages/api-v2/.env.testing rename to packages/api/.env.testing diff --git a/packages/api-v2/.eslintrc.js b/packages/api/.eslintrc.js similarity index 100% rename from packages/api-v2/.eslintrc.js rename to packages/api/.eslintrc.js diff --git a/packages/api-v2/README.md b/packages/api/README.md similarity index 95% rename from packages/api-v2/README.md rename to packages/api/README.md index 117626124..7fcab0b61 100644 --- a/packages/api-v2/README.md +++ b/packages/api/README.md @@ -1,10 +1,10 @@ -# GraduateNU API V2 +# GraduateNU API ## Running the API 1. Run `yarn dev:db:up` from the project root to run the database in docker. You can run `yarn dev:db:down` from the project root to take down the database when you're done. -2. Run `yarn dev` from within the api-v2 directory to run the server in watch mode. +2. Run `yarn dev` from within the api directory to run the server in watch mode. ## Testing diff --git a/packages/api-v2/migrations/1675976227120-InitialMigration.ts b/packages/api/migrations/1675976227120-InitialMigration.ts similarity index 100% rename from packages/api-v2/migrations/1675976227120-InitialMigration.ts rename to packages/api/migrations/1675976227120-InitialMigration.ts diff --git a/packages/api/migrations/1678404621776-EmailConfirmation.ts b/packages/api/migrations/1678404621776-EmailConfirmation.ts new file mode 100644 index 000000000..bc2653566 --- /dev/null +++ b/packages/api/migrations/1678404621776-EmailConfirmation.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class EmailConfirmation1678404621776 implements MigrationInterface { + name = "EmailConfirmation1678404621776"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "student" ADD "isEmailConfirmed" boolean NOT NULL DEFAULT false` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "student" DROP COLUMN "isEmailConfirmed"` + ); + } +} diff --git a/packages/api/migrations/1680386712510-OptionalMajorInPlan.ts b/packages/api/migrations/1680386712510-OptionalMajorInPlan.ts new file mode 100644 index 000000000..d2839c8f0 --- /dev/null +++ b/packages/api/migrations/1680386712510-OptionalMajorInPlan.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class OptionalMajorInPlan1680386712510 implements MigrationInterface { + name = "OptionalMajorInPlan1680386712510"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "plan" ALTER COLUMN "major" DROP NOT NULL` + ); + await queryRunner.query( + `ALTER TABLE "plan" ALTER COLUMN "catalogYear" DROP NOT NULL` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "plan" ALTER COLUMN "catalogYear" SET NOT NULL` + ); + await queryRunner.query( + `ALTER TABLE "plan" ALTER COLUMN "major" SET NOT NULL` + ); + } +} diff --git a/packages/api-v2/nest-cli.json b/packages/api/nest-cli.json similarity index 100% rename from packages/api-v2/nest-cli.json rename to packages/api/nest-cli.json diff --git a/packages/api-v2/ormconfig.ts b/packages/api/ormconfig.ts similarity index 100% rename from packages/api-v2/ormconfig.ts rename to packages/api/ormconfig.ts diff --git a/packages/api-v2/package.json b/packages/api/package.json similarity index 99% rename from packages/api-v2/package.json rename to packages/api/package.json index 66f22a62d..60c4e593b 100644 --- a/packages/api-v2/package.json +++ b/packages/api/package.json @@ -1,5 +1,5 @@ { - "name": "@graduate/api-v2", + "name": "@graduate/api", "version": "0.0.1", "description": "", "author": "", diff --git a/packages/api-v2/src/app.module.ts b/packages/api/src/app.module.ts similarity index 69% rename from packages/api-v2/src/app.module.ts rename to packages/api/src/app.module.ts index 6b8c20b91..6ca2109b1 100644 --- a/packages/api-v2/src/app.module.ts +++ b/packages/api/src/app.module.ts @@ -5,10 +5,12 @@ import ormconfig from "../ormconfig"; import { StudentModule } from "./student/student.module"; import { AuthModule } from "./auth/auth.module"; import { PlanModule } from "./plan/plan.module"; -import { APP_INTERCEPTOR } from "@nestjs/core"; +import { APP_GUARD, APP_INTERCEPTOR } from "@nestjs/core"; import { LoggingInterceptor } from "./interceptors/logging.interceptor"; import { MajorModule } from "./major/major.module"; import { EmailModule } from "./email/email.module"; +import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler"; +import { MetaModule } from "./meta/meta.module"; @Module({ imports: [ @@ -17,17 +19,28 @@ import { EmailModule } from "./email/email.module"; envFilePath: [`.env.${process.env.NODE_ENV}.local`], isGlobal: true, }), + ThrottlerModule.forRoot([ + { + ttl: 60000, // no more than 100 requests in a minute + limit: 100, + }, + ]), StudentModule, AuthModule, PlanModule, MajorModule, EmailModule, + MetaModule, ], providers: [ { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor, }, + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, ], }) export class AppModule {} diff --git a/packages/api-v2/src/auth/auth.controller.ts b/packages/api/src/auth/auth.controller.ts similarity index 90% rename from packages/api-v2/src/auth/auth.controller.ts rename to packages/api/src/auth/auth.controller.ts index fbc8f6825..cdfdaabcc 100644 --- a/packages/api-v2/src/auth/auth.controller.ts +++ b/packages/api/src/auth/auth.controller.ts @@ -22,14 +22,16 @@ import { weakPasswordError, } from "@graduate/common"; import { Response } from "express"; -import EmailConfirmationService from "src/emailConfirmation/emailConfirmation.service"; +import EmailConfirmationService from "../emailConfirmation/emailConfirmation.service"; import { EmailAlreadyExists, EmailNotConfirmed, NoSuchEmail, WeakPassword, -} from "src/student/student.errors"; +} from "../student/student.errors"; import { BadToken, InvalidPayload, TokenExpiredError } from "./auth.errors"; +import { Throttle } from "@nestjs/throttler"; +import { COOKIE_DOMAIN } from "../constants"; @Controller("auth") export class AuthController { @@ -60,11 +62,13 @@ export class AuthController { const { accessToken } = student; const isSecure = process.env.NODE_ENV !== "development"; + // Store JWT token in a cookie response.cookie("auth_cookie", accessToken, { httpOnly: true, sameSite: "strict", secure: isSecure, + domain: COOKIE_DOMAIN, }); if (process.env.NODE_ENV !== "testing") { await this.emailConfirmationService.sendVerificationLink( @@ -74,6 +78,7 @@ export class AuthController { return student; } + @Throttle({ default: { limit: 20, ttl: 60000 } }) // restrict to no more than 20 requests per minute @Post("login") public async login( @Res({ passthrough: true }) response: Response, @@ -88,11 +93,13 @@ export class AuthController { const { accessToken } = student; const isSecure = process.env.NODE_ENV !== "development"; + // Store JWT token in a cookie response.cookie("auth_cookie", accessToken, { httpOnly: true, sameSite: "strict", secure: isSecure, + domain: COOKIE_DOMAIN, }); return student; @@ -150,10 +157,12 @@ export class AuthController { @Res({ passthrough: true }) response: Response ): Promise { const isSecure = process.env.NODE_ENV !== "development"; + response.clearCookie("auth_cookie", { httpOnly: true, sameSite: "strict", secure: isSecure, + domain: COOKIE_DOMAIN, }); } } diff --git a/packages/api-v2/src/auth/auth.errors.ts b/packages/api/src/auth/auth.errors.ts similarity index 100% rename from packages/api-v2/src/auth/auth.errors.ts rename to packages/api/src/auth/auth.errors.ts diff --git a/packages/api-v2/src/auth/auth.module.ts b/packages/api/src/auth/auth.module.ts similarity index 93% rename from packages/api-v2/src/auth/auth.module.ts rename to packages/api/src/auth/auth.module.ts index 74bfc207a..9182f114a 100644 --- a/packages/api-v2/src/auth/auth.module.ts +++ b/packages/api/src/auth/auth.module.ts @@ -5,7 +5,7 @@ import { StudentModule } from "../student/student.module"; import { AuthController } from "./auth.controller"; import { AuthService } from "./auth.service"; import { JwtStrategy } from "./jwt.strategy"; -import { EmailModule } from "src/email/email.module"; +import { EmailModule } from "../email/email.module"; @Module({ imports: [ diff --git a/packages/api-v2/src/auth/auth.service.ts b/packages/api/src/auth/auth.service.ts similarity index 93% rename from packages/api-v2/src/auth/auth.service.ts rename to packages/api/src/auth/auth.service.ts index e53113ed0..d4d148030 100644 --- a/packages/api-v2/src/auth/auth.service.ts +++ b/packages/api/src/auth/auth.service.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt"; -import { Student } from "../../src/student/entities/student.entity"; -import { StudentService } from "../../src/student/student.service"; +import { Student } from "../student/entities/student.entity"; +import { StudentService } from "../student/student.service"; import { LoginStudentDto, ResetPasswordDto, @@ -9,16 +9,16 @@ import { } from "@graduate/common"; import { JwtPayload } from "./interfaces/jwt-payload"; import * as bcrypt from "bcrypt"; -import { formatServiceCtx } from "../../src/utils"; +import { formatServiceCtx } from "../utils"; import { EmailAlreadyExists, EmailNotConfirmed, NoSuchEmail, WeakPassword, -} from "src/student/student.errors"; +} from "../student/student.errors"; import { ConfigService } from "@nestjs/config"; -import { EnvironmentVariables } from "src/environment-variables"; -import EmailService from "src/email/email.service"; +import { EnvironmentVariables } from "../environment-variables"; +import EmailService from "../email/email.service"; import { BadToken, InvalidPayload, TokenExpiredError } from "./auth.errors"; @Injectable() diff --git a/packages/api-v2/src/auth/interfaces/authenticated-request.ts b/packages/api/src/auth/interfaces/authenticated-request.ts similarity index 68% rename from packages/api-v2/src/auth/interfaces/authenticated-request.ts rename to packages/api/src/auth/interfaces/authenticated-request.ts index 76b7c3b20..55eebb56c 100644 --- a/packages/api-v2/src/auth/interfaces/authenticated-request.ts +++ b/packages/api/src/auth/interfaces/authenticated-request.ts @@ -1,4 +1,4 @@ -import { Student } from "src/student/entities/student.entity"; +import { Student } from "../../student/entities/student.entity"; /** Represents an authenticated request using the JwtAuthGuard. */ export interface AuthenticatedRequest extends Request { diff --git a/packages/api-v2/src/auth/interfaces/jwt-payload.ts b/packages/api/src/auth/interfaces/jwt-payload.ts similarity index 100% rename from packages/api-v2/src/auth/interfaces/jwt-payload.ts rename to packages/api/src/auth/interfaces/jwt-payload.ts diff --git a/packages/api-v2/src/auth/jwt.strategy.ts b/packages/api/src/auth/jwt.strategy.ts similarity index 91% rename from packages/api-v2/src/auth/jwt.strategy.ts rename to packages/api/src/auth/jwt.strategy.ts index a9b148980..764eed471 100644 --- a/packages/api-v2/src/auth/jwt.strategy.ts +++ b/packages/api/src/auth/jwt.strategy.ts @@ -3,9 +3,9 @@ import { ConfigService } from "@nestjs/config"; import { PassportStrategy } from "@nestjs/passport"; import { Request } from "express"; import { Strategy } from "passport-jwt"; -import { EnvironmentVariables } from "../../src/environment-variables"; -import { Student } from "../../src/student/entities/student.entity"; -import { formatServiceCtx } from "../../src/utils"; +import { EnvironmentVariables } from "../environment-variables"; +import { Student } from "../student/entities/student.entity"; +import { formatServiceCtx } from "../utils"; import { AuthService } from "./auth.service"; import { JwtPayload } from "./interfaces/jwt-payload"; diff --git a/packages/api-v2/src/auth/test/auth.controller.spec.ts b/packages/api/src/auth/test/auth.controller.spec.ts similarity index 100% rename from packages/api-v2/src/auth/test/auth.controller.spec.ts rename to packages/api/src/auth/test/auth.controller.spec.ts diff --git a/packages/api-v2/src/auth/test/auth.service.spec.ts b/packages/api/src/auth/test/auth.service.spec.ts similarity index 100% rename from packages/api-v2/src/auth/test/auth.service.spec.ts rename to packages/api/src/auth/test/auth.service.spec.ts diff --git a/packages/api/src/constants.ts b/packages/api/src/constants.ts new file mode 100644 index 000000000..4af358ff8 --- /dev/null +++ b/packages/api/src/constants.ts @@ -0,0 +1,9 @@ +/** + * The root Domain on which all cookies should be set. (See: + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent) + * + * In production, this should be set to "graduatenu.com" which allows + * api.graduatenu.com to set cookies on every other *.graduatenu.com domain. + */ +export const COOKIE_DOMAIN = + process.env.NODE_ENV === "production" ? "graduatenu.com" : "localhost"; diff --git a/packages/api-v2/src/email/email.module.ts b/packages/api/src/email/email.module.ts similarity index 80% rename from packages/api-v2/src/email/email.module.ts rename to packages/api/src/email/email.module.ts index 29317a561..2dba0e478 100644 --- a/packages/api-v2/src/email/email.module.ts +++ b/packages/api/src/email/email.module.ts @@ -3,8 +3,8 @@ import { ConfigModule } from "@nestjs/config"; import EmailService from "./email.service"; import { JwtModule } from "@nestjs/jwt"; import EmailConfirmationService from "../emailConfirmation/emailConfirmation.service"; -import { StudentModule } from "src/student/student.module"; -import { EmailConfirmationController } from "src/emailConfirmation/emailConfirmation.controller"; +import { StudentModule } from "../student/student.module"; +import { EmailConfirmationController } from "../emailConfirmation/emailConfirmation.controller"; @Module({ imports: [ diff --git a/packages/api-v2/src/email/email.service.ts b/packages/api/src/email/email.service.ts similarity index 91% rename from packages/api-v2/src/email/email.service.ts rename to packages/api/src/email/email.service.ts index 25190cf54..0b8b35b1e 100644 --- a/packages/api-v2/src/email/email.service.ts +++ b/packages/api/src/email/email.service.ts @@ -2,7 +2,7 @@ import { Injectable } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { createTransport } from "nodemailer"; import Mail from "nodemailer/lib/mailer"; -import { EnvironmentVariables } from "src/environment-variables"; +import { EnvironmentVariables } from "../environment-variables"; @Injectable() export default class EmailService { diff --git a/packages/api-v2/src/emailConfirmation/emailConfirmation.controller.ts b/packages/api/src/emailConfirmation/emailConfirmation.controller.ts similarity index 86% rename from packages/api-v2/src/emailConfirmation/emailConfirmation.controller.ts rename to packages/api/src/emailConfirmation/emailConfirmation.controller.ts index 38012a3cb..47a6c99f9 100644 --- a/packages/api-v2/src/emailConfirmation/emailConfirmation.controller.ts +++ b/packages/api/src/emailConfirmation/emailConfirmation.controller.ts @@ -12,8 +12,8 @@ import { emailAlreadyConfirmed, unableToSendEmail, } from "@graduate/common"; -import { AuthenticatedRequest } from "src/auth/interfaces/authenticated-request"; -import { JwtAuthGuard } from "src/guards/jwt-auth.guard"; +import { AuthenticatedRequest } from "../auth/interfaces/authenticated-request"; +import { JwtAuthGuard } from "../guards/jwt-auth.guard"; import EmailConfirmationService from "./emailConfirmation.service"; import { EmailAlreadyConfirmed, @@ -34,8 +34,9 @@ export class EmailConfirmationController { if (email instanceof Error) { throw new BadRequestException(); } - const updateResult = - await this.emailConfirmationService.confirmEmail(email); + const updateResult = await this.emailConfirmationService.confirmEmail( + email + ); if (updateResult instanceof EmailAlreadyConfirmed) { throw new BadRequestException("Email is already confirmed"); } diff --git a/packages/api-v2/src/emailConfirmation/emailConfirmation.service.ts b/packages/api/src/emailConfirmation/emailConfirmation.service.ts similarity index 95% rename from packages/api-v2/src/emailConfirmation/emailConfirmation.service.ts rename to packages/api/src/emailConfirmation/emailConfirmation.service.ts index af0dec3c0..f302050da 100644 --- a/packages/api-v2/src/emailConfirmation/emailConfirmation.service.ts +++ b/packages/api/src/emailConfirmation/emailConfirmation.service.ts @@ -1,8 +1,8 @@ import { Injectable, Logger } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { JwtService } from "@nestjs/jwt"; -import { EnvironmentVariables } from "src/environment-variables"; -import { StudentService } from "src/student/student.service"; +import { EnvironmentVariables } from "../environment-variables"; +import { StudentService } from "../student/student.service"; import { UpdateResult } from "typeorm"; import EmailService from "../email/email.service"; import { diff --git a/packages/api-v2/src/emailConfirmation/emailConfirmationErrors.ts b/packages/api/src/emailConfirmation/emailConfirmationErrors.ts similarity index 100% rename from packages/api-v2/src/emailConfirmation/emailConfirmationErrors.ts rename to packages/api/src/emailConfirmation/emailConfirmationErrors.ts diff --git a/packages/api-v2/src/environment-variables.ts b/packages/api/src/environment-variables.ts similarity index 100% rename from packages/api-v2/src/environment-variables.ts rename to packages/api/src/environment-variables.ts diff --git a/packages/api-v2/src/graduate-logger.ts b/packages/api/src/graduate-logger.ts similarity index 89% rename from packages/api-v2/src/graduate-logger.ts rename to packages/api/src/graduate-logger.ts index 4b548dad9..1c8aa2560 100644 --- a/packages/api-v2/src/graduate-logger.ts +++ b/packages/api/src/graduate-logger.ts @@ -1,5 +1,5 @@ import { ConsoleLogger, LogLevel } from "@nestjs/common"; -import { deepFilter } from "src/utils"; +import { deepFilter } from "./utils"; const DENYLIST = ["password", "passwordConfirm"]; diff --git a/packages/api-v2/src/guards/dev-route.guard.ts b/packages/api/src/guards/dev-route.guard.ts similarity index 100% rename from packages/api-v2/src/guards/dev-route.guard.ts rename to packages/api/src/guards/dev-route.guard.ts diff --git a/packages/api-v2/src/guards/emailConfirmation.guard.ts b/packages/api/src/guards/emailConfirmation.guard.ts similarity index 84% rename from packages/api-v2/src/guards/emailConfirmation.guard.ts rename to packages/api/src/guards/emailConfirmation.guard.ts index 18c81cbe7..2555e778c 100644 --- a/packages/api-v2/src/guards/emailConfirmation.guard.ts +++ b/packages/api/src/guards/emailConfirmation.guard.ts @@ -6,8 +6,8 @@ import { UnauthorizedException, Logger, } from "@nestjs/common"; -import { AuthenticatedRequest } from "src/auth/interfaces/authenticated-request"; -import { formatServiceCtx } from "src/utils"; +import { AuthenticatedRequest } from "../auth/interfaces/authenticated-request"; +import { formatServiceCtx } from "../utils"; @Injectable() export class EmailConfirmationGuard implements CanActivate { diff --git a/packages/api-v2/src/guards/jwt-auth.guard.ts b/packages/api/src/guards/jwt-auth.guard.ts similarity index 100% rename from packages/api-v2/src/guards/jwt-auth.guard.ts rename to packages/api/src/guards/jwt-auth.guard.ts diff --git a/packages/api-v2/src/guards/own-plan.guard.ts b/packages/api/src/guards/own-plan.guard.ts similarity index 97% rename from packages/api-v2/src/guards/own-plan.guard.ts rename to packages/api/src/guards/own-plan.guard.ts index bb4268e95..f490ab93f 100644 --- a/packages/api-v2/src/guards/own-plan.guard.ts +++ b/packages/api/src/guards/own-plan.guard.ts @@ -7,7 +7,7 @@ import { } from "@nestjs/common"; import { PlanService } from "../plan/plan.service"; import { Student } from "../student/entities/student.entity"; -import { formatServiceCtx } from "../../src/utils"; +import { formatServiceCtx } from "../utils"; /** * Used to protect GET/PUT/PATCH :id Plan controller methods from being accessed diff --git a/packages/api-v2/src/interceptors/logging.interceptor.ts b/packages/api/src/interceptors/logging.interceptor.ts similarity index 100% rename from packages/api-v2/src/interceptors/logging.interceptor.ts rename to packages/api/src/interceptors/logging.interceptor.ts diff --git a/packages/api-v2/src/main.ts b/packages/api/src/main.ts similarity index 90% rename from packages/api-v2/src/main.ts rename to packages/api/src/main.ts index 86120b93f..a794a58d4 100644 --- a/packages/api-v2/src/main.ts +++ b/packages/api/src/main.ts @@ -6,7 +6,7 @@ import { } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { NestFactory, Reflector } from "@nestjs/core"; -import { GraduateLogger } from "src/graduate-logger"; +import { GraduateLogger } from "./graduate-logger"; import { AppModule } from "./app.module"; import { EnvironmentVariables } from "./environment-variables"; import * as cookieParser from "cookie-parser"; @@ -19,6 +19,11 @@ async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: graduateLogger, + cors: { + origin: "https://frontend-staging.graduatenu.com", + credentials: true, + methods: ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"], + }, }); /** diff --git a/packages/api-v2/src/major/major.controller.ts b/packages/api/src/major/major.controller.ts similarity index 100% rename from packages/api-v2/src/major/major.controller.ts rename to packages/api/src/major/major.controller.ts diff --git a/packages/api-v2/src/major/major.module.ts b/packages/api/src/major/major.module.ts similarity index 100% rename from packages/api-v2/src/major/major.module.ts rename to packages/api/src/major/major.module.ts diff --git a/packages/api-v2/src/major/major.service.ts b/packages/api/src/major/major.service.ts similarity index 98% rename from packages/api-v2/src/major/major.service.ts rename to packages/api/src/major/major.service.ts index eba5acd77..e75222814 100644 --- a/packages/api-v2/src/major/major.service.ts +++ b/packages/api/src/major/major.service.ts @@ -145,11 +145,10 @@ export class MajorService { majorName: string, catalogYear: number, concentrationName: string - ):boolean{ - + ): boolean { const majorsByCatalogue = this.findByMajorAndYear(majorName, catalogYear); - if(!majorsByCatalogue){ + if (!majorsByCatalogue) { this.logger.debug( { message: "Invalid catalogue year for major", @@ -158,7 +157,7 @@ export class MajorService { concentrationName, }, MajorService.formatMajorServiceCtx("isValidCatalogueYear") - ) + ); return false; } return true; diff --git a/packages/api-v2/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2021.json b/packages/api/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2021.json similarity index 99% rename from packages/api-v2/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2021.json rename to packages/api/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2021.json index 521cbb779..731593dba 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2021.json @@ -886,4 +886,4 @@ } ] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2022.json b/packages/api/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2022.json rename to packages/api/src/major/majors/Computer_Science_BACS/Computer_Science_BACS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2021.json b/packages/api/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2021.json rename to packages/api/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2022.json b/packages/api/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2022.json rename to packages/api/src/major/majors/Computer_Science_BSCS/Computer_Science_BSCS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Behavioral_Neuroscience_BS/Computer_Science_and_Behavioral_Neuroscience_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Biology_BS/Computer_Science_and_Biology_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2022.json index e4a115227..139beae03 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2022.json +++ b/packages/api/src/major/majors/Computer_Science_and_Business_Administration_BS/Computer_Science_and_Business_Administration_BS-2022.json @@ -1802,4 +1802,4 @@ } ] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Cognitive_Psychology_BS/Computer_Science_and_Cognitive_Psychology_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Communication_Studies_BS/Computer_Science_and_Communication_Studies_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Criminal_Justice_BS/Computer_Science_and_Criminal_Justice_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Design_BS/Computer_Science_and_Design_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2021.json similarity index 98% rename from packages/api-v2/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2021.json index 7eb2f625a..9eebc7375 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2021.json @@ -264,7 +264,9 @@ { "type": "SECTION", "title": "Economics Electives", - "warnings": ["No more than 2 of the four required courses can be in the ECON 1200-1999 range"], + "warnings": [ + "No more than 2 of the four required courses can be in the ECON 1200-1999 range" + ], "requirements": [ { "type": "RANGE", @@ -482,6 +484,6 @@ ], "concentrations": { "minOptions": 0, - "concentrationOptions": [] + "concentrationOptions": [] } } diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2022.json similarity index 98% rename from packages/api-v2/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2022.json index e0148ceba..8435cbb54 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2022.json +++ b/packages/api/src/major/majors/Computer_Science_and_Economics_BS/Computer_Science_and_Economics_BS-2022.json @@ -262,7 +262,9 @@ { "type": "SECTION", "title": "Economics Electives", - "warnings": ["No more than 2 of the four required courses can be in the ECON 1200-1999 range"], + "warnings": [ + "No more than 2 of the four required courses can be in the ECON 1200-1999 range" + ], "requirements": [ { "type": "RANGE", @@ -480,6 +482,6 @@ ], "concentrations": { "minOptions": 0, - "concentrationOptions": [] + "concentrationOptions": [] } } diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_English_BS/Computer_Science_and_English_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2022.json similarity index 89% rename from packages/api-v2/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2022.json index 84b898b11..3a6467e10 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2022.json +++ b/packages/api/src/major/majors/Computer_Science_and_Game_Development_BS/Computer_Science_and_Game_Development_BS-2022.json @@ -271,48 +271,48 @@ "type": "XOM", "numCreditsMin": 12, "courses": [ - { - "type": "RANGE", - "subject": "ARTD", - "idRangeStart": 0, - "idRangeEnd": 9999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "ARTE", - "idRangeStart": 0, - "idRangeEnd": 9999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "ARTF", - "idRangeStart": 0, - "idRangeEnd": 9999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "ARTG", - "idRangeStart": 0, - "idRangeEnd": 9999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "ARTH", - "idRangeStart": 0, - "idRangeEnd": 9999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "GAME", - "idRangeStart": 0, - "idRangeEnd": 9999, - "exceptions": [] - }, + { + "type": "RANGE", + "subject": "ARTD", + "idRangeStart": 0, + "idRangeEnd": 9999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "ARTE", + "idRangeStart": 0, + "idRangeEnd": 9999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "ARTF", + "idRangeStart": 0, + "idRangeEnd": 9999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "ARTG", + "idRangeStart": 0, + "idRangeEnd": 9999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "ARTH", + "idRangeStart": 0, + "idRangeEnd": 9999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "GAME", + "idRangeStart": 0, + "idRangeEnd": 9999, + "exceptions": [] + }, { "type": "RANGE", "subject": "CS", @@ -481,4 +481,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2021.json similarity index 98% rename from packages/api-v2/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2021.json index 746763239..3531e70a9 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2021.json @@ -315,7 +315,9 @@ { "type": "SECTION", "title": "History Capstone Seminar or Senior Project", - "warnings": ["Only Public History concentrators may also select the second course."], + "warnings": [ + "Only Public History concentrators may also select the second course." + ], "requirements": [ { "type": "COURSE", @@ -474,4 +476,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_History_BS/Computer_Science_and_History_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Journalism_BS/Computer_Science_and_Journalism_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Linguistics_BS/Computer_Science_and_Linguistics_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Mathematics_BS/Computer_Science_and_Mathematics_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2021.json similarity index 98% rename from packages/api-v2/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2021.json index f6cb9ae87..e780bea39 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2021.json @@ -308,7 +308,9 @@ { "type": "SECTION", "title": "Media Arts Electives", - "warnings": ["At least two of the following courses must be a 3000-level course."], + "warnings": [ + "At least two of the following courses must be a 3000-level course." + ], "requirements": [ { "type": "AND", @@ -435,7 +437,7 @@ "requirements": [ { "type": "OR", - "courses":[ + "courses": [ { "type": "COURSE", "classId": 2600, @@ -528,4 +530,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2022.json index 683d57434..6247598f8 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2022.json +++ b/packages/api/src/major/majors/Computer_Science_and_Media_Arts_BS/Computer_Science_and_Media_Arts_BS-2022.json @@ -333,7 +333,9 @@ { "type": "SECTION", "title": "Media Arts Electives", - "warnings": ["At least two of the following must be a 3000-level course."], + "warnings": [ + "At least two of the following must be a 3000-level course." + ], "requirements": [ { "type": "AND", @@ -460,7 +462,7 @@ "requirements": [ { "type": "OR", - "courses":[ + "courses": [ { "type": "COURSE", "classId": 2600, @@ -553,4 +555,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2021.json similarity index 99% rename from packages/api-v2/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2021.json index 2e38ea73e..61d1b6f9d 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2021.json @@ -546,4 +546,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2022.json index b7ce23244..78d9d8b40 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2022.json +++ b/packages/api/src/major/majors/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS/Computer_Science_and_Music_with_Concentration_in_Music_Technology_BS-2022.json @@ -588,4 +588,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2021.json similarity index 98% rename from packages/api-v2/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2021.json index f2c723d3d..1e8945c40 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2021.json @@ -290,7 +290,9 @@ { "type": "SECTION", "title": "Integrative Course Requirement", - "warnings": ["These courses will double count in other areas of your major."], + "warnings": [ + "These courses will double count in other areas of your major." + ], "requirements": [ { "type": "COURSE", @@ -360,4 +362,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Philosophy_BS/Computer_Science_and_Philosophy_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Physics_BS/Computer_Science_and_Physics_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2021.json similarity index 93% rename from packages/api-v2/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2021.json index d842eece1..ae8156d3f 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2021.json @@ -260,27 +260,27 @@ "type": "SECTION", "title": "Sociology Electives", "requirements": [ - { - "type": "RANGE", - "subject": "SOCL", - "idRangeStart": 1000, - "idRangeEnd": 1999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "SOCL", - "idRangeStart": 2000, - "idRangeEnd": 3999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "SOCL", - "idRangeStart": 4000, - "idRangeEnd": 4999, - "exceptions": [] - } + { + "type": "RANGE", + "subject": "SOCL", + "idRangeStart": 1000, + "idRangeEnd": 1999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "SOCL", + "idRangeStart": 2000, + "idRangeEnd": 3999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "SOCL", + "idRangeStart": 4000, + "idRangeEnd": 4999, + "exceptions": [] + } ], "minRequirementCount": 3 }, @@ -377,4 +377,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2022.json similarity index 93% rename from packages/api-v2/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2022.json index 4ea37a58b..3a7e1be7b 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2022.json +++ b/packages/api/src/major/majors/Computer_Science_and_Sociology_BS/Computer_Science_and_Sociology_BS-2022.json @@ -260,27 +260,27 @@ "type": "SECTION", "title": "Sociology Electives", "requirements": [ - { - "type": "RANGE", - "subject": "SOCL", - "idRangeStart": 1000, - "idRangeEnd": 1999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "SOCL", - "idRangeStart": 2000, - "idRangeEnd": 3999, - "exceptions": [] - }, - { - "type": "RANGE", - "subject": "SOCL", - "idRangeStart": 4000, - "idRangeEnd": 4999, - "exceptions": [] - } + { + "type": "RANGE", + "subject": "SOCL", + "idRangeStart": 1000, + "idRangeEnd": 1999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "SOCL", + "idRangeStart": 2000, + "idRangeEnd": 3999, + "exceptions": [] + }, + { + "type": "RANGE", + "subject": "SOCL", + "idRangeStart": 4000, + "idRangeEnd": 4999, + "exceptions": [] + } ], "minRequirementCount": 3 }, @@ -377,4 +377,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2021.json b/packages/api/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2021.json similarity index 99% rename from packages/api-v2/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2021.json rename to packages/api/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2021.json index fc7b59fd0..59ebcbff8 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2021.json +++ b/packages/api/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2021.json @@ -527,4 +527,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2022.json b/packages/api/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2022.json rename to packages/api/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2022.json index 733043eff..7a2279a09 100644 --- a/packages/api-v2/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2022.json +++ b/packages/api/src/major/majors/Computer_Science_and_Theatre_BS/Computer_Science_and_Theatre_BS-2022.json @@ -552,4 +552,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2021.json b/packages/api/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2021.json rename to packages/api/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2022.json b/packages/api/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2022.json rename to packages/api/src/major/majors/Cybersecurity_BS/Cybersecurity_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2021.json b/packages/api/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2021.json rename to packages/api/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2022.json b/packages/api/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2022.json rename to packages/api/src/major/majors/Cybersecurity_and_Business_Administration_BS/Cybersecurity_and_Business_Administration_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2021.json b/packages/api/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2021.json similarity index 99% rename from packages/api-v2/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2021.json rename to packages/api/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2021.json index b97ce286b..c2d0e5ba2 100644 --- a/packages/api-v2/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2021.json +++ b/packages/api/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2021.json @@ -575,7 +575,7 @@ "classId": 3000, "subject": "CRIM" }, - { + { "type": "COURSE", "classId": 4000, "subject": "CRIM" @@ -647,4 +647,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2022.json b/packages/api/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2022.json rename to packages/api/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2022.json index 52a7ef437..a32e39b42 100644 --- a/packages/api-v2/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2022.json +++ b/packages/api/src/major/majors/Cybersecurity_and_Criminal_Justice_BS/Cybersecurity_and_Criminal_Justice_BS-2022.json @@ -657,4 +657,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2021.json b/packages/api/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2021.json similarity index 99% rename from packages/api-v2/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2021.json rename to packages/api/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2021.json index ea6b67e56..3e2fe3890 100644 --- a/packages/api-v2/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2021.json +++ b/packages/api/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2021.json @@ -481,7 +481,9 @@ { "type": "SECTION", "title": "Electives", - "warnings": ["At least two of the following must be numbered ECON 3000 or above."], + "warnings": [ + "At least two of the following must be numbered ECON 3000 or above." + ], "requirements": [ { "type": "XOM", diff --git a/packages/api-v2/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2022.json b/packages/api/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2022.json rename to packages/api/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2022.json index c106f3fb9..7951564e1 100644 --- a/packages/api-v2/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2022.json +++ b/packages/api/src/major/majors/Cybersecurity_and_Economics_BS/Cybersecurity_and_Economics_BS-2022.json @@ -491,9 +491,7 @@ { "type": "SECTION", "title": "Electives", - "warnings": [ - "At most two of the following may be at the 1000 level." - ], + "warnings": ["At most two of the following may be at the 1000 level."], "requirements": [ { "type": "XOM", diff --git a/packages/api-v2/src/major/majors/Data_Science_BS/Data_Science_BS-2021.json b/packages/api/src/major/majors/Data_Science_BS/Data_Science_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_BS/Data_Science_BS-2021.json rename to packages/api/src/major/majors/Data_Science_BS/Data_Science_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_BS/Data_Science_BS-2022.json b/packages/api/src/major/majors/Data_Science_BS/Data_Science_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_BS/Data_Science_BS-2022.json rename to packages/api/src/major/majors/Data_Science_BS/Data_Science_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Behavioral_Neuroscience_BS/Data_Science_and_Behavioral_Neuroscience_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2022.json index aadd99a52..98849991c 100644 --- a/packages/api-v2/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2022.json +++ b/packages/api/src/major/majors/Data_Science_and_Biology_BS/Data_Science_and_Biology_BS-2022.json @@ -749,4 +749,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2021.json similarity index 99% rename from packages/api-v2/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2021.json index d1a4e9031..db53a82a3 100644 --- a/packages/api-v2/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2021.json +++ b/packages/api/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2021.json @@ -1709,4 +1709,4 @@ } ] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2022.json index 8c1f3398a..9ae745dcf 100644 --- a/packages/api-v2/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2022.json +++ b/packages/api/src/major/majors/Data_Science_and_Business_Administration_BS/Data_Science_and_Business_Administration_BS-2022.json @@ -1791,4 +1791,4 @@ } ] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2022.json index 3f70bc768..9c3714fba 100644 --- a/packages/api-v2/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2022.json +++ b/packages/api/src/major/majors/Data_Science_and_Chemistry_BS/Data_Science_and_Chemistry_BS-2022.json @@ -602,4 +602,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Ecology_and_Evolutionary_Biology_BS/Data_Science_and_Ecology_and_Evolutionary_Biology_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Economics_BS/Data_Science_and_Economics_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Health_Science_BS/Data_Science_and_Health_Science_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Journalism_BS/Data_Science_and_Journalism_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Linguistics_BS/Data_Science_and_Linguistics_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Mathematics_BS/Data_Science_and_Mathematics_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Physics_BS/Data_Science_and_Physics_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2021.json b/packages/api/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2021.json rename to packages/api/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2022.json b/packages/api/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2022.json rename to packages/api/src/major/majors/Data_Science_and_Psychology_BS/Data_Science_and_Psychology_BS-2022.json diff --git a/packages/api-v2/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2021.json b/packages/api/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2021.json similarity index 99% rename from packages/api-v2/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2021.json rename to packages/api/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2021.json index de0e5c760..db387f761 100644 --- a/packages/api-v2/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2021.json +++ b/packages/api/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2021.json @@ -439,4 +439,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2022.json b/packages/api/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2022.json rename to packages/api/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2022.json index abc95f74b..9b8514fda 100644 --- a/packages/api-v2/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2022.json +++ b/packages/api/src/major/majors/Game_Art_and_Animation_BFA/Game_Art_and_Animation_BFA-2022.json @@ -509,4 +509,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/Game_Design_BFA/Game_Design_BFA-2021.json b/packages/api/src/major/majors/Game_Design_BFA/Game_Design_BFA-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Game_Design_BFA/Game_Design_BFA-2021.json rename to packages/api/src/major/majors/Game_Design_BFA/Game_Design_BFA-2021.json diff --git a/packages/api-v2/src/major/majors/Game_Design_BFA/Game_Design_BFA-2022.json b/packages/api/src/major/majors/Game_Design_BFA/Game_Design_BFA-2022.json similarity index 100% rename from packages/api-v2/src/major/majors/Game_Design_BFA/Game_Design_BFA-2022.json rename to packages/api/src/major/majors/Game_Design_BFA/Game_Design_BFA-2022.json diff --git a/packages/api-v2/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2021.json b/packages/api/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2021.json similarity index 100% rename from packages/api-v2/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2021.json rename to packages/api/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2021.json diff --git a/packages/api-v2/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2022.json b/packages/api/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2022.json similarity index 99% rename from packages/api-v2/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2022.json rename to packages/api/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2022.json index 6b6ca35aa..043de9c79 100644 --- a/packages/api-v2/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2022.json +++ b/packages/api/src/major/majors/Game_Design_and_Music_with_concentration_in_Music_Technology_BS/Game_Design_and_Music_with_concentration_in_Music_Technology_BS-2022.json @@ -346,4 +346,4 @@ "minOptions": 0, "concentrationOptions": [] } -} \ No newline at end of file +} diff --git a/packages/api-v2/src/major/majors/index.ts b/packages/api/src/major/majors/index.ts similarity index 100% rename from packages/api-v2/src/major/majors/index.ts rename to packages/api/src/major/majors/index.ts diff --git a/packages/api-v2/src/major/test/major.controller.spec.ts b/packages/api/src/major/test/major.controller.spec.ts similarity index 100% rename from packages/api-v2/src/major/test/major.controller.spec.ts rename to packages/api/src/major/test/major.controller.spec.ts diff --git a/packages/api-v2/src/major/test/major.service.spec.ts b/packages/api/src/major/test/major.service.spec.ts similarity index 100% rename from packages/api-v2/src/major/test/major.service.spec.ts rename to packages/api/src/major/test/major.service.spec.ts diff --git a/packages/api/src/meta/meta.controller.ts b/packages/api/src/meta/meta.controller.ts new file mode 100644 index 000000000..20fec45e3 --- /dev/null +++ b/packages/api/src/meta/meta.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get } from "@nestjs/common"; +import { MetaService } from "./meta.service"; +import { type MetaInfo } from "@graduate/common"; + +@Controller("meta") +export class MetaController { + constructor(private readonly metaService: MetaService) {} + + @Get("/info") + getMetaInfo(): MetaInfo { + return this.metaService.getMetaInfo(); + } +} diff --git a/packages/api/src/meta/meta.module.ts b/packages/api/src/meta/meta.module.ts new file mode 100644 index 000000000..5d0433160 --- /dev/null +++ b/packages/api/src/meta/meta.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { MetaService } from "./meta.service"; +import { MetaController } from "./meta.controller"; + +@Module({ + controllers: [MetaController], + providers: [MetaService], + exports: [MetaService], +}) +export class MetaModule {} diff --git a/packages/api/src/meta/meta.service.ts b/packages/api/src/meta/meta.service.ts new file mode 100644 index 000000000..b1b0bfe90 --- /dev/null +++ b/packages/api/src/meta/meta.service.ts @@ -0,0 +1,17 @@ +import { type MetaInfo } from "@graduate/common"; +import { Injectable } from "@nestjs/common"; + +@Injectable() +export class MetaService { + getMetaInfo(): MetaInfo { + return { + commit: process.env.COMMIT_HASH ?? false, + commitMessage: process.env.COMMIT_MESSAGE ?? false, + build_timestamp: + process.env.BUILD_TIMESTAMP !== undefined + ? Number(process.env.BUILD_TIMESTAMP) + : false, + environment: process.env.NODE_ENV ?? false, + }; + } +} diff --git a/packages/api-v2/src/plan/entities/plan.entity.ts b/packages/api/src/plan/entities/plan.entity.ts similarity index 100% rename from packages/api-v2/src/plan/entities/plan.entity.ts rename to packages/api/src/plan/entities/plan.entity.ts diff --git a/packages/api-v2/src/plan/plan.controller.ts b/packages/api/src/plan/plan.controller.ts similarity index 100% rename from packages/api-v2/src/plan/plan.controller.ts rename to packages/api/src/plan/plan.controller.ts diff --git a/packages/api-v2/src/plan/plan.errors.ts b/packages/api/src/plan/plan.errors.ts similarity index 62% rename from packages/api-v2/src/plan/plan.errors.ts rename to packages/api/src/plan/plan.errors.ts index 626c4c3f9..f03d1d67f 100644 --- a/packages/api-v2/src/plan/plan.errors.ts +++ b/packages/api/src/plan/plan.errors.ts @@ -3,19 +3,19 @@ // InvalidConcentration export class InvalidMajor extends Error { - constructor() { - super(); - } + constructor() { + super(); + } } export class InvalidCatalogYear extends Error { - constructor() { - super(); - } + constructor() { + super(); + } } export class InvalidConcentration extends Error { - constructor() { - super(); - } + constructor() { + super(); + } } diff --git a/packages/api-v2/src/plan/plan.module.ts b/packages/api/src/plan/plan.module.ts similarity index 100% rename from packages/api-v2/src/plan/plan.module.ts rename to packages/api/src/plan/plan.module.ts diff --git a/packages/api-v2/src/plan/plan.service.ts b/packages/api/src/plan/plan.service.ts similarity index 94% rename from packages/api-v2/src/plan/plan.service.ts rename to packages/api/src/plan/plan.service.ts index 027a2039d..f8a902b20 100644 --- a/packages/api-v2/src/plan/plan.service.ts +++ b/packages/api/src/plan/plan.service.ts @@ -5,9 +5,13 @@ import { StudentService } from "../student/student.service"; import { DeleteResult, Repository, UpdateResult } from "typeorm"; import { CreatePlanDto, UpdatePlanDto } from "@graduate/common"; import { Plan } from "./entities/plan.entity"; -import { formatServiceCtx } from "../../src/utils"; +import { formatServiceCtx } from "../utils"; import { MajorService } from "../major/major.service"; -import { InvalidCatalogYear, InvalidConcentration, InvalidMajor } from "./plan.errors"; +import { + InvalidCatalogYear, + InvalidConcentration, + InvalidMajor, +} from "./plan.errors"; @Injectable() export class PlanService { @@ -141,8 +145,9 @@ export class PlanService { * TODO: Fix the DTO issue that populates undefined values for fields not * present. https://github.com/sandboxnu/graduatenu/issues/533 */ - // It is necessary for this to be OR because we need to run an update if any of these are true. - const isMajorInfoUpdate = newMajorName || newCatalogYear || newConcentrationName; + // It is necessary for this to be OR because we need to run an update if any of these are true. + const isMajorInfoUpdate = + newMajorName || newCatalogYear || newConcentrationName; /** Wipe Major => Remove existing major from the plan. */ const isWipeMajorUpdate = @@ -182,14 +187,13 @@ export class PlanService { throw new InvalidMajor(); } - const isValidMajorCatalogueYear = this.majorService.isValidCatalogueYear( - newMajorName, - newCatalogYear, + newMajorName, + newCatalogYear, newConcentrationName ); - if(!isValidMajorCatalogueYear){ + if (!isValidMajorCatalogueYear) { this.logger.debug( { message: "Attempting to add plan with an invalid catalogue year", @@ -204,11 +208,11 @@ export class PlanService { } const isValidConcentrationForMajor = - this.majorService.isValidConcentrationForMajor( - newMajorName, - newCatalogYear, - newConcentrationName - ); + this.majorService.isValidConcentrationForMajor( + newMajorName, + newCatalogYear, + newConcentrationName + ); if (!isValidConcentrationForMajor) { this.logger.debug( diff --git a/packages/api-v2/src/plan/test/plan.controller.spec.ts b/packages/api/src/plan/test/plan.controller.spec.ts similarity index 100% rename from packages/api-v2/src/plan/test/plan.controller.spec.ts rename to packages/api/src/plan/test/plan.controller.spec.ts diff --git a/packages/api-v2/src/plan/test/plan.service.spec.ts b/packages/api/src/plan/test/plan.service.spec.ts similarity index 100% rename from packages/api-v2/src/plan/test/plan.service.spec.ts rename to packages/api/src/plan/test/plan.service.spec.ts diff --git a/packages/api-v2/src/student/entities/student.entity.ts b/packages/api/src/student/entities/student.entity.ts similarity index 100% rename from packages/api-v2/src/student/entities/student.entity.ts rename to packages/api/src/student/entities/student.entity.ts diff --git a/packages/api-v2/src/student/student.controller.ts b/packages/api/src/student/student.controller.ts similarity index 100% rename from packages/api-v2/src/student/student.controller.ts rename to packages/api/src/student/student.controller.ts diff --git a/packages/api-v2/src/student/student.errors.ts b/packages/api/src/student/student.errors.ts similarity index 100% rename from packages/api-v2/src/student/student.errors.ts rename to packages/api/src/student/student.errors.ts diff --git a/packages/api-v2/src/student/student.module.ts b/packages/api/src/student/student.module.ts similarity index 100% rename from packages/api-v2/src/student/student.module.ts rename to packages/api/src/student/student.module.ts diff --git a/packages/api-v2/src/student/student.service.ts b/packages/api/src/student/student.service.ts similarity index 99% rename from packages/api-v2/src/student/student.service.ts rename to packages/api/src/student/student.service.ts index 69c9589aa..a264221ca 100644 --- a/packages/api-v2/src/student/student.service.ts +++ b/packages/api/src/student/student.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger } from "@nestjs/common"; import { InjectRepository } from "@nestjs/typeorm"; -import { formatServiceCtx } from "../../src/utils"; +import { formatServiceCtx } from "../utils"; import { DeleteResult, FindOneOptions, diff --git a/packages/api-v2/src/student/test/student.controller.spec.ts b/packages/api/src/student/test/student.controller.spec.ts similarity index 100% rename from packages/api-v2/src/student/test/student.controller.spec.ts rename to packages/api/src/student/test/student.controller.spec.ts diff --git a/packages/api-v2/src/student/test/student.service.spec.ts b/packages/api/src/student/test/student.service.spec.ts similarity index 100% rename from packages/api-v2/src/student/test/student.service.spec.ts rename to packages/api/src/student/test/student.service.spec.ts diff --git a/packages/api-v2/src/utils/deepFilter.utils.ts b/packages/api/src/utils/deepFilter.utils.ts similarity index 100% rename from packages/api-v2/src/utils/deepFilter.utils.ts rename to packages/api/src/utils/deepFilter.utils.ts diff --git a/packages/api-v2/src/utils/formatServiceCtx.utils.ts b/packages/api/src/utils/formatServiceCtx.utils.ts similarity index 100% rename from packages/api-v2/src/utils/formatServiceCtx.utils.ts rename to packages/api/src/utils/formatServiceCtx.utils.ts diff --git a/packages/api-v2/src/utils/index.ts b/packages/api/src/utils/index.ts similarity index 100% rename from packages/api-v2/src/utils/index.ts rename to packages/api/src/utils/index.ts diff --git a/packages/api-v2/src/utils/test/deepFilter.utils.spec.ts b/packages/api/src/utils/test/deepFilter.utils.spec.ts similarity index 100% rename from packages/api-v2/src/utils/test/deepFilter.utils.spec.ts rename to packages/api/src/utils/test/deepFilter.utils.spec.ts diff --git a/packages/api-v2/test/auth/auth.e2e-spec.ts b/packages/api/test/auth/auth.e2e-spec.ts similarity index 94% rename from packages/api-v2/test/auth/auth.e2e-spec.ts rename to packages/api/test/auth/auth.e2e-spec.ts index 1d6b055bb..fef0d38b2 100644 --- a/packages/api-v2/test/auth/auth.e2e-spec.ts +++ b/packages/api/test/auth/auth.e2e-spec.ts @@ -2,8 +2,8 @@ import { INestApplication } from "@nestjs/common"; import { Student } from "../../src/student/entities/student.entity"; import * as request from "supertest"; import { Connection } from "typeorm"; -import { dropStudentTable, initializeApp } from "../../test/utils"; -import { testUser1 } from "../../test/testingData"; +import { dropStudentTable, initializeApp } from "../utils"; +import { testUser1 } from "../testingData"; describe("AuthController (e2e)", () => { let app: INestApplication; diff --git a/packages/api-v2/test/jest-e2e.json b/packages/api/test/jest-e2e.json similarity index 100% rename from packages/api-v2/test/jest-e2e.json rename to packages/api/test/jest-e2e.json diff --git a/packages/api-v2/test/major/major.e2e-spec.ts b/packages/api/test/major/major.e2e-spec.ts similarity index 95% rename from packages/api-v2/test/major/major.e2e-spec.ts rename to packages/api/test/major/major.e2e-spec.ts index 3efc89b7c..126528d8b 100644 --- a/packages/api-v2/test/major/major.e2e-spec.ts +++ b/packages/api/test/major/major.e2e-spec.ts @@ -1,6 +1,6 @@ import { INestApplication } from "@nestjs/common"; import * as request from "supertest"; -import { initializeApp } from "../../test/utils"; +import { initializeApp } from "../utils"; describe("MajorController (e2e)", () => { let app: INestApplication; diff --git a/packages/api-v2/test/plan/plan.e2e-spec.ts b/packages/api/test/plan/plan.e2e-spec.ts similarity index 95% rename from packages/api-v2/test/plan/plan.e2e-spec.ts rename to packages/api/test/plan/plan.e2e-spec.ts index 06fe9c545..0995f9f0d 100644 --- a/packages/api-v2/test/plan/plan.e2e-spec.ts +++ b/packages/api/test/plan/plan.e2e-spec.ts @@ -2,13 +2,8 @@ import { INestApplication } from "@nestjs/common"; import { Plan } from "../../src/plan/entities/plan.entity"; import * as request from "supertest"; import { Connection } from "typeorm"; -import { dropStudentTable, initializeApp } from "../../test/utils"; -import { - testPlan, - testUser1, - testUser2, - testUser3, -} from "../../test/testingData"; +import { dropStudentTable, initializeApp } from "../utils"; +import { testPlan, testUser1, testUser2, testUser3 } from "../testingData"; describe("PlanController (e2e)", () => { let app: INestApplication; diff --git a/packages/api-v2/test/student/student.e2e-spec.ts b/packages/api/test/student/student.e2e-spec.ts similarity index 94% rename from packages/api-v2/test/student/student.e2e-spec.ts rename to packages/api/test/student/student.e2e-spec.ts index 21c31ebe5..10fd74a26 100644 --- a/packages/api-v2/test/student/student.e2e-spec.ts +++ b/packages/api/test/student/student.e2e-spec.ts @@ -2,8 +2,8 @@ import { INestApplication } from "@nestjs/common"; import { Plan } from "../../src/plan/entities/plan.entity"; import * as request from "supertest"; import { Connection } from "typeorm"; -import { dropStudentTable, initializeApp } from "../../test/utils"; -import { onboardedUser, testPlan, testUser1 } from "../../test/testingData"; +import { dropStudentTable, initializeApp } from "../utils"; +import { onboardedUser, testPlan, testUser1 } from "../testingData"; describe("StudentController (e2e)", () => { let app: INestApplication; diff --git a/packages/api-v2/test/testingData.ts b/packages/api/test/testingData.ts similarity index 100% rename from packages/api-v2/test/testingData.ts rename to packages/api/test/testingData.ts diff --git a/packages/api-v2/test/utils.ts b/packages/api/test/utils.ts similarity index 100% rename from packages/api-v2/test/utils.ts rename to packages/api/test/utils.ts diff --git a/packages/api-v2/tsconfig.build.json b/packages/api/tsconfig.build.json similarity index 100% rename from packages/api-v2/tsconfig.build.json rename to packages/api/tsconfig.build.json diff --git a/packages/api-v2/tsconfig.json b/packages/api/tsconfig.json similarity index 100% rename from packages/api-v2/tsconfig.json rename to packages/api/tsconfig.json diff --git a/packages/common/package.json b/packages/common/package.json index 26a638637..e5326c87a 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -23,7 +23,7 @@ "typescript": "^4.6.2" }, "dependencies": { - "axios": "^0.26.1", + "axios": "^1.6.0", "class-validator": "^0.13.2" }, "jest": { diff --git a/packages/common/src/api-dtos.ts b/packages/common/src/api-dtos.ts index 2e17c61f7..45df6e985 100644 --- a/packages/common/src/api-dtos.ts +++ b/packages/common/src/api-dtos.ts @@ -84,7 +84,6 @@ export class SignUpStudentDto { } export class UpdateStudentDto { @IsOptional() - @IsNotEmpty() @IsString() fullName?: string; diff --git a/packages/common/src/api-response-types.ts b/packages/common/src/api-response-types.ts index 859146677..7cb2c1dee 100644 --- a/packages/common/src/api-response-types.ts +++ b/packages/common/src/api-response-types.ts @@ -3,6 +3,8 @@ import { Schedule2, SupportedMajors, ScheduleCourse2, + MetaInfo, + Maybe, } from "./types"; /** Types our API responds with. */ @@ -52,3 +54,10 @@ export class GetSupportedMajorsResponse { // { year => { majorName => {concentrations, minRequiredConcentrations} }} supportedMajors: SupportedMajors; } + +export class GetMetaInfoResponse implements MetaInfo { + commit: Maybe; + commitMessage: Maybe; + build_timestamp: Maybe; + environment: Maybe; +} diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 0ca2d54a4..2e3ddfb78 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1,22 +1,21 @@ /** - * Describes an abbreviation for one of Northeastern's NUPath academic breadth - * requirements. Each two-character NUPath directly corresponds to - * Northeastern's abbreviation of the requirement. + * Describes the term SearchNEU uses for each of Northeastern's NUPath academic + * breadth requirements. */ export enum NUPathEnum { - ND = "ND", - EI = "EI", - IC = "IC", - FQ = "FQ", - SI = "SI", - AD = "AD", - DD = "DD", - ER = "ER", - WF = "WF", - WD = "WD", - WI = "WI", - EX = "EX", - CE = "CE", + ND = "Natural and Designed World", + EI = "Creative Expression/Innovation", + IC = "Interpreting Culture", + FQ = "Formal and Quantitative Reasoning", + SI = "Societies and Institutions", + AD = "Analyzing/Using Data", + DD = "Difference and Diversity", + ER = "Ethical Reasoning", + WF = "1st Yr Writing", + WD = "Adv Writ Dscpl", + WI = "Writing Intensive", + EX = "Integration Experience", + CE = "Capstone Experience", } /** @@ -40,7 +39,6 @@ export enum StatusEnum { HOVERCOOP = "HOVERCOOP", } -export type NUPath = keyof typeof NUPathEnum; export type Status = keyof typeof StatusEnum; export type Season = keyof typeof SeasonEnum; @@ -257,7 +255,7 @@ export interface Concentrations2 { } /** - * A clean version of a student's schedule as used in V2 of the App with no + * A clean version of a student's schedule as used in of the App with no * redundunt year information. * * @param years A list of the years of this object @@ -292,9 +290,9 @@ export interface ScheduleYear2 { } /** - * A clean version of the ScheduleTerm used by V2 of the App. A generic id field - * is used for book keeping purposes by the drag and drop library, in cases - * where we don't care about this id, T can null. + * A clean version of the ScheduleTerm used by of the App. A generic id field is + * used for book keeping purposes by the drag and drop library, in cases where + * we don't care about this id, T can null. * * @param year The year of this term * @param season The season of this term @@ -310,15 +308,16 @@ export interface ScheduleTerm2 { } /** - * A course within a schedule used by V2 of the App. A generic id field is used - * for book keeping purposes by the drag and drop library, in cases where we - * don't care about this id, T can null. + * A course within a schedule used by of the App. A generic id field is used for + * book keeping purposes by the drag and drop library, in cases where we don't + * care about this id, T can null. * * @param name The name of the course * @param classId The classId of this course (1210, 1800, etc) * @param subject The subject of this course (CS, DS, etc) * @param prereqs The prerequisites for this course * @param coreqs The corequisites for this course + * @param nupaths The nupaths this course fulfills * @param numCreditsMin The minimum number of credits this course gives * @param numCreditsMax The maximum number of credits this course gives * @param id Unique id used as a book keeping field for dnd. @@ -329,6 +328,7 @@ export interface ScheduleCourse2 { subject: string; prereqs?: INEUAndReq | INEUOrReq; coreqs?: INEUAndReq | INEUOrReq; + nupaths?: NUPathEnum[]; numCreditsMin: number; numCreditsMax: number; id: T; @@ -483,3 +483,12 @@ export const Err = (err: E): Result => ({ err, type: ResultType.Err, }); + +export type Maybe = T | false; + +export interface MetaInfo { + commit: Maybe; + commitMessage: Maybe; + build_timestamp: Maybe; + environment: Maybe; +} diff --git a/packages/frontend-v2/components/AddCourseModal/AddCourseButton.tsx b/packages/frontend-v2/components/AddCourseModal/AddCourseButton.tsx deleted file mode 100644 index 918e121f9..000000000 --- a/packages/frontend-v2/components/AddCourseModal/AddCourseButton.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { AddIcon } from "@chakra-ui/icons"; -import { Button, ButtonProps } from "@chakra-ui/react"; - -interface AddCourseButtonProps { - onOpen: () => void; -} - -export const AddCourseButton: React.FC = ({ - onOpen, - ...buttonProps -}) => { - const hoverStyle = { - backgroundColor: "neutral.700", - borderColor: "primary.blue.dark.300", - }; - - const activeStyle = { - backgroundColor: "neutral.900", - borderColor: "primary.blue.dark.300", - transform: "scale(0.96)", - }; - - return ( - - ); -}; diff --git a/packages/frontend-v2/components/Authentication/AuthForm.tsx b/packages/frontend-v2/components/Authentication/AuthForm.tsx deleted file mode 100644 index f2195c562..000000000 --- a/packages/frontend-v2/components/Authentication/AuthForm.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Flex, Heading } from "@chakra-ui/react"; -import { FormEventHandler, ReactNode } from "react"; - -type NewType = ReactNode; - -interface AuthFormProps { - onSubmit?: FormEventHandler; - headingText: string; - inputs: ReactNode; - footer: NewType; -} -export const AuthForm: React.FC = ({ - onSubmit, - headingText, - inputs, - footer, -}) => { - return ( - - - {headingText} - - - {inputs} - - {footer} - - ); -}; diff --git a/packages/frontend-v2/components/Button/GrayButton.tsx b/packages/frontend-v2/components/Button/GrayButton.tsx deleted file mode 100644 index fd3e25ada..000000000 --- a/packages/frontend-v2/components/Button/GrayButton.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Button, ButtonProps, ComponentWithAs } from "@chakra-ui/react"; - -export const GrayButton: ComponentWithAs<"button", ButtonProps> = ({ - children, - ...rest -}) => { - return ( - - ); -}; diff --git a/packages/frontend-v2/components/Error/ClientSideError.tsx b/packages/frontend-v2/components/Error/ClientSideError.tsx deleted file mode 100644 index 408b92e3e..000000000 --- a/packages/frontend-v2/components/Error/ClientSideError.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const ClientSideError: React.FC = () => { - return ( - <> -
-

GraduateNU

-
-
-

There was an error rendering your page.

-
- - ); -}; diff --git a/packages/frontend-v2/components/Error/ErrorBoundary.tsx b/packages/frontend-v2/components/Error/ErrorBoundary.tsx deleted file mode 100644 index 3cc39949c..000000000 --- a/packages/frontend-v2/components/Error/ErrorBoundary.tsx +++ /dev/null @@ -1,22 +0,0 @@ -// import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary"; -import React, { PropsWithChildren } from "react"; -// import { logger } from "../../utils"; -// import { ClientSideError } from "./ClientSideError"; - -// const clientSideErrorHandler = ( -// error: Error, -// { componentStack }: { componentStack: string } -// ) => { -// logger.error(error.message, componentStack); -// }; - -export const ErrorBoundary: React.FC = ({ children }) => { - return ( - // -
{children}
- //
- ); -}; diff --git a/packages/frontend-v2/components/Header/HeaderContainer.tsx b/packages/frontend-v2/components/Header/HeaderContainer.tsx deleted file mode 100644 index da2a41afa..000000000 --- a/packages/frontend-v2/components/Header/HeaderContainer.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Flex } from "@chakra-ui/react"; -import React, { PropsWithChildren } from "react"; - -export const HeaderContainer: React.FC = ({ children }) => { - return ( - - {children} - - ); -}; diff --git a/packages/frontend-v2/pages/index.tsx b/packages/frontend-v2/pages/index.tsx deleted file mode 100644 index f05549b3f..000000000 --- a/packages/frontend-v2/pages/index.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import type { NextPage } from "next"; -import { - Box, - Flex, - Heading, - HStack, - Image, - SimpleGrid, - Text, - VStack, -} from "@chakra-ui/react"; -import { GraduateButtonLink, GraduatePreAuthHeader } from "../components"; - -type InfoSectionProps = InfoImageProps & InfoTextProps; - -interface InfoImageProps { - imageSource: string; - altInfo: string; -} - -interface InfoTextProps { - title: string; - description: string; -} - -const LandingPage: NextPage = () => { - return ( - - - - - - ); -}; - -const Banner = (): JSX.Element => { - return ( - - - husky - - - - Graduate - - - Your Way - - - Navigate the Northeastern graduation requirements and create a - personalized plan of study. - - - - Get Started - - - - - ); -}; - -const Info = (): JSX.Element => { - const infoSectionData = [ - { - imageSource: "/landing_start.png", - altInfo: "Start", - title: "Start", - description: - "Select a major and concentration to get started with a multi-year plan.", - }, - { - imageSource: "/landing_personalize.svg", - altInfo: "Personalize", - title: "Personalize", - description: - "Create multiple plans to experiment with different majors, concentrations, and plans of study. Pick the classes you want. We'll take care of NUPath, prerequisites, and everything in between.", - }, - { - imageSource: "/landing_graduate.svg", - altInfo: "Graduate", - title: "Graduate", - description: - "Build a plan of study that lets you graduate faster, with better classes, and a lot less headaches.", - }, - ]; - - return ( - - - - How It Works - - - {infoSectionData.map((info) => ( - - ))} - - - - ); -}; - -const InfoSection = ({ - imageSource, - altInfo, - title, - description, -}: InfoSectionProps): JSX.Element => { - return ( - - - - - ); -}; - -const InfoImage = ({ imageSource, altInfo }: InfoImageProps): JSX.Element => { - return ( - {altInfo} - ); -}; - -const InfoText = ({ title, description }: InfoTextProps): JSX.Element => { - return ( - - - {title} - - - {description} - - - ); -}; - -export default LandingPage; diff --git a/packages/frontend-v2/public/husky.svg b/packages/frontend-v2/public/husky.svg deleted file mode 100644 index f589ea3b9..000000000 --- a/packages/frontend-v2/public/husky.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/frontend-v2/utils/constants.ts b/packages/frontend-v2/utils/constants.ts deleted file mode 100644 index b1b8ccc08..000000000 --- a/packages/frontend-v2/utils/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const DELETE_COURSE_AREA_DND_ID = "delete-course-area"; -export const SIDEBAR_DND_ID_PREFIX = "sidebar"; -export const WEAK_PASSWORD_MSG = - "A password should be at least 8 characters with digits and letters"; -export const SEARCH_NEU_FETCH_COURSE_ERROR_MSG = - "Sorry, we can't load details for this course right now 😞. We rely on SearchNEU for our course details, and there may be an ongoing issue on their end. We recommend refreshing the page and trying again soon."; diff --git a/packages/frontend-v2/validation-worker/worker-messages.ts b/packages/frontend-v2/validation-worker/worker-messages.ts deleted file mode 100644 index b0cde7e10..000000000 --- a/packages/frontend-v2/validation-worker/worker-messages.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Major2, MajorValidationResult, ScheduleCourse2 } from "@graduate/common" - -export type WorkerMessage = Loaded | ValidationResult - -export enum WorkerMessageType { - Loaded = "Loaded", - ValidationResult = "ValidationResult" -} - -export type ValidationResult = { - type: WorkerMessageType.ValidationResult, - result: MajorValidationResult, - requestNumber: number -} -export type Loaded = {type: WorkerMessageType.Loaded} - -export interface WorkerPostInfo { - major: Major2, - taken: ScheduleCourse2[], - concentration?: string, - requestNumber: number -} \ No newline at end of file diff --git a/packages/frontend-v2/validation-worker/worker.ts b/packages/frontend-v2/validation-worker/worker.ts deleted file mode 100644 index d377069cc..000000000 --- a/packages/frontend-v2/validation-worker/worker.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { validateMajor2 } from "@graduate/common"; -import { Loaded, ValidationResult, WorkerMessageType, WorkerPostInfo } from "./worker-messages"; - -// Let the host page know the worker is ready. -const loadMessage: Loaded = {type: WorkerMessageType.Loaded} -postMessage(loadMessage) - -addEventListener("message", ({data}: MessageEvent) => { - const validationResult: ValidationResult = { - type: WorkerMessageType.ValidationResult, - result: validateMajor2(data.major, data.taken, data.concentration), - requestNumber: data.requestNumber - } - - postMessage(validationResult) -}) \ No newline at end of file diff --git a/packages/frontend-v2/.eslintrc.js b/packages/frontend/.eslintrc.js similarity index 70% rename from packages/frontend-v2/.eslintrc.js rename to packages/frontend/.eslintrc.js index 601fa0ffa..d48bd31aa 100644 --- a/packages/frontend-v2/.eslintrc.js +++ b/packages/frontend/.eslintrc.js @@ -2,7 +2,7 @@ module.exports = { extends: "next/core-web-vitals", settings: { next: { - rootDir: "packages/frontend-v2/", + rootDir: "packages/frontend/", }, }, }; diff --git a/packages/frontend-v2/.gitignore b/packages/frontend/.gitignore similarity index 100% rename from packages/frontend-v2/.gitignore rename to packages/frontend/.gitignore diff --git a/packages/frontend-v2/README.md b/packages/frontend/README.md similarity index 84% rename from packages/frontend-v2/README.md rename to packages/frontend/README.md index c9b3b4fef..644205e94 100644 --- a/packages/frontend-v2/README.md +++ b/packages/frontend/README.md @@ -1,12 +1,12 @@ -# GraduateNU Frontend V2 +# GraduateNU Frontend This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## Getting Started -Run the full stack application with `yarn dev:v2` from the root of the monorepo. +Run the full stack application with `yarn dev` from the root of the monorepo. -To run only the frontend, use `yarn dev` from the root of this workspace(`packages/frontend-v2`). +To run only the frontend, use `yarn dev` from the root of this workspace(`packages/frontend`). You can start editing the pages by modifying files in `pages/`. The pages auto-update as you edit the file. diff --git a/packages/frontend/components/AddCourseModal/AddCourseButton.tsx b/packages/frontend/components/AddCourseModal/AddCourseButton.tsx new file mode 100644 index 000000000..3b4de4031 --- /dev/null +++ b/packages/frontend/components/AddCourseModal/AddCourseButton.tsx @@ -0,0 +1,22 @@ +import { AddIcon } from "@chakra-ui/icons"; +import { ButtonProps } from "@chakra-ui/react"; +import { SecondaryBlueButton } from "../Button/SecondaryBlueButton"; + +interface AddCourseButtonProps { + onOpen: () => void; +} + +export const AddCourseButton: React.FC = ({ + onOpen, + ...buttonProps +}) => { + return ( + } + onClick={onOpen} + {...buttonProps} + > + Add Courses + + ); +}; diff --git a/packages/frontend-v2/components/AddCourseModal/AddCourseModal.tsx b/packages/frontend/components/AddCourseModal/AddCourseModal.tsx similarity index 100% rename from packages/frontend-v2/components/AddCourseModal/AddCourseModal.tsx rename to packages/frontend/components/AddCourseModal/AddCourseModal.tsx diff --git a/packages/frontend-v2/components/AddCourseModal/SearchCoursesInput.tsx b/packages/frontend/components/AddCourseModal/SearchCoursesInput.tsx similarity index 96% rename from packages/frontend-v2/components/AddCourseModal/SearchCoursesInput.tsx rename to packages/frontend/components/AddCourseModal/SearchCoursesInput.tsx index cc2bb528c..8a4b211b6 100644 --- a/packages/frontend-v2/components/AddCourseModal/SearchCoursesInput.tsx +++ b/packages/frontend/components/AddCourseModal/SearchCoursesInput.tsx @@ -36,7 +36,7 @@ export const SearchCoursesInput: React.FC = ({ borderRadius={10} fontSize="sm" color="primary.blue.light.main" - backgroundColor="neutral.main" + backgroundColor="neutral.100" placeholder="SEARCH BY NAME, CRN, ETC." /> diff --git a/packages/frontend-v2/components/AddCourseModal/SearchResult.tsx b/packages/frontend/components/AddCourseModal/SearchResult.tsx similarity index 93% rename from packages/frontend-v2/components/AddCourseModal/SearchResult.tsx rename to packages/frontend/components/AddCourseModal/SearchResult.tsx index ae49a62f4..f903ae224 100644 --- a/packages/frontend-v2/components/AddCourseModal/SearchResult.tsx +++ b/packages/frontend/components/AddCourseModal/SearchResult.tsx @@ -1,7 +1,7 @@ import { SmallAddIcon } from "@chakra-ui/icons"; import { Flex, Box, Heading, IconButton, Text } from "@chakra-ui/react"; import { ScheduleCourse2 } from "@graduate/common"; -import { getCourseDisplayString } from "../../utils/"; +import { getCourseDisplayString } from "../../utils"; import { GraduateToolTip } from "../GraduateTooltip"; interface SearchResultProps { @@ -33,7 +33,7 @@ export const SearchResult: React.FC = ({ {searchResult.name} - + {getCourseDisplayString(searchResult)} diff --git a/packages/frontend-v2/components/AddCourseModal/SelectedCourse.tsx b/packages/frontend/components/AddCourseModal/SelectedCourse.tsx similarity index 89% rename from packages/frontend-v2/components/AddCourseModal/SelectedCourse.tsx rename to packages/frontend/components/AddCourseModal/SelectedCourse.tsx index f95f6e8e7..1465558a3 100644 --- a/packages/frontend-v2/components/AddCourseModal/SelectedCourse.tsx +++ b/packages/frontend/components/AddCourseModal/SelectedCourse.tsx @@ -1,6 +1,6 @@ import { Flex, Text } from "@chakra-ui/react"; import { ScheduleCourse2 } from "@graduate/common"; -import { getCourseDisplayString } from "../../utils/"; +import { getCourseDisplayString } from "../../utils"; import { CourseTrashButton } from "../ScheduleCourse/CourseTrashButton"; interface SelectedCourseProps { @@ -16,7 +16,7 @@ export const SelectedCourse: React.FC = ({ diff --git a/packages/frontend-v2/components/AddCourseModal/index.ts b/packages/frontend/components/AddCourseModal/index.ts similarity index 100% rename from packages/frontend-v2/components/AddCourseModal/index.ts rename to packages/frontend/components/AddCourseModal/index.ts diff --git a/packages/frontend/components/Authentication/AuthForm.tsx b/packages/frontend/components/Authentication/AuthForm.tsx new file mode 100644 index 000000000..710142b0e --- /dev/null +++ b/packages/frontend/components/Authentication/AuthForm.tsx @@ -0,0 +1,59 @@ +import { AbsoluteCenter, Box, Divider, Flex, Heading } from "@chakra-ui/react"; +import router from "next/router"; +import { FormEventHandler, ReactNode, useContext } from "react"; +import { SecondaryBlueButton } from "../Button/SecondaryBlueButton"; +import { IsGuestContext } from "../../pages/_app"; + +type NewType = ReactNode; + +interface AuthFormProps { + onSubmit?: FormEventHandler; + headingText: string; + inputs: ReactNode; + footer: NewType; +} +export const AuthForm: React.FC = ({ + onSubmit, + headingText, + inputs, + footer, +}) => { + const { setIsGuest } = useContext(IsGuestContext); + return ( + + + {headingText} + + + {inputs} + + {footer} + + + + or + + + { + setIsGuest(true); + router.push("/home"); + }} + > + Continue as Guest + + + ); +}; diff --git a/packages/frontend-v2/components/Authentication/AuthPageLayout.tsx b/packages/frontend/components/Authentication/AuthPageLayout.tsx similarity index 89% rename from packages/frontend-v2/components/Authentication/AuthPageLayout.tsx rename to packages/frontend/components/Authentication/AuthPageLayout.tsx index f5c36d9e1..fe9af5b2a 100644 --- a/packages/frontend-v2/components/Authentication/AuthPageLayout.tsx +++ b/packages/frontend/components/Authentication/AuthPageLayout.tsx @@ -13,11 +13,7 @@ export const AuthenticationPageLayout: React.FC< {form} - + GraduateNU app snippet = ({ }) => { return ( + + + + + + + ); +}; diff --git a/packages/frontend-v2/components/Plan/EditPlanModal.tsx b/packages/frontend/components/Plan/EditPlanModal.tsx similarity index 91% rename from packages/frontend-v2/components/Plan/EditPlanModal.tsx rename to packages/frontend/components/Plan/EditPlanModal.tsx index 6ad732175..5cd524e38 100644 --- a/packages/frontend-v2/components/Plan/EditPlanModal.tsx +++ b/packages/frontend/components/Plan/EditPlanModal.tsx @@ -18,11 +18,12 @@ import { import { API } from "@graduate/api-client"; import { PlanModel, UpdatePlanDto } from "@graduate/common"; import { useRouter } from "next/router"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useContext, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { useSWRConfig } from "swr"; import { USE_STUDENT_WITH_PLANS_SWR_KEY, + useStudentWithPlans, useSupportedMajors, } from "../../hooks"; import { @@ -36,6 +37,7 @@ import { BlueButton } from "../Button"; import { PlanInput, PlanSelect } from "../Form"; import { HelperToolTip } from "../Help"; import { PlanConcentrationsSelect } from "./PlanConcentrationsSelect"; +import { IsGuestContext } from "../../pages/_app"; type EditPlanModalProps = { plan: PlanModel; @@ -67,6 +69,8 @@ export const EditPlanModal: React.FC = ({ plan }) => { mode: "onTouched", shouldFocusError: true, }); + const { student } = useStudentWithPlans(); + const { isGuest } = useContext(IsGuestContext); const resetValuesToCurrPlan = useCallback(() => { if (!plan) { @@ -96,6 +100,10 @@ export const EditPlanModal: React.FC = ({ plan }) => { handleApiClientError(supportedMajorsError, router); } + if (!student) { + return <>; + } + const catalogYear = watch("catalogYear"); const majorName = watch("major"); const concentration = watch("concentration"); @@ -121,11 +129,28 @@ export const EditPlanModal: React.FC = ({ plan }) => { concentration: isNoMajorSelected ? undefined : payload.concentration, }; - try { - await API.plans.update(plan.id, newPlan); - } catch (error) { - handleApiClientError(error as Error, router); - return; + if (isGuest) { + const newTempPlan = { + ...plan, + ...newPlan, + }; + + window.localStorage.setItem( + "student", + JSON.stringify({ + ...student, + plans: student.plans.map((cur) => + cur.id === plan.id ? newTempPlan : cur + ), + }) + ); + } else { + try { + await API.plans.update(plan.id, newPlan); + } catch (error) { + handleApiClientError(error as Error, router); + return; + } } mutate(USE_STUDENT_WITH_PLANS_SWR_KEY); @@ -178,6 +203,13 @@ export const EditPlanModal: React.FC = ({ plan }) => { })} /> + setIsNoMajorSelected(!isNoMajorSelected)} + /> = ({ plan }) => { > No Major - setIsNoMajorSelected(!isNoMajorSelected)} - /> {!isNoMajorSelected && ( diff --git a/packages/frontend-v2/components/Plan/Plan.tsx b/packages/frontend/components/Plan/Plan.tsx similarity index 98% rename from packages/frontend-v2/components/Plan/Plan.tsx rename to packages/frontend/components/Plan/Plan.tsx index e0b255eaa..46d5d892d 100644 --- a/packages/frontend-v2/components/Plan/Plan.tsx +++ b/packages/frontend/components/Plan/Plan.tsx @@ -9,7 +9,7 @@ import { } from "@graduate/common"; import { useState } from "react"; import { addClassesToTerm, removeYearFromPlan } from "../../utils"; -import { removeCourseFromTerm } from "../../utils/"; +import { removeCourseFromTerm } from "../../utils"; import { ScheduleYear } from "./ScheduleYear"; import { useDroppable } from "@dnd-kit/core"; import { AddYearButton } from "./AddYearButton"; diff --git a/packages/frontend-v2/components/Plan/PlanConcentrationsSelect.tsx b/packages/frontend/components/Plan/PlanConcentrationsSelect.tsx similarity index 100% rename from packages/frontend-v2/components/Plan/PlanConcentrationsSelect.tsx rename to packages/frontend/components/Plan/PlanConcentrationsSelect.tsx diff --git a/packages/frontend-v2/components/Plan/PlanDropdown.tsx b/packages/frontend/components/Plan/PlanDropdown.tsx similarity index 100% rename from packages/frontend-v2/components/Plan/PlanDropdown.tsx rename to packages/frontend/components/Plan/PlanDropdown.tsx diff --git a/packages/frontend-v2/components/Plan/ReqErrorModal.tsx b/packages/frontend/components/Plan/ReqErrorModal.tsx similarity index 98% rename from packages/frontend-v2/components/Plan/ReqErrorModal.tsx rename to packages/frontend/components/Plan/ReqErrorModal.tsx index 81aec83fa..96ed299ba 100644 --- a/packages/frontend-v2/components/Plan/ReqErrorModal.tsx +++ b/packages/frontend/components/Plan/ReqErrorModal.tsx @@ -52,7 +52,7 @@ export const ReqErrorModal: React.FC = ({ }} _active={{ background: "primary.red.900" }} onClick={() => { - setHovered(false) + setHovered(false); onOpen(); }} > @@ -70,7 +70,7 @@ export const ReqErrorModal: React.FC = ({ = ({ diff --git a/packages/frontend-v2/components/Plan/ScheduleTerm.tsx b/packages/frontend/components/Plan/ScheduleTerm.tsx similarity index 98% rename from packages/frontend-v2/components/Plan/ScheduleTerm.tsx rename to packages/frontend/components/Plan/ScheduleTerm.tsx index 119e5b35e..8e11ab213 100644 --- a/packages/frontend-v2/components/Plan/ScheduleTerm.tsx +++ b/packages/frontend/components/Plan/ScheduleTerm.tsx @@ -60,7 +60,7 @@ export const ScheduleTerm: React.FC = ({ = ({ const backgroundColor = isExpanded ? "primary.blue.dark" : "primary.blue.light"; - return ( = ({ _hover={{ backgroundColor: "primary.blue.light.600", }} + _active={{ + bg: "primary.blue.dark.700", + }} transition="background-color 0.15s ease" paddingTop="sm" paddingBottom="sm" @@ -180,8 +182,8 @@ const YearHeader: React.FC = ({ variant="ghost" color="primary.red.main" icon={} - _hover={{ bg: 'none', cursor: 'auto' }} - _active={{ bg: `${backgroundColor}.900` }} + _hover={{ bg: "none", cursor: "auto" }} + _active={{ bg: "none" }} onClick={(e) => { if (isExpanded) { e.stopPropagation(); @@ -190,23 +192,10 @@ const YearHeader: React.FC = ({ /> )} - - } - marginLeft="auto" - marginRight="sm" - _hover={{ bg: "white", color: "primary.red.main" }} - _active={{ bg: `${backgroundColor}.900` }} - onClick={(e) => { - // important to prevent the click from propogating upwards and triggering the toggle - e.stopPropagation(); - removeYearFromCurrPlan(); - }} - /> - + ); diff --git a/packages/frontend-v2/components/Plan/TransferCourses.tsx b/packages/frontend/components/Plan/TransferCourses.tsx similarity index 88% rename from packages/frontend-v2/components/Plan/TransferCourses.tsx rename to packages/frontend/components/Plan/TransferCourses.tsx index c75b2ea92..0eb46b07f 100644 --- a/packages/frontend-v2/components/Plan/TransferCourses.tsx +++ b/packages/frontend/components/Plan/TransferCourses.tsx @@ -13,6 +13,8 @@ import { import { AddCourseButton, AddCourseModal } from "../AddCourseModal"; import { HelperToolTip } from "../Help"; import { NonDraggableScheduleCourse } from "../ScheduleCourse"; +import { useContext } from "react"; +import { IsGuestContext } from "../../pages/_app"; interface TransferCoursesToggleProps { isExpanded: boolean; @@ -25,6 +27,7 @@ export const TransferCourses: React.FC = ({ }) => { const { student, isLoading, mutateStudent } = useStudentWithPlans(); const router = useRouter(); + const { isGuest } = useContext(IsGuestContext); /* Simply refrain from displaying the transfer courses section if @@ -57,8 +60,15 @@ export const TransferCourses: React.FC = ({ async () => { // have to clean all dnd ids before sending the student to our API const studentWithoutDndIds = cleanDndIdsFromStudent(updatedStudent); - await API.student.update(studentWithoutDndIds); - return fetchStudentAndPrepareForDnd(); + if (isGuest) { + window.localStorage.setItem( + "student", + JSON.stringify(studentWithoutDndIds) + ); + } else { + await API.student.update(studentWithoutDndIds); + } + return fetchStudentAndPrepareForDnd(isGuest); }, { optimisticData: updatedStudent, @@ -71,7 +81,7 @@ export const TransferCourses: React.FC = ({ }; return ( - + = ({ removeCourse={() => removeTransferCourse(course)} /> ))} - + = ({ = ({ cursor="pointer" userSelect="none" borderBottomWidth={isExpanded ? "2px" : "0px"} - borderBottomColor="neutral.900" + borderBottomColor="neutral.200" paddingX="md" > diff --git a/packages/frontend-v2/components/Plan/index.ts b/packages/frontend/components/Plan/index.ts similarity index 100% rename from packages/frontend-v2/components/Plan/index.ts rename to packages/frontend/components/Plan/index.ts diff --git a/packages/frontend-v2/components/ScheduleCourse/CourseDragIcon.tsx b/packages/frontend/components/ScheduleCourse/CourseDragIcon.tsx similarity index 100% rename from packages/frontend-v2/components/ScheduleCourse/CourseDragIcon.tsx rename to packages/frontend/components/ScheduleCourse/CourseDragIcon.tsx diff --git a/packages/frontend-v2/components/ScheduleCourse/CourseTrashButton.tsx b/packages/frontend/components/ScheduleCourse/CourseTrashButton.tsx similarity index 100% rename from packages/frontend-v2/components/ScheduleCourse/CourseTrashButton.tsx rename to packages/frontend/components/ScheduleCourse/CourseTrashButton.tsx diff --git a/packages/frontend-v2/components/ScheduleCourse/ScheduleCourse.tsx b/packages/frontend/components/ScheduleCourse/ScheduleCourse.tsx similarity index 89% rename from packages/frontend-v2/components/ScheduleCourse/ScheduleCourse.tsx rename to packages/frontend/components/ScheduleCourse/ScheduleCourse.tsx index e652f85d1..fd5c4656b 100644 --- a/packages/frontend-v2/components/ScheduleCourse/ScheduleCourse.tsx +++ b/packages/frontend/components/ScheduleCourse/ScheduleCourse.tsx @@ -1,5 +1,5 @@ -import { DeleteIcon } from "@chakra-ui/icons"; -import { Flex } from "@chakra-ui/react"; +import { DeleteIcon, CheckIcon } from "@chakra-ui/icons"; +import { Box, Flex } from "@chakra-ui/react"; import { useDraggable } from "@dnd-kit/core"; import { CSS } from "@dnd-kit/utilities"; import { @@ -24,6 +24,8 @@ interface DraggableScheduleCourseProps { scheduleCourse: ScheduleCourse2; coReqErr?: INEUReqError; preReqErr?: INEUReqError; + isInSidebar?: boolean; + isChecked?: boolean; /** Function to remove the course from whatever the schedule it is part of. */ removeCourse?: (course: ScheduleCourse2) => void; isEditable?: boolean; @@ -40,6 +42,8 @@ export const DraggableScheduleCourse: React.FC< removeCourse, preReqErr = undefined, coReqErr = undefined, + isInSidebar = false, + isChecked = false, isEditable = false, isDisabled = false, setIsRemove, @@ -64,6 +68,8 @@ export const DraggableScheduleCourse: React.FC< ref={setNodeRef} scheduleCourse={scheduleCourse} removeCourse={removeCourse} + isInSidebar={isInSidebar} + isChecked={isChecked} isEditable={isEditable} isDragging={isDragging} listeners={listeners} @@ -114,6 +120,7 @@ export const NonDraggableScheduleCourse: React.FC< isDisabled={false} isEditable={true} removeCourse={removeCourse} + isDraggable={false} /> ); }; @@ -179,6 +186,8 @@ const ScheduleCourse = forwardRef( preReqErr = undefined, scheduleCourse, removeCourse, + isInSidebar = false, + isChecked = false, isEditable = false, isDragging = false, listeners, @@ -218,7 +227,7 @@ const ScheduleCourse = forwardRef( flex: scheduleCourse.classId === COOP_BLOCK.classId ? 1 : 0, marginBottom: "6px", transition: "transform 0.15s ease, opacity 0.25s ease", - transform: hovered ? "scale(1.04)" : "scale(1)", + transform: hovered && isDraggable ? "scale(1.04)" : "scale(1)", opacity: isValidRemove ? 0.5 : 1, justifyContent: "space-between", width: "100%", @@ -238,7 +247,7 @@ const ScheduleCourse = forwardRef( isOverlay={isOverlay} isDraggable={isDraggable} /> - + {isCourseError && ( ( } /> )} + {isInSidebar && isChecked && ( + + + + )} + {isEditable && !hovered && } {isOverlay && !isFromSidebar && ( @@ -321,7 +351,7 @@ const ScheduleCourseDraggedContents: React.FC<
= ({ const renderXOM = (requirement: IXofManyCourse) => { return (
- - Complete {requirement.numCreditsMin} credits from the following + + Complete {requirement.numCreditsMin} credits from the following: {requirement.courses.map((course, index) => ( = ({ const renderAND = (requirement: IAndCourse2) => { return (
- Complete all of the following + + Complete all of the following: + {requirement.courses.map((course, index) => ( = ({ const renderOR = (requirement: IOrCourse2) => { return (
- Complete one of the following + + Complete 1 of the following: + {requirement.courses.map((course, index) => ( = ({ const renderRange = (requirement: ICourseRange2) => { return ( - + Complete any course in range {requirement.subject} {requirement.idRangeStart} to {requirement.subject} {requirement.idRangeEnd}{" "} @@ -127,6 +131,9 @@ const SectionRequirement: React.FC = ({ ...scheduleCourse, id: dndIdPrefix + "-" + courseKey, }} + isInSidebar + // TODO: isChecked is for when the requirement is added to the plan and validated. When true, this will render a checkmark. + isChecked={false} isDisabled={false} /> ); diff --git a/packages/frontend-v2/components/Sidebar/Sidebar.tsx b/packages/frontend/components/Sidebar/Sidebar.tsx similarity index 98% rename from packages/frontend-v2/components/Sidebar/Sidebar.tsx rename to packages/frontend/components/Sidebar/Sidebar.tsx index 62f00113b..f6db4d986 100644 --- a/packages/frontend-v2/components/Sidebar/Sidebar.tsx +++ b/packages/frontend/components/Sidebar/Sidebar.tsx @@ -325,7 +325,12 @@ const SidebarContainer: React.FC> = ({ children, }) => { return ( - + @@ -355,7 +360,7 @@ const SidebarContainer: React.FC> = ({ {creditsTaken} {creditsToTake !== undefined && `/${creditsToTake}`} - Completed Credits + Credits Completed )} {renderCoopBlock && ( diff --git a/packages/frontend-v2/components/Sidebar/SidebarSection.tsx b/packages/frontend/components/Sidebar/SidebarSection.tsx similarity index 95% rename from packages/frontend-v2/components/Sidebar/SidebarSection.tsx rename to packages/frontend/components/Sidebar/SidebarSection.tsx index 2eafef705..33b63ffd7 100644 --- a/packages/frontend-v2/components/Sidebar/SidebarSection.tsx +++ b/packages/frontend/components/Sidebar/SidebarSection.tsx @@ -49,7 +49,7 @@ const SidebarSection: React.FC = ({ return ( @@ -65,10 +65,10 @@ const SidebarSection: React.FC = ({ py="md" px="md" margin="0" - backgroundColor="neutral.main" + backgroundColor="neutral.50" transition="background-color 0.25s ease" _hover={{ - backgroundColor: "neutral.900", + backgroundColor: "neutral.100", }} _active={{ backgroundColor: "neutral.200", @@ -160,7 +160,9 @@ const SidebarSection: React.FC = ({ @@ -173,8 +175,8 @@ const SidebarSection: React.FC = ({ {opened && !loading && ( <> {section.minRequirementCount < section.requirements.length && ( - - Complete {section.minRequirementCount} of the following + + Complete {section.minRequirementCount} of the following: )} {section.requirements.map((requirement, index) => ( diff --git a/packages/frontend-v2/components/Sidebar/index.ts b/packages/frontend/components/Sidebar/index.ts similarity index 100% rename from packages/frontend-v2/components/Sidebar/index.ts rename to packages/frontend/components/Sidebar/index.ts diff --git a/packages/frontend-v2/components/Spinner/LoadingPage.tsx b/packages/frontend/components/Spinner/LoadingPage.tsx similarity index 100% rename from packages/frontend-v2/components/Spinner/LoadingPage.tsx rename to packages/frontend/components/Spinner/LoadingPage.tsx diff --git a/packages/frontend-v2/components/Spinner/PageSpinner.tsx b/packages/frontend/components/Spinner/PageSpinner.tsx similarity index 100% rename from packages/frontend-v2/components/Spinner/PageSpinner.tsx rename to packages/frontend/components/Spinner/PageSpinner.tsx diff --git a/packages/frontend-v2/components/Spinner/index.ts b/packages/frontend/components/Spinner/index.ts similarity index 100% rename from packages/frontend-v2/components/Spinner/index.ts rename to packages/frontend/components/Spinner/index.ts diff --git a/packages/frontend-v2/components/index.ts b/packages/frontend/components/index.ts similarity index 100% rename from packages/frontend-v2/components/index.ts rename to packages/frontend/components/index.ts diff --git a/packages/frontend-v2/hooks/index.ts b/packages/frontend/hooks/index.ts similarity index 100% rename from packages/frontend-v2/hooks/index.ts rename to packages/frontend/hooks/index.ts diff --git a/packages/frontend-v2/hooks/useFetchCourse.ts b/packages/frontend/hooks/useFetchCourse.ts similarity index 100% rename from packages/frontend-v2/hooks/useFetchCourse.ts rename to packages/frontend/hooks/useFetchCourse.ts diff --git a/packages/frontend-v2/hooks/useFetchCourses.ts b/packages/frontend/hooks/useFetchCourses.ts similarity index 100% rename from packages/frontend-v2/hooks/useFetchCourses.ts rename to packages/frontend/hooks/useFetchCourses.ts diff --git a/packages/frontend-v2/hooks/useLocalStorage.ts b/packages/frontend/hooks/useLocalStorage.ts similarity index 85% rename from packages/frontend-v2/hooks/useLocalStorage.ts rename to packages/frontend/hooks/useLocalStorage.ts index d2d72d065..702d85c49 100644 --- a/packages/frontend-v2/hooks/useLocalStorage.ts +++ b/packages/frontend/hooks/useLocalStorage.ts @@ -16,9 +16,10 @@ import { logger } from "../utils"; * @param key Key for the localstorage value. */ export function useLocalStorage( - key: string -): [T | null, Dispatch] { - const [storedValue, setStoredValue] = useState(null); + key: string, + defaultValue: T +): [T, Dispatch] { + const [storedValue, setStoredValue] = useState(defaultValue); useEffect(() => { try { @@ -32,7 +33,7 @@ export function useLocalStorage( const setValue = (value: T) => { try { setStoredValue(value); - window?.localStorage.setItem(key, JSON.stringify(value)); + window.localStorage.setItem(key, JSON.stringify(value)); } catch (error) { logger.error(error); } diff --git a/packages/frontend-v2/hooks/useMajor.ts b/packages/frontend/hooks/useMajor.ts similarity index 100% rename from packages/frontend-v2/hooks/useMajor.ts rename to packages/frontend/hooks/useMajor.ts diff --git a/packages/frontend-v2/hooks/usePlan.ts b/packages/frontend/hooks/usePlan.ts similarity index 100% rename from packages/frontend-v2/hooks/usePlan.ts rename to packages/frontend/hooks/usePlan.ts diff --git a/packages/frontend-v2/hooks/useRedirectIfLoggedIn.ts b/packages/frontend/hooks/useRedirectIfLoggedIn.ts similarity index 51% rename from packages/frontend-v2/hooks/useRedirectIfLoggedIn.ts rename to packages/frontend/hooks/useRedirectIfLoggedIn.ts index e6b56f14f..b42e40224 100644 --- a/packages/frontend-v2/hooks/useRedirectIfLoggedIn.ts +++ b/packages/frontend/hooks/useRedirectIfLoggedIn.ts @@ -1,8 +1,9 @@ import { API } from "@graduate/api-client"; import router from "next/router"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { logger } from "../utils"; import { AxiosError } from "axios"; +import { IsGuestContext } from "../pages/_app"; /** * If the user is already logged in, then redirect to onboarding/home. @@ -12,22 +13,23 @@ import { AxiosError } from "axios"; */ export const useRedirectIfLoggedIn = () => { const [renderSpinner, setRenderSpinner] = useState(false); - - const loginWithCookie = async () => { - setRenderSpinner(true); - try { - await API.student.getMe(); - router.push("/home"); - } catch (err) { - const error = err as AxiosError; - logger.error(error); - setRenderSpinner(false); - } - }; + const { setIsGuest } = useContext(IsGuestContext); useEffect(() => { + const loginWithCookie = async () => { + setRenderSpinner(true); + try { + await API.student.getMe(); + setIsGuest(false); + router.push("/home"); + } catch (err) { + const error = err as AxiosError; + logger.error(error); + setRenderSpinner(false); + } + }; loginWithCookie(); - }, []); + }, [setIsGuest]); return renderSpinner; }; diff --git a/packages/frontend-v2/hooks/useRedirectIfUnauthorized.ts b/packages/frontend/hooks/useRedirectIfUnauthorized.ts similarity index 100% rename from packages/frontend-v2/hooks/useRedirectIfUnauthorized.ts rename to packages/frontend/hooks/useRedirectIfUnauthorized.ts diff --git a/packages/frontend-v2/hooks/useSearchCourses.ts b/packages/frontend/hooks/useSearchCourses.ts similarity index 100% rename from packages/frontend-v2/hooks/useSearchCourses.ts rename to packages/frontend/hooks/useSearchCourses.ts diff --git a/packages/frontend-v2/hooks/useStudentWithPlans.ts b/packages/frontend/hooks/useStudentWithPlans.ts similarity index 58% rename from packages/frontend-v2/hooks/useStudentWithPlans.ts rename to packages/frontend/hooks/useStudentWithPlans.ts index af4297938..b2ebad640 100644 --- a/packages/frontend-v2/hooks/useStudentWithPlans.ts +++ b/packages/frontend/hooks/useStudentWithPlans.ts @@ -2,7 +2,9 @@ import useSWR, { KeyedMutator, SWRResponse } from "swr"; import { API } from "@graduate/api-client"; import { GetStudentResponse, StudentModel } from "@graduate/common"; import { AxiosError } from "axios"; -import { preparePlanForDnd } from "../utils"; +import { defaultGuestStudent, preparePlanForDnd } from "../utils"; +import { useContext } from "react"; +import { IsGuestContext } from "../pages/_app"; type StudentResponse = Omit< SWRResponse, @@ -22,9 +24,10 @@ export const USE_STUDENT_WITH_PLANS_SWR_KEY = `api/students/me`; * to cookies. */ export function useStudentWithPlans(): UseStudentReturn { - const { data, mutate, ...rest } = useSWR( - USE_STUDENT_WITH_PLANS_SWR_KEY, - fetchStudentAndPrepareForDnd + const { isGuest } = useContext(IsGuestContext); + + const { data, mutate, ...rest } = useSWR(USE_STUDENT_WITH_PLANS_SWR_KEY, () => + fetchStudentAndPrepareForDnd(isGuest) ); return { @@ -39,10 +42,20 @@ export function useStudentWithPlans(): UseStudentReturn { * Fetches the student with plans and prepares all of the student's plans for * drag and drop by adding drag and drop ids. */ -export const fetchStudentAndPrepareForDnd = async (): Promise< - StudentModel -> => { - const student = await API.student.getMeWithPlan(); +export const fetchStudentAndPrepareForDnd = async ( + isGuest: boolean +): Promise> => { + const studentString = window.localStorage.getItem("student"); + const studentFromLocalStorage: GetStudentResponse = studentString + ? JSON.parse(studentString) + : defaultGuestStudent; + + let student: GetStudentResponse; + if (!isGuest) { + student = await API.student.getMeWithPlan(); + } else { + student = studentFromLocalStorage; + } const plansWithDndIds = student.plans.map(preparePlanForDnd); return { diff --git a/packages/frontend-v2/hooks/useSupportedMajors.ts b/packages/frontend/hooks/useSupportedMajors.ts similarity index 100% rename from packages/frontend-v2/hooks/useSupportedMajors.ts rename to packages/frontend/hooks/useSupportedMajors.ts diff --git a/packages/frontend-v2/hooks/useWindowSize.ts b/packages/frontend/hooks/useWindowSize.ts similarity index 100% rename from packages/frontend-v2/hooks/useWindowSize.ts rename to packages/frontend/hooks/useWindowSize.ts diff --git a/packages/frontend-v2/next-env.d.ts b/packages/frontend/next-env.d.ts similarity index 100% rename from packages/frontend-v2/next-env.d.ts rename to packages/frontend/next-env.d.ts diff --git a/packages/frontend-v2/next.config.js b/packages/frontend/next.config.js similarity index 100% rename from packages/frontend-v2/next.config.js rename to packages/frontend/next.config.js diff --git a/packages/frontend-v2/package.json b/packages/frontend/package.json similarity index 96% rename from packages/frontend-v2/package.json rename to packages/frontend/package.json index 76433fcfc..65887e3b1 100644 --- a/packages/frontend-v2/package.json +++ b/packages/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "@graduate/frontend-v2", + "name": "@graduate/frontend", "version": "0.1.0", "private": true, "scripts": { diff --git a/packages/frontend/pages/404.tsx b/packages/frontend/pages/404.tsx new file mode 100644 index 000000000..973821498 --- /dev/null +++ b/packages/frontend/pages/404.tsx @@ -0,0 +1,5 @@ +import { ClientSideError } from "../components/Error/ClientSideError"; + +export default function Custom404() { + return ; +} diff --git a/packages/frontend-v2/pages/_app.tsx b/packages/frontend/pages/_app.tsx similarity index 56% rename from packages/frontend-v2/pages/_app.tsx rename to packages/frontend/pages/_app.tsx index 73dc36dc3..9f4ee2f2f 100644 --- a/packages/frontend-v2/pages/_app.tsx +++ b/packages/frontend/pages/_app.tsx @@ -1,35 +1,53 @@ -import Head from "next/head"; +import { Dispatch, SetStateAction, createContext, useState } from "react"; +import { ChakraProvider, Flex, Heading, Image, Text } from "@chakra-ui/react"; +import "@fontsource/montserrat-alternates"; import type { AppProps } from "next/app"; -import { ChakraProvider, Flex, Heading, Text, Image } from "@chakra-ui/react"; -import { theme } from "../utils"; -import "react-toastify/dist/ReactToastify.min.css"; +import Head from "next/head"; +import { useRouter } from "next/router"; import { ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.min.css"; import { ErrorBoundary, GraduateDisabledAppHeader } from "../components"; -import "@fontsource/montserrat-alternates"; import { useWindowSize } from "../hooks"; +import { theme } from "../utils"; +import { ClientSideError } from "../components/Error/ClientSideError"; + +interface IsGuestContextType { + isGuest: boolean; + setIsGuest: Dispatch>; +} + +export const IsGuestContext = createContext({ + isGuest: false, + setIsGuest: () => undefined, +}); function MyApp({ Component, pageProps }: AppProps) { + const router = useRouter(); const { width } = useWindowSize(); - const disableApp = width && width <= 1100; + const isLandingPage = router.asPath === "/"; + const disableApp = !isLandingPage && width && width <= 1100; + const [isGuest, setIsGuest] = useState(false); return ( <> - - GraduateNU - - - - - + + + GraduateNU + + + + - {disableApp ? : } + + {disableApp ? : } + + - ); } diff --git a/packages/frontend-v2/pages/confirmEmail.tsx b/packages/frontend/pages/confirmEmail.tsx similarity index 100% rename from packages/frontend-v2/pages/confirmEmail.tsx rename to packages/frontend/pages/confirmEmail.tsx diff --git a/packages/frontend-v2/pages/emailConfirmation.tsx b/packages/frontend/pages/emailConfirmation.tsx similarity index 100% rename from packages/frontend-v2/pages/emailConfirmation.tsx rename to packages/frontend/pages/emailConfirmation.tsx diff --git a/packages/frontend-v2/pages/forgotPassword.tsx b/packages/frontend/pages/forgotPassword.tsx similarity index 100% rename from packages/frontend-v2/pages/forgotPassword.tsx rename to packages/frontend/pages/forgotPassword.tsx diff --git a/packages/frontend-v2/pages/home.tsx b/packages/frontend/pages/home.tsx similarity index 84% rename from packages/frontend-v2/pages/home.tsx rename to packages/frontend/pages/home.tsx index edfe71ec0..a80b31729 100644 --- a/packages/frontend-v2/pages/home.tsx +++ b/packages/frontend/pages/home.tsx @@ -18,13 +18,14 @@ import { } from "@graduate/common"; import { NextPage } from "next"; import { useRouter } from "next/router"; -import { PropsWithChildren, useEffect, useState } from "react"; +import { PropsWithChildren, useContext, useEffect, useState } from "react"; import { AddPlanModal, DeletePlanModal, DraggedScheduleCourse, EditPlanModal, GraduatePostAuthHeader, + GraduatePreAuthHeader, LoadingPage, NoMajorSidebar, NoPlanSidebar, @@ -39,6 +40,7 @@ import { DELETE_COURSE_AREA_DND_ID, handleApiClientError, logger, + toast, updatePlanForStudent, updatePlanOnDragEnd, } from "../utils"; @@ -46,6 +48,7 @@ import { getCoReqWarnings, getPreReqWarnings, } from "../utils/plan/preAndCoReqCheck"; +import { IsGuestContext } from "./_app"; // Algorithm to decide which droppable the course is currently over (if any). // See https://docs.dndkit.com/api-documentation/context-provider/collision-detection-algorithms for more info. @@ -87,13 +90,15 @@ const HomePage: NextPage = () => { const [isTransferCoursesExpanded, setIsTransferCoursesExpanded] = useState(false); + const { isGuest } = useContext(IsGuestContext); + useEffect(() => { // once the student is fetched, set the selected plan id to the last updated plan if (student && selectedPlanId === undefined) { if (student.plans.length > 0) { const sortedPlans = student.plans.sort( (p1, p2) => - new Date(p1.updatedAt).getTime() - new Date(p2.updatedAt).getTime() + new Date(p2.updatedAt).getTime() - new Date(p1.updatedAt).getTime() ); setSelectedPlanId(sortedPlans[0].id); } @@ -101,12 +106,26 @@ const HomePage: NextPage = () => { if (student) { const plan = student.plans.find((plan) => plan.id === selectedPlanId); if (plan) { - setPreReqWarnings(getPreReqWarnings(plan.schedule, student.coursesTransfered)); + setPreReqWarnings( + getPreReqWarnings(plan.schedule, student.coursesTransfered) + ); setCoReqWarnings(getCoReqWarnings(plan.schedule)); } } }, [student, selectedPlanId, setSelectedPlanId]); + /** + * Render a warning modal to let users know that if they are on a guest + * account, we don't save information + */ + useEffect(() => { + if (isGuest) { + toast.warn( + "You are logged in on a guest account. Your data will be saved locally, but not on our servers" + ); + } + }, [isGuest]); + /** * Handle errors from useStudentWithPlans. * @@ -170,7 +189,9 @@ const HomePage: NextPage = () => { return; } - setPreReqWarnings(getPreReqWarnings(updatedPlan.schedule, student.coursesTransfered)); + setPreReqWarnings( + getPreReqWarnings(updatedPlan.schedule, student.coursesTransfered) + ); setCoReqWarnings(getCoReqWarnings(updatedPlan.schedule)); mutateStudentWithUpdatedPlan(updatedPlan); }; @@ -187,8 +208,26 @@ const HomePage: NextPage = () => { async () => { // remove dnd ids, update the plan, and refetch the student const cleanedPlan = cleanDndIdsFromPlan(updatedPlan); - await API.plans.update(updatedPlan.id, cleanedPlan); - return fetchStudentAndPrepareForDnd(); + if (isGuest) { + const cleanedPlanWithUpdatedTimeStamp: PlanModel = { + ...cleanedPlan, + updatedAt: new Date(), + }; + window.localStorage.setItem( + "student", + JSON.stringify({ + ...student, + plans: student.plans.map((plan) => + plan.id === cleanedPlanWithUpdatedTimeStamp.id + ? cleanedPlanWithUpdatedTimeStamp + : plan + ), + }) + ); + } else { + await API.plans.update(updatedPlan.id, cleanedPlan); + } + return fetchStudentAndPrepareForDnd(isGuest); }, { optimisticData: updatedStudent, @@ -222,7 +261,7 @@ const HomePage: NextPage = () => { > { mutateStudentWithUpdatedPlan={mutateStudentWithUpdatedPlan} setIsRemove={setIsRemove} /> - + )} { */ const PageLayout: React.FC = ({ children }) => { const { setNodeRef } = useDroppable({ id: DELETE_COURSE_AREA_DND_ID }); + const { isGuest } = useContext(IsGuestContext); return ( = ({ children }) => { overflow="hidden" ref={setNodeRef} > - + {isGuest ? : } {children} diff --git a/packages/frontend/pages/index.tsx b/packages/frontend/pages/index.tsx new file mode 100644 index 000000000..66bd15c75 --- /dev/null +++ b/packages/frontend/pages/index.tsx @@ -0,0 +1,300 @@ +import type { NextPage } from "next"; +import { + Box, + Flex, + Heading, + Image, + SimpleGrid, + Text, + VStack, + Link, + useMediaQuery, +} from "@chakra-ui/react"; +import { GraduateButtonLink, GraduatePreAuthHeader } from "../components"; + +type InfoSectionProps = InfoImageProps & InfoTextProps; + +interface InfoImageProps { + imageSource: string; + altInfo: string; +} + +interface InfoTextProps { + title: string; + description: string; +} + +const LandingPage: NextPage = () => { + const [isMobile] = useMediaQuery("(max-width: 640px)"); + + return ( + + + + + {isMobile && } +