"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 type { CommissionCustomInput, CommissionCustomCard, CommissionCustomCardExtra, CommissionCustomCardOption, 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 type * 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 CommissionCustomCardWithRelations = CommissionCustomCard & { options: (CommissionCustomCardOption & { option: CommissionOption })[]; extras: (CommissionCustomCardExtra & { extra: CommissionExtra })[]; }; type Props = { types: CommissionTypeWithRelations[]; customCards: CommissionCustomCardWithRelations[]; }; type SelectedOption = { id: string; optionId: string; option: CommissionOption; price: number | null; pricePercent: number | null; priceRange: string | null; }; type SelectedExtra = { id: string; extraId: string; extra: CommissionExtra; price: number | null; pricePercent: number | null; priceRange: string | null; }; export function CommissionOrderForm({ types, customCards }: Props) { const form = useForm>({ resolver: zodResolver(commissionOrderSchema), defaultValues: { typeId: "", customCardId: "", optionId: "", extraIds: [], customerName: "", customerEmail: "", customerSocials: "", message: "", }, }); const [files, setFiles] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); const typeId = useWatch({ control: form.control, name: "typeId" }); const customCardId = useWatch({ control: form.control, name: "customCardId" }); 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 selectedCustomCard = useMemo( () => customCards.find((c) => c.id === customCardId), [customCards, customCardId] ); const selection = useMemo<{ kind: "type" | "custom"; name: string; options: SelectedOption[]; extras: SelectedExtra[]; } | null>(() => { if (selectedCustomCard) { return { kind: "custom", name: selectedCustomCard.name, options: selectedCustomCard.options, extras: selectedCustomCard.extras, }; } if (selectedType) { return { kind: "type", name: selectedType.name, options: selectedType.options, extras: selectedType.extras, }; } return null; }, [selectedCustomCard, selectedType]); const selectedOption = useMemo( () => selection?.options.find((o) => o.optionId === optionId), [selection, optionId] ); const selectedExtras = useMemo( () => selection?.extras.filter((e) => extraIds?.includes(e.extraId)) ?? [], [selection, 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, customCardId: values.customCardId || 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: "", customCardId: "", 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) => ( ))}
)} /> {customCards.length > 0 ? ( ( Custom requests / YCH
{customCards.map((card) => ( ))}
)} /> ) : null} {selection && ( <> ( Base Option
{selection.options.map((opt) => ( ))}
)} /> ( Extras
{selection.extras.map((ext) => ( ))}
)} /> )}
( Your Name )} /> ( Email )} /> ( Socials )} />
( Project Details