From 3dc0bccb586328240f6aa67b4ab257534734053a Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Wed, 12 Nov 2025 15:52:04 +0100 Subject: [PATCH] feat: implement lazy loading for CardAnnuncio component; add skeleton loading state and enhance InputNumber component with height prop --- .../src/components/annuncio_card.tsx | 67 +++++- .../src/components/custom_ui/InputNumber.tsx | 18 +- .../src/components/servizio/conferma.tsx | 2 + apps/infoalloggi/src/components/svgs.tsx | 2 +- .../src/forms/FormEditAnnuncio.tsx | 2 +- apps/infoalloggi/src/pages/annunci.tsx | 224 +++++++----------- .../admin/user-view/onboard/[servizioId].tsx | 2 +- .../admin/user-view/servizio/[userId].tsx | 2 +- .../pages/area-riservata/comunicazioni.tsx | 2 +- apps/infoalloggi/src/pages/chi-siamo.tsx | 2 +- apps/infoalloggi/src/pages/contatti.tsx | 2 +- apps/infoalloggi/src/pages/guida.tsx | 2 +- apps/infoalloggi/src/pages/index.tsx | 2 +- apps/infoalloggi/src/pages/prezzi.tsx | 2 +- apps/infoalloggi/src/pages/proprietari.tsx | 2 +- .../src/providers/RicercaProvider.tsx | 13 +- .../server/controllers/annunci.controller.ts | 1 + apps/infoalloggi/src/styles/globals.css | 6 +- 18 files changed, 199 insertions(+), 154 deletions(-) diff --git a/apps/infoalloggi/src/components/annuncio_card.tsx b/apps/infoalloggi/src/components/annuncio_card.tsx index e44982c..a9a2966 100644 --- a/apps/infoalloggi/src/components/annuncio_card.tsx +++ b/apps/infoalloggi/src/components/annuncio_card.tsx @@ -10,7 +10,8 @@ import { } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; -import { useState } from "react"; +import { type RefObject, useEffect, useRef, useState } from "react"; +import { useIntersection } from "react-use"; import { ImageFlbk } from "~/components/ImageWithFallback"; import { Carousel, @@ -30,15 +31,77 @@ import { camereTesti, handleConsegna } from "~/lib/annuncio_details"; import { cn, formatCurrency } from "~/lib/utils"; import { useTranslation } from "~/providers/I18nProvider"; import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller"; + import { Badge } from "./ui/badge"; +import { Skeleton } from "./ui/skeleton"; import { VideoPlayer } from "./videoPlayer"; type CardAnnuncioProps = AnnuncioRicerca & { className?: string; noLink?: boolean; onlyFirstImage?: boolean; + eager?: boolean; }; +export const LazyCardAnnuncio = (props: CardAnnuncioProps) => { + const { eager = false } = props; + const intersectionRef = useRef(null); + const hasRenderedRef = useRef(eager); + const [shouldRender, setShouldRender] = useState(eager); + + const intersection = useIntersection( + intersectionRef as RefObject, + { + root: null, + rootMargin: "300px", + threshold: 0.01, + }, + ); + + useEffect(() => { + if ( + !hasRenderedRef.current && + intersection && + intersection.intersectionRatio > 0 + ) { + hasRenderedRef.current = true; + setShouldRender(true); + } + }, [intersection]); + + return ( +
+ {shouldRender ? : } +
+ ); +}; + +const SkeletonCardAnnuncio = () => { + return ( +
+
+ +
+ + +
+ + +
+
+ + +
+ + + +
+
+
+
+
+ ); +}; export const CardAnnuncio = ({ id, codice, @@ -81,7 +144,7 @@ export const CardAnnuncio = ({ return (
void; fixedDecimalScale?: boolean; decimalScale?: number; + height?: string; } export const NumberInput = forwardRef( @@ -35,6 +37,8 @@ export const NumberInput = forwardRef( suffix, prefix, value: controlledValue, + className, + height = "h-9", ...props }, externalRef, @@ -117,10 +121,14 @@ export const NumberInput = forwardRef( }; return ( -
+
( {...props} /> -
+
+ + ); +}; + +const RicercaSideFilters = () => { + return ( +
+
); }; const DrowdownFilters = () => { const { t } = useTranslation(); - const { - tipo, - setTipo, - comune, - setComune, - consegna, - setConsegna, - sort, - setSort, - comuniOptions, - tipologieOptions, - consegnaOptions, - ResetParams, - nFiltri, - } = useRicerca(); + const { nFiltri } = useRicerca(); const [open, setOpen] = useState(false); - const mappedConsegnaOptions = useMemo( - // biome-ignore lint/style/noNonNullAssertion: - () => consegnaOptions.map((m) => t.preferenze.mesi[m]!).filter(Boolean), - [consegnaOptions, t.preferenze.mesi], - ); - // Close dropdown when page resizes const handleResize = useCallback(() => { setOpen(false); @@ -271,7 +255,7 @@ const DrowdownFilters = () => { }, [handleResize]); return ( -
+
+
@@ -369,7 +285,7 @@ const RicercaHeader = () => { const { map, setMap } = useRicerca(); return ( -
+
@@ -417,26 +333,33 @@ const AnnunciList = () => { sort, map, page, - setPage, isLoading: ricercaLoading, + annunciNumber, } = useRicerca(); - const { data, status, isPlaceholderData } = - api.annunci.getWithCursor.useQuery( - { - comune: comune || undefined, - consegna: consegna || undefined, - page, - pageSize: 6, - sort: sort || undefined, - tipologia: tipo || undefined, - }, - { - enabled: !map, - placeholderData: keepPreviousData, - staleTime: 5000, - }, - ); + const { + data, + status, + //isPlaceholderData + } = api.annunci.getWithCursor.useQuery( + { + comune: comune || undefined, + consegna: consegna || undefined, + page, + pageSize: annunciNumber, + sort: sort || undefined, + tipologia: tipo || undefined, + }, + { + enabled: !map, + placeholderData: keepPreviousData, + staleTime: 5000, + }, + ); + + /* + DISABILITATO FUNZIONALITA PAGING E PAGE SIZE + const utils = api.useUtils(); useEffect(() => { const prefetchData = async () => { @@ -445,7 +368,7 @@ const AnnunciList = () => { comune: comune || undefined, consegna: consegna || undefined, page: page + 1, - pageSize: 6, + pageSize: annunciNumber, sort: sort || undefined, tipologia: tipo || undefined, }); @@ -454,7 +377,7 @@ const AnnunciList = () => { prefetchData().catch((err) => { console.error("Error prefetching next page:", err); }); - }, [data, isPlaceholderData, page]); + }, [data, isPlaceholderData, page, annunciNumber]); useEffect(() => { // Reset to page 0 if current page has no data or less than page size @@ -463,16 +386,29 @@ const AnnunciList = () => { if (data.annunci.length === 0 && page !== 0) { await setPage(0); } - if (data.annunci.length < 6 || !data.hasMore) { - await setPage(0); - } } }; checkPage().catch((err) => { console.error("Error checking data for page reset:", err); }); - }, [data, tipo, comune, consegna, sort, setPage]); + }, [data, tipo, comune, consegna, sort, setPage, annunciNumber]); + const [debouncedAnnunciNumber, setDebouncedAnnunciNumber] = + useState(annunciNumber); + + useDebounce( + async () => { + console.log("Setting annunciNumber to", debouncedAnnunciNumber); + console.log("Current annunciNumber is", annunciNumber); + if (debouncedAnnunciNumber !== annunciNumber) { + await setAnnunciNumber(debouncedAnnunciNumber); + await setPage(0); + } + }, + 500, + [debouncedAnnunciNumber], + ); +*/ if (ricercaLoading) return ; if (status === "error") return ; @@ -481,14 +417,34 @@ const AnnunciList = () => { {status === "pending" ? ( ) : ( -
- {data.annunci.map((annuncio) => ( - +
+ {data.annunci.map((annuncio, idx) => ( + ))}
)} -
+ {/*
+
+ + annunci per pagina: + + { + if (val !== undefined) { + setDebouncedAnnunciNumber(val); + } + }} + value={debouncedAnnunciNumber} + /> +
{page >= 2 && (
-
{page + 1}
-
-
+
*/}
); }; @@ -571,7 +525,11 @@ export const SelectFilter = ({ {label} {value !== defaultValue && ( - )} diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/onboard/[servizioId].tsx b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/onboard/[servizioId].tsx index a3aec57..18d828e 100644 --- a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/onboard/[servizioId].tsx +++ b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/onboard/[servizioId].tsx @@ -28,7 +28,7 @@ const OnboardUser: NextPageWithLayout = ({ return ; } return ( -
+

Onboarding Utente (Versione Admin) diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/servizio/[userId].tsx b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/servizio/[userId].tsx index 8b8a892..3c54cc5 100644 --- a/apps/infoalloggi/src/pages/area-riservata/admin/user-view/servizio/[userId].tsx +++ b/apps/infoalloggi/src/pages/area-riservata/admin/user-view/servizio/[userId].tsx @@ -29,7 +29,7 @@ const ServizioUser: NextPageWithLayout = ({ const [open, setOpen] = useState(false); const [editData, setEditData] = useState(undefined); return ( -
+

Servizi Utente

{ -
+

Comunicazioni

diff --git a/apps/infoalloggi/src/pages/chi-siamo.tsx b/apps/infoalloggi/src/pages/chi-siamo.tsx index 7baef9f..7c5778a 100644 --- a/apps/infoalloggi/src/pages/chi-siamo.tsx +++ b/apps/infoalloggi/src/pages/chi-siamo.tsx @@ -11,7 +11,7 @@ const ChiSiamo: NextPage = () => { {t.heads.chi_siamo_titolo} -
+
{t.chi_siamo.title}
diff --git a/apps/infoalloggi/src/pages/contatti.tsx b/apps/infoalloggi/src/pages/contatti.tsx index 4c2fdbe..cb24fca 100644 --- a/apps/infoalloggi/src/pages/contatti.tsx +++ b/apps/infoalloggi/src/pages/contatti.tsx @@ -67,7 +67,7 @@ const Contatto: NextPage = () => { {t.heads.contatti_titolo} -
+
{t.contatti}
diff --git a/apps/infoalloggi/src/pages/guida.tsx b/apps/infoalloggi/src/pages/guida.tsx index 35ab131..3e7d123 100644 --- a/apps/infoalloggi/src/pages/guida.tsx +++ b/apps/infoalloggi/src/pages/guida.tsx @@ -19,7 +19,7 @@ const Guida: NextPage = () => { {t.heads.guida_titolo} -
+
{t.guida}
diff --git a/apps/infoalloggi/src/pages/index.tsx b/apps/infoalloggi/src/pages/index.tsx index f606acd..e1ccbe0 100644 --- a/apps/infoalloggi/src/pages/index.tsx +++ b/apps/infoalloggi/src/pages/index.tsx @@ -37,7 +37,7 @@ const Home: NextPage = () => {
-
+
diff --git a/apps/infoalloggi/src/pages/prezzi.tsx b/apps/infoalloggi/src/pages/prezzi.tsx index d26d688..988cae3 100644 --- a/apps/infoalloggi/src/pages/prezzi.tsx +++ b/apps/infoalloggi/src/pages/prezzi.tsx @@ -43,7 +43,7 @@ const Prezzi: NextPage = () => { {t.heads.prezzi_titolo} -
+
{t.prezzi_titolo}
diff --git a/apps/infoalloggi/src/pages/proprietari.tsx b/apps/infoalloggi/src/pages/proprietari.tsx index af5633b..7a02173 100644 --- a/apps/infoalloggi/src/pages/proprietari.tsx +++ b/apps/infoalloggi/src/pages/proprietari.tsx @@ -16,7 +16,7 @@ const Proprietari: NextPage = () => { {t.proprietari.head} -
+
{t.proprietari.titolo}
diff --git a/apps/infoalloggi/src/providers/RicercaProvider.tsx b/apps/infoalloggi/src/providers/RicercaProvider.tsx index c66d061..655dbaf 100644 --- a/apps/infoalloggi/src/providers/RicercaProvider.tsx +++ b/apps/infoalloggi/src/providers/RicercaProvider.tsx @@ -46,6 +46,10 @@ type RicercaContextType = { value: number | ((old: number | null) => number | null) | null, ) => Promise; ResetParams: () => Promise; + annunciNumber: number; + setAnnunciNumber: ( + value: number | ((old: number) => number | null) | null, + ) => Promise; nFiltri: number; isLoading: boolean; }; @@ -104,6 +108,10 @@ export const RicercaProvider: FC<{ "p", parseAsInteger.withDefault(0).withOptions({ clearOnDefault: true }), ); + const [annunciNumber, setAnnunciNumber] = useQueryState( + "n", + parseAsInteger.withDefault(1000).withOptions({ startTransition }), + ); const tipologieOptions = useMemo(() => { const set = new Set(); @@ -153,7 +161,7 @@ export const RicercaProvider: FC<{ await setComune(""); await setConsegna(null); await setSort("Recenti"); - await setPage(0); + //await setPage(0); }; const nFiltri = @@ -187,6 +195,9 @@ export const RicercaProvider: FC<{ sortOptions: sortOptions, tipo, tipologieOptions, + + annunciNumber, + setAnnunciNumber, }} > {children} diff --git a/apps/infoalloggi/src/server/controllers/annunci.controller.ts b/apps/infoalloggi/src/server/controllers/annunci.controller.ts index 80a7a46..72e4b91 100644 --- a/apps/infoalloggi/src/server/controllers/annunci.controller.ts +++ b/apps/infoalloggi/src/server/controllers/annunci.controller.ts @@ -366,6 +366,7 @@ export const getCursor_AnnunciHandler = async ({ .limit(pageSize + 1) .offset(page * pageSize) .execute(); + const hasMore = annunci.length > pageSize; if (annunci.length > pageSize) { annunci.pop(); // Remove the last item if it exceeds the page size diff --git a/apps/infoalloggi/src/styles/globals.css b/apps/infoalloggi/src/styles/globals.css index 89b7ed3..1f03d3d 100644 --- a/apps/infoalloggi/src/styles/globals.css +++ b/apps/infoalloggi/src/styles/globals.css @@ -36,7 +36,9 @@ --breakpoint-3xl: 120rem; --breakpoint-4xl: 160rem; --breakpoint-5xl: 192rem; - + --container-8xl: 88rem; + --container-9xl: 96rem; + --container-10xl: 104rem; --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); @@ -160,7 +162,7 @@ :root { --radius: 0.625rem; --background: oklch(1 0 0); - --background2: oklch(95.041% 0.02791 62.217); + --background2: oklch(80.395% 0.06402 83.429); --foreground: oklch(0.145 0 0); --card: oklch(1 0 0); --card-foreground: oklch(0.145 0 0);