Files
breakpilot-lehrer/studio-v2/components/learn/ExerciseLayout.tsx
Benjamin Admin d14826b199 Fix: Build error + explanation above exercise + aligned columns + impressum link
1. Removed stale 'explanations' export that broke build
2. Explanation banner now spans full width ABOVE the 2/3+1/3 layout
   so exercise cards and native words start at the same height
3. Both columns use items-start for visual alignment
4. Impressum link added at bottom of learn layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 14:54:12 +02:00

133 lines
4.8 KiB
TypeScript

'use client'
import React from 'react'
import { useTheme } from '@/lib/ThemeContext'
import { useNativeLanguage } from '@/lib/useNativeLanguage'
import { exerciseExplanations } from '@/lib/exerciseExplanations'
interface ExerciseLayoutProps {
/** Main exercise content (2/3 left) */
children: React.ReactNode
/** Native language helper content for the right panel */
nativeHelper?: React.ReactNode
/** Explanation text for parents (shown above native words) */
exerciseExplanation?: string
/** Exercise type key for auto-explanation lookup (e.g. 'match', 'flashcards') */
exerciseType?: string
/** Title for the exercise in the header */
title: string
/** Progress: current / total */
progress?: { current: number; total: number }
/** Back button handler */
onBack?: () => void
/** Score display */
score?: React.ReactNode
}
// Explanations imported from exerciseExplanations.ts (26 languages each)
/**
* Standard exercise layout: 2/3 work area (left) + 1/3 native helper (right).
* The right panel only appears for non-DE/EN speakers.
*/
export function ExerciseLayout({
children,
nativeHelper,
exerciseExplanation,
exerciseType,
title,
progress,
onBack,
score,
}: ExerciseLayoutProps) {
const { isDark } = useTheme()
const { nativeLang, isThirdLanguage, t } = useNativeLanguage()
const glassCard = isDark
? 'bg-white/10 backdrop-blur-xl border border-white/10'
: 'bg-white/80 backdrop-blur-xl border border-black/5'
const typeKey = exerciseType || title.toLowerCase()
const explanation = exerciseExplanation
|| exerciseExplanations[typeKey]?.[nativeLang]
|| exerciseExplanations[typeKey]?.['en']
|| exerciseExplanations[typeKey]?.['de']
|| ''
return (
<>
{/* Header */}
<div className={`${glassCard} border-0 border-b`}>
<div className="max-w-5xl mx-auto px-6 py-3 flex items-center justify-between">
{onBack ? (
<button onClick={onBack} className={`flex items-center gap-2 text-sm ${isDark ? 'text-white/60 hover:text-white' : 'text-slate-500 hover:text-slate-900'}`}>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" /></svg>
{t('back')}
</button>
) : <span />}
<h1 className={`text-lg font-bold ${isDark ? 'text-white' : 'text-slate-900'}`}>{title}</h1>
{score || <span />}
</div>
</div>
{/* Progress bar */}
{progress && (
<div className="max-w-5xl mx-auto w-full px-6 pt-3">
<div className="flex items-center gap-3">
<div className={`flex-1 h-2 rounded-full ${isDark ? 'bg-white/10' : 'bg-slate-200'}`}>
<div className="h-full rounded-full bg-gradient-to-r from-indigo-500 to-violet-500 transition-all"
style={{ width: `${(progress.current / Math.max(progress.total, 1)) * 100}%` }} />
</div>
<span className={`text-xs font-medium tabular-nums ${isDark ? 'text-white/50' : 'text-slate-400'}`}>
{progress.current}/{progress.total}
</span>
</div>
</div>
)}
{/* Main content */}
<div className="max-w-5xl mx-auto w-full px-6 py-6">
{/* Explanation banner — full width above exercise, only for non-DE/EN */}
{isThirdLanguage && explanation && (
<div className={`rounded-2xl p-4 mb-5 ${isDark ? 'bg-cyan-500/5 border border-cyan-400/20' : 'bg-cyan-50 border border-cyan-200'}`}>
<div className="flex items-start gap-3">
<span className="text-lg">💡</span>
<div>
<h3 className={`text-xs font-semibold mb-1 ${isDark ? 'text-cyan-300' : 'text-cyan-700'}`}>
{nativeLang.toUpperCase()} · {t('english')} · {t('german')}
</h3>
<p className={`text-xs leading-relaxed ${isDark ? 'text-white/60' : 'text-slate-600'}`}>
{explanation}
</p>
</div>
</div>
</div>
)}
{/* 2/3 left + 1/3 right — both start at same height */}
<div className="flex gap-0 items-start">
{/* Left: Exercise area (2/3 or full) */}
<div className={isThirdLanguage ? 'w-2/3 pr-6' : 'w-full'}>
{children}
</div>
{/* Divider line */}
{isThirdLanguage && (
<div className={`w-px self-stretch ${isDark ? 'bg-white/10' : 'bg-slate-200'}`} />
)}
{/* Right: Native language words (1/3) */}
{isThirdLanguage && (
<div className="w-1/3 pl-6">
<div className="sticky top-20">
{nativeHelper}
</div>
</div>
)}
</div>
</div>
</>
)
}