/** * Document-generation domain hooks (TaskMaster #214). * * The "הפקת מסמכים" card in the case "טיוטות והערות" tab fires two deterministic, * host-side generation actions through the CEO (structured-action wakeup): * • party_claims_summary → an executive summary of the parties' claims * • interim_draft → a partial draft (blocks ה–ט, up to the discussion) * * Generation runs on the host (claude_session → claude -p), NOT in the container, * so the trigger endpoints return 202-accepted and the UI POLLS for completion: * • summary → GET /api/cases/{n}/research/party-claims-summary (200 = ready) * • interim draft → the exports list grows a "טיוטת-ביניים-{n}-vN.docx" file * (polled via useExports — see exports.ts). * * The full-decision export ("הפק טיוטת החלטה מלאה") is NOT here — it reuses the * existing in-container useExportDocx (exports.ts), no LLM, no wakeup. */ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { ApiError, apiRequest } from "./client"; /** Shape returned by the structured-action wakeup endpoints (#214). */ export type GenerateTriggerResult = { status: "ok" | "skipped" | "error"; action?: string; issue_id?: string; ceo_id?: string; reason?: string; error?: string; }; export const generateKeys = { all: ["generate"] as const, partyClaimsSummary: (caseNumber: string) => [...generateKeys.all, "party-claims-summary", caseNumber] as const, }; /* ── Read: party-claims executive summary (poll target for button 1) ── 404 (not generated yet) is a normal "not ready" state, not an error — we coerce it to null so the panel renders the empty/idle state cleanly. */ export type PartyClaimsSummary = { markdown: string } | null; export function usePartyClaimsSummary(caseNumber: string | undefined) { return useQuery({ queryKey: generateKeys.partyClaimsSummary(caseNumber ?? ""), queryFn: async ({ signal }) => { try { return await apiRequest<{ markdown: string }>( `/api/cases/${caseNumber}/research/party-claims-summary`, { signal }, ); } catch (e) { if (e instanceof ApiError && e.status === 404) return null; throw e; } }, enabled: Boolean(caseNumber), staleTime: 5_000, retry: false, }); } /* ── Trigger: party-claims executive summary (button 1) ── */ export function useGeneratePartyClaimsSummary(caseNumber: string) { const qc = useQueryClient(); return useMutation({ mutationFn: () => apiRequest( `/api/cases/${caseNumber}/generate/party-claims-summary`, { method: "POST" }, ), onSuccess: () => { // Start polling fresh — the file will appear after the host run completes. qc.invalidateQueries({ queryKey: generateKeys.partyClaimsSummary(caseNumber), }); }, }); } /* ── Trigger: interim ("party-claims") partial draft (button 2) ── */ export function useGenerateInterimDraft(caseNumber: string) { return useMutation({ mutationFn: () => apiRequest( `/api/cases/${caseNumber}/generate/interim-draft`, { method: "POST" }, ), // The result lands in the exports list (טיוטת-ביניים-*.docx); useExports // already polls on a 5s interval, so no extra invalidation is needed here. }); }