feat(admin-v2): Katalogverwaltung ins Admin-Dashboard integrieren

Katalogverwaltung von /sdk/catalog-manager nach /dashboard/catalog-manager
verschoben, damit sie im Admin-Dashboard mit Sidebar erscheint statt im
SDK-Bereich. Shared Components extrahiert, SDK-Route bleibt funktionsfaehig.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
BreakPilot Dev
2026-02-10 12:12:38 +01:00
parent 8c77df494b
commit dd1771be1e
10 changed files with 1985 additions and 3 deletions

View File

@@ -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',
},
],
},
// =========================================================================

View File

@@ -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<string, unknown>)) {
return String((value as Record<string, string>).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<CatalogId, CatalogMeta> = {
'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<CatalogId, Record<string, unknown>[]> = {
'dsfa-risks': RISK_CATALOG as unknown as Record<string, unknown>[],
'dsfa-mitigations': MITIGATION_LIBRARY as unknown as Record<string, unknown>[],
'ai-risks': AI_RISK_CATALOG as unknown as Record<string, unknown>[],
'ai-mitigations': AI_MITIGATION_LIBRARY as unknown as Record<string, unknown>[],
'prohibited-ai-practices': PROHIBITED_AI_PRACTICES as unknown as Record<string, unknown>[],
'vvt-templates': VVT_BASELINE_CATALOG as unknown as Record<string, unknown>[],
'loeschfristen-templates': BASELINE_TEMPLATES as unknown as Record<string, unknown>[],
'vendor-templates': VENDOR_TEMPLATES as unknown as Record<string, unknown>[],
'country-risk-profiles': COUNTRY_RISK_PROFILES as unknown as Record<string, unknown>[],
'legal-bases': LEGAL_BASIS_INFO as unknown as Record<string, unknown>[],
'retention-periods': STANDARD_RETENTION_PERIODS as unknown as Record<string, unknown>[],
'eu-legal-frameworks': EU_BASE_FRAMEWORKS as unknown as Record<string, unknown>[],
'national-legal-frameworks': NATIONAL_FRAMEWORKS as unknown as Record<string, unknown>[],
'gdpr-enforcement-cases': GDPR_ENFORCEMENT_CASES as unknown as Record<string, unknown>[],
'wp248-criteria': WP248_CRITERIA as unknown as Record<string, unknown>[],
'sdm-goals': SDM_GOALS_ENTRIES as unknown as Record<string, unknown>[],
'dsfa-authority-resources': DSFA_AUTHORITY_RESOURCES as unknown as Record<string, unknown>[],
}
// =============================================================================
// ENTRY CONVERTERS
// =============================================================================
function systemEntryToCatalogEntry(
catalogId: CatalogId,
data: Record<string, unknown>,
): 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<CatalogModule, { catalogs: number; entries: number }>
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)
}

View File

@@ -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<string, unknown>
createdAt: string // ISO date
updatedAt: string // ISO date
createdBy?: string
}
// All custom entries, keyed by CatalogId
export type CustomCatalogs = Partial<Record<CatalogId, CustomCatalogEntry[]>>
// Combined view entry (system or custom)
export interface CatalogEntry {
id: string
catalogId: CatalogId
source: 'system' | 'custom'
data: Record<string, unknown>
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<CatalogModule, { catalogs: number; entries: number }>
}
// Module labels
export const CATALOG_MODULE_LABELS: Record<CatalogModule, string> = {
dsfa: 'DSFA & Risiken',
vvt: 'VVT & Loeschfristen',
vendor: 'Auftragsverarbeitung',
ai_act: 'AI Act',
reference: 'Referenzdaten',
}
// Module icons (lucide names)
export const CATALOG_MODULE_ICONS: Record<CatalogModule, string> = {
dsfa: 'ShieldAlert',
vvt: 'FileText',
vendor: 'Building2',
ai_act: 'Bot',
reference: 'BookOpen',
}