Story 7 van PBI "Alle dialogen conform docs/patterns/dialog.md". - lib/schemas/question-answer.ts — gedeeld zod-schema + ANSWER_MAX_CHARS constant - actions/questions.ts gebruikt het gedeelde schema - AnswerModal: entityDialog* layout-classes, useDirtyCloseGuard, useDialogSubmitShortcut, DemoTooltip rond submit + multiple-choice knoppen - docs/specs/dialogs/answer-modal.md — entity-profile Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.8 KiB
| title | status | audience | language | last_updated | ||
|---|---|---|---|---|---|---|
| AnswerModal Profiel | active |
|
nl | 2026-05-04 |
AnswerModal Profiel
Volgt
docs/patterns/dialog.md. Beschrijft alleen de Q&A-specifieke afwijkingen.
Doel
Een Claude-agent vraagt tijdens een lopende job een verduidelijking aan de gebruiker. De vraag verschijnt als notification (bell + SSE event). Klik op de notification opent deze dialog waarin de gebruiker antwoordt — vrij tekst (max ANSWER_MAX_CHARS) of een keuze uit options als die meegegeven is.
Velden
| Veld | Type | Mode | Validatie |
|---|---|---|---|
answer |
string | both | min 1, max ANSWER_MAX_CHARS (4000), trim |
questionId komt uit de prop question.id, niet uit het formulier.
Schema
lib/schemas/question-answer.ts:
answerQuestionSchema— gedeeld door form +answerQuestionactionANSWER_MAX_CHARSconstant — gebruikt door textarea + char-counter
URL- of state-pattern
- Gekozen: state-based —
question: NotificationQuestion | null-prop uitnotifications-sheet.
Server action
answerQuestion(questionId, answer)inactions/questions.ts- Result-shape:
{ ok: true } | { ok: false; error: string } - Demo-policy:
session.isDemo-check in actie blokkeert demo-writes (laag 2). Laag 3 wordt verzorgd door<DemoTooltip>rond submit-knop en disabled-state op de textarea. - Atomic transition met
updateManyvoorkomt double-submit races.
Layout
Gebruikt entityDialogContentClasses (§4 spec). Body bevat naast de textarea ook de gestelde vraag (read-only block) en een link naar de bijbehorende sprint. Geen klassieke form-tag — de Textarea is een controlled component.
Speciale gedragingen
Multiple-choice mode
Als question.options niet leeg is, wordt de textarea vervangen door een lijst van knoppen. Klikken op een knop submit direct met die waarde. De submit-knop in de footer wordt dan verborgen (alleen Annuleren blijft).
Optimistic remove
Na succesvolle submit wordt de vraag direct uit useNotificationsStore verwijderd. De SSE-event komt later met dezelfde verwijdering — voorkomt extra render.
Dirty-tracking
Single-field form: dirty = answer.trim().length > 0. Esc/backdrop/Cancel met dirty-state opent de standaard guard.
Foutcodes
Action geeft alleen { ok, error: string } terug — geen 422-fieldErrors omdat het een single-field form is. Errors worden via toast getoond. Validatie (min 1, max 4000) wordt UI-side voorkomen via maxLength + submit-disable.
Bewust NIET in v1
- ❌ Markdown rendering — antwoord wordt als plain text doorgegeven; Claude leest 'm direct als context.
- ❌ Cmd/Ctrl+Enter shortcut — werkt wél voor de textarea-mode (via
useDialogSubmitShortcut); voor multiple-choice mode is er geen submit om te triggeren. - ❌ Bulk-answer — één vraag tegelijk per dialog.