diff --git a/src/pages/Upload.tsx b/src/pages/Upload.tsx index 9a89058..1eea36b 100644 --- a/src/pages/Upload.tsx +++ b/src/pages/Upload.tsx @@ -28,6 +28,7 @@ import { import { api, type ParseProgress } from "@/lib/api"; import { fmt, toNum } from "@/lib/format"; import { useAppStore } from "@/store"; +import { useParse } from "@/hooks/useParse"; import type { ClaimOutput, ClaimPayment, @@ -465,7 +466,6 @@ export function Upload() { const [file, setFile] = useState(null); const [kind, setKind] = useState("837p"); const [payer, setPayer] = useState(PAYERS_837[0]!.value); - const [running, setRunning] = useState(false); const [stream, setStream] = useState({ items: [], expectedTotal: null, @@ -474,11 +474,16 @@ export function Upload() { }); const addParsedBatch = useAppStore((s) => s.addParsedBatch); const parsedBatches = useAppStore((s) => s.parsedBatches); + // useParse wires the mutation; on success it invalidates every list query + // (batches, claims, remittances, providers, activity) so the other pages + // see the new data without manual refetching. + const parseMutation = useParse(kind); const payerOptions = kind === "837p" ? PAYERS_837 : PAYERS_835; const expectedTotal = stream.expectedTotal; const totalSoFar = stream.items.length; + const running = parseMutation.isPending; const progressPct = useMemo(() => { if (!expectedTotal || expectedTotal <= 0) return 0; return Math.min(100, Math.round((totalSoFar / expectedTotal) * 100)); @@ -497,39 +502,41 @@ export function Upload() { ); return; } - setRunning(true); setStream({ items: [], expectedTotal: null, passed: 0, failed: 0 }); - const startedAt = new Date().toISOString(); const items: StreamedClaim[] = []; - try { - const onProgress: ParseProgress = (event) => { - if (event.type === "claim") { - items.push({ kind: "837p", data: event.data }); - setStream((s) => ({ - ...s, - items: [...items], - })); - } else if (event.type === "claim_payment") { - items.push({ kind: "835", data: event.data }); - setStream((s) => ({ - ...s, - items: [...items], - })); - } else if (event.type === "summary") { - setStream((s) => ({ - ...s, - expectedTotal: event.data.total_claims, - passed: event.data.passed, - failed: event.data.failed, - })); - } - // financial_info / trace / payer / payee are accepted silently; - // the page summary at the end reports the real pass/fail counts. - }; + const onProgress: ParseProgress = (event) => { + if (event.type === "claim") { + items.push({ kind: "837p", data: event.data }); + setStream((s) => ({ + ...s, + items: [...items], + })); + } else if (event.type === "claim_payment") { + items.push({ kind: "835", data: event.data }); + setStream((s) => ({ + ...s, + items: [...items], + })); + } else if (event.type === "summary") { + setStream((s) => ({ + ...s, + expectedTotal: event.data.total_claims, + passed: event.data.passed, + failed: event.data.failed, + })); + } + // financial_info / trace / payer / payee are accepted silently; + // the page summary at the end reports the real pass/fail counts. + }; - const summary = await (kind === "837p" - ? api.parse837(file, { payer, onProgress }) - : api.parse835(file, { payer, onProgress })); + try { + // mutateAsync triggers the mutation; the streaming callbacks above + // populate `stream` as claims arrive, and the resolver returns the + // final BatchSummary from the "summary" NDJSON line. + const summary = await parseMutation.mutateAsync({ + file, + options: { payer, onProgress }, + }); // The final return value is a BatchSummary in the streaming case. const finalSummary = @@ -548,7 +555,7 @@ export function Upload() { id, kind, inputFilename: file.name, - parsedAt: startedAt, + parsedAt: new Date().toISOString(), claimCount: finalSummary.total_claims, passed: finalSummary.passed, failed: finalSummary.failed, @@ -567,8 +574,6 @@ export function Upload() { toast.error( err instanceof Error ? err.message : "Failed to parse file" ); - } finally { - setRunning(false); } }