feat(fp): WP-25 — server-rendered letter HTML preview

Adds LetterHtml.Render, a pure composer mirroring the FE letter canvas'
class vocabulary, behind two ExcludeFromDescription()'d endpoints
(GET /brief/preview, GET /admin/org-template/{subOrgId}/preview).
Auto-resolvable placeholders pull from seed/case data; unresolved
manual ones render as "[NOG IN TE VULLEN: label]". A sent brief
archives its composed HTML (BriefEntity.ArchivedHtml) so a later
org-template republish never changes it. FE gets a hand-written fetch
adapter (text/html, not JSON) and a "Voorbeeld" button that opens the
preview in a new tab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 12:56:36 +02:00
parent c07a33ee3e
commit 1bb9383344
17 changed files with 1020 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
# WP-25 — Server-rendered letter preview (HTML; PDF seam deferred)
Status: todo
Status: done
Phase: 6 — Brief v2 (edit-on-the-letter, org templates, server-rendered preview)
## Why
@@ -63,13 +63,13 @@ same composition is archived with the brief, making sent letters immutable.
## Acceptance criteria
- [ ] Preview opens the composed print-ready letter in a new tab; browser print
- [x] Preview opens the composed print-ready letter in a new tab; browser print
shows correct margins via `@page`.
- [ ] Unresolved manual placeholders render `[NOG IN TE VULLEN: …]`; preview works
- [x] Unresolved manual placeholders render `[NOG IN TE VULLEN: …]`; preview works
despite lint errors (only send blocks).
- [ ] A sent brief serves its archived HTML unchanged after an org-template republish.
- [ ] Golden + parity tests green without any browser installed.
- [ ] `swagger.json` unchanged by the two endpoints (drift check green).
- [x] A sent brief serves its archived HTML unchanged after an org-template republish.
- [x] Golden + parity tests green without any browser installed.
- [x] `swagger.json` unchanged by the two endpoints (drift check green).
## Verification