search pages with filters in process
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import RestartIcon from "./icons/RestartIcon";
|
||||
import Button from "./ui/Button";
|
||||
import MultiRangeSlider from "./ui/MultiRangeSlider";
|
||||
import { api } from "../api/ky";
|
||||
import { RefObject, useEffect, useState } from "react";
|
||||
import ProjectFilter from "./ProjectFilter";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import UnitTypesFilter from "./UnitTypesFilter";
|
||||
import { projects } from "../data/projects";
|
||||
import { useDebounce } from "../hooks/useDebounce";
|
||||
|
||||
interface Filters {
|
||||
unitTypes: string[];
|
||||
views: string[];
|
||||
area: [number, number];
|
||||
cost: [number, number];
|
||||
floor: [number, number];
|
||||
}
|
||||
|
||||
function SearchFilters({
|
||||
inModal = false,
|
||||
ref,
|
||||
}: {
|
||||
inModal?: boolean;
|
||||
ref: RefObject<HTMLDivElement | null>;
|
||||
}) {
|
||||
const [project, setProject] = useState<string>();
|
||||
const [unitTypes, setUnitTypes] = useState<string[]>([]);
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data: filters } = useQuery({
|
||||
queryKey: ["filters", project],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(`units/filters?${project ? `project=${project}` : ""}`)
|
||||
.json<Filters>(),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const projectValue = searchParams.get("project");
|
||||
const unitTypesValue = searchParams.getAll("unitTypes");
|
||||
if (projectValue) setProject(projectValue);
|
||||
if (unitTypesValue) setUnitTypes(unitTypesValue);
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (filters) {
|
||||
setCurrentMinCost(filters.cost[0]);
|
||||
setCurrentMaxCost(filters.cost[1]);
|
||||
setCurrentMinArea(filters.area[0]);
|
||||
setCurrentMaxArea(filters.area[1]);
|
||||
setCurrentMinFloor(filters.floor[0]);
|
||||
setCurrentMaxFloor(filters.floor[1]);
|
||||
}
|
||||
}, [filters]);
|
||||
|
||||
const [currentMinCost, setCurrentMinCost] = useState<number>();
|
||||
const [currentMaxCost, setCurrentMaxCost] = useState<number>();
|
||||
|
||||
const [currentMinArea, setCurrentMinArea] = useState<number>();
|
||||
const [currentMaxArea, setCurrentMaxArea] = useState<number>();
|
||||
|
||||
const [currentMinFloor, setCurrentMinFloor] = useState<number>();
|
||||
const [currentMaxFloor, setCurrentMaxFloor] = useState<number>();
|
||||
|
||||
function resetFilters() {
|
||||
setProject(undefined);
|
||||
setUnitTypes([]);
|
||||
if (filters) {
|
||||
setCurrentMinCost(filters.cost[0]);
|
||||
setCurrentMaxCost(filters.cost[1]);
|
||||
setCurrentMinArea(filters.area[0]);
|
||||
setCurrentMaxArea(filters.area[1]);
|
||||
setCurrentMinFloor(filters.floor[0]);
|
||||
setCurrentMaxFloor(filters.floor[1]);
|
||||
}
|
||||
navigate("/search");
|
||||
}
|
||||
|
||||
const debouncedMinCost = useDebounce(currentMinCost, 500);
|
||||
const debouncedMaxCost = useDebounce(currentMaxCost, 500);
|
||||
|
||||
const debouncedMinArea = useDebounce(currentMinArea, 500);
|
||||
const debouncedMaxArea = useDebounce(currentMaxArea, 500);
|
||||
|
||||
const debouncedMinFloor = useDebounce(currentMinFloor, 500);
|
||||
const debouncedMaxFloor = useDebounce(currentMaxFloor, 500);
|
||||
|
||||
const { data: count } = useQuery({
|
||||
queryKey: [
|
||||
"units",
|
||||
"count",
|
||||
project,
|
||||
unitTypes,
|
||||
debouncedMinCost,
|
||||
debouncedMaxCost,
|
||||
debouncedMinArea,
|
||||
debouncedMaxArea,
|
||||
debouncedMinFloor,
|
||||
debouncedMaxFloor,
|
||||
],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(
|
||||
`units/count?${project ? `project=${project}` : ""}${unitTypes
|
||||
.map((unitType) => `&unitTypes=${unitType}`)
|
||||
.join("")}${
|
||||
debouncedMinCost && debouncedMaxCost
|
||||
? `&cost=${Math.round(debouncedMinCost)},${Math.round(
|
||||
debouncedMaxCost
|
||||
)}`
|
||||
: ""
|
||||
}${
|
||||
debouncedMinArea && debouncedMaxArea
|
||||
? `&area=${Math.round(debouncedMinArea)},${Math.round(
|
||||
debouncedMaxArea
|
||||
)}`
|
||||
: ""
|
||||
}${
|
||||
debouncedMinFloor && debouncedMaxFloor
|
||||
? `&floor=${Math.round(debouncedMinFloor)},${Math.round(
|
||||
debouncedMaxFloor
|
||||
)}`
|
||||
: ""
|
||||
}`
|
||||
)
|
||||
.json<number>(),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedMinCost && debouncedMaxCost)
|
||||
setSearchParams((prev) => {
|
||||
prev.set(
|
||||
"cost",
|
||||
`${Math.round(debouncedMinCost)},${Math.round(debouncedMaxCost)}`
|
||||
);
|
||||
return prev;
|
||||
});
|
||||
|
||||
if (debouncedMinArea && debouncedMaxArea)
|
||||
setSearchParams((prev) => {
|
||||
prev.set(
|
||||
"area",
|
||||
`${Math.round(debouncedMinArea)},${Math.round(debouncedMaxArea)}`
|
||||
);
|
||||
return prev;
|
||||
});
|
||||
|
||||
if (debouncedMinFloor && debouncedMaxFloor)
|
||||
setSearchParams((prev) => {
|
||||
prev.set(
|
||||
"floor",
|
||||
`${Math.round(debouncedMinFloor)},${Math.round(debouncedMaxFloor)}`
|
||||
);
|
||||
return prev;
|
||||
});
|
||||
}, [
|
||||
debouncedMinCost,
|
||||
debouncedMaxCost,
|
||||
debouncedMinArea,
|
||||
debouncedMaxArea,
|
||||
debouncedMinFloor,
|
||||
debouncedMaxFloor,
|
||||
setSearchParams,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="2xl:p-[2.222vw] md:max-2xl:p-6 p-4 2xl:-mx-[2.222vw] md:max-2xl:-mx-6 -mx-4 bg-white 2xl:rounded-b-[1.667vw] rounded-b-3xl 2xl:space-y-[2.222vw] md:max-2xl:space-y-8 space-y-4"
|
||||
>
|
||||
<div className="2xl:space-y-[1.111vw] space-y-4">
|
||||
<p className="2xl:text-[2.222vw] md:max-2xl:text-[32px] text-2xl font-semibold leading-[135%]">
|
||||
Search
|
||||
</p>
|
||||
<div className="flex 2xl:gap-[0.556vw] gap-2">
|
||||
{projects.map((project) => (
|
||||
<ProjectFilter
|
||||
key={project.title}
|
||||
title={project.title}
|
||||
img={project.img}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<hr className="2xl:h-[0.069vw] h-px border-[#E2E2DC]" />
|
||||
{filters && (
|
||||
<>
|
||||
<div className="2xl:space-y-[0.556vw] space-y-2">
|
||||
<p>Apartment type</p>
|
||||
<div className="flex 2xl:gap-[0.556vw] gap-2">
|
||||
{filters.unitTypes.map((unitType) => (
|
||||
<UnitTypesFilter key={unitType} title={unitType} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid 2xl:grid-cols-4 md:max-2xl:grid-cols-2 md:max-2xl:grid-rows-2 2xl:gap-[1.111vw] gap-4">
|
||||
<MultiRangeSlider
|
||||
min={filters?.cost[0]}
|
||||
max={filters?.cost[1]}
|
||||
currentMin={currentMinCost!}
|
||||
currentMax={currentMaxCost!}
|
||||
step={1}
|
||||
onChangeMin={setCurrentMinCost}
|
||||
onChangeMax={setCurrentMaxCost}
|
||||
label="Cost, AED"
|
||||
/>
|
||||
<MultiRangeSlider
|
||||
min={filters?.floor[0]}
|
||||
max={filters?.floor[1]}
|
||||
currentMin={currentMinFloor!}
|
||||
currentMax={currentMaxFloor!}
|
||||
step={1}
|
||||
onChangeMin={setCurrentMinFloor}
|
||||
onChangeMax={setCurrentMaxFloor}
|
||||
label="Floor"
|
||||
/>
|
||||
<MultiRangeSlider
|
||||
min={filters?.area[0]}
|
||||
max={filters?.area[1]}
|
||||
currentMin={currentMinArea!}
|
||||
currentMax={currentMaxArea!}
|
||||
step={1}
|
||||
onChangeMin={setCurrentMinArea}
|
||||
onChangeMax={setCurrentMaxArea}
|
||||
label="Total Area, Sqft"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center 2xl:gap-[1.111vw] md:max-2xl:gap-4">
|
||||
{inModal ? (
|
||||
<Button>Show {count} apartments</Button>
|
||||
) : (
|
||||
<p className="text-[#00BED7] text-s">{count} Apartments found</p>
|
||||
)}
|
||||
<Button variant="secondary" onClick={resetFilters}>
|
||||
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5 text-[#0D1922]/70">
|
||||
<RestartIcon />
|
||||
</span>
|
||||
<p className="text-s">Reset filters</p>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchFilters;
|
||||
Reference in New Issue
Block a user