feat(codes): generateNextTaskCode + CODE_REGEX export + Zod regex op alle 3 schemas

- lib/code.ts: rename VALID_CODE_RE naar geexporteerde CODE_REGEX,
  verwijder ongebruikte deriveTaskCode
- lib/code-server.ts: generateNextTaskCode(productId) — flat per-product
  T-N teller, hergebruikt nextSequential helper. Export
  isCodeUniqueConflict zodat callers P2002 op code-veld kunnen detecteren
- Zod schemas (pbi/story/task): codeField met trim + max-length + regex,
  optional input (server vult bij ontbreken). Task krijgt voor het
  eerst een codeField

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-04 08:36:28 +02:00
parent 611b621d75
commit 829122d437
5 changed files with 38 additions and 12 deletions

View file

@ -1,12 +1,12 @@
// Pure helpers — safe to import from client components.
// DB-backed helpers (generateNextStoryCode/PbiCode) live in lib/code-server.ts.
const VALID_CODE_RE = /^[A-Za-z0-9._-]+$/
export const CODE_REGEX = /^[A-Za-z0-9._-]+$/
export const MAX_CODE_LENGTH = 30
export function isValidCode(code: string): boolean {
return code.length > 0 && code.length <= MAX_CODE_LENGTH && VALID_CODE_RE.test(code)
return code.length > 0 && code.length <= MAX_CODE_LENGTH && CODE_REGEX.test(code)
}
export function normalizeCode(input: string | null | undefined): string | null {
@ -14,8 +14,3 @@ export function normalizeCode(input: string | null | undefined): string | null {
const trimmed = input.trim()
return trimmed === '' ? null : trimmed
}
export function deriveTaskCode(storyCode: string | null, indexOneBased: number): string | null {
if (!storyCode) return null
return `${storyCode}.${indexOneBased}`
}