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
@@ -1712,6 +1712,7 @@ export interface LibraryPassageDto {
content?: RichTextBlockDto;
version?: number;
beroep?: string | undefined;
isDefault?: boolean;
}
export interface ManualDiplomaPolicyDto {
+9
View File
@@ -49,6 +49,15 @@ export function deepCopyBlock(block: RichTextBlock): RichTextBlock {
return structuredClone(block) as RichTextBlock;
}
/** All visible text of a block as one lowercased string — for client-side search over
passages. Placeholders contribute their key so "naam" matches a `naam_zorgverlener` chip. */
export function textOf(block: RichTextBlock): string {
return block.paragraphs
.flatMap((p) => p.nodes.map((n) => (n.type === 'text' ? n.text : n.type === 'placeholder' ? n.key : '')))
.join(' ')
.toLowerCase();
}
/** Every placeholder key used in a block, in document order (duplicates kept — the
caller dedupes when it wants a set). */
export function placeholderKeysIn(block: RichTextBlock): string[] {