refactor(admin): split loeschfristen and vvt pages

Reduce both page.tsx files below the 500-LOC hard cap by extracting
all inline tab components and API helpers into colocated _components/.
- loeschfristen/page.tsx: 2720 → 467 LOC
- vvt/page.tsx: 2297 → 256 LOC
New files: LoeschkonzeptTab, loeschfristen/api, TabDokument, TabProcessor
Updated: TabVerzeichnis (template picker + badge), vvt/api (template helpers)
Fixed: VVTLinkSection wrong field name (linkedVVTActivityIds), VendorLinkSection added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-16 17:11:45 +02:00
parent 2ade65431a
commit e0c1d21879
10 changed files with 1279 additions and 4424 deletions

View File

@@ -370,18 +370,18 @@ export function VVTLinkSection({
Verknuepfen Sie diese Loeschfrist mit einer Verarbeitungstaetigkeit aus Ihrem VVT.
</p>
<div className="space-y-2">
{policy.linkedVvtIds && policy.linkedVvtIds.length > 0 && (
{policy.linkedVVTActivityIds && policy.linkedVVTActivityIds.length > 0 && (
<div className="mb-3">
<label className="block text-xs font-medium text-gray-500 mb-1">Verknuepfte Taetigkeiten:</label>
<div className="flex flex-wrap gap-1">
{policy.linkedVvtIds.map((vvtId: string) => {
{policy.linkedVVTActivityIds.map((vvtId: string) => {
const activity = vvtActivities.find((a: any) => a.id === vvtId)
return (
<span key={vvtId} className="inline-flex items-center gap-1 bg-blue-100 text-blue-800 text-xs font-medium px-2 py-0.5 rounded-full">
{activity?.name || vvtId}
<button type="button"
onClick={() => updatePolicy(pid, (p) => ({
...p, linkedVvtIds: (p.linkedVvtIds || []).filter((id: string) => id !== vvtId),
...p, linkedVVTActivityIds: (p.linkedVVTActivityIds || []).filter((id: string) => id !== vvtId),
}))}
className="text-blue-600 hover:text-blue-900">x</button>
</span>
@@ -393,15 +393,15 @@ export function VVTLinkSection({
<select
onChange={(e) => {
const val = e.target.value
if (val && !(policy.linkedVvtIds || []).includes(val)) {
updatePolicy(pid, (p) => ({ ...p, linkedVvtIds: [...(p.linkedVvtIds || []), val] }))
if (val && !(policy.linkedVVTActivityIds || []).includes(val)) {
updatePolicy(pid, (p) => ({ ...p, linkedVVTActivityIds: [...(p.linkedVVTActivityIds || []), val] }))
}
e.target.value = ''
}}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
<option value="">Verarbeitungstaetigkeit verknuepfen...</option>
{vvtActivities
.filter((a: any) => !(policy.linkedVvtIds || []).includes(a.id))
.filter((a: any) => !(policy.linkedVVTActivityIds || []).includes(a.id))
.map((a: any) => (<option key={a.id} value={a.id}>{a.name || a.id}</option>))}
</select>
</div>
@@ -415,6 +415,70 @@ export function VVTLinkSection({
)
}
// ---------------------------------------------------------------------------
// Sektion 5b: Auftragsverarbeiter-Verknuepfung
// ---------------------------------------------------------------------------
export function VendorLinkSection({
policy, pid, vendorList, updatePolicy,
}: {
policy: LoeschfristPolicy; pid: string; vendorList: Array<{id: string; name: string}>
updatePolicy: (id: string, updater: (p: LoeschfristPolicy) => LoeschfristPolicy) => void
}) {
return (
<div className="bg-white rounded-xl border border-gray-200 p-6 space-y-4">
<h3 className="text-lg font-semibold text-gray-900">5b. Verknuepfte Auftragsverarbeiter</h3>
{vendorList.length > 0 ? (
<div>
<p className="text-sm text-gray-500 mb-3">
Verknuepfen Sie diese Loeschfrist mit relevanten Auftragsverarbeitern.
</p>
<div className="space-y-2">
{policy.linkedVendorIds && policy.linkedVendorIds.length > 0 && (
<div className="mb-3">
<label className="block text-xs font-medium text-gray-500 mb-1">Verknuepfte Auftragsverarbeiter:</label>
<div className="flex flex-wrap gap-1">
{policy.linkedVendorIds.map((vendorId: string) => {
const vendor = vendorList.find((v) => v.id === vendorId)
return (
<span key={vendorId} className="inline-flex items-center gap-1 bg-orange-100 text-orange-800 text-xs font-medium px-2 py-0.5 rounded-full">
{vendor?.name || vendorId}
<button type="button"
onClick={() => updatePolicy(pid, (p) => ({
...p, linkedVendorIds: (p.linkedVendorIds || []).filter((id: string) => id !== vendorId),
}))}
className="text-orange-600 hover:text-orange-900">x</button>
</span>
)
})}
</div>
</div>
)}
<select
onChange={(e) => {
const val = e.target.value
if (val && !(policy.linkedVendorIds || []).includes(val)) {
updatePolicy(pid, (p) => ({ ...p, linkedVendorIds: [...(p.linkedVendorIds || []), val] }))
}
e.target.value = ''
}}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
<option value="">Auftragsverarbeiter verknuepfen...</option>
{vendorList
.filter((v) => !(policy.linkedVendorIds || []).includes(v.id))
.map((v) => (<option key={v.id} value={v.id}>{v.name || v.id}</option>))}
</select>
</div>
</div>
) : (
<p className="text-sm text-gray-400">
Keine Auftragsverarbeiter gefunden. Erstellen Sie zuerst Auftragsverarbeiter im Vendor-Compliance-Modul, um hier Verknuepfungen herstellen zu koennen.
</p>
)}
</div>
)
}
// ---------------------------------------------------------------------------
// Sektion 6: Review-Einstellungen
// ---------------------------------------------------------------------------