'use client'
import { useState, useEffect, useCallback, useMemo } from 'react'
import { useRouter } from 'next/navigation'
import { useTheme } from '@/lib/ThemeContext'
import { useLanguage } from '@/lib/LanguageContext'
import { Sidebar } from '@/components/Sidebar'
import { ThemeToggle } from '@/components/ThemeToggle'
import { LanguageDropdown } from '@/components/LanguageDropdown'
import { korrekturApi } from '@/lib/korrektur/api'
// =============================================================================
// TYPES
// =============================================================================
interface AbiturDokument {
id: string
dateiname: string
fach: string
jahr: number
bundesland: string
niveau: string
dokumenttyp: string
aufgabentyp?: string
thema?: string
download_url: string
preview_url?: string
file_size?: number
page_count?: number
}
interface ThemaSuggestion {
label: string
count: number
aufgabentyp: string
kategorie?: string
}
// =============================================================================
// CONSTANTS
// =============================================================================
// Default filter options (will be updated from API)
const DEFAULT_FAECHER = ['Alle', 'Deutsch', 'Englisch', 'Mathematik', 'Geschichte', 'Politik']
const DEFAULT_JAHRE = ['Alle', '2025', '2024', '2023', '2022', '2021']
const BUNDESLAENDER = ['Alle', 'Niedersachsen', 'NRW', 'Bayern', 'Baden-Wuerttemberg', 'Hessen']
const DEFAULT_NIVEAUS = ['Alle', 'eA', 'gA']
const DEFAULT_DOKUMENTTYPEN = ['Alle', 'Aufgabe', 'Erwartungshorizont', 'Loesungshinweise']
const POPULAR_THEMES = [
'Textanalyse',
'Gedichtanalyse',
'Eroerterung',
'Dramenanalyse',
'Sprachreflexion',
'Romantik',
'Expressionismus',
]
// =============================================================================
// GLASS CARD COMPONENT
// =============================================================================
interface GlassCardProps {
children: React.ReactNode
className?: string
onClick?: () => void
size?: 'sm' | 'md' | 'lg'
delay?: number
isDark?: boolean
}
function GlassCard({ children, className = '', onClick, size = 'md', delay = 0, isDark = true }: GlassCardProps) {
const [isVisible, setIsVisible] = useState(false)
const [isHovered, setIsHovered] = useState(false)
useEffect(() => {
const timer = setTimeout(() => setIsVisible(true), delay)
return () => clearTimeout(timer)
}, [delay])
const sizeClasses = { sm: 'p-4', md: 'p-5', lg: 'p-6' }
return (
setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onClick={onClick}
>
{children}
)
}
// =============================================================================
// FILTER DROPDOWN
// =============================================================================
interface FilterDropdownProps {
label: string
value: string
options: string[]
onChange: (value: string) => void
isDark: boolean
}
function FilterDropdown({ label, value, options, onChange, isDark }: FilterDropdownProps) {
const inputId = `filter-${label.toLowerCase().replace(/\s+/g, '-')}`
return (
)
}
// =============================================================================
// DOCUMENT CARD
// =============================================================================
interface DokumentCardProps {
dokument: AbiturDokument
onPreview: () => void
onUseAsTemplate: () => void
delay?: number
isDark: boolean
}
function DokumentCard({ dokument, onPreview, onUseAsTemplate, delay = 0, isDark }: DokumentCardProps) {
const typeColor = dokument.dokumenttyp === 'Erwartungshorizont' ? '#22c55e' : '#3b82f6'
return (
{/* Header */}
{dokument.fach} {dokument.jahr} {dokument.niveau}
{dokument.thema || dokument.aufgabentyp || dokument.dateiname}
{dokument.dokumenttyp === 'Erwartungshorizont' ? 'EH' : 'Aufgabe'}
{/* Meta */}
{dokument.bundesland}
{dokument.page_count && {dokument.page_count} Seiten}
{/* Actions */}
)
}
// =============================================================================
// PREVIEW MODAL
// =============================================================================
interface PreviewModalProps {
dokument: AbiturDokument | null
onClose: () => void
onUseAsTemplate: () => void
isDark: boolean
}
function PreviewModal({ dokument, onClose, onUseAsTemplate, isDark }: PreviewModalProps) {
const [zoom, setZoom] = useState(100)
if (!dokument) return null
return (
{/* Backdrop */}
{/* Content */}
{/* PDF Viewer */}
{/* Toolbar */}
{zoom}%
{/* PDF Content */}
{dokument.preview_url ? (
) : (
)}
{/* Sidebar */}
{/* Close Button - prominent */}
Details
Bundesland
{dokument.bundesland}
{dokument.thema && (
)}
Aktionen
)
}
// =============================================================================
// CREATE KLAUSUR FROM TEMPLATE MODAL
// =============================================================================
interface CreateKlausurFromTemplateModalProps {
template: AbiturDokument
onClose: () => void
onCreate: (title: string) => void
onFallback: () => void
isLoading: boolean
error: string | null
isDark: boolean
}
function CreateKlausurFromTemplateModal({
template,
onClose,
onCreate,
onFallback,
isLoading,
error,
isDark,
}: CreateKlausurFromTemplateModalProps) {
const [title, setTitle] = useState(
`${template.fach} ${template.aufgabentyp || ''} ${template.jahr}`.trim()
)
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onCreate(title)
}
return (
Klausur aus Vorlage erstellen
Basierend auf: {template.thema || template.dateiname}
{error && (
{error}
)}
)
}
// =============================================================================
// MAIN PAGE
// =============================================================================
export default function ArchivPage() {
const { isDark } = useTheme()
const { t } = useLanguage()
const router = useRouter()
// State
const [searchQuery, setSearchQuery] = useState('')
const [suggestions, setSuggestions] = useState([])
const [showSuggestions, setShowSuggestions] = useState(false)
const [dokumente, setDokumente] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
// Filters
const [fach, setFach] = useState('Alle')
const [jahr, setJahr] = useState('Alle')
const [bundesland, setBundesland] = useState('Alle')
const [niveau, setNiveau] = useState('Alle')
const [dokumenttyp, setDokumenttyp] = useState('Alle')
// Preview
const [previewDokument, setPreviewDokument] = useState(null)
// Create Klausur Modal
const [showCreateModal, setShowCreateModal] = useState(false)
const [selectedTemplate, setSelectedTemplate] = useState(null)
const [isCreating, setIsCreating] = useState(false)
const [createError, setCreateError] = useState(null)
// Example PDF URL (Mozilla public sample) - fallback when API fails
const SAMPLE_PDF = 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf'
// Available filter options (updated from API response)
const [availableFilters, setAvailableFilters] = useState<{
subjects: string[]
years: number[]
niveaus: string[]
doc_types: string[]
}>({
subjects: ['Deutsch', 'Englisch', 'Mathematik'],
years: [2025, 2024, 2023, 2022, 2021],
niveaus: ['eA', 'gA'],
doc_types: ['EWH', 'Aufgabe']
})
// Load data from API with fallback to mock
useEffect(() => {
const loadDocuments = async () => {
setIsLoading(true)
setError(null)
try {
// Build filter params
const filters: {
subject?: string
year?: number
niveau?: string
doc_type?: string
search?: string
} = {}
if (fach !== 'Alle') filters.subject = fach
if (jahr !== 'Alle') filters.year = parseInt(jahr)
if (niveau !== 'Alle') filters.niveau = niveau
if (dokumenttyp !== 'Alle') {
// Map frontend names to API names
const docTypeMap: Record = {
'Erwartungshorizont': 'EWH',
'Aufgabe': 'Aufgabe',
'Loesungshinweise': 'Material'
}
filters.doc_type = docTypeMap[dokumenttyp] || dokumenttyp
}
if (searchQuery) filters.search = searchQuery
const response = await korrekturApi.getArchivDocuments(filters)
// Map API response to frontend interface
const mappedDokumente: AbiturDokument[] = response.documents.map((doc) => ({
id: doc.id,
dateiname: `${doc.subject}_${doc.niveau}_${doc.year}_${doc.doc_type}.pdf`,
fach: doc.subject,
jahr: doc.year,
bundesland: doc.bundesland === 'NI' ? 'Niedersachsen' : doc.bundesland,
niveau: doc.niveau,
dokumenttyp: doc.doc_type === 'EWH' ? 'Erwartungshorizont' : doc.doc_type,
aufgabentyp: doc.doc_type,
thema: doc.title,
download_url: doc.preview_url || SAMPLE_PDF,
preview_url: doc.preview_url || SAMPLE_PDF,
}))
// Update available filters from API
if (response.filters) {
setAvailableFilters({
subjects: response.filters.subjects || ['Deutsch', 'Englisch', 'Mathematik'],
years: response.filters.years || [2025, 2024, 2023, 2022, 2021],
niveaus: response.filters.niveaus || ['eA', 'gA'],
doc_types: response.filters.doc_types || ['EWH', 'Aufgabe']
})
}
// If API returns documents, use them
if (mappedDokumente.length > 0) {
setDokumente(mappedDokumente)
} else {
// API returned empty, use fallback mock data
console.warn('API returned empty documents, using fallback data')
throw new Error('Empty response')
}
} catch (err) {
console.warn('Failed to load from API, using fallback data:', err)
// Fallback to mock data
const mockDokumente: AbiturDokument[] = [
{
id: '1',
dateiname: 'Deutsch_eA_2024_Aufgabe1.pdf',
fach: 'Deutsch',
jahr: 2024,
bundesland: 'Niedersachsen',
niveau: 'eA',
dokumenttyp: 'Aufgabe',
aufgabentyp: 'Textanalyse',
thema: 'Textanalyse: "Der Prozess" - Kafka',
download_url: SAMPLE_PDF,
preview_url: SAMPLE_PDF,
page_count: 4,
},
{
id: '2',
dateiname: 'Deutsch_eA_2024_EH1.pdf',
fach: 'Deutsch',
jahr: 2024,
bundesland: 'Niedersachsen',
niveau: 'eA',
dokumenttyp: 'Erwartungshorizont',
aufgabentyp: 'Textanalyse',
thema: 'EH zu Kafka-Analyse',
download_url: SAMPLE_PDF,
preview_url: SAMPLE_PDF,
page_count: 8,
},
{
id: '3',
dateiname: 'Deutsch_gA_2024_Aufgabe2.pdf',
fach: 'Deutsch',
jahr: 2024,
bundesland: 'Niedersachsen',
niveau: 'gA',
dokumenttyp: 'Aufgabe',
aufgabentyp: 'Gedichtanalyse',
thema: 'Gedichtvergleich Romantik',
download_url: SAMPLE_PDF,
preview_url: SAMPLE_PDF,
page_count: 3,
},
{
id: '4',
dateiname: 'Deutsch_eA_2023_Aufgabe1.pdf',
fach: 'Deutsch',
jahr: 2023,
bundesland: 'Niedersachsen',
niveau: 'eA',
dokumenttyp: 'Aufgabe',
aufgabentyp: 'Eroerterung',
thema: 'Materialgestuetzte Eroerterung: Digitalisierung',
download_url: SAMPLE_PDF,
preview_url: SAMPLE_PDF,
page_count: 5,
},
{
id: '5',
dateiname: 'Deutsch_eA_2023_EH1.pdf',
fach: 'Deutsch',
jahr: 2023,
bundesland: 'Niedersachsen',
niveau: 'eA',
dokumenttyp: 'Erwartungshorizont',
aufgabentyp: 'Eroerterung',
thema: 'EH zu Digitalisierungs-Eroerterung',
download_url: SAMPLE_PDF,
preview_url: SAMPLE_PDF,
page_count: 10,
},
{
id: '6',
dateiname: 'Deutsch_gA_2023_Aufgabe3.pdf',
fach: 'Deutsch',
jahr: 2023,
bundesland: 'Niedersachsen',
niveau: 'gA',
dokumenttyp: 'Aufgabe',
aufgabentyp: 'Dramenanalyse',
thema: 'Szenenanalyse: "Faust I"',
download_url: SAMPLE_PDF,
preview_url: SAMPLE_PDF,
page_count: 4,
},
]
setDokumente(mockDokumente)
} finally {
setIsLoading(false)
}
}
loadDocuments()
}, [fach, jahr, bundesland, niveau, dokumenttyp, searchQuery])
// Documents are now filtered by API, but keep local filtering for bundesland (API only filters NI)
// and for fallback mock data, also apply search filter locally for mock data
const filteredDokumente = useMemo(() => {
return dokumente.filter((dok) => {
// Bundesland filter (not yet supported by API)
if (bundesland !== 'Alle' && dok.bundesland !== bundesland) return false
// Search filter (for local mock data - API handles this server-side)
if (searchQuery) {
const query = searchQuery.toLowerCase()
const matchesSearch =
dok.thema?.toLowerCase().includes(query) ||
dok.fach.toLowerCase().includes(query) ||
dok.aufgabentyp?.toLowerCase().includes(query) ||
dok.dateiname.toLowerCase().includes(query)
if (!matchesSearch) return false
}
return true
})
}, [dokumente, bundesland, searchQuery])
// Handle theme search
const handleThemeClick = (theme: string) => {
setSearchQuery(theme)
setShowSuggestions(false)
}
// Handle use as template
const handleUseAsTemplate = (dokument: AbiturDokument) => {
setSelectedTemplate(dokument)
setShowCreateModal(true)
setCreateError(null)
}
// Create Klausur with template
const handleCreateKlausur = async (title: string) => {
if (!selectedTemplate) return
setIsCreating(true)
setCreateError(null)
try {
const newKlausur = await korrekturApi.createKlausur({
title: title || `${selectedTemplate.fach} ${selectedTemplate.aufgabentyp || ''} ${selectedTemplate.jahr}`,
subject: selectedTemplate.fach,
year: selectedTemplate.jahr,
semester: 'Abitur',
modus: 'landes_abitur',
})
setShowCreateModal(false)
setSelectedTemplate(null)
router.push(`/korrektur/${newKlausur.id}`)
} catch (err) {
console.error('Failed to create klausur:', err)
const errorMsg = err instanceof Error ? err.message : 'Unbekannter Fehler'
if (errorMsg.includes('fetch') || errorMsg.includes('Load') || errorMsg.includes('network')) {
setCreateError('Verbindung zum Server fehlgeschlagen. Bitte pruefen Sie, ob der Klausur-Service laeuft.')
} else {
setCreateError(`Klausur konnte nicht erstellt werden: ${errorMsg}`)
}
} finally {
setIsCreating(false)
}
}
// Navigate to korrektur page without creating (fallback)
const handleGoToKorrektur = () => {
setShowCreateModal(false)
setSelectedTemplate(null)
router.push('/korrektur')
}
const activeFilters = [fach, jahr, bundesland, niveau, dokumenttyp].filter(f => f !== 'Alle').length
// Computed filter options with "Alle" prefix
const FAECHER = useMemo(() => ['Alle', ...availableFilters.subjects], [availableFilters.subjects])
const JAHRE = useMemo(() => ['Alle', ...availableFilters.years.map(String)], [availableFilters.years])
const NIVEAUS = useMemo(() => ['Alle', ...availableFilters.niveaus], [availableFilters.niveaus])
const DOKUMENTTYPEN = useMemo(() => ['Alle', ...availableFilters.doc_types.map(t => t === 'EWH' ? 'Erwartungshorizont' : t)], [availableFilters.doc_types])
return (
{/* Animated Background */}
{/* Sidebar */}
{/* Main Content */}
{/* Header */}
Abitur-Archiv
Zentralabitur-Materialien 2021-2025 durchsuchen
{/* Search Bar */}
{/* Popular Themes */}
{POPULAR_THEMES.map((theme) => (
))}
{/* Filters */}
{activeFilters > 0 && (
)}
{filteredDokumente.length} Dokumente
{/* Loading */}
{isLoading && (
)}
{/* Error */}
{error && (
)}
{/* Documents Grid */}
{!isLoading && (
{filteredDokumente.map((dok, index) => (
setPreviewDokument(dok)}
onUseAsTemplate={() => handleUseAsTemplate(dok)}
delay={200 + index * 50}
isDark={isDark}
/>
))}
{filteredDokumente.length === 0 && !isLoading && (
Keine Dokumente gefunden
Versuchen Sie andere Filtereinstellungen
)}
)}
{/* Preview Modal */}
setPreviewDokument(null)}
onUseAsTemplate={() => {
if (previewDokument) handleUseAsTemplate(previewDokument)
setPreviewDokument(null)
}}
isDark={isDark}
/>
{/* Create Klausur Modal */}
{showCreateModal && selectedTemplate && (
{ setShowCreateModal(false); setSelectedTemplate(null); setCreateError(null) }}
onCreate={handleCreateKlausur}
onFallback={handleGoToKorrektur}
isLoading={isCreating}
error={createError}
isDark={isDark}
/>
)}
)
}