Chapter 14 - form validation completed (create form only)
This commit is contained in:
@@ -16,7 +16,7 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
|
|||||||
const [state, dispatch] = useFormState(createInvoice, initialState);
|
const [state, dispatch] = useFormState(createInvoice, initialState);
|
||||||
console.log(state);
|
console.log(state);
|
||||||
return (
|
return (
|
||||||
<form action={dispatch}>
|
<form action={dispatch} aria-describedby="form-error">
|
||||||
<div className="rounded-md bg-gray-50 p-4 md:p-6">
|
<div className="rounded-md bg-gray-50 p-4 md:p-6">
|
||||||
{/* Customer Name */}
|
{/* Customer Name */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
@@ -70,6 +70,13 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
|
|||||||
/>
|
/>
|
||||||
<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" />
|
<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(s => (
|
||||||
|
<p className="mt-2 text-sm text-red-500" key={s}>{s}</p>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -82,42 +89,54 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
|
|||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
id="pending"
|
id="pending"
|
||||||
name="status"
|
name="status"
|
||||||
type="radio"
|
type="radio"
|
||||||
value="pending"
|
value="pending"
|
||||||
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
||||||
|
aria-describedby="status-error"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="pending"
|
htmlFor="pending"
|
||||||
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600"
|
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600"
|
||||||
>
|
>
|
||||||
Pending <ClockIcon className="h-4 w-4" />
|
Pending <ClockIcon className="h-4 w-4"/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
id="paid"
|
id="paid"
|
||||||
name="status"
|
name="status"
|
||||||
type="radio"
|
type="radio"
|
||||||
value="paid"
|
value="paid"
|
||||||
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="paid"
|
htmlFor="paid"
|
||||||
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white"
|
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white"
|
||||||
>
|
>
|
||||||
Paid <CheckIcon className="h-4 w-4" />
|
Paid <CheckIcon className="h-4 w-4"/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="status-error" aria-live="polite" aria-atomic="true">
|
||||||
|
{state.errors?.status && state.errors?.status.map(e => (
|
||||||
|
<p key={e} className="mt-2 text-sm text-red-500">{e}</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="form-error">
|
||||||
|
{
|
||||||
|
state.message && (<p className="mt-2 text-sm text-red-500 font-bold">{state.message}</p>)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex justify-end gap-4">
|
<div className="mt-6 flex justify-end gap-4">
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/invoices"
|
href="/dashboard/invoices"
|
||||||
className="flex h-10 items-center rounded-lg bg-gray-100 px-4 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-200"
|
className="flex h-10 items-center rounded-lg bg-gray-100 px-4 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-200"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
Reference in New Issue
Block a user