Files
breakpilot-lehrer/website/components/klausur-korrektur/ErstellenTab.tsx
Benjamin Admin 6811264756 [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>
2026-04-24 23:17:30 +02:00

209 lines
9.6 KiB
TypeScript

'use client'
/**
* Create new Klausur form tab.
* Supports both Abitur and Vorabitur modes with EH template selection.
*/
import type { TabId, CreateKlausurForm, VorabiturEHForm, EHTemplate } from './list-types'
interface ErstellenTabProps {
form: CreateKlausurForm
ehForm: VorabiturEHForm
templates: EHTemplate[]
creating: boolean
loadingTemplates: boolean
onFormChange: (form: CreateKlausurForm) => void
onEhFormChange: (form: VorabiturEHForm) => void
onSubmit: (e: React.FormEvent) => void
onCancel: () => void
}
export default function ErstellenTab({
form, ehForm, templates, creating, loadingTemplates,
onFormChange, onEhFormChange, onSubmit, onCancel,
}: ErstellenTabProps) {
return (
<div className="max-w-2xl mx-auto">
<div className="bg-white rounded-lg border border-slate-200 shadow-sm p-6">
<h2 className="text-lg font-semibold text-slate-800 mb-6">Neue Klausur erstellen</h2>
<form onSubmit={onSubmit} className="space-y-4">
{/* Title */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Titel der Klausur *</label>
<input
type="text"
value={form.title}
onChange={(e) => onFormChange({ ...form, title: e.target.value })}
placeholder="z.B. Deutsch LK Abitur 2025 - Kurs D1"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
required
/>
</div>
{/* Subject + Year */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Fach</label>
<select
value={form.subject}
onChange={(e) => onFormChange({ ...form, subject: e.target.value })}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
>
<option value="Deutsch">Deutsch</option>
<option value="Englisch">Englisch</option>
<option value="Mathematik">Mathematik</option>
<option value="Geschichte">Geschichte</option>
<option value="Biologie">Biologie</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Jahr</label>
<input
type="number"
value={form.year}
onChange={(e) => onFormChange({ ...form, year: parseInt(e.target.value) })}
min={2020} max={2030}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
/>
</div>
</div>
{/* Semester + Modus */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Semester / Pruefung</label>
<select
value={form.semester}
onChange={(e) => onFormChange({ ...form, semester: e.target.value })}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
>
<option value="Abitur">Abitur</option>
<option value="Q1">Q1 (11/1)</option>
<option value="Q2">Q2 (11/2)</option>
<option value="Q3">Q3 (12/1)</option>
<option value="Q4">Q4 (12/2)</option>
<option value="Vorabitur">Vorabitur</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Modus</label>
<select
value={form.modus}
onChange={(e) => onFormChange({ ...form, modus: e.target.value as 'abitur' | 'vorabitur' })}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
>
<option value="abitur">Abitur (mit offiziellem EH)</option>
<option value="vorabitur">Vorabitur (eigener EH)</option>
</select>
</div>
</div>
{/* Vorabitur EH Form */}
{form.modus === 'vorabitur' && (
<div className="p-4 bg-blue-50 border border-blue-200 rounded-lg space-y-4">
<div className="flex items-start gap-3">
<svg className="w-5 h-5 text-blue-600 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<div className="text-sm text-blue-800">
<p className="font-medium mb-1">Eigenen Erwartungshorizont erstellen</p>
<p>Waehlen Sie einen Aufgabentyp und beschreiben Sie die Aufgabenstellung. Der EH wird automatisch mit Ihrer Klausur verknuepft.</p>
</div>
</div>
{/* Aufgabentyp */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Aufgabentyp *</label>
{loadingTemplates ? (
<div className="flex items-center gap-2 text-sm text-slate-500">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600"></div>
Lade Vorlagen...
</div>
) : (
<select
value={ehForm.aufgabentyp}
onChange={(e) => onEhFormChange({ ...ehForm, aufgabentyp: e.target.value })}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white"
>
<option value="">-- Aufgabentyp waehlen --</option>
{templates.map(t => (
<option key={t.aufgabentyp} value={t.aufgabentyp}>{t.name}</option>
))}
</select>
)}
{ehForm.aufgabentyp && templates.find(t => t.aufgabentyp === ehForm.aufgabentyp) && (
<p className="mt-1 text-xs text-slate-500">
{templates.find(t => t.aufgabentyp === ehForm.aufgabentyp)?.description}
</p>
)}
</div>
{/* Text Details */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Texttitel (optional)</label>
<input
type="text"
value={ehForm.text_titel}
onChange={(e) => onEhFormChange({ ...ehForm, text_titel: e.target.value })}
placeholder="z.B. 'Die Verwandlung'"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Autor (optional)</label>
<input
type="text"
value={ehForm.text_autor}
onChange={(e) => onEhFormChange({ ...ehForm, text_autor: e.target.value })}
placeholder="z.B. 'Franz Kafka'"
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
/>
</div>
</div>
{/* Aufgabenstellung */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Aufgabenstellung *</label>
<textarea
value={ehForm.aufgabenstellung}
onChange={(e) => onEhFormChange({ ...ehForm, aufgabenstellung: e.target.value })}
placeholder="Beschreiben Sie hier die konkrete Aufgabenstellung fuer die Schueler..."
rows={4}
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent resize-none"
/>
<p className="mt-1 text-xs text-slate-500">
Die Aufgabenstellung wird zusammen mit dem Template in den Erwartungshorizont eingebunden.
</p>
</div>
</div>
)}
{/* Submit */}
<div className="flex gap-3 pt-4">
<button type="button" onClick={onCancel} className="px-4 py-2 text-slate-600 hover:bg-slate-100 rounded-lg">
Abbrechen
</button>
<button
type="submit"
disabled={creating}
className="flex-1 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
>
{creating ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
Erstelle...
</>
) : (
'Klausur erstellen'
)}
</button>
</div>
</form>
</div>
</div>
)
}