feat: refactor Annuncio context and related data fetching for improved structure and clarity
This commit is contained in:
parent
337ca80fec
commit
58ea254180
4 changed files with 133 additions and 139 deletions
|
|
@ -25,7 +25,7 @@ import { TouchProvider } from "~/components/custom_ui/HybridTooltip";
|
|||
import Input from "~/components/custom_ui/input";
|
||||
import { ComeFunziona } from "~/components/expand_guida";
|
||||
import FailedAnnuncioLoading from "~/components/failed-loading";
|
||||
import { IconMatrix, type IconType } from "~/components/IconComponents";
|
||||
import { IconMatrix } from "~/components/IconComponents";
|
||||
import InformationBubble from "~/components/InformationBubble";
|
||||
import { LoadingPage } from "~/components/loading";
|
||||
import { MappaDialogFullscreen } from "~/components/MapDialog";
|
||||
|
|
@ -54,10 +54,7 @@ import {
|
|||
import { Label } from "~/components/ui/label";
|
||||
import { VideoPlayer } from "~/components/videoPlayer";
|
||||
import { env } from "~/env";
|
||||
import {
|
||||
type CaratteristicheFiltered,
|
||||
filteredCaratteristiche,
|
||||
} from "~/hooks/schedaAnnuncioUtils";
|
||||
import { filteredCaratteristiche } from "~/hooks/schedaAnnuncioUtils";
|
||||
import { useStaleImageReload } from "~/hooks/staleImage";
|
||||
import { useMediaQuery } from "~/hooks/use-media-query";
|
||||
import { handleConsegna } from "~/lib/annuncio_details";
|
||||
|
|
@ -68,9 +65,7 @@ import { AnnuncioContext, useAnnuncio } from "~/providers/AnnuncioProvider";
|
|||
import { useTranslation } from "~/providers/I18nProvider";
|
||||
import { buildRicercaUrl } from "~/providers/RicercaProvider";
|
||||
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";
|
||||
|
||||
|
|
@ -135,10 +130,9 @@ const AnnuncioDettaglio: NextPage<AnnuncioProps> = ({
|
|||
|
||||
const AnnuncioView = ({ cod, flag }: Omit<AnnuncioProps, "meta">) => {
|
||||
const session = useSession();
|
||||
const { locale } = useTranslation();
|
||||
useStaleImageReload();
|
||||
|
||||
const { data, isLoading } = api.annunci.getAnnuncio.useQuery(
|
||||
const { data, isLoading } = api.annunci.getAnnuncioData.useQuery(
|
||||
{
|
||||
cod: cod,
|
||||
},
|
||||
|
|
@ -156,13 +150,6 @@ const AnnuncioView = ({ cod, flag }: Omit<AnnuncioProps, "meta">) => {
|
|||
return <FailedAnnuncioLoading />;
|
||||
}
|
||||
|
||||
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 (
|
||||
<AnnuncioContext.Provider value={data}>
|
||||
<TouchProvider>
|
||||
|
|
@ -176,7 +163,7 @@ const AnnuncioView = ({ cod, flag }: Omit<AnnuncioProps, "meta">) => {
|
|||
<div className="flex flex-col sm:py-8 md:flex-row-reverse md:gap-2">
|
||||
<div className="mb-6 flex flex-col md:mb-0 md:w-1/3">
|
||||
<div className="flex w-full flex-col flex-nowrap items-center justify-center gap-6">
|
||||
<CardInfos data={data} />
|
||||
<CardInfos />
|
||||
{session.user?.isAdmin ? (
|
||||
<>
|
||||
<Link
|
||||
|
|
@ -217,21 +204,10 @@ const AnnuncioView = ({ cod, flag }: Omit<AnnuncioProps, "meta">) => {
|
|||
</div>
|
||||
</div>
|
||||
<div className="mx-2 md:w-2/3">
|
||||
<TestiAnnuncio
|
||||
description={description}
|
||||
filteredCaratt={filteredCaratt}
|
||||
titolo={titolo}
|
||||
/>
|
||||
<TestiAnnuncio />
|
||||
</div>
|
||||
</div>
|
||||
<AnnuncioFooter
|
||||
comune={data.comune}
|
||||
consegna={data.consegna}
|
||||
external_videos={data.external_videos || []}
|
||||
tipo={data.tipo}
|
||||
updated_at={data.media_updated_at}
|
||||
videos={data.videos}
|
||||
/>
|
||||
<AnnuncioFooter />
|
||||
</div>
|
||||
</TouchProvider>
|
||||
</AnnuncioContext.Provider>
|
||||
|
|
@ -248,7 +224,7 @@ export async function getStaticProps(
|
|||
notFound: true,
|
||||
};
|
||||
}
|
||||
await ssg.annunci.getAnnuncio.prefetch({ cod: cod });
|
||||
await ssg.annunci.getAnnuncioData.prefetch({ cod: cod });
|
||||
|
||||
const meta = await ssg.annunci.getAnnuncioMeta.fetch({ cod: cod });
|
||||
|
||||
|
|
@ -388,42 +364,30 @@ const Pricing = ({ isDesktop }: { isDesktop: boolean }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const TestiAnnuncio = ({
|
||||
titolo,
|
||||
description,
|
||||
filteredCaratt,
|
||||
}: {
|
||||
titolo: string;
|
||||
description: string;
|
||||
filteredCaratt:
|
||||
| {
|
||||
text: string;
|
||||
icon: IconType;
|
||||
value: string | null;
|
||||
}[]
|
||||
| null;
|
||||
}) => {
|
||||
const TestiAnnuncio = () => {
|
||||
const { locale } = useTranslation();
|
||||
const { titolo_it, titolo_en, desc_it, desc_en } = useAnnuncio();
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="font-bold text-3xl uppercase">{titolo}</h1>
|
||||
<h1 className="font-bold text-3xl uppercase">
|
||||
{locale === "it" ? titolo_it : titolo_en}
|
||||
</h1>
|
||||
<h1
|
||||
className="py-6"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: replaceWithBr(description),
|
||||
__html: replaceWithBr((locale === "it" ? desc_it : desc_en) || ""),
|
||||
}}
|
||||
/>
|
||||
{filteredCaratt && (
|
||||
<CaratteristicheAccordion filteredCaratt={filteredCaratt} />
|
||||
)}
|
||||
|
||||
<CaratteristicheAccordion />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const CaratteristicheAccordion = ({
|
||||
filteredCaratt,
|
||||
}: {
|
||||
filteredCaratt: CaratteristicheFiltered;
|
||||
}) => {
|
||||
const CaratteristicheAccordion = () => {
|
||||
const { caratteristiche } = useAnnuncio();
|
||||
if (!caratteristiche) return null;
|
||||
return (
|
||||
<Accordion
|
||||
className="my-5"
|
||||
|
|
@ -437,43 +401,59 @@ const CaratteristicheAccordion = ({
|
|||
>
|
||||
<AccordionTrigger>Altre caratteristiche</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{Object.entries(filteredCaratt).map(([key, scheda_value]) => {
|
||||
const { text, icon, value } = scheda_value;
|
||||
if (value === null) return null;
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center gap-2 text-base"
|
||||
key={key}
|
||||
>
|
||||
<IconMatrix className="size-4" type={icon} />
|
||||
{text}: {value}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{Object.entries(filteredCaratteristiche(caratteristiche)).map(
|
||||
([key, scheda_value]) => {
|
||||
const { text, icon, value } = scheda_value;
|
||||
if (value === null) return null;
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center gap-2 text-base"
|
||||
key={key}
|
||||
>
|
||||
<IconMatrix className="size-4" type={icon} />
|
||||
{text}: {value}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
const CardInfos = ({ data }: { data: Annunci }) => {
|
||||
const CardInfos = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
codice,
|
||||
prezzo,
|
||||
tipo,
|
||||
numero_camere,
|
||||
numero_bagni,
|
||||
caratteristiche,
|
||||
numero_postiauto,
|
||||
mq,
|
||||
piano,
|
||||
stato,
|
||||
consegna,
|
||||
lat_secondario,
|
||||
lon_secondario,
|
||||
} = useAnnuncio();
|
||||
return (
|
||||
<Card className="relative w-full bg-secondary text-secondary-foreground outline outline-secondary-foreground">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center font-bold text-3xl md:pl-8 lg:pl-0">
|
||||
{t.annunci.codice} {data.codice}
|
||||
{t.annunci.codice} {codice}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4 p-4 py-0">
|
||||
<div className="grid grid-cols-2 gap-3 font-semibold">
|
||||
<div className="relative">
|
||||
<div className="flex h-10 items-center justify-center rounded-md bg-primary-foreground text-center text-primary">
|
||||
{data.prezzo && (data.prezzo / 1e2).toFixed(2).replace(".", ",")}{" "}
|
||||
{prezzo && (prezzo / 1e2).toFixed(2).replace(".", ",")}{" "}
|
||||
{t.annunci.euro_mese}
|
||||
</div>
|
||||
{data.prezzo && (
|
||||
{prezzo && (
|
||||
<InformationBubble
|
||||
icon="euro"
|
||||
iconClassName="h-[19.5px] w-[19.5px] bg-orange-500 text-white rounded-full"
|
||||
|
|
@ -483,7 +463,7 @@ const CardInfos = ({ data }: { data: Annunci }) => {
|
|||
{t.annunci.prezzo_giorno}
|
||||
</span>
|
||||
<div className="text-sm">
|
||||
{(data.prezzo / 1e2 / 30).toFixed(2).replace(".", ",")}{" "}
|
||||
{(prezzo / 1e2 / 30).toFixed(2).replace(".", ",")}{" "}
|
||||
{t.annunci.euro_giorno}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -494,13 +474,13 @@ const CardInfos = ({ data }: { data: Annunci }) => {
|
|||
<div
|
||||
className={cn(
|
||||
"h-10 rounded-md bg-primary-foreground p-2 text-center text-primary outline-2",
|
||||
data.tipo === "Transitorio" && "outline-transitorio",
|
||||
data.tipo === "Stabile" && "outline-stabile",
|
||||
tipo === "Transitorio" && "outline-transitorio",
|
||||
tipo === "Stabile" && "outline-stabile",
|
||||
)}
|
||||
>
|
||||
{data.tipo}
|
||||
{tipo}
|
||||
</div>
|
||||
{data.tipo === "Transitorio" && (
|
||||
{tipo === "Transitorio" && (
|
||||
<InformationBubble
|
||||
href="/guida#faq-transitori"
|
||||
target="_blank"
|
||||
|
|
@ -516,30 +496,30 @@ const CardInfos = ({ data }: { data: Annunci }) => {
|
|||
<BedDouble />
|
||||
<span className="line-clamp-1 break-all">
|
||||
{(() => {
|
||||
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}`;
|
||||
if (!numero_camere) return "N/A";
|
||||
if (numero_camere > 1)
|
||||
return `${numero_camere} ${t.card.camere2}`;
|
||||
if (numero_camere === 1)
|
||||
return `${numero_camere} ${t.card.camere1}`;
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-center gap-2 rounded-md bg-primary-foreground p-2 text-primary">
|
||||
<Bath />
|
||||
<span className="line-clamp-1 break-all">
|
||||
{data.numero_bagni || 0} {t.annunci.bagni}
|
||||
{numero_bagni || 0} {t.annunci.bagni}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-center gap-2 rounded-md bg-primary-foreground p-2 text-primary">
|
||||
<Armchair />
|
||||
<span className="line-clamp-1 break-all">
|
||||
{data.caratteristiche?.Scheda_Arredi || ""}
|
||||
{caratteristiche?.Scheda_Arredi || ""}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-center gap-2 rounded-md bg-primary-foreground p-2 text-primary">
|
||||
<Car />
|
||||
<span className="line-clamp-1 break-all">
|
||||
{data.numero_postiauto || 0} {t.annunci.posti_auto}
|
||||
{numero_postiauto || 0} {t.annunci.posti_auto}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -550,21 +530,19 @@ const CardInfos = ({ data }: { data: Annunci }) => {
|
|||
<div className="flex justify-center gap-2 rounded-md bg-primary-foreground p-2 text-primary">
|
||||
<Ruler />
|
||||
<span className="line-clamp-1 break-all">
|
||||
{data.mq ? Number(data.mq).toLocaleString("it-IT") : "--"} m
|
||||
<sup>2</sup>
|
||||
{mq ? Number(mq).toLocaleString("it-IT") : "--"} m<sup>2</sup>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center gap-2 rounded-md bg-primary-foreground p-2 text-primary">
|
||||
{data.piano !== null && data.piano !== "" && (
|
||||
{piano !== null && piano !== "" && (
|
||||
<>
|
||||
<ArrowUp01 />
|
||||
<span className="line-clamp-1 break-all">
|
||||
{(() => {
|
||||
if (data.piano === "0") return t.annunci.p_terra;
|
||||
if (data.piano === "0.5") return t.annunci.p_rialzato;
|
||||
if (data.piano)
|
||||
return `${data.piano}° ${t.annunci.piano}`;
|
||||
if (piano === "0") return t.annunci.p_terra;
|
||||
if (piano === "0.5") return t.annunci.p_rialzato;
|
||||
if (piano) return `${piano}° ${t.annunci.piano}`;
|
||||
return "--";
|
||||
})()}
|
||||
</span>
|
||||
|
|
@ -575,7 +553,7 @@ const CardInfos = ({ data }: { data: Annunci }) => {
|
|||
</div>
|
||||
|
||||
<div className="text-center font-bold text-base">
|
||||
{data.stato === "Trattativa" ? (
|
||||
{stato === "Trattativa" ? (
|
||||
<div className="flex items-center justify-center gap-1 rounded-md py-1.5 text-trattativa outline-2 outline-trattativa dark:bg-secondary-foreground">
|
||||
<TrafficCone className="inline-block size-5" />
|
||||
<span>{t.annunci.in_aggiornamento}</span>
|
||||
|
|
@ -587,7 +565,7 @@ const CardInfos = ({ data }: { data: Annunci }) => {
|
|||
<span className="text-red-500">
|
||||
{handleConsegna({
|
||||
aggiornamento: t.card.in_aggiornamento,
|
||||
consegna: data.consegna,
|
||||
consegna: consegna,
|
||||
consegna_da: t.card.consegna_da,
|
||||
mesi: t.parametri.mesi,
|
||||
subito: t.card.consegna_subito,
|
||||
|
|
@ -597,10 +575,7 @@ const CardInfos = ({ data }: { data: Annunci }) => {
|
|||
)}
|
||||
</div>
|
||||
<div className="flex flex-row justify-around gap-3">
|
||||
<MappaDialogFullscreen
|
||||
lat={data.lat_secondario}
|
||||
long={data.lon_secondario}
|
||||
/>
|
||||
<MappaDialogFullscreen lat={lat_secondario} long={lon_secondario} />
|
||||
|
||||
<ShareComponent />
|
||||
</div>
|
||||
|
|
@ -677,28 +652,16 @@ const ShareComponent = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const AnnuncioFooter = ({
|
||||
tipo,
|
||||
comune,
|
||||
consegna,
|
||||
videos,
|
||||
external_videos,
|
||||
updated_at,
|
||||
}: {
|
||||
tipo: string;
|
||||
comune: string | null;
|
||||
consegna: number | null;
|
||||
videos: Pick<VideosRefs, "video" | "thumb">[];
|
||||
external_videos: string[];
|
||||
updated_at: Date | null;
|
||||
}) => {
|
||||
const AnnuncioFooter = () => {
|
||||
const { tipo, comune, consegna, videos, external_videos, media_updated_at } =
|
||||
useAnnuncio();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p
|
||||
className={cn(
|
||||
"px-2 font-semibold text-lg",
|
||||
videos.length === 0 && external_videos.length === 0 && "hidden",
|
||||
videos.length === 0 && external_videos?.length === 0 && "hidden",
|
||||
)}
|
||||
>
|
||||
Video:
|
||||
|
|
@ -710,17 +673,23 @@ const AnnuncioFooter = ({
|
|||
className="h-96 max-w-96"
|
||||
coverImage={getStorageUrl({
|
||||
storageId: video.thumb,
|
||||
params: { cacheKey: updated_at?.toISOString(), media: "image" },
|
||||
params: {
|
||||
cacheKey: media_updated_at?.toISOString(),
|
||||
media: "image",
|
||||
},
|
||||
})}
|
||||
key={`videoplayer-${video.video}`} // Cache busting
|
||||
videoSrc={getStorageUrl({
|
||||
storageId: video.video,
|
||||
params: { cacheKey: updated_at?.toISOString(), media: "video" },
|
||||
params: {
|
||||
cacheKey: media_updated_at?.toISOString(),
|
||||
media: "video",
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{external_videos.map((videoUrl) => {
|
||||
{external_videos?.map((videoUrl) => {
|
||||
const embedUrl = getYouTubeEmbedUrl(videoUrl);
|
||||
if (!embedUrl) return null;
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { createContext, useContext } from "react";
|
||||
import type { Annunci } from "~/schemas/public/Annunci";
|
||||
import type { AnnuncioData } from "~/server/controllers/annunci.controller";
|
||||
|
||||
export const AnnuncioContext = createContext<Pick<
|
||||
Annunci,
|
||||
"id" | "tipo" | "prezzo" | "codice"
|
||||
> | null>(null);
|
||||
export const AnnuncioContext = createContext<AnnuncioData | null>(null);
|
||||
|
||||
export const useAnnuncio = () => {
|
||||
const context = useContext(AnnuncioContext);
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ import {
|
|||
import {
|
||||
editAnnuncioHandler,
|
||||
get_AnnunciPositionsHandler,
|
||||
getAnnunciByCod,
|
||||
getAnnunciById_rawImgUrls,
|
||||
getAnnunciListHandler,
|
||||
getAnnunciMetaByCod,
|
||||
getAnnuncioData,
|
||||
getAnnunciRicerca,
|
||||
getCodici_AnnunciHandler,
|
||||
getOptions_AnnunciHandler,
|
||||
|
|
@ -52,15 +52,14 @@ export const annunciRouter = createTRPCRouter({
|
|||
getAnnunciOptions: publicProcedure.query(async () => {
|
||||
return await getOptions_AnnunciHandler();
|
||||
}),
|
||||
/** TODO da limitare per evitare leak */
|
||||
getAnnuncio: publicProcedure
|
||||
getAnnuncioData: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
cod: z.string(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
return await getAnnunciByCod({ ...input });
|
||||
return await getAnnuncioData({ ...input });
|
||||
}),
|
||||
getAnnuncioFull: adminProcedure
|
||||
.input(
|
||||
|
|
|
|||
|
|
@ -72,26 +72,55 @@ export const getCodici_AnnunciHandler = async (): Promise<string[]> => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getAnnunciByCod = async ({
|
||||
cod,
|
||||
}: {
|
||||
cod: string;
|
||||
}): Promise<AnnunciWithMedia | null> => {
|
||||
export type AnnuncioData = Awaited<ReturnType<typeof getAnnuncioData>>;
|
||||
export const getAnnuncioData = async ({ cod }: { cod: string }) => {
|
||||
try {
|
||||
const annuncio = await db
|
||||
.selectFrom("annunci")
|
||||
.selectAll()
|
||||
.select([
|
||||
"anno",
|
||||
"accessori",
|
||||
"caratteristiche",
|
||||
"categorie",
|
||||
"desc_en",
|
||||
"desc_it",
|
||||
"tipo",
|
||||
"titolo_it",
|
||||
"titolo_en",
|
||||
"stato",
|
||||
"media_updated_at",
|
||||
"id",
|
||||
"codice",
|
||||
"comune",
|
||||
"consegna",
|
||||
"external_videos",
|
||||
"prezzo",
|
||||
"numero_camere",
|
||||
"numero_bagni",
|
||||
"numero_balconi",
|
||||
"numero_postiauto",
|
||||
"numero_box",
|
||||
"numero_vani",
|
||||
"numero_terrazzi",
|
||||
"mq",
|
||||
"piano",
|
||||
"piano_palazzo",
|
||||
"lat_secondario",
|
||||
"lon_secondario",
|
||||
"media_updated_at",
|
||||
])
|
||||
.select((_eb) => [withImages(), withVideos()])
|
||||
.where("annunci.web", "=", true)
|
||||
.where("tipo", "is not", null)
|
||||
.where("codice", "=", cod)
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!annuncio || annuncio.tipo === null) {
|
||||
.executeTakeFirstOrThrow(
|
||||
() => new Error(`Annuncio non trovato - codice: ${cod}`),
|
||||
);
|
||||
const { tipo, ...rest } = annuncio;
|
||||
if (tipo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return annuncio as AnnunciWithMedia;
|
||||
return { tipo, ...rest };
|
||||
} catch (e) {
|
||||
throw new TRPCError({
|
||||
cause: (e as Error).cause,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue