search page + sorting starts
This commit is contained in:
+11
-20
@@ -1,46 +1,37 @@
|
||||
import { useState } from "react";
|
||||
import MinusIcon from "./icons/MinusIcon";
|
||||
import SelectedCheckboxIcon from "./icons/SelectedCheckboxIcon";
|
||||
import { ICheckbox } from "../types/checkbox";
|
||||
|
||||
interface CheckboxProps {
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
selected?: boolean;
|
||||
onClick: (id: string) => void;
|
||||
checkbox: ICheckbox;
|
||||
}
|
||||
|
||||
const Checkbox = ({ title, disabled, selected = false }: CheckboxProps) => {
|
||||
const [isSelected, setIsSelected] = useState(selected);
|
||||
|
||||
const handleOnClick = () => {
|
||||
if (!disabled) {
|
||||
setIsSelected((prev) => !prev);
|
||||
}
|
||||
};
|
||||
|
||||
const Checkbox = ({ onClick, checkbox }: CheckboxProps) => {
|
||||
return (
|
||||
<div
|
||||
onClick={handleOnClick}
|
||||
onClick={() => onClick(checkbox.id)}
|
||||
className={`flex justify-between bg-white p-3 rounded-2xl transition-[background] duration-300 ease-in-out select-none ${
|
||||
disabled ? "" : "hover:bg-[#F3F3F2] cursor-pointer"
|
||||
checkbox.disabled ? "" : "hover:bg-[#F3F3F2] cursor-pointer"
|
||||
}`}
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
{disabled ? (
|
||||
{checkbox.disabled ? (
|
||||
<div className="bg-[#E2E2DC] rounded-[4px] w-6 h-6 text-white flex justify-center items-center">
|
||||
<MinusIcon />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={` rounded-[4px] w-6 h-6 flex justify-center items-center ease-in-out duration-300 transition-[background] ${
|
||||
isSelected ? "bg-[#00BED7]" : "bg-[#E2E2DC]"
|
||||
checkbox.selected ? "bg-[#00BED7]" : "bg-[#E2E2DC]"
|
||||
}`}
|
||||
>
|
||||
{isSelected ? <SelectedCheckboxIcon /> : <></>}
|
||||
{checkbox.selected ? <SelectedCheckboxIcon /> : <></>}
|
||||
</div>
|
||||
)}
|
||||
<p className="text-s text-[#73787C]">{title}</p>
|
||||
<p className="text-s text-[#73787C]">{checkbox.title}</p>
|
||||
</div>
|
||||
{!disabled ? (
|
||||
{!checkbox.disabled ? (
|
||||
<div className="bg-[#00BED7] rounded-full text-white w-6 h-6 flex items-center justify-center font-semibold text-caption-s ">
|
||||
<div className="h-fit align-middle text-center flex">8</div>
|
||||
</div>
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useRef, useEffect } from "react";
|
||||
import RangeSlider from "react-range-slider-input";
|
||||
// import "react-range-slider-input/dist/style.css";
|
||||
import "./multiRangeSlider.css";
|
||||
import { IMultirangeSlider } from "../types/multirangeSlider";
|
||||
|
||||
interface MultiRangeSliderProps {
|
||||
onChange?: () => void;
|
||||
min: number;
|
||||
max: number;
|
||||
onChange: (sliderId: string, e: [a: number, b: number]) => void;
|
||||
multirangeSlider: IMultirangeSlider;
|
||||
}
|
||||
|
||||
const MultiRangeSlider = ({ min, max }: MultiRangeSliderProps) => {
|
||||
const [firstValue, setFirstValue] = useState(min);
|
||||
const [secondValue, setSecondValue] = useState(max);
|
||||
const MultiRangeSlider = ({
|
||||
onChange,
|
||||
multirangeSlider,
|
||||
}: MultiRangeSliderProps) => {
|
||||
const firstInputRef = useRef(null);
|
||||
const secondInputRef = useRef(null);
|
||||
|
||||
const handleOnRangeInputChange = (e: [a: number, b: number]) => {
|
||||
setFirstValue(e[0]);
|
||||
setSecondValue(e[1]);
|
||||
onChange(multirangeSlider.id, e);
|
||||
};
|
||||
|
||||
const handleOnFirstInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = Number(e.target.value);
|
||||
if (value >= min && value <= max) {
|
||||
setFirstValue(value);
|
||||
if (
|
||||
value >= multirangeSlider.minValue &&
|
||||
value <= multirangeSlider.maxValue
|
||||
) {
|
||||
const rangeValue: [a: number, b: number] = [
|
||||
value,
|
||||
multirangeSlider.endValue,
|
||||
];
|
||||
onChange(multirangeSlider.id, rangeValue);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -32,20 +37,27 @@ const MultiRangeSlider = ({ min, max }: MultiRangeSliderProps) => {
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const value = Number(e.target.value);
|
||||
if (value >= min && value <= max) {
|
||||
setSecondValue(value);
|
||||
if (
|
||||
value >= multirangeSlider.minValue &&
|
||||
value <= multirangeSlider.maxValue
|
||||
) {
|
||||
const rangeValue: [a: number, b: number] = [
|
||||
multirangeSlider.startValue,
|
||||
value,
|
||||
];
|
||||
onChange(multirangeSlider.id, rangeValue);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!firstInputRef.current) return;
|
||||
(firstInputRef.current as HTMLInputElement).focus();
|
||||
}, [firstValue]);
|
||||
}, [multirangeSlider.startValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!secondInputRef.current) return;
|
||||
(secondInputRef.current as HTMLInputElement).focus();
|
||||
}, [secondValue]);
|
||||
}, [multirangeSlider.endValue]);
|
||||
|
||||
return (
|
||||
<div className="px-2">
|
||||
@@ -53,25 +65,25 @@ const MultiRangeSlider = ({ min, max }: MultiRangeSliderProps) => {
|
||||
<div className="flex justify-between">
|
||||
<input
|
||||
ref={firstInputRef}
|
||||
key={firstValue}
|
||||
key={multirangeSlider.startValue}
|
||||
type="number"
|
||||
onChange={handleOnFirstInputChange}
|
||||
defaultValue={firstValue}
|
||||
defaultValue={multirangeSlider.startValue}
|
||||
className="focus:outline-none input_number w-1/2"
|
||||
/>
|
||||
<input
|
||||
ref={secondInputRef}
|
||||
onChange={handleOnSecondInputChange}
|
||||
key={secondValue}
|
||||
key={multirangeSlider.endValue}
|
||||
type="number"
|
||||
defaultValue={secondValue}
|
||||
defaultValue={multirangeSlider.endValue}
|
||||
className="focus:outline-none appearance-none input_number text-right w-1/2"
|
||||
/>
|
||||
</div>
|
||||
<RangeSlider
|
||||
min={min}
|
||||
max={max}
|
||||
value={[firstValue, secondValue]}
|
||||
min={multirangeSlider.minValue}
|
||||
max={multirangeSlider.maxValue}
|
||||
value={[multirangeSlider.startValue, multirangeSlider.endValue]}
|
||||
className="absolute -bottom-3 left-0 w-[calc(100%-16px)] z-20"
|
||||
onInput={handleOnRangeInputChange}
|
||||
/>
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { useState } from "react";
|
||||
import { ISwitcher } from "../types/switcher";
|
||||
|
||||
const Switch = () => {
|
||||
const [isSwitched, setIsSwitched] = useState(false);
|
||||
interface ISwitchProps {
|
||||
switcher: ISwitcher;
|
||||
onClick: (id: string) => void;
|
||||
}
|
||||
|
||||
const Switch = ({ switcher, onClick }: ISwitchProps) => {
|
||||
function handleOnClick() {
|
||||
setIsSwitched((prev) => !prev);
|
||||
// setIsSwitched((prev) => !prev);
|
||||
onClick(switcher.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`w-10 h-6 relative rounded-full cursor-pointer transition-all duration-300 ease-in-out ${
|
||||
isSwitched ? "bg-[#00BED7]" : "bg-[#E2E2DC]"
|
||||
switcher.isSwitched ? "bg-[#00BED7]" : "bg-[#E2E2DC]"
|
||||
}`}
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
<div
|
||||
className={`w-5 h-5 bg-[#fff] rounded-full absolute transition-all duration-300 ease-in-out ${
|
||||
isSwitched ? "top-[2px] left-[18px]" : "top-[2px] left-[2px]"
|
||||
className={`w-5 h-5 bg-[#fff] rounded-full absolute transition-all duration-300 ease-in-out top-[2px] ${
|
||||
switcher.isSwitched ? "left-[18px]" : "left-[2px]"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
const CheckIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4.1665 9.16699L8.33317 13.3337L15.8332 6.66699"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckIcon;
|
||||
@@ -0,0 +1,21 @@
|
||||
const ChevronDownIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19 9.00002L12 16L5 9.00002"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChevronDownIcon;
|
||||
@@ -6,43 +6,67 @@ import ResetIcon from "../icons/ResetIcon";
|
||||
import Checkbox from "../Checkbox";
|
||||
import Switch from "../Switch";
|
||||
import MultiRangeSlider from "../MultiRangeSlider";
|
||||
|
||||
interface ICheckbox {
|
||||
title: string;
|
||||
id: string;
|
||||
disabled?: boolean;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
const checkboxes: ICheckbox[] = [
|
||||
{ title: "Studio Flex", id: "1", disabled: false, selected: false },
|
||||
{ title: "Studio", id: "2", selected: false },
|
||||
{ title: "1 Bedroom", id: "3", selected: false },
|
||||
{ title: "2 Bedroom", id: "4", selected: false },
|
||||
];
|
||||
import { ICheckbox } from "../../types/checkbox";
|
||||
import { IMultirangeSlider } from "../../types/multirangeSlider";
|
||||
import { ISwitcher } from "../../types/switcher";
|
||||
import {
|
||||
initialCheckboxes,
|
||||
initialSliders,
|
||||
initialSwitchers,
|
||||
} from "../../consts/initialMasterplanFilters";
|
||||
|
||||
const MasterplanFilters = () => {
|
||||
const { setModal } = useModal();
|
||||
// const [currentCheckboxes, setCurrentCheckboxes] =
|
||||
// useState<ICheckbox[]>(checkboxes);
|
||||
const [keyIndex, setKeyIndex] = useState(1);
|
||||
const [checkboxes, setCheckboxes] = useState<ICheckbox[]>(initialCheckboxes);
|
||||
const [sliders, setSliders] = useState<IMultirangeSlider[]>(initialSliders);
|
||||
const [switchers, setSwitchers] = useState<ISwitcher[]>(initialSwitchers);
|
||||
|
||||
const handleOnCloseClick = () => setModal(null);
|
||||
|
||||
const handleOnCheckboxClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = checkboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnSliderValueChange = (
|
||||
sliderId: string,
|
||||
e: [a: number, b: number]
|
||||
) => {
|
||||
const updatedSliders = sliders.map((slider) => {
|
||||
if (sliderId !== slider.id) return slider;
|
||||
|
||||
return { ...slider, startValue: e[0], endValue: e[1] };
|
||||
});
|
||||
|
||||
setSliders(updatedSliders);
|
||||
};
|
||||
|
||||
const handleOnSwitcherClick = (switcherId: string) => {
|
||||
const updatedSwitchers = switchers.map((switcher) => {
|
||||
if (switcherId !== switcher.id) return switcher;
|
||||
const { isSwitched } = switcher;
|
||||
|
||||
return { ...switcher, isSwitched: !isSwitched };
|
||||
});
|
||||
|
||||
setSwitchers(updatedSwitchers);
|
||||
};
|
||||
|
||||
const handleOnResetClick = () => {
|
||||
// const updatedCheckboxes = checkboxes.map((checkbox) => {
|
||||
// return { ...checkbox, selected: false };
|
||||
// });
|
||||
// setCurrentCheckboxes(updatedCheckboxes);
|
||||
setKeyIndex((prev) => prev + 1);
|
||||
setCheckboxes(initialCheckboxes);
|
||||
setSliders(initialSliders);
|
||||
setSwitchers(initialSwitchers);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute z-50 top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] grid grid-cols-12 items-center ">
|
||||
<div
|
||||
className="h-full col-span-3 bg-[#F3F3F2] flex flex-col items-center justify-between "
|
||||
key={keyIndex}
|
||||
>
|
||||
<div className="h-full col-span-3 bg-[#F3F3F2] flex flex-col items-center justify-between ">
|
||||
<div className="w-full py-6 px-6 flex flex-col items-center">
|
||||
<div className="flex justify-between border-b border-[#E2E2DC] w-full pb-4">
|
||||
<h2 className="text-subheadline-m font-semibold">Filters</h2>
|
||||
@@ -59,47 +83,38 @@ const MasterplanFilters = () => {
|
||||
<div className="flex flex-col gap-2">
|
||||
{checkboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
title={checkbox.title}
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
disabled={checkbox.disabled}
|
||||
onClick={handleOnCheckboxClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col pt-6 w-full gap-8">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-m font-semibold ">Cost</p>
|
||||
<p className="text-[#73787C] text-m font-semibold">AED</p>
|
||||
{sliders.map((slider) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-m font-semibold ">
|
||||
{slider.title}
|
||||
</p>
|
||||
<p className="text-[#73787C] text-m font-semibold">
|
||||
{slider.unit}
|
||||
</p>
|
||||
</div>
|
||||
<MultiRangeSlider
|
||||
onChange={handleOnSliderValueChange}
|
||||
multirangeSlider={slider}
|
||||
/>
|
||||
</div>
|
||||
<MultiRangeSlider min={1048888} max={2408888} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-m font-semibold ">
|
||||
Total Area
|
||||
</p>
|
||||
<p className="text-[#73787C] text-m font-semibold">Sqft</p>
|
||||
</div>
|
||||
<MultiRangeSlider min={341} max={891} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-m font-semibold ">Floor</p>
|
||||
<p className="text-[#73787C] text-m font-semibold"></p>
|
||||
</div>
|
||||
<MultiRangeSlider min={5} max={81} />
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between w-full">
|
||||
<p className="text-s text-[#73787C]">Not the first floor</p>
|
||||
<Switch />
|
||||
</div>
|
||||
<div className="flex justify-between w-full">
|
||||
<p className="text-s text-[#73787C]">Not the top floor</p>
|
||||
<Switch />
|
||||
</div>
|
||||
{switchers.map((switcher) => (
|
||||
<div key={switcher.id} className="flex justify-between w-full">
|
||||
<p className="text-s text-[#73787C]">{switcher.title}</p>
|
||||
<Switch switcher={switcher} onClick={handleOnSwitcherClick} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { ILayoutCard } from "../../types/layoutCard";
|
||||
|
||||
interface LayoutCardProps {
|
||||
layoutCard: ILayoutCard;
|
||||
}
|
||||
|
||||
const LayoutCard = ({ layoutCard }: LayoutCardProps) => {
|
||||
const {
|
||||
floorEnd,
|
||||
floorStart,
|
||||
apartmentType,
|
||||
roveHome,
|
||||
wing,
|
||||
units,
|
||||
square,
|
||||
cost,
|
||||
} = layoutCard;
|
||||
|
||||
return (
|
||||
<div className="bg-white flex flex-col p-4 rounded-lg gap-4">
|
||||
<div className="flex gap-4 justify-between">
|
||||
<div className="flex gap-1 flex-col">
|
||||
<p className="text-[#00BED7] text-s leading-5">
|
||||
Rove Home {roveHome}
|
||||
</p>
|
||||
<div className="text-[#73787C] flex gap-2 items-center w-fit">
|
||||
<p className="text-caption-m font-semibold leading-4">{wing}</p>
|
||||
<div className="w-1 h-1 bg-[#E2E2DC] rounded-full"></div>
|
||||
<p className="text-caption-m font-semibold leading-4">
|
||||
Floor {floorStart}-{floorEnd}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-[#00BED7] text-white text-caption-m font-semibold rounded-full py-[3px] px-2 self-start text-nowrap">
|
||||
{units} units
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full bg-slate-500 aspect-square rounded-lg"></div>
|
||||
<div className="flex flex-col">
|
||||
<p className="text[#0D1922] text-s">
|
||||
{apartmentType}, {square} Sqft
|
||||
</p>
|
||||
<p className="text-[#00BED7] text-m font-bold">AED {cost}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutCard;
|
||||
@@ -0,0 +1,192 @@
|
||||
import { useState } from "react";
|
||||
import { ILayoutCard } from "../../types/layoutCard";
|
||||
import { ISort } from "../../types/sortType";
|
||||
import LayoutCard from "./LayoutCard";
|
||||
import SortButton from "./SortButton";
|
||||
|
||||
const layoutsCards: ILayoutCard[] = [
|
||||
{
|
||||
id: "1",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "Studio Flex",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 10488888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1138888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "Studio Flex",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 10488888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1138888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "9",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "Studio Flex",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 10488888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "10",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "11",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "12",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1138888,
|
||||
square: 609,
|
||||
},
|
||||
];
|
||||
|
||||
const initialSortList: ISort[] = [
|
||||
{
|
||||
id: "1",
|
||||
title: "Ascending price",
|
||||
isSelected: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "Decreasing price",
|
||||
isSelected: false,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "Ascending Squares",
|
||||
isSelected: false,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
title: "Ascending Floor",
|
||||
isSelected: false,
|
||||
},
|
||||
];
|
||||
|
||||
const LayoutOptions = () => {
|
||||
const [sortList, setSortList] = useState(initialSortList);
|
||||
const handleOnSortClick = (sortId: string) => {
|
||||
const updatedSortList = sortList.map((sort) => {
|
||||
const isSelected = sort.id === sortId;
|
||||
return { ...sort, isSelected: isSelected };
|
||||
});
|
||||
setSortList(updatedSortList);
|
||||
};
|
||||
return (
|
||||
<section className="w-full p-6 flex flex-col">
|
||||
<div className="flex justify-between w-full items-center border-b pb-4">
|
||||
<div className="flex gap-4 font-semibold text-subheadline-s leading-7">
|
||||
<h2 className="text-[#0D1922]">Layout options</h2>
|
||||
<p className="text-[#73787C]">145</p>
|
||||
</div>
|
||||
<SortButton sortList={sortList} onClick={handleOnSortClick} />
|
||||
</div>
|
||||
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-4 pt-6">
|
||||
{layoutsCards.map((layoutsCard) => (
|
||||
<LayoutCard layoutCard={layoutsCard} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutOptions;
|
||||
@@ -0,0 +1,153 @@
|
||||
import { useState } from "react";
|
||||
import Button from "../Button";
|
||||
import ResetIcon from "../icons/ResetIcon";
|
||||
import Checkbox from "../Checkbox";
|
||||
import Switch from "../Switch";
|
||||
import MultiRangeSlider from "../MultiRangeSlider";
|
||||
import { ICheckbox } from "../../types/checkbox";
|
||||
import { IMultirangeSlider } from "../../types/multirangeSlider";
|
||||
import { ISwitcher } from "../../types/switcher";
|
||||
import {
|
||||
initialApartmentTypeCheckboxes,
|
||||
initialSliders,
|
||||
initialSwitchers,
|
||||
initialRoveHomeCheckboxes,
|
||||
} from "../../consts/initialSearchFilters";
|
||||
|
||||
const SidebarFilters = () => {
|
||||
const [apartmentTypeCheckboxes, setApartmentTypeCheckboxes] = useState<
|
||||
ICheckbox[]
|
||||
>(initialApartmentTypeCheckboxes);
|
||||
const [roveHomeCheckboxes, setRoveHomeCheckboxes] = useState<ICheckbox[]>(
|
||||
initialRoveHomeCheckboxes
|
||||
);
|
||||
const [sliders, setSliders] = useState<IMultirangeSlider[]>(initialSliders);
|
||||
const [switchers, setSwitchers] = useState<ISwitcher[]>(initialSwitchers);
|
||||
|
||||
const handleOnCheckboxApartmentClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = apartmentTypeCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setApartmentTypeCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnCheckboxRoveHomeClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = roveHomeCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setRoveHomeCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnSliderValueChange = (
|
||||
sliderId: string,
|
||||
e: [a: number, b: number]
|
||||
) => {
|
||||
const updatedSliders = sliders.map((slider) => {
|
||||
if (sliderId !== slider.id) return slider;
|
||||
|
||||
return { ...slider, startValue: e[0], endValue: e[1] };
|
||||
});
|
||||
|
||||
setSliders(updatedSliders);
|
||||
};
|
||||
|
||||
const handleOnSwitcherClick = (switcherId: string) => {
|
||||
const updatedSwitchers = switchers.map((switcher) => {
|
||||
if (switcherId !== switcher.id) return switcher;
|
||||
const { isSwitched } = switcher;
|
||||
|
||||
return { ...switcher, isSwitched: !isSwitched };
|
||||
});
|
||||
|
||||
setSwitchers(updatedSwitchers);
|
||||
};
|
||||
|
||||
const handleOnResetClick = () => {
|
||||
setApartmentTypeCheckboxes(initialApartmentTypeCheckboxes);
|
||||
setRoveHomeCheckboxes(initialRoveHomeCheckboxes);
|
||||
setSliders(initialSliders);
|
||||
setSwitchers(initialSwitchers);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="h-full w-[360px] bg-[#F3F3F2] flex flex-col items-center justify-between border-r">
|
||||
<div className="w-full py-6 px-6 flex flex-col items-center">
|
||||
<div className="flex justify-between border-b border-[#E2E2DC] w-full pb-4">
|
||||
<h2 className="text-subheadline-m font-semibold">Filters</h2>
|
||||
<Button
|
||||
icon={<ResetIcon />}
|
||||
buttonType="fab"
|
||||
onClick={handleOnResetClick}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col pt-6 w-full">
|
||||
<p className="text-[#0D1922] text-m font-semibold pb-4">
|
||||
Rove Home
|
||||
</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
{roveHomeCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
onClick={handleOnCheckboxRoveHomeClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col pt-6 w-full">
|
||||
<p className="text-[#0D1922] text-m font-semibold pb-4">
|
||||
Apartment type
|
||||
</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
{apartmentTypeCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
onClick={handleOnCheckboxApartmentClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col pt-6 w-full gap-8">
|
||||
{sliders.map((slider) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-m font-semibold ">
|
||||
{slider.title}
|
||||
</p>
|
||||
<p className="text-[#73787C] text-m font-semibold">
|
||||
{slider.unit}
|
||||
</p>
|
||||
</div>
|
||||
<MultiRangeSlider
|
||||
onChange={handleOnSliderValueChange}
|
||||
multirangeSlider={slider}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{switchers.map((switcher) => (
|
||||
<div key={switcher.id} className="flex justify-between w-full">
|
||||
<p className="text-s text-[#73787C]">{switcher.title}</p>
|
||||
<Switch switcher={switcher} onClick={handleOnSwitcherClick} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarFilters;
|
||||
@@ -0,0 +1,57 @@
|
||||
import { useState } from "react";
|
||||
import { ISort } from "../../types/sortType";
|
||||
import CheckIcon from "../icons/CheckIcon";
|
||||
import ChevronDownIcon from "../icons/ChevronDownIcon";
|
||||
|
||||
interface SortButtonProps {
|
||||
sortList: ISort[];
|
||||
onClick: (id: string) => void;
|
||||
}
|
||||
|
||||
const SortButton = ({ sortList, onClick }: SortButtonProps) => {
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
|
||||
const handleOnClick = () => {
|
||||
setIsSelected((prev) => !prev);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<button
|
||||
className="text-[#00BED7] text-m leading-5 flex gap-2"
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
Sort by ascending price{" "}
|
||||
<div
|
||||
className={`transition-all duration-300 ease-in-out ${
|
||||
isSelected ? "rotate-180" : "rotate-0"
|
||||
}`}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
className={`absolute flex flex-col bg-white p-2 text-[#0D1922] rounded-lg w-full shadow-lg transition-opacity duration-300 ease-in-out ${
|
||||
isSelected ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
{sortList.map((sort) => (
|
||||
<button
|
||||
onClick={() => onClick(sort.id)}
|
||||
key={sort.id}
|
||||
className="hover:bg-[#F3F3F2] py-2 pl-3 rounded-lg transition-all duration-300 ease-in-out text-left flex items-center gap-2"
|
||||
>
|
||||
{sort.isSelected && (
|
||||
<div className="text-[#00BED7]">
|
||||
<CheckIcon />
|
||||
</div>
|
||||
)}
|
||||
{sort.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SortButton;
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ICheckbox } from "../types/checkbox";
|
||||
import { IMultirangeSlider } from "../types/multirangeSlider";
|
||||
import { ISwitcher } from "../types/switcher";
|
||||
|
||||
const initialSliders: IMultirangeSlider[] = [
|
||||
{
|
||||
minValue: 1048888,
|
||||
startValue: 1048888,
|
||||
endValue: 2408888,
|
||||
maxValue: 2408888,
|
||||
title: "Cost",
|
||||
unit: "AED",
|
||||
id: "1",
|
||||
},
|
||||
{
|
||||
minValue: 341,
|
||||
startValue: 341,
|
||||
endValue: 891,
|
||||
maxValue: 891,
|
||||
title: "Total Area",
|
||||
unit: "Sqrt",
|
||||
id: "2",
|
||||
},
|
||||
{
|
||||
minValue: 5,
|
||||
startValue: 5,
|
||||
endValue: 81,
|
||||
maxValue: 81,
|
||||
title: "Floor",
|
||||
id: "3",
|
||||
},
|
||||
];
|
||||
|
||||
const initialCheckboxes: ICheckbox[] = [
|
||||
{ title: "Studio Flex", id: "1", disabled: true, selected: false },
|
||||
{ title: "Studio", id: "2", selected: false },
|
||||
{ title: "1 Bedroom", id: "3", selected: false },
|
||||
{ title: "2 Bedroom", id: "4", selected: false },
|
||||
];
|
||||
|
||||
const initialSwitchers: ISwitcher[] = [
|
||||
{ id: "1", title: "Not the first floor", isSwitched: false },
|
||||
{ id: "2", title: "Not the top floor", isSwitched: false },
|
||||
];
|
||||
|
||||
export { initialCheckboxes, initialSliders, initialSwitchers };
|
||||
@@ -0,0 +1,57 @@
|
||||
import { ICheckbox } from "../types/checkbox";
|
||||
import { IMultirangeSlider } from "../types/multirangeSlider";
|
||||
import { ISwitcher } from "../types/switcher";
|
||||
|
||||
const initialSliders: IMultirangeSlider[] = [
|
||||
{
|
||||
minValue: 1048888,
|
||||
startValue: 1048888,
|
||||
endValue: 2408888,
|
||||
maxValue: 2408888,
|
||||
title: "Cost",
|
||||
unit: "AED",
|
||||
id: "1",
|
||||
},
|
||||
{
|
||||
minValue: 341,
|
||||
startValue: 341,
|
||||
endValue: 891,
|
||||
maxValue: 891,
|
||||
title: "Total Area",
|
||||
unit: "Sqrt",
|
||||
id: "2",
|
||||
},
|
||||
{
|
||||
minValue: 5,
|
||||
startValue: 5,
|
||||
endValue: 81,
|
||||
maxValue: 81,
|
||||
title: "Floor",
|
||||
id: "3",
|
||||
},
|
||||
];
|
||||
|
||||
const initialApartmentTypeCheckboxes: ICheckbox[] = [
|
||||
{ title: "Studio Flex", id: "1", disabled: true, selected: false },
|
||||
{ title: "Studio", id: "2", selected: false },
|
||||
{ title: "1 Bedroom", id: "3", selected: false },
|
||||
{ title: "2 Bedroom", id: "4", selected: false },
|
||||
];
|
||||
|
||||
const initialRoveHomeCheckboxes: ICheckbox[] = [
|
||||
{ title: "Downtown", id: "1", disabled: false, selected: false },
|
||||
{ title: "Marasi Drive", id: "2", selected: false },
|
||||
{ title: "Dubai Marina", id: "3", selected: false, disabled: true },
|
||||
];
|
||||
|
||||
const initialSwitchers: ISwitcher[] = [
|
||||
{ id: "1", title: "Not the first floor", isSwitched: false },
|
||||
{ id: "2", title: "Not the top floor", isSwitched: false },
|
||||
];
|
||||
|
||||
export {
|
||||
initialApartmentTypeCheckboxes,
|
||||
initialSliders,
|
||||
initialSwitchers,
|
||||
initialRoveHomeCheckboxes,
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import Company from "./pages/Company";
|
||||
import "./index.css";
|
||||
import Complex from "./pages/Complex";
|
||||
import ComplexWing from "./pages/ComplexWing";
|
||||
import Search from "./pages/Search";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@@ -32,6 +33,10 @@ const router = createBrowserRouter([
|
||||
path: "/company",
|
||||
element: <Company />,
|
||||
},
|
||||
{
|
||||
path: "/search",
|
||||
element: <Search />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -4,7 +4,7 @@ import SequenceWing from "../components/complexWingPage/SequenceWing";
|
||||
|
||||
const ComplexWing = () => {
|
||||
return (
|
||||
<div className="overflow-hidden h-screen w-screen">
|
||||
<div className="overflow-hidden h-screen w-screen ">
|
||||
<ComplexTopPanel />
|
||||
<SequenceWing />
|
||||
<ComplexButtomPanel />
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import Footer from "../components/Footer";
|
||||
import LayoutOptions from "../components/searchPage/LayoutOptions";
|
||||
import SidebarFilters from "../components/searchPage/SidebarFilters";
|
||||
|
||||
const Search = () => {
|
||||
return (
|
||||
<div className="overflow-scroll h-screen w-screen pt-14">
|
||||
<div className="flex">
|
||||
<SidebarFilters />
|
||||
<LayoutOptions />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
||||
@@ -0,0 +1,8 @@
|
||||
interface ICheckbox {
|
||||
title: string;
|
||||
id: string;
|
||||
disabled?: boolean;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export type { ICheckbox };
|
||||
@@ -0,0 +1,17 @@
|
||||
type RoveHome = "Downtown" | "Marasi Drive" | "Dubai Marina";
|
||||
type ApartmentType = "Studio Flex" | "Studio" | "1 Bedroom" | "2 Bedroom";
|
||||
|
||||
interface ILayoutCard {
|
||||
id: string;
|
||||
roveHome: RoveHome;
|
||||
apartmentType: ApartmentType;
|
||||
wing: string;
|
||||
floorStart: number;
|
||||
floorEnd: number;
|
||||
units: number;
|
||||
cost: number;
|
||||
square: number;
|
||||
imgSrc?: string;
|
||||
}
|
||||
|
||||
export type { ILayoutCard };
|
||||
@@ -0,0 +1,11 @@
|
||||
interface IMultirangeSlider {
|
||||
minValue: number;
|
||||
maxValue: number;
|
||||
startValue: number;
|
||||
endValue: number;
|
||||
title: string;
|
||||
unit?: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type { IMultirangeSlider };
|
||||
@@ -0,0 +1,7 @@
|
||||
interface ISort {
|
||||
id: string;
|
||||
title: string;
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
export type { ISort };
|
||||
@@ -0,0 +1,7 @@
|
||||
interface ISwitcher {
|
||||
id: string;
|
||||
title: string;
|
||||
isSwitched: boolean;
|
||||
}
|
||||
|
||||
export type { ISwitcher };
|
||||
Reference in New Issue
Block a user