Files
breakpilot-lehrer/website/app/admin/middleware/test-wizard/_components/TestCards.tsx
Benjamin Admin 451365a312 [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>
2026-04-25 08:56:45 +02:00

132 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { TestResult, FullTestResults, EDUCATION_CONTENT } from './types'
export function EducationCard({ stepId }: { stepId: string }) {
const content = EDUCATION_CONTENT[stepId]
if (!content) return null
return (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6 mb-6">
<h3 className="text-lg font-semibold text-blue-800 mb-4 flex items-center">
<span className="mr-2">💡</span>
Warum ist das wichtig?
</h3>
<h4 className="text-md font-medium text-blue-700 mb-3">{content.title}</h4>
<div className="space-y-2 text-blue-900">
{content.content.map((line, index) => (
<p key={index} className={line.startsWith('•') ? 'ml-4' : ''}>
{line}
</p>
))}
</div>
</div>
)
}
export function TestResultCard({ result }: { result: TestResult }) {
const statusColors = {
passed: 'bg-green-100 border-green-300 text-green-800',
failed: 'bg-red-100 border-red-300 text-red-800',
pending: 'bg-yellow-100 border-yellow-300 text-yellow-800',
skipped: 'bg-gray-100 border-gray-300 text-gray-600',
}
const statusIcons = {
passed: '✓',
failed: '✗',
pending: '○',
skipped: '',
}
return (
<div className={`border rounded-lg p-4 mb-3 ${statusColors[result.status]}`}>
<div className="flex items-start justify-between">
<div className="flex-1">
<h4 className="font-medium flex items-center">
<span className="mr-2">{statusIcons[result.status]}</span>
{result.name}
</h4>
<p className="text-sm opacity-80 mt-1">{result.description}</p>
</div>
<span className="text-xs opacity-60">{result.duration_ms.toFixed(0)}ms</span>
</div>
<div className="mt-3 grid grid-cols-2 gap-4 text-sm">
<div>
<span className="font-medium">Erwartet:</span>
<code className="block mt-1 bg-white bg-opacity-50 px-2 py-1 rounded text-xs">
{result.expected}
</code>
</div>
<div>
<span className="font-medium">Erhalten:</span>
<code className="block mt-1 bg-white bg-opacity-50 px-2 py-1 rounded text-xs">
{result.actual}
</code>
</div>
</div>
{result.error_message && (
<div className="mt-2 text-xs text-red-700 bg-red-50 p-2 rounded">
Fehler: {result.error_message}
</div>
)}
</div>
)
}
export function TestSummaryCard({ results }: { results: FullTestResults }) {
const passRate = results.total_tests > 0
? ((results.total_passed / results.total_tests) * 100).toFixed(1)
: '0'
return (
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-xl font-bold mb-4">Test-Ergebnisse</h3>
{/* Summary Stats */}
<div className="grid grid-cols-4 gap-4 mb-6">
<div className="bg-gray-50 rounded-lg p-4 text-center">
<div className="text-3xl font-bold text-gray-700">{results.total_tests}</div>
<div className="text-sm text-gray-500">Gesamt</div>
</div>
<div className="bg-green-50 rounded-lg p-4 text-center">
<div className="text-3xl font-bold text-green-600">{results.total_passed}</div>
<div className="text-sm text-green-600">Bestanden</div>
</div>
<div className="bg-red-50 rounded-lg p-4 text-center">
<div className="text-3xl font-bold text-red-600">{results.total_failed}</div>
<div className="text-sm text-red-600">Fehlgeschlagen</div>
</div>
<div className={`rounded-lg p-4 text-center ${
parseFloat(passRate) >= 80 ? 'bg-green-50' : parseFloat(passRate) >= 50 ? 'bg-yellow-50' : 'bg-red-50'
}`}>
<div className={`text-3xl font-bold ${
parseFloat(passRate) >= 80 ? 'text-green-600' : parseFloat(passRate) >= 50 ? 'text-yellow-600' : 'text-red-600'
}`}>{passRate}%</div>
<div className="text-sm text-gray-500">Erfolgsrate</div>
</div>
</div>
{/* Category Results */}
<div className="space-y-4">
{results.categories.map((category) => (
<div key={category.category} className="border rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<h4 className="font-medium">{category.display_name}</h4>
<span className={`text-sm px-2 py-1 rounded ${
category.failed === 0 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'
}`}>
{category.passed}/{category.total} bestanden
</span>
</div>
<p className="text-sm text-gray-600">{category.description}</p>
</div>
))}
</div>
{/* Duration */}
<div className="mt-6 text-sm text-gray-500 text-right">
Gesamtdauer: {(results.duration_ms / 1000).toFixed(2)}s
</div>
</div>
)
}