Merge branch 'main' of http://192.168.1.163:3000/inmake/irth-new-client
This commit is contained in:
+122
-50
@@ -1,112 +1,184 @@
|
||||
import { Link } from 'react-router';
|
||||
import YoutubeIcon from './icons/YoutubeIcon';
|
||||
import InstagramIcon from './icons/InstagramIcon';
|
||||
import FacebookIcon from './icons/FacebookIcon';
|
||||
import LinkedInIcon from './icons/LinkedInIcon';
|
||||
import TwitterIcon from './icons/TwitterIcon';
|
||||
import ChevronDownIcon from './icons/ChevronDownIcon';
|
||||
import { Link } from "react-router";
|
||||
import YoutubeIcon from "./icons/YoutubeIcon";
|
||||
import InstagramIcon from "./icons/InstagramIcon";
|
||||
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'>
|
||||
<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
|
||||
src='/images/logo.svg'
|
||||
className='2xl:w-[5.972vw] w-[86px] cursor-pointer'
|
||||
src="/images/logo.svg"
|
||||
className="2xl:w-[5.972vw] w-[86px] cursor-pointer"
|
||||
onClick={() => {
|
||||
window.location.href = '/';
|
||||
window.location.href = "/";
|
||||
}}
|
||||
alt='IRTH'
|
||||
alt="IRTH"
|
||||
/>
|
||||
|
||||
<p className='2xl:max-w-[17.083vw] text-s text-[#0D1922]/40 2xl:col-start-1 md:max-2xl:col-start-3 max-2xl:col-span-3 md:max-2xl:row-start-2 max-md:row-start-3 md:max-2xl:mt-[52px] max-md:mt-12'>
|
||||
<p className="2xl:max-w-[17.083vw] text-s text-[#0D1922]/40 2xl:col-start-1 md:max-2xl:col-start-3 max-2xl:col-span-3 md:max-2xl:row-start-2 max-md:row-start-3 md:max-2xl:mt-[52px] max-md:mt-12">
|
||||
For more information, visit
|
||||
<br />
|
||||
our website:
|
||||
<Link className='text-[#00BED7] underline' to={'https://www.irth.ae'}>
|
||||
<Link className="text-[#00BED7] underline" to={"https://www.irth.ae"}>
|
||||
www.irth.ae
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<div className='2xl:space-y-[0.833vw] space-y-3 md:max-2xl:col-start-3 max-2xl:col-span-3 md:max-2xl:row-start-3 max-md:row-start-4 max-md:mt-6'>
|
||||
<p className='text-s text-[#0D1922]/40'>Follow us for more:</p>
|
||||
<div className='flex 2xl:gap-[0.278vw] gap-1'>
|
||||
<div className='2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded'>
|
||||
<div className='2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70'>
|
||||
<div className="2xl:space-y-[0.833vw] space-y-3 md:max-2xl:col-start-3 max-2xl:col-span-3 md:max-2xl:row-start-3 max-md:row-start-4 max-md:mt-6">
|
||||
<p className="text-s text-[#0D1922]/40">Follow us for more:</p>
|
||||
<div className="flex 2xl:gap-[0.278vw] gap-1">
|
||||
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
|
||||
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
|
||||
<YoutubeIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className='2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded'>
|
||||
<div className='2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70'>
|
||||
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
|
||||
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
|
||||
<InstagramIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className='2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded'>
|
||||
<div className='2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70'>
|
||||
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
|
||||
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
|
||||
<FacebookIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className='2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded'>
|
||||
<div className='2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70'>
|
||||
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
|
||||
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
|
||||
<LinkedInIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className='2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded'>
|
||||
<div className='2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70'>
|
||||
<div className="2xl:p-[0.417vw] p-1.5 bg-[#E2E2DC] 2xl:rounded-[0.278vw] rounded">
|
||||
<div className="2xl:w-[2.222vw] 2xl:h-[2.222vw] md:max-2xl:w-8 md:max-2xl:h-8 w-9 h-9 text-[#0D1922]/70">
|
||||
<TwitterIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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'>
|
||||
<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'
|
||||
to={"/"}
|
||||
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'
|
||||
to={"/unit-types"}
|
||||
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'
|
||||
to={"/about-irth"}
|
||||
className="md:text-btn-l text-btn-m flex-1 content-center md:my-4 my-[13px] text-[#0D1922]/70"
|
||||
>
|
||||
About IRTH
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<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'>
|
||||
<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'
|
||||
to={"/favorites"}
|
||||
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-2.5 text-[#0D1922]/70'
|
||||
to={"/search"}
|
||||
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 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={'/'}
|
||||
className='md:text-caption-m text-caption-s max-2xl:text-[#73787C] text-[#0D1922]/70'
|
||||
<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]">
|
||||
<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>
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
@@ -67,7 +68,7 @@ function Header() {
|
||||
ref={burgerRef}
|
||||
onlyIcon
|
||||
variant="secondary"
|
||||
className="2xl:hidden !outline !outline-[#E2E2DC] md:mr-6 mr-4"
|
||||
className="2xl:hidden !bg-[#F3F3F2] md:mr-6 mr-4"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (modal) setModal(null);
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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]">
|
||||
|
||||
@@ -67,7 +67,10 @@ function ProjectSelect<T extends boolean = false>({
|
||||
))}
|
||||
</div>
|
||||
<Select
|
||||
options={["All", ...projects.map((project) => project.title)]}
|
||||
options={[
|
||||
...(withAll ? ["All"] : []),
|
||||
...projects.map((project) => project.title),
|
||||
]}
|
||||
onSelect={(option) =>
|
||||
setSelectedProject(
|
||||
projects.find((project) => project.title === option) ||
|
||||
|
||||
@@ -388,6 +388,7 @@ function SearchFilters({
|
||||
prev.delete("unitTypes");
|
||||
return prev;
|
||||
});
|
||||
if (inModal) setInModal(false);
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
@@ -644,19 +645,14 @@ function SearchFilters({
|
||||
onlyIcon={!inModal && innerWidth < 768}
|
||||
onClick={resetFilters}
|
||||
className={clsx(
|
||||
!inModal && "max-md:bg-[#F3F3F2]",
|
||||
"max-md:!transition-none"
|
||||
!inModal ? "max-md:bg-[#F3F3F2]" : "max-md:w-full",
|
||||
"max-md:!transition-none justify-center"
|
||||
)}
|
||||
>
|
||||
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5 text-[#0D1922]/70">
|
||||
<RestartIcon />
|
||||
</span>
|
||||
<p
|
||||
className={clsx(
|
||||
"text-s max-md:w-full",
|
||||
!inModal && "max-md:hidden"
|
||||
)}
|
||||
>
|
||||
<p className={clsx("text-s", !inModal && "max-md:hidden")}>
|
||||
Reset filters
|
||||
</p>
|
||||
</Button>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -4,7 +4,7 @@ function RestartIcon() {
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="m14.26 3.583.95-.557a.75.75 0 0 0-.758-1.294L11.848 3.26a1 1 0 0 0-.33 1.413l1.53 2.32a.75.75 0 0 0 1.252-.826l-.806-1.224c3.288.687 5.756 3.596 5.756 7.078 0 3.991-3.244 7.23-7.25 7.23s-7.25-3.239-7.25-7.23a7.2 7.2 0 0 1 1.838-4.812c.276-.309.302-.78.023-1.087s-.755-.331-1.037-.027A8.7 8.7 0 0 0 3.25 12.02c0 4.823 3.92 8.73 8.75 8.73s8.75-3.907 8.75-8.73c0-4.044-2.753-7.443-6.49-8.437"
|
||||
d="m14.26 3.583.95-.557a.75.75 0 0 0-.758-1.294L11.848 3.26a1 1 0 0 0-.33 1.413l1.53 2.32a.75.75 0 0 0 1.252-.826l-.806-1.223c3.288.686 5.756 3.595 5.756 7.077 0 3.991-3.244 7.23-7.25 7.23s-7.25-3.239-7.25-7.23a7.2 7.2 0 0 1 1.838-4.812c.276-.309.302-.78.023-1.087s-.755-.331-1.037-.027A8.7 8.7 0 0 0 3.25 12.02c0 4.823 3.92 8.73 8.75 8.73s8.75-3.907 8.75-8.73c0-4.043-2.753-7.443-6.49-8.437"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
function SquaresIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10 13.75H5a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h5a.25.25 0 0 0 .25-.25v-5l-.005-.05a.25.25 0 0 0-.245-.2Zm9 0h-5a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h5a.25.25 0 0 0 .25-.25v-5l-.005-.05a.25.25 0 0 0-.245-.2Zm0-9h-5a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h5a.25.25 0 0 0 .25-.25V5l-.005-.05a.25.25 0 0 0-.245-.2Zm-9 0H5a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h5a.25.25 0 0 0 .25-.25V5l-.005-.05a.25.25 0 0 0-.245-.2Z"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default SquaresIcon;
|
||||
@@ -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"],
|
||||
]);
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
@import url('/fonts/Usual/stylesheet.css');
|
||||
@import url('/fonts/Mixcase/stylesheet.css');
|
||||
@import 'tailwindcss';
|
||||
@import url("/fonts/Usual/stylesheet.css");
|
||||
@import url("/fonts/Mixcase/stylesheet.css");
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--breakpoint-2xl: 1440px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Usual', sans-serif;
|
||||
font-family: "Usual", sans-serif;
|
||||
color: #0d1922;
|
||||
}
|
||||
|
||||
@@ -73,5 +73,5 @@ button {
|
||||
}
|
||||
|
||||
@utility font-mixcase-unmixed {
|
||||
font-family: 'Mixcase Unmixed', sans-serif;
|
||||
font-family: "Mixcase Unmixed", sans-serif;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import Header from '../components/Header';
|
||||
import { Outlet, useLocation } from 'react-router';
|
||||
import Footer from '../components/Footer';
|
||||
import clsx from 'clsx';
|
||||
import Header from "../components/Header";
|
||||
import { Outlet, useLocation } from "react-router";
|
||||
import Footer from "../components/Footer";
|
||||
import clsx from "clsx";
|
||||
|
||||
function DefaultLayout() {
|
||||
const { pathname } = useLocation();
|
||||
console.log(pathname);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'min-h-dvh flex flex-col select-none',
|
||||
pathname.endsWith('/about') ? 'bg-white' : 'bg-[#F3F3F2]'
|
||||
"min-h-dvh flex flex-col select-none",
|
||||
pathname.endsWith("/about") ? "bg-white" : "bg-[#F3F3F2]"
|
||||
)}
|
||||
>
|
||||
<Header />
|
||||
<div className='flex-1 flex flex-col justify-between'>
|
||||
<div className="flex-1 flex flex-col justify-between">
|
||||
<Outlet />
|
||||
</div>
|
||||
<Footer />
|
||||
|
||||
+25
-25
@@ -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} />
|
||||
|
||||
+360
-33
@@ -6,27 +6,78 @@ 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 } = useFavoritesUnitsStore();
|
||||
const { favoriteUnits, setFavoriteUnits } = useFavoritesUnitsStore();
|
||||
|
||||
const [sort, setSort] = useState<keyof typeof SORT_OPTIONS>(
|
||||
"Sort by ascending price"
|
||||
);
|
||||
|
||||
const [selectedProject, setSelectedProject] = useState<string>();
|
||||
const [viewMode, setViewMode] = useState<"Collection" | "Compare">(
|
||||
"Collection"
|
||||
);
|
||||
const [removeSimilar, setRemoveSimilar] = useState(false);
|
||||
const [currentUnit, setCurrentUnit] = useState(0);
|
||||
const [filteredFavoriteUnits, setFilteredFavoriteUnits] =
|
||||
useState(favoriteUnits);
|
||||
|
||||
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 gap-6">
|
||||
<div className="flex flex-col 2xl:gap-y-[1.667vw] gap-y-6 2xl:pb-[1.667vw] pb-6">
|
||||
<div
|
||||
className={clsx(
|
||||
"2xl:p-[2.222vw] md:max-2xl:p-6 p-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",
|
||||
"2xl:px-[2.222vw] md:max-2xl:px-6 px-4"
|
||||
)}
|
||||
>
|
||||
<div className="2xl:space-y-[1.111vw] space-y-4">
|
||||
<div className="flex flex-col 2xl:gap-y-[1.111vw] gap-y-4">
|
||||
<p className="text-h2 font-medium">Favorites</p>
|
||||
<ProjectSelect
|
||||
projects={projects}
|
||||
@@ -36,45 +87,321 @@ function FavoritesPage() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="2xl:px-[2.222vw] md:max-2xl:px-6 px-4">
|
||||
<div className="flex 2xl:gap-[1.111vw] gap-4">
|
||||
<div className="2xl:px-[2.222vw] md:max-2xl:px-6 px-4 flex flex-col 2xl:gap-y-[1.667vw] gap-y-6">
|
||||
<div className="flex 2xl:gap-[1.111vw] gap-4 max-md:flex-col">
|
||||
<Select
|
||||
options={Object.keys(SORT_OPTIONS)}
|
||||
defaultOption={sort}
|
||||
onSelect={(opt) => setSort(opt as keyof typeof SORT_OPTIONS)}
|
||||
className="2xl:w-[22.778vw] md:max-2xl:max-w-[45.833vw]"
|
||||
className="2xl:w-[22.778vw] w-full"
|
||||
/>
|
||||
<Button variant="secondary" size="large">
|
||||
<div className="2xl:w-[1.667vw] 2xl:h-[1.667vw] w-6 h-6">
|
||||
<ChartIcon />
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="large"
|
||||
className="max-2xl:w-full"
|
||||
onClick={() =>
|
||||
setViewMode(viewMode === "Collection" ? "Compare" : "Collection")
|
||||
}
|
||||
>
|
||||
<div className="2xl:w-[1.667vw] 2xl:h-[1.667vw] w-6 h-6 2xl:[&_path]:stroke-[clamp(1.5px,0.104vw,2px)] [&_path]:stroke-[1.5px]">
|
||||
{viewMode === "Compare" ? <ChartIcon /> : <SquaresIcon />}
|
||||
</div>
|
||||
<p className="text-btn-l">Compare</p>
|
||||
<p className="text-btn-l">{viewMode}</p>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="2xl:grid-cols-4 md:max-2xl:grid-cols-2 grid 2xl:gap-[1.111vw] gap-4 py-6">
|
||||
{favoriteUnits
|
||||
.filter(
|
||||
(unit) => !selectedProject || unit.project === selectedProject
|
||||
)
|
||||
.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) => (
|
||||
<UnitCard key={unit.id} unit={unit} />
|
||||
))}
|
||||
</div>
|
||||
{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">
|
||||
{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) => (
|
||||
<UnitCard key={unit.id} unit={unit} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="2xl:rounded-[1.667vw] rounded-3xl 2xl:p-[2.222vw] md:max-2xl:p-[clamp(24px,3.125vw,32px)] p-[clamp(16px,4.444vw,24px)] bg-white flex flex-col 2xl:gap-[1.111vw] gap-4 relative overflow-hidden">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
onlyIcon
|
||||
variant="secondary"
|
||||
className="!bg-[#F3F3F2]"
|
||||
onClick={handlePrev}
|
||||
disabled={currentUnit === 0}
|
||||
>
|
||||
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5">
|
||||
<ArrowLeftIcon />
|
||||
</span>
|
||||
</Button>
|
||||
<div className="flex 2xl:gap-[0.556vw] gap-2 items-center">
|
||||
<div
|
||||
className="2xl:w-[2.778vw] w-10 2xl:p-[0.139vw] p-0.5 rounded-full bg-[#E2E2DC] cursor-pointer"
|
||||
onClick={() => setRemoveSimilar((prev) => !prev)}
|
||||
>
|
||||
<motion.div
|
||||
className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5 rounded-full bg-white"
|
||||
animate={{
|
||||
x: removeSimilar ? "80%" : 0,
|
||||
}}
|
||||
transition={{ bounce: 0, duration: 0.15 }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-s">
|
||||
Remove similar
|
||||
<span className="max-md:hidden"> parameters</span>
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onlyIcon
|
||||
variant="secondary"
|
||||
disabled={
|
||||
filteredFavoriteUnits.length - currentUnit <=
|
||||
(innerWidth >= 1440 ? 4 : 2)
|
||||
}
|
||||
className="!bg-[#F3F3F2]"
|
||||
onClick={handleNext}
|
||||
>
|
||||
<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) * ${-currentUnit}))`
|
||||
: innerWidth >= 768
|
||||
? `translateX(calc((50% + clamp(12px, 1.5625vw, 16px)) * ${-currentUnit}))`
|
||||
: `translateX(calc((50% + clamp(8px, 2.222vw, 12px)) * ${-currentUnit}))`,
|
||||
}}
|
||||
>
|
||||
{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))]"
|
||||
>
|
||||
<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">
|
||||
{unitTypesFormatted.get(unit.unitType)}
|
||||
<span className="max-md:hidden">
|
||||
{`, ${unit.squareFt.toLocaleString(undefined, {
|
||||
maximumFractionDigits: 2,
|
||||
})} Sqft`}
|
||||
</span>
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onlyIcon
|
||||
className="!bg-[#F3F3F2]"
|
||||
onClick={() =>
|
||||
setFavoriteUnits(
|
||||
favoriteUnits.filter((u) => u.id !== unit.id)
|
||||
)
|
||||
}
|
||||
>
|
||||
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5">
|
||||
<FilledHeartIcon />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<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)}`}
|
||||
/>
|
||||
)}
|
||||
{(!removeSimilar ||
|
||||
filteredFavoriteUnits.some(
|
||||
({ squareFt }) => squareFt !== unit.squareFt
|
||||
)) && (
|
||||
<UnitParameter
|
||||
current={currentUnit}
|
||||
paramName="Total area"
|
||||
value={`${unit.squareFt.toLocaleString(undefined, {
|
||||
maximumFractionDigits: 2,
|
||||
})} Sqft`}
|
||||
/>
|
||||
)}
|
||||
{(!removeSimilar ||
|
||||
filteredFavoriteUnits.some(
|
||||
({ suitsArea }) => suitsArea !== unit.suitsArea
|
||||
)) && (
|
||||
<UnitParameter
|
||||
current={currentUnit}
|
||||
paramName="Suite area"
|
||||
value={`${unit.suitsArea.toLocaleString(undefined, {
|
||||
maximumFractionDigits: 2,
|
||||
})} 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`
|
||||
: "—"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{(!removeSimilar ||
|
||||
filteredFavoriteUnits.some(
|
||||
({ floor }) => floor !== unit.floor
|
||||
)) && (
|
||||
<UnitParameter
|
||||
current={currentUnit}
|
||||
paramName="Floor"
|
||||
value={`Floor ${unit.floor}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Button size="large" className="w-full">
|
||||
Book
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user