add form validation

This commit is contained in:
Michi 2025-04-21 17:30:19 +02:00
parent 7d2dea886c
commit eb75a90936
4 changed files with 93 additions and 14 deletions

View file

@ -1,3 +1,5 @@
'use client';
import { CustomerField } from '@/app/lib/definitions';
import Link from 'next/link';
import {
@ -7,11 +9,15 @@ import {
UserCircleIcon,
} from '@heroicons/react/24/outline';
import { Button } from '@/app/ui/button';
import { createInvoice } from '@/app/lib/actions';
import { createInvoice, State } from '@/app/lib/actions';
import { useActionState } from 'react';
export default function Form({ customers }: { customers: CustomerField[] }) {
const initialState: State = { message: null, errors: {}};
const [state, formAction] = useActionState(createInvoice, initialState);
return (
<form action={createInvoice}>
<form action={formAction}>
<div className="rounded-md bg-gray-50 p-4 md:p-6">
{/* Customer Name */}
<div className="mb-4">
@ -24,6 +30,7 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
name="customerId"
className="peer block w-full cursor-pointer rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
defaultValue=""
aria-describedby="customer-error"
>
<option value="" disabled>
Select a customer
@ -36,6 +43,14 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
</select>
<UserCircleIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500" />
</div>
<div id="customer-error" aria-live="polite" aria-atomic="true">
{state.errors?.customerId &&
state.errors.customerId.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
</div>
{/* Invoice Amount */}
@ -52,10 +67,20 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
step="0.01"
placeholder="Enter USD amount"
className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
aria-describedby="amount-error"
required
/>
<CurrencyDollarIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
</div>
</div>
<div id="amount-error" aria-live="polite" aria-atomic="true">
{state.errors?.amount &&
state.errors.amount.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
</div>
{/* Invoice Status */}
@ -72,6 +97,7 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
type="radio"
value="pending"
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
aria-describedby="status-error"
/>
<label
htmlFor="pending"
@ -95,6 +121,14 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
Paid <CheckIcon className="h-4 w-4" />
</label>
</div>
<div id="status-error" aria-live="polite" aria-atomic="true">
{state.errors?.status &&
state.errors.status.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
</div>
</div>
</fieldset>