feat(codes): UI toont en accepteert code voor taken

- TaskDialog: code-input boven titel (font-mono, optional,
  placeholder "auto"), CodeBadge in dialog header bij edit
- EditTaskLoader: select code uit DB voor de dialog
- Solo page: vervang inline deriveTaskCode-logica door directe
  task.code uit DB
- Sprint page + TaskList + SprintBoardClient: Task-type krijgt
  verplicht code-veld; TaskList laat ongebruikte storyCode prop
  vallen omdat code-derivatie niet meer nodig is

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-04 08:36:52 +02:00
parent 7c82a736f5
commit 081a0a51c3
6 changed files with 52 additions and 29 deletions

View file

@ -27,6 +27,8 @@ import {
} from '@/components/ui/alert-dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { CodeBadge } from '@/components/shared/code-badge'
import { MAX_CODE_LENGTH } from '@/lib/code'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import {
useDirtyCloseGuard,
@ -45,6 +47,7 @@ import { cn } from '@/lib/utils'
export interface TaskDialogTask {
id: string
code: string | null
title: string
description: string | null
implementation_plan: string | null
@ -88,6 +91,7 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
resolver: zodResolver(taskSchema),
mode: 'onTouched',
defaultValues: {
code: task?.code ?? '',
title: task?.title ?? '',
description: task?.description ?? '',
implementation_plan: task?.implementation_plan ?? '',
@ -173,9 +177,12 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
>
{/* Sticky header */}
<div className={entityDialogHeaderClasses}>
<DialogTitle className="text-xl font-semibold">
{isEdit ? 'Taak bewerken' : 'Nieuwe taak'}
</DialogTitle>
<div className="flex items-center gap-2">
<DialogTitle className="text-xl font-semibold">
{isEdit ? 'Taak bewerken' : 'Nieuwe taak'}
</DialogTitle>
{isEdit && task?.code && <CodeBadge code={task.code} />}
</div>
{isEdit && (
<span className="text-xs text-muted-foreground">
Aangemaakt:{' '}
@ -190,6 +197,27 @@ export function TaskDialog({ task, storyId, productId, closePath, isDemo = false
{/* Scrollable form body */}
<div className={entityDialogBodyClasses}>
{/* Code */}
<div>
<label htmlFor="task-code" className="text-sm font-medium mb-2 block">
Code
</label>
<Input
id="task-code"
{...form.register('code')}
aria-invalid={!!form.formState.errors.code}
placeholder="auto (T-1, T-2, ...)"
className="font-mono"
maxLength={MAX_CODE_LENGTH}
onKeyDown={(e) => { if (e.key === 'Enter') e.preventDefault() }}
/>
{form.formState.errors.code && (
<p className="text-xs text-destructive mt-1">
{form.formState.errors.code.message}
</p>
)}
</div>
{/* Title */}
<div>
<label className="text-sm font-medium mb-2 block">