This commit is contained in:
2025-04-29 12:58:18 +05:00
parent f82b114db1
commit 9a7e52b95f
9 changed files with 75 additions and 69 deletions
+5
View File
@@ -6,6 +6,7 @@
"dependencies": {
"@tailwindcss/vite": "^4.1.3",
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-query-devtools": "^5.74.7",
"@tweenjs/tween.js": "^25.0.0",
"@uidotdev/usehooks": "^2.4.1",
"clsx": "^2.1.1",
@@ -218,8 +219,12 @@
"@tanstack/query-core": ["@tanstack/query-core@5.74.4", "", {}, "sha512-YuG0A0+3i9b2Gfo9fkmNnkUWh5+5cFhWBN0pJAHkHilTx6A0nv8kepkk4T4GRt4e5ahbtFj2eTtkiPcVU1xO4A=="],
"@tanstack/query-devtools": ["@tanstack/query-devtools@5.74.7", "", {}, "sha512-nSNlfuGdnHf4yB0S+BoNYOE1o3oAH093weAYZolIHfS2stulyA/gWfSk/9H4ZFk5mAAHb5vNqAeJOmbdcGPEQw=="],
"@tanstack/react-query": ["@tanstack/react-query@5.74.4", "", { "dependencies": { "@tanstack/query-core": "5.74.4" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-mAbxw60d4ffQ4qmRYfkO1xzRBPUEf/72Dgo3qqea0J66nIKuDTLEqQt0ku++SDFlMGMnB6uKDnEG1xD/TDse4Q=="],
"@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.74.7", "", { "dependencies": { "@tanstack/query-devtools": "5.74.7" }, "peerDependencies": { "@tanstack/react-query": "^5.74.7", "react": "^18 || ^19" } }, "sha512-j60esTQF+ES0x52kQUYOX0Z8AJUcqCGANj6GaOf8J3YQz2bZPB1imLSw4SFeM3Ozv8uO/X/Dmh3IT1z+y57ZLQ=="],
"@tweenjs/tween.js": ["@tweenjs/tween.js@25.0.0", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="],
"@types/bun": ["@types/bun@1.2.9", "", { "dependencies": { "bun-types": "1.2.9" } }, "sha512-epShhLGQYc4Bv/aceHbmBhOz1XgUnuTZgcxjxk+WXwNyDXavv5QHD1QEFV0FwbTSQtNq6g4ZcV6y0vZakTjswg=="],
+1
View File
@@ -12,6 +12,7 @@
"dependencies": {
"@tailwindcss/vite": "^4.1.3",
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-query-devtools": "^5.74.7",
"@tweenjs/tween.js": "^25.0.0",
"@uidotdev/usehooks": "^2.4.1",
"clsx": "^2.1.1",
+7 -5
View File
@@ -7,15 +7,15 @@ import Select from "./ui/Select";
function ProjectSelect({
projects,
onSelect,
defaultProject,
}: {
projects: Project[];
onSelect: (project: Project) => void;
defaultProject: Project;
}) {
const [selectedProject, setSelectedProject] = useState(projects[0]);
const [selectedProject, setSelectedProject] = useState(defaultProject);
useEffect(() => {
onSelect(selectedProject);
}, [selectedProject]);
useEffect(() => onSelect(selectedProject), [selectedProject]);
return (
<>
@@ -45,9 +45,11 @@ function ProjectSelect({
options={projects.map((project) => project.title)}
onSelect={(option) =>
setSelectedProject(
projects.find((project) => project.title === option) || projects[0]
projects.find((project) => project.title === option) ||
defaultProject
)
}
defaultOption={defaultProject.title}
className="md:hidden"
/>
</>
+13 -49
View File
@@ -24,12 +24,14 @@ export interface Filters {
function SearchFilters({
inModal = false,
filters,
ref,
}: {
inModal?: boolean;
filters?: Filters;
ref?: RefObject<HTMLDivElement | null>;
}) {
const [project, setProject] = useState<string>(projects[0].title);
const [project, setProject] = useState<string>();
const [unitTypes, setUnitTypes] = useState<string[]>([]);
const [view, setView] = useState<string>("Any view");
@@ -37,19 +39,10 @@ function SearchFilters({
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);
const viewValue = searchParams.get("view");
if (viewValue) setView(viewValue);
}, [searchParams]);
@@ -110,6 +103,7 @@ function SearchFilters({
debouncedMinFloor,
debouncedMaxFloor,
],
enabled: !!project,
queryFn: () =>
api
.get(
@@ -138,43 +132,6 @@ function SearchFilters({
.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,
]);
function handleProjectSelect(project: Project) {
setProject(project.title);
setSearchParams((prev) => {
@@ -208,7 +165,13 @@ function SearchFilters({
<p className="2xl:text-[2.222vw] md:max-2xl:text-[32px] text-2xl font-semibold leading-[135%]">
Search
</p>
<ProjectSelect projects={projects} onSelect={handleProjectSelect} />
{project && (
<ProjectSelect
projects={projects}
onSelect={handleProjectSelect}
defaultProject={projects.find(({ title }) => title === project)!}
/>
)}
</div>
<hr className="2xl:h-[0.069vw] h-px border-[#E2E2DC]" />
{filters && (
@@ -255,6 +218,7 @@ function SearchFilters({
label="View"
options={["Any view", ...filters.views]}
onSelect={handleViewSelect}
defaultOption={view}
/>
</div>
<div className="flex items-center 2xl:gap-[1.111vw] md:max-2xl:gap-4">
-1
View File
@@ -18,7 +18,6 @@ function UnitTypesSelect({
useEffect(() => {
const unitTypesValue = searchParams.getAll("unitTypes");
if (unitTypesValue) setSelectedUnitTypes(unitTypesValue);
}, [searchParams]);
+6 -4
View File
@@ -6,24 +6,26 @@ import { useClickAway } from "@uidotdev/usehooks";
import clsx from "clsx";
import ChevronDownIcon from "../icons/ChevronDownIcon";
import CheckIcon from "../icons/CheckIcon";
function Select({
options,
onSelect,
className = "",
label = "",
defaultOption = "",
}: {
options: string[];
onSelect: (option: string) => void;
defaultOption: string;
className?: string;
label?: string;
}) {
const [isShow, setIsShow] = useState(false);
const [selectedOption, setSelectedOption] = useState(options[0]);
const [selectedOption, setSelectedOption] = useState(defaultOption);
const ref = useClickAway<HTMLDivElement>(() => setIsShow(false));
useEffect(() => {
onSelect(selectedOption);
}, [selectedOption]);
useEffect(() => onSelect(selectedOption), [selectedOption]);
return (
<div ref={ref} className={clsx("relative", className)}>
+3 -1
View File
@@ -1,4 +1,6 @@
export const projects = [
import Project from "../types/Project";
export const projects: Project[] = [
{
title: "Rove Home Marasi Drive",
img: "/images/search/rove_home_marasi_drive.png",
+2
View File
@@ -1,4 +1,5 @@
import "./index.css";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { QueryClientProvider } from "@tanstack/react-query";
import { createRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router";
@@ -60,6 +61,7 @@ createRoot(document.getElementById("root")!).render(
<QueryClientProvider client={queryClient}>
<RouterProvider router={route} />
<ModalContainer />
<ReactQueryDevtools />
</QueryClientProvider>
</>
);
+38 -9
View File
@@ -1,9 +1,9 @@
import { useInfiniteQuery } from "@tanstack/react-query";
import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
import { api } from "../api/ky";
import { IUnit } from "../types/IUnit";
import UnitCard from "../components/UnitCard";
import { useEffect, useRef, useState } from "react";
import SearchFilters from "../components/SearchFilters";
import SearchFilters, { Filters } from "../components/SearchFilters";
import { useNavigate, useSearchParams } from "react-router";
import Button from "../components/ui/Button";
import FiltersIcon from "../components/icons/FiltersIcon";
@@ -17,12 +17,37 @@ function SearchPage() {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const project = searchParams.get("project");
const unitTypes = searchParams.getAll("unitTypes");
const cost = searchParams.getAll("cost");
const floor = searchParams.getAll("floor");
const area = searchParams.getAll("area");
const view = searchParams.get("view");
// const project = searchParams.get("project");
// const unitTypes = searchParams.getAll("unitTypes");
// const cost = searchParams.getAll("cost");
// const floor = searchParams.getAll("floor");
// const area = searchParams.getAll("area");
// const view = searchParams.get("view");
const [project, setProject] = useState<string>();
const [unitTypes, setUnitTypes] = useState<string[]>([]);
const [cost, setCost] = useState<string[]>([]);
const [floor, setFloor] = useState<string[]>([]);
const [area, setArea] = useState<string[]>([]);
const [view, setView] = useState<string>();
const { data: filters } = useQuery({
queryKey: ["filters", project],
enabled: !!project,
queryFn: () =>
api
.get(`units/filters?${project ? `project=${project}` : ""}`)
.json<Filters>(),
});
useEffect(() => {
setProject(searchParams.get("project"));
setUnitTypes(searchParams.getAll("unitTypes"));
setCost(searchParams.getAll("cost"));
setFloor(searchParams.getAll("floor"));
setArea(searchParams.getAll("area"));
setView(searchParams.get("view"));
}, [searchParams]);
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery({
@@ -95,7 +120,11 @@ function SearchPage() {
return (
<div>
<SearchFilters ref={filtersRef} inModal={filtersInModal} />
<SearchFilters
ref={filtersRef}
inModal={filtersInModal}
filters={filters}
/>
<div className="2xl:p-[2.222vw] p-4">
<AnimatePresence mode="wait">
{project && unitTypes && cost && area && floor && (