מוסיף כרטיס "הפקת מסמכים" בראש טאב "טיוטות והערות" (mockup 26 — תוספת ל-18h),
תוספת לעמוד הקיים, כל הסקשנים נשמרים.
- כפתור 1 "הפק סיכום מנהלים" → endpoint חדש POST /generate/party-claims-summary
→ CEO wakeup סטרוקטורלי action="party_claims_summary" (שלב H2, host-side).
Polling: GET /research/party-claims-summary (200=מוכן → פתח/DOCX).
- כפתור 2 "הפק טיוטה של טענות הצדדים" → endpoint חדש POST /generate/interim-draft
→ CEO wakeup action="interim_draft" (שלב H, host-side).
Polling: רשימת ה-exports עד שמופיע טיוטת-ביניים-{case}-vN.docx.
- כפתור 3 "הפק טיוטת החלטה מלאה" → ה-useExportDocx הקיים (in-container, ללא LLM);
הועבר מהבאנר (כפתור "הפק DOCX" הוסר משם — אין כפילות). disabled+title כשאין
בלוקים כתובים (isDraftReady).
מסלול-עירור יחיד דרך ה-Platform Port (pc_wake_ceo_for_action) → API helper
/api/agents/{id}/wakeup, לעולם לא DB insert. Israel-time + cache-invalidation.
Invariants: G12 (מגע-Paperclip רק ב-paperclip_client + agent_platform_port;
leak-guard ירוק) · G2 (אין מסלול-עירור מקביל — שימוש חוזר ב-pc_request הקיים) ·
G10 (הפקה מגודרת-יו"ר/CEO) · INV-UI9 (Asia/Jerusalem) · X6 (UI↔API contract).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
93 lines
3.4 KiB
TypeScript
93 lines
3.4 KiB
TypeScript
/**
|
|
* 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<PartyClaimsSummary>({
|
|
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<GenerateTriggerResult>(
|
|
`/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<GenerateTriggerResult>(
|
|
`/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.
|
|
});
|
|
}
|