/* Admin → Dashboard overview */ function Sparkline({ data, color }) { const w = 96, h = 30, max = Math.max(...data), min = Math.min(...data); const rng = max - min || 1; const pts = data.map((v, i) => [ (i/(data.length-1))*w, h - ((v-min)/rng)*(h-4) - 2 ]); const d = pts.map((p,i)=> (i?'L':'M')+p[0].toFixed(1)+' '+p[1].toFixed(1)).join(' '); const area = d + ` L${w} ${h} L0 ${h} Z`; const gid = 'sg' + color.replace(/[^a-z0-9]/gi,''); return ( ); } function StatTile({ icon, tint, ic, value, label, delta, deltaColor, trend, spark, onClick }) { const [hover, setHover] = React.useState(false); const up = trend === 'up', down = trend === 'down'; const pillBg = up ? 'var(--success-100)' : down ? 'var(--danger-100)' : 'var(--gray-100)'; const pillFg = up ? 'var(--success-600)' : down ? 'var(--danger-600)' : 'var(--gray-600)'; return (
setHover(true)} onMouseLeave={()=>setHover(false)} style={{ background:'#fff', border:'1px solid var(--gray-200)', borderRadius:16, padding:'18px', boxShadow: hover ? 'var(--shadow-md)' : 'var(--shadow-sm)', cursor: onClick?'pointer':'default', transform: hover ? 'translateY(-2px)' : 'none', transition:'transform .16s ease, box-shadow .16s ease', position:'relative', overflow:'hidden', }}>
{delta && ( {(up||down) && } {delta} )}
{value}
{label}
{spark &&
}
); } function Panel({ title, action, children, pad=true }) { return (
{title}
{action}
{children}
); } function AdminMap() { const BUSES = [ { id:'12', driver:'Mark Thompson', route:'Route 1 · East', riders:24, eta:'5 min', status:'On Route', color:'var(--success-500)', x:22, y:30, path:'M40 200 L40 120 L150 120 L150 60 L300 60' }, { id:'7', driver:'Carla Reyes', route:'Route 3 · West Loop', riders:19, eta:'8 min late', status:'Delayed', color:'var(--danger-500)', x:60, y:56, path:'M300 60 L300 150 L180 150 L180 220' }, { id:'15', driver:'Maya Okafor', route:'Route 4 · South', riders:17, eta:'On time', status:'On Route', color:'var(--success-500)', x:42, y:74, path:'M120 230 L120 150 L60 150 L60 60' }, ]; const [sel, setSel] = React.useState('12'); const [hoverPin, setHoverPin] = React.useState(null); const cur = BUSES.find(b => b.id === sel); return (
{/* terrain */}
{/* water */}
{/* roads */}
{/* selected route line */} {/* school marker */}
{/* bus pins */} {BUSES.map(b => { const on = b.id === sel, hov = hoverPin === b.id; return ( ); })} {/* live chip */}
{BUSES.length} buses live
{/* selected bus detail bar */}
Bus #{cur.id} · {cur.driver}
{cur.route} · {cur.riders} riders
{cur.status==='Delayed'?:}{cur.eta}
); } function DashboardScreen({ onNav }) { const [alerts, setAlerts] = React.useState([ { id:1, icon:'bus', tint:'var(--danger-100)', ic:'var(--danger-500)', t:'EJ Hatten missed Bus #12', s:'Self-reported · Stayed after class', time:'just now' }, { id:2, icon:'alert-triangle', tint:'var(--danger-100)', ic:'var(--danger-500)', t:'Bus #7 running 8 min late', s:'Route 3 · West Loop', time:'3m ago' }, { id:3, icon:'user-x', tint:'var(--warning-100)', ic:'var(--warning-600)', t:'Unverified pickup attempt', s:'Katy Middle · Sophia P.', time:'12m ago' }, ]); const dismissAlert = (id) => { setAlerts(a => a.filter(x => x.id !== id)); jlToast('Alert resolved', { icon:'check' }); }; const [queue, setQueue] = React.useState([ { n:'EJ Hatten', g:'Grade 6', ty:'Car Rider', t:'3:30 PM', st:'Waiting' }, { n:'James Walker', g:'Grade 7', ty:'Car Rider', t:'3:31 PM', st:'Waiting' }, { n:'Sophia Perez', g:'Grade 6', ty:'Walker', t:'3:31 PM', st:'Picked Up' }, { n:'Liam Carter', g:'Grade 8', ty:'Bus #12', t:'3:32 PM', st:'On Bus' }, ]); const ST = { 'Waiting': { bg:'var(--gray-200)', fg:'var(--gray-600)' }, 'Picked Up': { bg:'var(--success-100)', fg:'var(--success-600)' }, 'On Bus': { bg:'var(--blue-100)', fg:'var(--blue-600)' }, }; const release = (i) => { setQueue(q => q.map((r,idx)=> idx===i ? { ...r, st:'Picked Up' } : r)); jlToast(queue[i].n + ' marked picked up', { icon:'check-circle-2' }); }; const waiting = queue.filter(r => r.st==='Waiting').length; return (
{/* stat row */}
onNav && onNav('students')} /> onNav && onNav('transportation')} /> onNav && onNav('pickup')} /> onNav && onNav('payments')} />
{/* two columns */}
Live} pad={false}> jlToast('Showing all alerts', { icon:'bell' })} style={{ fontSize:12.5, fontWeight:700, color:'var(--blue-500)', cursor:'pointer' }}>View All}> {alerts.length === 0 && (
All caught up — no active alerts
)} {alerts.map((a,i)=>(
{a.t}
{a.s}
{a.time}
))}
{/* charts row */}
+6% vs last week}>
{/* pickup queue table */}
onNav && onNav('pickup')} style={{ fontSize:12.5, fontWeight:700, color:'var(--blue-500)', cursor:'pointer' }}>Open Pickup Manager} pad={false}>
StudentGradeMethodTimeStatus
{queue.map((r,i)=>(
w[0]).join('')} color={['blue','navy','gold','green'][i%4]} size={30} />{r.n} {r.g} {r.ty} {r.t} {r.st === 'Waiting' ? : {r.st}}
))}
); } Object.assign(window, { DashboardScreen, StatTile, Panel, AdminMap });