"use client"; import React, { useEffect, useState } from "react"; import { AdminHeader, AlertBanner, Modal, ConfirmDeleteModal, getClientToken } from "../_components/AdminComponents"; import { adminGetSemesters, adminCreateSemester, adminUpdateSemester, adminDeleteSemester, adminCreateCourse, adminUpdateCourse, adminDeleteCourse, AdminSemester, AdminCourse, } from "../../../lib/adminApi"; export default function AdminLecturesPage() { const [semesters, setSemesters] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [successMsg, setSuccessMsg] = useState(null); // Semester Modal State const [isSemModalOpen, setIsSemModalOpen] = useState(false); const [editingSemester, setEditingSemester] = useState(null); const [semForm, setSemForm] = useState({ year: new Date().getFullYear(), term: "Spring" as "Spring" | "Fall", }); // Course Modal State const [isCourseModalOpen, setIsCourseModalOpen] = useState(false); const [targetSemesterId, setTargetSemesterId] = useState(null); const [editingCourse, setEditingCourse] = useState(null); const [courseForm, setCourseForm] = useState({ code: "", name_en: "", name_kr: "", note: "", display_order: 0, }); const [isSubmitting, setIsSubmitting] = useState(false); const [deletingSemId, setDeletingSemId] = useState(null); const [deletingCourseId, setDeletingCourseId] = useState(null); const loadSemesters = async () => { try { setIsLoading(true); setError(null); const data = await adminGetSemesters(); setSemesters(data); } catch (err: any) { setError(err?.message || "Failed to load semesters."); } finally { setIsLoading(false); } }; useEffect(() => { loadSemesters(); }, []); // Semester Actions const handleOpenSemCreate = () => { setEditingSemester(null); setSemForm({ year: new Date().getFullYear(), term: "Spring", }); setIsSemModalOpen(true); }; const handleOpenSemEdit = (s: AdminSemester) => { setEditingSemester(s); setSemForm({ year: s.year, term: s.term, }); setIsSemModalOpen(true); }; const handleSemSubmit = async (e: React.FormEvent) => { e.preventDefault(); const token = getClientToken(); if (!token) return; const payload = { year: Number(semForm.year), term: semForm.term, }; setIsSubmitting(true); setError(null); try { if (editingSemester) { await adminUpdateSemester(token, editingSemester.id, payload); setSuccessMsg(`Semester ${payload.year} ${payload.term} updated.`); } else { await adminCreateSemester(token, payload); setSuccessMsg(`Semester ${payload.year} ${payload.term} created.`); } setIsSemModalOpen(false); await loadSemesters(); } catch (err: any) { setError(err?.message || "Failed to save semester."); } finally { setIsSubmitting(false); } }; const handleConfirmDeleteSem = async () => { if (!deletingSemId) return; const token = getClientToken(); if (!token) return; setIsSubmitting(true); try { await adminDeleteSemester(token, deletingSemId); setSuccessMsg("Semester and all its courses deleted successfully (Cascade)."); setDeletingSemId(null); await loadSemesters(); } catch (err: any) { setError(err?.message || "Failed to delete semester."); } finally { setIsSubmitting(false); } }; // Course Actions const handleOpenCourseCreate = (semesterId: number) => { setTargetSemesterId(semesterId); setEditingCourse(null); const sem = semesters.find((s) => s.id === semesterId); setCourseForm({ code: "", name_en: "", name_kr: "", note: "", display_order: (sem?.courses?.length || 0) + 1, }); setIsCourseModalOpen(true); }; const handleOpenCourseEdit = (c: AdminCourse) => { setTargetSemesterId(c.semester_id); setEditingCourse(c); setCourseForm({ code: c.code, name_en: c.name_en, name_kr: c.name_kr || "", note: c.note || "", display_order: c.display_order, }); setIsCourseModalOpen(true); }; const handleCourseSubmit = async (e: React.FormEvent) => { e.preventDefault(); const token = getClientToken(); if (!token || !targetSemesterId) return; const payload = { code: courseForm.code.trim(), name_en: courseForm.name_en.trim(), name_kr: courseForm.name_kr.trim() || undefined, note: courseForm.note.trim() || undefined, display_order: Number(courseForm.display_order) || 0, }; setIsSubmitting(true); setError(null); try { if (editingCourse) { await adminUpdateCourse(token, editingCourse.id, payload); setSuccessMsg(`Course "${payload.name_en}" updated.`); } else { await adminCreateCourse(token, targetSemesterId, payload); setSuccessMsg(`Course "${payload.name_en}" added.`); } setIsCourseModalOpen(false); await loadSemesters(); } catch (err: any) { setError(err?.message || "Failed to save course."); } finally { setIsSubmitting(false); } }; const handleConfirmDeleteCourse = async () => { if (!deletingCourseId) return; const token = getClientToken(); if (!token) return; setIsSubmitting(true); try { await adminDeleteCourse(token, deletingCourseId); setSuccessMsg("Course deleted successfully."); setDeletingCourseId(null); await loadSemesters(); } catch (err: any) { setError(err?.message || "Failed to delete course."); } finally { setIsSubmitting(false); } }; return (
+ Add New Semester } /> {error && setError(null)} />} {successMsg && setSuccessMsg(null)} />} {isLoading ? (
Loading semesters and courses...
) : semesters.length === 0 ? (
No lecture semesters recorded. Click "+ Add New Semester" to get started.
) : (
{semesters.map((sem) => (
{/* Semester Header Bar */}
{sem.year} {sem.term} Semester {sem.courses?.length || 0} Courses
{/* Courses Table */} {!sem.courses || sem.courses.length === 0 ? ( ) : ( sem.courses.map((c) => ( )) )}
Order Course Code Course Name (En / Kr) Note Actions
No courses added for this semester.
{c.display_order} {c.code} {c.name_en} {c.name_kr && ({c.name_kr})} {c.note || "—"}
))}
)} {/* Semester Modal */} setIsSemModalOpen(false)} >
setSemForm({ ...semForm, year: Number(e.target.value) })} required className="w-full px-3 py-2 text-sm border border-line rounded bg-ivory text-ink focus:outline-none focus:ring-1 focus:ring-cobalt" />
{/* Course Modal */} setIsCourseModalOpen(false)} >
setCourseForm({ ...courseForm, code: e.target.value })} placeholder="e.g. ITNT683" required className="w-full px-3 py-2 text-sm border border-line rounded bg-ivory text-ink focus:outline-none focus:ring-1 focus:ring-cobalt" />
setCourseForm({ ...courseForm, display_order: Number(e.target.value) })} className="w-full px-3 py-2 text-sm border border-line rounded bg-ivory text-ink focus:outline-none focus:ring-1 focus:ring-cobalt" />
setCourseForm({ ...courseForm, name_en: e.target.value })} placeholder="e.g. Digital Signal Processing" required className="w-full px-3 py-2 text-sm border border-line rounded bg-ivory text-ink focus:outline-none focus:ring-1 focus:ring-cobalt" />
setCourseForm({ ...courseForm, name_kr: e.target.value })} placeholder="e.g. 디지털신호처리" className="w-full px-3 py-2 text-sm border border-line rounded bg-ivory text-ink focus:outline-none focus:ring-1 focus:ring-cobalt" />
setCourseForm({ ...courseForm, note: e.target.value })} placeholder="e.g. Graduate" className="w-full px-3 py-2 text-sm border border-line rounded bg-ivory text-ink focus:outline-none focus:ring-1 focus:ring-cobalt" />
setDeletingSemId(null)} onConfirm={handleConfirmDeleteSem} isSubmitting={isSubmitting} title="Delete Semester (Cascade Warning)" description="Are you sure you want to delete this semester? All courses under this semester will also be permanently deleted (Database Cascade)." /> setDeletingCourseId(null)} onConfirm={handleConfirmDeleteCourse} isSubmitting={isSubmitting} title="Delete Course" description="Are you sure you want to delete this course?" />
); }