"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[]; busy: boolean; // 新增一条规则(乐观追加 + 失败回滚 + Toast)。 add: ( projectId: string, level: RuleLevel, content: string, ) => Promise; } // 规则页(UX §7):列出 + 新增规则。乐观追加,失败回滚(仿 useForeshadow)。 export function useRules(initial: RuleView[]): UseRules { const [items, setItems] = useState(initial); const [busy, setBusy] = useState(false); const toast = useToast(); const add = useCallback( async (projectId, level, content) => { if (!hasUsableContent(content)) { toast("请填写规则正文。", "error"); return false; } const snapshot = items; const optimistic: RuleView = { level, content: content.trim() }; setItems((prev) => [...prev, optimistic]); setBusy(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 { setBusy(false); } }, [items, toast], ); return { items, busy, add }; }