Skip to content

Commit

Permalink
Merge pull request #799 from sandboxnu/lc/791
Browse files Browse the repository at this point in the history
Modal Error Component + Co-op in first/last semester bug
  • Loading branch information
liamcarvajal authored Feb 9, 2025
2 parents 740892d + 4eae90f commit 365cee7
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 39 deletions.
70 changes: 70 additions & 0 deletions packages/frontend/components/Plan/ErrorModalError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { ChevronDownIcon, ChevronUpIcon, WarningIcon } from "@chakra-ui/icons";
import { Box, Flex, Text, Collapse } from "@chakra-ui/react";
import { useState } from "react";

interface ErrorModalErrorProps {
title: string;
message: string;
}

export const ErrorModalError: React.FC<ErrorModalErrorProps> = ({
title,
message,
}) => {
const [opened, setOpened] = useState(false);

return (
<Box
border="1px solid"
borderColor="primary.red.main"
borderRadius="lg"
backgroundColor="#FAE8E7"
transition="background-color 0.25s ease"
_hover={{ backgroundColor: "#F9DAD8" }}
>
<Flex
onClick={() => setOpened(!opened)}
direction="row"
justifyContent="space-between"
alignItems="flex-start"
height="45px"
color="black"
fontWeight="bold"
p="sm"
position="sticky"
top="0px"
zIndex={1}
>
<Flex direction="row" height="100%" columnGap="sm">
<Flex direction="row" height="100%" alignItems="center" gap="1">
<WarningIcon
color="primary.red.main"
width="2rem"
alignSelf="center"
alignItems="center"
justifySelf="center"
transition="background 0.15s ease"
/>
<Text color="black" mt="0" fontSize="sm">
{title}
</Text>
</Flex>
</Flex>

<Flex ml="xs" alignItems="center" justifyItems="center">
{opened ? (
<ChevronUpIcon boxSize="25px" color="primary.red.main" />
) : (
<ChevronDownIcon boxSize="25px" color="primary.red.main" />
)}
</Flex>
</Flex>

<Collapse in={opened} animateOpacity>
<Box px="sm" py="xs" borderRadius="lg" backgroundColor="transparent">
<Text fontSize="sm">{message}</Text>
</Box>
</Collapse>
</Box>
);
};
76 changes: 41 additions & 35 deletions packages/frontend/components/Plan/Plan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
ScheduleYear2,
SeasonEnum,
} from "@graduate/common";
import { useState } from "react";
import { useState, createContext } from "react";
import { addClassesToTerm, removeYearFromPlan } from "../../utils";
import { removeCourseFromTerm } from "../../utils";
import { ScheduleYear } from "./ScheduleYear";
Expand All @@ -27,6 +27,7 @@ interface PlanProps {
mutateStudentWithUpdatedPlan: (updatedPlan: PlanModel<string>) => void;
}

export const TotalYearsContext = createContext<number | null>(null);
export const Plan: React.FC<PlanProps> = ({
plan,
mutateStudentWithUpdatedPlan,
Expand All @@ -35,6 +36,7 @@ export const Plan: React.FC<PlanProps> = ({
setIsRemove,
}) => {
const [expandedYears, setExpandedYears] = useState<Set<number>>(new Set());
const totalYears = plan.schedule.years.length;

const toggleExpanded = (year: ScheduleYear2<unknown>) => {
if (expandedYears.has(year.year)) {
Expand Down Expand Up @@ -109,41 +111,45 @@ export const Plan: React.FC<PlanProps> = ({
};

return (
<Flex direction="column" rowGap="sm">
<Flex flexDirection="column" rowGap="4xs" ref={setNodeRef}>
{plan.schedule.years.map((scheduleYear) => {
const isExpanded = expandedYears.has(scheduleYear.year);
<TotalYearsContext.Provider value={totalYears}>
<Flex direction="column" rowGap="sm">
<Flex flexDirection="column" rowGap="4xs" ref={setNodeRef}>
{plan.schedule.years.map((scheduleYear) => {
const isExpanded = expandedYears.has(scheduleYear.year);

return (
<Flex key={scheduleYear.year} flexDirection="column">
<ScheduleYear
catalogYear={plan.catalogYear}
yearCoReqError={coReqErr?.years.find(
(year) => year.year == scheduleYear.year
)}
yearPreReqError={preReqErr?.years.find(
(year) => year.year == scheduleYear.year
)}
scheduleYear={scheduleYear}
isExpanded={isExpanded}
toggleExpanded={() => toggleExpanded(scheduleYear)}
addClassesToTermInCurrPlan={addClassesToTermInCurrPlan}
removeCourseFromTermInCurrPlan={removeCourseFromTermInCurrPlan}
removeYearFromCurrPlan={() =>
removeYearFromCurrPlan(scheduleYear.year)
}
setIsRemove={setIsRemove}
/>
</Flex>
);
})}
return (
<Flex key={scheduleYear.year} flexDirection="column">
<ScheduleYear
catalogYear={plan.catalogYear}
yearCoReqError={coReqErr?.years.find(
(year) => year.year == scheduleYear.year
)}
yearPreReqError={preReqErr?.years.find(
(year) => year.year == scheduleYear.year
)}
scheduleYear={scheduleYear}
isExpanded={isExpanded}
toggleExpanded={() => toggleExpanded(scheduleYear)}
addClassesToTermInCurrPlan={addClassesToTermInCurrPlan}
removeCourseFromTermInCurrPlan={
removeCourseFromTermInCurrPlan
}
removeYearFromCurrPlan={() =>
removeYearFromCurrPlan(scheduleYear.year)
}
setIsRemove={setIsRemove}
/>
</Flex>
);
})}
</Flex>
<Flex>
<AddYearButton
plan={plan}
mutateStudentWithUpdatedPlan={mutateStudentWithUpdatedPlan}
/>
</Flex>
</Flex>
<Flex>
<AddYearButton
plan={plan}
mutateStudentWithUpdatedPlan={mutateStudentWithUpdatedPlan}
/>
</Flex>
</Flex>
</TotalYearsContext.Provider>
);
};
32 changes: 30 additions & 2 deletions packages/frontend/components/Plan/ReqErrorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,53 @@ import {
INEUReqCourseError,
INEUReqError,
ScheduleCourse2,
ScheduleTerm2,
SeasonEnum,
} from "@graduate/common";
import { HelperToolTip } from "../Help";
import {
COOP_TITLE,
FALL_1,
FALL_1_COOP_ERROR_MSG,
GENERIC_ERROR_MSG,
SEARCH_NEU_FETCH_COURSE_ERROR_MSG,
SPRING_4_COOP_ERROR_MSG,
getCourseDisplayString,
} from "../../utils";
import { useFetchCourse } from "../../hooks";
import { GraduateToolTip } from "../GraduateTooltip";
import { SetStateAction } from "react";
import { SetStateAction, useContext } from "react";
import { ErrorModalError, TotalYearsContext } from "./";

interface ReqErrorModalProps {
setHovered: (isHovered: SetStateAction<boolean>) => void;
course: ScheduleCourse2<unknown>;
term?: ScheduleTerm2<string>;
preReqErr?: INEUReqError;
coReqErr?: INEUReqError;
}

export const ReqErrorModal: React.FC<ReqErrorModalProps> = ({
course,
term,
setHovered,
coReqErr = undefined,
preReqErr = undefined,
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();

const totalYears = useContext(TotalYearsContext);
const isFinalYear = term && parseInt(term.id[0]) === totalYears;
const coopErr =
course.name === COOP_TITLE &&
term !== undefined &&
(term.id === FALL_1 || (isFinalYear && term.season == SeasonEnum.SP));
let msg = GENERIC_ERROR_MSG;
if (coopErr && term.id === FALL_1) {
msg = FALL_1_COOP_ERROR_MSG;
} else if (coopErr) {
msg = SPRING_4_COOP_ERROR_MSG;
}
return (
<Flex
justifySelf="stretch"
Expand Down Expand Up @@ -107,7 +129,7 @@ export const ReqErrorModal: React.FC<ReqErrorModalProps> = ({
<ParseCourse course={coReqErr} parent={true} />
</Flex>
)}
{preReqErr && (
{(preReqErr || coopErr) && (
<Flex direction="column">
<Flex
alignItems="center"
Expand All @@ -127,6 +149,12 @@ export const ReqErrorModal: React.FC<ReqErrorModalProps> = ({
<ParseCourse course={preReqErr} parent={true} />
</Flex>
)}
{coopErr && (
<ErrorModalError
title="Cannot add co-op!"
message={msg}
></ErrorModalError>
)}
</ModalBody>
</ModalContent>
</Modal>
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 @@ -78,6 +78,7 @@ export const ScheduleTerm: React.FC<ScheduleTermProps> = ({
coReqErr={termCoReqErr?.[courseToString(scheduleCourse)]}
preReqErr={termPreReqErr?.[courseToString(scheduleCourse)]}
scheduleCourse={scheduleCourse}
scheduleTerm={scheduleTerm}
removeCourse={(course: ScheduleCourse2<unknown>) =>
removeCourseFromTermInCurrPlan(
course,
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/components/Plan/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from "./EditPlanModal";
export * from "./DeletePlanModal";
export * from "./AddYearButton";
export * from "./TransferCourses";
export * from "./ErrorModalError";
29 changes: 27 additions & 2 deletions packages/frontend/components/ScheduleCourse/ScheduleCourse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ import {
INEUReqError,
IRequiredCourse,
ScheduleCourse2,
ScheduleTerm2,
SeasonEnum,
} from "@graduate/common";
import { forwardRef, PropsWithChildren, useEffect, useState } from "react";
import {
forwardRef,
PropsWithChildren,
useEffect,
useState,
useContext,
} from "react";
import {
COOP_TITLE,
DELETE_COURSE_AREA_DND_ID,
FALL_1,
SEARCH_NEU_FETCH_COURSE_ERROR_MSG,
isCourseFromSidebar,
} from "../../utils";
Expand All @@ -20,9 +29,11 @@ import { COOP_BLOCK } from "../Sidebar";
import { CourseDragIcon } from "./CourseDragIcon";
import { CourseTrashButton } from "./CourseTrashButton";
import { GraduateToolTip } from "../GraduateTooltip";
import { TotalYearsContext } from "../Plan/Plan";

interface DraggableScheduleCourseProps {
scheduleCourse: ScheduleCourse2<string>;
scheduleTerm?: ScheduleTerm2<string>;
coReqErr?: INEUReqError;
preReqErr?: INEUReqError;
isInSidebar?: boolean;
Expand All @@ -40,6 +51,7 @@ export const DraggableScheduleCourse: React.FC<
DraggableScheduleCourseProps
> = ({
scheduleCourse,
scheduleTerm,
removeCourse,
preReqErr = undefined,
coReqErr = undefined,
Expand Down Expand Up @@ -68,6 +80,7 @@ export const DraggableScheduleCourse: React.FC<
preReqErr={preReqErr}
ref={setNodeRef}
scheduleCourse={scheduleCourse}
scheduleTerm={scheduleTerm}
removeCourse={removeCourse}
isInSidebar={isInSidebar}
isChecked={isChecked}
Expand Down Expand Up @@ -186,6 +199,7 @@ const ScheduleCourse = forwardRef<HTMLElement | null, ScheduleCourseProps>(
coReqErr = undefined,
preReqErr = undefined,
scheduleCourse,
scheduleTerm,
removeCourse,
isInSidebar = false,
isChecked = false,
Expand All @@ -202,7 +216,17 @@ const ScheduleCourse = forwardRef<HTMLElement | null, ScheduleCourseProps>(
) => {
const [hovered, setHovered] = useState(false);
const isValidRemove = isRemove && !isFromSidebar;
const isCourseError = coReqErr !== undefined || preReqErr !== undefined;
const totalYears = useContext(TotalYearsContext);
const isFinalYear =
scheduleTerm && parseInt(scheduleTerm.id[0]) === totalYears;

const isCourseError =
coReqErr !== undefined ||
preReqErr !== undefined ||
(scheduleCourse.name === COOP_TITLE &&
scheduleTerm !== undefined &&
(scheduleTerm.id === FALL_1 ||
(isFinalYear && scheduleTerm.season === SeasonEnum.SP)));

/*
This component uses some plain HTML elements instead of Chakra
Expand Down Expand Up @@ -253,6 +277,7 @@ const ScheduleCourse = forwardRef<HTMLElement | null, ScheduleCourseProps>(
<ReqErrorModal
setHovered={setHovered}
course={scheduleCourse}
term={scheduleTerm}
coReqErr={coReqErr}
preReqErr={preReqErr}
/>
Expand Down
7 changes: 7 additions & 0 deletions packages/frontend/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export const BETA_MAJOR_TOOLTIP_MSG =
"Requirements for beta majors have not been validated and therefore may be inconsistent with your degree audit.";
export const GEN_PLACEHOLDER_MSG =
"General Placeholderse are generic courses that you can place in your plan if you do not know yet what to take but want the requirements to be fulfilled";
export const FALL_1 = "1-FL";
export const COOP_TITLE = "Co-op Education";
export const FALL_1_COOP_ERROR_MSG =
"You may only register a co-op in your second year and beyond.";
export const SPRING_4_COOP_ERROR_MSG =
"You cannot register a co-op in your last semester.";
export const GENERIC_ERROR_MSG = "This is an error.";

export const defaultGuestStudent: GetStudentResponse = {
uuid: undefined,
Expand Down

0 comments on commit 365cee7

Please sign in to comment.