2025-08-04 17:45:44 +02:00
|
|
|
"use client";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { format } from "date-fns";
|
|
|
|
|
import { enUS, it } from "date-fns/locale";
|
|
|
|
|
import { CalendarIcon, HelpCircleIcon, Mail } from "lucide-react";
|
|
|
|
|
import { useRouter } from "next/router";
|
2025-10-30 18:13:50 +01:00
|
|
|
import { type Control, useWatch } from "react-hook-form";
|
2025-08-28 18:27:07 +02:00
|
|
|
import toast from "react-hot-toast";
|
2025-08-07 14:50:40 +02:00
|
|
|
import { z } from "zod/v4";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { Counter } from "~/components/counter";
|
2025-08-04 17:45:44 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Form,
|
|
|
|
|
FormControl,
|
|
|
|
|
FormDescription,
|
|
|
|
|
FormField,
|
|
|
|
|
FormItem,
|
|
|
|
|
FormLabel,
|
|
|
|
|
FormMessage,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "~/components/custom_ui/form";
|
|
|
|
|
import Input from "~/components/custom_ui/input";
|
|
|
|
|
import InputWIcon from "~/components/custom_ui/input-icon";
|
2025-11-13 15:45:55 +01:00
|
|
|
import { MultiSelect, UnpackOptions } from "~/components/custom_ui/multiselect";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { PhoneInput } from "~/components/phone-input";
|
|
|
|
|
import { Badge } from "~/components/ui/badge";
|
|
|
|
|
import { Button } from "~/components/ui/button";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { Calendar } from "~/components/ui/calendar";
|
|
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Card,
|
|
|
|
|
CardContent,
|
|
|
|
|
CardDescription,
|
|
|
|
|
CardHeader,
|
|
|
|
|
CardTitle,
|
|
|
|
|
} from "~/components/ui/card";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { Label } from "~/components/ui/label";
|
|
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Popover,
|
|
|
|
|
PopoverContent,
|
|
|
|
|
PopoverTrigger,
|
|
|
|
|
} from "~/components/ui/popover";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { Separator } from "~/components/ui/separator";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { Slider } from "~/components/ui/slider";
|
|
|
|
|
import { Switch } from "~/components/ui/switch";
|
|
|
|
|
import {
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipProvider,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "~/components/ui/tooltip";
|
2025-11-13 15:45:55 +01:00
|
|
|
import {
|
|
|
|
|
getProvinciaFromSigla,
|
|
|
|
|
ITALY_CATASTO_CODE,
|
|
|
|
|
parseDBComune,
|
|
|
|
|
parseDBNazione,
|
|
|
|
|
} from "~/lib/catasto";
|
2025-12-17 16:38:15 +01:00
|
|
|
import {
|
|
|
|
|
checkFiscalCodeValidity,
|
|
|
|
|
NullableStringOnChange,
|
|
|
|
|
} from "~/lib/form_utils";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { cn } from "~/lib/utils";
|
|
|
|
|
import { useZodForm } from "~/lib/zodForm";
|
2025-11-13 15:45:55 +01:00
|
|
|
import { useCatasto } from "~/providers/CatastoProvider";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
2026-01-16 15:47:38 +01:00
|
|
|
import type { Servizio, ServizioUpdate } from "~/schemas/public/Servizio";
|
2025-08-28 18:27:07 +02:00
|
|
|
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
|
|
|
|
import type { Users, UsersId } from "~/schemas/public/Users";
|
2026-01-16 15:47:38 +01:00
|
|
|
import type {
|
|
|
|
|
UsersAnagrafica,
|
|
|
|
|
UsersAnagraficaUpdate,
|
|
|
|
|
} from "~/schemas/public/UsersAnagrafica";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { api } from "~/utils/api";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
type InitialData = {
|
2025-08-28 18:27:07 +02:00
|
|
|
servizio: Servizio;
|
|
|
|
|
userData: Pick<Users, "id" | "nome" | "cognome" | "email" | "telefono">;
|
|
|
|
|
anagrafica: UsersAnagrafica | undefined;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
2025-10-30 18:13:50 +01:00
|
|
|
const FormNewServizioSchema = z.object({
|
|
|
|
|
animali: z.boolean(),
|
|
|
|
|
arredato: z.boolean(),
|
|
|
|
|
ascensore: z.boolean(),
|
|
|
|
|
budget: z.number().positive(),
|
|
|
|
|
cap_recapiti: z.string().length(5),
|
2025-11-13 15:45:55 +01:00
|
|
|
citta_recapiti: z.string().nullable(),
|
|
|
|
|
civico_recapiti: z.string(),
|
2025-10-30 18:13:50 +01:00
|
|
|
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"),
|
2025-11-13 15:45:55 +01:00
|
|
|
luogo_nascita: z.string().nullable(),
|
2025-10-30 18:13:50 +01:00
|
|
|
motivazione_transitorio: z.string().nullable(),
|
|
|
|
|
n_adulti: z.number().min(1),
|
|
|
|
|
n_minori: z.number(),
|
2025-11-13 15:45:55 +01:00
|
|
|
nazione_nascita: z.string().nullable(),
|
|
|
|
|
nazione_recapiti: z.string().nullable(),
|
2025-10-30 18:13:50 +01:00
|
|
|
nome: z.string().nonempty("Inserisci un nome valido"),
|
|
|
|
|
parcheggio: z.boolean(),
|
|
|
|
|
permanenza: z.number().nullable(),
|
|
|
|
|
pianoterra: z.boolean(),
|
2025-11-13 15:45:55 +01:00
|
|
|
provincia_recapiti: z.string().nullable(),
|
2025-10-30 18:13:50 +01:00
|
|
|
reddito: z.string().nullable(),
|
|
|
|
|
sesso: z.string(),
|
|
|
|
|
telefono: z.string(),
|
|
|
|
|
terrazzo: z.boolean(),
|
|
|
|
|
tipologia: z.custom<TipologiaPosizioneEnum>(),
|
2025-10-30 19:39:35 +01:00
|
|
|
scadenza_motivazione_transitoria: z.date().nullable(),
|
2025-11-06 17:03:42 +01:00
|
|
|
skipPayment: z.boolean(),
|
2025-10-30 18:13:50 +01:00
|
|
|
});
|
|
|
|
|
type FormNewServizioValues = z.infer<typeof FormNewServizioSchema>;
|
|
|
|
|
|
2025-08-04 17:45:44 +02:00
|
|
|
export const FormNewServizioAcquisto = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
initialData,
|
|
|
|
|
userId,
|
|
|
|
|
isAdmin = false,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
initialData: InitialData;
|
|
|
|
|
userId: UsersId;
|
|
|
|
|
isAdmin?: boolean;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const router = useRouter();
|
2025-11-13 15:45:55 +01:00
|
|
|
const { comuni, nazioni } = useCatasto();
|
2025-10-30 18:13:50 +01:00
|
|
|
const schema = FormNewServizioSchema.superRefine(
|
|
|
|
|
(
|
|
|
|
|
{
|
|
|
|
|
telefono,
|
|
|
|
|
codice_fiscale,
|
|
|
|
|
sesso,
|
|
|
|
|
cognome,
|
|
|
|
|
nome,
|
|
|
|
|
data_nascita,
|
|
|
|
|
luogo_nascita,
|
|
|
|
|
nazione_nascita,
|
|
|
|
|
n_adulti,
|
|
|
|
|
permanenza,
|
|
|
|
|
entromese,
|
|
|
|
|
motivazione_transitorio,
|
2025-10-30 19:39:35 +01:00
|
|
|
scadenza_motivazione_transitoria,
|
2025-10-30 18:13:50 +01:00
|
|
|
reddito,
|
2025-11-13 15:45:55 +01:00
|
|
|
citta_recapiti,
|
|
|
|
|
nazione_recapiti,
|
|
|
|
|
provincia_recapiti,
|
2025-10-30 18:13:50 +01:00
|
|
|
},
|
|
|
|
|
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",
|
2025-11-13 15:45:55 +01:00
|
|
|
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),
|
2025-10-30 18:13:50 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (n_adulti <= 0) {
|
|
|
|
|
refinementContext.issues.push({
|
|
|
|
|
code: "custom",
|
|
|
|
|
input: "",
|
|
|
|
|
message: "Inserisci il numero di persone",
|
|
|
|
|
path: ["n_adulti"],
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-11-03 10:59:45 +01:00
|
|
|
if (!permanenza && tipologia === TipologiaPosizioneEnum.Transitorio) {
|
2025-10-30 18:13:50 +01:00
|
|
|
refinementContext.issues.push({
|
|
|
|
|
code: "custom",
|
|
|
|
|
input: "",
|
|
|
|
|
message: "Inserisci permanenza",
|
|
|
|
|
path: ["permanenza"],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (tipologia === TipologiaPosizioneEnum.Transitorio) {
|
2025-08-28 18:27:07 +02:00
|
|
|
if (
|
2025-10-30 18:13:50 +01:00
|
|
|
motivazione_transitorio === "" ||
|
|
|
|
|
motivazione_transitorio === null
|
2025-08-28 18:27:07 +02:00
|
|
|
) {
|
|
|
|
|
refinementContext.issues.push({
|
|
|
|
|
code: "custom",
|
2025-08-29 16:18:32 +02:00
|
|
|
input: "",
|
2025-10-30 18:13:50 +01:00
|
|
|
message: "Inserisci la motivazione",
|
|
|
|
|
path: ["motivazione_transitorio"],
|
2025-08-28 18:27:07 +02:00
|
|
|
});
|
|
|
|
|
}
|
2025-10-30 19:39:35 +01:00
|
|
|
if (!scadenza_motivazione_transitoria) {
|
|
|
|
|
refinementContext.issues.push({
|
|
|
|
|
code: "custom",
|
|
|
|
|
input: "",
|
|
|
|
|
message: "Inserisci la scadenza della motivazione",
|
|
|
|
|
path: ["scadenza_motivazione_transitoria"],
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-11-03 10:59:45 +01:00
|
|
|
if (!permanenza) {
|
|
|
|
|
refinementContext.issues.push({
|
|
|
|
|
code: "custom",
|
|
|
|
|
input: "",
|
|
|
|
|
message: "Inserisci permanenza",
|
|
|
|
|
path: ["permanenza"],
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-10-30 18:13:50 +01:00
|
|
|
} else {
|
|
|
|
|
if (reddito === "" || reddito === null) {
|
|
|
|
|
refinementContext.issues.push({
|
|
|
|
|
code: "custom",
|
|
|
|
|
input: "",
|
|
|
|
|
message: "Inserisci il reddito",
|
|
|
|
|
path: ["reddito"],
|
|
|
|
|
});
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
2025-10-30 18:13:50 +01:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
// This can come from your database or API.
|
2025-10-30 18:13:50 +01:00
|
|
|
const defaultValues: FormNewServizioValues = {
|
2025-08-28 18:27:07 +02:00
|
|
|
...initialData.userData,
|
2025-08-29 16:18:32 +02:00
|
|
|
cap_recapiti: initialData.anagrafica?.cap_residenza || "",
|
2025-11-13 15:45:55 +01:00
|
|
|
citta_recapiti: initialData.anagrafica?.comune_residenza || null,
|
2025-08-29 16:18:32 +02:00
|
|
|
civico_recapiti: initialData.anagrafica?.civico_residenza || "",
|
2025-08-28 18:27:07 +02:00
|
|
|
codice_fiscale: initialData.anagrafica?.codice_fiscale || "",
|
|
|
|
|
data_nascita: initialData.anagrafica?.data_nascita
|
|
|
|
|
? new Date(initialData.anagrafica.data_nascita)
|
|
|
|
|
: new Date(),
|
2025-08-29 16:18:32 +02:00
|
|
|
indirizzo_recapiti: initialData.anagrafica?.via_residenza || "",
|
2025-11-13 15:45:55 +01:00
|
|
|
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,
|
2025-08-29 16:18:32 +02:00
|
|
|
sesso: initialData.anagrafica?.sesso || "M",
|
2025-08-28 18:27:07 +02:00
|
|
|
...initialData.servizio,
|
|
|
|
|
};
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const { locale, t } = useTranslation();
|
|
|
|
|
z.config(z.locales[locale]());
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-10-30 18:13:50 +01:00
|
|
|
const form = useZodForm(schema, {
|
2025-08-28 18:27:07 +02:00
|
|
|
defaultValues: defaultValues,
|
|
|
|
|
});
|
|
|
|
|
const DatePickerLocale = locale === "it" ? it : enUS;
|
2025-11-03 10:59:45 +01:00
|
|
|
const { mutate } = api.servizio.submitOnboardForPurchase.useMutation({
|
2025-08-29 16:18:32 +02:00
|
|
|
onError: (error) => {
|
|
|
|
|
toast.error(`Errore invio dati: ${error.message}`, {
|
|
|
|
|
id: "salvataggio",
|
|
|
|
|
});
|
|
|
|
|
},
|
2025-08-28 18:27:07 +02:00
|
|
|
onMutate: () => {
|
|
|
|
|
toast.loading("Invio dati in corso", {
|
|
|
|
|
id: "salvataggio",
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
onSuccess: async (data) => {
|
|
|
|
|
toast.success("Servizio impostato con successo", {
|
|
|
|
|
id: "salvataggio",
|
|
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
if (isAdmin) {
|
2025-11-06 17:03:42 +01:00
|
|
|
if (data.ordineIdForPayment) {
|
2025-08-28 18:27:07 +02:00
|
|
|
await router.push(`/area-riservata/admin/user-view/ordini/${userId}`);
|
|
|
|
|
} else {
|
|
|
|
|
await router.push(
|
|
|
|
|
`/area-riservata/admin/user-view/ricerca/${userId}`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-11-06 17:03:42 +01:00
|
|
|
if (data.ordineIdForPayment) {
|
|
|
|
|
await router.push(`/servizio/pagamento/${data.ordineIdForPayment}`);
|
|
|
|
|
} else {
|
|
|
|
|
await router.push("/area-riservata/dashboard");
|
|
|
|
|
}
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-10-30 18:13:50 +01:00
|
|
|
function onSubmit(fields: FormNewServizioValues) {
|
2026-01-16 15:47:38 +01:00
|
|
|
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()}`,
|
|
|
|
|
};
|
2025-08-28 18:27:07 +02:00
|
|
|
mutate({
|
2026-01-16 15:47:38 +01:00
|
|
|
anagrafica: a,
|
|
|
|
|
servizio: s,
|
2025-08-29 16:18:32 +02:00
|
|
|
servizioId: initialData.servizio.servizio_id,
|
2026-01-16 15:47:38 +01:00
|
|
|
user: u,
|
2025-08-29 16:18:32 +02:00
|
|
|
userId,
|
2025-08-28 18:27:07 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
type altrePrefMappingType = [
|
|
|
|
|
["animali", string],
|
|
|
|
|
["fumatori", string],
|
|
|
|
|
["giardino", string],
|
|
|
|
|
["parcheggio", string],
|
|
|
|
|
["terrazzo", string],
|
|
|
|
|
["ascensore", string],
|
|
|
|
|
["pianoterra", string],
|
|
|
|
|
];
|
2025-12-18 12:11:08 +01:00
|
|
|
const altrePrefMapping = t.altriParamsMapping as altrePrefMappingType;
|
2025-11-13 15:45:55 +01:00
|
|
|
const [tipologia, nazione_nascita, budget] = form.watch([
|
2025-08-28 18:27:07 +02:00
|
|
|
"tipologia",
|
|
|
|
|
"nazione_nascita",
|
|
|
|
|
"budget",
|
|
|
|
|
]);
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
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",
|
2025-11-03 10:59:45 +01:00
|
|
|
salva: "Salva e procedi",
|
2025-08-29 16:18:32 +02:00
|
|
|
sdi: "Codice destinatario SDI",
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
dettagli: "Service Details",
|
|
|
|
|
dettagli_desc: "These data represent your search",
|
|
|
|
|
personali: "Personal Data",
|
|
|
|
|
personali_desc: "Enter your personal data",
|
2025-11-03 10:59:45 +01:00
|
|
|
salva: "Save and proceed",
|
2025-08-29 16:18:32 +02:00
|
|
|
sdi: "SDI Recipient Code",
|
2025-08-28 18:27:07 +02:00
|
|
|
};
|
2025-08-13 17:29:26 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<Form {...form}>
|
2025-10-30 18:13:50 +01:00
|
|
|
<form
|
|
|
|
|
className="space-y-8 px-0.5 pb-5"
|
|
|
|
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
|
|
|
>
|
2025-08-28 18:27:07 +02:00
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>{texts.dettagli}</CardTitle>
|
|
|
|
|
<CardDescription>{texts.dettagli_desc}</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-6">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="tipologia"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem>
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-tipologia">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.tipologia}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
field.value === TipologiaPosizioneEnum.Transitorio
|
|
|
|
|
? {
|
|
|
|
|
label: "Transitorio",
|
2025-08-29 16:18:32 +02:00
|
|
|
value: TipologiaPosizioneEnum.Transitorio,
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
label: "Stabile",
|
2025-08-29 16:18:32 +02:00
|
|
|
value: TipologiaPosizioneEnum.Stabile,
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-29 16:18:32 +02:00
|
|
|
elementId="select-tipologia"
|
|
|
|
|
elementName="tipologia"
|
|
|
|
|
isMulti={false}
|
|
|
|
|
onValueChange={(value) => {
|
|
|
|
|
field.onChange(value.value);
|
|
|
|
|
form.setValue("motivazione_transitorio", null);
|
|
|
|
|
form.setValue("reddito", null);
|
|
|
|
|
}}
|
2025-08-28 18:27:07 +02:00
|
|
|
options={[
|
|
|
|
|
{
|
|
|
|
|
label: "Transitorio",
|
2025-08-29 16:18:32 +02:00
|
|
|
value: TipologiaPosizioneEnum.Transitorio,
|
2025-08-28 18:27:07 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: "Stabile",
|
2025-08-29 16:18:32 +02:00
|
|
|
value: TipologiaPosizioneEnum.Stabile,
|
2025-08-28 18:27:07 +02:00
|
|
|
},
|
|
|
|
|
]}
|
2025-08-29 16:18:32 +02:00
|
|
|
placeholder={t.seleziona_placeholder}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="budget"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem>
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2 pb-1">
|
|
|
|
|
<FormLabel htmlFor="budgetslider">
|
2025-12-18 12:11:08 +01:00
|
|
|
{`${t.parametri.budget_label1} - ${field.value},00 ${
|
|
|
|
|
t.parametri.budget_label2
|
2025-08-28 18:27:07 +02:00
|
|
|
}`}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Slider
|
|
|
|
|
defaultValue={[field.value]}
|
2025-08-29 16:18:32 +02:00
|
|
|
id="budgetslider"
|
|
|
|
|
max={2000}
|
|
|
|
|
min={0}
|
2025-08-28 18:27:07 +02:00
|
|
|
onValueChange={(vals) => {
|
|
|
|
|
field.onChange(Number(vals[0]));
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
step={50}
|
2025-08-28 18:27:07 +02:00
|
|
|
value={[budget]}
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
2025-12-18 12:11:08 +01:00
|
|
|
<FormDescription>{t.parametri.budget_desc}</FormDescription>
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="entromese"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-mesi">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.dalmese_label}...
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
field.value !== null
|
|
|
|
|
? {
|
|
|
|
|
// biome-ignore lint/style/noNonNullAssertion: <known size>
|
2025-12-18 12:11:08 +01:00
|
|
|
label: t.parametri.mesi[field.value]!,
|
2025-08-29 16:18:32 +02:00
|
|
|
value: String(field.value),
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
2025-08-29 16:18:32 +02:00
|
|
|
elementId="select-mesi"
|
|
|
|
|
elementName="entro_mese"
|
|
|
|
|
isMulti={false}
|
2025-08-28 18:27:07 +02:00
|
|
|
onValueChange={(value) => {
|
|
|
|
|
field.onChange(
|
|
|
|
|
value.value !== "" ? parseInt(value.value) : null,
|
|
|
|
|
);
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
options={UnpackOptions({
|
2025-12-18 12:11:08 +01:00
|
|
|
options: t.parametri.mesi,
|
2025-08-29 16:18:32 +02:00
|
|
|
})}
|
2025-08-28 18:27:07 +02:00
|
|
|
placeholder="Seleziona..."
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
disabled={tipologia !== TipologiaPosizioneEnum.Transitorio}
|
2025-08-29 16:18:32 +02:00
|
|
|
name="permanenza"
|
2025-08-28 18:27:07 +02:00
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
2026-01-16 17:11:53 +01:00
|
|
|
<FormLabel
|
|
|
|
|
className={cn(
|
|
|
|
|
tipologia !== TipologiaPosizioneEnum.Transitorio &&
|
|
|
|
|
"opacity-50",
|
|
|
|
|
)}
|
|
|
|
|
htmlFor="permanenza"
|
|
|
|
|
>
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.permanenza_label}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormControl>
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
field.value
|
|
|
|
|
? {
|
|
|
|
|
label:
|
2025-12-18 12:11:08 +01:00
|
|
|
t.parametri.permanenza[field.value] || "",
|
2025-08-29 16:18:32 +02:00
|
|
|
value: field.value.toString(),
|
2025-08-28 18:27:07 +02:00
|
|
|
}
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
2025-08-29 16:18:32 +02:00
|
|
|
disabled={
|
|
|
|
|
tipologia !== TipologiaPosizioneEnum.Transitorio
|
|
|
|
|
}
|
|
|
|
|
elementId="permanenza"
|
|
|
|
|
elementName="permanenza"
|
|
|
|
|
isMulti={false}
|
2025-08-28 18:27:07 +02:00
|
|
|
onValueChange={(value) => {
|
|
|
|
|
field.onChange(
|
|
|
|
|
value.value !== "" ? parseInt(value.value) : null,
|
|
|
|
|
);
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
options={UnpackOptions({
|
2025-12-18 12:11:08 +01:00
|
|
|
options: t.parametri.permanenza,
|
2025-08-29 16:18:32 +02:00
|
|
|
})}
|
2025-08-28 18:27:07 +02:00
|
|
|
placeholder="Seleziona..."
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
2026-01-16 17:11:53 +01:00
|
|
|
{tipologia === TipologiaPosizioneEnum.Transitorio && (
|
|
|
|
|
<FormDescription>
|
|
|
|
|
{t.parametri.permanenza_desc}
|
|
|
|
|
</FormDescription>
|
|
|
|
|
)}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex w-full flex-col items-center justify-between gap-4 sm:flex-row">
|
|
|
|
|
<div className="flex w-full items-center justify-between gap-4">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="n_adulti"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className={cn("w-full")}>
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel className={cn("")} htmlFor="n_adulti">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.nadulti_label}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Counter
|
|
|
|
|
name="n_adulti"
|
|
|
|
|
onlyPositive
|
2025-08-29 16:18:32 +02:00
|
|
|
setValue={(v) => field.onChange(Number(v))}
|
|
|
|
|
value={field.value}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="n_minori"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className={cn("w-full")}>
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel className={cn("")} htmlFor="n_minori">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.nminori_label}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Counter
|
|
|
|
|
name="n_minori"
|
|
|
|
|
onlyPositive
|
2025-08-29 16:18:32 +02:00
|
|
|
setValue={(v) => field.onChange(Number(v))}
|
|
|
|
|
value={field.value}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="arredato"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="arredato">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.arredato_label}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Switch
|
|
|
|
|
className="data-[state=checked]:bg-neutral-700"
|
2026-02-24 10:40:07 +01:00
|
|
|
defaultChecked={field.value}
|
2025-08-29 16:18:32 +02:00
|
|
|
id="arredato"
|
|
|
|
|
onCheckedChange={field.onChange}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormDescription>
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.arredato_desc}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormDescription>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
{tipologia === TipologiaPosizioneEnum.Transitorio ? (
|
2025-11-03 10:59:45 +01:00
|
|
|
<div className="items-top flex w-full flex-col justify-between gap-4 sm:flex-row">
|
2025-10-30 19:39:35 +01:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
key={tipologia}
|
|
|
|
|
name="motivazione_transitorio"
|
|
|
|
|
render={({ field }) => {
|
2025-12-18 12:11:08 +01:00
|
|
|
const existingValue = t.parametri.motivazioni_mappings.find(
|
|
|
|
|
(m) => m.id === field.value,
|
|
|
|
|
);
|
2025-10-30 19:39:35 +01:00
|
|
|
return (
|
2025-11-03 10:59:45 +01:00
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex w-full flex-wrap items-center gap-x-2">
|
2025-10-30 19:39:35 +01:00
|
|
|
<FormLabel htmlFor="motivazione_transitorio">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.motivazione_label}
|
2025-10-30 19:39:35 +01:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
existingValue
|
|
|
|
|
? {
|
|
|
|
|
label: existingValue.title,
|
|
|
|
|
value: existingValue.id,
|
|
|
|
|
}
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
elementId="select-motivazione_transitorio"
|
|
|
|
|
elementName="motivazione_transitorio"
|
|
|
|
|
isMulti={false}
|
|
|
|
|
onValueChange={(value) => {
|
|
|
|
|
field.onChange(value.value);
|
|
|
|
|
}}
|
2025-12-18 12:11:08 +01:00
|
|
|
options={t.parametri.motivazioni_mappings.map(
|
2025-10-30 19:39:35 +01:00
|
|
|
(option) => ({
|
|
|
|
|
label: option.title,
|
|
|
|
|
value: option.id,
|
|
|
|
|
}),
|
|
|
|
|
)}
|
|
|
|
|
placeholder={t.seleziona_placeholder}
|
|
|
|
|
/>
|
|
|
|
|
<FormDescription>
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.motivazione_desc}
|
2025-10-30 19:39:35 +01:00
|
|
|
</FormDescription>
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="scadenza_motivazione_transitoria"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="flex w-full flex-col">
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
2025-10-30 19:39:35 +01:00
|
|
|
<FormLabel htmlFor="scadenza_motivazione_transitoria">
|
|
|
|
|
Scadenza motivazione
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-10-30 19:39:35 +01:00
|
|
|
<Popover>
|
|
|
|
|
<PopoverTrigger asChild>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Button
|
|
|
|
|
className={cn(
|
2026-01-14 14:21:57 +01:00
|
|
|
"h-10.5 w-full pl-3 text-left font-medium",
|
2025-10-30 19:39:35 +01:00
|
|
|
!field.value && "text-muted-foreground",
|
2025-11-14 17:21:21 +01:00
|
|
|
"rounded-lg border border-neutral-300 shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm disabled:cursor-not-allowed disabled:opacity-50 dark:focus:border-transparent",
|
2025-11-17 18:39:58 +01:00
|
|
|
)}
|
2025-10-30 19:39:35 +01:00
|
|
|
id="scadenza_motivazione_transitoria"
|
|
|
|
|
variant={"outline"}
|
|
|
|
|
>
|
|
|
|
|
{field.value ? (
|
|
|
|
|
format(field.value, "PPP", {
|
|
|
|
|
locale: it,
|
|
|
|
|
})
|
|
|
|
|
) : (
|
|
|
|
|
<span>{t.anagrafica.scegli_data}</span>
|
|
|
|
|
)}
|
|
|
|
|
<CalendarIcon className="ml-auto size-4 opacity-50" />
|
|
|
|
|
</Button>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</PopoverTrigger>
|
|
|
|
|
<PopoverContent align="start" className="w-auto p-0">
|
|
|
|
|
<Calendar
|
|
|
|
|
autoFocus
|
|
|
|
|
captionLayout="dropdown"
|
|
|
|
|
defaultMonth={field.value || new Date()}
|
|
|
|
|
endMonth={new Date(2050, 12)}
|
|
|
|
|
locale={it}
|
|
|
|
|
mode="single"
|
|
|
|
|
onSelect={field.onChange}
|
|
|
|
|
selected={field.value || undefined}
|
|
|
|
|
/>
|
|
|
|
|
</PopoverContent>
|
|
|
|
|
</Popover>
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormItem>
|
2025-10-30 19:39:35 +01:00
|
|
|
)}
|
|
|
|
|
/>
|
2025-11-03 10:59:45 +01:00
|
|
|
</div>
|
2025-08-28 18:27:07 +02:00
|
|
|
) : (
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
2025-08-29 16:18:32 +02:00
|
|
|
key={tipologia}
|
2025-08-28 18:27:07 +02:00
|
|
|
name="reddito"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem>
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-reddito">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.reddito_label}
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<MultiSelect
|
2025-08-29 16:18:32 +02:00
|
|
|
defaultValue={undefined}
|
2025-08-28 18:27:07 +02:00
|
|
|
elementId="select-reddito"
|
|
|
|
|
elementName="reddito"
|
|
|
|
|
isMulti={false}
|
|
|
|
|
onValueChange={(value) => {
|
|
|
|
|
field.onChange(value.value);
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
options={UnpackOptions({
|
2025-12-18 12:11:08 +01:00
|
|
|
options: t.parametri.reddito_options,
|
2025-08-29 16:18:32 +02:00
|
|
|
})}
|
|
|
|
|
placeholder={t.seleziona_placeholder}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex flex-col space-y-2">
|
|
|
|
|
<Label htmlFor="altrePrefDialogButton">
|
2025-12-18 12:11:08 +01:00
|
|
|
{t.parametri.altriparams_label}
|
2025-08-28 18:27:07 +02:00
|
|
|
</Label>
|
2025-12-18 12:11:08 +01:00
|
|
|
<FormDescription>{t.parametri.altriparams_desc}</FormDescription>
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="mt-2 flex flex-wrap gap-2">
|
2025-10-28 11:55:01 +01:00
|
|
|
{altrePrefMapping.map((pair) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
if (pair) {
|
|
|
|
|
return (
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
2025-10-28 11:55:01 +01:00
|
|
|
key={pair[0]}
|
2025-08-28 18:27:07 +02:00
|
|
|
name={pair[0]}
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Badge
|
2025-11-17 18:39:58 +01:00
|
|
|
className={cn("cursor-pointer text-sm")}
|
2025-08-29 16:18:32 +02:00
|
|
|
onClick={() => {
|
|
|
|
|
field.onChange(!field.value);
|
|
|
|
|
}}
|
2025-11-17 18:39:58 +01:00
|
|
|
variant={field.value ? "default" : "secondary"}
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
{pair[1]}
|
|
|
|
|
</Badge>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>{texts.personali}</CardTitle>
|
|
|
|
|
<CardDescription>{texts.personali_desc}</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-6">
|
|
|
|
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="nome"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="nome">{t.account.nome}</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormControl>
|
2025-08-29 16:18:32 +02:00
|
|
|
<Input {...field} id="nome" type="text" />
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="cognome"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="cognome">
|
|
|
|
|
{t.account.cognome}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormControl>
|
2025-08-29 16:18:32 +02:00
|
|
|
<Input {...field} id="cognome" type="text" />
|
2025-08-28 18:27:07 +02:00
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Separator />
|
|
|
|
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="data_nascita"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="flex w-full flex-col">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="dataNascita">
|
|
|
|
|
{t.anagrafica.dataNascita}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<Popover>
|
|
|
|
|
<PopoverTrigger asChild>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Button
|
|
|
|
|
className={cn(
|
2026-01-14 14:21:57 +01:00
|
|
|
"h-10.5 w-full pl-3 text-left font-medium",
|
2025-08-28 18:27:07 +02:00
|
|
|
!field.value && "text-muted-foreground",
|
2025-11-14 17:21:21 +01:00
|
|
|
"rounded-lg border border-neutral-300 shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm disabled:cursor-not-allowed disabled:opacity-50 dark:focus:border-transparent",
|
2025-11-17 18:39:58 +01:00
|
|
|
)}
|
2025-08-29 16:18:32 +02:00
|
|
|
id="dataNascita"
|
|
|
|
|
variant={"outline"}
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
{field.value ? (
|
|
|
|
|
format(field.value, "PPP", {
|
|
|
|
|
locale: DatePickerLocale,
|
|
|
|
|
})
|
|
|
|
|
) : (
|
|
|
|
|
<span>{t.anagrafica.scegli_data}</span>
|
|
|
|
|
)}
|
|
|
|
|
<CalendarIcon className="ml-auto size-4 opacity-50" />
|
|
|
|
|
</Button>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</PopoverTrigger>
|
2025-08-29 16:18:32 +02:00
|
|
|
<PopoverContent align="start" className="w-auto p-0">
|
2025-08-28 18:27:07 +02:00
|
|
|
<Calendar
|
2025-08-29 16:18:32 +02:00
|
|
|
autoFocus
|
2025-08-28 18:27:07 +02:00
|
|
|
captionLayout="dropdown"
|
2025-08-29 16:18:32 +02:00
|
|
|
defaultMonth={field.value}
|
|
|
|
|
endMonth={new Date()}
|
|
|
|
|
locale={DatePickerLocale}
|
|
|
|
|
mode="single"
|
|
|
|
|
onSelect={field.onChange}
|
|
|
|
|
selected={field.value}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</PopoverContent>
|
|
|
|
|
</Popover>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="nazione_nascita"
|
2025-11-13 15:45:55 +01:00
|
|
|
render={({ field }) => {
|
|
|
|
|
const parsedNazioneNascita = parseDBNazione(
|
|
|
|
|
field.value,
|
|
|
|
|
nazioni,
|
|
|
|
|
);
|
|
|
|
|
return (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-naz-nascita">
|
|
|
|
|
{t.anagrafica.nazioneNascita}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-08 19:58:03 +02:00
|
|
|
|
2025-11-13 15:45:55 +01:00
|
|
|
<FormControl>
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
field.value
|
|
|
|
|
? parsedNazioneNascita
|
|
|
|
|
? {
|
|
|
|
|
label: parsedNazioneNascita.nome,
|
|
|
|
|
value: parsedNazioneNascita.catasto,
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
label: field.value,
|
|
|
|
|
value: field.value,
|
|
|
|
|
}
|
|
|
|
|
: undefined
|
2025-10-30 18:13:50 +01:00
|
|
|
}
|
2025-11-13 15:45:55 +01:00
|
|
|
elementId="select-naz-nascita"
|
|
|
|
|
elementName="nazioneNascita"
|
|
|
|
|
isMulti={false}
|
|
|
|
|
onValueChange={async (value) => {
|
|
|
|
|
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..."
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
2025-08-08 19:58:03 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="luogo_nascita"
|
|
|
|
|
render={({ field }) => {
|
2025-11-13 15:45:55 +01:00
|
|
|
if (nazione_nascita === ITALY_CATASTO_CODE) {
|
|
|
|
|
const parsedComuneNascita = parseDBComune(
|
|
|
|
|
field.value,
|
|
|
|
|
comuni,
|
|
|
|
|
);
|
|
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-comune-nascita">
|
|
|
|
|
{t.anagrafica.luogoNascita}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormControl>
|
|
|
|
|
<MultiSelect
|
2025-11-13 15:45:55 +01:00
|
|
|
defaultValue={
|
|
|
|
|
field.value
|
|
|
|
|
? parsedComuneNascita
|
|
|
|
|
? {
|
|
|
|
|
label: `${parsedComuneNascita.nome} (${parsedComuneNascita.sigla})`,
|
|
|
|
|
value: parsedComuneNascita.catasto,
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
label: field.value,
|
|
|
|
|
value: field.value,
|
|
|
|
|
}
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
2025-08-29 16:18:32 +02:00
|
|
|
elementId="select-comune-nascita"
|
|
|
|
|
elementName="luogoNascita"
|
|
|
|
|
isMulti={false}
|
2025-08-28 18:27:07 +02:00
|
|
|
onValueChange={async (value) => {
|
|
|
|
|
field.onChange(value.value);
|
|
|
|
|
await form.trigger("luogo_nascita");
|
2025-11-13 15:45:55 +01:00
|
|
|
|
|
|
|
|
await form.trigger("codice_fiscale");
|
2025-08-28 18:27:07 +02:00
|
|
|
}}
|
2025-11-13 15:45:55 +01:00
|
|
|
options={comuni.map((c) => ({
|
|
|
|
|
label: `${c.nome} (${c.sigla})`,
|
|
|
|
|
value: c.catasto,
|
|
|
|
|
}))}
|
2025-08-28 18:27:07 +02:00
|
|
|
placeholder="Seleziona..."
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="luogoNascita">
|
|
|
|
|
{t.anagrafica.luogoNascita}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Input
|
|
|
|
|
{...field}
|
|
|
|
|
id="luogoNascita"
|
2025-12-17 16:38:15 +01:00
|
|
|
onChange={async (e) => {
|
|
|
|
|
NullableStringOnChange(e, field.onChange);
|
|
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
await form.trigger("luogo_nascita");
|
2025-11-13 15:45:55 +01:00
|
|
|
await form.trigger("codice_fiscale");
|
2025-08-28 18:27:07 +02:00
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
type="text"
|
2025-12-17 16:38:15 +01:00
|
|
|
value={field.value || ""}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="sesso"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-sesso">
|
|
|
|
|
<TooltipProvider>
|
|
|
|
|
<Tooltip delayDuration={0}>
|
|
|
|
|
<TooltipTrigger className="relative flex items-center gap-x-2">
|
|
|
|
|
{t.anagrafica.sesso}{" "}
|
2026-01-14 14:21:57 +01:00
|
|
|
<HelpCircleIcon className="absolute -right-6 size-5" />
|
2025-08-28 18:27:07 +02:00
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>
|
|
|
|
|
{t.anagrafica.sesso_disclamer}
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</TooltipProvider>
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={t.anagrafica.sesso_options.filter(
|
|
|
|
|
(v) => v.value === field.value,
|
|
|
|
|
)}
|
2025-08-29 16:18:32 +02:00
|
|
|
elementId="select-sesso"
|
|
|
|
|
elementName="sesso"
|
|
|
|
|
isMulti={false}
|
2025-08-28 18:27:07 +02:00
|
|
|
onValueChange={(value) => {
|
|
|
|
|
field.onChange(value.value);
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
options={t.anagrafica.sesso_options}
|
|
|
|
|
placeholder=""
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="codice_fiscale"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="codice_fiscale">
|
|
|
|
|
{t.anagrafica.codiceFiscale}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Input
|
|
|
|
|
{...field}
|
2025-08-29 16:18:32 +02:00
|
|
|
className="uppercase"
|
|
|
|
|
id="codice_fiscale"
|
2025-08-28 18:27:07 +02:00
|
|
|
onChange={async (v) => {
|
|
|
|
|
field.onChange(v.target.value.toUpperCase());
|
|
|
|
|
await form.trigger("codice_fiscale");
|
|
|
|
|
}}
|
|
|
|
|
type="text"
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="email"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="email">{t.account.email}</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormControl>
|
|
|
|
|
<InputWIcon
|
|
|
|
|
{...field}
|
2025-08-29 16:18:32 +02:00
|
|
|
iconPosition="start"
|
2025-08-28 18:27:07 +02:00
|
|
|
id="email"
|
|
|
|
|
type="email"
|
|
|
|
|
>
|
2025-08-29 16:18:32 +02:00
|
|
|
<Mail aria-hidden="true" size={16} strokeWidth={2} />
|
2025-08-28 18:27:07 +02:00
|
|
|
</InputWIcon>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="telefono"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="numero">
|
|
|
|
|
{t.account.telefono}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormControl>
|
|
|
|
|
<PhoneInput {...field} id="telefono" />
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Separator />
|
2025-11-13 15:45:55 +01:00
|
|
|
<ResidenzaSection
|
|
|
|
|
control={form.control}
|
|
|
|
|
setValue={form.setValue}
|
|
|
|
|
trigger={form.trigger}
|
|
|
|
|
/>
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="indirizzo_recapiti"
|
|
|
|
|
render={({ field }) => (
|
2025-10-10 16:18:43 +02:00
|
|
|
<FormItem className="w-full">
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="indirizzo">
|
|
|
|
|
{t.recapiti.indirizzo}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Input
|
|
|
|
|
{...field}
|
|
|
|
|
id="indirizzo"
|
|
|
|
|
placeholder="Via Roma"
|
2025-08-29 16:18:32 +02:00
|
|
|
type="text"
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="civico_recapiti"
|
|
|
|
|
render={({ field }) => (
|
2025-10-10 16:18:43 +02:00
|
|
|
<FormItem className="w-full">
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="civico">
|
|
|
|
|
{t.recapiti.civico}
|
|
|
|
|
</FormLabel>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Input
|
|
|
|
|
{...field}
|
|
|
|
|
id="civico"
|
|
|
|
|
placeholder="50"
|
2025-08-29 16:18:32 +02:00
|
|
|
type="text"
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
2025-10-30 18:13:50 +01:00
|
|
|
<div className="flex w-full flex-wrap items-center justify-center gap-2 pb-8 sm:justify-end">
|
2025-08-28 18:27:07 +02:00
|
|
|
{isAdmin && (
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
2025-11-06 17:03:42 +01:00
|
|
|
name="skipPayment"
|
2025-08-28 18:27:07 +02:00
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="py-2">
|
|
|
|
|
<div className="flex items-center justify-start gap-x-4">
|
2025-11-06 17:03:42 +01:00
|
|
|
<FormLabel htmlFor="skipPayment">
|
2025-08-28 18:27:07 +02:00
|
|
|
Vuoi saltare la creazione dell'ordine e impostare il
|
|
|
|
|
servizio come attivo?
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Switch
|
2025-08-29 16:18:32 +02:00
|
|
|
className="data-[state=checked]:bg-neutral-700"
|
2026-02-24 10:40:07 +01:00
|
|
|
defaultChecked={field.value}
|
2025-11-06 17:03:42 +01:00
|
|
|
id="skipPayment"
|
2025-08-28 18:27:07 +02:00
|
|
|
onCheckedChange={(v) => {
|
|
|
|
|
field.onChange(v);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-08-29 16:18:32 +02:00
|
|
|
<Button className="w-full sm:w-auto" type="submit">
|
2025-08-28 18:27:07 +02:00
|
|
|
{texts.salva}
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</Form>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
2025-10-30 18:13:50 +01:00
|
|
|
|
2025-11-13 15:45:55 +01:00
|
|
|
const ResidenzaSection = ({
|
|
|
|
|
control,
|
|
|
|
|
trigger,
|
|
|
|
|
setValue,
|
|
|
|
|
}: {
|
|
|
|
|
control: Control<FormNewServizioValues>;
|
|
|
|
|
trigger: (name?: keyof FormNewServizioValues) => Promise<boolean>;
|
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: <fine here>
|
|
|
|
|
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 (
|
|
|
|
|
<>
|
|
|
|
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
|
|
|
|
<FormField
|
|
|
|
|
control={control}
|
|
|
|
|
name="nazione_recapiti"
|
|
|
|
|
render={({ field }) => {
|
|
|
|
|
const parsedNazioneResidenza = parseDBNazione(field.value, nazioni);
|
|
|
|
|
return (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-naz">
|
|
|
|
|
{t.recapiti.nazione}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<FormControl>
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
field.value
|
|
|
|
|
? parsedNazioneResidenza
|
|
|
|
|
? {
|
|
|
|
|
label: parsedNazioneResidenza.nome,
|
|
|
|
|
value: parsedNazioneResidenza.catasto,
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
label: field.value,
|
|
|
|
|
value: field.value,
|
|
|
|
|
}
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
elementId="select-naz"
|
|
|
|
|
elementName="nazioneRecapiti"
|
|
|
|
|
isMulti={false}
|
|
|
|
|
onValueChange={async (value) => {
|
|
|
|
|
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..."
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={control}
|
|
|
|
|
name="provincia_recapiti"
|
|
|
|
|
render={({ field }) => {
|
|
|
|
|
const parseProvincia = getProvinciaFromSigla(
|
|
|
|
|
field.value,
|
|
|
|
|
provincie,
|
|
|
|
|
);
|
|
|
|
|
return (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="provinciaRecapiti">
|
|
|
|
|
{t.recapiti.provincia}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{nazione_recapiti === ITALY_CATASTO_CODE ? (
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
field.value && parseProvincia
|
|
|
|
|
? {
|
|
|
|
|
label: parseProvincia.nome,
|
|
|
|
|
value: parseProvincia.sigla,
|
|
|
|
|
}
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
elementId="provinciaRecapiti"
|
|
|
|
|
elementName="provincia"
|
|
|
|
|
isMulti={false}
|
|
|
|
|
onValueChange={(value) => {
|
|
|
|
|
field.onChange(value.value);
|
|
|
|
|
setValue("citta_recapiti", null);
|
|
|
|
|
}}
|
|
|
|
|
options={provincie.map((prov) => ({
|
|
|
|
|
label: prov.nome,
|
|
|
|
|
value: prov.sigla,
|
|
|
|
|
}))}
|
|
|
|
|
placeholder="Seleziona"
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Input
|
|
|
|
|
{...field}
|
|
|
|
|
id="provinciaRecapiti"
|
2025-12-17 16:38:15 +01:00
|
|
|
onChange={(e) =>
|
|
|
|
|
NullableStringOnChange(e, field.onChange)
|
|
|
|
|
}
|
2025-11-13 15:45:55 +01:00
|
|
|
type="text"
|
2025-12-17 16:38:15 +01:00
|
|
|
value={field.value || ""}
|
2025-11-13 15:45:55 +01:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
)}
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
|
|
|
|
|
<FormField
|
|
|
|
|
control={control}
|
|
|
|
|
name="citta_recapiti"
|
|
|
|
|
render={({ field }) => {
|
|
|
|
|
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 (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="select-comune">
|
|
|
|
|
{t.recapiti.comune}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<FormControl>
|
|
|
|
|
<MultiSelect
|
|
|
|
|
defaultValue={
|
|
|
|
|
field.value && parsedComuneResidenza
|
|
|
|
|
? {
|
|
|
|
|
label: parsedComuneResidenza.nome,
|
|
|
|
|
value: parsedComuneResidenza.catasto,
|
|
|
|
|
}
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
elementId="select-comune"
|
|
|
|
|
elementName="citta_recapiti"
|
|
|
|
|
isMulti={false}
|
|
|
|
|
onValueChange={async (value) => {
|
|
|
|
|
field.onChange(value.value);
|
|
|
|
|
await trigger("citta_recapiti");
|
|
|
|
|
}}
|
|
|
|
|
options={options}
|
|
|
|
|
placeholder="Seleziona..."
|
|
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="citta_recapiti">
|
|
|
|
|
{t.recapiti.comune}
|
|
|
|
|
</FormLabel>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Input
|
|
|
|
|
{...field}
|
|
|
|
|
id="citta_recapiti"
|
2025-12-17 16:38:15 +01:00
|
|
|
onChange={async (e) => {
|
|
|
|
|
NullableStringOnChange(e, field.onChange);
|
2025-11-13 15:45:55 +01:00
|
|
|
await trigger("citta_recapiti");
|
|
|
|
|
}}
|
|
|
|
|
type="text"
|
2025-12-17 16:38:15 +01:00
|
|
|
value={field.value || ""}
|
2025-11-13 15:45:55 +01:00
|
|
|
/>
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={control}
|
|
|
|
|
name="cap_recapiti"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem className="w-full">
|
|
|
|
|
<div className="flex flex-wrap items-center gap-x-2">
|
|
|
|
|
<FormLabel htmlFor="cap">{t.recapiti.cap}</FormLabel>
|
|
|
|
|
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</div>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<Input {...field} id="cap" placeholder="00100" type="text" />
|
|
|
|
|
</FormControl>
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|