diff --git a/src/components/BatchDiffView.tsx b/src/components/BatchDiffView.tsx index 234e710..a7b3311 100644 --- a/src/components/BatchDiffView.tsx +++ b/src/components/BatchDiffView.tsx @@ -1,4 +1,10 @@ -import { Plus, Minus, Equal, ArrowRight, type LucideIcon } from "lucide-react"; +import { + ArrowRight, + Equal, + Minus, + Plus, + type LucideIcon, +} from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { @@ -9,19 +15,20 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { KpiCard } from "@/components/KpiCard"; import { fmt } from "@/lib/format"; import { cn } from "@/lib/utils"; import type { + BatchClaimDiffSummary, BatchDiff, BatchDiffChangedRow, BatchDiffSideMeta, BatchDiffSummary, - BatchClaimDiffSummary, } from "@/types"; // --------------------------------------------------------------------------- -// Side meta header — small panel identifying which batch is on the left / right. +// Side meta header — small panel identifying which batch is on the +// left / right. Paper-toned to sit inside the cream "paper plane" of +// the BatchDiff page. data-testid pinned by BatchDiff.test.tsx. // --------------------------------------------------------------------------- function SideMeta({ @@ -39,10 +46,19 @@ function SideMeta({ : "text-amber-300 border-amber-400/30 bg-amber-400/10"; return (
-
+
{label}
@@ -54,12 +70,23 @@ function SideMeta({ > {side.kind} - {side.id} + + {side.id} +
-
+
{side.inputFilename}
-
+
Parsed {side.parsedAt ? fmt.dateShort(side.parsedAt) : "—"} {fmt.num(side.claimCount)} claim{side.claimCount === 1 ? "" : "s"} @@ -70,40 +97,125 @@ function SideMeta({ } // --------------------------------------------------------------------------- -// Summary cards — the four-count tile row. +// Summary cards — the four-count tile row. Paper-toned via +// DiffKpiTile so the counts sit naturally on the cream paper plane. // --------------------------------------------------------------------------- function SummaryCards({ summary }: { summary: BatchDiffSummary }) { return ( -
- + - - -
); } +// --------------------------------------------------------------------------- +// DiffKpiTile — paper-toned metric tile for the diff summary row. +// Mirrors AckKpiTile / BatchesKpiTile from the other hybrid pages. +// --------------------------------------------------------------------------- + +function DiffKpiTile({ + label, + value, + icon: Icon, + hint, + tone, +}: { + label: string; + value: string; + icon: LucideIcon; + hint: string; + tone: "success" | "destructive" | "amber" | "ink"; +}) { + const accentMap = { + success: "hsl(152 64% 38%)", + destructive: "hsl(358 70% 42%)", + amber: "hsl(36 92% 50%)", + ink: "hsl(var(--surface-ink))", + } as const; + const tintMap = { + success: "hsl(152 50% 88%)", + destructive: "hsl(358 70% 92%)", + amber: "hsl(36 82% 92%)", + ink: "hsl(36 22% 90%)", + } as const; + const accent = accentMap[tone]; + const tint = tintMap[tone]; + return ( +
+
+
+
+ {label} +
+
+ +
+
+
+ {value} +
+
+ ); +} + // --------------------------------------------------------------------------- // Row indicator — `+` / `-` / `~` gutter on the left of every row. // --------------------------------------------------------------------------- @@ -152,7 +264,10 @@ function RowIndicator({ function ClaimIdCell({ id }: { id: string }) { return ( - + {id} ); @@ -160,14 +275,31 @@ function ClaimIdCell({ id }: { id: string }) { function PatientCell({ name }: { name: string }) { if (!name) { - return ; + return ( + + — + + ); } - return {name}; + return ( + + {name} + + ); } function ChargeCell({ value }: { value: number }) { return ( - + {fmt.usdPrecise(value)} ); @@ -175,19 +307,44 @@ function ChargeCell({ value }: { value: number }) { function DateCell({ value }: { value: string | null }) { if (!value) { - return ; + return ( + + — + + ); } - return {value}; + return ( + + {value} + + ); } function StatusCell({ value }: { value: string }) { if (!value) { - return ; + return ( + + — + + ); } return ( {value} @@ -196,17 +353,28 @@ function StatusCell({ value }: { value: string }) { function CptCell({ codes }: { codes: string[] }) { if (!codes || codes.length === 0) { - return ; + return ( + + — + + ); } return ( - + {codes.join(", ")} ); } // --------------------------------------------------------------------------- -// Section tables — one per bucket (added / removed / changed). +// Section tables — one per bucket (added / removed / changed). Uses +// `tone="paper"` so the table chrome matches the cream paper plane. // --------------------------------------------------------------------------- type Side = "a" | "b"; @@ -386,25 +554,53 @@ function SectionTable({
-
+
{eyebrow}
-

{title}

+

+ {title} +

- + {fmt.num(count)}
{count === 0 ? (
{emptyMessage}
) : ( -
- +
+
@@ -425,7 +621,8 @@ function SectionTable({ } // --------------------------------------------------------------------------- -// Skeleton — used by the page while the diff is loading. +// Skeleton — used by the page while the diff is loading. Paper-toned +// (cream blocks) to match the paper plane chrome. // --------------------------------------------------------------------------- export function BatchDiffViewSkeleton() { @@ -462,11 +659,24 @@ export function BatchDiffEmpty({ data }: { data: BatchDiff }) { -
-
+
+
Diff · no deltas
-
+
These two batches are identical — no claims added, removed, or changed between A and B.
diff --git a/src/pages/BatchDiff.tsx b/src/pages/BatchDiff.tsx index 1228ce0..14607ea 100644 --- a/src/pages/BatchDiff.tsx +++ b/src/pages/BatchDiff.tsx @@ -1,6 +1,11 @@ import { useCallback, useMemo } from "react"; import { useSearchParams } from "react-router-dom"; -import { GitCompareArrows, CircleDashed, AlertCircle } from "lucide-react"; +import { + AlertCircle, + ArrowRight, + CircleDashed, + GitCompareArrows, +} from "lucide-react"; import { ApiError } from "@/lib/api"; import { Button } from "@/components/ui/button"; import { @@ -12,7 +17,10 @@ import { } from "@/components/ui/select"; import { EmptyState } from "@/components/ui/empty-state"; import { ErrorState } from "@/components/ui/error-state"; -import { BatchDiffView, BatchDiffViewSkeleton } from "@/components/BatchDiffView"; +import { + BatchDiffView, + BatchDiffViewSkeleton, +} from "@/components/BatchDiffView"; import { useBatches } from "@/hooks/useBatches"; import { useBatchDiff } from "@/hooks/useBatchDiff"; import type { BatchSummary as ApiBatchSummary } from "@/lib/api"; @@ -38,9 +46,11 @@ function readIdsFromParams(params: URLSearchParams): { } // --------------------------------------------------------------------------- -// Batch picker — one Select dropdown for each side. Keeps the same -// kind-colored badge inline so the operator can spot which batch is -// which at a glance. +// Kind badge — small inline label so the operator can spot which batch +// is which at a glance. Identical contract to the one in +// `BatchesList.tsx`; duplicated here because each lives inside a +// different page surface (the picker is part of this page, the row +// badge belongs to Batches). // --------------------------------------------------------------------------- function KindBadge({ kind }: { kind: ApiBatchSummary["kind"] }) { @@ -63,6 +73,7 @@ function KindBadge({ kind }: { kind: ApiBatchSummary["kind"] }) { function BatchPicker({ label, + side, value, onChange, items, @@ -70,6 +81,7 @@ function BatchPicker({ testid, }: { label: string; + side: "A" | "B"; value: string | null; onChange: (id: string) => void; items: ApiBatchSummary[]; @@ -85,10 +97,54 @@ function BatchPicker({ [items, excludeId], ); const selected = items.find((it) => it.id === value); + const accent = side === "A" ? "hsl(212 100% 45%)" : "hsl(36 92% 50%)"; + const tint = side === "A" ? "hsl(212 85% 95%)" : "hsl(36 82% 92%)"; return ( -
-
- {label} +
+
+
+
+
+ {side} +
+ + {label} + +
+ {selected ? ( + + {selected.claimCount}{" "} + + claims + + + ) : null}