[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:
131
website/app/admin/middleware/wizard/_components/TestCards.tsx
Normal file
131
website/app/admin/middleware/wizard/_components/TestCards.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user