diff --git a/public/img/surroundings/location.png b/public/img/surroundings/location.png
new file mode 100644
index 0000000..49e928f
Binary files /dev/null and b/public/img/surroundings/location.png differ
diff --git a/public/img/surroundings/map.png b/public/img/surroundings/map.png
new file mode 100644
index 0000000..525b74d
Binary files /dev/null and b/public/img/surroundings/map.png differ
diff --git a/src/components/layouts/LayoutWithFooter.tsx b/src/components/layouts/LayoutWithFooter.tsx
new file mode 100644
index 0000000..0ea8534
--- /dev/null
+++ b/src/components/layouts/LayoutWithFooter.tsx
@@ -0,0 +1,22 @@
+import { Outlet, useLocation } from "react-router";
+import NavMenu from "./NavMenu";
+import { useEffect } from "react";
+
+function LayoutWithFooter() {
+ const location = useLocation();
+
+ useEffect(() => {
+ window.scrollTo(0, 0);
+ }, [location.pathname]);
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default LayoutWithFooter;
diff --git a/src/components/pages/SurroundingsPage.tsx b/src/components/pages/SurroundingsPage.tsx
new file mode 100644
index 0000000..ee6209f
--- /dev/null
+++ b/src/components/pages/SurroundingsPage.tsx
@@ -0,0 +1,679 @@
+/* eslint-disable react-hooks/exhaustive-deps */
+import clsx from "clsx";
+import { useEffect, useRef, useState } from "react";
+import {
+ surroundingPoints,
+ categories,
+ CENTER_POINT,
+ type ISurroundingPoint,
+} from "../../consts/surroundingPoints";
+import { usePopupStore } from "../../stores/usePopupStore";
+import LocationPopup from "../popups/LocationPopup";
+import CategoryFilter from "../ui/CategoryFilter";
+
+interface Position {
+ x: number;
+ y: number;
+}
+
+interface Size {
+ width: number;
+ height: number;
+}
+
+interface MapProps {
+ maxZoom?: number;
+}
+
+const getEventPosition = (
+ e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent
+): Position => {
+ if ("touches" in e)
+ return {
+ x: e.touches[0].clientX,
+ y: e.touches[0].clientY,
+ };
+ return {
+ x: e.clientX,
+ y: e.clientY,
+ };
+};
+
+const constrainPosition = (
+ position: Position,
+ containerSize: Size,
+ imageSize: Size,
+ zoom: number
+): Position => {
+ const scaledWidth = imageSize.width * zoom;
+ const scaledHeight = imageSize.height * zoom;
+ const minX = containerSize.width - scaledWidth;
+ const minY = containerSize.height - scaledHeight;
+
+ return {
+ x: Math.min(0, Math.max(minX, position.x)),
+ y: Math.min(0, Math.max(minY, position.y)),
+ };
+};
+
+const calculateMinZoom = (containerSize: Size, imageSize: Size): number => {
+ if (imageSize.width === 0 || imageSize.height === 0) return 0.1;
+
+ const widthRatio = containerSize.width / imageSize.width;
+ const heightRatio = containerSize.height / imageSize.height;
+
+ return Math.max(widthRatio, heightRatio);
+};
+
+function SurroundingsPage({ maxZoom = 1 }: MapProps) {
+ const [originalSize, setOriginalSize] = useState({
+ width: 0,
+ height: 0,
+ });
+
+ const mapRef = useRef(null);
+ const containerRef = useRef(null);
+
+ const containerSizeRef = useRef({ width: 0, height: 0 });
+
+ const [isDragging, setIsDragging] = useState(false);
+ const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });
+ const [position, setPosition] = useState({ x: 0, y: 0 });
+ const [zoom, setZoom] = useState(0);
+ const previousTouchDistance = useRef(null);
+ const initialTouchDistance = useRef(null);
+ const minZoomRef = useRef(1);
+ const markersContainerRef = useRef(null);
+ const [hoveredPoint, setHoveredPoint] = useState(
+ null
+ );
+ const [selectedPoint, setSelectedPoint] = useState(
+ null
+ );
+ const [isImageLoaded, setIsImageLoaded] = useState(false);
+ const [lastClickTime, setLastClickTime] = useState(0);
+ const [selectedCategories, setSelectedCategories] = useState>(
+ new Set(Array.from(categories.keys()))
+ );
+ const { setPopup, setPosition: setPopupPosition, setSide } = usePopupStore();
+
+ useEffect(() => {
+ if (!containerRef.current || !isImageLoaded || originalSize.width === 0)
+ return;
+
+ const containerRect = containerRef.current.getBoundingClientRect();
+ const scaledWidth = originalSize.width * zoom;
+ const scaledHeight = originalSize.height * zoom;
+
+ const maxOffsetX = Math.max(0, scaledWidth - containerRect.width);
+ const maxOffsetY = Math.max(0, scaledHeight - containerRect.height);
+
+ const desiredOffsetX = containerRect.width * 0.35;
+ const desiredOffsetY = containerRect.height * 0.58;
+
+ const boundedOffsetX = Math.min(desiredOffsetX, maxOffsetX);
+ const boundedOffsetY = Math.min(desiredOffsetY, maxOffsetY);
+
+ setPosition({
+ x: -boundedOffsetX,
+ y: -boundedOffsetY,
+ });
+ }, [isImageLoaded]);
+
+ useEffect(() => {
+ const updateContainerSize = () => {
+ if (!containerRef.current) return;
+
+ const { width, height } = containerRef.current.getBoundingClientRect();
+
+ if (
+ width === containerSizeRef.current.width &&
+ height === containerSizeRef.current.height
+ )
+ return;
+
+ containerSizeRef.current = { width, height };
+
+ const newMinZoom = calculateMinZoom({ width, height }, originalSize);
+ minZoomRef.current = newMinZoom;
+
+ setZoom(newMinZoom);
+
+ const scaledWidth = originalSize.width * newMinZoom;
+ const scaledHeight = originalSize.height * newMinZoom;
+
+ const maxOffsetX = Math.max(0, scaledWidth - width);
+ const maxOffsetY = Math.max(0, scaledHeight - height);
+
+ // const desiredOffsetX = width * 0.35;
+ // const desiredOffsetY = height * 0.58;
+
+ const boundedOffsetX = Math.min(maxOffsetX);
+ const boundedOffsetY = Math.min(maxOffsetY);
+
+ setPosition({
+ x: -boundedOffsetX,
+ y: -boundedOffsetY,
+ });
+ };
+
+ updateContainerSize();
+
+ addEventListener("resize", updateContainerSize);
+
+ return () => {
+ removeEventListener("resize", updateContainerSize);
+ };
+ }, [originalSize, zoom]);
+
+ function handleLoad() {
+ if (!mapRef.current || !containerRef.current) return;
+
+ const img = new Image();
+ img.src = mapRef.current.src;
+
+ img.onload = () => {
+ const newOriginalSize = {
+ width: img.naturalWidth || img.width,
+ height: img.naturalHeight || img.height,
+ };
+
+ setOriginalSize(newOriginalSize);
+
+ const containerRect = containerRef.current!.getBoundingClientRect();
+ const minZoom = calculateMinZoom(
+ {
+ width: containerRect.width,
+ height: containerRect.height,
+ },
+ newOriginalSize
+ );
+
+ minZoomRef.current = minZoom;
+ setZoom(minZoom);
+ setIsImageLoaded(true);
+ };
+ }
+
+ const getContainerSize = (): Size => containerSizeRef.current;
+
+ const updatePosition = (newPosition: Position, newZoom: number = zoom) => {
+ if (!containerRef.current) return;
+
+ const containerSize = getContainerSize();
+ const constrainedPosition = constrainPosition(
+ newPosition,
+ containerSize,
+ originalSize,
+ newZoom
+ );
+
+ setPosition(constrainedPosition);
+ };
+
+ const zoomToPoint = (point: Position, targetZoom: number) => {
+ if (!containerRef.current) return;
+
+ const boundedZoom = Math.min(
+ maxZoom,
+ Math.max(minZoomRef.current, targetZoom)
+ );
+
+ const containerRect = containerRef.current.getBoundingClientRect();
+ const mouseX = point.x - containerRect.left;
+ const mouseY = point.y - containerRect.top;
+
+ const scale = boundedZoom / zoom;
+ const dx = mouseX - position.x;
+ const dy = mouseY - position.y;
+ const newPosition = {
+ x: mouseX - dx * scale,
+ y: mouseY - dy * scale,
+ };
+
+ setZoom(boundedZoom);
+ updatePosition(newPosition, boundedZoom);
+ };
+
+ const handleTouchStart = (e: React.TouchEvent) => {
+ if (e.touches.length === 2) {
+ const touch1 = e.touches[0];
+ const touch2 = e.touches[1];
+ const distance = Math.hypot(
+ touch1.clientX - touch2.clientX,
+ touch1.clientY - touch2.clientY
+ );
+ initialTouchDistance.current = distance;
+ previousTouchDistance.current = distance;
+ return;
+ }
+
+ if (e.touches.length === 1) handleStart(e);
+ };
+
+ const handleTouchMove = (e: React.TouchEvent) => {
+ if (!containerRef.current) return;
+
+ if (e.touches.length === 2) {
+ e.preventDefault();
+ const touch1 = e.touches[0];
+ const touch2 = e.touches[1];
+ const distance = Math.hypot(
+ touch1.clientX - touch2.clientX,
+ touch1.clientY - touch2.clientY
+ );
+
+ if (initialTouchDistance.current === null) {
+ initialTouchDistance.current = distance;
+ previousTouchDistance.current = distance;
+ return;
+ }
+
+ if (previousTouchDistance.current === null) {
+ previousTouchDistance.current = distance;
+ return;
+ }
+
+ const distanceChange = Math.abs(distance - previousTouchDistance.current);
+ const changePercentage =
+ (distanceChange / previousTouchDistance.current) * 100;
+
+ if (changePercentage >= 5) {
+ const zoomFactor = distance > previousTouchDistance.current ? 1.1 : 0.9;
+ const centerX = (touch1.clientX + touch2.clientX) / 2;
+ const centerY = (touch1.clientY + touch2.clientY) / 2;
+
+ const newZoom = Math.min(
+ maxZoom,
+ Math.max(minZoomRef.current, zoom * zoomFactor)
+ );
+
+ if (Math.abs(newZoom - zoom) > 0.001) {
+ setZoom(newZoom);
+
+ const containerRect = containerRef.current.getBoundingClientRect();
+ const mouseX = centerX - containerRect.left;
+ const mouseY = centerY - containerRect.top;
+
+ const scale = newZoom / zoom;
+ const dx = mouseX - position.x;
+ const dy = mouseY - position.y;
+ const newPosition = {
+ x: mouseX - dx * scale,
+ y: mouseY - dy * scale,
+ };
+
+ updatePosition(newPosition, newZoom);
+ }
+
+ previousTouchDistance.current = distance;
+ }
+ return;
+ }
+
+ if (isDragging && e.touches.length === 1) {
+ const { x, y } = getEventPosition(e);
+ const newPosition = {
+ x: x - startPosition.x,
+ y: y - startPosition.y,
+ };
+
+ updatePosition(newPosition);
+ }
+ };
+
+ const handleEnd = () => setIsDragging(false);
+
+ const handleTouchEnd = () => {
+ setIsDragging(false);
+ previousTouchDistance.current = null;
+ initialTouchDistance.current = null;
+ };
+
+ const handleWheel = (e: WheelEvent) => {
+ e.preventDefault();
+
+ if (!containerRef.current || !mapRef.current) return;
+
+ const containerRect = containerRef.current.getBoundingClientRect();
+ const mouseX = e.clientX - containerRect.left;
+ const mouseY = e.clientY - containerRect.top;
+
+ const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
+ const newZoom = Math.min(
+ maxZoom,
+ Math.max(minZoomRef.current, zoom * zoomFactor)
+ );
+
+ if (Math.abs(newZoom - zoom) < 0.01) return;
+
+ const scale = newZoom / zoom;
+ const dx = mouseX - position.x;
+ const dy = mouseY - position.y;
+ const newPosition = {
+ x: mouseX - dx * scale,
+ y: mouseY - dy * scale,
+ };
+
+ if (isDragging) {
+ const eventPosition = getEventPosition(e);
+ setStartPosition({
+ x: eventPosition.x - newPosition.x,
+ y: eventPosition.y - newPosition.y,
+ });
+ }
+
+ setZoom(newZoom);
+ updatePosition(newPosition, newZoom);
+ };
+
+ const handleMouseMove = (
+ e: React.MouseEvent | React.TouchEvent
+ ) => {
+ if (!isDragging || !containerRef.current) return;
+
+ const { x, y } = getEventPosition(e);
+ const newPosition = {
+ x: x - startPosition.x,
+ y: y - startPosition.y,
+ };
+
+ updatePosition(newPosition);
+ };
+
+ const handleStart = (
+ e: React.MouseEvent | React.TouchEvent
+ ) => {
+ if (!mapRef.current) return;
+
+ setIsDragging(true);
+ const { x, y } = getEventPosition(e);
+ setStartPosition({
+ x: x - position.x,
+ y: y - position.y,
+ });
+ };
+
+ function smoothZoomTo(targetZoom: number, point: Position) {
+ const duration = 400;
+ const startZoom = zoom;
+ const startTime = performance.now();
+
+ function animateZoom(now: number) {
+ const elapsed = now - startTime;
+ const t = Math.min(elapsed / duration, 1);
+ const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
+ const currentZoom = startZoom + (targetZoom - startZoom) * ease;
+
+ zoomToPoint(point, currentZoom);
+
+ if (t < 1) {
+ requestAnimationFrame(animateZoom);
+ } else {
+ setZoom(targetZoom);
+ zoomToPoint(point, targetZoom);
+ }
+ }
+
+ requestAnimationFrame(animateZoom);
+ }
+
+ const handleClick = (
+ e: React.MouseEvent | React.TouchEvent
+ ) => {
+ const currentTime = Date.now();
+
+ if (
+ (e.target as HTMLElement).closest("button") ||
+ (e.target as HTMLElement).closest("#selected-complex-card")
+ )
+ return;
+
+ if (currentTime - lastClickTime < 200) {
+ const targetZoom =
+ Math.abs(zoom - maxZoom) < 0.01 ? minZoomRef.current : maxZoom;
+ const point = getEventPosition(e);
+
+ smoothZoomTo(targetZoom, point);
+
+ setLastClickTime(0);
+ } else {
+ setLastClickTime(currentTime);
+ }
+ };
+
+ useEffect(() => {
+ containerRef.current?.addEventListener("wheel", handleWheel, {
+ passive: false,
+ });
+
+ return () =>
+ containerRef.current?.removeEventListener("wheel", handleWheel);
+ }, [isDragging, position]);
+
+ const handlePointMouseEnter = (
+ _e: React.MouseEvent,
+ point: ISurroundingPoint
+ ) => {
+ if (!containerRef.current) return;
+
+ const containerRect = containerRef.current.getBoundingClientRect();
+ const pointX = position.x + point.coordinates.x * zoom;
+ const pointY = position.y + point.coordinates.y * zoom;
+
+ const screenX = containerRect.left + pointX;
+ const screenY = containerRect.top + pointY;
+
+ setHoveredPoint(point);
+ setPopupPosition({ x: screenX, y: screenY });
+ setPopup(
+
+ );
+
+ // Определяем сторону для попапа
+ const windowWidth = window.innerWidth;
+
+ if (screenX < windowWidth / 2) {
+ setSide("right");
+ } else {
+ setSide("left");
+ }
+ };
+
+ const handlePointMouseLeave = () => {
+ setHoveredPoint(null);
+ setPopup(null);
+ };
+
+ const handleToggleCategory = (category: string) => {
+ const newSelected = new Set(selectedCategories);
+ if (newSelected.has(category)) {
+ newSelected.delete(category);
+ } else {
+ newSelected.add(category);
+ }
+ setSelectedCategories(newSelected);
+ };
+
+ const filteredPoints = surroundingPoints.filter((point) =>
+ selectedCategories.has(point.category)
+ );
+
+ const handlePointClick = (
+ e: React.MouseEvent,
+ point: ISurroundingPoint
+ ) => {
+ e.stopPropagation();
+ // Если кликнули по уже выбранной точке - снимаем выделение
+ if (selectedPoint?.title === point.title) {
+ setSelectedPoint(null);
+ } else {
+ setSelectedPoint(point);
+ }
+ };
+
+ const imageStyle = {
+ transform: `translateX(${position.x}px) translateY(${position.y}px) translateZ(0px) scale(${zoom})`,
+ transformOrigin: "0 0",
+ ...originalSize,
+ };
+
+ return (
+ //
+ // {/* Переключатель категорий */}
+ //
+ //
+ //
+ //
+ // Baraha Town
+ //
+ //
+ // Select location on the map
+ //
+ //
+ //
+ //
+ //
+ //
+
+
{
+ handleEnd();
+ handlePointMouseLeave();
+ }}
+ onMouseMove={handleMouseMove}
+ onClick={handleClick}
+ >
+ {containerRef.current?.clientWidth && (
+

+ )}
+
+ {/* SVG для путей из Figma */}
+
+
+ {/* Контейнер для точек */}
+
+
+ {/* Центральная точка (Baraha Town) */}
+
+
+
![]()
{
+ if (!el) return;
+ el.style.minWidth = `${el.naturalWidth}px`;
+ }}
+ src="/img/surroundings/location.png"
+ className="select-none"
+ draggable={false}
+ />
+
+
+ {/* Точки на карте */}
+ {filteredPoints.map((point) => {
+ const color = categories.get(point.category) || "#F47F52";
+
+ return (
+
handlePointMouseEnter(e, point)}
+ onMouseLeave={handlePointMouseLeave}
+ onClick={(e) => handlePointClick(e, point)}
+ >
+
+
+ );
+ })}
+
+
+
+ //
+ );
+}
+
+export default SurroundingsPage;
diff --git a/src/components/popups/LocationPopup.tsx b/src/components/popups/LocationPopup.tsx
new file mode 100644
index 0000000..7edc90b
--- /dev/null
+++ b/src/components/popups/LocationPopup.tsx
@@ -0,0 +1,24 @@
+import CarIcon from "../icons/CarIcon";
+
+interface LocationPopupProps {
+ title: string;
+ travelTime: number;
+}
+
+function LocationPopup({ title, travelTime }: LocationPopupProps) {
+ return (
+
+
+ {title}
+
+
+
+
+
+
{travelTime} min
+
+
+ );
+}
+
+export default LocationPopup;
diff --git a/src/components/ui/CategoryFilter.tsx b/src/components/ui/CategoryFilter.tsx
new file mode 100644
index 0000000..ec64c67
--- /dev/null
+++ b/src/components/ui/CategoryFilter.tsx
@@ -0,0 +1,50 @@
+import clsx from "clsx";
+import { categories } from "../../consts/surroundingPoints";
+
+interface CategoryFilterProps {
+ selectedCategories: Set;
+ onToggleCategory: (category: string) => void;
+}
+
+function CategoryFilter({
+ selectedCategories,
+ onToggleCategory,
+}: CategoryFilterProps) {
+ return (
+
+ {Array.from(categories.entries()).map(([category, color]) => {
+ const isSelected = selectedCategories.has(category);
+ return (
+
+ );
+ })}
+
+ );
+}
+
+export default CategoryFilter;
diff --git a/src/consts/surroundingPoints.ts b/src/consts/surroundingPoints.ts
new file mode 100644
index 0000000..cff5d74
--- /dev/null
+++ b/src/consts/surroundingPoints.ts
@@ -0,0 +1,178 @@
+export const categories = new Map([
+ ["Airports", "#93D7EC"],
+ ["Health care", "#7794CC"],
+ ["Souqs", "#F0D925"],
+ ["Leisure", "#E86748"],
+ ["Education", "#8F3B44"],
+ ["Parks", "#799E97"],
+ ["Rest", "#EF88B7"],
+ ["Other", "#B352F4"],
+]);
+
+export interface ISurroundingPoint {
+ category: string;
+ title: string;
+ coordinates: {
+ x: number;
+ y: number;
+ };
+ travelTime: number;
+ path: string | [string, string]; // svg path маршрута или маршрут с пунктиром
+}
+
+// Центральная точка (Baraha Town) - координаты центра эллипса из Figma
+// Ellipse 3010: x: 1864, y: 1145, размер: 10x10, центр: x: 1869, y: 1150
+export const CENTER_POINT = { x: 1853, y: 1135 };
+
+// Координаты точек - центры эллипсов (Ellipse 3008, размер 12x12) из Figma
+export const surroundingPoints: ISurroundingPoint[] = [
+ {
+ title: "Palace Gardens 1",
+ category: "Other",
+ coordinates: { x: 1899.5, y: 1112 },
+ travelTime: 3,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.8 15.69 24.3 46.21 35.77 58.22.14.15.25.32.37.48.45.63 1.44 1.29 2.84.24 2-1.5 1.5-3.5 0-5s-28-37-28.5-45 2.5-7 7.5-11c3.56-2.85 11.34-10.13 15.95-14.52.84-.8.81-2.14-.05-2.92l-8.4-7.56",
+ },
+ {
+ title: "The English Kindergarten",
+ category: "Education",
+ coordinates: { x: 1929, y: 1121 },
+ travelTime: 4,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.39-.34 2.92.76 7.58 15.4 21.4 42.34 32.84 54.28.1.1.19.21.26.33 1.39 2.2 4.22 5.88 5.39 4.32 1.5-2 2.5-1.5-1-5.5-3.42-3.91-20.2-27.86-28.43-46.22-.35-.77-.16-1.67.46-2.24l35.61-33.27c.77-.72 1.96-.72 2.72-.01l4.74 4.37c.26.24.45.55.56.89l1.34 4.48",
+ },
+ {
+ title: "Aspire Park",
+ category: "Parks",
+ coordinates: { x: 1284, y: 985 },
+ travelTime: 17,
+ path: [
+ "m1869.5 1149.5 7.59-5.19c1.01-.69 2.39-.34 2.92.76 7.58 15.4 21.4 42.34 32.84 54.28.1.1.19.21.26.33 1.39 2.2 4.22 5.87 5.39 4.32 1.5-2 2.5-1.5-1-5.5s-22.5-30.5-30.5-49-22.5-34-32.5-51.5-51.5-83-53.5-87.5c-2.33-5.67-9.2-19.601-18-30.001-11-13-28-28.5-45-55.5-13.53-21.487-27.52-52.342-32.92-65.297-.05-.14-.12-.266-.22-.379-1.03-1.141-4.4-2.486-10.86.176-8.5 3.5-202.5 91.001-213.5 95.501s-134 57.5-145 61-62 26-64.5 26.5-8 1.5-8-6v-95.519c0-1.104.9-1.981 2-1.981h13",
+ "M1278 939c5 9 12 23.502 12 52.501",
+ ],
+ },
+ {
+ title: "Khalifa International Stadium",
+ category: "Leisure",
+ coordinates: { x: 1424, y: 919 },
+ travelTime: 10,
+ path: [
+ "m1869.5 1149.5 7.59-5.19c1.01-.69 2.39-.34 2.92.76 7.58 15.4 21.4 42.34 32.84 54.28.1.1.19.21.26.33 1.39 2.2 4.22 5.88 5.39 4.32 1.5-2 2.5-1.5-1-5.5s-22.5-30.5-30.5-49-22.5-34-32.5-51.5-51.5-83-53.5-87.5c-4.5-7.33-14.3-23.1-17.5-27.5-4-5.5-21-24.5-30-36.5-7.2-9.6-31-53-42-73.5l-7.64-14.833c-.48-.937-1.58-1.354-2.54-.932C1648.4 880.332 1543.08 926.668 1532 933c-14 8-116.5 49-119 50.5-2 1.2-6.83-.167-9-1",
+ "M1404 983c-1.83-12 .6-40.2 25-57",
+ ],
+ },
+ {
+ title: "Al Khebra Driving Academy",
+ category: "Education",
+ coordinates: { x: 2002, y: 1199 },
+ travelTime: 5,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.39-.34 2.92.76 7.58 15.4 21.4 42.34 32.84 54.28.1.1.19.21.26.33 1.39 2.2 4.22 5.88 5.39 4.32 1.48-1.98 2.48-1.51-.88-5.37-.08-.08-.15-.18-.21-.28l-7.57-12.47a2 2 0 0 1 .32-2.49l29.74-28.34a1.99 1.99 0 0 1 2.41-.26l67.73 40.73a2 2 0 0 1 .22 3.27l-1.76 1.41c-.61.49-1.45.58-2.14.23l-1.86-.93",
+ },
+ {
+ title: "Al Thumama Stadium",
+ category: "Leisure",
+ coordinates: { x: 2248, y: 1229 },
+ travelTime: 8,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 30.66 37.37 30.57 40.31 40.18 50.17.74.75 1.93.8 2.72.11 14.28-12.43 41.12-36.48 44.6-42.28 4.5-7.5 15.5-24 23-30.5s34.5-31 37-33 16-15.5 24-14 20 12 28 11.5 8.5.5 12-2 5-6 11.5-9.5c4.78-2.57 20.07-5.63 28.07-6.75 1.08-.15 2.05.61 2.21 1.68L2253 1235",
+ },
+ {
+ title: "Wakrah Park Beach",
+ category: "Rest",
+ coordinates: { x: 3087, y: 1786 },
+ travelTime: 17,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5c12.59-1.94 69.29-11.38 89.64-20.15 1.03-.45 2.23-.06 2.7.96 33.14 71.82 100.24 216.75 110.16 236.19 9.9 19.41 66.05 133.24 93.69 189.36.47.94 1.57 1.33 2.54.92l31.54-13.06c.98-.41 2.09 0 2.55.94 11.93 24.02 34.62 69.71 36.18 72.84 1.45 2.9 3.55 12.29 4.68 17.88.19.94 1.01 1.62 1.97 1.62H2996c6 0 75 .5 76.5 1.5s1.5 3.5 4 4 8 1.5 9 0 4-4 6.5-4",
+ },
+ {
+ title: "Hamad Intranational Airport",
+ category: "Airports",
+ coordinates: { x: 2892, y: 1150 },
+ travelTime: 13,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5c23.5-3.83 74.8-13.7 92-22.5s140.17-86.33 199.5-124",
+ },
+ {
+ title: "Doha Intranational Airport",
+ category: "Airports",
+ coordinates: { x: 2503, y: 917 },
+ travelTime: 13,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5c22.75-3.71 71.55-13.08 90.25-21.65.98-.45 1.4-1.59.93-2.57-33.47-69.78-101.06-210.53-110.18-228.78-11.5-23-36-74-40.5-80.5-3.49-5.038-26.97-28.319-39.34-40.374a1.994 1.994 0 0 1-.3-2.495l1.64-2.631",
+ },
+ {
+ title: "974 Stadium",
+ category: "Leisure",
+ coordinates: { x: 2592, y: 652 },
+ travelTime: 15,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5 73-12 91.5-21 71-42.5 81-49.5 63-40.5 67.5-42.5 11-9 16-8 6.5 4 6.5 6 0 6.5-4.5 8.5-9-2-10-4-7.5-17-9-20.5-52-148-63-173.5-55.5-138-67-160.5-33.5-59.5-57-85c-23.36-25.346-54.13-46.741-61.37-49.944a2 2 0 0 1-.26-.139c-1.34-.894-3.42-3.093-1.87-5.417 1.28-1.914 4.12-5.652 6.15-8.27a2.01 2.01 0 0 0-.49-2.92l-11.89-7.67a2 2 0 0 1-.54-2.848L2598 661",
+ },
+ {
+ title: "Mina District",
+ category: "Other",
+ coordinates: { x: 2453, y: 497 },
+ travelTime: 20,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5 73-12 91.5-21 71-42.5 81-49.5 63-40.5 67.5-42.5 11-9 16-8 6.5 4 6.5 6 0 6.5-4.5 8.5-9-2-10-4-7.5-17-9-20.5-52-148-63-173.5-55.5-138-67-160.5-33.5-59.5-57-85-54.5-47-61.5-50-14.5-9.5-31.5-8-30 9.001-68 8-37.5 1-40-12-3-23-9.5-34.5-19.5-31-37.5-38.5c-3.64-1.656-13.86-4.56-25.75-3.033-.17.021-.33.067-.49.113-1.72.502-4.76-.559-4.76-8.58 0-7.553.27-15.134.45-18.888a1.97 1.97 0 0 1 1.03-1.647l88.99-48.63c.88-.479 1.27-1.53.91-2.463L2458.5 503",
+ },
+ {
+ title: "Souq Waqif",
+ category: "Souqs",
+ coordinates: { x: 2252, y: 648 },
+ travelTime: 17,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.39-.34 2.92.76 7.58 15.4 21.4 42.34 32.84 54.28.1.1.19.21.26.33 1.39 2.2 4.22 5.88 5.39 4.32 1.5-2 2.5-1.5-1-5.5s-22.5-30.5-30.5-49-22.5-34-32.5-51.5-51.5-83-53.5-87.5-6.5-8.5 0-14S2028 845 2035.5 840s38-29 44-35.5 35-34.5 47.5-43c11.47-7.802 51.17-33.295 58.77-38.204.75-.481 1.05-1.376.8-2.225-2.3-7.717-6.44-23.696-7.57-36.071-1.08-11.908-.62-25.255-.15-32.036.07-1.091 1.03-1.901 2.12-1.822l60.53 4.358h16",
+ },
+ {
+ title: "Al Bidda Park",
+ category: "Parks",
+ coordinates: { x: 2117, y: 515 },
+ travelTime: 17,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.39-.34 2.92.76 7.58 15.4 21.4 42.34 32.84 54.28q.15.15.27.33c1.38 2.2 4.21 5.87 5.38 4.32 1.5-2 2.5-1.5-1-5.5s-22.5-30.5-30.5-49-22.5-34-32.5-51.5-51.5-83-53.5-87.5c-2.33-5.67-9.2-19.601-18-30.001-11-13-28-28.5-45-55.5-13.6-21.6-27.67-52.667-33-65.5-6.5-18.333-20-57.5-22-67.5-2.5-12.5-3-19-6-25.5s0-9 6-9.5c5.84-.487 178.49-15.19 200.54-16.429.9-.051 1.6-.663 1.81-1.537 4.82-20.228 14.75-64.056 18.65-89.034 5-32 11.5-76 29.5-95s60-43.5 94-54 51.5-19 58.5-21 28.5-.5 30.5 1.5c1.54 1.537 4.43 20.719 5.83 31.243.11.771-.25 1.525-.9 1.942L2117 517.5",
+ },
+ {
+ title: "Rixos Gulf Hotel Doha",
+ category: "Rest",
+ coordinates: { x: 2546, y: 664 },
+ travelTime: 15,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5 73-12 91.5-21 71-42.5 81-49.5 63-40.5 67.5-42.5 11-9 16-8 6.5 4 6.5 6 0 6.5-4.5 8.5-9-2-10-4-7.5-17-9-20.5-52-148-63-173.5-55.5-138-67-160.5-33.5-59.5-57-85-54.5-47-61.5-50c-4.97-2.886-17.31-8.456-28.22-8.519-.78-.005-1.51-.429-1.82-1.147-1.08-2.458-2.62-6.788-2.96-10.834v-1.089c0-.579-.25-1.129-.69-1.509L2552 670",
+ },
+ {
+ title: "Museum of Islamic Art",
+ category: "Leisure",
+ coordinates: { x: 2325, y: 569 },
+ travelTime: 17,
+ path: [
+ "m1869.5 1149.5 7.59-5.19a2 2 0 0 1 2.93.76c7.84 15.78 24.5 46.57 35.98 58.43 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5 73-12 91.5-21 71-42.5 81-49.5 63-40.5 67.5-42.5 11-9 16-8 6.5 4 6.5 6 0 6.5-4.5 8.5-9-2-10-4-7.5-17-9-20.5-52-148-63-173.498c-11-25.5-55.5-138-67-160.5s-33.5-59.5-57-85-54.5-47-61.5-50-14.5-9.5-31.5-8-30 9.001-68 8-37.5 1-40-12-3-23-9.5-34.5-19.5-31-37.5-38.5c-3.67-1.667-14-4.6-26-3-1 0-5 1.498-11 1.498s-11-.497-15.5 1.502c-3.95 1.752-17.13 3.506-23 3.911-1.11.076-2-.808-2-1.913v-13",
+ "M2332.5 606.5v-22.825q0-.175-.03-.347L2331 575",
+ ],
+ },
+ {
+ title: "Msherieb Downtown",
+ category: "Other",
+ coordinates: { x: 2208, y: 672 },
+ travelTime: 15,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.39-.34 2.92.76 7.58 15.4 21.4 42.34 32.84 54.28.1.1.19.21.26.33 1.39 2.2 4.22 5.88 5.39 4.32 1.5-2 2.5-1.5-1-5.5s-22.5-30.5-30.5-49-22.5-34-32.5-51.5-51.5-83-53.5-87.5-6.5-8.5 0-14S2028 845 2035.5 840s38-29 44-35.5 35-34.5 47.5-43 58.5-38 60-39-8-30-7.5-34 2.5-5 5.5-5c2.16 0 18.48.539 28.78.89 1.48.05 2.5-1.476 1.88-2.828L2214.5 679",
+ },
+ {
+ title: "Al Wakrah Souq",
+ category: "Souqs",
+ coordinates: { x: 3042, y: 1920 },
+ travelTime: 18,
+ path: "m1869.5 1149.5 7.59-5.19c1.01-.69 2.38-.34 2.93.75 7.84 15.79 24.5 46.58 35.98 58.44 16.33 16 55.4 55.8 81 87 32 39 39 49 50 60s64.5 53 110 57 82 1.5 138-16 203.5-60 218.5-63.5 79.5-23.5 92.5-25.5c12.59-1.94 69.29-11.38 89.64-20.15 1.03-.45 2.23-.06 2.7.96 33.14 71.82 100.24 216.75 110.16 236.19 10 19.6 67.17 135.5 94.5 191l74 158c.67 1.5 3.7 3.6 10.5 0 8.5-4.5 14-6.5 19.5-6 4.17.38 30.19 3.18 44.68 4.75 1.23.13 2.03 1.32 1.74 2.51-1.16 4.72-2.6 11.7-1.92 13.74.63 1.88 5.59 4.7 9.56 6.64 1.33.65 1.6 2.49.51 3.51-1.78 1.66-3.57 3.55-3.57 4.35 0 1.5 0 9-2.5 10.5s-6 .5-6.5 2c-.4 1.2-1.83 10.5-2.5 15",
+ },
+ {
+ title: "Venus Medical Center",
+ category: "Health care",
+ coordinates: { x: 1822, y: 1146 },
+ travelTime: 4,
+ path: "m1869.5 1149.5 7.67-5.25c.98-.67 2.32-.35 2.89.69l4.11 7.55c.48.87.24 1.95-.55 2.55l-62.52 47.25c-.89.67-2.14.5-2.81-.39l-13.1-17.33c-.66-.87-.5-2.11.37-2.78l63.44-49.29c.5-.5 1.77-1.3.5-3-1.5-2-3.67.5-5 1.5l-31.72 24.62c-.95.73-2.32.47-2.94-.55L1828 1152",
+ },
+ {
+ title: "International Medical Company",
+ category: "Health care",
+ coordinates: { x: 1737, y: 1092 },
+ travelTime: 9,
+ path: "m1869.5 1149.5 7.67-5.25c.98-.67 2.32-.35 2.89.69l4.12 7.55c.47.87.24 1.95-.55 2.55l-63.06 47.77c-.87.66-2.11.5-2.78-.36l-13.6-17.43a1.99 1.99 0 0 0-2.75-.38l-7.77 5.65c-.92.66-2.2.44-2.83-.49l-65.2-95.62c-.62-.92-.38-2.18.56-2.8l17.18-11.31c.9-.6 2.11-.36 2.73.52l8.29 11.84c.61.88.43 2.08-.43 2.73l-7.33 5.59c-.9.68-2.18.49-2.84-.43l-1.3-1.82",
+ },
+ {
+ title: "Abu Hamour Petrol Station",
+ category: "Other",
+ coordinates: { x: 1931, y: 1196 },
+ travelTime: 4,
+ path: "m1869.5 1149.5 7.68-5.26c.98-.66 2.31-.36 2.88.68 8.45 15.17 25.51 44.27 33.94 53.08 2 2.5 7.5 5.8 13.5-1 7.5-8.5 8.5-10 10.5-8.5 1.49 1.11 1.07 2.6.61 3.34-.07.11-.16.2-.25.29-1.88 1.71-5.58 5.2-6.36 6.37-.8 1.2 3 2.83 5 3.5",
+ },
+];
diff --git a/src/main.tsx b/src/main.tsx
index 7feb222..b8b50b8 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -5,19 +5,13 @@ import DefaultLayout from "./components/layouts/DefaultLayout.tsx";
import AboutPage from "./components/pages/AboutPage.tsx";
import ContactsPage from "./components/pages/ContactsPage.tsx";
import PopupContainer from "./components/ui/PopupContainer.tsx";
+import SurroundingsPage from "./components/pages/SurroundingsPage.tsx";
+import LayoutWithFooter from "./components/layouts/LayoutWithFooter.tsx";
const router = createBrowserRouter([
{
element: ,
children: [
- {
- path: "/",
- element: <>>,
- },
- {
- path: "/surroundings",
- element: <>>,
- },
{
path: "/about",
element: ,
@@ -28,6 +22,19 @@ const router = createBrowserRouter([
},
],
},
+ {
+ element: ,
+ children: [
+ {
+ path: "/",
+ element: <>>,
+ },
+ {
+ path: "/surroundings",
+ element: ,
+ },
+ ],
+ },
]);
createRoot(document.getElementById("root")!).render(