"use client"; import { useCallback, useEffect, useState } from "react"; import { useActiveClass } from "@/hooks/use-active-class"; import { fetchAPI, postAPI, putAPI, deleteAPI, getErrorMessage } from "@/lib/api"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Textarea } from "@/components/ui/textarea"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { RoleGuard } from "@/components/role-guard"; import { ConfirmDialog } from "@/components/confirm-dialog"; import { ErrorState } from "@/components/error-state"; import { Pagination } from "@/components/pagination"; import { CalendarView } from "@/components/calendar-view"; import { toast } from "sonner"; import type { PageResponse, ScheduleItem } from "@/lib/types"; import { SCHEDULE_TYPES } from "@/lib/constants"; export default function SchedulePage() { const { activeClassId } = useActiveClass(); const [items, setItems] = useState([]); const [upcoming, setUpcoming] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const [viewMode, setViewMode] = useState<"list" | "calendar">("list"); const [dialogOpen, setDialogOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [editingId, setEditingId] = useState(null); // Form state const [formType, setFormType] = useState("course"); const [formTitle, setFormTitle] = useState(""); const [formStartTime, setFormStartTime] = useState(""); const [formEndTime, setFormEndTime] = useState(""); const [formLocation, setFormLocation] = useState(""); const [formDesc, setFormDesc] = useState(""); const isDeadlineType = formType === "deadline"; const getDisplayTime = (item: ScheduleItem) => item.type === "deadline" && item.end_time ? item.end_time : item.start_time; const loadData = useCallback(async () => { if (!activeClassId) { setItems([]); setUpcoming([]); setError(null); setLoading(false); return; } setLoading(true); setError(null); try { const params = { class_id: String(activeClassId), page: String(page), page_size: "20" }; const [allRes, upcomingRes] = await Promise.all([ fetchAPI>("/api/schedule/", params), fetchAPI("/api/schedule/upcoming", { limit: "10", class_id: String(activeClassId) }), ]); setItems(allRes.items ?? []); setTotalPages(allRes.total_pages ?? 1); setUpcoming(upcomingRes); } catch (err: unknown) { setError(getErrorMessage(err, "加载失败")); } finally { setLoading(false); } }, [activeClassId, page]); useEffect(() => { void loadData(); }, [loadData]); const getCountdown = (dateTime: string) => { const diff = new Date(dateTime).getTime() - Date.now(); const days = Math.ceil(diff / (1000 * 60 * 60 * 24)); if (days <= 0) return { text: "已截止", urgent: false }; if (days === 1) return { text: "明天", urgent: true }; if (days <= 3) return { text: `${days}天后`, urgent: true }; if (days <= 7) return { text: `${days}天后`, urgent: false }; return { text: `${days}天后`, urgent: false }; }; const handleSubmit = async () => { const primaryTime = isDeadlineType ? formEndTime : formStartTime; if (!formTitle.trim() || !primaryTime) return; setSubmitting(true); try { const payload = { type: formType, title: formTitle, start_time: isDeadlineType ? primaryTime : formStartTime, end_time: isDeadlineType ? primaryTime : formEndTime || null, location: isDeadlineType ? null : formLocation || null, description: formDesc || null, }; if (editingId) { await putAPI(`/api/schedule/${editingId}`, payload); toast.success("排期已更新"); } else { await postAPI(`/api/schedule/?class_id=${activeClassId}`, payload); toast.success("排期已创建"); } setDialogOpen(false); resetForm(); await loadData(); } catch (err: unknown) { toast.error(getErrorMessage(err, editingId ? "更新失败" : "创建失败")); } finally { setSubmitting(false); } }; const openEdit = (item: ScheduleItem) => { setEditingId(item.id); setFormType(item.type); setFormTitle(item.title); // Format datetime for datetime-local input (YYYY-MM-DDTHH:mm) const pad = (n: number) => String(n).padStart(2, "0"); const fmt = (d: string) => { const dt = new Date(d); return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())}T${pad(dt.getHours())}:${pad(dt.getMinutes())}`; }; const effectiveTime = item.type === "deadline" && item.end_time ? item.end_time : item.start_time; setFormStartTime(item.type === "deadline" ? "" : fmt(item.start_time)); setFormEndTime(item.type === "deadline" ? fmt(effectiveTime) : item.end_time ? fmt(item.end_time) : ""); setFormLocation(item.type === "deadline" ? "" : item.location || ""); setFormDesc(item.description || ""); setDialogOpen(true); }; const [deleteTarget, setDeleteTarget] = useState(null); const handleDelete = async (id: number) => { try { await deleteAPI(`/api/schedule/${id}`); toast.success("已删除"); setDeleteTarget(null); await loadData(); } catch (err: unknown) { toast.error(getErrorMessage(err, "删除失败")); } }; const resetForm = () => { setEditingId(null); setFormType("course"); setFormTitle(""); setFormStartTime(""); setFormEndTime(""); setFormLocation(""); setFormDesc(""); }; return (

排期表

课程、截止日、活动安排

{/* View mode toggle */}
{ setDialogOpen(open); if (!open) resetForm(); }}> {editingId ? "编辑排期" : "添加排期"}
setFormTitle(e.target.value)} /> {isDeadlineType ? (
setFormEndTime(e.target.value)} />
) : ( <>
setFormStartTime(e.target.value)} />
setFormEndTime(e.target.value)} />
setFormLocation(e.target.value)} /> )}