Scrum4Me/lib/schemas/idea.ts
Madhura68 bba3f11269 lib: idea schemas + status mappers + transition guards (M12 T-493)
- lib/schemas/idea.ts: ideaCreateSchema, ideaUpdateSchema, ideaPlanMdFrontmatterSchema
  (yaml-frontmatter contract for materialize-step parser)
- lib/idea-status.ts: bidirectional DB↔API mapping, canTransition state-machine
  guard, isIdeaEditable + isGrillMdEditable + isPlanMdEditable helpers
- includes auto-regen docs/erd.svg from prisma generate

Tests: 26 cases (status round-trip, transitions valid/invalid, schema validation
edge-cases, priority bounds, verify-enum).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 19:38:52 +02:00

53 lines
2.1 KiB
TypeScript

import { z } from 'zod'
// Velden die de gebruiker invult bij create/edit. Status wordt door
// server-actions gezet (niet door client-input).
export const ideaCreateSchema = z.object({
title: z.string().trim().min(1, 'Titel is verplicht').max(200, 'Maximaal 200 tekens'),
description: z.string().max(4000, 'Maximaal 4000 tekens').optional().nullable(),
product_id: z.string().cuid('Ongeldig product').optional().nullable(),
})
export const ideaUpdateSchema = ideaCreateSchema.partial()
export type IdeaCreateInput = z.infer<typeof ideaCreateSchema>
export type IdeaUpdateInput = z.infer<typeof ideaUpdateSchema>
// ---------------------------------------------------------------------------
// plan_md frontmatter — strict format dat door make-plan-job geproduceerd
// wordt en door materializeIdeaPlanAction wordt geparseerd. Zie
// docs/plans/M12-ideas.md "Plan-md formaat A".
const verifyRequiredEnum = z.enum(['ALIGNED', 'ALIGNED_OR_PARTIAL', 'ANY'])
const planTaskSchema = z.object({
title: z.string().min(1).max(200),
description: z.string().max(4000).optional(),
implementation_plan: z.string().max(8000).optional(),
priority: z.number().int().min(1).max(4),
verify_required: verifyRequiredEnum.optional(),
verify_only: z.boolean().optional(),
})
const planStorySchema = z.object({
title: z.string().min(1).max(200),
description: z.string().max(4000).optional(),
acceptance_criteria: z.string().max(4000).optional(),
priority: z.number().int().min(1).max(4),
tasks: z.array(planTaskSchema).min(1, 'Story moet minimaal 1 taak hebben'),
})
const planPbiSchema = z.object({
title: z.string().min(1).max(200),
description: z.string().max(4000).optional(),
priority: z.number().int().min(1).max(4),
})
export const ideaPlanMdFrontmatterSchema = z.object({
pbi: planPbiSchema,
stories: z.array(planStorySchema).min(1, 'Plan moet minimaal 1 story bevatten'),
})
export type IdeaPlanFrontmatter = z.infer<typeof ideaPlanMdFrontmatterSchema>
export type IdeaPlanStory = z.infer<typeof planStorySchema>
export type IdeaPlanTask = z.infer<typeof planTaskSchema>