/* Admin → Live Fleet Map: all buses on a stylized map + fleet status list. */ function FleetScreen() { const STATUS = { route: { label:'On route', bg:'var(--success-100)', fg:'var(--success-600)', dot:'var(--success-500)' }, delayed: { label:'Delayed', bg:'var(--warning-100)', fg:'var(--warning-600)', dot:'var(--warning-500)' }, idle: { label:'At depot', bg:'var(--gray-100)', fg:'var(--gray-600)', dot:'var(--gray-400)' }, }; const baseBuses = [ { id:'12', driver:'Marcus Reed', route:'KMS Loop 3', status:'route', students:11, eta:'7:20 AM', x:34, y:58 }, { id:'07', driver:'Dana Cole', route:'Elm & Park', status:'route', students:14, eta:'7:24 AM', x:62, y:38 }, { id:'19', driver:'Sam Ortiz', route:'North Ridge', status:'delayed', students:9, eta:'7:35 AM', x:20, y:30 }, { id:'23', driver:'Priya Nair', route:'Cinco West', status:'route', students:12, eta:'7:28 AM', x:78, y:64 }, { id:'04', driver:'Leon Baptiste',route:'South Gate', status:'idle', students:0, eta:'—', x:48, y:82 }, ]; const [sel, setSel] = React.useState('12'); const [live, setLive] = React.useState({}); const [assign, setAssign] = React.useState({}); const [alerts, setAlerts] = React.useState([]); React.useEffect(() => { const read = () => { try { setLive(JSON.parse(localStorage.getItem('jl_fleet') || '{}')); setAssign(JSON.parse(localStorage.getItem('jl_routes') || '{}')); setAlerts(JSON.parse(localStorage.getItem('jl_alerts') || '[]')); } catch(e){} }; read(); const onStore = (e) => { if (e.key === 'jl_fleet' || e.key === 'jl_routes' || e.key === 'jl_alerts') read(); }; window.addEventListener('storage', onStore); const iv = setInterval(read, 1500); return () => { window.removeEventListener('storage', onStore); clearInterval(iv); }; }, []); const clearAlerts = () => { try { localStorage.setItem('jl_alerts', '[]'); } catch(e){} setAlerts([]); jlToast('Alerts cleared', { icon:'check-check' }); }; const setAlert = (id, patch, toast) => { setAlerts(prev => { const n = prev.map(a => a.id===id ? { ...a, ...patch } : a); try { localStorage.setItem('jl_alerts', JSON.stringify(n)); } catch(e){} if (patch.status==='resolved') { try { const a = prev.find(x=>x.id===id); if (a) { const log = JSON.parse(localStorage.getItem('jl_incidentlog') || '[]'); log.unshift({ id:a.id, kind:a.kind, label:a.label, bus:a.bus, driver:a.driver, reportedAt:a.at, resolvedAt:Date.now() }); localStorage.setItem('jl_incidentlog', JSON.stringify(log.slice(0,50))); } } catch(e){} } return n; }); if (toast) jlToast(toast.msg, { icon:toast.icon, color:toast.color }); }; const ago = (t) => { const s = Math.round((Date.now()-t)/1000); return s<60?'just now':(s<3600?Math.floor(s/60)+'m ago':Math.floor(s/3600)+'h ago'); }; const buses = baseBuses.map(b => { let out = { ...b }; const a = assign[b.id]; if (a) out = { ...out, route:a.route || out.route, driver:a.driver || out.driver, students:a.students }; const l = live[b.id]; if (l) out = { ...out, status:l.status, students:l.students, driver:l.driver || out.driver, route:l.route || out.route, next:l.next, isLive:true }; return out; }); const onRoute = buses.filter(b=>b.status==='route').length; const delayed = buses.filter(b=>b.status==='delayed').length; const aboard = buses.reduce((n,b)=>n+b.students,0); return (