/** * Research analysis hooks — reads and mutates the * `analysis-and-research.md` file that backs each case's compose screen. * * Schema mirrors research_md.parse() in the FastAPI backend. Kept as * hand-typed interfaces because the endpoint does not declare a * response_model in the OpenAPI schema. */ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiRequest } from "./client"; export type ResearchField = { label: string; content: string; }; export type ResearchSubsection = { id: string; // e.g. "threshold_1" or "issue_3" number: string; title: string; fields: ResearchField[]; chair_position?: string; }; export type ResearchAnalysis = { header?: { date?: string; modified_at?: string; [k: string]: unknown; }; represented_party?: string; procedural_background?: string; agreed_facts?: string; disputed_facts?: string; threshold_claims?: ResearchSubsection[]; issues?: ResearchSubsection[]; conclusions?: string; other_sections?: Array<{ title: string; body: string }>; }; export const researchKeys = { all: ["research"] as const, analysis: (caseNumber: string) => [...researchKeys.all, "analysis", caseNumber] as const, }; export function useResearchAnalysis(caseNumber: string | undefined) { return useQuery({ queryKey: researchKeys.analysis(caseNumber ?? ""), queryFn: ({ signal }) => apiRequest( `/api/cases/${caseNumber}/research/analysis`, { signal }, ), enabled: Boolean(caseNumber), /* No polling — the user is editing; refetching would clobber focus */ staleTime: 60_000, }); } export function useSaveChairPosition(caseNumber: string | undefined) { const qc = useQueryClient(); return useMutation({ mutationFn: async (vars: { sectionId: string; position: string }) => apiRequest( `/api/cases/${caseNumber}/research/analysis/chair-position`, { method: "PATCH", body: { section_id: vars.sectionId, position: vars.position }, }, ), onSuccess: (_res, vars) => { /* Locally patch the cached analysis so other consumers stay in sync without an immediate refetch that would steal focus from the editor. */ qc.setQueryData( researchKeys.analysis(caseNumber ?? ""), (prev) => { if (!prev) return prev; const patch = (arr?: ResearchSubsection[]) => arr?.map((s) => s.id === vars.sectionId ? { ...s, chair_position: vars.position } : s, ); return { ...prev, threshold_claims: patch(prev.threshold_claims), issues: patch(prev.issues), }; }, ); }, }); }