diff --git a/backend/degree/views.py b/backend/degree/views.py index c61e3784f..e0223c289 100644 --- a/backend/degree/views.py +++ b/backend/degree/views.py @@ -160,7 +160,7 @@ def create(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) except Http404: return super().create(request, *args, **kwargs) - + @api_view(["GET"]) def courses_for_rule(request, rule_id: int): """ diff --git a/frontend/degree-plan/components/FourYearPlan/CourseInPlan.tsx b/frontend/degree-plan/components/FourYearPlan/CourseInPlan.tsx index 5bb15ee96..73025af8c 100644 --- a/frontend/degree-plan/components/FourYearPlan/CourseInPlan.tsx +++ b/frontend/degree-plan/components/FourYearPlan/CourseInPlan.tsx @@ -1,11 +1,11 @@ import { useDrag } from "react-dnd"; import { ItemTypes } from "../dnd/constants"; -import { Course, DnDCourse } from "@/types"; +import { Course, DnDCourse, Fulfillment } from "@/types"; import 'react-loading-skeleton/dist/skeleton.css' import CourseComponent from "../Course/Course"; interface CoursePlannedProps { - course: DnDCourse; + course: Fulfillment; removeCourse: (course: Course["id"]) => void; semester: Course["semester"]; isDisabled: boolean; diff --git a/frontend/degree-plan/components/FourYearPlan/PlanPanel.tsx b/frontend/degree-plan/components/FourYearPlan/PlanPanel.tsx index 4ccb230fa..2cec8663f 100644 --- a/frontend/degree-plan/components/FourYearPlan/PlanPanel.tsx +++ b/frontend/degree-plan/components/FourYearPlan/PlanPanel.tsx @@ -73,13 +73,7 @@ const PlanPanel = ({ setModalObject(item) }, create: () => { - /** When a semester is created, - * if there is no localStorage.getItem('PDP-start-grad-years'), - * the onboarding page will pop up which then sets PDP-start-grad-years - * in localStorage */ - if (typeof window !== "undefined" && !!localStorage.getItem('PDP-start-grad-years')) - setModalKey("plan-create") - else setShowOnboardingModal(true); + setShowOnboardingModal(true); } }} isLoading={isLoading} diff --git a/frontend/degree-plan/components/Requirements/CourseInReq.tsx b/frontend/degree-plan/components/Requirements/CourseInReq.tsx index 46fd87434..dc01f03b9 100644 --- a/frontend/degree-plan/components/Requirements/CourseInReq.tsx +++ b/frontend/degree-plan/components/Requirements/CourseInReq.tsx @@ -7,7 +7,8 @@ import { ReviewPanelTrigger } from "../Infobox/ReviewPanel"; import { Draggable } from "../common/DnD"; import CourseComponent, { PlannedCourseContainer } from "../Course/Course"; import { CourseXButton } from "../Course/Course"; -import { useSWRCrud } from "@/hooks/swrcrud"; +import { deleteFetcher, useSWRCrud } from "@/hooks/swrcrud"; +import { mutate } from "swr"; interface CourseInReqProps { course: DnDCourse; @@ -22,14 +23,22 @@ interface CourseInReqProps { const CourseInReq = (props : CourseInReqProps) => { const { course, activeDegreePlanId, rule_id } = props; - const { remove } = useSWRCrud( + const { remove: removeFulfillment, createOrUpdate: updateFulfillment } = useSWRCrud( `/api/degree/degreeplans/${activeDegreePlanId}/fulfillments`, { idKey: "full_code", - createDefaultOptimisticData: { semester: null, rules: [] } + // createDefaultOptimisticData: { semester: null, rules: [] } }); const { createOrUpdate } = useSWRCrud(`/api/degree/docked`, { idKey: 'full_code' }); - const handleRemoveCourse = (full_code: string) => { - remove(full_code); + + const handleRemoveCourse = async (full_code: string) => { + const updated_rules = course.rules?.filter(rule => rule != rule_id); + /** If the current rule about to be removed is the only rule + * the course satisfied, then we delete the fulfillment */ + if (updated_rules && updated_rules.length == 0) { + removeFulfillment(full_code); + } else { + updateFulfillment({rules: updated_rules}, full_code); + } createOrUpdate({"full_code": full_code}, full_code); } diff --git a/frontend/degree-plan/components/Requirements/QObject.tsx b/frontend/degree-plan/components/Requirements/QObject.tsx index 083f8cfd0..046834bd6 100644 --- a/frontend/degree-plan/components/Requirements/QObject.tsx +++ b/frontend/degree-plan/components/Requirements/QObject.tsx @@ -275,7 +275,7 @@ const QObject = ({ q, fulfillments, rule, satisfied, activeDegreePlanId }: QObje // we've already used this course, so delete it if (isChosen) fulfillmentsMap.delete(course.full_code); // return
what
- return ; + return ; }); const displayCoursesWithoutSemesters = courses.map(course => { assert(typeof course.semester === "undefined") @@ -284,7 +284,7 @@ const QObject = ({ q, fulfillments, rule, satisfied, activeDegreePlanId }: QObje // we've already used this course, so delete it if (isChosen) fulfillmentsMap.delete(course.full_code); - return ; + return ; }); // transformations applied to parse tree should guarantee that searchConditions is a singleton @@ -304,8 +304,8 @@ const QObject = ({ q, fulfillments, rule, satisfied, activeDegreePlanId }: QObje case "SEARCH": return ; case "COURSE": - const isChosen = fulfillments.find(fulfillment => fulfillment.full_code == q.full_code && (!q.semester || q.semester === fulfillment.semester)) - return ; + const [fulfillment] = fulfillments.filter(fulfillment => fulfillment.full_code == q.full_code && (!q.semester || q.semester === fulfillment.semester)) + return ; // onClick={() => { console.log("fired"); createOrUpdate({ rules: [rule.id] }, q.full_code)}} } } diff --git a/frontend/degree-plan/components/Requirements/Rule.tsx b/frontend/degree-plan/components/Requirements/Rule.tsx index 20a6f760c..c630d771a 100644 --- a/frontend/degree-plan/components/Requirements/Rule.tsx +++ b/frontend/degree-plan/components/Requirements/Rule.tsx @@ -146,8 +146,7 @@ const RuleComponent = (ruleTree : RuleTree) => { const [{ isOver, canDrop }, drop] = useDrop({ accept: [ItemTypes.COURSE_IN_PLAN, ItemTypes.COURSE_IN_DOCK], drop: (course: DnDCourse) => { - createOrUpdate({ rules: [rule.id] }, course.full_code) - + createOrUpdate({ rules: course.rules !== undefined ? [...course.rules, rule.id] : [rule.id] }, course.full_code); }, // TODO: this doesn't handle fulfillments that already have a rule canDrop: () => { return !satisfied && !!rule.q }, collect: monitor => ({ diff --git a/frontend/degree-plan/pages/OnboardingPage.tsx b/frontend/degree-plan/pages/OnboardingPage.tsx index 5dde68308..a1142e275 100644 --- a/frontend/degree-plan/pages/OnboardingPage.tsx +++ b/frontend/degree-plan/pages/OnboardingPage.tsx @@ -272,17 +272,7 @@ const OnboardingPage = ({ `/api/degree/degreeplans/${res.id}/degrees`, { degree_ids: majors.map((m) => m.value.id) } ); // add degree - // mutate(`api/degree/degreeplans/${res.id}`, updated, { populateCache: true, revalidate: false }) // use updated degree plan returned - // mutate(key => key && key.startsWith(`/api/degree/degreeplans/${res.id}/fulfillments`)) // refetch the fulfillments setActiveDegreeplan(res); - localStorage.setItem( - "PDP-start-grad-years", - JSON.stringify({ - startingYear: startingYear?.value, - graduationYear: graduationYear?.value, - }) - ); - // TODO: update the backend on user's start/grad years setShowOnboardingModal(false); } }); diff --git a/frontend/degree-plan/types.ts b/frontend/degree-plan/types.ts index b650497c8..13e96b2c4 100644 --- a/frontend/degree-plan/types.ts +++ b/frontend/degree-plan/types.ts @@ -97,7 +97,7 @@ export interface Course { // The interface we use with React DND export interface DnDCourse { full_code: string; - rule_id?: number; + rules?: number[]; } export interface Fulfillment extends DBObject, DnDCourse {