// ===== APP: header, nav, drawer, routing, mood, dark mode, AI news ===== const SCREENS = { Home: { title: 'NRSA', comp: HomeScreen }, Traffic: { title: 'Live Traffic', subtitle: 'Metro Manila · Real-time', comp: TrafficScreen }, Courses: { title: 'Education', subtitle: 'Drive smarter, safer', comp: CoursesScreen }, Laws: { title: 'Traffic Laws', subtitle: 'Know the rules', comp: LawsScreen }, Emergency: { title: 'Emergency SOS', subtitle: 'Help is one tap away', comp: EmergencyScreen }, Weather: { title: 'Weather & Roads', subtitle: 'Drive to the conditions', comp: WeatherScreen }, Forum: { title: 'Community', subtitle: 'Drivers helping drivers', comp: ForumScreen }, Coach: { title: 'AI Driving Coach', subtitle: 'Powered by Claude', comp: CoachScreen }, Score: { title: 'Safety Score', subtitle: 'Your driving report card', comp: ScoreScreen }, Commute: { title: 'Commute Tracker', subtitle: 'Plan a safer route', comp: CommuteScreen }, Buddy: { title: 'SOS Buddy', subtitle: 'Share trips with loved ones', comp: BuddyScreen }, Checklist: { title: 'Pre-Trip Checklist', subtitle: 'Inspect before you drive', comp: ChecklistScreen }, Logger: { title: 'Evidence Logger', subtitle: 'Document incidents', comp: LoggerScreen }, PoliceTips: { title: 'Know Your Rights', subtitle: 'Philippines · Enforcers', comp: PoliceTipsScreen }, Offices: { title: 'Traffic Offices', subtitle: 'PH directory', comp: OfficesScreen }, PHOrdinances: { title: 'PH Ordinances', subtitle: 'City-by-city rules', comp: PHOrdinancesScreen }, Alerts: { title: 'Notifications', subtitle: 'Your latest alerts', comp: AlertsScreen }, Profile: { title: 'My Profile', subtitle: 'Account & settings', comp: ProfileScreen }, PWD: { title: 'PWD Assistance', subtitle: 'Accessibility & priority support', comp: PWDScreen }, Bicycle: { title: 'Cyclist Hub', subtitle: 'Ride safe · lanes · rights', comp: BicycleScreen }, Ebike: { title: 'E-Bike Center', subtitle: 'Classes · rules · charging', comp: EbikeScreen }, RoadHazard: { title: 'Road Hazard Report', subtitle: 'Ibalik ang sidewalk sa mga tao', comp: RoadHazardScreen }, CrashReport: { title: 'Crash Report', subtitle: 'Real-time intake · 24 fields', comp: CrashReportScreen }, ResponderRegister: { title: 'Crash Responder', subtitle: 'Application & verification', comp: ResponderRegisterScreen }, AdminConsole: { title: 'Admin Console', subtitle: 'NRSA system administration', comp: AdminConsoleScreen }, Programs: { title: 'Programs & Campaigns', subtitle: 'NRSA · road safety initiatives', comp: ProgramsScreen }, Donate: { title: 'Donate', subtitle: 'Support road safety', comp: DonateScreen } }; const TABS = [ { id: 'Home', label: 'Home', icon: 'home' }, { id: 'Traffic', label: 'Traffic', icon: 'traffic' }, { id: 'Courses', label: 'Learn', icon: 'learn' }, { id: '__more', label: 'More', icon: 'grid' }, { id: 'Emergency', label: 'SOS', icon: 'sos' }]; const DRAWER_ITEMS = [ { id: 'Laws', icon: '⚖️', color: '#7b2d8b' }, { id: 'Weather', icon: '🌦️', color: '#00c48c' }, { id: 'Forum', icon: '💬', color: '#ffa502' }, { id: 'Coach', icon: '🤖', color: '#2556a8' }, { id: 'Score', icon: '🏆', color: '#f5a623' }, { id: 'Commute', icon: '🛣️', color: '#ff6b2b' }, { id: 'Buddy', icon: '👥', color: '#00c48c' }, { id: 'Checklist', icon: '✅', color: '#1a3a6e' }, { id: 'Logger', icon: '📍', color: '#ff4757' }, { id: 'PoliceTips', icon: '🪪', color: '#2556a8' }, { id: 'Offices', icon: '🏛️', color: '#7b2d8b' }, { id: 'PHOrdinances', icon: '🏙️', color: '#ff6b2b' }, { id: 'Alerts', icon: '🔔', color: '#ffa502' }, { id: 'Programs', icon: '📢', color: '#2556a8' }, { id: 'Donate', icon: '❤️', color: '#ff6b2b' }, { id: 'CrashReport', icon: '🚨', color: '#ff4757' }, { id: 'PWD', icon: '♿', color: '#2556a8' }, { id: 'Bicycle', icon: '🚲', color: '#00c48c' }, { id: 'Ebike', icon: '🛵', color: '#7b2d8b' }, { id: 'RoadHazard', icon: '🚧', color: '#ff6b2b' }, { id: 'Profile', icon: '🙋', color: '#00c48c' }]; const DRAWER_LABELS = { Laws: 'Traffic Laws', Weather: 'Weather', Forum: 'Community', Coach: 'AI Coach', Score: 'Safety Score', Commute: 'Commute', Buddy: 'SOS Buddy', Checklist: 'Pre-Trip', Logger: 'Evidence Log', PoliceTips: 'Your Rights', Offices: 'Offices', PHOrdinances: 'PH Ordinances', Alerts: 'Alerts', Programs: 'Programs', Donate: 'Donate', CrashReport: 'Crash Report', PWD: 'PWD Assistance', Bicycle: 'Bicycles', Ebike: 'E-Bikes', RoadHazard: 'Road Hazard', Profile: 'Profile', AdminConsole: 'Admin Console' }; function App() { const [user, setUser] = useState(() => { try { return JSON.parse(localStorage.getItem('nrsa_auth')) || null; } catch (e) { return null; } }); const [consent, setConsent] = useState(() => { const c = getConsent(); return c && c.version === CONSENT_VERSION ? c : null; }); const [dark, setDark] = useState(() => localStorage.getItem('nrsa_dark') === '1'); const [moodId, setMoodId] = useState(() => localStorage.getItem('nrsa_mood') || 'focused'); const [screen, setScreen] = useState('Home'); const [drawer, setDrawer] = useState(false); const [moodSheet, setMoodSheet] = useState(false); const [news, setNews] = useState(news_static); const [newsLoading, setNewsLoading] = useState(false); const [newsKey, setNewsKey] = useState(0); const t = getTheme(dark); const mood = MOODS.find((m) => m.id === moodId) || MOODS[0]; useEffect(() => {localStorage.setItem('nrsa_dark', dark ? '1' : '0');}, [dark]); useEffect(() => {localStorage.setItem('nrsa_mood', moodId);}, [moodId]); const handleAuth = (u) => { const full = { role: 'driver', ...u }; localStorage.setItem('nrsa_auth', JSON.stringify(full)); setUser(full); setScreen(full.role === 'admin' ? 'AdminConsole' : 'Home'); // Live mode: sync the consent captured at the gate (it needs a session). if (NRSAApi.isLive() && consent && !consent.synced) { NRSAApi.recordConsent({ privacy: !!consent.privacy, terms: !!consent.terms, marketing: !!consent.marketing, version: consent.version }) .then(() => { const c = { ...consent, synced: true }; localStorage.setItem(CONSENT_KEY, JSON.stringify(c)); setConsent(c); }) .catch(() => { /* will retry next login */ }); } }; const updateUser = (patch) => setUser(prev => { const next = { ...prev, ...patch }; localStorage.setItem('nrsa_auth', JSON.stringify(next)); if (NRSAApi.isLive()) NRSAApi.updateProfile(patch).catch(() => {}); return next; }); const logout = () => { if (NRSAApi.isLive()) NRSAApi.logout().catch(() => {}); localStorage.removeItem('nrsa_auth'); setUser(null); setDrawer(false); }; const acceptConsent = (c) => { localStorage.setItem(CONSENT_KEY, JSON.stringify(c)); setConsent(c); }; const withdrawConsent = () => { localStorage.removeItem(CONSENT_KEY); localStorage.removeItem('nrsa_auth'); setConsent(null); setUser(null); setDrawer(false); }; // Gate 1: no data-privacy consent yet → consent notice (RA 10173) if (!consent) { return (
); } // Gate 2: not signed in → auth flow if (!user) { return (
); } const go = (id) => { if (id === '__more') {setDrawer(true);return;} setDrawer(false); setScreen(id); }; const refreshNews = async () => { setNewsLoading(true); try { const prompt = `Generate exactly 4 realistic road-safety news items relevant to drivers in the Philippines. Return ONLY a JSON array (no markdown, no prose). Each object: {"id":number,"tag":one of "SAFETY"/"ALERT"/"POLICY"/"TIP","tagColor":hex matching SAFETY=#ff6b2b ALERT=#ff4757 POLICY=#2556a8 TIP=#00c48c,"title":short headline,"time":like "2h ago","icon":one emoji,"summary":one sentence under 22 words}.`; const res = await window.claude.complete(prompt); const cleaned = (res || '').replace(/```json/gi, '').replace(/```/g, '').trim(); const start = cleaned.indexOf('[');const end = cleaned.lastIndexOf(']'); const parsed = JSON.parse(cleaned.slice(start, end + 1)); const colorFor = { SAFETY: '#ff6b2b', ALERT: '#ff4757', POLICY: '#2556a8', TIP: '#00c48c' }; if (Array.isArray(parsed) && parsed.length) { setNews(parsed.slice(0, 4).map((n, i) => ({ id: n.id || i + 1, tag: n.tag || 'SAFETY', tagColor: n.tagColor || colorFor[n.tag] || '#2556a8', title: n.title || 'Road Safety Update', time: n.time || 'now', icon: n.icon || '📰', summary: n.summary || '' }))); setNewsKey((k) => k + 1); } } catch (e) {/* keep existing news */} setNewsLoading(false); }; const meta = SCREENS[screen]; const ScreenComp = meta.comp; const isPrimary = TABS.some((tab) => tab.id === screen); const isCoach = screen === 'Coach'; return (
{/* ===== Header ===== */}
go('Home')} style={{ display: 'flex', alignItems: 'center', gap: 9, cursor: 'pointer' }}>
NRSA
NRSA PH
ROAD SAFETY APP
{/* Mood pill */} {/* Bell */} {/* Dark toggle */}
{/* ===== Screen title bar (not for Home) ===== */} {screen !== 'Home' &&
{!isPrimary && }

{meta.title}

{meta.subtitle &&

{meta.subtitle}

}
} {/* ===== Main content ===== */}
{!isCoach &&
}
{/* ===== Bottom nav ===== */} {/* ===== More drawer ===== */} {drawer &&
setDrawer(false)} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.45)', zIndex: 40, display: 'flex', alignItems: 'flex-end', animation: 'screenIn 0.2s ease' }}>
e.stopPropagation()} style={{ width: '100%', background: t.bg, borderTopLeftRadius: 26, borderTopRightRadius: 26, padding: '10px 18px max(20px, env(safe-area-inset-bottom))', maxHeight: '76%', overflowY: 'auto' }}>

All Features

{(user.role === 'admin' ? [{ id: 'AdminConsole', icon: '🛡️', color: '#f5a623' }, ...DRAWER_ITEMS] : DRAWER_ITEMS).map((d) => )}
} {/* ===== Mood sheet ===== */} {moodSheet &&
setMoodSheet(false)} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.45)', zIndex: 45, display: 'flex', alignItems: 'flex-end', animation: 'screenIn 0.2s ease' }}>
e.stopPropagation()} style={{ width: '100%', background: t.bg, borderTopLeftRadius: 26, borderTopRightRadius: 26, padding: '10px 18px max(20px, env(safe-area-inset-bottom))' }}>

How are you driving?

Sets your mood glow and tailors safety nudges.

{MOODS.map((m) => { const sel = m.id === moodId; return ( ); })}
}
); } ReactDOM.createRoot(document.getElementById('root')).render();