Scrum4Me/components/auth/auth-form.tsx
janpeter visser 8017968e60 feat: ST-006-ST-008 auth pages, middleware, nav shell en dashboard
- Login/register pages met AuthForm (useActionState + useFormStatus)
- Server Actions voor login, register, logout met Zod validatie
- Middleware checkt session cookie zonder iron-session op Edge runtime
- AppLayout met auth-check en NavBar met demo badge en actieve links
- Dashboard toont productenlijst via ProductList Client Component
- Fix: a-in-a hydration error opgelost door div plus useRouter te gebruiken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 11:18:42 +02:00

79 lines
2.3 KiB
TypeScript

'use client'
import { useActionState } from 'react'
import { useFormStatus } from 'react-dom'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
type ActionResult = { error: string | Record<string, string[]> } | undefined
function SubmitButton({ label }: { label: string }) {
const { pending } = useFormStatus()
return (
<Button type="submit" className="w-full" disabled={pending}>
{pending ? 'Even wachten…' : label}
</Button>
)
}
function getErrorMessage(error: ActionResult): string | null {
if (!error) return null
if (typeof error.error === 'string') return error.error
// Field errors object — flatten to first message
const first = Object.values(error.error).flat()[0]
return first ?? null
}
interface AuthFormProps {
action: (_prevState: unknown, formData: FormData) => Promise<ActionResult>
submitLabel: string
}
export function AuthForm({ action, submitLabel }: AuthFormProps) {
const [state, formAction] = useActionState(action, undefined)
const errorMessage = getErrorMessage(state)
return (
<form action={formAction} className="space-y-4">
<div className="space-y-2">
<label htmlFor="username" className="text-sm font-medium text-foreground">
Gebruikersnaam
</label>
<Input
id="username"
name="username"
type="text"
autoComplete="username"
required
minLength={3}
placeholder="jouw-naam"
className="bg-input-background border-border focus-visible:ring-primary"
/>
</div>
<div className="space-y-2">
<label htmlFor="password" className="text-sm font-medium text-foreground">
Wachtwoord
</label>
<Input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
minLength={8}
placeholder="••••••••"
className="bg-input-background border-border focus-visible:ring-primary"
/>
</div>
{errorMessage && (
<div className="bg-error-container text-error-container-foreground rounded-lg px-3 py-2 text-sm border-l-4 border-error">
{errorMessage}
</div>
)}
<SubmitButton label={submitLabel} />
</form>
)
}