simpse / src /components /SimpleLeafletMap.tsx
canboi99's picture
Deploy Simpse real estate platform with Vancouver multifamily building interactive map
0d2425e verified
Raw
History Blame Contribute Delete
4.52 kB
"use client"
import { useEffect, useRef } from "react"
import L from "leaflet"
// Import Leaflet CSS
import "leaflet/dist/leaflet.css"
interface Property {
id: number
coordinates: [number, number]
price: number
type: string
bedrooms: number
bathrooms: number
sqft: number
address: string
status: 'active' | 'pending' | 'sold'
}
interface SimpleLeafletMapProps {
properties: Property[]
onPropertySelect: (property: Property | null) => void
selectedProperty: Property | null
}
export default function SimpleLeafletMap({
properties,
onPropertySelect,
selectedProperty
}: SimpleLeafletMapProps) {
const mapRef = useRef<L.Map | null>(null)
const mapContainerRef = useRef<HTMLDivElement>(null)
const markersRef = useRef<L.Marker[]>([])
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('en-CA', {
style: 'currency',
currency: 'CAD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(value).replace('CAD', '$').trim()
}
const getMarkerColor = (status: string) => {
switch (status) {
case 'active':
return '#10B981' // green
case 'pending':
return '#F59E0B' // orange
case 'sold':
return '#EF4444' // red
default:
return '#3B82F6' // blue
}
}
useEffect(() => {
if (!mapContainerRef.current || mapRef.current) return
console.log('Initializing map with', properties.length, 'properties')
// Initialize the map
mapRef.current = L.map(mapContainerRef.current).setView([56.1304, -96.0000], 4)
// Add OpenStreetMap tiles
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
attribution: '© OpenStreetMap contributors © CARTO',
subdomains: 'abcd',
maxZoom: 19
}).addTo(mapRef.current)
// Clear any existing markers
markersRef.current.forEach(marker => marker.remove())
markersRef.current = []
// Add property markers
properties.forEach(property => {
console.log('Adding marker for property:', property.address, 'at coordinates:', property.coordinates)
const color = getMarkerColor(property.status)
const priceText = formatCurrency(property.price)
// Create custom icon
const customIcon = L.divIcon({
html: `
<div style="
background: ${color};
color: white;
padding: 4px 8px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
text-align: center;
white-space: nowrap;
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
">
${priceText}
</div>
`,
className: 'custom-marker',
iconSize: [120, 30],
iconAnchor: [60, 30]
})
const marker = L.marker(property.coordinates, {
icon: customIcon
}).addTo(mapRef.current!)
marker.on('click', () => {
console.log('Marker clicked:', property.address)
onPropertySelect(property)
mapRef.current?.setView(property.coordinates, 12, { animate: true })
})
markersRef.current.push(marker)
})
console.log('Added', markersRef.current.length, 'markers to map')
return () => {
if (mapRef.current) {
mapRef.current.remove()
mapRef.current = null
}
markersRef.current = []
}
}, [properties, onPropertySelect])
// Update marker styles when selection changes
useEffect(() => {
if (!selectedProperty) return
markersRef.current.forEach((marker, index) => {
const property = properties[index]
if (property.id === selectedProperty.id) {
// Highlight selected marker
const element = marker.getElement()
if (element) {
element.style.transform = 'scale(1.1)'
element.style.zIndex = '1000'
}
} else {
// Reset other markers
const element = marker.getElement()
if (element) {
element.style.transform = 'scale(1)'
element.style.zIndex = '1'
}
}
})
}, [selectedProperty, properties])
return (
<>
<div
ref={mapContainerRef}
className="w-full h-full"
style={{ minHeight: '400px' }}
/>
<style jsx global>{`
.custom-marker {
background: transparent !important;
border: none !important;
}
`}</style>
</>
)
}