"use client"; import * as React from "react"; import { LibraryIcon, PlusIcon, SaveIcon, Settings2Icon, Trash2Icon } from "lucide-react"; import { toast } from "sonner"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@/components/ui/combobox"; import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet"; import { Spinner } from "@/components/ui/spinner"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/lib/api"; import type { TaskTemplate } from "@/lib/types"; import { cn } from "@/lib/utils"; interface TemplateDraft { name: string; description: string; goal: string; } interface TaskTemplateManagerProps { open: boolean; onOpenChange: (open: boolean) => void; templates: TaskTemplate[]; seed: Pick | null; onCreated: (template: TaskTemplate) => void; onUpdated: (template: TaskTemplate) => void; onDeleted: (id: number) => void; } const emptyDraft = (): TemplateDraft => ({ name: "", description: "", goal: "" }); function templateDraft(template: TaskTemplate): TemplateDraft { return { name: template.name, description: template.description, goal: template.goal, }; } function TaskTemplateManager({ open, onOpenChange, templates, seed, onCreated, onUpdated, onDeleted, }: TaskTemplateManagerProps) { const [selectedID, setSelectedID] = React.useState(null); const [draft, setDraft] = React.useState(emptyDraft); const [saving, setSaving] = React.useState(false); const [deleteOpen, setDeleteOpen] = React.useState(false); const [deleting, setDeleting] = React.useState(false); const wasOpen = React.useRef(false); React.useEffect(() => { if (open && !wasOpen.current) { if (seed) { setSelectedID(null); setDraft({ name: "", description: seed.description, goal: seed.goal }); } else if (templates[0]) { setSelectedID(templates[0].id); setDraft(templateDraft(templates[0])); } else { setSelectedID(null); setDraft(emptyDraft()); } } wasOpen.current = open; }, [open, seed, templates]); const selectTemplate = (template: TaskTemplate) => { setSelectedID(template.id); setDraft(templateDraft(template)); }; const startNew = () => { setSelectedID(null); setDraft(emptyDraft()); }; const updateDraft = (field: keyof TemplateDraft, value: string) => { setDraft((current) => ({ ...current, [field]: value })); }; async function save() { const input = { name: draft.name.trim(), description: draft.description.trim(), goal: draft.goal.trim(), }; if (!input.name || !input.description || !input.goal) { toast.error("请填写模板名称、描述和目标"); return; } setSaving(true); try { if (selectedID == null) { const created = await api.createTaskTemplate(input); onCreated(created); setSelectedID(created.id); setDraft(templateDraft(created)); toast.success("模板已创建"); } else { const updated = await api.updateTaskTemplate(selectedID, input); onUpdated(updated); setDraft(templateDraft(updated)); toast.success("模板已更新"); } } catch (error) { toast.error(`保存失败:${(error as Error).message}`); } finally { setSaving(false); } } async function remove() { if (selectedID == null) return; const deletedID = selectedID; setDeleting(true); try { await api.deleteTaskTemplate(deletedID); onDeleted(deletedID); const next = templates.find((template) => template.id !== deletedID); if (next) { setSelectedID(next.id); setDraft(templateDraft(next)); } else { startNew(); } setDeleteOpen(false); toast.success("模板已删除"); } catch (error) { toast.error(`删除失败:${(error as Error).message}`); } finally { setDeleting(false); } } let saveLabel = saving ? "保存中" : "保存修改"; if (!saving && selectedID == null) saveLabel = "创建模板"; return ( <> 任务模板管理 模板只保存描述与目标;修改不会影响已经创建的任务。
{templates.length === 0 && (

暂无模板

)} {templates.map((template) => ( ))}
模板名称 updateDraft("name", event.target.value)} /> 描述