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 3 counted, under 4 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 get_guidelines, read_file, list_components 1,559 136 $0.0002 1 get_component, get_component, get_component, get_component, get_component, list_tokens 4,253 254 $0.0006 2 write_file 9,412 8,440 $0.0052
Output cap The provider's default 4 import {
5 Button,
6 Card,
7 CardBody,
8 CardDescription,
9 CardFooter,
10 CardHeader,
11 CardTitle,
12 FormField,
13 Input,
14 Select,
15 } from "@vektos/ds";
16 import { css } from "styled-system/css";
17 import data from "./fixture.json";
18
19 const currencyFormatter = new Intl.NumberFormat("en-IE", {
20 style: "currency",
21 currency: data.currency,
22 });
23
24 const IBAN_PATTERN = /^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/;
25
26 function formatMoney(cents: number) {
27 return currencyFormatter.format(cents / 100);
28 }
29
30 /** Parses "250", "250.5" or "1250,00" into cents. Returns null when the format is wrong. */
31 function parseAmountToCents(raw: string): number | null {
32 const value = raw.trim().replace(/\s/g, "");
33 if (!/^\d+([.,]\d{1,2})?$/.test(value)) return null;
34 const [whole, fraction = ""] = value.replace(",", ".").split(".");
35 return Number(whole) * 100 + Number(fraction.padEnd(2, "0"));
36 }
37
38 type FieldErrors = {
39 amount?: string;
40 name?: string;
41 iban?: string;
42 };
43
44 type Confirmation = {
45 amount: string;
46 beneficiary: string;
47 };
48
49 export default function SepaTransferPage() {
50 const [accountId, setAccountId] = useState<string>(data.defaultAccountId);
51 const [beneficiary, setBeneficiary] = useState("");
52 const [iban, setIban] = useState("");
53 const [amount, setAmount] = useState("");
54 const [reference, setReference] = useState("");
55 const [submitted, setSubmitted] = useState(false);
56 const [confirmation, setConfirmation] = useState<Confirmation | null>(null);
57
58 const account = data.accounts.find((a) => a.id === accountId) ?? data.accounts[0];
59 const balanceCents = Math.round(account.available * 100);
60
61 /**
62 * Overflowing the balance is reported as soon as the amount is typed.
63 * Missing or malformed values are reported only after a submit attempt.
64 */
65 function validate(showRequired: boolean): FieldErrors {
66 const errors: FieldErrors = {};
67 const cents = parseAmountToCents(amount);
68
69 if (cents !== null && cents > balanceCents) {
70 errors.amount = `The amount is higher than the available balance of ${formatMoney(balanceCents)}.`;
71 } else if (showRequired) {
72 if (amount.trim() === "") {
73 errors.amount = "Enter an amount.";
74 } else if (cents === null) {
75 errors.amount = "Enter an amount with up to two decimals, for example 250.00.";
76 } else if (cents <= 0) {
77 errors.amount = "Enter an amount greater than zero.";
78 }
79 }
80
81 if (showRequired && beneficiary.trim() === "") {
82 errors.name = "Enter the beneficiary name.";
83 }
84
85 const normalizedIban = iban.replace(/\s/g, "").toUpperCase();
86 if (showRequired) {
87 if (normalizedIban === "") {
88 errors.iban = "Enter the beneficiary IBAN.";
89 } else if (!IBAN_PATTERN.test(normalizedIban)) {
90 errors.iban = "Enter a valid IBAN, for example IT60 X054 2811 1010 0000 0123 456.";
91 }
92 }
93
94 return errors;
95 }
96
97 const liveErrors = validate(submitted);
98 const canSubmit = !validate(false).amount;
99
100 function handleSubmit(event: FormEvent<HTMLFormElement>) {
101 event.preventDefault();
102 setSubmitted(true);
103
104 const errors = validate(true);
105 const cents = parseAmountToCents(amount);
106 if (Object.keys(errors).length > 0 || cents === null || cents > balanceCents) {
107 return;
108 }
109
110 setConfirmation({
111 amount: formatMoney(cents),
112 beneficiary: beneficiary.trim(),
113 });
114 }
115
116 function startNewTransfer() {
117 setAccountId(data.defaultAccountId);
118 setBeneficiary("");
119 setIban("");
120 setAmount("");
121 setReference("");
122 setSubmitted(false);
123 setConfirmation(null);
124 }
125
126 return (
127 <main
128 className={[
129 "vektos",
130 css({
131 bg: "bg.canvas",
132 color: "fg.default",
133 minHeight: "100vh",
134 p: "6",
135 }),
136 ].join(" ")}
137 >
138 <div
139 className={css({
140 maxWidth: "md",
141 mx: "auto",
142 display: "flex",
143 flexDirection: "column",
144 gap: "6",
145 })}
146 >
147 <h1 className={css({ fontSize: "lg", fontWeight: "semibold", lineHeight: "tight" })}>
148 Send a SEPA transfer
149 </h1>
150
151 {confirmation ? (
152 <Card>
153 <CardHeader>
154 <CardTitle>Transfer sent</CardTitle>
155 <CardDescription>Your transfer has been submitted.</CardDescription>
156 </CardHeader>
157 <CardBody>
158 <p role="status" className={css({ fontSize: "md" })}>
159 You sent {confirmation.amount} to {confirmation.beneficiary}.
160 </p>
161 </CardBody>
162 <CardFooter>
163 <Button variant="outline" size="sm" onClick={startNewTransfer}>
164 Make another transfer
165 </Button>
166 </CardFooter>
167 </Card>
168 ) : (
169 <form noValidate onSubmit={handleSubmit} aria-label="SEPA transfer">
170 <Card>
171 <CardHeader>
172 <CardTitle>Transfer details</CardTitle>
173 <CardDescription>Payments in EUR to a SEPA account.</CardDescription>
174 </CardHeader>
175 <CardBody>
176 <div
177 className={css({
178 display: "flex",
179 flexDirection: "column",
180 gap: "4",
181 })}
182 >
183 <FormField
184 label="From account"
185 helperText={`Available balance: ${formatMoney(balanceCents)}`}
186 >
187 <Select
188 name="account"
189 value={accountId}
190 onValueChange={(value) => setAccountId(value)}
191 options={data.accounts.map((a) => ({
192 label: `${a.name} (${a.iban})`,
193 value: a.id,
194 }))}
195 />
196 </FormField>
197
198 <FormField label="Beneficiary name" required error={liveErrors.name}>
199 <Input
200 name="beneficiaryName"
201 autoComplete="off"
202 required
203 value={beneficiary}
204 onChange={(e) => setBeneficiary(e.target.value)}
205 />
206 </FormField>
207
208 <FormField label="Beneficiary IBAN" required error={liveErrors.iban}>
209 <Input
210 name="beneficiaryIban"
211 autoComplete="off"
212 spellCheck={false}
213 required
214 value={iban}
215 onChange={(e) => setIban(e.target.value)}
216 />
217 </FormField>
218
219 <FormField
220 label="Amount (EUR)"
221 required
222 error={liveErrors.amount}
223 helperText="For example 250.00."
224 >
225 <Input
226 name="amount"
227 inputMode="decimal"
228 placeholder="0.00"
229 autoComplete="off"
230 required
231 value={amount}
232 onChange={(e) => setAmount(e.target.value)}
233 />
234 </FormField>
235
236 <FormField label="Reference (optional)">
237 <Input
238 name="reference"
239 autoComplete="off"
240 maxLength={140}
241 value={reference}
242 onChange={(e) => setReference(e.target.value)}
243 />
244 </FormField>
245 </div>
246 </CardBody>
247 <CardFooter>
248 <Button type="submit" disabled={!canSubmit}>
249 Send transfer
250 </Button>
251 </CardFooter>
252 </Card>
253 </form>
254 )}
255 </div>
256 </main>
257 );
258 }
259
typecheck/TS2769 0 quality