[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,165 @@
import { useState } from 'react'
import type { ValidationResult, LegalReference } from './types'
interface OutputAreaProps {
generatedMessage: string
setGeneratedMessage: (msg: string) => void
subject: string
validation: ValidationResult | null
legalRefs: LegalReference[]
loading: boolean
onImprove: () => void
onCopy: () => void
}
export function OutputArea({
generatedMessage,
setGeneratedMessage,
subject,
validation,
legalRefs,
loading,
onImprove,
onCopy,
}: OutputAreaProps) {
const [showLegalInfo, setShowLegalInfo] = useState(false)
return (
<div className="lg:col-span-2 space-y-6">
{/* Generated Message */}
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold">Generierte Nachricht</h2>
{generatedMessage && (
<div className="flex gap-2">
<button
onClick={onImprove}
disabled={loading}
className="text-sm bg-green-100 text-green-700 px-3 py-1 rounded hover:bg-green-200"
>
Verbessern
</button>
<button
onClick={onCopy}
className="text-sm bg-gray-100 text-gray-700 px-3 py-1 rounded hover:bg-gray-200"
>
Kopieren
</button>
</div>
)}
</div>
{subject && (
<div className="mb-3 p-2 bg-gray-50 rounded">
<span className="text-sm font-medium text-gray-500">Betreff: </span>
<span className="text-sm">{subject}</span>
</div>
)}
{generatedMessage ? (
<textarea
value={generatedMessage}
onChange={(e) => setGeneratedMessage(e.target.value)}
className="w-full border rounded-md p-4 min-h-[400px] font-mono text-sm"
/>
) : (
<div className="text-gray-400 text-center py-20 border-2 border-dashed rounded-md">
Hier erscheint die generierte Nachricht
</div>
)}
</div>
{/* Validation */}
{validation && (
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">GFK-Analyse</h3>
{/* Score */}
<div className="mb-4">
<div className="flex items-center justify-between mb-1">
<span className="text-sm font-medium">GFK-Score</span>
<span className={`text-sm font-bold ${
validation.gfk_score >= 0.8 ? 'text-green-600' :
validation.gfk_score >= 0.6 ? 'text-yellow-600' : 'text-red-600'
}`}>
{Math.round(validation.gfk_score * 100)}%
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className={`h-2 rounded-full ${
validation.gfk_score >= 0.8 ? 'bg-green-500' :
validation.gfk_score >= 0.6 ? 'bg-yellow-500' : 'bg-red-500'
}`}
style={{ width: `${validation.gfk_score * 100}%` }}
/>
</div>
</div>
{/* Issues */}
{validation.issues.length > 0 && (
<div className="mb-4">
<h4 className="text-sm font-medium text-red-600 mb-2">
Verbesserungsvorschlaege:
</h4>
<ul className="text-sm space-y-1">
{validation.issues.map((issue, i) => (
<li key={i} className="flex items-start">
<span className="text-red-500 mr-2">!</span>
<span>{issue}</span>
</li>
))}
</ul>
</div>
)}
{/* Positive Elements */}
{validation.positive_elements.length > 0 && (
<div>
<h4 className="text-sm font-medium text-green-600 mb-2">
Positive Elemente:
</h4>
<ul className="text-sm space-y-1">
{validation.positive_elements.map((elem, i) => (
<li key={i} className="flex items-start">
<span className="text-green-500 mr-2">+</span>
<span>{elem}</span>
</li>
))}
</ul>
</div>
)}
</div>
)}
{/* Legal References */}
{legalRefs.length > 0 && (
<div className="bg-white rounded-lg shadow p-6">
<button
onClick={() => setShowLegalInfo(!showLegalInfo)}
className="flex items-center justify-between w-full text-left"
>
<h3 className="text-lg font-semibold">Rechtliche Grundlagen</h3>
<span>{showLegalInfo ? '-' : '+'}</span>
</button>
{showLegalInfo && (
<div className="mt-4 space-y-4">
{legalRefs.map((ref, i) => (
<div key={i} className="border rounded-md p-3 bg-gray-50">
<div className="font-medium">
{ref.law} {ref.paragraph}
</div>
<div className="text-sm text-gray-600">{ref.title}</div>
<div className="text-sm mt-1">{ref.summary}</div>
<div className="text-xs text-blue-600 mt-1">
Relevanz: {ref.relevance}
</div>
</div>
))}
</div>
)}
</div>
)}
</div>
)
}