diff --git a/public/images/map/markers/popups/downtown.png b/public/images/map/markers/popups/downtown.png index d79b0f6..d53ff47 100644 Binary files a/public/images/map/markers/popups/downtown.png and b/public/images/map/markers/popups/downtown.png differ diff --git a/public/images/map/markers/popups/downtown_old.png b/public/images/map/markers/popups/downtown_old.png new file mode 100644 index 0000000..d79b0f6 Binary files /dev/null and b/public/images/map/markers/popups/downtown_old.png differ diff --git a/public/images/map/markers/popups/dubai-marina.png b/public/images/map/markers/popups/dubai-marina.png index bb8dcd7..69455db 100644 Binary files a/public/images/map/markers/popups/dubai-marina.png and b/public/images/map/markers/popups/dubai-marina.png differ diff --git a/public/images/map/markers/popups/dubai-marina_old.png b/public/images/map/markers/popups/dubai-marina_old.png new file mode 100644 index 0000000..bb8dcd7 Binary files /dev/null and b/public/images/map/markers/popups/dubai-marina_old.png differ diff --git a/public/images/map/markers/popups/hq.png b/public/images/map/markers/popups/hq.png index 95e4283..62fb5ec 100644 Binary files a/public/images/map/markers/popups/hq.png and b/public/images/map/markers/popups/hq.png differ diff --git a/public/images/map/markers/popups/marasi-drive.png b/public/images/map/markers/popups/marasi-drive.png index 925bde3..f4c76a2 100644 Binary files a/public/images/map/markers/popups/marasi-drive.png and b/public/images/map/markers/popups/marasi-drive.png differ diff --git a/public/images/map/markers/popups/marasi-drive_old.png b/public/images/map/markers/popups/marasi-drive_old.png new file mode 100644 index 0000000..925bde3 Binary files /dev/null and b/public/images/map/markers/popups/marasi-drive_old.png differ diff --git a/src/components/AboutHQ.tsx b/src/components/AboutHQ.tsx index 1e90577..b959901 100644 --- a/src/components/AboutHQ.tsx +++ b/src/components/AboutHQ.tsx @@ -39,9 +39,9 @@ export default function AboutHQ() {

- {`Rove Home HQ — - Work looks different - here`} + {`HQ by Rove instead — + Work looks different + here`}

@@ -55,7 +55,7 @@ export default function AboutHQ() { {`Welcome to the office you actually want to show up for`} -

+

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() { More than an office,{" "}
a lifestyle. -

+

{`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() {

-

+

{`Work looks different here`}

diff --git a/src/components/FloorPlanMarasiDriveEast.tsx b/src/components/FloorPlanMarasiDriveEast.tsx index 2802f74..fa3c528 100644 --- a/src/components/FloorPlanMarasiDriveEast.tsx +++ b/src/components/FloorPlanMarasiDriveEast.tsx @@ -6,12 +6,14 @@ interface FloorPlanMarasiDriveEastProps { selectedFloor: string | null; unitsOnFloor: Unit[]; chosenUnit?: Unit; + isUnitPage?: boolean; } function FloorPlanMarasiDriveEast({ selectedFloor, unitsOnFloor, chosenUnit, + isUnitPage, }: FloorPlanMarasiDriveEastProps) { return ( diff --git a/src/components/FloorPlanMarasiDriveWestLower.tsx b/src/components/FloorPlanMarasiDriveWestLower.tsx index 85170d7..79d0e9c 100644 --- a/src/components/FloorPlanMarasiDriveWestLower.tsx +++ b/src/components/FloorPlanMarasiDriveWestLower.tsx @@ -6,12 +6,14 @@ interface FloorPlanMarasiDriveWestLowerProps { selectedFloor: string | null; unitsOnFloor: Unit[]; chosenUnit?: Unit; + isUnitPage?: boolean; } function FloorPlanMarasiDriveWestLower({ selectedFloor, unitsOnFloor, chosenUnit, + isUnitPage, }: FloorPlanMarasiDriveWestLowerProps) { return ( diff --git a/src/components/FloorPlanMarasiDriveWestUpper.tsx b/src/components/FloorPlanMarasiDriveWestUpper.tsx index add5453..789219a 100644 --- a/src/components/FloorPlanMarasiDriveWestUpper.tsx +++ b/src/components/FloorPlanMarasiDriveWestUpper.tsx @@ -6,12 +6,14 @@ interface FloorPlanMarasiDriveWestUpperProps { selectedFloor: string | null; unitsOnFloor: Unit[]; chosenUnit?: Unit; + isUnitPage?: boolean; } function FloorPlanMarasiDriveWestUpper({ unitsOnFloor, selectedFloor, chosenUnit, + isUnitPage, }: FloorPlanMarasiDriveWestUpperProps) { return ( diff --git a/src/components/Marker.tsx b/src/components/Marker.tsx index b0e0432..0c15c04 100644 --- a/src/components/Marker.tsx +++ b/src/components/Marker.tsx @@ -64,7 +64,7 @@ function Marker({ />

diff --git a/src/components/OnFloorMask.tsx b/src/components/OnFloorMask.tsx index 0716bb8..6f3032d 100644 --- a/src/components/OnFloorMask.tsx +++ b/src/components/OnFloorMask.tsx @@ -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(), + 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 ( ); } @@ -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} /> ); diff --git a/src/components/PopupContainer.tsx b/src/components/PopupContainer.tsx index 3e2987c..5dfeb2b 100644 --- a/src/components/PopupContainer.tsx +++ b/src/components/PopupContainer.tsx @@ -61,13 +61,14 @@ function PopupContainer() {
diff --git a/src/components/SearchFilters.tsx b/src/components/SearchFilters.tsx index aa240cf..809b264 100644 --- a/src/components/SearchFilters.tsx +++ b/src/components/SearchFilters.tsx @@ -173,7 +173,7 @@ function SearchFilters({ )}
-

+

{inModal ? "Filters" : "Search"}

@@ -372,7 +372,7 @@ function SearchFilters({
-

Filters

+

Filters

- ROVE Home{" "} {markers.find((marker) => marker.name === complexName)?.title}

diff --git a/src/components/UnitTypeCard.tsx b/src/components/UnitTypeCard.tsx index 09630ea..2fca05d 100644 --- a/src/components/UnitTypeCard.tsx +++ b/src/components/UnitTypeCard.tsx @@ -25,7 +25,11 @@ function UnitTypeCard({ project, type }: { project: Project; type: UnitType }) {
diff --git a/src/components/UnitTypeImageWithMarkers.tsx b/src/components/UnitTypeImageWithMarkers.tsx index 971ef42..559320c 100644 --- a/src/components/UnitTypeImageWithMarkers.tsx +++ b/src/components/UnitTypeImageWithMarkers.tsx @@ -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 ( 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 (
-

{floor.displayName}

+

{floor.displayName}

setModal()} > + Play Video )}
@@ -94,7 +94,7 @@ function AmenitiesFloorView({ floor }: AmenitiesFloorViewProps) { {hasIndoorOutdoorSplit && indoorAmenities.length > 0 && (
-

Indoor Amenities

+

Indoor Amenities

{indoorAmenities.map((amenity, index) => ( @@ -108,7 +108,7 @@ function AmenitiesFloorView({ floor }: AmenitiesFloorViewProps) { )}
-

+

{hasIndoorOutdoorSplit ? "Outdoor Amenities" : "Amenities"}

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 && } - {children} + {imagePath && ( + + )} + {children ? ( + {children} + ) : 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( @@ -157,18 +181,35 @@ function GenericFloorPlanUnit({ !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} /> diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index 99e9e59..70ed8d4 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -3,7 +3,7 @@ import { clsx } from "clsx"; interface ButtonProps extends React.ButtonHTMLAttributes { 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 )} diff --git a/src/data/brochures.ts b/src/data/brochures.ts index c047d73..ac9805a 100644 --- a/src/data/brochures.ts +++ b/src/data/brochures.ts @@ -101,7 +101,7 @@ export const projectBrochures: ProjectBrochures[] = [ ], }, { - projectTitle: "Rove Home HQ", + projectTitle: "HQ by Rove", brochures: [ { title: "Main Brochure", diff --git a/src/data/complex-config.ts b/src/data/complex-config.ts index 1e8d2a5..feb5e8b 100644 --- a/src/data/complex-config.ts +++ b/src/data/complex-config.ts @@ -38,7 +38,7 @@ const complexConfigs: Record = { }, hq: { slug: "hq", - projectName: "Rove Home HQ", + projectName: "HQ by Rove", floors: hqFloors, hasWings: false, hasCombinable: false, diff --git a/src/data/floor-plan-config.ts b/src/data/floor-plan-config.ts index 5d3ceb0..7432feb 100644 --- a/src/data/floor-plan-config.ts +++ b/src/data/floor-plan-config.ts @@ -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"; diff --git a/src/data/markers.ts b/src/data/markers.ts index 8ce1970..042cc61 100644 --- a/src/data/markers.ts +++ b/src/data/markers.ts @@ -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, - } + }, ]; diff --git a/src/data/projects.ts b/src/data/projects.ts index e73a392..d52164a 100644 --- a/src/data/projects.ts +++ b/src/data/projects.ts @@ -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", diff --git a/src/main.tsx b/src/main.tsx index ded2b88..56a6e21 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -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: , - }, ]); createRoot(document.getElementById("root")!).render( diff --git a/src/pages/UnitPage.tsx b/src/pages/UnitPage.tsx index 2c2ddbf..0b2854c 100644 --- a/src/pages/UnitPage.tsx +++ b/src/pages/UnitPage.tsx @@ -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
Loading...
; - const unitType = projects .find((project) => project.slug === unit.projectSlug) ?.types.find((type) => type.slug === unit.unitTypeVariantSlug); return ( - - - {unit.unitTypeVariantSlug} - - -
- - {unit.isLoft ? ( - <> - - - - - - - - ) : ( - +
+
+ + {unit.isLoft ? ( + <> + - )} - - - - - - {innerWidth >= 768 ? ( - - + - ) : ( - unitType?.interiors.map((interior, index) => ( - - + ) : ( + + + + )} + + + + + + {innerWidth >= 768 ? ( + + + + ) : ( + unitType?.interiors.map((interior, index) => ( + + + + )) + )} + + {unit.projectSlug === "hq" && + [ + "loft-edge", + "penthouse-loft", + "presidential-loft", + "studio", + ].includes(unit.unitTypeVariantSlug) && ( + + )} +
+
+
+
+
+

+ {formattedUnitTypes.get(unit.unitType) || unit.unitType} +

+
+

{unit.project}

+
+ {unit.wing && ( + <> +

{unit.wing}

-

{unit.unitNo}

-
-
-
-
- - + + )} +

Floor {unit.floor}

+
+

{unit.unitNo}

-
- {unitType?.video && ( - - )} -
-
-

Total Area

-

{unit.squareFt.toFixed(2)} Sqft

-
- {/*
+
+
+ + +
+
+
+ {unitType?.video && ( + + )} +
+
+

Total Area

+

{unit.squareFt.toFixed(2)} Sqft

+
+ {/*

Suite Area

{unit.suitsArea.toFixed(2)} Sqft

@@ -205,7 +233,7 @@ function UnitPage() { {unit.balconyArea.toFixed(2)} Sqft

*/} - {/*
+ {/*

Status

{unit.projectSlug === "dubai-marina" && @@ -215,20 +243,20 @@ function UnitPage() { unit.state.slice(1).replace(/_/g, " ")}

*/} -
-

Parking Space

-

{unit.noOfParkingSpace}

-
-
- {/*

{`AED ${Intl.NumberFormat( +

+

Parking Space

+

{unit.noOfParkingSpace}

+
+
+ {/*

{`AED ${Intl.NumberFormat( "ar-AE", { currency: "AED", minimumFractionDigits: 0, } ).format(unit.salesPrice)}`}

*/} -
- {/*
+
+ {/*
*/} -
- {projects - .find((project) => project.slug === params.complexName) - ?.types.find((type) => type.slug === unit.unitTypeVariantSlug) - ?.tourAvailable && ( - - )} - {/* + )} + {/* */} - {/* videos for hq units */} - {unit.projectSlug === "hq" && - [ - "loft-edge", - "penthouse-loft", - "presidential-loft", - "studio", - ].includes(unit.unitTypeVariantSlug) && ( - - )} -
-
+ {/* videos for hq units */} + {/* {unit.projectSlug === "hq" && + [ + "loft-edge", + "penthouse-loft", + "presidential-loft", + "studio", + ].includes(unit.unitTypeVariantSlug) && ( + + )} */}
- - +
+
); }