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>
171 lines
6.8 KiB
TypeScript
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">
|
|
← 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} />
|
|
}
|