feat: implement lazy loading for CardAnnuncio component; add skeleton loading state and enhance InputNumber component with height prop
This commit is contained in:
parent
f64597cc32
commit
3dc0bccb58
18 changed files with 199 additions and 154 deletions
|
|
@ -10,7 +10,8 @@ import {
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
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 { ImageFlbk } from "~/components/ImageWithFallback";
|
||||||
import {
|
import {
|
||||||
Carousel,
|
Carousel,
|
||||||
|
|
@ -30,15 +31,77 @@ import { camereTesti, handleConsegna } from "~/lib/annuncio_details";
|
||||||
import { cn, formatCurrency } from "~/lib/utils";
|
import { cn, formatCurrency } from "~/lib/utils";
|
||||||
import { useTranslation } from "~/providers/I18nProvider";
|
import { useTranslation } from "~/providers/I18nProvider";
|
||||||
import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller";
|
import type { AnnuncioRicerca } from "~/server/controllers/annunci.controller";
|
||||||
|
|
||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "./ui/badge";
|
||||||
|
import { Skeleton } from "./ui/skeleton";
|
||||||
import { VideoPlayer } from "./videoPlayer";
|
import { VideoPlayer } from "./videoPlayer";
|
||||||
|
|
||||||
type CardAnnuncioProps = AnnuncioRicerca & {
|
type CardAnnuncioProps = AnnuncioRicerca & {
|
||||||
className?: string;
|
className?: string;
|
||||||
noLink?: boolean;
|
noLink?: boolean;
|
||||||
onlyFirstImage?: boolean;
|
onlyFirstImage?: boolean;
|
||||||
|
eager?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const LazyCardAnnuncio = (props: CardAnnuncioProps) => {
|
||||||
|
const { eager = false } = props;
|
||||||
|
const intersectionRef = useRef<HTMLDivElement>(null);
|
||||||
|
const hasRenderedRef = useRef(eager);
|
||||||
|
const [shouldRender, setShouldRender] = useState(eager);
|
||||||
|
|
||||||
|
const intersection = useIntersection(
|
||||||
|
intersectionRef as RefObject<HTMLElement>,
|
||||||
|
{
|
||||||
|
root: null,
|
||||||
|
rootMargin: "300px",
|
||||||
|
threshold: 0.01,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!hasRenderedRef.current &&
|
||||||
|
intersection &&
|
||||||
|
intersection.intersectionRatio > 0
|
||||||
|
) {
|
||||||
|
hasRenderedRef.current = true;
|
||||||
|
setShouldRender(true);
|
||||||
|
}
|
||||||
|
}, [intersection]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-[31.1rem]" ref={intersectionRef}>
|
||||||
|
{shouldRender ? <CardAnnuncio {...props} /> : <SkeletonCardAnnuncio />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SkeletonCardAnnuncio = () => {
|
||||||
|
return (
|
||||||
|
<div className="h-[31.1rem] rounded-md bg-white p-2 outline outline-neutral-300 hover:outline-neutral-600">
|
||||||
|
<div className="flex h-full flex-col gap-2 p-2">
|
||||||
|
<Skeleton className="h-64 w-full rounded-t-md" />
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Skeleton className="h-8 w-full rounded-md" />
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-between gap-3 px-4">
|
||||||
|
<Skeleton className="h-10 w-full rounded-md" />
|
||||||
|
<Skeleton className="h-10 w-full rounded-md" />
|
||||||
|
</div>
|
||||||
|
<div className="flex h-[8.5rem] flex-col justify-between">
|
||||||
|
<Skeleton className="h-20 w-full rounded-md" />
|
||||||
|
|
||||||
|
<div className="flex h-9 items-center justify-around gap-1.5">
|
||||||
|
<Skeleton className="size-full rounded-md" />
|
||||||
|
<Skeleton className="size-full rounded-md" />
|
||||||
|
<Skeleton className="size-full rounded-md" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
export const CardAnnuncio = ({
|
export const CardAnnuncio = ({
|
||||||
id,
|
id,
|
||||||
codice,
|
codice,
|
||||||
|
|
@ -81,7 +144,7 @@ export const CardAnnuncio = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative h-[31.1rem] rounded-md bg-white shadow-neutral-100 shadow-sm outline outline-neutral-300 hover:shadow-md hover:outline-neutral-600",
|
"relative h-[31.1rem] rounded-md bg-white outline outline-neutral-300 hover:outline-neutral-600",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
id={`card-annuncio-${id}`}
|
id={`card-annuncio-${id}`}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||||
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
|
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { NumericFormat, type NumericFormatProps } from "react-number-format";
|
import { NumericFormat, type NumericFormatProps } from "react-number-format";
|
||||||
|
import { cn } from "~/lib/utils";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
|
|
||||||
|
|
@ -18,6 +19,7 @@ export interface NumberInputProps
|
||||||
onValueChange?: (value: number | undefined) => void;
|
onValueChange?: (value: number | undefined) => void;
|
||||||
fixedDecimalScale?: boolean;
|
fixedDecimalScale?: boolean;
|
||||||
decimalScale?: number;
|
decimalScale?: number;
|
||||||
|
height?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
||||||
|
|
@ -35,6 +37,8 @@ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
||||||
suffix,
|
suffix,
|
||||||
prefix,
|
prefix,
|
||||||
value: controlledValue,
|
value: controlledValue,
|
||||||
|
className,
|
||||||
|
height = "h-9",
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
externalRef,
|
externalRef,
|
||||||
|
|
@ -117,10 +121,14 @@ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center">
|
<div className={cn("flex items-stretch", height)}>
|
||||||
<NumericFormat
|
<NumericFormat
|
||||||
allowNegative={min < 0}
|
allowNegative={min < 0}
|
||||||
className="relative rounded-r-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
className={cn(
|
||||||
|
"relative rounded-r-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
|
||||||
|
height,
|
||||||
|
className,
|
||||||
|
)}
|
||||||
customInput={Input}
|
customInput={Input}
|
||||||
decimalScale={decimalScale}
|
decimalScale={decimalScale}
|
||||||
fixedDecimalScale={fixedDecimalScale}
|
fixedDecimalScale={fixedDecimalScale}
|
||||||
|
|
@ -138,10 +146,10 @@ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col self-stretch">
|
||||||
<Button
|
<Button
|
||||||
aria-label="Increase value"
|
aria-label="Increase value"
|
||||||
className="h-5 rounded-l-none rounded-br-none border-input border-b-[0.5px] border-l-0 px-2 focus-visible:relative"
|
className="h-1/2 rounded-l-none rounded-br-none border-input border-b-[0.5px] border-l-0 px-2 focus-visible:relative"
|
||||||
disabled={value === max}
|
disabled={value === max}
|
||||||
onClick={handleIncrement}
|
onClick={handleIncrement}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -150,7 +158,7 @@ export const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
aria-label="Decrease value"
|
aria-label="Decrease value"
|
||||||
className="h-5 rounded-l-none rounded-tr-none border-input border-t-[0.5px] border-l-0 px-2 focus-visible:relative"
|
className="h-1/2 rounded-l-none rounded-tr-none border-input border-t-[0.5px] border-l-0 px-2 focus-visible:relative"
|
||||||
disabled={value === min}
|
disabled={value === min}
|
||||||
onClick={handleDecrement}
|
onClick={handleDecrement}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ export const ConfermaAnnuncioModal = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
//TODO need to show messaggio in alert dialog after invio conferma about adeguamento saldo e permanenza based on doc presented
|
||||||
|
|
||||||
const needsDocuments =
|
const needsDocuments =
|
||||||
!servizio.skipDocMotivazione &&
|
!servizio.skipDocMotivazione &&
|
||||||
!servizio.doc_motivazione &&
|
!servizio.doc_motivazione &&
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export const HeroSvg = (props: SVGProps<SVGSVGElement>) => (
|
||||||
height={500}
|
height={500}
|
||||||
width={500}
|
width={500}
|
||||||
>
|
>
|
||||||
<title>hero</title>
|
<title>Infoalloggi.it</title>
|
||||||
<path
|
<path
|
||||||
d="m240.441 54.013-137.11 137.098V362.3h285.188V202.08L240.441 54.013Z"
|
d="m240.441 54.013-137.11 137.098V362.3h285.188V202.08L240.441 54.013Z"
|
||||||
fill="#EAEAEA"
|
fill="#EAEAEA"
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithImages }) => {
|
||||||
className="mt-3 grid flex-1 items-start gap-4 p-2 sm:px-4 sm:py-0 md:gap-8"
|
className="mt-3 grid flex-1 items-start gap-4 p-2 sm:px-4 sm:py-0 md:gap-8"
|
||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
>
|
>
|
||||||
<div className="mx-auto grid w-full max-w-7xl flex-1 auto-rows-max gap-4">
|
<div className="mx-auto grid w-full flex-1 auto-rows-max gap-4">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<h1 className="whitespace-nowrap font-semibold text-2xl tracking-tight">
|
<h1 className="whitespace-nowrap font-semibold text-2xl tracking-tight">
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import { keepPreviousData } from "@tanstack/react-query";
|
import { keepPreviousData } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronLeft,
|
|
||||||
ChevronRight,
|
|
||||||
ChevronsLeft,
|
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
Filter,
|
Filter,
|
||||||
FilterX,
|
FilterX,
|
||||||
|
|
@ -17,7 +14,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { AccordionComp } from "~/components/accordionComp";
|
import { AccordionComp } from "~/components/accordionComp";
|
||||||
import { MapSection } from "~/components/annunci_map";
|
import { MapSection } from "~/components/annunci_map";
|
||||||
import { CTA_TipologiaModal } from "~/components/annunci_tutorial";
|
import { CTA_TipologiaModal } from "~/components/annunci_tutorial";
|
||||||
import { CardAnnuncio } from "~/components/annuncio_card";
|
import { LazyCardAnnuncio } from "~/components/annuncio_card";
|
||||||
import { CodiceBox } from "~/components/codiceRicerca";
|
import { CodiceBox } from "~/components/codiceRicerca";
|
||||||
import { Layout } from "~/components/Layout";
|
import { Layout } from "~/components/Layout";
|
||||||
import { LoadingPage } from "~/components/loading";
|
import { LoadingPage } from "~/components/loading";
|
||||||
|
|
@ -62,7 +59,7 @@ const Annunci = ({ options }: AnnunciPageProps) => {
|
||||||
<meta content={t.heads.annunci_description} name="description" />
|
<meta content={t.heads.annunci_description} name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<main className="relative mx-auto w-full max-w-7xl space-y-5 bg-background2 px-2 py-5 md:px-8">
|
<main className="relative mx-auto w-full max-w-9xl space-y-5 bg-background2 px-2 py-5 md:px-8">
|
||||||
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
||||||
{t.annunci.titolo}
|
{t.annunci.titolo}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -125,9 +122,9 @@ const Ricerca = () => {
|
||||||
}, [page]);
|
}, [page]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-7xl space-y-5 xl:w-[100rem]">
|
<div className="mx-auto w-full space-y-5">
|
||||||
<RicercaHeader />
|
<RicercaHeader />
|
||||||
<div className="flex w-full gap-2">
|
<div className="relative flex w-full gap-4">
|
||||||
<RicercaSideFilters />
|
<RicercaSideFilters />
|
||||||
<div className="flex-1">{map ? <MapSection /> : <AnnunciList />}</div>
|
<div className="flex-1">{map ? <MapSection /> : <AnnunciList />}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -135,7 +132,7 @@ const Ricerca = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const RicercaSideFilters = () => {
|
const Filters = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
tipo,
|
tipo,
|
||||||
|
|
@ -157,9 +154,8 @@ const RicercaSideFilters = () => {
|
||||||
() => consegnaOptions.map((m) => t.preferenze.mesi[m]!).filter(Boolean),
|
() => consegnaOptions.map((m) => t.preferenze.mesi[m]!).filter(Boolean),
|
||||||
[consegnaOptions, t.preferenze.mesi],
|
[consegnaOptions, t.preferenze.mesi],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hidden h-fit w-46 flex-col gap-3 rounded-md bg-white p-2 outline-neutral-300 xl:flex">
|
<>
|
||||||
<SelectFilter
|
<SelectFilter
|
||||||
defaultValue={""}
|
defaultValue={""}
|
||||||
handleReset={async () => {
|
handleReset={async () => {
|
||||||
|
|
@ -229,35 +225,23 @@ const RicercaSideFilters = () => {
|
||||||
>
|
>
|
||||||
<FilterX className="size-5" /> {t.annunci.filtri_reset}
|
<FilterX className="size-5" /> {t.annunci.filtri_reset}
|
||||||
</Button>
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RicercaSideFilters = () => {
|
||||||
|
return (
|
||||||
|
<div className="hidden h-fit w-46 max-w-46 shrink-0 grow-0 flex-col gap-3 overflow-clip rounded-md bg-white p-2 outline-neutral-300 2xl:flex">
|
||||||
|
<Filters />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DrowdownFilters = () => {
|
const DrowdownFilters = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const { nFiltri } = useRicerca();
|
||||||
tipo,
|
|
||||||
setTipo,
|
|
||||||
comune,
|
|
||||||
setComune,
|
|
||||||
consegna,
|
|
||||||
setConsegna,
|
|
||||||
sort,
|
|
||||||
setSort,
|
|
||||||
comuniOptions,
|
|
||||||
tipologieOptions,
|
|
||||||
consegnaOptions,
|
|
||||||
ResetParams,
|
|
||||||
nFiltri,
|
|
||||||
} = useRicerca();
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const mappedConsegnaOptions = useMemo(
|
|
||||||
// biome-ignore lint/style/noNonNullAssertion: <known lenght>
|
|
||||||
() => consegnaOptions.map((m) => t.preferenze.mesi[m]!).filter(Boolean),
|
|
||||||
[consegnaOptions, t.preferenze.mesi],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Close dropdown when page resizes
|
// Close dropdown when page resizes
|
||||||
const handleResize = useCallback(() => {
|
const handleResize = useCallback(() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|
@ -271,7 +255,7 @@ const DrowdownFilters = () => {
|
||||||
}, [handleResize]);
|
}, [handleResize]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full sm:w-auto xl:hidden">
|
<div className="flex w-full sm:w-auto 2xl:hidden">
|
||||||
<DropdownMenu onOpenChange={setOpen} open={open}>
|
<DropdownMenu onOpenChange={setOpen} open={open}>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -290,75 +274,7 @@ const DrowdownFilters = () => {
|
||||||
align="start"
|
align="start"
|
||||||
className="flex w-60 max-w-3xl flex-col gap-2 p-4"
|
className="flex w-60 max-w-3xl flex-col gap-2 p-4"
|
||||||
>
|
>
|
||||||
<SelectFilter
|
<Filters />
|
||||||
defaultValue={""}
|
|
||||||
handleReset={async () => {
|
|
||||||
await setTipo("");
|
|
||||||
}}
|
|
||||||
label={t.annunci.fs_tipologia.titolo}
|
|
||||||
onValueChange={async (value: string) => {
|
|
||||||
await setTipo(value);
|
|
||||||
}}
|
|
||||||
options={tipologieOptions}
|
|
||||||
placeholder={t.seleziona_placeholder}
|
|
||||||
value={tipo}
|
|
||||||
/>
|
|
||||||
<SelectFilter
|
|
||||||
defaultValue={""}
|
|
||||||
handleReset={async () => {
|
|
||||||
await setComune(null);
|
|
||||||
}}
|
|
||||||
label={t.annunci.comune}
|
|
||||||
onValueChange={async (value: string) => {
|
|
||||||
await setComune(value);
|
|
||||||
}}
|
|
||||||
options={comuniOptions}
|
|
||||||
placeholder={t.seleziona_placeholder}
|
|
||||||
value={comune}
|
|
||||||
/>
|
|
||||||
<SelectFilter
|
|
||||||
defaultValue={""}
|
|
||||||
handleReset={async () => {
|
|
||||||
await setConsegna(null);
|
|
||||||
}}
|
|
||||||
label={t.annunci.consegna}
|
|
||||||
onValueChange={async (value: string) => {
|
|
||||||
if (value === "") {
|
|
||||||
await setConsegna(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (t.preferenze.mesi.includes(value)) {
|
|
||||||
await setConsegna(t.preferenze.mesi.indexOf(value));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.warn("Value not found in preferenze.mesi");
|
|
||||||
await setConsegna(null);
|
|
||||||
}}
|
|
||||||
options={mappedConsegnaOptions}
|
|
||||||
placeholder={t.seleziona_placeholder}
|
|
||||||
value={consegna ? t.preferenze.mesi[consegna] || "" : ""}
|
|
||||||
/>
|
|
||||||
<SelectFilter
|
|
||||||
defaultValue={sort}
|
|
||||||
handleReset={async () => {
|
|
||||||
await setSort("Recenti");
|
|
||||||
}}
|
|
||||||
label={t.ordina_per}
|
|
||||||
onValueChange={async (value: SortTypes) => {
|
|
||||||
await setSort(value);
|
|
||||||
}}
|
|
||||||
options={["Recenti", "Prezzo", "Consegna", "Mq"]}
|
|
||||||
placeholder={t.seleziona_placeholder}
|
|
||||||
value={sort}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className="flex flex-row gap-2"
|
|
||||||
disabled={nFiltri === 0}
|
|
||||||
onClick={ResetParams}
|
|
||||||
variant="destructive"
|
|
||||||
>
|
|
||||||
<FilterX className="size-5" /> {t.annunci.filtri_reset}
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -369,7 +285,7 @@ const RicercaHeader = () => {
|
||||||
const { map, setMap } = useRicerca();
|
const { map, setMap } = useRicerca();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-7xl space-y-5">
|
<div className="mx-auto w-full max-w-9xl space-y-5">
|
||||||
<div className="flex w-full flex-col-reverse gap-4 sm:flex-row sm:gap-2">
|
<div className="flex w-full flex-col-reverse gap-4 sm:flex-row sm:gap-2">
|
||||||
<div className="flex w-full gap-2 sm:w-auto">
|
<div className="flex w-full gap-2 sm:w-auto">
|
||||||
<DrowdownFilters />
|
<DrowdownFilters />
|
||||||
|
|
@ -417,17 +333,20 @@ const AnnunciList = () => {
|
||||||
sort,
|
sort,
|
||||||
map,
|
map,
|
||||||
page,
|
page,
|
||||||
setPage,
|
|
||||||
isLoading: ricercaLoading,
|
isLoading: ricercaLoading,
|
||||||
|
annunciNumber,
|
||||||
} = useRicerca();
|
} = useRicerca();
|
||||||
|
|
||||||
const { data, status, isPlaceholderData } =
|
const {
|
||||||
api.annunci.getWithCursor.useQuery(
|
data,
|
||||||
|
status,
|
||||||
|
//isPlaceholderData
|
||||||
|
} = api.annunci.getWithCursor.useQuery(
|
||||||
{
|
{
|
||||||
comune: comune || undefined,
|
comune: comune || undefined,
|
||||||
consegna: consegna || undefined,
|
consegna: consegna || undefined,
|
||||||
page,
|
page,
|
||||||
pageSize: 6,
|
pageSize: annunciNumber,
|
||||||
sort: sort || undefined,
|
sort: sort || undefined,
|
||||||
tipologia: tipo || undefined,
|
tipologia: tipo || undefined,
|
||||||
},
|
},
|
||||||
|
|
@ -437,6 +356,10 @@ const AnnunciList = () => {
|
||||||
staleTime: 5000,
|
staleTime: 5000,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
DISABILITATO FUNZIONALITA PAGING E PAGE SIZE
|
||||||
|
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const prefetchData = async () => {
|
const prefetchData = async () => {
|
||||||
|
|
@ -445,7 +368,7 @@ const AnnunciList = () => {
|
||||||
comune: comune || undefined,
|
comune: comune || undefined,
|
||||||
consegna: consegna || undefined,
|
consegna: consegna || undefined,
|
||||||
page: page + 1,
|
page: page + 1,
|
||||||
pageSize: 6,
|
pageSize: annunciNumber,
|
||||||
sort: sort || undefined,
|
sort: sort || undefined,
|
||||||
tipologia: tipo || undefined,
|
tipologia: tipo || undefined,
|
||||||
});
|
});
|
||||||
|
|
@ -454,7 +377,7 @@ const AnnunciList = () => {
|
||||||
prefetchData().catch((err) => {
|
prefetchData().catch((err) => {
|
||||||
console.error("Error prefetching next page:", err);
|
console.error("Error prefetching next page:", err);
|
||||||
});
|
});
|
||||||
}, [data, isPlaceholderData, page]);
|
}, [data, isPlaceholderData, page, annunciNumber]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Reset to page 0 if current page has no data or less than page size
|
// 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) {
|
if (data.annunci.length === 0 && page !== 0) {
|
||||||
await setPage(0);
|
await setPage(0);
|
||||||
}
|
}
|
||||||
if (data.annunci.length < 6 || !data.hasMore) {
|
|
||||||
await setPage(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
checkPage().catch((err) => {
|
checkPage().catch((err) => {
|
||||||
console.error("Error checking data for page reset:", 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 <LoadingPage />;
|
if (ricercaLoading) return <LoadingPage />;
|
||||||
if (status === "error") return <Status500 />;
|
if (status === "error") return <Status500 />;
|
||||||
|
|
||||||
|
|
@ -481,14 +417,34 @@ const AnnunciList = () => {
|
||||||
{status === "pending" ? (
|
{status === "pending" ? (
|
||||||
<LoadingPage />
|
<LoadingPage />
|
||||||
) : (
|
) : (
|
||||||
<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">
|
<div className="relative z-0 mx-auto grid max-w-8xl grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 2xl:w-7xl 2xl:grid-cols-4">
|
||||||
{data.annunci.map((annuncio) => (
|
{data.annunci.map((annuncio, idx) => (
|
||||||
<CardAnnuncio key={annuncio.codice} {...annuncio} />
|
<LazyCardAnnuncio
|
||||||
|
key={annuncio.codice}
|
||||||
|
{...annuncio}
|
||||||
|
eager={idx <= 9}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-center gap-2">
|
{/* <div className="relative flex items-center justify-center gap-2">
|
||||||
|
<div className="absolute left-0 flex items-center gap-2">
|
||||||
|
<span className="text-muted-foreground text-sm">
|
||||||
|
annunci per pagina:
|
||||||
|
</span>
|
||||||
|
<NumberInput
|
||||||
|
allowNegative={false}
|
||||||
|
className="w-16 bg-white"
|
||||||
|
min={0}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
if (val !== undefined) {
|
||||||
|
setDebouncedAnnunciNumber(val);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={debouncedAnnunciNumber}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="flex w-24 items-center justify-end gap-2">
|
<div className="flex w-24 items-center justify-end gap-2">
|
||||||
{page >= 2 && (
|
{page >= 2 && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -515,11 +471,9 @@ const AnnunciList = () => {
|
||||||
<ChevronLeft className="h-4 w-4" />
|
<ChevronLeft className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx-4 w-12 rounded-md bg-white py-2 text-center font-medium text-lg">
|
<div className="mx-4 w-12 rounded-md bg-white py-2 text-center font-medium text-lg">
|
||||||
{page + 1}
|
{page + 1}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex w-24 items-center justify-start gap-2">
|
<div className="flex w-24 items-center justify-start gap-2">
|
||||||
<Button
|
<Button
|
||||||
aria-label="Next page"
|
aria-label="Next page"
|
||||||
|
|
@ -535,7 +489,7 @@ const AnnunciList = () => {
|
||||||
<ChevronRight className="h-4 w-4" />
|
<ChevronRight className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -571,7 +525,11 @@ export const SelectFilter = <T extends string>({
|
||||||
{label}
|
{label}
|
||||||
</Label>
|
</Label>
|
||||||
{value !== defaultValue && (
|
{value !== defaultValue && (
|
||||||
<button onClick={handleReset} type="button">
|
<button
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={handleReset}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
<RotateCcwIcon className="size-4" />
|
<RotateCcwIcon className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ const OnboardUser: NextPageWithLayout<OnboardUserProps> = ({
|
||||||
return <Status500 />;
|
return <Status500 />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl grow space-y-4">
|
<div className="mx-auto max-w-8xl grow space-y-4">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<h3 className="font-bold text-2xl text-primary">
|
<h3 className="font-bold text-2xl text-primary">
|
||||||
Onboarding Utente (Versione Admin)
|
Onboarding Utente (Versione Admin)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const ServizioUser: NextPageWithLayout<ServizioUserProps> = ({
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [editData, setEditData] = useState<Servizio | undefined>(undefined);
|
const [editData, setEditData] = useState<Servizio | undefined>(undefined);
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl grow space-y-4">
|
<div className="mx-auto max-w-8xl grow space-y-4">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<h3 className="font-bold text-2xl">Servizi Utente</h3>
|
<h3 className="font-bold text-2xl">Servizi Utente</h3>
|
||||||
<NewServizioModal
|
<NewServizioModal
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const Comunicazioni: NextPageWithLayout = () => {
|
||||||
<meta content={t.heads.area_riservata_description} name="description" />
|
<meta content={t.heads.area_riservata_description} name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<div className="mx-auto max-w-7xl grow p-4">
|
<div className="mx-auto max-w-8xl grow p-4">
|
||||||
<div className="p-1">
|
<div className="p-1">
|
||||||
<h1 className="font-bold text-2xl text-primary">Comunicazioni</h1>
|
<h1 className="font-bold text-2xl text-primary">Comunicazioni</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const ChiSiamo: NextPage = () => {
|
||||||
<title>{t.heads.chi_siamo_titolo}</title>
|
<title>{t.heads.chi_siamo_titolo}</title>
|
||||||
<meta content={t.heads.main_description} name="description" />
|
<meta content={t.heads.main_description} name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
<main className="mx-auto w-full max-w-7xl space-y-5 px-2 py-5 md:px-8">
|
<main className="mx-auto w-full max-w-8xl space-y-5 px-2 py-5 md:px-8">
|
||||||
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
||||||
{t.chi_siamo.title}
|
{t.chi_siamo.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ const Contatto: NextPage = () => {
|
||||||
<title>{t.heads.contatti_titolo}</title>
|
<title>{t.heads.contatti_titolo}</title>
|
||||||
<meta content={t.heads.main_description} name="description" />
|
<meta content={t.heads.main_description} name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
<main className="mx-auto w-full max-w-7xl px-2 py-5 md:px-8">
|
<main className="mx-auto w-full max-w-8xl px-2 py-5 md:px-8">
|
||||||
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
||||||
{t.contatti}
|
{t.contatti}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const Guida: NextPage = () => {
|
||||||
<title>{t.heads.guida_titolo}</title>
|
<title>{t.heads.guida_titolo}</title>
|
||||||
<meta content={t.heads.main_description} name="description" />
|
<meta content={t.heads.main_description} name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
<main className="mx-auto w-full max-w-7xl px-2 py-5 md:px-8">
|
<main className="mx-auto w-full max-w-8xl px-2 py-5 md:px-8">
|
||||||
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
||||||
{t.guida}
|
{t.guida}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ const Home: NextPage = () => {
|
||||||
<meta content="Infoalloggi.it" property="og:site_name" />
|
<meta content="Infoalloggi.it" property="og:site_name" />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="mx-5 mt-0 mb-12 sm:mt-[2rem]">
|
<div className="mx-5 mt-0 mb-12 sm:mt-[2rem]">
|
||||||
<main className="mx-auto mb-4 max-w-7xl space-y-5 lg:mb-20">
|
<main className="mx-auto mb-4 max-w-8xl space-y-5 lg:mb-20">
|
||||||
<div className="flex flex-col items-center justify-center lg:flex-row lg:justify-around">
|
<div className="flex flex-col items-center justify-center lg:flex-row lg:justify-around">
|
||||||
<div className="-mt-2 xs:mt-5 flex h-[19rem] max-w-full items-center justify-center min-[22rem]:h-[23rem] min-[25rem]:h-[26rem]">
|
<div className="-mt-2 xs:mt-5 flex h-[19rem] max-w-full items-center justify-center min-[22rem]:h-[23rem] min-[25rem]:h-[26rem]">
|
||||||
<div className="flex scale-50 items-center justify-center drop-shadow-xl sm:scale-100 md:max-h-96 min-[20rem]:scale-[0.6] min-[22rem]:scale-[0.7] min-[25rem]:scale-[0.8]">
|
<div className="flex scale-50 items-center justify-center drop-shadow-xl sm:scale-100 md:max-h-96 min-[20rem]:scale-[0.6] min-[22rem]:scale-[0.7] min-[25rem]:scale-[0.8]">
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ const Prezzi: NextPage = () => {
|
||||||
<title>{t.heads.prezzi_titolo}</title>
|
<title>{t.heads.prezzi_titolo}</title>
|
||||||
<meta content={t.heads.prezzi_description} name="description" />
|
<meta content={t.heads.prezzi_description} name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
<section className="mx-auto max-w-7xl px-2 py-5 md:px-8">
|
<section className="mx-auto max-w-8xl px-2 py-5 md:px-8">
|
||||||
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
||||||
{t.prezzi_titolo}
|
{t.prezzi_titolo}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const Proprietari: NextPage = () => {
|
||||||
<title>{t.proprietari.head}</title>
|
<title>{t.proprietari.head}</title>
|
||||||
<meta content={t.proprietari.description} name="description" />
|
<meta content={t.proprietari.description} name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
<main className="mx-auto w-full max-w-7xl space-y-5 px-2 py-5 md:px-8">
|
<main className="mx-auto w-full max-w-8xl space-y-5 px-2 py-5 md:px-8">
|
||||||
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
<div className="inline-block rounded-md bg-accent px-3 py-1 text-primary text-sm outline outline-neutral-500">
|
||||||
{t.proprietari.titolo}
|
{t.proprietari.titolo}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,10 @@ type RicercaContextType = {
|
||||||
value: number | ((old: number | null) => number | null) | null,
|
value: number | ((old: number | null) => number | null) | null,
|
||||||
) => Promise<URLSearchParams>;
|
) => Promise<URLSearchParams>;
|
||||||
ResetParams: () => Promise<void>;
|
ResetParams: () => Promise<void>;
|
||||||
|
annunciNumber: number;
|
||||||
|
setAnnunciNumber: (
|
||||||
|
value: number | ((old: number) => number | null) | null,
|
||||||
|
) => Promise<URLSearchParams>;
|
||||||
nFiltri: number;
|
nFiltri: number;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
};
|
};
|
||||||
|
|
@ -104,6 +108,10 @@ export const RicercaProvider: FC<{
|
||||||
"p",
|
"p",
|
||||||
parseAsInteger.withDefault(0).withOptions({ clearOnDefault: true }),
|
parseAsInteger.withDefault(0).withOptions({ clearOnDefault: true }),
|
||||||
);
|
);
|
||||||
|
const [annunciNumber, setAnnunciNumber] = useQueryState(
|
||||||
|
"n",
|
||||||
|
parseAsInteger.withDefault(1000).withOptions({ startTransition }),
|
||||||
|
);
|
||||||
|
|
||||||
const tipologieOptions = useMemo(() => {
|
const tipologieOptions = useMemo(() => {
|
||||||
const set = new Set<string>();
|
const set = new Set<string>();
|
||||||
|
|
@ -153,7 +161,7 @@ export const RicercaProvider: FC<{
|
||||||
await setComune("");
|
await setComune("");
|
||||||
await setConsegna(null);
|
await setConsegna(null);
|
||||||
await setSort("Recenti");
|
await setSort("Recenti");
|
||||||
await setPage(0);
|
//await setPage(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const nFiltri =
|
const nFiltri =
|
||||||
|
|
@ -187,6 +195,9 @@ export const RicercaProvider: FC<{
|
||||||
sortOptions: sortOptions,
|
sortOptions: sortOptions,
|
||||||
tipo,
|
tipo,
|
||||||
tipologieOptions,
|
tipologieOptions,
|
||||||
|
|
||||||
|
annunciNumber,
|
||||||
|
setAnnunciNumber,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -366,6 +366,7 @@ export const getCursor_AnnunciHandler = async ({
|
||||||
.limit(pageSize + 1)
|
.limit(pageSize + 1)
|
||||||
.offset(page * pageSize)
|
.offset(page * pageSize)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
const hasMore = annunci.length > pageSize;
|
const hasMore = annunci.length > pageSize;
|
||||||
if (annunci.length > pageSize) {
|
if (annunci.length > pageSize) {
|
||||||
annunci.pop(); // Remove the last item if it exceeds the page size
|
annunci.pop(); // Remove the last item if it exceeds the page size
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
--breakpoint-3xl: 120rem;
|
--breakpoint-3xl: 120rem;
|
||||||
--breakpoint-4xl: 160rem;
|
--breakpoint-4xl: 160rem;
|
||||||
--breakpoint-5xl: 192rem;
|
--breakpoint-5xl: 192rem;
|
||||||
|
--container-8xl: 88rem;
|
||||||
|
--container-9xl: 96rem;
|
||||||
|
--container-10xl: 104rem;
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
|
|
@ -160,7 +162,7 @@
|
||||||
:root {
|
:root {
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
--background: oklch(1 0 0);
|
--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);
|
--foreground: oklch(0.145 0 0);
|
||||||
--card: oklch(1 0 0);
|
--card: oklch(1 0 0);
|
||||||
--card-foreground: oklch(0.145 0 0);
|
--card-foreground: oklch(0.145 0 0);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue