feat(ST-507): add code input to Product, Pbi and Story forms
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9da9f6ae56
commit
66063f035a
4 changed files with 93 additions and 38 deletions
|
|
@ -48,6 +48,7 @@ export default async function ProductSettingsPage({ params }: Props) {
|
|||
defaultValues={{
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
code: product.code,
|
||||
description: product.description ?? '',
|
||||
repo_url: product.repo_url ?? '',
|
||||
definition_of_done: product.definition_of_done,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export interface PbiDialogPbi {
|
|||
title: string
|
||||
priority: number
|
||||
description?: string | null
|
||||
code?: string | null
|
||||
}
|
||||
|
||||
type CreateState = { mode: 'create'; productId: string; defaultPriority?: number }
|
||||
|
|
@ -101,17 +102,30 @@ export function PbiDialog({ state, onClose }: PbiDialogProps) {
|
|||
{!isEdit && <input type="hidden" name="productId" value={(state as CreateState | null)?.productId ?? ''} />}
|
||||
<input type="hidden" name="priority" value={priority} />
|
||||
|
||||
<div className="grid gap-1.5">
|
||||
<label htmlFor="pbi-title" className="text-sm font-medium">Titel</label>
|
||||
<Input
|
||||
id="pbi-title"
|
||||
ref={titleRef}
|
||||
name="title"
|
||||
defaultValue={pbi?.title ?? ''}
|
||||
placeholder="PBI-titel…"
|
||||
required
|
||||
maxLength={200}
|
||||
/>
|
||||
<div className="grid grid-cols-[6rem_1fr] gap-3">
|
||||
<div className="grid gap-1.5">
|
||||
<label htmlFor="pbi-code" className="text-sm font-medium">Code</label>
|
||||
<Input
|
||||
id="pbi-code"
|
||||
name="code"
|
||||
defaultValue={pbi?.code ?? ''}
|
||||
placeholder={isEdit ? '' : 'auto'}
|
||||
maxLength={30}
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<label htmlFor="pbi-title" className="text-sm font-medium">Titel</label>
|
||||
<Input
|
||||
id="pbi-title"
|
||||
ref={titleRef}
|
||||
name="title"
|
||||
defaultValue={pbi?.title ?? ''}
|
||||
placeholder="PBI-titel…"
|
||||
required
|
||||
maxLength={200}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-1.5">
|
||||
|
|
|
|||
|
|
@ -124,7 +124,14 @@ export function StoryDialog({ state, onClose, isDemo = false }: StoryDialogProps
|
|||
<Dialog open={!!state} onOpenChange={(open) => { if (!open) onClose() }}>
|
||||
<DialogContent className="sm:max-w-lg flex flex-col gap-0 p-0 max-h-[90vh] overflow-hidden">
|
||||
<DialogHeader className="px-5 pt-5 pb-4 border-b border-border shrink-0 pr-14">
|
||||
<DialogTitle>{isEdit ? story!.title : 'Nieuwe story'}</DialogTitle>
|
||||
<div className="flex items-start gap-2">
|
||||
<DialogTitle className="flex-1">{isEdit ? story!.title : 'Nieuwe story'}</DialogTitle>
|
||||
{isEdit && story!.code && (
|
||||
<span className="font-mono text-[11px] text-muted-foreground border border-border rounded-md bg-surface-container px-1.5 py-0.5 shrink-0 mt-0.5">
|
||||
{story!.code}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{isEdit && (
|
||||
<div className="flex gap-2 mt-1">
|
||||
<Badge className={cn('text-xs border', PRIORITY_COLORS[priority])}>
|
||||
|
|
@ -154,17 +161,30 @@ export function StoryDialog({ state, onClose, isDemo = false }: StoryDialogProps
|
|||
<div className="flex-1 overflow-y-auto">
|
||||
{showForm ? (
|
||||
<div className="p-5 space-y-4">
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Titel</label>
|
||||
<Input
|
||||
ref={titleRef}
|
||||
name="title"
|
||||
defaultValue={story?.title ?? ''}
|
||||
required
|
||||
maxLength={200}
|
||||
className={fieldError('title') ? 'border-error' : ''}
|
||||
/>
|
||||
{fieldError('title') && <p className="text-xs text-error">{fieldError('title')}</p>}
|
||||
<div className="grid grid-cols-[6rem_1fr] gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Code</label>
|
||||
<Input
|
||||
name="code"
|
||||
defaultValue={story?.code ?? ''}
|
||||
placeholder={isEdit ? '' : 'auto'}
|
||||
maxLength={30}
|
||||
className={cn('font-mono text-sm', fieldError('code') ? 'border-error' : '')}
|
||||
/>
|
||||
{fieldError('code') && <p className="text-xs text-error">{fieldError('code')}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Titel</label>
|
||||
<Input
|
||||
ref={titleRef}
|
||||
name="title"
|
||||
defaultValue={story?.title ?? ''}
|
||||
required
|
||||
maxLength={200}
|
||||
className={fieldError('title') ? 'border-error' : ''}
|
||||
/>
|
||||
{fieldError('title') && <p className="text-xs text-error">{fieldError('title')}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useFormStatus } from 'react-dom'
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
type FieldErrors = Record<string, string[]>
|
||||
type ActionResult = { error?: string | FieldErrors; success?: boolean } | undefined
|
||||
|
|
@ -34,6 +35,7 @@ interface ProductFormProps {
|
|||
defaultValues?: {
|
||||
id?: string
|
||||
name?: string
|
||||
code?: string | null
|
||||
description?: string
|
||||
repo_url?: string
|
||||
definition_of_done?: string
|
||||
|
|
@ -52,21 +54,39 @@ export function ProductForm({ action, submitLabel, defaultValues }: ProductFormP
|
|||
<input type="hidden" name="id" value={defaultValues.id} />
|
||||
)}
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label htmlFor="name" className="text-sm font-medium text-foreground">
|
||||
Naam <span className="text-error">*</span>
|
||||
</label>
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
required
|
||||
defaultValue={defaultValues?.name}
|
||||
placeholder="bijv. DevPlanner"
|
||||
className={fieldError('name') ? 'border-error' : ''}
|
||||
/>
|
||||
{fieldError('name') && (
|
||||
<p className="text-xs text-error">{fieldError('name')}</p>
|
||||
)}
|
||||
<div className="grid grid-cols-[8rem_1fr] gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<label htmlFor="code" className="text-sm font-medium text-foreground">
|
||||
Code
|
||||
</label>
|
||||
<Input
|
||||
id="code"
|
||||
name="code"
|
||||
defaultValue={defaultValues?.code ?? ''}
|
||||
placeholder="optioneel"
|
||||
maxLength={30}
|
||||
className={cn('font-mono text-sm', fieldError('code') ? 'border-error' : '')}
|
||||
/>
|
||||
{fieldError('code') && (
|
||||
<p className="text-xs text-error">{fieldError('code')}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<label htmlFor="name" className="text-sm font-medium text-foreground">
|
||||
Naam <span className="text-error">*</span>
|
||||
</label>
|
||||
<Input
|
||||
id="name"
|
||||
name="name"
|
||||
required
|
||||
defaultValue={defaultValues?.name}
|
||||
placeholder="bijv. DevPlanner"
|
||||
className={fieldError('name') ? 'border-error' : ''}
|
||||
/>
|
||||
{fieldError('name') && (
|
||||
<p className="text-xs text-error">{fieldError('name')}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue