Files
breakpilot-lehrer/studio-v2/components/learn/ExerciseLayout.tsx
Benjamin Admin 693989c1a6 Add exercise explanations in all 26 languages
exerciseExplanations.ts: Match and Flashcard explanations translated
into DE, EN, TR, AR, UK, RU, PL, FR, ES, IT, PT, NL, RO, EL, BG,
HR, CS, HU, SV, DA, FI, SK, SL, LT, LV, ET.

Each exercise type gets a parent-facing explanation in the user's
selected language. No more German fallback for unsupported languages.

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

130 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: 2/3 left + 1/3 right */}
<div className="max-w-5xl mx-auto w-full px-6 py-6">
<div className="flex gap-0">
{/* 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 helper (1/3) — only for migrants */}
{isThirdLanguage && (
<div className="w-1/3 pl-6">
<div className="sticky top-20 space-y-4">
{/* Explanation card */}
{explanation && (
<div className={`rounded-2xl p-4 ${isDark ? 'bg-cyan-500/5 border border-cyan-400/20' : 'bg-cyan-50 border border-cyan-200'}`}>
<h3 className={`text-xs font-semibold mb-2 flex items-center gap-2 ${isDark ? 'text-cyan-300' : 'text-cyan-700'}`}>
<span className="text-base">💡</span>
{nativeLang.toUpperCase()} · {t('english')} · {t('german')}
</h3>
<p className={`text-xs leading-relaxed ${isDark ? 'text-white/60' : 'text-slate-600'}`}>
{explanation}
</p>
</div>
)}
{/* Native words */}
{nativeHelper}
</div>
</div>
)}
</div>
</div>
</>
)
}
export { explanations as exerciseExplanations }