import { useEffect, useState } from 'react' import { EnrollPage } from './pages/EnrollPage' import { PayPage } from './pages/PayPage' import { NotFoundPage } from './pages/NotFoundPage' import { HomePage } from './pages/HomePage' import { SecurityPage } from './pages/SecurityPage' import { BiometricsLabPage } from './pages/BiometricsLabPage' import { AgenticDemoPage } from './pages/AgenticDemoPage' import { FederatedDemoPage } from './pages/FederatedDemoPage' import { ActivityPage } from './pages/ActivityPage' import { BiometricsDashboard } from './pages/BiometricsDashboard' import { BiometricEnrollPage } from './pages/BiometricEnrollPage' import { BiometricVerifyPage } from './pages/BiometricVerifyPage' import { LivenessPage } from './pages/LivenessPage' import { ResultsPage } from './pages/ResultsPage' import { ReportCardPage } from './pages/ReportCardPage' import { OobPage } from './pages/OobPage' import { D2EnrollPage } from './pages/D2EnrollPage' import { D2SignInPage } from './pages/D2SignInPage' import { D2AccountPage } from './pages/D2AccountPage' import { D2SecurityPage } from './pages/D2SecurityPage' import { D2SessionsPage } from './pages/D2SessionsPage' import { D2AccountStatusPage } from './pages/D2AccountStatusPage' import { D2OperatorPage } from './pages/D2OperatorPage' import { D2RecoveryPage } from './pages/D2RecoveryPage' import { FinancePage } from './pages/FinancePage' import { AtmPage } from './pages/AtmPage' import { AppFooter } from './components/AppFooter' import { useSession } from './hooks/useSession' import { useD3Enabled } from './hooks/useD3Enabled' import { useI18n } from './i18n' import { Button } from './components/ui' import { AGENTIC_DEMO_ENABLED } from './api/ai' const ROUTES = [ 'home', 'pay', 'security', 'activity', 'demo/agentic-security', 'demo/federated-learning', 'biometrics/lab', 'enroll', 'biometrics', 'biometrics/enroll', 'biometrics/verify', 'biometrics/liveness', 'biometrics/reportcard', 'biometrics/oob', 'results', 'id/enroll', 'id/signin', 'id/account', 'id/security', 'id/sessions', 'id/status', 'id/operator', 'id/recovery', 'finance', 'atm', 'notfound', ] as const type Route = (typeof ROUTES)[number] function parse(): Route { const raw = window.location.hash.replace(/^#\/?/, '') if (raw === '' || raw === 'home') return 'home' return (ROUTES as readonly string[]).includes(raw) ? (raw as Route) : 'notfound' } function useHashRoute(): [Route, (r: string) => void] { const [route, setRoute] = useState(parse()) useEffect(() => { const onHash = () => setRoute(parse()) window.addEventListener('hashchange', onHash) return () => window.removeEventListener('hashchange', onHash) }, []) const nav = (r: string) => { window.location.hash = `#/${r}`; setRoute(parse()) } return [route, nav] } // Primary customer destinations (desktop nav + mobile bottom bar). const PRIMARY: { route: string; key: string }[] = [ { route: 'home', key: 'nav.home' }, { route: 'finance', key: 'nav.finance' }, { route: 'pay', key: 'nav.pay' }, { route: 'security', key: 'nav.security' }, { route: 'activity', key: 'nav.activity' }, { route: 'id/account', key: 'nav.account' }, ] export function App() { const { t, locale, setLocale } = useI18n() const { activeUser } = useSession() const [route, nav] = useHashRoute() const [moreOpen, setMoreOpen] = useState(false) // D3 (simulated wallet) is dormant on deployments that don't set its env flags (e.g. the public // Space). Hide the Wallet/ATM entry points there so they aren't dead ends; show them once the // runtime reports enabled (a local `make start`, or an activated deployment). const d3Enabled = useD3Enabled() const primary = PRIMARY.filter((p) => p.route !== 'finance' || d3Enabled === true) const matches = (r: string) => route === r || (r === 'home' && route === 'home') const tab = (r: string, label: string) => ( ) return (
{t('app.title')}
{activeUser ? t('header.activeUser', { user: activeUser.userId }) : t('header.noUser')}

{t('demo.banner')}

{route === 'home' && nav(r)} />} {route === 'pay' && nav('enroll')} onNav={(r) => nav(r)} />} {route === 'security' && nav(r)} />} {route === 'demo/agentic-security' && } {route === 'demo/federated-learning' && } {route === 'biometrics/lab' && nav(r)} />} {route === 'activity' && } {route === 'enroll' && } {route === 'biometrics' && nav(r)} />} {route === 'biometrics/enroll' && } {route === 'biometrics/verify' && } {route === 'biometrics/liveness' && } {route === 'biometrics/reportcard' && } {route === 'biometrics/oob' && } {route === 'results' && } {route === 'id/enroll' && nav(r)} />} {route === 'id/signin' && nav(r)} />} {route === 'id/account' && nav(r)} />} {route === 'id/security' && } {route === 'id/sessions' && } {route === 'id/status' && nav(r)} />} {route === 'id/operator' && nav(r)} />} {route === 'id/recovery' && nav(r)} />} {/* Wallet/ATM routes only resolve when D3 is enabled; otherwise fall through to 404 so a dormant deployment can't reach a broken page (null = still probing → render nothing). */} {route === 'finance' && d3Enabled === true && nav(r)} />} {route === 'atm' && d3Enabled === true && nav(r)} />} {(route === 'finance' || route === 'atm') && d3Enabled === false && nav(r)} />} {route === 'notfound' && nav(r)} />}
{moreOpen && (
{AGENTIC_DEMO_ENABLED && ( )} {AGENTIC_DEMO_ENABLED && ( )}
)}
) }