[split-required] Split final batch of monoliths >1000 LOC

Python (6 files in klausur-service):
- rbac.py (1,132 → 4), admin_api.py (1,012 → 4)
- routes/eh.py (1,111 → 4), ocr_pipeline_geometry.py (1,105 → 5)

Python (2 files in backend-lehrer):
- unit_api.py (1,226 → 6), game_api.py (1,129 → 5)

Website (6 page files):
- 4x klausur-korrektur pages (1,249-1,328 LOC each) → shared components
  in website/components/klausur-korrektur/ (17 shared files)
- companion (1,057 → 10), magic-help (1,017 → 8)

All re-export barrels preserve backward compatibility.
Zero import errors verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-24 23:17:30 +02:00
parent b2a0126f14
commit 6811264756
67 changed files with 12270 additions and 13651 deletions

View File

@@ -0,0 +1,151 @@
'use client'
/**
* Welcome/Onboarding tab for the Klausur-Korrektur page.
* Shows hero, workflow explanation, and action cards.
*/
import type { Klausur } from '../../app/admin/klausur-korrektur/types'
import type { TabId } from './list-types'
interface WillkommenTabProps {
klausuren: Klausur[]
onNavigate: (tab: TabId) => void
markAsVisited: () => void
}
export default function WillkommenTab({ klausuren, onNavigate, markAsVisited }: WillkommenTabProps) {
const goTo = (tab: TabId) => { markAsVisited(); onNavigate(tab) }
return (
<div className="max-w-4xl mx-auto space-y-8">
{/* Hero Section */}
<div className="text-center py-8">
<div className="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-purple-500 to-purple-700 rounded-2xl mb-6">
<svg className="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h1 className="text-3xl font-bold text-slate-800 mb-3">Willkommen zur Abiturklausur-Korrektur</h1>
<p className="text-lg text-slate-600 max-w-2xl mx-auto">
KI-gestuetzte Korrektur fuer Deutsch-Abiturklausuren nach dem 15-Punkte-System.
Sparen Sie bis zu 80% Zeit bei der Erstkorrektur.
</p>
</div>
{/* Workflow Explanation */}
<div className="bg-gradient-to-r from-blue-50 to-purple-50 border border-blue-200 rounded-xl p-6">
<h2 className="text-lg font-semibold text-slate-800 mb-4 flex items-center gap-2">
<svg className="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
So funktioniert es
</h2>
<div className="grid md:grid-cols-4 gap-4">
{[
{ step: 1, title: 'Arbeiten hochladen', desc: 'Scans der Schuelerarbeiten als PDF oder Bilder hochladen' },
{ step: 2, title: 'EH bereitstellen', desc: 'Erwartungshorizont hochladen oder aus Vorlage erstellen' },
{ step: 3, title: 'KI korrigiert', desc: 'Automatische Bewertung und Gutachten-Vorschlaege erhalten' },
{ step: 4, title: 'Pruefen & Anpassen', desc: 'Vorschlaege pruefen, anpassen und finalisieren' },
].map(({ step, title, desc }) => (
<div key={step} className="text-center">
<div className="text-xs text-blue-600 font-medium mb-1">Schritt {step}</div>
<div className="font-medium text-slate-800 text-sm">{title}</div>
<div className="text-xs text-slate-500 mt-1">{desc}</div>
</div>
))}
</div>
</div>
{/* Action Cards */}
<div className="grid md:grid-cols-2 gap-6">
{/* Option 1: Standard Flow */}
<div className="bg-white border-2 border-slate-200 rounded-xl p-6 hover:border-purple-300 hover:shadow-lg transition-all cursor-pointer"
onClick={() => goTo('erstellen')}>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-purple-100 rounded-xl flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
</div>
<div className="flex-1">
<h3 className="font-semibold text-slate-800 mb-1">Neue Klausur erstellen</h3>
<p className="text-sm text-slate-600 mb-3">
Empfohlen fuer regelmaessige Nutzung. Erstellen Sie eine Klausur mit allen Metadaten,
laden Sie dann die Arbeiten hoch.
</p>
<ul className="text-xs text-slate-500 space-y-1">
{['Volle Metadaten (Fach, Jahr, Kurs)', 'Zweitkorrektur-Workflow', 'Fairness-Analyse'].map(text => (
<li key={text} className="flex items-center gap-1">
<svg className="w-3 h-3 text-green-500" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" /></svg>
{text}
</li>
))}
</ul>
<div className="mt-4 text-sm text-purple-600 font-medium flex items-center gap-1">
Klausur erstellen
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</div>
</div>
{/* Option 2: Quick Upload */}
<div className="bg-white border-2 border-slate-200 rounded-xl p-6 hover:border-blue-300 hover:shadow-lg transition-all cursor-pointer"
onClick={() => goTo('direktupload')}>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center flex-shrink-0">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
</div>
<div className="flex-1">
<h3 className="font-semibold text-slate-800 mb-1">Schnellstart - Direkt hochladen</h3>
<p className="text-sm text-slate-600 mb-3">
Ideal wenn Sie sofort loslegen moechten. Laden Sie Arbeiten und EH direkt hoch,
wir erstellen die Klausur automatisch.
</p>
<ul className="text-xs text-slate-500 space-y-1">
{['Schnellster Weg zum Korrigieren', 'Drag & Drop Upload', 'Sofort einsatzbereit'].map(text => (
<li key={text} className="flex items-center gap-1">
<svg className="w-3 h-3 text-blue-500" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clipRule="evenodd" /></svg>
{text}
</li>
))}
</ul>
<div className="mt-4 text-sm text-blue-600 font-medium flex items-center gap-1">
Schnellstart
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</div>
</div>
</div>
{/* Already have klausuren? */}
{klausuren.length > 0 && (
<div className="bg-slate-50 border border-slate-200 rounded-xl p-4 flex items-center justify-between">
<div>
<p className="font-medium text-slate-800">Sie haben {klausuren.length} Klausur{klausuren.length !== 1 ? 'en' : ''}</p>
<p className="text-sm text-slate-500">Setzen Sie Ihre Arbeit fort oder starten Sie eine neue Korrektur.</p>
</div>
<button
onClick={() => goTo('klausuren')}
className="px-4 py-2 bg-slate-800 text-white rounded-lg hover:bg-slate-700 text-sm"
>
Zu meinen Klausuren
</button>
</div>
)}
{/* Help Links */}
<div className="text-center text-sm text-slate-500">
<p>Fragen? Lesen Sie unsere <button className="text-purple-600 hover:underline">Dokumentation</button> oder kontaktieren Sie den <button className="text-purple-600 hover:underline">Support</button>.</p>
</div>
</div>
)
}