filters server + filter client

This commit is contained in:
2024-06-19 18:26:17 +05:00
parent 960e502db2
commit 9630f42a08
44 changed files with 817 additions and 126 deletions
+1
View File
@@ -14,6 +14,7 @@
"@react-three/fiber": "^8.16.6",
"@types/react-router-dom": "^5.3.3",
"gsap": "^3.12.5",
"ky": "^1.3.0",
"react": "^18.2.0",
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

+17
View File
@@ -0,0 +1,17 @@
import { apartmentsApi } from "./urls";
import ky from "ky";
async function getApartments(token: string | null) {
// const token =
// "1000.04c54423a172cff4f2075ef60dc46403.b4a66d1663ed3b39fa86c9b28dbed2fa";
const res = await ky.get(apartmentsApi, {
headers: {
Authorization: `Zoho-oauthtoken ${token}`,
},
});
return res.json();
}
export { getApartments };
+19
View File
@@ -0,0 +1,19 @@
import ky from "ky";
import { updateAccessTokenApi } from "./urls";
import { IUpdateAccessToken } from "../types/updateAccessToken";
async function updateAccessToken() {
try {
const res = await ky.get(updateAccessTokenApi);
res.json().then((data) => {
const accessToken = (data as IUpdateAccessToken).accessToken;
localStorage.setItem("ACCESS_TOKEN", accessToken);
});
} catch (error) {
console.log("error", error);
}
}
export { updateAccessToken };
+7
View File
@@ -0,0 +1,7 @@
const serverApi = "http://192.168.56.1:3000";
const apartmentsApi = `${serverApi}/apartments`;
const updateAccessTokenApi = `${serverApi}/updateAccessToken`;
export { apartmentsApi, updateAccessTokenApi };
@@ -3,8 +3,6 @@ import { IDesctiptionFloor } from "../../../types/descriptionFloor";
import ApartmentDescription from "./ApartmentDescription";
import FloorWestWingHighlighting from "./FloorWestWingHighlighting";
import FloorWestWingLayout from "./FloorWestWingLayout";
import { useNavigate } from "react-router-dom";
import useWingSidebar from "../../../store/useWingSidebar";
import FloorEastWingHighlighting from "./FloorEastWingHighlighting";
import FloorEastWingLayout from "./FloorEastWingLayout";
import useModal from "../../../store/useModal";
@@ -23,11 +21,8 @@ const FloorSidebar = ({ currentFloor, onMouseEnter }: IFloorSidebarProps) => {
const [mousePos, setMousePos] = useState<number[]>([0, 0]);
const [isDescVisible, setIsDescVisible] = useState(false);
const [is3DTourAvailable] = useState(true);
const { setIsSidebar } = useWingSidebar();
const { setModal } = useModal();
const navigate = useNavigate();
function handleOnApartmentClick(
event: React.MouseEvent<SVGSVGElement, MouseEvent>
) {
@@ -37,7 +32,6 @@ const FloorSidebar = ({ currentFloor, onMouseEnter }: IFloorSidebarProps) => {
setModal(<AboutComplexModal apartment={apartment} />);
setIsDescVisible(false);
}
// console.log("e", apartments);
}
function handleMouseMove(e: MouseEvent) {
+37 -29
View File
@@ -1,23 +1,25 @@
import { useNavigate } from "react-router-dom";
import { ILayoutCard } from "../../types/layoutCard";
import { formatNumber } from "../../calc/formatNumber";
import Button from "../Button";
import HeartIcon from "../icons/Heart";
import { IAparmentRes } from "../../types/apartmentsRes";
interface LayoutCardProps {
layoutCard: ILayoutCard;
apartmentCard: IAparmentRes;
}
const LayoutCard = ({ layoutCard }: LayoutCardProps) => {
const LayoutCard = ({ apartmentCard }: LayoutCardProps) => {
const {
floorEnd,
floorStart,
apartmentType,
roveHome,
wing,
units,
square,
cost,
Project_Name: projectName,
Floor: floor,
id,
} = layoutCard;
Unit_No: unit,
Unit_Type: unitType,
Total_Area_Sqft: _totalArea,
} = apartmentCard;
const wing = unit.split("-")[0] === "E" ? "East" : "West";
const unitNumber = unit.split("-")[1];
const totalArea = `${_totalArea}`.split(".").join(",");
const navigate = useNavigate();
const handleOnClick = () => {
@@ -26,35 +28,41 @@ const LayoutCard = ({ layoutCard }: LayoutCardProps) => {
return (
<div
className="bg-white flex flex-col p-4 rounded-2xl gap-4 cursor-pointer select-none"
className="bg-white flex flex-col p-4 rounded-2xl cursor-pointer select-none"
onClick={handleOnClick}
>
<div className="flex gap-4 justify-between">
<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">{projectName}</p>
<div className="text-[#73787C] flex gap-2 items-center w-fit">
<p className="text-caption-m font-semibold leading-4">{wing}</p>
<p className="text-caption-m font-semibold leading-4">
{wing} 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 className="w-1 h-1 bg-[#E2E2DC] rounded-full"></div>
<p className="text-caption-m font-semibold leading-4">
{unitNumber}
</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
</div>
<Button
icon={<HeartIcon isFilled={false} />}
buttonType="favorite"
isCircleRounded
/>
</div>
<div className="w-full aspect-square rounded-lg">
<div className="w-full aspect-square rounded-lg pt-6">
<img src="/images/layout-1.png" alt="" className="h-full" />
</div>
<div className="flex flex-col">
<p className="text[#0D1922] text-s">
{apartmentType}, {square} Sqft
<div className="flex flex-col gap-1 pt-6">
<p className="text-[#0D1922] text-s">
{unitType}, {totalArea} Sqft
</p>
<p className="text-[#00BED7] text-m font-bold">
AED {formatNumber(cost, ",", 3, 1)}
<p className="text-[#00BED7] text-subheadline-s font-semibold">
Unavailable
</p>
</div>
</div>
@@ -1,19 +1,24 @@
import { useEffect, useState } from "react";
import LayoutCard from "./LayoutCard";
import SortButton from "./SortButton";
import useSearchFilters from "../../store/useSearchFilters";
import { initialSortList, layoutsCards } from "../../consts/initialSearchPage";
import { initialSortList } from "../../consts/initialSearchPage";
import SearchIcon from "../icons/SearchIcon";
import { sortCardBy } from "../../calc/sortCard";
import Button from "../Button";
import FilterIcon from "../icons/FilterIcon";
import useModal from "../../store/useModal";
import { MobileModalWrapper } from "../modals/mobile/MobileModalWrapper";
import SearchFiltersModal from "../modals/mobile/SearchFiltersModal";
import { getApartments } from "../../api/apartments";
import { IApartmentsRes, InfoRes } from "../../types/apartmentsRes";
import { updateAccessToken } from "../../api/updateAccessToken";
import useApartments from "../../store/useApartments";
import useSearchFilters from "../../store/useSearchFilters";
const LayoutOptions = () => {
const [sortList, setSortList] = useState(initialSortList);
const [cards, setCards] = useState(layoutsCards);
const { apartments, setApartments } = useApartments();
const [infoRes, setInfoRes] = useState<InfoRes>(null);
const { setModal } = useModal();
const { roveHomeTypeCheckboxes, apartmentTypeCheckboxes, multirangeSliders } =
useSearchFilters();
@@ -35,22 +40,49 @@ const LayoutOptions = () => {
};
useEffect(() => {
const sortedCards = sortCardBy(sortList, layoutsCards);
const zohoToken = localStorage.getItem("ACCESS_TOKEN");
setCards(sortedCards);
getApartments(zohoToken)
.then((data) => {
const apartmentsData = (data as IApartmentsRes).apartments;
console.log("apartmentsData", apartmentsData);
setApartments(apartmentsData);
})
.catch((error) => {
const errorStatus = error.response.status;
if (errorStatus === 401) {
updateAccessToken();
const zohoToken = localStorage.getItem("ACCESS_TOKEN");
getApartments(zohoToken).then((data) => {
const apartmentsData = (data as unknown as IApartmentsRes)
.apartments;
setApartments(apartmentsData);
});
}
});
}, [setApartments]);
useEffect(() => {
console.log("apartments", apartments);
}, [
sortList,
roveHomeTypeCheckboxes,
apartmentTypeCheckboxes,
multirangeSliders,
apartments,
]);
return (
<section className="w-full p-6 flex flex-col">
<div className="flex justify-between items-center border-b">
<div className="flex w-full justify-between pb-4 lg:flex-row lg:items-center flex-col">
<div className="flex gap-4 font-semibold text-subheadline-s leading-7 py-[6px]">
<h2 className="text-[#0D1922]">Units</h2>
<p className="text-[#73787C]">145</p>
<p className="text-[#73787C]">
{apartments ? apartments.length : 0}
</p>
</div>
<SortButton sortList={sortList} onClick={handleOnSortClick} />
</div>
@@ -64,9 +96,10 @@ const LayoutOptions = () => {
</div>
</div>
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 grid-cols-2 gap-4 pt-6">
{cards.map((layoutsCard) => (
<LayoutCard layoutCard={layoutsCard} />
))}
{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">
<SearchIcon /> Show 12 more apartments
+1 -1
View File
@@ -41,7 +41,7 @@ const initialApartmentTypeCheckboxes: ICheckbox[] = [
const initialRoveHomeCheckboxes: ICheckbox[] = [
{ title: "Downtown", id: "1", disabled: false, selected: false },
{ title: "Marasi Drive", id: "2", selected: false },
{ title: "Dubai Marina", id: "3", selected: false, disabled: true },
{ title: "Dubai Marina", id: "3", selected: false },
];
const initialSwitchers: ISwitcher[] = [
+455
View File
@@ -388,5 +388,460 @@
]
}
]
},
{
"id": "apartments-studio-4",
"map": "/images/virtual-tour/BDR-2-B_21/map.jpg",
"spheres": [
{
"id": "studio-3_sp-01",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_010000.jpg",
"roomType": "entrance 1",
"position": [-14.16, 0, 24.11],
"mapPosition": [120, 220],
"links": [
{
"id": "studio-3_sp-07",
"type": "default",
"labelPosition": [-12.16, 0, 20.11]
},
{
"id": "studio-3_sp-014",
"type": "default",
"labelPosition": [-30.16, 0, 230.11]
},
{
"id": "studio-3_sp-02",
"type": "default",
"labelPosition": [100.16, 0, 30.11]
}
]
},
{
"id": "studio-3_sp-014",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_030000.jpg",
"roomType": "bathroom 2",
"position": [-14.16, 0, 24.11],
"mapPosition": [188, 220],
"links": [
{
"id": "studio-3_sp-01",
"type": "default",
"labelPosition": [-13, 0, 1.11]
},
{
"id": "studio-3_sp-010",
"type": "default",
"labelPosition": [40, 0, 22.11]
}
]
},
{
"id": "studio-3_sp-010",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_040000.jpg",
"roomType": "bedroom 3",
"position": [-14.16, 0, 24.11],
"mapPosition": [188, 144],
"links": [
{
"id": "studio-3_sp-014",
"type": "default",
"labelPosition": [-45, 0, 20.11]
},
{
"id": "studio-3_sp-02",
"type": "default",
"labelPosition": [-15, 0, 3.11]
},
{
"id": "studio-3_sp-013",
"type": "default",
"labelPosition": [-5, 0, 23.31]
}
]
},
{
"id": "studio-3_sp-013",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_050000.jpg",
"roomType": "bedroom 4",
"position": [-14.16, 0, 24.11],
"mapPosition": [188, 80],
"links": [
{
"id": "studio-3_sp-010",
"type": "default",
"labelPosition": [-45, 0, 25.11]
},
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-12, 0, -50.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [120, 0, 20.11]
}
]
},
{
"id": "studio-3_sp-02",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_060000.jpg",
"roomType": "living room 5",
"position": [-14.16, 0, 24.11],
"mapPosition": [128, 155],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-0.16, 0, 21.11]
},
{
"id": "studio-3_sp-010",
"type": "default",
"labelPosition": [-14.16, 0, 40.11]
},
{
"id": "studio-3_sp-01",
"type": "default",
"labelPosition": [-45, 0, 24.11]
},
{
"id": "studio-3_sp-07",
"type": "default",
"labelPosition": [-80, 0, -40.11]
}
]
},
{
"id": "studio-3_sp-07",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_070000.jpg",
"roomType": "kitchen 6",
"position": [-14.16, 0, 24.11],
"mapPosition": [65, 205],
"links": [
{
"id": "studio-3_sp-01",
"type": "default",
"labelPosition": [-60, 0, 60.11]
},
{
"id": "studio-3_sp-02",
"type": "default",
"labelPosition": [0, 0, 35.11]
}
]
},
{
"id": "studio-3_sp-04",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_080000.jpg",
"roomType": "glass doors 7",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 60],
"links": [
{
"id": "studio-3_sp-021",
"type": "default",
"labelPosition": [90.16, 0, 30.11]
},
{
"id": "studio-3_sp-013",
"type": "default",
"labelPosition": [-15, 0, 120.11]
},
{
"id": "studio-3_sp-02",
"type": "default",
"labelPosition": [-45, 0, 40.11]
}
]
},
{
"id": "studio-3_sp-016",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_090000.jpg",
"roomType": "balcony 8",
"position": [-14.16, 0, 24.11],
"mapPosition": [155, 20],
"links": [
{
"id": "studio-3_sp-013",
"type": "default",
"labelPosition": [-45, 0, 25.11]
},
{
"id": "studio-3_sp-018",
"type": "default",
"labelPosition": [-10, 0, -60.11]
}
]
},
{
"id": "studio-3_sp-018",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_100000.jpg",
"roomType": "balcony 9",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-019",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_110000.jpg",
"roomType": "balcony 10",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-020",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_120000.jpg",
"roomType": "balcony 11",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-021",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_130000.jpg",
"roomType": "balcony 12",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-022",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_140000.jpg",
"roomType": "balcony 13",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-023",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_150000.jpg",
"roomType": "balcony 14",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-024",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_160000.jpg",
"roomType": "balcony 15",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-025",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_170000.jpg",
"roomType": "balcony 16",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-026",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_180000.jpg",
"roomType": "balcony 17",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-027",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_190000.jpg",
"roomType": "balcony 18",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-028",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_200000.jpg",
"roomType": "balcony 19",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-029",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_210000.jpg",
"roomType": "balcony 20",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-030",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_220000.jpg",
"roomType": "balcony 21",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
},
{
"id": "studio-3_sp-031",
"sphereImage": "/images/virtual-tour/BDR-2-B_21/BDR-2-B_21_230000.jpg",
"roomType": "balcony 22",
"position": [-14.16, 0, 24.11],
"mapPosition": [85, 20],
"links": [
{
"id": "studio-3_sp-04",
"type": "default",
"labelPosition": [-70, 0, 25.11]
},
{
"id": "studio-3_sp-016",
"type": "default",
"labelPosition": [-10, 0, 100.11]
}
]
}
]
}
]
+14
View File
@@ -0,0 +1,14 @@
import { create } from "zustand";
import { IAparmentRes } from "../types/apartmentsRes";
interface ApartmentsStore {
apartments: IAparmentRes[];
setApartments: (apartments: IAparmentRes[]) => void;
}
const useApartments = create<ApartmentsStore>((set) => ({
apartments: [],
setApartments: (apartments) => set(() => ({ apartments: apartments })),
}));
export default useApartments;
+34
View File
@@ -0,0 +1,34 @@
interface IAparmentRes {
Floor: number;
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;
}
type InfoRes = null | {
per_page: number;
next_page_token: string;
count: number;
sort_by: string;
page: number;
previous_page_token: string | null;
page_token_expiry: string;
sort_order: string;
more_records: boolean;
};
interface IApartmentsRes {
message: string;
apartments: IAparmentRes[];
}
export type { IApartmentsRes, IAparmentRes, InfoRes };
+6
View File
@@ -0,0 +1,6 @@
interface IError {
code: number;
message: string;
}
export type { IError };
+5
View File
@@ -0,0 +1,5 @@
interface IUpdateAccessToken {
accessToken: string;
}
export type { IUpdateAccessToken };
+5
View File
@@ -1787,6 +1787,11 @@ keyv@^4.5.3:
dependencies:
json-buffer "3.0.1"
ky@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/ky/-/ky-1.3.0.tgz#d6a02490cb50f5e6df4de4989665a76f30b02728"
integrity sha512-QUViPXlgP6NKA57IAPff/aZSmRA6qs9wKxlEpayBorwRZG+x2LG7jD4kXh8lnH3q/gkUr64NyZ7kwErUEZJmlw==
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz"
+7 -3
View File
@@ -4,15 +4,19 @@ const clientId = "1000.6ZV07WFOC7PQOY3X109UN55Q9BMBBY";
const clientSecret = "595f5262886a6e81475b533350a81e46fecda57fb5";
const grantType = "refresh_token";
const updateAccessTokenApi = `https://accounts.zoho.com/oauth/v2/token`;
const updateAccessTokenApi = `https://accounts.zoho.com/oauth/v2/token?refresh_token=${refreshToken}&client_id=${clientId}&client_secret=${clientSecret}&grant_type=${grantType}`;
const aparmentsApi =
"https://www.zohoapis.com/crm/v2/Apartments?fields=Floor,Property_Status,Project_Name,Balcony_Area_Sqft,Unit_Type,Suite_Area_Sqft,No_Of_Bedrooms,Total_Area_Sqft,No_of_Bathrooms,Property_Name,Unit_View,Balcony_Area_Sqft,Unit_No,Suite_Area_Sqft";
"https://www.zohoapis.com/crm/v6/Apartments?fields=Floor,Property_Status,Project_Name,Balcony_Area_Sqft,Unit_Type,Suite_Area_Sqft,No_Of_Bedrooms,Total_Area_Sqft,No_of_Bathrooms,Property_Name,Unit_View,Balcony_Area_Sqft,Unit_No,Suite_Area_Sqft";
const searchApartmentApi =
"https://www.zohoapis.com/crm/v6/Apartments/search?fields=Floor,Property_Status,Project_Name,Balcony_Area_Sqft,Unit_Type,Suite_Area_Sqft,No_Of_Bedrooms,Total_Area_Sqft,No_of_Bathrooms,Property_Name,Unit_View,Balcony_Area_Sqft,Unit_No,Suite_Area_Sqft";
export {
aparmentsApi,
updateAccessTokenApi,
aparmentsApi,
refreshToken,
clientId,
clientSecret,
grantType,
searchApartmentApi,
};
+5 -3
View File
@@ -5,8 +5,8 @@ import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import morgan from "morgan";
import apartmentsRoute from "./routes/aparments.js";
import accessTokenMiddleware from "./middlewares/accessTokenMiddleware.js";
import apartmentsRoute from "./routes/apartments.js";
import updateAccessToken from "./routes/zohoAccessToken.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -23,7 +23,9 @@ app.use(cors());
app.use(json());
app.use(morgan("combined", { stream: accessLogStream }));
app.use("/aparments", accessTokenMiddleware, apartmentsRoute);
app.use("/apartments", apartmentsRoute);
app.use("/updateAccessToken", updateAccessToken);
app.listen(port, () => {
console.log(`Server is listening on port ${port}`);
@@ -1,47 +0,0 @@
import { Request, Response, NextFunction } from "express";
import { aparmentsApi } from "../consts.js";
import { logger } from "../utils/logger.js";
var checkAccessToken = async function (
req: Request,
res: Response,
next: NextFunction
) {
const accessToken = req.headers.authorization;
if (!accessToken)
return res.status(401).json({ error: "Not found access token" });
try {
const respose = await fetch(aparmentsApi, {
headers: {
Authorization: accessToken,
},
});
if (respose.status === 401) {
try {
const response2 = await fetch(
`https://accounts.zoho.com/oauth/v2/token?refresh_token=1000.da3146d49fa8a399f0c635e74954ff9c.e010dbb1bb605d7e1aa5bf7fc0521f8b&client_id=1000.6ZV07WFOC7PQOY3X109UN55Q9BMBBY&client_secret=595f5262886a6e81475b533350a81e46fecda57fb5&grant_type=refresh_token`,
{
method: "post",
}
);
const { access_token } = await response2.json();
return res.json({ accessToken: access_token });
} catch (error) {
console.log("error", (error as Error).message);
logger.error(error);
return res.json({ error: (error as Error).message });
}
}
next();
} catch (error) {
console.log("error", (error as Error).message);
logger.error(error);
return res.json({ error: (error as Error).message });
}
};
export default checkAccessToken;
-26
View File
@@ -1,26 +0,0 @@
import { Router } from "express";
import { aparmentsApi } from "../consts.js";
const router = Router();
router.get("/", async (req, res) => {
const accessToken = req.headers.authorization;
if (!accessToken) return;
console.log("accessToken", `[${accessToken}]`);
const apartments = await fetch(aparmentsApi, {
headers: {
Authorization: accessToken,
},
});
const result = await apartments.json();
res.json({ ok: 1, apartments: result });
});
const apartmentsRoute = router;
export default apartmentsRoute;
+139
View File
@@ -0,0 +1,139 @@
import { Router } from "express";
import { aparmentsApi, searchApartmentApi } from "../consts.js";
import { logger } from "../utils/logger.js";
const router = Router();
async function getAllApartments(
page: number,
accessToken: string,
apartments: any[]
) {
const response = await fetch(`${aparmentsApi}&page=${page}&per_page=200`, {
headers: {
Authorization: accessToken,
},
});
const result = await response.json();
if (result?.code && result?.code === "INVALID_TOKEN") {
throw new Error("INVALID_TOKEN");
}
const updatedApartment = apartments.concat([...result.data]);
const isMoreRecords = result.info.more_records;
if (!isMoreRecords) {
return updatedApartment;
}
return await getAllApartments(page + 1, accessToken, updatedApartment);
}
router.get("/", async (req, res) => {
const accessToken = req?.headers?.authorization;
const {
per_page: perPage = 20,
page = 10,
rove_home = "",
apartment_type = "",
cost_between = "",
total_area_between = "",
floor_between = "",
views = "",
} = req.query;
let api = aparmentsApi;
if (!accessToken)
return res
.status(401)
.json({ message: "Отсутсвует access token", code: 401 });
try {
const allApartments = await getAllApartments(1, accessToken, []);
const filteredApartments = allApartments.slice(
page as number,
perPage as number
);
res.status(200).json({
message: "ok",
apartments: filteredApartments,
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 });
}
});
// router.get("/", async (req, res) => {
// const accessToken = req.headers.authorization;
// const { perPage, criteria } = req.query;
// let api = perPage || criteria ? searchApartmentApi : aparmentsApi;
// if (criteria) {
// api = `${api}&criteria=${criteria}`;
// }
// console.log("api", api);
// if (!accessToken)
// return res
// .status(401)
// .json({ message: "Отсутсвует access token", code: 401 });
// try {
// const apartments = await fetch(api, {
// headers: {
// Authorization: accessToken,
// },
// });
// const result = await apartments.json();
// if (result?.code && result?.code === "INVALID_TOKEN") {
// return res.status(401).json({ code: 401, message: "INVALID_TOKEN" });
// }
// res.status(200).json({ message: "ok", apartments: result, code: 200 });
// return;
// } catch (error) {
// if ((error as Error).message === "invalid oauth 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 });
// }
// });
const apartmentsRoute = router;
export default apartmentsRoute;
+21
View File
@@ -0,0 +1,21 @@
import { Request, Response, NextFunction } from "express";
import { aparmentsApi, updateAccessTokenApi } from "../consts.js";
import { logger } from "../utils/logger.js";
var updateAccessToken = async function (req: Request, res: Response) {
try {
const response = await fetch(updateAccessTokenApi, {
method: "post",
});
const { access_token } = await response.json();
return res.json({ accessToken: access_token });
} catch (error) {
console.log("error", (error as Error).message);
logger.error(error);
return res.json({ error: (error as Error).message });
}
};
export default updateAccessToken;