[split-required] Split remaining 500-680 LOC files (final batch)

website (17 pages + 3 components):
- multiplayer/wizard, middleware/wizard+test-wizard, communication
- builds/wizard, staff-search, voice, sbom/wizard
- foerderantrag, mail/tasks, tools/communication, sbom
- compliance/evidence, uni-crawler, brandbook (already done)
- CollectionsTab, IngestionTab, RiskHeatmap

backend-lehrer (5 files):
- letters_api (641 → 2), certificates_api (636 → 2)
- alerts_agent/db/models (636 → 3)
- llm_gateway/communication_service (614 → 2)
- game/database already done in prior batch

klausur-service (2 files):
- hybrid_vocab_extractor (664 → 2)
- klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2)

voice-service (3 files):
- bqas/rag_judge (618 → 3), runner (529 → 2)
- enhanced_task_orchestrator (519 → 2)

studio-v2 (6 files):
- korrektur/[klausurId] (578 → 4), fairness (569 → 2)
- AlertsWizard (552 → 2), OnboardingWizard (513 → 2)
- korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-25 08:56:45 +02:00
parent b4613e26f3
commit 451365a312
115 changed files with 10694 additions and 13839 deletions

View File

@@ -0,0 +1,210 @@
import { useState } from 'react'
import type { Option, GFKPrinciple } from './types'
interface InputFormProps {
communicationType: string
setCommunicationType: (v: string) => void
tone: string
setTone: (v: string) => void
state: string
setState: (v: string) => void
studentName: string
setStudentName: (v: string) => void
parentName: string
setParentName: (v: string) => void
situation: string
setSituation: (v: string) => void
additionalInfo: string
setAdditionalInfo: (v: string) => void
types: Option[]
tones: Option[]
states: Option[]
gfkPrinciples: GFKPrinciple[]
error: string | null
loading: boolean
onGenerate: () => void
}
export function InputForm({
communicationType, setCommunicationType,
tone, setTone,
state, setState,
studentName, setStudentName,
parentName, setParentName,
situation, setSituation,
additionalInfo, setAdditionalInfo,
types, tones, states,
gfkPrinciples,
error, loading,
onGenerate,
}: InputFormProps) {
return (
<div className="lg:col-span-1 space-y-6">
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Nachricht erstellen</h2>
{/* Communication Type */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Art der Kommunikation *
</label>
<select
value={communicationType}
onChange={(e) => setCommunicationType(e.target.value)}
className="w-full border rounded-md p-2"
>
{types.map((t) => (
<option key={t.value} value={t.value}>
{t.label}
</option>
))}
</select>
</div>
{/* Tone */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Tonalitaet
</label>
<select
value={tone}
onChange={(e) => setTone(e.target.value)}
className="w-full border rounded-md p-2"
>
{tones.map((t) => (
<option key={t.value} value={t.value}>
{t.label}
</option>
))}
</select>
</div>
{/* State */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Bundesland (fuer rechtliche Referenzen)
</label>
<select
value={state}
onChange={(e) => setState(e.target.value)}
className="w-full border rounded-md p-2"
>
{states.map((s) => (
<option key={s.value} value={s.value}>
{s.label}
</option>
))}
</select>
</div>
{/* Student Name */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Name des Schuelers/der Schuelerin *
</label>
<input
type="text"
value={studentName}
onChange={(e) => setStudentName(e.target.value)}
placeholder="z.B. Max"
className="w-full border rounded-md p-2"
/>
</div>
{/* Parent Name */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Anrede der Eltern *
</label>
<input
type="text"
value={parentName}
onChange={(e) => setParentName(e.target.value)}
placeholder="z.B. Frau Mueller"
className="w-full border rounded-md p-2"
/>
</div>
{/* Situation */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Beschreibung der Situation *
</label>
<textarea
value={situation}
onChange={(e) => setSituation(e.target.value)}
placeholder="Beschreiben Sie die Situation sachlich und konkret..."
rows={4}
className="w-full border rounded-md p-2"
/>
</div>
{/* Additional Info */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
Zusaetzliche Informationen (optional)
</label>
<textarea
value={additionalInfo}
onChange={(e) => setAdditionalInfo(e.target.value)}
placeholder="Besondere Umstaende, gewuenschte Termine, etc."
rows={2}
className="w-full border rounded-md p-2"
/>
</div>
{/* Error */}
{error && (
<div className="mb-4 p-3 bg-red-50 text-red-700 rounded-md text-sm">
{error}
</div>
)}
{/* Generate Button */}
<button
onClick={onGenerate}
disabled={loading}
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50"
>
{loading ? 'Wird generiert...' : 'Nachricht generieren'}
</button>
</div>
{/* GFK Info Toggle */}
<GFKInfoPanel gfkPrinciples={gfkPrinciples} />
</div>
)
}
function GFKInfoPanel({ gfkPrinciples }: { gfkPrinciples: GFKPrinciple[] }) {
const [showGFKInfo, setShowGFKInfo] = useState(false)
return (
<div className="bg-white rounded-lg shadow p-6">
<button
onClick={() => setShowGFKInfo(!showGFKInfo)}
className="flex items-center justify-between w-full text-left"
>
<span className="font-semibold">Was ist GFK?</span>
<span>{showGFKInfo ? '-' : '+'}</span>
</button>
{showGFKInfo && (
<div className="mt-4 space-y-4">
<p className="text-sm text-gray-600">
Die Gewaltfreie Kommunikation (GFK) nach Marshall Rosenberg
ist ein Kommunikationsmodell, das auf vier Schritten basiert:
</p>
{gfkPrinciples.map((p, i) => (
<div key={i} className="border-l-4 border-blue-500 pl-3">
<h4 className="font-medium">{i + 1}. {p.principle}</h4>
<p className="text-sm text-gray-600">{p.description}</p>
<p className="text-xs text-gray-500 italic mt-1">
Beispiel: {p.example}
</p>
</div>
))}
</div>
)}
</div>
)
}