Transactions · Vektos
Tasks / Transactions All transactions of a current account, newest first, in three views (all, money in, money out) and ten per page with page navigation.
The brief What the agent reads, word for word.
What a good answer has The page must show these texts, or the run is not done. A good answer uses these components.
Best and worst runs By design-system violations.
By context and model Mean design-system violations per run on this task.
The reference answer Written by hand, graded with zero violations, rendered by the browser check in every build. The agent never sees it.
0
13 Tabs,
14 } from "@vektos/ds";
15 import { useState } from "react";
16 import { css } from "styled-system/css";
17 import data from "./fixture.json";
18
19 type Transaction = (typeof data.transactions)[number];
20
21 const money = new Intl.NumberFormat("en-IE", {
22 style: "currency",
23 currency: data.account.currency,
24 signDisplay: "exceptZero",
25 });
26 const date = new Intl.DateTimeFormat("en-GB", { dateStyle: "medium", timeZone: "UTC" });
27
28 const VIEWS = [
29 { value: "all", label: "All", keep: () => true },
30 { value: "in", label: "Money in", keep: (item: Transaction) => item.amount > 0 },
31 { value: "out", label: "Money out", keep: (item: Transaction) => item.amount < 0 },
32 ];
33
34 function TransactionTable({ rows, label }: { rows: Transaction[]; label: string }) {
35 return (
36 <Table aria-label={label}>
37 <TableHead>
38 <TableRow>
39 <TableHeader>Date</TableHeader>
40 <TableHeader>Description</TableHeader>
41 <TableHeader>Status</TableHeader>
42 <TableHeader numeric>Amount</TableHeader>
43 </TableRow>
44 </TableHead>
45 <TableBody>
46 {rows.map((item) => (
47 <TableRow key={item.id}>
48 <TableCell>{date.format(new Date(item.date))}</TableCell>
49 <TableCell>{item.description}</TableCell>
50 <TableCell>
51 {item.status === "pending" ? <Badge tone="warning">Pending</Badge> : "Booked"}
52 </TableCell>
53 <TableCell numeric>{money.format(item.amount)}</TableCell>
54 </TableRow>
55 ))}
56 </TableBody>
57 </Table>
58 );
59 }
60
61 export default function TransactionsPage() {
62 const [view, setView] = useState("all");
63 const [page, setPage] = useState(1);
64
65 const changeView = (next: string) => {
66 setView(next);
67 setPage(1);
68 };
69
70 return (
71 <main className={css({ p: "6" })}>
72 <Stack gap="6">
73 <Stack gap="1">
74 <h1 className={css({ fontSize: "xl", fontWeight: "semibold" })}>Transactions</h1>
75 <p className={css({ color: "fg.muted" })}>
76 {data.account.name} · {data.account.holder}
77 </p>
78 </Stack>
79 <Tabs
80 aria-label="Transactions"
81 value={view}
82 onValueChange={changeView}
83 items={VIEWS.map(({ value, label, keep }) => {
84 const rows = data.transactions.filter(keep);
85 const pages = Math.max(1, Math.ceil(rows.length / data.pageSize));
86 const shown = rows.slice((page - 1) * data.pageSize, page * data.pageSize);
87 return {
88 value,
89 label,
90 content: (
91 <Stack gap="4">
92 <TransactionTable rows={shown} label={label} />
93 <Pagination page={page} count={pages} onPageChange={setPage} />
94 </Stack>
95 ),
96 };
97 })}
98 />
99 </Stack>
100 </main>
101 );
102 }
103