'use client'; import { useEffect, useMemo, useState, useCallback, useRef } from 'react'; import { MapContainer, TileLayer, Marker, Popup, Polyline, useMap } from 'react-leaflet'; import { OrderItem, Supplier, DriverLocation } from '@/lib/types'; import { formatCurrency, calculateDistance, parseCoordinates } from '@/lib/utils'; import { getDriverTrail, isAuthenticated, getCurrentUser } from '@/lib/pocketbase'; import { Navigation, AlertTriangle, Route, Crosshair, Layers } from 'lucide-react'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; 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: [22, 22], iconAnchor: [11, 11], }); interface MapViewProps { items: OrderItem[]; onNavigate: (lat: number, lng: number, name: string) => void; } function MapBounds({ items }: { items: OrderItem[] }) { const map = useMap(); useEffect(() => { const validPoints = items .map((i) => { const coords = parseCoordinates(i.expand?.supplier?.coordinates); return coords ? [coords.latitude, coords.longitude] as [number, number] : null; }) .filter((point): point is [number, number] => point !== null); if (validPoints.length > 0) { const bounds = L.latLngBounds(validPoints); map.fitBounds(bounds, { padding: [60, 60], maxZoom: 15 }); } }, [map, items]); return null; } function RecenterButton({ center }: { center: [number, number] }) { const map = useMap(); return ( ); } export default function CollectionMap({ items, onNavigate }: MapViewProps) { const [userLocation, setUserLocation] = useState<[number, number] | null>(null); const [trail, setTrail] = useState<[number, number][]>([]); const [showTrail, setShowTrail] = useState(true); const intervalRef = useRef | null>(null); // Get current position useEffect(() => { if ('geolocation' in navigator) { navigator.geolocation.getCurrentPosition( (pos) => setUserLocation([pos.coords.latitude, pos.coords.longitude]), () => {}, { enableHighAccuracy: false, timeout: 30000 } ); } }, []); // Fetch trail data periodically const fetchTrail = useCallback(async () => { if (!isAuthenticated()) return; const user = getCurrentUser(); if (!user) return; const data = await getDriverTrail(user.id); const points = data .filter((loc) => loc.latitude && loc.longitude) .map((loc) => [loc.latitude, loc.longitude] as [number, number]); if (points.length > 0) { setTrail(points); } }, []); useEffect(() => { fetchTrail(); intervalRef.current = setInterval(fetchTrail, 60000); return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [fetchTrail]); // Group by supplier const suppliers = useMemo(() => { const map = new Map(); items.forEach((item) => { if (!item.expand?.supplier) return; const supplier = item.expand.supplier; const coords = parseCoordinates(supplier.coordinates); if (!coords) return; const key = supplier.id; if (!map.has(key)) { map.set(key, { supplier, items: [], coords }); } map.get(key)!.items.push(item); }); return Array.from(map.values()); }, [items]); const defaultCenter: [number, number] = [-33.9249, 18.4241]; const center = userLocation || defaultCenter; const getDistance = (coords: { latitude: number; longitude: number }) => { if (!userLocation) return null; return calculateDistance(userLocation[0], userLocation[1], coords.latitude, coords.longitude); }; return (
{userLocation && (
Your Location
)} {/* Trail polyline */} {showTrail && trail.length > 1 && ( <>
', iconSize: [14, 14], iconAnchor: [7, 7], })} >
Trip Start
)} {suppliers.map(({ supplier, items: supplierItems, coords }) => { const distance = getDistance(coords); return (

{supplier.suppliername}

{supplier.supplieraddress && (

{supplier.supplieraddress}

)} {distance && (

{distance < 1 ? `${Math.round(distance * 1000)}m` : `${distance.toFixed(1)} km`} away

)}
{supplierItems.map((i) => (
{(i.item_status === 'problem' || i.problem_flag) && } {i.description}
Qty: {i.order_qty || i.quantity || 0}
))}
); })} {/* Floating controls */}
{/* Recenter */} {userLocation && ( )} {/* Trail toggle */} {trail.length > 1 && ( )} {/* Legend */}

Legend

You
Stop
{trail.length > 1 && (
Route
)}
); }