Add CLAUDE.md, MkDocs docs, docs page in admin, .claude/rules

- CLAUDE.md: Comprehensive documentation for Compliance SDK platform
- docs-src: AI-Compliance-SDK docs (architecture, developer, auditor, SBOM)
- mkdocs.yml: Compliance-specific nav with purple theme
- docker-compose: Added docs service (port 8011, profile: docs)
- admin-compliance: New /development/docs page with iframe + quick links
- navigation.ts: Added development category with docs module
- .claude/rules: testing, docs, open-source, compliance-checklist

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Boenisch
2026-02-12 00:49:28 +01:00
parent 4435e7ea0a
commit c11270f8e0
22 changed files with 4374 additions and 31 deletions

View File

@@ -0,0 +1,188 @@
'use client'
import { useState } from 'react'
import { ExternalLink, Maximize2, Minimize2, RefreshCw, Search, BookOpen, ArrowRight } from 'lucide-react'
// Quick links to compliance documentation sections
const quickLinks = [
{ name: 'AI Compliance SDK', path: 'services/ai-compliance-sdk/', icon: '🔒' },
{ name: 'Architektur', path: 'services/ai-compliance-sdk/ARCHITECTURE/', icon: '🏗️' },
{ name: 'Developer Guide', path: 'services/ai-compliance-sdk/DEVELOPER/', icon: '👩‍💻' },
{ name: 'Auditor Doku', path: 'services/ai-compliance-sdk/AUDITOR_DOCUMENTATION/', icon: '📋' },
{ name: 'SBOM', path: 'services/ai-compliance-sdk/SBOM/', icon: '📦' },
{ name: 'CI/CD Pipeline', path: 'development/ci-cd-pipeline/', icon: '🚀' },
]
export default function DocsPage() {
const [isFullscreen, setIsFullscreen] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [currentPath, setCurrentPath] = useState('')
const getDocsUrl = () => {
if (typeof window !== 'undefined') {
const protocol = window.location.protocol
const hostname = window.location.hostname
const port = window.location.port
return `${protocol}//${hostname}${port ? ':' + port : ''}/docs`
}
return '/docs'
}
const docsUrl = getDocsUrl()
const handleIframeLoad = () => {
setIsLoading(false)
}
const navigateTo = (path: string) => {
setCurrentPath(path)
setIsLoading(true)
}
const toggleFullscreen = () => {
setIsFullscreen(!isFullscreen)
}
const openInNewTab = () => {
window.open(`${docsUrl}/${currentPath}`, '_blank')
}
const refreshDocs = () => {
setIsLoading(true)
setCurrentPath(currentPath + '?refresh=' + Date.now())
setTimeout(() => setCurrentPath(currentPath), 100)
}
if (isFullscreen) {
return (
<div className="fixed inset-0 z-50 bg-white">
<div className="absolute top-0 left-0 right-0 h-12 bg-slate-900 flex items-center justify-between px-4 z-10">
<div className="flex items-center gap-2 text-white">
<BookOpen className="w-5 h-5" />
<span className="font-semibold">BreakPilot Compliance Dokumentation</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={openInNewTab}
className="p-2 text-slate-300 hover:text-white hover:bg-slate-700 rounded transition-colors"
title="In neuem Tab oeffnen"
>
<ExternalLink className="w-4 h-4" />
</button>
<button
onClick={toggleFullscreen}
className="p-2 text-slate-300 hover:text-white hover:bg-slate-700 rounded transition-colors"
title="Vollbild beenden"
>
<Minimize2 className="w-4 h-4" />
</button>
</div>
</div>
<iframe
src={`${docsUrl}/${currentPath}`}
className="w-full h-full pt-12"
title="BreakPilot Compliance Documentation"
onLoad={handleIframeLoad}
/>
</div>
)
}
return (
<div className="space-y-6">
{/* Quick Links */}
<div className="bg-white border border-slate-200 rounded-xl p-4">
<h3 className="text-sm font-semibold text-slate-700 mb-3 flex items-center gap-2">
<Search className="w-4 h-4" />
Schnellzugriff
</h3>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-2">
{quickLinks.map((link) => (
<button
key={link.path}
onClick={() => navigateTo(link.path)}
className="flex items-center gap-2 px-3 py-2 text-sm bg-slate-50 hover:bg-slate-100 border border-slate-200 rounded-lg transition-colors text-left"
>
<span>{link.icon}</span>
<span className="truncate">{link.name}</span>
</button>
))}
</div>
</div>
{/* Toolbar */}
<div className="flex items-center justify-between bg-white border border-slate-200 rounded-xl p-3">
<div className="flex items-center gap-2">
<BookOpen className="w-5 h-5 text-slate-500" />
<span className="text-sm font-medium text-slate-700">
BreakPilot Compliance Dokumentation
</span>
<span className="text-xs text-slate-400">
(MkDocs Material)
</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={refreshDocs}
className="p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors"
title="Aktualisieren"
>
<RefreshCw className={`w-4 h-4 ${isLoading ? 'animate-spin' : ''}`} />
</button>
<button
onClick={openInNewTab}
className="p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors"
title="In neuem Tab oeffnen"
>
<ExternalLink className="w-4 h-4" />
</button>
<button
onClick={toggleFullscreen}
className="p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 rounded-lg transition-colors"
title="Vollbild"
>
<Maximize2 className="w-4 h-4" />
</button>
</div>
</div>
{/* Documentation Iframe */}
<div className="relative bg-white border border-slate-200 rounded-xl overflow-hidden" style={{ height: 'calc(100vh - 350px)', minHeight: '500px' }}>
{isLoading && (
<div className="absolute inset-0 bg-white flex items-center justify-center z-10">
<div className="flex flex-col items-center gap-3">
<div className="w-8 h-8 border-2 border-slate-300 border-t-slate-600 rounded-full animate-spin" />
<span className="text-sm text-slate-500">Dokumentation wird geladen...</span>
</div>
</div>
)}
<iframe
key={currentPath}
src={`${docsUrl}/${currentPath}`}
className="w-full h-full"
title="BreakPilot Compliance Documentation"
onLoad={handleIframeLoad}
/>
</div>
{/* Info Box */}
<div className="bg-slate-50 border border-slate-200 rounded-xl p-4">
<div className="flex items-start gap-3">
<div className="p-2 bg-slate-200 rounded-lg">
<ArrowRight className="w-4 h-4 text-slate-600" />
</div>
<div>
<h4 className="font-medium text-slate-800">Dokumentation bearbeiten</h4>
<p className="text-sm text-slate-600 mt-1">
Die Dokumentation befindet sich im Repository unter <code className="text-xs bg-slate-200 px-1.5 py-0.5 rounded">docs-src/</code>.
Nach Aenderungen muss der Docs-Container neu gebaut werden:
</p>
<div className="mt-2 text-xs text-slate-500 font-mono bg-slate-100 p-2 rounded">
docker compose --profile docs build docs && docker compose --profile docs up -d docs
</div>
</div>
</div>
</div>
</div>
)
}

View File

@@ -8,6 +8,15 @@ import type {
CatalogFieldSchema,
} from '@/lib/sdk/catalog-manager/types'
// Resolve localized text objects to German string for display
function resolveDisplayValue(value: unknown): unknown {
if (value === null || value === undefined) return value
if (typeof value === 'object' && !Array.isArray(value) && 'de' in (value as Record<string, unknown>)) {
return String((value as Record<string, string>).de || '')
}
return value
}
interface CatalogEntryFormProps {
catalog: CatalogMeta
entry?: CatalogEntry | null
@@ -39,7 +48,7 @@ export default function CatalogEntryForm({
if (isEditMode && entry) {
const initialData: Record<string, unknown> = {}
for (const field of catalog.fields) {
initialData[field.key] = entry.data?.[field.key] ?? getDefaultValue(field)
initialData[field.key] = resolveDisplayValue(entry.data?.[field.key]) ?? getDefaultValue(field)
}
setFormData(initialData)
} else {

View File

@@ -66,7 +66,7 @@ interface CategoryCardProps {
export function CategoryCard({ category, showModuleCount = true }: CategoryCardProps) {
return (
<Link
href={`/${category.id}`}
href={category.id === 'compliance-sdk' ? '/sdk' : `/${category.id}`}
className="block p-6 rounded-xl border-2 transition-all hover:shadow-lg bg-white"
style={{
borderColor: `${category.color}40`,

View File

@@ -5,7 +5,7 @@
* Extracted from admin-v2, keeping only compliance-relevant modules.
*/
export type CategoryId = 'compliance-sdk'
export type CategoryId = 'compliance-sdk' | 'development'
export interface NavModule {
id: string
@@ -15,8 +15,8 @@ export interface NavModule {
purpose: string
audience: string[]
gdprArticles?: string[]
oldAdminPath?: string // Reference to old admin for migration
subgroup?: string // Optional subgroup for visual grouping in sidebar
oldAdminPath?: string
subgroup?: string
}
export interface NavCategory {
@@ -51,6 +51,27 @@ export const navigation: NavCategory[] = [
},
],
},
// =========================================================================
// Development - Entwickler-Tools und Dokumentation
// =========================================================================
{
id: 'development',
name: 'Entwicklung',
icon: 'code',
color: '#0ea5e9', // Sky-500
colorClass: 'development',
description: 'Dokumentation & Entwickler-Tools',
modules: [
{
id: 'docs',
name: 'Dokumentation',
href: '/development/docs',
description: 'MkDocs Projekt-Dokumentation',
purpose: 'Technische Dokumentation der Compliance-Plattform mit Architektur, API-Referenz und Entwickler-Guides.',
audience: ['Entwickler', 'Architekten'],
},
],
},
]
// Meta modules (always visible)

View File

@@ -298,9 +298,78 @@ export const CATALOG_REGISTRY: Record<CatalogId, CatalogMeta> = {
{ key: 'id', label: 'ID', type: 'text', required: true },
{ key: 'name', label: 'Name', type: 'text', required: true },
{ key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
{ key: 'serviceCategory', label: 'Kategorie', type: 'text', required: true },
{ key: 'serviceCategory', label: 'Dienstleistungskategorie', type: 'select', required: true, options: [
{ value: 'HOSTING', label: 'Hosting' },
{ value: 'CLOUD_INFRASTRUCTURE', label: 'Cloud-Infrastruktur' },
{ value: 'ANALYTICS', label: 'Analyse' },
{ value: 'CRM', label: 'CRM' },
{ value: 'ERP', label: 'ERP' },
{ value: 'HR_SOFTWARE', label: 'HR-Software' },
{ value: 'PAYMENT', label: 'Zahlungsdienstleister' },
{ value: 'EMAIL', label: 'E-Mail' },
{ value: 'MARKETING', label: 'Marketing' },
{ value: 'SUPPORT', label: 'Support' },
{ value: 'SECURITY', label: 'Sicherheit' },
{ value: 'COMMUNICATION', label: 'Kommunikation' },
{ value: 'STORAGE', label: 'Speicher' },
{ value: 'BACKUP', label: 'Backup' },
{ value: 'CDN', label: 'CDN' },
{ value: 'ACCOUNTING', label: 'Buchhaltung' },
{ value: 'CONSULTING', label: 'Beratung' },
{ value: 'OTHER', label: 'Sonstige' },
]},
{ key: 'suggestedRole', label: 'Vorgeschlagene Rolle', type: 'select', required: false, options: [
{ value: 'PROCESSOR', label: 'Auftragsverarbeiter' },
{ value: 'JOINT_CONTROLLER', label: 'Gemeinsam Verantwortlicher' },
{ value: 'CONTROLLER', label: 'Eigenstaendiger Verantwortlicher' },
{ value: 'SUB_PROCESSOR', label: 'Unterauftragnehmer' },
{ value: 'THIRD_PARTY', label: 'Dritter' },
]},
{ key: 'suggestedDataAccess', label: 'Datenzugriff', type: 'select', required: false, options: [
{ value: 'NONE', label: 'Kein Zugriff' },
{ value: 'POTENTIAL', label: 'Potenzieller Zugriff' },
{ value: 'ADMINISTRATIVE', label: 'Administrativer Zugriff' },
{ value: 'CONTENT', label: 'Inhaltlicher Zugriff' },
]},
{ key: 'suggestedTransferMechanisms', label: 'Transfer-Mechanismen', type: 'multiselect', required: false, options: [
{ value: 'ADEQUACY_DECISION', label: 'Angemessenheitsbeschluss' },
{ value: 'SCC_CONTROLLER', label: 'SCC Controller-to-Controller' },
{ value: 'SCC_PROCESSOR', label: 'SCC Controller-to-Processor' },
{ value: 'BCR', label: 'Binding Corporate Rules' },
{ value: 'DEROGATION_CONSENT', label: 'Einwilligung' },
{ value: 'DEROGATION_CONTRACT', label: 'Vertragserfuellung' },
{ value: 'CERTIFICATION', label: 'Zertifizierung' },
{ value: 'CODE_OF_CONDUCT', label: 'Verhaltensregeln' },
]},
{ key: 'suggestedContractTypes', label: 'Vertragstypen', type: 'multiselect', required: false, options: [
{ value: 'AVV', label: 'AVV (Auftragsverarbeitung)' },
{ value: 'MSA', label: 'MSA (Master Service)' },
{ value: 'SLA', label: 'SLA (Service Level)' },
{ value: 'SCC', label: 'SCC (Standardvertragsklauseln)' },
{ value: 'NDA', label: 'NDA (Geheimhaltung)' },
{ value: 'TOM_ANNEX', label: 'TOM-Anlage' },
{ value: 'CERTIFICATION', label: 'Zertifikat' },
{ value: 'SUB_PROCESSOR_LIST', label: 'Unterauftragnehmer-Liste' },
]},
{ key: 'typicalDataCategories', label: 'Typische Datenkategorien', type: 'multiselect', required: false, options: [
{ value: 'NAME', label: 'Name' },
{ value: 'CONTACT', label: 'Kontaktdaten' },
{ value: 'ADDRESS', label: 'Adressdaten' },
{ value: 'DOB', label: 'Geburtsdatum' },
{ value: 'BANK_ACCOUNT', label: 'Bankverbindung' },
{ value: 'PAYMENT_DATA', label: 'Zahlungsdaten' },
{ value: 'EMPLOYMENT_DATA', label: 'Beschaeftigungsdaten' },
{ value: 'SALARY_DATA', label: 'Gehaltsdaten' },
{ value: 'HEALTH_DATA', label: 'Gesundheitsdaten' },
{ value: 'IP_ADDRESS', label: 'IP-Adresse' },
{ value: 'USAGE_DATA', label: 'Nutzungsdaten' },
{ value: 'LOCATION_DATA', label: 'Standortdaten' },
{ value: 'COMMUNICATION_DATA', label: 'Kommunikationsdaten' },
]},
{ key: 'typicalCertifications', label: 'Typische Zertifizierungen', type: 'tags', required: false, placeholder: 'z.B. ISO 27001, SOC 2, C5' },
{ key: 'commonProviders', label: 'Typische Anbieter', type: 'tags', required: false, placeholder: 'z.B. AWS, Azure, GCP' },
],
searchableFields: ['id', 'name', 'description', 'serviceCategory'],
searchableFields: ['id', 'name', 'description', 'serviceCategory', 'suggestedRole', 'commonProviders'],
},
'country-risk-profiles': {