infoalloggi-monorepo/apps/infoalloggi/src/forms/FormEditAnnuncio.tsx

1776 lines
52 KiB
TypeScript
Raw Normal View History

2025-08-28 18:27:07 +02:00
import { format } from "date-fns";
import { it } from "date-fns/locale";
import {
CalendarIcon,
CodeSquare,
Eye,
Printer,
RefreshCcw,
RotateCw,
TriangleAlert,
} from "lucide-react";
2025-08-28 18:27:07 +02:00
import Link from "next/link";
import toast from "react-hot-toast";
import z from "zod";
2025-08-28 18:27:07 +02:00
import { CarouselAnnuncio } from "~/components/annuncio_card";
import { Confirm } from "~/components/confirm";
import {
2025-08-28 18:27:07 +02:00
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "~/components/custom_ui/form";
import { NumberInput } from "~/components/custom_ui/InputNumber";
2025-08-28 18:27:07 +02:00
import Input from "~/components/custom_ui/input";
import { DualInputLayout } from "~/components/custom_ui/inputLayouts";
import {
2025-08-28 18:27:07 +02:00
type ListOption,
MultiSelect,
} from "~/components/custom_ui/multiselect";
2025-08-28 18:27:07 +02:00
import { Textarea } from "~/components/custom_ui/textarea";
import { PhoneInput } from "~/components/phone-input";
import { Button } from "~/components/ui/button";
2025-08-28 18:27:07 +02:00
import { Calendar } from "~/components/ui/calendar";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
2025-08-28 18:27:07 +02:00
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "~/components/ui/popover";
import { Switch } from "~/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
2025-08-28 18:27:07 +02:00
import { filteredCaratteristiche } from "~/hooks/schedaAnnuncioUtils";
import { cn } from "~/lib/utils";
2025-08-28 18:27:07 +02:00
import { useZodForm } from "~/lib/zodForm";
import { StatusBadge } from "~/pages/area-riservata/admin/edit-annuncio/[id]";
import { useTranslation } from "~/providers/I18nProvider";
import type { AnnunciWithMedia } from "~/server/controllers/annunci.controller";
2025-08-28 18:27:07 +02:00
import { zAnnuncioId } from "~/server/utils/zod_types";
import { api } from "~/utils/api";
import type { Caratteristiche } from "~/utils/kanel-types";
export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
2025-08-28 18:27:07 +02:00
const schema = z.object({
2025-08-29 16:18:32 +02:00
accessori: z.string().array().nullable(),
2025-08-28 18:27:07 +02:00
2025-08-29 16:18:32 +02:00
// ALTRI DATI IMMOBILE
anno: z.string().nullable(),
cap: z.string().nullable(),
caratteristiche: z.custom<Caratteristiche>().nullable(),
categorie: z.string().array().nullable(),
civico: z.string().nullable(),
civico_secondario: z.string().nullable(),
classe: z.string().nullable(),
codice: z.string(),
comune: z.string().nullable(),
consegna: z.number().nullable(),
desc_en: z.string().nullable(),
desc_it: z.string().nullable(),
disponibile_da: z.date().nullable(),
2025-08-28 18:27:07 +02:00
email: z.email().optional(),
2025-08-29 16:18:32 +02:00
homepage: z.boolean().nullable(),
//BASE
id: zAnnuncioId,
2025-08-28 18:27:07 +02:00
// DATI IMMOBILE
indirizzo: z.string().nullable(),
indirizzo_secondario: z.string().nullable(),
2025-08-29 16:18:32 +02:00
lat: z.string().nullable(),
2025-08-28 18:27:07 +02:00
lat_secondario: z.string().nullable(),
2025-08-29 16:18:32 +02:00
// DATI LOCATORE
locatore: z.string().nullable(),
lon: z.string().nullable(),
lon_secondario: z.string().nullable(),
2025-08-28 18:27:07 +02:00
mq: z.string().nullable(),
2025-08-29 16:18:32 +02:00
numero: z.string().nullable(),
2025-08-28 18:27:07 +02:00
numero_bagni: z.number().nullable(),
numero_balconi: z.number().nullable(),
numero_box: z.number().nullable(),
2025-08-29 16:18:32 +02:00
numero_camere: z.number().nullable(),
2025-08-28 18:27:07 +02:00
numero_postiauto: z.number().nullable(),
2025-08-29 16:18:32 +02:00
numero_terrazzi: z.number().nullable(),
numero_vani: z.number().nullable(),
permanenza: z.number().array().nullable(),
persone: z.string().array().nullable(),
piano: z.string().nullable(),
piano_palazzo: z.number().nullable(),
prezzo: z.number(),
provincia: z.string().nullable(),
regione: z.string().nullable(),
stato: z.string().nullable(),
// DATI CONTRATTO
tipo: z.string().nullable(),
titolo_en: z.string().nullable(),
titolo_it: z.string().nullable(),
unita_condominio: z.number().nullable(),
web: z.boolean().nullable(),
2025-08-28 18:27:07 +02:00
// MEDIA
//url_immagini: z.string().array().nullable(),
//url_video: z.string().array().nullable(),
//og_url: z.string().nullable(),
//creato_il: z.date().nullable(),
//modificato_il: z.date().nullable(),
});
type FormValues = z.infer<typeof schema>;
const { locale } = useTranslation();
const utils = api.useUtils();
const { mutate: edit } = api.annunci.editAnnuncio.useMutation({
onSettled: async () => {
await utils.annunci.getAnnuncio.invalidate({ cod: data.codice });
await utils.annunci.getAnnuncioById_rawImgUrls.invalidate({
id: data.id,
});
2025-08-28 18:27:07 +02:00
await utils.annunci.getAnnunciList.invalidate();
toast.success("Annuncio modificato con successo");
},
});
const { mutateAsync: revalidate } =
api.settings.revalidateAnnuncio.useMutation({
onSettled: async () => {
await utils.annunci.getAnnuncio.invalidate({ cod: data.codice });
await utils.annunci.getAnnuncioById_rawImgUrls.invalidate({
id: data.id,
});
toast.success("Revalidazione completata");
},
});
const { mutateAsync: update } = api.settings.updateAnnuncio.useMutation({
onSettled: async () => {
await utils.annunci.getAnnuncio.invalidate({ cod: data.codice });
await utils.annunci.getAnnuncioById_rawImgUrls.invalidate({
id: data.id,
});
toast.success(
"L'aggiornamento dell'annuncio è stata richiesta con successo,\n potrebbero volerci alcuni minuti prima che le modifiche siano visibili.",
{
duration: 5000,
},
);
},
});
2025-08-28 18:27:07 +02:00
const defaultValues: FormValues = {
...data,
email: data.email || undefined,
};
z.config(z.locales[locale]());
const form = useZodForm(schema, {
defaultValues: defaultValues,
});
function onSubmit(fields: FormValues) {
edit({
annuncioId: data.id,
data: { ...fields, email: fields.email || null },
});
}
const personeOptions: ListOption[] = [
"single",
"coppia",
"colleghi",
"famiglia",
].map((p) => ({
label: p,
2025-08-29 16:18:32 +02:00
value: p,
2025-08-28 18:27:07 +02:00
}));
const permanenzaOptions: ListOption[] = [
2025-08-29 16:18:32 +02:00
{ label: "1 mese", value: "1" },
{ label: "3 mesi", value: "3" },
{ label: "6 mesi", value: "6" },
{ label: "9 mesi", value: "9" },
{ label: "12 mesi", value: "12" },
{ label: "18 mesi", value: "18" },
{ label: "24 mesi", value: "24" },
2025-08-28 18:27:07 +02:00
];
const tipoOptions: ListOption[] = [
"Transitorio",
"Stabile",
"Vendita",
"Cessione",
"Errore",
2025-08-29 16:18:32 +02:00
].map((o) => ({ label: o, value: o }));
2025-08-28 18:27:07 +02:00
const statoOptions = ["Attivo", "Sospeso", "Trattativa"].map((o) => ({
label: o,
2025-08-29 16:18:32 +02:00
value: o,
2025-08-28 18:27:07 +02:00
}));
const consegnaOptions: ListOption[] = [
2025-08-29 16:18:32 +02:00
{ label: "Gennaio", value: "1" },
{ label: "Febbraio", value: "2" },
{ label: "Marzo", value: "3" },
{ label: "Aprile", value: "4" },
{ label: "Maggio", value: "5" },
{ label: "Giugno", value: "6" },
{ label: "Luglio", value: "7" },
{ label: "Agosto", value: "8" },
{ label: "Settembre", value: "9" },
{ label: "Ottobre", value: "10" },
{ label: "Novembre", value: "11" },
{ label: "Dicembre", value: "12" },
2025-08-28 18:27:07 +02:00
];
return (
<>
<Form {...form}>
<form
className="mt-3 grid flex-1 items-start gap-4 p-2 sm:px-4 sm:py-0 md:gap-8"
2025-08-29 16:18:32 +02:00
onSubmit={form.handleSubmit(onSubmit)}
2025-08-28 18:27:07 +02:00
>
<div className="mx-auto grid w-full flex-1 auto-rows-max gap-4">
<div className="flex flex-col gap-2">
<div className="flex flex-wrap items-center gap-2">
<h1 className="whitespace-nowrap font-semibold text-2xl tracking-tight">
Annuncio {data.codice}
</h1>
<StatusBadge status={data.stato} />
<span>{data.media_updated_at?.toLocaleString("it-IT")}</span>
<div className="ml-auto flex items-center gap-2">
<Button className="bg-green-500" size="sm" type="submit">
Salva
</Button>
</div>
</div>
<div className="flex flex-wrap items-center gap-2">
<Link href={`/annuncio/${data.codice}`} target="_blank">
<Button size="sm" type="button" variant="outline">
<Eye /> pubblica
</Button>
</Link>
<Link
href={`/area-riservata/admin/scheda-annuncio-stampa/${data.id}`}
target="_blank"
>
<Button size="sm" type="button" variant="outline">
<Printer /> Scheda
</Button>
</Link>
<Popover>
<PopoverTrigger asChild>
<Button size="sm" type="button">
<CodeSquare /> Strumenti
</Button>
</PopoverTrigger>
<PopoverContent className="flex w-fit items-center gap-2">
<Button
className="bg-purple-500"
onClick={async () => {
await revalidate({
cod: data.codice,
});
}}
size="sm"
type="button"
>
<RefreshCcw /> Rigenera pagina
</Button>
<Confirm
description="Sei sicuro di voler richiedere l'aggiornamento dell'annuncio? Questa operazione potrebbe richiedere alcuni minuti prima che le modifiche siano visibili e resetterà eventuali modifiche effettuate dopo la sincronizzazione giornaliera."
onConfirm={async () => {
await update({ cod: data.codice });
}}
title="Aggiornamento Annuncio"
>
<Button size="sm" type="button" variant="warning">
<TriangleAlert /> Aggiorna
</Button>
</Confirm>
</PopoverContent>
</Popover>
2025-08-28 18:27:07 +02:00
</div>
</div>
<div className="grid h-max gap-3 md:grid-cols-[1fr_250px] lg:grid-cols-3">
<div className="h-full items-start gap-3 lg:col-span-2">
<Card className="h-full py-4">
<CardContent className="h-full w-full grow px-4">
<Tabs defaultValue="ita">
<TabsList className="grid h-full w-full grid-cols-2">
<TabsTrigger value="ita">Italiano</TabsTrigger>
<TabsTrigger value="eng">Inglese</TabsTrigger>
</TabsList>
<TabsContent value="ita">
<Card>
<CardHeader>
<CardTitle>Descrizione ITA</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-6">
<div className="grid gap-3">
<FormField
control={form.control}
name="titolo_it"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="titolo_it">
Titolo ITA
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="titolo_it"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="grid gap-3">
<FormField
control={form.control}
name="desc_it"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="desc_it">
Descrizione ITA
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Textarea
className="min-h-64"
2025-08-29 16:18:32 +02:00
placeholder=""
2025-08-28 18:27:07 +02:00
{...field}
id="desc_it"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="eng">
<Card>
<CardHeader>
<CardTitle>Descrizione ENG</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-6">
<div className="grid gap-3">
<FormField
control={form.control}
name="titolo_en"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="titolo_en">
Titolo ENG
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="titolo_en"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div className="grid gap-3">
<FormField
control={form.control}
name="desc_en"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="desc_en">
Descrizione ENG
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Textarea
className="min-h-64"
2025-08-29 16:18:32 +02:00
placeholder=""
2025-08-28 18:27:07 +02:00
{...field}
id="desc_en"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</div>
<div className="grid auto-rows-max items-start gap-3 lg:gap-8">
<Card>
<CardHeader>
<CardTitle>Status Annuncio</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<FormField
control={form.control}
name="stato"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-stato">Stato</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={
field.value
? statoOptions.filter((o) =>
field.value?.includes(o.value),
)
: undefined
}
2025-08-29 16:18:32 +02:00
elementId="select-stato"
elementName="stato"
isMulti={false}
2025-08-28 18:27:07 +02:00
onValueChange={async (value) => {
field.onChange(value.value);
}}
2025-08-29 16:18:32 +02:00
options={statoOptions}
2025-08-28 18:27:07 +02:00
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="web"
render={({ field }) => (
<FormItem className="mt-6 w-full px-4">
<div className="flex w-full items-center justify-between gap-x-4">
<FormLabel htmlFor="web">Web</FormLabel>
<FormControl>
<Switch
checked={field.value || false}
className="data-[state=checked]:bg-neutral-700"
2025-08-29 16:18:32 +02:00
id="web"
onCheckedChange={field.onChange}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="homepage"
render={({ field }) => (
<FormItem className="mt-6 w-full px-4">
<div className="flex items-center justify-between gap-x-4">
<FormLabel htmlFor="homepage">Homepage</FormLabel>
<FormControl>
<Switch
checked={field.value || false}
className="data-[state=checked]:bg-neutral-700"
2025-08-29 16:18:32 +02:00
id="homepage"
onCheckedChange={field.onChange}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
<FormMessage />
</div>
</FormItem>
)}
/>
</CardContent>
</Card>
<Card className="overflow-hidden">
<CardHeader>
<CardTitle>Imagini Annuncio</CardTitle>
</CardHeader>
<CardContent>
<CarouselAnnuncio
immagini={data.images}
single
updated_at={data.media_updated_at}
videos={data.videos}
/>
2025-08-28 18:27:07 +02:00
</CardContent>
</Card>
</div>
</div>
<Card>
<CardHeader>
<CardTitle>Dati di contatto</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<FormField
control={form.control}
name="locatore"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="locatore">
Locatore o incaricato
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="locatore"
onChange={(e) =>
field.onChange(e.target.value || null)
}
2025-08-29 16:18:32 +02:00
value={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<DualInputLayout
children1={
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="email">Email</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder="esempio@email.com"
{...field}
id="email"
onChange={(e) =>
field.onChange(
e.target.value && e.target.value !== ""
? e.target.value
: null,
)
}
2025-08-29 16:18:32 +02:00
value={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="numero"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero">Telefono</FormLabel>
<FormMessage />
</div>
<FormControl>
<PhoneInput
{...field}
id="numero"
onChange={(n) => field.onChange(n || null)}
2025-08-29 16:18:32 +02:00
value={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<p>
Indirizo: {data.indirizzo}, {data.civico} {data.comune} (
{data.provincia})
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Dati Immobile</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<DualInputLayout
children1={
<FormField
control={form.control}
name="indirizzo"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="indirizzo">Indirizzo</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="indirizzo"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="civico"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="civico">Civico</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="civico"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<DualInputLayout
children1={
<FormField
control={form.control}
name="comune"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="comune">Comune</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="comune"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="cap"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="cap">Cap</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="cap"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<DualInputLayout
children1={
<FormField
control={form.control}
name="provincia"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="provincia">Provincia</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="provincia"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="regione"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="regione">Regione</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="regione"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<DualInputLayout
children1={
<FormField
control={form.control}
name="lat"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="lat">Lat</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="lat"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="lon"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="lon">Lon</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="lon"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<p>
<Link
href={`https://www.google.com/maps/place/${form.getValues("lat")},${form.getValues("lon")}`}
target="_blank"
>
🗺 <span className="underline">Vedi su maps</span>
</Link>
</p>
<DualInputLayout
children1={
<FormField
control={form.control}
name="indirizzo_secondario"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="indirizzo_secondario">
Indirizzo Secondario
</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="indirizzo_secondario"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="civico_secondario"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="civico_secondario">
Civico Secondario
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="civico_secondario"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<DualInputLayout
children1={
<FormField
control={form.control}
name="lat_secondario"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="lat_secondario">
Lat Secondario
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="lat_secondario"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="lon_secondario"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="lon_secondario">
Lon Secondario
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="lon_secondario"
value={field.value || undefined}
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<p>
<Link
href={`https://www.google.com/maps/place/${form.getValues("lat_secondario")},${form.getValues("lon_secondario")}`}
target="_blank"
>
🗺 <span className="underline">Vedi su maps</span>
</Link>
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Dati Annuncio</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<DualInputLayout
children1={
<FormField
control={form.control}
name="tipo"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-tipo">
Tipo annuncio
</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={
field.value
? tipoOptions.filter((o) =>
field.value?.includes(o.value),
)
: undefined
}
2025-08-29 16:18:32 +02:00
elementId="select-tipo"
elementName="tipo"
isMulti={false}
2025-08-28 18:27:07 +02:00
onValueChange={async (value) => {
field.onChange(value.value);
}}
2025-08-29 16:18:32 +02:00
options={tipoOptions}
2025-08-28 18:27:07 +02:00
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="categorie"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="categorie">Categorie</FormLabel>
<FormMessage />
</div>
<p className="h-[42px]" id="categorie">
{field.value?.join(", ")}
</p>
</FormItem>
)}
/>
}
/>
<DualInputLayout
children1={
<FormField
control={form.control}
name="prezzo"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="prezzo">Prezzo</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<NumberInput
suffix=" €"
2025-08-28 18:27:07 +02:00
{...field}
decimalScale={2}
decimalSeparator=","
fixedDecimalScale
2025-08-28 18:27:07 +02:00
id="prezzo"
min={0}
onValueChange={(v) => {
field.onChange((v || 0) * 100);
}}
value={Number(field.value) / 100}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="disponibile_da"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="disponibile_da">
Disponibile Da
</FormLabel>
<FormMessage />
</div>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
className={cn(
"h-[42px] w-full pl-3 text-left font-medium",
!field.value && "text-muted-foreground",
"rounded-lg border border-neutral-300 shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm disabled:cursor-not-allowed disabled:opacity-50 dark:focus:border-transparent",
2025-08-28 18:27:07 +02:00
)}
2025-08-29 16:18:32 +02:00
id="disponibile_da"
variant={"outline"}
2025-08-28 18:27:07 +02:00
>
{field.value ? (
format(field.value, "PPP", {
locale: it,
})
) : (
<span>Scegli</span>
)}
<CalendarIcon className="ml-auto size-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent
align="start"
2025-08-29 16:18:32 +02:00
className="w-auto p-0"
2025-08-28 18:27:07 +02:00
>
<Calendar
2025-08-29 16:18:32 +02:00
autoFocus
captionLayout="dropdown"
defaultMonth={field.value || undefined}
endMonth={new Date(2050, 12)}
2025-08-28 18:27:07 +02:00
locale={it}
mode="single"
onSelect={field.onChange}
2025-08-29 16:18:32 +02:00
selected={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</PopoverContent>
</Popover>
</FormItem>
)}
/>
}
/>
<DualInputLayout
children1={
<FormField
control={form.control}
name="consegna"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-consegna">
Consegna
</FormLabel>
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={
field.value
? consegnaOptions.filter((o) =>
field.value?.toString().includes(o.value),
)
: undefined
}
2025-08-29 16:18:32 +02:00
elementId="select-consegna"
elementName="consegna"
isMulti={false}
2025-08-28 18:27:07 +02:00
onValueChange={async (value) => {
field.onChange(parseInt(value.value));
}}
2025-08-29 16:18:32 +02:00
options={consegnaOptions}
2025-08-28 18:27:07 +02:00
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/>
}
children2={
<FormField
control={form.control}
name="persone"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-pers">Persone</FormLabel>
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={
field.value
? personeOptions.filter((o) =>
field.value?.includes(o.value),
)
: undefined
}
2025-08-29 16:18:32 +02:00
elementId="select-pers"
elementName="persone"
isMulti={true}
2025-08-28 18:27:07 +02:00
onValueChange={async (value) => {
field.onChange(value.map((v) => v.value));
}}
2025-08-29 16:18:32 +02:00
options={personeOptions}
2025-08-28 18:27:07 +02:00
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/>
}
/>
<FormField
control={form.control}
name="permanenza"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-perm">Permanenza</FormLabel>
<FormMessage />
</div>
<FormControl>
<MultiSelect
defaultValue={
field.value
? permanenzaOptions.filter((o) =>
field.value?.includes(parseInt(o.value)),
)
: undefined
}
2025-08-29 16:18:32 +02:00
elementId="select-perm"
elementName="permanenza"
isMulti={true}
2025-08-28 18:27:07 +02:00
onValueChange={async (value) => {
field.onChange(value.map((v) => parseInt(v.value)));
}}
2025-08-29 16:18:32 +02:00
options={permanenzaOptions}
2025-08-28 18:27:07 +02:00
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Altri Dati Immobile</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex w-full flex-col flex-wrap items-start justify-between gap-4 sm:flex-row">
<FormField
control={form.control}
name="anno"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="anno">Anno costruzione</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="anno"
onChange={(e) =>
field.onChange(
e.target.value !== "" ? e.target.value : null,
)
}
2025-08-29 16:18:32 +02:00
value={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="classe"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="classe">Classe</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="classe"
onChange={(e) =>
field.onChange(
e.target.value !== "" ? e.target.value : null,
)
}
2025-08-29 16:18:32 +02:00
value={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="mq"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="mq">Metri Quadri</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="mq"
onChange={(e) =>
field.onChange(
e.target.value !== "" ? e.target.value : null,
)
}
2025-08-29 16:18:32 +02:00
value={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="piano"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="piano">Piano</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
{...field}
id="piano"
onChange={(e) =>
field.onChange(
e.target.value !== "" ? e.target.value : null,
)
}
2025-08-29 16:18:32 +02:00
value={field.value || undefined}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="piano_palazzo"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="piano_palazzo">
Piani Palazzo
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("piano_palazzo", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="piano_palazzo"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="unita_condominio"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="unita_condominio">
Unità Condominio
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("unita_condominio", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="unita_condominio"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="numero_vani"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero_vani">
Numero Vani
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("numero_vani", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="numero_vani"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="numero_camere"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero_camere">
Numero Camere
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("numero_camere", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="numero_camere"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="numero_bagni"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero_bagni">
Numero Bagni
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("numero_bagni", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="numero_bagni"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="numero_balconi"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero_balconi">
Numero Balconi
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("numero_balconi", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="numero_balconi"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="numero_terrazzi"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero_terrazzi">
Numero Terrazzi
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("numero_terrazzi", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="numero_terrazzi"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="numero_box"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero_box">
Numero Box Auto
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("numero_box", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="numero_box"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="numero_postiauto"
render={({ field }) => (
<FormItem>
<div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="numero_postiauto">
Numero Posti Auto
</FormLabel>
{field.value !== null && field.value !== 0 && (
<Button
2025-10-10 16:18:43 +02:00
className="-top-2 absolute right-0 flex items-center gap-1"
2025-08-28 18:27:07 +02:00
onClick={() => {
form.setValue("numero_postiauto", null);
}}
2025-08-29 16:18:32 +02:00
size="sm"
type="button"
variant="link"
2025-08-28 18:27:07 +02:00
>
<RotateCw className="size-4" />
Reset
</Button>
)}
<FormMessage />
</div>
<FormControl>
<Input
placeholder=""
type="number"
{...field}
id="numero_postiauto"
onChange={(e) =>
field.onChange(
e.target.value === "0" || e.target.value === ""
? null
: parseInt(e.target.value),
)
}
2025-08-29 16:18:32 +02:00
value={field.value || 0}
2025-08-28 18:27:07 +02:00
/>
</FormControl>
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="accessori"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="accessori">Accessori</FormLabel>
<FormMessage />
</div>
<FormControl>
<p className="h-[42px]" id="accessori">
{field.value?.join(", ")}
</p>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="caratteristiche"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="caratteristiche">
Caratteristiche
</FormLabel>
<FormMessage />
</div>
<FormControl>
<p className="h-[42px]" id="caratteristiche">
{field.value
? filteredCaratteristiche(field.value)
.map((v) => v.text)
.join(", ")
: ""}
</p>
</FormControl>
</FormItem>
)}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Media</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row"></div>
</CardContent>
</Card>
</div>
</form>
</Form>
</>
);
};