From 581ffde7fcb1d42045874ef91f73d7faf613b902 Mon Sep 17 00:00:00 2001
From: BizzleBot
Date: Thu, 19 Feb 2026 18:26:07 +0000
Subject: [PATCH] Add interactive Leaflet burrow map + fix stream API field
mapping
---
.npmrc | 1 +
app/map/BurrowMap.tsx | 84 +++++++++++++++++++++++++++++++++++++++++++
app/map/page.tsx | 32 ++++++-----------
package-lock.json | 50 ++++++++++++++++++++++++++
package.json | 4 +++
5 files changed, 150 insertions(+), 21 deletions(-)
create mode 100644 app/map/BurrowMap.tsx
diff --git a/.npmrc b/.npmrc
index 3ae7f31..daa23d5 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1,3 +1,4 @@
# npm configuration
registry=https://registry.npmjs.org/
save-exact=true
+legacy-peer-deps=true
diff --git a/app/map/BurrowMap.tsx b/app/map/BurrowMap.tsx
new file mode 100644
index 0000000..fcaf5b6
--- /dev/null
+++ b/app/map/BurrowMap.tsx
@@ -0,0 +1,84 @@
+'use client';
+import React, { useEffect, useState } from 'react';
+import { MapContainer, TileLayer, Marker, Popup, CircleMarker } from 'react-leaflet';
+import L from 'leaflet';
+import 'leaflet/dist/leaflet.css';
+
+interface Burrow {
+ id: string;
+ gps_lat: number;
+ gps_lng: number;
+ status: string;
+ location_description: string;
+}
+
+// Fix default marker icons in Next.js
+const activeIcon = new L.DivIcon({
+ className: '',
+ html: '',
+ iconSize: [14, 14],
+ iconAnchor: [7, 7],
+});
+
+const inactiveIcon = new L.DivIcon({
+ className: '',
+ html: '',
+ iconSize: [12, 12],
+ iconAnchor: [6, 6],
+});
+
+export default function BurrowMap() {
+ const [burrows, setBurrows] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
+ fetch(`${apiUrl}/api/burrows`)
+ .then((r) => r.json())
+ .then((data) => { setBurrows(data); setLoading(false); })
+ .catch(() => setLoading(false));
+ }, []);
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ // Cape Coral center
+ const center: [number, number] = [26.6029, -81.9595];
+
+ return (
+
+
+ {burrows.map((b) => (
+
+
+
+
{b.status === 'active' ? 'π’ Active' : 'β« Inactive'} Burrow
+
{b.location_description}
+
+ {b.gps_lat.toFixed(4)}, {b.gps_lng.toFixed(4)}
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/app/map/page.tsx b/app/map/page.tsx
index 9a2bafe..b450438 100644
--- a/app/map/page.tsx
+++ b/app/map/page.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import dynamic from 'next/dynamic';
import { Map } from 'lucide-react';
import type { Metadata } from 'next';
@@ -8,7 +9,14 @@ export const metadata: Metadata = {
description: 'Interactive map of burrowing owl burrows in Cape Coral, Florida.',
};
-
+const BurrowMap = dynamic(() => import('./BurrowMap'), {
+ ssr: false,
+ loading: () => (
+
+ ),
+});
export default function MapPage() {
return (
@@ -23,27 +31,9 @@ export default function MapPage() {
- {/* Map placeholder β interactive Leaflet map requires client component */}
+ {/* Interactive Leaflet Map */}
-
-
πΊοΈ
-
-
Interactive Burrow Map
-
- Leaflet map with burrow markers loading from{' '}
- /api/burrows
-
-
-
- {/* Decorative grid pattern */}
-
+
{/* Legend */}
diff --git a/package-lock.json b/package-lock.json
index 2077b4b..c5184c0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,14 +8,18 @@
"name": "owl-stream-frontend",
"version": "1.1.0",
"dependencies": {
+ "@react-leaflet/core": "2.1.0",
+ "@types/leaflet": "1.9.21",
"axios": "^1.6.7",
"clsx": "^2.1.0",
"date-fns": "^3.3.1",
"hls.js": "^1.5.7",
+ "leaflet": "1.9.4",
"lucide-react": "^0.344.0",
"next": "14.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-leaflet": "4.2.1",
"tailwind-merge": "^2.2.1"
},
"devDependencies": {
@@ -529,6 +533,17 @@
"node": ">=14"
}
},
+ "node_modules/@react-leaflet/core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
+ "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
+ "license": "Hippocratic-2.1",
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -552,6 +567,12 @@
"tslib": "^2.4.0"
}
},
+ "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/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -559,6 +580,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.16.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.13.tgz",
@@ -3463,6 +3493,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",
@@ -4354,6 +4390,20 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/react-leaflet": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
+ "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
+ "license": "Hippocratic-2.1",
+ "dependencies": {
+ "@react-leaflet/core": "^2.1.0"
+ },
+ "peerDependencies": {
+ "leaflet": "^1.9.0",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
diff --git a/package.json b/package.json
index 2f86dd6..a5d27b8 100644
--- a/package.json
+++ b/package.json
@@ -9,14 +9,18 @@
"lint": "next lint"
},
"dependencies": {
+ "@react-leaflet/core": "2.1.0",
+ "@types/leaflet": "1.9.21",
"axios": "^1.6.7",
"clsx": "^2.1.0",
"date-fns": "^3.3.1",
"hls.js": "^1.5.7",
+ "leaflet": "1.9.4",
"lucide-react": "^0.344.0",
"next": "14.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-leaflet": "4.2.1",
"tailwind-merge": "^2.2.1"
},
"devDependencies": {