Files
cyclone/src/components/BatchesList.tsx
T

126 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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).
*/
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 (
<span
data-testid={`kind-badge-${kind}`}
className={cn(
"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10.5px] font-semibold uppercase tracking-[0.14em] font-mono",
color,
)}
>
{kind}
</span>
);
}
/**
* 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.
*/
export function BatchesListSkeleton() {
return (
<div className="p-4 space-y-2" data-testid="batches-skeleton">
{Array.from({ length: 5 }).map((_, i) => (
<Skeleton key={i} variant="row" />
))}
</div>
);
}
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;
};
/**
* 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 }: BatchesListProps) {
return (
<Table data-testid="batches-table">
<TableHeader>
<TableRow>
<TableHead>Kind</TableHead>
<TableHead>Batch</TableHead>
<TableHead>Input file</TableHead>
<TableHead className="text-right">Claims</TableHead>
<TableHead>Parsed</TableHead>
<TableHead className="w-6" aria-label="Open" />
</TableRow>
</TableHeader>
<TableBody>
{items.map((b) => (
<TableRow
key={b.id}
onClick={() => onOpen(b.id)}
data-testid={`batch-row-${b.id}`}
data-open={openId === b.id ? "true" : undefined}
className={cn(
"animate-row-flash cursor-pointer",
openId === b.id && "bg-muted/40",
)}
>
<TableCell>
<KindBadge kind={b.kind} />
</TableCell>
<TableCell className="display num text-[13px]">
{b.id}
</TableCell>
<TableCell className="font-mono text-[12px] text-muted-foreground truncate max-w-[280px]">
{b.inputFilename}
</TableCell>
<TableCell className="text-right display num text-[13px]">
<AnimatedNumber
value={b.claimCount}
format={(n) => fmt.num(Math.round(n))}
/>
</TableCell>
<TableCell className="text-muted-foreground num text-[12.5px]">
{b.parsedAt ? fmt.dateShort(b.parsedAt) : "—"}
</TableCell>
<TableCell className="text-muted-foreground text-right">
<span aria-hidden></span>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}