2025-08-04 17:45:44 +02:00
|
|
|
import { ExternalLink, UnfoldVertical } from "lucide-react";
|
|
|
|
|
import Link from "next/link";
|
|
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Credenza,
|
|
|
|
|
CredenzaBody,
|
|
|
|
|
CredenzaClose,
|
|
|
|
|
CredenzaContent,
|
|
|
|
|
CredenzaDescription,
|
|
|
|
|
CredenzaFooter,
|
|
|
|
|
CredenzaHeader,
|
|
|
|
|
CredenzaTitle,
|
|
|
|
|
CredenzaTrigger,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "~/components/custom_ui/credenza";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { ImageFlbk } from "~/components/ImageWithFallback";
|
|
|
|
|
import { AnnuncioActions } from "~/components/servizio/annuncio_actions";
|
|
|
|
|
import { Interactions } from "~/components/servizio/interactions";
|
|
|
|
|
import { Button } from "~/components/ui/button";
|
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
2025-08-04 17:45:44 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Carousel,
|
|
|
|
|
CarouselContent,
|
|
|
|
|
CarouselItem,
|
|
|
|
|
CarouselNext,
|
|
|
|
|
CarouselPrevious,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "~/components/ui/carousel";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { handleConsegna } from "~/lib/annuncio_details";
|
2025-08-29 18:18:37 +02:00
|
|
|
import { replaceWithBr } from "~/lib/newlineToBr";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { cn, formatCurrency } from "~/lib/utils";
|
|
|
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
|
|
|
import { useServizioAnnuncio } from "~/providers/ServizioProvider";
|
|
|
|
|
import type { Annunci } from "~/schemas/public/Annunci";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
export const AnnuncioCard = ({ className }: { className?: string }) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { data } = useServizioAnnuncio();
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<BasicAnnuncioCard
|
|
|
|
|
actions={<AnnuncioActions />}
|
2025-08-29 16:18:32 +02:00
|
|
|
className={className}
|
|
|
|
|
data={data}
|
2025-08-28 18:27:07 +02:00
|
|
|
interactions={<Interactions />}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
export const BasicAnnuncioCard = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
actions,
|
|
|
|
|
interactions,
|
|
|
|
|
className,
|
|
|
|
|
data,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
actions?: React.ReactNode;
|
|
|
|
|
interactions?: React.ReactNode;
|
|
|
|
|
className?: string;
|
|
|
|
|
data: Pick<
|
|
|
|
|
Annunci,
|
|
|
|
|
| "id"
|
|
|
|
|
| "codice"
|
|
|
|
|
| "prezzo"
|
|
|
|
|
| "desc_en"
|
|
|
|
|
| "desc_it"
|
|
|
|
|
| "url_immagini"
|
|
|
|
|
| "titolo_en"
|
|
|
|
|
| "titolo_it"
|
|
|
|
|
| "tipo"
|
|
|
|
|
| "consegna"
|
|
|
|
|
| "stato"
|
|
|
|
|
| "web"
|
|
|
|
|
>;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { t } = useTranslation();
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<Card className={cn("w-full border-white py-5", className)}>
|
|
|
|
|
<CardHeader className="flex flex-row flex-wrap items-center justify-between space-y-0 pb-0">
|
|
|
|
|
<CardTitle className="text-2xl">Codice: {data.codice}</CardTitle>
|
|
|
|
|
{actions}
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="px-4">
|
|
|
|
|
<div className="flex w-full flex-col gap-4 md:flex-row md:gap-10">
|
|
|
|
|
<ImageFlbk
|
2025-08-29 16:18:32 +02:00
|
|
|
alt={data.codice}
|
|
|
|
|
className="h-auto w-full rounded-md object-contain sm:max-w-xs"
|
|
|
|
|
height={1080}
|
|
|
|
|
priority
|
2025-08-28 18:27:07 +02:00
|
|
|
src={
|
|
|
|
|
(data.url_immagini &&
|
|
|
|
|
`/go-api/images/get/${data.url_immagini[0]}`) ||
|
|
|
|
|
"/fallback-image.png"
|
|
|
|
|
}
|
|
|
|
|
width={1920}
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex w-full flex-col gap-5">
|
|
|
|
|
{interactions}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
|
|
|
<TipoBadge tipo={data.tipo} />
|
|
|
|
|
<span>
|
|
|
|
|
{handleConsegna({
|
2025-08-29 16:18:32 +02:00
|
|
|
aggiornamento: t.card.in_aggiornamento,
|
2025-08-28 18:27:07 +02:00
|
|
|
consegna: data.consegna,
|
|
|
|
|
consegna_da: t.card.consegna_da,
|
|
|
|
|
mesi: t.preferenze.mesi,
|
|
|
|
|
subito: t.card.consegna_subito,
|
|
|
|
|
})}
|
|
|
|
|
</span>
|
|
|
|
|
<span>{formatCurrency(data.prezzo / 1e2)}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex w-full flex-wrap items-center gap-5">
|
|
|
|
|
<span className="text-wrap">{data.titolo_it || data.codice}</span>
|
|
|
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
|
|
|
<AnnuncioDettaglio data={data} />
|
|
|
|
|
{data.stato !== "Sospeso" && data.web && (
|
|
|
|
|
<Link
|
|
|
|
|
aria-label="Pagina Annuncio"
|
2025-08-29 16:18:32 +02:00
|
|
|
href={`/annuncio/${data.codice}`}
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<Button
|
|
|
|
|
className="flex items-center gap-2"
|
2025-08-29 16:18:32 +02:00
|
|
|
variant="outline"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<ExternalLink className="size-6" />
|
|
|
|
|
</Button>
|
|
|
|
|
</Link>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const TipoBadge = ({ tipo }: { tipo: Annunci["tipo"] }) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex w-fit items-center justify-center rounded-md px-3",
|
|
|
|
|
tipo === "Transitorio" && "bg-orange-400",
|
|
|
|
|
tipo === "Stabile" && "bg-red-400",
|
|
|
|
|
tipo === "Cessione" && "bg-blue-400",
|
|
|
|
|
tipo === "Vendita" && "bg-green-400",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<div className="font-semibold text-white">{tipo}</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const AnnuncioDettaglio = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
data,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
data: Pick<
|
|
|
|
|
Annunci,
|
|
|
|
|
| "id"
|
|
|
|
|
| "codice"
|
|
|
|
|
| "prezzo"
|
|
|
|
|
| "desc_en"
|
|
|
|
|
| "desc_it"
|
|
|
|
|
| "url_immagini"
|
|
|
|
|
| "titolo_en"
|
|
|
|
|
| "titolo_it"
|
|
|
|
|
| "tipo"
|
|
|
|
|
| "consegna"
|
|
|
|
|
| "stato"
|
|
|
|
|
| "web"
|
|
|
|
|
>;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { locale, t } = useTranslation();
|
2025-08-29 18:18:37 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<Credenza>
|
|
|
|
|
<CredenzaTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
aria-label="Dettagli Annuncio"
|
2025-08-29 16:18:32 +02:00
|
|
|
className="flex items-center gap-2"
|
|
|
|
|
variant="outline"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<UnfoldVertical className="size-6" /> Dettagli annuncio
|
|
|
|
|
</Button>
|
|
|
|
|
</CredenzaTrigger>
|
|
|
|
|
<CredenzaContent className="max-h-[90vh] md:max-w-6xl">
|
|
|
|
|
<CredenzaHeader>
|
|
|
|
|
<CredenzaTitle className="text-xl">
|
|
|
|
|
Annuncio {data.codice}
|
|
|
|
|
</CredenzaTitle>
|
|
|
|
|
<CredenzaDescription className="sr-only">
|
|
|
|
|
Annuncio {data.codice}
|
|
|
|
|
</CredenzaDescription>
|
|
|
|
|
</CredenzaHeader>
|
|
|
|
|
<CredenzaBody className="scrollbar-default max-h-[80vh] overflow-y-auto pb-5">
|
|
|
|
|
<div className="flex flex-col gap-2 px-2">
|
|
|
|
|
<Carousel opts={{ loop: true }}>
|
|
|
|
|
<CarouselContent>
|
|
|
|
|
{data?.url_immagini?.map((img, idx) => (
|
|
|
|
|
<CarouselItem key={img}>
|
|
|
|
|
<ImageFlbk
|
|
|
|
|
alt={`${data.codice} - ${idx}`}
|
2025-08-29 16:18:32 +02:00
|
|
|
className={"h-80 w-full object-contain sm:h-[35rem]"}
|
2025-08-28 18:27:07 +02:00
|
|
|
height={200}
|
|
|
|
|
priority={idx === 0}
|
|
|
|
|
src={`/go-api/images/get/${img}`}
|
2025-08-29 16:18:32 +02:00
|
|
|
width={400}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</CarouselItem>
|
|
|
|
|
))}
|
|
|
|
|
</CarouselContent>
|
|
|
|
|
<div
|
2025-08-29 16:18:32 +02:00
|
|
|
className="block opacity-0 transition-all duration-200 group-hover:opacity-60 disabled:group-hover:opacity-0"
|
|
|
|
|
onClick={(e) => e.preventDefault()}
|
2025-08-28 18:27:07 +02:00
|
|
|
onKeyDown={(e) => e.stopPropagation()}
|
|
|
|
|
role="button"
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
>
|
|
|
|
|
<CarouselPrevious
|
|
|
|
|
className="left-2 cursor-pointer"
|
|
|
|
|
disabled={
|
|
|
|
|
!data.url_immagini || data.url_immagini.length === 1
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
<CarouselNext
|
|
|
|
|
className="right-2 cursor-pointer"
|
|
|
|
|
disabled={
|
|
|
|
|
!data.url_immagini || data.url_immagini.length === 1
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</Carousel>
|
|
|
|
|
<div>
|
|
|
|
|
<TipoBadge tipo={data.tipo} />
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="mt-1 space-y-2">
|
|
|
|
|
<div className="flex flex-row justify-around">
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<dt className="sr-only">{t.card.codice}</dt>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<dd className="text-lg font-semibold">
|
|
|
|
|
Cod: {data.codice}
|
|
|
|
|
</dd>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<dt className="sr-only">{t.card.prezzo}</dt>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<dd className="text-lg font-semibold">
|
|
|
|
|
{formatCurrency(data.prezzo / 1e2)}
|
|
|
|
|
</dd>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="mb-3">
|
|
|
|
|
<dt className="sr-only">{t.card.titolo}</dt>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<dd className="text-base font-semibold md:text-lg">
|
|
|
|
|
{locale === "it" ? data.titolo_it : data.titolo_en}
|
|
|
|
|
</dd>
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="mb-3">
|
|
|
|
|
<dd
|
|
|
|
|
className="text-base"
|
|
|
|
|
dangerouslySetInnerHTML={{
|
|
|
|
|
__html: replaceWithBr(
|
2025-08-29 18:18:37 +02:00
|
|
|
(locale === "it" ? data.desc_it : data.desc_en) || "",
|
2025-08-28 18:27:07 +02:00
|
|
|
),
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</CredenzaBody>
|
|
|
|
|
<CredenzaFooter>
|
|
|
|
|
<CredenzaClose asChild>
|
|
|
|
|
<Button aria-label="Chiudi Credenza">Chiudi</Button>
|
|
|
|
|
</CredenzaClose>
|
|
|
|
|
</CredenzaFooter>
|
|
|
|
|
</CredenzaContent>
|
|
|
|
|
</Credenza>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|