This commit is contained in:
Marco Pedone 2025-11-13 15:45:55 +01:00
parent 306be40555
commit 219f76dd10
16 changed files with 827 additions and 41077 deletions

View file

@ -18,11 +18,7 @@ import {
} from "~/components/custom_ui/form";
import Input from "~/components/custom_ui/input";
import InputWIcon from "~/components/custom_ui/input-icon";
import {
MultiSelect,
RepackValues,
UnpackOptions,
} from "~/components/custom_ui/multiselect";
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";
@ -49,12 +45,16 @@ import {
TooltipProvider,
TooltipTrigger,
} from "~/components/ui/tooltip";
import { getComuni } from "~/i18n/comuni";
import { GetNazioni } from "~/i18n/nazioni";
import { getProvinciaFromSigla, provincieWithSigla } from "~/i18n/provincie";
import { checkFiscalCodeValidity, getComuneFieldValue } from "~/lib/form_utils";
import {
getProvinciaFromSigla,
ITALY_CATASTO_CODE,
parseDBComune,
parseDBNazione,
} from "~/lib/catasto";
import { checkFiscalCodeValidity } 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 } from "~/schemas/public/Servizio";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
@ -75,8 +75,8 @@ const FormNewServizioSchema = z.object({
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"),
citta_recapiti: z.string().nullable(),
civico_recapiti: z.string(),
codice_destinatario: z.string().nullable(),
codice_fiscale: z.string(),
cognome: z.string().nonempty("Inserisci un cognome valido"),
@ -88,22 +88,18 @@ const FormNewServizioSchema = z.object({
giardino: z.boolean(),
indirizzo_recapiti: z.string().nonempty("Inserisci un indirizzo"),
legale_rappresentante: z.string().nullable(),
luogo_nascita: z.string(),
luogo_nascita: z.string().nullable(),
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" } }),
nazione_nascita: z.string().nullable(),
nazione_recapiti: z.string().nullable(),
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" } }),
provincia_recapiti: z.string().nullable(),
ragione_sociale: z.string().nullable(),
reddito: z.string().nullable(),
sede_legale: z.string().nullable(),
@ -126,6 +122,7 @@ export const FormNewServizioAcquisto = ({
isAdmin?: boolean;
}) => {
const router = useRouter();
const { comuni, nazioni } = useCatasto();
const schema = FormNewServizioSchema.superRefine(
(
{
@ -150,6 +147,9 @@ export const FormNewServizioAcquisto = ({
ragione_sociale,
fatturazione_aziendale,
codice_destinatario,
citta_recapiti,
nazione_recapiti,
provincia_recapiti,
},
refinementContext,
) => {
@ -212,19 +212,64 @@ export const FormNewServizioAcquisto = ({
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),
});
}
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",
@ -287,7 +332,7 @@ export const FormNewServizioAcquisto = ({
...initialData.userData,
azienda: initialData.anagrafica?.azienda || false,
cap_recapiti: initialData.anagrafica?.cap_residenza || "",
citta_recapiti: initialData.anagrafica?.comune_residenza || "",
citta_recapiti: initialData.anagrafica?.comune_residenza || null,
civico_recapiti: initialData.anagrafica?.civico_residenza || "",
codice_destinatario: initialData.anagrafica?.codice_destinatario || "",
codice_fiscale: initialData.anagrafica?.codice_fiscale || "",
@ -298,12 +343,12 @@ export const FormNewServizioAcquisto = ({
initialData.anagrafica?.fatturazione_aziendale || false,
indirizzo_recapiti: initialData.anagrafica?.via_residenza || "",
legale_rappresentante: initialData.anagrafica?.legale_rappresentante || "",
luogo_nascita: initialData.anagrafica?.luogo_nascita || "",
nazione_nascita: initialData.anagrafica?.nazione_nascita || "",
nazione_recapiti: initialData.anagrafica?.nazione_residenza || "",
luogo_nascita: initialData.anagrafica?.luogo_nascita || null,
nazione_nascita: initialData.anagrafica?.nazione_nascita || null,
nazione_recapiti: initialData.anagrafica?.nazione_residenza || null,
p_iva: initialData.anagrafica?.p_iva || "",
provincia_recapiti: initialData.anagrafica?.provincia_residenza || "",
provincia_recapiti: initialData.anagrafica?.provincia_residenza || null,
ragione_sociale: initialData.anagrafica?.ragione_sociale || "",
sede_legale: initialData.anagrafica?.sede_legale || "",
sesso: initialData.anagrafica?.sesso || "M",
@ -311,8 +356,6 @@ export const FormNewServizioAcquisto = ({
};
const { locale, t } = useTranslation();
const nazioni_options = GetNazioni();
const comuni_options = getComuni();
z.config(z.locales[locale]());
const form = useZodForm(schema, {
@ -417,10 +460,9 @@ export const FormNewServizioAcquisto = ({
["pianoterra", string],
];
const altrePrefMapping = t.altrePrefMapping as altrePrefMappingType;
const [tipologia, nazione_nascita, nazione_recapiti, budget] = form.watch([
const [tipologia, nazione_nascita, budget] = form.watch([
"tipologia",
"nazione_nascita",
"nazione_recapiti",
"budget",
]);
@ -1004,45 +1046,66 @@ export const FormNewServizioAcquisto = ({
<FormField
control={form.control}
name="nazione_nascita"
render={({ field }) => (
<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>
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>
<FormControl>
<MultiSelect
defaultValue={RepackValues({
options: nazioni_options,
values: field.value,
})}
elementId="select-naz-nascita"
elementName="nazioneNascita"
isMulti={false}
onValueChange={async (value) => {
field.onChange(value.value);
await form.trigger("luogo_nascita");
if (value.value !== "0") {
form.setValue("luogo_nascita", "");
<FormControl>
<MultiSelect
defaultValue={
field.value
? parsedNazioneNascita
? {
label: parsedNazioneNascita.nome,
value: parsedNazioneNascita.catasto,
}
: {
label: field.value,
value: field.value,
}
: undefined
}
}}
options={UnpackOptions({ options: nazioni_options })}
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
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>
);
}}
/>
<FormField
control={form.control}
name="luogo_nascita"
render={({ field }) => {
if (nazione_nascita === "0") {
const options = comuni_options.map((c) => c.nome);
if (nazione_nascita === ITALY_CATASTO_CODE) {
const parsedComuneNascita = parseDBComune(
field.value,
comuni,
);
return (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
@ -1054,18 +1117,32 @@ export const FormNewServizioAcquisto = ({
<FormControl>
<MultiSelect
defaultValue={getComuneFieldValue(
field.value,
options,
)}
defaultValue={
field.value
? parsedComuneNascita
? {
label: `${parsedComuneNascita.nome} (${parsedComuneNascita.sigla})`,
value: parsedComuneNascita.catasto,
}
: {
label: field.value,
value: field.value,
}
: undefined
}
elementId="select-comune-nascita"
elementName="luogoNascita"
isMulti={false}
onValueChange={async (value) => {
field.onChange(value.value);
await form.trigger("luogo_nascita");
await form.trigger("codice_fiscale");
}}
options={UnpackOptions({ options })}
options={comuni.map((c) => ({
label: `${c.nome} (${c.sigla})`,
value: c.catasto,
}))}
placeholder="Seleziona..."
/>
</FormControl>
@ -1087,8 +1164,10 @@ export const FormNewServizioAcquisto = ({
onChange={async (v) => {
field.onChange(v.target.value);
await form.trigger("luogo_nascita");
await form.trigger("codice_fiscale");
}}
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
@ -1204,179 +1283,11 @@ export const FormNewServizioAcquisto = ({
/>
</div>
<Separator />
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
<FormField
control={form.control}
name="nazione_recapiti"
render={({ field }) => (
<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={RepackValues({
options: nazioni_options,
values: field.value,
})}
elementId="select-naz"
elementName="nazioneRecapiti"
isMulti={false}
onValueChange={async (value) => {
field.onChange(value.value);
form.setValue("provincia_recapiti", "");
if (value.value !== "0") {
form.setValue("citta_recapiti", "");
}
}}
options={UnpackOptions({ options: nazioni_options })}
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="provincia_recapiti"
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={
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">
<FormField
control={form.control}
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">
<FormLabel htmlFor="select-comune">
{t.recapiti.comune}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={getComuneFieldValue(
field.value,
options,
)}
elementId="select-comune"
elementName="citta_recapiti"
isMulti={false}
onValueChange={async (value) => {
field.onChange(value.value);
await form.trigger("citta_recapiti");
}}
options={UnpackOptions({ 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"
onChange={async (v) => {
field.onChange(v.target.value);
await form.trigger("citta_recapiti");
}}
type="text"
/>
</FormControl>
</FormItem>
);
}}
/>
<FormField
control={form.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>
<ResidenzaSection
control={form.control}
setValue={form.setValue}
trigger={form.trigger}
/>
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
<FormField
control={form.control}
@ -1653,3 +1564,229 @@ const FatturazioneSection = ({
/>
);
};
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"
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={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"
onChange={async (v) => {
field.onChange(v.target.value);
await trigger("citta_recapiti");
}}
type="text"
value={field.value || undefined}
/>
</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>
</>
);
};

View file

@ -13,11 +13,7 @@ import {
FormMessage,
} from "~/components/custom_ui/form";
import Input from "~/components/custom_ui/input";
import {
MultiSelect,
RepackValues,
UnpackOptions,
} from "~/components/custom_ui/multiselect";
import { MultiSelect } from "~/components/custom_ui/multiselect";
import { Button } from "~/components/ui/button";
import { Calendar } from "~/components/ui/calendar";
import {
@ -27,12 +23,16 @@ import {
} from "~/components/ui/popover";
import { Separator } from "~/components/ui/separator";
import { Switch } from "~/components/ui/switch";
import { getComuni } from "~/i18n/comuni";
import { GetNazioni } from "~/i18n/nazioni";
import { getProvinciaFromSigla, provincieWithSigla } from "~/i18n/provincie";
import { checkFiscalCodeValidity, getComuneFieldValue } from "~/lib/form_utils";
import {
getProvinciaFromSigla,
ITALY_CATASTO_CODE,
parseDBComune,
parseDBNazione,
} from "~/lib/catasto";
import { checkFiscalCodeValidity } 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 { UserWithAnagrafica } from "~/server/services/user.service";
import { api } from "~/utils/api";
@ -43,15 +43,15 @@ const profileFormSchema = z.object({
civico_residenza: z.string().optional(),
codice_destinatario: z.string().length(7).nullable(),
codice_fiscale: z.string().optional(),
comune_residenza: z.string().optional(),
comune_residenza: z.string().nullable(),
data_nascita: z.date().optional(),
fatturazione_aziendale: z.boolean(),
legale_rappresentante: z.string().nullable(),
luogo_nascita: z.string().optional(),
nazione_nascita: z.string().optional(),
nazione_residenza: z.string().optional(),
luogo_nascita: z.string().nullable(),
nazione_nascita: z.string().nullable(),
nazione_residenza: z.string().nullable(),
p_iva: z.string().nullable(),
provincia_residenza: z.string().optional(),
provincia_residenza: z.string().nullable(),
ragione_sociale: z.string().nullable(),
sede_legale: z.string().nullable(),
sesso: z.string().optional(),
@ -64,6 +64,8 @@ export const ProfileFormAnagrafica = ({
}: {
userData: UserWithAnagrafica;
}) => {
const { comuni, nazioni } = useCatasto();
const schema = profileFormSchema.superRefine(
(
{
@ -167,6 +169,8 @@ export const ProfileFormAnagrafica = ({
path: "codice_fiscale",
refinementContext,
sesso,
comuneCatasto: comuni.find((c) => c.catasto === luogo_nascita),
nazioneCatasto: nazioni.find((c) => c.catasto === nazione_nascita),
});
}
},
@ -178,15 +182,15 @@ export const ProfileFormAnagrafica = ({
civico_residenza: userData.civico_residenza || "",
codice_destinatario: userData.codice_destinatario || null,
codice_fiscale: userData.codice_fiscale || "",
comune_residenza: userData.comune_residenza || "",
comune_residenza: userData.comune_residenza,
data_nascita: userData.data_nascita || new Date(),
fatturazione_aziendale: userData.fatturazione_aziendale || false,
legale_rappresentante: userData.legale_rappresentante || null,
luogo_nascita: userData.luogo_nascita || "",
nazione_nascita: userData.nazione_nascita || "",
nazione_residenza: userData.nazione_residenza || "",
luogo_nascita: userData.luogo_nascita,
nazione_nascita: userData.nazione_nascita,
nazione_residenza: userData.nazione_residenza,
p_iva: userData.p_iva || null,
provincia_residenza: userData.provincia_residenza || "",
provincia_residenza: userData.provincia_residenza,
ragione_sociale: userData.ragione_sociale || null,
sede_legale: userData.sede_legale || null,
sesso: userData.sesso || "",
@ -222,8 +226,6 @@ export const ProfileFormAnagrafica = ({
data: { ...fields },
});
}
const nazioni_options = GetNazioni();
const comuni_options = getComuni();
return (
<>
@ -288,46 +290,64 @@ export const ProfileFormAnagrafica = ({
<FormField
control={form.control}
name="nazione_nascita"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-naz">
{t.anagrafica.nazioneNascita}
</FormLabel>
<FormMessage />
</div>
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">
{t.anagrafica.nazioneNascita}
</FormLabel>
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={
field.value
? RepackValues({
options: nazioni_options,
values: field.value,
})
: undefined
}
elementId="select-naz"
elementName="nazione_nascita"
isMulti={false}
onValueChange={async (value) => {
field.onChange(value.value);
await form.trigger("luogo_nascita");
}}
options={UnpackOptions({ options: nazioni_options })}
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
<FormControl>
<MultiSelect
defaultValue={
field.value
? parsedNazioneNascita
? {
label: parsedNazioneNascita.nome,
value: parsedNazioneNascita.catasto,
}
: {
label: field.value,
value: field.value,
}
: undefined
}
elementId="select-naz"
elementName="nazione_nascita"
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>
);
}}
/>
<FormField
control={form.control}
name="luogo_nascita"
render={({ field }) => {
if (form.watch("nazione_nascita") === "0") {
const options = comuni_options.map((c) => c.nome);
if (form.watch("nazione_nascita") === ITALY_CATASTO_CODE) {
const parsedComuneNascita = parseDBComune(
field.value,
comuni,
);
return (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
@ -340,8 +360,11 @@ export const ProfileFormAnagrafica = ({
<FormControl>
<MultiSelect
defaultValue={
field.value
? getComuneFieldValue(field.value, options)
field.value && parsedComuneNascita
? {
label: `${parsedComuneNascita.nome} (${parsedComuneNascita.sigla})`,
value: parsedComuneNascita.catasto,
}
: undefined
}
elementId="select-comune"
@ -352,7 +375,10 @@ export const ProfileFormAnagrafica = ({
await form.trigger("luogo_nascita");
await form.trigger("codice_fiscale");
}}
options={UnpackOptions({ options })}
options={comuni.map((c) => ({
label: `${c.nome} (${c.sigla})`,
value: c.catasto,
}))}
placeholder="Seleziona..."
/>
</FormControl>
@ -377,6 +403,7 @@ export const ProfileFormAnagrafica = ({
await form.trigger("codice_fiscale");
}}
type="text"
value={field.value || ""}
/>
</FormControl>
</FormItem>
@ -680,9 +707,9 @@ const ResidenzaSection = ({
setValue: (field: keyof ProfileFormValues, value: any) => void;
trigger: (name?: keyof ProfileFormValues) => Promise<boolean>;
}) => {
const { comuni, nazioni, provincie } = useCatasto();
const { t } = useTranslation();
const nazioni_options = GetNazioni();
const comuni_options = getComuni();
const nazione_residenza = useWatch({
control: control,
name: "nazione_residenza",
@ -698,44 +725,58 @@ const ResidenzaSection = ({
<FormField
control={control}
name="nazione_residenza"
render={({ field }) => (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="nazione_residenza">
{t.recapiti.nazione}
</FormLabel>
<FormMessage />
</div>
<MultiSelect
defaultValue={
field.value
? RepackValues({
options: nazioni_options,
values: field.value,
})
: undefined
}
elementId="nazione_residenza"
elementName="nazione"
isMulti={false}
onValueChange={async (value) => {
setValue("provincia_residenza", "");
field.onChange(value.value);
await trigger("comune_residenza");
}}
options={UnpackOptions({ options: nazioni_options })}
placeholder="Seleziona"
/>
</FormItem>
)}
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="nazione_residenza">
{t.recapiti.nazione}
</FormLabel>
<FormMessage />
</div>
<MultiSelect
defaultValue={
field.value
? parsedNazioneResidenza
? {
label: parsedNazioneResidenza.nome,
value: parsedNazioneResidenza.catasto,
}
: {
label: field.value,
value: field.value,
}
: undefined
}
elementId="nazione_residenza"
elementName="nazione"
isMulti={false}
onValueChange={async (value) => {
setValue("provincia_residenza", null);
setValue("comune_residenza", null);
field.onChange(value.value);
await trigger("comune_residenza");
}}
options={nazioni.map((n) => ({
label: n.nome,
value: n.catasto,
}))}
placeholder="Seleziona"
/>
</FormItem>
);
}}
/>
<FormField
control={control}
name="provincia_residenza"
render={({ field }) => {
const provinciaName =
field.value && getProvinciaFromSigla(field.value);
const parseProvincia = getProvinciaFromSigla(
field.value,
provincie,
);
return (
<FormItem className="w-full">
<div className="flex flex-wrap items-center gap-x-2">
@ -745,13 +786,13 @@ const ResidenzaSection = ({
<FormMessage />
</div>
{nazione_residenza === "0" ? (
{nazione_residenza === ITALY_CATASTO_CODE ? (
<MultiSelect
defaultValue={
field.value && provinciaName
field.value && parseProvincia
? {
label: provinciaName,
value: field.value,
label: parseProvincia.nome,
value: parseProvincia.sigla,
}
: undefined
}
@ -760,8 +801,9 @@ const ResidenzaSection = ({
isMulti={false}
onValueChange={(value) => {
field.onChange(value.value);
setValue("comune_residenza", null);
}}
options={provincieWithSigla.map((prov) => ({
options={provincie.map((prov) => ({
label: prov.nome,
value: prov.sigla,
}))}
@ -769,7 +811,12 @@ const ResidenzaSection = ({
/>
) : (
<FormControl>
<Input {...field} id="provincia_residenza" type="text" />
<Input
{...field}
id="provincia_residenza"
type="text"
value={field.value || undefined}
/>
</FormControl>
)}
</FormItem>
@ -782,14 +829,19 @@ const ResidenzaSection = ({
control={control}
name="comune_residenza"
render={({ field }) => {
if (nazione_residenza === "0") {
const options = comuni_options
if (nazione_residenza === ITALY_CATASTO_CODE) {
const parsedComuneResidenza = parseDBComune(field.value, comuni);
const options = comuni
.filter((comune) =>
provincia_residenza
? comune.provincia === provincia_residenza
? comune.sigla === provincia_residenza
: true,
)
.map((comune) => comune.nome);
.map((comune) => ({
label: comune.nome,
value: comune.catasto,
}));
return (
<FormItem className="w-full">
@ -803,8 +855,11 @@ const ResidenzaSection = ({
<FormControl>
<MultiSelect
defaultValue={
field.value
? getComuneFieldValue(field.value, options)
field.value && parsedComuneResidenza
? {
label: parsedComuneResidenza.nome,
value: parsedComuneResidenza.catasto,
}
: undefined
}
elementId="select-comune"
@ -814,9 +869,7 @@ const ResidenzaSection = ({
field.onChange(value.value);
await trigger("comune_residenza");
}}
options={UnpackOptions({
options: options,
})}
options={options}
placeholder="Seleziona..."
/>
</FormControl>
@ -841,6 +894,7 @@ const ResidenzaSection = ({
await trigger("comune_residenza");
}}
type="text"
value={field.value || undefined}
/>
</FormControl>
</FormItem>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,120 +0,0 @@
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

@ -0,0 +1,60 @@
import type { Comuni } from "~/schemas/public/Comuni";
import type { Nazioni } from "~/schemas/public/Nazioni";
import type { Provincie } from "~/schemas/public/Provincie";
export const ITALY_CATASTO_CODE = "0000";
export const isNazioneCatastoValid = (str: string) => {
if (str.length !== 4) return false;
if (str === ITALY_CATASTO_CODE) return true;
return /^Z[0-9]{3}$/.test(str);
};
export const isComuneCatastoValid = (str: string) => {
if (str.length !== 4) return false;
return /^[A-Z][0-9]{3}$/.test(str);
};
export const getComuneFromCatasto = (
codice_catasto: string,
comuni_options: Comuni[],
): Comuni | undefined => {
return comuni_options.find((c) => c.catasto === codice_catasto);
};
export const getNazioneFromCatasto = (
codice_catasto: string,
nazioni_options: Nazioni[],
): Nazioni | undefined => {
return nazioni_options.find((n) => n.catasto === codice_catasto);
};
export const parseDBComune = (
db_value: string | null,
comuni: Comuni[],
): Comuni | undefined => {
if (!db_value) return undefined;
if (isComuneCatastoValid(db_value)) {
return getComuneFromCatasto(db_value, comuni);
}
return undefined;
};
export const parseDBNazione = (
db_value: string | null,
nazioni: Nazioni[],
): Nazioni | undefined => {
if (!db_value) return undefined;
if (isNazioneCatastoValid(db_value)) {
return getNazioneFromCatasto(db_value, nazioni);
}
return undefined;
};
export const getProvinciaFromSigla = (
str: string | null,
provincie: Provincie[],
): Provincie | undefined => {
if (!str) return undefined;
if (str.length !== 2) return undefined;
return provincie.find((p) => p.sigla === str);
};

View file

@ -1,8 +1,9 @@
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";
import type { Comuni } from "~/schemas/public/Comuni";
import type { Nazioni } from "~/schemas/public/Nazioni";
import { ITALY_CATASTO_CODE } from "./catasto";
export const FormDebug: React.ElementType =
process.env.NODE_ENV === "development"
@ -87,50 +88,22 @@ const getAlternativeCF = (input: string): string[] => {
return result;
};
const genCatasto = ({
luogoNascita,
nazioneNascita,
}: {
luogoNascita: string;
nazioneNascita: string;
}) => {
let catasto = "";
if (nazioneNascita === "0") {
if (luogoNascita !== "") {
const comune = getComuneFromIndex(parseInt(luogoNascita));
if (comune) {
catasto = comune.CATASTO;
}
}
} else {
if (nazioneNascita !== "") {
const nazione = searchNazioneByIndex(parseInt(nazioneNascita));
if (nazione) {
catasto = nazione.CATASTO;
}
}
}
return catasto;
};
const gen15CF = ({
cognome,
nome,
dataNascita,
sesso,
luogoNascita,
nazioneNascita,
catasto,
}: {
cognome: string;
nome: string;
dataNascita: Date;
sesso: string;
luogoNascita: string;
nazioneNascita: string;
catasto: string;
}): string => {
const data = new Date(dataNascita);
return `${getInizialiCF(cognome)}${getInizialiCF(nome)}${data.getFullYear().toString().slice(-2)}${getMonthLetterCF(data.getMonth())}${getDayCF(data.getDate(), sesso)}${genCatasto({ luogoNascita, nazioneNascita })}`;
return `${getInizialiCF(cognome)}${getInizialiCF(nome)}${data.getFullYear().toString().slice(-2)}${getMonthLetterCF(data.getMonth())}${getDayCF(data.getDate(), sesso)}${catasto}`;
};
const set1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@ -165,6 +138,8 @@ export const checkFiscalCodeValidity = ({
refinementContext,
luogoNascita,
nazioneNascita,
comuneCatasto,
nazioneCatasto,
}: {
path: string;
codiceFiscale: string;
@ -175,14 +150,31 @@ export const checkFiscalCodeValidity = ({
refinementContext: z.RefinementCtx;
luogoNascita: string;
nazioneNascita: string;
comuneCatasto: Comuni | undefined;
nazioneCatasto: Nazioni | undefined;
}) => {
const cognomeUpper = cognome.toUpperCase();
const nomeUpper = nome.toUpperCase();
let codiceCatasto = "";
if (nazioneNascita === ITALY_CATASTO_CODE) {
if (luogoNascita !== "") {
if (comuneCatasto) {
codiceCatasto = comuneCatasto.catasto;
}
}
} else {
if (nazioneNascita !== "") {
if (nazioneCatasto) {
codiceCatasto = nazioneCatasto.catasto;
}
}
}
const gen = gen15CF({
cognome: cognomeUpper,
dataNascita,
luogoNascita,
nazioneNascita,
catasto: codiceCatasto,
nome: nomeUpper,
sesso,
});
@ -228,7 +220,7 @@ export const checkFiscalCodeValidity = ({
refinementContext.issues.push({
code: "custom",
input: "",
message: "Inserisci un codice fiscale valido",
message: `Inserisci un codice fiscale valido: received ${codiceFiscale}`,
path: [path],
});
}
@ -277,10 +269,13 @@ export const checkFiscalCodeValidity = ({
path: [path],
});
}
if (nazioneNascita === "0") {
if (nazioneNascita === ITALY_CATASTO_CODE) {
if (luogoNascita !== "") {
const comune = getComuneFromIndex(parseInt(luogoNascita));
if (comune && codiceFiscale.slice(11, 15) !== comune.CATASTO) {
if (
comuneCatasto &&
codiceFiscale.slice(11, 15) !== comuneCatasto.catasto
) {
refinementContext.issues.push({
code: "custom",
input: "",
@ -291,8 +286,10 @@ export const checkFiscalCodeValidity = ({
}
} else {
if (nazioneNascita !== "") {
const nazione = searchNazioneByIndex(parseInt(nazioneNascita));
if (nazione && codiceFiscale.slice(11, 15) !== nazione.CATASTO) {
if (
nazioneCatasto &&
codiceFiscale.slice(11, 15) !== nazioneCatasto.catasto
) {
refinementContext.issues.push({
code: "custom",
input: "",

View file

@ -33,6 +33,7 @@ import { OverridePasswordForm } from "~/forms/FormOverridePassword";
import { ProfileFormAccount } from "~/forms/FormProfilo_Account";
import { ProfileFormAnagrafica } from "~/forms/FormProfilo_Anagrafica";
import type { NextPageWithLayout } from "~/pages/_app";
import { CatastoProvider } from "~/providers/CatastoProvider";
import type { UsersId } from "~/schemas/public/Users";
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
import { zUserId } from "~/server/utils/zod_types";
@ -193,7 +194,9 @@ const EditUser: NextPageWithLayout<EditUserProps> = ({
</div>
</TabsContent>
<TabsContent className="mt-5 h-full space-y-6" value="anagrafica">
<ProfileFormAnagrafica userData={userData} />
<CatastoProvider>
<ProfileFormAnagrafica userData={userData} />
</CatastoProvider>
</TabsContent>
<TabsContent className="mt-5 h-full space-y-6" value="documenti">
<DocumentiPersonali userData={userData} />

View file

@ -4,6 +4,7 @@ import { LoadingPage } from "~/components/loading";
import { Status500 } from "~/components/status-page";
import { FormNewServizioAcquisto } from "~/forms/FormNewServizioAcquisto";
import type { NextPageWithLayout } from "~/pages/_app";
import { CatastoProvider } from "~/providers/CatastoProvider";
import type { ServizioServizioId } from "~/schemas/public/Servizio";
import type { UsersId } from "~/schemas/public/Users";
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
@ -34,11 +35,13 @@ const OnboardUser: NextPageWithLayout<OnboardUserProps> = ({
Onboarding Utente (Versione Admin)
</h3>
</div>
<FormNewServizioAcquisto
initialData={data}
isAdmin
userId={data.userData.id}
/>
<CatastoProvider>
<FormNewServizioAcquisto
initialData={data}
isAdmin
userId={data.userData.id}
/>
</CatastoProvider>
</div>
);
};

View file

@ -16,6 +16,7 @@ import { ChangePasswordForm } from "~/forms/FormChangePassword";
import { ProfileFormAccount } from "~/forms/FormProfilo_Account";
import { ProfileFormAnagrafica } from "~/forms/FormProfilo_Anagrafica";
import type { NextPageWithLayout } from "~/pages/_app";
import { CatastoProvider } from "~/providers/CatastoProvider";
import { useTranslation } from "~/providers/I18nProvider";
import { useEnforcedSession } from "~/providers/SessionProvider";
import { api } from "~/utils/api";
@ -104,7 +105,9 @@ const Dashboard: NextPageWithLayout = () => {
</CardDescription>
</CardHeader>
<CardContent>
<ProfileFormAnagrafica userData={userData} />
<CatastoProvider>
<ProfileFormAnagrafica userData={userData} />
</CatastoProvider>
</CardContent>
</Card>
</TabsContent>

View file

@ -5,6 +5,7 @@ import { OnboardTutorial } from "~/components/onboard_tutorial";
import { Status500 } from "~/components/status-page";
import { FormNewServizioAcquisto } from "~/forms/FormNewServizioAcquisto";
import type { NextPageWithLayout } from "~/pages/_app";
import { CatastoProvider } from "~/providers/CatastoProvider";
import { useTranslation } from "~/providers/I18nProvider";
import type { ServizioServizioId } from "~/schemas/public/Servizio";
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
@ -36,7 +37,9 @@ const OnboardServizio: NextPageWithLayout<OnboardServizioProps> = ({
</h3>
</div>
<OnboardTutorial />
<FormNewServizioAcquisto initialData={data} userId={data.userData.id} />
<CatastoProvider>
<FormNewServizioAcquisto initialData={data} userId={data.userData.id} />
</CatastoProvider>
</div>
);
};

View file

@ -0,0 +1,42 @@
import { createContext, type ReactNode, useContext } from "react";
import { LoadingPage } from "~/components/loading";
import type { Comuni } from "~/schemas/public/Comuni";
import type { Nazioni } from "~/schemas/public/Nazioni";
import type { Provincie } from "~/schemas/public/Provincie";
import { api } from "~/utils/api";
type CatastoContextType = {
comuni: Comuni[];
provincie: Provincie[];
nazioni: Nazioni[];
};
const CatastoContext = createContext<CatastoContextType | null>(null);
export const CatastoProvider = ({ children }: { children: ReactNode }) => {
const { data: comuni = [], isLoading: isLoadingComune } =
api.catasto.getComuni.useQuery();
const { data: provincie = [], isLoading: isLoadingProvincie } =
api.catasto.getProvincie.useQuery();
const { data: nazioni = [], isLoading: isLoadingNazioni } =
api.catasto.getNazioni.useQuery();
return (
<CatastoContext.Provider value={{ comuni, provincie, nazioni }}>
{isLoadingComune || isLoadingProvincie || isLoadingNazioni ? (
<LoadingPage />
) : (
children
)}
</CatastoContext.Provider>
);
};
export const useCatasto = () => {
const context = useContext(CatastoContext);
if (!context) {
throw new Error("useCatasto must be used within a CatastoProvider");
}
return context;
};

View file

@ -19,6 +19,7 @@ import { stripeRouter } from "~/server/api/routers/stripe";
import { testRouter } from "~/server/api/routers/test";
import { usersRouter } from "~/server/api/routers/users";
import { createTRPCRouter } from "~/server/api/trpc";
import { catastoRouter } from "./routers/catasto";
import { syncRouter } from "./routers/sync";
//import { lazy } from '@trpc/server';
@ -45,5 +46,6 @@ export const appRouter = createTRPCRouter({
test: testRouter,
users: usersRouter,
sync: syncRouter,
catasto: catastoRouter,
});
export type AppRouter = typeof appRouter;

View file

@ -0,0 +1,41 @@
import z from "zod";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
import {
getComuneById,
getComuni,
getNazioneById,
getNazioni,
getProvincie,
} from "~/server/controllers/catasto.controller";
import { zComuneId, zNazioneId } from "~/server/utils/zod_types";
export const catastoRouter = createTRPCRouter({
getComuni: publicProcedure.query(async () => {
return await getComuni();
}),
getComuneById: publicProcedure
.input(
z.object({
id: zComuneId,
}),
)
.query(async ({ input }) => {
return await getComuneById(input.id);
}),
getProvincie: publicProcedure.query(async () => {
return await getProvincie();
}),
getNazioni: publicProcedure.query(async () => {
return await getNazioni();
}),
getNazioneById: publicProcedure
.input(
z.object({
id: zNazioneId,
}),
)
.query(async ({ input }) => {
return await getNazioneById(input.id);
}),
});

View file

@ -0,0 +1,67 @@
import { TRPCError } from "@trpc/server";
import type { Comuni, ComuniId } from "~/schemas/public/Comuni";
import type { NazioniId } from "~/schemas/public/Nazioni";
import { db } from "../db";
export type Comune = Pick<Comuni, "id" | "nome" | "sigla">;
export const getComuni = async () => {
try {
return await db.selectFrom("comuni").selectAll().execute();
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Error while getting Comuni: ${(e as Error).message}`,
});
}
};
export const getComuneById = async (id: ComuniId) => {
try {
return await db
.selectFrom("comuni")
.selectAll()
.where("id", "=", id)
.executeTakeFirst();
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Error while getting ComuneById: ${(e as Error).message}`,
});
}
};
export const getProvincie = async () => {
try {
return await db.selectFrom("provincie").selectAll().execute();
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Error while getting Provincie: ${(e as Error).message}`,
});
}
};
export const getNazioni = async () => {
try {
return await db.selectFrom("nazioni").selectAll().execute();
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Error while getting Nazioni: ${(e as Error).message}`,
});
}
};
export const getNazioneById = async (id: NazioniId) => {
try {
return await db
.selectFrom("nazioni")
.selectAll()
.where("id", "=", id)
.executeTakeFirst();
} catch (e) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Error while getting NazioneById: ${(e as Error).message}`,
});
}
};

View file

@ -3,9 +3,11 @@ import type { AnnunciId } from "~/schemas/public/Annunci";
import type { BanlistId } from "~/schemas/public/Banlist";
import BanType from "~/schemas/public/BanType";
import type { ChatsChatid } from "~/schemas/public/Chats";
import type { ComuniId } from "~/schemas/public/Comuni";
import type { EtichetteIdEtichetta } from "~/schemas/public/Etichette";
import type { FlagsId } from "~/schemas/public/Flags";
import type { MessagesMessageid } from "~/schemas/public/Messages";
import type { NazioniId } from "~/schemas/public/Nazioni";
import type { OrdiniOrdineId } from "~/schemas/public/Ordini";
import type { PaymentsId } from "~/schemas/public/Payments";
import type { PrezziarioIdprezziario } from "~/schemas/public/Prezziario";
@ -82,3 +84,13 @@ export const zPagamentoId = z.custom<PaymentsId>(
(val) => typeof val === "string",
"falied to validate PagamentoId",
);
export const zComuneId = z.custom<ComuniId>(
(val) => typeof val === "number",
"falied to validate ComuneId",
);
export const zNazioneId = z.custom<NazioniId>(
(val) => typeof val === "number",
"falied to validate NazioneId",
);