Files
atomic-design-poc/docs/project/prd/0003-brief-v2-demo-script.md
ehoandClaude Sonnet 5 e7e2f070f9 feat(brief): WP-28 demo script, e2e spec, and story gap-fill
Closes phase 6 (Brief v2): a demo script mapping shipped scenarios to
URL+click paths (no Brief v2 PRD ever existed to translate one from —
written directly against the code instead), one e2e spec covering
compose→approve→send and admin republish→drafter-sees-it, and
Storybook state gaps (rejection diff, read-only viewer, org logo,
upload rejection) that prior WPs left uncovered. Flags passage-picker
as dead code, superseded by besluit-panel.

npm run e2e is not verified green in this sandbox — see WP-28's
Deviations section; the pre-existing, unmodified smoke.spec.ts fails
identically here, pointing at a sandbox rendering issue rather than a
regression.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 20:01:51 +02:00

17 KiB
Raw Permalink Blame History

PRD 0003 — Brief v2 demo script

Status: Reference · Date: 2026-07-27 · Context: phase 6 "Brief v2" (WP-23..28)

Cross-references: WP-23 (org-template backend), WP-24 (letter canvas), WP-25 (server-rendered preview), WP-26 (admin org-template editor), WP-27 (brief UX layer — undo/redo, besluit-driven guidance, rejection diff).

No standalone "Brief v2" PRD was ever committed to this repo — WP-23..27 cite one informally ("PRD Brief v2 §N") but it only ever existed as chat context. This script is the demonstrable spec now: every row below is a scenario that actually exists in the shipped code, not a scenario list carried over from an external document.

Phase 6 ships two independent axes over the same letter: content (what the letter says — drafted by the behandelaar, reviewed by an approver) and appearance (how it looks — the org template, edited by an admin). This script walks both, plus the degraded states each page can be in. No new scenario code backs this — it is a map onto toggles that already exist: ?role=drafter|approver|admin, ?scenario=slow|loading|error, and POST /brief/reset.

Before you start

  • Login: /login → BSN 123456782, any password → "Inloggen met DigiD".
  • Toggles are sticky per tab (sessionStorage, WP-33/WP-37): ?role= and ?scenario= in the URL win once per navigation, then persist for the tab. Change role by a full navigation (typing the URL, not an in-app link) — GET /me is fetched once per page load, so an in-app link keeps the stale role. Reset with ?role=drafter&scenario=default, the ⚙ state dev panel, or a fresh tab.
  • What the toggles don't reach: /brief/preview, /admin/org-template/{id}/preview and /brief/reveal-bignummer are hand-written fetch calls (same seam as uploads) and bypass scenarioInterceptor — they can't be forced into a scenario state from the URL. ?scenario=empty substitutes [] for every /api/ response, which breaks any object-returning endpoint (e.g. GET /api/v1/brief parse-fails into the error state) — use it only where a list/empty-state is actually being demonstrated.
  • Reset: the "Opnieuw beginnen (demo)" button on /brief (POST /brief/reset) resets the letter. Org templates have no reset endpoint — section E's walk ends with a restore step (E9) so the demo doesn't drift.

All URLs are relative to http://localhost:4200.

A — Setup

# Scenario URL Clicks
A1 Fresh demo state /brief?role=drafter "Opnieuw beginnen (demo)"

B — Compose (drafter, the besluit-driven workflow)

# Scenario URL Clicks / expect
B1 Case-framed drafting step /brief?role=drafter case header (referentie, naam, BIG-nummer, beroep) + stepper Beoordelen → Brief opstellen → Indienen
B2 Submit blocked on empty kern (same) "Indienen ter beoordeling" disabled + hint "Vul eerst alle verplichte secties en los fouten op."
B3 Guided drafting — positief (same) radio "Positief besluit (toewijzen)" → kern fills with standaardteksten + info alert "…standaardtekst(en) toegevoegd op basis van het besluit…"
B4 Guided drafting — negatief needs a reden (same) radio "Negatief besluit (afwijzen)" → warning "Kies een reden…" → tick "Onvoldoende scholing" → alert flips to info, kern updates
B5 Selection survives reload reload /brief the besluit panel re-seeds from the letter's own kern passages (no separate storage)
B6 Free text + placeholder (same) in a section, "Vrije tekst toevoegen" → type in the rich-text editor → "Veld invoegen" dropdown to insert a placeholder chip
B7 Diagnostics (deprecated / not-fillable) (same) insert oud_kenmerk (deprecated) and specialisme_code (not fillable for this beroep) → diagnostics panel shows a warning and an error
B8 Undo / redo (same) toolbar "Ongedaan maken" / "Opnieuw uitvoeren", or Ctrl+Z / Ctrl+Shift+Z outside a text field
B9 Autosave states (same) "Concept opslaan…" → "Concept opgeslagen" in the toolbar's live region
B10 Autosave failure + retry /brief?role=drafter&scenario=error, then edit "Niet opgeslagen — opnieuw proberen" → "Opnieuw proberen"
B11 PII reveal + step-up + audit /brief?role=drafter "Toon BIG-nummer" → native confirm dialog ("Extra verificatie vereist…") → unmasked; then /beheer/audit?role=admin lists the attempt (no PII in the log)

C — Preview (one rendering, used twice: in-app and as the sent artifact)

# Scenario URL Clicks / expect
C1 In-app preview modal /brief?role=drafter "Voorbeeld" → a <dialog> with the read-only letter canvas
C2 Zoom + sample values + page break (in the dialog) /+/100% zoom controls; "Voorbeeld met testwaarden" swaps placeholder chips for sample text; on a long letter, a "±pagina-einde — afdrukvoorbeeld is leidend" mark appears
C3 Server-rendered HTML document (in the dialog) "Openen als document (PDF)" → new tab, Content-Type: text/html, watermarked as a draft
C4 Sent letter serves its frozen archive after D9 same button on the sent letter → the archived HTML (no watermark), the org-template version pinned at send time even if templates change afterwards

D — Review (approver, segregation-of-duty)

# Scenario URL Clicks / expect
D1 Submit /brief?role=drafter "Indienen ter beoordeling"
D2 Drafter waits (four-eyes) /brief?role=drafter info alert "De brief wacht op beoordeling door een collega." — no approve/reject controls
D3 Read-only viewer (e.g. admin) /brief?role=admin info alert "Alleen-lezen weergave. De behandelaar stelt de brief op." — a pure viewer has no approve/reject/send capability
D4 Approver review /brief?role=approver "Goedkeuren" button + a reject-comment entry, both available
D5 Reject with reason (same) fill the reject textarea → "Afwijzen"
D6 Rejection visible to drafter /brief?role=drafter "Afgewezen:" alert showing the comment
D7 Rework + resubmit (same) edit the kern → "Opnieuw indienen"
D8 Rejection diff /brief?role=approver, same tab session as D5 "Toon wijzigingen" → changed/added blocks get an orange/green badge, plus a "N blok(ken) verwijderd sinds afwijzing." alert if any were deleted. POC limit: the diff snapshot is in-memory and reload-fragile — do D5→D8 without reloading in between
D9 Approve → send /brief?role=approver "Goedkeuren" → "Versturen" → "De brief is verzonden."

E — Appearance (admin, org templates)

# Scenario URL Clicks / expect
E1 Reach the editor /brief/huisstijl?role=admin (or via the Beheer navigation) — sub-org switcher defaults to "BIG-register"
E2 Denied without the capability /brief/huisstijl?role=drafter capabilityGuard redirects straight to /dashboard (no denial page is ever rendered for this route — the guard runs before the component loads)
E3 Edit in place on the letterhead /brief/huisstijl?role=admin type into Organisatienaam / Retouradres / Afsluiting / Naam ondertekenaar / Functie ondertekenaar / Contactgegevens (voettekst) / Juridische voettekst → the canvas re-renders live; "Concept opgeslagen"
E4 Margins (same) Boven / Rechts / Onder / Links (mm) number inputs, clamped 1040mm
E5 Logo upload (same) choose a file via the Logo file input → the letterhead <img> appears on the canvas
E6 Proefbrief (same) "Proefbrief" → new tab: the unpublished draft rendered over the fixed sample letter
E7 Publish with impact confirm (same) "Publiceren" → warning "Dit raakt N nog niet verzonden brieven. Publiceren?" → "Bevestigen" → "Gepubliceerde versie: N+1"
E8 Invalid draft blocks publish (same) clear Organisatienaam or a signer field → "Publiceren" disabled + hint "Vul organisatienaam en ondertekenaar in; marges tussen 10 en 40 mm."
E9 Rollback (also: restore after the walk) (same) Versiegeschiedenis → "Terugzetten in concept" on an old version copies it into the draft; "Publiceren" again makes it live — use this to put a demo sub-org back the way you found it
E10 Two axes, one render /brief?role=drafter after E7 "Voorbeeld" → the new letterhead over the same letter body that section B composed
E11 Sub-org isolation /brief/huisstijl?role=admin switch the sub-org dropdown to "CIBG Vakbekwaamheid" → its own values, untouched by edits made to "BIG-register"; Proefbrief on each proves it
E12 Version pinning send a letter (D9), then E7, then C4 the already-sent letter's archived HTML is unaffected by a later template publish

F — Degraded states (?scenario= on data pages)

# Scenario URL Expect
F1 Slow /brief?role=drafter&scenario=slow delay-gated skeleton (~250ms gate, then a multi-second response)
F2 Never resolves /brief?role=drafter&scenario=loading skeleton stays, aria-busy="true"
F3 Load error + retry /brief?role=drafter&scenario=error error alert "De brief kon niet worden geladen." + "Opnieuw proberen"
F4 Admin editor when /me fails /brief/huisstijl?role=admin&scenario=error capabilityGuard awaits /me, sees it fail, denies by default → redirects to /dashboard (not a denial alert on /brief/huisstijl itself — the guard never lets the page render)

Out of scope

New scenario-interceptor cases, a scenario-switcher UI, screenshots/video (per WP-28's Decisions).