refactor(admin): split consent-management, control-library, incidents, training pages

Agent-completed splits committed after agents hit rate limits before
committing their work. All 4 pages now under 500 LOC:

- consent-management: 1303 -> 193 LOC (+ 7 _components, _hooks, _data, _types)
- control-library: 1210 -> 298 LOC (+ _components, _types)
- incidents: 1150 -> 373 LOC (+ _components)
- training: 1127 -> 366 LOC (+ _components)

Verification: next build clean (142 pages generated).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-12 15:52:45 +02:00
parent ff9f5e849c
commit 375b34a0d8
40 changed files with 4319 additions and 3745 deletions

View File

@@ -0,0 +1,85 @@
'use client'
import type { TrainingStats, DeadlineInfo } from '@/lib/sdk/training/types'
import { KPICard } from './KPICard'
interface OverviewTabProps {
stats: TrainingStats
deadlines: DeadlineInfo[]
escalationResult: { total_checked: number; escalated: number } | null
onDismissEscalation: () => void
}
export function OverviewTab({ stats, deadlines, escalationResult, onDismissEscalation }: OverviewTabProps) {
return (
<div className="space-y-6">
{escalationResult && (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 flex items-center justify-between">
<div>
<span className="font-medium text-blue-900">Eskalation abgeschlossen: </span>
<span className="text-blue-700">
{escalationResult.total_checked} Zuweisungen geprueft, {escalationResult.escalated} eskaliert
</span>
</div>
<button onClick={onDismissEscalation} className="text-blue-400 hover:text-blue-600 text-lg font-bold">×</button>
</div>
)}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<KPICard label="Module" value={stats.total_modules} />
<KPICard label="Zuweisungen" value={stats.total_assignments} />
<KPICard label="Abschlussrate" value={`${stats.completion_rate.toFixed(1)}%`} color={stats.completion_rate >= 80 ? 'green' : stats.completion_rate >= 50 ? 'yellow' : 'red'} />
<KPICard label="Ueberfaellig" value={stats.overdue_count} color={stats.overdue_count > 0 ? 'red' : 'green'} />
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<KPICard label="Ausstehend" value={stats.pending_count} />
<KPICard label="In Bearbeitung" value={stats.in_progress_count} />
<KPICard label="Avg. Quiz-Score" value={`${stats.avg_quiz_score.toFixed(1)}%`} />
<KPICard label="Deadlines (7d)" value={stats.upcoming_deadlines} color={stats.upcoming_deadlines > 5 ? 'yellow' : 'green'} />
</div>
{/* Status Bar */}
<div className="bg-white border rounded-lg p-4">
<h3 className="text-sm font-medium text-gray-700 mb-3">Status-Verteilung</h3>
{stats.total_assignments > 0 && (
<div className="flex gap-1 h-6 rounded-full overflow-hidden bg-gray-100">
{stats.completed_count > 0 && <div className="bg-green-500" style={{ width: `${(stats.completed_count / stats.total_assignments) * 100}%` }} title={`Abgeschlossen: ${stats.completed_count}`} />}
{stats.in_progress_count > 0 && <div className="bg-blue-500" style={{ width: `${(stats.in_progress_count / stats.total_assignments) * 100}%` }} title={`In Bearbeitung: ${stats.in_progress_count}`} />}
{stats.pending_count > 0 && <div className="bg-gray-400" style={{ width: `${(stats.pending_count / stats.total_assignments) * 100}%` }} title={`Ausstehend: ${stats.pending_count}`} />}
{stats.overdue_count > 0 && <div className="bg-red-500" style={{ width: `${(stats.overdue_count / stats.total_assignments) * 100}%` }} title={`Ueberfaellig: ${stats.overdue_count}`} />}
</div>
)}
<div className="flex gap-4 mt-2 text-xs text-gray-500">
<span className="flex items-center gap-1"><span className="w-3 h-3 rounded-full bg-green-500 inline-block" /> Abgeschlossen</span>
<span className="flex items-center gap-1"><span className="w-3 h-3 rounded-full bg-blue-500 inline-block" /> In Bearbeitung</span>
<span className="flex items-center gap-1"><span className="w-3 h-3 rounded-full bg-gray-400 inline-block" /> Ausstehend</span>
<span className="flex items-center gap-1"><span className="w-3 h-3 rounded-full bg-red-500 inline-block" /> Ueberfaellig</span>
</div>
</div>
{/* Deadlines */}
{deadlines.length > 0 && (
<div className="bg-white border rounded-lg p-4">
<h3 className="text-sm font-medium text-gray-700 mb-3">Naechste Deadlines</h3>
<div className="space-y-2">
{deadlines.slice(0, 5).map(d => (
<div key={d.assignment_id} className="flex items-center justify-between text-sm">
<div>
<span className="font-medium">{d.module_title}</span>
<span className="text-gray-500 ml-2">({d.user_name})</span>
</div>
<span className={`px-2 py-0.5 rounded text-xs ${
d.days_left <= 0 ? 'bg-red-100 text-red-700' :
d.days_left <= 7 ? 'bg-yellow-100 text-yellow-700' :
'bg-gray-100 text-gray-600'
}`}>
{d.days_left <= 0 ? `${Math.abs(d.days_left)} Tage ueberfaellig` : `${d.days_left} Tage`}
</span>
</div>
))}
</div>
</div>
)}
</div>
)
}