"use client"; import { useCallback, useState } from "react"; import { api } from "@/lib/api/client"; import { useToast } from "@/components/Toast"; import type { RuleView } from "@/lib/api/types"; import { buildRuleCreateRequest, hasUsableContent, type RuleLevel } from "./rules"; export interface UseRules { items: RuleView[]; // 正在新增(仅冻结新增表单,不影响删除)。 adding: boolean; // 正在删除的那一条 id(仅冻结该行删除按钮,整页其余照常可操作)。 deletingId: string | null; // 新增一条规则(乐观追加 + 失败回滚 + Toast)。 add: ( projectId: string, level: RuleLevel, content: string, ) => Promise; // 删除一条规则(乐观移除 + 失败回滚 + Toast)。 remove: (projectId: string, ruleId: string) => Promise; } // 规则页(UX §7):列出 + 新增规则。乐观追加,失败回滚(仿 useForeshadow)。 // add 与 remove 用各自独立的进行中标志,避免删一条就冻结整页。 export function useRules(initial: RuleView[]): UseRules { const [items, setItems] = useState(initial); const [adding, setAdding] = useState(false); const [deletingId, setDeletingId] = useState(null); const toast = useToast(); const add = useCallback( async (projectId, level, content) => { if (!hasUsableContent(content)) { toast("请填写规则正文。", "error"); return false; } const snapshot = items; // 乐观项需一个临时 id(删除 handle 之前);服务端返回后被权威行替换。 const optimistic: RuleView = { id: crypto.randomUUID(), level, content: content.trim(), }; setItems((prev) => [...prev, optimistic]); setAdding(true); try { const { data, error } = await api.POST( "/projects/{project_id}/rules", { params: { path: { project_id: projectId } }, body: buildRuleCreateRequest(level, content), }, ); if (error || !data) { setItems(snapshot); // 回滚 toast("新增规则失败,请稍后重试。", "error"); return false; } // 用服务端权威行替换乐观行(去掉乐观项、追加返回项)。 setItems((prev) => [...prev.slice(0, snapshot.length), data]); toast("已新增规则", "success"); return true; } finally { setAdding(false); } }, [items, toast], ); const remove = useCallback( async (projectId, ruleId) => { const snapshot = items; setItems((prev) => prev.filter((r) => r.id !== ruleId)); setDeletingId(ruleId); try { const { error } = await api.DELETE( "/projects/{project_id}/rules/{rule_id}", { params: { path: { project_id: projectId, rule_id: ruleId } } }, ); if (error) { setItems(snapshot); // 回滚 toast("删除规则失败,请稍后重试。", "error"); return false; } toast("已删除规则", "success"); return true; } finally { setDeletingId(null); } }, [items, toast], ); return { items, adding, deletingId, add, remove }; }