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>
153 lines
6.8 KiB
TypeScript
153 lines
6.8 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import type { ApiGdprProcess, DsrOverview } from '../_types'
|
|
import { gdprProcesses } from '../_data'
|
|
import { ApiGdprProcessEditor } from './ApiGdprProcessEditor'
|
|
|
|
export function GdprTab({
|
|
router,
|
|
apiGdprProcesses,
|
|
gdprLoading,
|
|
savingProcessId,
|
|
saveApiGdprProcess,
|
|
dsrCounts,
|
|
dsrOverview,
|
|
}: {
|
|
router: { push: (path: string) => void }
|
|
apiGdprProcesses: ApiGdprProcess[]
|
|
gdprLoading: boolean
|
|
savingProcessId: string | null
|
|
saveApiGdprProcess: (p: { id: string; title: string; description: string }) => void
|
|
dsrCounts: Record<string, number>
|
|
dsrOverview: DsrOverview
|
|
}) {
|
|
return (
|
|
<div className="p-6">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<div>
|
|
<h2 className="text-lg font-semibold text-slate-900">DSGVO Betroffenenrechte</h2>
|
|
<p className="text-sm text-slate-500 mt-1">Artikel 15-21 Prozesse und Vorlagen</p>
|
|
</div>
|
|
<button
|
|
onClick={() => router.push('/sdk/dsr')}
|
|
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors text-sm font-medium"
|
|
>
|
|
+ DSR Anfrage erstellen
|
|
</button>
|
|
</div>
|
|
|
|
{/* Info Banner */}
|
|
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4 mb-6">
|
|
<div className="flex items-start gap-3">
|
|
<span className="text-2xl">*</span>
|
|
<div>
|
|
<h4 className="font-medium text-purple-900">Data Subject Rights (DSR)</h4>
|
|
<p className="text-sm text-purple-700 mt-1">
|
|
Hier verwalten Sie alle DSGVO-Anfragen. Jeder Artikel hat definierte Prozesse, SLAs und automatisierte Workflows.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* API-backed GDPR Processes */}
|
|
{gdprLoading ? (
|
|
<div className="text-center py-8 text-slate-500">Lade DSGVO-Prozesse...</div>
|
|
) : apiGdprProcesses.length > 0 ? (
|
|
<div className="space-y-4 mb-8">
|
|
<h3 className="text-sm font-semibold text-slate-700 uppercase tracking-wider mb-3">Konfigurierte Prozesse</h3>
|
|
{apiGdprProcesses.map((process) => (
|
|
<ApiGdprProcessEditor
|
|
key={process.id}
|
|
process={process}
|
|
saving={savingProcessId === process.id}
|
|
onSave={(title, description) => saveApiGdprProcess({ id: process.id, title, description })}
|
|
/>
|
|
))}
|
|
</div>
|
|
) : null}
|
|
|
|
{/* Static GDPR Process Cards (always shown as reference) */}
|
|
<div className="space-y-4">
|
|
<h3 className="text-sm font-semibold text-slate-700 uppercase tracking-wider mb-3">DSGVO Artikel-Uebersicht</h3>
|
|
{gdprProcesses.map((process) => (
|
|
<div
|
|
key={process.article}
|
|
className="border border-slate-200 rounded-xl p-5 hover:border-purple-300 transition-colors bg-white"
|
|
>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex items-start gap-4">
|
|
<div className="w-12 h-12 bg-purple-100 text-purple-700 rounded-lg flex items-center justify-center font-bold text-lg">
|
|
{process.article}
|
|
</div>
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<h3 className="font-semibold text-slate-900">{process.title}</h3>
|
|
<span className="px-2 py-0.5 bg-green-100 text-green-700 rounded text-xs">Aktiv</span>
|
|
</div>
|
|
<p className="text-sm text-slate-600 mb-3">{process.description}</p>
|
|
|
|
{/* Actions */}
|
|
<div className="flex flex-wrap gap-2 mb-3">
|
|
{process.actions.map((action, idx) => (
|
|
<span key={idx} className="px-2 py-1 bg-slate-100 text-slate-600 rounded text-xs">
|
|
{action}
|
|
</span>
|
|
))}
|
|
</div>
|
|
|
|
{/* SLA */}
|
|
<div className="flex items-center gap-4 text-sm">
|
|
<span className="text-slate-500">
|
|
SLA: <span className="font-medium text-slate-700">{process.sla}</span>
|
|
</span>
|
|
<span className="text-slate-300">|</span>
|
|
<span className="text-slate-500">
|
|
Offene Anfragen: <span className={`font-medium ${(dsrCounts[process.article] || 0) > 0 ? 'text-orange-600' : 'text-slate-700'}`}>{dsrCounts[process.article] || 0}</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-2">
|
|
<Link
|
|
href={`/sdk/dsr?type=${process.article === '15' ? 'access' : process.article === '16' ? 'rectification' : process.article === '17' ? 'erasure' : process.article === '18' ? 'restriction' : process.article === '20' ? 'portability' : 'objection'}`}
|
|
className="px-3 py-1.5 text-sm text-white bg-purple-600 hover:bg-purple-700 rounded-lg text-center"
|
|
>
|
|
Anfragen
|
|
</Link>
|
|
<button className="px-3 py-1.5 text-sm text-slate-600 hover:text-slate-900 border border-slate-300 rounded-lg hover:border-slate-400">
|
|
Vorlage
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* DSR Request Statistics */}
|
|
<div className="mt-8 pt-6 border-t border-slate-200">
|
|
<h3 className="text-sm font-semibold text-slate-700 uppercase tracking-wider mb-4">DSR Uebersicht</h3>
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<div className="bg-slate-50 rounded-lg p-4 text-center">
|
|
<div className={`text-2xl font-bold ${dsrOverview.open > 0 ? 'text-blue-600' : 'text-slate-900'}`}>{dsrOverview.open}</div>
|
|
<div className="text-xs text-slate-500 mt-1">Offen</div>
|
|
</div>
|
|
<div className="bg-green-50 rounded-lg p-4 text-center">
|
|
<div className="text-2xl font-bold text-green-700">{dsrOverview.completed}</div>
|
|
<div className="text-xs text-slate-500 mt-1">Erledigt</div>
|
|
</div>
|
|
<div className="bg-yellow-50 rounded-lg p-4 text-center">
|
|
<div className="text-2xl font-bold text-yellow-700">{dsrOverview.in_progress}</div>
|
|
<div className="text-xs text-slate-500 mt-1">In Bearbeitung</div>
|
|
</div>
|
|
<div className="bg-red-50 rounded-lg p-4 text-center">
|
|
<div className={`text-2xl font-bold ${dsrOverview.overdue > 0 ? 'text-red-700' : 'text-slate-400'}`}>{dsrOverview.overdue}</div>
|
|
<div className="text-xs text-slate-500 mt-1">Ueberfaellig</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|