"use client"; import { format } from "date-fns"; import { enUS, it } from "date-fns/locale"; import { CalendarIcon, HelpCircleIcon, Mail } from "lucide-react"; import { useRouter } from "next/router"; import { type Control, useWatch } from "react-hook-form"; import toast from "react-hot-toast"; import { z } from "zod/v4"; import { Counter } from "~/components/counter"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "~/components/custom_ui/form"; import Input from "~/components/custom_ui/input"; import InputWIcon from "~/components/custom_ui/input-icon"; import { MultiSelect, UnpackOptions } from "~/components/custom_ui/multiselect"; import { PhoneInput } from "~/components/phone-input"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Calendar } from "~/components/ui/calendar"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { Label } from "~/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger, } from "~/components/ui/popover"; import { Separator } from "~/components/ui/separator"; import { Slider } from "~/components/ui/slider"; import { Switch } from "~/components/ui/switch"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "~/components/ui/tooltip"; import { getProvinciaFromSigla, ITALY_CATASTO_CODE, parseDBComune, parseDBNazione, } from "~/lib/catasto"; import { checkFiscalCodeValidity, NullableStringOnChange, } from "~/lib/form_utils"; import { cn } from "~/lib/utils"; import { useZodForm } from "~/lib/zodForm"; import { useCatasto } from "~/providers/CatastoProvider"; import { useTranslation } from "~/providers/I18nProvider"; import type { Servizio, ServizioUpdate } from "~/schemas/public/Servizio"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import type { Users, UsersId } from "~/schemas/public/Users"; import type { UsersAnagrafica, UsersAnagraficaUpdate, } from "~/schemas/public/UsersAnagrafica"; import { api } from "~/utils/api"; type InitialData = { servizio: Servizio; userData: Pick; anagrafica: UsersAnagrafica | undefined; }; const FormNewServizioSchema = z.object({ animali: z.boolean(), arredato: z.boolean(), ascensore: z.boolean(), budget: z.number().positive(), cap_recapiti: z.string().length(5), citta_recapiti: z.string().nullable(), civico_recapiti: z.string(), codice_fiscale: z.string(), cognome: z.string().nonempty("Inserisci un cognome valido"), data_nascita: z.date(), email: z.email(), entromese: z.number().nullable(), fumatori: z.boolean(), giardino: z.boolean(), indirizzo_recapiti: z.string().nonempty("Inserisci un indirizzo"), luogo_nascita: z.string().nullable(), motivazione_transitorio: z.string().nullable(), n_adulti: z.number().min(1), n_minori: z.number(), nazione_nascita: z.string().nullable(), nazione_recapiti: z.string().nullable(), nome: z.string().nonempty("Inserisci un nome valido"), parcheggio: z.boolean(), permanenza: z.number().nullable(), pianoterra: z.boolean(), provincia_recapiti: z.string().nullable(), reddito: z.string().nullable(), sesso: z.string(), telefono: z.string(), terrazzo: z.boolean(), tipologia: z.custom(), scadenza_motivazione_transitoria: z.date().nullable(), skipPayment: z.boolean(), }); type FormNewServizioValues = z.infer; export const FormNewServizioAcquisto = ({ initialData, userId, isAdmin, }: { initialData: InitialData; userId: UsersId; isAdmin: boolean; }) => { const router = useRouter(); const { comuni, nazioni } = useCatasto(); console.log(isAdmin); const schema = FormNewServizioSchema.superRefine( ( { telefono, codice_fiscale, sesso, cognome, nome, data_nascita, luogo_nascita, nazione_nascita, n_adulti, permanenza, entromese, motivazione_transitorio, scadenza_motivazione_transitoria, reddito, citta_recapiti, nazione_recapiti, provincia_recapiti, }, refinementContext, ) => { if (entromese === null) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci il mese di inizio", path: ["entromese"], }); } if (!telefono || telefono.length < 10) { refinementContext.issues.push({ code: "custom", input: "", message: "Il numero di telefono deve essere lungo almeno 10 caratteri", path: ["telefono"], }); } if (!luogo_nascita || luogo_nascita.length < 1) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci il luogo di nascita", path: ["luogo_nascita"], }); } if (!nazione_nascita || nazione_nascita.length < 1) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci la nazione di nascita", path: ["nazione_nascita"], }); } if (!citta_recapiti || citta_recapiti.length < 1) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci la città di recapiti", path: ["citta_recapiti"], }); } if (!nazione_recapiti || nazione_recapiti.length < 1) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci la nazione di recapiti", path: ["nazione_recapiti"], }); } if (!provincia_recapiti || provincia_recapiti.length < 1) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci la provincia di recapiti", path: ["provincia_recapiti"], }); } if (luogo_nascita && nazione_nascita) { checkFiscalCodeValidity({ codiceFiscale: codice_fiscale, cognome, dataNascita: data_nascita, luogoNascita: luogo_nascita, nazioneNascita: nazione_nascita, nome, path: "codice_fiscale", refinementContext, sesso, comuneCatasto: comuni.find((c) => c.catasto === luogo_nascita), nazioneCatasto: nazioni.find((c) => c.catasto === nazione_nascita), }); } if (n_adulti <= 0) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci il numero di persone", path: ["n_adulti"], }); } if (tipologia === TipologiaPosizioneEnum.Transitorio) { if ( motivazione_transitorio === "" || motivazione_transitorio === null ) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci la motivazione", path: ["motivazione_transitorio"], }); } if (!scadenza_motivazione_transitoria) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci la scadenza della motivazione", path: ["scadenza_motivazione_transitoria"], }); } if (permanenza == null) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci permanenza", path: ["permanenza"], }); } } else { if (reddito === "" || reddito === null) { refinementContext.issues.push({ code: "custom", input: "", message: "Inserisci il reddito", path: ["reddito"], }); } } }, ); // This can come from your database or API. const defaultValues: FormNewServizioValues = { ...initialData.userData, cap_recapiti: initialData.anagrafica?.cap_residenza || "", citta_recapiti: initialData.anagrafica?.comune_residenza || null, civico_recapiti: initialData.anagrafica?.civico_residenza || "", codice_fiscale: initialData.anagrafica?.codice_fiscale || "", data_nascita: initialData.anagrafica?.data_nascita ? new Date(initialData.anagrafica.data_nascita) : new Date(), indirizzo_recapiti: initialData.anagrafica?.via_residenza || "", luogo_nascita: initialData.anagrafica?.luogo_nascita || null, nazione_nascita: initialData.anagrafica?.nazione_nascita || null, nazione_recapiti: initialData.anagrafica?.nazione_residenza || null, provincia_recapiti: initialData.anagrafica?.provincia_residenza || null, sesso: initialData.anagrafica?.sesso || "M", ...initialData.servizio, }; const { locale, t } = useTranslation(); z.config(z.locales[locale]()); const form = useZodForm(schema, { defaultValues: defaultValues, }); const DatePickerLocale = locale === "it" ? it : enUS; const { mutate } = api.servizio.processServizioOnboard.useMutation({ onError: (error) => { toast.error(`Errore invio dati: ${error.message}`, { id: "salvataggio", }); }, onMutate: () => { toast.loading("Invio dati in corso", { id: "salvataggio", }); }, onSuccess: async () => { toast.success("Servizio impostato con successo", { id: "salvataggio", }); if (isAdmin) { await router.push( `/area-riservata/admin/user-view/servizio/${userId}/${initialData.servizio.servizio_id}`, ); } else { await router.push( `/area-riservata/servizio/${initialData.servizio.servizio_id}`, ); } }, }); function onSubmit(fields: FormNewServizioValues) { const a: UsersAnagraficaUpdate = { cap_residenza: fields.cap_recapiti, civico_residenza: fields.civico_recapiti, codice_fiscale: fields.codice_fiscale, comune_residenza: fields.citta_recapiti, data_nascita: fields.data_nascita, idanagrafica: initialData.anagrafica?.idanagrafica, luogo_nascita: fields.luogo_nascita, nazione_nascita: fields.nazione_nascita, nazione_residenza: fields.nazione_recapiti, provincia_residenza: fields.provincia_recapiti, sesso: fields.sesso, userid: userId, via_residenza: fields.indirizzo_recapiti, }; const s: ServizioUpdate = { animali: fields.animali, arredato: fields.arredato, ascensore: fields.ascensore, budget: fields.budget, entromese: fields.entromese, fumatori: fields.fumatori, giardino: fields.giardino, motivazione_transitorio: fields.motivazione_transitorio, n_adulti: fields.n_adulti, n_minori: fields.n_minori, parcheggio: fields.parcheggio, permanenza: fields.permanenza, pianoterra: fields.pianoterra, reddito: fields.reddito, terrazzo: fields.terrazzo, tipologia: fields.tipologia, skipPayment: fields.skipPayment, scadenza_motivazione_transitoria: fields.scadenza_motivazione_transitoria, }; const u: Pick< Users, "nome" | "cognome" | "email" | "telefono" | "username" > = { cognome: fields.cognome, email: fields.email, nome: fields.nome, telefono: fields.telefono.replace(/\s/g, ""), username: `${fields.nome.trim()} ${fields.cognome.trim()}`, }; mutate({ anagrafica: a, servizio: s, servizioId: initialData.servizio.servizio_id, user: u, userId, }); } type altrePrefMappingType = [ ["animali", string], ["fumatori", string], ["giardino", string], ["parcheggio", string], ["terrazzo", string], ["ascensore", string], ["pianoterra", string], ]; const altrePrefMapping = t.altriParamsMapping as altrePrefMappingType; const [tipologia, nazione_nascita, budget] = form.watch([ "tipologia", "nazione_nascita", "budget", ]); const texts = locale === "it" ? { dettagli: "Dettagli del servizio", dettagli_desc: "Questi dati rappresentano la tua ricerca", personali: "Dati personali", personali_desc: "Inserisci i tuoi dati personali", salva: "Salva e procedi", sdi: "Codice destinatario SDI", } : { dettagli: "Service Details", dettagli_desc: "These data represent your search", personali: "Personal Data", personali_desc: "Enter your personal data", salva: "Save and proceed", sdi: "SDI Recipient Code", }; return (
{texts.dettagli} {texts.dettagli_desc} (
{t.parametri.tipologia}
{ field.onChange(value.value); form.setValue("motivazione_transitorio", null); form.setValue("reddito", null); }} options={[ { label: "Transitorio", value: TipologiaPosizioneEnum.Transitorio, }, { label: "Stabile", value: TipologiaPosizioneEnum.Stabile, }, ]} placeholder={t.seleziona_placeholder} />
)} /> (
{`${t.parametri.budget_label1} - ${field.value},00 ${ t.parametri.budget_label2 }`}
{ field.onChange(Number(vals[0])); }} step={50} value={[budget]} /> {t.parametri.budget_desc}
)} />
(
{t.parametri.dalmese_label}...
label: t.parametri.mesi[field.value]!, value: String(field.value), } : undefined } elementId="select-mesi" elementName="entro_mese" isMulti={false} onValueChange={(value) => { field.onChange( value.value !== "" ? parseInt(value.value) : null, ); }} options={UnpackOptions({ options: t.parametri.mesi, })} placeholder="Seleziona..." />
)} /> (
{t.parametri.permanenza_label}
{ field.onChange( value.value !== "" ? parseInt(value.value) : null, ); }} options={UnpackOptions({ options: t.parametri.permanenza, })} placeholder="Seleziona..." /> {tipologia === TipologiaPosizioneEnum.Transitorio && ( {t.parametri.permanenza_desc} )}
)} />
(
{t.parametri.nadulti_label}
field.onChange(Number(v))} value={field.value} />
)} /> (
{t.parametri.nminori_label}
field.onChange(Number(v))} value={field.value} />
)} />
(
{t.parametri.arredato_label}
{t.parametri.arredato_desc}
)} />
{tipologia === TipologiaPosizioneEnum.Transitorio ? (
{ const existingValue = t.parametri.motivazioni_mappings.find( (m) => m.id === field.value, ); return (
{t.parametri.motivazione_label}
{ field.onChange(value.value); }} options={t.parametri.motivazioni_mappings.map( (option) => ({ label: option.title, value: option.id, }), )} placeholder={t.seleziona_placeholder} /> {t.parametri.motivazione_desc}
); }} /> (
Scadenza motivazione
)} />
) : ( (
{t.parametri.reddito_label}
{ field.onChange(value.value); }} options={UnpackOptions({ options: t.parametri.reddito_options, })} placeholder={t.seleziona_placeholder} />
)} /> )}
{t.parametri.altriparams_desc}
{altrePrefMapping.map((pair) => { if (pair) { return ( ( { field.onChange(!field.value); }} variant={field.value ? "default" : "secondary"} > {pair[1]} )} /> ); } })}
{texts.personali} {texts.personali_desc}
(
{t.account.nome}
)} /> (
{t.account.cognome}
)} />
(
{t.anagrafica.dataNascita}
)} /> { const parsedNazioneNascita = parseDBNazione( field.value, nazioni, ); return (
{t.anagrafica.nazioneNascita}
{ field.onChange(value.value); await form.trigger("luogo_nascita"); form.setValue("luogo_nascita", null); }} options={nazioni.map((n) => ({ label: n.nome, value: n.catasto, }))} placeholder="Seleziona..." />
); }} /> { if (nazione_nascita === ITALY_CATASTO_CODE) { const parsedComuneNascita = parseDBComune( field.value, comuni, ); return (
{t.anagrafica.luogoNascita}
{ field.onChange(value.value); await form.trigger("luogo_nascita"); await form.trigger("codice_fiscale"); }} options={comuni.map((c) => ({ label: `${c.nome} (${c.sigla})`, value: c.catasto, }))} placeholder="Seleziona..." />
); } return (
{t.anagrafica.luogoNascita}
{ NullableStringOnChange(e, field.onChange); await form.trigger("luogo_nascita"); await form.trigger("codice_fiscale"); }} type="text" value={field.value || ""} />
); }} /> (
{t.anagrafica.sesso}{" "} {t.anagrafica.sesso_disclamer}
v.value === field.value, )} elementId="select-sesso" elementName="sesso" isMulti={false} onValueChange={(value) => { field.onChange(value.value); }} options={t.anagrafica.sesso_options} placeholder="" />
)} />
(
{t.anagrafica.codiceFiscale}
{ field.onChange(v.target.value.toUpperCase()); await form.trigger("codice_fiscale"); }} type="text" />
)} /> (
{t.account.email}
)} /> (
{t.account.telefono}
)} />
(
{t.recapiti.indirizzo}
)} /> (
{t.recapiti.civico}
)} />
); }; const ResidenzaSection = ({ control, trigger, setValue, }: { control: Control; trigger: (name?: keyof FormNewServizioValues) => Promise; // biome-ignore lint/suspicious/noExplicitAny: setValue: (field: keyof FormNewServizioValues, value: any) => void; }) => { const { t } = useTranslation(); const { comuni, nazioni, provincie } = useCatasto(); const nazione_recapiti = useWatch({ control, name: "nazione_recapiti", }); const provincia_recapiti = useWatch({ control, name: "provincia_recapiti", }); return ( <>
{ const parsedNazioneResidenza = parseDBNazione(field.value, nazioni); return (
{t.recapiti.nazione}
{ setValue("provincia_recapiti", null); setValue("citta_recapiti", null); field.onChange(value.value); await trigger("citta_recapiti"); }} options={nazioni.map((n) => ({ label: n.nome, value: n.catasto, }))} placeholder="Seleziona..." />
); }} /> { const parseProvincia = getProvinciaFromSigla( field.value, provincie, ); return (
{t.recapiti.provincia}
{nazione_recapiti === ITALY_CATASTO_CODE ? ( { field.onChange(value.value); setValue("citta_recapiti", null); }} options={provincie.map((prov) => ({ label: prov.nome, value: prov.sigla, }))} placeholder="Seleziona" /> ) : ( NullableStringOnChange(e, field.onChange) } type="text" value={field.value || ""} /> )}
); }} />
{ if (nazione_recapiti === ITALY_CATASTO_CODE) { const parsedComuneResidenza = parseDBComune(field.value, comuni); const options = comuni .filter((comune) => provincia_recapiti ? comune.sigla === provincia_recapiti : true, ) .map((comune) => ({ label: comune.nome, value: comune.catasto, })); return (
{t.recapiti.comune}
{ field.onChange(value.value); await trigger("citta_recapiti"); }} options={options} placeholder="Seleziona..." />
); } return (
{t.recapiti.comune}
{ NullableStringOnChange(e, field.onChange); await trigger("citta_recapiti"); }} type="text" value={field.value || ""} />
); }} /> (
{t.recapiti.cap}
)} />
); };