Add new popup images for Downtown, Dubai Marina, and Marasi Drive; update existing images for better quality. Remove TestPage route from main.tsx and introduce UnitSlider component for enhanced unit display functionality.
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
@@ -39,9 +39,9 @@ export default function AboutHQ() {
|
||||
|
||||
<div className="2xl:space-y-[1.667vw] md:max-2xl:space-y-6 space-y-4 relative">
|
||||
<h1 className="2xl:text-[5vw] md:max-2xl:text-7xl text-[32px] leading-none tracking-[-0.07em] font-mixcase-unmixed font-medium whitespace-pre-line">
|
||||
{`Rove Home HQ —
|
||||
Work looks different
|
||||
here`}
|
||||
{`HQ by Rove instead —
|
||||
Work looks different
|
||||
here`}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function AboutHQ() {
|
||||
{`Welcome to the office you
|
||||
actually want to show up for`}
|
||||
</h4>
|
||||
<p className="opacity-70 text-s 2xl:w-1/4 md:max-2xl:w-1/3">
|
||||
<p className="text-s 2xl:w-1/4 md:max-2xl:w-1/3 opacity-70">
|
||||
HQ by Rove was born out of a question: what if the office could
|
||||
feel alive again? Now, the first ever hospitality-branded office
|
||||
building in Dubai is here to answer it. Starting in Marasi Bay
|
||||
@@ -82,7 +82,7 @@ export default function AboutHQ() {
|
||||
<span className="opacity-40">More than an office,</span>{" "}
|
||||
<br className="2xl:hidden" /> a lifestyle.
|
||||
</h2>
|
||||
<p className="text-center whitespace-pre-line opacity-70 text-s max-md:whitespace-normal max-md:mb-12 md:max-2xl:mb-16">
|
||||
<p className="text-s max-md:whitespace-normal max-md:mb-12 md:max-2xl:mb-16 text-center whitespace-pre-line opacity-70">
|
||||
{`Living rooms became boardrooms, kitchens became creative hubs.
|
||||
But as the world returned, the office didn’t keep up. HQ by Rove is the
|
||||
answer - an office with a living touch.`}
|
||||
@@ -274,7 +274,7 @@ export default function AboutHQ() {
|
||||
</div>
|
||||
<div className="px-[2.778vw] flex flex-col gap-[3.333vw] max-2xl:px-0 md:max-2xl:gap-[6.25vw]">
|
||||
<div className="space-y-[1.111vw]">
|
||||
<h2 className="font-medium whitespace-pre-line text-h2 max-2xl:mb-4">
|
||||
<h2 className="text-h2 max-2xl:mb-4 font-medium whitespace-pre-line">
|
||||
{`Work looks different here`}
|
||||
</h2>
|
||||
<p className="opacity-40 text-s max-md:mb-[32px] md:max-2xl:max-w-[57.943vw]">
|
||||
|
||||
@@ -6,12 +6,14 @@ interface FloorPlanMarasiDriveEastProps {
|
||||
selectedFloor: string | null;
|
||||
unitsOnFloor: Unit[];
|
||||
chosenUnit?: Unit;
|
||||
isUnitPage?: boolean;
|
||||
}
|
||||
|
||||
function FloorPlanMarasiDriveEast({
|
||||
selectedFloor,
|
||||
unitsOnFloor,
|
||||
chosenUnit,
|
||||
isUnitPage,
|
||||
}: FloorPlanMarasiDriveEastProps) {
|
||||
return (
|
||||
<GenericFloorPlan
|
||||
@@ -22,6 +24,7 @@ function FloorPlanMarasiDriveEast({
|
||||
selectedFloor={selectedFloor}
|
||||
unitsOnFloor={unitsOnFloor}
|
||||
chosenUnit={chosenUnit}
|
||||
isUnitPage={isUnitPage}
|
||||
wing="East"
|
||||
>
|
||||
<g>
|
||||
|
||||
@@ -6,12 +6,14 @@ interface FloorPlanMarasiDriveWestLowerProps {
|
||||
selectedFloor: string | null;
|
||||
unitsOnFloor: Unit[];
|
||||
chosenUnit?: Unit;
|
||||
isUnitPage?: boolean;
|
||||
}
|
||||
|
||||
function FloorPlanMarasiDriveWestLower({
|
||||
selectedFloor,
|
||||
unitsOnFloor,
|
||||
chosenUnit,
|
||||
isUnitPage,
|
||||
}: FloorPlanMarasiDriveWestLowerProps) {
|
||||
return (
|
||||
<GenericFloorPlan
|
||||
@@ -22,6 +24,7 @@ function FloorPlanMarasiDriveWestLower({
|
||||
selectedFloor={selectedFloor}
|
||||
unitsOnFloor={unitsOnFloor}
|
||||
chosenUnit={chosenUnit}
|
||||
isUnitPage={isUnitPage}
|
||||
wing="West"
|
||||
>
|
||||
<g>
|
||||
|
||||
@@ -6,12 +6,14 @@ interface FloorPlanMarasiDriveWestUpperProps {
|
||||
selectedFloor: string | null;
|
||||
unitsOnFloor: Unit[];
|
||||
chosenUnit?: Unit;
|
||||
isUnitPage?: boolean;
|
||||
}
|
||||
|
||||
function FloorPlanMarasiDriveWestUpper({
|
||||
unitsOnFloor,
|
||||
selectedFloor,
|
||||
chosenUnit,
|
||||
isUnitPage,
|
||||
}: FloorPlanMarasiDriveWestUpperProps) {
|
||||
return (
|
||||
<GenericFloorPlan
|
||||
@@ -22,6 +24,7 @@ function FloorPlanMarasiDriveWestUpper({
|
||||
selectedFloor={selectedFloor}
|
||||
unitsOnFloor={unitsOnFloor}
|
||||
chosenUnit={chosenUnit}
|
||||
isUnitPage={isUnitPage}
|
||||
wing="West"
|
||||
>
|
||||
<g>
|
||||
|
||||
@@ -64,7 +64,7 @@ function Marker({
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
"absolute bottom-[10%]",
|
||||
"absolute bottom-[10%]ф top-1/2 -translate-y-1/2",
|
||||
marker.popupPosition === "left" ? "right-full" : "left-full"
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,16 +1,50 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { api } from "../api/ky";
|
||||
import { Unit } from "../types/IUnit";
|
||||
import GenericFloorPlan from "./floor-plans/GenericFloorPlan";
|
||||
import { getFloorPlanConfigForUnit } from "../data/floor-plan-config";
|
||||
import { getSlugFromProjectName } from "../data/complex-config";
|
||||
import { ComplexName } from "../types/ComplexName";
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────
|
||||
|
||||
function getFloorParam(complexName: ComplexName, unit: Unit): string {
|
||||
if (complexName === "dubai-marina" && (unit.floor === 39 || unit.floor === 40))
|
||||
return "39";
|
||||
if (complexName === "dubai-marina" && (unit.floor === 41 || unit.floor === 42))
|
||||
return "41";
|
||||
if (complexName === "hq" && (unit.floor === 29 || unit.floor === 30))
|
||||
return "29";
|
||||
return unit.floor.toString();
|
||||
}
|
||||
|
||||
// ─── Component ───────────────────────────────────────────
|
||||
|
||||
function OnFloorMask({ unit }: { unit: Unit }) {
|
||||
const complexName = getSlugFromProjectName(unit.project);
|
||||
if (!complexName) return null;
|
||||
const config = complexName
|
||||
? getFloorPlanConfigForUnit(complexName, unit)
|
||||
: null;
|
||||
const floorParam = complexName ? getFloorParam(complexName, unit) : "";
|
||||
|
||||
const config = getFloorPlanConfigForUnit(complexName, unit);
|
||||
if (!config) return null;
|
||||
const { data: unitsOnFloor } = useQuery({
|
||||
queryKey: ["units-on-floor", complexName, floorParam, unit.wing],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(
|
||||
`units/on-floor?project=${complexName}&floor=${floorParam}${
|
||||
complexName === "marasi-drive" && unit.wing
|
||||
? `&wing=${unit.wing}`
|
||||
: ""
|
||||
}`
|
||||
)
|
||||
.json<Unit[]>(),
|
||||
enabled: !!complexName && !!config,
|
||||
});
|
||||
|
||||
if (!complexName || !config) return null;
|
||||
|
||||
const units = unitsOnFloor ?? [unit];
|
||||
|
||||
// Marasi Drive uses component-based rendering (inline SVG)
|
||||
if (config.component) {
|
||||
@@ -18,8 +52,9 @@ function OnFloorMask({ unit }: { unit: Unit }) {
|
||||
return (
|
||||
<Component
|
||||
selectedFloor={unit.floor.toString()}
|
||||
unitsOnFloor={[unit]}
|
||||
unitsOnFloor={units}
|
||||
chosenUnit={unit}
|
||||
isUnitPage
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -34,8 +69,9 @@ function OnFloorMask({ unit }: { unit: Unit }) {
|
||||
getMaskKey={config.getMaskKey}
|
||||
filterUnits={config.filterUnits}
|
||||
selectedFloor={unit.floor.toString()}
|
||||
unitsOnFloor={[unit]}
|
||||
unitsOnFloor={units}
|
||||
chosenUnit={unit}
|
||||
isUnitPage
|
||||
wing={config.wing}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -61,13 +61,14 @@ function PopupContainer() {
|
||||
<div
|
||||
className={clsx(
|
||||
"max-md:hidden absolute 2xl:border-[0.556vw_0px_0.486vw_0.556vw] [border-width:8px_0px_7px_8px] [border-color:_transparent_transparent_transparent_#fff]",
|
||||
side === "left" && "top-1/2 [y:-50%] left-full [x:1px]",
|
||||
side === "left" &&
|
||||
"top-1/2 -translate-y-1/2 left-full -translate-x-[1px]",
|
||||
side === "right" &&
|
||||
"top-1/2 [y:-50%] right-full [x:1px] [rotate:180deg]",
|
||||
"top-1/2 translate-y-1/2 right-full -translate-x-[1px] [rotate:180deg]",
|
||||
side === "top" &&
|
||||
"left-1/2 [x:100%] absolute top-full [y:1px] [rotate:90deg] origin-top-left",
|
||||
"left-1/2 -translate-y-full -translate-x-[1px] absolute top-full [rotate:90deg] origin-top-left",
|
||||
side === "bottom" &&
|
||||
"left-1/2 [x:100%] absolute bottom-full [y:1px] [rotate:-90deg] origin-bottom-left"
|
||||
"left-1/2 translate-y-full -translate-x-[1px] absolute bottom-full [rotate:-90deg] origin-bottom-left"
|
||||
)}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
@@ -173,7 +173,7 @@ function SearchFilters({
|
||||
)}
|
||||
<div className="2xl:space-y-[2.222vw] space-y-8">
|
||||
<div className="2xl:space-y-[1.111vw] space-y-4">
|
||||
<p className="font-medium text-h2">
|
||||
<p className="text-h2 font-medium">
|
||||
{inModal ? "Filters" : "Search"}
|
||||
</p>
|
||||
<div className={clsx(!inModal && "max-md:hidden")}>
|
||||
@@ -372,7 +372,7 @@ function SearchFilters({
|
||||
<div className="w-5 h-5">
|
||||
<FiltersIcon />
|
||||
</div>
|
||||
<p className="text-sm leading-0">Filters</p>
|
||||
<p className="leading-0 text-sm">Filters</p>
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
||||
@@ -203,30 +203,29 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
|
||||
<span className="max-md:hidden">About</span>
|
||||
</Button>
|
||||
<p className="absolute md:text-h4 text-h5 font-medium text-white -translate-x-1/2 select-none left-1/2 2xl:top-[2.5vw] md:max-2xl:top-[3.646vw] top-[7.5vw] drop-shadow pointer-events-none">
|
||||
ROVE Home{" "}
|
||||
{markers.find((marker) => marker.name === complexName)?.title}
|
||||
</p>
|
||||
<Button
|
||||
onlyIcon
|
||||
variant="secondary"
|
||||
className="absolute top-1/2 -translate-y-1/2 2xl:left-[31.111vw] md:max-2xl:left-[8.854vw] left-4 !bg-[#0D1922] !bg-opacity-40 backdrop-blur-md"
|
||||
variant="fab"
|
||||
className="absolute top-1/2 -translate-y-1/2 2xl:left-[31.111vw] md:max-2xl:left-[8.854vw] left-4"
|
||||
roundedFull
|
||||
disabled={isAnimating || !isShowVideo}
|
||||
onClick={() => handleSwipe("prev")}
|
||||
>
|
||||
<span className="2xl:size-[1.111vw] size-4 text-white">
|
||||
<span className="2xl:size-[1.111vw] size-4">
|
||||
<ArrowLeftIcon />
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
onlyIcon
|
||||
variant="secondary"
|
||||
className="absolute top-1/2 -translate-y-1/2 2xl:right-[31.111vw] md:max-2xl:right-[8.854vw] right-4 !bg-[#0D1922] !bg-opacity-40 backdrop-blur-md"
|
||||
variant="fab"
|
||||
className="absolute top-1/2 -translate-y-1/2 2xl:right-[31.111vw] md:max-2xl:right-[8.854vw] right-4"
|
||||
roundedFull
|
||||
disabled={isAnimating || !isShowVideo}
|
||||
onClick={() => handleSwipe("next")}
|
||||
>
|
||||
<span className="2xl:size-[1.111vw] size-4 text-white">
|
||||
<span className="2xl:size-[1.111vw] size-4">
|
||||
<ArrowRightIcon />
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
@@ -25,7 +25,11 @@ function UnitTypeCard({ project, type }: { project: Project; type: UnitType }) {
|
||||
<img
|
||||
src={`/images/unit-types/${project.slug}/${type.slug}${
|
||||
type.slug.includes("loft") ? "-lower" : ""
|
||||
}${project.hasSides !== false && type.slug !== "2-bedroom-b" ? "-left" : ""}.jpg`}
|
||||
}${
|
||||
project.hasSides !== false && type.slug !== "2-bedroom-b"
|
||||
? "-left"
|
||||
: ""
|
||||
}.jpg`}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -60,24 +60,28 @@ function UnitTypeImageWithMarkers({
|
||||
}}
|
||||
/>
|
||||
{filteredLegend.map((item, index) => {
|
||||
const coords = unitTypeVariant?.endsWith("left") ? item.left : item.right;
|
||||
const coords = unitTypeVariant?.endsWith("left")
|
||||
? item.left
|
||||
: item.right;
|
||||
return (
|
||||
<rect
|
||||
key={`marker-${index}`}
|
||||
ref={refRect}
|
||||
x={coords.x}
|
||||
y={coords.y}
|
||||
width={16}
|
||||
height={16}
|
||||
rx={8}
|
||||
className="stroke-white fill-[#00BED7] hover:fill-white transition-colors cursor-pointer max-md:hidden"
|
||||
width={12}
|
||||
height={12}
|
||||
rx={6}
|
||||
className="stroke-white hover:fill-[#00BED7] fill-white transition-colors max-md:hidden"
|
||||
onMouseEnter={() => setHoveredIndex(index)}
|
||||
onMouseLeave={() => setHoveredIndex(null)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{filteredLegend.map((item, index) => {
|
||||
const coords = unitTypeVariant?.endsWith("left") ? item.left : item.right;
|
||||
const coords = unitTypeVariant?.endsWith("left")
|
||||
? item.left
|
||||
: item.right;
|
||||
return (
|
||||
<g
|
||||
key={`tooltip-${index}`}
|
||||
|
||||
@@ -43,7 +43,7 @@ function AmenitiesFloorView({ floor }: AmenitiesFloorViewProps) {
|
||||
<div className="2xl:space-y-[2.222vw] space-y-8">
|
||||
<div className="2xl:space-y-[1.667vw] space-y-6">
|
||||
<div className="2xl:space-y-[0.556vw] space-y-2 border-b border-[#E2E2DC] 2xl:pb-[1.667vw] pb-6">
|
||||
<p className="font-medium text-h4">{floor.displayName}</p>
|
||||
<p className="text-h4 font-medium">{floor.displayName}</p>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
text={`${floor.amenitiesCount.total} Amenities`}
|
||||
@@ -80,13 +80,13 @@ function AmenitiesFloorView({ floor }: AmenitiesFloorViewProps) {
|
||||
<Button
|
||||
variant="cta"
|
||||
className="absolute 2xl:top-[1.667vw] 2xl:right-[1.667vw] md:max-2xl:top-6 md:max-2xl:right-6 top-4 right-4"
|
||||
onlyIcon
|
||||
size="small"
|
||||
onClick={() => setModal(<VideoModal src={floor.video!} />)}
|
||||
>
|
||||
<span className="2xl:size-[1.111vw] size-4">
|
||||
<PlayIcon />
|
||||
</span>
|
||||
Play Video
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -94,7 +94,7 @@ function AmenitiesFloorView({ floor }: AmenitiesFloorViewProps) {
|
||||
|
||||
{hasIndoorOutdoorSplit && indoorAmenities.length > 0 && (
|
||||
<div className="2xl:space-y-[1.667vw] space-y-6">
|
||||
<p className="font-medium text-h4">Indoor Amenities</p>
|
||||
<p className="text-h4 font-medium">Indoor Amenities</p>
|
||||
<div className="grid md:grid-cols-4 grid-cols-3 2xl:gap-[1.111vw] gap-4">
|
||||
{indoorAmenities.map((amenity, index) => (
|
||||
<AmentitiesBadge key={index} title={amenity.title} />
|
||||
@@ -108,7 +108,7 @@ function AmenitiesFloorView({ floor }: AmenitiesFloorViewProps) {
|
||||
)}
|
||||
|
||||
<div className="2xl:space-y-[1.667vw] space-y-6">
|
||||
<p className="font-medium text-h4">
|
||||
<p className="text-h4 font-medium">
|
||||
{hasIndoorOutdoorSplit ? "Outdoor Amenities" : "Amenities"}
|
||||
</p>
|
||||
<div
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Fragment, useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { Unit } from "../../types/IUnit";
|
||||
import { ComplexName } from "../../types/ComplexName";
|
||||
import { FloorPlanMasks } from "../../types/FloorPlanMasks";
|
||||
@@ -18,6 +19,8 @@ interface GenericFloorPlanProps {
|
||||
selectedFloor: string | null;
|
||||
unitsOnFloor: Unit[];
|
||||
chosenUnit?: Unit | null;
|
||||
/** When true: dim background, gray other units, no hover/cursor. Only from OnFloorMask. */
|
||||
isUnitPage?: boolean;
|
||||
/** How to extract the mask key from unit.unitNo. Default: unitNo.slice(-2) */
|
||||
getMaskKey?: (unitNo: string) => string;
|
||||
/** Optional unit filter (e.g. filterDuplicateUnits for combinable layouts) */
|
||||
@@ -40,6 +43,7 @@ function GenericFloorPlan({
|
||||
selectedFloor,
|
||||
unitsOnFloor,
|
||||
chosenUnit,
|
||||
isUnitPage = false,
|
||||
getMaskKey = defaultGetMaskKey,
|
||||
filterUnits,
|
||||
wing,
|
||||
@@ -61,8 +65,16 @@ function GenericFloorPlan({
|
||||
{...svgProps}
|
||||
>
|
||||
{/* Background: either an image or custom inline SVG */}
|
||||
{imagePath && <image transform="scale(.5)" xlinkHref={imagePath} />}
|
||||
{children}
|
||||
{imagePath && (
|
||||
<image
|
||||
transform="scale(.5)"
|
||||
xlinkHref={imagePath}
|
||||
opacity={isUnitPage ? 0.3 : 1}
|
||||
/>
|
||||
)}
|
||||
{children ? (
|
||||
<g opacity={isUnitPage ? 0.3 : 1}>{children}</g>
|
||||
) : null}
|
||||
|
||||
{/* Unit overlays */}
|
||||
{units.map((unit) => {
|
||||
@@ -76,6 +88,8 @@ function GenericFloorPlan({
|
||||
key={unit.unitNo}
|
||||
complexName={complexName}
|
||||
wing={wing}
|
||||
chosenUnit={chosenUnit}
|
||||
isUnitPage={isUnitPage}
|
||||
selectedUnit={selectedUnit}
|
||||
onSelect={setSelectedUnit}
|
||||
formattedUnitType={maskData.formattedUnitType}
|
||||
@@ -104,6 +118,8 @@ function GenericFloorPlanUnit({
|
||||
formattedUnitType,
|
||||
onSelect,
|
||||
selectedUnit,
|
||||
chosenUnit,
|
||||
isUnitPage = false,
|
||||
}: {
|
||||
complexName: ComplexName;
|
||||
wing?: "East" | "West";
|
||||
@@ -114,18 +130,23 @@ function GenericFloorPlanUnit({
|
||||
formattedUnitType: string;
|
||||
selectedUnit: Unit | null;
|
||||
onSelect: (unit: Unit | null) => void;
|
||||
chosenUnit?: Unit | null;
|
||||
isUnitPage?: boolean;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const { setPopup, setSide, setPosition } = usePopupStore();
|
||||
|
||||
function handleClick(unit: Unit) {
|
||||
window.open(`/complex/${complexName}/${unit.unitNo}`, "_blank");
|
||||
const isOtherUnit =
|
||||
isUnitPage && chosenUnit && unit.unitNo !== chosenUnit.unitNo;
|
||||
|
||||
function handleClick(u: Unit) {
|
||||
setPopup(null);
|
||||
navigate(`/complex/${complexName}/${u.unitNo}`);
|
||||
}
|
||||
|
||||
function handleMouseEnter() {
|
||||
if (floor === null) return;
|
||||
|
||||
setSide("top");
|
||||
|
||||
if (!selectedUnit)
|
||||
setPopup(
|
||||
<UnitPopup
|
||||
@@ -146,7 +167,10 @@ function GenericFloorPlanUnit({
|
||||
<g>
|
||||
<text
|
||||
transform={textTransform}
|
||||
className="fill-white text-[8px] select-none"
|
||||
className={clsx(
|
||||
"text-[8px] select-none",
|
||||
isUnitPage && !isOtherUnit ? "fill-[#374151]" : "fill-white"
|
||||
)}
|
||||
textAnchor="middle"
|
||||
>
|
||||
<tspan x={0} y={0}>
|
||||
@@ -157,18 +181,35 @@ function GenericFloorPlanUnit({
|
||||
</tspan>
|
||||
</text>
|
||||
<path
|
||||
onClick={() => !isMobile && !selectedUnit && handleClick(unit)}
|
||||
onMouseEnter={!isMobile ? handleMouseEnter : undefined}
|
||||
onMouseLeave={() => !isMobile && setPopup(null)}
|
||||
onTouchStart={(e) => {
|
||||
onSelect(unit);
|
||||
setPosition({ x: e.touches[0].clientX, y: e.touches[0].clientY });
|
||||
handleMouseEnter();
|
||||
}}
|
||||
onClick={
|
||||
!isUnitPage && !isMobile && !selectedUnit
|
||||
? () => handleClick(unit)
|
||||
: undefined
|
||||
}
|
||||
onMouseEnter={
|
||||
!isUnitPage && !isMobile ? handleMouseEnter : undefined
|
||||
}
|
||||
onMouseLeave={!isUnitPage && !isMobile ? () => setPopup(null) : undefined}
|
||||
onTouchStart={
|
||||
!isUnitPage
|
||||
? (e) => {
|
||||
onSelect(unit);
|
||||
setPosition({ x: e.touches[0].clientX, y: e.touches[0].clientY });
|
||||
handleMouseEnter();
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
className={clsx(
|
||||
"fill-transparent hover:fill-[#00BED7] opacity-40 isolate cursor-pointer transition-colors",
|
||||
selectedUnit?.unitNo === unit.unitNo &&
|
||||
"!fill-[#00BED7] opacity-40 cursor-default"
|
||||
"isolate transition-colors",
|
||||
isUnitPage
|
||||
? isOtherUnit
|
||||
? "fill-[#9CA3AF] opacity-50 pointer-events-none"
|
||||
: "fill-[#00BED7] opacity-40"
|
||||
: clsx(
|
||||
"fill-transparent hover:fill-[#00BED7] opacity-40 cursor-pointer",
|
||||
selectedUnit?.unitNo === unit.unitNo &&
|
||||
"!fill-[#00BED7] opacity-40 cursor-default"
|
||||
)
|
||||
)}
|
||||
d={d}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { clsx } from "clsx";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: React.ReactNode;
|
||||
variant?: "link" | "primary" | "secondary" | "tertiary" | "cta";
|
||||
variant?: "link" | "primary" | "secondary" | "tertiary" | "cta" | "fab";
|
||||
className?: string;
|
||||
size?: "small" | "medium" | "large";
|
||||
onlyIcon?: boolean;
|
||||
@@ -50,6 +50,11 @@ function Button({
|
||||
"bg-white hover:!bg-white hover:bg-opacity-80 text-[#0D1922]/70 disabled:!bg-[#0D1922] disabled:!bg-opacity-[8%] disabled:text-[#0D1922]/40",
|
||||
variant === "tertiary" &&
|
||||
"text-xs leading-[135%] text-[#0D1922]/70 !px-0 hover:!text-[#0D1922] disabled:!bg-transparent",
|
||||
variant === "fab" &&
|
||||
"bg-[#0D192266] hover:bg-[#0D192299] active:bg-[#00BED7] disabled:bg-[#0D192214] disabled:text-[#0D192214] text-white backdrop-blur-md rounded-full",
|
||||
variant === "fab" && onlyIcon
|
||||
? "active:!bg-[#0D192299] active:!text-[#00BED7]"
|
||||
: "",
|
||||
roundedFull ? "rounded-full" : "2xl:rounded-[0.833vw] rounded-xl",
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -101,7 +101,7 @@ export const projectBrochures: ProjectBrochures[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
projectTitle: "Rove Home HQ",
|
||||
projectTitle: "HQ by Rove",
|
||||
brochures: [
|
||||
{
|
||||
title: "Main Brochure",
|
||||
|
||||
@@ -38,7 +38,7 @@ const complexConfigs: Record<ComplexName, ComplexConfig> = {
|
||||
},
|
||||
hq: {
|
||||
slug: "hq",
|
||||
projectName: "Rove Home HQ",
|
||||
projectName: "HQ by Rove",
|
||||
floors: hqFloors,
|
||||
hasWings: false,
|
||||
hasCombinable: false,
|
||||
|
||||
@@ -48,6 +48,7 @@ export interface FloorPlanLayoutConfig {
|
||||
selectedFloor: string | null;
|
||||
unitsOnFloor: Unit[];
|
||||
chosenUnit?: Unit;
|
||||
isUnitPage?: boolean;
|
||||
}>;
|
||||
/** Wing designation for Marasi Drive popups */
|
||||
wing?: "East" | "West";
|
||||
|
||||
@@ -7,7 +7,7 @@ export const markers: IMarker[] = [
|
||||
x: "54.2%",
|
||||
y: "50.6%",
|
||||
popupPosition: "left",
|
||||
title: "Downtown",
|
||||
title: "Rove Home Downtown",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
@@ -16,7 +16,7 @@ export const markers: IMarker[] = [
|
||||
x: "55.6%",
|
||||
y: "52.5%",
|
||||
popupPosition: "right",
|
||||
title: "Marasi Drive",
|
||||
title: "Rove Home Marasi Drive",
|
||||
numberOfUnits: 809,
|
||||
},
|
||||
{
|
||||
@@ -25,16 +25,16 @@ export const markers: IMarker[] = [
|
||||
x: "35.35%",
|
||||
y: "71.6%",
|
||||
popupPosition: "right",
|
||||
title: "Dubai Marina",
|
||||
title: "Rove Home Dubai Marina",
|
||||
numberOfUnits: 958,
|
||||
},
|
||||
{
|
||||
id:4 ,
|
||||
id: 4,
|
||||
name: "hq",
|
||||
x: "57%",
|
||||
y: "48%",
|
||||
popupPosition: "right",
|
||||
title: "HQ",
|
||||
title: "HQ by Rove",
|
||||
numberOfUnits: 1000,
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1989,14 +1989,14 @@ export const projects: Project[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Rove Home HQ",
|
||||
title: "HQ by Rove",
|
||||
slug: "hq",
|
||||
img: "/images/search/rove_home_hq.png",
|
||||
buildingType: "commercial",
|
||||
hasSides: false,
|
||||
types: [
|
||||
{
|
||||
name: "Studio",
|
||||
name: "Studio office",
|
||||
slug: "studio",
|
||||
floors: "Ground Floor",
|
||||
area: "TBD Sqft",
|
||||
@@ -2054,7 +2054,7 @@ export const projects: Project[] = [
|
||||
tourAvailable: false,
|
||||
},
|
||||
{
|
||||
name: "Simplex Edge",
|
||||
name: "Simplex Edge office",
|
||||
slug: "simplex-edge",
|
||||
floors: "Ground Floor",
|
||||
area: "TBD Sqft",
|
||||
@@ -2108,12 +2108,11 @@ export const projects: Project[] = [
|
||||
left: { x: 115, y: 185 },
|
||||
right: { x: 115, y: 185 },
|
||||
},
|
||||
|
||||
],
|
||||
tourAvailable: false,
|
||||
},
|
||||
{
|
||||
name: "Loft Edge",
|
||||
name: "Loft Edge office",
|
||||
slug: "loft-edge",
|
||||
floors: "Ground Floor & Mezzanine",
|
||||
area: "TBD Sqft",
|
||||
@@ -2263,7 +2262,7 @@ export const projects: Project[] = [
|
||||
tourAvailable: false,
|
||||
},
|
||||
{
|
||||
name: "Penthouse Loft",
|
||||
name: "Penthouse Loft office",
|
||||
slug: "penthouse-loft",
|
||||
floors: "Upper Floors",
|
||||
area: "TBD Sqft",
|
||||
@@ -2327,7 +2326,7 @@ export const projects: Project[] = [
|
||||
},
|
||||
{
|
||||
name: "Powder room",
|
||||
left: { x: 430, y: 536},
|
||||
left: { x: 430, y: 536 },
|
||||
right: { x: 430, y: 536 },
|
||||
floor: "lower",
|
||||
},
|
||||
@@ -2407,7 +2406,7 @@ export const projects: Project[] = [
|
||||
tourAvailable: false,
|
||||
},
|
||||
{
|
||||
name: "Presidential Loft",
|
||||
name: "Presidential Loft office",
|
||||
slug: "presidential-loft",
|
||||
floors: "Upper Floors",
|
||||
area: "TBD Sqft",
|
||||
|
||||
@@ -20,7 +20,6 @@ import LayoutWithoutFooter from "./layout/LayoutWithoutFooter.tsx";
|
||||
import { queryClient } from "./lib/queryClient.ts";
|
||||
import AboutComplexPage from "./pages/AboutComplexPage.tsx";
|
||||
import UnitTypeItemPage from "./pages/UnitTypeItemPage.tsx";
|
||||
import TestPage from "./pages/TestPage.tsx";
|
||||
import UnitPage from "./pages/UnitPage.tsx";
|
||||
import PopupContainer from "./components/PopupContainer.tsx";
|
||||
import VirtualTourPage from "./pages/VirtualTourPage.tsx";
|
||||
@@ -80,10 +79,6 @@ const route = createBrowserRouter([
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/test",
|
||||
element: <TestPage />,
|
||||
},
|
||||
]);
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useParams } from "react-router";
|
||||
import { api } from "../api/ky";
|
||||
@@ -42,6 +43,17 @@ function UnitPage() {
|
||||
|
||||
const { setModal } = useModalStore();
|
||||
|
||||
const displayUnitType = unit
|
||||
? formattedUnitTypes.get(unit.unitType) || unit.unitType
|
||||
: "Rove Home";
|
||||
|
||||
useEffect(() => {
|
||||
document.title = displayUnitType;
|
||||
return () => {
|
||||
document.title = "Rove Home";
|
||||
};
|
||||
}, [displayUnitType]);
|
||||
|
||||
if (!unit) return null;
|
||||
|
||||
function handleShare() {
|
||||
@@ -51,151 +63,167 @@ function UnitPage() {
|
||||
});
|
||||
}
|
||||
|
||||
if (!unit) return <div>Loading...</div>;
|
||||
|
||||
const unitType = projects
|
||||
.find((project) => project.slug === unit.projectSlug)
|
||||
?.types.find((type) => type.slug === unit.unitTypeVariantSlug);
|
||||
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<title>{unit.unitTypeVariantSlug}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div className="2xl:p-[2.222vw] md:max-2xl:p-6 p-4 max-2xl:pb-0 bg-white flex 2xl:gap-[2.222vw] md:max-2xl:gap-8 gap-6 max-2xl:flex-col">
|
||||
<NewUnitSlider>
|
||||
{unit.isLoft ? (
|
||||
<>
|
||||
<UnitSliderItem text="Lower">
|
||||
<UnitTypeImageWithMarkers
|
||||
complexName={unit.projectSlug}
|
||||
unitTypeVariant={
|
||||
unit.unitTypeVariantSlug +
|
||||
"-lower" +
|
||||
(unit.side ? `-${unit.side}` : "")
|
||||
}
|
||||
floor="lower"
|
||||
legend={unitType?.legend || []}
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
<UnitSliderItem text="Upper">
|
||||
<UnitTypeImageWithMarkers
|
||||
complexName={unit.projectSlug}
|
||||
unitTypeVariant={
|
||||
unit.unitTypeVariantSlug +
|
||||
"-upper" +
|
||||
(unit.side ? `-${unit.side}` : "")
|
||||
}
|
||||
floor="upper"
|
||||
legend={unitType?.legend || []}
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
</>
|
||||
) : (
|
||||
<UnitSliderItem text="Layout">
|
||||
<div className="2xl:p-[2.222vw] md:max-2xl:p-6 p-4 max-2xl:pb-0 bg-white flex 2xl:gap-[2.222vw] md:max-2xl:gap-8 gap-6 max-2xl:flex-col">
|
||||
<div className="relative">
|
||||
<NewUnitSlider>
|
||||
{unit.isLoft ? (
|
||||
<>
|
||||
<UnitSliderItem text="Lower">
|
||||
<UnitTypeImageWithMarkers
|
||||
complexName={unit.projectSlug}
|
||||
unitTypeVariant={
|
||||
unit.unitTypeVariantSlug +
|
||||
"-lower" +
|
||||
(unit.side ? `-${unit.side}` : "")
|
||||
}
|
||||
floor="lower"
|
||||
legend={unitType?.legend || []}
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
)}
|
||||
<UnitSliderItem text="On the floor">
|
||||
<svg className="2xl:py-[2.222vw] py-8 size-full">
|
||||
<OnFloorMask unit={unit} />
|
||||
</svg>
|
||||
</UnitSliderItem>
|
||||
{innerWidth >= 768 ? (
|
||||
<UnitSliderItem text="Interior">
|
||||
<InteriorSlider
|
||||
unitTypeSlug={unit.unitTypeVariantSlug}
|
||||
projectSlug={unit.projectSlug}
|
||||
<UnitSliderItem text="Upper">
|
||||
<UnitTypeImageWithMarkers
|
||||
complexName={unit.projectSlug}
|
||||
unitTypeVariant={
|
||||
unit.unitTypeVariantSlug +
|
||||
"-upper" +
|
||||
(unit.side ? `-${unit.side}` : "")
|
||||
}
|
||||
floor="upper"
|
||||
legend={unitType?.legend || []}
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
) : (
|
||||
unitType?.interiors.map((interior, index) => (
|
||||
<UnitSliderItem text={`interior ${index + 1}`} key={index}>
|
||||
<img
|
||||
src={interior}
|
||||
alt=""
|
||||
className="size-full object-cover pointer-events-none"
|
||||
</>
|
||||
) : (
|
||||
<UnitSliderItem text="Layout">
|
||||
<UnitTypeImageWithMarkers
|
||||
complexName={unit.projectSlug}
|
||||
unitTypeVariant={
|
||||
unit.unitTypeVariantSlug + (unit.side ? `-${unit.side}` : "")
|
||||
}
|
||||
legend={unitType?.legend || []}
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
)}
|
||||
<UnitSliderItem text="On the floor">
|
||||
<svg className="2xl:py-[2.222vw] py-8 size-full">
|
||||
<OnFloorMask unit={unit} />
|
||||
</svg>
|
||||
</UnitSliderItem>
|
||||
{innerWidth >= 768 ? (
|
||||
<UnitSliderItem text="Interior">
|
||||
<InteriorSlider
|
||||
unitTypeSlug={unit.unitTypeVariantSlug}
|
||||
projectSlug={unit.projectSlug}
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
) : (
|
||||
unitType?.interiors.map((interior, index) => (
|
||||
<UnitSliderItem text={`interior ${index + 1}`} key={index}>
|
||||
<img
|
||||
src={interior}
|
||||
alt=""
|
||||
className="size-full object-cover pointer-events-none"
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
))
|
||||
)}
|
||||
</NewUnitSlider>
|
||||
{unit.projectSlug === "hq" &&
|
||||
[
|
||||
"loft-edge",
|
||||
"penthouse-loft",
|
||||
"presidential-loft",
|
||||
"studio",
|
||||
].includes(unit.unitTypeVariantSlug) && (
|
||||
<Button
|
||||
variant="cta"
|
||||
onlyIcon={innerWidth < 768}
|
||||
size="large"
|
||||
className="absolute 2xl:right-[1.667vw] 2xl:top-[1.667vw] right-6 top-6"
|
||||
onClick={() =>
|
||||
setModal(
|
||||
<VideoModal
|
||||
src={`/videos/unit-types/hq/${unit.unitTypeVariantSlug}.mp4`}
|
||||
/>
|
||||
</UnitSliderItem>
|
||||
))
|
||||
)}
|
||||
</NewUnitSlider>
|
||||
<div className="flex flex-col justify-between md:max-2xl:gap-6 gap-4 2xl:w-[21.944vw] flex-shrink-0">
|
||||
<div className="2xl:space-y-[1.667vw] space-y-6">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="2xl:space-y-[1.111vw] space-y-4">
|
||||
<h3 className="text-h3 font-medium">
|
||||
{formattedUnitTypes.get(unit.unitType) || unit.unitType}
|
||||
</h3>
|
||||
<div className="2xl:space-y-[0.556vw] space-y-2">
|
||||
<p className="text-s text-[#00BED7]">{unit.project}</p>
|
||||
<div className="flex items-center 2xl:gap-[0.556vw]">
|
||||
{unit.wing && (
|
||||
<>
|
||||
<p className="text-s opacity-70">{unit.wing}</p>
|
||||
<div className="2xl:w-[0.278vw] 2xl:h-[0.278vw] w-1 h-1 rounded-full bg-[#E2E2DC]" />
|
||||
</>
|
||||
)}
|
||||
<p className="text-s opacity-70">Floor {unit.floor}</p>
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="2xl:size-[1.389vw] size-5">
|
||||
<PlayIcon />
|
||||
</div>
|
||||
<span className="max-md:hidden">Video tour</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col justify-between md:max-2xl:gap-6 gap-4 2xl:w-[21.944vw] flex-shrink-0">
|
||||
<div className="2xl:space-y-[1.667vw] space-y-6">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="2xl:space-y-[1.111vw] space-y-4">
|
||||
<h3 className="text-h3 font-medium">
|
||||
{formattedUnitTypes.get(unit.unitType) || unit.unitType}
|
||||
</h3>
|
||||
<div className="2xl:space-y-[0.556vw] space-y-2">
|
||||
<p className="text-s text-[#00BED7]">{unit.project}</p>
|
||||
<div className="flex items-center 2xl:gap-[0.556vw]">
|
||||
{unit.wing && (
|
||||
<>
|
||||
<p className="text-s opacity-70">{unit.wing}</p>
|
||||
<div className="2xl:w-[0.278vw] 2xl:h-[0.278vw] w-1 h-1 rounded-full bg-[#E2E2DC]" />
|
||||
<p className="text-s opacity-70">{unit.unitNo}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="tertiary" onlyIcon onClick={handleFavorite}>
|
||||
<span className="2xl:size-[1.389vw] size-5">
|
||||
{favoriteUnits.some(
|
||||
(favoriteUnit) => favoriteUnit.id === unit.id
|
||||
) ? (
|
||||
<FilledHeartIcon />
|
||||
) : (
|
||||
<HeartIcon />
|
||||
)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button variant="tertiary" onlyIcon onClick={handleShare}>
|
||||
<span className="2xl:size-[1.389vw] size-5">
|
||||
<ShareIcon />
|
||||
</span>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<p className="text-s opacity-70">Floor {unit.floor}</p>
|
||||
<div className="2xl:w-[0.278vw] 2xl:h-[0.278vw] w-1 h-1 rounded-full bg-[#E2E2DC]" />
|
||||
<p className="text-s opacity-70">{unit.unitNo}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="w-full border-[#E2E2DC] 2xl:h-[0.069vw] h-px" />
|
||||
{unitType?.video && (
|
||||
<button
|
||||
onClick={() =>
|
||||
setModal(<VideoModal src={unitType.video?.src || ""} />)
|
||||
}
|
||||
className="2xl:p-[1.111vw] p-4 2xl:rounded-[1.111vw] text-left rounded-2xl flex items-center gap-[0.556vw] ring-[0.069vw] ring-[#E2E2DC] cursor-pointer w-full"
|
||||
>
|
||||
<div className="lg:space-y-[0.278vw] space-y-1 flex-1">
|
||||
<p className="text-h5 font-medium">
|
||||
{unitType.video.title}
|
||||
</p>
|
||||
<p className="text-s text-[#00BED7]">
|
||||
{unitType.video.desc}
|
||||
</p>
|
||||
</div>
|
||||
<div className="2xl:size-[1.389vw] size-5">
|
||||
<PlayIcon />
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
<div className="flex flex-col 2xl:gap-y-[0.556vw]">
|
||||
<div className="flex justify-between">
|
||||
<p className="text-s opacity-70">Total Area</p>
|
||||
<p className="text-s">{unit.squareFt.toFixed(2)} Sqft</p>
|
||||
</div>
|
||||
{/* <div className="flex justify-between">
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="tertiary" onlyIcon onClick={handleFavorite}>
|
||||
<span className="2xl:size-[1.389vw] size-5">
|
||||
{favoriteUnits.some(
|
||||
(favoriteUnit) => favoriteUnit.id === unit.id
|
||||
) ? (
|
||||
<FilledHeartIcon />
|
||||
) : (
|
||||
<HeartIcon />
|
||||
)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button variant="tertiary" onlyIcon onClick={handleShare}>
|
||||
<span className="2xl:size-[1.389vw] size-5">
|
||||
<ShareIcon />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="w-full border-[#E2E2DC] 2xl:h-[0.069vw] h-px" />
|
||||
{unitType?.video && (
|
||||
<button
|
||||
onClick={() =>
|
||||
setModal(<VideoModal src={unitType.video?.src || ""} />)
|
||||
}
|
||||
className="2xl:p-[1.111vw] p-4 2xl:rounded-[1.111vw] text-left rounded-2xl flex items-center gap-[0.556vw] ring-[0.069vw] ring-[#E2E2DC] cursor-pointer w-full"
|
||||
>
|
||||
<div className="lg:space-y-[0.278vw] space-y-1 flex-1">
|
||||
<p className="text-h5 font-medium">{unitType.video.title}</p>
|
||||
<p className="text-s text-[#00BED7]">{unitType.video.desc}</p>
|
||||
</div>
|
||||
<div className="2xl:size-[1.389vw] size-5">
|
||||
<PlayIcon />
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
<div className="flex flex-col 2xl:gap-y-[0.556vw]">
|
||||
<div className="flex justify-between">
|
||||
<p className="text-s opacity-70">Total Area</p>
|
||||
<p className="text-s">{unit.squareFt.toFixed(2)} Sqft</p>
|
||||
</div>
|
||||
{/* <div className="flex justify-between">
|
||||
<p className="text-s opacity-70">Suite Area</p>
|
||||
<p className="text-s">{unit.suitsArea.toFixed(2)} Sqft</p>
|
||||
</div>
|
||||
@@ -205,7 +233,7 @@ function UnitPage() {
|
||||
{unit.balconyArea.toFixed(2)} Sqft
|
||||
</p>
|
||||
</div> */}
|
||||
{/* <div className="flex justify-between">
|
||||
{/* <div className="flex justify-between">
|
||||
<p className="text-s opacity-70">Status</p>
|
||||
<p className="text-s">
|
||||
{unit.projectSlug === "dubai-marina" &&
|
||||
@@ -215,20 +243,20 @@ function UnitPage() {
|
||||
unit.state.slice(1).replace(/_/g, " ")}
|
||||
</p>
|
||||
</div> */}
|
||||
<div className="flex justify-between">
|
||||
<p className="text-s opacity-70">Parking Space</p>
|
||||
<p className="text-s">{unit.noOfParkingSpace}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* <p className="text-h4 font-medium text-[#00BED7]">{`AED ${Intl.NumberFormat(
|
||||
<div className="flex justify-between">
|
||||
<p className="text-s opacity-70">Parking Space</p>
|
||||
<p className="text-s">{unit.noOfParkingSpace}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* <p className="text-h4 font-medium text-[#00BED7]">{`AED ${Intl.NumberFormat(
|
||||
"ar-AE",
|
||||
{
|
||||
currency: "AED",
|
||||
minimumFractionDigits: 0,
|
||||
}
|
||||
).format(unit.salesPrice)}`}</p> */}
|
||||
</div>
|
||||
{/* <div className="flex 2xl:flex-col 2xl:gap-[0.556vw] gap-2 2xl:bottom-[2.222vw] bottom-0 bg-white md:max-2xl:-mx-[3.125vw] md:max-2xl:p-[3.125vw] max-md:-mx-4 max-md:p-4 max-2xl:rounded-t-2xl max-2xl:shadow-[0_-4px_20px_0_rgba(0,0,0,0.05)]">
|
||||
</div>
|
||||
{/* <div className="flex 2xl:flex-col 2xl:gap-[0.556vw] gap-2 2xl:bottom-[2.222vw] bottom-0 bg-white md:max-2xl:-mx-[3.125vw] md:max-2xl:p-[3.125vw] max-md:-mx-4 max-md:p-4 max-2xl:rounded-t-2xl max-2xl:shadow-[0_-4px_20px_0_rgba(0,0,0,0.05)]">
|
||||
<Button
|
||||
disabled={!unitTypeVariantMarasiDrive}
|
||||
variant="cta"
|
||||
@@ -247,55 +275,53 @@ function UnitPage() {
|
||||
Book
|
||||
</Button>
|
||||
</div> */}
|
||||
<div className="flex 2xl:flex-col 2xl:gap-[0.556vw] gap-2 2xl:bottom-[2.222vw] bottom-0 bg-white md:max-2xl:-mx-[3.125vw] md:max-2xl:p-[3.125vw] max-md:-mx-4 max-md:p-4 max-2xl:rounded-t-2xl max-2xl:shadow-[0_-4px_20px_0_rgba(0,0,0,0.05)]">
|
||||
{projects
|
||||
.find((project) => project.slug === params.complexName)
|
||||
?.types.find((type) => type.slug === unit.unitTypeVariantSlug)
|
||||
?.tourAvailable && (
|
||||
<Button
|
||||
variant="cta"
|
||||
size="large"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
`/virtual-tour/${params.complexName}/${unit.unitTypeVariantSlug}`,
|
||||
"_blank"
|
||||
)
|
||||
}
|
||||
>
|
||||
Virtual tour
|
||||
</Button>
|
||||
)}
|
||||
{/* <Button disabled variant="cta" size="large">
|
||||
<div className="flex 2xl:flex-col 2xl:gap-[0.556vw] gap-2 2xl:bottom-[2.222vw] bottom-0 bg-white md:max-2xl:-mx-[3.125vw] md:max-2xl:p-[3.125vw] max-md:-mx-4 max-md:p-4 max-2xl:rounded-t-2xl max-2xl:shadow-[0_-4px_20px_0_rgba(0,0,0,0.05)]">
|
||||
{projects
|
||||
.find((project) => project.slug === params.complexName)
|
||||
?.types.find((type) => type.slug === unit.unitTypeVariantSlug)
|
||||
?.tourAvailable && (
|
||||
<Button
|
||||
variant="cta"
|
||||
size="large"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
`/virtual-tour/${params.complexName}/${unit.unitTypeVariantSlug}`,
|
||||
"_blank"
|
||||
)
|
||||
}
|
||||
>
|
||||
Virtual tour
|
||||
</Button>
|
||||
)}
|
||||
{/* <Button disabled variant="cta" size="large">
|
||||
Book
|
||||
</Button> */}
|
||||
|
||||
{/* videos for hq units */}
|
||||
{unit.projectSlug === "hq" &&
|
||||
[
|
||||
"loft-edge",
|
||||
"penthouse-loft",
|
||||
"presidential-loft",
|
||||
"studio",
|
||||
].includes(unit.unitTypeVariantSlug) && (
|
||||
<Button
|
||||
variant="cta"
|
||||
size="large"
|
||||
onClick={() =>
|
||||
setModal(
|
||||
<VideoModal
|
||||
src={`/videos/unit-types/hq/${unit.unitTypeVariantSlug}.mp4`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
Video tour
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* videos for hq units */}
|
||||
{/* {unit.projectSlug === "hq" &&
|
||||
[
|
||||
"loft-edge",
|
||||
"penthouse-loft",
|
||||
"presidential-loft",
|
||||
"studio",
|
||||
].includes(unit.unitTypeVariantSlug) && (
|
||||
<Button
|
||||
variant="cta"
|
||||
size="large"
|
||||
onClick={() =>
|
||||
setModal(
|
||||
<VideoModal
|
||||
src={`/videos/unit-types/hq/${unit.unitTypeVariantSlug}.mp4`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
Video tour
|
||||
</Button>
|
||||
)} */}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||