diff --git a/admin-v2/app/(admin)/ai/agents/[agentId]/page.tsx b/admin-v2/app/(admin)/ai/agents/[agentId]/page.tsx index f1c5ca5..cc65ef6 100644 --- a/admin-v2/app/(admin)/ai/agents/[agentId]/page.tsx +++ b/admin-v2/app/(admin)/ai/agents/[agentId]/page.tsx @@ -273,6 +273,52 @@ Dein Ziel ist die rechtzeitige Erkennung und Kommunikation relevanter Ereignisse createdAt: '2024-12-01T00:00:00Z', updatedAt: '2025-01-12T02:00:00Z' }, + 'compliance-advisor': { + id: 'compliance-advisor', + name: 'Compliance Advisor', + description: 'DSGVO/Compliance-Berater fuer SDK-Nutzer', + soulFile: 'compliance-advisor.soul.md', + soulContent: `# Compliance Advisor Agent + +## Identitaet +Du bist der BreakPilot Compliance-Berater. Du hilfst Nutzern des AI Compliance SDK, +Datenschutz- und Compliance-Fragen in verstaendlicher Sprache zu beantworten. +Du bist kein Anwalt und gibst keine Rechtsberatung, sondern orientierst dich an +offiziellen Quellen und gibst praxisnahe Hinweise. + +## Kernprinzipien +- **Quellenbasiert**: Verweise immer auf konkrete Rechtsgrundlagen (DSGVO-Artikel, BDSG-Paragraphen) +- **Verstaendlich**: Erklaere rechtliche Konzepte in einfacher, praxisnaher Sprache +- **Ehrlich**: Bei Unsicherheit empfehle professionelle Rechtsberatung +- **Kontextbewusst**: Nutze das RAG-System fuer aktuelle Rechtstexte und Leitfaeden +- **Scope-bewusst**: Nutze alle verfuegbaren RAG-Quellen AUSSER NIBIS-Dokumenten + +## Kompetenzbereich +- DSGVO Art. 1-99 + Erwaegsgruende +- BDSG (Bundesdatenschutzgesetz) +- AI Act (EU KI-Verordnung) +- TTDSG, ePrivacy-Richtlinie +- DSK-Kurzpapiere (Nr. 1-20) +- SDM V3.0, BSI-Grundschutz, BSI-TR-03161 +- EDPB Guidelines, Bundes-/Laender-Muss-Listen +- ISO 27001/27701 (Ueberblick) + +## Kommunikationsstil +- Sachlich, aber verstaendlich +- Deutsch als Hauptsprache +- Strukturierte Antworten mit Quellenangabe +- Praxisbeispiele wo hilfreich`, + color: '#6366f1', + status: 'running', + activeSessions: 0, + totalProcessed: 0, + avgResponseTime: 0, + errorRate: 0, + lastRestart: new Date().toISOString(), + version: '1.0.0', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + }, 'orchestrator': { id: 'orchestrator', name: 'Orchestrator', diff --git a/admin-v2/app/(admin)/ai/agents/page.tsx b/admin-v2/app/(admin)/ai/agents/page.tsx index 6d88670..de96094 100644 --- a/admin-v2/app/(admin)/ai/agents/page.tsx +++ b/admin-v2/app/(admin)/ai/agents/page.tsx @@ -94,6 +94,19 @@ const mockAgents: AgentConfig[] = [ totalProcessed: 8934, avgResponseTime: 12, lastActivity: 'just now' + }, + { + id: 'compliance-advisor', + name: 'Compliance Advisor', + description: 'DSGVO/Compliance-Berater fuer SDK-Nutzer', + soulFile: 'compliance-advisor.soul.md', + color: '#6366f1', + icon: 'message', + status: 'running', + activeSessions: 0, + totalProcessed: 0, + avgResponseTime: 0, + lastActivity: new Date().toISOString() } ] diff --git a/admin-v2/app/(admin)/ai/rag-pipeline/dsfa/page.tsx b/admin-v2/app/(admin)/ai/rag-pipeline/dsfa/page.tsx new file mode 100644 index 0000000..aedbf66 --- /dev/null +++ b/admin-v2/app/(admin)/ai/rag-pipeline/dsfa/page.tsx @@ -0,0 +1,674 @@ +'use client' + +/** + * DSFA Document Manager + * + * Manages DSFA-related sources and documents for the RAG pipeline. + * Features: + * - View all registered DSFA sources with license info + * - Upload new documents + * - Trigger re-indexing + * - View corpus statistics + */ + +import { useState, useEffect } from 'react' +import Link from 'next/link' +import { + ArrowLeft, + RefreshCw, + Upload, + FileText, + Database, + Scale, + ExternalLink, + ChevronDown, + ChevronUp, + Search, + Filter, + CheckCircle, + Clock, + AlertCircle, + BookOpen +} from 'lucide-react' +import { + DSFASource, + DSFACorpusStats, + DSFASourceStats, + DSFALicenseCode, + DSFA_LICENSE_LABELS, + DSFA_DOCUMENT_TYPE_LABELS +} from '@/lib/sdk/types' + +// ============================================================================ +// TYPES +// ============================================================================ + +interface APIError { + message: string + status?: number +} + +// ============================================================================ +// API FUNCTIONS +// ============================================================================ + +const API_BASE = process.env.NEXT_PUBLIC_KLAUSUR_SERVICE_URL || 'http://localhost:8086' + +async function fetchSources(): Promise { + try { + const response = await fetch(`${API_BASE}/api/v1/dsfa-rag/sources`) + if (!response.ok) throw new Error('Failed to fetch sources') + return await response.json() + } catch { + // Return mock data for demo + return MOCK_SOURCES + } +} + +async function fetchStats(): Promise { + try { + const response = await fetch(`${API_BASE}/api/v1/dsfa-rag/stats`) + if (!response.ok) throw new Error('Failed to fetch stats') + return await response.json() + } catch { + return MOCK_STATS + } +} + +async function initializeCorpus(): Promise<{ sources_registered: number }> { + const response = await fetch(`${API_BASE}/api/v1/dsfa-rag/init`, { + method: 'POST', + }) + if (!response.ok) throw new Error('Failed to initialize corpus') + return await response.json() +} + +async function triggerIngestion(sourceCode: string): Promise { + const response = await fetch(`${API_BASE}/api/v1/dsfa-rag/sources/${sourceCode}/ingest`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}), + }) + if (!response.ok) throw new Error('Failed to trigger ingestion') +} + +// ============================================================================ +// MOCK DATA +// ============================================================================ + +const MOCK_SOURCES: DSFASource[] = [ + { + id: '1', + sourceCode: 'WP248', + name: 'WP248 rev.01 - Leitlinien zur DSFA', + fullName: 'Leitlinien zur Datenschutz-Folgenabschaetzung', + organization: 'Artikel-29-Datenschutzgruppe / EDPB', + sourceUrl: 'https://ec.europa.eu/newsroom/article29/items/611236/en', + licenseCode: 'EDPB-LICENSE', + licenseName: 'EDPB Document License', + attributionRequired: true, + attributionText: 'Quelle: WP248 rev.01, Artikel-29-Datenschutzgruppe (2017)', + documentType: 'guideline', + language: 'de', + }, + { + id: '2', + sourceCode: 'DSK_KP5', + name: 'Kurzpapier Nr. 5 - DSFA nach Art. 35 DS-GVO', + organization: 'Datenschutzkonferenz (DSK)', + sourceUrl: 'https://www.datenschutzkonferenz-online.de/media/kp/dsk_kpnr_5.pdf', + licenseCode: 'DL-DE-BY-2.0', + licenseName: 'Datenlizenz DE – Namensnennung 2.0', + licenseUrl: 'https://www.govdata.de/dl-de/by-2-0', + attributionRequired: true, + attributionText: 'Quelle: DSK Kurzpapier Nr. 5 (Stand: 2018)', + documentType: 'guideline', + language: 'de', + }, + { + id: '3', + sourceCode: 'BFDI_MUSS_PUBLIC', + name: 'BfDI DSFA-Liste (oeffentlicher Bereich)', + organization: 'BfDI', + sourceUrl: 'https://www.bfdi.bund.de', + licenseCode: 'DL-DE-ZERO-2.0', + licenseName: 'Datenlizenz DE – Zero 2.0', + attributionRequired: false, + attributionText: 'Quelle: BfDI, Liste gem. Art. 35 Abs. 4 DSGVO', + documentType: 'checklist', + language: 'de', + }, + { + id: '4', + sourceCode: 'NI_MUSS_PRIVATE', + name: 'LfD NI DSFA-Liste (nicht-oeffentlich)', + organization: 'LfD Niedersachsen', + sourceUrl: 'https://www.lfd.niedersachsen.de/download/131098', + licenseCode: 'DL-DE-BY-2.0', + licenseName: 'Datenlizenz DE – Namensnennung 2.0', + attributionRequired: true, + attributionText: 'Quelle: LfD Niedersachsen, DSFA-Muss-Liste', + documentType: 'checklist', + language: 'de', + }, +] + +const MOCK_STATS: DSFACorpusStats = { + sources: [ + { + sourceId: '1', + sourceCode: 'WP248', + name: 'WP248 rev.01', + organization: 'EDPB', + licenseCode: 'EDPB-LICENSE', + documentType: 'guideline', + documentCount: 1, + chunkCount: 50, + lastIndexedAt: '2026-02-09T10:00:00Z', + }, + { + sourceId: '2', + sourceCode: 'DSK_KP5', + name: 'DSK Kurzpapier Nr. 5', + organization: 'DSK', + licenseCode: 'DL-DE-BY-2.0', + documentType: 'guideline', + documentCount: 1, + chunkCount: 35, + lastIndexedAt: '2026-02-09T10:00:00Z', + }, + ], + totalSources: 45, + totalDocuments: 45, + totalChunks: 850, + qdrantCollection: 'bp_dsfa_corpus', + qdrantPointsCount: 850, + qdrantStatus: 'green', +} + +// ============================================================================ +// COMPONENTS +// ============================================================================ + +function LicenseBadge({ licenseCode }: { licenseCode: DSFALicenseCode }) { + const colorMap: Record = { + 'DL-DE-BY-2.0': 'bg-blue-100 text-blue-700 border-blue-200', + 'DL-DE-ZERO-2.0': 'bg-gray-100 text-gray-700 border-gray-200', + 'CC-BY-4.0': 'bg-green-100 text-green-700 border-green-200', + 'EDPB-LICENSE': 'bg-purple-100 text-purple-700 border-purple-200', + 'PUBLIC_DOMAIN': 'bg-gray-100 text-gray-600 border-gray-200', + 'PROPRIETARY': 'bg-amber-100 text-amber-700 border-amber-200', + } + + return ( + + + {DSFA_LICENSE_LABELS[licenseCode] || licenseCode} + + ) +} + +function DocumentTypeBadge({ type }: { type?: string }) { + if (!type) return null + + const colorMap: Record = { + guideline: 'bg-indigo-100 text-indigo-700', + checklist: 'bg-emerald-100 text-emerald-700', + regulation: 'bg-red-100 text-red-700', + template: 'bg-orange-100 text-orange-700', + } + + return ( + + {DSFA_DOCUMENT_TYPE_LABELS[type as keyof typeof DSFA_DOCUMENT_TYPE_LABELS] || type} + + ) +} + +function StatusIndicator({ status }: { status: string }) { + const statusConfig: Record = { + green: { color: 'text-green-500', icon: , label: 'Aktiv' }, + yellow: { color: 'text-yellow-500', icon: , label: 'Ausstehend' }, + red: { color: 'text-red-500', icon: , label: 'Fehler' }, + } + + const config = statusConfig[status] || statusConfig.yellow + + return ( + + {config.icon} + {config.label} + + ) +} + +function SourceCard({ + source, + stats, + onIngest, + isIngesting +}: { + source: DSFASource + stats?: DSFASourceStats + onIngest: () => void + isIngesting: boolean +}) { + const [isExpanded, setIsExpanded] = useState(false) + + return ( +
+
+
+
+
+ + {source.sourceCode} + + +
+

+ {source.name} +

+ {source.organization && ( +

+ {source.organization} +

+ )} +
+ +
+ +
+ + {stats && ( + <> + + {stats.documentCount} Dok. + + + {stats.chunkCount} Chunks + + + )} +
+ + {source.attributionRequired && ( +
+ Attribution: {source.attributionText} +
+ )} +
+ + {isExpanded && ( +
+
+ {source.sourceUrl && ( + <> +
Quelle:
+
+ + Link + +
+ + )} + {source.licenseUrl && ( + <> +
Lizenz-URL:
+
+ + {source.licenseName} + +
+ + )} +
Sprache:
+
{source.language}
+ {stats?.lastIndexedAt && ( + <> +
Zuletzt indexiert:
+
{new Date(stats.lastIndexedAt).toLocaleString('de-DE')}
+ + )} +
+ +
+ +
+
+ )} +
+ ) +} + +function StatsOverview({ stats }: { stats: DSFACorpusStats }) { + return ( +
+
+

+ + Corpus-Statistik +

+ +
+ +
+
+

+ {stats.totalSources} +

+

Quellen

+
+
+

+ {stats.totalDocuments} +

+

Dokumente

+
+
+

+ {stats.totalChunks.toLocaleString()} +

+

Chunks

+
+
+

+ {stats.qdrantPointsCount.toLocaleString()} +

+

Vektoren

+
+
+ +
+

+ Collection:{' '} + + {stats.qdrantCollection} + +

+
+
+ ) +} + +// ============================================================================ +// MAIN PAGE +// ============================================================================ + +export default function DSFADocumentManagerPage() { + const [sources, setSources] = useState([]) + const [stats, setStats] = useState(null) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) + const [searchQuery, setSearchQuery] = useState('') + const [filterType, setFilterType] = useState('all') + const [ingestingSource, setIngestingSource] = useState(null) + const [isInitializing, setIsInitializing] = useState(false) + + useEffect(() => { + async function loadData() { + setIsLoading(true) + try { + const [sourcesData, statsData] = await Promise.all([ + fetchSources(), + fetchStats(), + ]) + setSources(sourcesData) + setStats(statsData) + setError(null) + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load data') + setSources(MOCK_SOURCES) + setStats(MOCK_STATS) + } finally { + setIsLoading(false) + } + } + loadData() + }, []) + + const handleInitialize = async () => { + setIsInitializing(true) + try { + await initializeCorpus() + // Reload data + const [sourcesData, statsData] = await Promise.all([ + fetchSources(), + fetchStats(), + ]) + setSources(sourcesData) + setStats(statsData) + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to initialize') + } finally { + setIsInitializing(false) + } + } + + const handleIngest = async (sourceCode: string) => { + setIngestingSource(sourceCode) + try { + await triggerIngestion(sourceCode) + // Reload stats + const statsData = await fetchStats() + setStats(statsData) + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to ingest') + } finally { + setIngestingSource(null) + } + } + + // Filter sources + const filteredSources = sources.filter(source => { + const matchesSearch = searchQuery === '' || + source.name.toLowerCase().includes(searchQuery.toLowerCase()) || + source.sourceCode.toLowerCase().includes(searchQuery.toLowerCase()) || + source.organization?.toLowerCase().includes(searchQuery.toLowerCase()) + + const matchesType = filterType === 'all' || source.documentType === filterType + + return matchesSearch && matchesType + }) + + // Get stats by source code + const getStatsForSource = (sourceCode: string): DSFASourceStats | undefined => { + return stats?.sources.find(s => s.sourceCode === sourceCode) + } + + return ( +
+
+ {/* Header */} +
+ + + Zurueck zur RAG-Pipeline + + +
+
+

+ + DSFA-Quellen Manager +

+

+ Verwalten Sie DSFA-Guidance Dokumente mit vollstaendiger Lizenzattribution +

+
+ +
+ + +
+
+
+ + {/* Error Banner */} + {error && ( +
+
+ + {error} + +
+
+ )} + + {/* Stats Overview */} + {stats && } + + {/* Search & Filter */} +
+
+ + setSearchQuery(e.target.value)} + className="w-full pl-10 pr-4 py-2 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800" + /> +
+
+ + +
+
+ + {/* Sources List */} +
+
+

+ Registrierte Quellen ({filteredSources.length}) +

+
+ + {isLoading ? ( +
+
+
+

Lade Quellen...

+
+
+ ) : filteredSources.length === 0 ? ( +
+ +

+ {searchQuery || filterType !== 'all' + ? 'Keine Quellen gefunden' + : 'Noch keine Quellen registriert'} +

+ {!searchQuery && filterType === 'all' && ( + + )} +
+ ) : ( +
+ {filteredSources.map(source => ( + handleIngest(source.sourceCode)} + isIngesting={ingestingSource === source.sourceCode} + /> + ))} +
+ )} +
+ + {/* Info Box */} +
+

+ Ueber die Lizenzattribution +

+

+ Alle DSFA-Quellen werden mit vollstaendiger Lizenzinformation gespeichert. + Bei der Nutzung der RAG-Suche werden automatisch die korrekten Attributionen angezeigt. +

+
+
+ + Namensnennung +
+
+ + Keine Attribution +
+
+ + CC Attribution +
+
+
+
+
+ ) +} diff --git a/admin-v2/app/(admin)/ai/rag-pipeline/page.tsx b/admin-v2/app/(admin)/ai/rag-pipeline/page.tsx index 375aa6e..8e39b8f 100644 --- a/admin-v2/app/(admin)/ai/rag-pipeline/page.tsx +++ b/admin-v2/app/(admin)/ai/rag-pipeline/page.tsx @@ -128,6 +128,16 @@ const MOCK_DATA_SOURCES: DataSource[] = [ last_updated: '2025-01-10T08:00:00Z', status: 'active', }, + { + id: 'dsfa', + name: 'DSFA-Guidance', + description: 'WP248, DSK Kurzpapiere, Muss-Listen aller Bundeslaender mit Quellenattribution', + collection: 'bp_dsfa_corpus', + document_count: 45, + chunk_count: 850, + last_updated: '2026-02-09T10:00:00Z', + status: 'active', + }, { id: 'schulordnungen', name: 'Schulordnungen', @@ -899,6 +909,21 @@ function DataSourcesTab({ sources }: { sources: DataSource[] }) { Regelwerk hinzufuegen β†’
+
+
πŸ“‹
+

+ DSFA-Quellen verwalten +

+

+ WP248, DSK, Muss-Listen mit Lizenzattribution +

+ + DSFA-Manager oeffnen β†’ + +
diff --git a/admin-v2/app/(admin)/development/companion/page.tsx b/admin-v2/app/(admin)/development/companion/page.tsx new file mode 100644 index 0000000..8499d6a --- /dev/null +++ b/admin-v2/app/(admin)/development/companion/page.tsx @@ -0,0 +1,39 @@ +'use client' + +import { PagePurpose } from '@/components/common/PagePurpose' +import { getModuleByHref } from '@/lib/navigation' +import { GraduationCap, Construction } from 'lucide-react' + +export default function CompanionPage() { + const moduleInfo = getModuleByHref('/development/companion') + + return ( +
+ {moduleInfo && ( + + )} + +
+
+
+ +
+
+

Companion Dev

+

+ Lesson-Modus Entwicklung fuer strukturiertes Lernen. +

+
+ + In Entwicklung +
+
+
+ ) +} diff --git a/admin-v2/app/(admin)/education/companion/page.tsx b/admin-v2/app/(admin)/education/companion/page.tsx new file mode 100644 index 0000000..448da1a --- /dev/null +++ b/admin-v2/app/(admin)/education/companion/page.tsx @@ -0,0 +1,76 @@ +'use client' + +import { Suspense } from 'react' +import { PagePurpose } from '@/components/common/PagePurpose' +import { getModuleByHref } from '@/lib/navigation' +import { CompanionDashboard } from '@/components/companion/CompanionDashboard' +import { GraduationCap } from 'lucide-react' + +function LoadingFallback() { + return ( +
+ {/* Header Skeleton */} +
+
+
+ {[1, 2, 3, 4].map((i) => ( +
+ ))} +
+
+ + {/* Phase Timeline Skeleton */} +
+
+
+ {[1, 2, 3, 4, 5].map((i) => ( +
+
+ {i < 5 &&
} +
+ ))} +
+
+ + {/* Stats Skeleton */} +
+ {[1, 2, 3, 4].map((i) => ( +
+
+
+
+ ))} +
+ + {/* Content Skeleton */} +
+
+
+
+
+ ) +} + +export default function CompanionPage() { + const moduleInfo = getModuleByHref('/education/companion') + + return ( +
+ {/* Page Purpose Header */} + {moduleInfo && ( + + )} + + {/* Main Companion Dashboard */} + }> + + +
+ ) +} diff --git a/admin-v2/app/(sdk)/layout.tsx b/admin-v2/app/(sdk)/layout.tsx index cfb6406..fb78d27 100644 --- a/admin-v2/app/(sdk)/layout.tsx +++ b/admin-v2/app/(sdk)/layout.tsx @@ -1,11 +1,12 @@ 'use client' import { useEffect, useState } from 'react' -import { useRouter } from 'next/navigation' +import { useRouter, usePathname } from 'next/navigation' import { SDKProvider } from '@/lib/sdk' import { SDKSidebar } from '@/components/sdk/Sidebar/SDKSidebar' import { CommandBar } from '@/components/sdk/CommandBar' import { SDKPipelineSidebar } from '@/components/sdk/SDKPipelineSidebar' +import { ComplianceAdvisorWidget } from '@/components/sdk/ComplianceAdvisorWidget' import { useSDK } from '@/lib/sdk' import { getStoredRole } from '@/lib/roles' @@ -86,6 +87,10 @@ function SDKHeader({ sidebarCollapsed }: { sidebarCollapsed: boolean }) { function SDKInnerLayout({ children }: { children: React.ReactNode }) { const { isCommandBarOpen, setCommandBarOpen } = useSDK() const [sidebarCollapsed, setSidebarCollapsed] = useState(false) + const pathname = usePathname() + + // Extract current step from pathname (e.g., /sdk/vvt -> vvt) + const currentStep = pathname?.split('/').pop() || 'default' // Load collapsed state from localStorage useEffect(() => { @@ -123,6 +128,9 @@ function SDKInnerLayout({ children }: { children: React.ReactNode }) { {/* Pipeline Sidebar (FAB on mobile/tablet, fixed on desktop xl+) */} + + {/* Compliance Advisor Widget */} +
) } diff --git a/admin-v2/app/(sdk)/sdk/advisory-board/documentation/page.tsx b/admin-v2/app/(sdk)/sdk/advisory-board/documentation/page.tsx new file mode 100644 index 0000000..a624c61 --- /dev/null +++ b/admin-v2/app/(sdk)/sdk/advisory-board/documentation/page.tsx @@ -0,0 +1,596 @@ +'use client' + +/** + * UCCA System Documentation Page (SDK Version) + * + * Displays architecture documentation, auditor information, + * and transparency data for the UCCA compliance system. + */ + +import { useState, useEffect } from 'react' +import Link from 'next/link' + +// ============================================================================ +// Types +// ============================================================================ + +type DocTab = 'overview' | 'architecture' | 'auditor' | 'rules' | 'legal-corpus' + +interface Rule { + code: string + category: string + title: string + description: string + severity: string + gdpr_ref: string + rationale?: string + risk_add?: number +} + +interface Pattern { + id: string + title: string + description: string + benefit?: string + effort?: string + risk_reduction?: number +} + +interface Control { + id: string + title: string + description: string + gdpr_ref?: string + effort?: string +} + +// ============================================================================ +// API Configuration +// ============================================================================ + +const API_BASE = process.env.NEXT_PUBLIC_SDK_API_URL || 'https://macmini:8090' + +// ============================================================================ +// Main Component +// ============================================================================ + +export default function DocumentationPage() { + const [activeTab, setActiveTab] = useState('overview') + const [rules, setRules] = useState([]) + const [patterns, setPatterns] = useState([]) + const [controls, setControls] = useState([]) + const [policyVersion, setPolicyVersion] = useState('') + const [loading, setLoading] = useState(false) + + useEffect(() => { + const fetchData = async () => { + setLoading(true) + try { + const rulesRes = await fetch(`${API_BASE}/sdk/v1/ucca/rules`, { + headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000000' } + }) + if (rulesRes.ok) { + const rulesData = await rulesRes.json() + setRules(rulesData.rules || []) + setPolicyVersion(rulesData.policy_version || '') + } + + const patternsRes = await fetch(`${API_BASE}/sdk/v1/ucca/patterns`, { + headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000000' } + }) + if (patternsRes.ok) { + const patternsData = await patternsRes.json() + setPatterns(patternsData.patterns || []) + } + + const controlsRes = await fetch(`${API_BASE}/sdk/v1/ucca/controls`, { + headers: { 'X-Tenant-ID': '00000000-0000-0000-0000-000000000000' } + }) + if (controlsRes.ok) { + const controlsData = await controlsRes.json() + setControls(controlsData.controls || []) + } + } catch (error) { + console.error('Failed to fetch documentation data:', error) + } finally { + setLoading(false) + } + } + + fetchData() + }, []) + + // ============================================================================ + // Tab Content Renderers + // ============================================================================ + + const renderOverview = () => ( +
+
+
+

Deterministische Regeln

+
{rules.length}
+

+ Alle Entscheidungen basieren auf transparenten, nachvollziehbaren Regeln. +

+
+
+

Architektur-Patterns

+
{patterns.length}
+

+ Best-Practice-Loesungen fuer datenschutzkonforme KI-Systeme. +

+
+
+

Compliance-Kontrollen

+
{controls.length}
+

+ Technische und organisatorische Massnahmen. +

+
+
+ +
+

Was ist UCCA?

+
+

+ UCCA (Use-Case Compliance & Feasibility Advisor) ist ein deterministisches + Compliance-Pruefwerkzeug, das Organisationen bei der Bewertung geplanter KI-Anwendungsfaelle + hinsichtlich ihrer datenschutzrechtlichen Zulaessigkeit unterstuetzt. +

+

Kernprinzipien

+
    +
  • + Determinismus: Alle Entscheidungen basieren auf transparenten Regeln. + Die KI trifft KEINE autonomen Entscheidungen. +
  • +
  • + Transparenz: Alle Regeln, Kontrollen und Patterns sind einsehbar. +
  • +
  • + Human-in-the-Loop: Kritische Entscheidungen erfordern immer + menschliche Pruefung durch DSB oder Legal. +
  • +
  • + Rechtsgrundlage: Jede Regel referenziert konkrete DSGVO-Artikel. +
  • +
+
+
+ +
+

+ Wichtiger Hinweis zur KI-Nutzung +

+

+ Das System verwendet KI (LLM) ausschliesslich zur Erklaerung bereits + getroffener Regelentscheidungen. Die eigentliche Compliance-Bewertung erfolgt + rein deterministisch durch die Policy Engine. BLOCK-Entscheidungen + koennen NICHT durch KI ueberschrieben werden. +

+
+
+ ) + + const renderArchitecture = () => ( +
+
+

Systemarchitektur

+ +
+
{`
+β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+β”‚                        Frontend (Next.js)                            β”‚
+β”‚                     admin-v2:3000/sdk/advisory-board                 β”‚
+β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+                                    β”‚ HTTPS
+                                    β–Ό
+β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+β”‚                     AI Compliance SDK (Go)                           β”‚
+β”‚                          Port 8090                                   β”‚
+β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
+β”‚  β”‚                    Policy Engine                              β”‚   β”‚
+β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚   β”‚
+β”‚  β”‚  β”‚  YAML-basierte Regeln (ucca_policy_v1.yaml)           β”‚   β”‚   β”‚
+β”‚  β”‚  β”‚  ~45 Regeln in 7 Kategorien                           β”‚   β”‚   β”‚
+β”‚  β”‚  β”‚  Deterministisch - Kein LLM in Entscheidungslogik     β”‚   β”‚   β”‚
+β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚   β”‚
+β”‚  β”‚                          β”‚                                    β”‚   β”‚
+β”‚  β”‚                          β–Ό                                    β”‚   β”‚
+β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚   β”‚
+β”‚  β”‚  β”‚  Controls      β”‚  β”‚  Patterns      β”‚  β”‚  Examples      β”‚  β”‚   β”‚
+β”‚  β”‚  β”‚  Library       β”‚  β”‚  Library       β”‚  β”‚  Library       β”‚  β”‚   β”‚
+β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚   β”‚
+β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
+β”‚                                                                      β”‚
+β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”‚
+β”‚  β”‚  LLM Integration β”‚  β”‚  Legal RAG       │──────┐                  β”‚
+β”‚  β”‚  (nur Explain)   β”‚  β”‚  Client          β”‚      β”‚                  β”‚
+β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚                  β”‚
+β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+                              β”‚                    β”‚
+                              β–Ό                    β–Ό
+β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
+β”‚                        Datenschicht                                  β”‚
+β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”‚
+β”‚  β”‚    PostgreSQL      β”‚  β”‚    Qdrant          β”‚                     β”‚
+β”‚  β”‚    (Assessments,   β”‚  β”‚    (Legal Corpus,  β”‚                     β”‚
+β”‚  β”‚     Escalations)   β”‚  β”‚     2,274 Chunks)  β”‚                     β”‚
+β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
+β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
+          `}
+
+ +
+
+

Datenfluss

+
    +
  1. Benutzer beschreibt Use Case im Frontend
  2. +
  3. Policy Engine evaluiert gegen alle Regeln
  4. +
  5. Ergebnis mit Controls + Patterns zurueck
  6. +
  7. Optional: LLM erklaert das Ergebnis
  8. +
  9. Bei Risiko: Automatische Eskalation
  10. +
+
+
+

Sicherheitsmerkmale

+
    +
  • TLS 1.3 Verschluesselung
  • +
  • RBAC mit Tenant-Isolation
  • +
  • JWT-basierte Authentifizierung
  • +
  • Audit-Trail aller Aktionen
  • +
  • Keine Rohtext-Speicherung (nur Hash)
  • +
+
+
+
+ +
+

Eskalations-Workflow

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LevelAusloeserPrueferSLA
E0Nur INFO-Regeln, Risiko < 20Automatisch-
E1WARN-Regeln, Risiko 20-40Team-Lead24h / 72h
E2Art. 9 Daten, DSFA empfohlen, Risiko 40-60DSB8h / 48h
E3BLOCK-Regeln, Art. 22, Risiko > 60DSB + Legal4h / 24h
+
+
+
+ ) + + const renderAuditorInfo = () => ( +
+
+

+ Dokumentation fuer externe Auditoren +

+

+ Diese Dokumentation erfuellt die Anforderungen nach Art. 30 DSGVO (Verzeichnis von + Verarbeitungstaetigkeiten) und dient als Grundlage fuer Audits nach Art. 32 DSGVO. +

+ +
+
+

1. Zweck des Systems

+

+ UCCA ist ein Compliance-Pruefwerkzeug zur Bewertung geplanter KI-Anwendungsfaelle + hinsichtlich ihrer datenschutzrechtlichen Zulaessigkeit. +

+
+ +
+

2. Rechtsgrundlage

+
    +
  • Art. 6 Abs. 1 lit. c DSGVO - Erfuellung rechtlicher Verpflichtungen
  • +
  • Art. 6 Abs. 1 lit. f DSGVO - Berechtigte Interessen (Compliance-Management)
  • +
+
+ +
+

3. Verarbeitete Datenkategorien

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KategorieSpeicherungAufbewahrung
Use-Case-BeschreibungNur Hash (SHA-256)10 Jahre
BewertungsergebnisVollstaendig10 Jahre
Audit-TrailVollstaendig10 Jahre
Eskalations-HistorieVollstaendig10 Jahre
+
+
+ +
+

4. Keine autonomen KI-Entscheidungen

+

+ Das System trifft KEINE automatisierten Einzelentscheidungen im Sinne + von Art. 22 DSGVO, da: +

+
    +
  • Regelauswertung ist keine rechtlich bindende Entscheidung
  • +
  • Alle kritischen Faelle werden menschlich geprueft (E1-E3)
  • +
  • BLOCK-Entscheidungen erfordern immer menschliche Freigabe
  • +
  • Betroffene haben Anfechtungsmoeglichkeit ueber Eskalation
  • +
+
+ +
+

5. Technische und Organisatorische Massnahmen

+
+
+ Vertraulichkeit +
    +
  • RBAC mit Tenant-Isolation
  • +
  • TLS 1.3 Verschluesselung
  • +
  • AES-256 at rest
  • +
+
+
+ Integritaet +
    +
  • Unveraenderlicher Audit-Trail
  • +
  • Policy-Versionierung
  • +
  • Input-Validierung
  • +
+
+
+
+
+
+
+ ) + + const renderRulesTab = () => ( +
+
+
+

Regel-Katalog

+

Policy Version: {policyVersion}

+
+
+ {rules.length} Regeln insgesamt +
+
+ + {loading ? ( +
Lade Regeln...
+ ) : ( +
+ {Array.from(new Set(rules.map(r => r.category))).map(category => ( +
+
+

{category}

+

+ {rules.filter(r => r.category === category).length} Regeln +

+
+
+ {rules.filter(r => r.category === category).map(rule => ( +
+
+
+
+ {rule.code} + + {rule.severity} + +
+
{rule.title}
+
{rule.description}
+ {rule.gdpr_ref && ( +
{rule.gdpr_ref}
+ )} +
+ {rule.risk_add && ( +
+ +{rule.risk_add} +
+ )} +
+
+ ))} +
+
+ ))} +
+ )} +
+ ) + + const renderLegalCorpus = () => ( +
+
+

Legal RAG Corpus

+

+ Das System verwendet einen semantischen Suchindex mit 2.274 Chunks aus 19 EU-Regulierungen + fuer rechtsgrundlagenbasierte Erklaerungen. +

+ +
+
+

Indexierte Regulierungen

+
    +
  • DSGVO - Datenschutz-Grundverordnung
  • +
  • AI Act - EU KI-Verordnung
  • +
  • NIS2 - Cybersicherheits-Richtlinie
  • +
  • CRA - Cyber Resilience Act
  • +
  • Data Act - Datengesetz
  • +
  • DSA/DMA - Digital Services/Markets Act
  • +
  • DPF - EU-US Data Privacy Framework
  • +
  • BSI-TR-03161 - Digitale Identitaeten
  • +
+
+
+

RAG-Funktionalitaet

+
    +
  • Hybride Suche (Dense + BM25)
  • +
  • Semantisches Chunking
  • +
  • Cross-Encoder Reranking
  • +
  • Artikel-Referenz-Extraktion
  • +
  • Mehrsprachig (DE/EN)
  • +
+
+
+
+ +
+

Verwendung im System

+
+
+
+ 1 +
+
+
Benutzer fordert Erklaerung an
+
+ Nach der Bewertung kann eine LLM-basierte Erklaerung generiert werden. +
+
+
+
+
+ 2 +
+
+
Legal RAG Client sucht relevante Artikel
+
+ Basierend auf den ausgeloesten Regeln werden passende Gesetzestexte gefunden. +
+
+
+
+
+ 3 +
+
+
LLM generiert Erklaerung mit Rechtsgrundlage
+
+ Die Erklaerung referenziert konkrete Artikel aus DSGVO, AI Act etc. +
+
+
+
+
+
+ ) + + // ============================================================================ + // Tabs Configuration + // ============================================================================ + + const tabs: { id: DocTab; label: string }[] = [ + { id: 'overview', label: 'Uebersicht' }, + { id: 'architecture', label: 'Architektur' }, + { id: 'auditor', label: 'Fuer Auditoren' }, + { id: 'rules', label: 'Regel-Katalog' }, + { id: 'legal-corpus', label: 'Legal RAG' }, + ] + + // ============================================================================ + // Main Render + // ============================================================================ + + return ( +
+
+
+

UCCA System-Dokumentation

+

+ Transparente Dokumentation des UCCA-Systems fuer Entwickler, Auditoren und Datenschutzbeauftragte. +

+
+ + Zurueck zum Advisory Board + +
+ + {/* Tab Navigation */} +
+
+ {tabs.map(tab => ( + + ))} +
+ +
+ {activeTab === 'overview' && renderOverview()} + {activeTab === 'architecture' && renderArchitecture()} + {activeTab === 'auditor' && renderAuditorInfo()} + {activeTab === 'rules' && renderRulesTab()} + {activeTab === 'legal-corpus' && renderLegalCorpus()} +
+
+
+ ) +} diff --git a/admin-v2/app/(sdk)/sdk/advisory-board/page.tsx b/admin-v2/app/(sdk)/sdk/advisory-board/page.tsx index c0ffd86..1276a80 100644 --- a/admin-v2/app/(sdk)/sdk/advisory-board/page.tsx +++ b/admin-v2/app/(sdk)/sdk/advisory-board/page.tsx @@ -1,6 +1,7 @@ 'use client' import React, { useState } from 'react' +import Link from 'next/link' import { useSDK, UseCaseAssessment } from '@/lib/sdk' // ============================================================================= @@ -601,17 +602,25 @@ export default function AdvisoryBoardPage() { Erfassen Sie Ihre KI-AnwendungsfΓ€lle und erhalten Sie eine erste Compliance-Bewertung

- {!showWizard && ( - - )} + UCCA-System Dokumentation ansehen + + {!showWizard && ( + + )} +
{/* Wizard or List */} diff --git a/admin-v2/app/(sdk)/sdk/audit-report/page.tsx b/admin-v2/app/(sdk)/sdk/audit-report/page.tsx new file mode 100644 index 0000000..906f2d3 --- /dev/null +++ b/admin-v2/app/(sdk)/sdk/audit-report/page.tsx @@ -0,0 +1,343 @@ +'use client' + +/** + * Audit Report Management Page (SDK Version) + * + * Create and manage GDPR audit sessions with PDF report generation. + */ + +import { useState, useEffect } from 'react' +import { useSDK } from '@/lib/sdk' +import StepHeader from '@/components/sdk/StepHeader/StepHeader' + +interface AuditSession { + id: string + name: string + description?: string + auditor_name: string + auditor_email?: string + auditor_organization?: string + status: 'draft' | 'in_progress' | 'completed' | 'archived' + regulation_ids?: string[] + total_items: number + completed_items: number + compliant_count: number + non_compliant_count: number + completion_percentage: number + created_at: string + started_at?: string + completed_at?: string +} + +const REGULATIONS = [ + { code: 'GDPR', name: 'DSGVO / GDPR', description: 'EU-Datenschutzgrundverordnung' }, + { code: 'BDSG', name: 'BDSG', description: 'Bundesdatenschutzgesetz' }, + { code: 'TTDSG', name: 'TTDSG', description: 'Telekommunikation-Telemedien-Datenschutz' }, +] + +export default function AuditReportPage() { + const { state } = useSDK() + const [sessions, setSessions] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const [activeTab, setActiveTab] = useState<'sessions' | 'new' | 'export'>('sessions') + + const [newSession, setNewSession] = useState({ + name: '', + description: '', + auditor_name: '', + auditor_email: '', + auditor_organization: '', + regulation_codes: [] as string[], + }) + const [creating, setCreating] = useState(false) + const [generatingPdf, setGeneratingPdf] = useState(null) + const [pdfLanguage, setPdfLanguage] = useState<'de' | 'en'>('de') + const [statusFilter, setStatusFilter] = useState('all') + + useEffect(() => { + fetchSessions() + }, [statusFilter]) + + const fetchSessions = async () => { + try { + setLoading(true) + const params = statusFilter !== 'all' ? `?status=${statusFilter}` : '' + const res = await fetch(`/api/admin/audit/sessions${params}`) + if (!res.ok) throw new Error('Fehler beim Laden der Audit-Sessions') + const data = await res.json() + setSessions(data.sessions || []) + } catch (err) { + setError(err instanceof Error ? err.message : 'Unbekannter Fehler') + } finally { + setLoading(false) + } + } + + const createSession = async () => { + if (!newSession.name || !newSession.auditor_name) { + setError('Name und Auditor-Name sind Pflichtfelder') + return + } + try { + setCreating(true) + const res = await fetch('/api/admin/audit/sessions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(newSession), + }) + if (!res.ok) throw new Error('Fehler beim Erstellen der Session') + setNewSession({ name: '', description: '', auditor_name: '', auditor_email: '', auditor_organization: '', regulation_codes: [] }) + setActiveTab('sessions') + fetchSessions() + } catch (err) { + setError(err instanceof Error ? err.message : 'Unbekannter Fehler') + } finally { + setCreating(false) + } + } + + const startSession = async (sessionId: string) => { + try { + const res = await fetch(`/api/admin/audit/sessions/${sessionId}/start`, { method: 'PUT' }) + if (!res.ok) throw new Error('Fehler beim Starten der Session') + fetchSessions() + } catch (err) { + setError(err instanceof Error ? err.message : 'Unbekannter Fehler') + } + } + + const completeSession = async (sessionId: string) => { + try { + const res = await fetch(`/api/admin/audit/sessions/${sessionId}/complete`, { method: 'PUT' }) + if (!res.ok) throw new Error('Fehler beim Abschliessen der Session') + fetchSessions() + } catch (err) { + setError(err instanceof Error ? err.message : 'Unbekannter Fehler') + } + } + + const deleteSession = async (sessionId: string) => { + if (!confirm('Session wirklich loeschen?')) return + try { + const res = await fetch(`/api/admin/audit/sessions/${sessionId}`, { method: 'DELETE' }) + if (!res.ok) throw new Error('Fehler beim Loeschen der Session') + fetchSessions() + } catch (err) { + setError(err instanceof Error ? err.message : 'Unbekannter Fehler') + } + } + + const downloadPdf = async (sessionId: string) => { + try { + setGeneratingPdf(sessionId) + const res = await fetch(`/api/admin/audit/sessions/${sessionId}/pdf?language=${pdfLanguage}`) + if (!res.ok) throw new Error('Fehler bei der PDF-Generierung') + const blob = await res.blob() + const url = window.URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `audit-report-${sessionId}.pdf` + document.body.appendChild(a) + a.click() + window.URL.revokeObjectURL(url) + document.body.removeChild(a) + } catch (err) { + setError(err instanceof Error ? err.message : 'Unbekannter Fehler') + } finally { + setGeneratingPdf(null) + } + } + + const getStatusBadge = (status: string) => { + const styles: Record = { + draft: 'bg-slate-100 text-slate-700', + in_progress: 'bg-blue-100 text-blue-700', + completed: 'bg-green-100 text-green-700', + archived: 'bg-purple-100 text-purple-700', + } + const labels: Record = { + draft: 'Entwurf', + in_progress: 'In Bearbeitung', + completed: 'Abgeschlossen', + archived: 'Archiviert', + } + return ( + + {labels[status] || status} + + ) + } + + const getComplianceColor = (percentage: number) => { + if (percentage >= 80) return 'text-green-600' + if (percentage >= 50) return 'text-yellow-600' + return 'text-red-600' + } + + return ( +
+ + + {error && ( +
+ {error} + +
+ )} + + {/* Tabs */} +
+ {(['sessions', 'new', 'export'] as const).map((tab) => ( + + ))} +
+ + {/* Sessions Tab */} + {activeTab === 'sessions' && ( +
+
+ + +
+ + {loading ? ( +
Lade Audit-Sessions...
+ ) : sessions.length === 0 ? ( +
+

Keine Audit-Sessions vorhanden

+

Erstellen Sie ein neues Audit, um mit der DSGVO-Pruefung zu beginnen.

+ +
+ ) : ( +
+ {sessions.map((session) => ( +
+
+
+
+

{session.name}

+ {getStatusBadge(session.status)} +
+ {session.description &&

{session.description}

} +
+ Auditor: {session.auditor_name} + {session.auditor_organization && | {session.auditor_organization}} + | Erstellt: {new Date(session.created_at).toLocaleDateString('de-DE')} +
+
+
+
{session.completion_percentage}%
+
{session.completed_items} / {session.total_items} Punkte
+
+
+
+
= 80 ? 'bg-green-500' : session.completion_percentage >= 50 ? 'bg-yellow-500' : 'bg-red-500'}`} style={{ width: `${session.completion_percentage}%` }} /> +
+
+
{session.compliant_count}
Konform
+
{session.non_compliant_count}
Nicht Konform
+
{session.total_items - session.completed_items}
Ausstehend
+
+
+ {session.status === 'draft' && } + {session.status === 'in_progress' && } + {(session.status === 'completed' || session.status === 'in_progress') && ( + + )} + {(session.status === 'draft' || session.status === 'archived') && } +
+
+ ))} +
+ )} +
+ )} + + {/* New Session Tab */} + {activeTab === 'new' && ( +
+

Neues Audit erstellen

+
+
+ + setNewSession({ ...newSession, name: e.target.value })} placeholder="z.B. DSGVO Jahresaudit 2026" className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> +
+
+ +