Make /learn the central landing page for parents + students
Non-DE/EN users (parents) see a guide panel explaining: - What this page is (vocabulary exercises from teacher) - How each exercise type works (cards, quiz, listen, speak) - All text in parent's native language (TR/AR/UK/RU/PL) German/English users see the original layout without the guide. This is the single entry point — no need to duplicate explanations in every exercise sub-page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { useTheme } from '@/lib/ThemeContext'
|
import { useTheme } from '@/lib/ThemeContext'
|
||||||
import { UnitCard } from '@/components/learn/UnitCard'
|
import { UnitCard } from '@/components/learn/UnitCard'
|
||||||
|
import { useNativeLanguage } from '@/lib/useNativeLanguage'
|
||||||
|
|
||||||
interface LearningUnit {
|
interface LearningUnit {
|
||||||
id: string
|
id: string
|
||||||
@@ -16,12 +17,87 @@ interface LearningUnit {
|
|||||||
created_at: string
|
created_at: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function getApiBase() {
|
// Parent guide translations
|
||||||
return '' // Same-origin proxy via /api/learning-units/...
|
const guide: Record<string, Record<string, string>> = {
|
||||||
|
welcome: {
|
||||||
|
de: 'Willkommen bei den Lernmodulen!',
|
||||||
|
en: 'Welcome to the learning modules!',
|
||||||
|
tr: 'Ogrenme modullerine hos geldiniz!',
|
||||||
|
ar: '\u0645\u0631\u062d\u0628\u0627 \u0628\u0643 \u0641\u064a \u0648\u062d\u062f\u0627\u062a \u0627\u0644\u062a\u0639\u0644\u0645!',
|
||||||
|
uk: '\u041b\u0430\u0441\u043a\u0430\u0432\u043e \u043f\u0440\u043e\u0441\u0438\u043c\u043e \u0434\u043e \u043d\u0430\u0432\u0447\u0430\u043b\u044c\u043d\u0438\u0445 \u043c\u043e\u0434\u0443\u043b\u0456\u0432!',
|
||||||
|
ru: '\u0414\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c \u0432 \u0443\u0447\u0435\u0431\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438!',
|
||||||
|
pl: 'Witamy w modulach do nauki!',
|
||||||
|
},
|
||||||
|
what_is_this: {
|
||||||
|
de: 'Was ist das?',
|
||||||
|
en: 'What is this?',
|
||||||
|
tr: 'Bu nedir?',
|
||||||
|
ar: '\u0645\u0627 \u0647\u0630\u0627\u061f',
|
||||||
|
uk: '\u0429\u043e \u0446\u0435?',
|
||||||
|
ru: '\u0427\u0442\u043e \u044d\u0442\u043e?',
|
||||||
|
pl: 'Co to jest?',
|
||||||
|
},
|
||||||
|
explanation: {
|
||||||
|
de: 'Der Lehrer Ihres Kindes hat Vokabeln zusammengestellt, die Ihr Kind lernen muss. Hier koennen Sie Ihrem Kind beim Ueben helfen — auch wenn Sie selbst kein Deutsch oder Englisch sprechen. Jedes Wort wird in Ihrer Sprache angezeigt.',
|
||||||
|
en: 'Your child\'s teacher has prepared vocabulary that your child needs to learn. Here you can help your child practice — even if you don\'t speak German or English. Every word is shown in your language.',
|
||||||
|
tr: 'Cocugunuzun ogretmeni, cocugunuzun ogrenmesi gereken kelimeleri hazirladi. Burada cocugunuzun pratik yapmasina yardimci olabilirsiniz — Almanca veya Ingilizce bilmeseniz bile. Her kelime sizin dilinizde gosterilir.',
|
||||||
|
ar: '\u0623\u0639\u062f \u0645\u0639\u0644\u0645 \u0637\u0641\u0644\u0643 \u0645\u0641\u0631\u062f\u0627\u062a \u064a\u062d\u062a\u0627\u062c \u0637\u0641\u0644\u0643 \u0644\u062a\u0639\u0644\u0645\u0647\u0627. \u0647\u0646\u0627 \u064a\u0645\u0643\u0646\u0643 \u0645\u0633\u0627\u0639\u062f\u0629 \u0637\u0641\u0644\u0643 \u0639\u0644\u0649 \u0627\u0644\u062a\u062f\u0631\u064a\u0628 \u2014 \u062d\u062a\u0649 \u0644\u043e \u0644\u0645 \u062a\u062a\u062d\u062f\u062b \u0627\u0644\u0623\u0644\u0645\u0627\u0646\u064a\u0629 \u0623\u043e \u0627\u0644\u0625\u0646\u062c\u0644\u064a\u0632\u064a\u0629. \u0643\u0644 \u0643\u0644\u0645\u0629 \u062a\u0638\u0647\u0631 \u0628\u0644\u063a\u062a\u0643.',
|
||||||
|
uk: '\u0412\u0447\u0438\u0442\u0435\u043b\u044c \u0432\u0430\u0448\u043e\u0457 \u0434\u0438\u0442\u0438\u043d\u0438 \u043f\u0456\u0434\u0433\u043e\u0442\u0443\u0432\u0430\u0432 \u0441\u043b\u043e\u0432\u0430, \u044f\u043a\u0456 \u0434\u0438\u0442\u0438\u043d\u0430 \u043f\u043e\u0432\u0438\u043d\u043d\u0430 \u0432\u0438\u0432\u0447\u0438\u0442\u0438. \u0422\u0443\u0442 \u0432\u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u043f\u043e\u043c\u043e\u0433\u0442\u0438 \u0434\u0438\u0442\u0438\u043d\u0456 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0443\u0432\u0430\u0442\u0438 \u2014 \u043d\u0430\u0432\u0456\u0442\u044c \u044f\u043a\u0449\u043e \u0432\u0438 \u043d\u0435 \u0440\u043e\u0437\u043c\u043e\u0432\u043b\u044f\u0454\u0442\u0435 \u043d\u0456\u043c\u0435\u0446\u044c\u043a\u043e\u044e \u0447\u0438 \u0430\u043d\u0433\u043b\u0456\u0439\u0441\u044c\u043a\u043e\u044e. \u041a\u043e\u0436\u043d\u0435 \u0441\u043b\u043e\u0432\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u0432\u0430\u0448\u043e\u044e \u043c\u043e\u0432\u043e\u044e.',
|
||||||
|
ru: '\u0423\u0447\u0438\u0442\u0435\u043b\u044c \u0432\u0430\u0448\u0435\u0433\u043e \u0440\u0435\u0431\u0435\u043d\u043a\u0430 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b \u0441\u043b\u043e\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u0431\u0435\u043d\u043e\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0443\u0447\u0438\u0442\u044c. \u0417\u0434\u0435\u0441\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043c\u043e\u0447\u044c \u0440\u0435\u0431\u0435\u043d\u043a\u0443 \u0442\u0440\u0435\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u2014 \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435 \u043f\u043e-\u043d\u0435\u043c\u0435\u0446\u043a\u0438 \u0438\u043b\u0438 \u043f\u043e-\u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u0438. \u041a\u0430\u0436\u0434\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u043d\u0430 \u0432\u0430\u0448\u0435\u043c \u044f\u0437\u044b\u043a\u0435.',
|
||||||
|
pl: 'Nauczyciel Twojego dziecka przygotowal slowka, ktorych dziecko musi sie nauczyc. Tutaj mozesz pomoc dziecku cwczyc — nawet jesli nie mowisz po niemiecku ani angielsku. Kazde slowo jest pokazane w Twoim jezyku.',
|
||||||
|
},
|
||||||
|
exercises_explained: {
|
||||||
|
de: 'Waehlen Sie eine Uebung:',
|
||||||
|
en: 'Choose an exercise:',
|
||||||
|
tr: 'Bir alistirma secin:',
|
||||||
|
ar: '\u0627\u062e\u062a\u0631 \u062a\u0645\u0631\u064a\u0646\u0627:',
|
||||||
|
uk: '\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0432\u043f\u0440\u0430\u0432\u0443:',
|
||||||
|
ru: '\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0443\u043f\u0440\u0430\u0436\u043d\u0435\u043d\u0438\u0435:',
|
||||||
|
pl: 'Wybierz cwiczenie:',
|
||||||
|
},
|
||||||
|
exercise_cards: {
|
||||||
|
de: 'Karteikarten — Wort umdrehen und pruefen',
|
||||||
|
tr: 'Kartlar — Kelimeyi cevir ve kontrol et',
|
||||||
|
ar: '\u0628\u0637\u0627\u0642\u0627\u062a \u2014 \u0627\u0642\u0644\u0628 \u0627\u0644\u0643\u0644\u0645\u0629 \u0648\u062a\u062d\u0642\u0642',
|
||||||
|
uk: '\u041a\u0430\u0440\u0442\u043a\u0438 \u2014 \u041f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0456\u0442\u044c \u0441\u043b\u043e\u0432\u043e \u0456 \u043f\u0435\u0440\u0435\u0432\u0456\u0440\u0442\u0435',
|
||||||
|
ru: '\u041a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u2014 \u041f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0438\u0442\u0435 \u0441\u043b\u043e\u0432\u043e \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435',
|
||||||
|
pl: 'Fiszki — Odwroc slowo i sprawdz',
|
||||||
|
en: 'Flashcards — Flip the word and check',
|
||||||
|
},
|
||||||
|
exercise_quiz: {
|
||||||
|
de: 'Quiz — Richtige Uebersetzung aus 4 Optionen waehlen',
|
||||||
|
tr: 'Quiz — 4 secenekten dogru ceviriyi sec',
|
||||||
|
ar: '\u0627\u062e\u062a\u0628\u0627\u0631 \u2014 \u0627\u062e\u062a\u0631 \u0627\u0644\u062a\u0631\u062c\u0645\u0629 \u0627\u0644\u0635\u062d\u064a\u062d\u0629 \u0645\u0646 4 \u062e\u064a\u0627\u0631\u0627\u062a',
|
||||||
|
uk: '\u0422\u0435\u0441\u0442 \u2014 \u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0438\u0439 \u043f\u0435\u0440\u0435\u043a\u043b\u0430\u0434 \u0437 4 \u0432\u0430\u0440\u0456\u0430\u043d\u0442\u0456\u0432',
|
||||||
|
ru: '\u0422\u0435\u0441\u0442 \u2014 \u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0438\u0437 4 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432',
|
||||||
|
pl: 'Quiz — Wybierz poprawne tlumaczenie z 4 opcji',
|
||||||
|
en: 'Quiz — Choose correct translation from 4 options',
|
||||||
|
},
|
||||||
|
exercise_listen: {
|
||||||
|
de: 'Hoeren — Wort hoeren und Uebersetzung waehlen',
|
||||||
|
tr: 'Dinleme — Kelimeyi dinle ve ceviriyi sec',
|
||||||
|
ar: '\u0627\u0633\u062a\u0645\u0627\u0639 \u2014 \u0627\u0633\u062a\u0645\u0639 \u0644\u0644\u0643\u0644\u0645\u0629 \u0648\u0627\u062e\u062a\u0631 \u0627\u0644\u062a\u0631\u062c\u0645\u0629',
|
||||||
|
uk: '\u0421\u043b\u0443\u0445\u0430\u043d\u043d\u044f \u2014 \u041f\u043e\u0441\u043b\u0443\u0445\u0430\u0439 \u0441\u043b\u043e\u0432\u043e \u0456 \u0432\u0438\u0431\u0435\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u0430\u0434',
|
||||||
|
ru: '\u0421\u043b\u0443\u0448\u0430\u043d\u0438\u0435 \u2014 \u041f\u043e\u0441\u043b\u0443\u0448\u0430\u0439\u0442\u0435 \u0441\u043b\u043e\u0432\u043e \u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434',
|
||||||
|
pl: 'Sluchanie — Posluchaj slowa i wybierz tlumaczenie',
|
||||||
|
en: 'Listening — Hear the word and choose the translation',
|
||||||
|
},
|
||||||
|
exercise_speak: {
|
||||||
|
de: 'Sprechen — Wort hoeren und nachsprechen',
|
||||||
|
tr: 'Konusma — Kelimeyi dinle ve tekrar et',
|
||||||
|
ar: '\u062a\u062d\u062f\u062b \u2014 \u0627\u0633\u062a\u0645\u0639 \u0644\u0644\u0643\u0644\u0645\u0629 \u0648\u0643\u0631\u0631\u0647\u0627',
|
||||||
|
uk: '\u0413\u043e\u0432\u043e\u0440\u0456\u043d\u043d\u044f \u2014 \u041f\u043e\u0441\u043b\u0443\u0445\u0430\u0439 \u0441\u043b\u043e\u0432\u043e \u0456 \u043f\u043e\u0432\u0442\u043e\u0440\u0438',
|
||||||
|
ru: '\u0413\u043e\u0432\u043e\u0440\u0435\u043d\u0438\u0435 \u2014 \u041f\u043e\u0441\u043b\u0443\u0448\u0430\u0439\u0442\u0435 \u0441\u043b\u043e\u0432\u043e \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435',
|
||||||
|
pl: 'Mowienie — Posluchaj slowa i powtorz',
|
||||||
|
en: 'Speaking — Hear the word and repeat it',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getApiBase() { return '' }
|
||||||
|
|
||||||
export default function LearnPage() {
|
export default function LearnPage() {
|
||||||
const { isDark } = useTheme()
|
const { isDark } = useTheme()
|
||||||
|
const { t, nativeLang, isThirdLanguage } = useNativeLanguage()
|
||||||
const [units, setUnits] = useState<LearningUnit[]>([])
|
const [units, setUnits] = useState<LearningUnit[]>([])
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
@@ -30,17 +106,16 @@ export default function LearnPage() {
|
|||||||
? 'bg-white/10 backdrop-blur-xl border border-white/10'
|
? 'bg-white/10 backdrop-blur-xl border border-white/10'
|
||||||
: 'bg-white/80 backdrop-blur-xl border border-black/5'
|
: 'bg-white/80 backdrop-blur-xl border border-black/5'
|
||||||
|
|
||||||
useEffect(() => {
|
const g = (key: string) => guide[key]?.[nativeLang] || guide[key]?.['de'] || key
|
||||||
loadUnits()
|
|
||||||
}, [])
|
useEffect(() => { loadUnits() }, [])
|
||||||
|
|
||||||
const loadUnits = async () => {
|
const loadUnits = async () => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`${getApiBase()}/api/learning-units/`)
|
const resp = await fetch(`${getApiBase()}/api/learning-units/`)
|
||||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
||||||
const data = await resp.json()
|
setUnits(await resp.json())
|
||||||
setUnits(data)
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message)
|
setError(err.message)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -51,88 +126,97 @@ export default function LearnPage() {
|
|||||||
const handleDelete = async (unitId: string) => {
|
const handleDelete = async (unitId: string) => {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`${getApiBase()}/api/learning-units/${unitId}`, { method: 'DELETE' })
|
const resp = await fetch(`${getApiBase()}/api/learning-units/${unitId}`, { method: 'DELETE' })
|
||||||
if (resp.ok) {
|
if (resp.ok) setUnits(prev => prev.filter(u => u.id !== unitId))
|
||||||
setUnits((prev) => prev.filter((u) => u.id !== unitId))
|
} catch {}
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Delete failed:', err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className={`${glassCard} border-0 border-b`}>
|
<div className={`${glassCard} border-0 border-b`}>
|
||||||
<div className="max-w-5xl mx-auto px-6 py-4">
|
<div className="max-w-5xl mx-auto px-6 py-4">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${
|
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-blue-500/30' : 'bg-blue-200'}`}>
|
||||||
isDark ? 'bg-blue-500/30' : 'bg-blue-200'
|
<svg className={`w-6 h-6 ${isDark ? 'text-blue-300' : 'text-blue-600'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
}`}>
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
||||||
<svg className={`w-6 h-6 ${isDark ? 'text-blue-300' : 'text-blue-600'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
</svg>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
</div>
|
||||||
</svg>
|
<div>
|
||||||
</div>
|
<h1 className={`text-xl font-bold ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
||||||
<div>
|
{isThirdLanguage ? g('welcome') : 'Meine Lernmodule'}
|
||||||
<h1 className={`text-xl font-bold ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
</h1>
|
||||||
Meine Lernmodule
|
<p className={`text-sm ${isDark ? 'text-white/60' : 'text-slate-500'}`}>
|
||||||
</h1>
|
{isThirdLanguage
|
||||||
<p className={`text-sm ${isDark ? 'text-white/60' : 'text-slate-500'}`}>
|
? g('exercises_explained')
|
||||||
Karteikarten, Quiz und Lueckentexte aus deinen Vokabeln
|
: 'Karteikarten, Quiz und Lueckentexte aus deinen Vokabeln'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
<div className="max-w-5xl mx-auto w-full px-6 py-6">
|
||||||
<div className="max-w-5xl mx-auto w-full px-6 py-6">
|
{/* Parent Guide Panel — only for non-DE/EN users */}
|
||||||
{isLoading && (
|
{isThirdLanguage && (
|
||||||
<div className="flex items-center justify-center py-20">
|
<div className={`${glassCard} rounded-2xl p-6 mb-6`}>
|
||||||
<div className={`w-8 h-8 border-4 ${isDark ? 'border-blue-400' : 'border-blue-600'} border-t-transparent rounded-full animate-spin`} />
|
<h3 className={`text-sm font-semibold mb-2 ${isDark ? 'text-cyan-300' : 'text-cyan-700'}`}>
|
||||||
</div>
|
{g('what_is_this')}
|
||||||
)}
|
</h3>
|
||||||
|
<p className={`text-sm leading-relaxed mb-4 ${isDark ? 'text-white/70' : 'text-slate-600'}`}>
|
||||||
|
{g('explanation')}
|
||||||
|
</p>
|
||||||
|
|
||||||
{error && (
|
<div className={`border-t pt-3 space-y-1.5 ${isDark ? 'border-white/10' : 'border-slate-200'}`}>
|
||||||
<div className={`${glassCard} rounded-2xl p-6 text-center`}>
|
<p className={`text-xs ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
|
||||||
<p className={`${isDark ? 'text-red-300' : 'text-red-600'}`}>Fehler: {error}</p>
|
{g('exercises_explained')}
|
||||||
<button onClick={loadUnits} className="mt-3 px-4 py-2 rounded-xl bg-blue-500 text-white text-sm">
|
|
||||||
Erneut versuchen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isLoading && !error && units.length === 0 && (
|
|
||||||
<div className={`${glassCard} rounded-2xl p-12 text-center`}>
|
|
||||||
<div className={`w-16 h-16 mx-auto mb-4 rounded-2xl flex items-center justify-center ${
|
|
||||||
isDark ? 'bg-blue-500/20' : 'bg-blue-100'
|
|
||||||
}`}>
|
|
||||||
<svg className={`w-8 h-8 ${isDark ? 'text-blue-300' : 'text-blue-600'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h3 className={`text-lg font-semibold mb-2 ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
|
||||||
Noch keine Lernmodule
|
|
||||||
</h3>
|
|
||||||
<p className={`text-sm mb-4 ${isDark ? 'text-white/60' : 'text-slate-500'}`}>
|
|
||||||
Scanne eine Schulbuchseite im Vokabel-Arbeitsblatt Generator und klicke "Lernmodule generieren".
|
|
||||||
</p>
|
</p>
|
||||||
<a
|
<p className={`text-xs ${isDark ? 'text-white/60' : 'text-slate-500'}`}>🃏 {g('exercise_cards')}</p>
|
||||||
href="/vocab-worksheet"
|
<p className={`text-xs ${isDark ? 'text-white/60' : 'text-slate-500'}`}>❓ {g('exercise_quiz')}</p>
|
||||||
className="inline-block px-6 py-3 rounded-xl bg-gradient-to-r from-blue-500 to-cyan-500 text-white font-medium hover:shadow-lg transition-all"
|
<p className={`text-xs ${isDark ? 'text-white/60' : 'text-slate-500'}`}>🔊 {g('exercise_listen')}</p>
|
||||||
>
|
<p className={`text-xs ${isDark ? 'text-white/60' : 'text-slate-500'}`}>🎤 {g('exercise_speak')}</p>
|
||||||
Zum Vokabel-Scanner
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isLoading && units.length > 0 && (
|
{isLoading && (
|
||||||
<div className="grid gap-4">
|
<div className="flex items-center justify-center py-20">
|
||||||
{units.map((unit) => (
|
<div className={`w-8 h-8 border-4 ${isDark ? 'border-blue-400' : 'border-blue-600'} border-t-transparent rounded-full animate-spin`} />
|
||||||
<UnitCard key={unit.id} unit={unit} isDark={isDark} glassCard={glassCard} onDelete={handleDelete} />
|
</div>
|
||||||
))}
|
)}
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className={`${glassCard} rounded-2xl p-6 text-center`}>
|
||||||
|
<p className={isDark ? 'text-red-300' : 'text-red-600'}>Fehler: {error}</p>
|
||||||
|
<button onClick={loadUnits} className="mt-3 px-4 py-2 rounded-xl bg-blue-500 text-white text-sm">
|
||||||
|
{t('again')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLoading && !error && units.length === 0 && (
|
||||||
|
<div className={`${glassCard} rounded-2xl p-12 text-center`}>
|
||||||
|
<div className={`w-16 h-16 mx-auto mb-4 rounded-2xl flex items-center justify-center ${isDark ? 'bg-blue-500/20' : 'bg-blue-100'}`}>
|
||||||
|
<svg className={`w-8 h-8 ${isDark ? 'text-blue-300' : 'text-blue-600'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<h3 className={`text-lg font-semibold mb-2 ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
||||||
</div>
|
{isThirdLanguage ? g('welcome') : 'Noch keine Lernmodule'}
|
||||||
|
</h3>
|
||||||
|
<a href="/vocabulary" className="inline-block mt-3 px-6 py-3 rounded-xl bg-gradient-to-r from-blue-500 to-cyan-500 text-white font-medium hover:shadow-lg transition-all">
|
||||||
|
{isThirdLanguage ? g('exercises_explained') : 'Zum Woerterbuch'}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLoading && units.length > 0 && (
|
||||||
|
<div className="grid gap-4">
|
||||||
|
{units.map(unit => (
|
||||||
|
<UnitCard key={unit.id} unit={unit} isDark={isDark} glassCard={glassCard} onDelete={handleDelete} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user