feat: refactor Ricerca component layout and enhance filter functionality in DrowdownFilters
This commit is contained in:
parent
1887f1ffec
commit
24253f0cfb
1 changed files with 219 additions and 103 deletions
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from "lucide-react";
|
||||
import type { GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { AccordionComp } from "~/components/accordionComp";
|
||||
import { MapSection } from "~/components/annunci_map";
|
||||
import { CTA_TipologiaModal } from "~/components/annunci_tutorial";
|
||||
|
|
@ -125,19 +125,117 @@ const Ricerca = () => {
|
|||
}, [page]);
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<RicercaFilters />
|
||||
|
||||
{map ? <MapSection /> : <AnnunciList />}
|
||||
<div className="mx-auto w-full max-w-7xl space-y-5 xl:w-[100rem]">
|
||||
<RicercaHeader />
|
||||
<div className="flex w-full gap-2">
|
||||
<RicercaSideFilters />
|
||||
<div className="flex-1">{map ? <MapSection /> : <AnnunciList />}</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 {
|
||||
map,
|
||||
setMap,
|
||||
tipo,
|
||||
setTipo,
|
||||
comune,
|
||||
|
|
@ -160,103 +258,121 @@ const RicercaFilters = () => {
|
|||
[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 (
|
||||
<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 gap-2 sm:w-auto">
|
||||
<div className="flex w-full sm:w-auto">
|
||||
<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>
|
||||
<DrowdownFilters />
|
||||
<Button
|
||||
className="flex w-fit flex-row gap-2"
|
||||
onClick={async () => {
|
||||
|
|
@ -361,7 +477,7 @@ const AnnunciList = () => {
|
|||
if (status === "error") return <Status500 />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-4">
|
||||
{status === "pending" ? (
|
||||
<LoadingPage />
|
||||
) : (
|
||||
|
|
@ -420,7 +536,7 @@ const AnnunciList = () => {
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue