refactor(admin): split SDKPipelineSidebar, SourcesTab, ScopeDecisionTab, ComplianceAdvisorWidget, EditorSections components

EditorSections.tsx (524 LOC) split into EditorSections.tsx (267 LOC) and
EditorSectionsB.tsx (279 LOC). DeletionLogicSection and StorageSection
moved to B; SetFn type canonical in B. EditorSections re-exports both
so all existing imports from EditorTab.tsx remain valid unchanged.
SDKPipelineSidebar (193), SourcesTab (311), ScopeDecisionTab (127),
ComplianceAdvisorWidget (265) were already under the 500-LOC hard cap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-18 00:10:34 +02:00
parent 91063f09b8
commit 788172e869
2 changed files with 292 additions and 270 deletions

View File

@@ -2,21 +2,25 @@
import React from 'react'
import {
LoeschfristPolicy, LegalHold, StorageLocation,
RETENTION_DRIVER_META, RetentionDriverType, DeletionMethodType,
DELETION_METHOD_LABELS, STATUS_LABELS,
STORAGE_LOCATION_LABELS, StorageLocationType, PolicyStatus,
ReviewInterval, DeletionTriggerLevel, RetentionUnit,
LegalHoldStatus, REVIEW_INTERVAL_LABELS,
LoeschfristPolicy,
STATUS_LABELS,
PolicyStatus,
ReviewInterval, REVIEW_INTERVAL_LABELS,
} from '@/lib/sdk/loeschfristen-types'
import { TagInput } from './TagInput'
import { renderTriggerBadge } from './UebersichtTab'
// ---------------------------------------------------------------------------
// Shared type
// Shared type (defined in EditorSectionsB to avoid circular imports)
// ---------------------------------------------------------------------------
export type SetFn = <K extends keyof LoeschfristPolicy>(key: K, val: LoeschfristPolicy[K]) => void
export type { SetFn } from './EditorSectionsB'
import type { SetFn } from './EditorSectionsB'
// ---------------------------------------------------------------------------
// Re-exports from EditorSectionsB (keeps existing import paths working)
// ---------------------------------------------------------------------------
export { DeletionLogicSection, StorageSection } from './EditorSectionsB'
// ---------------------------------------------------------------------------
// Sektion 1: Datenobjekt
@@ -58,267 +62,6 @@ export function DataObjectSection({ policy, set }: { policy: LoeschfristPolicy;
)
}
// ---------------------------------------------------------------------------
// Sektion 2: 3-stufige Loeschlogik
// ---------------------------------------------------------------------------
export function DeletionLogicSection({
policy, pid, set, updateLegalHoldItem, addLegalHold, removeLegalHold,
}: {
policy: LoeschfristPolicy; pid: string; set: SetFn
updateLegalHoldItem: (idx: number, updater: (h: LegalHold) => LegalHold) => void
addLegalHold: (policyId: string) => void
removeLegalHold: (policyId: string, idx: number) => 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">2. 3-stufige Loeschlogik</h3>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Loeschausloeser (Trigger-Stufe)</label>
<div className="space-y-2">
{(['PURPOSE_END', 'RETENTION_DRIVER', 'LEGAL_HOLD'] as DeletionTriggerLevel[]).map((trigger) => (
<label key={trigger} className="flex items-start gap-3 p-3 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer">
<input type="radio" name={`trigger-${pid}`} checked={policy.deletionTrigger === trigger}
onChange={() => set('deletionTrigger', trigger)} className="mt-0.5 text-purple-600 focus:ring-purple-500" />
<div>
<div className="flex items-center gap-2">{renderTriggerBadge(trigger)}</div>
<p className="text-xs text-gray-500 mt-1">
{trigger === 'PURPOSE_END' && 'Loeschung nach Wegfall des Verarbeitungszwecks'}
{trigger === 'RETENTION_DRIVER' && 'Loeschung nach Ablauf gesetzlicher oder vertraglicher Aufbewahrungsfrist'}
{trigger === 'LEGAL_HOLD' && 'Loeschung durch aktiven Legal Hold blockiert'}
</p>
</div>
</label>
))}
</div>
</div>
{policy.deletionTrigger === 'RETENTION_DRIVER' && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Aufbewahrungstreiber</label>
<select value={policy.retentionDriver}
onChange={(e) => {
const driver = e.target.value as RetentionDriverType
const meta = RETENTION_DRIVER_META[driver]
set('retentionDriver', driver)
if (meta) {
set('retentionDuration', meta.defaultDuration)
set('retentionUnit', meta.defaultUnit as RetentionUnit)
set('retentionDescription', meta.description)
}
}}
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="">Bitte waehlen...</option>
{Object.entries(RETENTION_DRIVER_META).map(([key, meta]) => (
<option key={key} value={key}>{meta.label}</option>
))}
</select>
</div>
)}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Aufbewahrungsdauer</label>
<input type="number" min={0} value={policy.retentionDuration}
onChange={(e) => set('retentionDuration', parseInt(e.target.value) || 0)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Einheit</label>
<select value={policy.retentionUnit} onChange={(e) => set('retentionUnit', e.target.value as RetentionUnit)}
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="DAYS">Tage</option>
<option value="MONTHS">Monate</option>
<option value="YEARS">Jahre</option>
</select>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Beschreibung der Aufbewahrungspflicht</label>
<input type="text" value={policy.retentionDescription} onChange={(e) => set('retentionDescription', e.target.value)}
placeholder="z.B. Handelsrechtliche Aufbewahrungspflicht gem. HGB"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Startereignis (Fristbeginn)</label>
<input type="text" value={policy.startEvent} onChange={(e) => set('startEvent', e.target.value)}
placeholder="z.B. Ende des Geschaeftsjahres, Vertragsende..."
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
{/* Legal Holds */}
<div className="border-t border-gray-200 pt-4">
<div className="flex items-center justify-between mb-3">
<h4 className="text-sm font-semibold text-gray-800">Legal Holds</h4>
<label className="flex items-center gap-2 text-sm">
<input type="checkbox" checked={policy.hasActiveLegalHold}
onChange={(e) => set('hasActiveLegalHold', e.target.checked)}
className="text-purple-600 focus:ring-purple-500 rounded" />
Aktiver Legal Hold
</label>
</div>
{policy.legalHolds.length > 0 && (
<div className="overflow-x-auto mb-3">
<table className="w-full text-sm border border-gray-200 rounded-lg">
<thead>
<tr className="bg-gray-50">
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Bezeichnung</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Grund</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Status</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Erstellt am</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Aktion</th>
</tr>
</thead>
<tbody>
{policy.legalHolds.map((hold, idx) => (
<tr key={idx} className="border-t border-gray-100">
<td className="px-3 py-2">
<input type="text" value={hold.name}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, name: e.target.value }))}
placeholder="Bezeichnung"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<input type="text" value={hold.reason}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, reason: e.target.value }))}
placeholder="Grund"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<select value={hold.status}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, status: e.target.value as LegalHoldStatus }))}
className="px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500">
<option value="ACTIVE">Aktiv</option>
<option value="RELEASED">Aufgehoben</option>
<option value="EXPIRED">Abgelaufen</option>
</select>
</td>
<td className="px-3 py-2">
<input type="date" value={hold.createdAt}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, createdAt: e.target.value }))}
className="px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<button onClick={() => removeLegalHold(pid, idx)}
className="text-red-500 hover:text-red-700 text-sm font-medium">Entfernen</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<button onClick={() => addLegalHold(pid)} className="text-sm text-purple-600 hover:text-purple-800 font-medium">
+ Legal Hold hinzufuegen
</button>
</div>
</div>
)
}
// ---------------------------------------------------------------------------
// Sektion 3: Speicherorte & Loeschmethode
// ---------------------------------------------------------------------------
export function StorageSection({
policy, pid, set, updateStorageLocationItem, addStorageLocation, removeStorageLocation,
}: {
policy: LoeschfristPolicy; pid: string; set: SetFn
updateStorageLocationItem: (idx: number, updater: (s: StorageLocation) => StorageLocation) => void
addStorageLocation: (policyId: string) => void
removeStorageLocation: (policyId: string, idx: number) => 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">3. Speicherorte & Loeschmethode</h3>
{policy.storageLocations.length > 0 && (
<div className="overflow-x-auto">
<table className="w-full text-sm border border-gray-200 rounded-lg">
<thead>
<tr className="bg-gray-50">
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Name</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Typ</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Backup</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Anbieter</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Loeschfaehig</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Aktion</th>
</tr>
</thead>
<tbody>
{policy.storageLocations.map((loc, idx) => (
<tr key={idx} className="border-t border-gray-100">
<td className="px-3 py-2">
<input type="text" value={loc.name}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, name: e.target.value }))}
placeholder="Name"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<select value={loc.type}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, type: e.target.value as StorageLocationType }))}
className="px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500">
{Object.entries(STORAGE_LOCATION_LABELS).map(([key, label]) => (
<option key={key} value={key}>{label}</option>
))}
</select>
</td>
<td className="px-3 py-2 text-center">
<input type="checkbox" checked={loc.isBackup}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, isBackup: e.target.checked }))}
className="text-purple-600 focus:ring-purple-500 rounded" />
</td>
<td className="px-3 py-2">
<input type="text" value={loc.provider}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, provider: e.target.value }))}
placeholder="Anbieter"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2 text-center">
<input type="checkbox" checked={loc.deletionCapable}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, deletionCapable: e.target.checked }))}
className="text-purple-600 focus:ring-purple-500 rounded" />
</td>
<td className="px-3 py-2">
<button onClick={() => removeStorageLocation(pid, idx)}
className="text-red-500 hover:text-red-700 text-sm font-medium">Entfernen</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<button onClick={() => addStorageLocation(pid)} className="text-sm text-purple-600 hover:text-purple-800 font-medium">
+ Speicherort hinzufuegen
</button>
<div className="border-t border-gray-200 pt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Loeschmethode</label>
<select value={policy.deletionMethod} onChange={(e) => set('deletionMethod', e.target.value as DeletionMethodType)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
{Object.entries(DELETION_METHOD_LABELS).map(([key, label]) => (
<option key={key} value={key}>{label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Details zur Loeschmethode</label>
<textarea value={policy.deletionMethodDetail} onChange={(e) => set('deletionMethodDetail', e.target.value)} rows={2}
placeholder="Weitere Details zum Loeschverfahren..."
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
</div>
</div>
)
}
// ---------------------------------------------------------------------------
// Sektion 4: Verantwortlichkeit
// ---------------------------------------------------------------------------

View File

@@ -0,0 +1,279 @@
'use client'
import React from 'react'
import {
LoeschfristPolicy, LegalHold, StorageLocation,
RETENTION_DRIVER_META, RetentionDriverType, DeletionMethodType,
DELETION_METHOD_LABELS,
STORAGE_LOCATION_LABELS, StorageLocationType,
RetentionUnit,
LegalHoldStatus,
} from '@/lib/sdk/loeschfristen-types'
import { renderTriggerBadge } from './UebersichtTab'
// ---------------------------------------------------------------------------
// Shared type (canonical home; EditorSections re-exports this)
// ---------------------------------------------------------------------------
export type SetFn = <K extends keyof LoeschfristPolicy>(key: K, val: LoeschfristPolicy[K]) => void
// ---------------------------------------------------------------------------
// Sektion 2: 3-stufige Loeschlogik
// ---------------------------------------------------------------------------
export function DeletionLogicSection({
policy, pid, set, updateLegalHoldItem, addLegalHold, removeLegalHold,
}: {
policy: LoeschfristPolicy; pid: string; set: SetFn
updateLegalHoldItem: (idx: number, updater: (h: LegalHold) => LegalHold) => void
addLegalHold: (policyId: string) => void
removeLegalHold: (policyId: string, idx: number) => 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">2. 3-stufige Loeschlogik</h3>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Loeschausloeser (Trigger-Stufe)</label>
<div className="space-y-2">
{(['PURPOSE_END', 'RETENTION_DRIVER', 'LEGAL_HOLD'] as const).map((trigger) => (
<label key={trigger} className="flex items-start gap-3 p-3 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer">
<input type="radio" name={`trigger-${pid}`} checked={policy.deletionTrigger === trigger}
onChange={() => set('deletionTrigger', trigger)} className="mt-0.5 text-purple-600 focus:ring-purple-500" />
<div>
<div className="flex items-center gap-2">{renderTriggerBadge(trigger)}</div>
<p className="text-xs text-gray-500 mt-1">
{trigger === 'PURPOSE_END' && 'Loeschung nach Wegfall des Verarbeitungszwecks'}
{trigger === 'RETENTION_DRIVER' && 'Loeschung nach Ablauf gesetzlicher oder vertraglicher Aufbewahrungsfrist'}
{trigger === 'LEGAL_HOLD' && 'Loeschung durch aktiven Legal Hold blockiert'}
</p>
</div>
</label>
))}
</div>
</div>
{policy.deletionTrigger === 'RETENTION_DRIVER' && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Aufbewahrungstreiber</label>
<select value={policy.retentionDriver}
onChange={(e) => {
const driver = e.target.value as RetentionDriverType
const meta = RETENTION_DRIVER_META[driver]
set('retentionDriver', driver)
if (meta) {
set('retentionDuration', meta.defaultDuration)
set('retentionUnit', meta.defaultUnit as RetentionUnit)
set('retentionDescription', meta.description)
}
}}
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="">Bitte waehlen...</option>
{Object.entries(RETENTION_DRIVER_META).map(([key, meta]) => (
<option key={key} value={key}>{meta.label}</option>
))}
</select>
</div>
)}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Aufbewahrungsdauer</label>
<input type="number" min={0} value={policy.retentionDuration}
onChange={(e) => set('retentionDuration', parseInt(e.target.value) || 0)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Einheit</label>
<select value={policy.retentionUnit} onChange={(e) => set('retentionUnit', e.target.value as RetentionUnit)}
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="DAYS">Tage</option>
<option value="MONTHS">Monate</option>
<option value="YEARS">Jahre</option>
</select>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Beschreibung der Aufbewahrungspflicht</label>
<input type="text" value={policy.retentionDescription} onChange={(e) => set('retentionDescription', e.target.value)}
placeholder="z.B. Handelsrechtliche Aufbewahrungspflicht gem. HGB"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Startereignis (Fristbeginn)</label>
<input type="text" value={policy.startEvent} onChange={(e) => set('startEvent', e.target.value)}
placeholder="z.B. Ende des Geschaeftsjahres, Vertragsende..."
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
{/* Legal Holds */}
<div className="border-t border-gray-200 pt-4">
<div className="flex items-center justify-between mb-3">
<h4 className="text-sm font-semibold text-gray-800">Legal Holds</h4>
<label className="flex items-center gap-2 text-sm">
<input type="checkbox" checked={policy.hasActiveLegalHold}
onChange={(e) => set('hasActiveLegalHold', e.target.checked)}
className="text-purple-600 focus:ring-purple-500 rounded" />
Aktiver Legal Hold
</label>
</div>
{policy.legalHolds.length > 0 && (
<div className="overflow-x-auto mb-3">
<table className="w-full text-sm border border-gray-200 rounded-lg">
<thead>
<tr className="bg-gray-50">
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Bezeichnung</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Grund</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Status</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Erstellt am</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Aktion</th>
</tr>
</thead>
<tbody>
{policy.legalHolds.map((hold, idx) => (
<tr key={idx} className="border-t border-gray-100">
<td className="px-3 py-2">
<input type="text" value={hold.name}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, name: e.target.value }))}
placeholder="Bezeichnung"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<input type="text" value={hold.reason}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, reason: e.target.value }))}
placeholder="Grund"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<select value={hold.status}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, status: e.target.value as LegalHoldStatus }))}
className="px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500">
<option value="ACTIVE">Aktiv</option>
<option value="RELEASED">Aufgehoben</option>
<option value="EXPIRED">Abgelaufen</option>
</select>
</td>
<td className="px-3 py-2">
<input type="date" value={hold.createdAt}
onChange={(e) => updateLegalHoldItem(idx, (h) => ({ ...h, createdAt: e.target.value }))}
className="px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<button onClick={() => removeLegalHold(pid, idx)}
className="text-red-500 hover:text-red-700 text-sm font-medium">Entfernen</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<button onClick={() => addLegalHold(pid)} className="text-sm text-purple-600 hover:text-purple-800 font-medium">
+ Legal Hold hinzufuegen
</button>
</div>
</div>
)
}
// ---------------------------------------------------------------------------
// Sektion 3: Speicherorte & Loeschmethode
// ---------------------------------------------------------------------------
export function StorageSection({
policy, pid, set, updateStorageLocationItem, addStorageLocation, removeStorageLocation,
}: {
policy: LoeschfristPolicy; pid: string; set: SetFn
updateStorageLocationItem: (idx: number, updater: (s: StorageLocation) => StorageLocation) => void
addStorageLocation: (policyId: string) => void
removeStorageLocation: (policyId: string, idx: number) => 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">3. Speicherorte & Loeschmethode</h3>
{policy.storageLocations.length > 0 && (
<div className="overflow-x-auto">
<table className="w-full text-sm border border-gray-200 rounded-lg">
<thead>
<tr className="bg-gray-50">
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Name</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Typ</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Backup</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Anbieter</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Loeschfaehig</th>
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500">Aktion</th>
</tr>
</thead>
<tbody>
{policy.storageLocations.map((loc, idx) => (
<tr key={idx} className="border-t border-gray-100">
<td className="px-3 py-2">
<input type="text" value={loc.name}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, name: e.target.value }))}
placeholder="Name"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2">
<select value={loc.type}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, type: e.target.value as StorageLocationType }))}
className="px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500">
{Object.entries(STORAGE_LOCATION_LABELS).map(([key, label]) => (
<option key={key} value={key}>{label}</option>
))}
</select>
</td>
<td className="px-3 py-2 text-center">
<input type="checkbox" checked={loc.isBackup}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, isBackup: e.target.checked }))}
className="text-purple-600 focus:ring-purple-500 rounded" />
</td>
<td className="px-3 py-2">
<input type="text" value={loc.provider}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, provider: e.target.value }))}
placeholder="Anbieter"
className="w-full px-2 py-1 border border-gray-200 rounded text-sm focus:ring-1 focus:ring-purple-500" />
</td>
<td className="px-3 py-2 text-center">
<input type="checkbox" checked={loc.deletionCapable}
onChange={(e) => updateStorageLocationItem(idx, (s) => ({ ...s, deletionCapable: e.target.checked }))}
className="text-purple-600 focus:ring-purple-500 rounded" />
</td>
<td className="px-3 py-2">
<button onClick={() => removeStorageLocation(pid, idx)}
className="text-red-500 hover:text-red-700 text-sm font-medium">Entfernen</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<button onClick={() => addStorageLocation(pid)} className="text-sm text-purple-600 hover:text-purple-800 font-medium">
+ Speicherort hinzufuegen
</button>
<div className="border-t border-gray-200 pt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Loeschmethode</label>
<select value={policy.deletionMethod} onChange={(e) => set('deletionMethod', e.target.value as DeletionMethodType)}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
{Object.entries(DELETION_METHOD_LABELS).map(([key, label]) => (
<option key={key} value={key}>{label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Details zur Loeschmethode</label>
<textarea value={policy.deletionMethodDetail} onChange={(e) => set('deletionMethodDetail', e.target.value)} rows={2}
placeholder="Weitere Details zum Loeschverfahren..."
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
</div>
</div>
</div>
)
}