fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
176
admin-v2/components/sdk/dsr/DSRWorkflowStepper.tsx
Normal file
176
admin-v2/components/sdk/dsr/DSRWorkflowStepper.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { DSRStatus, DSR_STATUS_INFO } from '@/lib/sdk/dsr/types'
|
||||
|
||||
interface WorkflowStep {
|
||||
id: DSRStatus
|
||||
label: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
const WORKFLOW_STEPS: WorkflowStep[] = [
|
||||
{ id: 'intake', label: 'Eingang', description: 'Anfrage dokumentiert' },
|
||||
{ id: 'identity_verification', label: 'ID-Pruefung', description: 'Identitaet verifizieren' },
|
||||
{ id: 'processing', label: 'Bearbeitung', description: 'Anfrage bearbeiten' },
|
||||
{ id: 'completed', label: 'Abschluss', description: 'Antwort versenden' }
|
||||
]
|
||||
|
||||
interface DSRWorkflowStepperProps {
|
||||
currentStatus: DSRStatus
|
||||
onStepClick?: (status: DSRStatus) => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function DSRWorkflowStepper({
|
||||
currentStatus,
|
||||
onStepClick,
|
||||
className = ''
|
||||
}: DSRWorkflowStepperProps) {
|
||||
const currentIndex = WORKFLOW_STEPS.findIndex(s => s.id === currentStatus)
|
||||
const isRejectedOrCancelled = currentStatus === 'rejected' || currentStatus === 'cancelled'
|
||||
|
||||
const getStepState = (index: number): 'completed' | 'current' | 'upcoming' => {
|
||||
if (isRejectedOrCancelled) {
|
||||
return index <= currentIndex ? 'completed' : 'upcoming'
|
||||
}
|
||||
if (index < currentIndex) return 'completed'
|
||||
if (index === currentIndex) return 'current'
|
||||
return 'upcoming'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${className}`}>
|
||||
<div className="flex items-center justify-between">
|
||||
{WORKFLOW_STEPS.map((step, index) => {
|
||||
const state = getStepState(index)
|
||||
const isLast = index === WORKFLOW_STEPS.length - 1
|
||||
|
||||
return (
|
||||
<React.Fragment key={step.id}>
|
||||
{/* Step */}
|
||||
<div
|
||||
className={`flex flex-col items-center ${
|
||||
onStepClick && state !== 'upcoming' ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
onClick={() => onStepClick && state !== 'upcoming' && onStepClick(step.id)}
|
||||
>
|
||||
{/* Circle */}
|
||||
<div
|
||||
className={`
|
||||
w-10 h-10 rounded-full flex items-center justify-center font-medium text-sm
|
||||
transition-all duration-200
|
||||
${state === 'completed'
|
||||
? 'bg-green-500 text-white'
|
||||
: state === 'current'
|
||||
? 'bg-purple-600 text-white ring-4 ring-purple-100'
|
||||
: 'bg-gray-200 text-gray-400'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{state === 'completed' ? (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
index + 1
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Label */}
|
||||
<div className="mt-2 text-center">
|
||||
<div
|
||||
className={`text-sm font-medium ${
|
||||
state === 'current' ? 'text-purple-600' :
|
||||
state === 'completed' ? 'text-green-600' : 'text-gray-400'
|
||||
}`}
|
||||
>
|
||||
{step.label}
|
||||
</div>
|
||||
{step.description && (
|
||||
<div className="text-xs text-gray-500 mt-0.5 hidden sm:block">
|
||||
{step.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Connector Line */}
|
||||
{!isLast && (
|
||||
<div
|
||||
className={`
|
||||
flex-1 h-1 mx-2 rounded-full
|
||||
${state === 'completed' || getStepState(index + 1) === 'completed' || getStepState(index + 1) === 'current'
|
||||
? 'bg-green-500'
|
||||
: 'bg-gray-200'
|
||||
}
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Rejected/Cancelled Badge */}
|
||||
{isRejectedOrCancelled && (
|
||||
<div className={`
|
||||
mt-4 px-4 py-2 rounded-lg text-center text-sm font-medium
|
||||
${currentStatus === 'rejected'
|
||||
? 'bg-red-100 text-red-700 border border-red-200'
|
||||
: 'bg-gray-100 text-gray-700 border border-gray-200'
|
||||
}
|
||||
`}>
|
||||
{currentStatus === 'rejected' ? 'Anfrage wurde abgelehnt' : 'Anfrage wurde storniert'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Compact version for list views
|
||||
export function DSRWorkflowStepperCompact({
|
||||
currentStatus,
|
||||
className = ''
|
||||
}: {
|
||||
currentStatus: DSRStatus
|
||||
className?: string
|
||||
}) {
|
||||
const statusInfo = DSR_STATUS_INFO[currentStatus]
|
||||
const currentIndex = WORKFLOW_STEPS.findIndex(s => s.id === currentStatus)
|
||||
const totalSteps = WORKFLOW_STEPS.length
|
||||
const isTerminal = currentStatus === 'rejected' || currentStatus === 'cancelled' || currentStatus === 'completed'
|
||||
|
||||
return (
|
||||
<div className={`flex items-center gap-2 ${className}`}>
|
||||
{/* Mini progress dots */}
|
||||
<div className="flex items-center gap-1">
|
||||
{WORKFLOW_STEPS.map((step, index) => (
|
||||
<div
|
||||
key={step.id}
|
||||
className={`
|
||||
w-2 h-2 rounded-full transition-all
|
||||
${index < currentIndex
|
||||
? 'bg-green-500'
|
||||
: index === currentIndex
|
||||
? isTerminal
|
||||
? currentStatus === 'completed'
|
||||
? 'bg-green-500'
|
||||
: currentStatus === 'rejected'
|
||||
? 'bg-red-500'
|
||||
: 'bg-gray-500'
|
||||
: 'bg-purple-500'
|
||||
: 'bg-gray-200'
|
||||
}
|
||||
`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Status label */}
|
||||
<span className={`text-xs font-medium px-2 py-0.5 rounded-full ${statusInfo.bgColor} ${statusInfo.color}`}>
|
||||
{statusInfo.label}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user