import { AnimatedNumber } from "@/components/AnimatedNumber"; import { Skeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { cn } from "@/lib/utils"; import { fmt } from "@/lib/format"; import type { BatchSummary } from "@/lib/api"; /** * Colored kind badge — 837p in cool blue, 835 in warm amber. The colors * are deliberately not the project-wide `--accent` electric blue (that's * reserved for active nav + primary CTAs) so the rows scan as a * two-flavor instrument instead of a uniform accent. * * Voice mirrors `AckCodeBadge` in `src/pages/Acks.tsx` (uppercase, * wide tracking, hairline border, low-opacity fill). * * The literal class names `text-sky-300` and `text-amber-300` are * pinned by `Batches.test.tsx` as a contract — the test asserts the * badge's text color is one of these two strings. Don't replace them * with arbitrary HSL values or the test will fail. */ function KindBadge({ kind }: { kind: BatchSummary["kind"] }) { const color = kind === "837p" ? "text-sky-300 border-sky-400/30 bg-sky-400/10" : "text-amber-300 border-amber-400/30 bg-amber-400/10"; return ( {kind} ); } /** * Skeleton rows for the batches table. Mirrors the row count used in * `Acks.tsx` (5 placeholders) so the loading density matches the rest * of the app. `data-testid="batches-skeleton"` is pinned by * `Batches.test.tsx`. */ export function BatchesListSkeleton() { return (
{Array.from({ length: 5 }).map((_, i) => ( ))}
); } type BatchesListProps = { items: BatchSummary[]; /** * Currently-open batch id (or null when drawer is closed). The open * row gets a subtle background tint so the operator knows which * batch the drawer is showing. */ openId: string | null; onOpen: (id: string) => void; /** * When `paper`, the table sits inside a cream "paper plane" section * and uses the paper-toned color scheme: warm hover, hairline border * in surface-line, surface-ink text. When `dark` (default), the * original dark-mode chrome is used. */ tone?: "dark" | "paper"; }; /** * Tabular list of every persisted batch. Rows are clickable — clicking * opens the detail drawer. `AnimatedNumber` is used for `claimCount` * so the numbers tick up from 0 on first render (gives the page a * little life on load; consistent with the Dashboard KPI cards). */ export function BatchesList({ items, openId, onOpen, tone = "dark", }: BatchesListProps) { const isPaper = tone === "paper"; return ( Kind Batch Input file Claims Parsed {items.map((b) => ( onOpen(b.id)} data-testid={`batch-row-${b.id}`} data-open={openId === b.id ? "true" : undefined} className={cn( "animate-row-flash cursor-pointer", isPaper && openId === b.id && "!bg-[hsl(212_85%_95%)] ring-1 ring-inset ring-[hsl(212_100%_45%_/_0.30)]", !isPaper && openId === b.id && "bg-muted/40", )} > {b.id} {b.inputFilename} fmt.num(Math.round(n))} /> {b.parsedAt ? fmt.dateShort(b.parsedAt) : "—"} ))}
); }