refactor(admin): split dsr page.tsx into colocated components
Extract TabNavigation, StatCard, RequestCard, FilterBar, DSRCreateModal, DSRDetailPanel, DSRHeaderActions, and banner components (LoadingSpinner, SettingsTab, OverdueAlert, DeadlineInfoBox, EmptyState) into _components/ so page.tsx drops from 1019 to 247 LOC (under the 300 soft target). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
121
admin-compliance/app/sdk/dsr/_components/RequestCard.tsx
Normal file
121
admin-compliance/app/sdk/dsr/_components/RequestCard.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
DSRRequest,
|
||||
DSR_TYPE_INFO,
|
||||
DSR_STATUS_INFO,
|
||||
getDaysRemaining,
|
||||
isOverdue,
|
||||
isUrgent
|
||||
} from '@/lib/sdk/dsr/types'
|
||||
import { DSRWorkflowStepperCompact } from '@/components/sdk/dsr'
|
||||
|
||||
export function RequestCard({ request, onClick }: { request: DSRRequest; onClick?: () => void }) {
|
||||
const typeInfo = DSR_TYPE_INFO[request.type]
|
||||
const statusInfo = DSR_STATUS_INFO[request.status]
|
||||
const daysRemaining = getDaysRemaining(request.deadline.currentDeadline)
|
||||
const overdue = isOverdue(request)
|
||||
const urgent = isUrgent(request)
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={`
|
||||
bg-white rounded-xl border-2 p-6 hover:shadow-md transition-all cursor-pointer
|
||||
${overdue ? 'border-red-300 hover:border-red-400' :
|
||||
urgent ? 'border-orange-300 hover:border-orange-400' :
|
||||
request.status === 'completed' ? 'border-green-200 hover:border-green-300' :
|
||||
'border-gray-200 hover:border-purple-300'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
{/* Header Badges */}
|
||||
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
||||
<span className="text-xs text-gray-500 font-mono">
|
||||
{request.referenceNumber}
|
||||
</span>
|
||||
<span className={`px-2 py-1 text-xs rounded-full ${typeInfo.bgColor} ${typeInfo.color}`}>
|
||||
{typeInfo.article} {typeInfo.labelShort}
|
||||
</span>
|
||||
{!request.identityVerification.verified && request.status !== 'completed' && request.status !== 'rejected' && (
|
||||
<span className="px-2 py-1 text-xs bg-yellow-100 text-yellow-700 rounded-full flex items-center gap-1">
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
ID fehlt
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Requester Info */}
|
||||
<h3 className="text-lg font-semibold text-gray-900 truncate">
|
||||
{request.requester.name}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 truncate">{request.requester.email}</p>
|
||||
|
||||
{/* Workflow Status */}
|
||||
<div className="mt-3">
|
||||
<DSRWorkflowStepperCompact currentStatus={request.status} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Deadline */}
|
||||
<div className={`text-right ml-4 ${
|
||||
overdue ? 'text-red-600' :
|
||||
urgent ? 'text-orange-600' :
|
||||
'text-gray-500'
|
||||
}`}>
|
||||
<div className="text-sm font-medium">
|
||||
{request.status === 'completed' || request.status === 'rejected' || request.status === 'cancelled'
|
||||
? statusInfo.label
|
||||
: overdue
|
||||
? `${Math.abs(daysRemaining)} Tage ueberfaellig`
|
||||
: `${daysRemaining} Tage`
|
||||
}
|
||||
</div>
|
||||
<div className="text-xs mt-0.5">
|
||||
{new Date(request.receivedAt).toLocaleDateString('de-DE')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Notes Preview */}
|
||||
{request.notes && (
|
||||
<div className="mt-3 p-3 bg-gray-50 rounded-lg text-sm text-gray-600 line-clamp-2">
|
||||
{request.notes}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-100 flex items-center justify-between">
|
||||
<div className="text-sm text-gray-500">
|
||||
{request.assignment.assignedTo
|
||||
? `Zugewiesen: ${request.assignment.assignedTo}`
|
||||
: 'Nicht zugewiesen'
|
||||
}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{request.status !== 'completed' && request.status !== 'rejected' && request.status !== 'cancelled' && (
|
||||
<>
|
||||
{!request.identityVerification.verified && (
|
||||
<span className="px-3 py-1 text-sm bg-yellow-50 text-yellow-700 rounded-lg">
|
||||
ID pruefen
|
||||
</span>
|
||||
)}
|
||||
<span className="px-3 py-1 text-sm text-purple-600 hover:bg-purple-50 rounded-lg transition-colors">
|
||||
Bearbeiten
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{request.status === 'completed' && (
|
||||
<span className="px-3 py-1 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
Details
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user