modal + favorite page + video modal + fixes
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,13 +1,26 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Button from "./Button";
|
||||
import { formatNumber } from "../calc/formatNumber";
|
||||
// import { formatNumber } from "../calc/formatNumber";
|
||||
import { IAparmentRes } from "../types/apartmentsRes";
|
||||
import { apartmentRoutes } from "../consts/apartmentsRoutes";
|
||||
|
||||
const ApartmentSidebar = () => {
|
||||
interface ApartmentSidebarProps {
|
||||
currentApartment: IAparmentRes;
|
||||
}
|
||||
|
||||
const ApartmentSidebar = ({ currentApartment }: ApartmentSidebarProps) => {
|
||||
const navigate = useNavigate();
|
||||
const route = apartmentRoutes.find(
|
||||
(aprt) => aprt.type === currentApartment.Unit_Type
|
||||
)?.virtualTour;
|
||||
const unitNo = currentApartment.Unit_No.split("-")[1];
|
||||
const wing =
|
||||
currentApartment.Unit_No.split("-")[0] === "E" ? "East Wing" : "West Wing";
|
||||
|
||||
const handleOn3DTourClick = () => {
|
||||
navigate(`../virtual-tour/apartments-studio-1`);
|
||||
navigate(`../virtual-tour/${route ? route : "apartments-studio-1"}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex lg:flex-col gap-2 h-full">
|
||||
<div className="rounded-2xl overflow-clip lg:flex-1 flex-none relative">
|
||||
@@ -28,29 +41,32 @@ const ApartmentSidebar = () => {
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex justify-between text-m">
|
||||
<p className="text-[#73787C]">Complex</p>
|
||||
<p className="text-[#0D1922]">ROVE Home Marasi Drive</p>
|
||||
<p className="text-[#0D1922]">{currentApartment.Project_Name}</p>
|
||||
</div>
|
||||
<div className="flex justify-between text-m">
|
||||
<p className="text-[#73787C]">Section</p>
|
||||
<p className="text-[#0D1922]">East Wing</p>
|
||||
<p className="text-[#0D1922]">{wing}</p>
|
||||
</div>
|
||||
<div className="flex justify-between text-m">
|
||||
<p className="text-[#73787C]">Floor</p>
|
||||
<p className="text-[#0D1922]">11</p>
|
||||
<p className="text-[#0D1922]">{currentApartment.Floor}</p>
|
||||
</div>
|
||||
<div className="flex justify-between text-m">
|
||||
<p className="text-[#73787C]">Number</p>
|
||||
<p className="text-[#0D1922]">213</p>
|
||||
<p className="text-[#0D1922]">{unitNo}</p>
|
||||
</div>
|
||||
<div className="flex justify-between text-m">
|
||||
<p className="text-[#73787C]">Size</p>
|
||||
<p className="text-[#0D1922]">609 Sqft</p>
|
||||
<p className="text-[#0D1922]">
|
||||
{currentApartment.Total_Area_Sqft} Sqft
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl bg-white flex flex-col p-6 gap-4 flex-1 lg:flex-none justify-between">
|
||||
<p className="text-[#00BED7] font-semibold text-subheadline-s leading-7">
|
||||
AED {formatNumber(1668888, ",", 3, 1)}
|
||||
Unvailiable
|
||||
{/* AED {formatNumber(1668888, ",", 3, 1)} */}
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
|
||||
@@ -35,6 +35,7 @@ const Button = ({
|
||||
const border = borders[buttonType];
|
||||
const padding = paddings[buttonType];
|
||||
const rounded = isCircleRounded ? "rounded-full" : "rounded-lg";
|
||||
const disabledStyle = disabled ? "bg-[#0D192214] text-gray-400" : "";
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -43,7 +44,7 @@ const Button = ({
|
||||
onClick={onClick}
|
||||
className={`min-w-10 max-h-10 ${rounded} ${
|
||||
icon && !text ? "p-[10px]" : padding
|
||||
} transition-all duration-300 ease-in-out text-s pointer-events-auto flex gap-1 items-center align-middle h-fit ${backgroundColor} ${textColor} ${border} ${
|
||||
} transition-all duration-300 ease-in-out text-s pointer-events-auto flex gap-1 items-center align-middle h-fit ${backgroundColor} ${textColor} ${border} ${disabledStyle} ${
|
||||
className ? className : ""
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -93,7 +93,7 @@ const MultiRangeSlider = ({
|
||||
max={multirangeSlider.maxValue}
|
||||
value={[multirangeSlider.startValue, multirangeSlider.endValue]}
|
||||
className={`${
|
||||
isDisabled ? "pointer-events-none text" : ""
|
||||
isDisabled ? "pointer-events-none text disabled" : ""
|
||||
} absolute -bottom-3 left-0 w-[calc(100%-16px)] z-20`}
|
||||
onInput={handleOnRangeInputChange}
|
||||
/>
|
||||
|
||||
@@ -151,6 +151,9 @@ function SequenceSlider({ path }: SequenceSliderProps) {
|
||||
setLeft(_left);
|
||||
}
|
||||
}
|
||||
function handleOnSequenceClick(): void {
|
||||
navigate("./wing");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
handleResize();
|
||||
@@ -158,10 +161,6 @@ function SequenceSlider({ path }: SequenceSliderProps) {
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
function handleOnSequenceClick(): void {
|
||||
navigate("./wing");
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
{...handlers}
|
||||
@@ -276,12 +275,22 @@ function SequenceSlider({ path }: SequenceSliderProps) {
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute top-0 left-0 w-full h-full flex items-center justify-center bg-neutral-950 z-50 transition-opacity duration-300 ${state}`}
|
||||
className={`absolute z-[99999999] top-0 left-0 w-screen bg-[#F3F3F2] h-screen flex justify-center items-center flex-col ${state}`}
|
||||
>
|
||||
<h2 className="text-2xl font-tenor text-white whitespace-nowrap">
|
||||
Loading... {Math.round((100 / arrayLength) * loadedImages)} %
|
||||
</h2>
|
||||
<div className="animate-spin">
|
||||
<img src="/images/loader.png" alt="" />
|
||||
</div>
|
||||
<div className="text-[#00BED7] text-m">
|
||||
{Math.round((100 / arrayLength) * loadedImages)} %
|
||||
</div>
|
||||
</div>
|
||||
// <div
|
||||
// className={`absolute top-0 left-0 w-full h-full flex items-center justify-center bg-neutral-950 z-50 transition-opacity duration-300 ${state}`}
|
||||
// >
|
||||
// <h2 className="text-2xl font-tenor text-white whitespace-nowrap">
|
||||
// Loading... {Math.round((100 / arrayLength) * loadedImages)} %
|
||||
// </h2>
|
||||
// </div>
|
||||
)}
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { IAparmentRes } from "../../../types/apartmentsRes";
|
||||
import VirtualTourIcon from "../../icons/VirtualTourIcon";
|
||||
import { apartmentsWithoutVirtualTour } from "../../../consts/apartmentsWithoutVirtualTour";
|
||||
|
||||
interface ApartmentDescriptionProps {
|
||||
isVisible: boolean;
|
||||
@@ -48,16 +49,20 @@ const ApartmentDescription = ({
|
||||
{/* <p className="font-semibold text-[#00BED7] text-subheadline-s">
|
||||
AED 1,668,888
|
||||
</p> */}
|
||||
<div
|
||||
className={`bg-[#30B21614] text-[#30B216] px-2 py-[6px] flex gap-1 items-center rounded-lg ${
|
||||
is3DTourAvailable ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<VirtualTourIcon />
|
||||
<p className="text-caption-m font-semibold text-[#30B216]">
|
||||
3D-tour
|
||||
</p>
|
||||
</div>
|
||||
{!apartmentsWithoutVirtualTour.some(
|
||||
(aprt) => aprt.type === hoveredApartment.Unit_Type
|
||||
) && (
|
||||
<div
|
||||
className={`bg-[#30B21614] text-[#30B216] px-2 py-[6px] flex gap-1 items-center rounded-lg ${
|
||||
is3DTourAvailable ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<VirtualTourIcon />
|
||||
<p className="text-caption-m font-semibold text-[#30B216]">
|
||||
3D-tour
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-0 h-0 border-transparent border-t-[14px] border-x-[6px] border-b-0 absolute left-6 -bottom-[13px] border-t-white"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
interface FloorWestWingHighlightingProps {
|
||||
interface EastWingHighlightingProps {
|
||||
handleOnMouseOut:
|
||||
| (() => void)
|
||||
| ((event: React.MouseEvent<SVGSVGElement, MouseEvent>) => void);
|
||||
@@ -10,11 +10,11 @@ interface FloorWestWingHighlightingProps {
|
||||
) => void;
|
||||
}
|
||||
|
||||
const FloorWestWingHighlighting = ({
|
||||
const EastWingHighlighting = ({
|
||||
handleOnMouseOut,
|
||||
handleOnMouseOver,
|
||||
handleOnApartmentClick,
|
||||
}: FloorWestWingHighlightingProps) => {
|
||||
}: EastWingHighlightingProps) => {
|
||||
return (
|
||||
<>
|
||||
<svg
|
||||
@@ -51,6 +51,7 @@ const FloorWestWingHighlighting = ({
|
||||
</svg>
|
||||
|
||||
<svg
|
||||
data-type="1 BR U1 Left"
|
||||
className="opacity-0 hover:opacity-100 ease-in-out duration-300 transition-opacity cursor-pointer"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
@@ -388,6 +389,7 @@ const FloorWestWingHighlighting = ({
|
||||
></path>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U1 Left"
|
||||
className="opacity-0 hover:opacity-100 ease-in-out duration-300 transition-opacity cursor-pointer"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
@@ -412,6 +414,7 @@ const FloorWestWingHighlighting = ({
|
||||
></path>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U1 Right"
|
||||
className="opacity-0 hover:opacity-100 ease-in-out duration-300 transition-opacity cursor-pointer"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
@@ -440,4 +443,4 @@ const FloorWestWingHighlighting = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default FloorWestWingHighlighting;
|
||||
export default EastWingHighlighting;
|
||||
@@ -1,4 +1,4 @@
|
||||
function FloorWestWingLayout() {
|
||||
function EastWingLayout() {
|
||||
return (
|
||||
<>
|
||||
<path
|
||||
@@ -441,4 +441,4 @@ function FloorWestWingLayout() {
|
||||
);
|
||||
}
|
||||
|
||||
export default FloorWestWingLayout;
|
||||
export default EastWingLayout;
|
||||
@@ -1,19 +1,16 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { IDesctiptionFloor } from "../../../types/descriptionFloor";
|
||||
import ApartmentDescription from "./ApartmentDescription";
|
||||
import FloorWestWingHighlighting from "./FloorWestWingHighlighting";
|
||||
import FloorWestWingLayout from "./FloorWestWingLayout";
|
||||
import FloorEastWingHighlighting from "./FloorEastWingHighlighting";
|
||||
import FloorEastWingLayout from "./FloorEastWingLayout";
|
||||
import EastWingHighlighting from "./EastWingHighlighting";
|
||||
import EastWingLayout from "./EastWingLayout";
|
||||
import WestWingHighlighting from "./WestWingHighlighting";
|
||||
import WestWingLayout from "./WestWingLayout";
|
||||
import useModal from "../../../store/useModal";
|
||||
import AboutComplexModal from "../../modals/AboutComplexModal";
|
||||
import { IAparmentRes } from "../../../types/apartmentsRes";
|
||||
import FloorEastWingTopLayout from "./FloorEastWingTopLayout";
|
||||
import FloorEastWingTopHighlighting from "./FloorEastWingTopHighlighting";
|
||||
|
||||
// import _appartment from "../../../data/appartments.json";
|
||||
// import { IAppartmentComplex } from "../../../types/apartmentSphere";
|
||||
// const appartments = _appartment as IAppartmentComplex[];
|
||||
import WestWingTopLevelsLayout from "./WestWingTopLevelsLayout";
|
||||
import WestWingTopLevelsHighlighting from "./WestWingTopLevelsHighlighting";
|
||||
import { apartmentsWithoutVirtualTour } from "../../../consts/apartmentsWithoutVirtualTour";
|
||||
|
||||
interface IFloorSidebarProps {
|
||||
currentFloor: IDesctiptionFloor | null;
|
||||
@@ -31,6 +28,9 @@ const FloorSidebar = ({
|
||||
const [hoveredApartment, setHoveredApartment] = useState<IAparmentRes | null>(
|
||||
null
|
||||
);
|
||||
const [defaultApartments, setDefaultApartments] = useState<IAparmentRes[]>(
|
||||
[]
|
||||
);
|
||||
const [is3DTourAvailable] = useState(true);
|
||||
const { setModal } = useModal();
|
||||
const descRef = useRef(null);
|
||||
@@ -46,7 +46,7 @@ const FloorSidebar = ({
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
) {
|
||||
const apartmentType = event.currentTarget.dataset.type;
|
||||
const apartment = floorApartments.find(
|
||||
const apartment = defaultApartments.find(
|
||||
(aprt) => aprt.Unit_Type === apartmentType
|
||||
);
|
||||
if (apartment) {
|
||||
@@ -58,11 +58,12 @@ const FloorSidebar = ({
|
||||
function handleOnMouseOut(): void {
|
||||
setIsDescVisible(false);
|
||||
}
|
||||
|
||||
function handleOnMouseOver(
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
): void {
|
||||
const apartmentType = event.currentTarget.dataset.type;
|
||||
const _hoveredApartment = floorApartments.find(
|
||||
const _hoveredApartment = defaultApartments.find(
|
||||
(apart) => apart.Unit_Type === apartmentType
|
||||
);
|
||||
if (_hoveredApartment) {
|
||||
@@ -81,6 +82,34 @@ const FloorSidebar = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentFloor) return;
|
||||
const _defaultApartment: IAparmentRes[] = apartmentsWithoutVirtualTour.map(
|
||||
(aprt, index) => {
|
||||
const unitNo = `${currentFloor?.wing.slice(1)}-${index}`;
|
||||
return {
|
||||
id: unitNo,
|
||||
Floor: currentFloor.floor,
|
||||
Property_Status: "Available",
|
||||
Unit_Type: aprt.type,
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Suite_Area_Sqft: 0,
|
||||
Balcony_Area_Sqft: 0,
|
||||
No_Of_Bedrooms: 1,
|
||||
Unit_No: unitNo,
|
||||
Total_Area_Sqft: 0,
|
||||
No_of_Bathrooms: 0,
|
||||
Property_Name: "-",
|
||||
Unit_View: "-",
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
setDefaultApartments([..._defaultApartment, ...floorApartments]);
|
||||
|
||||
return () => {};
|
||||
}, [currentFloor, currentFloor?.floor, currentFloor?.wing, floorApartments]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -135,6 +164,54 @@ const FloorSidebar = ({
|
||||
|
||||
<div className="px-10 bg-white flex gap-6 font-semibold text-caption-m rounded-2xl justify-center items-center flex-1 py-4">
|
||||
{currentFloor?.wing === "West Wing" ? (
|
||||
currentFloor && currentFloor.floor <= 21 ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="204"
|
||||
height="588"
|
||||
fill="none"
|
||||
viewBox="0 0 204 588"
|
||||
>
|
||||
<WestWingLayout />
|
||||
<WestWingHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
handleOnMouseOut={handleOnMouseOut}
|
||||
handleOnMouseOver={handleOnMouseOver}
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="205"
|
||||
height="588"
|
||||
fill="none"
|
||||
viewBox="0 0 205 588"
|
||||
>
|
||||
<WestWingTopLevelsLayout />
|
||||
<WestWingTopLevelsHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
handleOnMouseOut={handleOnMouseOut}
|
||||
handleOnMouseOver={handleOnMouseOver}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
width="672"
|
||||
height="280"
|
||||
viewBox="0 0 672 280"
|
||||
>
|
||||
<EastWingLayout />
|
||||
<EastWingHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
handleOnMouseOut={handleOnMouseOut}
|
||||
handleOnMouseOver={handleOnMouseOver}
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
{/* {currentFloor?.wing === "West Wing" ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
@@ -149,37 +226,7 @@ const FloorSidebar = ({
|
||||
handleOnMouseOver={handleOnMouseOver}
|
||||
/>
|
||||
</svg>
|
||||
) : currentFloor && currentFloor.floor <= 21 ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="204"
|
||||
height="588"
|
||||
fill="none"
|
||||
viewBox="0 0 204 588"
|
||||
>
|
||||
<FloorEastWingLayout />
|
||||
<FloorEastWingHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
handleOnMouseOut={handleOnMouseOut}
|
||||
handleOnMouseOver={handleOnMouseOver}
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="205"
|
||||
height="588"
|
||||
fill="none"
|
||||
viewBox="0 0 205 588"
|
||||
>
|
||||
<FloorEastWingTopLayout />
|
||||
<FloorEastWingTopHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
handleOnMouseOut={handleOnMouseOut}
|
||||
handleOnMouseOver={handleOnMouseOver}
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
) : } */}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -10,7 +10,7 @@ interface FloorEastWingHighlightingProps {
|
||||
) => void;
|
||||
}
|
||||
|
||||
function FloorEastWingHighlighting({
|
||||
function WestWingHighlighting({
|
||||
handleOnMouseOut,
|
||||
handleOnMouseOver,
|
||||
handleOnApartmentClick,
|
||||
@@ -79,6 +79,7 @@ function FloorEastWingHighlighting({
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U2 Left"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
onClick={handleOnApartmentClick}
|
||||
@@ -153,6 +154,7 @@ function FloorEastWingHighlighting({
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U2 Right"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
onClick={handleOnApartmentClick}
|
||||
@@ -473,4 +475,4 @@ function FloorEastWingHighlighting({
|
||||
);
|
||||
}
|
||||
|
||||
export default FloorEastWingHighlighting;
|
||||
export default WestWingHighlighting;
|
||||
@@ -1,4 +1,4 @@
|
||||
const FloorEastWingLayout = () => {
|
||||
const WestWingLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<path
|
||||
@@ -459,4 +459,4 @@ const FloorEastWingLayout = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default FloorEastWingLayout;
|
||||
export default WestWingLayout;
|
||||
@@ -1,4 +1,4 @@
|
||||
interface FloorEastWingTopHighlightingProps {
|
||||
interface WestWingTopLevelsHighlightingProps {
|
||||
handleOnMouseOut:
|
||||
| (() => void)
|
||||
| ((event: React.MouseEvent<SVGSVGElement, MouseEvent>) => void);
|
||||
@@ -10,11 +10,11 @@ interface FloorEastWingTopHighlightingProps {
|
||||
) => void;
|
||||
}
|
||||
|
||||
function FloorEastWingTopHighlighting({
|
||||
function WestWingTopLevelsHighlighting({
|
||||
handleOnMouseOut,
|
||||
handleOnMouseOver,
|
||||
handleOnApartmentClick,
|
||||
}: FloorEastWingTopHighlightingProps) {
|
||||
}: WestWingTopLevelsHighlightingProps) {
|
||||
return (
|
||||
<>
|
||||
<svg
|
||||
@@ -72,6 +72,7 @@ function FloorEastWingTopHighlighting({
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U2 Right"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
onClick={handleOnApartmentClick}
|
||||
@@ -96,6 +97,7 @@ function FloorEastWingTopHighlighting({
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U2 Left"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
onClick={handleOnApartmentClick}
|
||||
@@ -220,6 +222,7 @@ function FloorEastWingTopHighlighting({
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U2 Left"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
onClick={handleOnApartmentClick}
|
||||
@@ -244,6 +247,7 @@ function FloorEastWingTopHighlighting({
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="1 BR U2 Right"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
onClick={handleOnApartmentClick}
|
||||
@@ -379,8 +383,33 @@ function FloorEastWingTopHighlighting({
|
||||
fillOpacity="0.2"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-type="Studio Squared"
|
||||
onMouseOut={handleOnMouseOut}
|
||||
onMouseOver={handleOnMouseOver}
|
||||
onClick={handleOnApartmentClick}
|
||||
className="opacity-0 hover:opacity-100 ease-in-out duration-300 transition-opacity cursor-pointer"
|
||||
x={99}
|
||||
y={310}
|
||||
width="78"
|
||||
height="33"
|
||||
viewBox="0 0 78 33"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.9351 32.593H59.7103V1.90207H19.0635V0.658203H4.00961V5.53797H0.624023V7.06293V16.1567V26.9562H5.9351V32.593Z"
|
||||
fill="#00BED7"
|
||||
fillOpacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M77.4163 32.593V6.01846H60.0418V32.593H77.4163Z"
|
||||
fill="#00BED7"
|
||||
fillOpacity="0.2"
|
||||
/>
|
||||
</svg>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default FloorEastWingTopHighlighting;
|
||||
export default WestWingTopLevelsHighlighting;
|
||||
@@ -1,4 +1,4 @@
|
||||
const FloorEastWingTopLayout = () => {
|
||||
const WestWingTopLevelsLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<path
|
||||
@@ -414,4 +414,4 @@ const FloorEastWingTopLayout = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default FloorEastWingTopLayout;
|
||||
export default WestWingTopLevelsLayout;
|
||||
@@ -12,9 +12,14 @@ import {
|
||||
mobileWidht,
|
||||
descriptions,
|
||||
} from "../../consts/masterplan";
|
||||
import { getApartments } from "../../api/apartments";
|
||||
import { updateAccessToken } from "../../api/updateAccessToken";
|
||||
import { IAparmentRes, IApartmentsRes } from "../../types/apartmentsRes";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import useMasterplanFilters from "../../store/useMasterplanFilters";
|
||||
import { initialRoveHomeCheckboxes } from "../../consts/initialMasterplanFilters";
|
||||
import { pageInitial } from "../../consts/initialMasterplanFilters";
|
||||
import { useDebounce } from "@uidotdev/usehooks";
|
||||
import { getFilteredApartments } from "../../calc/getFilteredApartments";
|
||||
import MasterplanLoaderModal from "../modals/MasterplanLoaderModal";
|
||||
|
||||
const skyGardenFloor = 22;
|
||||
|
||||
@@ -27,6 +32,7 @@ const SequenceWing = () => {
|
||||
const [mousePos, setMousePos] = useState<[number, number]>([0, 0]);
|
||||
const [currentHoveredFloor, setHoverCurrentFloor] =
|
||||
useState<null | IDesctiptionFloor>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [currentFloor, setCurrentFloor] = useState<IDesctiptionFloor | null>(
|
||||
null
|
||||
);
|
||||
@@ -41,6 +47,14 @@ const SequenceWing = () => {
|
||||
const [selectedApartments, setSelectedApartments] = useState<IAparmentRes[]>(
|
||||
[]
|
||||
);
|
||||
const {
|
||||
apartmentTypeCheckboxes,
|
||||
switchers,
|
||||
multirangeSliders,
|
||||
viewCheckboxes,
|
||||
sortList,
|
||||
} = useMasterplanFilters();
|
||||
const debouncedSliders = useDebounce(multirangeSliders, 300);
|
||||
|
||||
function handleResize() {
|
||||
const screenWidth = window.innerWidth;
|
||||
@@ -149,25 +163,60 @@ const SequenceWing = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const zohoToken = localStorage.getItem("ACCESS_TOKEN");
|
||||
setIsLoading(true);
|
||||
|
||||
getApartments(zohoToken)
|
||||
getFilteredApartments(
|
||||
zohoToken,
|
||||
setApartments,
|
||||
initialRoveHomeCheckboxes,
|
||||
apartmentTypeCheckboxes,
|
||||
debouncedSliders,
|
||||
switchers,
|
||||
viewCheckboxes,
|
||||
sortList,
|
||||
pageInitial,
|
||||
1000
|
||||
)
|
||||
.catch((error) => {
|
||||
const errorStatus = error.response.status;
|
||||
|
||||
if (errorStatus === 401) {
|
||||
updateAccessToken().then((token) => {
|
||||
if (token) {
|
||||
getApartments(token);
|
||||
getFilteredApartments(
|
||||
token,
|
||||
setApartments,
|
||||
initialRoveHomeCheckboxes,
|
||||
apartmentTypeCheckboxes,
|
||||
debouncedSliders,
|
||||
switchers,
|
||||
viewCheckboxes,
|
||||
sortList,
|
||||
pageInitial,
|
||||
1000
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
const apartmentsData = (data as IApartmentsRes).apartments;
|
||||
const updatedApartments = [...apartmentsData];
|
||||
setApartments(updatedApartments);
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
}, [
|
||||
apartmentTypeCheckboxes,
|
||||
debouncedSliders,
|
||||
sortList,
|
||||
switchers,
|
||||
viewCheckboxes,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
setModal(<MasterplanLoaderModal />);
|
||||
} else {
|
||||
setModal(null);
|
||||
}
|
||||
}, [isLoading, setModal]);
|
||||
|
||||
return (
|
||||
<div className="absolute left-0 overflow-hidden h-screen w-screen select-none ">
|
||||
|
||||
@@ -9,6 +9,7 @@ import { MobileModalWrapper } from "../modals/mobile/MobileModalWrapper";
|
||||
import SendEnquiryMobileModal from "../modals/mobile/SendEnquiryMobileModal";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import useFavorites from "../../store/useFavorites";
|
||||
import { apartmentRoutes } from "../../consts/apartmentsRoutes";
|
||||
|
||||
interface FavoriteAppartmentCardProps {
|
||||
card: IAparmentRes;
|
||||
@@ -19,6 +20,9 @@ const FavoriteAppartmentCard = ({ card }: FavoriteAppartmentCardProps) => {
|
||||
const wing = Unit_No.split("-")[0] === "E" ? "East" : "West";
|
||||
const navigate = useNavigate();
|
||||
const { setFavorites } = useFavorites();
|
||||
const layoutImage = apartmentRoutes.find(
|
||||
(apr) => apr.type === card.Unit_Type
|
||||
)?.layout;
|
||||
|
||||
const { setModal } = useModal();
|
||||
|
||||
@@ -95,7 +99,11 @@ const FavoriteAppartmentCard = ({ card }: FavoriteAppartmentCardProps) => {
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full aspect-square rounded-lg">
|
||||
<img src="/images/layout-1.png" alt="" className="h-full" />
|
||||
<img
|
||||
src={layoutImage}
|
||||
alt=""
|
||||
className="w-full aspect-square object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// import { formatNumber } from "../../calc/formatNumber";
|
||||
import { apartmentRoutes } from "../../consts/apartmentsRoutes";
|
||||
import useFavorites from "../../store/useFavorites";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import Button from "../Button";
|
||||
@@ -13,6 +14,9 @@ interface FavoriteSliderCardProps {
|
||||
const FavoriteSliderCard = ({ card, elementRef }: FavoriteSliderCardProps) => {
|
||||
const wing = card.Unit_No.split("-")[0] === "E" ? "East" : "West";
|
||||
const { setFavorites } = useFavorites();
|
||||
const layoutImage = apartmentRoutes.find(
|
||||
(apr) => apr.type === card.Unit_Type
|
||||
)?.layout;
|
||||
|
||||
const handleOnAddFavoriteClick = (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
@@ -58,9 +62,9 @@ const FavoriteSliderCard = ({ card, elementRef }: FavoriteSliderCardProps) => {
|
||||
/>
|
||||
</div>
|
||||
<img
|
||||
src="/images/layout-1.png"
|
||||
src={layoutImage}
|
||||
alt=""
|
||||
className="w-full pointer-events-none select-none"
|
||||
className="w-full object-contain aspect-square pointer-events-none select-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 pb-6 border-b">
|
||||
|
||||
@@ -12,7 +12,7 @@ const Navbar = () => {
|
||||
const [tabs, setTabs] = useState(_tabs);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { favorites } = useFavorites();
|
||||
const { favorites, setFavorites } = useFavorites();
|
||||
|
||||
const onTabClick = (tab: Tab) => {
|
||||
setSelectedTab(tab);
|
||||
@@ -30,6 +30,13 @@ const Navbar = () => {
|
||||
}
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
const _favorites = localStorage.getItem("Favorites");
|
||||
if (!_favorites) return;
|
||||
const convertedFavorute = JSON.parse(_favorites);
|
||||
setFavorites(convertedFavorute);
|
||||
}, [setFavorites]);
|
||||
|
||||
useEffect(() => {
|
||||
const updatedTabs = _tabs.map((tab) => {
|
||||
if (tab.value === "Favorites") {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
const LoadingIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M52.267 32.0001C52.8561 32.0001 53.3364 31.5221 53.307 30.9338C53.0735 26.2653 51.3116 21.7914 48.2774 18.21C45.0129 14.3567 40.4871 11.7865 35.5055 10.9567C30.5238 10.1269 25.4094 11.0913 21.072 13.6784C16.7347 16.2655 13.4558 20.3074 11.8188 25.085C10.1818 29.8626 10.2928 35.0661 12.1321 39.7695C13.9714 44.473 17.4196 48.3714 21.8633 50.7711C26.307 53.1709 31.458 53.9163 36.3997 52.8749C40.9928 51.9069 45.1282 49.4536 48.1757 45.9094C48.5598 45.4627 48.4735 44.7906 48.0082 44.4293C47.5429 44.068 46.875 44.1546 46.4884 44.5991C43.7524 47.7455 40.0586 49.9235 35.9598 50.7874C31.5122 51.7247 26.8764 51.0538 22.877 48.894C18.8777 46.7343 15.7743 43.2257 14.1189 38.9926C12.4636 34.7595 12.3637 30.0764 13.837 25.7765C15.3103 21.4767 18.2613 17.839 22.1649 15.5106C26.0685 13.1822 30.6715 12.3142 35.155 13.061C39.6385 13.8078 43.7116 16.121 46.6497 19.589C49.3574 22.785 50.9393 26.7708 51.1707 30.9339C51.2034 31.522 51.6779 32.0001 52.267 32.0001Z"
|
||||
fill="url(#paint0_angular_1498_24245)"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient
|
||||
id="paint0_angular_1498_24245"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(32.0003 32.0001) rotate(45) scale(21.1189)"
|
||||
>
|
||||
<stop offset="0.874517" stopColor="#00BED7" />
|
||||
<stop offset="0.982613" stopColor="#00BED7" stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingIcon;
|
||||
@@ -8,35 +8,8 @@ import SwitchToggle from "../SwitchToggle";
|
||||
import HeartIcon from "../icons/Heart";
|
||||
import LeftArrowSliderIcon from "../icons/LeftArrowSliderIcon";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
|
||||
interface IApartmentRoute {
|
||||
type: string;
|
||||
virtualTour: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const apartmentRoute: IApartmentRoute[] = [
|
||||
{
|
||||
type: "1 BR Squared",
|
||||
virtualTour: "apartments-studio-3",
|
||||
id: "1",
|
||||
},
|
||||
{
|
||||
type: "Studio Squared",
|
||||
virtualTour: "apartments-studio-2",
|
||||
id: "2",
|
||||
},
|
||||
{
|
||||
type: "2 BR Squared",
|
||||
virtualTour: "apartments-studio-4",
|
||||
id: "3",
|
||||
},
|
||||
{
|
||||
type: "Studio Flex",
|
||||
virtualTour: "apartments-studio-1",
|
||||
id: "4",
|
||||
},
|
||||
];
|
||||
import { apartmentsWithoutVirtualTour } from "../../consts/apartmentsWithoutVirtualTour";
|
||||
import { apartmentRoutes } from "../../consts/apartmentsRoutes";
|
||||
|
||||
interface AboutComplexModalProps {
|
||||
apartment: IAparmentRes;
|
||||
@@ -48,6 +21,15 @@ const AboutComplexModal = ({ apartment }: AboutComplexModalProps) => {
|
||||
const navigate = useNavigate();
|
||||
const unitNumber = apartment.Unit_No.split("-")[1];
|
||||
const wing = apartment.Unit_No.split("-")[0] === "E" ? "East" : "West";
|
||||
const isVirtualTourAvailialbe = !apartmentsWithoutVirtualTour.some(
|
||||
(aprt) => aprt.type === apartment.Unit_Type
|
||||
);
|
||||
|
||||
const layoutWithoutVirtualTour = isVirtualTourAvailialbe
|
||||
? apartmentRoutes.find((aprt) => aprt.type === apartment.Unit_Type)?.layout
|
||||
: apartmentsWithoutVirtualTour.find(
|
||||
(aprt) => aprt.type === apartment.Unit_Type
|
||||
)?.layout;
|
||||
|
||||
const handleOnBackClick = () => {
|
||||
setModal(false);
|
||||
@@ -59,7 +41,7 @@ const AboutComplexModal = ({ apartment }: AboutComplexModalProps) => {
|
||||
|
||||
const handleOn3DTourClick = () => {
|
||||
setModal(null);
|
||||
const virtualTour = apartmentRoute.find(
|
||||
const virtualTour = apartmentRoutes.find(
|
||||
(route) => route.type === apartment.Unit_Type
|
||||
);
|
||||
|
||||
@@ -99,10 +81,10 @@ const AboutComplexModal = ({ apartment }: AboutComplexModalProps) => {
|
||||
</div>
|
||||
{/* Layout */}
|
||||
<div className="p-10 rounded-2xl bg-white flex flex-col items-center gap-8 relative justify-center h-full">
|
||||
<div className="w-full flex justify-center h-full">
|
||||
<div className="flex justify-center h-full">
|
||||
<img
|
||||
className="object-cover h-full"
|
||||
src="/images/layout-1.png"
|
||||
src={`${layoutWithoutVirtualTour}`}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
@@ -172,6 +154,7 @@ const AboutComplexModal = ({ apartment }: AboutComplexModalProps) => {
|
||||
text="3D Tour"
|
||||
buttonType="secondary"
|
||||
className="w-full flex justify-center"
|
||||
disabled={!isVirtualTourAvailialbe}
|
||||
/>
|
||||
<Button
|
||||
text="Send Enquiry"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useState } from "react";
|
||||
import useModal from "../../store/useModal";
|
||||
import Button from "../Button";
|
||||
import CrossIcon from "../icons/CrossIcon";
|
||||
@@ -6,38 +5,38 @@ import ResetIcon from "../icons/ResetIcon";
|
||||
import Checkbox from "../Checkbox";
|
||||
import Switch from "../Switch";
|
||||
import MultiRangeSlider from "../MultiRangeSlider";
|
||||
import { ICheckbox } from "../../types/checkbox";
|
||||
import { IMultirangeSlider } from "../../types/multirangeSlider";
|
||||
import { ISwitcher } from "../../types/switcher";
|
||||
import {
|
||||
initialAparmentTypeCheckboxes,
|
||||
initialSliders,
|
||||
initialSwitchers,
|
||||
initialViewCheckboxes,
|
||||
} from "../../consts/initialMasterplanFilters";
|
||||
import useMasterplanFilters from "../../store/useMasterplanFilters";
|
||||
|
||||
const MasterplanFilters = () => {
|
||||
const { setModal } = useModal();
|
||||
const [apartmentTypeCheckbox, setAparmentTypeCheckboxes] = useState<
|
||||
ICheckbox[]
|
||||
>(initialAparmentTypeCheckboxes);
|
||||
const [viewCheckboxes, setViewCheckboxes] = useState<ICheckbox[]>(
|
||||
initialViewCheckboxes
|
||||
);
|
||||
const [sliders, setSliders] = useState<IMultirangeSlider[]>(initialSliders);
|
||||
const [switchers, setSwitchers] = useState<ISwitcher[]>(initialSwitchers);
|
||||
const {
|
||||
apartmentTypeCheckboxes,
|
||||
setApartmentTypeCheckboxes,
|
||||
viewCheckboxes,
|
||||
setViewCheckboxes,
|
||||
multirangeSliders,
|
||||
setMultirangeSliders,
|
||||
switchers,
|
||||
setSwitchers,
|
||||
} = useMasterplanFilters();
|
||||
|
||||
const handleOnCloseClick = () => setModal(null);
|
||||
|
||||
const handleOnApartmentTypeCheckboxClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = apartmentTypeCheckbox.map((cbox) => {
|
||||
const updatedCheckboxes = apartmentTypeCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setAparmentTypeCheckboxes(updatedCheckboxes);
|
||||
setApartmentTypeCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnViewCheckboxClick = (checkboxId: string) => {
|
||||
@@ -55,7 +54,7 @@ const MasterplanFilters = () => {
|
||||
sliderId: string,
|
||||
e: [a: number, b: number]
|
||||
) => {
|
||||
const updatedSliders = sliders.map((slider) => {
|
||||
const updatedSliders = multirangeSliders.map((slider) => {
|
||||
if (sliderId !== slider.id) return slider;
|
||||
|
||||
return {
|
||||
@@ -65,7 +64,7 @@ const MasterplanFilters = () => {
|
||||
};
|
||||
});
|
||||
|
||||
setSliders(updatedSliders);
|
||||
setMultirangeSliders(updatedSliders);
|
||||
};
|
||||
|
||||
const handleOnSwitcherClick = (switcherId: string) => {
|
||||
@@ -80,14 +79,14 @@ const MasterplanFilters = () => {
|
||||
};
|
||||
|
||||
const handleOnResetClick = () => {
|
||||
setAparmentTypeCheckboxes(initialAparmentTypeCheckboxes);
|
||||
setApartmentTypeCheckboxes(initialAparmentTypeCheckboxes);
|
||||
setViewCheckboxes(initialViewCheckboxes);
|
||||
setSliders(initialSliders);
|
||||
setMultirangeSliders(initialSliders);
|
||||
setSwitchers(initialSwitchers);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute z-[99999901] top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] items-center ">
|
||||
<div className="absolute z-[99999901] top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] items-center select-none">
|
||||
<div className="h-screen bg-[#F3F3F2] flex flex-col items-center justify-between w-[360px] overflow-y-scroll">
|
||||
<div className="w-full py-6 px-6 flex flex-col items-center">
|
||||
<div className="flex justify-between border-b border-[#E2E2DC] w-full pb-4">
|
||||
@@ -102,7 +101,7 @@ const MasterplanFilters = () => {
|
||||
<div className="flex flex-col pt-6 w-full">
|
||||
<p className="text-[#0D1922] text-s pb-4">Apartment type</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
{apartmentTypeCheckbox.map((checkbox) => (
|
||||
{apartmentTypeCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
@@ -112,7 +111,7 @@ const MasterplanFilters = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col pt-6 w-full gap-8">
|
||||
{sliders.map((slider) => (
|
||||
{multirangeSliders.map((slider) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-s ">{slider.title}</p>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
const MasterplanLoaderModal = () => {
|
||||
return (
|
||||
<div className="absolute z-[99999] top-0 left-0 w-screen bg-[#F3F3F2] h-screen flex justify-center items-center">
|
||||
<div className="animate-spin">
|
||||
<img src="/images/loader.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MasterplanLoaderModal;
|
||||
@@ -0,0 +1,11 @@
|
||||
const SearchLoaderModal = () => {
|
||||
return (
|
||||
<div className="absolute z-[99] top-0 left-0 w-screen bg-[#F3F3F2] h-screen flex justify-center items-center">
|
||||
<div className="animate-spin">
|
||||
<img src="/images/loader.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchLoaderModal;
|
||||
@@ -0,0 +1,32 @@
|
||||
import useModal from "../../store/useModal";
|
||||
import CrossIcon from "../icons/CrossIcon";
|
||||
|
||||
interface VirtualTourVideoModalProps {
|
||||
videoHref: string;
|
||||
}
|
||||
|
||||
const VirtualTourVideoModal = ({ videoHref }: VirtualTourVideoModalProps) => {
|
||||
const { setModal } = useModal();
|
||||
|
||||
const handleOnCloseClick = () => {
|
||||
setModal(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute z-[99999999] top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] flex justify-center items-center">
|
||||
<div
|
||||
className="aspect-video xl:h-[768px] xl:w-auto w-screen relative select-none cursor-pointer"
|
||||
onClick={handleOnCloseClick}
|
||||
>
|
||||
<div className="absolute top-[10px] right-[10px] text-white">
|
||||
<CrossIcon />
|
||||
</div>
|
||||
<video autoPlay muted className="w-full" playsInline preload="metadata">
|
||||
<source src={videoHref} type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VirtualTourVideoModal;
|
||||
@@ -29,6 +29,16 @@
|
||||
border-radius: 50%;
|
||||
background: #00bed7;
|
||||
}
|
||||
/* .range-slider .range-slider__thumb .disabled {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
background: #0d192266;
|
||||
} */
|
||||
.range-slider .range-slider__thumb:focus-visible {
|
||||
outline: 0;
|
||||
}
|
||||
@@ -38,6 +48,7 @@
|
||||
.range-slider .range-slider__thumb[data-disabled] {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.range-slider .range-slider__range {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
@@ -47,6 +58,17 @@
|
||||
height: 100%;
|
||||
background: #00bed7;
|
||||
}
|
||||
|
||||
/* .range-slider .range-slider__range .disabled {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
transform: translate(0, -50%);
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: white;
|
||||
} */
|
||||
|
||||
.range-slider[data-vertical] .range-slider__range {
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
import { useState } from "react";
|
||||
import { ISwitchLabel } from "../../types/switchLabel";
|
||||
import SwitchToggle from "../SwitchToggle";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import { apartmentRoutes } from "../../consts/apartmentsRoutes";
|
||||
|
||||
const apartmentLayouts: ISwitchLabel[] = [
|
||||
{ id: "1", label: "Layout" },
|
||||
{ id: "2", label: "On the floor" },
|
||||
];
|
||||
|
||||
const ApartmentLayout = () => {
|
||||
interface ApartmentLayoutProps {
|
||||
currentApartment: IAparmentRes;
|
||||
}
|
||||
|
||||
const ApartmentLayout = ({ currentApartment }: ApartmentLayoutProps) => {
|
||||
const [currentLabel, setCurrentLabel] = useState(apartmentLayouts[0]);
|
||||
const handleOnSwitchClick = (label: ISwitchLabel) => {
|
||||
setCurrentLabel(label);
|
||||
};
|
||||
const imageLayout = apartmentRoutes.find(
|
||||
(aprt) => aprt.type === currentApartment.Unit_Type
|
||||
)?.layout;
|
||||
|
||||
return (
|
||||
<div className="p-10 pt-6 rounded-2xl bg-white flex flex-col items-center gap-8 relative h-full">
|
||||
<div className="w-full xl:px-[304px] sm:px-24 h-full">
|
||||
<div className="p-10 pt-6 rounded-2xl bg-white flex flex-col items-center gap-8 relative h-full ">
|
||||
<div className="w-full xl:px-[304px] sm:px-24 h-full max-h-[888px]">
|
||||
<img
|
||||
className="w-full h-full object-cover"
|
||||
src="/images/layout-1.png"
|
||||
src={imageLayout ? imageLayout : "/images/layout-1.png"}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,9 +2,15 @@ import { useNavigate } from "react-router-dom";
|
||||
import Button from "../Button";
|
||||
import HeartIcon from "../icons/Heart";
|
||||
import LeftArrowSliderIcon from "../icons/LeftArrowSliderIcon";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
|
||||
const ButtonPanel = () => {
|
||||
interface ButtonPanelProps {
|
||||
currentApartment: IAparmentRes;
|
||||
}
|
||||
|
||||
const ButtonPanel = ({ currentApartment }: ButtonPanelProps) => {
|
||||
const navigate = useNavigate();
|
||||
const unitNo = currentApartment.Unit_No.split("-")[1];
|
||||
|
||||
const handleOnBackClick = () => {
|
||||
navigate(-1);
|
||||
@@ -19,9 +25,11 @@ const ButtonPanel = () => {
|
||||
onClick={handleOnBackClick}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[#73787C] text-caption-m font-semibold">№ 213</p>
|
||||
<p className="text-[#73787C] text-caption-m font-semibold">
|
||||
№ {unitNo}
|
||||
</p>
|
||||
<h2 className="text-subheadline-s font-semibold text-[#0D1922]">
|
||||
1 bedroom apartment
|
||||
{currentApartment.Unit_Type}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { ILayoutCard } from "../../types/layoutCard";
|
||||
import { formatNumber } from "../../calc/formatNumber";
|
||||
// import { formatNumber } from "../../calc/formatNumber";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
|
||||
interface LayoutCardProps {
|
||||
layoutCard: ILayoutCard;
|
||||
layoutCard: IAparmentRes;
|
||||
elementRef: React.MutableRefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
const SimilarAppartmentCard = ({ layoutCard, elementRef }: LayoutCardProps) => {
|
||||
const {
|
||||
floorEnd,
|
||||
floorStart,
|
||||
apartmentType,
|
||||
roveHome,
|
||||
wing,
|
||||
units,
|
||||
square,
|
||||
cost,
|
||||
id,
|
||||
} = layoutCard;
|
||||
const { Floor, Unit_Type, Project_Name, Total_Area_Sqft, Unit_No, id } =
|
||||
layoutCard;
|
||||
const navigate = useNavigate();
|
||||
const wing = Unit_No.split("-")[0] === "E" ? "East Wing" : "West Wing";
|
||||
|
||||
const handleOnClick = () => {
|
||||
navigate(`${id}`);
|
||||
@@ -33,19 +25,17 @@ const SimilarAppartmentCard = ({ layoutCard, elementRef }: LayoutCardProps) => {
|
||||
>
|
||||
<div className="flex gap-4 justify-between">
|
||||
<div className="flex gap-1 flex-col">
|
||||
<p className="text-[#00BED7] text-s leading-5">
|
||||
Rove Home {roveHome}
|
||||
</p>
|
||||
<p className="text-[#00BED7] text-s leading-5">{Project_Name}</p>
|
||||
<div className="text-[#73787C] flex gap-2 items-center w-fit">
|
||||
<p className="text-caption-m font-semibold leading-4">{wing}</p>
|
||||
<div className="w-1 h-1 bg-[#E2E2DC] rounded-full"></div>
|
||||
<p className="text-caption-m font-semibold leading-4">
|
||||
Floor {floorStart}-{floorEnd}
|
||||
Floor {Floor}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-[#00BED7] text-white text-caption-m font-semibold rounded-full py-[3px] px-2 self-start text-nowrap">
|
||||
{units} units
|
||||
{Total_Area_Sqft} units
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full aspect-square rounded-lg">
|
||||
@@ -53,10 +43,11 @@ const SimilarAppartmentCard = ({ layoutCard, elementRef }: LayoutCardProps) => {
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p className="text[#0D1922] text-s">
|
||||
{apartmentType}, {square} Sqft
|
||||
{Unit_Type}, {Total_Area_Sqft} Sqft
|
||||
</p>
|
||||
<p className="text-[#00BED7] text-m font-bold">
|
||||
AED {formatNumber(cost, ",", 3, 1)}
|
||||
{/* AED {formatNumber(cost, ",", 3, 1)} */}
|
||||
Unavailiable
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,7 +85,7 @@ const SimilarSlider = () => {
|
||||
}}
|
||||
>
|
||||
<div className="flex w-fit gap-4 ">
|
||||
{Array.from({ length: Math.floor(cards.length / cols) }).map(
|
||||
{Array.from({ length: Math.round(cards.length / cols) }).map(
|
||||
(_, index) => {
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -4,6 +4,7 @@ import HeartIcon from "../icons/Heart";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import { useEffect, useState } from "react";
|
||||
import useFavorites from "../../store/useFavorites";
|
||||
import { apartmentRoutes } from "../../consts/apartmentsRoutes";
|
||||
|
||||
interface LayoutCardProps {
|
||||
apartmentCard: IAparmentRes;
|
||||
@@ -24,6 +25,9 @@ const LayoutCard = ({ apartmentCard }: LayoutCardProps) => {
|
||||
const totalArea = `${_totalArea}`.split(".").join(",");
|
||||
const [isFavorite, setIsFavorite] = useState(false);
|
||||
const { setFavorites } = useFavorites();
|
||||
const layoutImage = apartmentRoutes.find(
|
||||
(apr) => apr.type === unitType
|
||||
)?.layout;
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -102,8 +106,12 @@ const LayoutCard = ({ apartmentCard }: LayoutCardProps) => {
|
||||
isCircleRounded
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full aspect-square rounded-lg pt-6">
|
||||
<img src="/images/layout-1.png" alt="" className="h-full" />
|
||||
<div className="w-full aspect-square rounded-lg pt-6 ">
|
||||
<img
|
||||
src={`${layoutImage}`}
|
||||
alt=""
|
||||
className="w-full aspect-square object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 pt-6">
|
||||
<p className="text-[#0D1922] text-s">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import LayoutCard from "./LayoutCard";
|
||||
import SortButton from "./SortButton";
|
||||
import SearchIcon from "../icons/SearchIcon";
|
||||
@@ -17,9 +17,11 @@ import {
|
||||
pageInitial,
|
||||
} from "../../consts/initialMasterplanFilters";
|
||||
import { getFilteredApartments } from "../../calc/getFilteredApartments";
|
||||
import SearchLoader from "./SearchLoader";
|
||||
|
||||
const LayoutOptions = () => {
|
||||
const { apartments, setApartments } = useApartments();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const { setModal } = useModal();
|
||||
const {
|
||||
@@ -63,6 +65,7 @@ const LayoutOptions = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const zohoToken = localStorage.getItem("ACCESS_TOKEN");
|
||||
setIsLoading(true);
|
||||
|
||||
getFilteredApartments(
|
||||
zohoToken,
|
||||
@@ -75,28 +78,32 @@ const LayoutOptions = () => {
|
||||
sortList,
|
||||
pageInitial,
|
||||
perPage
|
||||
).catch((error) => {
|
||||
const errorStatus = error.response.status;
|
||||
)
|
||||
.catch((error) => {
|
||||
const errorStatus = error.response.status;
|
||||
|
||||
if (errorStatus === 401) {
|
||||
updateAccessToken().then((token) => {
|
||||
if (token) {
|
||||
getFilteredApartments(
|
||||
token,
|
||||
setApartments,
|
||||
roveHomeTypeCheckboxes,
|
||||
apartmentTypeCheckboxes,
|
||||
debouncedSliders,
|
||||
switchers,
|
||||
viewCheckboxes,
|
||||
sortList,
|
||||
pageInitial,
|
||||
perPage
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (errorStatus === 401) {
|
||||
updateAccessToken().then((token) => {
|
||||
if (token) {
|
||||
getFilteredApartments(
|
||||
token,
|
||||
setApartments,
|
||||
roveHomeTypeCheckboxes,
|
||||
apartmentTypeCheckboxes,
|
||||
debouncedSliders,
|
||||
switchers,
|
||||
viewCheckboxes,
|
||||
sortList,
|
||||
pageInitial,
|
||||
perPage
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, [
|
||||
setApartments,
|
||||
roveHomeTypeCheckboxes,
|
||||
@@ -129,12 +136,16 @@ const LayoutOptions = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 grid-cols-2 gap-4 pt-6">
|
||||
{apartments &&
|
||||
apartments.map((apartment) => (
|
||||
<LayoutCard apartmentCard={apartment} key={apartment.id} />
|
||||
))}
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<SearchLoader />
|
||||
) : (
|
||||
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 grid-cols-2 gap-4 pt-6">
|
||||
{apartments &&
|
||||
apartments.map((apartment) => (
|
||||
<LayoutCard apartmentCard={apartment} key={apartment.id} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="bg-white rounded-lg mt-4 py-[10px] flex justify-center select-none cursor-pointer items-center gap-2"
|
||||
onClick={handleOnShowMoreBtnClick}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
const SearchLoader = () => {
|
||||
return (
|
||||
<div className="h-full w-full flex justify-center pt-[120px]">
|
||||
<div className="w-fit h-fit animate-spin">
|
||||
<img src="/images/loader.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchLoader;
|
||||
@@ -1,16 +1,25 @@
|
||||
import useModal from "../../store/useModal";
|
||||
import { ISphereLink } from "../../types/apartmentSphere";
|
||||
import VideoIcon from "../icons/VideoIcon";
|
||||
import VirtualTourVideoModal from "../modals/VirtualTourVideoModal";
|
||||
|
||||
interface VideoMarkerProps {
|
||||
sphereLink: ISphereLink;
|
||||
}
|
||||
|
||||
const VideoMarker = ({ sphereLink }: VideoMarkerProps) => {
|
||||
const { setModal } = useModal();
|
||||
|
||||
const handleOnClick = () => {
|
||||
sphereLink.video &&
|
||||
setModal(<VirtualTourVideoModal videoHref={sphereLink.video} />);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div
|
||||
className="bg-white w-9 h-9 rounded-full text-[#00BED7] flex items-center justify-center cursor-pointer peer"
|
||||
// onClick={handleOnClick}
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
<VideoIcon />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { IApartmentRoute } from "../types/apartmentRoute";
|
||||
|
||||
const apartmentRoutes: IApartmentRoute[] = [
|
||||
{
|
||||
type: "1 BR Squared",
|
||||
virtualTour: "apartments-studio-3",
|
||||
id: "1",
|
||||
layout: "/images/layouts/layout-2.jpg",
|
||||
},
|
||||
{
|
||||
type: "Studio Squared",
|
||||
virtualTour: "apartments-studio-2",
|
||||
id: "2",
|
||||
layout: "/images/layouts/layout-4.jpg",
|
||||
},
|
||||
{
|
||||
type: "2 BR Squared",
|
||||
virtualTour: "apartments-studio-4",
|
||||
id: "3",
|
||||
layout: "/images/layouts/layout-3.jpg",
|
||||
},
|
||||
{
|
||||
type: "Studio Flex",
|
||||
virtualTour: "apartments-studio-1",
|
||||
id: "4",
|
||||
layout: "/images/layouts/layout-1.jpg",
|
||||
},
|
||||
];
|
||||
|
||||
export { apartmentRoutes };
|
||||
@@ -0,0 +1,14 @@
|
||||
interface IApartmentWithoutVirtualTour {
|
||||
type: string;
|
||||
layout: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const apartmentsWithoutVirtualTour: IApartmentWithoutVirtualTour[] = [
|
||||
{ type: "1 BR U1 Left", layout: "/images/layouts/layout-5.jpg", id: "1" },
|
||||
{ type: "1 BR U1 Right", layout: "/images/layouts/layout-6.jpg", id: "2" },
|
||||
{ type: "1 BR U2 Left", layout: "/images/layouts/layout-7.jpg", id: "3" },
|
||||
{ type: "1 BR U2 Right", layout: "/images/layouts/layout-8.jpg", id: "4" },
|
||||
];
|
||||
|
||||
export { apartmentsWithoutVirtualTour };
|
||||
@@ -1,137 +1,108 @@
|
||||
import { ILayoutCard } from "../types/layoutCard";
|
||||
import { IAparmentRes } from "../types/apartmentsRes";
|
||||
|
||||
const layoutsCards: ILayoutCard[] = [
|
||||
// Property_Status: string;
|
||||
// Unit_Type: string;
|
||||
// Project_Name: string;
|
||||
// Suite_Area_Sqft: number;
|
||||
// Balcony_Area_Sqft: number;
|
||||
// No_Of_Bedrooms: number;
|
||||
// Unit_No: string;
|
||||
// id: string;
|
||||
// Total_Area_Sqft: number;
|
||||
// No_of_Bathrooms: number;
|
||||
// Property_Name: string;
|
||||
// Unit_View: string;
|
||||
|
||||
const layoutsCards: IAparmentRes[] = [
|
||||
{
|
||||
id: "1",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "Studio Flex",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 10488888,
|
||||
square: 619,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 619,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1138888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "Studio Flex",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 10488888,
|
||||
square: 609,
|
||||
Property_Status: "",
|
||||
Unit_Type: "",
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Floor: 5,
|
||||
Balcony_Area_Sqft: 77.5,
|
||||
No_of_Bathrooms: 1,
|
||||
No_Of_Bedrooms: 1,
|
||||
Total_Area_Sqft: 402,
|
||||
Property_Name: "",
|
||||
Unit_View: "Canal / Amenities",
|
||||
Suite_Area_Sqft: 0,
|
||||
Unit_No: "W-502",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
Property_Status: "",
|
||||
Unit_Type: "",
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Floor: 5,
|
||||
Balcony_Area_Sqft: 77.5,
|
||||
No_of_Bathrooms: 1,
|
||||
No_Of_Bedrooms: 1,
|
||||
Total_Area_Sqft: 402,
|
||||
Property_Name: "",
|
||||
Unit_View: "Canal / Amenities",
|
||||
Suite_Area_Sqft: 0,
|
||||
Unit_No: "W-502",
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
id: "2",
|
||||
Property_Status: "",
|
||||
Unit_Type: "",
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Floor: 5,
|
||||
Balcony_Area_Sqft: 77.5,
|
||||
No_of_Bathrooms: 1,
|
||||
No_Of_Bedrooms: 1,
|
||||
Total_Area_Sqft: 402,
|
||||
Property_Name: "",
|
||||
Unit_View: "Canal / Amenities",
|
||||
Suite_Area_Sqft: 0,
|
||||
Unit_No: "W-502",
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1138888,
|
||||
square: 609,
|
||||
id: "3",
|
||||
Property_Status: "",
|
||||
Unit_Type: "",
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Floor: 5,
|
||||
Balcony_Area_Sqft: 77.5,
|
||||
No_of_Bathrooms: 1,
|
||||
No_Of_Bedrooms: 1,
|
||||
Total_Area_Sqft: 402,
|
||||
Property_Name: "",
|
||||
Unit_View: "Canal / Amenities",
|
||||
Suite_Area_Sqft: 0,
|
||||
Unit_No: "W-502",
|
||||
},
|
||||
{
|
||||
id: "9",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "Studio Flex",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 10488888,
|
||||
square: 609,
|
||||
id: "4",
|
||||
Property_Status: "",
|
||||
Unit_Type: "",
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Floor: 5,
|
||||
Balcony_Area_Sqft: 77.5,
|
||||
No_of_Bathrooms: 1,
|
||||
No_Of_Bedrooms: 1,
|
||||
Total_Area_Sqft: 402,
|
||||
Property_Name: "",
|
||||
Unit_View: "Canal / Amenities",
|
||||
Suite_Area_Sqft: 0,
|
||||
Unit_No: "W-502",
|
||||
},
|
||||
{
|
||||
id: "10",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "11",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1668888,
|
||||
square: 609,
|
||||
},
|
||||
{
|
||||
id: "12",
|
||||
roveHome: "Marasi Drive",
|
||||
apartmentType: "1 Bedroom",
|
||||
wing: "East Wing",
|
||||
floorStart: 11,
|
||||
floorEnd: 35,
|
||||
units: 234,
|
||||
cost: 1138888,
|
||||
square: 609,
|
||||
id: "5",
|
||||
Property_Status: "",
|
||||
Unit_Type: "",
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Floor: 5,
|
||||
Balcony_Area_Sqft: 77.5,
|
||||
No_of_Bathrooms: 1,
|
||||
No_Of_Bedrooms: 1,
|
||||
Total_Area_Sqft: 402,
|
||||
Property_Name: "",
|
||||
Unit_View: "Canal / Amenities",
|
||||
Suite_Area_Sqft: 0,
|
||||
Unit_No: "W-502",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
{
|
||||
"id": "studio-1_video-1",
|
||||
"type": "video",
|
||||
"video": "",
|
||||
"video": "/videos/studio_flex_bed.mp4",
|
||||
"videoTitle": "Cloud Bed",
|
||||
"labelPosition": [-5, 0, 36.11]
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ const router = createBrowserRouter([
|
||||
element: <Search />,
|
||||
},
|
||||
{
|
||||
path: "/search/:apartmentType",
|
||||
path: "/search/:id",
|
||||
element: <SearchApartment />,
|
||||
},
|
||||
// {
|
||||
|
||||
@@ -2,26 +2,94 @@ import Footer from "../components/Footer";
|
||||
import ButtonPanel from "../components/searchApartment/ButtonPanel";
|
||||
import ApartmentLayout from "../components/searchApartment/ApartmentLayout";
|
||||
import ApartmentSidebar from "../components/ApartmentSidebar";
|
||||
import SimilarSlider from "../components/searchApartment/SimilarSlider";
|
||||
// import SimilarSlider from "../components/searchApartment/SimilarSlider";
|
||||
import StudioDescriptionSection from "../components/searchApartment/StudioDescriptionSection";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getApartments } from "../api/apartments";
|
||||
import { updateAccessToken } from "../api/updateAccessToken";
|
||||
import { IAparmentRes, IApartmentsRes } from "../types/apartmentsRes";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import useModal from "../store/useModal";
|
||||
import SearchLoaderModal from "../components/modals/SearchLoaderModal";
|
||||
|
||||
const SearchApartment = () => {
|
||||
const { id } = useParams();
|
||||
const [currentApartment, setCurrentApartment] = useState<IAparmentRes>();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const { setModal } = useModal();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
const zohoToken = localStorage.getItem("ACCESS_TOKEN");
|
||||
|
||||
getApartments(zohoToken)
|
||||
.then((data) => {
|
||||
const apartmentsData = (data as IApartmentsRes).apartments;
|
||||
const _currentApartment = apartmentsData.find((aprt) => aprt.id === id);
|
||||
if (_currentApartment) {
|
||||
setCurrentApartment(_currentApartment);
|
||||
} else {
|
||||
navigate(-1);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorStatus = error.response.status;
|
||||
|
||||
if (errorStatus === 401) {
|
||||
updateAccessToken().then((token) => {
|
||||
if (token) {
|
||||
getApartments(zohoToken).then((data) => {
|
||||
const apartmentsData = (data as IApartmentsRes).apartments;
|
||||
const _currentApartment = apartmentsData.find(
|
||||
(aprt) => aprt.id === id
|
||||
);
|
||||
if (_currentApartment) {
|
||||
setCurrentApartment(_currentApartment);
|
||||
} else {
|
||||
navigate(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, [id, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
setModal(<SearchLoaderModal />);
|
||||
} else {
|
||||
setModal(null);
|
||||
}
|
||||
}, [isLoading, setModal]);
|
||||
|
||||
return (
|
||||
<div className="overflow-scroll h-screen w-screen pt-14">
|
||||
<div className="grid grid-cols-9 px-4 pt-6 pb-16 gap-4">
|
||||
<div className="flex flex-col lg:col-span-7 col-span-full">
|
||||
<ButtonPanel />
|
||||
<ApartmentLayout />
|
||||
</div>
|
||||
<div className="lg:col-span-2 rounded-lg col-span-full">
|
||||
<ApartmentSidebar />
|
||||
{currentApartment && (
|
||||
<ButtonPanel currentApartment={currentApartment} />
|
||||
)}
|
||||
{currentApartment && (
|
||||
<ApartmentLayout currentApartment={currentApartment} />
|
||||
)}
|
||||
</div>
|
||||
{currentApartment && (
|
||||
<div className="lg:col-span-2 rounded-lg col-span-full">
|
||||
<ApartmentSidebar currentApartment={currentApartment} />
|
||||
</div>
|
||||
)}
|
||||
<div className="col-span-full">
|
||||
<StudioDescriptionSection />
|
||||
</div>
|
||||
<div className="col-span-full">
|
||||
{/* <div className="col-span-full">
|
||||
<SimilarSlider />
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { create } from "zustand";
|
||||
import { ICheckbox } from "../types/checkbox";
|
||||
import {
|
||||
initialAparmentTypeCheckboxes,
|
||||
initialSliders,
|
||||
initialSwitchers,
|
||||
initialViewCheckboxes,
|
||||
initialRoveHomeCheckboxes,
|
||||
initialSortList,
|
||||
} from "../consts/initialMasterplanFilters";
|
||||
import { IMultirangeSlider } from "../types/multirangeSlider";
|
||||
import { ISwitcher } from "../types/switcher";
|
||||
import { ISort } from "../types/sortType";
|
||||
import { perPageInitial } from "../consts/initialMasterplanFilters";
|
||||
|
||||
interface Store {
|
||||
apartmentTypeCheckboxes: ICheckbox[];
|
||||
viewCheckboxes: ICheckbox[];
|
||||
sortList: ISort[];
|
||||
page: number;
|
||||
perPage: number;
|
||||
setPage: (page: number) => void;
|
||||
setPerPage: (perPage: number) => void;
|
||||
setSortList: (sortList: ISort[]) => void;
|
||||
setApartmentTypeCheckboxes: (typeCheckboxes: ICheckbox[]) => void;
|
||||
setViewCheckboxes: (checkboxes: ICheckbox[]) => void;
|
||||
roveHomeTypeCheckboxes: ICheckbox[];
|
||||
setRoveHomeTypeCheckboxes: (typeCheckboxes: ICheckbox[]) => void;
|
||||
multirangeSliders: IMultirangeSlider[];
|
||||
setMultirangeSliders: (multirangeSliders: IMultirangeSlider[]) => void;
|
||||
switchers: ISwitcher[];
|
||||
setSwitchers: (switchers: ISwitcher[]) => void;
|
||||
}
|
||||
|
||||
const useMasterplanFilters = create<Store>((set) => ({
|
||||
apartmentTypeCheckboxes: initialAparmentTypeCheckboxes,
|
||||
viewCheckboxes: initialViewCheckboxes,
|
||||
roveHomeTypeCheckboxes: initialRoveHomeCheckboxes,
|
||||
multirangeSliders: initialSliders,
|
||||
switchers: initialSwitchers,
|
||||
sortList: initialSortList,
|
||||
page: 1,
|
||||
perPage: perPageInitial,
|
||||
setPerPage: (perPage) => set(() => ({ perPage: perPage })),
|
||||
setPage: (page) => set(() => ({ page: page })),
|
||||
setSortList: (sortList) => set(() => ({ sortList: sortList })),
|
||||
setApartmentTypeCheckboxes: (typeCheckboxes) =>
|
||||
set(() => ({ apartmentTypeCheckboxes: typeCheckboxes })),
|
||||
setViewCheckboxes: (typeCheckboxes) =>
|
||||
set(() => ({ viewCheckboxes: typeCheckboxes })),
|
||||
setRoveHomeTypeCheckboxes: (typeCheckboxes) =>
|
||||
set(() => ({ roveHomeTypeCheckboxes: typeCheckboxes })),
|
||||
setMultirangeSliders: (multirangeSliders) =>
|
||||
set(() => ({ multirangeSliders: multirangeSliders })),
|
||||
setSwitchers: (switchers) => set(() => ({ switchers: switchers })),
|
||||
}));
|
||||
|
||||
export default useMasterplanFilters;
|
||||
@@ -0,0 +1,8 @@
|
||||
interface IApartmentRoute {
|
||||
type: string;
|
||||
virtualTour: string;
|
||||
id: string;
|
||||
layout: string;
|
||||
}
|
||||
|
||||
export type { IApartmentRoute };
|
||||
@@ -24,6 +24,7 @@ app.use(json());
|
||||
app.use(morgan("combined", { stream: accessLogStream }));
|
||||
|
||||
app.use("/apartments", apartmentsRoute);
|
||||
app.use("/apartments/:id", apartmentsRoute);
|
||||
app.use("/updateAccessToken", updateAccessToken);
|
||||
|
||||
app.listen(port, () => {
|
||||
|
||||
@@ -276,6 +276,54 @@ router.get("/", async (req, res) => {
|
||||
console.log("error", error);
|
||||
}
|
||||
});
|
||||
// router.get("/:id", async (req, res) => {
|
||||
// const accessToken = req?.headers?.authorization;
|
||||
// const {id} = req.params;
|
||||
|
||||
// try {
|
||||
// const response = await fetch(`${aparmentsApi}&page=${page}&per_page=200`, {
|
||||
// headers: {
|
||||
// Authorization: accessToken,
|
||||
// },
|
||||
// });
|
||||
|
||||
// if (!accessToken)
|
||||
// return res
|
||||
// .status(401)
|
||||
// .json({ message: "Отсутсвует access token", code: 401 });
|
||||
|
||||
// try {
|
||||
|
||||
// res.status(200).json({
|
||||
// message: "ok",
|
||||
// apartments: slicedApartments,
|
||||
// code: 200,
|
||||
// });
|
||||
|
||||
// return;
|
||||
// } catch (error) {
|
||||
// if (
|
||||
// (error as Error).message === "invalid oauth token" ||
|
||||
// (error as Error).message === "INVALID_TOKEN"
|
||||
// ) {
|
||||
// console.log("error", error);
|
||||
// logger.error(error);
|
||||
|
||||
// return res
|
||||
// .status(401)
|
||||
// .json({ message: "Неправильный токен или токен устарел", code: 401 });
|
||||
// }
|
||||
|
||||
// console.log("error", error);
|
||||
// logger.error(error);
|
||||
|
||||
// return res.status(500).json({ message: "Server Error", code: 500 });
|
||||
// }
|
||||
// } catch (error) {
|
||||
// logger.error(error);
|
||||
// console.log("error", error);
|
||||
// }
|
||||
// });
|
||||
|
||||
const apartmentsRoute = router;
|
||||
|
||||
|
||||