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": {