> = {
+ intake: [{ label: 'Identitaet pruefen', next: 'identity_verification' }],
+ identity_verification: [{ label: 'Bearbeitung starten', next: 'processing' }],
+ processing: [
+ { label: 'Anfrage abschliessen', next: 'completed' },
+ { label: 'Ablehnen', next: 'rejected', variant: 'danger' }
+ ]
+ }
+
+ const transitions = statusTransitions[request.status] || []
+
+ const handleStatusChange = async (newStatus: DSRStatus) => {
+ setIsUpdatingStatus(true)
+ setActionError(null)
+ try {
+ await updateSDKDSRStatus(request.id, newStatus)
+ onUpdated()
+ } catch (err: unknown) {
+ setActionError(err instanceof Error ? err.message : 'Unbekannter Fehler')
+ } finally {
+ setIsUpdatingStatus(false)
+ }
+ }
+
+ const handleExportPDF = () => {
+ window.open(`/api/sdk/v1/compliance/dsr/${request.id}/export`, '_blank')
+ }
+
+ return (
+ <>
+ {/* Backdrop */}
+
+
+ {/* Drawer */}
+
+ {/* Header */}
+
+
+
{request.referenceNumber}
+
+ {typeInfo.article} {typeInfo.labelShort}
+
+
+
+
+
+
+
+ PDF
+
+
+
+
+
+
+
+
+
+
+ {actionError && (
+
+ {actionError}
+
+ )}
+
+ {/* Workflow Stepper */}
+
+
Bearbeitungsstand
+
+
+
+ {/* Requester Info */}
+
+
Antragsteller
+
+
+
+ {request.requester.name.charAt(0).toUpperCase()}
+
+
+
{request.requester.name}
+
{request.requester.email}
+
+
+
+
+
+ {/* Deadline */}
+
+
+
+
Gesetzliche Frist
+
+ {new Date(request.deadline.currentDeadline).toLocaleDateString('de-DE')}
+
+
+
+
+ {overdue ? `${Math.abs(daysRemaining)}` : daysRemaining}
+
+
+ {overdue ? 'Tage ueberfaellig' : 'Tage verbleibend'}
+
+
+
+
+
+ {/* Details */}
+
+
+
Status
+
{statusInfo.label}
+
+
+
Zugewiesen an
+
+ {request.assignment?.assignedTo || '—'}
+
+
+
+
Eingegangen am
+
+ {new Date(request.receivedAt).toLocaleDateString('de-DE')}
+
+
+
+
Identitaet geprueft
+
+ {request.identityVerification.verified ? 'Ja' : 'Ausstehend'}
+
+
+
+
+ {/* Notes */}
+ {request.notes && (
+
+
Notizen
+
{request.notes}
+
+ )}
+
+ {/* Status Transitions */}
+ {transitions.length > 0 && (
+
+
Naechste Schritte
+
+ {transitions.map((t) => (
+ handleStatusChange(t.next)}
+ disabled={isUpdatingStatus}
+ className={`px-4 py-2 text-sm rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition-colors ${
+ t.variant === 'danger'
+ ? 'bg-red-600 text-white hover:bg-red-700'
+ : 'bg-purple-600 text-white hover:bg-purple-700'
+ }`}
+ >
+ {isUpdatingStatus ? 'Wird gespeichert...' : t.label}
+
+ ))}
+
+
+ )}
+
+
+ >
+ )
+}
diff --git a/admin-compliance/app/sdk/dsr/_components/DSRHeaderActions.tsx b/admin-compliance/app/sdk/dsr/_components/DSRHeaderActions.tsx
new file mode 100644
index 0000000..30609e3
--- /dev/null
+++ b/admin-compliance/app/sdk/dsr/_components/DSRHeaderActions.tsx
@@ -0,0 +1,35 @@
+'use client'
+
+export function DSRHeaderActions({
+ onOpenCreate,
+}: {
+ onOpenCreate: () => void
+}) {
+ return (
+
+
{
+ const link = document.createElement('a')
+ link.href = '/api/sdk/v1/compliance/dsr/export?format=csv'
+ link.download = 'dsr_export.csv'
+ link.click()
+ }}
+ className="flex items-center gap-2 px-4 py-2 text-gray-600 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
+ >
+
+
+
+ CSV Export
+
+
+
+
+
+ Anfrage erfassen
+
+
+ )
+}
diff --git a/admin-compliance/app/sdk/dsr/_components/FilterBar.tsx b/admin-compliance/app/sdk/dsr/_components/FilterBar.tsx
new file mode 100644
index 0000000..7fe5075
--- /dev/null
+++ b/admin-compliance/app/sdk/dsr/_components/FilterBar.tsx
@@ -0,0 +1,81 @@
+'use client'
+
+import {
+ DSRType,
+ DSRStatus,
+ DSR_TYPE_INFO,
+ DSR_STATUS_INFO,
+} from '@/lib/sdk/dsr/types'
+
+export 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
+
+ )}
+
+ )
+}
diff --git a/admin-compliance/app/sdk/dsr/_components/RequestCard.tsx b/admin-compliance/app/sdk/dsr/_components/RequestCard.tsx
new file mode 100644
index 0000000..0ea23d5
--- /dev/null
+++ b/admin-compliance/app/sdk/dsr/_components/RequestCard.tsx
@@ -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 (
+
+
+
+ {/* 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
+
+ )}
+
+
+
+ )
+}
diff --git a/admin-compliance/app/sdk/dsr/_components/StatCard.tsx b/admin-compliance/app/sdk/dsr/_components/StatCard.tsx
new file mode 100644
index 0000000..bb9799f
--- /dev/null
+++ b/admin-compliance/app/sdk/dsr/_components/StatCard.tsx
@@ -0,0 +1,51 @@
+'use client'
+
+import React from 'react'
+
+export 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}
+
+ )}
+
+
+ )
+}
diff --git a/admin-compliance/app/sdk/dsr/_components/TabNavigation.tsx b/admin-compliance/app/sdk/dsr/_components/TabNavigation.tsx
new file mode 100644
index 0000000..8a2c631
--- /dev/null
+++ b/admin-compliance/app/sdk/dsr/_components/TabNavigation.tsx
@@ -0,0 +1,45 @@
+'use client'
+
+import type { Tab, TabId } from '../_types'
+
+export 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}
+
+ )}
+
+
+ ))}
+
+
+ )
+}
diff --git a/admin-compliance/app/sdk/dsr/_types.ts b/admin-compliance/app/sdk/dsr/_types.ts
new file mode 100644
index 0000000..6e114a3
--- /dev/null
+++ b/admin-compliance/app/sdk/dsr/_types.ts
@@ -0,0 +1,8 @@
+export type TabId = 'overview' | 'intake' | 'processing' | 'completed' | 'settings'
+
+export interface Tab {
+ id: TabId
+ label: string
+ count?: number
+ countColor?: string
+}
diff --git a/admin-compliance/app/sdk/dsr/page.tsx b/admin-compliance/app/sdk/dsr/page.tsx
index 7d880b2..7722838 100644
--- a/admin-compliance/app/sdk/dsr/page.tsx
+++ b/admin-compliance/app/sdk/dsr/page.tsx
@@ -1,6 +1,6 @@
'use client'
-import React, { useState, useEffect, useMemo, useCallback } from 'react'
+import { useState, useEffect, useMemo, useCallback } from 'react'
import { useSDK } from '@/lib/sdk'
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
import {
@@ -8,693 +8,25 @@ import {
DSRType,
DSRStatus,
DSRStatistics,
- DSR_TYPE_INFO,
- DSR_STATUS_INFO,
getDaysRemaining,
isOverdue,
- isUrgent
} from '@/lib/sdk/dsr/types'
-import { fetchSDKDSRList, createSDKDSR, updateSDKDSRStatus } 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, 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 (
-
-
-
- {/* 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
-
- )}
-
- )
-}
-
-// =============================================================================
-// DSR CREATE MODAL
-// =============================================================================
-
-function DSRCreateModal({
- onClose,
- onSuccess
-}: {
- onClose: () => void
- onSuccess: () => void
-}) {
- const [type, setType] = useState('access')
- const [subjectName, setSubjectName] = useState('')
- const [subjectEmail, setSubjectEmail] = useState('')
- const [description, setDescription] = useState('')
- const [source, setSource] = useState('web_form')
- const [isSaving, setIsSaving] = useState(false)
- const [error, setError] = useState(null)
-
- const deadline = new Date()
- deadline.setDate(deadline.getDate() + 30)
- const deadlineStr = deadline.toLocaleDateString('de-DE')
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault()
- if (!subjectName.trim() || !subjectEmail.trim()) return
-
- setIsSaving(true)
- setError(null)
- try {
- await createSDKDSR({
- type,
- requester: { name: subjectName.trim(), email: subjectEmail.trim() },
- requestText: description.trim(),
- source
- })
- onSuccess()
- } catch (err: unknown) {
- setError(err instanceof Error ? err.message : 'Unbekannter Fehler')
- } finally {
- setIsSaving(false)
- }
- }
-
- return (
-
- {/* Backdrop */}
-
-
- {/* Modal */}
-
-
-
-
Neue Anfrage anlegen
-
-
-
-
-
-
-
- {error && (
-
- {error}
-
- )}
-
-
-
-
-
- )
-}
-
-// =============================================================================
-// DSR DETAIL PANEL
-// =============================================================================
-
-function DSRDetailPanel({
- request,
- onClose,
- onUpdated
-}: {
- request: DSRRequest
- onClose: () => void
- onUpdated: () => void
-}) {
- const [isUpdatingStatus, setIsUpdatingStatus] = useState(false)
- const [actionError, setActionError] = useState(null)
-
- const typeInfo = DSR_TYPE_INFO[request.type]
- const statusInfo = DSR_STATUS_INFO[request.status]
- const daysRemaining = getDaysRemaining(request.deadline.currentDeadline)
- const overdue = isOverdue(request)
-
- type StatusTransition = { label: string; next: DSRStatus; variant?: 'danger' }
- const statusTransitions: Partial> = {
- intake: [{ label: 'Identitaet pruefen', next: 'identity_verification' }],
- identity_verification: [{ label: 'Bearbeitung starten', next: 'processing' }],
- processing: [
- { label: 'Anfrage abschliessen', next: 'completed' },
- { label: 'Ablehnen', next: 'rejected', variant: 'danger' }
- ]
- }
-
- const transitions = statusTransitions[request.status] || []
-
- const handleStatusChange = async (newStatus: DSRStatus) => {
- setIsUpdatingStatus(true)
- setActionError(null)
- try {
- await updateSDKDSRStatus(request.id, newStatus)
- onUpdated()
- } catch (err: unknown) {
- setActionError(err instanceof Error ? err.message : 'Unbekannter Fehler')
- } finally {
- setIsUpdatingStatus(false)
- }
- }
-
- const handleExportPDF = () => {
- window.open(`/api/sdk/v1/compliance/dsr/${request.id}/export`, '_blank')
- }
-
- return (
- <>
- {/* Backdrop */}
-
-
- {/* Drawer */}
-
- {/* Header */}
-
-
-
{request.referenceNumber}
-
- {typeInfo.article} {typeInfo.labelShort}
-
-
-
-
-
-
-
- PDF
-
-
-
-
-
-
-
-
-
-
- {actionError && (
-
- {actionError}
-
- )}
-
- {/* Workflow Stepper */}
-
-
Bearbeitungsstand
-
-
-
- {/* Requester Info */}
-
-
Antragsteller
-
-
-
- {request.requester.name.charAt(0).toUpperCase()}
-
-
-
{request.requester.name}
-
{request.requester.email}
-
-
-
-
-
- {/* Deadline */}
-
-
-
-
Gesetzliche Frist
-
- {new Date(request.deadline.currentDeadline).toLocaleDateString('de-DE')}
-
-
-
-
- {overdue ? `${Math.abs(daysRemaining)}` : daysRemaining}
-
-
- {overdue ? 'Tage ueberfaellig' : 'Tage verbleibend'}
-
-
-
-
-
- {/* Details */}
-
-
-
Status
-
{statusInfo.label}
-
-
-
Zugewiesen an
-
- {request.assignment?.assignedTo || '—'}
-
-
-
-
Eingegangen am
-
- {new Date(request.receivedAt).toLocaleDateString('de-DE')}
-
-
-
-
Identitaet geprueft
-
- {request.identityVerification.verified ? 'Ja' : 'Ausstehend'}
-
-
-
-
- {/* Notes */}
- {request.notes && (
-
-
Notizen
-
{request.notes}
-
- )}
-
- {/* Status Transitions */}
- {transitions.length > 0 && (
-
-
Naechste Schritte
-
- {transitions.map((t) => (
- handleStatusChange(t.next)}
- disabled={isUpdatingStatus}
- className={`px-4 py-2 text-sm rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition-colors ${
- t.variant === 'danger'
- ? 'bg-red-600 text-white hover:bg-red-700'
- : 'bg-purple-600 text-white hover:bg-purple-700'
- }`}
- >
- {isUpdatingStatus ? 'Wird gespeichert...' : t.label}
-
- ))}
-
-
- )}
-
-
- >
- )
-}
+import { fetchSDKDSRList } from '@/lib/sdk/dsr/api'
+import type { Tab, TabId } from './_types'
+import { TabNavigation } from './_components/TabNavigation'
+import { StatCard } from './_components/StatCard'
+import { RequestCard } from './_components/RequestCard'
+import { FilterBar } from './_components/FilterBar'
+import { DSRCreateModal } from './_components/DSRCreateModal'
+import { DSRDetailPanel } from './_components/DSRDetailPanel'
+import { DSRHeaderActions } from './_components/DSRHeaderActions'
+import {
+ LoadingSpinner,
+ SettingsTab,
+ OverdueAlert,
+ DeadlineInfoBox,
+ EmptyState,
+} from './_components/DSRBanners'
// =============================================================================
// MAIN PAGE
@@ -808,31 +140,7 @@ export default function DSRPage() {
explanation={stepInfo.explanation}
tips={stepInfo.tips}
>
-
-
{
- const link = document.createElement('a')
- link.href = '/api/sdk/v1/compliance/dsr/export?format=csv'
- link.download = 'dsr_export.csv'
- link.click()
- }}
- className="flex items-center gap-2 px-4 py-2 text-gray-600 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
- >
-
-
-
- CSV Export
-
-
setShowCreateModal(true)}
- className="flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"
- >
-
-
-
- Anfrage erfassen
-
-
+ setShowCreateModal(true)} />
{/* Tab Navigation */}
@@ -844,37 +152,15 @@ export default function DSRPage() {
{/* 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 && (
-
-
-
-
- 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
-
-
+ {
+ setActiveTab('overview')
+ setSelectedStatus('all')
+ }}
+ />
)}
{/* 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.
-
-
-
-
- )}
+ {activeTab === 'overview' && }
{/* Filters */}
-
- 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
-
- ) : (
- setShowCreateModal(true)}
- className="mt-4 inline-flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"
- >
-
-
-
- Erste Anfrage erfassen
-
- )}
-
+ setShowCreateModal(true)}
+ />
)}
>
)}