feat(fp): WP-27 — brief UX layer (undo/redo, standaardbrief, passage search, diff badges)
CI / frontend (push) Failing after 1m15s
CI / storybook-a11y (push) Failing after 4m43s
CI / backend (push) Successful in 1m24s
CI / codeql (csharp) (push) Failing after 2m51s
CI / e2e (push) Failing after 3h4m8s
CI / codeql (javascript-typescript) (push) Failing after 1m30s
CI / api-client-drift (push) Successful in 1m53s

Brief letter-composition UX improvements:
- undo/redo history in the brief store (snapshot stacks, Ctrl/Cmd+Z) + retry-save
- "Standaardbrief invoegen" starter for empty sections; isDefault library passages
  (backend DTO/seed + adapter parse)
- passage-picker client-side search (rich-text textOf helper)
- rejection diff badges on the letter canvas + show/hide changes toggle
  (pure brief-diff domain fns + spec)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-20 14:36:32 +02:00
co-authored by Claude Opus 4.8
parent 950fb5f0b2
commit 5e36d68f11
21 changed files with 3537 additions and 1907 deletions
@@ -139,7 +139,7 @@ public sealed record BriefStatusDto(
public sealed record LibraryPassageDto(
string PassageId, string Scope, string SectionKey, string Label,
RichTextBlockDto Content, int Version, string? Beroep = null);
RichTextBlockDto Content, int Version, string? Beroep = null, bool IsDefault = false);
public sealed record BriefDto(
string BriefId, string Beroep, string TemplateId,
@@ -226,10 +226,12 @@ public static class BriefSeed
{
new("p-aanhef-1", "global", "aanhef", "Standaard aanhef",
Block(T("Geachte heer/mevrouw "), P("naam_zorgverlener"), T(",")), 1),
// The "standaardbrief" kern starter set (IsDefault): one button drops these
// in when the kern is still empty (WP-27).
new("p-kern-1", "global", "kern", "Beoordeling ontvangen",
Block(T("Op "), P("datum"), T(" hebben wij uw aanvraag beoordeeld.")), 1),
Block(T("Op "), P("datum"), T(" hebben wij uw aanvraag beoordeeld.")), 1, IsDefault: true),
new("p-kern-arts", "beroep", "kern", "Toelichting arts",
Block(T("Als arts met BIG-nummer "), P("big_nummer"), T(" delen wij u het volgende mee.")), 1, Beroep: "arts"),
Block(T("Als arts met BIG-nummer "), P("big_nummer"), T(" delen wij u het volgende mee.")), 1, Beroep: "arts", IsDefault: true),
new("p-slot-1", "global", "slot", "Standaard slot",
Block(T("Met vriendelijke groet,")), 1),
// These reference a deprecated / not-fillable field so the linter's
+3
View File
@@ -1720,6 +1720,9 @@
"beroep": {
"type": "string",
"nullable": true
},
"isDefault": {
"type": "boolean"
}
},
"additionalProperties": false