Files
breakpilot-compliance/admin-compliance/app/sdk/loeschfristen/_components/EditorTab.tsx
Sharang Parnerkar 6c883fb12e refactor(admin): split loeschfristen + dsb-portal page.tsx into colocated components
Split two oversized page files into _components/ directories following
Next.js 15 conventions and the 500-LOC hard cap:

- loeschfristen/page.tsx (2322 LOC -> 412 LOC orchestrator + 6 components)
- dsb-portal/page.tsx (2068 LOC -> 135 LOC orchestrator + 9 components)

All component files stay under 500 lines. Build verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:51:16 +02:00

171 lines
6.8 KiB
TypeScript

'use client'
import React from 'react'
import {
LoeschfristPolicy, LegalHold, StorageLocation,
} from '@/lib/sdk/loeschfristen-types'
import { renderStatusBadge } from './UebersichtTab'
import {
DataObjectSection, DeletionLogicSection, StorageSection,
ResponsibilitySection, VVTLinkSection, ReviewSection,
} from './EditorSections'
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
interface EditorTabProps {
policies: LoeschfristPolicy[]
editingId: string | null
editingPolicy: LoeschfristPolicy | null
vvtActivities: any[]
saving: boolean
setEditingId: (id: string | null) => void
setTab: (tab: 'uebersicht' | 'editor' | 'generator' | 'export') => void
updatePolicy: (id: string, updater: (p: LoeschfristPolicy) => LoeschfristPolicy) => void
createNewPolicy: () => void
deletePolicy: (policyId: string) => void
addLegalHold: (policyId: string) => void
removeLegalHold: (policyId: string, idx: number) => void
addStorageLocation: (policyId: string) => void
removeStorageLocation: (policyId: string, idx: number) => void
handleSaveAndClose: () => void
}
// ---------------------------------------------------------------------------
// No-selection view
// ---------------------------------------------------------------------------
function EditorNoSelection({
policies, setEditingId, createNewPolicy,
}: Pick<EditorTabProps, 'policies' | 'setEditingId' | 'createNewPolicy'>) {
return (
<div className="bg-white rounded-xl border border-gray-200 p-8">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
Loeschfrist zum Bearbeiten waehlen
</h3>
{policies.length === 0 ? (
<p className="text-gray-500">
Noch keine Loeschfristen vorhanden.{' '}
<button onClick={createNewPolicy}
className="text-purple-600 hover:text-purple-800 font-medium underline">
Neue Loeschfrist anlegen
</button>
</p>
) : (
<div className="space-y-2">
{policies.map((p) => (
<button key={p.policyId} onClick={() => setEditingId(p.policyId)}
className="w-full text-left px-4 py-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition flex items-center justify-between">
<div>
<span className="text-xs text-gray-400 font-mono mr-2">{p.policyId}</span>
<span className="font-medium text-gray-900">{p.dataObjectName || 'Ohne Bezeichnung'}</span>
</div>
{renderStatusBadge(p.status)}
</button>
))}
<button onClick={createNewPolicy}
className="w-full text-left px-4 py-3 border border-dashed border-gray-300 rounded-lg hover:bg-gray-50 transition text-purple-600 font-medium">
+ Neue Loeschfrist anlegen
</button>
</div>
)}
</div>
)
}
// ---------------------------------------------------------------------------
// Editor form
// ---------------------------------------------------------------------------
function EditorForm({
policy, vvtActivities, saving, setEditingId, setTab,
updatePolicy, deletePolicy, addLegalHold, removeLegalHold,
addStorageLocation, removeStorageLocation, handleSaveAndClose,
}: Omit<EditorTabProps, 'policies' | 'editingId' | 'editingPolicy' | 'createNewPolicy'> & {
policy: LoeschfristPolicy
}) {
const pid = policy.policyId
const set = <K extends keyof LoeschfristPolicy>(key: K, val: LoeschfristPolicy[K]) => {
updatePolicy(pid, (p) => ({ ...p, [key]: val }))
}
const updateLegalHoldItem = (idx: number, updater: (h: LegalHold) => LegalHold) => {
updatePolicy(pid, (p) => ({
...p, legalHolds: p.legalHolds.map((h, i) => (i === idx ? updater(h) : h)),
}))
}
const updateStorageLocationItem = (idx: number, updater: (s: StorageLocation) => StorageLocation) => {
updatePolicy(pid, (p) => ({
...p, storageLocations: p.storageLocations.map((s, i) => (i === idx ? updater(s) : s)),
}))
}
return (
<div className="space-y-6">
{/* Header with back button */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<button onClick={() => setEditingId(null)} className="text-gray-400 hover:text-gray-600 transition">
&larr; Zurueck
</button>
<h2 className="text-lg font-semibold text-gray-900">
{policy.dataObjectName || 'Neue Loeschfrist'}
</h2>
<span className="text-xs text-gray-400 font-mono">{policy.policyId}</span>
</div>
{renderStatusBadge(policy.status)}
</div>
<DataObjectSection policy={policy} set={set} />
<DeletionLogicSection policy={policy} pid={pid} set={set}
updateLegalHoldItem={updateLegalHoldItem} addLegalHold={addLegalHold} removeLegalHold={removeLegalHold} />
<StorageSection policy={policy} pid={pid} set={set}
updateStorageLocationItem={updateStorageLocationItem}
addStorageLocation={addStorageLocation} removeStorageLocation={removeStorageLocation} />
<ResponsibilitySection policy={policy} set={set} />
<VVTLinkSection policy={policy} pid={pid} vvtActivities={vvtActivities} updatePolicy={updatePolicy} />
<ReviewSection policy={policy} set={set} />
{/* Action buttons */}
<div className="flex items-center justify-between bg-white rounded-xl border border-gray-200 p-4">
<button
onClick={() => {
if (confirm('Moechten Sie diese Loeschfrist wirklich loeschen?')) {
deletePolicy(pid); setTab('uebersicht')
}
}}
className="text-red-600 hover:text-red-800 font-medium text-sm">
Loeschfrist loeschen
</button>
<div className="flex gap-3">
<button onClick={() => { setEditingId(null); setTab('uebersicht') }}
className="bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg px-4 py-2 font-medium transition">
Zurueck zur Uebersicht
</button>
<button onClick={handleSaveAndClose} disabled={saving}
className="bg-purple-600 text-white hover:bg-purple-700 disabled:opacity-50 rounded-lg px-4 py-2 font-medium transition">
{saving ? 'Speichern...' : 'Speichern & Schliessen'}
</button>
</div>
</div>
</div>
)
}
// ---------------------------------------------------------------------------
// Main export
// ---------------------------------------------------------------------------
export function EditorTab(props: EditorTabProps) {
if (!props.editingId || !props.editingPolicy) {
return (
<EditorNoSelection policies={props.policies}
setEditingId={props.setEditingId} createNewPolicy={props.createNewPolicy} />
)
}
return <EditorForm {...props} policy={props.editingPolicy} />
}