From de2756d9c64610185bf57a76fa1408698bc997fa Mon Sep 17 00:00:00 2001 From: Saisri24 Date: Thu, 23 Apr 2026 17:44:34 -0400 Subject: [PATCH 1/4] feat: fixing duplicate schedule creation and manage in-flight state for schedule operations --- frontend/src/pages/ScheduleList.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/ScheduleList.tsx b/frontend/src/pages/ScheduleList.tsx index 5cc7d41..78be38c 100644 --- a/frontend/src/pages/ScheduleList.tsx +++ b/frontend/src/pages/ScheduleList.tsx @@ -282,6 +282,8 @@ function StepIndicator({ current }: { current: Step }) { function CreateScheduleModal({ onClose, onCreated }: { onClose: () => void; onCreated: (s: ScheduleResponse) => void }) { const [step, setStep] = useState('upload'); const [createdSchedule, setCreatedSchedule] = useState(null); + const createInFlightRef = useRef(false); + const generateInFlightRef = useRef(false); /* ---------- step 1: csv uploads ---------- */ const [uploadResults, setUploadResults] = useState>({ @@ -348,12 +350,14 @@ function CreateScheduleModal({ onClose, onCreated }: { onClose: () => void; onCr } async function handleCreate() { + if (createInFlightRef.current) return; if (!name.trim() || semesterId === '' || campusId === '') { setFormError('All fields are required.'); return; } setFormError(null); setCreating(true); + createInFlightRef.current = true; try { const api = getAutomatedCourseSchedulerAPI(); const created = await api.createScheduleSchedulesPost({ @@ -371,13 +375,16 @@ function CreateScheduleModal({ onClose, onCreated }: { onClose: () => void; onCr setFormError(typeof detail === 'string' ? detail : 'Failed to create schedule.'); } finally { setCreating(false); + createInFlightRef.current = false; } } async function handleGenerate() { if (!createdSchedule) return; + if (generateInFlightRef.current) return; setGenerateError(null); setGenerating(true); + generateInFlightRef.current = true; try { await axiosInstance({ url: `/schedules/${createdSchedule.schedule_id}/generate`, @@ -393,6 +400,7 @@ function CreateScheduleModal({ onClose, onCreated }: { onClose: () => void; onCr setGenerateError(typeof detail === 'string' ? detail : 'Failed to start schedule generation.'); } finally { setGenerating(false); + generateInFlightRef.current = false; } } @@ -661,7 +669,10 @@ export default function ScheduleList() { }, []); function handleCreated(s: ScheduleResponse) { - setSchedules((prev) => [s, ...prev]); + setSchedules((prev) => { + if (prev.some((p) => p.schedule_id === s.schedule_id)) return prev; + return [s, ...prev]; + }); } async function handleUpdate(scheduleId: number, data: { name?: string; draft?: boolean }) { From 46f9c22bc9200b15b7d126e6331cd5203d4a4823 Mon Sep 17 00:00:00 2001 From: Saisri24 Date: Thu, 23 Apr 2026 17:46:43 -0400 Subject: [PATCH 2/4] fixing soft delete schedules not being created --- backend/app/services/course.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/app/services/course.py b/backend/app/services/course.py index dea7dba..9cbd590 100644 --- a/backend/app/services/course.py +++ b/backend/app/services/course.py @@ -113,7 +113,8 @@ def generate_course_list(db: Session, semester_id: int, campus_id: int) -> list[ if semester is None: raise ValueError(f"Semester {semester_id} not found") - schedules = semester_repo.get_schedules(db, semester, campus_id) + # Only consider active schedules. + schedules = semester_repo.get_schedules(db, semester, campus_id, active_only=True) if len(schedules) > 1: raise ValueError(f"Semester {semester_id} has multiple schedules for campus {campus_id}; expected 1") From cda805657d50dd7c55d31f79f43b525c0d111689 Mon Sep 17 00:00:00 2001 From: Saisri24 Date: Thu, 23 Apr 2026 17:49:26 -0400 Subject: [PATCH 3/4] lint --- backend/app/services/course.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/services/course.py b/backend/app/services/course.py index 9cbd590..4a2131b 100644 --- a/backend/app/services/course.py +++ b/backend/app/services/course.py @@ -113,7 +113,7 @@ def generate_course_list(db: Session, semester_id: int, campus_id: int) -> list[ if semester is None: raise ValueError(f"Semester {semester_id} not found") - # Only consider active schedules. + # Only consider active schedules. schedules = semester_repo.get_schedules(db, semester, campus_id, active_only=True) if len(schedules) > 1: raise ValueError(f"Semester {semester_id} has multiple schedules for campus {campus_id}; expected 1") From 3acf2cfa0f24006c1fe82f50d623c062df857245 Mon Sep 17 00:00:00 2001 From: Benjamin Welsh Date: Thu, 23 Apr 2026 18:35:02 -0400 Subject: [PATCH 4/4] Update course.py --- backend/app/services/course.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app/services/course.py b/backend/app/services/course.py index 4a2131b..eac5777 100644 --- a/backend/app/services/course.py +++ b/backend/app/services/course.py @@ -114,8 +114,8 @@ def generate_course_list(db: Session, semester_id: int, campus_id: int) -> list[ raise ValueError(f"Semester {semester_id} not found") # Only consider active schedules. - schedules = semester_repo.get_schedules(db, semester, campus_id, active_only=True) - if len(schedules) > 1: + schedules = semester_repo.get_schedules(db, semester, campus_id) + if len([s for s in schedules if s.active]) > 1: raise ValueError(f"Semester {semester_id} has multiple schedules for campus {campus_id}; expected 1") courses = schedule_repo.get_courses(schedules[0])