refactor: implement NullableStringOnChange utility for improved form handling across multiple components

This commit is contained in:
Marco Pedone 2025-12-17 16:38:15 +01:00
parent b376de48bb
commit bc9689cb4d
7 changed files with 151 additions and 68 deletions

View file

@ -118,7 +118,7 @@ export const FormBanlist = ({
placeholder="" placeholder=""
{...field} {...field}
id="testo" id="testo"
value={field.value || ""} value={field.value}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>

View file

@ -18,6 +18,7 @@ import {
SelectValue, SelectValue,
} from "~/components/ui/select"; } from "~/components/ui/select";
import { Switch } from "~/components/ui/switch"; import { Switch } from "~/components/ui/switch";
import { NullableStringOnChange } from "~/lib/form_utils";
import { useZodForm } from "~/lib/zodForm"; import { useZodForm } from "~/lib/zodForm";
import type { Banners, BannersIdbanner } from "~/schemas/public/Banners"; import type { Banners, BannersIdbanner } from "~/schemas/public/Banners";
@ -123,6 +124,7 @@ export const FormBanners = ({
placeholder="" placeholder=""
{...field} {...field}
id="titolo" id="titolo"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
@ -143,6 +145,7 @@ export const FormBanners = ({
placeholder="" placeholder=""
{...field} {...field}
id="testo" id="testo"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
@ -229,6 +232,7 @@ export const FormBanners = ({
placeholder="" placeholder=""
{...field} {...field}
id="cta_href" id="cta_href"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
@ -251,6 +255,7 @@ export const FormBanners = ({
placeholder="" placeholder=""
{...field} {...field}
id="cta_icon" id="cta_icon"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
@ -271,6 +276,7 @@ export const FormBanners = ({
placeholder="" placeholder=""
{...field} {...field}
id="cta_text" id="cta_text"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>

View file

@ -42,6 +42,7 @@ import {
import { Switch } from "~/components/ui/switch"; import { Switch } from "~/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
import { filteredCaratteristiche } from "~/hooks/schedaAnnuncioUtils"; import { filteredCaratteristiche } from "~/hooks/schedaAnnuncioUtils";
import { NullableStringOnChange } from "~/lib/form_utils";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm"; import { useZodForm } from "~/lib/zodForm";
import { StatusBadge } from "~/pages/area-riservata/admin/edit-annuncio/[id]"; import { StatusBadge } from "~/pages/area-riservata/admin/edit-annuncio/[id]";
@ -69,7 +70,7 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
desc_en: z.string().nullable(), desc_en: z.string().nullable(),
desc_it: z.string().nullable(), desc_it: z.string().nullable(),
disponibile_da: z.date().nullable(), disponibile_da: z.date().nullable(),
email: z.email().optional(), email: z.email().nullable(),
homepage: z.boolean().nullable(), homepage: z.boolean().nullable(),
//BASE //BASE
id: zAnnuncioId, id: zAnnuncioId,
@ -162,7 +163,6 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
const defaultValues: FormValues = { const defaultValues: FormValues = {
...data, ...data,
mq: Number(data.mq) || null, mq: Number(data.mq) || null,
email: data.email || undefined,
}; };
z.config(z.locales[locale]()); z.config(z.locales[locale]());
@ -174,7 +174,7 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
function onSubmit(fields: FormValues) { function onSubmit(fields: FormValues) {
edit({ edit({
annuncioId: data.id, annuncioId: data.id,
data: { ...fields, email: fields.email || null }, data: fields,
}); });
} }
@ -328,7 +328,13 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="titolo_it" id="titolo_it"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(
e,
field.onChange,
)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -354,7 +360,13 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="desc_it" id="desc_it"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(
e,
field.onChange,
)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -390,7 +402,13 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="titolo_en" id="titolo_en"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(
e,
field.onChange,
)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -416,7 +434,13 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="desc_en" id="desc_en"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(
e,
field.onChange,
)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -549,9 +573,9 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
{...field} {...field}
id="locatore" id="locatore"
onChange={(e) => onChange={(e) =>
field.onChange(e.target.value || null) NullableStringOnChange(e, field.onChange)
} }
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -573,15 +597,12 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
<Input <Input
placeholder="esempio@email.com" placeholder="esempio@email.com"
{...field} {...field}
autoComplete="off"
id="email" id="email"
onChange={(e) => onChange={(e) =>
field.onChange( NullableStringOnChange(e, field.onChange)
e.target.value && e.target.value !== ""
? e.target.value
: null,
)
} }
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -641,7 +662,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="indirizzo" id="indirizzo"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -664,7 +688,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="civico" id="civico"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -689,7 +716,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="comune" id="comune"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -712,7 +742,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="cap" id="cap"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -737,7 +770,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="provincia" id="provincia"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -760,7 +796,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="regione" id="regione"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -785,7 +824,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="lat" id="lat"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -808,7 +850,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="lon" id="lon"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -844,7 +889,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="indirizzo_secondario" id="indirizzo_secondario"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -869,7 +917,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="civico_secondario" id="civico_secondario"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -896,7 +947,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="lat_secondario" id="lat_secondario"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -921,7 +975,10 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
placeholder="" placeholder=""
{...field} {...field}
id="lon_secondario" id="lon_secondario"
value={field.value || undefined} onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -988,13 +1045,13 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className="flex flex-wrap items-center gap-x-2"> <div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="categorie">Categorie</FormLabel> <span className="font-medium text-sm leading-none">
Categorie
</span>
<FormMessage /> <FormMessage />
</div> </div>
<p className="h-[42px]" id="categorie"> <p className="h-[42px]">{field.value?.join(", ")}</p>
{field.value?.join(", ")}
</p>
</FormItem> </FormItem>
)} )}
/> />
@ -1235,11 +1292,9 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
{...field} {...field}
id="anno" id="anno"
onChange={(e) => onChange={(e) =>
field.onChange( NullableStringOnChange(e, field.onChange)
e.target.value !== "" ? e.target.value : null,
)
} }
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1262,11 +1317,9 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
{...field} {...field}
id="classe" id="classe"
onChange={(e) => onChange={(e) =>
field.onChange( NullableStringOnChange(e, field.onChange)
e.target.value !== "" ? e.target.value : null,
)
} }
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1322,11 +1375,9 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
{...field} {...field}
id="piano" id="piano"
onChange={(e) => onChange={(e) =>
field.onChange( NullableStringOnChange(e, field.onChange)
e.target.value !== "" ? e.target.value : null,
)
} }
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1748,14 +1799,14 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className="flex flex-wrap items-center gap-x-2"> <div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="accessori">Accessori</FormLabel> <span className="font-medium text-sm leading-none">
Accessori
</span>
<FormMessage /> <FormMessage />
</div> </div>
<FormControl> <FormControl>
<p className="h-[42px]" id="accessori"> <p className="h-[42px]">{field.value?.join(", ")}</p>
{field.value?.join(", ")}
</p>
</FormControl> </FormControl>
</FormItem> </FormItem>
)} )}
@ -1766,14 +1817,15 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className="flex flex-wrap items-center gap-x-2"> <div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="caratteristiche"> <span className="font-medium text-sm leading-none">
Caratteristiche Caratteristiche
</FormLabel> </span>
<FormMessage /> <FormMessage />
</div> </div>
<FormControl> <FormControl>
<p className="h-[42px]" id="caratteristiche"> <p className="h-[42px]">
{field.value {field.value
? filteredCaratteristiche(field.value) ? filteredCaratteristiche(field.value)
.map((v) => v.text) .map((v) => v.text)

View file

@ -86,7 +86,7 @@ export const FormEtichette = ({
placeholder="" placeholder=""
{...field} {...field}
id="value" id="value"
value={field.value || ""} value={field.value}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>

View file

@ -51,7 +51,10 @@ import {
parseDBComune, parseDBComune,
parseDBNazione, parseDBNazione,
} from "~/lib/catasto"; } from "~/lib/catasto";
import { checkFiscalCodeValidity } from "~/lib/form_utils"; import {
checkFiscalCodeValidity,
NullableStringOnChange,
} from "~/lib/form_utils";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm"; import { useZodForm } from "~/lib/zodForm";
import { useCatasto } from "~/providers/CatastoProvider"; import { useCatasto } from "~/providers/CatastoProvider";
@ -1157,13 +1160,14 @@ export const FormNewServizioAcquisto = ({
<Input <Input
{...field} {...field}
id="luogoNascita" id="luogoNascita"
onChange={async (v) => { onChange={async (e) => {
field.onChange(v.target.value); NullableStringOnChange(e, field.onChange);
await form.trigger("luogo_nascita"); await form.trigger("luogo_nascita");
await form.trigger("codice_fiscale"); await form.trigger("codice_fiscale");
}} }}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1409,8 +1413,9 @@ const AziendaSection = ({
<Input <Input
{...field} {...field}
id="ragione_sociale" id="ragione_sociale"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1431,8 +1436,9 @@ const AziendaSection = ({
<Input <Input
{...field} {...field}
id="sede_legale" id="sede_legale"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1459,8 +1465,9 @@ const AziendaSection = ({
<Input <Input
{...field} {...field}
id="p_iva" id="p_iva"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1481,8 +1488,9 @@ const AziendaSection = ({
<Input <Input
{...field} {...field}
id="legale_rappresentante" id="legale_rappresentante"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1551,8 +1559,9 @@ const FatturazioneSection = ({
<Input <Input
{...field} {...field}
id="codice_destinatario" id="codice_destinatario"
onChange={(e) => NullableStringOnChange(e, field.onChange)}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>
@ -1680,8 +1689,11 @@ const ResidenzaSection = ({
<Input <Input
{...field} {...field}
id="provinciaRecapiti" id="provinciaRecapiti"
onChange={(e) =>
NullableStringOnChange(e, field.onChange)
}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
)} )}
@ -1754,12 +1766,12 @@ const ResidenzaSection = ({
<Input <Input
{...field} {...field}
id="citta_recapiti" id="citta_recapiti"
onChange={async (v) => { onChange={async (e) => {
field.onChange(v.target.value); NullableStringOnChange(e, field.onChange);
await trigger("citta_recapiti"); await trigger("citta_recapiti");
}} }}
type="text" type="text"
value={field.value || undefined} value={field.value || ""}
/> />
</FormControl> </FormControl>
</FormItem> </FormItem>

View file

@ -29,7 +29,10 @@ import {
parseDBComune, parseDBComune,
parseDBNazione, parseDBNazione,
} from "~/lib/catasto"; } from "~/lib/catasto";
import { checkFiscalCodeValidity } from "~/lib/form_utils"; import {
checkFiscalCodeValidity,
NullableStringOnChange,
} from "~/lib/form_utils";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
import { useZodForm } from "~/lib/zodForm"; import { useZodForm } from "~/lib/zodForm";
import { useCatasto } from "~/providers/CatastoProvider"; import { useCatasto } from "~/providers/CatastoProvider";
@ -397,8 +400,9 @@ export const ProfileFormAnagrafica = ({
<Input <Input
{...field} {...field}
id="luogoNascita" id="luogoNascita"
onChange={async (v) => { onChange={async (e) => {
field.onChange(v.target.value); NullableStringOnChange(e, field.onChange);
await form.trigger("luogo_nascita"); await form.trigger("luogo_nascita");
await form.trigger("codice_fiscale"); await form.trigger("codice_fiscale");
}} }}

View file

@ -1,10 +1,19 @@
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import type { ChangeEvent } from "react";
import type { z } from "zod/v4"; import type { z } from "zod/v4";
import type { ListOption } from "~/components/custom_ui/multiselect"; import type { ListOption } from "~/components/custom_ui/multiselect";
import type { Comuni } from "~/schemas/public/Comuni"; import type { Comuni } from "~/schemas/public/Comuni";
import type { Nazioni } from "~/schemas/public/Nazioni"; import type { Nazioni } from "~/schemas/public/Nazioni";
import { ITALY_CATASTO_CODE } from "./catasto"; import { ITALY_CATASTO_CODE } from "./catasto";
export const NullableStringOnChange = (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
// biome-ignore lint/suspicious/noExplicitAny: <intended>
fn: (...event: any[]) => void,
) => {
fn(e.target.value && e.target.value !== "" ? e.target.value : null);
};
export const FormDebug: React.ElementType = export const FormDebug: React.ElementType =
process.env.NODE_ENV === "development" process.env.NODE_ENV === "development"
? dynamic( ? dynamic(