feat: onboarding mnemonic builder with step-through UI (Task 10)
- MnemonicBuilder: 12 NOVICE_LETTERS, real-time validatePhrase, save/skip - OnboardingPage: wraps builder, passes token and onComplete - App.tsx: post-auth fetch /api/mnemonics, route to onboarding if <12 saved - Fix: validatePhrase called with letter (not Morse pattern) as second arg Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+28
-15
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useAuth } from './hooks/useAuth'
|
||||
import { LoginPage } from './pages/LoginPage'
|
||||
import { OnboardingPage } from './pages/OnboardingPage'
|
||||
import './index.css'
|
||||
|
||||
type AppRoute = 'login' | 'sent' | 'onboarding' | 'game' | 'admin'
|
||||
@@ -8,8 +9,27 @@ 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)
|
||||
|
||||
if (loading) {
|
||||
// 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>
|
||||
@@ -17,28 +37,21 @@ export default function App() {
|
||||
)
|
||||
}
|
||||
|
||||
// If authenticated and still on an unauthenticated route, go to game
|
||||
// (Task 10 will refine this to check onboarding completion)
|
||||
const activeRoute: AppRoute = profile && (route === 'login' || route === 'sent') ? 'game' : route
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
{activeRoute === 'login' && (
|
||||
{route === 'login' && (
|
||||
<LoginPage onLoginSent={() => setRoute('sent')} />
|
||||
)}
|
||||
{activeRoute === '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>
|
||||
)}
|
||||
{activeRoute === 'onboarding' && (
|
||||
<div>
|
||||
<h1 style={{ fontFamily: 'var(--font-header)' }}>Welcome, {profile?.display_name || 'Adventurer'}!</h1>
|
||||
<p>Mnemonic builder coming in Task 10.</p>
|
||||
</div>
|
||||
{route === 'onboarding' && token && (
|
||||
<OnboardingPage token={token} onComplete={() => setRoute('game')} />
|
||||
)}
|
||||
{activeRoute === 'game' && (
|
||||
{route === 'game' && (
|
||||
<div>
|
||||
<h1 style={{ fontFamily: 'var(--font-header)' }}>Practice</h1>
|
||||
<p>Hello, {profile?.display_name || profile?.email || 'Adventurer'}!</p>
|
||||
@@ -48,7 +61,7 @@ export default function App() {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{activeRoute === 'admin' && (
|
||||
{route === 'admin' && (
|
||||
<div>
|
||||
<h1 style={{ fontFamily: 'var(--font-header)' }}>Admin</h1>
|
||||
<p>Admin panel coming in Task 12.</p>
|
||||
|
||||
Reference in New Issue
Block a user