Skip to content

Commit 492b2fc

Browse files
committed
create courseworkspace "api"
1 parent 8616599 commit 492b2fc

File tree

11 files changed

+455
-363
lines changed

11 files changed

+455
-363
lines changed

src/components/Course/CatalogCourse.tsx

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import React, { useState } from "react";
2-
import { v4 as uuidv4 } from "uuid";
3-
42
import AddButton from "./CatalogCourse/AddButton";
53
import { motion } from "framer-motion";
64
import { APICourse } from "../../types/interfaces/Course.interface";
@@ -13,9 +11,8 @@ import CourseBadge from "./shared/CourseBadge";
1311

1412
const findFiltersForCourse = (
1513
api_list: string[],
16-
filterDataType: FilterData[]
14+
filterDataType: FilterData[],
1715
) => {
18-
console.log("API List:", api_list);
1916
return filterDataType.filter((attr) => api_list.includes(attr.code));
2017
};
2118

@@ -24,7 +21,7 @@ interface CourseProps {
2421
}
2522

2623
const Course: React.FC<CourseProps> = ({ course }) => {
27-
const { toolboxCourses, setToolboxCourses } = useCourseWorkspace();
24+
const { addCourseToToolbox, getCourseCountInToolbox } = useCourseWorkspace();
2825
const { attributes, semesters } = useFilterData();
2926

3027
const attrFilters = findFiltersForCourse(course.attr_list || [], attributes);
@@ -36,41 +33,15 @@ const Course: React.FC<CourseProps> = ({ course }) => {
3633
if (target.id !== "add-button") setIsOpen((open) => !open);
3734
};
3835

39-
const courseDisplay: string = `${course.subj_code}-${course.code_num}`;
40-
const addCourse = (e: React.MouseEvent<HTMLButtonElement>) => {
41-
e.stopPropagation();
42-
43-
setToolboxCourses((prevCourses) => {
44-
const existingIndex = prevCourses.findIndex(
45-
(c) => c.name === courseDisplay
46-
);
47-
48-
if (existingIndex !== -1) {
49-
return prevCourses.map((c, i) =>
50-
i === existingIndex ? { ...c, count: c.count + 1 } : c
51-
);
52-
} else {
53-
return [
54-
...prevCourses,
55-
{
56-
id: uuidv4(),
57-
name: courseDisplay,
58-
count: 1,
59-
data: course,
60-
},
61-
];
62-
}
63-
});
64-
};
65-
const toolboxCourse =
66-
toolboxCourses[toolboxCourses.findIndex((c) => c.name === courseDisplay)];
67-
const courseCount = toolboxCourse ? toolboxCourse.count : undefined;
6836
return (
6937
<div
7038
className="relative bg-carpipink hover:cursor-pointer hover:bg-darkblue/10 border-1 border-black rounded-xl w-full p-4"
7139
onClick={toggleOpen}
7240
>
73-
<CourseBadge count={courseCount} className="absolute -top-2 -right-2" />
41+
<CourseBadge
42+
count={getCourseCountInToolbox(course)}
43+
className="absolute -top-2 -right-2"
44+
/>
7445

7546
<div className={`flex items-center justify-between`}>
7647
<div>
@@ -89,7 +60,7 @@ const Course: React.FC<CourseProps> = ({ course }) => {
8960
</div>
9061
</div>
9162
<div id="add-button" className={``}>
92-
<AddButton addCourse={addCourse} />
63+
<AddButton addCourse={() => addCourseToToolbox(course)} />
9364
</div>
9465
</div>
9566
<div className={`${isOpen ? "" : "hidden"} mt-2`}>

src/components/Course/PlannerCourse.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,13 @@ interface PlannerCourseProps {
1616
const PlannerCourse: React.FC<PlannerCourseProps> = ({
1717
course,
1818
semesterId,
19-
isFirstSemester = false,
20-
isLastSemester = false,
2119
}) => {
2220
const [openPopup, setOpenPopup] = useState<boolean>(false);
2321
const componentRef = useRef<HTMLDivElement>(null);
2422

2523
const menuOptions = usePlannerCourse({
2624
course,
2725
semesterId,
28-
isFirstSemester,
29-
isLastSemester,
3026
});
3127

3228
const togglePopup = (event: React.MouseEvent) => {

src/components/Course/ToolboxCourse.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import { UserCourse } from "../../types/interfaces/Course.interface";
3-
import CourseLabel from "./CourseLabel";
4-
import CourseBadge from "./CourseBadge";
3+
import CourseLabel from "./shared/CourseLabel";
4+
import CourseBadge from "./shared/CourseBadge";
55

66
interface ToolboxCourseProps {
77
course: UserCourse;

src/components/PlannerComponents/AddSemester.tsx

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,12 @@
1-
import React, { useRef } from "react";
2-
import { v4 as uuidv4 } from "uuid";
3-
4-
import { SemesterType } from "../../types/interfaces/Semester.interface";
1+
import React from "react";
52
import { useCourseWorkspace } from "../../hooks/useCourseWorkspace";
63

74
const AddSemester: React.FC = () => {
8-
const { setPlannerCourses } = useCourseWorkspace();
9-
10-
const nextId = useRef(1); // keeps a persistent counter across re-renders
11-
12-
const handleAddCourse = () => {
13-
setPlannerCourses((prevCourses) => {
14-
const nextNum = prevCourses.length + 1;
15-
const newSemester: SemesterType = {
16-
semesterTitle: `Semester ${nextNum}`,
17-
semesterID: uuidv4(), // e.g. sem-1, sem-2, sem-3...
18-
semesterNumber: prevCourses.length + 1,
19-
season: "Fall",
20-
creditsTotal: 0,
21-
courseList: [],
22-
};
23-
24-
nextId.current += 1;
25-
return [...prevCourses, newSemester];
26-
});
27-
};
5+
const { addSemester } = useCourseWorkspace();
286

297
return (
308
<button
31-
onClick={handleAddCourse}
9+
onClick={addSemester}
3210
className="border-1 border-black rounded-full px-4 py-2 w-fit text-xs font-medium hover:bg-darkblue/40 transition-colors"
3311
>
3412
Add Semester Block

src/components/PlannerComponents/DeleteSemester.tsx

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/components/PlannerComponents/SemesterBlock.tsx

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414

1515
import PlannerCourseHolder from "./PlannerCourseHolder";
1616
import { useCourseWorkspace } from "../../hooks/useCourseWorkspace";
17-
import DeleteSemester from "../PlannerComponents/DeleteSemester";
1817
import { MdEdit } from "react-icons/md";
1918
import PlannerCourse from "../Course/PlannerCourse";
2019

@@ -30,7 +29,8 @@ const SemesterBlock: React.FC<SemesterBlockProps> = ({ semester }) => {
3029
id: semester.semesterID,
3130
});
3231

33-
const { plannerCourses, setPlannerCourses } = useCourseWorkspace();
32+
const { updateSemesterName, updateSemesterSeason, deleteSemester } =
33+
useCourseWorkspace();
3434
const [isEditing, setIsEditing] = useState(false);
3535
const inputRef = useRef<HTMLInputElement>(null);
3636

@@ -57,33 +57,17 @@ const SemesterBlock: React.FC<SemesterBlockProps> = ({ semester }) => {
5757

5858
// customizable semester title
5959
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
60-
setPlannerCourses((prev) =>
61-
prev.map((sem) =>
62-
sem.semesterID === semester.semesterID
63-
? { ...sem, semesterTitle: e.target.value }
64-
: sem,
65-
),
66-
);
60+
updateSemesterName(semester.semesterID, e.target.value);
6761
};
6862

6963
// handle season dropdown
7064
const seasonDropdown = (e: React.ChangeEvent<HTMLSelectElement>) => {
7165
const newSeason = e.target.value as SemesterSeason;
72-
setPlannerCourses((prevCourses) =>
73-
prevCourses.map((sem) =>
74-
sem.semesterID === semester.semesterID
75-
? { ...sem, season: newSeason }
76-
: sem,
77-
),
78-
);
66+
updateSemesterSeason(semester.semesterID, newSeason);
7967
};
8068

81-
const handleDeleteSemester = (semesterNumber: number) => {
82-
setPlannerCourses((prev) =>
83-
prev
84-
.filter((s) => s.semesterNumber !== semesterNumber)
85-
.map((s, idx) => ({ ...s, semesterNumber: idx + 1 })),
86-
);
69+
const handleDeleteSemester = (semesterId: string) => {
70+
deleteSemester(semesterId);
8771
};
8872

8973
const finishEditing = () => setIsEditing(false);
@@ -172,21 +156,21 @@ const SemesterBlock: React.FC<SemesterBlockProps> = ({ semester }) => {
172156
<PlannerCourse
173157
course={course}
174158
semesterId={semester.semesterID}
175-
isFirstSemester={semester.semesterNumber === 1}
176-
isLastSemester={
177-
semester.semesterNumber === plannerCourses.length
178-
}
179159
/>
180160
</SortableItem>
181161
))
182162
)}
183163
</SortableContext>
184164
</div>
185165

186-
<DeleteSemester
187-
semesterNumber={semester.semesterNumber}
188-
onDelete={handleDeleteSemester}
189-
/>
166+
<div className="flex justify-end">
167+
<button
168+
onClick={() => handleDeleteSemester(semester.semesterID)}
169+
className="border border-black rounded-full px-3 py-0 h-fit font-medium text-sm hover:cursor-pointer hover:bg-darkblue hover:text-carpipink transition-colors duration-200"
170+
>
171+
Delete
172+
</button>
173+
</div>
190174
</div>
191175
);
192176
};

src/context/CourseWorkspaceContext.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,41 @@
1-
import React, { createContext } from "react";
2-
import { UserCourse } from "../types/interfaces/Course.interface.ts";
1+
import { createContext } from "react";
2+
import { APICourse, UserCourse } from "../types/interfaces/Course.interface.ts";
33
import { SemesterType } from "../types/interfaces/Semester.interface.ts";
44

55
export interface CourseWorkspaceContextType {
6+
// data
67
plannerCourses: SemesterType[];
7-
setPlannerCourses: React.Dispatch<React.SetStateAction<SemesterType[]>>;
88
toolboxCourses: UserCourse[];
9-
setToolboxCourses: React.Dispatch<React.SetStateAction<UserCourse[]>>;
9+
10+
// planner actions
11+
addCourseToSemester: (
12+
semesterId: string,
13+
course: UserCourse,
14+
index?: number,
15+
) => void;
16+
removeCourseFromSemester: (semesterId: string, courseId: string) => void;
17+
moveCourseInSemester: (
18+
semesterId: string,
19+
fromIndex: number,
20+
toIndex: number,
21+
) => void;
22+
updateSemesterName: (semesterId: string, newName: string) => void;
23+
updateSemesterSeason: (
24+
semesterId: string,
25+
newSeason: SemesterType["season"],
26+
) => void;
27+
deleteSemester: (semesterId: string) => void;
28+
addSemester: () => void;
29+
30+
// toolbox actions
31+
addCourseToToolbox: (courseData: APICourse) => void; // Smart add (increments count)
32+
insertCourseIntoToolbox: (course: UserCourse, index: number) => void; // Raw insert (for DnD)
33+
removeCourseFromToolbox: (courseId: string) => void;
34+
updateToolboxCourse: (course: UserCourse) => void; // Updates a specific item (e.g. changing count)
35+
moveCourseInToolbox: (fromIndex: number, toIndex: number) => void;
36+
consolidateToolboxCourses: () => void; // Merges duplicates (for Drag End)
37+
resetToolbox: (courses: UserCourse[]) => void; // For cancelling drags
38+
getCourseCountInToolbox: (courseData: APICourse) => number;
1039
}
1140

1241
const CourseWorkspaceContext = createContext<CourseWorkspaceContextType | null>(

0 commit comments

Comments
 (0)