/* Admin → Messaging: broadcasts + parent threads */ function DispatchThread() { const SEED = [ { from:'dispatch', t:'Morning Marcus — Elm Village Dr is closed for repairs. Use the Riverstone detour for stop 3.' }, { from:'driver', t:'Got it, taking the detour now.' }, { from:'dispatch', t:'Thanks. Let us know if you fall behind schedule.' }, ]; const [thread, setThread] = React.useState([]); const [draft, setDraft] = React.useState(''); React.useEffect(() => { JLBackend.seed({ dispatch: SEED }); setThread(JLBackend.get('dispatch', SEED)); return JLBackend.subscribe('dispatch', v => setThread(v || [])); }, []); const send = () => { const t = draft.trim(); if (!t) return; JLBackend.update('dispatch', prev => [...(prev||[]), { from:'dispatch', t }], SEED); setDraft(''); }; return ( Live}>
{thread.map((b,i)=>{ const me = b.from==='dispatch'; return (
{b.t}
); })}
setDraft(e.target.value)} onKeyDown={e=>{ if(e.key==='Enter') send(); }} placeholder="Message the driver…" style={{ flex:1, border:'1px solid var(--gray-200)', borderRadius:'999px', padding:'10px 15px', fontSize:13.5, fontFamily:'var(--font-sans)', outline:'none', color:'var(--ink)' }} />
); } function NotifReachPanel() { const CATS = [ { id:'alerts', label:'Emergency alerts', icon:'siren' }, { id:'pickup', label:'Pickup & bus', icon:'car-front' }, { id:'attend', label:'Attendance', icon:'calendar-check' }, { id:'grades', label:'Grades', icon:'graduation-cap' }, { id:'messages', label:'Messages', icon:'message-circle' }, { id:'payments', label:'Payments', icon:'wallet' }, { id:'events', label:'Events', icon:'calendar-days' }, ]; const [prefs, setPrefs] = React.useState(null); React.useEffect(() => { const read = () => { try { setPrefs(JSON.parse(localStorage.getItem('jl_notif') || 'null')); } catch(e){} }; read(); const onStore = (e) => { if (e.key === 'jl_notif') read(); }; window.addEventListener('storage', onStore); const iv = setInterval(read, 1500); return () => { window.removeEventListener('storage', onStore); clearInterval(iv); }; }, []); const Dot = ({ on }) => {on?'On':'Off'}; return ( live from parent app} pad={false}> {!prefs ? (
This family hasn't set notification preferences yet — defaults apply (push on).
) : (
CategoryPushSMS
{CATS.map((c,i) => { const p = prefs[c.id] || {}; return (
{c.label}
); })}
)}
); } function MessagingScreen() { const [tab, setTab] = React.useState('broadcasts'); const broadcasts = [ { t:'Early dismissal this Friday at 1:00 PM', aud:'All Parents · Katy Middle', time:'2h ago', reach:'1,248 sent', icon:'megaphone', tint:'var(--blue-100)', ic:'var(--blue-500)' }, { t:'Bus #7 running late on Route 3', aud:'Route 3 Families', time:'3h ago', reach:'19 sent', icon:'bus', tint:'var(--danger-100)', ic:'var(--danger-500)' }, { t:'Spring book fair next week', aud:'All Parents', time:'1d ago', reach:'1,248 sent', icon:'book-open', tint:'var(--gold-100)', ic:'var(--gold-600)' }, ]; const threads = [ { n:'Jermaine Hatten', re:'Re: EJ pickup change', prev:'Thank you, that works for us.', time:'12m', c:'navy', unread:true }, { n:'Ashley Brooks', re:'Bus stop question', prev:'Is the morning stop still at 7:40?', time:'1h', c:'blue', unread:true }, { n:'Marcus Lee', re:'Lunch balance', prev:'Just added funds — thanks!', time:'4h', c:'green', unread:false }, ]; return (
{/* composer */}