Files
breakpilot-compliance/admin-compliance/app/sdk/dsr/[requestId]/page.tsx
Sharang Parnerkar cc3a9a37dc refactor(admin): split dsr/[requestId] page.tsx into colocated components
Split the 854-line DSR detail page into colocated components under
_components/ and a data-loading hook under _hooks/. No behavior changes.

page.tsx is now 172 LOC, all extracted files under 300 LOC.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:20:24 +02:00

173 lines
6.0 KiB
TypeScript

'use client'
import React, { useState } from 'react'
import Link from 'next/link'
import { useParams } from 'next/navigation'
import { DSR_TYPE_INFO, isOverdue, isUrgent } from '@/lib/sdk/dsr/types'
import {
DSRWorkflowStepper,
DSRIdentityModal,
DSRCommunicationLog,
} from '@/components/sdk/dsr'
import { DSRHeader } from './_components/DSRHeader'
import { DSRDetailsTab } from './_components/DSRDetailsTab'
import { DSRTypeSpecificTab } from './_components/DSRTypeSpecificTab'
import { DSRSidebar } from './_components/DSRSidebar'
import { useDSRDetail } from './_hooks/useDSRDetail'
export default function DSRDetailPage() {
const params = useParams()
const requestId = params.requestId as string
const [showIdentityModal, setShowIdentityModal] = useState(false)
const [activeContentTab, setActiveContentTab] = useState<'details' | 'communication' | 'type-specific'>('details')
const {
request,
setRequest,
communications,
history,
exceptionChecks,
isLoading,
handleVerifyIdentity,
handleAssign,
handleExtendDeadline,
handleComplete,
handleReject,
handleSendCommunication,
handleExceptionCheckChange,
} = useDSRDetail(requestId)
if (isLoading) {
return (
<div className="flex items-center justify-center py-12">
<svg className="animate-spin w-8 h-8 text-purple-600" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
</div>
)
}
if (!request) {
return (
<div className="bg-white rounded-xl border border-gray-200 p-12 text-center">
<div className="w-16 h-16 mx-auto bg-red-100 rounded-full flex items-center justify-center mb-4">
<svg className="w-8 h-8 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900">Anfrage nicht gefunden</h3>
<p className="mt-2 text-gray-500">
Die angeforderte DSR-Anfrage existiert nicht oder wurde geloescht.
</p>
<Link
href="/sdk/dsr"
className="mt-4 inline-flex items-center gap-2 px-4 py-2 text-purple-600 hover:bg-purple-50 rounded-lg transition-colors"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
Zurueck zur Uebersicht
</Link>
</div>
)
}
const typeInfo = DSR_TYPE_INFO[request.type]
const overdue = isOverdue(request)
const urgent = isUrgent(request)
return (
<div className="space-y-6">
<DSRHeader request={request} />
{/* Workflow Stepper */}
<div className={`
bg-white rounded-xl border-2 p-6
${overdue ? 'border-red-200' : urgent ? 'border-orange-200' : 'border-gray-200'}
`}>
<DSRWorkflowStepper currentStatus={request.status} />
</div>
{/* Main Content: 2/3 + 1/3 Layout */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Left Column - 2/3 */}
<div className="lg:col-span-2 space-y-6">
{/* Content Tabs */}
<div className="bg-white rounded-xl border border-gray-200">
<div className="border-b border-gray-200">
<nav className="flex -mb-px">
{[
{ id: 'details', label: 'Details' },
{ id: 'communication', label: 'Kommunikation' },
{ id: 'type-specific', label: typeInfo.labelShort }
].map(tab => (
<button
key={tab.id}
onClick={() => setActiveContentTab(tab.id as any)}
className={`
px-6 py-4 text-sm font-medium border-b-2 transition-colors
${activeContentTab === tab.id
? 'border-purple-600 text-purple-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}
`}
>
{tab.label}
</button>
))}
</nav>
</div>
<div className="p-6">
{activeContentTab === 'details' && (
<DSRDetailsTab
request={request}
onShowIdentityModal={() => setShowIdentityModal(true)}
/>
)}
{activeContentTab === 'communication' && (
<DSRCommunicationLog
communications={communications}
onSendMessage={handleSendCommunication}
/>
)}
{activeContentTab === 'type-specific' && (
<DSRTypeSpecificTab
request={request}
setRequest={setRequest}
exceptionChecks={exceptionChecks}
onExceptionCheckChange={handleExceptionCheckChange}
/>
)}
</div>
</div>
</div>
{/* Right Column - 1/3 Sidebar */}
<DSRSidebar
request={request}
history={history}
onVerifyIdentity={() => setShowIdentityModal(true)}
onExtendDeadline={handleExtendDeadline}
onComplete={handleComplete}
onReject={handleReject}
onAssign={handleAssign}
/>
</div>
{/* Identity Modal */}
<DSRIdentityModal
isOpen={showIdentityModal}
onClose={() => setShowIdentityModal(false)}
onVerify={handleVerifyIdentity}
requesterName={request.requester.name}
requesterEmail={request.requester.email}
/>
</div>
)
}