/* Admin → Bus Routes editor: manage routes, stops, and bus/driver assignment. */ function RoutesScreen() { const DRIVERS = ['Marcus Reed', 'Dana Cole', 'Sam Ortiz', 'Priya Nair', 'Leon Baptiste', 'Unassigned']; const BUSES = ['#12', '#07', '#19', '#23', '#04', 'Unassigned']; const [routes, setRoutes] = React.useState([ { id:'r3', name:'KMS Loop 3', period:'AM', bus:'#12', driver:'Marcus Reed', stops:[ { id:1, name:'Oak & 3rd', time:'6:52 AM', students:2 }, { id:2, name:'Maple Court', time:'6:58 AM', students:1 }, { id:3, name:'Katy Elm Village', time:'7:05 AM', students:3 }, { id:4, name:'Riverstone Gate', time:'7:12 AM', students:1 }, { id:5, name:'Cinco Ranch Pkwy', time:'7:20 AM', students:2 }, ]}, { id:'r1', name:'Elm & Park', period:'AM', bus:'#07', driver:'Dana Cole', stops:[ { id:1, name:'Park Place', time:'6:48 AM', students:4 }, { id:2, name:'Elm & 5th', time:'6:55 AM', students:5 }, { id:3, name:'Grand Vista', time:'7:04 AM', students:5 }, ]}, { id:'r2', name:'North Ridge', period:'AM', bus:'#19', driver:'Sam Ortiz', stops:[ { id:1, name:'Ridge Top', time:'6:40 AM', students:3 }, { id:2, name:'Summit Way', time:'6:50 AM', students:6 }, ]}, { id:'r4', name:'South Gate', period:'PM', bus:'Unassigned', driver:'Unassigned', stops:[ { id:1, name:'Gateway Blvd', time:'3:45 PM', students:0 }, ]}, ]); const [selId, setSelId] = React.useState('r3'); const [dirty, setDirty] = React.useState(false); const sel = routes.find(r => r.id === selId); // Publish route→bus assignments so the Fleet Map reflects them live. React.useEffect(() => { try { const map = {}; routes.forEach(r => { if (r.bus && r.bus !== 'Unassigned') { const key = r.bus.replace('#',''); map[key] = { route:r.name, driver:r.driver, students:r.stops.reduce((m,s)=>m+(parseInt(s.students)||0),0), stops:r.stops.length }; } }); localStorage.setItem('jl_routes', JSON.stringify(map)); } catch(e) {} }, [routes]); const patch = (fn) => { setRoutes(rs => rs.map(r => r.id===selId ? fn(r) : r)); setDirty(true); }; const setField = (k, v) => patch(r => ({ ...r, [k]:v })); const setStop = (sid, k, v) => patch(r => ({ ...r, stops:r.stops.map(s => s.id===sid ? { ...s, [k]:v } : s) })); const addStop = () => patch(r => ({ ...r, stops:[...r.stops, { id:Date.now(), name:'New stop', time:'—', students:0 }] })); const rmStop = (sid) => patch(r => ({ ...r, stops:r.stops.filter(s => s.id!==sid) })); const totalStops = routes.reduce((n,r)=>n+r.stops.length,0); const totalStudents = routes.reduce((n,r)=>n+r.stops.reduce((m,s)=>m+ (parseInt(s.students)||0),0),0); const unassigned = routes.filter(r=>r.bus==='Unassigned'||r.driver==='Unassigned').length; return (
{/* route list */} { const id='r'+Date.now(); setRoutes(rs=>[...rs, { id, name:'New Route', period:'AM', bus:'Unassigned', driver:'Unassigned', stops:[] }]); setSelId(id); setDirty(true); }} style={{ display:'flex', alignItems:'center', gap:6, background:'var(--navy-600)', color:'#fff', border:0, borderRadius:9, padding:'8px 13px', fontWeight:700, fontSize:12.5, cursor:'pointer', fontFamily:'var(--font-sans)' }}>Add} pad={false}> {routes.map((r,i) => { const on = r.id===selId; const needs = r.bus==='Unassigned'||r.driver==='Unassigned'; return ( ); })} {/* editor */} { setDirty(false); jlToast(sel.name + ' saved', { icon:'check-check' }); }} style={{ display:'flex', alignItems:'center', gap:6, background:'var(--gold-400)', color:'var(--on-gold)', border:0, borderRadius:9, padding:'8px 13px', fontWeight:700, fontSize:12.5, cursor:'pointer', fontFamily:'var(--font-sans)', boxShadow:'var(--shadow-gold)' }}>Save Route}> {sel && (
{/* route meta */}
{['AM','PM'].map(p => ( ))}
{/* stops */}
Stops ({sel.stops.length})
{sel.stops.length===0 &&
No stops yet — add the first stop.
} {sel.stops.map((s, idx) => (
{idx+1} setStop(s.id,'name',e.target.value)} style={{ ...rtField, flex:2, marginTop:0 }} /> setStop(s.id,'time',e.target.value)} style={{ ...rtField, width:90, marginTop:0 }} />
setStop(s.id,'students',e.target.value.replace(/[^0-9]/g,''))} style={{ ...rtField, width:46, marginTop:0, textAlign:'center', fontFamily:'var(--font-mono)' }} />
))}
)}
); } const rtLbl = { display:'block', fontSize:11.5, fontWeight:700, color:'var(--gray-500)', marginBottom:5 }; const rtField = { width:'100%', boxSizing:'border-box', border:'1px solid var(--gray-200)', borderRadius:9, padding:'9px 11px', fontSize:13.5, fontFamily:'var(--font-sans)', color:'var(--ink)', outline:'none', background:'#fff' }; Object.assign(window, { RoutesScreen });