fix: vocab worksheet — wider table, show original pages, better layout
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 24s
CI / test-go-edu-search (push) Successful in 24s
CI / test-python-klausur (push) Failing after 1m44s
CI / test-python-agent-core (push) Successful in 15s
CI / test-nodejs-website (push) Successful in 17s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 24s
CI / test-go-edu-search (push) Successful in 24s
CI / test-python-klausur (push) Failing after 1m44s
CI / test-python-agent-core (push) Successful in 15s
CI / test-nodejs-website (push) Successful in 17s
- Swap from 3/5-2/5 grid to 1/3-2/3 flexbox (original left, table right) - Table uses 3 equal 1fr columns for EN/DE/example instead of cramped 13-col grid - Full viewport height minus header (calc(100vh - 240px)) for more visible rows - Show only processed pages in original preview (filtered by selectedPages) - Remove per-row insert buttons to reduce vertical noise - Compact row spacing (py-1.5) to fit ~15+ rows without scrolling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1417,10 +1417,55 @@ export default function VocabWorksheetPage() {
|
|||||||
|
|
||||||
{/* Vocabulary Tab */}
|
{/* Vocabulary Tab */}
|
||||||
{session && activeTab === 'vocabulary' && (
|
{session && activeTab === 'vocabulary' && (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-5 gap-6">
|
<div className="flex flex-col lg:flex-row gap-4" style={{ height: 'calc(100vh - 240px)', minHeight: '500px' }}>
|
||||||
{/* Left: Vocabulary List (3/5) */}
|
{/* Left: Original pages (scrollable, 1/3 width) */}
|
||||||
<div className={`${glassCard} rounded-2xl p-6 lg:col-span-3`}>
|
<div className={`${glassCard} rounded-2xl p-4 lg:w-1/3 flex flex-col overflow-hidden`}>
|
||||||
<div className="flex items-center justify-between mb-4">
|
<h2 className={`text-sm font-semibold mb-3 flex-shrink-0 ${isDark ? 'text-white/70' : 'text-slate-600'}`}>
|
||||||
|
Original ({(() => { const pp = selectedPages.length > 0 ? selectedPages : [...new Set(vocabulary.map(v => (v.source_page || 1) - 1))]; return pp.length; })()} Seiten)
|
||||||
|
</h2>
|
||||||
|
<div className="flex-1 overflow-y-auto space-y-3">
|
||||||
|
{(() => {
|
||||||
|
const processedPageIndices = selectedPages.length > 0
|
||||||
|
? selectedPages
|
||||||
|
: [...new Set(vocabulary.map(v => (v.source_page || 1) - 1))].sort((a, b) => a - b)
|
||||||
|
const thumbsToShow = processedPageIndices
|
||||||
|
.filter(idx => idx >= 0 && idx < pagesThumbnails.length)
|
||||||
|
.map(idx => ({ idx, src: pagesThumbnails[idx] }))
|
||||||
|
|
||||||
|
if (thumbsToShow.length > 0) {
|
||||||
|
return thumbsToShow.map(({ idx, src }) => (
|
||||||
|
<div key={idx} className={`relative rounded-xl overflow-hidden border ${isDark ? 'border-white/10' : 'border-black/10'}`}>
|
||||||
|
<div className={`absolute top-2 left-2 px-2 py-0.5 rounded-lg text-xs font-medium ${isDark ? 'bg-black/60 text-white' : 'bg-white/90 text-slate-700'}`}>
|
||||||
|
S. {idx + 1}
|
||||||
|
</div>
|
||||||
|
<img src={src} alt={`Seite ${idx + 1}`} className="w-full h-auto" />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
if (uploadedImage) {
|
||||||
|
return (
|
||||||
|
<div className={`relative rounded-xl overflow-hidden border ${isDark ? 'border-white/10' : 'border-black/10'}`}>
|
||||||
|
<img src={uploadedImage} alt="Arbeitsblatt" className="w-full h-auto" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={`flex-1 flex items-center justify-center py-12 ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
|
||||||
|
<div className="text-center">
|
||||||
|
<svg className="w-12 h-12 mx-auto mb-2 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
<p className="text-xs">Kein Bild verfuegbar</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right: Vocabulary table (2/3 width) */}
|
||||||
|
<div className={`${glassCard} rounded-2xl p-4 lg:w-2/3 flex flex-col overflow-hidden`}>
|
||||||
|
<div className="flex items-center justify-between mb-3 flex-shrink-0">
|
||||||
<h2 className={`text-lg font-semibold ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
<h2 className={`text-lg font-semibold ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
||||||
Vokabeln ({vocabulary.length})
|
Vokabeln ({vocabulary.length})
|
||||||
</h2>
|
</h2>
|
||||||
@@ -1436,9 +1481,9 @@ export default function VocabWorksheetPage() {
|
|||||||
|
|
||||||
{/* Error messages for failed pages */}
|
{/* Error messages for failed pages */}
|
||||||
{processingErrors.length > 0 && (
|
{processingErrors.length > 0 && (
|
||||||
<div className={`rounded-xl p-4 mb-4 ${isDark ? 'bg-orange-500/20 text-orange-200 border border-orange-500/30' : 'bg-orange-100 text-orange-700 border border-orange-200'}`}>
|
<div className={`rounded-xl p-3 mb-3 flex-shrink-0 ${isDark ? 'bg-orange-500/20 text-orange-200 border border-orange-500/30' : 'bg-orange-100 text-orange-700 border border-orange-200'}`}>
|
||||||
<div className="font-medium mb-2">Einige Seiten konnten nicht verarbeitet werden:</div>
|
<div className="font-medium mb-1 text-sm">Einige Seiten konnten nicht verarbeitet werden:</div>
|
||||||
<ul className="text-sm space-y-1">
|
<ul className="text-xs space-y-0.5">
|
||||||
{processingErrors.map((err, idx) => (
|
{processingErrors.map((err, idx) => (
|
||||||
<li key={idx}>• {err}</li>
|
<li key={idx}>• {err}</li>
|
||||||
))}
|
))}
|
||||||
@@ -1448,12 +1493,12 @@ export default function VocabWorksheetPage() {
|
|||||||
|
|
||||||
{/* Processing Progress */}
|
{/* Processing Progress */}
|
||||||
{currentlyProcessingPage && (
|
{currentlyProcessingPage && (
|
||||||
<div className={`rounded-xl p-4 mb-4 ${isDark ? 'bg-purple-500/20 border border-purple-500/30' : 'bg-purple-100 border border-purple-200'}`}>
|
<div className={`rounded-xl p-3 mb-3 flex-shrink-0 ${isDark ? 'bg-purple-500/20 border border-purple-500/30' : 'bg-purple-100 border border-purple-200'}`}>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className={`w-5 h-5 border-2 ${isDark ? 'border-purple-300' : 'border-purple-600'} border-t-transparent rounded-full animate-spin`} />
|
<div className={`w-4 h-4 border-2 ${isDark ? 'border-purple-300' : 'border-purple-600'} border-t-transparent rounded-full animate-spin`} />
|
||||||
<div>
|
<div>
|
||||||
<div className={`font-medium ${isDark ? 'text-purple-200' : 'text-purple-700'}`}>Verarbeite Seite {currentlyProcessingPage}...</div>
|
<div className={`text-sm font-medium ${isDark ? 'text-purple-200' : 'text-purple-700'}`}>Verarbeite Seite {currentlyProcessingPage}...</div>
|
||||||
<div className={`text-sm ${isDark ? 'text-purple-300/70' : 'text-purple-600'}`}>
|
<div className={`text-xs ${isDark ? 'text-purple-300/70' : 'text-purple-600'}`}>
|
||||||
{successfulPages.length > 0 && `${successfulPages.length} Seite(n) fertig • `}
|
{successfulPages.length > 0 && `${successfulPages.length} Seite(n) fertig • `}
|
||||||
{vocabulary.length} Vokabeln bisher
|
{vocabulary.length} Vokabeln bisher
|
||||||
</div>
|
</div>
|
||||||
@@ -1464,14 +1509,14 @@ export default function VocabWorksheetPage() {
|
|||||||
|
|
||||||
{/* Success info */}
|
{/* Success info */}
|
||||||
{!currentlyProcessingPage && successfulPages.length > 0 && failedPages.length === 0 && (
|
{!currentlyProcessingPage && successfulPages.length > 0 && failedPages.length === 0 && (
|
||||||
<div className={`rounded-xl p-3 mb-4 text-sm ${isDark ? 'bg-green-500/20 text-green-200 border border-green-500/30' : 'bg-green-100 text-green-700 border border-green-200'}`}>
|
<div className={`rounded-xl p-2 mb-3 text-xs flex-shrink-0 ${isDark ? 'bg-green-500/20 text-green-200 border border-green-500/30' : 'bg-green-100 text-green-700 border border-green-200'}`}>
|
||||||
Alle {successfulPages.length} Seite(n) erfolgreich verarbeitet - {vocabulary.length} Vokabeln insgesamt
|
Alle {successfulPages.length} Seite(n) erfolgreich verarbeitet - {vocabulary.length} Vokabeln insgesamt
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Partial success info */}
|
{/* Partial success info */}
|
||||||
{!currentlyProcessingPage && successfulPages.length > 0 && failedPages.length > 0 && (
|
{!currentlyProcessingPage && successfulPages.length > 0 && failedPages.length > 0 && (
|
||||||
<div className={`rounded-xl p-3 mb-4 text-sm ${isDark ? 'bg-yellow-500/20 text-yellow-200 border border-yellow-500/30' : 'bg-yellow-100 text-yellow-700 border border-yellow-200'}`}>
|
<div className={`rounded-xl p-2 mb-3 text-xs flex-shrink-0 ${isDark ? 'bg-yellow-500/20 text-yellow-200 border border-yellow-500/30' : 'bg-yellow-100 text-yellow-700 border border-yellow-200'}`}>
|
||||||
{successfulPages.length} Seite(n) erfolgreich, {failedPages.length} fehlgeschlagen - {vocabulary.length} Vokabeln extrahiert
|
{successfulPages.length} Seite(n) erfolgreich, {failedPages.length} fehlgeschlagen - {vocabulary.length} Vokabeln extrahiert
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1479,10 +1524,10 @@ export default function VocabWorksheetPage() {
|
|||||||
{vocabulary.length === 0 ? (
|
{vocabulary.length === 0 ? (
|
||||||
<p className={`text-center py-8 ${isDark ? 'text-white/60' : 'text-slate-500'}`}>Keine Vokabeln gefunden.</p>
|
<p className={`text-center py-8 ${isDark ? 'text-white/60' : 'text-slate-500'}`}>Keine Vokabeln gefunden.</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col" style={{ height: 'calc(100vh - 400px)', minHeight: '300px' }}>
|
<div className="flex flex-col flex-1 overflow-hidden">
|
||||||
{/* Fixed Header */}
|
{/* Fixed Header */}
|
||||||
<div className={`grid grid-cols-13 gap-2 px-3 py-2 text-sm font-medium border-b ${isDark ? 'border-white/10 text-white/60' : 'border-black/10 text-slate-500'}`} style={{ gridTemplateColumns: 'auto repeat(12, minmax(0, 1fr))' }}>
|
<div className={`flex-shrink-0 grid gap-2 px-3 py-2 text-sm font-medium border-b ${isDark ? 'border-white/10 text-white/60' : 'border-black/10 text-slate-500'}`} style={{ gridTemplateColumns: '32px 36px 1fr 1fr 1fr 32px' }}>
|
||||||
<div className="flex items-center justify-center w-6">
|
<div className="flex items-center justify-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={vocabulary.length > 0 && vocabulary.every(v => v.selected)}
|
checked={vocabulary.length > 0 && vocabulary.every(v => v.selected)}
|
||||||
@@ -1491,37 +1536,20 @@ export default function VocabWorksheetPage() {
|
|||||||
title="Alle auswählen"
|
title="Alle auswählen"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">S.</div>
|
<div>S.</div>
|
||||||
<div className="col-span-3">Englisch</div>
|
<div>Englisch</div>
|
||||||
<div className="col-span-4">Deutsch</div>
|
<div>Deutsch</div>
|
||||||
<div className="col-span-3">Beispiel</div>
|
<div>Beispiel</div>
|
||||||
<div className="col-span-1"></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Scrollable Content */}
|
{/* Scrollable Content */}
|
||||||
<div className="flex-1 overflow-y-auto py-2">
|
<div className="flex-1 overflow-y-auto py-1">
|
||||||
{/* Insert button at the beginning */}
|
|
||||||
<div className="flex justify-center py-1 group">
|
|
||||||
<button
|
|
||||||
onClick={() => addVocabularyEntry(0)}
|
|
||||||
className={`px-3 py-0.5 rounded-full text-xs flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity ${
|
|
||||||
isDark
|
|
||||||
? 'bg-purple-500/20 text-purple-300 hover:bg-purple-500/30'
|
|
||||||
: 'bg-purple-100 text-purple-600 hover:bg-purple-200'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
||||||
</svg>
|
|
||||||
Zeile einfügen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{vocabulary.map((entry, index) => (
|
{vocabulary.map((entry, index) => (
|
||||||
<React.Fragment key={entry.id}>
|
<React.Fragment key={entry.id}>
|
||||||
{/* Vocabulary row */}
|
{/* Vocabulary row */}
|
||||||
<div className={`grid gap-2 px-3 py-2 rounded-xl ${isDark ? 'bg-white/5' : 'bg-black/5'}`} style={{ gridTemplateColumns: 'auto repeat(12, minmax(0, 1fr))' }}>
|
<div className={`grid gap-2 px-3 py-1.5 rounded-lg mb-0.5 ${isDark ? 'hover:bg-white/5' : 'hover:bg-black/5'}`} style={{ gridTemplateColumns: '32px 36px 1fr 1fr 1fr 32px' }}>
|
||||||
<div className="flex items-center justify-center w-6">
|
<div className="flex items-center justify-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={entry.selected || false}
|
checked={entry.selected || false}
|
||||||
@@ -1529,126 +1557,65 @@ export default function VocabWorksheetPage() {
|
|||||||
className="w-4 h-4 rounded border-gray-300 text-purple-600 focus:ring-purple-500 cursor-pointer"
|
className="w-4 h-4 rounded border-gray-300 text-purple-600 focus:ring-purple-500 cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={`col-span-1 flex items-center justify-center text-xs font-medium rounded ${isDark ? 'bg-white/10 text-white/60' : 'bg-black/10 text-slate-600'}`}>
|
<div className={`flex items-center justify-center text-xs font-medium rounded ${isDark ? 'bg-white/10 text-white/60' : 'bg-black/10 text-slate-600'}`}>
|
||||||
{entry.source_page || '-'}
|
{entry.source_page || '-'}
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={entry.english}
|
value={entry.english}
|
||||||
onChange={(e) => updateVocabularyEntry(entry.id, 'english', e.target.value)}
|
onChange={(e) => updateVocabularyEntry(entry.id, 'english', e.target.value)}
|
||||||
className={`col-span-3 px-2 py-1 rounded-lg border ${glassInput} focus:outline-none focus:ring-1 focus:ring-purple-500`}
|
className={`px-2 py-1 rounded-lg border text-sm ${glassInput} focus:outline-none focus:ring-1 focus:ring-purple-500`}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={entry.german}
|
value={entry.german}
|
||||||
onChange={(e) => updateVocabularyEntry(entry.id, 'german', e.target.value)}
|
onChange={(e) => updateVocabularyEntry(entry.id, 'german', e.target.value)}
|
||||||
className={`col-span-4 px-2 py-1 rounded-lg border ${glassInput} focus:outline-none focus:ring-1 focus:ring-purple-500`}
|
className={`px-2 py-1 rounded-lg border text-sm ${glassInput} focus:outline-none focus:ring-1 focus:ring-purple-500`}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={entry.example_sentence || ''}
|
value={entry.example_sentence || ''}
|
||||||
onChange={(e) => updateVocabularyEntry(entry.id, 'example_sentence', e.target.value)}
|
onChange={(e) => updateVocabularyEntry(entry.id, 'example_sentence', e.target.value)}
|
||||||
placeholder="Beispiel"
|
placeholder="Beispiel"
|
||||||
className={`col-span-3 px-2 py-1 rounded-lg border text-sm ${glassInput} focus:outline-none focus:ring-1 focus:ring-purple-500`}
|
className={`px-2 py-1 rounded-lg border text-sm ${glassInput} focus:outline-none focus:ring-1 focus:ring-purple-500`}
|
||||||
/>
|
/>
|
||||||
<button onClick={() => deleteVocabularyEntry(entry.id)} className={`col-span-1 p-1 rounded-lg ${isDark ? 'hover:bg-red-500/20 text-red-400' : 'hover:bg-red-100 text-red-500'}`}>
|
<button onClick={() => deleteVocabularyEntry(entry.id)} className={`p-1 rounded-lg ${isDark ? 'hover:bg-red-500/20 text-red-400' : 'hover:bg-red-100 text-red-500'}`}>
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Insert button after each row */}
|
|
||||||
<div className="flex justify-center py-1 group">
|
|
||||||
<button
|
|
||||||
onClick={() => addVocabularyEntry(index + 1)}
|
|
||||||
className={`px-3 py-0.5 rounded-full text-xs flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity ${
|
|
||||||
isDark
|
|
||||||
? 'bg-purple-500/20 text-purple-300 hover:bg-purple-500/30'
|
|
||||||
: 'bg-purple-100 text-purple-600 hover:bg-purple-200'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
||||||
</svg>
|
|
||||||
Zeile einfügen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Add new row button at the end */}
|
{/* Footer */}
|
||||||
<button
|
<div className={`flex-shrink-0 pt-2 border-t flex items-center justify-between text-xs ${isDark ? 'border-white/10 text-white/50' : 'border-black/10 text-slate-400'}`}>
|
||||||
onClick={() => addVocabularyEntry()}
|
<span>
|
||||||
className={`w-full py-2 mt-2 rounded-xl border-2 border-dashed flex items-center justify-center gap-2 transition-colors ${
|
{vocabulary.length} Vokabeln
|
||||||
isDark
|
{vocabulary.filter(v => v.selected).length > 0 && ` (${vocabulary.filter(v => v.selected).length} ausgewaehlt)`}
|
||||||
? 'border-white/20 text-white/60 hover:border-purple-400 hover:text-purple-400 hover:bg-purple-500/10'
|
{(() => {
|
||||||
: 'border-black/20 text-slate-500 hover:border-purple-500 hover:text-purple-500 hover:bg-purple-50'
|
const pages = [...new Set(vocabulary.map(v => v.source_page).filter(Boolean))].sort((a, b) => (a || 0) - (b || 0))
|
||||||
}`}
|
return pages.length > 1 ? ` • Seiten: ${pages.join(', ')}` : ''
|
||||||
>
|
})()}
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
</span>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
<button
|
||||||
</svg>
|
onClick={() => addVocabularyEntry()}
|
||||||
Neue Zeile hinzufügen
|
className={`px-3 py-1 rounded-lg text-xs flex items-center gap-1 transition-colors ${
|
||||||
</button>
|
isDark
|
||||||
|
? 'bg-white/10 hover:bg-white/20 text-white/70'
|
||||||
{/* Footer with scroll hint */}
|
: 'bg-slate-100 hover:bg-slate-200 text-slate-600'
|
||||||
<div className={`pt-2 border-t text-center text-sm ${isDark ? 'border-white/10 text-white/50' : 'border-black/10 text-slate-400'}`}>
|
}`}
|
||||||
{vocabulary.length} Vokabeln insgesamt
|
>
|
||||||
{vocabulary.filter(v => v.selected).length > 0 && ` (${vocabulary.filter(v => v.selected).length} ausgewählt)`}
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
{(() => {
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||||
const pages = [...new Set(vocabulary.map(v => v.source_page).filter(Boolean))].sort((a, b) => (a || 0) - (b || 0))
|
</svg>
|
||||||
return pages.length > 1 ? ` • Seiten: ${pages.join(', ')}` : ''
|
Zeile
|
||||||
})()}
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right: Original Worksheet Preview (2/5) */}
|
|
||||||
<div className={`${glassCard} rounded-2xl p-6 lg:col-span-2`}>
|
|
||||||
<h2 className={`text-lg font-semibold mb-4 ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
|
||||||
Original-Arbeitsblatt
|
|
||||||
</h2>
|
|
||||||
<div className="flex flex-col" style={{ height: 'calc(100vh - 400px)', minHeight: '300px' }}>
|
|
||||||
{pagesThumbnails.length > 0 ? (
|
|
||||||
<div className="flex-1 overflow-y-auto space-y-4">
|
|
||||||
{pagesThumbnails.map((thumb, idx) => (
|
|
||||||
<div key={idx} className={`relative rounded-xl overflow-hidden border ${isDark ? 'border-white/10' : 'border-black/10'}`}>
|
|
||||||
<div className={`absolute top-2 left-2 px-2 py-1 rounded-lg text-xs font-medium ${isDark ? 'bg-black/50 text-white' : 'bg-white/90 text-slate-700'}`}>
|
|
||||||
Seite {idx + 1}
|
|
||||||
</div>
|
|
||||||
<img
|
|
||||||
src={thumb}
|
|
||||||
alt={`Seite ${idx + 1}`}
|
|
||||||
className="w-full h-auto"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : uploadedImage ? (
|
|
||||||
<div className="flex-1 overflow-y-auto">
|
|
||||||
<div className={`relative rounded-xl overflow-hidden border ${isDark ? 'border-white/10' : 'border-black/10'}`}>
|
|
||||||
<img
|
|
||||||
src={uploadedImage}
|
|
||||||
alt="Hochgeladenes Arbeitsblatt"
|
|
||||||
className="w-full h-auto"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={`flex-1 flex items-center justify-center ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
|
|
||||||
<div className="text-center">
|
|
||||||
<svg className="w-16 h-16 mx-auto mb-3 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
<p className="text-sm">Kein Bild verfügbar</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user