1- import React , { useState , useEffect } from "react" ;
1+ import React , { useEffect , useState } from "react" ;
22import { RButton } from "./ui" ;
33import { getUrl } from "../../lib/helpers" ;
4+ import { MapViewer , type MarkerData } from "./MapViewer" ;
45
56type LivingLab = {
67 id : string ;
@@ -21,30 +22,19 @@ type Props = {
2122
2223export function LivingLabsMapSection ( { labs } : Props ) {
2324 const [ selectedLab , setSelectedLab ] = useState < LivingLab | null > ( null ) ;
24-
25- // state to hold dynamically imported components
26- const [ leafletComponents , setLeafletComponents ] = useState < any | null > ( null ) ;
27-
25+ const [ mapCenter , setMapCenter ] = useState < [ number , number ] > ( [ 50 , 10 ] ) ;
26+ const [ mapZoom , setMapZoom ] = useState < number > ( 4 ) ;
27+ const mapKey = mapCenter ? `${ mapCenter [ 0 ] } ,${ mapCenter [ 1 ] } ` : "no-center" ;
2828 useEffect ( ( ) => {
29- let mounted = true ;
30- // load react-leaflet components and CSS on the client only
31- async function loadLeaflet ( ) {
32- if ( typeof window === "undefined" ) return ;
33- try {
34- const comps = await import ( "react-leaflet" ) ;
35- // dynamically load css so server doesn't try to process it at build time
36- await import ( "leaflet/dist/leaflet.css" ) ;
37- if ( mounted ) setLeafletComponents ( comps ) ;
38- } catch ( e ) {
39- // optional: handle or log load failure
40- if ( mounted ) setLeafletComponents ( null ) ;
41- }
29+ if (
30+ selectedLab &&
31+ selectedLab . coordinates ?. lat &&
32+ selectedLab . coordinates ?. lng
33+ ) {
34+ setMapCenter ( [ selectedLab . coordinates . lat , selectedLab . coordinates . lng ] ) ;
35+ setMapZoom ( 6 ) ;
4236 }
43- loadLeaflet ( ) ;
44- return ( ) => {
45- mounted = false ;
46- } ;
47- } , [ ] ) ;
37+ } , [ selectedLab ] ) ;
4838
4939 const getStatusColor = ( status : LivingLab [ "status" ] ) => {
5040 switch ( status ) {
@@ -57,8 +47,17 @@ export function LivingLabsMapSection({ labs }: Props) {
5747 }
5848 } ;
5949
50+ // convert labs to MarkerData for MapViewer
51+ const markers : MarkerData [ ] = labs . map ( ( lab ) => ( {
52+ id : lab . id ,
53+ name : lab . name ,
54+ coordinates : lab . coordinates ,
55+ radius : lab . radius * 1000 , // convert km to meters
56+ meta : { lab } ,
57+ } ) ) ;
58+
6059 return (
61- < section className = "bg-light py-12 px-4 sm:px-8" >
60+ < section className = "py-12 px-4 sm:px-8" >
6261 < div className = "max-w-7xl mx-auto" >
6362 { /* Section Title */ }
6463 < div className = "mb-8" >
@@ -75,49 +74,22 @@ export function LivingLabsMapSection({ labs }: Props) {
7574 < div className = "grid grid-cols-1 lg:grid-cols-3 gap-6" >
7675 { /* Map */ }
7776 < div className = "lg:col-span-2 h-[600px] rounded shadow overflow-hidden" >
78- { /* <div className=""> */ }
79- { ! leafletComponents && (
80- < div className = "flex items-center justify-center h-full w-full bg-gray-100 text-gray-600" >
81- Loading map...
82- </ div >
83- ) }
84-
85- { leafletComponents &&
86- ( ( ) => {
87- const { MapContainer, TileLayer, Marker, Popup, Circle } =
88- leafletComponents as any ;
89- return (
90- < MapContainer
91- center = { [ 48.85 , 2.35 ] }
92- zoom = { 5 }
93- scrollWheelZoom = { false }
94- className = "h-full w-full z-0"
95- >
96- < TileLayer
97- attribution = '© <a href="https://osm.org">OpenStreetMap</a>'
98- url = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
99- />
77+ < MapViewer
78+ key = { mapKey }
79+ markers = { markers }
80+ center = { mapCenter }
81+ zoom = { mapZoom }
82+ className = "h-full w-full z-0"
83+ onMarkerClick = { ( m ) => {
84+ // prefer passing the original lab if available in meta
85+ if ( m . meta && m . meta . lab ) setSelectedLab ( m . meta . lab ) ;
86+ else {
87+ const found = labs . find ( ( l ) => l . id === m . id ) ;
88+ if ( found ) setSelectedLab ( found ) ;
89+ }
90+ } }
91+ />
10092
101- { labs . map ( ( lab ) => (
102- < >
103- < Marker
104- key = { lab . id }
105- position = { [ lab . coordinates . lat , lab . coordinates . lng ] }
106- eventHandlers = { {
107- click : ( ) => setSelectedLab ( lab ) ,
108- } }
109- />
110- < Circle
111- center = { [ lab . coordinates . lat , lab . coordinates . lng ] }
112- radius = { lab . radius }
113- pathOptions = { { color : "#2563eb" , fillOpacity : 0.2 } }
114- />
115- </ >
116- ) ) }
117- </ MapContainer >
118- ) ;
119- } ) ( ) }
120- { /* </div> */ }
12193 { /* Slide-up Detail Panel */ }
12294 { selectedLab && (
12395 < div className = " lg:mr-80 bg-white rounded-lg p-3 shadow border border-primary sticky bottom-0 left-0 z-50" >
@@ -177,9 +149,6 @@ export function LivingLabsMapSection({ labs }: Props) {
177149 >
178150 🔍 Explore { selectedLab . name } Living Lab
179151 </ RButton >
180- { /* <button className="bg-secondary text-white px-4 py-2 rounded hover:bg-dark transition">
181- ➕ More About This Lab
182- </button> */ }
183152 </ div >
184153 </ div >
185154 ) }
0 commit comments