Phase 1 — Python (klausur-service): 5 monoliths → 36 files - dsfa_corpus_ingestion.py (1,828 LOC → 5 files) - cv_ocr_engines.py (2,102 LOC → 7 files) - cv_layout.py (3,653 LOC → 10 files) - vocab_worksheet_api.py (2,783 LOC → 8 files) - grid_build_core.py (1,958 LOC → 6 files) Phase 2 — Go (edu-search-service, school-service): 8 monoliths → 19 files - staff_crawler.go (1,402 → 4), policy/store.go (1,168 → 3) - policy_handlers.go (700 → 2), repository.go (684 → 2) - search.go (592 → 2), ai_extraction_handlers.go (554 → 2) - seed_data.go (591 → 2), grade_service.go (646 → 2) Phase 3 — TypeScript (admin-lehrer): 45 monoliths → 220+ files - sdk/types.ts (2,108 → 16 domain files) - ai/rag/page.tsx (2,686 → 14 files) - 22 page.tsx files split into _components/ + _hooks/ - 11 component files split into sub-components - 10 SDK data catalogs added to loc-exceptions - Deleted dead backup index_original.ts (4,899 LOC) All original public APIs preserved via re-export facades. Zero new errors: Python imports verified, Go builds clean, TypeScript tsc --noEmit shows only pre-existing errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
160 lines
5.1 KiB
TypeScript
160 lines
5.1 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Abitur-Archiv - Hauptseite
|
|
* Zentralabitur-Materialien 2021-2025 mit erweiterter Themensuche
|
|
*/
|
|
|
|
import { Search, X, Archive } from 'lucide-react'
|
|
import { ThemenSuche } from './components/ThemenSuche'
|
|
import { FullscreenViewer } from './components/FullscreenViewer'
|
|
import { useAbiturArchiv } from './_components/useAbiturArchiv'
|
|
import { FilterBar } from './_components/FilterBar'
|
|
import { DocumentDisplay } from './_components/DocumentDisplay'
|
|
|
|
export default function AbiturArchivPage() {
|
|
const {
|
|
documents,
|
|
loading,
|
|
error,
|
|
page,
|
|
setPage,
|
|
totalPages,
|
|
total,
|
|
limit,
|
|
viewMode,
|
|
setViewMode,
|
|
filterOpen,
|
|
setFilterOpen,
|
|
filterFach,
|
|
setFilterFach,
|
|
filterJahr,
|
|
setFilterJahr,
|
|
filterBundesland,
|
|
setFilterBundesland,
|
|
filterNiveau,
|
|
setFilterNiveau,
|
|
filterTyp,
|
|
setFilterTyp,
|
|
searchQuery,
|
|
selectedDocument,
|
|
setSelectedDocument,
|
|
stats,
|
|
clearFilters,
|
|
handleSearch,
|
|
handleClearSearch,
|
|
handleDownload,
|
|
handleAddToKlausur,
|
|
hasActiveFilters,
|
|
fetchDocuments,
|
|
} = useAbiturArchiv()
|
|
|
|
return (
|
|
<div className="min-h-screen bg-slate-50">
|
|
{/* Header */}
|
|
<div className="bg-white border-b border-slate-200">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-12 h-12 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center">
|
|
<Archive className="w-6 h-6 text-white" />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-slate-900">Abitur-Archiv</h1>
|
|
<p className="text-sm text-slate-500">Zentralabitur-Materialien 2021-2025</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats */}
|
|
<div className="hidden md:flex items-center gap-6">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-slate-800">{stats.total}</div>
|
|
<div className="text-xs text-slate-500">Dokumente</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-green-600">{stats.indexed}</div>
|
|
<div className="text-xs text-slate-500">Indexiert</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-blue-600">{stats.faecher}</div>
|
|
<div className="text-xs text-slate-500">Faecher</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Content */}
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 space-y-6">
|
|
{/* Theme Search */}
|
|
<div className="bg-white rounded-xl border border-slate-200 p-6">
|
|
<ThemenSuche onSearch={handleSearch} onClear={handleClearSearch} />
|
|
</div>
|
|
|
|
<FilterBar
|
|
filterOpen={filterOpen}
|
|
onToggleFilter={() => setFilterOpen(!filterOpen)}
|
|
hasActiveFilters={hasActiveFilters}
|
|
onClearFilters={clearFilters}
|
|
total={total}
|
|
viewMode={viewMode}
|
|
onViewModeChange={setViewMode}
|
|
filterFach={filterFach}
|
|
filterJahr={filterJahr}
|
|
filterBundesland={filterBundesland}
|
|
filterNiveau={filterNiveau}
|
|
filterTyp={filterTyp}
|
|
onFachChange={setFilterFach}
|
|
onJahrChange={setFilterJahr}
|
|
onBundeslandChange={setFilterBundesland}
|
|
onNiveauChange={setFilterNiveau}
|
|
onTypChange={setFilterTyp}
|
|
onResetPage={() => setPage(1)}
|
|
searchQuery={searchQuery}
|
|
/>
|
|
|
|
{/* Active Search Query Display */}
|
|
{searchQuery && (
|
|
<div className="flex items-center gap-2 px-4 py-2 bg-blue-50 border border-blue-200 rounded-lg">
|
|
<Search className="w-4 h-4 text-blue-600" />
|
|
<span className="text-sm text-blue-700">
|
|
Suche: <strong>{searchQuery}</strong>
|
|
</span>
|
|
<button
|
|
onClick={handleClearSearch}
|
|
className="ml-auto text-blue-600 hover:text-blue-800"
|
|
>
|
|
<X className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
<DocumentDisplay
|
|
documents={documents}
|
|
loading={loading}
|
|
error={error}
|
|
viewMode={viewMode}
|
|
hasActiveFilters={hasActiveFilters}
|
|
onClearFilters={clearFilters}
|
|
onSelectDocument={setSelectedDocument}
|
|
onDownload={handleDownload}
|
|
onAddToKlausur={handleAddToKlausur}
|
|
onRetry={fetchDocuments}
|
|
page={page}
|
|
totalPages={totalPages}
|
|
total={total}
|
|
limit={limit}
|
|
onPageChange={setPage}
|
|
/>
|
|
</div>
|
|
|
|
{/* Fullscreen Viewer Modal */}
|
|
<FullscreenViewer
|
|
document={selectedDocument}
|
|
onClose={() => setSelectedDocument(null)}
|
|
onAddToKlausur={handleAddToKlausur}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|