feat(pbi-dialog): conform aan dialog-pattern + DemoTooltip + dirty-guard

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

- lib/schemas/pbi.ts — gedeeld zod-schema (createPbiSchema/updatePbiSchema)
- actions/pbis.ts — returnen nu code+fieldErrors (422) en code: 403 voor
  auth/demo errors
- PbiDialog adopt useDirtyCloseGuard, useDialogSubmitShortcut,
  entityDialog* layout-classes; submit-knop + Annuleren in DemoTooltip
- isDemo-prop toegevoegd, pbi-list geeft 'm door
- docs/specs/dialogs/pbi.md — "Bekende gaps" weggewerkt; alleen bewuste
  uitsluitingen blijven

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-04 07:23:14 +02:00
parent 03a248b0fb
commit 97dc4ee553
6 changed files with 231 additions and 154 deletions

26
lib/schemas/pbi.ts Normal file
View file

@ -0,0 +1,26 @@
import { z } from 'zod'
import { MAX_CODE_LENGTH } from '@/lib/code'
const codeField = z.string().max(MAX_CODE_LENGTH).optional()
const statusField = z.enum(['ready', 'blocked', 'done']).optional()
export const createPbiSchema = z.object({
productId: z.string(),
code: codeField,
title: z.string().min(1, 'Titel is verplicht').max(200),
description: z.string().max(2000).optional(),
priority: z.coerce.number().int().min(1).max(4),
status: statusField,
})
export const updatePbiSchema = z.object({
id: z.string(),
code: codeField,
title: z.string().min(1, 'Titel is verplicht').max(200),
description: z.string().max(2000).optional(),
priority: z.coerce.number().int().min(1).max(4),
status: statusField,
})
export type CreatePbiInput = z.infer<typeof createPbiSchema>
export type UpdatePbiInput = z.infer<typeof updatePbiSchema>