One letter surface for every role: LetterCanvasComponent renders the org template's letterhead/signature/footer around the case-type sections, with editableRegions content|template|none. public/letter.css is the FE⇄BE rendering contract (WP-25 inlines it verbatim). letter-preview deleted — its read-only rendering absorbed into 'none' mode. brief.machine.ts byte-identical; orgTemplate parses at the adapter boundary and lives beside the machine in BriefStore. Also fixes passage-picker multi-select (checkboxes all shared id="undefined", so labels only toggled the first box) and keeps the ±page-break marks from drawing through canvas content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.2 KiB
WP-24 — Letter canvas (edit on the letter)
Status: done Phase: 6 — Brief v2 (edit-on-the-letter, org templates, server-rendered preview)
Why
The drafter should compose on the letter — letterhead above, footer/signature
below, content blocks edited in place — instead of in an abstract form next to a
separate preview. PRD Brief v2 §4. This is a presentation rebuild only: the
domain model, brief.machine.ts, and every BriefMsg stay byte-identical.
Read first
- PRD Brief v2 §2b (fidelity note), §4, §10; the sample
voorbeeldbrief-inschrijving.pdf src/app/brief/ui/letter-composer/letter-composer.component.ts(thecanEditpivot)src/app/brief/ui/letter-preview/letter-preview.component.ts(rendering that migrates in)docs/backlog/WP-23-org-template-backend.md(theorgTemplateonBriefViewDto)
Decisions (pre-made, don't relitigate)
- One stylesheet is the FE⇄BE rendering contract:
public/letter.css— class vocabulary.letter,.letter__letterhead,.letter__body,.letter__signature,.letter__footer,.letter__page-break; margins as--letter-margin-*custom props;@page/print rules. Loaded via<link>inindex.html(app + Storybook). WP-25's backend renderer inlines the same file; its parity test is the fence. LetterCanvasComponent(new organism,Domein/Brief/Letter Canvas) witheditableRegions: 'content' | 'template' | 'none'— one component serves drafter composing, approver review (and in WP-26, the admin editor). Letter typography on the canvas is the letter's, not the portal UI's — but still token-bridged.'content'mode hosts the existingletter-section/letter-blockcomponents unchanged; letterhead/footer/signature render read-only with a subtle tint and a first-use caption ("komt uit de huisstijl van de organisatie").'none'mode absorbsletter-preview's rendering (paragraph grouping, placeholder chips, sample-values toggle);ui/letter-preview/is then deleted — the PRD explicitly supersedes it; no third rendering is maintained.- Page-break indicator is approximate by design: a dashed line per A4-content interval with the caption "±pagina-einde — afdrukvoorbeeld is leidend" (PRD §2b honesty requirement).
brief.machine.tsandBriefMsgare untouched —git diffmust prove it.
Files
public/letter.css(new),src/index.html(link)src/app/brief/domain/org-template.ts(new, pure) —OrgTemplatetypesrc/app/brief/infrastructure/brief.adapter.ts(+spec) — parseorgTemplatesrc/app/brief/ui/letter-canvas/*(new: component + stories)src/app/brief/ui/letter-composer/letter-composer.component.ts(pivot swap) + storiessrc/app/brief/ui/brief.page.ts(pass orgTemplate through)- delete
src/app/brief/ui/letter-preview/*
Steps
letter.cssfrom the sample PDF's geometry (A4 proportions, letterhead, address window + reference block, footer rule, signature).OrgTemplatedomain type +parseOrgTemplateboundary in the adapter (+spec).- Canvas organism: three modes, zoom input, page-break indicator.
- Migrate
letter-previewrendering into'none'mode; delete the component, fold its stories into the canvas stories. - Swap the composer's
@if (canEdit())pivot for<app-letter-canvas [editableRegions]="canEdit() ? 'content' : 'none'">. - Stories: three modes + zoom + page-break, all axe-gated.
Acceptance criteria
- Drafter edits blocks in place on the letter surface; passage picker and diagnostics click-to-locate still work on the canvas.
- Approver sees the identical surface read-only with the action bar.
- Letterhead/footer/signature come from
orgTemplateand are visibly non-editable. git diffshows zero changes inbrief.machine.ts/brief.tsMsg surface.letter-previewis gone; no story regression (test-storybook:cigreen).- Full GREEN + e2e smoke.
Field notes (landed alongside): the CIBG huisstijl styles bare <header>/<footer>
elements (robijn background), so the canvas regions are <div>s — the letter surface
must stay letter.css-only. The passage picker's checkboxes now get unique
checkboxIds (all previously resolved to id="undefined", so labels toggled only
the first box — multi-select was broken) and an opaque background; .letter__body
stacks above the page-break marks so the dashed line never draws through content,
while the "±pagina-einde" caption floats above everything for legibility.
Verification
GREEN one-liner; npm run e2e (brief flow); manual: ?role=drafter compose on
canvas, ?role=approver review; ?scenario=slow|error still degrade gracefully
through <app-async>.
Out of scope
Server-rendered preview + [NOG IN TE VULLEN] resolution (WP-25); admin 'template'
mode wiring beyond the input existing (WP-26 gives it a consumer); zoom controls
polish + standaardbrief + diff badges (WP-27).
Risks
The canvas duplicates the letter markup that WP-25's backend renderer will emit —
acceptable only because letter.css is shared and WP-25 adds the class-parity
test; until WP-25 lands, the canvas is the sole consumer, so no drift is possible.
Deleting letter-preview breaks any deep import of it — repo-wide grep before delete.