Refactor comuni and provincie data structures; add dynamic FormDebug component

- Updated `initializeComuniNames` to accept an optional `provincia` parameter for filtering comuni by province.
- Renamed `getComuniNames` to `getComuni` and modified it to return an array of objects containing both comune names and their respective province siglas.
- Introduced `provincieWithSigla` array to map province names to their siglas, along with a new function `getProvinciaFromSigla` for retrieving province names based on sigla.
- Added a dynamic import for the `FormDebug` component to facilitate debugging in development mode while preventing it from rendering in production.
- Commented out console logs in `checkFiscalCodeValidity` for cleaner production output.
This commit is contained in:
Marco Pedone 2025-10-30 18:13:50 +01:00
parent da7683ba45
commit 967935f0a8
7 changed files with 1159 additions and 1012 deletions

View file

@ -11,7 +11,6 @@ import {
} from "react-hook-form";
import { Label } from "src/components/ui/label";
import { cn } from "src/lib/utils";
import { env } from "~/env";
const Form = FormProvider;
@ -171,23 +170,6 @@ const FormMessage = React.forwardRef<
});
FormMessage.displayName = "FormMessage";
const FormDebug = React.forwardRef<
React.ElementType,
React.HTMLAttributes<HTMLDivElement>
>(async () => {
if (env.NODE_ENV !== "development") {
return null;
}
const { control, formState } = useFormContext();
const devtools = await import(/* webpackIgnore: true */ "@hookform/devtools");
if (Object.keys(formState.errors).length > 0) {
console.error("errors", formState.errors);
}
return <devtools.DevTool control={control} />;
});
FormDebug.displayName = "FormDebug";
export {
useFormField,
Form,
@ -197,5 +179,4 @@ export {
FormDescription,
FormMessage,
FormField,
FormDebug,
};

View file

@ -1,7 +0,0 @@
import dynamic from "next/dynamic";
import type React from "react";
export const DevT: React.ElementType = dynamic(
() => import("@hookform/devtools").then((mod) => mod.DevTool),
{ ssr: false },
);

View file

@ -3,6 +3,7 @@ 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";
@ -48,9 +49,9 @@ import {
TooltipProvider,
TooltipTrigger,
} from "~/components/ui/tooltip";
import { getComuniNames } from "~/i18n/comuni";
import { getComuni } from "~/i18n/comuni";
import { GetNazioni } from "~/i18n/nazioni";
import { provincie } from "~/i18n/provincie";
import { getProvinciaFromSigla, provincieWithSigla } from "~/i18n/provincie";
import { checkFiscalCodeValidity, getComuneFieldValue } from "~/lib/form_utils";
import { cn } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm";
@ -67,6 +68,53 @@ type InitialData = {
anagrafica: UsersAnagrafica | undefined;
};
const FormNewServizioSchema = z.object({
animali: z.boolean(),
arredato: z.boolean(),
ascensore: z.boolean(),
azienda: z.boolean(),
budget: z.number().positive(),
cap_recapiti: z.string().length(5),
citta_recapiti: z.string().nonempty("Inserisci una città"),
civico_recapiti: z.string().nonempty("Inserisci un civico"),
codice_destinatario: z.string().nullable(),
codice_fiscale: z.string(),
cognome: z.string().nonempty("Inserisci un cognome valido"),
data_nascita: z.date(),
email: z.email(),
entromese: z.number().nullable(),
fatturazione_aziendale: z.boolean(),
fumatori: z.boolean(),
giardino: z.boolean(),
indirizzo_recapiti: z.string().nonempty("Inserisci un indirizzo"),
legale_rappresentante: z.string().nullable(),
luogo_nascita: z.string(),
motivazione_transitorio: z.string().nullable(),
n_adulti: z.number().min(1),
n_minori: z.number(),
nazione_nascita: z.string(),
nazione_recapiti: z
.string()
.refine((v) => v !== "", { params: { type: "nazione" } }),
nome: z.string().nonempty("Inserisci un nome valido"),
p_iva: z.string().nullable(),
parcheggio: z.boolean(),
permanenza: z.number().nullable(),
pianoterra: z.boolean(),
provincia_recapiti: z
.string()
.refine((v) => v !== "", { params: { type: "provincia" } }),
ragione_sociale: z.string().nullable(),
reddito: z.string().nullable(),
sede_legale: z.string().nullable(),
sesso: z.string(),
skipOrdine: z.boolean(),
telefono: z.string(),
terrazzo: z.boolean(),
tipologia: z.custom<TipologiaPosizioneEnum>(),
});
type FormNewServizioValues = z.infer<typeof FormNewServizioSchema>;
export const FormNewServizioAcquisto = ({
initialData,
userId,
@ -77,198 +125,150 @@ export const FormNewServizioAcquisto = ({
isAdmin?: boolean;
}) => {
const router = useRouter();
const Schema = z
.object({
animali: z.boolean(),
arredato: z.boolean(),
ascensore: z.boolean(),
azienda: z.boolean(),
budget: z.number().positive(),
cap_recapiti: z.string().length(5),
citta_recapiti: z.string().nonempty("Inserisci una città"),
civico_recapiti: z.string().nonempty("Inserisci un civico"),
codice_destinatario: z.string().nullable(),
codice_fiscale: z.string(),
cognome: z.string().nonempty("Inserisci un cognome valido"),
data_nascita: z.date(),
email: z.email(),
entromese: z.number().nullable(),
fatturazione_aziendale: z.boolean(),
fumatori: z.boolean(),
giardino: z.boolean(),
indirizzo_recapiti: z.string().nonempty("Inserisci un indirizzo"),
legale_rappresentante: z.string().nullable(),
luogo_nascita: z.string(),
motivazione_transitorio: z.string().nullable(),
n_adulti: z.number().min(1),
n_minori: z.number(),
nazione_nascita: z.string(),
nazione_recapiti: z
.string()
.refine((v) => v !== "", { params: { type: "nazione" } }),
nome: z.string().nonempty("Inserisci un nome valido"),
p_iva: z.string().nullable(),
parcheggio: z.boolean(),
permanenza: z.number().nullable(),
pianoterra: z.boolean(),
provincia_recapiti: z
.string()
.refine((v) => v !== "", { params: { type: "provincia" } }),
ragione_sociale: z.string().nullable(),
reddito: z.string().nullable(),
sede_legale: z.string().nullable(),
sesso: z.string(),
skipOrdine: z.boolean(),
telefono: z.string(),
terrazzo: z.boolean(),
tipologia: z.custom<TipologiaPosizioneEnum>(),
})
.superRefine(
(
{
telefono,
codice_fiscale,
sesso,
cognome,
nome,
data_nascita,
luogo_nascita,
nazione_nascita,
n_adulti,
permanenza,
entromese,
motivazione_transitorio,
reddito,
azienda,
p_iva,
sede_legale,
legale_rappresentante,
ragione_sociale,
fatturazione_aziendale,
codice_destinatario,
},
refinementContext,
) => {
if (entromese === null) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il mese di inizio",
path: ["entromese"],
});
}
if (azienda) {
if (!ragione_sociale || ragione_sociale.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci la ragione sociale",
path: ["ragione_sociale"],
});
}
if (!sede_legale || sede_legale.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci la sede legale",
path: ["sede_legale"],
});
}
if (!p_iva || p_iva.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci la partita iva",
path: ["p_iva"],
});
}
if (!legale_rappresentante || legale_rappresentante.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il legale rappresentante",
path: ["legale_rappresentante"],
});
}
}
if (fatturazione_aziendale) {
if (!codice_destinatario || codice_destinatario.length !== 7) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci un codice destinatario valido",
path: ["codice_destinatario"],
});
}
}
if (!telefono || telefono.length < 10) {
refinementContext.issues.push({
code: "custom",
input: "",
message:
"Il numero di telefono deve essere lungo almeno 10 caratteri",
});
}
checkFiscalCodeValidity({
codiceFiscale: codice_fiscale,
cognome,
dataNascita: data_nascita,
luogoNascita: luogo_nascita,
nazioneNascita: nazione_nascita,
nome,
path: "codice_fiscale",
refinementContext,
sesso,
const schema = FormNewServizioSchema.superRefine(
(
{
telefono,
codice_fiscale,
sesso,
cognome,
nome,
data_nascita,
luogo_nascita,
nazione_nascita,
n_adulti,
permanenza,
entromese,
motivazione_transitorio,
reddito,
azienda,
p_iva,
sede_legale,
legale_rappresentante,
ragione_sociale,
fatturazione_aziendale,
codice_destinatario,
},
refinementContext,
) => {
if (entromese === null) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il mese di inizio",
path: ["entromese"],
});
if (n_adulti <= 0) {
}
if (azienda) {
if (!ragione_sociale || ragione_sociale.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il numero di persone",
path: ["n_adulti"],
message: "Inserisci la ragione sociale",
path: ["ragione_sociale"],
});
}
if (!sede_legale || sede_legale.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci la sede legale",
path: ["sede_legale"],
});
}
if (!p_iva || p_iva.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci la partita iva",
path: ["p_iva"],
});
}
if (!legale_rappresentante || legale_rappresentante.length < 1) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il legale rappresentante",
path: ["legale_rappresentante"],
});
}
}
if (fatturazione_aziendale) {
if (!codice_destinatario || codice_destinatario.length !== 7) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci un codice destinatario valido",
path: ["codice_destinatario"],
});
}
}
if (!telefono || telefono.length < 10) {
refinementContext.issues.push({
code: "custom",
input: "",
message:
"Il numero di telefono deve essere lungo almeno 10 caratteri",
});
}
checkFiscalCodeValidity({
codiceFiscale: codice_fiscale,
cognome,
dataNascita: data_nascita,
luogoNascita: luogo_nascita,
nazioneNascita: nazione_nascita,
nome,
path: "codice_fiscale",
refinementContext,
sesso,
});
if (n_adulti <= 0) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il numero di persone",
path: ["n_adulti"],
});
}
if (
permanenza === null &&
tipologia === TipologiaPosizioneEnum.Transitorio
) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci permanenza",
path: ["permanenza"],
});
}
if (tipologia === TipologiaPosizioneEnum.Transitorio) {
if (
permanenza === null &&
tipologia === TipologiaPosizioneEnum.Transitorio
motivazione_transitorio === "" ||
motivazione_transitorio === null
) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci permanenza",
path: ["permanenza"],
message: "Inserisci la motivazione",
path: ["motivazione_transitorio"],
});
}
if (tipologia === TipologiaPosizioneEnum.Transitorio) {
if (
motivazione_transitorio === "" ||
motivazione_transitorio === null
) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci la motivazione",
path: ["motivazione_transitorio"],
});
}
} else {
if (reddito === "" || reddito === null) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il reddito",
path: ["reddito"],
});
}
} else {
if (reddito === "" || reddito === null) {
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci il reddito",
path: ["reddito"],
});
}
},
);
type FormValues = z.infer<typeof Schema>;
}
},
);
// This can come from your database or API.
const defaultValues: FormValues = {
const defaultValues: FormNewServizioValues = {
...initialData.userData,
azienda: initialData.anagrafica?.azienda || false,
cap_recapiti: initialData.anagrafica?.cap_residenza || "",
@ -298,10 +298,10 @@ export const FormNewServizioAcquisto = ({
const { locale, t } = useTranslation();
const nazioni_options = GetNazioni();
const comuni_options = getComuniNames();
const comuni_options = getComuni();
z.config(z.locales[locale]());
const form = useZodForm(Schema, {
const form = useZodForm(schema, {
defaultValues: defaultValues,
});
const DatePickerLocale = locale === "it" ? it : enUS;
@ -335,7 +335,7 @@ export const FormNewServizioAcquisto = ({
},
});
function onSubmit(fields: FormValues) {
function onSubmit(fields: FormNewServizioValues) {
mutate({
anagrafica: {
azienda: fields.azienda,
@ -399,17 +399,8 @@ export const FormNewServizioAcquisto = ({
["pianoterra", string],
];
const altrePrefMapping = t.altrePrefMapping as altrePrefMappingType;
const [
tipologia,
azienda,
fatturazione_aziendale,
nazione_nascita,
nazione_recapiti,
budget,
] = form.watch([
const [tipologia, nazione_nascita, nazione_recapiti, budget] = form.watch([
"tipologia",
"azienda",
"fatturazione_aziendale",
"nazione_nascita",
"nazione_recapiti",
"budget",
@ -438,7 +429,10 @@ export const FormNewServizioAcquisto = ({
return (
<Form {...form}>
<form className="space-y-8 px-0.5" onSubmit={form.handleSubmit(onSubmit)}>
<form
className="space-y-8 px-0.5 pb-5"
onSubmit={form.handleSubmit(onSubmit)}
>
<Card>
<CardHeader>
<CardTitle>{texts.dettagli}</CardTitle>
@ -882,154 +876,9 @@ export const FormNewServizioAcquisto = ({
/>
</div>
{azienda && (
<>
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
<FormField
control={form.control}
name="ragione_sociale"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="ragione_sociale">
{t.anagrafica.ragioneSociale}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="ragione_sociale"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="sede_legale"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="sede_legale">
{t.anagrafica.sedeLegale}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="sede_legale"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
<FormField
control={form.control}
name="p_iva"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="p_iva">
{t.anagrafica.p_iva}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="p_iva"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="legale_rappresentante"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="legale_rappresentante">
{t.anagrafica.legale_rappresentante}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="legale_rappresentante"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="fatturazione_aziendale"
render={({ field }) => (
<FormItem className="py-2">
<div className="flex items-center justify-start gap-x-4">
<FormLabel htmlFor="fatturazione_aziendale">
{texts.fattura}
</FormLabel>
<FormControl>
<Switch
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="fatturazione_aziendale"
onCheckedChange={(v) => {
field.onChange(v);
if (!v) {
form.setValue("codice_destinatario", null);
}
}}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
{fatturazione_aziendale && (
<FormField
control={form.control}
name="codice_destinatario"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="codice_destinatario">
{texts.sdi}
</FormLabel>
<FormMessage />
</div>
<AziendaSection control={form.control} setValue={form.setValue} />
<FatturazioneSection control={form.control} />
<FormControl>
<Input
{...field}
id="codice_destinatario"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
)}
</>
)}
<Separator />
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
<FormField
@ -1110,6 +959,9 @@ export const FormNewServizioAcquisto = ({
onValueChange={async (value) => {
field.onChange(value.value);
await form.trigger("luogo_nascita");
if (value.value !== "0") {
form.setValue("luogo_nascita", "");
}
}}
options={UnpackOptions({ options: nazioni_options })}
placeholder="Seleziona..."
@ -1124,6 +976,7 @@ export const FormNewServizioAcquisto = ({
name="luogo_nascita"
render={({ field }) => {
if (nazione_nascita === "0") {
const options = comuni_options.map((c) => c.nome);
return (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
@ -1137,7 +990,7 @@ export const FormNewServizioAcquisto = ({
<MultiSelect
defaultValue={getComuneFieldValue(
field.value,
comuni_options,
options,
)}
elementId="select-comune-nascita"
elementName="luogoNascita"
@ -1146,7 +999,7 @@ export const FormNewServizioAcquisto = ({
field.onChange(value.value);
await form.trigger("luogo_nascita");
}}
options={UnpackOptions({ options: comuni_options })}
options={UnpackOptions({ options })}
placeholder="Seleziona..."
/>
</FormControl>
@ -1310,7 +1163,9 @@ export const FormNewServizioAcquisto = ({
onValueChange={async (value) => {
field.onChange(value.value);
form.setValue("provincia_recapiti", "");
await form.trigger("citta_recapiti");
if (value.value !== "0") {
form.setValue("citta_recapiti", "");
}
}}
options={UnpackOptions({ options: nazioni_options })}
placeholder="Seleziona..."
@ -1322,37 +1177,52 @@ export const FormNewServizioAcquisto = ({
<FormField
control={form.control}
name="provincia_recapiti"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="provinciaRecapiti">
{t.recapiti.provincia}
</FormLabel>
<FormMessage />
</div>
render={({ field }) => {
const provinciaName =
field.value && getProvinciaFromSigla(field.value);
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 === "0" ? (
<MultiSelect
defaultValue={RepackValues({
options: provincie,
values: field.value,
})}
elementId="provinciaRecapiti"
elementName="provincia"
isMulti={false}
onValueChange={(value) => {
field.onChange(value.value);
}}
options={UnpackOptions({ options: provincie })}
placeholder="Seleziona"
/>
) : (
<FormControl>
<Input {...field} id="provinciaRecapiti" type="text" />
</FormControl>
)}
</FormItem>
)}
{nazione_recapiti === "0" ? (
<MultiSelect
defaultValue={
field.value && provinciaName
? {
label: provinciaName,
value: field.value,
}
: undefined
}
elementId="provinciaRecapiti"
elementName="provincia"
isMulti={false}
onValueChange={(value) => {
field.onChange(value.value);
}}
options={provincieWithSigla.map((prov) => ({
label: prov.nome,
value: prov.sigla,
}))}
placeholder="Seleziona"
/>
) : (
<FormControl>
<Input
{...field}
id="provinciaRecapiti"
type="text"
/>
</FormControl>
)}
</FormItem>
);
}}
/>
</div>
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
@ -1361,6 +1231,12 @@ export const FormNewServizioAcquisto = ({
name="citta_recapiti"
render={({ field }) => {
if (nazione_recapiti === "0") {
const provincia = form.watch("provincia_recapiti");
const options = comuni_options
.filter((comune) =>
provincia ? comune.provincia === provincia : true,
)
.map((comune) => comune.nome);
return (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
@ -1374,7 +1250,7 @@ export const FormNewServizioAcquisto = ({
<MultiSelect
defaultValue={getComuneFieldValue(
field.value,
comuni_options,
options,
)}
elementId="select-comune"
elementName="citta_recapiti"
@ -1383,7 +1259,7 @@ export const FormNewServizioAcquisto = ({
field.onChange(value.value);
await form.trigger("citta_recapiti");
}}
options={UnpackOptions({ options: comuni_options })}
options={UnpackOptions({ options })}
placeholder="Seleziona..."
/>
</FormControl>
@ -1484,7 +1360,7 @@ export const FormNewServizioAcquisto = ({
</div>
</CardContent>
</Card>
<div className="flex w-full items-center justify-center gap-2 pb-8 sm:justify-end">
<div className="flex w-full flex-wrap items-center justify-center gap-2 pb-8 sm:justify-end">
{isAdmin && (
<FormField
control={form.control}
@ -1520,3 +1396,194 @@ export const FormNewServizioAcquisto = ({
</Form>
);
};
const AziendaSection = ({
control,
setValue,
}: {
control: Control<FormNewServizioValues>;
setValue: (
name: keyof FormNewServizioValues,
// biome-ignore lint/suspicious/noExplicitAny: <fine here>
value: any,
) => void;
}) => {
const { t, locale } = useTranslation();
const azienda = useWatch({
control,
name: "azienda",
});
return (
<>
<div
className={cn(
"flex w-full flex-col items-start justify-between gap-4 sm:flex-row",
!azienda && "hidden",
)}
>
<FormField
control={control}
name="ragione_sociale"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="ragione_sociale">
{t.anagrafica.ragioneSociale}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="ragione_sociale"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={control}
name="sede_legale"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="sede_legale">
{t.anagrafica.sedeLegale}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="sede_legale"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn(
"flex w-full flex-col items-start justify-between gap-4 sm:flex-row",
!azienda && "hidden",
)}
>
<FormField
control={control}
name="p_iva"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="p_iva">{t.anagrafica.p_iva}</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="p_iva"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={control}
name="legale_rappresentante"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="legale_rappresentante">
{t.anagrafica.legale_rappresentante}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="legale_rappresentante"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<FormField
control={control}
name="fatturazione_aziendale"
render={({ field }) => (
<FormItem className={cn("py-2", !azienda && "hidden")}>
<div className="flex items-center justify-start gap-x-4">
<FormLabel htmlFor="fatturazione_aziendale">
{locale === "it"
? "Vuoi che la fatturazione sia intestata alla tua azienda?"
: "Do you want the invoice to be issued to your company?"}
</FormLabel>
<FormControl>
<Switch
checked={field.value}
className="data-[state=checked]:bg-neutral-700"
id="fatturazione_aziendale"
onCheckedChange={(v) => {
field.onChange(v);
if (!v) {
setValue("codice_destinatario", null);
}
}}
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
</>
);
};
const FatturazioneSection = ({
control,
}: {
control: Control<FormNewServizioValues>;
}) => {
const { locale } = useTranslation();
const fatturazione_aziendale = useWatch({
control,
name: "fatturazione_aziendale",
});
return (
<FormField
control={control}
name="codice_destinatario"
render={({ field }) => (
<FormItem className={cn("w-full", !fatturazione_aziendale && "hidden")}>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="codice_destinatario">
{locale === "it"
? "Codice destinatario SDI"
: "SDI Recipient Code"}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
id="codice_destinatario"
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
);
};

File diff suppressed because it is too large Load diff

View file

@ -24,14 +24,14 @@ const searchComune = (comune: string): Comune | null => {
return result;
};
let comuniNames: string[] | null = null;
const initializeComuniNames = () => {
const initializeComuniNames = (provincia?: string) => {
if (!comuniNames) {
comuniNames = comuniArray.map((comune) => comune.NOME);
comuniNames = comuniArray.filter((c)=> provincia ? c.SIGLA==provincia:true).map((comune) => comune.NOME);
}
};
export const getComuniNames = () => {
initializeComuniNames();
return comuniNames!;
export const getComuni = () => {
return comuniArray.map((comune) => ({nome:comune.NOME, provincia:comune.SIGLA}));
};
export const getComuneFromIndex = (index: number): Comune | null => {
initializeComuniNames();

View file

@ -1,112 +1,120 @@
export const provincie = [
"AGRIGENTO",
"ALESSANDRIA",
"ANCONA",
"AOSTA",
"AREZZO",
"ASCOLI PICENO",
"ASTI",
"AVELLINO",
"BARI",
"BARLETTA-ANDRIA-TRANI",
"BELLUNO",
"BENEVENTO",
"BERGAMO",
"BIELLA",
"BOLOGNA",
"BOLZANO",
"BRESCIA",
"BRINDISI",
"CAGLIARI",
"CALTANISSETTA",
"CAMPOBASSO",
"CARBONIA-IGLESIAS",
"CASERTA",
"CATANIA",
"CATANZARO",
"CHIETI",
"COMO",
"COSENZA",
"CREMONA",
"CROTONE",
"CUNEO",
"ENNA",
"FERMO",
"FERRARA",
"FIRENZE",
"FOGGIA",
"FORLÌ-CESENA",
"FROSINONE",
"GENOVA",
"GORIZIA",
"GROSSETO",
"IMPERIA",
"ISERNIA",
"LA SPEZIA",
"L'AQUILA",
"LATINA",
"LECCE",
"LECCO",
"LIVORNO",
"LODI",
"LUCCA",
"MACERATA",
"MANTOVA",
"MASSA-CARRARA",
"MATERA",
"MESSINA",
"MILANO",
"MODENA",
"MONZA E DELLA BRIANZA",
"NAPOLI",
"NOVARA",
"NUORO",
"OLBIA-TEMPIO",
"ORISTANO",
"PADOVA",
"PALERMO",
"PARMA",
"PAVIA",
"PERUGIA",
"PESARO E URBINO",
"PESCARA",
"PIACENZA",
"PISA",
"PISTOIA",
"PORDENONE",
"POTENZA",
"PRATO",
"RAGUSA",
"RAVENNA",
"REGGIO CALABRIA",
"REGGIO EMILIA",
"RIETI",
"RIMINI",
"ROMA",
"ROVIGO",
"SALERNO",
"MEDIO CAMPIDANO",
"SASSARI",
"SAVONA",
"SIENA",
"SIRACUSA",
"SONDRIO",
"TARANTO",
"TERAMO",
"TERNI",
"TORINO",
"OGLIASTRA",
"TRAPANI",
"TRENTO",
"TREVISO",
"TRIESTE",
"UDINE",
"VARESE",
"VENEZIA",
"VERBANO-CUSIO-OSSOLA",
"VERCELLI",
"VERONA",
"VIBO VALENTIA",
"VICENZA",
"VITERBO",
export const getProvinciaFromSigla = (sigla: string): string | undefined => {
const provincia = provincieWithSigla.find((prov) => prov.sigla === sigla);
return provincia ? provincia.nome : undefined;
}
export const provincieWithSigla: {
nome: string;
sigla: string;
}[] = [
{nome: "AGRIGENTO",sigla: "AG"},
{nome: "ALESSANDRIA",sigla: "AL"},
{nome: "ANCONA",sigla: "AN"},
{nome: "AOSTA",sigla: "AO"},
{nome: "AREZZO",sigla: "AR"},
{nome: "ASCOLI PICENO",sigla: "AP"},
{nome: "ASTI",sigla: "AT"},
{nome: "AVELLINO",sigla: "AV"},
{nome: "BARI",sigla: "BA"},
{nome: "BARLETTA-ANDRIA-TRANI",sigla: "BT"},
{nome: "BELLUNO",sigla: "BL"},
{nome: "BENEVENTO",sigla: "BN"},
{nome: "BERGAMO",sigla: "BG"},
{nome: "BIELLA",sigla: "BI"},
{nome: "BOLOGNA",sigla: "BO"},
{nome: "BOLZANO",sigla: "BZ"},
{nome: "BRESCIA",sigla: "BS"},
{nome: "BRINDISI",sigla: "BR"},
{nome: "CAGLIARI",sigla: "CA"},
{nome: "CALTANISSETTA",sigla: "CL"},
{nome: "CAMPOBASSO",sigla: "CB"},
{nome: "CARBONIA-IGLESIAS",sigla: "CI"},
{nome: "CASERTA",sigla: "CE"},
{nome: "CATANIA",sigla: "CT"},
{nome: "CATANZARO",sigla: "CZ"},
{nome: "CHIETI",sigla: "CH"},
{nome: "COMO",sigla: "CO"},
{nome: "COSENZA",sigla: "CS"},
{nome: "CREMONA",sigla: "CR"},
{nome: "CROTONE",sigla: "KR"},
{nome: "CUNEO",sigla: "CN"},
{nome: "ENNA",sigla: "EN"},
{nome: "FERMO",sigla: "FM"},
{nome: "FERRARA",sigla: "FE"},
{nome: "FIRENZE",sigla: "FI"},
{nome: "FOGGIA",sigla: "FG"},
{nome: "FORLÌ-CESENA",sigla: "FC"},
{nome: "FROSINONE",sigla: "FR"},
{nome: "GENOVA",sigla: "GE"},
{nome: "GORIZIA",sigla: "GO"},
{nome: "GROSSETO",sigla: "GR"},
{nome: "IMPERIA",sigla: "IM"},
{nome: "ISERNIA",sigla: "IS"},
{nome: "LA SPEZIA",sigla: "SP"},
{nome: "L'AQUILA",sigla: "AQ"},
{nome: "LATINA",sigla: "LT"},
{nome: "LECCE",sigla: "LE"},
{nome: "LECCO",sigla: "LC"},
{nome: "LIVORNO",sigla: "LI"},
{nome: "LODI",sigla: "LO"},
{nome: "LUCCA",sigla: "LU"},
{nome: "MACERATA",sigla: "MC"},
{nome: "MANTOVA",sigla: "MN"},
{nome: "MASSA-CARRARA",sigla: "MS"},
{nome: "MATERA",sigla: "MT"},
{nome: "MESSINA",sigla: "ME"},
{nome: "MILANO",sigla: "MI"},
{nome: "MODENA",sigla: "MO"},
{nome: "MONZA E DELLA BRIANZA",sigla: "MB"},
{nome: "NAPOLI",sigla: "NA"},
{nome: "NOVARA",sigla: "NO"},
{nome: "NUORO",sigla: "NU"},
{nome: "OLBIA-TEMPIO",sigla: "OT"},
{nome: "ORISTANO",sigla: "OR"},
{nome: "PADOVA",sigla: "PD"},
{nome: "PALERMO",sigla: "PA"},
{nome: "PARMA",sigla: "PR"},
{nome: "PAVIA",sigla: "PV"},
{nome: "PERUGIA",sigla: "PG"},
{nome: "PESARO E URBINO",sigla: "PU"},
{nome: "PESCARA",sigla: "PE"},
{nome: "PIACENZA",sigla: "PC"},
{nome: "PISA",sigla: "PI"},
{nome: "PISTOIA",sigla: "PT"},
{nome: "PORDENONE",sigla: "PN"},
{nome: "POTENZA",sigla: "PZ"},
{nome: "PRATO",sigla: "PO"},
{nome: "RAGUSA",sigla: "RG"},
{nome: "RAVENNA",sigla: "RA"},
{nome: "REGGIO CALABRIA",sigla: "RC"},
{nome: "REGGIO EMILIA",sigla: "RE"},
{nome: "RIETI",sigla: "RI"},
{nome: "RIMINI",sigla: "RN"},
{nome: "ROMA",sigla: "RM"},
{nome: "ROVIGO",sigla: "RO"},
{nome: "SALERNO",sigla: "SA"},
{nome: "MEDIO CAMPIDANO",sigla: "VS"},
{nome: "SASSARI",sigla: "SS"},
{nome: "SAVONA",sigla: "SV"},
{nome: "SIENA",sigla: "SI"},
{nome: "SIRACUSA",sigla: "SR"},
{nome: "SONDRIO",sigla: "SO"},
{nome: "TARANTO",sigla: "TA"},
{nome: "TERAMO",sigla: "TE"},
{nome: "TERNI",sigla: "TR"},
{nome: "TORINO",sigla: "TO"},
{nome: "OGLIASTRA",sigla: "OG"},
{nome: "TRAPANI",sigla: "TP"},
{nome: "TRENTO",sigla: "TN"},
{nome: "TREVISO",sigla: "TV"},
{nome: "TRIESTE",sigla: "TS"},
{nome: "UDINE",sigla: "UD"},
{nome: "VARESE",sigla: "VA"},
{nome: "VENEZIA",sigla: "VE"},
{nome: "VERBANO-CUSIO-OSSOLA",sigla: "VB"},
{nome: "VERCELLI",sigla: "VC"},
{nome: "VERONA",sigla: "VR"},
{nome: "VIBO VALENTIA",sigla: "VV"},
{nome: "VICENZA",sigla: "VI"},
{nome: "VITERBO",sigla: "VT"}
];

View file

@ -1,8 +1,17 @@
import dynamic from "next/dynamic";
import type { z } from "zod/v4";
import type { ListOption } from "~/components/custom_ui/multiselect";
import { getComuneFromIndex } from "~/i18n/comuni";
import { searchNazioneByIndex } from "~/i18n/nazioni";
export const FormDebug: React.ElementType =
process.env.NODE_ENV === "development"
? dynamic(
() => import("@hookform/devtools").then((module) => module.DevTool),
{ ssr: false },
)
: () => null; // Return null component in production
export const getComuneFieldValue = (
fieldValue: string,
comuni_options: string[],
@ -183,8 +192,8 @@ export const checkFiscalCodeValidity = ({
gen !== codiceFiscale.slice(0, 15) &&
!(codiceFiscale in getAlternativeCF(gen))
) {
console.log("gen", gen);
console.log("codiceFiscale", codiceFiscale);
//console.log("gen", gen);
//console.log("codiceFiscale", codiceFiscale);
console.log("alternative", getAlternativeCF(gen));
refinementContext.issues.push({
code: "custom",