---
title: "ProductDialog Profiel"
status: active
audience: [ai-agent, contributor]
language: nl
last_updated: 2026-05-04
---

# ProductDialog Profiel

> Volgt `docs/patterns/dialog.md`. Dit document beschrijft alleen de afwijkingen en entity-specifieke keuzes.

## Velden

| Veld | Type | Mode | Validatie |
|---|---|---|---|
| `name` | string | both | min 1, max 200 |
| `code` | string? | both | max 20, alleen `[a-zA-Z0-9._-]`; uniek per gebruiker |
| `description` | string? | both | max 4000, markdown vrij |
| `repo_url` | string? \| null | both | https URL, moet beginnen met `https://github.com/` |
| `definition_of_done` | string? | both | max 4000, vrije tekst |
| `auto_pr` | boolean | both | default `false` |

## URL- of state-pattern

- Gekozen: **state-based** (§11.2)
- Reden: dialog leeft binnen één parent-component (`ProductList` op `/dashboard` en de product-actions-bar op `/products/[id]`); deep-linking is niet vereist
- Open-state komt uit `ProductList` (lijst-context) of `EditProductButton` (single-item context)

## Status-veld

N.v.t. — Product heeft geen status-enum. `archived` is een boolean buiten dit dialog (eigen archive-flow).

## Server actions

- `createProductAction(data)` in `actions/products.ts` — context-arg via `revalidatePath('/products')` + `revalidatePath('/dashboard')`
- `updateProductAction(id, data)` in `actions/products.ts` — context-arg via `revalidatePath('/products/${id}')` + `revalidatePath('/dashboard')` + `pg_notify('product_updated')`
- Beide hebben `session.userId`-check, `session.isDemo`-check (laag 2 demo-policy) en `productAccessFilter` voor update
- Resultaat-shape: `{ success: true, productId? }` of `{ error: string, code?: 422|403, fieldErrors?: Record<string, string[]> }`

## Foutcodes

| Code | Wanneer | UI |
|---|---|---|
| 422 | zod-validatie of code-uniqueness | `fieldErrors` → `form.setError`, geen toast, focus naar eerste error-veld |
| 403 | niet ingelogd, demo-modus, of geen toegang tot product | toast met message |
| 500 | onverwacht | huidige behandeling: error wordt door React opgevangen — laat de form open |

## Speciale gedragingen

- **Custom switch voor `auto_pr`**: native `<button role="switch">` met MD3-kleur-tokens (geen aparte primitive in v1; zou gepromoot moeten worden naar `components/shared/switch.tsx` zodra elders nodig).
- **Code-uniqueness server-side**: bij conflict wordt `fieldErrors.code` gezet; veld krijgt rode rand.
- **`useProductsStore` updates**: na succesvolle save wordt de in-memory store synchroon bijgewerkt zodat de productlijst onmiddellijk reageert (lokaal-first).

## Bewust NIET in v1

- Verwijderen vanuit deze dialog (loopt via `archiveProductAction` op een andere knop)
- Bulk edit
- Members beheren (eigen scherm op `/products/[id]/settings`)
