From 4ed290ccf3d15a5538f81388a2271bbf1c50091c Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 10 Feb 2026 11:53:35 +0100 Subject: [PATCH] feat(sdk): Replace mock data with real API calls in consent, DSR, and consent-management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /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 --- .../app/(sdk)/sdk/consent-management/page.tsx | 293 +++++++++++++++++- admin-v2/app/(sdk)/sdk/consent/page.tsx | 155 ++++----- .../app/(sdk)/sdk/dsr/[requestId]/page.tsx | 49 ++- admin-v2/app/(sdk)/sdk/dsr/new/page.tsx | 7 +- admin-v2/app/(sdk)/sdk/dsr/page.tsx | 13 +- admin-v2/lib/sdk/dsr/api.ts | 219 ++++++++++++- 6 files changed, 613 insertions(+), 123 deletions(-) diff --git a/admin-v2/app/(sdk)/sdk/consent-management/page.tsx b/admin-v2/app/(sdk)/sdk/consent-management/page.tsx index 8a16978..0bab1bf 100644 --- a/admin-v2/app/(sdk)/sdk/consent-management/page.tsx +++ b/admin-v2/app/(sdk)/sdk/consent-management/page.tsx @@ -12,6 +12,7 @@ */ import { useState, useEffect } from 'react' +import Link from 'next/link' import { useSDK } from '@/lib/sdk' import StepHeader from '@/components/sdk/StepHeader/StepHeader' @@ -41,6 +42,13 @@ interface Version { created_at: string } +// Email template editor types +interface EmailTemplateData { + key: string + subject: string + body: string +} + export default function ConsentManagementPage() { const { state } = useSDK() const [activeTab, setActiveTab] = useState('documents') @@ -50,6 +58,18 @@ export default function ConsentManagementPage() { const [error, setError] = useState(null) const [selectedDocument, setSelectedDocument] = useState('') + // Stats state + const [consentStats, setConsentStats] = useState<{ activeConsents: number; documentCount: number; openDSRs: number }>({ activeConsents: 0, documentCount: 0, openDSRs: 0 }) + + // GDPR tab state + const [dsrCounts, setDsrCounts] = useState>({}) + const [dsrOverview, setDsrOverview] = useState<{ open: number; completed: number; in_progress: number; overdue: number }>({ open: 0, completed: 0, in_progress: 0, overdue: 0 }) + + // Email template editor state + const [editingTemplate, setEditingTemplate] = useState(null) + const [previewTemplate, setPreviewTemplate] = useState(null) + const [savedTemplates, setSavedTemplates] = useState>({}) + // Auth token (in production, get from auth context) const [authToken, setAuthToken] = useState('') @@ -58,6 +78,13 @@ export default function ConsentManagementPage() { if (token) { setAuthToken(token) } + // Load saved email templates from localStorage + try { + const saved = localStorage.getItem('sdk-email-templates') + if (saved) { + setSavedTemplates(JSON.parse(saved)) + } + } catch { /* ignore */ } }, []) useEffect(() => { @@ -65,6 +92,10 @@ export default function ConsentManagementPage() { loadDocuments() } else if (activeTab === 'versions' && selectedDocument) { loadVersions(selectedDocument) + } else if (activeTab === 'stats') { + loadStats() + } else if (activeTab === 'gdpr') { + loadGDPRData() } }, [activeTab, selectedDocument, authToken]) @@ -110,6 +141,111 @@ export default function ConsentManagementPage() { } } + async function loadStats() { + try { + const token = localStorage.getItem('bp_admin_token') + const [statsRes, docsRes] = await Promise.all([ + fetch(`${API_BASE}/stats`, { + headers: token ? { 'Authorization': `Bearer ${token}` } : {} + }), + fetch(`${API_BASE}/documents`, { + headers: token ? { 'Authorization': `Bearer ${token}` } : {} + }), + ]) + + let activeConsents = 0 + let documentCount = 0 + let openDSRs = 0 + + if (statsRes.ok) { + const statsData = await statsRes.json() + activeConsents = statsData.total_consents || statsData.active_consents || 0 + } + + if (docsRes.ok) { + const docsData = await docsRes.json() + documentCount = (docsData.documents || []).length + } + + // Try to get DSR count + try { + const dsrRes = await fetch('/api/sdk/v1/dsgvo/dsr', { + headers: { + 'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '', + 'X-User-ID': localStorage.getItem('bp_user_id') || '', + } + }) + if (dsrRes.ok) { + const dsrData = await dsrRes.json() + const dsrs = dsrData.dsrs || [] + const now = new Date() + openDSRs = dsrs.filter((r: any) => r.status !== 'completed' && r.status !== 'rejected').length + } + } catch { /* DSR endpoint might not be available */ } + + setConsentStats({ activeConsents, documentCount, openDSRs }) + } catch (err) { + console.error('Failed to load stats:', err) + } + } + + async function loadGDPRData() { + try { + const res = await fetch('/api/sdk/v1/dsgvo/dsr', { + headers: { + 'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '', + 'X-User-ID': localStorage.getItem('bp_user_id') || '', + } + }) + if (!res.ok) return + + const data = await res.json() + const dsrs = data.dsrs || [] + const now = new Date() + + // Count per article type + const counts: Record = {} + const typeMapping: Record = { + 'access': '15', + 'rectification': '16', + 'erasure': '17', + 'restriction': '18', + 'portability': '20', + 'objection': '21', + } + + for (const dsr of dsrs) { + if (dsr.status === 'completed' || dsr.status === 'rejected') continue + const article = typeMapping[dsr.request_type] + if (article) { + counts[article] = (counts[article] || 0) + 1 + } + } + setDsrCounts(counts) + + // Calculate overview + const open = dsrs.filter((r: any) => r.status === 'received' || r.status === 'verified').length + const completed = dsrs.filter((r: any) => r.status === 'completed').length + const in_progress = dsrs.filter((r: any) => r.status === 'in_progress').length + const overdue = dsrs.filter((r: any) => { + if (r.status === 'completed' || r.status === 'rejected') return false + const deadline = r.extended_deadline_at ? new Date(r.extended_deadline_at) : new Date(r.deadline_at) + return deadline < now + }).length + + setDsrOverview({ open, completed, in_progress, overdue }) + } catch (err) { + console.error('Failed to load GDPR data:', err) + } + } + + function saveEmailTemplate(template: EmailTemplateData) { + const updated = { ...savedTemplates, [template.key]: template } + setSavedTemplates(updated) + localStorage.setItem('sdk-email-templates', JSON.stringify(updated)) + setEditingTemplate(null) + } + const tabs: { id: Tab; label: string }[] = [ { id: 'documents', label: 'Dokumente' }, { id: 'versions', label: 'Versionen' }, @@ -459,11 +595,33 @@ export default function ConsentManagementPage() {
- Aktiv - -
@@ -536,16 +694,19 @@ export default function ConsentManagementPage() { | - Offene Anfragen: 0 + Offene Anfragen: 0 ? 'text-orange-600' : 'text-slate-700'}`}>{dsrCounts[process.article] || 0}
- + @@ -560,19 +721,19 @@ export default function ConsentManagementPage() {

DSR Uebersicht

-
0
+
0 ? 'text-blue-600' : 'text-slate-900'}`}>{dsrOverview.open}
Offen
-
0
+
{dsrOverview.completed}
Erledigt
-
0
+
{dsrOverview.in_progress}
In Bearbeitung
-
0
+
0 ? 'text-red-700' : 'text-slate-400'}`}>{dsrOverview.overdue}
Ueberfaellig
@@ -587,29 +748,131 @@ export default function ConsentManagementPage() {
-
0
+
{consentStats.activeConsents}
Aktive Zustimmungen
-
0
+
{consentStats.documentCount}
Dokumente
-
0
+
0 ? 'text-orange-600' : 'text-slate-900'}`}> + {consentStats.openDSRs} +
Offene DSR-Anfragen

Zustimmungsrate nach Dokument

-
- Noch keine Daten verfuegbar +
+ Diagramm wird in einer zukuenftigen Version verfuegbar sein
)}
+ {/* Email Template Edit Modal */} + {editingTemplate && ( +
+
+
+

E-Mail Vorlage bearbeiten

+ +
+
+
+ + setEditingTemplate({ ...editingTemplate, subject: e.target.value })} + className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm" + /> +
+
+ +