Files
breakpilot-compliance/admin-compliance/components/sdk/dsr/DSRWorkflowStepper.tsx
Benjamin Boenisch 4435e7ea0a Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance,
AI-Compliance-SDK, Consent-SDK, Developer-Portal,
PCA-Platform, DSMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:28 +01:00

177 lines
5.9 KiB
TypeScript

'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>
)
}