Commit Graph

179 Commits

Author SHA1 Message Date
Tyler c94a1f8201 merge: ui/a11y-followup 2026-06-20 17:29:22 -06:00
Tyler e617d11fee merge: ui/print-styles 2026-06-20 17:29:20 -06:00
Tyler 7c4da65625 merge: ui/csv-export 2026-06-20 17:29:00 -06:00
Tyler 1359b0753a merge: SP5 live-tail — pub/sub + 3 stream endpoints + frontend tail integration
Brings the SP5 live-tail implementation into main:

Backend
  * EventBus (cyclone.pubsub): per-kind fan-out with drop-oldest overflow
  * FastAPI lifespan initializes the bus + db once per process
  * store.add() publishes claim_written / remittance_written /
    activity_recorded events on every batch write
  * GET /api/{claims,remittances,activity}/stream: NDJSON snapshot +
    live subscription + 15s idle heartbeat
  * EventBus.unsubscribe() lets the tail loop release its queue on
    client disconnect (no queue leak per open stream)

Frontend
  * src/lib/tail-stream.ts: streamTail() async-generator over fetch
  * src/store/tail-store.ts: zustand with FIFO cap 10k per slice
  * src/hooks/useTailStream.ts: connecting/live/reconnecting/stalled/error/closed
    state machine with 1→2→4→8→16→30s backoff
  * src/hooks/useMergedTail.ts: base + tail merge with filter
  * src/components/TailStatusPill.tsx: badge + Reconnect button
  * Claims, Remittances, ActivityLog pages wired to the tail

Tests
  * 437 backend tests pass (was 418 before SP5)
  * 154 frontend tests pass (was 124)
  * npm run typecheck clean
  * end-to-end smoke: open /api/claims/stream, POST 837, see new claims
    arrive in real time without refresh

# Conflicts:
#	src/pages/ActivityLog.tsx
#	src/pages/Remittances.test.tsx
#	src/pages/Remittances.tsx
2026-06-20 17:28:58 -06:00
Tyler a79668192d feat(frontend): global Cmd-K search across claims/remits/activity 2026-06-20 17:25:40 -06:00
Tyler c8d84c08c4 a11y(frontend): select outline, CardTitle polymorphic as prop, cheatsheet labels 2026-06-20 17:22:43 -06:00
Tyler 7b394fff1a feat(batch-diff): side-by-side claim diff between two batches 2026-06-20 17:17:09 -06:00
Tyler 25d47a5621 feat(frontend): RemitDrawer component with CAS adjustments + parties + claim payments 2026-06-20 17:16:23 -06:00
Tyler fa23eb182e feat(frontend): Remittances + ActivityLog wire live tail 2026-06-20 17:14:37 -06:00
Tyler 162127b494 feat(frontend): CSV export utility + ExportCsvButton component 2026-06-20 17:12:44 -06:00
Tyler 365e64e25a feat(frontend): Claims page wires live tail + TailStatusPill 2026-06-20 17:11:56 -06:00
Tyler 8523772792 style(frontend): print styles for detail drawers (Cmd-P paper records) 2026-06-20 17:09:37 -06:00
Tyler f1407dbb1d feat(frontend): TailStatusPill with reconnect button 2026-06-20 17:07:01 -06:00
Tyler e56a1eb503 feat(frontend): useMergedTail merges base + tail with filter 2026-06-20 17:05:24 -06:00
Tyler 0e766ce654 feat(frontend): useTailStream lifecycle hook with backoff + stall detection 2026-06-20 17:03:36 -06:00
Tyler 4cd52c3084 merge: ui/activity-filter — kind + since filters on ActivityLog 2026-06-20 17:00:07 -06:00
Tyler e76f821f22 merge: ui/keyboard-acks-remits — j/k row nav + ? cheatsheet on Acks and Remittances 2026-06-20 16:59:48 -06:00
Tyler d376647f5f merge: ui/batches-browser — list + detail drawer for /api/batches 2026-06-20 16:59:44 -06:00
Tyler cff4883412 merge: ui/mobile-responsive — Dashboard/Providers/Upload/Reconciliation breakpoints 2026-06-20 16:59:31 -06:00
Tyler 2fedb26c6a merge: ui/a11y-polish — focus rings, ARIA labels, skip-link 2026-06-20 16:59:25 -06:00
Tyler f4a545f379 fix(frontend): UnmatchedClaim/UnmatchedRemittance types match store.to_ui_claim_from_orm wire shape
The Reconciliation page crashed with 'c.chargeAmount.toFixed is undefined' because
the TS types declared fields the backend never emitted (chargeAmount vs billedAmount,
patientControlNumber vs patientName, statusCode vs status, totalPaid vs paidAmount).

- UnmatchedClaim: chargeAmount -> billedAmount, patientControlNumber -> patientName,
  payerId/serviceDate now optional (backend omits them)
- UnmatchedRemittance: statusCode -> status, totalPaid -> paidAmount,
  totalCharge/serviceDate/isReversal now optional
- Reconciliation page + test fixtures updated to use the wire shape
- Tests: 124/124 pass
2026-06-20 16:59:22 -06:00
Tyler 8f419cdeba feat(frontend): j/k row nav + ? cheatsheet on Acks and Remittances 2026-06-20 16:57:22 -06:00
Tyler ff4906d0c1 feat(frontend): kind + since filters on ActivityLog page 2026-06-20 16:53:19 -06:00
Tyler 354268330c feat(frontend): Batches browser page with list + detail drawer 2026-06-20 16:52:20 -06:00
Tyler b37fb5a26c a11y(frontend): focus rings, ARIA labels, skip-link across ui components 2026-06-20 16:51:36 -06:00
Tyler 1476c90570 style(frontend): mobile responsive polish for Dashboard/Providers/Upload/Reconciliation 2026-06-20 16:51:17 -06:00
Tyler 64fb11a9cf feat(frontend): tail-store Zustand with FIFO cap 10k per slice 2026-06-20 16:05:21 -06:00
Tyler 11a4eaa480 feat(frontend): NDJSON streamTail parser for /api/{resource}/stream 2026-06-20 16:02:25 -06:00
Tyler d03293ed1d feat(frontend): wire KeyboardCheatsheet into Claims page via ? toggle 2026-06-20 12:23:06 -06:00
Tyler d44aa1de3f feat(frontend): KeyboardCheatsheet overlay 2026-06-20 12:18:08 -06:00
Tyler 67d5c13939 feat(frontend): Claims page wires click-to-open drawer with URL sync 2026-06-20 12:09:42 -06:00
Tyler 436cbead11 feat(frontend): ClaimDrawer root orchestrator with skeleton/error/data states 2026-06-20 12:00:27 -06:00
Tyler 7117f75764 feat(frontend): ClaimDrawer barrel export 2026-06-20 11:50:44 -06:00
Tyler 3fec07ca4d style(frontend): tidy drawer review nits
- RawSegmentsPanel: summary now reads 'Show N raw segments' for
  non-empty (with singular pluralization). The previous identical
  ternary branches read as a bug.
- PartiesGrid: replace the verbose intersection cast on AddressBlock
  with a direct ClaimDetailAddress cast (the union's other arm is
  Record<string, never>, ruled out by isEmptyAddress above).
2026-06-20 11:49:18 -06:00
Tyler dc89244100 feat(frontend): StateHistoryTimeline with kind-colored dots 2026-06-20 11:43:49 -06:00
Tyler 1671e96a10 feat(frontend): MatchedRemitCard with view-remittance link 2026-06-20 11:41:30 -06:00
Tyler 7c7e724464 feat(frontend): RawSegmentsPanel collapsible debug aid 2026-06-20 11:39:59 -06:00
Tyler 94a39e9dd6 feat(frontend): PartiesGrid with billing provider/subscriber/payer cards 2026-06-20 11:38:51 -06:00
Tyler 042597217f feat(frontend): DiagnosesList with qualifier + code formatting 2026-06-20 11:34:00 -06:00
Tyler 7f389fe04b feat(frontend): ServiceLinesTable with procedure + charge columns 2026-06-20 11:29:10 -06:00
Tyler 19b2eb7eaa fix(frontend): announce blocking validation errors via role=alert
ClaimDrawerError.tsx already wraps its error surface with role=alert
(line 36). Apply the same pattern to the ValidationPanel errors
sub-section so assistive tech announces blocking validation failures
when the drawer opens, matching the established sibling pattern.

Warnings stay decorative (no role change) — they're advisories, not
blocks.
2026-06-20 11:25:28 -06:00
Tyler 88da603c9b feat(frontend): ValidationPanel with rule-grouped errors + warnings 2026-06-20 11:21:54 -06:00
Tyler 702104c26d test(frontend): cover all 6 states in ClaimDrawerHeader badge variant matrix
The parametrized variant test only exercised 4 of the 6 mapped states
(submitted/denied/paid/pending). Add accepted (-> default) and matched
(-> success) so the entire STATE_VARIANT map is verified, not just the
branches whose tokens happened to be easy to reach.
2026-06-20 11:18:50 -06:00
Tyler 8933596c77 feat(frontend): ClaimDrawerHeader with state badge + close button 2026-06-20 11:15:49 -06:00
Tyler 67653f0da8 feat(frontend): ClaimDrawer skeleton + error states 2026-06-20 11:09:41 -06:00
Tyler 7dbcba8fa3 feat(frontend): Modern palette CSS variables + Inter Tight/JetBrains Mono fonts 2026-06-20 10:57:46 -06:00
Tyler 24d13faca7 fix(frontend): type pushState mocks with real History.pushState signature
- npm run typecheck failed on the 0-arg vi.fn<() => void>() because
  mock.calls[0] became an empty tuple, so calls[0][2] (the URL arg)
  was typed as undefined.
- Constrain the mock to the real (state, unused, url?) signature so
  mock.calls[0] is a 3-tuple and [2] is string-or-URL-or-null. Drops
  the as-string cast at every callsite.
2026-06-20 10:55:03 -06:00
Tyler e7469d39ec fix(frontend): normalize empty ?claim= to null; preserve sibling params
- readClaimId: URLSearchParams.get returns '' for ?claim=, not null.
  Normalize to null so the contract matches the JSDoc and consumers
  can treat empty as absent.

- Tests: regression for ?claim= → null; open/close preserves other
  query params (?foo=bar survives open/close unchanged).

- Tighten pushStateMock/replaceStateMock typing to vi.fn<() => void>()
  per code-review nit (drops the verbose ReturnType<typeof vi.fn>).
2026-06-20 10:52:42 -06:00
Tyler 234cd8b28b feat(frontend): useDrawerUrlState hook for ?claim= sync 2026-06-20 10:39:53 -06:00
Tyler c737bd742d feat(frontend): useDrawerKeyboard hook for j/k/esc/? 2026-06-20 10:23:40 -06:00