favorites compare completed

This commit is contained in:
2025-05-16 19:02:18 +05:00
parent 501ed130be
commit bde2ab91fe
11 changed files with 422 additions and 288 deletions
+86 -14
View File
@@ -5,8 +5,25 @@ import FacebookIcon from "./icons/FacebookIcon";
import LinkedInIcon from "./icons/LinkedInIcon";
import TwitterIcon from "./icons/TwitterIcon";
import ChevronDownIcon from "./icons/ChevronDownIcon";
import { useFavoritesUnitsStore } from "../stores/useFavoritesUnitsStore";
import { AnimatePresence, motion } from "motion/react";
import { useState } from "react";
import { BrochureButton } from "./Header";
import clsx from "clsx";
import { useClickAway } from "@uidotdev/usehooks";
import Button from "./ui/Button";
import useModalStore from "../stores/useModalStore";
import PrivacyPolicyModal from "./modals/PrivacyPolicyModal";
function Footer() {
const { favoriteUnits } = useFavoritesUnitsStore();
const [opened, setOpened] = useState(false);
const ref = useClickAway<HTMLDivElement>(() => setOpened(false));
const { setModal } = useModalStore();
return (
<footer className="z-1 2xl:px-[2.222vw] 2xl:pb-[2.222vw] 2xl:pt-[2.778vw] md:max-2xl:p-6 px-4 py-6 grid 2xl:grid-cols-6 md:max-2xl:grid-cols-4 grid-cols-2 2xl:grid-rows-2 2xl:gap-x-[1.667vw] 2xl:gap-y-[1.111vw] max-2xl:gap-y-6 2xl:rounded-t-[1.667vw] rounded-t-3xl outline outline-[#E2E2DC] bg-white">
<img
@@ -61,19 +78,19 @@ function Footer() {
<div className="2xl:border-l-[0.069vw] border-l border-[#E2E2DC] 2xl:pl-[1.111vw] pl-4 flex flex-col items-start 2xl:col-start-4 2xl:row-start-1 2xl:row-span-2 md:max-2xl:col-start-3 col-start-1">
<Link
to={"/"}
className="text-btn-l flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
className="md:text-btn-l text-btn-m flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
>
Map
</Link>
<Link
to={"/unit-types"}
className="text-btn-l flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
className="md:text-btn-l text-btn-m flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
>
Unit Types
</Link>
<Link
to={"/about-irth"}
className="text-btn-l flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
className="md:text-btn-l text-btn-m flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
>
About IRTH
</Link>
@@ -82,31 +99,86 @@ function Footer() {
<div className="2xl:border-l-[0.069vw] border-l border-[#E2E2DC] 2xl:pl-[1.111vw] md:max-2xl:pl-6 pl-3.5 flex flex-col items-start 2xl:col-start-5 2xl:row-start-1 2xl:row-span-2">
<Link
to={"/favorites"}
className="text-btn-l flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
className="md:text-btn-l text-btn-m flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70 relative"
>
Favorites
{!!favoriteUnits.length && (
<div className="absolute top-0 right-0 translate-x-full max-2xl:-translate-y-full rounded-full w-5 flex items-center justify-center aspect-square bg-[#00BED7] text-white text-caption-s">
{favoriteUnits.length}
</div>
)}
</Link>
<Link
to={"/search"}
className="text-btn-l flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
className="md:text-btn-l text-btn-m flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
>
Search
</Link>
<button className="text-btn-l flex-1 content-center md:my-3 my-[13px] text-[#0D1922]/70 flex items-center gap-2">
<span>Brochures</span>
<div className="2xl:w-[1.667vw] 2xl:h-[1.667vw] md:max-2xl:w-6 md:max-2xl:h-6 w-5 h-5">
<ChevronDownIcon />
</div>
</button>
<div ref={ref}>
<Button
variant="tertiary"
size="large"
className="2xl:!py-[17px] !py-[13px]"
onClick={() => setOpened((prev) => !prev)}
>
<span className="md:text-btn-l text-btn-m text-[#0D1922]/70">
Brochures
</span>
<span
className={clsx(
"2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5 transition-transform duration-300 text-[#0D1922]/70",
opened && "rotate-180"
)}
>
<ChevronDownIcon />
</span>
</Button>
<AnimatePresence>
{opened && (
<motion.div
initial={{ opacity: 0, x: "100%" }}
animate={{ opacity: 1, x: "0%" }}
exit={{ opacity: 0, x: "100%" }}
transition={{ bounce: 0, duration: 0.3 }}
className="max-2xl:hidden p-[1.667vw] flex gap-[1.111vw] justify-stretch items-stretch fixed top-[calc(3.889vw+20px)] left-[57.083vw] w-[32.222vw] rounded-[1.111vw] bg-white shadow-[0_2px_8px_rgba(0,0,0,0.15)]"
>
<div className="flex-1 space-y-4">
<p className="text-s font-medium">Rove Home Marasi Drive</p>
<div className="flex flex-col gap-[0.556vw]">
{[
"Rove Main Brochure",
"Rove Amenties Brochure",
"Rove Technical Brochure",
].map((title) => (
<BrochureButton title={title} key={title} />
))}
</div>
</div>
<div className="flex-1 space-y-4">
<p className="text-s font-medium">Rove Home Downtown</p>
<div className="flex flex-col gap-[0.556vw]">
{[
"Rove Main Brochure",
"Rove Amenties Brochure",
"Rove Technical Brochure",
].map((title) => (
<BrochureButton title={title} key={title} />
))}
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
<div className="content-end 2xl:text-right 2xl:col-start-6 2xl:row-start-1 2xl:row-span-2 md:max-2xl:col-start-1 md:max-2xl:row-start-3 max-md:col-span-3 max-md:pt-3 max-md:border-t border-[#E2E2DC]">
<Link
to={"/"}
<button
className="md:text-caption-m text-caption-s max-2xl:text-[#73787C] text-[#0D1922]/70"
onClick={() => setModal(<PrivacyPolicyModal />)}
>
Privacy Policy
</Link>
</button>
</div>
</footer>
);
+15 -3
View File
@@ -12,6 +12,7 @@ import { projects } from "../data/projects";
import useModalStore from "../stores/useModalStore";
import PrivacyPolicyModal from "./modals/PrivacyPolicyModal";
import ChevronDownIcon from "./icons/ChevronDownIcon";
import { useFavoritesUnitsStore } from "../stores/useFavoritesUnitsStore";
function Header() {
function handleLogoClick() {
@@ -35,7 +36,7 @@ function Header() {
return (
<>
<header className="sticky top-0 left-0 z-2 w-full h-14 md:max-2xl:h-16 2xl:h-[4.444vw] flex items-center justify-center bg-white ring ring-[#E2E2DC]">
<header className="sticky top-0 left-0 z-4 w-full h-14 md:max-2xl:h-16 2xl:h-[4.444vw] flex items-center justify-center bg-white ring ring-[#E2E2DC]">
<div className="flex 2xl:gap-[1.111vw] gap-4 flex-1">
<div
className="2xl:px-[2.222vw] 2xl:py-[1.111vw] md:max-2xl:px-6 max-md:px-4 py-4 cursor-pointer"
@@ -92,7 +93,7 @@ function Header() {
animate={{ opacity: 1, y: "0%" }}
exit={{ opacity: 0, y: "-100%" }}
transition={{ duration: 0.3 }}
className="2xl:hidden fixed left-0 md:top-16 top-14 md:p-4 p-3 z-1 w-full md:rounded-b-2xl flex flex-col gap-10 bg-white overflow-y-auto max-h-[calc(100dvh-56px)] pointer-events-auto"
className="2xl:hidden fixed left-0 md:top-16 top-14 md:p-4 p-3 z-3 w-full md:rounded-b-2xl flex flex-col gap-10 bg-white overflow-y-auto max-h-[calc(100dvh-56px)] pointer-events-auto ring ring-[#E2E2DC]"
>
<div className="space-y-4">
<p className="text-h3 font-medium">Projects</p>
@@ -191,17 +192,28 @@ function Header() {
export default Header;
function NavItem({ href, title }: { href: string; title: string }) {
const { favoriteUnits } = useFavoritesUnitsStore();
return (
<NavLink
to={href}
className={({ isActive }) =>
clsx(
"text-btn-m 2xl:px-[1.25vw] 2xl:py-[0.903vw] p-4 2xl:rounded-[0.833vw] rounded-xl transition-colors duration-300 !leading-none max-2xl:text-center max-2xl:bg-[#F3F3F2]",
"text-btn-m 2xl:px-[1.25vw] 2xl:py-[0.903vw] p-4 2xl:rounded-[0.833vw] rounded-xl transition-colors duration-300 !leading-none max-2xl:text-center max-2xl:bg-[#F3F3F2] relative",
isActive && "!bg-[#00BED7] text-[#FFFFFF]"
)
}
>
{title}
{title === "Favorites" && !!favoriteUnits.length && (
<div
className={clsx(
"absolute 2xl:top-0 2xl:right-0 top-0.5 right-0.5 rounded-full w-5 flex items-center justify-center aspect-square bg-[#00BED7] text-white text-caption-s"
)}
>
{favoriteUnits.length}
</div>
)}
</NavLink>
);
}
+4 -4
View File
@@ -44,14 +44,14 @@ function ModalContainer() {
{modal && (
<motion.div
ref={rootRef}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
// initial={{ opacity: 0 }}
// animate={{ opacity: 1 }}
// exit={{ opacity: 0 }}
className="h-full"
>
<div
ref={popoverRef}
className="bg-black/70 fixed inset-0 max-md:top-14 flex flex-col items-center justify-center max-md:justify-start overflow-y-auto z-10"
className="bg-black/70 fixed inset-0 max-md:top-14 flex flex-col items-center justify-center max-md:justify-start overflow-y-auto z-5"
>
<div className="max-h-full">
<div ref={divRef} className="md:p-[1.08vw]">
+4 -1
View File
@@ -5,6 +5,7 @@ import HeartIcon from "./icons/HeartIcon";
import Button from "./ui/Button";
import "react-loading-skeleton/dist/skeleton.css";
import Skeleton from "react-loading-skeleton";
import { unitTypesFormatted } from "../data/unitTypes";
function UnitCard({ unit }: { unit: IUnit }) {
const { favoriteUnits, setFavoriteUnits } = useFavoritesUnitsStore();
@@ -52,7 +53,9 @@ function UnitCard({ unit }: { unit: IUnit }) {
</div>
<div className="2xl:space-y-[0.278vw] space-y-1">
<p className="text-s">
{`${unit.unitType}, ${unit.squareFt.toLocaleString(undefined, {
{`${unitTypesFormatted.get(
unit.unitType
)}, ${unit.squareFt.toLocaleString(undefined, {
maximumFractionDigits: 2,
})} Sqft`}
</p>
+2 -1
View File
@@ -1,6 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useEffect, useState } from "react";
import clsx from "clsx";
import { unitTypesFormatted } from "../data/unitTypes";
function UnitTypesSelect({
unitTypes,
@@ -37,7 +38,7 @@ function UnitTypesSelect({
: "ring-[#E2E2DC] text-[#0D1922]/70"
)}
>
{unitType}
{unitTypesFormatted.get(unitType)}
</p>
))}
</div>
+10
View File
@@ -0,0 +1,10 @@
export const unitTypesFormatted = new Map([
["1 BR Squared", "1 Bedroom²"],
["2 BR Squared", "2 Bedroom²"],
["Studio Flex", "Studio Flex"],
["Studio Squared", "Studio²"],
["Studio2", "Studio²"],
["One Bedroom2", "1 Bedroom²"],
["One Bedroom Loft", "1 Bedroom Loft"],
["Two Bedroom Loft", "2 Bedroom Loft"],
]);
+41 -43
View File
@@ -16,62 +16,60 @@ button {
outline: none;
}
@layer utilities {
.text-h1 {
@apply 2xl:text-[3.889vw] text-[56px] leading-none;
}
@utility text-h1 {
@apply 2xl:text-[3.889vw] text-[56px] leading-none;
}
.text-h15 {
@apply 2xl:text-[2.778vw] text-[40px] leading-[135%];
}
@utility text-h15 {
@apply 2xl:text-[2.778vw] text-[40px] leading-[135%];
}
.text-h2 {
@apply 2xl:text-[2.222vw] text-[32px] leading-[125%];
}
@utility text-h2 {
@apply 2xl:text-[2.222vw] text-[32px] leading-[125%];
}
.text-h3 {
@apply 2xl:text-[1.667vw] text-[24px] leading-[135%];
}
@utility text-h3 {
@apply 2xl:text-[1.667vw] text-[24px] leading-[135%];
}
.text-h4 {
@apply 2xl:text-[1.389vw] text-[20px] leading-[120%];
}
@utility text-h4 {
@apply 2xl:text-[1.389vw] text-[20px] leading-[120%];
}
.text-h5 {
@apply 2xl:text-[0.972vw] text-sm leading-[125%];
}
@utility text-h5 {
@apply 2xl:text-[0.972vw] text-sm leading-[125%];
}
.text-l {
@apply 2xl:text-[1.389vw] text-[20px] leading-[135%];
}
@utility text-l {
@apply 2xl:text-[1.389vw] text-[20px] leading-[135%];
}
.text-m {
@apply 2xl:text-[1.111vw] leading-[125%];
}
@utility text-m {
@apply 2xl:text-[1.111vw] leading-[125%];
}
.text-s {
@apply 2xl:text-[0.972vw] text-sm leading-[140%];
}
@utility text-s {
@apply 2xl:text-[0.972vw] text-sm leading-[140%];
}
.text-btn-l {
@apply 2xl:text-[1.111vw] leading-none;
}
@utility text-btn-l {
@apply 2xl:text-[1.111vw] leading-none;
}
.text-bnt-m {
@apply 2xl:text-[0.972vw] text-sm leading-none;
}
@utility text-btn-m {
@apply 2xl:text-[0.972vw] text-sm leading-none;
}
.text-btn-s {
@apply 2xl:text-[0.833vw] text-xs leading-none;
}
@utility text-btn-s {
@apply 2xl:text-[0.833vw] text-xs leading-none;
}
.text-caption-m {
@apply 2xl:text-[0.833vw] text-xs leading-[135%];
}
@utility text-caption-m {
@apply 2xl:text-[0.833vw] text-xs leading-[135%];
}
.text-caption-s {
@apply 2xl:text-[0.694vw] text-[10px] leading-[135%];
}
@utility text-caption-s {
@apply 2xl:text-[0.694vw] text-[10px] leading-[135%];
}
@utility font-mixcase-unmixed {
+25 -25
View File
@@ -1,42 +1,42 @@
import './index.css';
import { QueryClientProvider } from '@tanstack/react-query';
import { createRoot } from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router';
import DefaultLayout from './layout/DefaultLayout.tsx';
import MainPage from './pages/MainPage.tsx';
import ModalContainer from './components/ModalContainer.tsx';
import ComplexPage from './pages/ComplexPage.tsx';
import FloorsPage from './pages/FloorsPage.tsx';
import UnitTypesPage from './pages/UnitTypesPage.tsx';
import AboutPage from './pages/AboutPages.tsx';
import FavoritesPage from './pages/FavouritesPage.tsx';
import SearchPage from './pages/SearchPage.tsx';
import LayoutWithoutFooter from './layout/LayoutWithoutFooter.tsx';
import { queryClient } from './lib/queryClient.ts';
import AboutComplexPage from './pages/AboutComplexPage.tsx';
import "./index.css";
import { QueryClientProvider } from "@tanstack/react-query";
import { createRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router";
import DefaultLayout from "./layout/DefaultLayout.tsx";
import MainPage from "./pages/MainPage.tsx";
import ModalContainer from "./components/ModalContainer.tsx";
import ComplexPage from "./pages/ComplexPage.tsx";
import FloorsPage from "./pages/FloorsPage.tsx";
import UnitTypesPage from "./pages/UnitTypesPage.tsx";
import AboutPage from "./pages/AboutPages.tsx";
import FavoritesPage from "./pages/FavouritesPage.tsx";
import SearchPage from "./pages/SearchPage.tsx";
import LayoutWithoutFooter from "./layout/LayoutWithoutFooter.tsx";
import { queryClient } from "./lib/queryClient.ts";
import AboutComplexPage from "./pages/AboutComplexPage.tsx";
const route = createBrowserRouter([
{
element: <DefaultLayout />,
children: [
{
path: '/unit-types',
path: "/unit-types",
element: <UnitTypesPage />,
},
{
path: '/about',
path: "/about",
element: <AboutPage />,
},
{
path: '/favorites',
path: "/favorites",
element: <FavoritesPage />,
},
{
path: '/search',
path: "/search",
element: <SearchPage />,
},
{
path: '/complex/:complexName/about',
path: "/complex/:complexName/about",
element: <AboutComplexPage />,
},
],
@@ -45,22 +45,22 @@ const route = createBrowserRouter([
element: <LayoutWithoutFooter />,
children: [
{
path: '/',
path: "/",
element: <MainPage />,
},
{
path: '/complex/:complexName',
path: "/complex/:complexName",
element: <ComplexPage />,
},
{
path: '/complex/:complexName/floors',
path: "/complex/:complexName/floors",
element: <FloorsPage />,
},
],
},
]);
createRoot(document.getElementById('root')!).render(
createRoot(document.getElementById("root")!).render(
<>
<QueryClientProvider client={queryClient}>
<RouterProvider router={route} />
+231 -193
View File
@@ -6,13 +6,16 @@ import Button from "../components/ui/Button";
import ChartIcon from "../components/icons/ChartIcon";
import { useFavoritesUnitsStore } from "../stores/useFavoritesUnitsStore";
import UnitCard from "../components/UnitCard";
import { useState } from "react";
import { useEffect, useState } from "react";
import { SORT_OPTIONS } from "../data/sortOptions";
import SquaresIcon from "../components/icons/SQuaresIcon";
import ArrowLeftIcon from "../components/icons/ArrowLeftIcon";
import ArrowRightIcon from "../components/icons/ArrowRightIcon";
import { motion } from "motion/react";
import FilledHeartIcon from "../components/icons/FilledHeartIcon";
import { useSwipeable } from "react-swipeable";
import { useNavigate } from "react-router";
import { unitTypesFormatted } from "../data/unitTypes";
function FavoritesPage() {
const { favoriteUnits, setFavoriteUnits } = useFavoritesUnitsStore();
@@ -20,14 +23,51 @@ function FavoritesPage() {
const [sort, setSort] = useState<keyof typeof SORT_OPTIONS>(
"Sort by ascending price"
);
const [selectedProject, setSelectedProject] = useState<string>();
const [viewMode, setViewMode] = useState<"Collection" | "Compare">("Compare");
const [viewMode, setViewMode] = useState<"Collection" | "Compare">(
"Collection"
);
const [removeSimilar, setRemoveSimilar] = useState(false);
const [currentUnit, setCurrentUnit] = useState(0);
const [filteredFavoriteUnits, setFilteredFavoriteUnits] =
useState(favoriteUnits);
const [current, setCurrent] = useState(0);
useEffect(() => {
setFilteredFavoriteUnits(
selectedProject
? favoriteUnits.filter(({ project }) => project === selectedProject)
: favoriteUnits
);
}, [favoriteUnits, selectedProject]);
useEffect(() => window.scrollTo({ top: 0, behavior: "smooth" }), []);
useEffect(() => setCurrentUnit(0), [selectedProject]);
function handlePrev() {
setCurrentUnit(Math.max(currentUnit - 1, 0));
}
function handleNext() {
setCurrentUnit(
Math.min(
currentUnit + 1,
Math.max(0, filteredFavoriteUnits.length - (innerWidth >= 1440 ? 4 : 2))
)
);
}
const handlers = useSwipeable({
onSwipedLeft: handleNext,
onSwipedRight: handlePrev,
preventScrollOnSwipe: true,
touchEventOptions: {
passive: false,
},
trackMouse: true,
});
const navigate = useNavigate();
return (
<div className="flex flex-col 2xl:gap-y-[1.667vw] gap-y-6 2xl:pb-[1.667vw] pb-6">
@@ -69,12 +109,31 @@ function FavoritesPage() {
<p className="text-btn-l">{viewMode}</p>
</Button>
</div>
{viewMode === "Collection" ? (
{filteredFavoriteUnits.length === 0 ? (
<div className="2xl:aspect-[1376/396] md:max-2xl:aspect-[720/808] aspect-[328/240] flex justify-center items-center">
<div className="flex flex-col 2xl:gap-[2.778vw] gap-10 items-center">
<div className="flex flex-col 2xl:gap-[1.111vw] gap-4 items-center">
<p className="text-h3 font-medium text-[#00BED7] text-center">
There's nothing in your favorites.
</p>
<p className="text-m text-center">
Add apartments from the search or from
<br />
the master plan using the to favorites button
</p>
</div>
<Button
variant="primary"
size="large"
onClick={() => navigate("/search")}
>
Search
</Button>
</div>
</div>
) : viewMode === "Collection" ? (
<div className="2xl:grid-cols-4 md:max-2xl:grid-cols-2 grid 2xl:gap-[1.111vw] gap-4">
{favoriteUnits
.filter(
(unit) => !selectedProject || unit.project === selectedProject
)
{filteredFavoriteUnits
.sort((a, b) => {
if (sort === "Sort by ascending price") {
return a.salesPrice - b.salesPrice;
@@ -96,12 +155,12 @@ function FavoritesPage() {
<div className="flex justify-between">
<Button
onlyIcon
variant="secondary"
className="!bg-[#F3F3F2]"
onClick={() => {
setCurrent(Math.max(current - 1, 0));
}}
onClick={handlePrev}
disabled={currentUnit === 0}
>
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5 text-[#0D1922]">
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5">
<ArrowLeftIcon />
</span>
</Button>
@@ -125,45 +184,54 @@ function FavoritesPage() {
</div>
<Button
onlyIcon
variant="secondary"
disabled={
filteredFavoriteUnits.length - currentUnit <=
(innerWidth >= 1440 ? 4 : 2)
}
className="!bg-[#F3F3F2]"
onClick={() => {
setCurrent(
Math.min(current + 1, favoriteUnits.length * 6 - 1)
);
}}
onClick={handleNext}
>
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5 text-[#0D1922]">
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5">
<ArrowRightIcon />
</span>
</Button>
</div>
<hr className="border-[#E2E2DC] 2xl:border-t-[0.069vw] border-t" />
<div
{...handlers}
className="flex 2xl:gap-[2.222vw] md:max-2xl:gap-[clamp(24px,3.125vw,32px)] gap-[clamp(16px,4.444vw,24px)] transition-transform"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((25% + 2.222vw / 4) * ${-current}))`
? `translateX(calc((25% + 2.222vw / 4) * ${-currentUnit}))`
: innerWidth >= 768
? `translateX(calc((50% + clamp(12px, 1.5625vw, 16px)) * ${-current}))`
: `translateX(calc((50% + clamp(8px, 2.222vw, 12px)) * ${-current}))`,
? `translateX(calc((50% + clamp(12px, 1.5625vw, 16px)) * ${-currentUnit}))`
: `translateX(calc((50% + clamp(8px, 2.222vw, 12px)) * ${-currentUnit}))`,
}}
>
{favoriteUnits
.concat(favoriteUnits)
.concat(favoriteUnits)
.concat(favoriteUnits)
.concat(favoriteUnits)
.concat(favoriteUnits)
{filteredFavoriteUnits
.sort((a, b) => {
if (sort === "Sort by ascending price") {
return a.salesPrice - b.salesPrice;
} else if (sort === "Sort by descending price") {
return b.salesPrice - a.salesPrice;
} else if (sort === "Sort by ascending area") {
return a.squareFt - b.squareFt;
} else if (sort === "Sort by descending area") {
return b.squareFt - a.squareFt;
}
return 0;
})
.map((unit, index) => (
<div
key={index}
className="group flex-shrink-0 2xl:w-[calc(25%-3*2.222vw/4)] md:max-2xl:w-[calc(50%-clamp(12px,1.5625vw,16px))] w-[calc(50%-clamp(8px,2.222vw,12px))] 2xl:space-y-[3.889vw] md:max-2xl:space-y-[7.292vw] space-y-[11.111vw]"
className="group flex-shrink-0 2xl:w-[calc(25%-3*2.222vw/4)] md:max-2xl:w-[calc(50%-clamp(12px,1.5625vw,16px))] w-[calc(50%-clamp(8px,2.222vw,12px))]"
>
<div className="2xl:p-[1.111vw] 2xl:pt-[0.556vw] md:max-2xl:p-4 md:max-2xl:pt-2 p-3 flex flex-col 2xl:gap-[0.556vw] gap-2 2xl:rounded-[1.111vw] rounded-2xl bg-[#F3F3F2] 2xl:aspect-[310/334] md:max-2xl:aspect-[320/334] aspect-[144/158]">
<div className="2xl:p-[1.111vw] 2xl:pt-[0.556vw] md:max-2xl:p-4 md:max-2xl:pt-2 p-3 flex flex-col 2xl:gap-[0.556vw] gap-2 2xl:rounded-[1.111vw] rounded-2xl bg-[#F3F3F2] 2xl:aspect-[310/334] md:max-2xl:aspect-[320/334] aspect-[144/158] 2xl:mb-[1.806vw] md:max-xl:mb-[4.427vw] mb-[5.556vw]">
<div className="flex justify-between items-center">
<p className="md:text-s text-caption-m">
{unit.unitType}
{unitTypesFormatted.get(unit.unitType)}
<span className="max-md:hidden">
{`, ${unit.squareFt.toLocaleString(undefined, {
maximumFractionDigits: 2,
@@ -186,172 +254,104 @@ function FavoritesPage() {
</Button>
</div>
</div>
<div className="2xl:space-y-[1.25vw] md:max-2xl:space-y-[3.385vw] space-y-[4.444vw]">
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform group-not-first:opacity-0"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((100% + 2.222vw) * ${current}))`
: innerWidth >= 768
? `translateX(calc((100% + clamp(24px, 3.125vw, 32px)) * ${current}))`
: `translateX(calc((100% + clamp(16px, 4.444vw, 24px)) * ${current}))`,
}}
>
<p className="opacity-40 text-caption-s">Price</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw]" />
</div>
<p className="md:text-m text-caption-m text-[#00BED7]">{`AED ${Intl.NumberFormat(
"ar-AE",
{
<div className="2xl:space-y-[1.25vw] md:max-2xl:space-y-[3.385vw] space-y-[4.444vw] 2xl:mb-[3.889vw] md:max-2xl:mb-[7.292vw] mb-[11.111vw]">
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ salesPrice }) => salesPrice !== unit.salesPrice
)) && (
<UnitParameter
current={currentUnit}
paramName="Price"
value={`AED ${Intl.NumberFormat("ar-AE", {
currency: "AED",
minimumFractionDigits: 0,
}
).format(unit.salesPrice)}`}</p>
</div>
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((100% + 2.222vw) * ${current}))`
: innerWidth >= 768
? `translateX(calc((100% + clamp(24px, 3.125vw, 32px)) * ${current}))`
: `translateX(calc((100% + clamp(16px, 4.444vw, 24px)) * ${current}))`,
}}
>
<p className="group-first:block hidden opacity-40 text-caption-s">
Total area
</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw]" />
</div>
<p className="md:text-m text-caption-m">{`${unit.squareFt.toLocaleString(
undefined,
{
}).format(unit.salesPrice)}`}
/>
)}
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ squareFt }) => squareFt !== unit.squareFt
)) && (
<UnitParameter
current={currentUnit}
paramName="Total area"
value={`${unit.squareFt.toLocaleString(undefined, {
maximumFractionDigits: 2,
}
)} Sqft`}</p>
</div>
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((100% + 2.222vw) * ${current}))`
: innerWidth >= 768
? `translateX(calc((100% + clamp(24px, 3.125vw, 32px)) * ${current}))`
: `translateX(calc((100% + clamp(16px, 4.444vw, 24px)) * ${current}))`,
}}
>
<p className="group-first:block hidden opacity-40 text-caption-s">
Suite area
</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw]" />
</div>
<p className="md:text-m text-caption-m">{`${unit.suitsArea.toLocaleString(
undefined,
{
})} Sqft`}
/>
)}
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ suitsArea }) => suitsArea !== unit.suitsArea
)) && (
<UnitParameter
current={currentUnit}
paramName="Suite area"
value={`${unit.suitsArea.toLocaleString(undefined, {
maximumFractionDigits: 2,
}
)} Sqft`}</p>
</div>
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((100% + 2.222vw) * ${current}))`
: innerWidth >= 768
? `translateX(calc((100% + clamp(24px, 3.125vw, 32px)) * ${current}))`
: `translateX(calc((100% + clamp(16px, 4.444vw, 24px)) * ${current}))`,
}}
>
<p className="group-first:block hidden opacity-40 text-caption-s">
Balcony area
</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw]" />{" "}
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw] group-[&:nth-child(2n)]:-translate-x-[2.222vw] group-[&:nth-child(3n)]:-translate-x-[2.222vw]" />
</div>
<p className="md:text-m text-caption-m">{`${unit.balconyArea.toLocaleString(
undefined,
{
})} Sqft`}
/>
)}
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ project }) => project !== unit.project
)) && (
<UnitParameter
current={currentUnit}
paramName="Project"
value={unit.project}
/>
)}
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ balconyArea }) => balconyArea !== unit.balconyArea
)) && (
<UnitParameter
current={currentUnit}
paramName="Balcony area"
value={`${unit.balconyArea.toLocaleString(undefined, {
maximumFractionDigits: 2,
})} Sqft`}
/>
)}
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ unitNo }) => unitNo !== unit.unitNo
)) && (
<UnitParameter
current={currentUnit}
paramName="Number"
value={unit.unitNo}
/>
)}
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ project, unitNo }) =>
project !== unit.project ||
unitNo[0] !== unit.unitNo[0]
)) && (
<UnitParameter
current={currentUnit}
paramName="Section"
value={
unit.project === "Rove Home Marasi Drive"
? `${
unit.unitNo[0] === "W" ? "West" : "East"
} Wing`
: "—"
}
)} Sqft`}</p>
</div>
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((100% + 2.222vw) * ${current}))`
: innerWidth >= 768
? `translateX(calc((100% + clamp(24px, 3.125vw, 32px)) * ${current}))`
: `translateX(calc((100% + clamp(16px, 4.444vw, 24px)) * ${current}))`,
}}
>
<p className="group-first:block hidden opacity-40 text-caption-s">
Project
</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw]" />{" "}
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw] group-[&:nth-child(2n)]:-translate-x-[2.222vw] group-[&:nth-child(3n)]:-translate-x-[2.222vw]" />
</div>
<p className="md:text-[#00BED7] text-caption-m text-nowrap">
{unit.project}
</p>
</div>
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((100% + 2.222vw) * ${current}))`
: innerWidth >= 768
? `translateX(calc((100% + clamp(24px, 3.125vw, 32px)) * ${current}))`
: `translateX(calc((100% + clamp(16px, 4.444vw, 24px)) * ${current}))`,
}}
>
<p className="group-first:block hidden opacity-40 text-caption-s">
Number
</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw]" />{" "}
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw] group-[&:nth-child(2n)]:-translate-x-[2.222vw] group-[&:nth-child(3n)]:-translate-x-[2.222vw]" />
</div>
<p className="md:text-m text-caption-m">
{unit.unitNo}
</p>
</div>
{/* section */}
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((100% + 2.222vw) * ${current}))`
: innerWidth >= 768
? `translateX(calc((100% + clamp(24px, 3.125vw, 32px)) * ${current}))`
: `translateX(calc((100% + clamp(16px, 4.444vw, 24px)) * ${current}))`,
}}
>
<p className="group-first:block hidden opacity-40 text-caption-s">
Floor
</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw]" />{" "}
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px 2xl:min-w-[88.264vw] md:max-2xl:min-w-[80.078vw] min-w-[70.833vw] group-[&:nth-child(2n)]:-translate-x-[2.222vw] group-[&:nth-child(3n)]:-translate-x-[2.222vw]" />
</div>
<p className="md:text-m text-caption-m">
Floor {unit.floor}
</p>
</div>
/>
)}
{(!removeSimilar ||
filteredFavoriteUnits.some(
({ floor }) => floor !== unit.floor
)) && (
<UnitParameter
current={currentUnit}
paramName="Floor"
value={`Floor ${unit.floor}`}
/>
)}
</div>
<Button size="large" className="w-full">
Book
@@ -367,3 +367,41 @@ function FavoritesPage() {
}
export default FavoritesPage;
function UnitParameter({
paramName,
value,
current,
}: {
paramName: string;
value: string;
current: number;
}) {
return (
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-[0.556vw] space-y-[0.781vw]">
<div
className="flex items-center 2xl:gap-[1.111vw] gap-4 2xl:h-[calc(0.694vw*1.35)] h-[13.5px] transition-transform group-not-first:invisible 2xl:min-w-[calc(400%+3*2.222vw)] md:max-2xl:min-w-[calc(200%+clamp(24px,3.125vw,32px))] min-w-[calc(200%+clamp(16px,4.444vw,24px))]"
style={{
transform:
innerWidth >= 1440
? `translateX(calc((25% + 2.222vw / 4) * ${current}))`
: innerWidth >= 768
? `translateX(calc((50% + clamp(12px, 1.5625vw, 16px)) * ${current}))`
: `translateX(calc((50% + clamp(8px, 2.222vw, 12px)) * ${current}))`,
}}
>
<p className="opacity-40 text-caption-s">{paramName}</p>
<hr className="flex-1 border-[#E2E2DC] 2xl:h-[0.069vw] h-px" />
</div>
<p
className={clsx(
"md:text-m text-caption-m text-ellipsis [-webkit-line-clamp:1] line-clamp-1",
paramName === "Project" &&
"text-[#00BED7] 2xl:max-w-[calc((100vw-7*2.222vw)/4)] md:max-2xl:max-w-[calc(50vw-60px)] max-w-[calc(50vw-40px)]"
)}
>
{value}
</p>
</div>
);
}
+2 -2
View File
@@ -222,7 +222,7 @@ function SearchPage() {
{showButtons && (
<div
className={clsx(
"fixed left-1/2 -translate-x-1/2 flex justify-center 2xl:gap-[0.278vw] gap-2 transition-all",
"fixed left-1/2 -translate-x-1/2 flex justify-center 2xl:gap-[0.278vw] gap-2 transition-all z-2",
footerReached && !hasNextPage
? "top-[calc(100dvh-17.222vw)] translate-y-0"
: "top-[calc(100dvh-2.222vw)] -translate-y-full"
@@ -234,7 +234,7 @@ function SearchPage() {
</span>
<span className="text-caption-m">Filters</span>
{!!activeFiltersCount && (
<div className="absolute 2xl:top-[0.139vw] 2xl:right-[0.139vw] top-0.5 right-0.5 rounded-full w-4 text-caption-s aspect-square bg-white text-[#00BED7]">
<div className="absolute 2xl:top-[0.139vw] 2xl:right-[0.139vw] top-0.5 right-0.5 rounded-full w-4 text-caption-s aspect-square bg-white text-[#00BED7] flex justify-center items-center">
{activeFiltersCount}
</div>
)}
+2 -2
View File
@@ -4,12 +4,12 @@ export interface IUnit {
project: string;
floor: string;
unitType: string;
noOfBathrooms: number;
unitView: string;
state: string;
noOfBathrooms: number;
suitsArea: number;
squareFt: number;
noOfParkingSpace: number;
salesPrice: number;
state: string;
balconyArea: number;
}