feat(sp30): Dashboard Recent batches widget with billing outcome

- backend: GET /api/batches now returns acceptedCount/rejectedCount/
  pendingCount/billedTotal/topRejectionReason/hasProblem per item
  (one GROUP BY query + one ordered scan, no N+1)
- backend: 2 tests pin the 837p full-bucket case + the 835 zero case
- frontend: BatchSummary extended with 6 optional fields
  (backwards compat preserved)
- frontend: new RecentBatchesWidget renders one row per batch with
  status icon + billed total + accepted count + top rejection reason
- frontend: 837p rows show $ total + 'N/M accepted'; 835 rows show
  payment count (Remittance has no batch-level total_charge)
- frontend: full-width row between KPI tiles and Activity on the
  Dashboard; click navigates to /batches?batch=ID
- frontend: 3 component tests cover empty/clean/problem/835 branches
This commit is contained in:
Nora
2026-07-02 14:18:55 -06:00
parent bde3060e9e
commit 97512ec4a7
7 changed files with 661 additions and 0 deletions
+19
View File
@@ -256,6 +256,25 @@ export interface BatchSummary {
* instead of an extra round-trip to `/api/batches/{id}`.
*/
claimIds: string[];
/**
* SP30: billing-outcome rollup computed server-side via one
* batched SQL aggregate (no N+1) so the Dashboard "Recent batches"
* widget can render one row per batch without a follow-up
* `/api/batches/{id}` call. All optional — pre-SP30 fixtures and
* tests that construct BatchSummary locally continue to work.
*/
/** # claims whose state is paid / received / partial / reconciled. */
acceptedCount?: number;
/** # claims whose state is rejected / denied / reversed. */
rejectedCount?: number;
/** # claims whose state is submitted (or draft). */
pendingCount?: number;
/** SUM(charge_amount) across all claims (837P only; 0 for 835). */
billedTotal?: number;
/** Most-recent rejection_reason on the batch, truncated to 60 chars. */
topRejectionReason?: string | null;
/** True when rejectedCount > 0 OR any 277CA A4/A6/A7 claim. */
hasProblem?: boolean;
}
// ---------------------------------------------------------------------------