# WP-26 — Admin org-template editor Status: done Phase: 6 — Brief v2 (edit-on-the-letter, org templates, server-rendered preview) ## Why The org template (WP-23) needs its editor: an admin edits, per sub-organization, the letter's appearance **in place on the same canvas** the drafter composes on — the mirror image (`editableRegions='template'`: letterhead/footer/signature editable, content a read-only sample). PRD Brief v2 §5, §7h. ## Read first - PRD Brief v2 §5, §7h; `docs/backlog/WP-23/24/25` (endpoints, canvas, proefbrief) - `src/app/shared/application/access.store.ts` (`can('orgtemplate:edit')`) - `.claude/skills/form-machine` — the house form idiom this editor follows - `src/app/shared/ui/upload/single-upload` (logo upload reuse) ## Decisions (pre-made, don't relitigate) - **Lives in the `brief` context** (route `/brief/huisstijl`, lazy) — same bounded capability, no new context. Gated by `AccessStore.can('orgtemplate:edit')` with a denial alert (deny-by-default); no new route guard. - **House form-machine idiom**: `org-template.machine.ts` (`OrgTemplateState`/`OrgTemplateMsg`, pure `reduce` + spec) — draft fields are form state, not brief state. Store (`org-template.store.ts`, root singleton) does debounced draft save (mirror `BriefStore.scheduleSave`), publish, rollback. - **Publish shows impact first**: confirmation displays the WP-23 impact count ("Dit raakt N nog niet verzonden brieven") before the POST. - **Version history is a list, rollback copies into draft** (WP-23 semantics) — no side-by-side rendered diff (deferred; field-level history list is enough here). - **Logo upload reuses `single-upload`** against the `org-logo` category; the canvas shows ``. - **Proefbrief** = the WP-25 admin preview endpoint; just a button. - Margins are bounded number inputs (server re-validates, WP-23). ## Files - `src/app/brief/domain/org-template.machine.ts` (+spec) - `src/app/brief/application/org-template.store.ts` - `src/app/brief/infrastructure/org-template.adapter.ts` (+spec, `parseOrgTemplateAdminView`) - `src/app/brief/ui/org-template-editor/*` (organism + stories) - `src/app/brief/ui/org-template.page.ts` - `src/app/app.routes.ts` (route `brief/huisstijl`) ## Steps 1. Machine (fields, `FieldEdited`/`MarginEdited`/`LogoSet`/`DraftLoaded`/save-publish outcome Msgs) + spec. 2. Adapter (generated client CRUD + parse boundary) + spec. 3. Store: load (sub-org list + selected), debounced save, publish (impact confirm), rollback. 4. Editor UI: sub-org switcher, canvas in `'template'` mode with inline-editable regions, margins inputs, logo upload, version history + rollback, proefbrief button, publish bar showing live version + published-at. 5. Route + capability gate + stories (axe). ## Acceptance criteria - [x] `?role=admin` can switch sub-orgs, edit all template fields in place on the canvas, and see the canvas update live. - [x] Draft saves are debounced; publish asks for confirmation showing the impact count; after publish the drafter's canvas (WP-24) reflects it on reload. - [x] Version history lists published versions (who is faked, when is real); rollback copies an old version into the draft. - [x] Non-admin on `/brief/huisstijl` sees the denial alert; API would 403 anyway. - [x] Logo upload validates type/size client-side (existing `rejectReason`) and renders on the canvas after upload. - [x] Machine spec covers field edits, dirty tracking, publish/rollback outcomes. - [x] Full GREEN. ## Deviations / notes (as built) - **`check:tokens` was already red on `main`** (WP-24's canvas landed `var(--rhc-*, #hex)` fallbacks + an `rgb()` paper shadow, and WP-25 committed over it). Fixed here to end GREEN: dropped the redundant hex fallbacks (the token bridge defines every one) and marked the paper drop-shadow `token-ok`; also fixed a pre-existing `passage-picker` `var(--rhc-color-wit, #fff)` hit. - **Canvas edit-in-place**: `editableRegions='template'` now renders the seven org-identity text fields as inline ``/`