import { Armchair, Bath, BedDouble, Building2, Car, Clock, Copy, Edit, ExternalLink, Printer, Ruler, Share2, TrafficCone, } from "lucide-react"; import type { GetStaticPaths, GetStaticPropsContext, NextPage } from "next"; import Head from "next/head"; import Link from "next/link"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; import { CarouselAnnuncio } from "~/components/annuncio_card"; import { AnnuncioInteractions } from "~/components/annuncio-interactions/annuncio_interactions"; import { TouchProvider } from "~/components/custom_ui/HybridTooltip"; import Input from "~/components/custom_ui/input"; import FailedAnnuncioLoading from "~/components/failed-loading"; import { IconMatrix, type IconType } from "~/components/IconComponents"; import InformationBubble from "~/components/InformationBubble"; import { LoadingPage } from "~/components/loading"; import { MappaDialogFullscreen } from "~/components/MapDialog"; import { PricingComponent } from "~/components/prezzi"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "~/components/ui/accordion"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "~/components/ui/dialog"; import { Label } from "~/components/ui/label"; import { VideoPlayer } from "~/components/videoPlayer"; import { env } from "~/env"; import { type CaratteristicheFiltered, filteredCaratteristiche, } from "~/hooks/schedaAnnuncioUtils"; import { useMediaQuery } from "~/hooks/use-media-query"; import { handleConsegna } from "~/lib/annuncio_details"; import { replaceWithBr } from "~/lib/newlineToBr"; import { cn } from "~/lib/utils"; import { AnnuncioContext } from "~/providers/AnnuncioProvider"; import { useTranslation } from "~/providers/I18nProvider"; import { useSession } from "~/providers/SessionProvider"; import type { Annunci } from "~/schemas/public/Annunci"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import type { VideosRefs } from "~/schemas/public/VideosRefs"; import { generateSSGHelper } from "~/server/utils/ssgHelper"; import { api } from "~/utils/api"; type AnnuncioProps = { cod: string; flag: string; meta: { ogImage: string; title: string; description: string; ogUrl: string; } | null; }; const AnnuncioDettaglio: NextPage = ({ cod, flag, meta, }: AnnuncioProps) => { return (
{`${cod} | ${meta?.title}`}
); }; const AnnuncioView = ({ cod, flag }: Omit) => { const session = useSession(); const { locale } = useTranslation(); const { data, isLoading } = api.annunci.getAnnuncio.useQuery( { cod: cod, }, { refetchOnMount: false }, ); const isDesktop = useMediaQuery("(min-width: 768px)"); if (isLoading || session.status === "pending") return ; if ( !data || session.status === "error" || (data && data.stato === "Sospeso") ) { return ; } const titolo = (locale === "it" ? data.titolo_it : data.titolo_en) || ""; const description = (locale === "it" ? data.desc_it : data.desc_en) || ""; const filteredCaratt = data.caratteristiche ? filteredCaratteristiche(data.caratteristiche) : null; return (
{session.user?.isAdmin ? ( <> ) : ( )} {data.tipo && (data.tipo === "Transitorio" || data.tipo === "Stabile") && ( <> )}
); }; export async function getStaticProps( context: GetStaticPropsContext<{ cod: string }>, ) { const ssg = generateSSGHelper(); const cod = context.params?.cod; if (typeof cod !== "string") { return { notFound: true, }; } await ssg.annunci.getAnnuncio.prefetch({ cod: cod }); const meta = await ssg.annunci.getAnnuncioMeta.fetch({ cod: cod }); const flag = await ssg.flags.GetFlagValue.fetch({ id: "ANNUNCIO_INTERACTIONS_DISABLED", }); return { props: { cod, flag, meta, trpcState: ssg.dehydrate(), }, revalidate: 21600, // 6 hours - ensures fresh data 4x per day }; } export const getStaticPaths: GetStaticPaths = async () => { return { fallback: "blocking", paths: [], }; }; export default AnnuncioDettaglio; const Pricing = ({ isDesktop, tipologia, }: { isDesktop: boolean; tipologia: TipologiaPosizioneEnum; }) => { const { t } = useTranslation(); const { data: prezziario, isLoading } = api.prezziario.getPrezziPerTipologia.useQuery({ tipologia, }); if (isLoading) return ; if (!prezziario) return null; const cta = tipologia === TipologiaPosizioneEnum.Transitorio ? t.pricing_cmp.cta_breve : t.pricing_cmp.cta_stabile; const opzioni = tipologia === TipologiaPosizioneEnum.Transitorio ? [ t.pricing_cmp.breve_option1, t.pricing_cmp.breve_option2, t.pricing_cmp.breve_option3, t.pricing_cmp.breve_option4, t.pricing_cmp.breve_option5, ] : [t.pricing_cmp.stabile_option1, t.pricing_cmp.stabile_option2]; const style = tipologia === TipologiaPosizioneEnum.Transitorio ? "border-transitorio bg-transitorio" : "border-stabile bg-stabile"; const pricing = prezziario.saldi.map((s) => ({ downPayment: prezziario.acconto.prezzo_cent / 100, secondPayment: s.prezzo_cent / 100, })); if (isDesktop) { return ( ); } return ( {t.pricing_cmp.cta_costo} ); }; const TestiAnnuncio = ({ titolo, description, filteredCaratt, }: { titolo: string; description: string; filteredCaratt: | { text: string; icon: IconType; value: string | null; }[] | null; }) => { return ( <>

{titolo}

{filteredCaratt && ( )} ); }; const CaratteristicheAccordion = ({ filteredCaratt, }: { filteredCaratt: CaratteristicheFiltered; }) => { return ( Altre caratteristiche {Object.entries(filteredCaratt).map(([key, scheda_value]) => { const { text, icon, value } = scheda_value; if (value === null) return null; return (
{text}: {value}
); })}
); }; const CardInfos = ({ data }: { data: Annunci }) => { const { t } = useTranslation(); return ( Codice {data.codice}
{data.prezzo && (data.prezzo / 1e2).toFixed(2).replace(".", ",")}{" "} €/mese
{data.prezzo && (
Prezzo al giorno
{(data.prezzo / 1e2 / 30).toFixed(2).replace(".", ",")} €/gg
)}
{data.tipo}
{data.tipo === "Transitorio" && ( )}

Fornitura:

{(() => { if (!data.numero_camere) return "N/A"; if (data.numero_camere > 1) return `${data.numero_camere} ${t.card.camere2}`; if (data.numero_camere === 1) return `${data.numero_camere} ${t.card.camere1}`; })()}
{data.numero_bagni} bagni
{data.caratteristiche?.Scheda_Arredi || ""}
{data.numero_postiauto} posti auto

Dati immobile:

{data.mq ? Number(data.mq).toLocaleString("it-IT") : "--"} m 2
{(() => { if (data.piano === "0") return "piano terra"; if (data.piano === "0.5") return "piano rialz."; if (data.piano) return `${data.piano}° piano`; return ""; })()}
{data.stato === "Trattativa" ? (
Annuncio in aggiornamento
) : (
Disponibilità:{" "} {handleConsegna({ aggiornamento: t.card.in_aggiornamento, consegna: data.consegna, consegna_da: t.card.consegna_da, mesi: t.preferenze.mesi, subito: t.card.consegna_subito, })}
)}
); }; const ShareComponent = () => { const { t } = useTranslation(); const [link, setLink] = useState(""); useEffect(() => { if (typeof window !== "undefined") { setLink(window.location.href); } }, []); const handleCopyLink = async () => { if (navigator.clipboard?.writeText) { try { await navigator.clipboard.writeText(link); toast(t.share.copiato, { icon: "📋" }); } catch (err) { console.error("Failed to copy: ", err); } } else { // Fallback for browsers that do not support navigator.clipboard.writeText const textArea = document.createElement("textarea"); textArea.value = link; document.body.appendChild(textArea); textArea.select(); try { document.execCommand("copy"); toast(t.share.copiato, { icon: "📋" }); } catch (err) { console.error("Failed to copy: ", err); } document.body.removeChild(textArea); } }; return ( {t.share.condividi} {t.share.descrizione}
); }; const AnnuncioFooter = ({ tipo, comune, consegna, videos, external_videos, updated_at, }: { tipo: string | null; comune: string | null; consegna: number | null; videos: Pick[]; external_videos: string[]; updated_at: Date | null; }) => { return (

Video:

{videos.map((video) => { return ( ); })} {external_videos.map((videoUrl) => { const embedUrl = getYouTubeEmbedUrl(videoUrl); if (!embedUrl) return null; return (