'use client'; import { useEffect, useMemo, useState } from 'react'; import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet'; import { Collection, Supplier } from '@/lib/types'; import { formatCurrency, calculateDistance } from '@/lib/utils'; import { Navigation, Check, AlertTriangle } from 'lucide-react'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; // Fix Leaflet default markers in Next.js const defaultIcon = L.icon({ iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png', iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png', shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41], }); const currentLocationIcon = L.divIcon({ className: 'custom-div-icon', html: `
`, iconSize: [20, 20], iconAnchor: [10, 10], }); interface MapViewProps { collections: Collection[]; onNavigate: (lat: number, lng: number, name: string) => void; } function MapBounds({ collections }: { collections: Collection[] }) { const map = useMap(); useEffect(() => { const validPoints = collections .filter((c) => c.expand?.supplier?.latitude && c.expand?.supplier?.longitude) .map((c) => [c.expand!.supplier!.latitude!, c.expand!.supplier!.longitude!] as [number, number]); if (validPoints.length > 0) { const bounds = L.latLngBounds(validPoints); map.fitBounds(bounds, { padding: [50, 50], maxZoom: 15 }); } }, [map, collections]); return null; } export default function CollectionMap({ collections, onNavigate }: MapViewProps) { const [userLocation, setUserLocation] = useState<[number, number] | null>(null); const [selectedSupplier, setSelectedSupplier] = useState(null); useEffect(() => { if ('geolocation' in navigator) { navigator.geolocation.getCurrentPosition( (pos) => { setUserLocation([pos.coords.latitude, pos.coords.longitude]); }, (error) => console.error('Could not get location:', error), { enableHighAccuracy: true, timeout: 10000 } ); const watchId = navigator.geolocation.watchPosition( (pos) => { setUserLocation([pos.coords.latitude, pos.coords.longitude]); }, (error) => console.error('Watch error:', error), { enableHighAccuracy: true } ); return () => navigator.geolocation.clearWatch(watchId); } }, []); // Group collections by supplier const suppliers = useMemo(() => { const map = new Map(); collections.forEach((collection) => { if (!collection.expand?.supplier) return; const supplier = collection.expand.supplier; if (!supplier.latitude || !supplier.longitude) return; const key = supplier.id; if (!map.has(key)) { map.set(key, { supplier, collections: [] }); } map.get(key)!.collections.push(collection); }); return Array.from(map.values()); }, [collections]); const defaultCenter: [number, number] = [-33.9249, 18.4241]; // Cape Town default const center = userLocation || defaultCenter; const getSupplierDistance = (supplier: Supplier) => { if (!userLocation || !supplier.latitude || !supplier.longitude) return null; return calculateDistance( userLocation[0], userLocation[1], supplier.latitude, supplier.longitude ); }; return (
{userLocation && (
Your Location
)} {suppliers.map(({ supplier, collections: supplierCollections }) => { const distance = getSupplierDistance(supplier); const hasProblem = supplierCollections.some((c) => c.status === 'problem'); return ( setSelectedSupplier(supplier.id), }} >

{supplier.suppliername}

{supplier.address}

{distance && (

{distance.toFixed(1)} km away

)}
{supplierCollections.map((c) => (
{c.status === 'problem' && ( )} {c.is_manual ? c.description : c.expand?.order_item?.description || 'Unknown'}
Qty: {c.quantity} {c.unit || c.expand?.order_item?.unit || 'units'}
))}
); })}
{/* Legend overlay */}

Legend

Your Location
Collection Stop
); }