import { useState } from 'react'; import { TransportEvent, TransportLocation } from '../../lib/transport/types'; interface JourneyMapProps { plantId: string; events: TransportEvent[]; currentLocation?: TransportLocation; } interface MapPoint { lat: number; lng: number; label: string; eventType: string; timestamp: string; } export default function JourneyMap({ plantId, events, currentLocation }: JourneyMapProps) { const [selectedPoint, setSelectedPoint] = useState(null); // Extract unique locations from events const mapPoints: MapPoint[] = []; const seenCoords = new Set(); events.forEach((event) => { const fromKey = `${event.fromLocation.latitude},${event.fromLocation.longitude}`; const toKey = `${event.toLocation.latitude},${event.toLocation.longitude}`; if (!seenCoords.has(fromKey)) { seenCoords.add(fromKey); mapPoints.push({ lat: event.fromLocation.latitude, lng: event.fromLocation.longitude, label: event.fromLocation.facilityName || event.fromLocation.city || 'Origin', eventType: event.eventType, timestamp: event.timestamp, }); } if (!seenCoords.has(toKey)) { seenCoords.add(toKey); mapPoints.push({ lat: event.toLocation.latitude, lng: event.toLocation.longitude, label: event.toLocation.facilityName || event.toLocation.city || 'Destination', eventType: event.eventType, timestamp: event.timestamp, }); } }); // Calculate map bounds const lats = mapPoints.map((p) => p.lat); const lngs = mapPoints.map((p) => p.lng); const minLat = Math.min(...lats); const maxLat = Math.max(...lats); const minLng = Math.min(...lngs); const maxLng = Math.max(...lngs); // Calculate center and scale for SVG const centerLat = (minLat + maxLat) / 2; const centerLng = (minLng + maxLng) / 2; const latRange = Math.max(maxLat - minLat, 0.01); const lngRange = Math.max(maxLng - minLng, 0.01); // Convert geo coords to SVG coords const toSvgCoords = (lat: number, lng: number) => { const x = ((lng - minLng) / lngRange) * 280 + 60; const y = ((maxLat - lat) / latRange) * 180 + 60; return { x, y }; }; // Calculate total distance const totalDistance = events.reduce((sum, e) => sum + e.distanceKm, 0); if (events.length === 0) { return (

Journey Map

🗺️

No journey data available yet.

Transport events will appear here as they're recorded.

); } return (

Journey Map

{mapPoints.length} locations
{/* SVG Map */}
{/* Grid lines */} {/* Draw paths between consecutive events */} {events.map((event, index) => { const from = toSvgCoords(event.fromLocation.latitude, event.fromLocation.longitude); const to = toSvgCoords(event.toLocation.latitude, event.toLocation.longitude); return ( {/* Arrow head */} ); })} {/* Draw location points */} {mapPoints.map((point, index) => { const { x, y } = toSvgCoords(point.lat, point.lng); const isSelected = selectedPoint?.lat === point.lat && selectedPoint?.lng === point.lng; return ( setSelectedPoint(point)} className="cursor-pointer" > {index + 1} ); })} {/* Current location marker */} {currentLocation && ( {(() => { const { x, y } = toSvgCoords(currentLocation.latitude, currentLocation.longitude); return ( <> ); })()} )} {/* Legend */}
Origin
Waypoint
Current
{/* Selected point details */} {selectedPoint && (

{selectedPoint.label}

Event: {selectedPoint.eventType.replace(/_/g, ' ')}

{new Date(selectedPoint.timestamp).toLocaleString()}

Coords: {selectedPoint.lat.toFixed(4)}, {selectedPoint.lng.toFixed(4)}

)} {/* Summary stats */}

{totalDistance.toFixed(1)} km

Total Distance

{mapPoints.length}

Locations

{events.length}

Transports

); }