diff --git a/package-lock.json b/package-lock.json
index c87708f..d3039d2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,14 +8,17 @@
"name": "antigravity_dev",
"version": "0.1.0",
"dependencies": {
+ "@types/leaflet": "^1.9.21",
"@types/react-google-recaptcha": "^2.1.9",
"axios": "^1.13.2",
+ "leaflet": "^1.9.4",
"lucide-react": "^0.554.0",
"next": "16.0.3",
"next-themes": "^0.4.6",
"react": "19.2.0",
"react-dom": "19.2.0",
"react-google-recaptcha": "^3.1.0",
+ "react-leaflet": "^5.0.0",
"selenium-webdriver": "^4.38.0",
"sitemap": "^9.0.0",
"tailwindcss-animate": "^1.0.7",
@@ -1241,6 +1244,17 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@react-leaflet/core": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz",
+ "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==",
+ "license": "Hippocratic-2.1",
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -1546,6 +1560,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT"
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1560,6 +1580,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/leaflet": {
+ "version": "1.9.21",
+ "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
+ "integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
"node_modules/@types/node": {
"version": "20.19.25",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz",
@@ -4687,6 +4716,12 @@
"node": ">=0.10"
}
},
+ "node_modules/leaflet": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
+ "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
+ "license": "BSD-2-Clause"
+ },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -5668,6 +5703,20 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
+ "node_modules/react-leaflet": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
+ "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==",
+ "license": "Hippocratic-2.1",
+ "dependencies": {
+ "@react-leaflet/core": "^3.0.0"
+ },
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
diff --git a/package.json b/package.json
index d06df5c..f326764 100644
--- a/package.json
+++ b/package.json
@@ -9,14 +9,17 @@
"lint": "eslint"
},
"dependencies": {
+ "@types/leaflet": "^1.9.21",
"@types/react-google-recaptcha": "^2.1.9",
"axios": "^1.13.2",
+ "leaflet": "^1.9.4",
"lucide-react": "^0.554.0",
"next": "16.0.3",
"next-themes": "^0.4.6",
"react": "19.2.0",
"react-dom": "19.2.0",
"react-google-recaptcha": "^3.1.0",
+ "react-leaflet": "^5.0.0",
"selenium-webdriver": "^4.38.0",
"sitemap": "^9.0.0",
"tailwindcss-animate": "^1.0.7",
diff --git a/src/components/ConnectivityMap.tsx b/src/components/ConnectivityMap.tsx
new file mode 100644
index 0000000..bb51cfa
--- /dev/null
+++ b/src/components/ConnectivityMap.tsx
@@ -0,0 +1,268 @@
+"use client";
+
+import { useState, useEffect, useMemo } from 'react';
+import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet';
+import L from 'leaflet';
+import 'leaflet/dist/leaflet.css';
+
+// Fix for default markers in Next.js
+const DefaultIcon = L.icon({
+ iconUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png',
+ shadowUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png',
+ iconSize: [25, 41],
+ iconAnchor: [12, 41]
+});
+L.Marker.prototype.options.icon = DefaultIcon;
+
+// Custom Icons
+const createCustomIcon = (color: string, number?: string) => {
+ return L.divIcon({
+ className: 'custom-icon',
+ html: `
${number || ''}
`,
+ iconSize: [36, 36],
+ iconAnchor: [18, 18],
+ });
+};
+
+const homeIcon = L.divIcon({
+ className: 'home-icon',
+ html: ``,
+ iconSize: [48, 48],
+ iconAnchor: [24, 24],
+});
+
+
+// Mock Data
+type LocationData = {
+ category: string;
+ items: {
+ id: number;
+ name: string;
+ dist: string;
+ time: string;
+ lat: number;
+ lng: number;
+ count: number;
+ }[];
+};
+
+const PROPERTY_LOCATION: [number, number] = [12.9385, 77.7297]; // Approximate Varthur
+
+const MOCK_DATA: Record = {
+ 'Commute': [
+ { id: 1, name: "Dommasandra Circle Metro Station", dist: "3.62 Km", time: "7 mins", lat: 12.9250, lng: 77.7450, count: 6 },
+ { id: 2, name: "Sompura Metro Station", dist: "7.05 Km", time: "15 mins", lat: 12.9100, lng: 77.7600, count: 4 },
+ { id: 3, name: "Sarjapur Metro Station", dist: "8.28 Km", time: "18 mins", lat: 12.8900, lng: 77.7800, count: 3 },
+ { id: 4, name: "Ambedkar nagar Metro Station", dist: "8.89 Km", time: "18 mins", lat: 12.9550, lng: 77.7100, count: 5 },
+ ],
+ 'Education': [
+ { id: 5, name: "Whitefield Global School", dist: "2.5 Km", time: "6 mins", lat: 12.9550, lng: 77.7350, count: 8 },
+ { id: 6, name: "Greenwood High", dist: "4.1 Km", time: "10 mins", lat: 12.9150, lng: 77.7550, count: 5 },
+ ],
+ 'Hospitals': [
+ { id: 7, name: "Manipal Hospital Varthur", dist: "1.2 Km", time: "4 mins", lat: 12.9420, lng: 77.7320, count: 2 },
+ ],
+ 'Work': [
+ { id: 8, name: "RGA Tech Park", dist: "5.5 Km", time: "12 mins", lat: 12.9050, lng: 77.7150, count: 12 },
+ ],
+ 'Entertainment': [
+ { id: 9, name: "Nexus Whitefield", dist: "3.2 Km", time: "9 mins", lat: 12.9600, lng: 77.7400, count: 7 },
+ ],
+};
+
+function MapController({ center }: { center: [number, number] }) {
+ const map = useMap();
+ useEffect(() => {
+ map.setView(center, 13);
+ }, [center, map]);
+ return null;
+}
+
+function ZoomHandler({ zoomIn, zoomOut }: { zoomIn: () => void, zoomOut: () => void }) {
+ return (
+
+
+
+
+ );
+}
+
+
+export default function ConnectivityMap() {
+ const [activeTab, setActiveTab] = useState("Commute");
+ const [searchQuery, setSearchQuery] = useState("");
+ const [mapZoom, setMapZoom] = useState(13);
+ const [mapRef, setMapRef] = useState(null);
+
+ const activeData = useMemo(() => {
+ let data = activeTab === "Search"
+ ? Object.values(MOCK_DATA).flat()
+ : MOCK_DATA[activeTab] || [];
+
+ if (searchQuery) {
+ data = data.filter(item =>
+ item.name.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+ }
+ return data;
+ }, [activeTab, searchQuery]);
+
+ const handleZoomIn = () => {
+ if (mapRef) mapRef.zoomIn();
+ };
+
+ const handleZoomOut = () => {
+ if (mapRef) mapRef.zoomOut();
+ };
+
+
+ return (
+
+ {/* Tabs */}
+
+ {["Commute", "Education", "Hospitals", "Work", "Entertainment", "Search"].map((tab) => (
+
+ ))}
+
+
+
+ {/* Map Area */}
+
+
+
+
+
+
+ {/* Property Marker */}
+
+
+ {/* POI Markers */}
+ {activeData.map((item) => (
+
+ {item.name}
+
+ ))}
+
+
+ {/* Layer Toggle Button (Bottom Left) */}
+
+
+
+ {/* Floating List Card (Right Side) */}
+
+
+ {/* Search Input for Search Tab */}
+ {activeTab === "Search" && (
+
+ setSearchQuery(e.target.value)}
+ className="w-full px-4 py-2 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-orange-500"
+ />
+
+ )}
+
+
+ {activeData.length > 0 ? (
+ activeData.map((item) => (
+
+
{item.name}
+
+ {item.dist}
+ |
+ {item.time}
+
+
+ ))
+ ) : (
+
+ No results found
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/src/components/PropertyDetailClient.tsx b/src/components/PropertyDetailClient.tsx
index 6bf61fd..c8fd3e6 100644
--- a/src/components/PropertyDetailClient.tsx
+++ b/src/components/PropertyDetailClient.tsx
@@ -9,6 +9,12 @@ import InnerBanner from "@/components/InnerBanner";
import { Property } from "@/data/properties";
import axios from "axios";
import { useCompare } from "@/context/CompareContext";
+import dynamic from 'next/dynamic';
+
+const ConnectivityMap = dynamic(() => import('./ConnectivityMap'), {
+ ssr: false,
+ loading: () =>
+});
interface FormData {
name: string;
@@ -595,128 +601,7 @@ export default function PropertyDetailClient({ property }: { property: Property
{/* Map Interface */}
-
- {/* Tabs */}
-
- {["Commute", "Education", "Hospitals", "Work", "Entertainment ", "Search"].map((tab) => (
-
- ))}
-
-
-
- {/* Map Area */}
-
- {/* Map Image Placeholder */}
-
-

-
-
- {/* Map Controls */}
-
-
-
-
-
- {/* Layer Toggle */}
-
-
- {/* Pins (Static for demo) */}
-
-
-
-
-
-
- {/* List Area */}
-
-
- {[
- { name: "Dommasandra Circle Metro Station", dist: "3.62 Km", time: "7 mins" },
- { name: "Sompura Metro Station", dist: "7.05 Km", time: "15 mins" },
- { name: "Sarjapur Metro Station", dist: "8.28 Km", time: "18 mins" },
- { name: "Ambedkar nagar Metro Station", dist: "8.89 Km", time: "18 mins" },
- ].map((item, idx) => (
-
-
{item.name}
-
- {item.dist}
- |
- {item.time}
-
-
- ))}
-
-
-
-
+
{/* Master Plan Section */}
diff --git a/src/components/PropertyNav.tsx b/src/components/PropertyNav.tsx
index 40e81aa..4beae26 100644
--- a/src/components/PropertyNav.tsx
+++ b/src/components/PropertyNav.tsx
@@ -44,7 +44,7 @@ export default function PropertyNav({ sections }: PropertyNavProps) {
};
return (
-
+