Extract components and hooks from oversized pages into colocated _components/ and _hooks/ subdirectories to enforce the 500-LOC hard cap. page.tsx files reduced to 205, 121, and 136 LOC respectively. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
3.7 KiB
TypeScript
76 lines
3.7 KiB
TypeScript
'use client'
|
|
|
|
import { GCIBreakdown } from '@/lib/sdk/gci/types'
|
|
import { getScoreColor } from '@/lib/sdk/gci/types'
|
|
import { LoadingSpinner } from './GCIHelpers'
|
|
|
|
export function BreakdownTab({ breakdown }: { breakdown: GCIBreakdown | null; loading: boolean }) {
|
|
if (!breakdown) return <LoadingSpinner />
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
<h3 className="text-base font-semibold text-gray-900 mb-4">Level 1: Modul-Scores</h3>
|
|
<div className="overflow-x-auto">
|
|
<table className="min-w-full text-sm">
|
|
<thead>
|
|
<tr className="border-b border-gray-200">
|
|
<th className="text-left py-2 pr-4 font-medium text-gray-600">Modul</th>
|
|
<th className="text-left py-2 pr-4 font-medium text-gray-600">Kategorie</th>
|
|
<th className="text-right py-2 pr-4 font-medium text-gray-600">Zugewiesen</th>
|
|
<th className="text-right py-2 pr-4 font-medium text-gray-600">Abgeschlossen</th>
|
|
<th className="text-right py-2 pr-4 font-medium text-gray-600">Raw Score</th>
|
|
<th className="text-right py-2 pr-4 font-medium text-gray-600">Validitaet</th>
|
|
<th className="text-right py-2 font-medium text-gray-600">Final</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{breakdown.level1_modules.map(m => (
|
|
<tr key={m.module_id} className="border-b border-gray-100 hover:bg-gray-50">
|
|
<td className="py-2 pr-4 font-medium text-gray-900">{m.module_name}</td>
|
|
<td className="py-2 pr-4">
|
|
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-700">
|
|
{m.category}
|
|
</span>
|
|
</td>
|
|
<td className="py-2 pr-4 text-right text-gray-600">{m.assigned}</td>
|
|
<td className="py-2 pr-4 text-right text-gray-600">{m.completed}</td>
|
|
<td className="py-2 pr-4 text-right text-gray-600">{(m.raw_score * 100).toFixed(1)}%</td>
|
|
<td className="py-2 pr-4 text-right text-gray-600">{(m.validity_factor * 100).toFixed(0)}%</td>
|
|
<td className={`py-2 text-right font-semibold ${getScoreColor(m.final_score * 100)}`}>
|
|
{(m.final_score * 100).toFixed(1)}%
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
<h3 className="text-base font-semibold text-gray-900 mb-4">Level 2: Regulierungsbereiche (risikogewichtet)</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{breakdown.level2_areas.map(area => (
|
|
<div key={area.area_id} className="border border-gray-200 rounded-lg p-4">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<h4 className="font-medium text-gray-900">{area.area_name}</h4>
|
|
<span className={`text-lg font-bold ${getScoreColor(area.area_score)}`}>
|
|
{area.area_score.toFixed(1)}%
|
|
</span>
|
|
</div>
|
|
<div className="space-y-1">
|
|
{area.modules.map(m => (
|
|
<div key={m.module_id} className="flex justify-between text-xs text-gray-500">
|
|
<span>{m.module_name}</span>
|
|
<span>{(m.final_score * 100).toFixed(0)}% (w:{m.risk_weight.toFixed(1)})</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|