Skip to content

Commit de519bd

Browse files
committed
Catalog to Planner Drag and Drop
Yeah, it can do it now but it looks a little awkward. Will fix in next commit.
1 parent c7f4476 commit de519bd

5 files changed

Lines changed: 64 additions & 5 deletions

File tree

src/components/course/Course.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function PlannerCourseView({
111111
)}
112112
>
113113
<div className="flex gap-2 items-center">
114-
<button ref={handleRef}>
114+
<button ref={handleRef} type="button" aria-label="Drag to reorder">
115115
<MdDragIndicator size={22} />
116116
</button>
117117
<CourseLabel course={course.data} showCredits />
@@ -122,7 +122,11 @@ function PlannerCourseView({
122122
onOpenChange={setPopoverOpen}
123123
>
124124
<Popover.Trigger asChild>
125-
<button className="outline-none">
125+
<button
126+
type="button"
127+
className="outline-none"
128+
aria-label="Course options"
129+
>
126130
<MdOutlineMoreHoriz className="cursor-pointer text-2xl" />
127131
</button>
128132
</Popover.Trigger>

src/features/catalog/components/CatalogCourse.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState } from "react";
22

3+
import { useDraggable } from "@dnd-kit/react";
34
import { motion } from "framer-motion";
45
import { IoAdd } from "react-icons/io5";
56

@@ -26,6 +27,12 @@ const Course: React.FC<CourseProps> = ({ course }) => {
2627
const { filters } = useCatalog();
2728
const { getCourseCount } = useCourseWorkspace();
2829

30+
const { ref, isDragging } = useDraggable({
31+
id: `catalog-${course.subj_code}-${course.code_num}`,
32+
type: "catalog-course",
33+
data: { type: "catalog-course", course },
34+
});
35+
2936
const attrFilters = findFiltersForCourse(
3037
course.attr_list || [],
3138
filters.attributes,
@@ -43,7 +50,8 @@ const Course: React.FC<CourseProps> = ({ course }) => {
4350

4451
return (
4552
<div
46-
className="relative bg-carpipink hover:cursor-pointer hover:bg-darkblue/10 border-1 border-black rounded-xl w-full p-4"
53+
ref={ref}
54+
className={`relative bg-carpipink hover:cursor-pointer hover:bg-darkblue/10 border-1 border-black rounded-xl w-full p-4 ${isDragging ? "opacity-50" : ""}`}
4755
onClick={toggleOpen}
4856
>
4957
<CourseBadge
@@ -98,6 +106,8 @@ interface AddButtonProps {
98106
const AddButton: React.FC<AddButtonProps> = ({ addCourse }) => {
99107
return (
100108
<button
109+
type="button"
110+
aria-label="Add course to toolbox"
101111
className={`hover:cursor-pointer hover:bg-darkblue hover:text-carpipink border-2 border-darkblue rounded-full p-1 text-4xl`}
102112
onClick={addCourse}
103113
>

src/features/dnd/dnd.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { PointerSensor, KeyboardSensor } from "@dnd-kit/dom";
44
import { DragDropProvider, DragDropEventHandlers } from "@dnd-kit/react";
55
import { isSortable } from "@dnd-kit/react/sortable";
66

7+
import { v4 as uuidv4 } from "uuid";
8+
79
import { useCourseWorkspace } from "@/core/workspace/useCourseWorkspace";
8-
import { UserCourse } from "@/lib/types";
10+
import { APICourse, UserCourse } from "@/lib/types";
911

1012
export default function WorkspaceDndProvider({
1113
children,
@@ -85,6 +87,9 @@ export default function WorkspaceDndProvider({
8587
const targetType = target.data?.type;
8688
const sourceCourse = source.data?.course as UserCourse | undefined;
8789

90+
// Catalog courses are handled in onDragEnd only
91+
if (sourceType === "catalog-course") return;
92+
8893
if (!sourceCourse) return;
8994

9095
let targetIndex;
@@ -175,6 +180,40 @@ export default function WorkspaceDndProvider({
175180
const sourceType = source.data?.type;
176181
const targetType = target.data?.type;
177182

183+
// --- CATALOG TO PLANNER ---
184+
if (sourceType === "catalog-course") {
185+
const apiCourse = source.data?.course as APICourse | undefined;
186+
if (!apiCourse) return;
187+
188+
const targetSemesterId =
189+
targetType === "semester"
190+
? target.data?.semesterId || targetId
191+
: courseLocationMap.get(targetId)?.semesterId;
192+
193+
if (!targetSemesterId) return;
194+
195+
let targetIndex;
196+
if (isSortable(target)) {
197+
targetIndex = target.index;
198+
} else {
199+
const targetSemester =
200+
plannerCourses[
201+
courseLocationMap.get(targetId)?.semesterIndex || -1
202+
];
203+
targetIndex = targetSemester?.courseList.length || 0;
204+
}
205+
206+
const newCourse: UserCourse = {
207+
id: uuidv4(),
208+
name: `${apiCourse.subj_code} ${apiCourse.code_num}`,
209+
count: 1,
210+
data: apiCourse,
211+
};
212+
213+
addCourseToSemester(targetSemesterId, newCourse, targetIndex);
214+
return;
215+
}
216+
178217
// --- UTILITY DROPZONES ---
179218
if (targetId === "garbage") {
180219
const semId = courseLocationMap.get(sourceId)?.semesterId;
@@ -223,6 +262,7 @@ export default function WorkspaceDndProvider({
223262
resetToolbox,
224263
consolidateToolbox,
225264
courseLocationMap,
265+
addCourseToSemester,
226266
removeCourseFromSemester,
227267
removeCourseFromToolbox,
228268
insertCourseIntoToolbox,

src/features/planner/components/semester/SemesterBlock.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export default function SemesterBlock({ semester, index }: SemesterBlockProps) {
4141
// --- DROPPABLE: For receiving Courses inside the Semester ---
4242
const { ref: droppableRef, isDropTarget } = useDroppable({
4343
id: `dropzone-${semester.semesterID}`,
44-
accept: ["planner-course", "toolbox-course"], // Accept courses
44+
accept: ["planner-course", "toolbox-course", "catalog-course"], // Accept courses
4545
data: { type: "semester", semesterId: semester.semesterID },
4646
});
4747

@@ -102,6 +102,8 @@ export default function SemesterBlock({ semester, index }: SemesterBlockProps) {
102102
<div className="flex items-center gap-2 flex-1 min-w-0">
103103
<button
104104
ref={handleRef}
105+
type="button"
106+
aria-label="Drag to reorder semester"
105107
className={cn(
106108
"hover:bg-darkblue/20 py-1.5 px-0.75 rounded-lg shrink-0 ",
107109
isDragging
@@ -136,6 +138,7 @@ export default function SemesterBlock({ semester, index }: SemesterBlockProps) {
136138
<div className="flex items-center gap-1">
137139
<div className="relative group/delete-btn flex flex-col items-center">
138140
<button
141+
type="button"
139142
onClick={() => handleDeleteSemester(semester.semesterID)}
140143
className="text-darkblue/40 hover:text-rosewood transition-colors p-1 hover:bg-rosewood/10 rounded-lg hover:cursor-pointer"
141144
aria-label="Delete Semester"

src/features/toolbox/GarbageBin.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export default function GarbageBin() {
4848
return (
4949
<button
5050
ref={ref}
51+
type="button"
52+
aria-label="Delete course"
5153
className={cn(
5254
"w-fit transition-opacity duration-200",
5355
showBin ? "opacity-100" : "opacity-0 pointer-events-none",

0 commit comments

Comments
 (0)