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) : "—"}
›
))}
);
}