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>
122 lines
4.6 KiB
TypeScript
122 lines
4.6 KiB
TypeScript
'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>
|
|
)
|
|
}
|