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:
@@ -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[] {
|
||||
|
||||
Reference in New Issue
Block a user