Skip to content

Commit

Permalink
Merge branch 'main' into whats-new-modal
Browse files Browse the repository at this point in the history
  • Loading branch information
KobeZ123 committed Nov 3, 2024
2 parents a43a443 + 6d77811 commit 672224a
Show file tree
Hide file tree
Showing 21 changed files with 437 additions and 116 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ GraduateNU aims to empower Northeastern students to customize their plan of stud

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.

5a. For Windows machines, run `yarn concurrently "yarn workspaces foreach --parallel --verbose --interlaced run dev" "yarn dev:proxy"` instead of `yarn dev`

6. Visit [http://localhost:3002](http://localhost:3002/) to view the app.

To run the two separately, visit the frontend and api packages(sub directories of the monorepo).
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/major2-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type CourseError = {
type: typeof MajorValidationErrorType.Course;
requiredCourse: string;
};

export const CourseError = (c: IRequiredCourse): CourseError => ({
type: MajorValidationErrorType.Course,
requiredCourse: courseToString(c),
Expand Down
Empty file added packages/common/src/test.js
Empty file.
1 change: 0 additions & 1 deletion packages/common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ export type Requirement2 =
| ICourseRange2
| IRequiredCourse
| Section;

/**
* Represents a requirement where X number of credits need to be completed from
* a list of courses.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Text,
VStack,
} from "@chakra-ui/react";
import { NUPathEnum, ScheduleCourse2 } from "@graduate/common";
import { NUPathEnum, ScheduleCourse2, SeasonEnum } from "@graduate/common";
import { useState } from "react";
import { useSearchCourses } from "../../hooks";
import {
Expand All @@ -33,6 +33,7 @@ import { SecondaryButton } from "../Button";
interface AddCourseModalProps {
isOpen: boolean;
catalogYear?: number;
season: SeasonEnum;
addTo: string;
/** Function to close the modal UX, returned from the useDisclosure chakra hook */
closeModalDisplay: () => void;
Expand All @@ -50,6 +51,7 @@ interface AddCourseModalProps {
export const AddCourseModal: React.FC<AddCourseModalProps> = ({
isOpen,
catalogYear,
season,
addTo,
closeModalDisplay,
isCourseAlreadyAdded,
Expand Down Expand Up @@ -227,6 +229,8 @@ export const AddCourseModal: React.FC<AddCourseModalProps> = ({
(course) => (
<SearchResult
key={getCourseDisplayString(course)}
year={catalogYear}
season={season}
course={course}
addSelectedCourse={addSelectedCourse}
isResultAlreadyAdded={isCourseAlreadyAdded(course)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const NUPathLabel: React.FC<NuPathLabelProps> = ({
}

return (
<Flex justifyContent="end" gap="2xs" flex="1" ml="xs">
<Flex justifyContent="end" gap="2xs" ml="xs">
{nuPaths.map((nuPath) => (
<Flex
key={nuPaths.indexOf(nuPath)}
Expand Down
29 changes: 27 additions & 2 deletions packages/frontend/components/AddCourseModal/SearchResult.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { AddIcon } from "@chakra-ui/icons";
import { AddIcon, QuestionOutlineIcon } from "@chakra-ui/icons";
import { Box, Flex, IconButton, Text } from "@chakra-ui/react";
import { NUPathEnum, ScheduleCourse2 } from "@graduate/common";
import { NUPathEnum, ScheduleCourse2, SeasonEnum } from "@graduate/common";
import { getCourseDisplayString } from "../../utils/";
import { GraduateToolTip } from "../GraduateTooltip";
import { NUPathLabel } from "./NUPathLabel";
import { getSearchLink } from "../ScheduleCourse";

interface SearchResultProps {
course: ScheduleCourse2<null>;
year: number | undefined;
season: SeasonEnum;
addSelectedCourse: (course: ScheduleCourse2<null>) => Promise<void>;
isResultAlreadySelected: boolean;
isResultAlreadyAdded: boolean;
Expand All @@ -17,6 +20,8 @@ interface SearchResultProps {

export const SearchResult: React.FC<SearchResultProps> = ({
course,
year,
season,
addSelectedCourse,
isResultAlreadySelected,
isResultAlreadyAdded,
Expand Down Expand Up @@ -48,6 +53,26 @@ export const SearchResult: React.FC<SearchResultProps> = ({
{course.name}
</Text>
</Box>
<Box ml="auto" mr="sm"></Box>
<a
href={getSearchLink(year ?? 2022, season, course)}
target="_blank"
rel="noreferrer"
>
<IconButton
aria-label="Add class"
icon={<QuestionOutlineIcon />}
color="primary.blue.light.main"
border={0}
colorScheme="primary.blue.light.main"
isRound
size="sm"
pr={1}
isLoading={isSelectingAnotherCourse}
isDisabled={isResultAlreadyAdded || isResultAlreadySelected}
alignSelf="center"
/>
</a>
<NUPathLabel
nuPaths={course.nupaths ? course.nupaths : []}
filteredPaths={filteredPaths}
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/components/Plan/AddPlanModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ export const AddPlanModal: React.FC<AddPlanModalProps> = ({
<Flex alignItems="center">
<Checkbox
mr="md"
borderColor="primary.blue.dark.main"
{...register("agreeToBetaMajor", {
required: "You must agree to continue",
})}
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/components/Plan/ScheduleTerm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export const ScheduleTerm: React.FC<ScheduleTermProps> = ({
))}
<AddCourseButton onOpen={onOpen} />
<AddCourseModal
season={scheduleTerm.season}
isOpen={isOpen}
catalogYear={catalogYear}
addTo={getSeasonDisplayWord(scheduleTerm.season)}
Expand Down
11 changes: 10 additions & 1 deletion packages/frontend/components/Plan/TransferCourses.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChevronDownIcon, ChevronUpIcon } from "@chakra-ui/icons";
import { Flex, Grid, Stack, Text, useDisclosure } from "@chakra-ui/react";
import { API } from "@graduate/api-client";
import { ScheduleCourse2, StudentModel } from "@graduate/common";
import { ScheduleCourse2, SeasonEnum, StudentModel } from "@graduate/common";
import { useRouter } from "next/router";
import { fetchStudentAndPrepareForDnd, useStudentWithPlans } from "../../hooks";
import {
Expand All @@ -19,11 +19,13 @@ import { IsGuestContext } from "../../pages/_app";
interface TransferCoursesToggleProps {
isExpanded: boolean;
toggleExpanded: () => void;
year: number;
}

export const TransferCourses: React.FC<TransferCoursesToggleProps> = ({
isExpanded,
toggleExpanded,
year,
}) => {
const { student, isLoading, mutateStudent } = useStudentWithPlans();
const router = useRouter();
Expand Down Expand Up @@ -86,11 +88,13 @@ export const TransferCourses: React.FC<TransferCoursesToggleProps> = ({
isExpanded={isExpanded}
toggleExpanded={toggleExpanded}
totalTransferCredits={totalTransferCredits}
year={year}
/>
{isExpanded && (
<TransferCoursesBody
transferCourses={transferCourses}
updateTransferCourses={updateTransferCourses}
year={year}
/>
)}
</Flex>
Expand All @@ -102,11 +106,13 @@ interface TransferCoursesBodyProps {
updateTransferCourses: (
updateTransferCourses: ScheduleCourse2<null>[]
) => void;
year: number;
}

const TransferCoursesBody: React.FC<TransferCoursesBodyProps> = ({
transferCourses,
updateTransferCourses,
year,
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();

Expand Down Expand Up @@ -145,6 +151,9 @@ const TransferCoursesBody: React.FC<TransferCoursesBodyProps> = ({
))}
<AddCourseButton onOpen={onOpen} />
<AddCourseModal
//is season really necessary? i have placeholder here
season={SeasonEnum.FL}
catalogYear={year}
isOpen={isOpen}
addTo="Transfer Courses"
closeModalDisplay={onClose}
Expand Down
31 changes: 31 additions & 0 deletions packages/frontend/components/ScheduleCourse/ScheduleCourse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
INEUReqError,
IRequiredCourse,
ScheduleCourse2,
SeasonEnum,
} from "@graduate/common";
import { forwardRef, PropsWithChildren, useEffect, useState } from "react";
import {
Expand Down Expand Up @@ -383,3 +384,33 @@ const ScheduleCourseDraggedContents: React.FC<
const ScheduleCourseSpacer: React.FC = () => {
return <div style={{ width: "32px", height: "32px", flexShrink: 0 }}></div>;
};

export function getSearchLink(
catalogYear: number,
szn: SeasonEnum,
course: ScheduleCourse2<unknown>
): string {
let sznInt = -1;
switch (szn) {
case SeasonEnum.FL:
sznInt = 1;
break;
case SeasonEnum.SP:
sznInt = 3;
break;
case SeasonEnum.S1:
sznInt = 4;
break;
case SeasonEnum.SM:
sznInt = 5;
break;
case SeasonEnum.S2:
sznInt = 6;
break;
default:
sznInt = 1;
}
return `https://searchneu.com/NEU/${catalogYear}${sznInt}${0}/search/${
course.name
}`;
}
21 changes: 20 additions & 1 deletion packages/frontend/components/Sidebar/NUPathSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ const NUPathSection: React.FC<NUPathSectionProps> = ({

if (loading) {
validationStatus = SidebarValidationStatus.Loading;
} else if (
Object.keys(nupathMap).length > 0 &&
Object.keys(nupathMap).length < 13
) {
validationStatus = SidebarValidationStatus.InProgress;
} else if (Object.keys(nupathMap).length === 13 && wiCount >= 2) {
// Sidebar is complete if all 13 nupaths have been fulfilled (including 2 writing intensives)
validationStatus = SidebarValidationStatus.Complete;
Expand Down Expand Up @@ -105,11 +110,15 @@ const NUPathSection: React.FC<NUPathSectionProps> = ({
? green
: validationStatus === SidebarValidationStatus.Error
? grey
: validationStatus === SidebarValidationStatus.InProgress
? "orange"
: "transparent"
}
borderColor={
validationStatus === SidebarValidationStatus.Complete
? green
: validationStatus === SidebarValidationStatus.InProgress
? "orange"
: grey
}
color={
Expand All @@ -130,7 +139,7 @@ const NUPathSection: React.FC<NUPathSectionProps> = ({
p="xs"
>
{/*
The following three icons appear and disappear based on opacity to allow for transitions (if they're conditionally rendered, then transitions can't occur).
The following four icons appear and disappear based on opacity to allow for transitions (if they're conditionally rendered, then transitions can't occur).
*/}
<CheckIcon
position="absolute"
Expand Down Expand Up @@ -160,6 +169,16 @@ const NUPathSection: React.FC<NUPathSectionProps> = ({
transition="opacity 0.25s ease"
transitionDelay="0.1s"
/>
<Text
opacity={
validationStatus === SidebarValidationStatus.InProgress ? 1 : 0
}
fontSize="s"
boxSize="34px"
color="white"
>
...
</Text>
</Box>
<Text color="primary.blue.dark.main" mt="0" fontSize="sm">
NUpath Requirements
Expand Down
100 changes: 100 additions & 0 deletions packages/frontend/components/Sidebar/SandboxArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
Box,
VStack,
Flex,
Image,
Text,
Textarea,
TextareaProps,
} from "@chakra-ui/react";
import { forwardRef, useEffect, useState } from "react";
import ResizeTextarea from "react-textarea-autosize";
import { toast } from "react-toastify";

const AutoResizeTextarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
(props, ref) => {
return (
<Textarea
minH="unset"
w="100%"
ref={ref}
minRows={4}
as={ResizeTextarea}
maxRows={10}
{...props}
/>
);
}
);

AutoResizeTextarea.displayName = "AutoResizeTextarea";

interface SandboxAreaProps {
planId: string | number;
}

type StoredNotes = {
[planId: string]: string;
};

function getStoredNotes(): StoredNotes {
const storedNotes = localStorage.getItem("notes");
if (!storedNotes) {
return {};
}

return JSON.parse(storedNotes);
}

function setStoredNotes(notes: StoredNotes) {
try {
localStorage.setItem("notes", JSON.stringify(notes));
} catch (e) {
toast.error("Maximum local storage quota exceed. Too many plans.");
}
}

function getPlanNote(planId: string): string {
const notesObject = getStoredNotes();
return notesObject[planId] || "";
}

export const SandboxArea: React.FC<SandboxAreaProps> = ({ planId }) => {
const [notes, setNotes] = useState<string>("");

const handleNewNotes = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (!planId) return;
setNotes(e.target.value);
const notesObj = getStoredNotes();
notesObj[planId] = e.target.value;
setStoredNotes(notesObj);
};

useEffect(() => {
if (!planId) return;
setNotes(getPlanNote(String(planId)));
}, [planId]);

return (
<Box backgroundColor="white" pt="6" pb="6" px="3">
<VStack align="left" px="4">
<Flex mb="3">
<Image src="/sandbox_logo.svg" alt="sandbox logo" mr="2" />
<Text color="primary.blue.dark.main" fontSize="sm" fontWeight="bold">
Sandbox Area
</Text>
</Flex>
<Text color="primary.blue.dark.main" fontSize="sm" fontWeight="bold">
Notes
</Text>
<AutoResizeTextarea
placeholder="notes here!"
resize="vertical"
height="initial"
value={notes}
onChange={handleNewNotes}
/>
</VStack>
</Box>
);
};
Loading

0 comments on commit 672224a

Please sign in to comment.