import { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { gdprApi } from '../../services/api';
import type { DataDeletionRequest, DeletionRequestStatus } from '../../types';
import Card from '../../components/ui/Card';
import Button from '../../components/ui/Button';
import Select from '../../components/ui/Select';
import { ArrowLeft, FileText, Users, CheckCircle, Clock, XCircle, AlertTriangle, Download, X, ChevronRight } from 'lucide-react';
const STATUS_OPTIONS = [
{ value: '', label: 'Alle Status' },
{ value: 'PENDING', label: 'Ausstehend' },
{ value: 'IN_PROGRESS', label: 'In Bearbeitung' },
{ value: 'COMPLETED', label: 'Abgeschlossen' },
{ value: 'PARTIALLY_COMPLETED', label: 'Teilweise abgeschlossen' },
{ value: 'REJECTED', label: 'Abgelehnt' },
];
function getStatusBadge(status: DeletionRequestStatus) {
switch (status) {
case 'PENDING':
return Ausstehend;
case 'IN_PROGRESS':
return In Bearbeitung;
case 'COMPLETED':
return Abgeschlossen;
case 'PARTIALLY_COMPLETED':
return Teilweise;
case 'REJECTED':
return Abgelehnt;
default:
return {status};
}
}
function formatDate(date: string): string {
return new Date(date).toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
});
}
interface ProcessModalProps {
request: DataDeletionRequest;
onClose: () => void;
onProcess: (action: 'complete' | 'partial' | 'reject', reason?: string) => void;
isPending: boolean;
}
function ProcessModal({ request, onClose, onProcess, isPending }: ProcessModalProps) {
const [action, setAction] = useState<'complete' | 'partial' | 'reject'>('complete');
const [reason, setReason] = useState('');
return (
Löschanfrage bearbeiten
Kunde:
{request.customer?.firstName} {request.customer?.lastName} ({request.customer?.customerNumber})
Quelle: {request.requestSource}
Angefordert: {formatDate(request.requestedAt)}
{(action === 'partial' || action === 'reject') && (
)}
);
}
export default function GDPRDashboard() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const [statusFilter, setStatusFilter] = useState('');
const [selectedRequest, setSelectedRequest] = useState(null);
const { data: statsData } = useQuery({
queryKey: ['gdpr-stats'],
queryFn: () => gdprApi.getDashboardStats(),
});
const { data: requestsData, isLoading } = useQuery({
queryKey: ['deletion-requests', statusFilter],
queryFn: () => gdprApi.getDeletionRequests({ status: statusFilter || undefined }),
});
const { data: consentData } = useQuery({
queryKey: ['consent-overview'],
queryFn: () => gdprApi.getConsentOverview(),
});
const processMutation = useMutation({
mutationFn: ({ id, data }: { id: number; data: { processedBy: string; action: 'complete' | 'partial' | 'reject'; retentionReason?: string } }) =>
gdprApi.processDeletionRequest(id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['deletion-requests'] });
queryClient.invalidateQueries({ queryKey: ['gdpr-stats'] });
setSelectedRequest(null);
},
});
const stats = statsData?.data;
const requests = requestsData?.data || [];
// Backend gibt ein Array mit { type, label, description, granted, withdrawn, pending } zurück
const consentsList: Array<{ type: string; label?: string; description?: string; granted: number; withdrawn: number; pending: number }> =
Array.isArray(consentData?.data) ? consentData.data : [];
const handleProcess = (action: 'complete' | 'partial' | 'reject', reason?: string) => {
if (!selectedRequest) return;
const user = JSON.parse(localStorage.getItem('user') || '{}');
processMutation.mutate({
id: selectedRequest.id,
data: {
processedBy: user.email || 'System',
action,
retentionReason: reason,
},
});
};
const consentLabels: Record = {
DATA_PROCESSING: 'Datenverarbeitung',
MARKETING_EMAIL: 'E-Mail-Marketing',
MARKETING_PHONE: 'Telefonmarketing',
DATA_SHARING_PARTNER: 'Datenweitergabe',
};
return (
DSGVO-Dashboard
{/* Statistik-Kacheln */}
{stats?.deletionRequests.pending ?? '-'}
Offene Löschanfragen
{stats?.deletionRequests.completedLast30Days ?? '-'}
Gelöscht (30 Tage)
{stats?.dataExports.last30Days ?? '-'}
Datenexporte (30 Tage)
{stats?.consents.granted ?? '-'}
Aktive Einwilligungen
{/* Consent-Übersicht */}
{consentsList.length > 0 && (
Einwilligungen nach Typ
{consentsList.map((item) => (
{item.label || consentLabels[item.type] || item.type}
{item.granted}
erteilt
{item.withdrawn}
widerrufen
{item.pending}
ausstehend
))}
)}
{/* Löschanfragen */}
Löschanfragen
{isLoading ? (
Laden...
) : requests.length === 0 ? (
Keine Löschanfragen gefunden.
) : (
| Kunde |
Status |
Quelle |
Angefordert |
Bearbeitet |
|
{requests.map((request) => (
{request.customer ? (
{request.customer.firstName} {request.customer.lastName}
{request.customer.customerNumber}
) : (
Kunde #{request.customerId}
)}
|
{getStatusBadge(request.status)} |
{request.requestSource} |
{formatDate(request.requestedAt)}
von {request.requestedBy}
|
{request.processedAt ? (
{formatDate(request.processedAt)}
von {request.processedBy}
) : (
-
)}
|
{(request.status === 'PENDING' || request.status === 'IN_PROGRESS') && (
)}
{request.proofDocument && (
)}
|
))}
)}
{/* Bearbeitungs-Modal */}
{selectedRequest && (
setSelectedRequest(null)}
onProcess={handleProcess}
isPending={processMutation.isPending}
/>
)}
);
}