'use client'
import React, { useState, useEffect, useMemo } from 'react'
import Link from 'next/link'
import { useSDK } from '@/lib/sdk'
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
import {
DSRRequest,
DSRType,
DSRStatus,
DSRStatistics,
DSR_TYPE_INFO,
DSR_STATUS_INFO,
getDaysRemaining,
isOverdue,
isUrgent
} from '@/lib/sdk/dsr/types'
import { fetchSDKDSRList } from '@/lib/sdk/dsr/api'
import { DSRWorkflowStepperCompact } from '@/components/sdk/dsr'
// =============================================================================
// TYPES
// =============================================================================
type TabId = 'overview' | 'intake' | 'processing' | 'completed' | 'settings'
interface Tab {
id: TabId
label: string
count?: number
countColor?: string
}
// =============================================================================
// COMPONENTS
// =============================================================================
function TabNavigation({
tabs,
activeTab,
onTabChange
}: {
tabs: Tab[]
activeTab: TabId
onTabChange: (tab: TabId) => void
}) {
return (
{tabs.map(tab => (
onTabChange(tab.id)}
className={`
px-4 py-3 text-sm font-medium border-b-2 transition-colors
${activeTab === tab.id
? 'border-purple-600 text-purple-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}
`}
>
{tab.label}
{tab.count !== undefined && tab.count > 0 && (
{tab.count}
)}
))}
)
}
function StatCard({
label,
value,
color = 'gray',
icon,
trend
}: {
label: string
value: number | string
color?: 'gray' | 'blue' | 'yellow' | 'red' | 'green' | 'purple'
icon?: React.ReactNode
trend?: { value: number; label: string }
}) {
const colorClasses = {
gray: 'border-gray-200 text-gray-900',
blue: 'border-blue-200 text-blue-600',
yellow: 'border-yellow-200 text-yellow-600',
red: 'border-red-200 text-red-600',
green: 'border-green-200 text-green-600',
purple: 'border-purple-200 text-purple-600'
}
return (
{label}
{value}
{trend && (
= 0 ? 'text-green-600' : 'text-red-600'}`}>
{trend.value >= 0 ? '+' : ''}{trend.value} {trend.label}
)}
{icon && (
{icon}
)}
)
}
function RequestCard({ request }: { request: DSRRequest }) {
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 (
{/* Header Badges */}
{request.referenceNumber}
{typeInfo.article} {typeInfo.labelShort}
{!request.identityVerification.verified && request.status !== 'completed' && request.status !== 'rejected' && (
ID fehlt
)}
{/* Requester Info */}
{request.requester.name}
{request.requester.email}
{/* Workflow Status */}
{/* Right Side - Deadline */}
{request.status === 'completed' || request.status === 'rejected' || request.status === 'cancelled'
? statusInfo.label
: overdue
? `${Math.abs(daysRemaining)} Tage ueberfaellig`
: `${daysRemaining} Tage`
}
{new Date(request.receivedAt).toLocaleDateString('de-DE')}
{/* Notes Preview */}
{request.notes && (
{request.notes}
)}
{/* Footer */}
{request.assignment.assignedTo
? `Zugewiesen: ${request.assignment.assignedTo}`
: 'Nicht zugewiesen'
}
{request.status !== 'completed' && request.status !== 'rejected' && request.status !== 'cancelled' && (
<>
{!request.identityVerification.verified && (
ID pruefen
)}
Bearbeiten
>
)}
{request.status === 'completed' && (
Details
)}
)
}
function FilterBar({
selectedType,
selectedStatus,
selectedPriority,
onTypeChange,
onStatusChange,
onPriorityChange,
onClear
}: {
selectedType: DSRType | 'all'
selectedStatus: DSRStatus | 'all'
selectedPriority: string
onTypeChange: (type: DSRType | 'all') => void
onStatusChange: (status: DSRStatus | 'all') => void
onPriorityChange: (priority: string) => void
onClear: () => void
}) {
const hasFilters = selectedType !== 'all' || selectedStatus !== 'all' || selectedPriority !== 'all'
return (
Filter:
{/* Type Filter */}
onTypeChange(e.target.value as DSRType | 'all')}
className="px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
>
Alle Typen
{Object.entries(DSR_TYPE_INFO).map(([type, info]) => (
{info.article} - {info.labelShort}
))}
{/* Status Filter */}
onStatusChange(e.target.value as DSRStatus | 'all')}
className="px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
>
Alle Status
{Object.entries(DSR_STATUS_INFO).map(([status, info]) => (
{info.label}
))}
{/* Priority Filter */}
onPriorityChange(e.target.value)}
className="px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
>
Alle Prioritaeten
Kritisch
Hoch
Normal
Niedrig
{/* Clear Filters */}
{hasFilters && (
Filter zuruecksetzen
)}
)
}
// =============================================================================
// MAIN PAGE
// =============================================================================
export default function DSRPage() {
const { state } = useSDK()
const [activeTab, setActiveTab] = useState('overview')
const [requests, setRequests] = useState([])
const [statistics, setStatistics] = useState(null)
const [isLoading, setIsLoading] = useState(true)
// Filters
const [selectedType, setSelectedType] = useState('all')
const [selectedStatus, setSelectedStatus] = useState('all')
const [selectedPriority, setSelectedPriority] = useState('all')
// Load data from SDK backend
useEffect(() => {
const loadData = async () => {
setIsLoading(true)
try {
const { requests: dsrRequests, statistics: dsrStats } = await fetchSDKDSRList()
setRequests(dsrRequests)
setStatistics(dsrStats)
} catch (error) {
console.error('Failed to load DSR data:', error)
} finally {
setIsLoading(false)
}
}
loadData()
}, [])
// Calculate tab counts
const tabCounts = useMemo(() => {
return {
intake: requests.filter(r => r.status === 'intake' || r.status === 'identity_verification').length,
processing: requests.filter(r => r.status === 'processing').length,
completed: requests.filter(r => r.status === 'completed' || r.status === 'rejected' || r.status === 'cancelled').length,
overdue: requests.filter(r => isOverdue(r)).length
}
}, [requests])
// Filter requests based on active tab and filters
const filteredRequests = useMemo(() => {
let filtered = [...requests]
// Tab-based filtering
if (activeTab === 'intake') {
filtered = filtered.filter(r => r.status === 'intake' || r.status === 'identity_verification')
} else if (activeTab === 'processing') {
filtered = filtered.filter(r => r.status === 'processing')
} else if (activeTab === 'completed') {
filtered = filtered.filter(r => r.status === 'completed' || r.status === 'rejected' || r.status === 'cancelled')
}
// Type filter
if (selectedType !== 'all') {
filtered = filtered.filter(r => r.type === selectedType)
}
// Status filter
if (selectedStatus !== 'all') {
filtered = filtered.filter(r => r.status === selectedStatus)
}
// Priority filter
if (selectedPriority !== 'all') {
filtered = filtered.filter(r => r.priority === selectedPriority)
}
// Sort by urgency
return filtered.sort((a, b) => {
const getUrgency = (r: DSRRequest) => {
if (r.status === 'completed' || r.status === 'rejected' || r.status === 'cancelled') return 100
const days = getDaysRemaining(r.deadline.currentDeadline)
if (days < 0) return -100 + days // Overdue items first
return days
}
return getUrgency(a) - getUrgency(b)
})
}, [requests, activeTab, selectedType, selectedStatus, selectedPriority])
const tabs: Tab[] = [
{ id: 'overview', label: 'Uebersicht' },
{ id: 'intake', label: 'Eingang', count: tabCounts.intake, countColor: 'bg-blue-100 text-blue-600' },
{ id: 'processing', label: 'In Bearbeitung', count: tabCounts.processing, countColor: 'bg-yellow-100 text-yellow-600' },
{ id: 'completed', label: 'Abgeschlossen', count: tabCounts.completed, countColor: 'bg-green-100 text-green-600' },
{ id: 'settings', label: 'Einstellungen' }
]
const stepInfo = STEP_EXPLANATIONS['dsr']
const clearFilters = () => {
setSelectedType('all')
setSelectedStatus('all')
setSelectedPriority('all')
}
return (
{/* Step Header */}
Anfrage erfassen
{/* Tab Navigation */}
{/* Loading State */}
{isLoading ? (
) : activeTab === 'settings' ? (
/* Settings Tab */
Einstellungen
DSR-Portal-Einstellungen, E-Mail-Vorlagen und Workflow-Konfiguration
werden in einer spaeteren Version verfuegbar sein.
) : (
<>
{/* Statistics (Overview Tab) */}
{activeTab === 'overview' && statistics && (
0 ? 'red' : 'green'}
/>
)}
{/* Overdue Alert */}
{tabCounts.overdue > 0 && (
Achtung: {tabCounts.overdue} ueberfaellige Anfrage(n)
Die gesetzliche Frist ist abgelaufen. Handeln Sie umgehend, um Bussgelder zu vermeiden.
{
setActiveTab('overview')
setSelectedStatus('all')
}}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm font-medium"
>
Anzeigen
)}
{/* Info Box (Overview Tab) */}
{activeTab === 'overview' && (
Fristen beachten
Nach Art. 12 DSGVO muessen Anfragen innerhalb von einem Monat beantwortet werden.
Eine Verlaengerung um zwei weitere Monate ist bei komplexen Anfragen moeglich,
sofern der Betroffene innerhalb eines Monats darueber informiert wird.
)}
{/* Filters */}
{/* Requests List */}
{filteredRequests.map(request => (
))}
{/* Empty State */}
{filteredRequests.length === 0 && (
Keine Anfragen gefunden
{selectedType !== 'all' || selectedStatus !== 'all' || selectedPriority !== 'all'
? 'Passen Sie die Filter an oder'
: 'Es sind noch keine Anfragen vorhanden.'
}
{(selectedType !== 'all' || selectedStatus !== 'all' || selectedPriority !== 'all') ? (
Filter zuruecksetzen
) : (
Erste Anfrage erfassen
)}
)}
>
)}
)
}