Add central layout.tsx for /learn/* and /parent/* routes
Next.js route-level layouts provide Sidebar + gradient background automatically for all sub-pages. Individual pages no longer need their own wrapper divs or Sidebar imports. - learn/layout.tsx: Sidebar + purple gradient for all learning pages - parent/layout.tsx: Same for all parent portal pages - LearnLayout.tsx: Reusable component for other pages - Fixed broken <LearnLayout>}> artifacts from previous refactoring - Removed duplicate Sidebar/wrapper code from 9 sub-pages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -101,7 +101,7 @@ export default function FlashcardsPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen flex flex-col ${isDark ? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800' : 'bg-gradient-to-br from-slate-100 via-blue-50 to-cyan-100'}`}>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function ListenPage() {
|
|||||||
const currentItem = items[currentIndex]
|
const currentItem = items[currentIndex]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen flex flex-col ${isDark ? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800' : 'bg-gradient-to-br from-slate-100 via-green-50 to-emerald-100'}`}>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export default function MatchPage() {
|
|||||||
const matchedTotal = round * 6 + matched.size
|
const matchedTotal = round * 6 + matched.size
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen flex flex-col ${isDark ? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800' : 'bg-gradient-to-br from-slate-100 via-indigo-50 to-violet-100'}`}>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export default function PronouncePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen flex flex-col ${isDark ? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800' : 'bg-gradient-to-br from-slate-100 via-rose-50 to-red-100'}`}>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default function QuizPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen flex flex-col ${isDark ? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800' : 'bg-gradient-to-br from-slate-100 via-blue-50 to-cyan-100'}`}>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default function StoryPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen flex flex-col ${isDark ? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800' : 'bg-gradient-to-br from-slate-100 via-amber-50 to-orange-100'}`}>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export default function TypePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen flex flex-col ${isDark ? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800' : 'bg-gradient-to-br from-slate-100 via-blue-50 to-cyan-100'}`}>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
<div className="max-w-2xl mx-auto px-6 py-4 flex items-center justify-between">
|
||||||
|
|||||||
28
studio-v2/app/learn/layout.tsx
Normal file
28
studio-v2/app/learn/layout.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Sidebar } from '@/components/Sidebar'
|
||||||
|
import { useTheme } from '@/lib/ThemeContext'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared layout for ALL /learn/* pages.
|
||||||
|
* Provides: Sidebar + gradient background + flex container.
|
||||||
|
* Individual pages only need to render their content.
|
||||||
|
*/
|
||||||
|
export default function LearnLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
const { isDark } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`min-h-screen flex relative overflow-hidden ${
|
||||||
|
isDark
|
||||||
|
? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800'
|
||||||
|
: 'bg-gradient-to-br from-slate-100 via-blue-50 to-cyan-100'
|
||||||
|
}`}>
|
||||||
|
<div className="relative z-10 p-4 flex-shrink-0">
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 flex flex-col relative z-10 overflow-y-auto">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'
|
|||||||
import { useTheme } from '@/lib/ThemeContext'
|
import { useTheme } from '@/lib/ThemeContext'
|
||||||
import { useLanguage } from '@/lib/LanguageContext'
|
import { useLanguage } from '@/lib/LanguageContext'
|
||||||
import type { Language } from '@/lib/i18n'
|
import type { Language } from '@/lib/i18n'
|
||||||
|
import { LearnLayout } from '@/components/learn/LearnLayout'
|
||||||
|
|
||||||
interface LangOption {
|
interface LangOption {
|
||||||
code: string
|
code: string
|
||||||
|
|||||||
27
studio-v2/app/parent/layout.tsx
Normal file
27
studio-v2/app/parent/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Sidebar } from '@/components/Sidebar'
|
||||||
|
import { useTheme } from '@/lib/ThemeContext'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared layout for ALL /parent/* pages.
|
||||||
|
* Same design as learn layout — Sidebar + gradient.
|
||||||
|
*/
|
||||||
|
export default function ParentLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
const { isDark } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`min-h-screen flex relative overflow-hidden ${
|
||||||
|
isDark
|
||||||
|
? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800'
|
||||||
|
: 'bg-gradient-to-br from-slate-100 via-blue-50 to-cyan-100'
|
||||||
|
}`}>
|
||||||
|
<div className="relative z-10 p-4 flex-shrink-0">
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 flex flex-col relative z-10 overflow-y-auto">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -46,9 +46,7 @@ export default function ParentPage() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`min-h-screen ${isDark ? 'bg-gradient-to-br from-slate-900 via-blue-900 to-indigo-900' : 'bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50'}`}
|
<div dir={language === 'ar' ? 'rtl' : 'ltr'}>
|
||||||
dir={language === 'ar' ? 'rtl' : 'ltr'}>
|
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-lg mx-auto px-6 py-5">
|
<div className="max-w-lg mx-auto px-6 py-5">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useParams, useRouter } from 'next/navigation'
|
|||||||
import { useTheme } from '@/lib/ThemeContext'
|
import { useTheme } from '@/lib/ThemeContext'
|
||||||
import { useLanguage } from '@/lib/LanguageContext'
|
import { useLanguage } from '@/lib/LanguageContext'
|
||||||
import { AudioButton } from '@/components/learn/AudioButton'
|
import { AudioButton } from '@/components/learn/AudioButton'
|
||||||
|
import { LearnLayout } from '@/components/learn/LearnLayout'
|
||||||
|
|
||||||
interface QAItem {
|
interface QAItem {
|
||||||
id: string; question: string; answer: string
|
id: string; question: string; answer: string
|
||||||
@@ -65,7 +66,7 @@ export default function ParentQuizPage() {
|
|||||||
const nativeTranslation = currentItem?.translations?.[language]?.text || ''
|
const nativeTranslation = currentItem?.translations?.[language]?.text || ''
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <div className={`min-h-screen flex items-center justify-center ${isDark ? 'bg-gradient-to-br from-slate-900 via-blue-900 to-indigo-900' : 'bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50'}`}>
|
return <LearnLayout>
|
||||||
<div className="w-8 h-8 border-4 border-blue-400 border-t-transparent rounded-full animate-spin" />
|
<div className="w-8 h-8 border-4 border-blue-400 border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
33
studio-v2/components/learn/LearnLayout.tsx
Normal file
33
studio-v2/components/learn/LearnLayout.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { useTheme } from '@/lib/ThemeContext'
|
||||||
|
import { Sidebar } from '@/components/Sidebar'
|
||||||
|
|
||||||
|
interface LearnLayoutProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
gradient?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared layout for all learn sub-pages.
|
||||||
|
* Adds Sidebar + consistent gradient background.
|
||||||
|
*/
|
||||||
|
export function LearnLayout({ children, gradient }: LearnLayoutProps) {
|
||||||
|
const { isDark } = useTheme()
|
||||||
|
|
||||||
|
const bg = gradient || (isDark
|
||||||
|
? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800'
|
||||||
|
: 'bg-gradient-to-br from-slate-100 via-blue-50 to-cyan-100')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`min-h-screen flex relative overflow-hidden ${bg}`}>
|
||||||
|
<div className="relative z-10 p-4 flex-shrink-0">
|
||||||
|
<Sidebar />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 flex flex-col relative z-10 overflow-y-auto">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user