"use client"; import { submitCommissionRequest } from "@/actions/commissions/submitCommissionRequest"; import { Button } from "@/components/ui/button"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { CommissionCustomInput, CommissionExtra, CommissionOption, CommissionType, CommissionTypeCustomInput, CommissionTypeExtra, CommissionTypeOption, } from "@/generated/prisma/client"; import { commissionOrderSchema } from "@/schemas/commissionOrder"; import { calculatePriceRange } from "@/utils/calculatePrice"; import { zodResolver } from "@hookform/resolvers/zod"; import Link from "next/link"; import { useMemo, useState } from "react"; import { useForm, useWatch } from "react-hook-form"; import { toast } from "sonner"; import * as z from "zod/v4"; import { FileDropzone } from "./FileDropzone"; type CommissionTypeWithRelations = CommissionType & { options: (CommissionTypeOption & { option: CommissionOption })[]; extras: (CommissionTypeExtra & { extra: CommissionExtra })[]; customInputs: (CommissionTypeCustomInput & { customInput: CommissionCustomInput })[]; }; type Props = { types: CommissionTypeWithRelations[]; }; export function CommissionOrderForm({ types }: Props) { const form = useForm>({ resolver: zodResolver(commissionOrderSchema), defaultValues: { typeId: "", optionId: "", extraIds: [], customerName: "", customerEmail: "", customerSocials: "", message: "", }, }); const [files, setFiles] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); const typeId = useWatch({ control: form.control, name: "typeId" }); const optionId = useWatch({ control: form.control, name: "optionId" }); const extraIds = useWatch({ control: form.control, name: "extraIds" }); const selectedType = useMemo(() => types.find((t) => t.id === typeId), [types, typeId]); const selectedOption = useMemo( () => selectedType?.options.find((o) => o.optionId === optionId), [selectedType, optionId] ); const selectedExtras = useMemo( () => selectedType?.extras.filter((e) => extraIds?.includes(e.extraId)) ?? [], [selectedType, extraIds] ); const [minPrice, maxPrice] = useMemo(() => { return calculatePriceRange(selectedOption, selectedExtras); }, [selectedOption, selectedExtras]); async function onSubmit(values: z.infer) { setIsSubmitting(true); try { const payload = { typeId: values.typeId || null, optionId: values.optionId || null, extraIds: values.extraIds ?? [], customerName: values.customerName, customerEmail: values.customerEmail, customerSocials: values.customerSocials ?? null, message: values.message, }; await submitCommissionRequest({ payload, files }); toast.success("Request submitted", { description: "Thanks! I’ll get back to you as soon as possible.", }); form.reset({ typeId: "", optionId: "", extraIds: [], customerName: "", customerEmail: "", customerSocials: "", message: "", }); setFiles([]); form.clearErrors(); } catch (err) { const message = err instanceof Error ? err.message : "Submission failed. Please try again."; toast.error("Submission failed", { description: message }); } finally { setIsSubmitting(false); } } return (
( Choose a commission type
{types.map((type) => ( ))}
)} /> {selectedType && ( <> ( Base Option
{selectedType.options.map((opt) => ( ))}
)} /> ( Extras
{selectedType.extras.map((ext) => ( ))}
)} /> )}
( Your Name )} /> ( Email )} /> ( Socials )} />
( Project Details