/* ============================================
APP — Router & screen orchestrator
============================================ */
const { useState: useStateApp } = React;
function App() {
const [screen, setScreen] = useStateApp(null);
const [user, setUser] = useStateApp(null);
const [ready, setReady] = useStateApp(false);
// Boot: validate token via /api/auth/me, then resolve initial screen.
React.useEffect(() => {
let cancelled = false;
(async () => {
if (window.API.getToken()) {
try {
const { user: u } = await window.API.me();
if (cancelled) return;
setUser(u);
} catch (_) { /* fall through to login */ }
}
if (cancelled) return;
const hash = window.location.hash.replace("#", "");
const initial = hash && (SCREEN_META[hash] || hash === "login") ? hash : "workspace";
setScreen(initial);
setReady(true);
})();
return () => { cancelled = true; };
}, []);
// Keep screen in sync with URL hash.
React.useEffect(() => {
const onHash = () => {
const h = window.location.hash.replace("#", "");
if (h && (SCREEN_META[h] || h === "login")) setScreen(h);
};
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
const navigate = (s) => {
setScreen(s);
window.location.hash = s;
window.scrollTo(0, 0);
};
const handleLogin = (u) => {
setUser(u);
navigate("workspace");
};
const handleLogout = async () => {
await window.API.logout();
setUser(null);
navigate("login");
};
// Still validating the saved token — render nothing to avoid flashing the login screen.
if (!ready) return null;
// Route guard: any screen other than login requires an authed user.
if (screen === "login" || !user) {
return