This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/lib/sdk/dsr/api.ts
Benjamin Admin 4ed290ccf3 feat(sdk): Replace mock data with real API calls in consent, DSR, and consent-management
- /sdk/consent: Replace hardcoded mockDocuments with GET /api/admin/consent/documents
- /sdk/dsr: Replace createMockDSRList with fetchSDKDSRList via /api/sdk/v1/dsgvo/dsr
- /sdk/dsr/new: Replace console.log mock with real POST to create DSR requests
- /sdk/dsr/[requestId]: Replace mock lookup with real GET/PUT for DSR details and status updates
- /sdk/consent-management: Add real stats, GDPR process counts, and email template editor
- lib/sdk/dsr/api.ts: Add transformBackendDSR adapter (flat backend → nested frontend types)

Prepares for removal of /dsgvo and /compliance pages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 12:47:19 +01:00

882 lines
26 KiB
TypeScript

/**
* DSR API Client
*
* API client for Data Subject Request management
* Connects to the Go Consent Service backend
*/
import {
DSRRequest,
DSRListResponse,
DSRFilters,
DSRCreateRequest,
DSRUpdateRequest,
DSRVerifyIdentityRequest,
DSRCompleteRequest,
DSRRejectRequest,
DSRExtendDeadlineRequest,
DSRSendCommunicationRequest,
DSRCommunication,
DSRAuditEntry,
DSRStatistics,
DSRDataExport,
DSRErasureChecklist
} from './types'
// =============================================================================
// CONFIGURATION
// =============================================================================
const DSR_API_BASE = process.env.NEXT_PUBLIC_CONSENT_SERVICE_URL || 'http://localhost:8081'
const API_TIMEOUT = 30000 // 30 seconds
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
function getTenantId(): string {
// In a real app, this would come from auth context or localStorage
if (typeof window !== 'undefined') {
return localStorage.getItem('tenantId') || 'default-tenant'
}
return 'default-tenant'
}
function getAuthHeaders(): HeadersInit {
const headers: HeadersInit = {
'Content-Type': 'application/json',
'X-Tenant-ID': getTenantId()
}
// Add auth token if available
if (typeof window !== 'undefined') {
const token = localStorage.getItem('authToken')
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
}
return headers
}
async function fetchWithTimeout<T>(
url: string,
options: RequestInit = {},
timeout: number = API_TIMEOUT
): Promise<T> {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
headers: {
...getAuthHeaders(),
...options.headers
}
})
if (!response.ok) {
const errorBody = await response.text()
let errorMessage = `HTTP ${response.status}: ${response.statusText}`
try {
const errorJson = JSON.parse(errorBody)
errorMessage = errorJson.error || errorJson.message || errorMessage
} catch {
// Keep the HTTP status message
}
throw new Error(errorMessage)
}
// Handle empty responses
const contentType = response.headers.get('content-type')
if (contentType && contentType.includes('application/json')) {
return response.json()
}
return {} as T
} finally {
clearTimeout(timeoutId)
}
}
// =============================================================================
// DSR LIST & CRUD
// =============================================================================
/**
* Fetch all DSR requests with optional filters
*/
export async function fetchDSRList(filters?: DSRFilters): Promise<DSRListResponse> {
const params = new URLSearchParams()
if (filters) {
if (filters.status) {
const statuses = Array.isArray(filters.status) ? filters.status : [filters.status]
statuses.forEach(s => params.append('status', s))
}
if (filters.type) {
const types = Array.isArray(filters.type) ? filters.type : [filters.type]
types.forEach(t => params.append('type', t))
}
if (filters.priority) params.set('priority', filters.priority)
if (filters.assignedTo) params.set('assignedTo', filters.assignedTo)
if (filters.overdue !== undefined) params.set('overdue', String(filters.overdue))
if (filters.search) params.set('search', filters.search)
if (filters.dateFrom) params.set('dateFrom', filters.dateFrom)
if (filters.dateTo) params.set('dateTo', filters.dateTo)
}
const queryString = params.toString()
const url = `${DSR_API_BASE}/api/v1/admin/dsr${queryString ? `?${queryString}` : ''}`
return fetchWithTimeout<DSRListResponse>(url)
}
/**
* Fetch a single DSR request by ID
*/
export async function fetchDSR(id: string): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(`${DSR_API_BASE}/api/v1/admin/dsr/${id}`)
}
/**
* Create a new DSR request
*/
export async function createDSR(request: DSRCreateRequest): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(`${DSR_API_BASE}/api/v1/admin/dsr`, {
method: 'POST',
body: JSON.stringify(request)
})
}
/**
* Update a DSR request
*/
export async function updateDSR(id: string, update: DSRUpdateRequest): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(`${DSR_API_BASE}/api/v1/admin/dsr/${id}`, {
method: 'PUT',
body: JSON.stringify(update)
})
}
/**
* Delete a DSR request (soft delete - marks as cancelled)
*/
export async function deleteDSR(id: string): Promise<void> {
await fetchWithTimeout<void>(`${DSR_API_BASE}/api/v1/admin/dsr/${id}`, {
method: 'DELETE'
})
}
// =============================================================================
// DSR WORKFLOW ACTIONS
// =============================================================================
/**
* Verify the identity of the requester
*/
export async function verifyIdentity(
dsrId: string,
verification: DSRVerifyIdentityRequest
): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/verify-identity`,
{
method: 'POST',
body: JSON.stringify(verification)
}
)
}
/**
* Complete a DSR request
*/
export async function completeDSR(
dsrId: string,
completion?: DSRCompleteRequest
): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/complete`,
{
method: 'POST',
body: JSON.stringify(completion || {})
}
)
}
/**
* Reject a DSR request
*/
export async function rejectDSR(
dsrId: string,
rejection: DSRRejectRequest
): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/reject`,
{
method: 'POST',
body: JSON.stringify(rejection)
}
)
}
/**
* Extend the deadline for a DSR request
*/
export async function extendDeadline(
dsrId: string,
extension: DSRExtendDeadlineRequest
): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/extend`,
{
method: 'POST',
body: JSON.stringify(extension)
}
)
}
/**
* Assign a DSR request to a user
*/
export async function assignDSR(
dsrId: string,
assignedTo: string
): Promise<DSRRequest> {
return fetchWithTimeout<DSRRequest>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/assign`,
{
method: 'POST',
body: JSON.stringify({ assignedTo })
}
)
}
// =============================================================================
// COMMUNICATION
// =============================================================================
/**
* Get all communications for a DSR request
*/
export async function getCommunications(dsrId: string): Promise<DSRCommunication[]> {
return fetchWithTimeout<DSRCommunication[]>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/communications`
)
}
/**
* Send a communication (email, letter, internal note)
*/
export async function sendCommunication(
dsrId: string,
communication: DSRSendCommunicationRequest
): Promise<DSRCommunication> {
return fetchWithTimeout<DSRCommunication>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/send-communication`,
{
method: 'POST',
body: JSON.stringify(communication)
}
)
}
// =============================================================================
// AUDIT LOG
// =============================================================================
/**
* Get audit log entries for a DSR request
*/
export async function getAuditLog(dsrId: string): Promise<DSRAuditEntry[]> {
return fetchWithTimeout<DSRAuditEntry[]>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/audit`
)
}
// =============================================================================
// STATISTICS
// =============================================================================
/**
* Get DSR statistics
*/
export async function getDSRStatistics(): Promise<DSRStatistics> {
return fetchWithTimeout<DSRStatistics>(
`${DSR_API_BASE}/api/v1/admin/dsr/statistics`
)
}
// =============================================================================
// DATA EXPORT (Art. 15, 20)
// =============================================================================
/**
* Generate data export for Art. 15 (access) or Art. 20 (portability)
*/
export async function generateDataExport(
dsrId: string,
format: 'json' | 'csv' | 'xml' | 'pdf' = 'json'
): Promise<DSRDataExport> {
return fetchWithTimeout<DSRDataExport>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/export`,
{
method: 'POST',
body: JSON.stringify({ format })
}
)
}
/**
* Download generated data export
*/
export async function downloadDataExport(dsrId: string): Promise<Blob> {
const response = await fetch(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/export/download`,
{
headers: getAuthHeaders()
}
)
if (!response.ok) {
throw new Error(`Download failed: ${response.statusText}`)
}
return response.blob()
}
// =============================================================================
// ERASURE CHECKLIST (Art. 17)
// =============================================================================
/**
* Get the erasure checklist for an Art. 17 request
*/
export async function getErasureChecklist(dsrId: string): Promise<DSRErasureChecklist> {
return fetchWithTimeout<DSRErasureChecklist>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/erasure-checklist`
)
}
/**
* Update the erasure checklist
*/
export async function updateErasureChecklist(
dsrId: string,
checklist: DSRErasureChecklist
): Promise<DSRErasureChecklist> {
return fetchWithTimeout<DSRErasureChecklist>(
`${DSR_API_BASE}/api/v1/admin/dsr/${dsrId}/erasure-checklist`,
{
method: 'PUT',
body: JSON.stringify(checklist)
}
)
}
// =============================================================================
// EMAIL TEMPLATES
// =============================================================================
/**
* Get available email templates
*/
export async function getEmailTemplates(): Promise<{ id: string; name: string; stage: string }[]> {
return fetchWithTimeout<{ id: string; name: string; stage: string }[]>(
`${DSR_API_BASE}/api/v1/admin/dsr/email-templates`
)
}
/**
* Preview an email with variables filled in
*/
export async function previewEmail(
templateId: string,
dsrId: string
): Promise<{ subject: string; body: string }> {
return fetchWithTimeout<{ subject: string; body: string }>(
`${DSR_API_BASE}/api/v1/admin/dsr/email-templates/${templateId}/preview`,
{
method: 'POST',
body: JSON.stringify({ dsrId })
}
)
}
// =============================================================================
// SDK API FUNCTIONS (via Next.js proxy to ai-compliance-sdk)
// =============================================================================
interface BackendDSR {
id: string
tenant_id: string
namespace_id?: string
request_type: string
status: string
subject_name: string
subject_email: string
subject_identifier?: string
request_description: string
request_channel: string
received_at: string
verified_at?: string
verification_method?: string
deadline_at: string
extended_deadline_at?: string
extension_reason?: string
completed_at?: string
response_sent: boolean
response_sent_at?: string
response_method?: string
rejection_reason?: string
notes?: string
affected_systems?: string[]
assigned_to?: string
created_at: string
updated_at: string
}
function mapBackendStatus(status: string): import('./types').DSRStatus {
const mapping: Record<string, import('./types').DSRStatus> = {
'received': 'intake',
'verified': 'identity_verification',
'in_progress': 'processing',
'completed': 'completed',
'rejected': 'rejected',
'extended': 'processing',
}
return mapping[status] || 'intake'
}
function mapBackendChannel(channel: string): import('./types').DSRSource {
const mapping: Record<string, import('./types').DSRSource> = {
'email': 'email',
'form': 'web_form',
'phone': 'phone',
'letter': 'letter',
}
return mapping[channel] || 'other'
}
/**
* Transform flat backend DSR to nested SDK DSRRequest format
*/
export function transformBackendDSR(b: BackendDSR): DSRRequest {
const deadlineAt = b.extended_deadline_at || b.deadline_at
const receivedDate = new Date(b.received_at)
const defaultDeadlineDays = 30
const originalDeadline = b.deadline_at || new Date(receivedDate.getTime() + defaultDeadlineDays * 24 * 60 * 60 * 1000).toISOString()
return {
id: b.id,
referenceNumber: `DSR-${new Date(b.created_at).getFullYear()}-${b.id.slice(0, 6).toUpperCase()}`,
type: b.request_type as DSRRequest['type'],
status: mapBackendStatus(b.status),
priority: 'normal',
requester: {
name: b.subject_name,
email: b.subject_email,
customerId: b.subject_identifier,
},
source: mapBackendChannel(b.request_channel),
requestText: b.request_description,
receivedAt: b.received_at,
deadline: {
originalDeadline,
currentDeadline: deadlineAt,
extended: !!b.extended_deadline_at,
extensionReason: b.extension_reason,
},
completedAt: b.completed_at,
identityVerification: {
verified: !!b.verified_at,
verifiedAt: b.verified_at,
method: b.verification_method as any,
},
assignment: {
assignedTo: b.assigned_to || null,
},
notes: b.notes,
createdAt: b.created_at,
createdBy: 'system',
updatedAt: b.updated_at,
tenantId: b.tenant_id,
}
}
function getSdkHeaders(): HeadersInit {
if (typeof window === 'undefined') return {}
return {
'Content-Type': 'application/json',
'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '',
'X-User-ID': localStorage.getItem('bp_user_id') || '',
}
}
/**
* Fetch DSR list from SDK backend via proxy
*/
export async function fetchSDKDSRList(): Promise<{ requests: DSRRequest[]; statistics: DSRStatistics }> {
const res = await fetch('/api/sdk/v1/dsgvo/dsr', {
headers: getSdkHeaders(),
})
if (!res.ok) {
throw new Error(`HTTP ${res.status}`)
}
const data = await res.json()
const backendDSRs: BackendDSR[] = data.dsrs || []
const requests = backendDSRs.map(transformBackendDSR)
// Calculate statistics locally
const now = new Date()
const statistics: DSRStatistics = {
total: requests.length,
byStatus: {
intake: requests.filter(r => r.status === 'intake').length,
identity_verification: requests.filter(r => r.status === 'identity_verification').length,
processing: requests.filter(r => r.status === 'processing').length,
completed: requests.filter(r => r.status === 'completed').length,
rejected: requests.filter(r => r.status === 'rejected').length,
cancelled: requests.filter(r => r.status === 'cancelled').length,
},
byType: {
access: requests.filter(r => r.type === 'access').length,
rectification: requests.filter(r => r.type === 'rectification').length,
erasure: requests.filter(r => r.type === 'erasure').length,
restriction: requests.filter(r => r.type === 'restriction').length,
portability: requests.filter(r => r.type === 'portability').length,
objection: requests.filter(r => r.type === 'objection').length,
},
overdue: requests.filter(r => {
if (r.status === 'completed' || r.status === 'rejected' || r.status === 'cancelled') return false
return new Date(r.deadline.currentDeadline) < now
}).length,
dueThisWeek: requests.filter(r => {
if (r.status === 'completed' || r.status === 'rejected' || r.status === 'cancelled') return false
const deadline = new Date(r.deadline.currentDeadline)
const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)
return deadline >= now && deadline <= weekFromNow
}).length,
averageProcessingDays: 0,
completedThisMonth: requests.filter(r => {
if (r.status !== 'completed' || !r.completedAt) return false
const completed = new Date(r.completedAt)
return completed.getMonth() === now.getMonth() && completed.getFullYear() === now.getFullYear()
}).length,
}
return { requests, statistics }
}
/**
* Create a new DSR via SDK backend
*/
export async function createSDKDSR(request: DSRCreateRequest): Promise<void> {
const body = {
request_type: request.type,
subject_name: request.requester.name,
subject_email: request.requester.email,
subject_identifier: request.requester.customerId || '',
request_description: request.requestText || '',
request_channel: request.source === 'web_form' ? 'form' : request.source,
notes: '',
}
const res = await fetch('/api/sdk/v1/dsgvo/dsr', {
method: 'POST',
headers: getSdkHeaders(),
body: JSON.stringify(body),
})
if (!res.ok) {
throw new Error(`HTTP ${res.status}`)
}
}
/**
* Fetch a single DSR by ID from SDK backend
*/
export async function fetchSDKDSR(id: string): Promise<DSRRequest | null> {
const res = await fetch(`/api/sdk/v1/dsgvo/dsr/${id}`, {
headers: getSdkHeaders(),
})
if (!res.ok) {
return null
}
const data = await res.json()
if (!data || !data.id) return null
return transformBackendDSR(data)
}
/**
* Update DSR status via SDK backend
*/
export async function updateSDKDSRStatus(id: string, status: string): Promise<void> {
const res = await fetch(`/api/sdk/v1/dsgvo/dsr/${id}`, {
method: 'PUT',
headers: getSdkHeaders(),
body: JSON.stringify({ status }),
})
if (!res.ok) {
throw new Error(`HTTP ${res.status}`)
}
}
// =============================================================================
// MOCK DATA FUNCTIONS (kept as fallback)
// =============================================================================
export function createMockDSRList(): DSRRequest[] {
const now = new Date()
return [
{
id: 'dsr-001',
referenceNumber: 'DSR-2025-000001',
type: 'access',
status: 'intake',
priority: 'high',
requester: {
name: 'Max Mustermann',
email: 'max.mustermann@example.de'
},
source: 'web_form',
sourceDetails: 'Kontaktformular auf breakpilot.de',
receivedAt: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
deadline: {
originalDeadline: new Date(now.getTime() + 28 * 24 * 60 * 60 * 1000).toISOString(),
currentDeadline: new Date(now.getTime() + 28 * 24 * 60 * 60 * 1000).toISOString(),
extended: false
},
identityVerification: {
verified: false
},
assignment: {
assignedTo: null
},
createdAt: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
createdBy: 'system',
updatedAt: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
tenantId: 'default-tenant'
},
{
id: 'dsr-002',
referenceNumber: 'DSR-2025-000002',
type: 'erasure',
status: 'identity_verification',
priority: 'high',
requester: {
name: 'Anna Schmidt',
email: 'anna.schmidt@example.de',
phone: '+49 170 1234567'
},
source: 'email',
requestText: 'Ich moechte, dass alle meine Daten geloescht werden.',
receivedAt: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(),
deadline: {
originalDeadline: new Date(now.getTime() + 9 * 24 * 60 * 60 * 1000).toISOString(),
currentDeadline: new Date(now.getTime() + 9 * 24 * 60 * 60 * 1000).toISOString(),
extended: false
},
identityVerification: {
verified: false
},
assignment: {
assignedTo: 'DSB Mueller',
assignedAt: new Date(now.getTime() - 4 * 24 * 60 * 60 * 1000).toISOString()
},
createdAt: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(),
createdBy: 'system',
updatedAt: new Date(now.getTime() - 4 * 24 * 60 * 60 * 1000).toISOString(),
tenantId: 'default-tenant'
},
{
id: 'dsr-003',
referenceNumber: 'DSR-2025-000003',
type: 'rectification',
status: 'processing',
priority: 'normal',
requester: {
name: 'Peter Meier',
email: 'peter.meier@example.de'
},
source: 'email',
requestText: 'Meine Adresse ist falsch gespeichert.',
receivedAt: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(),
deadline: {
originalDeadline: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
currentDeadline: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
extended: false
},
identityVerification: {
verified: true,
method: 'existing_account',
verifiedAt: new Date(now.getTime() - 6 * 24 * 60 * 60 * 1000).toISOString(),
verifiedBy: 'DSB Mueller'
},
assignment: {
assignedTo: 'DSB Mueller',
assignedAt: new Date(now.getTime() - 6 * 24 * 60 * 60 * 1000).toISOString()
},
rectificationDetails: {
fieldsToCorrect: [
{
field: 'Adresse',
currentValue: 'Musterstr. 1, 12345 Berlin',
requestedValue: 'Musterstr. 10, 12345 Berlin',
corrected: false
}
]
},
createdAt: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(),
createdBy: 'system',
updatedAt: new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000).toISOString(),
tenantId: 'default-tenant'
},
{
id: 'dsr-004',
referenceNumber: 'DSR-2025-000004',
type: 'portability',
status: 'processing',
priority: 'normal',
requester: {
name: 'Lisa Weber',
email: 'lisa.weber@example.de'
},
source: 'web_form',
receivedAt: new Date(now.getTime() - 10 * 24 * 60 * 60 * 1000).toISOString(),
deadline: {
originalDeadline: new Date(now.getTime() + 20 * 24 * 60 * 60 * 1000).toISOString(),
currentDeadline: new Date(now.getTime() + 20 * 24 * 60 * 60 * 1000).toISOString(),
extended: false
},
identityVerification: {
verified: true,
method: 'id_document',
verifiedAt: new Date(now.getTime() - 8 * 24 * 60 * 60 * 1000).toISOString(),
verifiedBy: 'DSB Mueller'
},
assignment: {
assignedTo: 'IT Team',
assignedAt: new Date(now.getTime() - 8 * 24 * 60 * 60 * 1000).toISOString()
},
notes: 'JSON-Export wird vorbereitet',
createdAt: new Date(now.getTime() - 10 * 24 * 60 * 60 * 1000).toISOString(),
createdBy: 'system',
updatedAt: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
tenantId: 'default-tenant'
},
{
id: 'dsr-005',
referenceNumber: 'DSR-2025-000005',
type: 'objection',
status: 'rejected',
priority: 'low',
requester: {
name: 'Thomas Klein',
email: 'thomas.klein@example.de'
},
source: 'letter',
requestText: 'Ich widerspreche der Verarbeitung meiner Daten fuer Marketingzwecke.',
receivedAt: new Date(now.getTime() - 35 * 24 * 60 * 60 * 1000).toISOString(),
deadline: {
originalDeadline: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(),
currentDeadline: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(),
extended: false
},
completedAt: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(),
identityVerification: {
verified: true,
method: 'postal',
verifiedAt: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString(),
verifiedBy: 'DSB Mueller'
},
assignment: {
assignedTo: 'Rechtsabteilung',
assignedAt: new Date(now.getTime() - 28 * 24 * 60 * 60 * 1000).toISOString()
},
objectionDetails: {
processingPurpose: 'Marketing',
legalBasis: 'Berechtigtes Interesse (Art. 6(1)(f))',
objectionGrounds: 'Keine konkreten Gruende genannt',
decision: 'rejected',
decisionReason: 'Zwingende schutzwuerdige Gruende fuer die Verarbeitung ueberwiegen',
decisionBy: 'Rechtsabteilung',
decisionAt: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString()
},
notes: 'Widerspruch unberechtigt - zwingende schutzwuerdige Gruende',
createdAt: new Date(now.getTime() - 35 * 24 * 60 * 60 * 1000).toISOString(),
createdBy: 'system',
updatedAt: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(),
tenantId: 'default-tenant'
},
{
id: 'dsr-006',
referenceNumber: 'DSR-2025-000006',
type: 'access',
status: 'completed',
priority: 'normal',
requester: {
name: 'Sarah Braun',
email: 'sarah.braun@example.de'
},
source: 'email',
receivedAt: new Date(now.getTime() - 45 * 24 * 60 * 60 * 1000).toISOString(),
deadline: {
originalDeadline: new Date(now.getTime() - 15 * 24 * 60 * 60 * 1000).toISOString(),
currentDeadline: new Date(now.getTime() - 15 * 24 * 60 * 60 * 1000).toISOString(),
extended: false
},
completedAt: new Date(now.getTime() - 20 * 24 * 60 * 60 * 1000).toISOString(),
identityVerification: {
verified: true,
method: 'id_document',
verifiedAt: new Date(now.getTime() - 42 * 24 * 60 * 60 * 1000).toISOString(),
verifiedBy: 'DSB Mueller'
},
assignment: {
assignedTo: 'DSB Mueller',
assignedAt: new Date(now.getTime() - 42 * 24 * 60 * 60 * 1000).toISOString()
},
dataExport: {
format: 'pdf',
generatedAt: new Date(now.getTime() - 20 * 24 * 60 * 60 * 1000).toISOString(),
generatedBy: 'DSB Mueller',
fileName: 'datenauskunft_sarah_braun.pdf',
fileSize: 245000,
includesThirdPartyData: false
},
createdAt: new Date(now.getTime() - 45 * 24 * 60 * 60 * 1000).toISOString(),
createdBy: 'system',
updatedAt: new Date(now.getTime() - 20 * 24 * 60 * 60 * 1000).toISOString(),
tenantId: 'default-tenant'
}
]
}
export function createMockStatistics(): DSRStatistics {
return {
total: 6,
byStatus: {
intake: 1,
identity_verification: 1,
processing: 2,
completed: 1,
rejected: 1,
cancelled: 0
},
byType: {
access: 2,
rectification: 1,
erasure: 1,
restriction: 0,
portability: 1,
objection: 1
},
overdue: 0,
dueThisWeek: 2,
averageProcessingDays: 18,
completedThisMonth: 1
}
}