feat: refactor Ricerca component layout and enhance filter functionality in DrowdownFilters

This commit is contained in:
Marco Pedone 2025-11-11 17:15:29 +01:00
parent 1887f1ffec
commit 24253f0cfb

View file

@ -13,7 +13,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import type { GetServerSideProps } from "next"; import type { GetServerSideProps } from "next";
import Head from "next/head"; import Head from "next/head";
import { useEffect, useMemo, useState } from "react"; 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";
@ -125,19 +125,117 @@ const Ricerca = () => {
}, [page]); }, [page]);
return ( return (
<div className="space-y-5"> <div className="mx-auto w-full max-w-7xl space-y-5 xl:w-[100rem]">
<RicercaFilters /> <RicercaHeader />
<div className="flex w-full gap-2">
{map ? <MapSection /> : <AnnunciList />} <RicercaSideFilters />
<div className="flex-1">{map ? <MapSection /> : <AnnunciList />}</div>
</div>
</div> </div>
); );
}; };
const RicercaFilters = () => { const RicercaSideFilters = () => {
const { t } = useTranslation();
const {
tipo,
setTipo,
comune,
setComune,
consegna,
setConsegna,
sort,
setSort,
comuniOptions,
tipologieOptions,
consegnaOptions,
ResetParams,
nFiltri,
} = useRicerca();
const mappedConsegnaOptions = useMemo(
// biome-ignore lint/style/noNonNullAssertion: <known lenght>
() => consegnaOptions.map((m) => t.preferenze.mesi[m]!).filter(Boolean),
[consegnaOptions, t.preferenze.mesi],
);
return (
<div className="hidden h-fit w-46 flex-col gap-3 rounded-md bg-white p-2 outline-neutral-300 xl:flex">
<SelectFilter
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>
</div>
);
};
const DrowdownFilters = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
map,
setMap,
tipo, tipo,
setTipo, setTipo,
comune, comune,
@ -160,103 +258,121 @@ const RicercaFilters = () => {
[consegnaOptions, t.preferenze.mesi], [consegnaOptions, t.preferenze.mesi],
); );
// Close dropdown when page resizes
const handleResize = useCallback(() => {
setOpen(false);
}, []);
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [handleResize]);
return (
<div className="flex w-full sm:w-auto xl:hidden">
<DropdownMenu onOpenChange={setOpen} open={open}>
<DropdownMenuTrigger asChild>
<Button
className="group flex w-full flex-row gap-2 sm:w-auto"
variant="default"
>
<Badge className="w-fit rounded-md bg-secondary px-1.5 py-0.5 text-secondary-foreground dark:group-hover:bg-transparent">
{nFiltri}
</Badge>
<Filter className="size-5" />{" "}
{nFiltri === 1 ? t.annunci.filtro_attivo : t.annunci.filtri_attivi}
{open ? <ChevronUp /> : <ChevronDown />}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="flex w-60 max-w-3xl flex-col gap-2 p-4"
>
<SelectFilter
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>
</DropdownMenu>
</div>
);
};
const RicercaHeader = () => {
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-7xl 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">
<div className="flex w-full sm:w-auto"> <DrowdownFilters />
<DropdownMenu onOpenChange={setOpen} open={open}>
<DropdownMenuTrigger asChild>
<Button
className="group flex w-full flex-row gap-2 sm:w-auto"
variant="default"
>
<Badge className="w-fit rounded-md bg-secondary px-1.5 py-0.5 text-secondary-foreground dark:group-hover:bg-transparent">
{nFiltri}
</Badge>
<Filter className="size-5" />{" "}
{nFiltri === 1
? t.annunci.filtro_attivo
: t.annunci.filtri_attivi}
{open ? <ChevronUp /> : <ChevronDown />}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="flex w-60 max-w-3xl flex-col gap-2 p-4"
>
<SelectFilter
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>
</DropdownMenu>
</div>
<Button <Button
className="flex w-fit flex-row gap-2" className="flex w-fit flex-row gap-2"
onClick={async () => { onClick={async () => {
@ -361,7 +477,7 @@ const AnnunciList = () => {
if (status === "error") return <Status500 />; if (status === "error") return <Status500 />;
return ( return (
<> <div className="space-y-4">
{status === "pending" ? ( {status === "pending" ? (
<LoadingPage /> <LoadingPage />
) : ( ) : (
@ -420,7 +536,7 @@ const AnnunciList = () => {
</Button> </Button>
</div> </div>
</div> </div>
</> </div>
); );
}; };