diff --git a/backend/app/services/course.py b/backend/app/services/course.py index dea7dba..eac5777 100644 --- a/backend/app/services/course.py +++ b/backend/app/services/course.py @@ -113,8 +113,9 @@ 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. schedules = semester_repo.get_schedules(db, semester, campus_id) - if len(schedules) > 1: + 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]) 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 }) {