client,server folders
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { ILayoutCard } from "../../types/layoutCard";
|
||||
import { formatNumber } from "../../calc/formatNumber";
|
||||
|
||||
interface LayoutCardProps {
|
||||
layoutCard: ILayoutCard;
|
||||
}
|
||||
|
||||
const LayoutCard = ({ layoutCard }: LayoutCardProps) => {
|
||||
const {
|
||||
floorEnd,
|
||||
floorStart,
|
||||
apartmentType,
|
||||
roveHome,
|
||||
wing,
|
||||
units,
|
||||
square,
|
||||
cost,
|
||||
id,
|
||||
} = layoutCard;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleOnClick = () => {
|
||||
navigate(`${id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="bg-white flex flex-col p-4 rounded-2xl gap-4 cursor-pointer select-none"
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
<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 aspect-square rounded-lg">
|
||||
<img src="/images/layout-1.png" alt="" className="h-full" />
|
||||
</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 {formatNumber(cost, ",", 3, 1)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutCard;
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import LayoutCard from "./LayoutCard";
|
||||
import SortButton from "./SortButton";
|
||||
import useSearchFilters from "../../store/useSearchFilters";
|
||||
import { initialSortList, layoutsCards } from "../../consts/initialSearchPage";
|
||||
import SearchIcon from "../icons/SearchIcon";
|
||||
import { sortCardBy } from "../../calc/sortCard";
|
||||
import Button from "../Button";
|
||||
import FilterIcon from "../icons/FilterIcon";
|
||||
import useModal from "../../store/useModal";
|
||||
import { MobileModalWrapper } from "../modals/mobile/MobileModalWrapper";
|
||||
import SearchFiltersModal from "../modals/mobile/SearchFiltersModal";
|
||||
|
||||
const LayoutOptions = () => {
|
||||
const [sortList, setSortList] = useState(initialSortList);
|
||||
const [cards, setCards] = useState(layoutsCards);
|
||||
const { setModal } = useModal();
|
||||
const { roveHomeTypeCheckboxes, apartmentTypeCheckboxes, multirangeSliders } =
|
||||
useSearchFilters();
|
||||
|
||||
const handleOnSortClick = (sortId: string) => {
|
||||
const updatedSortList = sortList.map((sort) => {
|
||||
const isSelected = sort.id === sortId;
|
||||
return { ...sort, isSelected: isSelected };
|
||||
});
|
||||
setSortList(updatedSortList);
|
||||
};
|
||||
|
||||
const handleOnFilterClick = () => {
|
||||
setModal(
|
||||
<MobileModalWrapper>
|
||||
<SearchFiltersModal />
|
||||
</MobileModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const sortedCards = sortCardBy(sortList, layoutsCards);
|
||||
|
||||
setCards(sortedCards);
|
||||
}, [
|
||||
sortList,
|
||||
roveHomeTypeCheckboxes,
|
||||
apartmentTypeCheckboxes,
|
||||
multirangeSliders,
|
||||
]);
|
||||
return (
|
||||
<section className="w-full p-6 flex flex-col">
|
||||
<div className="flex justify-between items-center border-b">
|
||||
<div className="flex w-full justify-between pb-4 lg:flex-row lg:items-center flex-col">
|
||||
<div className="flex gap-4 font-semibold text-subheadline-s leading-7 py-[6px]">
|
||||
<h2 className="text-[#0D1922]">Units</h2>
|
||||
<p className="text-[#73787C]">145</p>
|
||||
</div>
|
||||
<SortButton sortList={sortList} onClick={handleOnSortClick} />
|
||||
</div>
|
||||
<div className="lg:hidden block">
|
||||
<Button
|
||||
icon={<FilterIcon />}
|
||||
text="Filters"
|
||||
className="text-[#0D1922B2]"
|
||||
onClick={handleOnFilterClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 grid-cols-2 gap-4 pt-6">
|
||||
{cards.map((layoutsCard) => (
|
||||
<LayoutCard layoutCard={layoutsCard} />
|
||||
))}
|
||||
</div>
|
||||
<div className="bg-white rounded-lg mt-4 py-[10px] flex justify-center select-none cursor-pointer items-center gap-2">
|
||||
<SearchIcon /> Show 12 more apartments
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutOptions;
|
||||
@@ -0,0 +1,176 @@
|
||||
import Button from "../Button";
|
||||
import ResetIcon from "../icons/ResetIcon";
|
||||
import Checkbox from "../Checkbox";
|
||||
import Switch from "../Switch";
|
||||
import MultiRangeSlider from "../MultiRangeSlider";
|
||||
import {
|
||||
initialApartmentTypeCheckboxes,
|
||||
initialSliders,
|
||||
initialSwitchers,
|
||||
initialRoveHomeCheckboxes,
|
||||
} from "../../consts/initialSearchFilters";
|
||||
import useSearchFilters from "../../store/useSearchFilters";
|
||||
import { initialViewCheckboxes } from "../../consts/initialMasterplanFilters";
|
||||
import { useState } from "react";
|
||||
import { ICheckbox } from "../../types/checkbox";
|
||||
|
||||
const SidebarFilters = () => {
|
||||
const {
|
||||
multirangeSliders,
|
||||
setMultirangeSliders,
|
||||
switchers,
|
||||
setSwitchers,
|
||||
apartmentTypeCheckboxes,
|
||||
setApartmentTypeCheckboxes,
|
||||
roveHomeTypeCheckboxes,
|
||||
setRoveHomeTypeCheckboxes,
|
||||
} = useSearchFilters();
|
||||
|
||||
const [viewCheckboxes, setViewCheckboxes] = useState<ICheckbox[]>(
|
||||
initialViewCheckboxes
|
||||
);
|
||||
|
||||
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 = roveHomeTypeCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setRoveHomeTypeCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnSliderValueChange = (
|
||||
sliderId: string,
|
||||
e: [a: number, b: number]
|
||||
) => {
|
||||
const updatedSliders = multirangeSliders.map((slider) => {
|
||||
if (sliderId !== slider.id) return slider;
|
||||
|
||||
return { ...slider, startValue: e[0], endValue: e[1] };
|
||||
});
|
||||
|
||||
setMultirangeSliders(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 handleOnViewCheckboxClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = viewCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setViewCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnResetClick = () => {
|
||||
setViewCheckboxes(initialViewCheckboxes);
|
||||
setApartmentTypeCheckboxes(initialApartmentTypeCheckboxes);
|
||||
setRoveHomeTypeCheckboxes(initialRoveHomeCheckboxes);
|
||||
setMultirangeSliders(initialSliders);
|
||||
setSwitchers(initialSwitchers);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="h-full w-[360px] bg-[#F3F3F2] flex-col items-center justify-between border-r lg:flex hidden">
|
||||
<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
|
||||
isCircleRounded
|
||||
icon={<ResetIcon />}
|
||||
buttonType="fab"
|
||||
onClick={handleOnResetClick}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col pt-6 w-full">
|
||||
<p className="text-[#0D1922] text-s pb-4">Rove Home</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
{roveHomeTypeCheckboxes.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-s 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">
|
||||
{multirangeSliders.map((slider) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-s ">{slider.title}</p>
|
||||
<p className="text-[#73787C] text-s">{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 className="flex flex-col w-full">
|
||||
<p className="text-[#0D1922] text-s pb-4">Views</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
{viewCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
onClick={handleOnViewCheckboxClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarFilters;
|
||||
@@ -0,0 +1,60 @@
|
||||
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 items-center"
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
Sort by{" "}
|
||||
<div>
|
||||
{sortList.find((sort) => sort.isSelected)?.title.toLocaleLowerCase()}{" "}
|
||||
</div>
|
||||
<div
|
||||
className={`transition-all duration-300 ease-in-out ${
|
||||
isSelected ? "rotate-180" : "rotate-0"
|
||||
}`}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
className={`absolute z-20 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;
|
||||
Reference in New Issue
Block a user