feat(todos): fix QuickInput — allow input without a product selected

- Product select is no longer required; 'Geen product' is the default
- Input and submit button are no longer disabled for users with no products
- Form resets only on success (useEffect on state.success) instead of
  resetting on every submit including failures
- Inline error from server action is now displayed below the form
- Removed 'Maak eerst een product aan' message that blocked the form

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-25 19:17:12 +02:00
parent e991f4f185
commit 4d08c92af5

View file

@ -41,39 +41,39 @@ interface TodoListProps {
} }
function QuickInput({ products, isDemo }: { products: Product[]; isDemo: boolean }) { function QuickInput({ products, isDemo }: { products: Product[]; isDemo: boolean }) {
const [, formAction] = useActionState(createTodoAction, undefined) const [state, formAction] = useActionState(createTodoAction, undefined)
const ref = useRef<HTMLFormElement>(null) const ref = useRef<HTMLFormElement>(null)
useEffect(() => {
if (state?.success) ref.current?.reset()
}, [state])
return ( return (
<form <div className="mb-6 space-y-1">
ref={ref} <form ref={ref} action={formAction} className="flex gap-2">
action={formAction} <select
onSubmit={() => setTimeout(() => ref.current?.reset(), 0)} name="productId"
className="flex gap-2 mb-6" disabled={isDemo}
> className="border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background shrink-0"
<select >
name="productId" <option value="">Geen product</option>
required {products.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
disabled={isDemo || products.length === 0} </select>
className="border border-border rounded-lg px-3 py-1.5 text-sm bg-input-background shrink-0" <Input
> name="title"
{products.length === 0 ? ( placeholder={isDemo ? 'Alleen-lezen in demo' : 'Nieuwe todo… (Enter om op te slaan)'}
<option value="">Geen producten</option> disabled={isDemo}
) : ( className="flex-1"
products.map(p => <option key={p.id} value={p.id}>{p.name}</option>) autoComplete="off"
)} />
</select> <DemoTooltip show={isDemo}>
<Input <QuickSubmitButton isDemo={isDemo} />
name="title" </DemoTooltip>
placeholder={isDemo ? 'Alleen-lezen in demo' : 'Nieuwe todo… (Enter om op te slaan)'} </form>
disabled={isDemo || products.length === 0} {typeof state?.error === 'string' && (
className="flex-1" <p className="text-xs text-error">{state.error}</p>
autoComplete="off" )}
/> </div>
<DemoTooltip show={isDemo}>
<QuickSubmitButton isDemo={isDemo || products.length === 0} />
</DemoTooltip>
</form>
) )
} }
@ -260,15 +260,11 @@ export function TodoList({ todos, products, isDemo }: TodoListProps) {
<div className="space-y-4"> <div className="space-y-4">
<QuickInput products={products} isDemo={isDemo} /> <QuickInput products={products} isDemo={isDemo} />
{products.length === 0 && ( {todos.length === 0 ? (
<p className="text-sm text-muted-foreground">Maak eerst een product aan om todo&apos;s toe te voegen.</p>
)}
{todos.length === 0 && products.length > 0 ? (
<div className="bg-surface-container-low border border-border rounded-xl p-12 text-center"> <div className="bg-surface-container-low border border-border rounded-xl p-12 text-center">
<p className="text-muted-foreground text-sm">Geen todo&apos;s. Voeg er een toe hierboven.</p> <p className="text-muted-foreground text-sm">Geen todo&apos;s. Voeg er een toe hierboven.</p>
</div> </div>
) : todos.length > 0 ? ( ) : (
<> <>
<div className="bg-surface-container-low border border-border rounded-xl divide-y divide-border"> <div className="bg-surface-container-low border border-border rounded-xl divide-y divide-border">
{open.map(todo => ( {open.map(todo => (
@ -321,7 +317,7 @@ export function TodoList({ todos, products, isDemo }: TodoListProps) {
</div> </div>
)} )}
</> </>
) : null} )}
{promotePbi && ( {promotePbi && (
<PromotePbiDialog todo={promotePbi} products={products} onClose={() => setPromotePbi(null)} /> <PromotePbiDialog todo={promotePbi} products={products} onClose={() => setPromotePbi(null)} />