c9c12a4c2c
- AdminPage: password gate (sessionStorage), user table, stats banner - Auto-login using stored sessionStorage password on mount - App.tsx: route to admin when pathname === '/admin' - Fix: remove redundant setPassword in auto-login effect (useState already seeds from sessionStorage) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { useAuth } from './hooks/useAuth'
|
|
import { LoginPage } from './pages/LoginPage'
|
|
import { OnboardingPage } from './pages/OnboardingPage'
|
|
import { GamePage } from './pages/GamePage'
|
|
import { AdminPage } from './pages/AdminPage'
|
|
import './index.css'
|
|
|
|
type AppRoute = 'login' | 'sent' | 'onboarding' | 'game' | 'admin'
|
|
|
|
export default function App() {
|
|
const { profile, token, loading, logout } = useAuth()
|
|
const [route, setRoute] = useState<AppRoute>('login')
|
|
const [checkingOnboarding, setCheckingOnboarding] = useState(false)
|
|
|
|
// Check for /admin path
|
|
useEffect(() => {
|
|
if (window.location.pathname === '/admin') {
|
|
setRoute('admin')
|
|
}
|
|
}, [])
|
|
|
|
// After auth resolves, check onboarding status
|
|
useEffect(() => {
|
|
if (!profile || !token) return
|
|
if (route !== 'login' && route !== 'sent') return
|
|
|
|
setCheckingOnboarding(true)
|
|
fetch('/api/mnemonics', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.then(r => r.json())
|
|
.then((mnemonics: Record<string, string>) => {
|
|
const count = Object.keys(mnemonics).length
|
|
setRoute(count >= 12 ? 'game' : 'onboarding')
|
|
})
|
|
.catch(() => setRoute('game'))
|
|
.finally(() => setCheckingOnboarding(false))
|
|
}, [profile, token])
|
|
|
|
if (loading || checkingOnboarding) {
|
|
return (
|
|
<div className="app-shell" style={{ textAlign: 'center', paddingTop: '4rem' }}>
|
|
<p style={{ fontFamily: 'var(--font-header)' }}>Loading quest...</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="app-shell">
|
|
{route === 'login' && (
|
|
<LoginPage onLoginSent={() => setRoute('sent')} />
|
|
)}
|
|
{route === 'sent' && (
|
|
<div style={{ maxWidth: 420, margin: '4rem auto 0', textAlign: 'center' }}>
|
|
<h2 style={{ fontFamily: 'var(--font-header)', marginBottom: '1rem' }}>Check your email</h2>
|
|
<p>We sent a magic link to your inbox. Click it to begin your quest!</p>
|
|
</div>
|
|
)}
|
|
{route === 'onboarding' && token && (
|
|
<OnboardingPage token={token} onComplete={() => setRoute('game')} />
|
|
)}
|
|
{route === 'game' && token && (
|
|
<GamePage token={token} onLogout={() => { logout(); setRoute('login') }} />
|
|
)}
|
|
{route === 'admin' && <AdminPage />}
|
|
</div>
|
|
)
|
|
}
|