| "use client" |
|
|
| import { useEffect, useRef } from "react" |
| import L from "leaflet" |
|
|
| |
| 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' |
| case 'pending': |
| return '#F59E0B' |
| case 'sold': |
| return '#EF4444' |
| default: |
| return '#3B82F6' |
| } |
| } |
|
|
| useEffect(() => { |
| if (!mapContainerRef.current || mapRef.current) return |
|
|
| console.log('Initializing map with', properties.length, 'properties') |
|
|
| |
| mapRef.current = L.map(mapContainerRef.current).setView([56.1304, -96.0000], 4) |
|
|
| |
| 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) |
|
|
| |
| markersRef.current.forEach(marker => marker.remove()) |
| markersRef.current = [] |
|
|
| |
| 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) |
| |
| |
| 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]) |
|
|
| |
| useEffect(() => { |
| if (!selectedProperty) return |
|
|
| markersRef.current.forEach((marker, index) => { |
| const property = properties[index] |
| if (property.id === selectedProperty.id) { |
| |
| const element = marker.getElement() |
| if (element) { |
| element.style.transform = 'scale(1.1)' |
| element.style.zIndex = '1000' |
| } |
| } else { |
| |
| 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> |
| </> |
| ) |
| } |