refactor: completely remove @nav parallel routes and simplify navigation

- Remove entire src/app/@nav directory and all parallel route files
- Delete complex AppNav component that handled route-based nav detection
- Update layout.tsx to remove nav slot parameter entirely
- Create simple PageWithNav component that takes title/emoji as props
- Update matching and memory-quiz games to use PageWithNav directly
- Each page now controls its own navigation - dead simple and direct

This eliminates the over-engineered parallel routes approach in favor of
straightforward React prop passing. Much easier to understand and maintain.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-09-29 11:51:59 -05:00
parent 7a3e34b4fa
commit 54ff20c755
12 changed files with 46 additions and 107 deletions

View File

@ -1,3 +0,0 @@
export default function CreateNav() {
return null
}

View File

@ -1,3 +0,0 @@
export default function DefaultNav() {
return null // No navigation content for routes without specific @nav slots
}

View File

@ -1,14 +0,0 @@
export default function MatchingNav() {
return (
<h1 style={{
fontSize: '18px',
fontWeight: 'bold',
background: 'linear-gradient(135deg, #60a5fa, #a78bfa, #f472b6)',
backgroundClip: 'text',
color: 'transparent',
margin: 0
}}>
🧩 Memory Pairs
</h1>
)
}

View File

@ -1,14 +0,0 @@
export default function MemoryQuizNav() {
return (
<h1 style={{
fontSize: '18px',
fontWeight: 'bold',
background: 'linear-gradient(135deg, #60a5fa, #a78bfa, #f472b6)',
backgroundClip: 'text',
color: 'transparent',
margin: 0
}}>
🧠 Memory Lightning
</h1>
)
}

View File

@ -1,3 +0,0 @@
export default function GamesNav() {
return null
}

View File

@ -1,3 +0,0 @@
export default function GuideNav() {
return null
}

View File

@ -1,3 +0,0 @@
export default function HomeNav() {
return null
}

View File

@ -1,10 +1,13 @@
import { PageWithNav } from '@/components/PageWithNav'
import { MemoryPairsProvider } from './context/MemoryPairsContext'
import { MemoryPairsGame } from './components/MemoryPairsGame'
export default function MatchingPage() {
return (
<MemoryPairsProvider>
<MemoryPairsGame />
</MemoryPairsProvider>
<PageWithNav navTitle="Memory Pairs" navEmoji="🧩">
<MemoryPairsProvider>
<MemoryPairsGame />
</MemoryPairsProvider>
</PageWithNav>
)
}

View File

@ -6,7 +6,7 @@ import { css } from '../../../../styled-system/css'
import { AbacusReact } from '@soroban/abacus-react'
import { useAbacusConfig } from '@soroban/abacus-react'
import { isPrefix } from '../../../lib/memory-quiz-utils'
import { StandardGameLayout } from '../../../components/StandardGameLayout'
import { PageWithNav } from '@/components/PageWithNav'
interface QuizCard {
@ -1733,7 +1733,7 @@ export default function MemoryQuizPage() {
}, [state.prefixAcceptanceTimeout])
return (
<StandardGameLayout>
<PageWithNav navTitle="Memory Lightning" navEmoji="🧠">
<style dangerouslySetInnerHTML={{ __html: globalAnimations }} />
<div
@ -1741,14 +1741,16 @@ export default function MemoryQuizPage() {
flex: 1,
display: 'flex',
flexDirection: 'column',
overflow: 'auto'
overflow: 'auto',
padding: '20px 8px',
minHeight: '100vh',
background: 'linear-gradient(135deg, #f8fafc, #e2e8f0)'
}}
>
<div
style={{
maxWidth: '100%',
margin: '0 auto',
padding: '0 8px',
flex: 1,
display: 'flex',
flexDirection: 'column'
@ -1800,6 +1802,6 @@ export default function MemoryQuizPage() {
</div>
</div>
</div>
</StandardGameLayout>
</PageWithNav>
)
}

View File

@ -1,7 +1,6 @@
import type { Metadata, Viewport } from 'next'
import './globals.css'
import { ClientProviders } from '@/components/ClientProviders'
import { AppNav } from '@/components/AppNav'
export const metadata: Metadata = {
title: 'Soroban Flashcard Generator',
@ -17,16 +16,13 @@ export const viewport: Viewport = {
export default function RootLayout({
children,
nav,
}: {
children: React.ReactNode
nav: React.ReactNode
}) {
return (
<html lang="en">
<body>
<ClientProviders>
<AppNav>{nav}</AppNav>
{children}
</ClientProviders>
</body>

View File

@ -1,52 +0,0 @@
import React from 'react'
import { headers } from 'next/headers'
import { AppNavBar } from './AppNavBar'
interface AppNavProps {
children: React.ReactNode
}
function getNavContentForPath(pathname: string): React.ReactNode {
// Route-based nav content - no lazy loading needed
if (pathname === '/games/matching' || pathname.startsWith('/arcade') && pathname.includes('matching')) {
return (
<h1 style={{
fontSize: '18px',
fontWeight: 'bold',
background: 'linear-gradient(135deg, #60a5fa, #a78bfa, #f472b6)',
backgroundClip: 'text',
color: 'transparent',
margin: 0
}}>
🧩 Memory Pairs
</h1>
)
}
if (pathname === '/games/memory-quiz' || pathname.startsWith('/arcade') && pathname.includes('memory-quiz')) {
return (
<h1 style={{
fontSize: '18px',
fontWeight: 'bold',
background: 'linear-gradient(135deg, #60a5fa, #a78bfa, #f472b6)',
backgroundClip: 'text',
color: 'transparent',
margin: 0
}}>
🧠 Memory Lightning
</h1>
)
}
return null
}
export function AppNav({ children }: AppNavProps) {
const headersList = headers()
const pathname = headersList.get('x-pathname') || ''
// Use @nav slot content if available, otherwise fall back to route-based detection
const navContent = children || getNavContentForPath(pathname)
return <AppNavBar navSlot={navContent} />
}

View File

@ -0,0 +1,33 @@
'use client'
import React from 'react'
import { AppNavBar } from './AppNavBar'
interface PageWithNavProps {
navTitle?: string
navEmoji?: string
children: React.ReactNode
}
export function PageWithNav({ navTitle, navEmoji, children }: PageWithNavProps) {
// Create nav content if title is provided
const navContent = navTitle ? (
<h1 style={{
fontSize: '18px',
fontWeight: 'bold',
background: 'linear-gradient(135deg, #60a5fa, #a78bfa, #f472b6)',
backgroundClip: 'text',
color: 'transparent',
margin: 0
}}>
{navEmoji && `${navEmoji} `}{navTitle}
</h1>
) : null
return (
<>
<AppNavBar navSlot={navContent} />
{children}
</>
)
}