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>
This commit is contained in:
Benjamin Admin
2026-02-10 11:53:35 +01:00
parent 8c77df494b
commit 4ed290ccf3
6 changed files with 613 additions and 123 deletions

View File

@@ -406,7 +406,224 @@ export async function previewEmail(
}
// =============================================================================
// MOCK DATA FUNCTIONS (for development without backend)
// 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[] {