- Toegankelijke
<Modal>(native dialog, focus-trap + Escape) → publiceer-modal +window.promptvervangen - Data-bug hardgecodeerde datum
2026-05-15→ afgeleid uit de rapportagemaand - Responsive werkblad (sidebar stapelt onder breakpoint), uren-pattern verankerd, status-badge, breadcrumb met klantnaam, a11y-headings + aria-live, loading/error-boundaries, viewport-meta, robots noindex, CLS-reservering
- Valse blocker "leeg op mobiel" geschrapt na verificatie met verse ingelogde screenshots
Scores per view
Hoe dieper de flow in, hoe lager (uitgangssituatie)
Rapportage-overzicht
Rapportage-editor
Uren-werkblad
Executive summary
Fundament staat, afwerking niet
De drie views vormen samen de admin-rapportageflow voor één klant (De Waal): de rapportagelijst, een specifieke maandrapportage, en het onderliggende uren-werkblad. De set is functioneel maar nog niet productierijp. Scores dalen consistent naarmate je dieper de flow ingaat (6,0 → 5,5 → 5,0), wat erop wijst dat de meer interactie-zware schermen (editor, werkblad) de meeste schuld dragen op toegankelijkheid en robuustheid.
De rode draad: het fundament (navigatie, semantiek, happy-path-rendering, schone DOM, geen console-errors) staat, maar de set faalt structureel op drie assen die WCAG-conformiteit en mobiel gebruik blokkeren: (1) toegankelijkheid van interactieve elementen (focus-traps, labels, screen-reader-feedback), (2) mobiele weergave, en (3) het gebruik van native browser-dialogen (window.prompt) voor destructieve acties.
Geen enkele view is "af". Bruikbaar voor een interne pilot, niet leverbaar als de klant of een toetsende partij meekijkt. De grootste hefboom zit in gedeelde componenten (dialoog-primitive, breadcrumb, formulier-laag), niet in losse patches.
Prioriteiten
Wat eerst, set-breed
- Responsive werkblad fixenHet werkblad heeft een vaste 256px-sidebar zonder media-query, onbruikbaar onder ~700px. Een breakpoint die sidebar en main stapelt. (Het overzicht rendert op mobiel wél correct — de oorspronkelijke "leeg op mobiel"-blocker was een screenshot-artefact en is geschrapt.)
- Eén gedeelde dialoog-primitive met focus-trap + focus-teruggaveVervangt
window.prompt()(editor + werkblad "Uitsluiten") én repareert de publiceer-modal en bevestigingsdialoog zonder focus-management (WCAG 2.4.3 / 2.1.2). Hoogste hefboom: één component, meerdere plekken beter. - Formulier-toegankelijkheid set-breed
<label>op alle inputs (Omschrijving/Reden), uren-pattern verankeren (^…$) met inline foutfeedback i.p.v. een server-roundtrip. Eén formulier-hygiene-pass over alle views. - Hardgecodeerde datum fixen (data-bug)De handmatige werkblad-regel krijgt standaard
2026-05-15mee — buiten de rapportageperiode. Afleiden uitreportLabel. Stille data-correctheidsbug. - Breadcrumb consoliderenKlantnaam altijd in het pad (verdwijnt nu op de editor), maand-formattering consistent ("juni" vs "jun"). Eén gedeelde breadcrumb-component.
- A11y-afronding
aria-livevoor pending-states, sectiekoppen als headings i.p.v.<p>, skip-link,loading.tsx/error.tsxboundaries,robots: noindexop admin, viewport-meta in root layout.
Cross-view consistentie
Breadcrumb loopt uiteen
Wat overal identiek hoort te zijn (je zit binnen dezelfde klantcontext), is het niet:
| View | Breadcrumb nu | Probleem |
|---|---|---|
| Overzicht | Klanten / De Waal / Rapportages | Referentie |
| Editor | Klanten / Rapportages / juni 2026 | Klantnaam "De Waal" verdwenen uit het pad |
| Werkblad | Klanten / De Waal / Rapportages / jun 2026 | Maand afgekort ("jun" i.p.v. "juni"), inconsistent met editor |
View ① · webapp (admin)
Rapportage-overzicht
UX/a11y 6 · Frontend/perf 6/admin/clients/[id]/rapportages — maand/jaar-formulier ("concept openen of aanmaken") + lijst bestaande rapportages.
Top correctness-issues
| Ernst | Issue | Aanbeveling |
|---|---|---|
| Hoog | Geen error.tsx boundary op de rapportages-route — bij een falende query crasht de pagina naar de generieke Next-foutpagina. | Client error.tsx met statusText + "Probeer opnieuw". Ook voor [reportId]. |
| Hoog | ?fout=maand redirect levert geen inline-fout (WCAG 3.3.1) — gebruiker wordt stil teruggestuurd. | Searchparam uitlezen, foutbanner/Field-error tonen. |
| Hoog | Geen skip-link naar hoofdinhoud in de AppShell (WCAG 2.4.1). | Visually-hidden skip-link als eerste element, focus naar #main-content. |
| Mid | Admin-routes missen robots: noindex — bij publieke bereikbaarheid indexeerbaar. | export const metadata = { robots: { index:false } } in (authed)/layout.tsx. |
| Mid | Geen loading.tsx-fallback; statuslabels zijn rauwe DB-waarden zonder badge. | Skeleton-loading + status naar badge met statuskleur + aria-label. |
Screenshots vers · ingelogd


View ② · webapp (admin)
Rapportage-editor
UX/a11y 5 · Frontend/perf 6/admin/clients/[id]/rapportages/[reportId] — metrics, uren-velden, narrative, opslaan/publiceren.
Top correctness-issues
| Ernst | Issue | Aanbeveling |
|---|---|---|
| Kritiek | Publiceren-modal zonder focus-trap (WCAG 2.4.3 / 2.1.2) — toetsenbordgebruikers tabben achter de backdrop. | Focus-trap bij mount, restore bij sluiten; inert op de rest. |
| Hoog | window.prompt() voor destructieve "Uitsluiten" — niet toegankelijk, niet stylebaar (WCAG 3.3.4). | Inline mini-modal met gelabeld veld + Bevestigen/Annuleren. |
| Hoog | Inputs "Omschrijving"/"Reden" zonder <label>, alleen placeholder (WCAG 1.3.1). | aria-label of bestaand <Field>-component hergebruiken. |
| Hoog | Uren-pattern niet verankerd: \d+(\.\d{1,2})? accepteert 12abc in browser, faalt server-side. | pattern="^\d+(\.\d{1,2})?$" + required op beide inputs. |
| Hoog | Viewport-meta ontbreekt in root layout — geen correcte mobiele schaling. | export const viewport = { width:'device-width', initialScale:1 }. |
Screenshots vers · ingelogd


View ③ · webapp (admin)
Uren-werkblad
UX/a11y 5 · Frontend/perf 5/admin/clients/[id]/rapportages/[reportId]/werkblad — freeze, regels, type-toggles, handmatige regels, budget, publiceer-modal. Het rijkste scherm van de set.
Top correctness-issues
| Ernst | Issue | Aanbeveling |
|---|---|---|
| Kritiek | Geen responsive layout: vaste 256px-sidebar zonder media-query, onbruikbaar onder ~700px. | @media (max-width:720px) → sidebar/main stapelen, width:100%. |
| Kritiek | window.prompt() voor uitsluitingsreden — niet toegankelijk, blokkeert event loop, faalt in WebView. | Inline-form of bestaande ConfirmProvider/confirm-dialog. |
| Hoog | Hardgecodeerde datum 2026-05-15 in ManualForm — handmatige regel buiten de rapportageperiode. Data-bug. | Afleiden uit reportLabel: ${y}-${m}-01. |
| Hoog | Publiceer-dialoog mist focus-trap, Escape sluit niet, focus keert niet terug (WCAG 2.1.2). | Bestaand confirm-dialog.tsx-systeem hergebruiken. |
| Hoog | Sidebar-sectiekoppen zijn <p>; geen aria-live voor pending-states. | Koppen naar <h2>/<h3> + aria-live="polite"-regio. |
Screenshots vers · ingelogd


Proces-notitie
Wat de volgende run beter moet doen
De code-review-laag was sterk (echte issues met file:line-bewijs), maar twee dingen liepen mis en horen in de workflow gefixt te worden voordat dit een herhaalbaar instrument is:
- Visuele capture niet ingelogd. De screenshot-agent raakte de auth-gate en legde het login-scherm vast. De visuele review moet met een geldige sessie draaien.
- E-commerce-checklist op een admin-tool. De visuele agent gebruikte buy-box/kenteken/winkelwagen-criteria en de ClickUp-list heette "Webshop" (inconsistent met "Klant-dashboard" op view 3). De rolprompts vallen nog terug op Oliereus-defaults ondanks de generieke aanroep.