// FRAZIER Finance OS — entry point. Session bootstrap (fin_me), login gate, // hash-based page routing (no router dependency — matches the "don't // overengineer V1" principle), dark-mode default per fin-tokens.css. function finPageFromHash() { const h = (location.hash || '').replace(/^#/, ''); return FIN_NAV.some((n) => n.id === h) ? h : 'home'; } function FinApp() { const [user, setUser] = React.useState(undefined); // undefined = loading, null = logged out const [page, setPage] = React.useState(finPageFromHash()); const [jarvisQ, setJarvisQ] = React.useState(null); React.useEffect(() => { document.documentElement.setAttribute('data-theme', 'dark'); }, []); React.useEffect(() => { let alive = true; finApi('fin_me').then((d) => { if (alive) setUser(d.user || null); }).catch(() => { if (alive) setUser(null); }); return () => { alive = false; }; }, []); React.useEffect(() => { const onHash = () => setPage(finPageFromHash()); window.addEventListener('hashchange', onHash); return () => window.removeEventListener('hashchange', onHash); }, []); const navigate = (id, question) => { location.hash = id; setPage(id); if (id === 'jarvis' && question) setJarvisQ(question); }; if (user === undefined) { return
F
; } if (!user) { return setUser(u)} />; } const logout = async () => { try { await finApi('fin_logout', { body: {} }); } catch (e) { /* ignore */ } setUser(null); }; let body; switch (page) { case 'home': body = ; break; case 'jarvis': body = ; break; case 'invoices': body = ; break; case 'payroll': body = ; break; case 'forecast': body = ; break; case 'files': body = ; break; default: body = ; } return ( {body} ); } const finRoot = ReactDOM.createRoot(document.getElementById('fin-root')); finRoot.render();