refactor: Remove AnnunciGrid component and update Annunci page to use CardAnnuncio directly

This commit is contained in:
Marco Pedone 2025-11-04 19:18:13 +01:00
parent 608e6a8e10
commit 7b0469467f
12 changed files with 290 additions and 263 deletions

View file

@ -1,14 +0,0 @@
import { CardAnnuncio } from "~/components/annuncio_card";
import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller";
//TODO usare anche in accordion e dialog pag ricerca servizio
export const AnnunciGrid = ({ pagedata }: { pagedata: AnnuncioRicerca[] }) => {
return (
<div className="relative z-0 mx-auto grid max-w-7xl grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3">
{pagedata.map((annuncio) => (
<CardAnnuncio key={annuncio.codice} {...annuncio} />
))}
</div>
);
};

View file

@ -7,6 +7,7 @@ import LoadingButton from "~/components/custom_ui/loading-button";
import { Button, buttonVariants } from "~/components/ui/button";
import { cn } from "~/lib/utils";
import { useAnnuncio } from "~/providers/AnnuncioProvider";
import { useServizio } from "~/providers/ServizioProvider";
import type { SessionContextType } from "~/providers/SessionProvider";
import type { AnnunciId } from "~/schemas/public/Annunci";
import type { UsersId } from "~/schemas/public/Users";
@ -108,10 +109,14 @@ const ServizioInteraction = ({
);
}
return <InteressatoButton annuncioId={annuncioId} />;
return <InteressatoButtonUserOnly annuncioId={annuncioId} />;
};
const InteressatoButton = ({ annuncioId }: { annuncioId: AnnunciId }) => {
const InteressatoButtonUserOnly = ({
annuncioId,
}: {
annuncioId: AnnunciId;
}) => {
const { data: hasInterest, isLoading } =
api.intrests.hasUserInterest.useQuery({
annuncioId,
@ -161,36 +166,57 @@ const InteressatoButton = ({ annuncioId }: { annuncioId: AnnunciId }) => {
);
};
export const InteressatoButtonBatchV = ({
export const InteressatoButtonServizio = ({
annuncioId,
hasInterest,
}: {
annuncioId: AnnunciId;
hasInterest: boolean;
}) => {
const { isAdmin, servizioId, userId } = useServizio();
const utils = api.useUtils();
const { mutate: add } = api.intrests.addIntrest.useMutation({
const { mutate: adminAdd, isPending: isAdminPending } =
api.servizio.addServizioAnnunci.useMutation({
onError: (error) => {
console.error(error);
toast.error("Errore durante l'aggiunta dell'annuncio");
},
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getAnnunciAvailableToAdd.invalidate({
servizioId,
});
await utils.servizio.AnnuncioServizioTipologiaMatch.invalidate({
userId,
});
await utils.servizio.getCompatibileAnnunci.invalidate({
servizioId,
});
toast.success("Annuncio aggiunto alla ricerca");
},
});
const { mutate: add, isPending } = api.intrests.addIntrest.useMutation({
onSuccess: async () => {
await utils.intrests.getUserInterests.invalidate();
toast.success("Richiesta di interesse inviata con successo!");
},
});
if (hasInterest) {
return (
<span className="flex w-full items-center justify-center gap-2 rounded-md bg-green-500 p-2 font-semibold text-sm text-white">
<BadgePlus className="inline size-5" />
Hai già mandato una richiesta
</span>
);
}
return (
<Button
<LoadingButton
aria-label="Sono interessato"
className="w-full"
//size="xl"
loading={isPending || isAdminPending}
onClick={() => {
if (isAdmin && servizioId) {
adminAdd({
servizioId,
annunci: [annuncioId],
});
return;
}
add({
annuncioId,
});
@ -198,7 +224,7 @@ export const InteressatoButtonBatchV = ({
variant="info"
>
<BadgePlus className="size-6" />
Sono interessato
</Button>
{isAdmin ? "Aggiungi annuncio" : "Sono interessato"}
</LoadingButton>
);
};

View file

@ -35,6 +35,8 @@ import { VideoPlayer } from "./videoPlayer";
type CardAnnuncioProps = AnnuncioRicerca & {
className?: string;
noLink?: boolean;
onlyFirstImage?: boolean;
};
export const CardAnnuncio = ({
@ -55,23 +57,36 @@ export const CardAnnuncio = ({
updated_at,
images,
homepage,
noLink = false,
onlyFirstImage = false,
}: CardAnnuncioProps) => {
const { t, locale } = useTranslation();
const currentMonth = new Date().getMonth() + 1;
const isAvailableNow = consegna === 0 || consegna === currentMonth;
const Wrapper = ({ children }: { children: React.ReactNode }) => {
if (noLink) {
return <div className="flex h-full flex-col gap-2 p-2">{children}</div>;
}
return (
<Link
className="flex h-full flex-col gap-2 p-2"
href={`/annuncio/${codice}`}
>
{children}
</Link>
);
};
return (
<div
className={cn(
"h-[31.1rem] rounded-md bg-white shadow-neutral-100 shadow-sm hover:shadow-md hover:outline-neutral-600",
"relative h-[31.1rem] rounded-md bg-white shadow-neutral-100 shadow-sm outline outline-neutral-300 hover:shadow-md hover:outline-neutral-600",
className,
)}
id={`card-annuncio-${id}`}
>
<Link
className="flex h-full flex-col gap-2 p-2"
href={`/annuncio/${codice}`} //duration-700 ease-in-out animate-in fade-in
>
<Wrapper>
<div
className={cn("group relative overflow-clip text-clip rounded-md")}
>
@ -120,69 +135,99 @@ export const CardAnnuncio = ({
</Badge>
</div>
)}
<Carousel opts={{ loop: true }}>
<CarouselContent>
{images?.map((img, idx) => (
<CarouselItem key={`${img.img}key`}>
<ImageFlbk
alt={t.card.alt_immagine}
blurDataURL={`/storage-api/get/${img.thumb}?image=true`}
className={"h-64 w-full object-cover"}
height={1080}
priority={idx === 0}
src={`/storage-api/get/${img.img}?image=true`}
width={1920}
/>
</CarouselItem>
))}
{videos?.map((video) => {
if (!video) return null;
if (video.includes("youtu")) {
return null;
}
return (
<CarouselItem
className={cn("group relative")}
key={`videoplayer-${video}`}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<VideoPlayer
cacheKey={updated_at?.toString() || new Date().toString()}
className="h-56"
coverImage={
images?.[0]
? `/storage-api/get/${images[0].img}?image=true`
: ""
}
key={`videoplayer-${video}`}
videoSrc={video}
{!onlyFirstImage ? (
<Carousel opts={{ loop: true }}>
<CarouselContent>
{images?.map((img, idx) => (
<CarouselItem key={`${img.img}key`}>
<ImageFlbk
alt={t.card.alt_immagine}
blurDataURL={`/storage-api/get/${img.thumb}?image=true`}
className={"h-64 w-full object-cover"}
height={1080}
priority={idx === 0}
src={`/storage-api/get/${img.img}?image=true`}
width={1920}
/>
</CarouselItem>
);
})}
</CarouselContent>
))}
{videos?.map((video) => {
if (!video) return null;
if (video.includes("youtu")) {
return null;
}
return (
<CarouselItem
className={cn("group relative")}
key={`videoplayer-${video}`}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<VideoPlayer
cacheKey={
updated_at?.toString() || new Date().toString()
}
className="h-56"
coverImage={
images?.[0]
? `/storage-api/get/${images[0].img}?image=true`
: ""
}
key={`videoplayer-${video}`}
videoSrc={video}
/>
</CarouselItem>
);
})}
</CarouselContent>
<div
className="block opacity-0 transition-all duration-200 group-hover:opacity-60 disabled:group-hover:opacity-0"
onClick={(e) => e.preventDefault()}
onKeyDown={(e) => e.stopPropagation()}
role="button"
tabIndex={0}
>
<CarouselPrevious
className="left-2 cursor-pointer"
disabled={!images || images.length === 1}
/>
<CarouselNext
className="right-2 cursor-pointer"
disabled={!images || images.length === 1}
/>
</div>
</Carousel>
<div
className="block opacity-0 transition-all duration-200 group-hover:opacity-60 disabled:group-hover:opacity-0"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onKeyDown={(e) => e.stopPropagation()}
role="button"
tabIndex={0}
>
<CarouselPrevious
className="left-2 cursor-pointer"
disabled={!images || images.length === 1}
onClick={(e) => {
e.preventDefault();
e.stopPropagation(); // Add this to the buttons too
}}
/>
<CarouselNext
className="right-2 cursor-pointer"
disabled={!images || images.length === 1}
onClick={(e) => {
e.preventDefault();
e.stopPropagation(); // Add this to the buttons too
}}
/>
</div>
</Carousel>
) : (
<>
{images && images.length > 0 && images[0] ? (
<ImageFlbk
alt={t.card.alt_immagine}
blurDataURL={`/storage-api/get/${images[0].thumb}?image=true`}
className={"h-64 w-full object-cover"}
height={1080}
priority={true}
src={`/storage-api/get/${images[0].img}?image=true`}
width={1920}
/>
) : (
<div className="flex h-64 w-full items-center justify-center bg-neutral-200 text-neutral-500"></div>
)}
</>
)}
</div>
<div className="flex h-full grow flex-col gap-2">
<div
@ -270,7 +315,7 @@ export const CardAnnuncio = ({
</div>
</div>
</div>
</Link>
</Wrapper>
</div>
);
};

View file

@ -15,17 +15,14 @@ const LoadingButton = ({
}: ButtonProps & { loading?: boolean }) => {
return (
<Button
className={cn(buttonVariants({ className, size, variant }))}
className={cn(
buttonVariants({ className, size, variant }),
loading && "text-transparent",
)}
disabled={loading || props.disabled}
{...props}
>
{loading && (
<LoaderCircle
aria-hidden="true"
className="-ms-1 me-2 animate-spin"
size={16}
strokeWidth={2}
/>
)}
{loading && <LoaderCircle className="absolute animate-spin text-muted" />}
{props.children}
</Button>
);

View file

@ -6,21 +6,16 @@ import {
ExternalLink,
FolderKanban,
Pen,
Plus,
ShieldUser,
Trash2,
Wrench,
} from "lucide-react";
import Link from "next/link";
import { useRef, useState } from "react";
import { useState } from "react";
import toast from "react-hot-toast";
import { z } from "zod/v4";
import { ExtIcon } from "~/components/area-riservata/allegati";
import { Confirm } from "~/components/confirm";
import {
AnimatedButton,
type AnimatedButtonRef,
} from "~/components/custom_ui/animated-button";
import {
Credenza,
CredenzaBody,
@ -62,7 +57,6 @@ import { UploadModal } from "~/components/upload_modal";
import { cn, formatCurrency } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider";
import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider";
import type { AnnunciId } from "~/schemas/public/Annunci";
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import type { FileMetadataWithAdmin } from "~/server/services/storage.service";
@ -117,7 +111,7 @@ export const AnnuncioActions = () => {
</PopoverTrigger>
<PopoverContent
align="end"
className="flex w-fit flex-col items-center gap-2"
className="flex w-fit flex-col items-center gap-4"
>
<Link
href={`/area-riservata/admin/edit-annuncio/${annuncioId}`}
@ -1213,51 +1207,3 @@ const SendConferma = ({
</Card>
);
};
export const AddButton = ({ annuncioId }: { annuncioId: AnnunciId }) => {
const { servizioId, userId } = useServizio();
const utils = api.useUtils();
const { mutate } = api.servizio.addServizioAnnunci.useMutation({
onError: (error) => {
console.error(error);
toast.error("Errore durante l'aggiunta dell'annuncio");
},
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getAnnunciAvailableToAdd.invalidate({
servizioId,
});
await utils.servizio.AnnuncioServizioTipologiaMatch.invalidate({
userId,
});
await utils.servizio.getCompatibileAnnunci.invalidate({
servizioId,
});
toast.success("Annuncio aggiunto alla ricerca");
},
});
const buttonRef = useRef<AnimatedButtonRef>(null);
return (
<AnimatedButton
className="group flex w-full gap-2"
onClick={async () => {
buttonRef.current?.sparkle();
//wait for the animation to finish
await new Promise((resolve) => setTimeout(resolve, 1000));
mutate({ annunci: [annuncioId], servizioId });
}}
ref={buttonRef}
sparklesClassName="fill-yellow-500 stroke-transparent"
sparklesIcon="thumbs-up"
sparklesNumber={15}
sparklesRadius={60}
sparklesScale={[1, 1.5]}
variant="info"
>
<Plus className="size-5" />
<span>Aggiungi agli annunci da contattare</span>
</AnimatedButton>
);
};

View file

@ -1,5 +1,5 @@
import { add } from "date-fns";
import { Package, PackageCheck, UserCircle } from "lucide-react";
import { CalendarClock, Package, PackageCheck, UserCircle } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect } from "react";
@ -209,7 +209,7 @@ export const ServizioContent = () => {
<ServizioCard
content={
<>
<div className="flex w-full flex-col gap-4 sm:flex-row sm:gap-8">
<div className="flex w-full flex-col gap-8 sm:flex-row sm:items-start">
<div className="flex flex-2 flex-col gap-2 sm:max-w-1/2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
@ -225,10 +225,15 @@ export const ServizioContent = () => {
value={openContattiAnnunci.length * 10}
/>
</div>
<span>
{`Durata servizio: ${new Date(servizio.decorrenza).toLocaleDateString("it-IT")} - ${new Date(add(servizio.decorrenza, { days: 60 })).toLocaleDateString("it-IT")}`}
</span>
{/* <ServizioDuration decorrenza={servizio.decorrenza} /> */}
<div className="flex flex-col flex-wrap gap-2 sm:flex-row sm:items-center">
<div className="flex items-center gap-2">
<CalendarClock className="size-6 text-primary" />
<span className="font-medium">Durata servizio:</span>
</div>
<span>
{`${new Date(servizio.decorrenza).toLocaleDateString("it-IT")} - ${new Date(add(servizio.decorrenza, { days: 60 })).toLocaleDateString("it-IT")}`}
</span>
</div>
</div>
{annuncioConfermato.length > 0 && (
<div className="flex flex-col gap-2">
@ -245,17 +250,17 @@ export const ServizioContent = () => {
>
<h3>
{a.codice} - confermato il:{" "}
{/** biome-ignore lint/style/noNonNullAssertion: <already checked above> */}
{new Date(a.accettato_conferma_at!).toLocaleDateString(
"it",
{
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
month: "2-digit",
year: "numeric",
},
)}
{a.accettato_conferma_at &&
new Date(a.accettato_conferma_at).toLocaleDateString(
"it",
{
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
month: "2-digit",
year: "numeric",
},
)}
</h3>
<SaldoButton
annuncioId={a.annunci_id}

View file

@ -299,7 +299,7 @@ const EditPreferenze = () => {
Preferenze
</CredenzaDescription>
</CredenzaHeader>
<CredenzaBody className="max-h-[80vh] w-full max-w-3xl overflow-y-auto pb-5">
<CredenzaBody className="max-h-[80vh] w-full max-w-3xl overflow-y-auto pb-5 sm:max-w-5xl">
{isLoading ? (
<LoadingPage />
) : (

View file

@ -2,8 +2,7 @@ import { ExternalLink, Trash2 } from "lucide-react";
import Link from "next/link";
import type { ReactNode } from "react";
import toast from "react-hot-toast";
import { InteressatoButtonBatchV } from "~/components/annuncio-interactions/annuncio_interactions";
import { AddButton } from "~/components/servizio/annuncio_actions";
import { InteressatoButtonServizio } from "~/components/annuncio-interactions/annuncio_interactions";
import {
AnnuncioCard,
BasicAnnuncioCard,
@ -22,6 +21,7 @@ import {
import type { UsersId } from "~/schemas/public/Users";
import type { ServizioData } from "~/server/controllers/servizio.controller";
import { api } from "~/utils/api";
import { CardAnnuncio } from "../annuncio_card";
import { LoadingPage } from "../loading";
import {
Dialog,
@ -44,7 +44,7 @@ const DialogServizio = ({
return (
<Dialog>
<DialogTrigger asChild>{trigger}</DialogTrigger>
<DialogContent className="md:max-w-5xl">
<DialogContent className="p-2 sm:max-w-5xl sm:p-6 md:max-w-7xl">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
<DialogDescription className="sr-only">
@ -221,25 +221,21 @@ export const AnnunciSelezionatiAccordion = ({
);
};
export const AnnunciCompatibili = () => {
const { servizioId, userId, isAdmin } = useServizio();
const { servizioId } = useServizio();
const { data, isLoading } = api.servizio.getCompatibileAnnunci.useQuery({
servizioId,
});
const { data: userIntrests } = api.intrests.getUserInterests.useQuery({
userId,
});
return (
<DialogServizio
title="Annunci compatibili"
title="Annunci compatibili con le preferenze del servizio"
trigger={
<Button className="w-full bg-purple-500 sm:w-fit">
Esplora Annunci Compatibili
</Button>
}
>
<div className="flex max-h-[80vh] flex-col gap-2 overflow-y-auto">
<div className="flex max-h-[80vh] flex-col gap-2 overflow-y-auto p-0.5 sm:p-2">
{isLoading ? (
<LoadingPage />
) : (
@ -252,25 +248,23 @@ export const AnnunciCompatibili = () => {
</span>
</div>
) : (
data.map((a) => (
<BasicAnnuncioCard
className="border-2 border-neutral-500"
data={a}
interactions={
<>
{isAdmin ? (
<AddButton annuncioId={a.id} />
) : (
<InteressatoButtonBatchV
annuncioId={a.id}
hasInterest={userIntrests?.includes(a.id) || false}
/>
)}
</>
}
key={a.id}
/>
))
<div className="relative z-0 mx-auto grid max-w-7xl grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 sm:gap-y-8 lg:grid-cols-3">
{data.map((annuncio) => (
<div
className="flex flex-col justify-center gap-1"
key={annuncio.codice}
>
<CardAnnuncio
{...annuncio}
className="outline outline-neutral-500"
noLink
onlyFirstImage
/>
<InteressatoButtonServizio annuncioId={annuncio.id} />
</div>
))}
</div>
)}
</>
)}

View file

@ -471,42 +471,56 @@ export const FormNewServizio = ({
</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 bg-white text-primary shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm hover:border-neutral-400 focus:border-neutral-400 disabled:cursor-not-allowed disabled:opacity-50 aria-expanded:border-neutral-400 dark:border-neutral-500 dark:bg-primary dark:text-white dark:aria-expanded:border-neutral-400 dark:focus:border-transparent dark:hover:border-neutral-400 dark:placeholder:text-neutral-400`,
)}
id="scadenza_motivazione_transitoria"
variant={"outline"}
>
{field.value ? (
format(field.value, "PPP", {
locale: it,
})
) : (
<span>{t.anagrafica.scegli_data}</span>
)}
<CalendarIcon className="ml-auto size-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent align="start" className="w-auto p-0">
<Calendar
autoFocus
captionLayout="dropdown"
defaultMonth={field.value || new Date()}
endMonth={new Date(2050, 12)}
locale={it}
mode="single"
onSelect={field.onChange}
selected={field.value || undefined}
/>
</PopoverContent>
</Popover>
{isLimited ? (
<div className="space-y-0">
<p className="font-semibold text-lg">
{field.value
? format(field.value, "dd/MM/yyyy")
: "Errore data invalida"}
</p>
<p className="text-muted-foreground text-sm">
La scadenza non può essere modificata. Contatta il
supporto se necessario.
</p>
</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 bg-white text-primary shadow-xs outline-hidden file:border-0 file:bg-transparent file:font-medium file:text-sm hover:border-neutral-400 focus:border-neutral-400 disabled:cursor-not-allowed disabled:opacity-50 aria-expanded:border-neutral-400 dark:border-neutral-500 dark:bg-primary dark:text-white dark:aria-expanded:border-neutral-400 dark:focus:border-transparent dark:hover:border-neutral-400 dark:placeholder:text-neutral-400`,
)}
id="scadenza_motivazione_transitoria"
variant={"outline"}
>
{field.value ? (
format(field.value, "PPP", {
locale: it,
})
) : (
<span>{t.anagrafica.scegli_data}</span>
)}
<CalendarIcon className="ml-auto size-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent align="start" className="w-auto p-0">
<Calendar
autoFocus
captionLayout="dropdown"
defaultMonth={field.value || new Date()}
endMonth={new Date(2050, 12)}
locale={it}
mode="single"
onSelect={field.onChange}
selected={field.value || undefined}
/>
</PopoverContent>
</Popover>
)}
</FormItem>
)}
/>

View file

@ -15,9 +15,9 @@ import type { GetServerSideProps } from "next";
import Head from "next/head";
import { useEffect, useMemo, useState } from "react";
import { AccordionComp } from "~/components/accordionComp";
import { AnnunciGrid } from "~/components/annunci_grid";
import { MapSection } from "~/components/annunci_map";
import { CTA_TipologiaModal } from "~/components/annunci_tutorial";
import { CardAnnuncio } from "~/components/annuncio_card";
import { CodiceBox } from "~/components/codiceRicerca";
import { Layout } from "~/components/Layout";
import { LoadingPage } from "~/components/loading";
@ -367,7 +367,11 @@ const AnnunciList = () => {
{status === "pending" ? (
<LoadingPage />
) : (
<AnnunciGrid pagedata={data.annunci} />
<div className="relative z-0 mx-auto grid max-w-7xl grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3">
{data.annunci.map((annuncio) => (
<CardAnnuncio key={annuncio.codice} {...annuncio} />
))}
</div>
)}
<div className="flex items-center justify-center gap-2">

View file

@ -152,7 +152,7 @@ const NewServizioModal = ({
Nuovo Servizio
</Button>
</DialogTrigger>
<DialogContent className="max-h-[80%] overflow-auto p-4 sm:max-w-5xl">
<DialogContent className="max-h-[80%] w-full overflow-auto p-4 sm:max-w-5xl">
<DialogHeader>
<DialogTitle>Creazione Servizio</DialogTitle>
<DialogDescription className="sr-only">desc</DialogDescription>

View file

@ -41,6 +41,7 @@ import {
import { db } from "~/server/db";
import { NewMail, type NewMailProps } from "~/server/services/mailer";
import { getUser } from "~/server/services/user.service";
import type { AnnuncioRicerca } from "./annunci.controller";
export const addServizio = async (data: NewServizio) => {
try {
@ -1514,7 +1515,9 @@ export const SendContactEmail = async ({
}
};
export const getCompatibileAnnunci = async (servizioId: ServizioServizioId) => {
export const getCompatibileAnnunci = async (
servizioId: ServizioServizioId,
): Promise<AnnuncioRicerca[]> => {
try {
const servizio = await getServizioById(servizioId);
if (!servizio) {
@ -1527,18 +1530,25 @@ export const getCompatibileAnnunci = async (servizioId: ServizioServizioId) => {
let qry = db
.selectFrom("annunci")
.select((eb) => [
"annunci.id",
"annunci.codice",
"annunci.prezzo",
"annunci.desc_en",
"annunci.desc_it",
"annunci.titolo_en",
"annunci.titolo_it",
"annunci.tipo",
"annunci.consegna",
"annunci.stato",
"annunci.web",
"annunci.updated_at",
"id",
"codice",
"comune",
"provincia",
"mq",
"prezzo",
"desc_en",
"desc_it",
"titolo_en",
"titolo_it",
"tipo",
"consegna",
"stato",
"web",
"updated_at",
"homepage",
"numero_camere",
"url_video",
"modificato_il",
jsonArrayFrom(
eb
.selectFrom("images_refs")