"use client"; import { useCallback, useState } from "react"; import { useToast } from "@/components/Toast"; import { Button } from "@/components/ui/Button"; import { Field } from "@/components/ui/Field"; import { SectionHeader } from "@/components/ui/SectionHeader"; import { TextArea } from "@/components/ui/TextArea"; import { TextInput } from "@/components/ui/TextInput"; import { api } from "@/lib/api/client"; import type { TemplateResponse } from "@/lib/api/types"; import { buildTemplateCreateRequest, EMPTY_TEMPLATE_DRAFT, isTemplateDraftValid, type TemplateDraft, } from "@/lib/templates/templates"; import { cardClass } from "@/lib/ui/variants"; interface TemplatesManagerProps { // 初始模板列表(Server Component 预取;失败给空数组 + loadError 文案)。 initial: TemplateResponse[]; } // 提示词/模板库管理(F3,单用户本地版):列出 / 新建 / 删除。 // 列表本地维护(乐观更新失败回滚);CRUD 走 OpenAPI 客户端。分享/市场不做(需多租户)。 export function TemplatesManager({ initial }: TemplatesManagerProps) { const toast = useToast(); const [templates, setTemplates] = useState(initial); const [draft, setDraft] = useState(EMPTY_TEMPLATE_DRAFT); const [creating, setCreating] = useState(false); const setField = useCallback( (field: keyof TemplateDraft, value: string): void => { setDraft((prev) => ({ ...prev, [field]: value })); }, [], ); const onCreate = useCallback(async (): Promise => { if (!isTemplateDraftValid(draft)) { toast("请填写标题与正文。", "error"); return; } setCreating(true); try { const { data, error } = await api.POST("/templates", { body: buildTemplateCreateRequest(draft), }); if (error || !data) { toast("新建模板失败,请重试。", "error"); return; } setTemplates((prev) => [data, ...prev]); setDraft(EMPTY_TEMPLATE_DRAFT); toast("已新建模板。", "success"); } catch { toast("新建模板请求异常,请检查网络。", "error"); } finally { setCreating(false); } }, [draft, toast]); const onDelete = useCallback( async (id: string): Promise => { const prev = templates; // 乐观删除:先移除,失败回滚。 setTemplates((cur) => cur.filter((t) => t.id !== id)); try { const { error } = await api.DELETE("/templates/{template_id}", { params: { path: { template_id: id } }, }); if (error) { setTemplates(prev); toast("删除模板失败,请重试。", "error"); return; } toast("已删除模板。", "success"); } catch { setTemplates(prev); toast("删除模板请求异常,请检查网络。", "error"); } }, [templates, toast], ); return (
{ e.preventDefault(); void onCreate(); }} > setField("title", e.target.value)} aria-label="标题" />