import React, { useState, useEffect, useRef } from 'react'; const WS_BASE = (import.meta.env.VITE_API_URL || 'http://localhost:8000').replace('http', 'ws'); const STATUS_COLOR = { DELIVERING: 'var(--accent)', RETURNING: 'var(--warning)', IDLE: 'var(--on-surface-variant)' }; export default function RouteIntelligence() { const [dispatch, setDispatch] = useState(null); const [connected, setConnected] = useState(false); const [log, setLog] = useState([]); const wsRef = useRef(null); const logEndRef = useRef(null); useEffect(() => { connect(); return () => wsRef.current?.close(); }, []); useEffect(() => { logEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [log]); const connect = () => { try { const ws = new WebSocket(`${WS_BASE}/ws/dispatch`); wsRef.current = ws; ws.onopen = () => setConnected(true); ws.onclose = () => { setConnected(false); setTimeout(connect, 3000); // auto-reconnect }; ws.onmessage = (e) => { const data = JSON.parse(e.data); setDispatch(data); if (data.batch) { setLog(prev => [ ...prev.slice(-49), { ts: new Date().toTimeString().slice(0, 8), rider: data.batch.rider_id, orders: data.batch.orders, efficiency: data.batch.efficiency_score, saved: data.batch.saved_distance_km, }, ]); } }; } catch { setTimeout(connect, 3000); } }; const riders = dispatch?.riders || []; const delivering = riders.filter(r => r.status === 'DELIVERING').length; const idle = riders.filter(r => r.status === 'IDLE').length; return (
{/* Header */}
Route Intelligence
Greedy Radius Dispatch Batcher · Kalman ETA Smoother · WebSocket live feed
{connected ? 'WebSocket Live' : 'Connecting...'}
{/* KPI row */}
Active Orders
{dispatch?.active_orders ?? '—'}
in flight
Avg ETA
{dispatch?.avg_eta_min ? `${dispatch.avg_eta_min}m` : '—'}
{dispatch?.eta_confidence ? `${(dispatch.eta_confidence * 100).toFixed(0)}% confidence` : ''}
Riders Delivering
{delivering || '—'}
{idle} idle
Last Batch Saved
{dispatch?.batch?.saved_distance_km ? `${dispatch.batch.saved_distance_km}km` : '—'}
distance optimized
{/* Rider board */}
Rider Status Board
{riders.length === 0 ? (
Connecting to dispatch WebSocket...
) : riders.map(r => (
{r.id} {r.name} {r.status} {r.order_id && ( {r.order_id} )} {r.eta_min && ( {r.eta_min}m ETA )}
))}
{/* Dispatch log */}
Batch Dispatch Log {log.length} batches
{log.map((entry, i) => (
{entry.rider} {entry.ts}
{entry.orders.join(', ')}
eff {(entry.efficiency * 100).toFixed(0)}% -{entry.saved}km
))}
); } const riderRow = { display: 'flex', alignItems: 'center', gap: 10, padding: '9px 10px', background: 'rgba(255,255,255,0.02)', border: '1px solid var(--border-glass)', borderRadius: 10, };