"use client"; import { useCallback, useEffect, useState } from "react"; import { useTranslations } from "next-intl"; import { X, Calendar, Trash2, Plus } from "lucide-react"; import { Button } from "@/shared/components/ui/button"; import { FocusTrap } from "@/shared/components/a11y/focus-trap"; import { toast } from "sonner"; import { getLessonPlanSchedulesAction, createLessonPlanScheduleAction, deleteLessonPlanScheduleAction, } from "../actions-schedules"; import type { LessonPlanScheduleRecord } from "../data-access-schedules"; interface ScheduleOption { id: string; name: string; } interface Props { planId: string; classes: ScheduleOption[]; onClose: () => void; onScheduled?: () => void; } /** * V5-7:课时绑定对话框 * * 功能: * 1. 列出课案已绑定的课时 * 2. 添加新课时绑定(选班级 + 日期 + 节次 + 时长) * 3. 删除课时绑定 * * 不通过 service 注入,直接调用 actions(因为是新增功能,且仅教师使用)。 */ export function ScheduleDialog({ planId, classes, onClose, onScheduled }: Props) { const t = useTranslations("lessonPreparation"); const [schedules, setSchedules] = useState([]); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); // 表单状态 const [classId, setClassId] = useState(""); const [scheduledDate, setScheduledDate] = useState(""); const [period, setPeriod] = useState(1); const [durationMin, setDurationMin] = useState(40); const [error, setError] = useState(null); const loadSchedules = useCallback(async () => { setLoading(true); try { const res = await getLessonPlanSchedulesAction(planId); if (res.success && res.data) { setSchedules(res.data.items); } } catch (e) { console.error("[ScheduleDialog] load failed", e); toast.error(t("error.getOne")); } finally { setLoading(false); } }, [planId, t]); useEffect(() => { void loadSchedules(); }, [loadSchedules]); useEffect(() => { function handleEsc(e: KeyboardEvent) { if (e.key === "Escape") onClose(); } document.addEventListener("keydown", handleEsc); return () => document.removeEventListener("keydown", handleEsc); }, [onClose]); async function handleAdd() { if (!classId) { setError(t("schedule.selectClass")); return; } if (!scheduledDate) { setError(t("schedule.selectDate")); return; } setSubmitting(true); setError(null); try { const res = await createLessonPlanScheduleAction({ planId, classId, scheduledDate, period, durationMin, }); if (res.success) { toast.success(t("schedule.addSuccess")); void loadSchedules(); onScheduled?.(); } else { setError(res.message ?? t("error.save")); } } catch (e) { console.error("[ScheduleDialog] add failed", e); setError(t("error.save")); } finally { setSubmitting(false); } } async function handleDelete(id: string) { try { const res = await deleteLessonPlanScheduleAction(id); if (res.success) { toast.success(t("schedule.deleteSuccess")); void loadSchedules(); onScheduled?.(); } else { toast.error(res.message ?? t("error.save")); } } catch (e) { console.error("[ScheduleDialog] delete failed", e); toast.error(t("error.save")); } } return (
{/* arbitrary-value: dialog fixed width */}

{/* 已绑定课时列表 */}
{loading ? (

{t("version.loading")}

) : schedules.length === 0 ? (

{t("schedule.empty")}

) : (
    {schedules.map((s) => (
  • {s.className} {s.scheduledDate} · {t("schedule.period", { n: s.period })} · {t("schedule.duration", { n: s.durationMin })}
  • ))}
)}
{/* 添加新绑定 */}
setScheduledDate(e.target.value)} className="w-full border rounded px-2 py-1 mt-0.5 text-sm" />
setDurationMin(Number(e.target.value))} className="w-full border rounded px-2 py-1 mt-0.5 text-sm" />
{error &&

{error}

}
); }