feat: embedded interactive map in property + match detail views

Integrates react-leaflet with OpenStreetMap tiles. PropertyDetailView
and MatchDetail now show property photo + zoomable/pannable map above
the tabs/content — always visible on card click, no redirect. Map
renders at correct lat/lng with a location marker and popup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-18 13:34:57 +02:00
parent c75d3c8121
commit 5b917bd619
6 changed files with 163 additions and 16 deletions
+60
View File
@@ -0,0 +1,60 @@
import { useEffect } from 'react'
import { Box } from '@mui/material'
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
// Fix default marker icons in Vite build
delete (L.Icon.Default.prototype as unknown as Record<string, unknown>)._getIconUrl
L.Icon.Default.mergeOptions({
iconRetinaUrl: new URL('leaflet/dist/images/marker-icon-2x.png', import.meta.url).href,
iconUrl: new URL('leaflet/dist/images/marker-icon.png', import.meta.url).href,
shadowUrl: new URL('leaflet/dist/images/marker-shadow.png', import.meta.url).href,
})
interface Props {
lat: number
lng: number
label?: string
height?: number
}
function RecenterOnChange({ lat, lng }: { lat: number; lng: number }) {
const map = useMap()
useEffect(() => {
map.setView([lat, lng])
}, [lat, lng, map])
return null
}
export function PropertyMap({ lat, lng, label, height = 260 }: Props) {
return (
<Box
sx={{
height,
width: '100%',
overflow: 'hidden',
flexShrink: 0,
// Ensure leaflet controls appear above MUI elements
'& .leaflet-container': { height: '100%', width: '100%', zIndex: 0 },
'& .leaflet-control': { zIndex: 1 },
}}
>
<MapContainer
center={[lat, lng]}
zoom={14}
scrollWheelZoom
style={{ height: '100%', width: '100%' }}
>
<RecenterOnChange lat={lat} lng={lng} />
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[lat, lng]}>
{label && <Popup>{label}</Popup>}
</Marker>
</MapContainer>
</Box>
)
}
+1
View File
@@ -1,4 +1,5 @@
export { ViewToggle } from './ViewToggle'
export { LocationPreview } from './LocationPreview'
export { PropertyMap } from './PropertyMap'
export { getScoreTier, SCORE_THEME } from './scoreTheme'
export type { ScoreTier } from './scoreTheme'