feat(sprint-dialogs): conform aan dialog-pattern + entity-profile

Story 5 van PBI "Alle dialogen conform docs/patterns/dialog.md".

- lib/schemas/sprint.ts — gedeelde zod-schemas (create/dates/goal)
- actions/sprints.ts — code+fieldErrors voor 422; code: 403 voor
  auth/demo errors
- StartSprintButton dialog: useDirtyCloseGuard, useDialogSubmitShortcut,
  entityDialog* layout-classes; DemoTooltip op trigger; veld-niveau
  errors via fieldErrors
- SprintHeader's date- en complete-dialogen: zelfde behandeling; date-
  dialog krijgt dirty-guard, complete-dialog krijgt DemoTooltip op
  bevestigen
- docs/specs/dialogs/sprint.md — entity-profile dat alle drie de modes
  documenteert; consolidatie naar één SprintDialog component bewust
  uitgesteld
- Sprint-dates tests aangepast aan nieuwe action-shape

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-04 07:30:46 +02:00
parent 01e77fc560
commit 784791d8f9
7 changed files with 320 additions and 125 deletions

View file

@ -25,6 +25,7 @@ Auto-generated on 2026-05-04 from front-matter and headings.
|---|---|---|
| [PbiDialog Profiel](./specs/dialogs/pbi.md) | active | 2026-05-04 |
| [ProductDialog Profiel](./specs/dialogs/product.md) | active | 2026-05-04 |
| [Sprint Dialogs Profiel](./specs/dialogs/sprint.md) | active | 2026-05-04 |
| [StoryDialog Profiel](./specs/dialogs/story.md) | active | 2026-05-04 |
| [TaskDialog Profiel](./specs/dialogs/task.md) | active | 2026-05-03 |
| [Scrum4Me — Functionele Specificatie](./specs/functional.md) | active | 2026-05-03 |

View file

@ -0,0 +1,72 @@
---
title: "Sprint Dialogs Profiel"
status: active
audience: [ai-agent, contributor]
language: nl
last_updated: 2026-05-04
---
# Sprint Dialogs Profiel
> Volgt `docs/patterns/dialog.md`. Dit document beschrijft alleen de Sprint-specifieke afwijkingen en keuzes.
Sprint heeft drie dialog-flows verspreid over twee componenten:
| Flow | Locatie | Mode |
|---|---|---|
| Sprint starten | `components/sprint/start-sprint-button.tsx` | create |
| Datums bewerken | `components/sprint/sprint-header.tsx` (inline) | edit |
| Sprint afronden | `components/sprint/sprint-header.tsx` (inline) | actie-bevestiging |
Daarnaast bestaat een **inline edit-form** voor de Sprint Goal in `sprint-header.tsx` — dat is geen dialog (geen modale overlay) maar een toggleable form-row.
## Velden
### Create / Edit dates
| Veld | Type | Validatie |
|---|---|---|
| `sprint_goal` | string (alleen create) | min 1, max 500 |
| `start_date` | date \| null | optioneel; `end_date >= start_date` |
| `end_date` | date \| null | optioneel; `end_date >= start_date` |
### Complete sprint
Geen form-velden. Per story-rij in de sprint kiest de gebruiker `'DONE'` of `'OPEN'` (default `'OPEN'`). De decisions-map wordt direct aan `completeSprintAction` doorgegeven.
## URL- of state-pattern
- Gekozen: **state-based** (§11.2)
- Reden: alle dialogen zijn local-state in hun parent-component (button of header). Sprint heeft geen deep-link-bare detail-pagina voor zijn dialogen.
## Server actions
- `createSprintAction(_prev, fd)``actions/sprints.ts` — revalidate `/products/${productId}`
- `updateSprintDatesAction(_prev, fd)` — idem — revalidate `/products/${productId}/sprint`
- `updateSprintGoalAction(_prev, fd)` — idem — revalidate `/products/${productId}/sprint`
- `completeSprintAction(sprintId, decisions)` — niet form-based; directe argumenten
- Alle hebben `session.userId`-check, `session.isDemo`-check (laag 2 demo-policy) en `productAccessFilter`/`getAccessibleProduct` voor scope
- Resultaat-shape: `{ success: true, ... }` of `{ error: string, code?: 422|403, fieldErrors?: Record<string, string[]> }`
## Foutcodes
| Code | Wanneer | UI |
|---|---|---|
| 422 | zod-validatie of date-order constraint | `fieldErrors` onder de velden, geen toast |
| 403 | niet ingelogd, demo-modus, of geen toegang | toast met message |
## Schema
`lib/schemas/sprint.ts` exporteert:
- `createSprintSchema` — productId, sprint_goal, start_date, end_date
- `updateSprintDatesSchema` — id, start_date, end_date
- `updateSprintGoalSchema` — id, sprint_goal
- `validateDateOrder` — refinement gebruikt door beide date-schemas
Alle drie de actions importeren hier; geen inline schemas meer.
## Bewust NIET in v1
- ❌ **Eén consolideerde `SprintDialog`-component** met `mode: 'create' | 'edit-dates' | 'complete'` — overwogen tijdens story 5 maar niet uitgevoerd; de dialogen leven natuurlijker in hun parent-component (button / header) en worden niet hergebruikt elders. Indien een vierde sprint-dialog ontstaat, hernieuw deze afweging.
- ❌ Bewerken van de Sprint Goal vanuit deze dialogen — gebeurt via een inline-form in `sprint-header.tsx` (toggleable, geen modal)
- ❌ Sprint-templates / kopiëren van vorige sprint