docs(dialog-pattern): add generic entity-dialog spec
Introduceert docs/patterns/dialog.md als bron-of-truth voor elke create/edit/detail-dialog in Scrum4Me, ongeacht het achterliggende dataobject. Bevat 14 secties: uitgangspunten, stack, component- architectuur, layout, validatie, drielaagse demo-policy, submission, dialog-gedrag, theming, footer, triggers/URL-state, per-entiteit profile-template, out-of-scope, en een verificatie-checklist. Registreert het patroon in CLAUDE.md "Implementatiepatronen"-tabel zodat Claude (en mensen) de spec verplicht raadplegen voor elke nieuwe dialog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6c6c8b96b7
commit
b3b5b910c0
2 changed files with 388 additions and 0 deletions
|
|
@ -111,6 +111,7 @@ Lees het relevante patroon vóór je begint. Nooit uit het hoofd schrijven.
|
|||
| Middleware (route protection) | `docs/patterns/middleware.md` |
|
||||
| QR-pairing (unauth-SSE + pre-auth cookie) | `docs/patterns/qr-login.md` |
|
||||
| Bidirectionele async-comms MCP-agent ↔ user | `docs/patterns/claude-question-channel.md` |
|
||||
| **Entity Dialog (verplicht voor élke create/edit/detail-dialog)** | `docs/patterns/dialog.md` — bron-of-truth; per entiteit één profile-doc (bv. `docs/scrum4me-task-dialog.md`) |
|
||||
| Status-enum mapping (DB ↔ API) | `lib/task-status.ts` |
|
||||
| Client/server module-boundary | `*-server.ts` bevat DB-calls of node-only deps; `*.ts` is pure (client-safe). Nooit `import { ... } from '@/lib/foo-server'` in een client-component, anders krijg je `Module not found: 'dns'`/`'pg'`-style runtime fouten |
|
||||
|
||||
|
|
|
|||
387
docs/patterns/dialog.md
Normal file
387
docs/patterns/dialog.md
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
# Pattern — Entity Dialog
|
||||
|
||||
Deze pagina is **bindend** voor elke create/edit/detail-dialog in Scrum4Me, ongeacht het achterliggende dataobject (PBI, Story, Task, Todo, Sprint, Product, User, of toekomstige entiteiten). Een nieuwe dialog die hier niet aan voldoet, hoort niet gemerged te worden.
|
||||
|
||||
> **Doel:** elke dialog voelt identiek aan voor de gebruiker, hergebruikt dezelfde primitives, en heeft de drielaagse demo-policy + auth-scoping standaard ingebakken.
|
||||
|
||||
Voor entity-specifieke afwijkingen of velden: schrijf één begeleidende doc per entiteit (zie sectie [§ Per-entiteit profile](#per-entiteit-profile-verplicht)). Voorbeeld: `docs/scrum4me-task-dialog.md` is het Task-profiel.
|
||||
|
||||
---
|
||||
|
||||
## 1 — Verplichte uitgangspunten
|
||||
|
||||
| # | Regel | Bron / waarom |
|
||||
|---|---|---|
|
||||
| 1.1 | Bouw op `components/ui/dialog.tsx` (de bestaande shadcn/`@base-ui/react`-wrapper). **Geen** directe imports van dialog-primitives uit `@base-ui/react`. | Voorkomt twee parallelle dialog-implementaties met inconsistente animatie/focus-trap/theming |
|
||||
| 1.2 | Gebruik composition via de **`render`-prop** (zie `CLAUDE.md` "UI Library Conventions"). Nooit Radix' `asChild`. | Project gebruikt `@base-ui/react`, niet Radix |
|
||||
| 1.3 | Mode (`create` vs `edit` vs `detail`) wordt afgeleid uit één input — een prop, een `state`-object of een `searchParam`. **Niet** twee aparte componenten. | Voorkomt code-duplicatie en inconsistente labels/footer-layouts |
|
||||
| 1.4 | Auth-scoping op elke server action via `productAccessFilter(userId)` (of het scope-helper-equivalent). Cross-tenant writes mogen onmogelijk zijn. | `CLAUDE.md` "Toegangsmodel" + `docs/patterns/server-action.md` |
|
||||
| 1.5 | **Drielaagse demo-policy** (verplicht — zie § 6) op elke write-actie. | `CLAUDE.md` "Demo-check" + `docs/scrum4me-architecture.md#demo-user-policy` |
|
||||
| 1.6 | Validatie via één gedeeld zod-schema (`lib/schemas/<entity>.ts`) — gebruikt door zowel form als server action. | `CLAUDE.md` "Validatie" |
|
||||
| 1.7 | Foutcodes volgen de project-conventie (§ 5). | `CLAUDE.md` "Foutcodes API" |
|
||||
| 1.8 | Geen willekeurige Tailwind-kleuren (`bg-blue-500` etc.). Alleen MD3-tokens uit `app/styles/theme.css`. | `docs/scrum4me-styling.md` |
|
||||
|
||||
---
|
||||
|
||||
## 2 — Stack & dependencies
|
||||
|
||||
Toegestane runtime-deps voor dialog-werk (al aanwezig of standaard pattern):
|
||||
|
||||
| Doel | Voorkeur | Acceptabele alternatief |
|
||||
|---|---|---|
|
||||
| Form-state | `react-hook-form` + `@hookform/resolvers/zod` | `useActionState` + `useFormStatus` (Server Actions, native React) |
|
||||
| Auto-grow textarea | `react-textarea-autosize` | — |
|
||||
| Markdown-rendering (preview) | `react-markdown` + `remark-gfm` (via bestaande `<Markdown>`-wrapper) | — |
|
||||
| Toasts | `sonner` | — |
|
||||
| Iconen | `lucide-react` | — |
|
||||
|
||||
> **Per-dialog mag je kiezen tussen `react-hook-form` of `useActionState`.** Beide patronen draaien al in deze codebase. Kies één per dialog en blijf consistent binnen dat bestand. Mix ze niet.
|
||||
|
||||
Verboden in dialog-context (v1):
|
||||
- `material-color-utilities` (dynamic color valt buiten v1)
|
||||
- Nieuwe form-libraries — geen `formik`, `final-form`, etc.
|
||||
|
||||
---
|
||||
|
||||
## 3 — Component-architectuur
|
||||
|
||||
### 3.1 Reusables (`components/entity-dialog/` of `components/shared/`)
|
||||
|
||||
Deze primitives kennen géén entity-specifieke types en mogen door élke dialog gebruikt worden:
|
||||
|
||||
| Primitive | Locatie | Verantwoordelijkheid |
|
||||
|---|---|---|
|
||||
| `<Dialog>` + `<DialogContent>` etc. | `components/ui/dialog.tsx` | Shell, motion, focus-trap, backdrop |
|
||||
| `<PrioritySelect>` / `<PrioritySegmented>` | `components/shared/priority-select.tsx` | P1-P4 — identiek over alle entiteiten |
|
||||
| `<DemoTooltip>` | `components/shared/demo-tooltip.tsx` | Wrapper rond write-knoppen voor demo-modus (laag 3 van 3) |
|
||||
| Auto-grow textarea | (toe te voegen wanneer nodig in `components/shared/`) | Wrapper rond `react-textarea-autosize` met char-counter + markdown-hint |
|
||||
| Dirty-close-guard | (gedeelde AlertDialog-flow) | "Wijzigingen niet opgeslagen — weggooien?" |
|
||||
| `<Markdown>` | `components/markdown.tsx` | `react-markdown` + `remark-gfm` voor description/criteria-preview |
|
||||
|
||||
> Wanneer je een primitive twee keer kopieert tussen entity-dialogs, **promote 'm meteen** naar `components/shared/` (of `components/entity-dialog/`). Drie keer is te laat.
|
||||
|
||||
### 3.2 Entity-specifieke laag (`components/<domain>/<entity>-dialog.tsx`)
|
||||
|
||||
Per entiteit één wrapper-bestand dat:
|
||||
1. De juiste form/body rendert
|
||||
2. De juiste server actions koppelt (`save<Entity>Action`, `delete<Entity>Action`)
|
||||
3. Entiteit-specifieke labels levert ("Story bewerken", "PBI aanmaken", etc.)
|
||||
|
||||
Een entity-dialog bevat **geen** layout-mechanica, motion-config of dirty-check zelf — die komen uit § 3.1.
|
||||
|
||||
---
|
||||
|
||||
## 4 — Layout & responsive gedrag
|
||||
|
||||
Identiek voor élke dialog (geen entity-specifieke variaties tenzij expliciet beargumenteerd in het entity-profile):
|
||||
|
||||
| Breakpoint | Breedte | Hoogte |
|
||||
|---|---|---|
|
||||
| Mobiel (< 640px) | full-screen | full-screen |
|
||||
| Tablet (640–1024px) | `90vw` | `max-h-[85vh]` |
|
||||
| Desktop (≥ 1024px) | `max-w-[50vw]`, `min-w-[480px]` | `max-h-[85vh]` |
|
||||
|
||||
Verplicht:
|
||||
- Padding `p-6` rondom (24px)
|
||||
- Veld-spacing in body `space-y-6` (24px)
|
||||
- **Sticky** header (titel + close) en **sticky** footer (knoppen)
|
||||
- Body scrollt onafhankelijk; geneste scrolls vermijden
|
||||
- Footer heeft `border-t` in `outline-variant`
|
||||
|
||||
---
|
||||
|
||||
## 5 — Validatie & foutcodes
|
||||
|
||||
### 5.1 zod-schema
|
||||
|
||||
Eén `lib/schemas/<entity>.ts` per entiteit. Geïmporteerd door zowel form als server action — geen aparte definities.
|
||||
|
||||
### 5.2 Foutcodes (verplicht)
|
||||
|
||||
| Code | Wanneer | UI-respons |
|
||||
|---|---|---|
|
||||
| **422** | zod-validatiefout (server-side dubbelcheck) | `fieldErrors` mappen naar `form.setError()`, géén toast |
|
||||
| **403** | demo-sessie probeert te schrijven, of cross-tenant write geblokkeerd | toast "Niet toegestaan in demo-modus" of "Geen toegang", form blijft open |
|
||||
| **400** | malformed JSON-body (`request.json()` faalt) — alleen bij REST-route-handlers | toast "Ongeldige aanvraag" |
|
||||
| **500** | onverwachte serverfout | toast met "Opnieuw proberen"-knop, form-state behouden |
|
||||
|
||||
> Field-level errors zijn **alleen** geldig bij `code: 422`. Bij andere codes is `fieldErrors` ongedefinieerd.
|
||||
|
||||
### 5.3 Field-level rendering
|
||||
|
||||
- Errors **onder** het veld, in `text-error`, met `border-error` op het input-element
|
||||
- Géén toast voor field-level errors
|
||||
- Submit-knop **blijft enabled** bij errors — klik scrollt naar eerste error-veld + focus
|
||||
- react-hook-form mode: `onTouched` (eerste validatie bij blur, daarna onChange)
|
||||
|
||||
---
|
||||
|
||||
## 6 — Drielaagse demo-policy (verplicht voor write-dialogs)
|
||||
|
||||
Elke dialog die schrijft (create / edit / delete) MOET door alle drie de lagen heen:
|
||||
|
||||
1. **Middleware-guard** in `proxy.ts` — blokkeert demo-sessies op write-routes vóór de server action loopt. Returnt **403**.
|
||||
2. **`session.isDemo`-check** binnen elke `save<Entity>Action` / `delete<Entity>Action` zelf — defense-in-depth voor het geval een actie buiten een proxy-route loopt. Returnt **403**.
|
||||
3. **`<DemoTooltip show={isDemo}>`** rond de submit- en delete-knoppen — UI-laag: knoppen `disabled` met tooltip "Demo-modus: opslaan uitgeschakeld".
|
||||
|
||||
> Eén laag missen = bug. Reviewers moeten alle drie de lagen kunnen aanwijzen in de PR.
|
||||
|
||||
---
|
||||
|
||||
## 7 — Submission-flow
|
||||
|
||||
### 7.1 Server Action (template)
|
||||
|
||||
```ts
|
||||
// actions/<entity>.ts
|
||||
'use server'
|
||||
|
||||
export async function save<Entity>Action(
|
||||
input: <Entity>Input,
|
||||
context: { /* ids voor revalidatePath en scope */ },
|
||||
): Promise<Save<Entity>Result> {
|
||||
const session = await getSession()
|
||||
if (!session.userId) return { ok: false, code: 403, error: 'forbidden' }
|
||||
if (session.isDemo) return { ok: false, code: 403, error: 'demo_readonly' }
|
||||
|
||||
const scope = await productAccessFilter(session.userId) // verplicht
|
||||
const parsed = <entity>Schema.safeParse(input)
|
||||
if (!parsed.success) {
|
||||
return { ok: false, code: 422, error: 'validation', fieldErrors: parsed.error.flatten().fieldErrors }
|
||||
}
|
||||
// ... Prisma write binnen `scope` ...
|
||||
// revalidatePath(...) op de context-route
|
||||
return { ok: true, <entity>: row }
|
||||
}
|
||||
|
||||
type Save<Entity>Result =
|
||||
| { ok: true; <entity>: <Entity> }
|
||||
| { ok: false; code: 422; error: 'validation'; fieldErrors: Record<string, string[]> }
|
||||
| { ok: false; code: 403; error: 'demo_readonly' | 'forbidden' }
|
||||
| { ok: false; code: 500; error: 'server_error' }
|
||||
```
|
||||
|
||||
### 7.2 Revalidation
|
||||
|
||||
`revalidatePath` op de **context-route** waarin de dialog werd geopend, niet op een statisch path. Context wordt door de aanroepende client meegegeven (geen hard-coded paths in de action).
|
||||
|
||||
### 7.3 Submit-flow
|
||||
|
||||
- Synchroon (geen optimistic update in v1, behalve waar het store-patroon `usePlannerStore` al bestaat)
|
||||
- Tijdens submit: cancel- en submit-knop disabled, spinner of "…" in submit-knop, velden **blijven enabled**
|
||||
- Server saniteert en valideert opnieuw met hetzelfde zod-schema
|
||||
|
||||
---
|
||||
|
||||
## 8 — Dialog-gedrag (UX-regels)
|
||||
|
||||
### 8.1 Sluiten met dirty state
|
||||
|
||||
- Form niet aangeraakt → Esc / backdrop-klik / Cancel sluiten **direct**
|
||||
- Form `isDirty` → Esc / backdrop-klik / Cancel triggeren `AlertDialog`: *"Wijzigingen niet opgeslagen — weggooien?"*
|
||||
|
||||
### 8.2 Keyboard shortcuts
|
||||
|
||||
| Toets | Actie |
|
||||
|---|---|
|
||||
| **Esc** | Sluiten (met dirty-check) |
|
||||
| **Cmd/Ctrl + Enter** | Submit vanuit elk veld |
|
||||
| **Enter in `<input type=text>`** | **Geen** submit (alleen Cmd/Ctrl+Enter) |
|
||||
| **Enter in `<textarea>`** | Newline (default browser, niet overriden) |
|
||||
| **Tab** | Top-naar-bottom door velden, dan Cancel → Submit (en Delete in edit-mode) |
|
||||
|
||||
### 8.3 Focus management
|
||||
|
||||
- Bij openen: focus op het eerste tekstveld (meestal `title` / `name`)
|
||||
- Edit-mode: cursor aan het einde van bestaande waarde, **geen auto-select** (anders typt user per ongeluk weg)
|
||||
- Bij sluiten: focus terug naar het element dat de dialog opende (`@base-ui/react` doet dit by default — niet breken)
|
||||
- Bij submit-error: focus naar eerste error-veld
|
||||
|
||||
### 8.4 Motion (MD3-conform)
|
||||
|
||||
- Open: 250ms, easing `cubic-bezier(0.2, 0, 0, 1)`, scale 0.95→1 + opacity 0→1
|
||||
- Close: 200ms, easing `cubic-bezier(0.4, 0, 1, 1)`
|
||||
|
||||
### 8.5 Backdrop
|
||||
|
||||
`rgba(0,0,0,0.4)` (iets sterker dan MD3-default 0.32 voor contrast op zowel licht als donker).
|
||||
|
||||
---
|
||||
|
||||
## 9 — Theming (MD3-tokens)
|
||||
|
||||
| Surface | Token |
|
||||
|---|---|
|
||||
| Dialog-background | `surface-container-high` + `shadow-2xl` (getemperde opacity) |
|
||||
| Form input-background | `surface-container-low`, geen shadow |
|
||||
| Footer top-border | `outline-variant` |
|
||||
| Submit-knop (filled) | `primary` background + `on-primary` tekst |
|
||||
| Cancel-knop (text) | geen background + `primary` tekst |
|
||||
| Delete-knop (tonal error) | `error-container` background + `on-error-container` tekst |
|
||||
|
||||
Density: **comfortable** (geen compact). Single-line input `h-14` (56px MD3-default).
|
||||
|
||||
Typography:
|
||||
- `headline-small` (24px) — dialog-titel
|
||||
- `body-large` (16px) — form-input tekst
|
||||
- `body-medium` (14px) — helptext, counter
|
||||
|
||||
---
|
||||
|
||||
## 10 — Footer
|
||||
|
||||
### 10.1 Edit-mode
|
||||
|
||||
```
|
||||
[ Verwijderen ] [ Annuleren ] [ Opslaan ]
|
||||
tonal (error-container) text filled (primary)
|
||||
```
|
||||
|
||||
### 10.2 Create-mode
|
||||
|
||||
```
|
||||
[ Annuleren ] [ Aanmaken ]
|
||||
text filled (primary)
|
||||
```
|
||||
|
||||
### 10.3 Detail-mode (read-only)
|
||||
|
||||
```
|
||||
[ Sluiten ]
|
||||
filled (primary)
|
||||
```
|
||||
|
||||
### 10.4 Delete-flow (verplicht patroon)
|
||||
|
||||
1. Klik "Verwijderen" → `AlertDialog`: *"Weet je zeker? Dit kan niet ongedaan worden."*
|
||||
2. Bevestigen → `delete<Entity>Action` (zelfde auth-scoping én demo-checks als save) → `revalidatePath` op context-route → dialog sluit → toast "<Entity> verwijderd"
|
||||
3. Geen undo in v1
|
||||
|
||||
---
|
||||
|
||||
## 11 — Triggers & URL-state
|
||||
|
||||
Elke dialog wordt geopend vanuit een context-pagina (sprint, backlog, dashboard, …). Twee patronen zijn toegestaan:
|
||||
|
||||
### 11.1 URL-based (voorkeur voor deep-link-bare dialogen)
|
||||
|
||||
```
|
||||
?new<Entity>=1 → create-dialog open
|
||||
?edit<Entity>=<id> → edit-dialog open
|
||||
```
|
||||
|
||||
Kies dit patroon wanneer de dialog gedeeld of gebookmarkt moet kunnen worden, of wanneer Suspense + skeleton voor edit-mode gewenst is.
|
||||
|
||||
Sluiten = dezelfde route opnieuw pushen zonder de query-params.
|
||||
|
||||
### 11.2 State-based (voor pure UI-dialogen binnen één client-component)
|
||||
|
||||
```tsx
|
||||
const [dialogState, setDialogState] = useState<DialogState | null>(null)
|
||||
<EntityDialog state={dialogState} onClose={() => setDialogState(null)} />
|
||||
```
|
||||
|
||||
Kies dit patroon voor dialogen die altijd binnen één parent-component leven en geen deep-linking nodig hebben (bv. PBI-dialog binnen PbiList, Story-dialog binnen StoryPanel).
|
||||
|
||||
> Mix de twee patronen niet binnen één entity-dialog. Documenteer in het entity-profile welk patroon gekozen is en waarom.
|
||||
|
||||
### 11.3 Server-side fetch in edit-mode
|
||||
|
||||
Bij URL-based pattern: server component fetcht het record vóór render — **inclusief auth-scoping**. Bestaat het record niet of valt het buiten scope → toast + redirect naar context-route zonder query-param.
|
||||
|
||||
Voor state-based pattern: het record komt al uit de parent (in-memory store of server-prop), dus geen aparte fetch nodig.
|
||||
|
||||
---
|
||||
|
||||
## 12 — Per-entiteit profile (verplicht)
|
||||
|
||||
Voor elke entiteit met een dialog hoort één profiel-doc te bestaan: `docs/<scrum4me-<entity>-dialog>.md` (of vergelijkbaar). Het profiel **vult de generieke spec aan** en bevat **alleen** de entity-specifieke onderdelen.
|
||||
|
||||
### Verplichte secties van het entity-profile
|
||||
|
||||
```md
|
||||
# <Entity>Dialog Profiel
|
||||
|
||||
> Volgt `docs/patterns/dialog.md`. Dit document beschrijft alleen de afwijkingen en entity-specifieke keuzes.
|
||||
|
||||
## Velden
|
||||
| Veld | Type | Mode | Validatie |
|
||||
|---|---|---|---|
|
||||
| ... | ... | create / edit / both | ... |
|
||||
|
||||
## URL- of state-pattern
|
||||
- Gekozen: <URL-based | state-based>
|
||||
- Reden: <korte motivatie>
|
||||
- Routes (indien URL-based): `?new<Entity>=1` op `<route>`, `?edit<Entity>=<id>` op `<route>`
|
||||
|
||||
## Status-veld (indien aanwezig)
|
||||
- Enum: ...
|
||||
- Dot-kleur-mapping: ...
|
||||
- Default bij create: ...
|
||||
|
||||
## Server actions
|
||||
- `save<Entity>Action` — locatie, context-arg, revalidate-paden
|
||||
- `delete<Entity>Action` — locatie, context-arg, revalidate-paden
|
||||
|
||||
## Speciale gedragingen (alleen indien de generieke spec niet volstaat)
|
||||
- ... (bv. story-log-tab, claude-question-historie, file-upload, etc.)
|
||||
|
||||
## Bewust NIET in v1
|
||||
- ...
|
||||
```
|
||||
|
||||
> Geen entity-profile = geen merge. Reviewers checken op het bestaan van het profiel-bestand bij een PR die een nieuwe dialog introduceert.
|
||||
|
||||
---
|
||||
|
||||
## 13 — Bewust **niet** in deze pattern (out of scope)
|
||||
|
||||
Deze items zijn over het algemeen niet welkom in een dialog. Een entity-profile mag ze toevoegen, maar moet dan de afweging documenteren:
|
||||
|
||||
- ❌ Bulk-edit (meerdere records tegelijk)
|
||||
- ❌ Drag-and-drop binnen de dialog (drag hoort op de lijst-view)
|
||||
- ❌ Tabs voor secties — alleen spacing-gebaseerde groepering
|
||||
- ❌ Section-headers — implicit via spacing, geen labels
|
||||
- ❌ Sub-entiteiten (parent-child binnen dezelfde dialog)
|
||||
- ❌ File uploads (uitzondering: avatar — eigen pattern)
|
||||
- ❌ Comments / activity log binnen de dialog (mag wel als read-only side-panel; zie `StoryLog`)
|
||||
- ❌ Optimistic locking — last-write-wins binnen scope
|
||||
- ❌ Cmd+K / quick-create zonder dialog
|
||||
- ❌ Templates voor terugkerende records
|
||||
- ❌ Telemetrie / per-veld analytics
|
||||
|
||||
---
|
||||
|
||||
## 14 — Verificatie-checklist (per nieuwe dialog)
|
||||
|
||||
Reviewer en bouwer lopen deze door vóór merge:
|
||||
|
||||
- [ ] Bouwt op `components/ui/dialog.tsx` (geen directe `@base-ui/react`-imports)
|
||||
- [ ] Composition via `render`-prop waar nodig
|
||||
- [ ] Layout volgt § 4 (responsive breakpoints, padding, sticky header/footer)
|
||||
- [ ] zod-schema in `lib/schemas/<entity>.ts`, gedeeld door form en action
|
||||
- [ ] Server action heeft `productAccessFilter(userId)` + `session.isDemo`-check
|
||||
- [ ] `proxy.ts` heeft een guard voor de bijbehorende write-route(s) (laag 1)
|
||||
- [ ] Submit/Delete-knoppen omwikkeld met `<DemoTooltip show={isDemo}>` (laag 3)
|
||||
- [ ] Foutcodes 422/403/500 correct teruggemapt naar UI (zie § 5)
|
||||
- [ ] Dirty-close-guard actief
|
||||
- [ ] Cmd/Ctrl+Enter submit, Esc sluit (met dirty-check)
|
||||
- [ ] Focus management: opent op eerste veld, geen auto-select in edit-mode
|
||||
- [ ] Motion + backdrop conform § 8.4–8.5
|
||||
- [ ] Alleen MD3-tokens; geen `bg-blue-500`-style classes
|
||||
- [ ] Footer-layout per mode conform § 10
|
||||
- [ ] Delete-flow: AlertDialog → action → toast
|
||||
- [ ] URL- of state-pattern gekozen + gedocumenteerd in entity-profile
|
||||
- [ ] Entity-profile bestaat in `docs/` en is up-to-date
|
||||
- [ ] Test gedekt: server action met validatie + demo + scope, en component-test op render + submit-flow
|
||||
|
||||
---
|
||||
|
||||
## 15 — Referenties
|
||||
|
||||
- `CLAUDE.md` — UI Library Conventions, Demo-check, Foutcodes API, Validatie
|
||||
- `docs/scrum4me-styling.md` — MD3-tokens, kleurklassen
|
||||
- `docs/scrum4me-architecture.md` — Demo user policy, scope-helpers
|
||||
- `docs/patterns/server-action.md` — Server Action template (auth + Zod)
|
||||
- `docs/patterns/zustand-optimistic.md` — voor lijst-views die de dialog aanroepen
|
||||
- `docs/scrum4me-task-dialog.md` — voorbeeld-profile voor entiteit "Task"
|
||||
Loading…
Add table
Add a link
Reference in a new issue