Files
breakpilot-compliance/admin-compliance/app/sdk/audit-llm/_components/ComplianceTab.tsx
Sharang Parnerkar 519ffdc8dc refactor(admin): split dsfa, audit-llm, quality pages
Extract components and hooks from oversized page files (563/561/520 LOC)
into colocated _components/ and _hooks/ subdirectories. All three
page.tsx files are now thin orchestrators under 300 LOC each
(dsfa: 216, audit-llm: 121, quality: 163). Zero behavior changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 13:20:17 +02:00

129 lines
5.8 KiB
TypeScript

'use client'
import { StatCard } from './StatCard'
import { formatNumber, type ComplianceReport } from './types'
export function ComplianceTab({ complianceReport }: { complianceReport: ComplianceReport }) {
return (
<div>
{/* Summary Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<StatCard label="Requests" value={formatNumber(complianceReport.total_requests)} />
<StatCard
label="PII-Vorfaelle"
value={formatNumber(complianceReport.pii_incidents)}
highlight={complianceReport.pii_incidents > 0}
/>
<StatCard
label="PII-Rate"
value={`${(complianceReport.pii_rate * 100).toFixed(1)}%`}
highlight={complianceReport.pii_rate > 0.05}
/>
<StatCard label="Redaction-Rate" value={`${(complianceReport.redaction_rate * 100).toFixed(1)}%`} />
</div>
{complianceReport.policy_violations > 0 && (
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-xl">
<div className="flex items-center gap-2 text-red-700 font-semibold">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
{complianceReport.policy_violations} Policy-Verletzungen im Zeitraum
</div>
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* PII Categories */}
<div className="bg-white rounded-xl border border-gray-200 p-5">
<h3 className="font-semibold text-gray-900 mb-4">PII-Kategorien</h3>
{Object.entries(complianceReport.top_pii_categories || {}).length === 0 ? (
<p className="text-gray-400 text-sm">Keine PII erkannt</p>
) : (
<div className="space-y-2">
{Object.entries(complianceReport.top_pii_categories).sort((a, b) => b[1] - a[1]).map(([cat, count]) => (
<div key={cat} className="flex items-center justify-between py-1">
<span className="text-sm text-gray-700">{cat}</span>
<span className="px-2 py-0.5 bg-red-50 text-red-700 rounded text-xs font-mono">{count}</span>
</div>
))}
</div>
)}
</div>
{/* Namespace Breakdown */}
<div className="bg-white rounded-xl border border-gray-200 p-5">
<h3 className="font-semibold text-gray-900 mb-4">Namespace-Analyse</h3>
{Object.entries(complianceReport.namespace_breakdown || {}).length === 0 ? (
<p className="text-gray-400 text-sm">Keine Namespace-Daten</p>
) : (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-100">
<th className="text-left py-2 text-gray-500 font-medium">Namespace</th>
<th className="text-right py-2 text-gray-500 font-medium">Requests</th>
<th className="text-right py-2 text-gray-500 font-medium">PII</th>
</tr>
</thead>
<tbody>
{Object.entries(complianceReport.namespace_breakdown).map(([ns, data]) => (
<tr key={ns} className="border-b border-gray-50">
<td className="py-2 font-mono text-xs">{ns}</td>
<td className="py-2 text-right">{formatNumber(data.requests)}</td>
<td className="py-2 text-right">
{data.pii_incidents > 0 ? (
<span className="text-red-600 font-medium">{data.pii_incidents}</span>
) : (
<span className="text-gray-400">0</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
{/* User Breakdown */}
{Object.entries(complianceReport.user_breakdown || {}).length > 0 && (
<div className="mt-6 bg-white rounded-xl border border-gray-200 p-5">
<h3 className="font-semibold text-gray-900 mb-4">Top-Nutzer</h3>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-100">
<th className="text-left py-2 text-gray-500 font-medium">User-ID</th>
<th className="text-right py-2 text-gray-500 font-medium">Requests</th>
<th className="text-right py-2 text-gray-500 font-medium">PII-Vorfaelle</th>
</tr>
</thead>
<tbody>
{Object.entries(complianceReport.user_breakdown)
.sort((a, b) => b[1].requests - a[1].requests)
.slice(0, 10)
.map(([userId, data]) => (
<tr key={userId} className="border-b border-gray-50">
<td className="py-2 font-mono text-xs">{userId}</td>
<td className="py-2 text-right">{formatNumber(data.requests)}</td>
<td className="py-2 text-right">
{data.pii_incidents > 0 ? (
<span className="text-red-600 font-medium">{data.pii_incidents}</span>
) : (
<span className="text-gray-400">0</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
)
}