{visibleCategories.map((category) => {
- const categoryHref = `/${category.id === 'compliance' ? 'compliance' : category.id}`
- const isCategoryActive = pathname.startsWith(categoryHref)
+ const categoryHref = category.id === 'compliance-sdk' ? '/dashboard/catalog-manager' : `/${category.id === 'compliance' ? 'compliance' : category.id}`
+ const isCategoryActive = category.id === 'compliance-sdk'
+ ? category.modules.some(m => pathname.startsWith(m.href))
+ : pathname.startsWith(categoryHref)
return (
diff --git a/admin-v2/lib/navigation.ts b/admin-v2/lib/navigation.ts
index de74b3f..70146ae 100644
--- a/admin-v2/lib/navigation.ts
+++ b/admin-v2/lib/navigation.ts
@@ -5,7 +5,7 @@
* DSGVO (Datenschutz) and Compliance (Audit & GRC) are now separate
*/
-export type CategoryId = 'dsgvo' | 'compliance' | 'ai' | 'infrastructure' | 'education' | 'communication' | 'development' | 'sdk-docs'
+export type CategoryId = 'dsgvo' | 'compliance' | 'compliance-sdk' | 'ai' | 'infrastructure' | 'education' | 'communication' | 'development' | 'sdk-docs'
export interface NavModule {
id: string
@@ -260,6 +260,27 @@ export const navigation: NavCategory[] = [
],
},
// =========================================================================
+ // Compliance SDK - Datenschutz-Werkzeuge & Kataloge
+ // =========================================================================
+ {
+ id: 'compliance-sdk',
+ name: 'Compliance SDK',
+ icon: 'database',
+ color: '#8b5cf6', // Violet-500
+ colorClass: 'compliance-sdk',
+ description: 'SDK-Kataloge, Risiken & Massnahmen',
+ modules: [
+ {
+ id: 'catalog-manager',
+ name: 'Katalogverwaltung',
+ href: '/dashboard/catalog-manager',
+ description: 'SDK-Kataloge & Auswahltabellen',
+ purpose: 'Zentrale Verwaltung aller Dropdown- und Auswahltabellen im SDK. Systemkataloge (Risiken, Massnahmen, Vorlagen) anzeigen und benutzerdefinierte Eintraege ergaenzen, bearbeiten und loeschen.',
+ audience: ['DSB', 'Compliance Officer', 'Administratoren'],
+ },
+ ],
+ },
+ // =========================================================================
// KI & Automatisierung
// =========================================================================
{
@@ -486,6 +507,15 @@ export const navigation: NavCategory[] = [
audience: ['Lehrer', 'Entwickler'],
oldAdminPath: '/admin/klausur-korrektur',
},
+ {
+ id: 'companion',
+ name: 'Companion',
+ href: '/education/companion',
+ description: 'Unterrichts-Timer & Phasen',
+ purpose: 'Strukturierter Unterricht mit 5-Phasen-Modell (E-A-S-T-R). Visual Timer, Hausaufgaben-Tracking und Reflexion.',
+ audience: ['Lehrer'],
+ oldAdminPath: '/admin/companion',
+ },
],
},
// =========================================================================
diff --git a/admin-v2/lib/sdk/catalog-manager/catalog-registry.ts b/admin-v2/lib/sdk/catalog-manager/catalog-registry.ts
new file mode 100644
index 0000000..70a1702
--- /dev/null
+++ b/admin-v2/lib/sdk/catalog-manager/catalog-registry.ts
@@ -0,0 +1,665 @@
+/**
+ * SDK Catalog Manager - Central Registry
+ *
+ * Maps all SDK catalogs to a unified interface for browsing, searching, and CRUD.
+ */
+
+import type {
+ CatalogId,
+ CatalogMeta,
+ CatalogModule,
+ CatalogEntry,
+ CatalogStats,
+ CatalogOverviewStats,
+ CustomCatalogEntry,
+ CustomCatalogs,
+} from './types'
+
+// =============================================================================
+// CATALOG DATA IMPORTS
+// =============================================================================
+
+import { RISK_CATALOG } from '../dsfa/risk-catalog'
+import { MITIGATION_LIBRARY } from '../dsfa/mitigation-library'
+import { AI_RISK_CATALOG } from '../dsfa/ai-risk-catalog'
+import { AI_MITIGATION_LIBRARY } from '../dsfa/ai-mitigation-library'
+import { PROHIBITED_AI_PRACTICES } from '../dsfa/prohibited-ai-practices'
+import { EU_BASE_FRAMEWORKS, NATIONAL_FRAMEWORKS } from '../dsfa/eu-legal-frameworks'
+import { GDPR_ENFORCEMENT_CASES } from '../dsfa/gdpr-enforcement-cases'
+import { WP248_CRITERIA, SDM_GOALS, DSFA_AUTHORITY_RESOURCES } from '../dsfa/types'
+import { VVT_BASELINE_CATALOG } from '../vvt-baseline-catalog'
+import { BASELINE_TEMPLATES } from '../loeschfristen-baseline-catalog'
+import { VENDOR_TEMPLATES, COUNTRY_RISK_PROFILES } from '../vendor-compliance/catalog/vendor-templates'
+import { LEGAL_BASIS_INFO, STANDARD_RETENTION_PERIODS } from '../vendor-compliance/catalog/legal-basis'
+
+// =============================================================================
+// HELPER: Resolve localized text fields
+// =============================================================================
+
+function resolveField(value: unknown): string {
+ if (value === null || value === undefined) return ''
+ if (typeof value === 'string') return value
+ if (typeof value === 'number') return String(value)
+ if (typeof value === 'object' && 'de' in (value as Record)) {
+ return String((value as Record).de || '')
+ }
+ return String(value)
+}
+
+// =============================================================================
+// SDM_GOALS as entries array (it's a Record, not an array)
+// =============================================================================
+
+const SDM_GOALS_ENTRIES = Object.entries(SDM_GOALS).map(([key, val]) => ({
+ id: key,
+ name: val.name,
+ description: val.description,
+ article: val.article,
+}))
+
+// =============================================================================
+// CATALOG REGISTRY
+// =============================================================================
+
+export const CATALOG_REGISTRY: Record = {
+ 'dsfa-risks': {
+ id: 'dsfa-risks',
+ name: 'DSFA Risikokatalog',
+ description: 'Standardrisiken fuer Datenschutz-Folgenabschaetzungen',
+ module: 'dsfa',
+ icon: 'ShieldAlert',
+ systemCount: RISK_CATALOG.length,
+ allowCustom: true,
+ idField: 'id',
+ nameField: 'title',
+ descriptionField: 'description',
+ categoryField: 'category',
+ fields: [
+ { key: 'id', label: 'Risiko-ID', type: 'text', required: true, placeholder: 'R-XXX-01' },
+ { key: 'title', label: 'Titel', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'category', label: 'Kategorie', type: 'select', required: true, options: [
+ { value: 'confidentiality', label: 'Vertraulichkeit' },
+ { value: 'integrity', label: 'Integritaet' },
+ { value: 'availability', label: 'Verfuegbarkeit' },
+ { value: 'rights_freedoms', label: 'Rechte & Freiheiten' },
+ ]},
+ { key: 'typicalLikelihood', label: 'Typische Eintrittswahrscheinlichkeit', type: 'select', required: false, options: [
+ { value: 'low', label: 'Niedrig' },
+ { value: 'medium', label: 'Mittel' },
+ { value: 'high', label: 'Hoch' },
+ ]},
+ { key: 'typicalImpact', label: 'Typische Auswirkung', type: 'select', required: false, options: [
+ { value: 'low', label: 'Niedrig' },
+ { value: 'medium', label: 'Mittel' },
+ { value: 'high', label: 'Hoch' },
+ ]},
+ ],
+ searchableFields: ['id', 'title', 'description', 'category'],
+ },
+
+ 'dsfa-mitigations': {
+ id: 'dsfa-mitigations',
+ name: 'DSFA Massnahmenbibliothek',
+ description: 'Technische und organisatorische Massnahmen fuer DSFAs',
+ module: 'dsfa',
+ icon: 'Shield',
+ systemCount: MITIGATION_LIBRARY.length,
+ allowCustom: true,
+ idField: 'id',
+ nameField: 'title',
+ descriptionField: 'description',
+ categoryField: 'type',
+ fields: [
+ { key: 'id', label: 'Massnahmen-ID', type: 'text', required: true, placeholder: 'M-XXX-01' },
+ { key: 'title', label: 'Titel', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'type', label: 'Typ', type: 'select', required: true, options: [
+ { value: 'technical', label: 'Technisch' },
+ { value: 'organizational', label: 'Organisatorisch' },
+ { value: 'legal', label: 'Rechtlich' },
+ ]},
+ { key: 'effectiveness', label: 'Wirksamkeit', type: 'select', required: false, options: [
+ { value: 'low', label: 'Niedrig' },
+ { value: 'medium', label: 'Mittel' },
+ { value: 'high', label: 'Hoch' },
+ ]},
+ { key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: false },
+ ],
+ searchableFields: ['id', 'title', 'description', 'type', 'legalBasis'],
+ },
+
+ 'ai-risks': {
+ id: 'ai-risks',
+ name: 'KI-Risikokatalog',
+ description: 'Spezifische Risiken fuer KI-Systeme',
+ module: 'ai_act',
+ icon: 'Bot',
+ systemCount: AI_RISK_CATALOG.length,
+ allowCustom: true,
+ idField: 'id',
+ nameField: 'title',
+ descriptionField: 'description',
+ categoryField: 'category',
+ fields: [
+ { key: 'id', label: 'Risiko-ID', type: 'text', required: true, placeholder: 'R-AI-XXX-01' },
+ { key: 'title', label: 'Titel', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'category', label: 'Kategorie', type: 'select', required: true, options: [
+ { value: 'confidentiality', label: 'Vertraulichkeit' },
+ { value: 'integrity', label: 'Integritaet' },
+ { value: 'availability', label: 'Verfuegbarkeit' },
+ { value: 'rights_freedoms', label: 'Rechte & Freiheiten' },
+ ]},
+ { key: 'typicalLikelihood', label: 'Eintrittswahrscheinlichkeit', type: 'select', required: false, options: [
+ { value: 'low', label: 'Niedrig' },
+ { value: 'medium', label: 'Mittel' },
+ { value: 'high', label: 'Hoch' },
+ ]},
+ { key: 'typicalImpact', label: 'Auswirkung', type: 'select', required: false, options: [
+ { value: 'low', label: 'Niedrig' },
+ { value: 'medium', label: 'Mittel' },
+ { value: 'high', label: 'Hoch' },
+ ]},
+ ],
+ searchableFields: ['id', 'title', 'description', 'category'],
+ },
+
+ 'ai-mitigations': {
+ id: 'ai-mitigations',
+ name: 'KI-Massnahmenbibliothek',
+ description: 'Massnahmen fuer KI-spezifische Risiken',
+ module: 'ai_act',
+ icon: 'ShieldCheck',
+ systemCount: AI_MITIGATION_LIBRARY.length,
+ allowCustom: true,
+ idField: 'id',
+ nameField: 'title',
+ descriptionField: 'description',
+ categoryField: 'type',
+ fields: [
+ { key: 'id', label: 'Massnahmen-ID', type: 'text', required: true },
+ { key: 'title', label: 'Titel', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'type', label: 'Typ', type: 'select', required: true, options: [
+ { value: 'technical', label: 'Technisch' },
+ { value: 'organizational', label: 'Organisatorisch' },
+ { value: 'legal', label: 'Rechtlich' },
+ ]},
+ { key: 'effectiveness', label: 'Wirksamkeit', type: 'select', required: false, options: [
+ { value: 'low', label: 'Niedrig' },
+ { value: 'medium', label: 'Mittel' },
+ { value: 'high', label: 'Hoch' },
+ ]},
+ ],
+ searchableFields: ['id', 'title', 'description', 'type'],
+ },
+
+ 'prohibited-ai-practices': {
+ id: 'prohibited-ai-practices',
+ name: 'Verbotene KI-Praktiken',
+ description: 'Absolut und bedingt verbotene KI-Anwendungen nach AI Act',
+ module: 'ai_act',
+ icon: 'Ban',
+ systemCount: PROHIBITED_AI_PRACTICES.length,
+ allowCustom: false,
+ idField: 'id',
+ nameField: 'title',
+ descriptionField: 'description',
+ categoryField: 'severity',
+ fields: [
+ { key: 'id', label: 'ID', type: 'text', required: true },
+ { key: 'title', label: 'Titel', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'severity', label: 'Schwere', type: 'select', required: true, options: [
+ { value: 'absolute', label: 'Absolutes Verbot' },
+ { value: 'conditional', label: 'Bedingtes Verbot' },
+ ]},
+ { key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: false },
+ ],
+ searchableFields: ['id', 'title', 'description', 'severity', 'legalBasis'],
+ },
+
+ 'vvt-templates': {
+ id: 'vvt-templates',
+ name: 'VVT Baseline-Vorlagen',
+ description: 'Vorlagen fuer Verarbeitungstaetigkeiten',
+ module: 'vvt',
+ icon: 'FileText',
+ systemCount: VVT_BASELINE_CATALOG.length,
+ allowCustom: true,
+ idField: 'templateId',
+ nameField: 'name',
+ descriptionField: 'description',
+ categoryField: 'businessFunction',
+ fields: [
+ { key: 'templateId', label: 'Vorlagen-ID', type: 'text', required: true },
+ { key: 'name', label: 'Name', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'businessFunction', label: 'Geschaeftsbereich', type: 'select', required: true, options: [
+ { value: 'hr', label: 'Personal' },
+ { value: 'finance', label: 'Finanzen' },
+ { value: 'sales', label: 'Vertrieb' },
+ { value: 'marketing', label: 'Marketing' },
+ { value: 'support', label: 'Support' },
+ { value: 'it', label: 'IT' },
+ { value: 'other', label: 'Sonstige' },
+ ]},
+ { key: 'protectionLevel', label: 'Schutzniveau', type: 'select', required: false, options: [
+ { value: 'LOW', label: 'Niedrig' },
+ { value: 'MEDIUM', label: 'Mittel' },
+ { value: 'HIGH', label: 'Hoch' },
+ ]},
+ ],
+ searchableFields: ['templateId', 'name', 'description', 'businessFunction'],
+ },
+
+ 'loeschfristen-templates': {
+ id: 'loeschfristen-templates',
+ name: 'Loeschfristen-Vorlagen',
+ description: 'Baseline-Vorlagen fuer Aufbewahrungsfristen',
+ module: 'vvt',
+ icon: 'Clock',
+ systemCount: BASELINE_TEMPLATES.length,
+ allowCustom: true,
+ idField: 'templateId',
+ nameField: 'dataObjectName',
+ descriptionField: 'description',
+ categoryField: 'retentionDriver',
+ fields: [
+ { key: 'templateId', label: 'Vorlagen-ID', type: 'text', required: true },
+ { key: 'dataObjectName', label: 'Datenobjekt', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'retentionDuration', label: 'Aufbewahrungsdauer', type: 'number', required: true, min: 0 },
+ { key: 'retentionUnit', label: 'Einheit', type: 'select', required: true, options: [
+ { value: 'days', label: 'Tage' },
+ { value: 'months', label: 'Monate' },
+ { value: 'years', label: 'Jahre' },
+ ]},
+ { key: 'deletionMethod', label: 'Loeschmethode', type: 'text', required: false },
+ { key: 'responsibleRole', label: 'Verantwortlich', type: 'text', required: false },
+ ],
+ searchableFields: ['templateId', 'dataObjectName', 'description', 'retentionDriver'],
+ },
+
+ 'vendor-templates': {
+ id: 'vendor-templates',
+ name: 'AV-Vorlagen',
+ description: 'Vorlagen fuer Auftragsverarbeitungsvertraege',
+ module: 'vendor',
+ icon: 'Building2',
+ systemCount: VENDOR_TEMPLATES.length,
+ allowCustom: true,
+ idField: 'id',
+ nameField: 'name',
+ descriptionField: 'description',
+ categoryField: 'serviceCategory',
+ fields: [
+ { 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 },
+ ],
+ searchableFields: ['id', 'name', 'description', 'serviceCategory'],
+ },
+
+ 'country-risk-profiles': {
+ id: 'country-risk-profiles',
+ name: 'Laenderrisikoprofile',
+ description: 'Datenschutz-Risikobewertung nach Laendern',
+ module: 'vendor',
+ icon: 'Globe',
+ systemCount: COUNTRY_RISK_PROFILES.length,
+ allowCustom: false,
+ idField: 'code',
+ nameField: 'name',
+ categoryField: 'riskLevel',
+ fields: [
+ { key: 'code', label: 'Laendercode', type: 'text', required: true },
+ { key: 'name', label: 'Land', type: 'text', required: true },
+ { key: 'riskLevel', label: 'Risikostufe', type: 'select', required: true, options: [
+ { value: 'LOW', label: 'Niedrig' },
+ { value: 'MEDIUM', label: 'Mittel' },
+ { value: 'HIGH', label: 'Hoch' },
+ { value: 'VERY_HIGH', label: 'Sehr hoch' },
+ ]},
+ { key: 'isEU', label: 'EU-Mitglied', type: 'boolean', required: false },
+ { key: 'isEEA', label: 'EWR-Mitglied', type: 'boolean', required: false },
+ { key: 'hasAdequacyDecision', label: 'Angemessenheitsbeschluss', type: 'boolean', required: false },
+ ],
+ searchableFields: ['code', 'name', 'riskLevel'],
+ },
+
+ 'legal-bases': {
+ id: 'legal-bases',
+ name: 'Rechtsgrundlagen',
+ description: 'DSGVO Art. 6 und Art. 9 Rechtsgrundlagen',
+ module: 'reference',
+ icon: 'Scale',
+ systemCount: LEGAL_BASIS_INFO.length,
+ allowCustom: false,
+ idField: 'type',
+ nameField: 'name',
+ descriptionField: 'description',
+ categoryField: 'article',
+ fields: [
+ { key: 'type', label: 'Typ', type: 'text', required: true },
+ { key: 'article', label: 'Artikel', type: 'text', required: true },
+ { key: 'name', label: 'Name', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'isSpecialCategory', label: 'Besondere Kategorie (Art. 9)', type: 'boolean', required: false },
+ ],
+ searchableFields: ['type', 'article', 'name', 'description'],
+ },
+
+ 'retention-periods': {
+ id: 'retention-periods',
+ name: 'Aufbewahrungsfristen',
+ description: 'Gesetzliche Standard-Aufbewahrungsfristen',
+ module: 'reference',
+ icon: 'Timer',
+ systemCount: STANDARD_RETENTION_PERIODS.length,
+ allowCustom: false,
+ idField: 'id',
+ nameField: 'name',
+ descriptionField: 'description',
+ fields: [
+ { key: 'id', label: 'ID', type: 'text', required: true },
+ { key: 'name', label: 'Name', type: 'text', required: true },
+ { key: 'legalBasis', label: 'Rechtsgrundlage', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ ],
+ searchableFields: ['id', 'name', 'legalBasis', 'description'],
+ },
+
+ 'eu-legal-frameworks': {
+ id: 'eu-legal-frameworks',
+ name: 'EU-Rechtsrahmen',
+ description: 'EU-weite Datenschutzgesetze und -verordnungen',
+ module: 'reference',
+ icon: 'Landmark',
+ systemCount: EU_BASE_FRAMEWORKS.length,
+ allowCustom: false,
+ idField: 'id',
+ nameField: 'name',
+ descriptionField: 'description',
+ categoryField: 'type',
+ fields: [
+ { key: 'id', label: 'ID', type: 'text', required: true },
+ { key: 'name', label: 'Name', type: 'text', required: true },
+ { key: 'fullName', label: 'Vollstaendiger Name', type: 'text', required: true },
+ { key: 'type', label: 'Typ', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ ],
+ searchableFields: ['id', 'name', 'fullName', 'description', 'type'],
+ },
+
+ 'national-legal-frameworks': {
+ id: 'national-legal-frameworks',
+ name: 'Nationale Rechtsrahmen',
+ description: 'Nationale Datenschutzgesetze der EU/EWR-Staaten',
+ module: 'reference',
+ icon: 'Flag',
+ systemCount: NATIONAL_FRAMEWORKS.length,
+ allowCustom: false,
+ idField: 'id',
+ nameField: 'name',
+ descriptionField: 'description',
+ categoryField: 'countryCode',
+ fields: [
+ { key: 'id', label: 'ID', type: 'text', required: true },
+ { key: 'name', label: 'Name', type: 'text', required: true },
+ { key: 'countryCode', label: 'Land', type: 'text', required: true },
+ { key: 'type', label: 'Typ', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ ],
+ searchableFields: ['id', 'name', 'countryCode', 'description', 'type'],
+ },
+
+ 'gdpr-enforcement-cases': {
+ id: 'gdpr-enforcement-cases',
+ name: 'DSGVO-Bussgeldentscheidungen',
+ description: 'Relevante Bussgeldentscheidungen als Referenz',
+ module: 'reference',
+ icon: 'Gavel',
+ systemCount: GDPR_ENFORCEMENT_CASES.length,
+ allowCustom: false,
+ idField: 'id',
+ nameField: 'company',
+ descriptionField: 'description',
+ categoryField: 'country',
+ fields: [
+ { key: 'id', label: 'ID', type: 'text', required: true },
+ { key: 'company', label: 'Unternehmen', type: 'text', required: true },
+ { key: 'country', label: 'Land', type: 'text', required: true },
+ { key: 'year', label: 'Jahr', type: 'number', required: true },
+ { key: 'fineOriginal', label: 'Bussgeld (EUR)', type: 'number', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ ],
+ searchableFields: ['id', 'company', 'country', 'description'],
+ },
+
+ 'wp248-criteria': {
+ id: 'wp248-criteria',
+ name: 'WP248 Kriterien',
+ description: 'Kriterien zur DSFA-Pflichtpruefung nach WP248',
+ module: 'dsfa',
+ icon: 'ClipboardCheck',
+ systemCount: WP248_CRITERIA.length,
+ allowCustom: false,
+ idField: 'code',
+ nameField: 'title',
+ descriptionField: 'description',
+ fields: [
+ { key: 'code', label: 'Code', type: 'text', required: true },
+ { key: 'title', label: 'Titel', type: 'text', required: true },
+ { key: 'description', label: 'Beschreibung', type: 'textarea', required: true },
+ { key: 'gdprRef', label: 'DSGVO-Referenz', type: 'text', required: false },
+ ],
+ searchableFields: ['code', 'title', 'description', 'gdprRef'],
+ },
+
+ 'sdm-goals': {
+ id: 'sdm-goals',
+ name: 'SDM Gewaehrleistungsziele',
+ description: 'Standard-Datenschutzmodell Gewaehrleistungsziele',
+ module: 'dsfa',
+ icon: 'Target',
+ systemCount: SDM_GOALS_ENTRIES.length,
+ allowCustom: false,
+ idField: 'id',
+ nameField: 'name',
+ descriptionField: 'description',
+ fields: [
+ { 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: 'article', label: 'DSGVO-Artikel', type: 'text', required: false },
+ ],
+ searchableFields: ['id', 'name', 'description', 'article'],
+ },
+
+ 'dsfa-authority-resources': {
+ id: 'dsfa-authority-resources',
+ name: 'Aufsichtsbehoerden-Ressourcen',
+ description: 'DSFA-Ressourcen der deutschen Aufsichtsbehoerden',
+ module: 'dsfa',
+ icon: 'Building',
+ systemCount: DSFA_AUTHORITY_RESOURCES.length,
+ allowCustom: false,
+ idField: 'id',
+ nameField: 'shortName',
+ descriptionField: 'name',
+ categoryField: 'state',
+ fields: [
+ { key: 'id', label: 'ID', type: 'text', required: true },
+ { key: 'shortName', label: 'Kurzname', type: 'text', required: true },
+ { key: 'name', label: 'Voller Name', type: 'text', required: true },
+ { key: 'state', label: 'Bundesland', type: 'text', required: true },
+ ],
+ searchableFields: ['id', 'shortName', 'name', 'state'],
+ },
+}
+
+// =============================================================================
+// SYSTEM ENTRIES MAP
+// =============================================================================
+
+const SYSTEM_ENTRIES_MAP: Record[]> = {
+ 'dsfa-risks': RISK_CATALOG as unknown as Record[],
+ 'dsfa-mitigations': MITIGATION_LIBRARY as unknown as Record[],
+ 'ai-risks': AI_RISK_CATALOG as unknown as Record[],
+ 'ai-mitigations': AI_MITIGATION_LIBRARY as unknown as Record[],
+ 'prohibited-ai-practices': PROHIBITED_AI_PRACTICES as unknown as Record[],
+ 'vvt-templates': VVT_BASELINE_CATALOG as unknown as Record[],
+ 'loeschfristen-templates': BASELINE_TEMPLATES as unknown as Record[],
+ 'vendor-templates': VENDOR_TEMPLATES as unknown as Record[],
+ 'country-risk-profiles': COUNTRY_RISK_PROFILES as unknown as Record[],
+ 'legal-bases': LEGAL_BASIS_INFO as unknown as Record[],
+ 'retention-periods': STANDARD_RETENTION_PERIODS as unknown as Record[],
+ 'eu-legal-frameworks': EU_BASE_FRAMEWORKS as unknown as Record[],
+ 'national-legal-frameworks': NATIONAL_FRAMEWORKS as unknown as Record[],
+ 'gdpr-enforcement-cases': GDPR_ENFORCEMENT_CASES as unknown as Record[],
+ 'wp248-criteria': WP248_CRITERIA as unknown as Record[],
+ 'sdm-goals': SDM_GOALS_ENTRIES as unknown as Record[],
+ 'dsfa-authority-resources': DSFA_AUTHORITY_RESOURCES as unknown as Record[],
+}
+
+// =============================================================================
+// ENTRY CONVERTERS
+// =============================================================================
+
+function systemEntryToCatalogEntry(
+ catalogId: CatalogId,
+ data: Record,
+): CatalogEntry {
+ const meta = CATALOG_REGISTRY[catalogId]
+ const idValue = resolveField(data[meta.idField])
+ const nameValue = resolveField(data[meta.nameField])
+ const descValue = meta.descriptionField ? resolveField(data[meta.descriptionField]) : undefined
+ const catValue = meta.categoryField ? resolveField(data[meta.categoryField]) : undefined
+
+ return {
+ id: idValue || crypto.randomUUID(),
+ catalogId,
+ source: 'system',
+ data,
+ displayName: nameValue || idValue || '(Unbenannt)',
+ displayDescription: descValue,
+ category: catValue,
+ }
+}
+
+function customEntryToCatalogEntry(
+ catalogId: CatalogId,
+ entry: CustomCatalogEntry,
+): CatalogEntry {
+ const meta = CATALOG_REGISTRY[catalogId]
+ const nameValue = resolveField(entry.data[meta.nameField])
+ const descValue = meta.descriptionField ? resolveField(entry.data[meta.descriptionField]) : undefined
+ const catValue = meta.categoryField ? resolveField(entry.data[meta.categoryField]) : undefined
+
+ return {
+ id: entry.id,
+ catalogId,
+ source: 'custom',
+ data: entry.data,
+ displayName: nameValue || '(Unbenannt)',
+ displayDescription: descValue,
+ category: catValue,
+ }
+}
+
+// =============================================================================
+// PUBLIC API
+// =============================================================================
+
+export function getSystemEntries(catalogId: CatalogId): CatalogEntry[] {
+ const raw = SYSTEM_ENTRIES_MAP[catalogId] || []
+ return raw.map(data => systemEntryToCatalogEntry(catalogId, data))
+}
+
+export function getAllEntries(
+ catalogId: CatalogId,
+ customEntries: CustomCatalogEntry[] = [],
+): CatalogEntry[] {
+ const system = getSystemEntries(catalogId)
+ const custom = customEntries.map(e => customEntryToCatalogEntry(catalogId, e))
+ return [...system, ...custom]
+}
+
+export function getCatalogsByModule(module: CatalogModule): CatalogMeta[] {
+ return Object.values(CATALOG_REGISTRY).filter(c => c.module === module)
+}
+
+export function getCatalogStats(
+ catalogId: CatalogId,
+ customEntries: CustomCatalogEntry[] = [],
+): CatalogStats {
+ const meta = CATALOG_REGISTRY[catalogId]
+ return {
+ catalogId,
+ systemCount: meta.systemCount,
+ customCount: customEntries.length,
+ totalCount: meta.systemCount + customEntries.length,
+ }
+}
+
+export function getOverviewStats(customCatalogs: CustomCatalogs): CatalogOverviewStats {
+ const modules: CatalogModule[] = ['dsfa', 'vvt', 'vendor', 'ai_act', 'reference']
+ const byModule = {} as Record
+
+ let totalSystemEntries = 0
+ let totalCustomEntries = 0
+
+ for (const mod of modules) {
+ const cats = getCatalogsByModule(mod)
+ let entries = 0
+ for (const cat of cats) {
+ const customCount = customCatalogs[cat.id]?.length ?? 0
+ entries += cat.systemCount + customCount
+ totalSystemEntries += cat.systemCount
+ totalCustomEntries += customCount
+ }
+ byModule[mod] = { catalogs: cats.length, entries }
+ }
+
+ return {
+ totalCatalogs: Object.keys(CATALOG_REGISTRY).length,
+ totalSystemEntries,
+ totalCustomEntries,
+ totalEntries: totalSystemEntries + totalCustomEntries,
+ byModule,
+ }
+}
+
+export function searchCatalog(
+ catalogId: CatalogId,
+ query: string,
+ customEntries: CustomCatalogEntry[] = [],
+): CatalogEntry[] {
+ const allEntries = getAllEntries(catalogId, customEntries)
+ const meta = CATALOG_REGISTRY[catalogId]
+ const q = query.toLowerCase().trim()
+
+ if (!q) return allEntries
+
+ return allEntries
+ .map(entry => {
+ let score = 0
+ for (const field of meta.searchableFields) {
+ const value = resolveField(entry.data[field]).toLowerCase()
+ if (value.includes(q)) {
+ score += value.startsWith(q) ? 10 : 5
+ }
+ }
+ // Also search display name
+ if (entry.displayName.toLowerCase().includes(q)) {
+ score += 15
+ }
+ return { entry, score }
+ })
+ .filter(r => r.score > 0)
+ .sort((a, b) => b.score - a.score)
+ .map(r => r.entry)
+}
diff --git a/admin-v2/lib/sdk/catalog-manager/types.ts b/admin-v2/lib/sdk/catalog-manager/types.ts
new file mode 100644
index 0000000..540f2d0
--- /dev/null
+++ b/admin-v2/lib/sdk/catalog-manager/types.ts
@@ -0,0 +1,118 @@
+/**
+ * SDK Catalog Manager - Type Definitions
+ */
+
+// All catalog IDs in the system
+export type CatalogId =
+ | 'dsfa-risks'
+ | 'dsfa-mitigations'
+ | 'ai-risks'
+ | 'ai-mitigations'
+ | 'prohibited-ai-practices'
+ | 'vvt-templates'
+ | 'loeschfristen-templates'
+ | 'vendor-templates'
+ | 'country-risk-profiles'
+ | 'legal-bases'
+ | 'retention-periods'
+ | 'eu-legal-frameworks'
+ | 'national-legal-frameworks'
+ | 'gdpr-enforcement-cases'
+ | 'wp248-criteria'
+ | 'sdm-goals'
+ | 'dsfa-authority-resources'
+
+// Module grouping
+export type CatalogModule = 'dsfa' | 'vvt' | 'vendor' | 'ai_act' | 'reference'
+
+// Field types for dynamic forms
+export type CatalogFieldType = 'text' | 'textarea' | 'number' | 'select' | 'multiselect' | 'boolean' | 'tags'
+
+export interface CatalogFieldSchema {
+ key: string
+ label: string
+ type: CatalogFieldType
+ required: boolean
+ placeholder?: string
+ description?: string
+ options?: { value: string; label: string }[]
+ helpText?: string
+ min?: number
+ max?: number
+ step?: number
+}
+
+export interface CatalogMeta {
+ id: CatalogId
+ name: string
+ description: string
+ module: CatalogModule
+ icon: string // lucide icon name
+ systemCount: number
+ allowCustom: boolean
+ idField: string // which field is the unique ID (e.g. 'id', 'templateId', 'code')
+ nameField: string // which field is the display name (e.g. 'title', 'name', 'dataObjectName')
+ descriptionField?: string // which field holds description
+ categoryField?: string // optional grouping field
+ fields: CatalogFieldSchema[]
+ searchableFields: string[]
+}
+
+// A custom catalog entry added by the user
+export interface CustomCatalogEntry {
+ id: string // Generated UUID
+ catalogId: CatalogId
+ data: Record
+ createdAt: string // ISO date
+ updatedAt: string // ISO date
+ createdBy?: string
+}
+
+// All custom entries, keyed by CatalogId
+export type CustomCatalogs = Partial>
+
+// Combined view entry (system or custom)
+export interface CatalogEntry {
+ id: string
+ catalogId: CatalogId
+ source: 'system' | 'custom'
+ data: Record
+ displayName: string
+ displayDescription?: string
+ category?: string
+}
+
+// Stats for a single catalog
+export interface CatalogStats {
+ catalogId: CatalogId
+ systemCount: number
+ customCount: number
+ totalCount: number
+}
+
+// Stats for all catalogs
+export interface CatalogOverviewStats {
+ totalCatalogs: number
+ totalSystemEntries: number
+ totalCustomEntries: number
+ totalEntries: number
+ byModule: Record
+}
+
+// Module labels
+export const CATALOG_MODULE_LABELS: Record = {
+ dsfa: 'DSFA & Risiken',
+ vvt: 'VVT & Loeschfristen',
+ vendor: 'Auftragsverarbeitung',
+ ai_act: 'AI Act',
+ reference: 'Referenzdaten',
+}
+
+// Module icons (lucide names)
+export const CATALOG_MODULE_ICONS: Record = {
+ dsfa: 'ShieldAlert',
+ vvt: 'FileText',
+ vendor: 'Building2',
+ ai_act: 'Bot',
+ reference: 'BookOpen',
+}