"use client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import Link from "next/link"; import { signup } from "@/actions/auth-actions"; import { cn } from "@/lib/utils"; import type { Island, Prisma } from "@prisma/client"; import { Loader2 } from "lucide-react"; import { useSearchParams } from "next/navigation"; import * as React from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from "@/components/ui/select"; type AtollWithIslands = Prisma.AtollGetPayload<{ include: { islands: true; }; }>; export default function SignUpForm({ atolls }: { atolls: AtollWithIslands[] }) { const [atoll, setAtoll] = React.useState<AtollWithIslands>(); const [islands, setIslands] = React.useState<Island[]>(); const [actionState, action, isPending] = React.useActionState(signup, { message: "", }); React.useEffect(() => { setIslands(atoll?.islands); }, [atoll]); const params = useSearchParams(); const phoneNumberFromUrl = params.get("phone_number"); const NUMBER_WITHOUT_DASH = phoneNumberFromUrl?.split("-").join(""); if (actionState.db_error === "invalidPersonValidation") { return ( <> <div className="h-24 w-72 text-center text-green-500 p-4 flex my-4 flex-col items-center justify-center border dark:title-bg bg-white rounded-lg">{actionState.message}</div> <div className="mb-4 text-center text-sm"> Go to {" "} <Link href="login" className="underline"> login </Link> </div> </> ) } return ( <form action={action} className="max-w-xs mt-2 w-full bg-white dark:bg-transparent dark:border-2 shadow rounded-lg mx-auto" > <div className="py-2 px-4 my-2 space-y-2"> <div> <label htmlFor="name" className="text-sm"> Name </label> <Input className={cn( "text-base", actionState?.errors?.fieldErrors.name && "border-2 border-red-500", )} name="name" type="text" disabled={isPending} defaultValue={(actionState?.payload?.get("name") || "") as string} placeholder="Full Name" /> {actionState?.errors?.fieldErrors.name && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors.name} </span> )} </div> <div> <label htmlFor="id_card" className="text-sm"> ID Card </label> <Input name="id_card" type="text" maxLength={7} disabled={isPending} defaultValue={(actionState?.payload?.get("id_card") || "") as string} className={cn( "text-base", actionState?.errors?.fieldErrors?.id_card && "border-2 border-red-500", )} placeholder="ID Card" /> {actionState?.errors?.fieldErrors?.id_card?.[0] && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors.fieldErrors.id_card[0]} </span> )} {actionState?.db_error === "id_card" && ( <span className="text-sm inline-block text-red-500"> {actionState?.message} </span> )} </div> <div> <div> <label htmlFor="atoll" className="text-sm"> Atoll </label> <Select disabled={isPending} onValueChange={(v) => { setAtoll(atolls.find((atoll) => atoll.id === v)); setIslands([]); }} name="atoll_id" value={atoll?.id} > <SelectTrigger className="w-full"> <SelectValue placeholder="Select atoll" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Atolls</SelectLabel> {atolls.map((atoll) => ( <SelectItem key={atoll.id} value={atoll.id}> {atoll.name} </SelectItem> ))} </SelectGroup> </SelectContent> {actionState?.errors?.fieldErrors?.atoll_id && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors?.atoll_id} </span> )} </Select> </div> <div> <label htmlFor="island" className="text-sm"> Island </label> <Select disabled={isPending} name="island_id"> <SelectTrigger className="w-full"> <SelectValue placeholder="Select island" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Islands</SelectLabel> {islands?.map((island) => ( <SelectItem key={island.id} value={island.id}> {island.name} </SelectItem> ))} </SelectGroup> </SelectContent> {actionState?.errors?.fieldErrors?.island_id && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors?.island_id} </span> )} </Select> </div> </div> <div> <label htmlFor="address" className="text-sm"> Address </label> <Input className={cn( "text-base", actionState?.errors?.fieldErrors?.address && "border-2 border-red-500", )} disabled={isPending} name="address" defaultValue={ (actionState?.payload?.get("address") || "") as string } type="text" placeholder="Address" /> {actionState?.errors?.fieldErrors?.address && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors?.address} </span> )} </div> <div> <label htmlFor="dob" className="text-sm"> Date of Birth </label> <Input className={cn( "text-base", actionState?.errors?.fieldErrors?.dob && "border-2 border-red-500", )} name="dob" disabled={isPending} defaultValue={(actionState?.payload?.get("dob") || "") as string} type="date" placeholder="Date of birth" /> {actionState?.errors?.fieldErrors?.dob && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors?.dob} </span> )} </div> <div> <label htmlFor="accNo" className="text-sm"> Account Number </label> <Input className={cn( "text-base", actionState?.errors?.fieldErrors.accNo && "border-2 border-red-500", )} name="accNo" type="number" disabled={isPending} defaultValue={(actionState?.payload?.get("accNo") || "") as string} placeholder="Account no" /> {actionState?.errors?.fieldErrors.accNo && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors.accNo} </span> )} </div> <div> <label htmlFor="phone_number" className="text-sm"> Phone Number </label> <Input id="phone-number" name="phone_number" maxLength={8} disabled={isPending} className={cn( !phoneNumberFromUrl && actionState?.errors?.fieldErrors?.phone_number && "border-2 border-red-500 rounded-md", )} defaultValue={NUMBER_WITHOUT_DASH ?? ""} readOnly={Boolean(phoneNumberFromUrl)} placeholder={phoneNumberFromUrl ?? "Phone number"} /> </div> {actionState?.errors?.fieldErrors?.phone_number?.[0] && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors.fieldErrors.phone_number[0]} </span> )} {actionState?.db_error === "phone_number" && ( <span className="text-sm inline-block text-red-500"> {actionState?.message} </span> )} <div className="flex flex-col gap-2 items-start justify-start py-2"> <div className="flex gap-2 items-center"> <input type="checkbox" defaultChecked={(actionState?.payload?.get("terms") || "") as string === 'on'} name="terms" id="terms" /> <label htmlFor="terms" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" > <span> i accept </span> <Link className="ml-1 underline" href=""> terms and conditions </Link> </label> </div> {actionState?.errors?.fieldErrors?.terms && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors?.terms} </span> )} <div className="flex gap-2 items-center"> <input type="checkbox" defaultChecked={(actionState?.payload?.get("policy") || "") as string === 'on'} name="policy" id="terms" /> <label htmlFor="terms" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" > <span> i undertand </span> <Link className="ml-1 underline" href=""> the privacy policy </Link> </label> </div> {actionState?.errors?.fieldErrors?.policy && ( <span className="text-sm inline-block text-red-500"> {actionState?.errors?.fieldErrors?.policy} </span> )} </div> <Button disabled={isPending} className="mt-4 w-full" type="submit"> {isPending ? <Loader2 className="animate-spin" /> : "Submit"} </Button> </div> <div className="mb-4 text-center text-sm"> Already have an account?{" "} <Link href="login" className="underline"> login </Link> </div> </form> ); }