Runs / transfer-form Batch Runs from different design-system versions should not be compared.
The page A screenshot in each theme, as the benchmark took it.
page.tsx As the agent left it; marked lines broke a rule.
Violations 5 counted, under 2 rules. “Not counted” marks a compiler error that restates a design-system violation, “Ignored” one the agent silenced with a comment.
Steps One row per model call, with the tools it asked for. An agent that reads the guidelines before writing shows it here; what a checker or submit_view answered is under its call.
Step Tools Tokens in Tokens out Cost 0 list_files, read_file 148 65 $0.0001 1 docs-list 424 76 $0.0002 2 docs-show, docs-show 897 91 $0.0004 3
e02ef3974528
Output cap The provider's default 4 import type { FormEvent } from "react";
5 import {
6 Badge,
7 Button,
8 Card,
9 CardBody,
10 CardDescription,
11 CardFooter,
12 CardHeader,
13 CardTitle,
14 FormField,
15 Input,
16 Select,
17 } from "@vektos/ds";
18 import { css } from "styled-system/css";
19 import fixture from "./fixture.json";
20
21 type Account = {
22 id: string;
23 name: string;
24 iban: string;
25 available: number;
26 };
27
28 type FieldName = "name" | "iban" | "amount";
29
30 type Confirmation = {
31 amount: number;
32 beneficiaryName: string;
33 beneficiaryIban: string;
34 reference: string;
35 accountName: string;
36 accountIban: string;
37 };
38
39 const accounts: Account[] = fixture.accounts;
40 const currency: string = fixture.currency;
41
42 const emptyAccount: Account = {
43 id: "",
44 name: "No account",
45 iban: "—",
46 available: 0,
47 };
48
49 const money = new Intl.NumberFormat("en-IE", {
50 style: "currency",
51 currency,
52 currencyDisplay: "code",
53 });
54
55 function formatMoney(value: number): string {
56 return money.format(value);
57 }
58
59 function roundToCents(value: number): number {
60 return Math.round(value * 100) / 100;
61 }
62
63 /** Accepts "120", "120.50" and "120,50"; returns null when the text is not a number. */
64 function parseAmount(raw: string): number | null {
65 const cleaned = raw
66 .replace(/[\s\u00A0]/g, "")
67 .replace(/€/g, "")
68 .replace(/,/g, ".");
69
70 if (cleaned === "" || !/^\d*\.?\d*$/.test(cleaned)) {
71 return null;
72 }
73
74 const value = Number(cleaned);
75
76 return Number.isFinite(value) ? roundToCents(value) : null;
77 }
78
79 function normalizeIban(raw: string): string {
80 return raw.replace(/[\s-]/g, "").toUpperCase();
81 }
82
83 const ibanPattern = /^[A-Z]{2}\d{2}[A-Z0-9]{10,30}$/;
84
85 export default function SepaTransferPage() {
86 const [accountId, setAccountId] = useState<string>(fixture.defaultAccountId);
87 const [beneficiaryName, setBeneficiaryName] = useState("");
88 const [beneficiaryIban, setBeneficiaryIban] = useState("");
89 const [amount, setAmount] = useState("");
90 const [reference, setReference] = useState("");
91 const [touched, setTouched] = useState<Record<FieldName, boolean>>({
92 name: false,
93 iban: false,
94 amount: false,
95 });
96 const [confirmation, setConfirmation] = useState<Confirmation | null>(null);
97
98 const account =
99 accounts.find((item) => item.id === accountId) ?? emptyAccount;
100
101 const trimmedAmount = amount.trim();
102 const parsedAmount = parseAmount(trimmedAmount);
103 const normalizedIban = normalizeIban(beneficiaryIban);
104
105 const amountOverBalance =
106 parsedAmount !== null && parsedAmount > account.available
107 ? `This is more than the ${formatMoney(
108 account.available,
109 )} available on this account.`
110 : undefined;
111
112 const amountFormatIssue =
113 trimmedAmount === ""
114 ? "Enter the amount you want to send."
115 : parsedAmount === null
116 ? "Enter a valid amount, for example 120.50."
117 : parsedAmount <= 0
118 ? "Enter an amount greater than zero."
119 : undefined;
120
121 // The over-balance problem is reported as soon as the amount is typed;
122 // the other amount problems wait until the field has been visited.
123 const amountError = touched.amount
124 ? (amountFormatIssue ?? amountOverBalance)
125 : amountOverBalance;
126
127 const nameIssue =
128 beneficiaryName.trim() === "" ? "Enter the beneficiary name." : undefined;
129
130 const ibanIssue =
131 normalizedIban === ""
132 ? "Enter the beneficiary IBAN."
133 : ibanPattern.test(normalizedIban)
134 ? undefined
135 : "Enter a valid IBAN, for example IT60 X054 2811 1010 0000 0123 456.";
136
137 const canSubmit =
138 amountFormatIssue === undefined &&
139 amountOverBalance === undefined &&
140 nameIssue === undefined &&
141 ibanIssue === undefined;
142
143 function touch(field: FieldName) {
144 setTouched((current) =>
145 current[field] ? current : { ...current, [field]: true },
146 );
147 }
148
149 function handleSubmit(event: FormEvent<HTMLFormElement>) {
150 event.preventDefault();
151 setTouched({ name: true, iban: true, amount: true });
152
153 if (!canSubmit || parsedAmount === null) {
154 return;
155 }
156
157 setConfirmation({
158 amount: parsedAmount,
159 beneficiaryName: beneficiaryName.trim(),
160 beneficiaryIban: normalizedIban,
161 reference: reference.trim(),
162 accountName: account.name,
163 accountIban: account.iban,
164 });
165 }
166
167 function startOver() {
168 setConfirmation(null);
169 setBeneficiaryName("");
170 setBeneficiaryIban("");
171 setAmount("");
172 setReference("");
173 setTouched({ name: false, iban: false, amount: false });
174 }
175
176 if (confirmation) {
177 return (
178 <div className="vektos">
179 <main className={css({ maxW: "xl", mx: "auto", p: "6" })}>
180 <Card>
181 <CardHeader>
182 <CardTitle asChild>
183 <h1>Transfer sent</h1>
184 </CardTitle>
185 <CardDescription>
186 {formatMoney(confirmation.amount)} to{" "}
187 {confirmation.beneficiaryName}.
188 </CardDescription>
189 </CardHeader>
190 <CardBody>
191 <div
192 role="status"
193 className={css({ display: "grid", gap: "4" })}
194 >
195 <Badge tone="success">Sent</Badge>
196 <dl className={css({ display: "grid", gap: "3", m: "0" })}>
197 <Detail
198 label="Amount"
199 value={formatMoney(confirmation.amount)}
200 />
201 <Detail
202 label="Beneficiary"
203 value={confirmation.beneficiaryName}
204 />
205 <Detail
206 label="Beneficiary IBAN"
207 value={confirmation.beneficiaryIban}
208 />
209 {confirmation.reference === "" ? null : (
210 <Detail
211 label="Reference"
212 value={confirmation.reference}
213 />
214 )}
215 <Detail
216 label="Paid from"
217 value={`${confirmation.accountName} · ${confirmation.accountIban}`}
218 />
219 </dl>
220 </div>
221 </CardBody>
222 <CardFooter>
223 <Button variant="outline" onClick={startOver}>
224 Send another transfer
225 </Button>
226 </CardFooter>
227 </Card>
228 </main>
229 </div>
230 );
231 }
232
233 return (
234 <div className="vektos">
235 <main className={css({ maxW: "xl", mx: "auto", p: "6" })}>
236 <Card>
237 <form onSubmit={handleSubmit} noValidate>
238 <CardHeader>
239 <CardTitle asChild>
240 <h1>Send a SEPA transfer</h1>
241 </CardTitle>
242 <CardDescription>
243 Choose the account to pay from, then enter the beneficiary and
244 the amount.
245 </CardDescription>
246 </CardHeader>
247 <CardBody>
248 <div className={css({ display: "grid", gap: "5" })}>
249 <FormField
250 label="Pay from"
251 helperText={account.iban}
252 required
253 >
254 <Select
255 name="accountId"
256 value={account.id}
257 onValueChange={setAccountId}
258 options={accounts.map((item) => ({
259 label: item.name,
260 value: item.id,
261 }))}
262 />
263 </FormField>
264
265 <div className={css({ display: "grid", gap: "1" })}>
266 <p
267 className={css({ m: "0", fontSize: "sm", color: "fg.muted" })}
268 >
269 Available balance
270 </p>
271 <p
272 className={css({
273 m: "0",
274 fontSize: "xl",
275 fontWeight: "semibold",
276 fontVariantNumeric: "tabular-nums",
277 })}
278 >
279 {formatMoney(account.available)}
280 </p>
281 </div>
282
283 <FormField
284 label="Beneficiary name"
285 helperText="The person or company receiving the money."
286 error={touched.name ? nameIssue : undefined}
287 required
288 >
289 <Input
290 name="beneficiaryName"
291 value={beneficiaryName}
292 onChange={(event) =>
293 setBeneficiaryName(event.target.value)
294 }
295 onBlur={() => touch("name")}
296 placeholder="Mario Rossi"
297 autoComplete="off"
298 />
299 </FormField>
300
301 <FormField
302 label="Beneficiary IBAN"
303 helperText="Spaces are fine, for example IT60 X054 2811 1010 0000 0123 456."
304 error={touched.iban ? ibanIssue : undefined}
305 required
306 >
307 <Input
308 name="beneficiaryIban"
309 value={beneficiaryIban}
310 onChange={(event) => setBeneficiaryIban(event.target.value)}
311 onBlur={() => touch("iban")}
312 placeholder="IT60 X054 2811 1010 0000 0123 456"
313 autoComplete="off"
314 spellCheck={false}
315 />
316 </FormField>
317
318 <FormField
319 label="Amount"
320 helperText={`In ${currency}. At most ${formatMoney(
321 account.available,
322 )} on this account.`}
323 error={amountError}
324 required
325 >
326 <Input
327 name="amount"
328 value={amount}
329 onChange={(event) => setAmount(event.target.value)}
330 onBlur={() => touch("amount")}
331 placeholder="0.00"
332 inputMode="decimal"
333 autoComplete="off"
334 />
335 </FormField>
336
337 <FormField
338 label="Reference"
339 helperText="Optional. Up to 140 characters, shown to the beneficiary."
340 >
341 <Input
342 name="reference"
343 value={reference}
344 onChange={(event) => setReference(event.target.value)}
345 maxLength={140}
346 placeholder="Invoice 2026-114"
347 autoComplete="off"
348 />
349 </FormField>
350 </div>
351 </CardBody>
352 <CardFooter>
353 <Button type="submit" disabled={!canSubmit}>
354 Send transfer
355 </Button>
356 </CardFooter>
357 </form>
358 </Card>
359 </main>
360 </div>
361 );
362 }
363
364 function Detail({ label, value }: { label: string; value: string }) {
365 return (
366 <div>
367 <dt className={css({ fontSize: "sm", color: "fg.muted" })}>{label}</dt>
368 <dd
369 className={css({
370 m: "0",
371 fontSize: "md",
372 fontWeight: "semibold",
373 wordBreak: "break-word",
374 })}
375 >
376 {value}
377 </dd>
378 </div>
379 );
380 }
381
4 docs-show, docs-show 1,508 68 $0.0006
6 docs-show, read_file 1,265 407 $0.0009
7 write_file 1,463 12,607 $0.0156
8 stories-changed 12,811 34 $0.0040