Initial commit: breakpilot-compliance - Compliance SDK Platform

Services: Admin-Compliance, Backend-Compliance,
AI-Compliance-SDK, Consent-SDK, Developer-Portal,
PCA-Platform, DSMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Boenisch
2026-02-11 23:47:28 +01:00
commit 4435e7ea0a
734 changed files with 251369 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
/**
* YAML Catalog Loader - Erweiterte Version mit 128 Datenpunkten
*/
import {
DataPoint,
DataPointCategory,
RetentionMatrixEntry,
CookieBannerCategory,
LocalizedText,
DataPointCatalog,
} from '../types'
function l(de: string, en: string): LocalizedText {
return { de, en }
}
// =============================================================================
// VORDEFINIERTE DATENPUNKTE (128 Stueck in 18 Kategorien)
// =============================================================================
export const PREDEFINED_DATA_POINTS: DataPoint[] = [
// KATEGORIE A: STAMMDATEN (8)
{ id: 'dp-a1-firstname', code: 'A1', category: 'MASTER_DATA', name: l('Vorname', 'First Name'), description: l('Vorname der betroffenen Person', 'First name of the data subject'), purpose: l('Identifikation, Vertragserfuellung', 'Identification, contract fulfillment'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Erforderlich zur Vertragserfuellung', 'Required for contract performance'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung', 'Zugriffskontrolle'], tags: ['identity', 'master-data'] },
{ id: 'dp-a2-lastname', code: 'A2', category: 'MASTER_DATA', name: l('Nachname', 'Last Name'), description: l('Nachname der betroffenen Person', 'Last name of the data subject'), purpose: l('Identifikation, Vertragserfuellung', 'Identification, contract fulfillment'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Erforderlich zur Vertragserfuellung', 'Required for contract performance'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung', 'Zugriffskontrolle'], tags: ['identity', 'master-data'] },
{ id: 'dp-a3-birthdate', code: 'A3', category: 'MASTER_DATA', name: l('Geburtsdatum', 'Date of Birth'), description: l('Geburtsdatum zur Altersverifikation', 'Date of birth for age verification'), purpose: l('Altersverifikation, Identitaetspruefung', 'Age verification, identity check'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Erforderlich zur Altersverifikation', 'Required for age verification'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Fuer Identifikationszwecke', 'For identification purposes'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung'], tags: ['identity', 'master-data'] },
{ id: 'dp-a4-gender', code: 'A4', category: 'MASTER_DATA', name: l('Geschlecht', 'Gender'), description: l('Geschlechtsangabe (optional)', 'Gender (optional)'), purpose: l('Personalisierte Ansprache', 'Personalized communication'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Angabe', 'Voluntary'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['personal', 'master-data'] },
{ id: 'dp-a5-title', code: 'A5', category: 'MASTER_DATA', name: l('Anrede/Titel', 'Salutation/Title'), description: l('Akademischer Titel oder Anrede', 'Academic title or salutation'), purpose: l('Korrekte Ansprache', 'Correct salutation'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Bestandteil der Kommunikation', 'Part of communication'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['personal', 'master-data'] },
{ id: 'dp-a6-profile-picture', code: 'A6', category: 'MASTER_DATA', name: l('Profilbild', 'Profile Picture'), description: l('Vom Nutzer hochgeladenes Profilbild', 'User-uploaded profile picture'), purpose: l('Visuelle Identifikation', 'Visual identification'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwilliger Upload', 'Voluntary upload'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Loeschung bei Kontoschliessung', 'Deletion on account closure'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['CDN'], technicalMeasures: ['Verschluesselung'], tags: ['image', 'master-data'] },
{ id: 'dp-a7-nationality', code: 'A7', category: 'MASTER_DATA', name: l('Staatsangehoerigkeit', 'Nationality'), description: l('Staatsangehoerigkeit der Person', 'Nationality of the person'), purpose: l('Compliance-Pruefung', 'Compliance check'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Compliance (Sanktionslisten)', 'Compliance (sanction lists)'), retentionPeriod: '10_YEARS', retentionJustification: l('Aufbewahrungspflichten', 'Retention obligations'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung'], tags: ['compliance', 'master-data'] },
{ id: 'dp-a8-username', code: 'A8', category: 'MASTER_DATA', name: l('Benutzername', 'Username'), description: l('Selbst gewaehlter Benutzername', 'Self-chosen username'), purpose: l('Identifikation, Login', 'Identification, login'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Kontoverwaltung', 'For account management'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['account', 'master-data'] },
// KATEGORIE B: KONTAKTDATEN (10)
{ id: 'dp-b1-email', code: 'B1', category: 'CONTACT_DATA', name: l('E-Mail-Adresse', 'Email Address'), description: l('Primaere E-Mail-Adresse', 'Primary email address'), purpose: l('Kommunikation, Benachrichtigungen', 'Communication, notifications'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Vertragserfuellung', 'For contract performance'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['E-Mail-Dienstleister'], technicalMeasures: ['TLS-Verschluesselung'], tags: ['contact', 'essential'] },
{ id: 'dp-b2-phone', code: 'B2', category: 'CONTACT_DATA', name: l('Telefonnummer', 'Phone Number'), description: l('Festnetz-Telefonnummer', 'Landline phone number'), purpose: l('Telefonische Kontaktaufnahme', 'Phone contact'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Kundensupport', 'For customer support'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['contact', 'phone'] },
{ id: 'dp-b3-mobile', code: 'B3', category: 'CONTACT_DATA', name: l('Mobilnummer', 'Mobile Number'), description: l('Mobiltelefonnummer', 'Mobile phone number'), purpose: l('SMS-Benachrichtigungen, 2FA', 'SMS notifications, 2FA'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Sicherheit und Kommunikation', 'For security and communication'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['SMS-Provider'], technicalMeasures: ['Verschluesselung'], tags: ['contact', 'mobile', '2fa'] },
{ id: 'dp-b4-address-street', code: 'B4', category: 'CONTACT_DATA', name: l('Strasse/Hausnummer', 'Street/House Number'), description: l('Strassenadresse', 'Street address'), purpose: l('Lieferung, Rechnungsstellung', 'Delivery, billing'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Vertragserfuellung', 'For contract performance'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Versanddienstleister'], technicalMeasures: ['Verschluesselung'], tags: ['contact', 'address'] },
{ id: 'dp-b5-address-city', code: 'B5', category: 'CONTACT_DATA', name: l('PLZ/Ort', 'Postal Code/City'), description: l('Postleitzahl und Stadt', 'Postal code and city'), purpose: l('Lieferung, Rechnungsstellung', 'Delivery, billing'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Vertragserfuellung', 'For contract performance'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Versanddienstleister'], technicalMeasures: ['Verschluesselung'], tags: ['contact', 'address'] },
{ id: 'dp-b6-address-country', code: 'B6', category: 'CONTACT_DATA', name: l('Land', 'Country'), description: l('Wohnsitzland', 'Country of residence'), purpose: l('Lieferung, Steuer', 'Delivery, tax'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Vertragserfuellung', 'For contract performance'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['contact', 'address'] },
{ id: 'dp-b7-secondary-email', code: 'B7', category: 'CONTACT_DATA', name: l('Sekundaere E-Mail', 'Secondary Email'), description: l('Alternative E-Mail-Adresse', 'Alternative email address'), purpose: l('Backup-Kontakt, Wiederherstellung', 'Backup contact, recovery'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Angabe', 'Voluntary'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['TLS'], tags: ['contact', 'backup'] },
{ id: 'dp-b8-fax', code: 'B8', category: 'CONTACT_DATA', name: l('Faxnummer', 'Fax Number'), description: l('Faxnummer (geschaeftlich)', 'Fax number (business)'), purpose: l('Geschaeftliche Kommunikation', 'Business communication'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer geschaeftliche Kommunikation', 'For business communication'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['contact', 'business'] },
{ id: 'dp-b9-emergency-contact', code: 'B9', category: 'CONTACT_DATA', name: l('Notfallkontakt', 'Emergency Contact'), description: l('Kontaktdaten fuer Notfaelle', 'Emergency contact details'), purpose: l('Notfallbenachrichtigung', 'Emergency notification'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Angabe', 'Voluntary'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung'], tags: ['contact', 'emergency'] },
{ id: 'dp-b10-social-profiles', code: 'B10', category: 'CONTACT_DATA', name: l('Social-Media-Profile', 'Social Media Profiles'), description: l('Links zu sozialen Netzwerken', 'Links to social networks'), purpose: l('Vernetzung, Kommunikation', 'Networking, communication'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Angabe', 'Voluntary'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['contact', 'social'] },
// KATEGORIE C: AUTHENTIFIZIERUNGSDATEN (8)
{ id: 'dp-c1-password-hash', code: 'C1', category: 'AUTHENTICATION', name: l('Passwort-Hash', 'Password Hash'), description: l('Kryptografisch gehashtes Passwort', 'Cryptographically hashed password'), purpose: l('Sichere Authentifizierung', 'Secure authentication'), riskLevel: 'HIGH', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer sichere Kontoverwaltung', 'For secure account management'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['bcrypt/Argon2', 'Salting'], tags: ['auth', 'security'] },
{ id: 'dp-c2-session-token', code: 'C2', category: 'AUTHENTICATION', name: l('Session-Token', 'Session Token'), description: l('JWT oder Session-ID', 'JWT or Session ID'), purpose: l('Aufrechterhaltung der Sitzung', 'Maintaining session'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Technisch erforderlich', 'Technically required'), retentionPeriod: '24_HOURS', retentionJustification: l('Kurze Lebensdauer', 'Short lifespan'), cookieCategory: 'ESSENTIAL', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['JWT-Signatur', 'HttpOnly'], tags: ['auth', 'session'] },
{ id: 'dp-c3-refresh-token', code: 'C3', category: 'AUTHENTICATION', name: l('Refresh-Token', 'Refresh Token'), description: l('Token zur Session-Erneuerung', 'Token for session renewal'), purpose: l('Session-Erneuerung', 'Session renewal'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Benutzerfreundlichkeit', 'For user experience'), retentionPeriod: '30_DAYS', retentionJustification: l('Balance Sicherheit/UX', 'Balance security/UX'), cookieCategory: 'ESSENTIAL', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Token-Rotation'], tags: ['auth', 'token'] },
{ id: 'dp-c4-2fa-secret', code: 'C4', category: 'AUTHENTICATION', name: l('2FA-Secret', '2FA Secret'), description: l('TOTP-Geheimnis fuer Zwei-Faktor-Auth', 'TOTP secret for two-factor auth'), purpose: l('Erhoehte Kontosicherheit', 'Enhanced account security'), riskLevel: 'HIGH', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Sicherheit', 'For security'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange 2FA aktiv', 'While 2FA active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung', 'HSM'], tags: ['auth', '2fa', 'security'] },
{ id: 'dp-c5-passkey', code: 'C5', category: 'AUTHENTICATION', name: l('Passkey/WebAuthn', 'Passkey/WebAuthn'), description: l('FIDO2/WebAuthn Credential', 'FIDO2/WebAuthn Credential'), purpose: l('Passwortlose Authentifizierung', 'Passwordless authentication'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer sichere Anmeldung', 'For secure login'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Passkey aktiv', 'While passkey active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Asymmetrische Kryptografie'], tags: ['auth', 'passkey'] },
{ id: 'dp-c6-api-keys', code: 'C6', category: 'AUTHENTICATION', name: l('API-Keys', 'API Keys'), description: l('API-Schluessel fuer Integrationen', 'API keys for integrations'), purpose: l('API-Zugriff', 'API access'), riskLevel: 'HIGH', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer API-Nutzung', 'For API usage'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Hashing', 'Rate-Limiting'], tags: ['auth', 'api'] },
{ id: 'dp-c7-oauth-provider', code: 'C7', category: 'AUTHENTICATION', name: l('OAuth-Provider-ID', 'OAuth Provider ID'), description: l('ID vom externen Auth-Provider', 'ID from external auth provider'), purpose: l('Social Login', 'Social Login'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Social Login', 'For social login'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange verknuepft', 'While linked'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['OAuth-Provider'], technicalMeasures: ['Minimale Daten'], tags: ['auth', 'oauth'] },
{ id: 'dp-c8-recovery-codes', code: 'C8', category: 'AUTHENTICATION', name: l('Wiederherstellungscodes', 'Recovery Codes'), description: l('Backup-Codes fuer 2FA', 'Backup codes for 2FA'), purpose: l('Kontowiederherstellung', 'Account recovery'), riskLevel: 'HIGH', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Notfallzugriff', 'For emergency access'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange 2FA aktiv', 'While 2FA active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Hashing', 'Einmalnutzung'], tags: ['auth', 'recovery'] },
// KATEGORIE D: EINWILLIGUNGSDATEN (6)
{ id: 'dp-d1-consent-records', code: 'D1', category: 'CONSENT', name: l('Consent-Protokolle', 'Consent Records'), description: l('Protokollierte Einwilligungen', 'Recorded consents'), purpose: l('Nachweis gegenueber Behoerden', 'Proof to authorities'), riskLevel: 'LOW', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Nachweispflicht Art. 7 DSGVO', 'Accountability Art. 7 GDPR'), retentionPeriod: '6_YEARS', retentionJustification: l('Audit-Zwecke', 'Audit purposes'), cookieCategory: 'ESSENTIAL', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Unveraenderbare Logs'], tags: ['consent', 'compliance'] },
{ id: 'dp-d2-cookie-preferences', code: 'D2', category: 'CONSENT', name: l('Cookie-Praeferenzen', 'Cookie Preferences'), description: l('Cookie-Einstellungen des Nutzers', 'User cookie settings'), purpose: l('Speicherung der Consent-Entscheidung', 'Storage of consent decision'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Speicherung der Entscheidung', 'Storage of decision'), retentionPeriod: '12_MONTHS', retentionJustification: l('Branchenueblich', 'Industry standard'), cookieCategory: 'ESSENTIAL', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['First-Party Cookie'], tags: ['consent', 'cookie'] },
{ id: 'dp-d3-marketing-consent', code: 'D3', category: 'CONSENT', name: l('Marketing-Einwilligung', 'Marketing Consent'), description: l('Einwilligung fuer Werbung', 'Consent for advertising'), purpose: l('Marketing-Kommunikation', 'Marketing communication'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Einwilligung', 'Voluntary consent'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Double-Opt-In'], tags: ['consent', 'marketing'] },
{ id: 'dp-d4-data-sharing', code: 'D4', category: 'CONSENT', name: l('Datenweitergabe-Einwilligung', 'Data Sharing Consent'), description: l('Einwilligung zur Datenweitergabe', 'Consent to data sharing'), purpose: l('Weitergabe an Partner', 'Sharing with partners'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Einwilligung', 'Voluntary consent'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Dokumentation'], tags: ['consent', 'sharing'] },
{ id: 'dp-d5-locale-preferences', code: 'D5', category: 'CONSENT', name: l('Sprach-/Regionspraeferenz', 'Language/Region Preference'), description: l('Bevorzugte Sprache und Region', 'Preferred language and region'), purpose: l('Lokalisierung', 'Localization'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Servicefunktionalitaet', 'Service functionality'), retentionPeriod: '12_MONTHS', retentionJustification: l('Nutzereinstellungen', 'User settings'), cookieCategory: 'ESSENTIAL', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['First-Party Cookie'], tags: ['preferences', 'locale'] },
{ id: 'dp-d6-newsletter-consent', code: 'D6', category: 'CONSENT', name: l('Newsletter-Einwilligung', 'Newsletter Consent'), description: l('Einwilligung fuer Newsletter', 'Newsletter subscription consent'), purpose: l('E-Mail-Marketing', 'Email marketing'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Double-Opt-In erforderlich', 'Double opt-in required'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['E-Mail-Provider'], technicalMeasures: ['Double-Opt-In', 'Abmelde-Link'], tags: ['consent', 'newsletter'] },
// KATEGORIE E: KOMMUNIKATIONSDATEN (7)
{ id: 'dp-e1-support-tickets', code: 'E1', category: 'COMMUNICATION', name: l('Support-Tickets', 'Support Tickets'), description: l('Inhalt von Kundenanfragen', 'Content of customer inquiries'), purpose: l('Kundenservice', 'Customer service'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Supportleistungen', 'For support services'), retentionPeriod: '24_MONTHS', retentionJustification: l('Nachvollziehbarkeit', 'Traceability'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Helpdesk-Software'], technicalMeasures: ['Verschluesselung'], tags: ['support', 'communication'] },
{ id: 'dp-e2-chat-history', code: 'E2', category: 'COMMUNICATION', name: l('Chat-Verlaeufe', 'Chat Histories'), description: l('Live-Chat und Chatbot-Verlaeufe', 'Live chat and chatbot histories'), purpose: l('Kundenservice, QA', 'Customer service, QA'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Servicequalitaet', 'Service quality'), retentionPeriod: '12_MONTHS', retentionJustification: l('Qualitaetssicherung', 'Quality assurance'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Chat-Software'], technicalMeasures: ['Pseudonymisierung'], tags: ['support', 'chat'] },
{ id: 'dp-e3-call-recordings', code: 'E3', category: 'COMMUNICATION', name: l('Anrufaufzeichnungen', 'Call Recordings'), description: l('Aufzeichnungen von Telefonaten', 'Recordings of phone calls'), purpose: l('Qualitaetssicherung, Schulung', 'Quality assurance, training'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Ausdrueckliche Einwilligung vor Aufzeichnung', 'Explicit consent before recording'), retentionPeriod: '90_DAYS', retentionJustification: l('Begrenzte Qualitaetspruefung', 'Limited quality review'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Telefonie-Anbieter'], technicalMeasures: ['Verschluesselung', 'Auto-Loeschung'], tags: ['support', 'recording'] },
{ id: 'dp-e4-email-content', code: 'E4', category: 'COMMUNICATION', name: l('E-Mail-Inhalte', 'Email Content'), description: l('Inhalt von E-Mail-Korrespondenz', 'Content of email correspondence'), purpose: l('Kommunikation, Dokumentation', 'Communication, documentation'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Kommunikation', 'For communication'), retentionPeriod: '24_MONTHS', retentionJustification: l('Nachvollziehbarkeit', 'Traceability'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['E-Mail-Provider'], technicalMeasures: ['TLS', 'Archivierung'], tags: ['communication', 'email'] },
{ id: 'dp-e5-feedback', code: 'E5', category: 'COMMUNICATION', name: l('Feedback/Bewertungen', 'Feedback/Reviews'), description: l('Nutzerbewertungen und Feedback', 'User ratings and feedback'), purpose: l('Qualitaetsmessung', 'Quality measurement'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktqualitaet', 'Product quality'), retentionPeriod: '24_MONTHS', retentionJustification: l('Langzeitanalyse', 'Long-term analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Aggregierung'], tags: ['feedback', 'quality'] },
{ id: 'dp-e6-notifications', code: 'E6', category: 'COMMUNICATION', name: l('Benachrichtigungsverlauf', 'Notification History'), description: l('Historie gesendeter Benachrichtigungen', 'History of sent notifications'), purpose: l('Nachvollziehbarkeit', 'Traceability'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Servicefunktionalitaet', 'Service functionality'), retentionPeriod: '12_MONTHS', retentionJustification: l('Support-Zwecke', 'Support purposes'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Logging'], tags: ['communication', 'notifications'] },
{ id: 'dp-e7-forum-posts', code: 'E7', category: 'COMMUNICATION', name: l('Forum-/Community-Beitraege', 'Forum/Community Posts'), description: l('Beitraege in Foren oder Communities', 'Posts in forums or communities'), purpose: l('Community-Interaktion', 'Community interaction'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Community-Nutzung', 'Community usage'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Moderation'], tags: ['community', 'content'] },
// KATEGORIE F: ZAHLUNGSDATEN (8)
{ id: 'dp-f1-billing-address', code: 'F1', category: 'PAYMENT', name: l('Rechnungsadresse', 'Billing Address'), description: l('Vollstaendige Rechnungsanschrift', 'Complete billing address'), purpose: l('Rechnungsstellung, Steuer', 'Invoicing, tax'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('§147 AO, §257 HGB', '§147 AO, §257 HGB'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Steuerberater'], technicalMeasures: ['Verschluesselung', 'Archivierung'], tags: ['payment', 'billing'] },
{ id: 'dp-f2-payment-token', code: 'F2', category: 'PAYMENT', name: l('Zahlungs-Token', 'Payment Token'), description: l('Tokenisierte Zahlungsinformationen', 'Tokenized payment information'), purpose: l('Wiederkehrende Zahlungen', 'Recurring payments'), riskLevel: 'HIGH', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Zahlungsabwicklung', 'For payment processing'), retentionPeriod: '36_MONTHS', retentionJustification: l('Kundenbeziehung plus Rueckbuchungsfrist', 'Customer relationship plus chargeback period'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Payment Provider'], technicalMeasures: ['PCI-DSS', 'Tokenisierung'], tags: ['payment', 'token'] },
{ id: 'dp-f3-transactions', code: 'F3', category: 'PAYMENT', name: l('Transaktionshistorie', 'Transaction History'), description: l('Historie aller Transaktionen', 'History of all transactions'), purpose: l('Buchfuehrung, Nachweis', 'Accounting, proof'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('§147 AO', '§147 AO'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Steuerberater', 'Wirtschaftspruefer'], technicalMeasures: ['Revisionssichere Archivierung'], tags: ['payment', 'transactions'] },
{ id: 'dp-f4-iban', code: 'F4', category: 'PAYMENT', name: l('IBAN/Bankverbindung', 'IBAN/Bank Details'), description: l('Bankverbindung fuer Lastschrift', 'Bank details for direct debit'), purpose: l('Lastschrifteinzug', 'Direct debit'), riskLevel: 'HIGH', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer SEPA-Lastschrift', 'For SEPA direct debit'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Bank'], technicalMeasures: ['Verschluesselung', 'Zugriffskontrolle'], tags: ['payment', 'bank'] },
{ id: 'dp-f5-invoices', code: 'F5', category: 'PAYMENT', name: l('Rechnungen', 'Invoices'), description: l('Ausgestellte Rechnungen', 'Issued invoices'), purpose: l('Buchfuehrung, Steuer', 'Accounting, tax'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('§147 AO, §257 HGB', '§147 AO, §257 HGB'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Steuerberater'], technicalMeasures: ['Revisionssichere Archivierung'], tags: ['payment', 'invoices'] },
{ id: 'dp-f6-tax-id', code: 'F6', category: 'PAYMENT', name: l('USt-IdNr./Steuernummer', 'VAT ID/Tax Number'), description: l('Umsatzsteuer-ID oder Steuernummer', 'VAT ID or tax number'), purpose: l('Steuerliche Dokumentation', 'Tax documentation'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Steuerrecht', 'Tax law'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Steuerberater'], technicalMeasures: ['Verschluesselung'], tags: ['payment', 'tax'] },
{ id: 'dp-f7-subscription', code: 'F7', category: 'PAYMENT', name: l('Abonnement-Daten', 'Subscription Data'), description: l('Abonnement-Details und Status', 'Subscription details and status'), purpose: l('Abonnementverwaltung', 'Subscription management'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Vertragserfuellung', 'For contract performance'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['payment', 'subscription'] },
{ id: 'dp-f8-refunds', code: 'F8', category: 'PAYMENT', name: l('Erstattungen', 'Refunds'), description: l('Erstattungshistorie', 'Refund history'), purpose: l('Buchfuehrung', 'Accounting'), riskLevel: 'LOW', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('§147 AO', '§147 AO'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Steuerberater'], technicalMeasures: ['Revisionssichere Archivierung'], tags: ['payment', 'refunds'] },
// KATEGORIE G: NUTZUNGSDATEN (8)
{ id: 'dp-g1-session-duration', code: 'G1', category: 'USAGE_DATA', name: l('Sitzungsdauer', 'Session Duration'), description: l('Dauer einzelner Sitzungen', 'Duration of individual sessions'), purpose: l('Nutzungsanalyse', 'Usage analysis'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktverbesserung', 'Product improvement'), retentionPeriod: '24_MONTHS', retentionJustification: l('Langzeitanalyse', 'Long-term analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Aggregierung'], tags: ['usage', 'analytics'] },
{ id: 'dp-g2-page-views', code: 'G2', category: 'USAGE_DATA', name: l('Seitenaufrufe', 'Page Views'), description: l('Aufgerufene Seiten', 'Visited pages'), purpose: l('Nutzungsanalyse', 'Usage analysis'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktverbesserung', 'Product improvement'), retentionPeriod: '24_MONTHS', retentionJustification: l('Langzeitanalyse', 'Long-term analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Aggregierung'], tags: ['usage', 'analytics'] },
{ id: 'dp-g3-click-paths', code: 'G3', category: 'USAGE_DATA', name: l('Klickpfade', 'Click Paths'), description: l('Navigationsverhalten', 'Navigation behavior'), purpose: l('UX-Optimierung', 'UX optimization'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktverbesserung', 'Product improvement'), retentionPeriod: '12_MONTHS', retentionJustification: l('UX-Analyse', 'UX analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Pseudonymisierung'], tags: ['usage', 'ux'] },
{ id: 'dp-g4-search-queries', code: 'G4', category: 'USAGE_DATA', name: l('Suchanfragen', 'Search Queries'), description: l('Interne Suchanfragen', 'Internal search queries'), purpose: l('Suchoptimierung', 'Search optimization'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktverbesserung', 'Product improvement'), retentionPeriod: '90_DAYS', retentionJustification: l('Kurzfristige Analyse', 'Short-term analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Anonymisierung'], tags: ['usage', 'search'] },
{ id: 'dp-g5-feature-usage', code: 'G5', category: 'USAGE_DATA', name: l('Feature-Nutzung', 'Feature Usage'), description: l('Nutzung einzelner Features', 'Usage of individual features'), purpose: l('Produktentwicklung', 'Product development'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktverbesserung', 'Product improvement'), retentionPeriod: '24_MONTHS', retentionJustification: l('Langzeitanalyse', 'Long-term analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Aggregierung'], tags: ['usage', 'features'] },
{ id: 'dp-g6-error-logs', code: 'G6', category: 'USAGE_DATA', name: l('Fehlerprotokolle', 'Error Logs'), description: l('Client-seitige Fehler', 'Client-side errors'), purpose: l('Fehlerbehebung', 'Bug fixing'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Qualitaetssicherung', 'Quality assurance'), retentionPeriod: '90_DAYS', retentionJustification: l('Fehleranalyse', 'Error analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Error-Tracking-Dienst'], technicalMeasures: ['Pseudonymisierung'], tags: ['usage', 'errors'] },
{ id: 'dp-g7-preferences', code: 'G7', category: 'USAGE_DATA', name: l('Nutzereinstellungen', 'User Preferences'), description: l('Individuelle Einstellungen', 'Individual settings'), purpose: l('Personalisierung', 'Personalization'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Servicefunktionalitaet', 'Service functionality'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange Konto aktiv', 'While account active'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['usage', 'preferences'] },
{ id: 'dp-g8-ab-tests', code: 'G8', category: 'USAGE_DATA', name: l('A/B-Test-Zuordnung', 'A/B Test Assignment'), description: l('Zuordnung zu Testvarianten', 'Assignment to test variants'), purpose: l('Produktoptimierung', 'Product optimization'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktverbesserung', 'Product improvement'), retentionPeriod: '90_DAYS', retentionJustification: l('Testdauer', 'Test duration'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Pseudonymisierung'], tags: ['usage', 'testing'] },
// KATEGORIE H: STANDORTDATEN (7)
{ id: 'dp-h1-gps', code: 'H1', category: 'LOCATION', name: l('GPS-Standort', 'GPS Location'), description: l('Praeziser GPS-Standort', 'Precise GPS location'), purpose: l('Standortbasierte Dienste', 'Location-based services'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Ausdrueckliche Einwilligung', 'Explicit consent'), retentionPeriod: '30_DAYS', retentionJustification: l('Datensparsamkeit', 'Data minimization'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung', 'On-Device'], tags: ['location', 'gps'] },
{ id: 'dp-h2-ip-geo', code: 'H2', category: 'LOCATION', name: l('IP-Geolokation', 'IP Geolocation'), description: l('Ungefaehrer Standort aus IP', 'Approximate location from IP'), purpose: l('Regionalisierung', 'Regionalization'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Servicefunktionalitaet', 'Service functionality'), retentionPeriod: '90_DAYS', retentionJustification: l('Sicherheitsanalyse', 'Security analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Nur Landesebene'], tags: ['location', 'ip'] },
{ id: 'dp-h3-timezone', code: 'H3', category: 'LOCATION', name: l('Zeitzone', 'Timezone'), description: l('Zeitzone des Nutzers', 'User timezone'), purpose: l('Lokalisierung', 'Localization'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Servicefunktionalitaet', 'Service functionality'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Nutzereinstellung', 'User setting'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['location', 'timezone'] },
{ id: 'dp-h4-location-history', code: 'H4', category: 'LOCATION', name: l('Standortverlauf', 'Location History'), description: l('Historie von Standorten', 'History of locations'), purpose: l('Personalisierung', 'Personalization'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Funktion', 'Optional feature'), retentionPeriod: '30_DAYS', retentionJustification: l('Datensparsamkeit', 'Data minimization'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung'], tags: ['location', 'history'] },
{ id: 'dp-h5-country', code: 'H5', category: 'LOCATION', name: l('Herkunftsland', 'Country of Origin'), description: l('Land basierend auf IP', 'Country based on IP'), purpose: l('Compliance, Geo-Blocking', 'Compliance, geo-blocking'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Compliance', 'Compliance'), retentionPeriod: '12_MONTHS', retentionJustification: l('Sicherheit', 'Security'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['location', 'country'] },
{ id: 'dp-h6-wifi-networks', code: 'H6', category: 'LOCATION', name: l('WLAN-Netzwerke', 'WiFi Networks'), description: l('Erkannte WLAN-Netzwerke', 'Detected WiFi networks'), purpose: l('Standortbestimmung', 'Location detection'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Nur mit Einwilligung', 'Only with consent'), retentionPeriod: '24_HOURS', retentionJustification: l('Kurzlebig', 'Short-lived'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['On-Device'], tags: ['location', 'wifi'] },
{ id: 'dp-h7-travel-info', code: 'H7', category: 'LOCATION', name: l('Reiseinformationen', 'Travel Information'), description: l('Reiseziele und Plaene', 'Travel destinations and plans'), purpose: l('Reiseservices', 'Travel services'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer Reisedienste', 'For travel services'), retentionPeriod: '12_MONTHS', retentionJustification: l('Serviceerbringung', 'Service delivery'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Reiseanbieter'], technicalMeasures: ['Verschluesselung'], tags: ['location', 'travel'] },
// KATEGORIE I: GERAETEDATEN (10)
{ id: 'dp-i1-ip-address', code: 'I1', category: 'DEVICE_DATA', name: l('IP-Adresse', 'IP Address'), description: l('IP-Adresse des Nutzers', 'User IP address'), purpose: l('Sicherheit, Routing', 'Security, routing'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '90_DAYS', retentionJustification: l('Sicherheitsanalyse', 'Security analysis'), cookieCategory: 'ESSENTIAL', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Security-Monitoring'], technicalMeasures: ['IP-Anonymisierung'], tags: ['device', 'network'] },
{ id: 'dp-i2-fingerprint', code: 'I2', category: 'DEVICE_DATA', name: l('Device Fingerprint', 'Device Fingerprint'), description: l('Hash aus Geraetemerkmalen', 'Hash of device characteristics'), purpose: l('Betrugspraevention', 'Fraud prevention'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Betrugspraevention', 'Fraud prevention'), retentionPeriod: '30_DAYS', retentionJustification: l('Kurze Speicherung', 'Short storage'), cookieCategory: 'ESSENTIAL', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Einweg-Hashing'], tags: ['device', 'fingerprint'] },
{ id: 'dp-i3-browser', code: 'I3', category: 'DEVICE_DATA', name: l('Browser/User-Agent', 'Browser/User Agent'), description: l('Browser und Version', 'Browser and version'), purpose: l('Kompatibilitaet', 'Compatibility'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Technisch notwendig', 'Technically necessary'), retentionPeriod: '12_MONTHS', retentionJustification: l('Support', 'Support'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['device', 'browser'] },
{ id: 'dp-i4-os', code: 'I4', category: 'DEVICE_DATA', name: l('Betriebssystem', 'Operating System'), description: l('OS und Version', 'OS and version'), purpose: l('Kompatibilitaet', 'Compatibility'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Technisch notwendig', 'Technically necessary'), retentionPeriod: '12_MONTHS', retentionJustification: l('Support', 'Support'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['device', 'os'] },
{ id: 'dp-i5-screen', code: 'I5', category: 'DEVICE_DATA', name: l('Bildschirmaufloesung', 'Screen Resolution'), description: l('Bildschirmgroesse', 'Screen size'), purpose: l('Responsive Design', 'Responsive design'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('UX-Optimierung', 'UX optimization'), retentionPeriod: '12_MONTHS', retentionJustification: l('Analytics', 'Analytics'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['device', 'screen'] },
{ id: 'dp-i6-language', code: 'I6', category: 'DEVICE_DATA', name: l('Browser-Sprache', 'Browser Language'), description: l('Spracheinstellung des Browsers', 'Browser language setting'), purpose: l('Lokalisierung', 'Localization'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Servicefunktionalitaet', 'Service functionality'), retentionPeriod: '12_MONTHS', retentionJustification: l('Nutzereinstellung', 'User setting'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['device', 'language'] },
{ id: 'dp-i7-push-token', code: 'I7', category: 'DEVICE_DATA', name: l('Push-Token', 'Push Token'), description: l('Token fuer Push-Nachrichten', 'Token for push notifications'), purpose: l('Push-Benachrichtigungen', 'Push notifications'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Opt-In fuer Push', 'Opt-in for push'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Deaktivierung', 'Until deactivation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Push-Dienst'], technicalMeasures: ['Verschluesselung'], tags: ['device', 'push'] },
{ id: 'dp-i8-device-id', code: 'I8', category: 'DEVICE_DATA', name: l('Geraete-ID', 'Device ID'), description: l('Eindeutige Geraetekennung', 'Unique device identifier'), purpose: l('Geraeteverwaltung', 'Device management'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Multi-Device-Support', 'Multi-device support'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange verknuepft', 'While linked'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Hashing'], tags: ['device', 'id'] },
{ id: 'dp-i9-app-version', code: 'I9', category: 'DEVICE_DATA', name: l('App-Version', 'App Version'), description: l('Installierte App-Version', 'Installed app version'), purpose: l('Support, Updates', 'Support, updates'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Technisch notwendig', 'Technically necessary'), retentionPeriod: '12_MONTHS', retentionJustification: l('Support', 'Support'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['device', 'app'] },
{ id: 'dp-i10-hardware', code: 'I10', category: 'DEVICE_DATA', name: l('Hardware-Info', 'Hardware Info'), description: l('Geraetetyp, Hersteller', 'Device type, manufacturer'), purpose: l('Kompatibilitaet', 'Compatibility'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Produktentwicklung', 'Product development'), retentionPeriod: '12_MONTHS', retentionJustification: l('Analytics', 'Analytics'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['device', 'hardware'] },
// KATEGORIE J: MARKETINGDATEN (8)
{ id: 'dp-j1-tracking-pixel', code: 'J1', category: 'MARKETING', name: l('Tracking-Pixel', 'Tracking Pixel'), description: l('Conversion-Tracking-Pixel', 'Conversion tracking pixel'), purpose: l('Werbemessung', 'Ad measurement'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Cookie-Consent erforderlich', 'Cookie consent required'), retentionPeriod: '90_DAYS', retentionJustification: l('Conversion-Fenster', 'Conversion window'), cookieCategory: 'PERSONALIZATION', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Google Ads', 'Meta'], technicalMeasures: ['Nur bei Consent'], tags: ['marketing', 'tracking'] },
{ id: 'dp-j2-advertising-id', code: 'J2', category: 'MARKETING', name: l('Werbe-ID', 'Advertising ID'), description: l('Geraetuebergreifende Werbe-ID', 'Cross-device advertising ID'), purpose: l('Personalisierte Werbung', 'Personalized advertising'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Wegen Profilbildung', 'Due to profiling'), retentionPeriod: '90_DAYS', retentionJustification: l('Kampagnenzeitraum', 'Campaign period'), cookieCategory: 'PERSONALIZATION', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Werbenetzwerke'], technicalMeasures: ['Opt-out'], tags: ['marketing', 'advertising'] },
{ id: 'dp-j3-utm', code: 'J3', category: 'MARKETING', name: l('UTM-Parameter', 'UTM Parameters'), description: l('Kampagnen-Tracking-Parameter', 'Campaign tracking parameters'), purpose: l('Kampagnen-Attribution', 'Campaign attribution'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Kampagnenmessung', 'Campaign measurement'), retentionPeriod: '30_DAYS', retentionJustification: l('Session-Attribution', 'Session attribution'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Analytics'], technicalMeasures: ['Aggregierung'], tags: ['marketing', 'utm'] },
{ id: 'dp-j4-newsletter', code: 'J4', category: 'MARKETING', name: l('Newsletter-Daten', 'Newsletter Data'), description: l('E-Mail und Praeferenzen', 'Email and preferences'), purpose: l('Newsletter-Versand', 'Newsletter delivery'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Double-Opt-In', 'Double opt-in'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Abmeldung', 'Until unsubscribe'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['E-Mail-Provider'], technicalMeasures: ['Double-Opt-In'], tags: ['marketing', 'newsletter'] },
{ id: 'dp-j5-remarketing', code: 'J5', category: 'MARKETING', name: l('Remarketing-Listen', 'Remarketing Lists'), description: l('Zielgruppen fuer Remarketing', 'Audiences for remarketing'), purpose: l('Remarketing', 'Remarketing'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Profilbildung', 'Profiling'), retentionPeriod: '90_DAYS', retentionJustification: l('Kampagnenzeitraum', 'Campaign period'), cookieCategory: 'PERSONALIZATION', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Werbenetzwerke'], technicalMeasures: ['Hashing'], tags: ['marketing', 'remarketing'] },
{ id: 'dp-j6-email-opens', code: 'J6', category: 'MARKETING', name: l('E-Mail-Oeffnungen', 'Email Opens'), description: l('Oeffnungsraten von E-Mails', 'Email open rates'), purpose: l('E-Mail-Optimierung', 'Email optimization'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Teil der Newsletter-Einwilligung', 'Part of newsletter consent'), retentionPeriod: '12_MONTHS', retentionJustification: l('Analyse', 'Analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['E-Mail-Provider'], technicalMeasures: ['Aggregierung'], tags: ['marketing', 'email'] },
{ id: 'dp-j7-ad-clicks', code: 'J7', category: 'MARKETING', name: l('Anzeigen-Klicks', 'Ad Clicks'), description: l('Klicks auf Werbeanzeigen', 'Clicks on advertisements'), purpose: l('Werbemessung', 'Ad measurement'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Teil der Werbe-Einwilligung', 'Part of advertising consent'), retentionPeriod: '90_DAYS', retentionJustification: l('Conversion-Fenster', 'Conversion window'), cookieCategory: 'PERSONALIZATION', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Werbenetzwerke'], technicalMeasures: ['Aggregierung'], tags: ['marketing', 'advertising'] },
{ id: 'dp-j8-referrer', code: 'J8', category: 'MARKETING', name: l('Referrer-URL', 'Referrer URL'), description: l('Herkunftsseite', 'Source page'), purpose: l('Traffic-Analyse', 'Traffic analysis'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Marketing-Attribution', 'Marketing attribution'), retentionPeriod: '30_DAYS', retentionJustification: l('Kurzfristige Analyse', 'Short-term analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Aggregierung'], tags: ['marketing', 'referrer'] },
// KATEGORIE K: ANALYSEDATEN (7)
{ id: 'dp-k1-google-analytics', code: 'K1', category: 'ANALYTICS', name: l('Google Analytics', 'Google Analytics'), description: l('GA4-Analysedaten', 'GA4 analytics data'), purpose: l('Web-Analyse', 'Web analytics'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Cookie-Consent erforderlich', 'Cookie consent required'), retentionPeriod: '26_MONTHS', retentionJustification: l('GA4-Standard', 'GA4 standard'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Google'], technicalMeasures: ['IP-Anonymisierung', 'Consent-Mode'], tags: ['analytics', 'ga4'] },
{ id: 'dp-k2-heatmaps', code: 'K2', category: 'ANALYTICS', name: l('Heatmaps', 'Heatmaps'), description: l('Klick- und Scroll-Heatmaps', 'Click and scroll heatmaps'), purpose: l('UX-Analyse', 'UX analysis'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Cookie-Consent erforderlich', 'Cookie consent required'), retentionPeriod: '12_MONTHS', retentionJustification: l('UX-Optimierung', 'UX optimization'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Hotjar/Clarity'], technicalMeasures: ['Anonymisierung'], tags: ['analytics', 'heatmaps'] },
{ id: 'dp-k3-session-recording', code: 'K3', category: 'ANALYTICS', name: l('Session-Recordings', 'Session Recordings'), description: l('Aufzeichnung von Sitzungen', 'Recording of sessions'), purpose: l('UX-Analyse, Debugging', 'UX analysis, debugging'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Ausdrueckliche Einwilligung', 'Explicit consent'), retentionPeriod: '90_DAYS', retentionJustification: l('Begrenzte Aufbewahrung', 'Limited retention'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Recording-Dienst'], technicalMeasures: ['Passwort-Maskierung', 'PII-Filterung'], tags: ['analytics', 'recording'] },
{ id: 'dp-k4-events', code: 'K4', category: 'ANALYTICS', name: l('Event-Tracking', 'Event Tracking'), description: l('Benutzerdefinierte Events', 'Custom events'), purpose: l('Produktanalyse', 'Product analysis'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Cookie-Consent', 'Cookie consent'), retentionPeriod: '26_MONTHS', retentionJustification: l('Langzeitanalyse', 'Long-term analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Analytics-Dienst'], technicalMeasures: ['Aggregierung'], tags: ['analytics', 'events'] },
{ id: 'dp-k5-conversion', code: 'K5', category: 'ANALYTICS', name: l('Conversion-Daten', 'Conversion Data'), description: l('Konversions-Events', 'Conversion events'), purpose: l('Conversion-Optimierung', 'Conversion optimization'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Cookie-Consent', 'Cookie consent'), retentionPeriod: '26_MONTHS', retentionJustification: l('Business-Analyse', 'Business analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Analytics-Dienst'], technicalMeasures: ['Aggregierung'], tags: ['analytics', 'conversion'] },
{ id: 'dp-k6-funnel', code: 'K6', category: 'ANALYTICS', name: l('Funnel-Analyse', 'Funnel Analysis'), description: l('Trichterdaten', 'Funnel data'), purpose: l('Conversion-Optimierung', 'Conversion optimization'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Cookie-Consent', 'Cookie consent'), retentionPeriod: '26_MONTHS', retentionJustification: l('Business-Analyse', 'Business analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Analytics-Dienst'], technicalMeasures: ['Aggregierung'], tags: ['analytics', 'funnel'] },
{ id: 'dp-k7-cohort', code: 'K7', category: 'ANALYTICS', name: l('Kohorten-Analyse', 'Cohort Analysis'), description: l('Kohortenbasierte Daten', 'Cohort-based data'), purpose: l('Nutzeranalyse', 'User analysis'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Cookie-Consent', 'Cookie consent'), retentionPeriod: '26_MONTHS', retentionJustification: l('Langzeitanalyse', 'Long-term analysis'), cookieCategory: 'PERFORMANCE', isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Analytics-Dienst'], technicalMeasures: ['Aggregierung'], tags: ['analytics', 'cohort'] },
// KATEGORIE L: SOCIAL-MEDIA-DATEN (6)
{ id: 'dp-l1-profile-id', code: 'L1', category: 'SOCIAL_MEDIA', name: l('Social-Profil-ID', 'Social Profile ID'), description: l('ID aus Social Login', 'ID from social login'), purpose: l('Social Login', 'Social login'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwilliger Social Login', 'Voluntary social login'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange verknuepft', 'While linked'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Social-Network'], technicalMeasures: ['Minimale Daten'], tags: ['social', 'login'] },
{ id: 'dp-l2-avatar', code: 'L2', category: 'SOCIAL_MEDIA', name: l('Social-Avatar', 'Social Avatar'), description: l('Profilbild aus Social Network', 'Profile picture from social network'), purpose: l('Personalisierung', 'Personalization'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwilliger Import', 'Voluntary import'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange gewuenscht', 'While desired'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Lokale Kopie'], tags: ['social', 'avatar'] },
{ id: 'dp-l3-connections', code: 'L3', category: 'SOCIAL_MEDIA', name: l('Social-Verbindungen', 'Social Connections'), description: l('Freunde/Follower', 'Friends/followers'), purpose: l('Social Features', 'Social features'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwilliger Import', 'Voluntary import'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: ['Social-Network'], technicalMeasures: ['Minimale Daten'], tags: ['social', 'connections'] },
{ id: 'dp-l4-shares', code: 'L4', category: 'SOCIAL_MEDIA', name: l('Geteilte Inhalte', 'Shared Content'), description: l('Auf Social Media geteilte Inhalte', 'Content shared on social media'), purpose: l('Social Sharing', 'Social sharing'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwilliges Teilen', 'Voluntary sharing'), retentionPeriod: '12_MONTHS', retentionJustification: l('Nachvollziehbarkeit', 'Traceability'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Social-Networks'], technicalMeasures: ['Logging'], tags: ['social', 'sharing'] },
{ id: 'dp-l5-likes', code: 'L5', category: 'SOCIAL_MEDIA', name: l('Likes/Reaktionen', 'Likes/Reactions'), description: l('Social-Media-Interaktionen', 'Social media interactions'), purpose: l('Social Features', 'Social features'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Teil des Services', 'Part of service'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Nutzerfunktion', 'User feature'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['social', 'interactions'] },
{ id: 'dp-l6-oauth-tokens', code: 'L6', category: 'SOCIAL_MEDIA', name: l('OAuth-Tokens', 'OAuth Tokens'), description: l('Zugangs-Token fuer Social APIs', 'Access tokens for social APIs'), purpose: l('API-Zugriff', 'API access'), riskLevel: 'MEDIUM', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwillige Verknuepfung', 'Voluntary linking'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Social-Network'], technicalMeasures: ['Verschluesselung', 'Token-Rotation'], tags: ['social', 'oauth'] },
// KATEGORIE M: GESUNDHEITSDATEN (7) - ART. 9 DSGVO!
{ id: 'dp-m1-health-status', code: 'M1', category: 'HEALTH_DATA', name: l('Gesundheitszustand', 'Health Status'), description: l('Allgemeiner Gesundheitszustand', 'General health status'), purpose: l('Gesundheitsdienste', 'Health services'), riskLevel: 'HIGH', legalBasis: 'EXPLICIT_CONSENT', legalBasisJustification: l('Art. 9 Abs. 2 lit. a DSGVO - Ausdrueckliche Einwilligung', 'Art. 9(2)(a) GDPR - Explicit consent'), retentionPeriod: '10_YEARS', retentionJustification: l('Medizinische Aufbewahrung', 'Medical retention'), cookieCategory: null, isSpecialCategory: true, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Ende-zu-Ende-Verschluesselung', 'Zugriffskontrolle', 'Audit-Logging'], tags: ['health', 'article9', 'sensitive'] },
{ id: 'dp-m2-fitness-data', code: 'M2', category: 'HEALTH_DATA', name: l('Fitnessdaten', 'Fitness Data'), description: l('Schritte, Kalorien, Aktivitaet', 'Steps, calories, activity'), purpose: l('Fitness-Tracking', 'Fitness tracking'), riskLevel: 'MEDIUM', legalBasis: 'EXPLICIT_CONSENT', legalBasisJustification: l('Art. 9 Abs. 2 lit. a DSGVO', 'Art. 9(2)(a) GDPR'), retentionPeriod: '24_MONTHS', retentionJustification: l('Langzeit-Tracking', 'Long-term tracking'), cookieCategory: null, isSpecialCategory: true, requiresExplicitConsent: true, thirdPartyRecipients: ['Fitness-App'], technicalMeasures: ['Verschluesselung', 'Pseudonymisierung'], tags: ['health', 'fitness', 'article9'] },
{ id: 'dp-m3-medication', code: 'M3', category: 'HEALTH_DATA', name: l('Medikation', 'Medication'), description: l('Aktuelle Medikamente', 'Current medications'), purpose: l('Gesundheitsmanagement', 'Health management'), riskLevel: 'HIGH', legalBasis: 'EXPLICIT_CONSENT', legalBasisJustification: l('Art. 9 Abs. 2 lit. a DSGVO', 'Art. 9(2)(a) GDPR'), retentionPeriod: '10_YEARS', retentionJustification: l('Medizinische Dokumentation', 'Medical documentation'), cookieCategory: null, isSpecialCategory: true, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Ende-zu-Ende-Verschluesselung', 'Strenge Zugriffskontrolle'], tags: ['health', 'medication', 'article9'] },
{ id: 'dp-m4-biometric', code: 'M4', category: 'HEALTH_DATA', name: l('Biometrische Daten', 'Biometric Data'), description: l('Fingerabdruck, Face-ID (zur Identifikation)', 'Fingerprint, Face ID (for identification)'), purpose: l('Biometrische Authentifizierung', 'Biometric authentication'), riskLevel: 'HIGH', legalBasis: 'EXPLICIT_CONSENT', legalBasisJustification: l('Art. 9 Abs. 2 lit. a DSGVO', 'Art. 9(2)(a) GDPR'), retentionPeriod: 'UNTIL_ACCOUNT_DELETION', retentionJustification: l('Solange gewuenscht', 'While desired'), cookieCategory: null, isSpecialCategory: true, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['On-Device-Speicherung', 'Keine Cloud-Uebertragung'], tags: ['health', 'biometric', 'article9'] },
{ id: 'dp-m5-allergies', code: 'M5', category: 'HEALTH_DATA', name: l('Allergien', 'Allergies'), description: l('Bekannte Allergien', 'Known allergies'), purpose: l('Gesundheitsschutz', 'Health protection'), riskLevel: 'HIGH', legalBasis: 'EXPLICIT_CONSENT', legalBasisJustification: l('Art. 9 Abs. 2 lit. a DSGVO', 'Art. 9(2)(a) GDPR'), retentionPeriod: '10_YEARS', retentionJustification: l('Medizinische Dokumentation', 'Medical documentation'), cookieCategory: null, isSpecialCategory: true, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Ende-zu-Ende-Verschluesselung'], tags: ['health', 'allergies', 'article9'] },
{ id: 'dp-m6-vital-signs', code: 'M6', category: 'HEALTH_DATA', name: l('Vitalzeichen', 'Vital Signs'), description: l('Blutdruck, Puls, etc.', 'Blood pressure, pulse, etc.'), purpose: l('Gesundheitsmonitoring', 'Health monitoring'), riskLevel: 'HIGH', legalBasis: 'EXPLICIT_CONSENT', legalBasisJustification: l('Art. 9 Abs. 2 lit. a DSGVO', 'Art. 9(2)(a) GDPR'), retentionPeriod: '10_YEARS', retentionJustification: l('Medizinische Dokumentation', 'Medical documentation'), cookieCategory: null, isSpecialCategory: true, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Ende-zu-Ende-Verschluesselung', 'Audit-Logging'], tags: ['health', 'vitals', 'article9'] },
{ id: 'dp-m7-disability', code: 'M7', category: 'HEALTH_DATA', name: l('Behinderung/Einschraenkung', 'Disability/Impairment'), description: l('Informationen zu Behinderungen', 'Information about disabilities'), purpose: l('Barrierefreiheit', 'Accessibility'), riskLevel: 'HIGH', legalBasis: 'EXPLICIT_CONSENT', legalBasisJustification: l('Art. 9 Abs. 2 lit. a DSGVO', 'Art. 9(2)(a) GDPR'), retentionPeriod: 'UNTIL_REVOCATION', retentionJustification: l('Bis Widerruf', 'Until revocation'), cookieCategory: null, isSpecialCategory: true, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Strenge Zugriffskontrolle'], tags: ['health', 'disability', 'article9'] },
// KATEGORIE N: BESCHAEFTIGTENDATEN (10) - BDSG § 26
{ id: 'dp-n1-employee-id', code: 'N1', category: 'EMPLOYEE_DATA', name: l('Personalnummer', 'Employee ID'), description: l('Eindeutige Mitarbeiter-ID', 'Unique employee ID'), purpose: l('Personalverwaltung', 'HR management'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('BDSG § 26 - Beschaeftigungsverhaeltnis', 'BDSG § 26 - Employment relationship'), retentionPeriod: '10_YEARS', retentionJustification: l('Aufbewahrungspflichten', 'Retention obligations'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['employee', 'hr'] },
{ id: 'dp-n2-salary', code: 'N2', category: 'EMPLOYEE_DATA', name: l('Gehalt/Verguetung', 'Salary/Compensation'), description: l('Gehaltsinformationen', 'Salary information'), purpose: l('Lohnabrechnung', 'Payroll'), riskLevel: 'HIGH', legalBasis: 'CONTRACT', legalBasisJustification: l('BDSG § 26', 'BDSG § 26'), retentionPeriod: '10_YEARS', retentionJustification: l('§ 147 AO', '§ 147 AO'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Lohnbuero', 'Finanzamt'], technicalMeasures: ['Verschluesselung', 'Strenge Zugriffskontrolle'], tags: ['employee', 'payroll'] },
{ id: 'dp-n3-tax-id', code: 'N3', category: 'EMPLOYEE_DATA', name: l('Steuer-ID', 'Tax ID'), description: l('Steueridentifikationsnummer', 'Tax identification number'), purpose: l('Lohnsteuer', 'Payroll tax'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Steuerrecht', 'Tax law'), retentionPeriod: '10_YEARS', retentionJustification: l('Steuerliche Aufbewahrung', 'Tax retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Finanzamt'], technicalMeasures: ['Verschluesselung'], tags: ['employee', 'tax'] },
{ id: 'dp-n4-social-security', code: 'N4', category: 'EMPLOYEE_DATA', name: l('Sozialversicherungsnummer', 'Social Security Number'), description: l('SV-Nummer', 'Social security number'), purpose: l('Sozialversicherung', 'Social security'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Sozialversicherungsrecht', 'Social security law'), retentionPeriod: '10_YEARS', retentionJustification: l('Gesetzliche Pflicht', 'Legal obligation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Krankenkasse', 'Rentenversicherung'], technicalMeasures: ['Verschluesselung'], tags: ['employee', 'social-security'] },
{ id: 'dp-n5-working-hours', code: 'N5', category: 'EMPLOYEE_DATA', name: l('Arbeitszeiten', 'Working Hours'), description: l('Erfasste Arbeitszeiten', 'Recorded working hours'), purpose: l('Arbeitszeiterfassung', 'Time tracking'), riskLevel: 'LOW', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('ArbZG', 'Working Time Act'), retentionPeriod: '6_YEARS', retentionJustification: l('Gesetzliche Aufbewahrung', 'Legal retention'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['employee', 'time-tracking'] },
{ id: 'dp-n6-vacation', code: 'N6', category: 'EMPLOYEE_DATA', name: l('Urlaubsdaten', 'Vacation Data'), description: l('Urlaubsanspruch und -nutzung', 'Vacation entitlement and usage'), purpose: l('Urlaubsverwaltung', 'Vacation management'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('BDSG § 26', 'BDSG § 26'), retentionPeriod: '6_YEARS', retentionJustification: l('Nachvollziehbarkeit', 'Traceability'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['employee', 'vacation'] },
{ id: 'dp-n7-sick-leave', code: 'N7', category: 'EMPLOYEE_DATA', name: l('Krankheitstage', 'Sick Leave'), description: l('Krankheitstage (ohne Diagnose)', 'Sick days (without diagnosis)'), purpose: l('Personalplanung', 'HR planning'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('BDSG § 26', 'BDSG § 26'), retentionPeriod: '6_YEARS', retentionJustification: l('Lohnfortzahlung', 'Sick pay'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Krankenkasse'], technicalMeasures: ['Zugriffskontrolle'], tags: ['employee', 'sick-leave'] },
{ id: 'dp-n8-performance', code: 'N8', category: 'EMPLOYEE_DATA', name: l('Leistungsbeurteilung', 'Performance Review'), description: l('Mitarbeiterbeurteilungen', 'Employee evaluations'), purpose: l('Personalentwicklung', 'HR development'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('BDSG § 26', 'BDSG § 26'), retentionPeriod: '6_YEARS', retentionJustification: l('Personalakte', 'Personnel file'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['employee', 'performance'] },
{ id: 'dp-n9-training', code: 'N9', category: 'EMPLOYEE_DATA', name: l('Schulungen/Weiterbildung', 'Training/Development'), description: l('Absolvierte Schulungen', 'Completed training'), purpose: l('Personalentwicklung', 'HR development'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('BDSG § 26', 'BDSG § 26'), retentionPeriod: '6_YEARS', retentionJustification: l('Personalakte', 'Personnel file'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['employee', 'training'] },
{ id: 'dp-n10-contract', code: 'N10', category: 'EMPLOYEE_DATA', name: l('Arbeitsvertrag', 'Employment Contract'), description: l('Arbeitsvertragsdaten', 'Employment contract data'), purpose: l('Vertragsverwaltung', 'Contract management'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('BDSG § 26', 'BDSG § 26'), retentionPeriod: '10_YEARS', retentionJustification: l('Verjaehrungsfristen', 'Limitation periods'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung', 'Archivierung'], tags: ['employee', 'contract'] },
// KATEGORIE O: VERTRAGSDATEN (7)
{ id: 'dp-o1-contract-number', code: 'O1', category: 'CONTRACT_DATA', name: l('Vertragsnummer', 'Contract Number'), description: l('Eindeutige Vertragsnummer', 'Unique contract number'), purpose: l('Vertragsverwaltung', 'Contract management'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Vertragserfuellung', 'Contract performance'), retentionPeriod: '10_YEARS', retentionJustification: l('§ 147 AO', '§ 147 AO'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['contract', 'id'] },
{ id: 'dp-o2-contract-duration', code: 'O2', category: 'CONTRACT_DATA', name: l('Vertragslaufzeit', 'Contract Duration'), description: l('Start- und Enddatum', 'Start and end date'), purpose: l('Vertragsverwaltung', 'Contract management'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Vertragserfuellung', 'Contract performance'), retentionPeriod: '10_YEARS', retentionJustification: l('§ 147 AO', '§ 147 AO'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: [], tags: ['contract', 'duration'] },
{ id: 'dp-o3-signature', code: 'O3', category: 'CONTRACT_DATA', name: l('Unterschrift', 'Signature'), description: l('Digitale oder gescannte Unterschrift', 'Digital or scanned signature'), purpose: l('Vertragsschluss', 'Contract conclusion'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Vertragserfuellung', 'Contract performance'), retentionPeriod: '10_YEARS', retentionJustification: l('Beweissicherung', 'Evidence preservation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung', 'Integritaetsschutz'], tags: ['contract', 'signature'] },
{ id: 'dp-o4-contract-documents', code: 'O4', category: 'CONTRACT_DATA', name: l('Vertragsdokumente', 'Contract Documents'), description: l('PDFs und Anlagen', 'PDFs and attachments'), purpose: l('Dokumentation', 'Documentation'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Vertragserfuellung', 'Contract performance'), retentionPeriod: '10_YEARS', retentionJustification: l('§ 147 AO', '§ 147 AO'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Revisionssichere Archivierung'], tags: ['contract', 'documents'] },
{ id: 'dp-o5-contract-terms', code: 'O5', category: 'CONTRACT_DATA', name: l('Vertragskonditionen', 'Contract Terms'), description: l('Preise, Rabatte, Bedingungen', 'Prices, discounts, conditions'), purpose: l('Vertragsabwicklung', 'Contract processing'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Vertragserfuellung', 'Contract performance'), retentionPeriod: '10_YEARS', retentionJustification: l('§ 147 AO', '§ 147 AO'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Zugriffskontrolle'], tags: ['contract', 'terms'] },
{ id: 'dp-o6-contract-history', code: 'O6', category: 'CONTRACT_DATA', name: l('Vertragshistorie', 'Contract History'), description: l('Aenderungen und Versionen', 'Changes and versions'), purpose: l('Nachvollziehbarkeit', 'Traceability'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Dokumentationspflicht', 'Documentation obligation'), retentionPeriod: '10_YEARS', retentionJustification: l('§ 147 AO', '§ 147 AO'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Versionierung'], tags: ['contract', 'history'] },
{ id: 'dp-o7-termination', code: 'O7', category: 'CONTRACT_DATA', name: l('Kuendigungsdaten', 'Termination Data'), description: l('Kuendigungen und Gruende', 'Terminations and reasons'), purpose: l('Vertragsbeendigung', 'Contract termination'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Vertragserfuellung', 'Contract performance'), retentionPeriod: '10_YEARS', retentionJustification: l('Beweissicherung', 'Evidence preservation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Archivierung'], tags: ['contract', 'termination'] },
// KATEGORIE P: PROTOKOLLDATEN (7)
{ id: 'dp-p1-login-logs', code: 'P1', category: 'LOG_DATA', name: l('Login-Protokolle', 'Login Logs'), description: l('Erfolgreiche und fehlgeschlagene Logins', 'Successful and failed logins'), purpose: l('Sicherheitsaudit', 'Security audit'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '12_MONTHS', retentionJustification: l('Sicherheitsforensik', 'Security forensics'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['SIEM'], technicalMeasures: ['Unveraenderbare Logs'], tags: ['logs', 'security'] },
{ id: 'dp-p2-access-logs', code: 'P2', category: 'LOG_DATA', name: l('Zugriffsprotokolle', 'Access Logs'), description: l('HTTP-Zugriffe', 'HTTP accesses'), purpose: l('Sicherheit, Debugging', 'Security, debugging'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '90_DAYS', retentionJustification: l('Fehleranalyse', 'Error analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['IP-Anonymisierung'], tags: ['logs', 'access'] },
{ id: 'dp-p3-api-logs', code: 'P3', category: 'LOG_DATA', name: l('API-Protokolle', 'API Logs'), description: l('API-Aufrufe', 'API calls'), purpose: l('Debugging, Monitoring', 'Debugging, monitoring'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Servicequalitaet', 'Service quality'), retentionPeriod: '90_DAYS', retentionJustification: l('Fehleranalyse', 'Error analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Pseudonymisierung'], tags: ['logs', 'api'] },
{ id: 'dp-p4-admin-logs', code: 'P4', category: 'LOG_DATA', name: l('Admin-Aktionen', 'Admin Actions'), description: l('Protokoll von Admin-Aktivitaeten', 'Log of admin activities'), purpose: l('Revisionssicherheit', 'Audit compliance'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Dokumentationspflicht', 'Documentation obligation'), retentionPeriod: '6_YEARS', retentionJustification: l('Revisionssicherheit', 'Audit compliance'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Unveraenderbare Logs', 'Signatur'], tags: ['logs', 'admin'] },
{ id: 'dp-p5-change-logs', code: 'P5', category: 'LOG_DATA', name: l('Aenderungshistorie', 'Change History'), description: l('Audit-Trail von Aenderungen', 'Audit trail of changes'), purpose: l('Nachvollziehbarkeit', 'Traceability'), riskLevel: 'LOW', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Dokumentationspflicht', 'Documentation obligation'), retentionPeriod: '6_YEARS', retentionJustification: l('Revisionssicherheit', 'Audit compliance'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Unveraenderbare Logs'], tags: ['logs', 'audit'] },
{ id: 'dp-p6-error-logs', code: 'P6', category: 'LOG_DATA', name: l('Fehlerprotokolle', 'Error Logs'), description: l('System- und Anwendungsfehler', 'System and application errors'), purpose: l('Fehlerbehebung', 'Bug fixing'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Servicequalitaet', 'Service quality'), retentionPeriod: '90_DAYS', retentionJustification: l('Fehleranalyse', 'Error analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Error-Tracking'], technicalMeasures: ['PII-Filterung'], tags: ['logs', 'errors'] },
{ id: 'dp-p7-security-logs', code: 'P7', category: 'LOG_DATA', name: l('Sicherheitsprotokolle', 'Security Logs'), description: l('Security Events', 'Security events'), purpose: l('Sicherheitsmonitoring', 'Security monitoring'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '12_MONTHS', retentionJustification: l('Forensik', 'Forensics'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['SIEM'], technicalMeasures: ['Unveraenderbare Logs'], tags: ['logs', 'security'] },
// KATEGORIE Q: KI-DATEN (7) - AI ACT
{ id: 'dp-q1-ai-prompts', code: 'Q1', category: 'AI_DATA', name: l('KI-Prompts', 'AI Prompts'), description: l('Nutzereingaben an KI', 'User inputs to AI'), purpose: l('KI-Funktionalitaet', 'AI functionality'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer KI-Service', 'For AI service'), retentionPeriod: '90_DAYS', retentionJustification: l('Kontexterhaltung', 'Context preservation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['KI-Provider'], technicalMeasures: ['Keine Verwendung fuer Training', 'Verschluesselung'], tags: ['ai', 'prompts'] },
{ id: 'dp-q2-ai-responses', code: 'Q2', category: 'AI_DATA', name: l('KI-Antworten', 'AI Responses'), description: l('Generierte KI-Antworten', 'Generated AI responses'), purpose: l('Qualitaetssicherung', 'Quality assurance'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer KI-Service', 'For AI service'), retentionPeriod: '90_DAYS', retentionJustification: l('QA', 'QA'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Logging'], tags: ['ai', 'responses'] },
{ id: 'dp-q3-rag-context', code: 'Q3', category: 'AI_DATA', name: l('RAG-Kontext', 'RAG Context'), description: l('Retrieval-Kontext', 'Retrieval context'), purpose: l('Kontextuelle KI-Antworten', 'Contextual AI responses'), riskLevel: 'MEDIUM', legalBasis: 'CONTRACT', legalBasisJustification: l('Fuer RAG-Funktionalitaet', 'For RAG functionality'), retentionPeriod: '24_HOURS', retentionJustification: l('Session-Kontext', 'Session context'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['In-Memory', 'Auto-Loeschung'], tags: ['ai', 'rag'] },
{ id: 'dp-q4-ai-feedback', code: 'Q4', category: 'AI_DATA', name: l('KI-Feedback', 'AI Feedback'), description: l('Nutzerfeedback zu KI-Antworten', 'User feedback on AI responses'), purpose: l('KI-Verbesserung', 'AI improvement'), riskLevel: 'LOW', legalBasis: 'CONSENT', legalBasisJustification: l('Freiwilliges Feedback', 'Voluntary feedback'), retentionPeriod: '24_MONTHS', retentionJustification: l('Qualitaetsanalyse', 'Quality analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Anonymisierung'], tags: ['ai', 'feedback'] },
{ id: 'dp-q5-training-data', code: 'Q5', category: 'AI_DATA', name: l('Trainingsdaten (mit Einwilligung)', 'Training Data (with consent)'), description: l('Fuer KI-Training freigegebene Daten', 'Data released for AI training'), purpose: l('Modellverbesserung', 'Model improvement'), riskLevel: 'HIGH', legalBasis: 'CONSENT', legalBasisJustification: l('Ausdrueckliche Einwilligung', 'Explicit consent'), retentionPeriod: '36_MONTHS', retentionJustification: l('Modellentwicklung', 'Model development'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: true, thirdPartyRecipients: [], technicalMeasures: ['Anonymisierung', 'Zugriffskontrolle'], tags: ['ai', 'training'] },
{ id: 'dp-q6-model-outputs', code: 'Q6', category: 'AI_DATA', name: l('Modell-Outputs', 'Model Outputs'), description: l('KI-generierte Inhalte', 'AI-generated content'), purpose: l('Dokumentation', 'Documentation'), riskLevel: 'LOW', legalBasis: 'CONTRACT', legalBasisJustification: l('Teil des Services', 'Part of service'), retentionPeriod: '12_MONTHS', retentionJustification: l('Nachvollziehbarkeit', 'Traceability'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Kennzeichnung als KI-generiert'], tags: ['ai', 'outputs'] },
{ id: 'dp-q7-ai-usage', code: 'Q7', category: 'AI_DATA', name: l('KI-Nutzungsstatistik', 'AI Usage Statistics'), description: l('Aggregierte KI-Nutzungsdaten', 'Aggregated AI usage data'), purpose: l('Kapazitaetsplanung', 'Capacity planning'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Serviceoptimierung', 'Service optimization'), retentionPeriod: '24_MONTHS', retentionJustification: l('Langzeitanalyse', 'Long-term analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Aggregierung'], tags: ['ai', 'usage'] },
// KATEGORIE R: SICHERHEITSDATEN (7)
{ id: 'dp-r1-failed-logins', code: 'R1', category: 'SECURITY', name: l('Fehlgeschlagene Logins', 'Failed Logins'), description: l('Fehlgeschlagene Anmeldeversuche', 'Failed login attempts'), purpose: l('Angriffserkennung', 'Attack detection'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '12_MONTHS', retentionJustification: l('Forensik', 'Forensics'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['SIEM'], technicalMeasures: ['Alerting', 'Rate-Limiting'], tags: ['security', 'auth'] },
{ id: 'dp-r2-fraud-score', code: 'R2', category: 'SECURITY', name: l('Betrugsrisiko-Score', 'Fraud Risk Score'), description: l('Berechnetes Betrugsrisiko', 'Calculated fraud risk'), purpose: l('Betrugspraevention', 'Fraud prevention'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('Betrugspraevention', 'Fraud prevention'), retentionPeriod: '12_MONTHS', retentionJustification: l('Analyse', 'Analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Maschinelles Lernen'], tags: ['security', 'fraud'] },
{ id: 'dp-r3-incident-reports', code: 'R3', category: 'SECURITY', name: l('Vorfallberichte', 'Incident Reports'), description: l('Sicherheitsvorfaelle', 'Security incidents'), purpose: l('Vorfallmanagement', 'Incident management'), riskLevel: 'MEDIUM', legalBasis: 'LEGAL_OBLIGATION', legalBasisJustification: l('Art. 33 DSGVO Meldepflicht', 'Art. 33 GDPR notification obligation'), retentionPeriod: '6_YEARS', retentionJustification: l('Dokumentationspflicht', 'Documentation obligation'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: ['Aufsichtsbehoerde'], technicalMeasures: ['Verschluesselung', 'Zugriffskontrolle'], tags: ['security', 'incidents'] },
{ id: 'dp-r4-threat-intel', code: 'R4', category: 'SECURITY', name: l('Bedrohungsinformationen', 'Threat Intelligence'), description: l('Erkannte Bedrohungen', 'Detected threats'), purpose: l('Sicherheitsmonitoring', 'Security monitoring'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '12_MONTHS', retentionJustification: l('Analyse', 'Analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Aggregierung'], tags: ['security', 'threats'] },
{ id: 'dp-r5-blocked-ips', code: 'R5', category: 'SECURITY', name: l('Gesperrte IPs', 'Blocked IPs'), description: l('Blacklist von IP-Adressen', 'IP address blacklist'), purpose: l('Angriffspraevention', 'Attack prevention'), riskLevel: 'LOW', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '12_MONTHS', retentionJustification: l('Sicherheit', 'Security'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Automatische Bereinigung'], tags: ['security', 'blocking'] },
{ id: 'dp-r6-vulnerability-scans', code: 'R6', category: 'SECURITY', name: l('Schwachstellen-Scans', 'Vulnerability Scans'), description: l('Ergebnisse von Security-Scans', 'Results of security scans'), purpose: l('Schwachstellenmanagement', 'Vulnerability management'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '12_MONTHS', retentionJustification: l('Trend-Analyse', 'Trend analysis'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Verschluesselung'], tags: ['security', 'vulnerabilities'] },
{ id: 'dp-r7-penetration-tests', code: 'R7', category: 'SECURITY', name: l('Penetrationstests', 'Penetration Tests'), description: l('Ergebnisse von Pentests', 'Results of pentests'), purpose: l('Sicherheitspruefung', 'Security testing'), riskLevel: 'MEDIUM', legalBasis: 'LEGITIMATE_INTEREST', legalBasisJustification: l('IT-Sicherheit', 'IT security'), retentionPeriod: '6_YEARS', retentionJustification: l('Compliance-Nachweis', 'Compliance evidence'), cookieCategory: null, isSpecialCategory: false, requiresExplicitConsent: false, thirdPartyRecipients: [], technicalMeasures: ['Strenge Zugriffskontrolle'], tags: ['security', 'pentests'] },
]
// =============================================================================
// RETENTION MATRIX (18 Kategorien)
// =============================================================================
export const RETENTION_MATRIX: RetentionMatrixEntry[] = [
{ category: 'MASTER_DATA', categoryName: l('Stammdaten', 'Master Data'), standardPeriod: 'UNTIL_ACCOUNT_DELETION', legalBasis: 'Art. 6 Abs. 1 lit. b DSGVO', exceptions: [] },
{ category: 'CONTACT_DATA', categoryName: l('Kontaktdaten', 'Contact Data'), standardPeriod: 'UNTIL_ACCOUNT_DELETION', legalBasis: 'Art. 6 Abs. 1 lit. b DSGVO', exceptions: [] },
{ category: 'AUTHENTICATION', categoryName: l('Authentifizierung', 'Authentication'), standardPeriod: 'UNTIL_ACCOUNT_DELETION', legalBasis: 'Art. 6 Abs. 1 lit. b DSGVO', exceptions: [{ condition: l('Session-Token', 'Session token'), period: '24_HOURS', reason: l('Sicherheit', 'Security') }] },
{ category: 'CONSENT', categoryName: l('Einwilligungsdaten', 'Consent Data'), standardPeriod: '6_YEARS', legalBasis: 'Art. 7 DSGVO, § 147 AO', exceptions: [] },
{ category: 'COMMUNICATION', categoryName: l('Kommunikation', 'Communication'), standardPeriod: '24_MONTHS', legalBasis: 'Art. 6 Abs. 1 lit. b/f DSGVO', exceptions: [] },
{ category: 'PAYMENT', categoryName: l('Zahlungsdaten', 'Payment Data'), standardPeriod: '10_YEARS', legalBasis: '§ 147 AO, § 257 HGB', exceptions: [] },
{ category: 'USAGE_DATA', categoryName: l('Nutzungsdaten', 'Usage Data'), standardPeriod: '24_MONTHS', legalBasis: 'Art. 6 Abs. 1 lit. f DSGVO', exceptions: [] },
{ category: 'LOCATION', categoryName: l('Standortdaten', 'Location Data'), standardPeriod: '90_DAYS', legalBasis: 'Art. 6 Abs. 1 lit. a DSGVO', exceptions: [] },
{ category: 'DEVICE_DATA', categoryName: l('Geraetedaten', 'Device Data'), standardPeriod: '12_MONTHS', legalBasis: 'Art. 6 Abs. 1 lit. f DSGVO', exceptions: [] },
{ category: 'MARKETING', categoryName: l('Marketingdaten', 'Marketing Data'), standardPeriod: '90_DAYS', legalBasis: 'Art. 6 Abs. 1 lit. a DSGVO', exceptions: [] },
{ category: 'ANALYTICS', categoryName: l('Analysedaten', 'Analytics Data'), standardPeriod: '26_MONTHS', legalBasis: 'Art. 6 Abs. 1 lit. a DSGVO', exceptions: [] },
{ category: 'SOCIAL_MEDIA', categoryName: l('Social-Media-Daten', 'Social Media Data'), standardPeriod: 'UNTIL_ACCOUNT_DELETION', legalBasis: 'Art. 6 Abs. 1 lit. a DSGVO', exceptions: [] },
{ category: 'HEALTH_DATA', categoryName: l('Gesundheitsdaten', 'Health Data'), standardPeriod: '10_YEARS', legalBasis: 'Art. 9 Abs. 2 lit. a DSGVO', exceptions: [] },
{ category: 'EMPLOYEE_DATA', categoryName: l('Beschaeftigtendaten', 'Employee Data'), standardPeriod: '10_YEARS', legalBasis: 'BDSG § 26', exceptions: [] },
{ category: 'CONTRACT_DATA', categoryName: l('Vertragsdaten', 'Contract Data'), standardPeriod: '10_YEARS', legalBasis: '§ 147 AO, § 257 HGB', exceptions: [] },
{ category: 'LOG_DATA', categoryName: l('Protokolldaten', 'Log Data'), standardPeriod: '12_MONTHS', legalBasis: 'Art. 6 Abs. 1 lit. f DSGVO', exceptions: [] },
{ category: 'AI_DATA', categoryName: l('KI-Daten', 'AI Data'), standardPeriod: '90_DAYS', legalBasis: 'Art. 6 Abs. 1 lit. a/b DSGVO', exceptions: [] },
{ category: 'SECURITY', categoryName: l('Sicherheitsdaten', 'Security Data'), standardPeriod: '12_MONTHS', legalBasis: 'Art. 6 Abs. 1 lit. f DSGVO', exceptions: [] },
]
// =============================================================================
// COOKIE CATEGORIES
// =============================================================================
export const DEFAULT_COOKIE_CATEGORIES: CookieBannerCategory[] = [
{ id: 'ESSENTIAL', name: l('Technisch notwendig', 'Essential'), description: l('Diese Cookies sind fuer den Betrieb erforderlich', 'These cookies are required for operation'), isRequired: true, defaultEnabled: true, dataPointIds: ['dp-c2-session-token', 'dp-c3-refresh-token', 'dp-d1-consent-records', 'dp-d2-cookie-preferences'], cookies: [] },
{ id: 'PERFORMANCE', name: l('Analyse & Performance', 'Analytics & Performance'), description: l('Helfen uns die Nutzung zu verstehen', 'Help us understand usage'), isRequired: false, defaultEnabled: false, dataPointIds: ['dp-g1-session-duration', 'dp-g2-page-views'], cookies: [] },
{ id: 'PERSONALIZATION', name: l('Personalisierung', 'Personalization'), description: l('Ermoeglichen personalisierte Werbung', 'Enable personalized advertising'), isRequired: false, defaultEnabled: false, dataPointIds: [], cookies: [] },
{ id: 'EXTERNAL_MEDIA', name: l('Externe Medien', 'External Media'), description: l('Erlauben Einbindung externer Medien', 'Allow embedding external media'), isRequired: false, defaultEnabled: false, dataPointIds: [], cookies: [] },
]
// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================
export function getDataPointById(id: string): DataPoint | undefined {
return PREDEFINED_DATA_POINTS.find((dp) => dp.id === id)
}
export function getDataPointByCode(code: string): DataPoint | undefined {
return PREDEFINED_DATA_POINTS.find((dp) => dp.code === code)
}
export function getDataPointsByCategory(category: DataPointCategory): DataPoint[] {
return PREDEFINED_DATA_POINTS.filter((dp) => dp.category === category)
}
export function getDataPointsByLegalBasis(legalBasis: string): DataPoint[] {
return PREDEFINED_DATA_POINTS.filter((dp) => dp.legalBasis === legalBasis)
}
export function getDataPointsByCookieCategory(cookieCategory: string): DataPoint[] {
return PREDEFINED_DATA_POINTS.filter((dp) => dp.cookieCategory === cookieCategory)
}
export function getDataPointsRequiringConsent(): DataPoint[] {
return PREDEFINED_DATA_POINTS.filter((dp) => dp.requiresExplicitConsent)
}
export function getHighRiskDataPoints(): DataPoint[] {
return PREDEFINED_DATA_POINTS.filter((dp) => dp.riskLevel === 'HIGH')
}
export function getSpecialCategoryDataPoints(): DataPoint[] {
return PREDEFINED_DATA_POINTS.filter((dp) => dp.isSpecialCategory)
}
export function countDataPointsByCategory(): Record<DataPointCategory, number> {
const counts = {} as Record<DataPointCategory, number>
for (const dp of PREDEFINED_DATA_POINTS) {
counts[dp.category] = (counts[dp.category] || 0) + 1
}
return counts
}
export function countDataPointsByRiskLevel(): Record<'LOW' | 'MEDIUM' | 'HIGH', number> {
const counts = { LOW: 0, MEDIUM: 0, HIGH: 0 }
for (const dp of PREDEFINED_DATA_POINTS) {
counts[dp.riskLevel]++
}
return counts
}
export function createDefaultCatalog(tenantId: string): DataPointCatalog {
return {
id: `catalog-${tenantId}`,
tenantId,
version: '2.0.0',
dataPoints: PREDEFINED_DATA_POINTS.map((dp) => ({ ...dp, isActive: true })),
customDataPoints: [],
retentionMatrix: RETENTION_MATRIX,
createdAt: new Date(),
updatedAt: new Date(),
}
}
export function searchDataPoints(dataPoints: DataPoint[], query: string, language: 'de' | 'en' = 'de'): DataPoint[] {
const lowerQuery = query.toLowerCase()
return dataPoints.filter(
(dp) =>
dp.code.toLowerCase().includes(lowerQuery) ||
dp.name[language].toLowerCase().includes(lowerQuery) ||
dp.description[language].toLowerCase().includes(lowerQuery) ||
dp.tags.some((tag) => tag.toLowerCase().includes(lowerQuery))
)
}

View File

@@ -0,0 +1,669 @@
'use client'
/**
* Einwilligungen Context & Reducer
*
* Zentrale State-Verwaltung fuer das Datenpunktkatalog & DSI-Generator Modul.
* Verwendet React Context + useReducer fuer vorhersehbare State-Updates.
*/
import {
createContext,
useContext,
useReducer,
useCallback,
useMemo,
ReactNode,
Dispatch,
} from 'react'
import {
EinwilligungenState,
EinwilligungenAction,
EinwilligungenTab,
DataPoint,
DataPointCatalog,
GeneratedPrivacyPolicy,
CookieBannerConfig,
CompanyInfo,
ConsentStatistics,
PrivacyPolicySection,
SupportedLanguage,
ExportFormat,
DataPointCategory,
LegalBasis,
RiskLevel,
} from './types'
import {
PREDEFINED_DATA_POINTS,
RETENTION_MATRIX,
DEFAULT_COOKIE_CATEGORIES,
createDefaultCatalog,
getDataPointById,
getDataPointsByCategory,
countDataPointsByCategory,
countDataPointsByRiskLevel,
} from './catalog/loader'
// =============================================================================
// INITIAL STATE
// =============================================================================
const initialState: EinwilligungenState = {
// Data
catalog: null,
selectedDataPoints: [],
privacyPolicy: null,
cookieBannerConfig: null,
companyInfo: null,
consentStatistics: null,
// UI State
activeTab: 'catalog',
isLoading: false,
isSaving: false,
error: null,
// Editor State
editingDataPoint: null,
editingSection: null,
// Preview
previewLanguage: 'de',
previewFormat: 'HTML',
}
// =============================================================================
// REDUCER
// =============================================================================
function einwilligungenReducer(
state: EinwilligungenState,
action: EinwilligungenAction
): EinwilligungenState {
switch (action.type) {
case 'SET_CATALOG':
return {
...state,
catalog: action.payload,
// Automatisch alle aktiven Datenpunkte auswaehlen
selectedDataPoints: [
...action.payload.dataPoints.filter((dp) => dp.isActive !== false).map((dp) => dp.id),
...action.payload.customDataPoints.filter((dp) => dp.isActive !== false).map((dp) => dp.id),
],
}
case 'SET_SELECTED_DATA_POINTS':
return {
...state,
selectedDataPoints: action.payload,
}
case 'TOGGLE_DATA_POINT': {
const id = action.payload
const isSelected = state.selectedDataPoints.includes(id)
return {
...state,
selectedDataPoints: isSelected
? state.selectedDataPoints.filter((dpId) => dpId !== id)
: [...state.selectedDataPoints, id],
}
}
case 'ADD_CUSTOM_DATA_POINT':
if (!state.catalog) return state
return {
...state,
catalog: {
...state.catalog,
customDataPoints: [...state.catalog.customDataPoints, action.payload],
updatedAt: new Date(),
},
selectedDataPoints: [...state.selectedDataPoints, action.payload.id],
}
case 'UPDATE_DATA_POINT': {
if (!state.catalog) return state
const { id, data } = action.payload
// Pruefe ob es ein vordefinierter oder kundenspezifischer Datenpunkt ist
const isCustom = state.catalog.customDataPoints.some((dp) => dp.id === id)
if (isCustom) {
return {
...state,
catalog: {
...state.catalog,
customDataPoints: state.catalog.customDataPoints.map((dp) =>
dp.id === id ? { ...dp, ...data } : dp
),
updatedAt: new Date(),
},
}
} else {
// Vordefinierte Datenpunkte: nur isActive aendern
return {
...state,
catalog: {
...state.catalog,
dataPoints: state.catalog.dataPoints.map((dp) =>
dp.id === id ? { ...dp, ...data } : dp
),
updatedAt: new Date(),
},
}
}
}
case 'DELETE_CUSTOM_DATA_POINT':
if (!state.catalog) return state
return {
...state,
catalog: {
...state.catalog,
customDataPoints: state.catalog.customDataPoints.filter((dp) => dp.id !== action.payload),
updatedAt: new Date(),
},
selectedDataPoints: state.selectedDataPoints.filter((id) => id !== action.payload),
}
case 'SET_PRIVACY_POLICY':
return {
...state,
privacyPolicy: action.payload,
}
case 'SET_COOKIE_BANNER_CONFIG':
return {
...state,
cookieBannerConfig: action.payload,
}
case 'UPDATE_COOKIE_BANNER_STYLING':
if (!state.cookieBannerConfig) return state
return {
...state,
cookieBannerConfig: {
...state.cookieBannerConfig,
styling: {
...state.cookieBannerConfig.styling,
...action.payload,
},
updatedAt: new Date(),
},
}
case 'UPDATE_COOKIE_BANNER_TEXTS':
if (!state.cookieBannerConfig) return state
return {
...state,
cookieBannerConfig: {
...state.cookieBannerConfig,
texts: {
...state.cookieBannerConfig.texts,
...action.payload,
},
updatedAt: new Date(),
},
}
case 'SET_COMPANY_INFO':
return {
...state,
companyInfo: action.payload,
}
case 'SET_CONSENT_STATISTICS':
return {
...state,
consentStatistics: action.payload,
}
case 'SET_ACTIVE_TAB':
return {
...state,
activeTab: action.payload,
}
case 'SET_LOADING':
return {
...state,
isLoading: action.payload,
}
case 'SET_SAVING':
return {
...state,
isSaving: action.payload,
}
case 'SET_ERROR':
return {
...state,
error: action.payload,
}
case 'SET_EDITING_DATA_POINT':
return {
...state,
editingDataPoint: action.payload,
}
case 'SET_EDITING_SECTION':
return {
...state,
editingSection: action.payload,
}
case 'SET_PREVIEW_LANGUAGE':
return {
...state,
previewLanguage: action.payload,
}
case 'SET_PREVIEW_FORMAT':
return {
...state,
previewFormat: action.payload,
}
case 'RESET_STATE':
return initialState
default:
return state
}
}
// =============================================================================
// CONTEXT
// =============================================================================
interface EinwilligungenContextValue {
state: EinwilligungenState
dispatch: Dispatch<EinwilligungenAction>
// Computed Values
allDataPoints: DataPoint[]
selectedDataPointsData: DataPoint[]
dataPointsByCategory: Record<DataPointCategory, DataPoint[]>
categoryStats: Record<DataPointCategory, number>
riskStats: Record<RiskLevel, number>
legalBasisStats: Record<LegalBasis, number>
// Actions
initializeCatalog: (tenantId: string) => void
loadCatalog: (tenantId: string) => Promise<void>
saveCatalog: () => Promise<void>
toggleDataPoint: (id: string) => void
addCustomDataPoint: (dataPoint: DataPoint) => void
updateDataPoint: (id: string, data: Partial<DataPoint>) => void
deleteCustomDataPoint: (id: string) => void
setActiveTab: (tab: EinwilligungenTab) => void
setPreviewLanguage: (language: SupportedLanguage) => void
setPreviewFormat: (format: ExportFormat) => void
setCompanyInfo: (info: CompanyInfo) => void
generatePrivacyPolicy: () => Promise<void>
generateCookieBannerConfig: () => void
}
const EinwilligungenContext = createContext<EinwilligungenContextValue | null>(null)
// =============================================================================
// PROVIDER
// =============================================================================
interface EinwilligungenProviderProps {
children: ReactNode
tenantId?: string
}
export function EinwilligungenProvider({ children, tenantId }: EinwilligungenProviderProps) {
const [state, dispatch] = useReducer(einwilligungenReducer, initialState)
// ---------------------------------------------------------------------------
// COMPUTED VALUES
// ---------------------------------------------------------------------------
const allDataPoints = useMemo(() => {
if (!state.catalog) return PREDEFINED_DATA_POINTS
return [...state.catalog.dataPoints, ...state.catalog.customDataPoints]
}, [state.catalog])
const selectedDataPointsData = useMemo(() => {
return allDataPoints.filter((dp) => state.selectedDataPoints.includes(dp.id))
}, [allDataPoints, state.selectedDataPoints])
const dataPointsByCategory = useMemo(() => {
const result: Partial<Record<DataPointCategory, DataPoint[]>> = {}
// 18 Kategorien (A-R)
const categories: DataPointCategory[] = [
'MASTER_DATA', // A
'CONTACT_DATA', // B
'AUTHENTICATION', // C
'CONSENT', // D
'COMMUNICATION', // E
'PAYMENT', // F
'USAGE_DATA', // G
'LOCATION', // H
'DEVICE_DATA', // I
'MARKETING', // J
'ANALYTICS', // K
'SOCIAL_MEDIA', // L
'HEALTH_DATA', // M - Art. 9 DSGVO
'EMPLOYEE_DATA', // N - BDSG § 26
'CONTRACT_DATA', // O
'LOG_DATA', // P
'AI_DATA', // Q - AI Act
'SECURITY', // R
]
for (const cat of categories) {
result[cat] = selectedDataPointsData.filter((dp) => dp.category === cat)
}
return result as Record<DataPointCategory, DataPoint[]>
}, [selectedDataPointsData])
const categoryStats = useMemo(() => {
const counts: Partial<Record<DataPointCategory, number>> = {}
for (const dp of selectedDataPointsData) {
counts[dp.category] = (counts[dp.category] || 0) + 1
}
return counts as Record<DataPointCategory, number>
}, [selectedDataPointsData])
const riskStats = useMemo(() => {
const counts: Record<RiskLevel, number> = { LOW: 0, MEDIUM: 0, HIGH: 0 }
for (const dp of selectedDataPointsData) {
counts[dp.riskLevel]++
}
return counts
}, [selectedDataPointsData])
const legalBasisStats = useMemo(() => {
// Alle 7 Rechtsgrundlagen
const counts: Record<LegalBasis, number> = {
CONTRACT: 0,
CONSENT: 0,
EXPLICIT_CONSENT: 0,
LEGITIMATE_INTEREST: 0,
LEGAL_OBLIGATION: 0,
VITAL_INTERESTS: 0,
PUBLIC_INTEREST: 0,
}
for (const dp of selectedDataPointsData) {
counts[dp.legalBasis]++
}
return counts
}, [selectedDataPointsData])
// ---------------------------------------------------------------------------
// ACTIONS
// ---------------------------------------------------------------------------
const initializeCatalog = useCallback(
(tid: string) => {
const catalog = createDefaultCatalog(tid)
dispatch({ type: 'SET_CATALOG', payload: catalog })
},
[dispatch]
)
const loadCatalog = useCallback(
async (tid: string) => {
dispatch({ type: 'SET_LOADING', payload: true })
dispatch({ type: 'SET_ERROR', payload: null })
try {
const response = await fetch(`/api/sdk/v1/einwilligungen/catalog`, {
headers: {
'X-Tenant-ID': tid,
},
})
if (response.ok) {
const data = await response.json()
dispatch({ type: 'SET_CATALOG', payload: data.catalog })
if (data.companyInfo) {
dispatch({ type: 'SET_COMPANY_INFO', payload: data.companyInfo })
}
if (data.cookieBannerConfig) {
dispatch({ type: 'SET_COOKIE_BANNER_CONFIG', payload: data.cookieBannerConfig })
}
} else if (response.status === 404) {
// Katalog existiert noch nicht - erstelle Default
initializeCatalog(tid)
} else {
throw new Error('Failed to load catalog')
}
} catch (error) {
console.error('Error loading catalog:', error)
dispatch({ type: 'SET_ERROR', payload: 'Fehler beim Laden des Katalogs' })
// Fallback zu Default
initializeCatalog(tid)
} finally {
dispatch({ type: 'SET_LOADING', payload: false })
}
},
[dispatch, initializeCatalog]
)
const saveCatalog = useCallback(async () => {
if (!state.catalog) return
dispatch({ type: 'SET_SAVING', payload: true })
dispatch({ type: 'SET_ERROR', payload: null })
try {
const response = await fetch(`/api/sdk/v1/einwilligungen/catalog`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Tenant-ID': state.catalog.tenantId,
},
body: JSON.stringify({
catalog: state.catalog,
companyInfo: state.companyInfo,
cookieBannerConfig: state.cookieBannerConfig,
}),
})
if (!response.ok) {
throw new Error('Failed to save catalog')
}
} catch (error) {
console.error('Error saving catalog:', error)
dispatch({ type: 'SET_ERROR', payload: 'Fehler beim Speichern des Katalogs' })
} finally {
dispatch({ type: 'SET_SAVING', payload: false })
}
}, [state.catalog, state.companyInfo, state.cookieBannerConfig, dispatch])
const toggleDataPoint = useCallback(
(id: string) => {
dispatch({ type: 'TOGGLE_DATA_POINT', payload: id })
},
[dispatch]
)
const addCustomDataPoint = useCallback(
(dataPoint: DataPoint) => {
dispatch({ type: 'ADD_CUSTOM_DATA_POINT', payload: { ...dataPoint, isCustom: true } })
},
[dispatch]
)
const updateDataPoint = useCallback(
(id: string, data: Partial<DataPoint>) => {
dispatch({ type: 'UPDATE_DATA_POINT', payload: { id, data } })
},
[dispatch]
)
const deleteCustomDataPoint = useCallback(
(id: string) => {
dispatch({ type: 'DELETE_CUSTOM_DATA_POINT', payload: id })
},
[dispatch]
)
const setActiveTab = useCallback(
(tab: EinwilligungenTab) => {
dispatch({ type: 'SET_ACTIVE_TAB', payload: tab })
},
[dispatch]
)
const setPreviewLanguage = useCallback(
(language: SupportedLanguage) => {
dispatch({ type: 'SET_PREVIEW_LANGUAGE', payload: language })
},
[dispatch]
)
const setPreviewFormat = useCallback(
(format: ExportFormat) => {
dispatch({ type: 'SET_PREVIEW_FORMAT', payload: format })
},
[dispatch]
)
const setCompanyInfo = useCallback(
(info: CompanyInfo) => {
dispatch({ type: 'SET_COMPANY_INFO', payload: info })
},
[dispatch]
)
const generatePrivacyPolicy = useCallback(async () => {
if (!state.catalog || !state.companyInfo) {
dispatch({ type: 'SET_ERROR', payload: 'Bitte zuerst Firmendaten eingeben' })
return
}
dispatch({ type: 'SET_LOADING', payload: true })
dispatch({ type: 'SET_ERROR', payload: null })
try {
const response = await fetch(`/api/sdk/v1/einwilligungen/privacy-policy/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Tenant-ID': state.catalog.tenantId,
},
body: JSON.stringify({
dataPointIds: state.selectedDataPoints,
companyInfo: state.companyInfo,
language: state.previewLanguage,
format: state.previewFormat,
}),
})
if (response.ok) {
const policy = await response.json()
dispatch({ type: 'SET_PRIVACY_POLICY', payload: policy })
} else {
throw new Error('Failed to generate privacy policy')
}
} catch (error) {
console.error('Error generating privacy policy:', error)
dispatch({ type: 'SET_ERROR', payload: 'Fehler bei der Generierung der Datenschutzerklaerung' })
} finally {
dispatch({ type: 'SET_LOADING', payload: false })
}
}, [
state.catalog,
state.companyInfo,
state.selectedDataPoints,
state.previewLanguage,
state.previewFormat,
dispatch,
])
const generateCookieBannerConfig = useCallback(() => {
if (!state.catalog) return
const config: CookieBannerConfig = {
id: `cookie-banner-${state.catalog.tenantId}`,
tenantId: state.catalog.tenantId,
categories: DEFAULT_COOKIE_CATEGORIES.map((cat) => ({
...cat,
// Filtere nur die ausgewaehlten Datenpunkte
dataPointIds: cat.dataPointIds.filter((id) => state.selectedDataPoints.includes(id)),
})),
styling: {
position: 'BOTTOM',
theme: 'LIGHT',
primaryColor: '#6366f1',
borderRadius: 12,
},
texts: {
title: { de: 'Cookie-Einstellungen', en: 'Cookie Settings' },
description: {
de: 'Wir verwenden Cookies, um Ihnen die bestmoegliche Nutzung unserer Website zu ermoeglichen.',
en: 'We use cookies to provide you with the best possible experience on our website.',
},
acceptAll: { de: 'Alle akzeptieren', en: 'Accept All' },
rejectAll: { de: 'Alle ablehnen', en: 'Reject All' },
customize: { de: 'Anpassen', en: 'Customize' },
save: { de: 'Auswahl speichern', en: 'Save Selection' },
privacyPolicyLink: { de: 'Datenschutzerklaerung', en: 'Privacy Policy' },
},
updatedAt: new Date(),
}
dispatch({ type: 'SET_COOKIE_BANNER_CONFIG', payload: config })
}, [state.catalog, state.selectedDataPoints, dispatch])
// ---------------------------------------------------------------------------
// CONTEXT VALUE
// ---------------------------------------------------------------------------
const value: EinwilligungenContextValue = {
state,
dispatch,
// Computed Values
allDataPoints,
selectedDataPointsData,
dataPointsByCategory,
categoryStats,
riskStats,
legalBasisStats,
// Actions
initializeCatalog,
loadCatalog,
saveCatalog,
toggleDataPoint,
addCustomDataPoint,
updateDataPoint,
deleteCustomDataPoint,
setActiveTab,
setPreviewLanguage,
setPreviewFormat,
setCompanyInfo,
generatePrivacyPolicy,
generateCookieBannerConfig,
}
return (
<EinwilligungenContext.Provider value={value}>{children}</EinwilligungenContext.Provider>
)
}
// =============================================================================
// HOOK
// =============================================================================
export function useEinwilligungen(): EinwilligungenContextValue {
const context = useContext(EinwilligungenContext)
if (!context) {
throw new Error('useEinwilligungen must be used within EinwilligungenProvider')
}
return context
}
// =============================================================================
// EXPORTS
// =============================================================================
export { initialState, einwilligungenReducer }

View File

@@ -0,0 +1,493 @@
// =============================================================================
// Privacy Policy DOCX Export
// Export Datenschutzerklaerung to Microsoft Word format
// =============================================================================
import {
GeneratedPrivacyPolicy,
PrivacyPolicySection,
CompanyInfo,
SupportedLanguage,
DataPoint,
CATEGORY_METADATA,
RETENTION_PERIOD_INFO,
} from '../types'
// =============================================================================
// TYPES
// =============================================================================
export interface DOCXExportOptions {
language: SupportedLanguage
includeTableOfContents: boolean
includeDataPointList: boolean
companyLogo?: string
primaryColor?: string
}
const DEFAULT_OPTIONS: DOCXExportOptions = {
language: 'de',
includeTableOfContents: true,
includeDataPointList: true,
primaryColor: '#6366f1',
}
// =============================================================================
// DOCX CONTENT STRUCTURE
// =============================================================================
export interface DocxParagraph {
type: 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'bullet' | 'title'
content: string
style?: Record<string, string>
}
export interface DocxTableRow {
cells: string[]
isHeader?: boolean
}
export interface DocxTable {
type: 'table'
headers: string[]
rows: DocxTableRow[]
}
export type DocxElement = DocxParagraph | DocxTable
// =============================================================================
// DOCX CONTENT GENERATION
// =============================================================================
/**
* Generate DOCX content structure for Privacy Policy
*/
export function generateDOCXContent(
policy: GeneratedPrivacyPolicy,
companyInfo: CompanyInfo,
dataPoints: DataPoint[],
options: Partial<DOCXExportOptions> = {}
): DocxElement[] {
const opts = { ...DEFAULT_OPTIONS, ...options }
const elements: DocxElement[] = []
const lang = opts.language
// Title
elements.push({
type: 'title',
content: lang === 'de' ? 'Datenschutzerklaerung' : 'Privacy Policy',
})
elements.push({
type: 'paragraph',
content: lang === 'de'
? 'gemaess Art. 13, 14 DSGVO'
: 'according to Art. 13, 14 GDPR',
style: { fontStyle: 'italic', textAlign: 'center' },
})
// Company Info
elements.push({
type: 'heading2',
content: lang === 'de' ? 'Verantwortlicher' : 'Controller',
})
elements.push({
type: 'paragraph',
content: companyInfo.name,
style: { fontWeight: 'bold' },
})
elements.push({
type: 'paragraph',
content: `${companyInfo.address}`,
})
elements.push({
type: 'paragraph',
content: `${companyInfo.postalCode} ${companyInfo.city}`,
})
if (companyInfo.country) {
elements.push({
type: 'paragraph',
content: companyInfo.country,
})
}
elements.push({
type: 'paragraph',
content: `${lang === 'de' ? 'E-Mail' : 'Email'}: ${companyInfo.email}`,
})
if (companyInfo.phone) {
elements.push({
type: 'paragraph',
content: `${lang === 'de' ? 'Telefon' : 'Phone'}: ${companyInfo.phone}`,
})
}
if (companyInfo.website) {
elements.push({
type: 'paragraph',
content: `Website: ${companyInfo.website}`,
})
}
// DPO Info
if (companyInfo.dpoName || companyInfo.dpoEmail) {
elements.push({
type: 'heading3',
content: lang === 'de' ? 'Datenschutzbeauftragter' : 'Data Protection Officer',
})
if (companyInfo.dpoName) {
elements.push({
type: 'paragraph',
content: companyInfo.dpoName,
})
}
if (companyInfo.dpoEmail) {
elements.push({
type: 'paragraph',
content: `${lang === 'de' ? 'E-Mail' : 'Email'}: ${companyInfo.dpoEmail}`,
})
}
if (companyInfo.dpoPhone) {
elements.push({
type: 'paragraph',
content: `${lang === 'de' ? 'Telefon' : 'Phone'}: ${companyInfo.dpoPhone}`,
})
}
}
// Document metadata
elements.push({
type: 'paragraph',
content: lang === 'de'
? `Stand: ${new Date(policy.generatedAt).toLocaleDateString('de-DE')}`
: `Date: ${new Date(policy.generatedAt).toLocaleDateString('en-US')}`,
style: { marginTop: '20px' },
})
elements.push({
type: 'paragraph',
content: `Version: ${policy.version}`,
})
// Table of Contents
if (opts.includeTableOfContents) {
elements.push({
type: 'heading2',
content: lang === 'de' ? 'Inhaltsverzeichnis' : 'Table of Contents',
})
policy.sections.forEach((section, idx) => {
elements.push({
type: 'bullet',
content: `${idx + 1}. ${section.title[lang]}`,
})
})
if (opts.includeDataPointList) {
elements.push({
type: 'bullet',
content: lang === 'de' ? 'Anhang: Datenpunktkatalog' : 'Appendix: Data Point Catalog',
})
}
}
// Privacy Policy Sections
policy.sections.forEach((section, idx) => {
elements.push({
type: 'heading1',
content: `${idx + 1}. ${section.title[lang]}`,
})
// Parse content
const content = section.content[lang]
const paragraphs = content.split('\n\n')
for (const para of paragraphs) {
if (para.startsWith('- ')) {
// List items
const items = para.split('\n').filter(l => l.startsWith('- '))
for (const item of items) {
elements.push({
type: 'bullet',
content: item.substring(2),
})
}
} else if (para.startsWith('### ')) {
elements.push({
type: 'heading3',
content: para.substring(4),
})
} else if (para.startsWith('## ')) {
elements.push({
type: 'heading2',
content: para.substring(3),
})
} else if (para.trim()) {
elements.push({
type: 'paragraph',
content: para.replace(/\*\*(.*?)\*\*/g, '$1'),
})
}
}
})
// Data Point Catalog Appendix
if (opts.includeDataPointList && dataPoints.length > 0) {
elements.push({
type: 'heading1',
content: lang === 'de' ? 'Anhang: Datenpunktkatalog' : 'Appendix: Data Point Catalog',
})
elements.push({
type: 'paragraph',
content: lang === 'de'
? 'Die folgende Tabelle zeigt alle verarbeiteten personenbezogenen Daten:'
: 'The following table shows all processed personal data:',
})
// Group by category
const categories = [...new Set(dataPoints.map(dp => dp.category))]
for (const category of categories) {
const categoryDPs = dataPoints.filter(dp => dp.category === category)
const categoryMeta = CATEGORY_METADATA[category]
elements.push({
type: 'heading3',
content: `${categoryMeta.code}. ${categoryMeta.name[lang]}`,
})
elements.push({
type: 'table',
headers: lang === 'de'
? ['Code', 'Datenpunkt', 'Rechtsgrundlage', 'Loeschfrist']
: ['Code', 'Data Point', 'Legal Basis', 'Retention'],
rows: categoryDPs.map(dp => ({
cells: [
dp.code,
dp.name[lang],
formatLegalBasis(dp.legalBasis, lang),
RETENTION_PERIOD_INFO[dp.retentionPeriod]?.label[lang] || dp.retentionPeriod,
],
})),
})
}
}
// Footer
elements.push({
type: 'paragraph',
content: lang === 'de'
? `Dieses Dokument wurde automatisch generiert mit dem Datenschutzerklaerung-Generator am ${new Date().toLocaleDateString('de-DE')}.`
: `This document was automatically generated with the Privacy Policy Generator on ${new Date().toLocaleDateString('en-US')}.`,
style: { fontStyle: 'italic', fontSize: '9pt' },
})
return elements
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
function formatLegalBasis(basis: string, language: SupportedLanguage): string {
const bases: Record<string, Record<SupportedLanguage, string>> = {
CONTRACT: { de: 'Vertrag (Art. 6 Abs. 1 lit. b)', en: 'Contract (Art. 6(1)(b))' },
CONSENT: { de: 'Einwilligung (Art. 6 Abs. 1 lit. a)', en: 'Consent (Art. 6(1)(a))' },
LEGITIMATE_INTEREST: { de: 'Ber. Interesse (Art. 6 Abs. 1 lit. f)', en: 'Legitimate Interest (Art. 6(1)(f))' },
LEGAL_OBLIGATION: { de: 'Rechtspflicht (Art. 6 Abs. 1 lit. c)', en: 'Legal Obligation (Art. 6(1)(c))' },
}
return bases[basis]?.[language] || basis
}
// =============================================================================
// DOCX BLOB GENERATION
// =============================================================================
/**
* Generate a DOCX file as a Blob
* This generates HTML that Word can open
*/
export async function generateDOCXBlob(
policy: GeneratedPrivacyPolicy,
companyInfo: CompanyInfo,
dataPoints: DataPoint[],
options: Partial<DOCXExportOptions> = {}
): Promise<Blob> {
const content = generateDOCXContent(policy, companyInfo, dataPoints, options)
const opts = { ...DEFAULT_OPTIONS, ...options }
const html = generateHTMLFromContent(content, opts)
return new Blob([html], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
}
function generateHTMLFromContent(
content: DocxElement[],
options: DOCXExportOptions
): string {
let html = `
<!DOCTYPE html>
<html lang="${options.language}">
<head>
<meta charset="utf-8">
<title>${options.language === 'de' ? 'Datenschutzerklaerung' : 'Privacy Policy'}</title>
<style>
body {
font-family: Calibri, Arial, sans-serif;
font-size: 11pt;
line-height: 1.5;
color: #1e293b;
max-width: 800px;
margin: 0 auto;
padding: 40px;
}
h1 {
font-size: 18pt;
color: ${options.primaryColor};
border-bottom: 1px solid ${options.primaryColor};
padding-bottom: 8px;
margin-top: 24pt;
}
h2 {
font-size: 14pt;
color: ${options.primaryColor};
margin-top: 18pt;
}
h3 {
font-size: 12pt;
color: #334155;
margin-top: 14pt;
}
.title {
font-size: 24pt;
color: ${options.primaryColor};
text-align: center;
font-weight: bold;
margin-bottom: 12pt;
}
p {
margin: 8pt 0;
text-align: justify;
}
table {
width: 100%;
border-collapse: collapse;
margin: 12pt 0;
font-size: 10pt;
}
th, td {
border: 1px solid #cbd5e1;
padding: 6pt 10pt;
text-align: left;
}
th {
background-color: ${options.primaryColor};
color: white;
font-weight: 600;
}
tr:nth-child(even) {
background-color: #f8fafc;
}
ul {
margin: 8pt 0;
padding-left: 20pt;
}
li {
margin: 4pt 0;
}
</style>
</head>
<body>
`
for (const element of content) {
if (element.type === 'table') {
html += '<table>\n<thead><tr>\n'
for (const header of element.headers) {
html += ` <th>${escapeHtml(header)}</th>\n`
}
html += '</tr></thead>\n<tbody>\n'
for (const row of element.rows) {
html += '<tr>\n'
for (const cell of row.cells) {
html += ` <td>${escapeHtml(cell)}</td>\n`
}
html += '</tr>\n'
}
html += '</tbody></table>\n'
} else {
const tag = getHtmlTag(element.type)
const className = element.type === 'title' ? ' class="title"' : ''
const processedContent = escapeHtml(element.content)
html += `<${tag}${className}>${processedContent}</${tag}>\n`
}
}
html += '</body></html>'
return html
}
function getHtmlTag(type: string): string {
switch (type) {
case 'title':
return 'div'
case 'heading1':
return 'h1'
case 'heading2':
return 'h2'
case 'heading3':
return 'h3'
case 'bullet':
return 'li'
default:
return 'p'
}
}
function escapeHtml(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
}
// =============================================================================
// FILENAME GENERATION
// =============================================================================
/**
* Generate a filename for the DOCX export
*/
export function generateDOCXFilename(
companyInfo: CompanyInfo,
language: SupportedLanguage = 'de'
): string {
const companyName = companyInfo.name.replace(/[^a-zA-Z0-9]/g, '-') || 'unknown'
const date = new Date().toISOString().split('T')[0]
const prefix = language === 'de' ? 'Datenschutzerklaerung' : 'Privacy-Policy'
return `${prefix}-${companyName}-${date}.doc`
}

View File

@@ -0,0 +1,8 @@
/**
* Einwilligungen Export Module
*
* PDF and DOCX export functionality for Privacy Policy documents.
*/
export * from './pdf'
export * from './docx'

View File

@@ -0,0 +1,505 @@
// =============================================================================
// Privacy Policy PDF Export
// Export Datenschutzerklaerung to PDF format
// =============================================================================
import {
GeneratedPrivacyPolicy,
PrivacyPolicySection,
CompanyInfo,
SupportedLanguage,
DataPoint,
CATEGORY_METADATA,
RETENTION_PERIOD_INFO,
} from '../types'
// =============================================================================
// TYPES
// =============================================================================
export interface PDFExportOptions {
language: SupportedLanguage
includeTableOfContents: boolean
includeDataPointList: boolean
companyLogo?: string
primaryColor?: string
pageSize?: 'A4' | 'LETTER'
orientation?: 'portrait' | 'landscape'
fontSize?: number
}
const DEFAULT_OPTIONS: PDFExportOptions = {
language: 'de',
includeTableOfContents: true,
includeDataPointList: true,
primaryColor: '#6366f1',
pageSize: 'A4',
orientation: 'portrait',
fontSize: 11,
}
// =============================================================================
// PDF CONTENT STRUCTURE
// =============================================================================
export interface PDFSection {
type: 'title' | 'heading' | 'subheading' | 'paragraph' | 'table' | 'list' | 'pagebreak'
content?: string
items?: string[]
table?: {
headers: string[]
rows: string[][]
}
style?: {
color?: string
fontSize?: number
bold?: boolean
italic?: boolean
align?: 'left' | 'center' | 'right'
}
}
// =============================================================================
// PDF CONTENT GENERATION
// =============================================================================
/**
* Generate PDF content structure for Privacy Policy
*/
export function generatePDFContent(
policy: GeneratedPrivacyPolicy,
companyInfo: CompanyInfo,
dataPoints: DataPoint[],
options: Partial<PDFExportOptions> = {}
): PDFSection[] {
const opts = { ...DEFAULT_OPTIONS, ...options }
const sections: PDFSection[] = []
const lang = opts.language
// Title page
sections.push({
type: 'title',
content: lang === 'de' ? 'Datenschutzerklaerung' : 'Privacy Policy',
style: { color: opts.primaryColor, fontSize: 28, bold: true, align: 'center' },
})
sections.push({
type: 'paragraph',
content: lang === 'de'
? 'gemaess Art. 13, 14 DSGVO'
: 'according to Art. 13, 14 GDPR',
style: { fontSize: 14, align: 'center', italic: true },
})
// Company information
sections.push({
type: 'paragraph',
content: companyInfo.name,
style: { fontSize: 16, bold: true, align: 'center' },
})
sections.push({
type: 'paragraph',
content: `${companyInfo.address}, ${companyInfo.postalCode} ${companyInfo.city}`,
style: { align: 'center' },
})
sections.push({
type: 'paragraph',
content: `${lang === 'de' ? 'Stand' : 'Date'}: ${new Date(policy.generatedAt).toLocaleDateString(lang === 'de' ? 'de-DE' : 'en-US')}`,
style: { align: 'center' },
})
sections.push({
type: 'paragraph',
content: `Version: ${policy.version}`,
style: { align: 'center', fontSize: 10 },
})
sections.push({ type: 'pagebreak' })
// Table of Contents
if (opts.includeTableOfContents) {
sections.push({
type: 'heading',
content: lang === 'de' ? 'Inhaltsverzeichnis' : 'Table of Contents',
style: { color: opts.primaryColor },
})
const tocItems = policy.sections.map((section, idx) =>
`${idx + 1}. ${section.title[lang]}`
)
if (opts.includeDataPointList) {
tocItems.push(lang === 'de' ? 'Anhang: Datenpunktkatalog' : 'Appendix: Data Point Catalog')
}
sections.push({
type: 'list',
items: tocItems,
})
sections.push({ type: 'pagebreak' })
}
// Privacy Policy Sections
policy.sections.forEach((section, idx) => {
sections.push({
type: 'heading',
content: `${idx + 1}. ${section.title[lang]}`,
style: { color: opts.primaryColor },
})
// Convert markdown-like content to paragraphs
const content = section.content[lang]
const paragraphs = content.split('\n\n')
for (const para of paragraphs) {
if (para.startsWith('- ')) {
// List items
const items = para.split('\n').filter(l => l.startsWith('- ')).map(l => l.substring(2))
sections.push({
type: 'list',
items,
})
} else if (para.startsWith('### ')) {
sections.push({
type: 'subheading',
content: para.substring(4),
})
} else if (para.startsWith('## ')) {
sections.push({
type: 'subheading',
content: para.substring(3),
style: { bold: true },
})
} else if (para.trim()) {
sections.push({
type: 'paragraph',
content: para.replace(/\*\*(.*?)\*\*/g, '$1'), // Remove markdown bold for plain text
})
}
}
// Add related data points if this section has them
if (section.dataPointIds.length > 0 && opts.includeDataPointList) {
const relatedDPs = dataPoints.filter(dp => section.dataPointIds.includes(dp.id))
if (relatedDPs.length > 0) {
sections.push({
type: 'paragraph',
content: lang === 'de'
? `Betroffene Datenkategorien: ${relatedDPs.map(dp => dp.name[lang]).join(', ')}`
: `Affected data categories: ${relatedDPs.map(dp => dp.name[lang]).join(', ')}`,
style: { italic: true, fontSize: 10 },
})
}
}
})
// Data Point Catalog Appendix
if (opts.includeDataPointList && dataPoints.length > 0) {
sections.push({ type: 'pagebreak' })
sections.push({
type: 'heading',
content: lang === 'de' ? 'Anhang: Datenpunktkatalog' : 'Appendix: Data Point Catalog',
style: { color: opts.primaryColor },
})
sections.push({
type: 'paragraph',
content: lang === 'de'
? 'Die folgende Tabelle zeigt alle verarbeiteten personenbezogenen Daten:'
: 'The following table shows all processed personal data:',
})
// Group by category
const categories = [...new Set(dataPoints.map(dp => dp.category))]
for (const category of categories) {
const categoryDPs = dataPoints.filter(dp => dp.category === category)
const categoryMeta = CATEGORY_METADATA[category]
sections.push({
type: 'subheading',
content: `${categoryMeta.code}. ${categoryMeta.name[lang]}`,
})
sections.push({
type: 'table',
table: {
headers: lang === 'de'
? ['Code', 'Datenpunkt', 'Zweck', 'Loeschfrist']
: ['Code', 'Data Point', 'Purpose', 'Retention'],
rows: categoryDPs.map(dp => [
dp.code,
dp.name[lang],
dp.purpose[lang].substring(0, 50) + (dp.purpose[lang].length > 50 ? '...' : ''),
RETENTION_PERIOD_INFO[dp.retentionPeriod]?.label[lang] || dp.retentionPeriod,
]),
},
})
}
}
// Footer
sections.push({
type: 'paragraph',
content: lang === 'de'
? `Generiert am ${new Date().toLocaleDateString('de-DE')} mit dem Datenschutzerklaerung-Generator`
: `Generated on ${new Date().toLocaleDateString('en-US')} with the Privacy Policy Generator`,
style: { italic: true, align: 'center', fontSize: 9 },
})
return sections
}
// =============================================================================
// PDF BLOB GENERATION
// =============================================================================
/**
* Generate a PDF file as a Blob
* This generates HTML that can be printed to PDF or used with a PDF library
*/
export async function generatePDFBlob(
policy: GeneratedPrivacyPolicy,
companyInfo: CompanyInfo,
dataPoints: DataPoint[],
options: Partial<PDFExportOptions> = {}
): Promise<Blob> {
const content = generatePDFContent(policy, companyInfo, dataPoints, options)
const opts = { ...DEFAULT_OPTIONS, ...options }
// Generate HTML for PDF conversion
const html = generateHTMLFromContent(content, opts)
return new Blob([html], { type: 'text/html' })
}
/**
* Generate printable HTML from PDF content
*/
function generateHTMLFromContent(
content: PDFSection[],
options: PDFExportOptions
): string {
const pageWidth = options.pageSize === 'A4' ? '210mm' : '8.5in'
const pageHeight = options.pageSize === 'A4' ? '297mm' : '11in'
let html = `
<!DOCTYPE html>
<html lang="${options.language}">
<head>
<meta charset="utf-8">
<title>${options.language === 'de' ? 'Datenschutzerklaerung' : 'Privacy Policy'}</title>
<style>
@page {
size: ${pageWidth} ${pageHeight};
margin: 20mm;
}
body {
font-family: 'Segoe UI', Calibri, Arial, sans-serif;
font-size: ${options.fontSize}pt;
line-height: 1.6;
color: #1e293b;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
font-size: 24pt;
color: ${options.primaryColor};
border-bottom: 2px solid ${options.primaryColor};
padding-bottom: 10px;
margin-top: 30px;
}
h2 {
font-size: 16pt;
color: ${options.primaryColor};
margin-top: 24px;
}
h3 {
font-size: 13pt;
color: #334155;
margin-top: 18px;
}
p {
margin: 12px 0;
text-align: justify;
}
.title {
font-size: 28pt;
text-align: center;
color: ${options.primaryColor};
font-weight: bold;
margin-bottom: 10px;
}
.subtitle {
text-align: center;
font-style: italic;
color: #64748b;
}
.center {
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 10pt;
}
th, td {
border: 1px solid #e2e8f0;
padding: 8px 12px;
text-align: left;
}
th {
background-color: ${options.primaryColor};
color: white;
font-weight: 600;
}
tr:nth-child(even) {
background-color: #f8fafc;
}
ul {
margin: 12px 0;
padding-left: 24px;
}
li {
margin: 6px 0;
}
.pagebreak {
page-break-after: always;
}
.footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #e2e8f0;
font-size: 9pt;
color: #94a3b8;
text-align: center;
}
@media print {
body {
padding: 0;
}
.pagebreak {
page-break-after: always;
}
}
</style>
</head>
<body>
`
for (const section of content) {
switch (section.type) {
case 'title':
html += `<div class="title" style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</div>\n`
break
case 'heading':
html += `<h1 style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</h1>\n`
break
case 'subheading':
html += `<h3 style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</h3>\n`
break
case 'paragraph':
const alignClass = section.style?.align === 'center' ? ' class="center"' : ''
html += `<p${alignClass} style="${getStyleString(section.style)}">${escapeHtml(section.content || '')}</p>\n`
break
case 'list':
html += '<ul>\n'
for (const item of section.items || []) {
html += ` <li>${escapeHtml(item)}</li>\n`
}
html += '</ul>\n'
break
case 'table':
if (section.table) {
html += '<table>\n<thead><tr>\n'
for (const header of section.table.headers) {
html += ` <th>${escapeHtml(header)}</th>\n`
}
html += '</tr></thead>\n<tbody>\n'
for (const row of section.table.rows) {
html += '<tr>\n'
for (const cell of row) {
html += ` <td>${escapeHtml(cell)}</td>\n`
}
html += '</tr>\n'
}
html += '</tbody></table>\n'
}
break
case 'pagebreak':
html += '<div class="pagebreak"></div>\n'
break
}
}
html += '</body></html>'
return html
}
function getStyleString(style?: PDFSection['style']): string {
if (!style) return ''
const parts: string[] = []
if (style.color) parts.push(`color: ${style.color}`)
if (style.fontSize) parts.push(`font-size: ${style.fontSize}pt`)
if (style.bold) parts.push('font-weight: bold')
if (style.italic) parts.push('font-style: italic')
if (style.align) parts.push(`text-align: ${style.align}`)
return parts.join('; ')
}
function escapeHtml(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
}
// =============================================================================
// FILENAME GENERATION
// =============================================================================
/**
* Generate a filename for the PDF export
*/
export function generatePDFFilename(
companyInfo: CompanyInfo,
language: SupportedLanguage = 'de'
): string {
const companyName = companyInfo.name.replace(/[^a-zA-Z0-9]/g, '-') || 'unknown'
const date = new Date().toISOString().split('T')[0]
const prefix = language === 'de' ? 'Datenschutzerklaerung' : 'Privacy-Policy'
return `${prefix}-${companyName}-${date}.html`
}

View File

@@ -0,0 +1,595 @@
/**
* Cookie Banner Generator
*
* Generiert Cookie-Banner Konfigurationen und Embed-Code aus dem Datenpunktkatalog.
* Die Cookie-Kategorien werden automatisch aus den Datenpunkten abgeleitet.
*/
import {
DataPoint,
CookieCategory,
CookieBannerCategory,
CookieBannerConfig,
CookieBannerStyling,
CookieBannerTexts,
CookieBannerEmbedCode,
CookieInfo,
LocalizedText,
SupportedLanguage,
} from '../types'
import { DEFAULT_COOKIE_CATEGORIES } from '../catalog/loader'
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
/**
* Holt den lokalisierten Text
*/
function t(text: LocalizedText, language: SupportedLanguage): string {
return text[language]
}
// =============================================================================
// COOKIE BANNER CONFIGURATION
// =============================================================================
/**
* Standard Cookie Banner Texte
*/
export const DEFAULT_COOKIE_BANNER_TEXTS: CookieBannerTexts = {
title: {
de: 'Cookie-Einstellungen',
en: 'Cookie Settings',
},
description: {
de: 'Wir verwenden Cookies, um Ihnen die bestmoegliche Nutzung unserer Website zu ermoeglichen. Einige Cookies sind technisch notwendig, waehrend andere uns helfen, Ihre Nutzererfahrung zu verbessern.',
en: 'We use cookies to provide you with the best possible experience on our website. Some cookies are technically necessary, while others help us improve your user experience.',
},
acceptAll: {
de: 'Alle akzeptieren',
en: 'Accept All',
},
rejectAll: {
de: 'Nur notwendige',
en: 'Essential Only',
},
customize: {
de: 'Einstellungen',
en: 'Customize',
},
save: {
de: 'Auswahl speichern',
en: 'Save Selection',
},
privacyPolicyLink: {
de: 'Mehr in unserer Datenschutzerklaerung',
en: 'More in our Privacy Policy',
},
}
/**
* Standard Styling fuer Cookie Banner
*/
export const DEFAULT_COOKIE_BANNER_STYLING: CookieBannerStyling = {
position: 'BOTTOM',
theme: 'LIGHT',
primaryColor: '#6366f1', // Indigo
secondaryColor: '#f1f5f9', // Slate-100
textColor: '#1e293b', // Slate-800
backgroundColor: '#ffffff',
borderRadius: 12,
maxWidth: 480,
}
// =============================================================================
// GENERATOR FUNCTIONS
// =============================================================================
/**
* Generiert Cookie-Banner Kategorien aus Datenpunkten
*/
export function generateCookieCategories(
dataPoints: DataPoint[]
): CookieBannerCategory[] {
// Filtere nur Datenpunkte mit Cookie-Kategorie
const cookieDataPoints = dataPoints.filter((dp) => dp.cookieCategory !== null)
// Erstelle die Kategorien basierend auf den Defaults
return DEFAULT_COOKIE_CATEGORIES.map((defaultCat) => {
// Filtere die Datenpunkte fuer diese Kategorie
const categoryDataPoints = cookieDataPoints.filter(
(dp) => dp.cookieCategory === defaultCat.id
)
// Erstelle Cookie-Infos aus den Datenpunkten
const cookies: CookieInfo[] = categoryDataPoints.map((dp) => ({
name: dp.code,
provider: 'First Party',
purpose: dp.purpose,
expiry: getExpiryFromRetention(dp.retentionPeriod),
type: 'FIRST_PARTY',
}))
return {
...defaultCat,
dataPointIds: categoryDataPoints.map((dp) => dp.id),
cookies,
}
}).filter((cat) => cat.dataPointIds.length > 0 || cat.isRequired)
}
/**
* Konvertiert Retention Period zu Cookie-Expiry String
*/
function getExpiryFromRetention(retention: string): string {
const mapping: Record<string, string> = {
'24_HOURS': '24 Stunden / 24 hours',
'30_DAYS': '30 Tage / 30 days',
'90_DAYS': '90 Tage / 90 days',
'12_MONTHS': '1 Jahr / 1 year',
'24_MONTHS': '2 Jahre / 2 years',
'36_MONTHS': '3 Jahre / 3 years',
'UNTIL_REVOCATION': 'Bis Widerruf / Until revocation',
'UNTIL_PURPOSE_FULFILLED': 'Session',
'UNTIL_ACCOUNT_DELETION': 'Bis Kontoschliessung / Until account deletion',
}
return mapping[retention] || 'Session'
}
/**
* Generiert die vollstaendige Cookie Banner Konfiguration
*/
export function generateCookieBannerConfig(
tenantId: string,
dataPoints: DataPoint[],
customTexts?: Partial<CookieBannerTexts>,
customStyling?: Partial<CookieBannerStyling>
): CookieBannerConfig {
const categories = generateCookieCategories(dataPoints)
return {
id: `cookie-banner-${tenantId}`,
tenantId,
categories,
styling: {
...DEFAULT_COOKIE_BANNER_STYLING,
...customStyling,
},
texts: {
...DEFAULT_COOKIE_BANNER_TEXTS,
...customTexts,
},
updatedAt: new Date(),
}
}
// =============================================================================
// EMBED CODE GENERATION
// =============================================================================
/**
* Generiert den Embed-Code fuer den Cookie Banner
*/
export function generateEmbedCode(
config: CookieBannerConfig,
privacyPolicyUrl: string = '/datenschutz'
): CookieBannerEmbedCode {
const css = generateCSS(config.styling)
const html = generateHTML(config, privacyPolicyUrl)
const js = generateJS(config)
const scriptTag = `<script src="/cookie-banner.js" data-tenant="${config.tenantId}"></script>`
return {
html,
css,
js,
scriptTag,
}
}
/**
* Generiert das CSS fuer den Cookie Banner
*/
function generateCSS(styling: CookieBannerStyling): string {
const positionStyles: Record<string, string> = {
BOTTOM: 'bottom: 0; left: 0; right: 0;',
TOP: 'top: 0; left: 0; right: 0;',
CENTER: 'top: 50%; left: 50%; transform: translate(-50%, -50%);',
}
const isDark = styling.theme === 'DARK'
const bgColor = isDark ? '#1e293b' : styling.backgroundColor || '#ffffff'
const textColor = isDark ? '#f1f5f9' : styling.textColor || '#1e293b'
const borderColor = isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)'
return `
/* Cookie Banner Styles */
.cookie-banner-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 9998;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.cookie-banner-overlay.active {
opacity: 1;
visibility: visible;
}
.cookie-banner {
position: fixed;
${positionStyles[styling.position]}
z-index: 9999;
background: ${bgColor};
color: ${textColor};
border-radius: ${styling.borderRadius || 12}px;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
padding: 24px;
max-width: ${styling.maxWidth}px;
margin: ${styling.position === 'CENTER' ? '0' : '16px'};
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
transform: translateY(100%);
opacity: 0;
transition: all 0.3s ease;
}
.cookie-banner.active {
transform: translateY(0);
opacity: 1;
}
.cookie-banner-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
}
.cookie-banner-description {
font-size: 14px;
line-height: 1.5;
margin-bottom: 16px;
opacity: 0.8;
}
.cookie-banner-buttons {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.cookie-banner-btn {
flex: 1;
min-width: 120px;
padding: 12px 20px;
border-radius: ${(styling.borderRadius || 12) / 2}px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
border: none;
}
.cookie-banner-btn-primary {
background: ${styling.primaryColor};
color: white;
}
.cookie-banner-btn-primary:hover {
filter: brightness(1.1);
}
.cookie-banner-btn-secondary {
background: ${styling.secondaryColor || borderColor};
color: ${textColor};
}
.cookie-banner-btn-secondary:hover {
filter: brightness(0.95);
}
.cookie-banner-link {
display: block;
margin-top: 16px;
font-size: 12px;
color: ${styling.primaryColor};
text-decoration: none;
}
.cookie-banner-link:hover {
text-decoration: underline;
}
/* Category Details */
.cookie-banner-details {
margin-top: 16px;
border-top: 1px solid ${borderColor};
padding-top: 16px;
display: none;
}
.cookie-banner-details.active {
display: block;
}
.cookie-banner-category {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid ${borderColor};
}
.cookie-banner-category:last-child {
border-bottom: none;
}
.cookie-banner-category-info {
flex: 1;
}
.cookie-banner-category-name {
font-weight: 500;
font-size: 14px;
}
.cookie-banner-category-desc {
font-size: 12px;
opacity: 0.7;
margin-top: 4px;
}
.cookie-banner-toggle {
position: relative;
width: 48px;
height: 28px;
background: ${borderColor};
border-radius: 14px;
cursor: pointer;
transition: all 0.2s ease;
}
.cookie-banner-toggle.active {
background: ${styling.primaryColor};
}
.cookie-banner-toggle.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.cookie-banner-toggle::after {
content: '';
position: absolute;
top: 4px;
left: 4px;
width: 20px;
height: 20px;
background: white;
border-radius: 50%;
transition: all 0.2s ease;
}
.cookie-banner-toggle.active::after {
left: 24px;
}
@media (max-width: 640px) {
.cookie-banner {
margin: 0;
border-radius: ${styling.position === 'CENTER' ? (styling.borderRadius || 12) : 0}px;
max-width: 100%;
}
.cookie-banner-buttons {
flex-direction: column;
}
.cookie-banner-btn {
width: 100%;
}
}
`.trim()
}
/**
* Generiert das HTML fuer den Cookie Banner
*/
function generateHTML(config: CookieBannerConfig, privacyPolicyUrl: string): string {
const categoriesHTML = config.categories
.map((cat) => {
const isRequired = cat.isRequired
return `
<div class="cookie-banner-category" data-category="${cat.id}">
<div class="cookie-banner-category-info">
<div class="cookie-banner-category-name">${cat.name.de}</div>
<div class="cookie-banner-category-desc">${cat.description.de}</div>
</div>
<div class="cookie-banner-toggle ${cat.defaultEnabled ? 'active' : ''} ${isRequired ? 'disabled' : ''}"
data-category="${cat.id}"
data-required="${isRequired}"></div>
</div>
`
})
.join('')
return `
<div class="cookie-banner-overlay" id="cookieBannerOverlay"></div>
<div class="cookie-banner" id="cookieBanner" role="dialog" aria-labelledby="cookieBannerTitle" aria-modal="true">
<div class="cookie-banner-title" id="cookieBannerTitle">${config.texts.title.de}</div>
<div class="cookie-banner-description">${config.texts.description.de}</div>
<div class="cookie-banner-buttons">
<button class="cookie-banner-btn cookie-banner-btn-secondary" id="cookieBannerReject">
${config.texts.rejectAll.de}
</button>
<button class="cookie-banner-btn cookie-banner-btn-secondary" id="cookieBannerCustomize">
${config.texts.customize.de}
</button>
<button class="cookie-banner-btn cookie-banner-btn-primary" id="cookieBannerAccept">
${config.texts.acceptAll.de}
</button>
</div>
<div class="cookie-banner-details" id="cookieBannerDetails">
${categoriesHTML}
<div class="cookie-banner-buttons" style="margin-top: 16px;">
<button class="cookie-banner-btn cookie-banner-btn-primary" id="cookieBannerSave">
${config.texts.save.de}
</button>
</div>
</div>
<a href="${privacyPolicyUrl}" class="cookie-banner-link" target="_blank">
${config.texts.privacyPolicyLink.de}
</a>
</div>
`.trim()
}
/**
* Generiert das JavaScript fuer den Cookie Banner
*/
function generateJS(config: CookieBannerConfig): string {
const categoryIds = config.categories.map((c) => c.id)
const requiredCategories = config.categories.filter((c) => c.isRequired).map((c) => c.id)
return `
(function() {
'use strict';
const COOKIE_NAME = 'cookie_consent';
const COOKIE_EXPIRY_DAYS = 365;
const CATEGORIES = ${JSON.stringify(categoryIds)};
const REQUIRED_CATEGORIES = ${JSON.stringify(requiredCategories)};
// Get consent from cookie
function getConsent() {
const cookie = document.cookie.split('; ').find(row => row.startsWith(COOKIE_NAME + '='));
if (!cookie) return null;
try {
return JSON.parse(decodeURIComponent(cookie.split('=')[1]));
} catch {
return null;
}
}
// Save consent to cookie
function saveConsent(consent) {
const date = new Date();
date.setTime(date.getTime() + (COOKIE_EXPIRY_DAYS * 24 * 60 * 60 * 1000));
document.cookie = COOKIE_NAME + '=' + encodeURIComponent(JSON.stringify(consent)) +
';expires=' + date.toUTCString() +
';path=/;SameSite=Lax';
// Dispatch event
window.dispatchEvent(new CustomEvent('cookieConsentUpdated', { detail: consent }));
}
// Check if category is consented
function hasConsent(category) {
const consent = getConsent();
if (!consent) return REQUIRED_CATEGORIES.includes(category);
return consent[category] === true;
}
// Initialize banner
function initBanner() {
const banner = document.getElementById('cookieBanner');
const overlay = document.getElementById('cookieBannerOverlay');
const details = document.getElementById('cookieBannerDetails');
if (!banner) return;
const consent = getConsent();
if (consent) {
// User has already consented
return;
}
// Show banner
setTimeout(() => {
banner.classList.add('active');
overlay.classList.add('active');
}, 500);
// Accept all
document.getElementById('cookieBannerAccept')?.addEventListener('click', () => {
const consent = {};
CATEGORIES.forEach(cat => consent[cat] = true);
saveConsent(consent);
closeBanner();
});
// Reject all (only essential)
document.getElementById('cookieBannerReject')?.addEventListener('click', () => {
const consent = {};
CATEGORIES.forEach(cat => consent[cat] = REQUIRED_CATEGORIES.includes(cat));
saveConsent(consent);
closeBanner();
});
// Customize
document.getElementById('cookieBannerCustomize')?.addEventListener('click', () => {
details.classList.toggle('active');
});
// Save selection
document.getElementById('cookieBannerSave')?.addEventListener('click', () => {
const consent = {};
CATEGORIES.forEach(cat => {
const toggle = document.querySelector('.cookie-banner-toggle[data-category="' + cat + '"]');
consent[cat] = toggle?.classList.contains('active') || REQUIRED_CATEGORIES.includes(cat);
});
saveConsent(consent);
closeBanner();
});
// Toggle handlers
document.querySelectorAll('.cookie-banner-toggle').forEach(toggle => {
if (toggle.dataset.required === 'true') return;
toggle.addEventListener('click', () => {
toggle.classList.toggle('active');
});
});
// Close on overlay click
overlay?.addEventListener('click', () => {
// Don't close - user must make a choice
});
}
function closeBanner() {
const banner = document.getElementById('cookieBanner');
const overlay = document.getElementById('cookieBannerOverlay');
banner?.classList.remove('active');
overlay?.classList.remove('active');
}
// Expose API
window.CookieConsent = {
getConsent,
saveConsent,
hasConsent,
show: () => {
document.getElementById('cookieBanner')?.classList.add('active');
document.getElementById('cookieBannerOverlay')?.classList.add('active');
},
hide: closeBanner
};
// Initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initBanner);
} else {
initBanner();
}
})();
`.trim()
}
// Note: All exports are defined inline with 'export const' and 'export function'

View File

@@ -0,0 +1,965 @@
/**
* Privacy Policy Generator
*
* Generiert Datenschutzerklaerungen (DSI) aus dem Datenpunktkatalog.
* Die DSI wird aus 9 Abschnitten generiert:
*
* 1. Verantwortlicher (companyInfo)
* 2. Erhobene Daten (dataPoints nach Kategorie)
* 3. Verarbeitungszwecke (dataPoints.purpose)
* 4. Rechtsgrundlagen (dataPoints.legalBasis)
* 5. Empfaenger/Dritte (dataPoints.thirdPartyRecipients)
* 6. Speicherdauer (retentionMatrix)
* 7. Betroffenenrechte (statischer Text + Links)
* 8. Cookies (cookieCategory-basiert)
* 9. Aenderungen (statischer Text + Versionierung)
*/
import {
DataPoint,
DataPointCategory,
CompanyInfo,
PrivacyPolicySection,
GeneratedPrivacyPolicy,
SupportedLanguage,
ExportFormat,
LocalizedText,
RetentionMatrixEntry,
LegalBasis,
CATEGORY_METADATA,
LEGAL_BASIS_INFO,
RETENTION_PERIOD_INFO,
ARTICLE_9_WARNING,
} from '../types'
import { RETENTION_MATRIX } from '../catalog/loader'
// =============================================================================
// KONSTANTEN - 18 Kategorien in der richtigen Reihenfolge
// =============================================================================
const ALL_CATEGORIES: DataPointCategory[] = [
'MASTER_DATA', // A
'CONTACT_DATA', // B
'AUTHENTICATION', // C
'CONSENT', // D
'COMMUNICATION', // E
'PAYMENT', // F
'USAGE_DATA', // G
'LOCATION', // H
'DEVICE_DATA', // I
'MARKETING', // J
'ANALYTICS', // K
'SOCIAL_MEDIA', // L
'HEALTH_DATA', // M - Art. 9 DSGVO
'EMPLOYEE_DATA', // N - BDSG § 26
'CONTRACT_DATA', // O
'LOG_DATA', // P
'AI_DATA', // Q - AI Act
'SECURITY', // R
]
// Alle Rechtsgrundlagen in der richtigen Reihenfolge
const ALL_LEGAL_BASES: LegalBasis[] = [
'CONTRACT',
'CONSENT',
'EXPLICIT_CONSENT',
'LEGITIMATE_INTEREST',
'LEGAL_OBLIGATION',
'VITAL_INTERESTS',
'PUBLIC_INTEREST',
]
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
/**
* Holt den lokalisierten Text
*/
function t(text: LocalizedText, language: SupportedLanguage): string {
return text[language]
}
/**
* Gruppiert Datenpunkte nach Kategorie
*/
function groupByCategory(dataPoints: DataPoint[]): Map<DataPointCategory, DataPoint[]> {
const grouped = new Map<DataPointCategory, DataPoint[]>()
for (const dp of dataPoints) {
const existing = grouped.get(dp.category) || []
grouped.set(dp.category, [...existing, dp])
}
return grouped
}
/**
* Gruppiert Datenpunkte nach Rechtsgrundlage
*/
function groupByLegalBasis(dataPoints: DataPoint[]): Map<LegalBasis, DataPoint[]> {
const grouped = new Map<LegalBasis, DataPoint[]>()
for (const dp of dataPoints) {
const existing = grouped.get(dp.legalBasis) || []
grouped.set(dp.legalBasis, [...existing, dp])
}
return grouped
}
/**
* Extrahiert alle einzigartigen Drittanbieter
*/
function extractThirdParties(dataPoints: DataPoint[]): string[] {
const thirdParties = new Set<string>()
for (const dp of dataPoints) {
for (const recipient of dp.thirdPartyRecipients) {
thirdParties.add(recipient)
}
}
return Array.from(thirdParties).sort()
}
/**
* Formatiert ein Datum fuer die Anzeige
*/
function formatDate(date: Date, language: SupportedLanguage): string {
return date.toLocaleDateString(language === 'de' ? 'de-DE' : 'en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
}
// =============================================================================
// SECTION GENERATORS
// =============================================================================
/**
* Abschnitt 1: Verantwortlicher
*/
function generateControllerSection(
companyInfo: CompanyInfo,
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '1. Verantwortlicher',
en: '1. Data Controller',
}
const dpoSection = companyInfo.dpoName
? language === 'de'
? `\n\n**Datenschutzbeauftragter:**\n${companyInfo.dpoName}${companyInfo.dpoEmail ? `\nE-Mail: ${companyInfo.dpoEmail}` : ''}${companyInfo.dpoPhone ? `\nTelefon: ${companyInfo.dpoPhone}` : ''}`
: `\n\n**Data Protection Officer:**\n${companyInfo.dpoName}${companyInfo.dpoEmail ? `\nEmail: ${companyInfo.dpoEmail}` : ''}${companyInfo.dpoPhone ? `\nPhone: ${companyInfo.dpoPhone}` : ''}`
: ''
const content: LocalizedText = {
de: `Verantwortlich fuer die Datenverarbeitung auf dieser Website ist:
**${companyInfo.name}**
${companyInfo.address}
${companyInfo.postalCode} ${companyInfo.city}
${companyInfo.country}
E-Mail: ${companyInfo.email}${companyInfo.phone ? `\nTelefon: ${companyInfo.phone}` : ''}${companyInfo.website ? `\nWebsite: ${companyInfo.website}` : ''}${companyInfo.registrationNumber ? `\n\nHandelsregister: ${companyInfo.registrationNumber}` : ''}${companyInfo.vatId ? `\nUSt-IdNr.: ${companyInfo.vatId}` : ''}${dpoSection}`,
en: `The controller responsible for data processing on this website is:
**${companyInfo.name}**
${companyInfo.address}
${companyInfo.postalCode} ${companyInfo.city}
${companyInfo.country}
Email: ${companyInfo.email}${companyInfo.phone ? `\nPhone: ${companyInfo.phone}` : ''}${companyInfo.website ? `\nWebsite: ${companyInfo.website}` : ''}${companyInfo.registrationNumber ? `\n\nCommercial Register: ${companyInfo.registrationNumber}` : ''}${companyInfo.vatId ? `\nVAT ID: ${companyInfo.vatId}` : ''}${dpoSection}`,
}
return {
id: 'controller',
order: 1,
title,
content,
dataPointIds: [],
isRequired: true,
isGenerated: false,
}
}
/**
* Abschnitt 2: Erhobene Daten (18 Kategorien)
*/
function generateDataCollectionSection(
dataPoints: DataPoint[],
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '2. Erhobene personenbezogene Daten',
en: '2. Personal Data We Collect',
}
const grouped = groupByCategory(dataPoints)
const sections: string[] = []
// Prüfe ob Art. 9 Daten enthalten sind
const hasSpecialCategoryData = dataPoints.some(dp => dp.isSpecialCategory || dp.category === 'HEALTH_DATA')
for (const category of ALL_CATEGORIES) {
const categoryData = grouped.get(category)
if (!categoryData || categoryData.length === 0) continue
const categoryMeta = CATEGORY_METADATA[category]
if (!categoryMeta) continue
const categoryTitle = t(categoryMeta.name, language)
// Spezielle Warnung für Art. 9 DSGVO Daten (Gesundheitsdaten)
let categoryNote = ''
if (category === 'HEALTH_DATA') {
categoryNote = language === 'de'
? `\n\n> **Hinweis:** Diese Daten gehoeren zu den besonderen Kategorien personenbezogener Daten gemaess Art. 9 DSGVO und erfordern eine ausdrueckliche Einwilligung.`
: `\n\n> **Note:** This data belongs to special categories of personal data under Art. 9 GDPR and requires explicit consent.`
} else if (category === 'EMPLOYEE_DATA') {
categoryNote = language === 'de'
? `\n\n> **Hinweis:** Die Verarbeitung von Beschaeftigtendaten erfolgt gemaess § 26 BDSG.`
: `\n\n> **Note:** Processing of employee data is carried out in accordance with § 26 BDSG (German Federal Data Protection Act).`
} else if (category === 'AI_DATA') {
categoryNote = language === 'de'
? `\n\n> **Hinweis:** Die Verarbeitung von KI-bezogenen Daten unterliegt den Transparenzpflichten des AI Acts.`
: `\n\n> **Note:** Processing of AI-related data is subject to AI Act transparency requirements.`
}
const dataList = categoryData
.map((dp) => {
const specialTag = dp.isSpecialCategory
? (language === 'de' ? ' *(Art. 9 DSGVO)*' : ' *(Art. 9 GDPR)*')
: ''
return `- **${t(dp.name, language)}**${specialTag}: ${t(dp.description, language)}`
})
.join('\n')
sections.push(`### ${categoryMeta.code}. ${categoryTitle}\n\n${dataList}${categoryNote}`)
}
const intro: LocalizedText = {
de: 'Wir erheben und verarbeiten die folgenden personenbezogenen Daten:',
en: 'We collect and process the following personal data:',
}
// Zusätzlicher Hinweis für Art. 9 Daten
const specialCategoryNote: LocalizedText = hasSpecialCategoryData
? {
de: '\n\n**Wichtig:** Einige der unten aufgefuehrten Daten gehoeren zu den besonderen Kategorien personenbezogener Daten nach Art. 9 DSGVO und werden nur mit Ihrer ausdruecklichen Einwilligung verarbeitet.',
en: '\n\n**Important:** Some of the data listed below belongs to special categories of personal data under Art. 9 GDPR and is only processed with your explicit consent.',
}
: { de: '', en: '' }
const content: LocalizedText = {
de: `${intro.de}${specialCategoryNote.de}\n\n${sections.join('\n\n')}`,
en: `${intro.en}${specialCategoryNote.en}\n\n${sections.join('\n\n')}`,
}
return {
id: 'data-collection',
order: 2,
title,
content,
dataPointIds: dataPoints.map((dp) => dp.id),
isRequired: true,
isGenerated: true,
}
}
/**
* Abschnitt 3: Verarbeitungszwecke
*/
function generatePurposesSection(
dataPoints: DataPoint[],
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '3. Zwecke der Datenverarbeitung',
en: '3. Purposes of Data Processing',
}
// Gruppiere nach Zweck (unique purposes)
const purposes = new Map<string, DataPoint[]>()
for (const dp of dataPoints) {
const purpose = t(dp.purpose, language)
const existing = purposes.get(purpose) || []
purposes.set(purpose, [...existing, dp])
}
const purposeList = Array.from(purposes.entries())
.map(([purpose, dps]) => {
const dataNames = dps.map((dp) => t(dp.name, language)).join(', ')
return `- **${purpose}**\n Betroffene Daten: ${dataNames}`
})
.join('\n\n')
const content: LocalizedText = {
de: `Wir verarbeiten Ihre personenbezogenen Daten fuer folgende Zwecke:\n\n${purposeList}`,
en: `We process your personal data for the following purposes:\n\n${purposeList}`,
}
return {
id: 'purposes',
order: 3,
title,
content,
dataPointIds: dataPoints.map((dp) => dp.id),
isRequired: true,
isGenerated: true,
}
}
/**
* Abschnitt 4: Rechtsgrundlagen (alle 7 Rechtsgrundlagen)
*/
function generateLegalBasisSection(
dataPoints: DataPoint[],
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '4. Rechtsgrundlagen der Verarbeitung',
en: '4. Legal Basis for Processing',
}
const grouped = groupByLegalBasis(dataPoints)
const sections: string[] = []
// Alle 7 Rechtsgrundlagen in der richtigen Reihenfolge
for (const basis of ALL_LEGAL_BASES) {
const basisData = grouped.get(basis)
if (!basisData || basisData.length === 0) continue
const basisInfo = LEGAL_BASIS_INFO[basis]
if (!basisInfo) continue
const basisTitle = `${t(basisInfo.name, language)} (${basisInfo.article})`
const basisDesc = t(basisInfo.description, language)
// Für Art. 9 Daten (EXPLICIT_CONSENT) zusätzliche Warnung hinzufügen
let additionalWarning = ''
if (basis === 'EXPLICIT_CONSENT') {
additionalWarning = language === 'de'
? `\n\n> **Wichtig:** Fuer die Verarbeitung dieser besonderen Kategorien personenbezogener Daten (Art. 9 DSGVO) ist eine separate, ausdrueckliche Einwilligung erforderlich, die Sie jederzeit widerrufen koennen.`
: `\n\n> **Important:** Processing of these special categories of personal data (Art. 9 GDPR) requires a separate, explicit consent that you can withdraw at any time.`
}
const dataLabel = language === 'de' ? 'Betroffene Daten' : 'Affected Data'
const dataList = basisData
.map((dp) => {
const specialTag = dp.isSpecialCategory
? (language === 'de' ? ' *(Art. 9 DSGVO)*' : ' *(Art. 9 GDPR)*')
: ''
return `- ${t(dp.name, language)}${specialTag}: ${t(dp.legalBasisJustification, language)}`
})
.join('\n')
sections.push(`### ${basisTitle}\n\n${basisDesc}${additionalWarning}\n\n**${dataLabel}:**\n${dataList}`)
}
const content: LocalizedText = {
de: `Die Verarbeitung Ihrer personenbezogenen Daten erfolgt auf Grundlage folgender Rechtsgrundlagen:\n\n${sections.join('\n\n')}`,
en: `The processing of your personal data is based on the following legal grounds:\n\n${sections.join('\n\n')}`,
}
return {
id: 'legal-basis',
order: 4,
title,
content,
dataPointIds: dataPoints.map((dp) => dp.id),
isRequired: true,
isGenerated: true,
}
}
/**
* Abschnitt 5: Empfaenger / Dritte
*/
function generateRecipientsSection(
dataPoints: DataPoint[],
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '5. Empfaenger und Datenweitergabe',
en: '5. Recipients and Data Sharing',
}
const thirdParties = extractThirdParties(dataPoints)
if (thirdParties.length === 0) {
const content: LocalizedText = {
de: 'Wir geben Ihre personenbezogenen Daten grundsaetzlich nicht an Dritte weiter, es sei denn, dies ist zur Vertragserfuellung erforderlich oder Sie haben ausdruecklich eingewilligt.',
en: 'We generally do not share your personal data with third parties unless this is necessary for contract performance or you have expressly consented.',
}
return {
id: 'recipients',
order: 5,
title,
content,
dataPointIds: [],
isRequired: true,
isGenerated: false,
}
}
// Gruppiere nach Drittanbieter
const recipientDetails = new Map<string, DataPoint[]>()
for (const dp of dataPoints) {
for (const recipient of dp.thirdPartyRecipients) {
const existing = recipientDetails.get(recipient) || []
recipientDetails.set(recipient, [...existing, dp])
}
}
const recipientList = Array.from(recipientDetails.entries())
.map(([recipient, dps]) => {
const dataNames = dps.map((dp) => t(dp.name, language)).join(', ')
return `- **${recipient}**: ${dataNames}`
})
.join('\n')
const content: LocalizedText = {
de: `Wir uebermitteln Ihre personenbezogenen Daten an folgende Empfaenger bzw. Kategorien von Empfaengern:\n\n${recipientList}\n\nMit allen Auftragsverarbeitern haben wir Auftragsverarbeitungsvertraege nach Art. 28 DSGVO abgeschlossen.`,
en: `We share your personal data with the following recipients or categories of recipients:\n\n${recipientList}\n\nWe have concluded data processing agreements pursuant to Art. 28 GDPR with all processors.`,
}
return {
id: 'recipients',
order: 5,
title,
content,
dataPointIds: dataPoints.filter((dp) => dp.thirdPartyRecipients.length > 0).map((dp) => dp.id),
isRequired: true,
isGenerated: true,
}
}
/**
* Abschnitt 6: Speicherdauer
*/
function generateRetentionSection(
dataPoints: DataPoint[],
retentionMatrix: RetentionMatrixEntry[],
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '6. Speicherdauer',
en: '6. Data Retention',
}
const grouped = groupByCategory(dataPoints)
const sections: string[] = []
for (const entry of retentionMatrix) {
const categoryData = grouped.get(entry.category)
if (!categoryData || categoryData.length === 0) continue
const categoryName = t(entry.categoryName, language)
const standardPeriod = t(RETENTION_PERIOD_INFO[entry.standardPeriod].label, language)
const dataRetention = categoryData
.map((dp) => {
const period = t(RETENTION_PERIOD_INFO[dp.retentionPeriod].label, language)
return `- ${t(dp.name, language)}: ${period}`
})
.join('\n')
sections.push(`### ${categoryName}\n\n**Standardfrist:** ${standardPeriod}\n\n${dataRetention}`)
}
const content: LocalizedText = {
de: `Wir speichern Ihre personenbezogenen Daten nur so lange, wie dies fuer die jeweiligen Zwecke erforderlich ist oder gesetzliche Aufbewahrungsfristen bestehen.\n\n${sections.join('\n\n')}`,
en: `We store your personal data only for as long as is necessary for the respective purposes or as required by statutory retention periods.\n\n${sections.join('\n\n')}`,
}
return {
id: 'retention',
order: 6,
title,
content,
dataPointIds: dataPoints.map((dp) => dp.id),
isRequired: true,
isGenerated: true,
}
}
/**
* Abschnitt 6a: Besondere Kategorien (Art. 9 DSGVO)
* Wird nur generiert, wenn Art. 9 Daten vorhanden sind
*/
function generateSpecialCategoriesSection(
dataPoints: DataPoint[],
language: SupportedLanguage
): PrivacyPolicySection | null {
// Filtere Art. 9 Datenpunkte
const specialCategoryDataPoints = dataPoints.filter(dp => dp.isSpecialCategory || dp.category === 'HEALTH_DATA')
if (specialCategoryDataPoints.length === 0) {
return null
}
const title: LocalizedText = {
de: '6a. Besondere Kategorien personenbezogener Daten (Art. 9 DSGVO)',
en: '6a. Special Categories of Personal Data (Art. 9 GDPR)',
}
const dataList = specialCategoryDataPoints
.map((dp) => `- **${t(dp.name, language)}**: ${t(dp.description, language)}`)
.join('\n')
const content: LocalizedText = {
de: `Gemaess Art. 9 DSGVO verarbeiten wir auch besondere Kategorien personenbezogener Daten, die einen erhoehten Schutz geniessen. Diese Daten umfassen:
${dataList}
### Ihre ausdrueckliche Einwilligung
Die Verarbeitung dieser besonderen Kategorien personenbezogener Daten erfolgt nur auf Grundlage Ihrer **ausdruecklichen Einwilligung** gemaess Art. 9 Abs. 2 lit. a DSGVO.
### Ihre Rechte bei Art. 9 Daten
- Sie koennen Ihre Einwilligung **jederzeit widerrufen**
- Der Widerruf beruehrt nicht die Rechtmaessigkeit der bisherigen Verarbeitung
- Bei Widerruf werden Ihre Daten unverzueglich geloescht
- Sie haben das Recht auf **Auskunft, Berichtigung und Loeschung**
### Besondere Schutzmassnahmen
Fuer diese sensiblen Daten haben wir besondere technische und organisatorische Massnahmen implementiert:
- Ende-zu-Ende-Verschluesselung
- Strenge Zugriffskontrolle (Need-to-Know-Prinzip)
- Audit-Logging aller Zugriffe
- Regelmaessige Datenschutz-Folgenabschaetzungen`,
en: `In accordance with Art. 9 GDPR, we also process special categories of personal data that enjoy enhanced protection. This data includes:
${dataList}
### Your Explicit Consent
Processing of these special categories of personal data only takes place on the basis of your **explicit consent** pursuant to Art. 9(2)(a) GDPR.
### Your Rights Regarding Art. 9 Data
- You can **withdraw your consent at any time**
- Withdrawal does not affect the lawfulness of previous processing
- Upon withdrawal, your data will be deleted immediately
- You have the right to **access, rectification, and erasure**
### Special Protection Measures
For this sensitive data, we have implemented special technical and organizational measures:
- End-to-end encryption
- Strict access control (need-to-know principle)
- Audit logging of all access
- Regular data protection impact assessments`,
}
return {
id: 'special-categories',
order: 6.5, // Zwischen Speicherdauer (6) und Rechte (7)
title,
content,
dataPointIds: specialCategoryDataPoints.map((dp) => dp.id),
isRequired: false,
isGenerated: true,
}
}
/**
* Abschnitt 7: Betroffenenrechte
*/
function generateRightsSection(language: SupportedLanguage): PrivacyPolicySection {
const title: LocalizedText = {
de: '7. Ihre Rechte als betroffene Person',
en: '7. Your Rights as a Data Subject',
}
const content: LocalizedText = {
de: `Sie haben gegenueber uns folgende Rechte hinsichtlich der Sie betreffenden personenbezogenen Daten:
### Auskunftsrecht (Art. 15 DSGVO)
Sie haben das Recht, Auskunft ueber die von uns verarbeiteten personenbezogenen Daten zu verlangen.
### Recht auf Berichtigung (Art. 16 DSGVO)
Sie haben das Recht, die Berichtigung unrichtiger oder die Vervollstaendigung unvollstaendiger Daten zu verlangen.
### Recht auf Loeschung (Art. 17 DSGVO)
Sie haben das Recht, die Loeschung Ihrer personenbezogenen Daten zu verlangen, sofern keine gesetzlichen Aufbewahrungspflichten entgegenstehen.
### Recht auf Einschraenkung der Verarbeitung (Art. 18 DSGVO)
Sie haben das Recht, die Einschraenkung der Verarbeitung Ihrer Daten zu verlangen.
### Recht auf Datenuebertragbarkeit (Art. 20 DSGVO)
Sie haben das Recht, Ihre Daten in einem strukturierten, gaengigen und maschinenlesbaren Format zu erhalten.
### Widerspruchsrecht (Art. 21 DSGVO)
Sie haben das Recht, der Verarbeitung Ihrer Daten jederzeit zu widersprechen, soweit die Verarbeitung auf berechtigtem Interesse beruht.
### Recht auf Widerruf der Einwilligung (Art. 7 Abs. 3 DSGVO)
Sie haben das Recht, Ihre erteilte Einwilligung jederzeit zu widerrufen. Die Rechtmaessigkeit der aufgrund der Einwilligung bis zum Widerruf erfolgten Verarbeitung wird dadurch nicht beruehrt.
### Beschwerderecht bei der Aufsichtsbehoerde (Art. 77 DSGVO)
Sie haben das Recht, sich bei einer Datenschutz-Aufsichtsbehoerde ueber die Verarbeitung Ihrer personenbezogenen Daten zu beschweren.
**Zur Ausuebung Ihrer Rechte wenden Sie sich bitte an die oben angegebenen Kontaktdaten.**`,
en: `You have the following rights regarding your personal data:
### Right of Access (Art. 15 GDPR)
You have the right to request information about the personal data we process about you.
### Right to Rectification (Art. 16 GDPR)
You have the right to request the correction of inaccurate data or the completion of incomplete data.
### Right to Erasure (Art. 17 GDPR)
You have the right to request the deletion of your personal data, unless statutory retention obligations apply.
### Right to Restriction of Processing (Art. 18 GDPR)
You have the right to request the restriction of processing of your data.
### Right to Data Portability (Art. 20 GDPR)
You have the right to receive your data in a structured, commonly used, and machine-readable format.
### Right to Object (Art. 21 GDPR)
You have the right to object to the processing of your data at any time, insofar as the processing is based on legitimate interest.
### Right to Withdraw Consent (Art. 7(3) GDPR)
You have the right to withdraw your consent at any time. The lawfulness of processing based on consent before its withdrawal is not affected.
### Right to Lodge a Complaint with a Supervisory Authority (Art. 77 GDPR)
You have the right to lodge a complaint with a data protection supervisory authority about the processing of your personal data.
**To exercise your rights, please contact us using the contact details provided above.**`,
}
return {
id: 'rights',
order: 7,
title,
content,
dataPointIds: [],
isRequired: true,
isGenerated: false,
}
}
/**
* Abschnitt 8: Cookies
*/
function generateCookiesSection(
dataPoints: DataPoint[],
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '8. Cookies und aehnliche Technologien',
en: '8. Cookies and Similar Technologies',
}
// Filtere Datenpunkte mit Cookie-Kategorie
const cookieDataPoints = dataPoints.filter((dp) => dp.cookieCategory !== null)
if (cookieDataPoints.length === 0) {
const content: LocalizedText = {
de: 'Wir verwenden auf dieser Website keine Cookies.',
en: 'We do not use cookies on this website.',
}
return {
id: 'cookies',
order: 8,
title,
content,
dataPointIds: [],
isRequired: false,
isGenerated: false,
}
}
// Gruppiere nach Cookie-Kategorie
const essential = cookieDataPoints.filter((dp) => dp.cookieCategory === 'ESSENTIAL')
const performance = cookieDataPoints.filter((dp) => dp.cookieCategory === 'PERFORMANCE')
const personalization = cookieDataPoints.filter((dp) => dp.cookieCategory === 'PERSONALIZATION')
const externalMedia = cookieDataPoints.filter((dp) => dp.cookieCategory === 'EXTERNAL_MEDIA')
const sections: string[] = []
if (essential.length > 0) {
const list = essential.map((dp) => `- **${t(dp.name, language)}**: ${t(dp.purpose, language)}`).join('\n')
sections.push(
language === 'de'
? `### Technisch notwendige Cookies\n\nDiese Cookies sind fuer den Betrieb der Website erforderlich und koennen nicht deaktiviert werden.\n\n${list}`
: `### Essential Cookies\n\nThese cookies are required for the website to function and cannot be disabled.\n\n${list}`
)
}
if (performance.length > 0) {
const list = performance.map((dp) => `- **${t(dp.name, language)}**: ${t(dp.purpose, language)}`).join('\n')
sections.push(
language === 'de'
? `### Analyse- und Performance-Cookies\n\nDiese Cookies helfen uns, die Nutzung der Website zu verstehen und zu verbessern.\n\n${list}`
: `### Analytics and Performance Cookies\n\nThese cookies help us understand and improve website usage.\n\n${list}`
)
}
if (personalization.length > 0) {
const list = personalization.map((dp) => `- **${t(dp.name, language)}**: ${t(dp.purpose, language)}`).join('\n')
sections.push(
language === 'de'
? `### Personalisierungs-Cookies\n\nDiese Cookies ermoeglichen personalisierte Werbung und Inhalte.\n\n${list}`
: `### Personalization Cookies\n\nThese cookies enable personalized advertising and content.\n\n${list}`
)
}
if (externalMedia.length > 0) {
const list = externalMedia.map((dp) => `- **${t(dp.name, language)}**: ${t(dp.purpose, language)}`).join('\n')
sections.push(
language === 'de'
? `### Cookies fuer externe Medien\n\nDiese Cookies erlauben die Einbindung externer Medien wie Videos und Karten.\n\n${list}`
: `### External Media Cookies\n\nThese cookies allow embedding external media like videos and maps.\n\n${list}`
)
}
const intro: LocalizedText = {
de: `Wir verwenden Cookies und aehnliche Technologien, um Ihnen die bestmoegliche Nutzung unserer Website zu ermoeglichen. Sie koennen Ihre Cookie-Einstellungen jederzeit ueber unseren Cookie-Banner anpassen.`,
en: `We use cookies and similar technologies to provide you with the best possible experience on our website. You can adjust your cookie settings at any time through our cookie banner.`,
}
const content: LocalizedText = {
de: `${intro.de}\n\n${sections.join('\n\n')}`,
en: `${intro.en}\n\n${sections.join('\n\n')}`,
}
return {
id: 'cookies',
order: 8,
title,
content,
dataPointIds: cookieDataPoints.map((dp) => dp.id),
isRequired: true,
isGenerated: true,
}
}
/**
* Abschnitt 9: Aenderungen
*/
function generateChangesSection(
version: string,
date: Date,
language: SupportedLanguage
): PrivacyPolicySection {
const title: LocalizedText = {
de: '9. Aenderungen dieser Datenschutzerklaerung',
en: '9. Changes to this Privacy Policy',
}
const formattedDate = formatDate(date, language)
const content: LocalizedText = {
de: `Diese Datenschutzerklaerung ist aktuell gueltig und hat den Stand: **${formattedDate}** (Version ${version}).
Wir behalten uns vor, diese Datenschutzerklaerung anzupassen, damit sie stets den aktuellen rechtlichen Anforderungen entspricht oder um Aenderungen unserer Leistungen umzusetzen.
Fuer Ihren erneuten Besuch gilt dann die neue Datenschutzerklaerung.`,
en: `This privacy policy is currently valid and was last updated: **${formattedDate}** (Version ${version}).
We reserve the right to amend this privacy policy to ensure it always complies with current legal requirements or to implement changes to our services.
The new privacy policy will then apply for your next visit.`,
}
return {
id: 'changes',
order: 9,
title,
content,
dataPointIds: [],
isRequired: true,
isGenerated: false,
}
}
// =============================================================================
// MAIN GENERATOR FUNCTIONS
// =============================================================================
/**
* Generiert alle Abschnitte der Privacy Policy (18 Kategorien + Art. 9)
*/
export function generatePrivacyPolicySections(
dataPoints: DataPoint[],
companyInfo: CompanyInfo,
language: SupportedLanguage,
version: string = '1.0.0'
): PrivacyPolicySection[] {
const now = new Date()
const sections: PrivacyPolicySection[] = [
generateControllerSection(companyInfo, language),
generateDataCollectionSection(dataPoints, language),
generatePurposesSection(dataPoints, language),
generateLegalBasisSection(dataPoints, language),
generateRecipientsSection(dataPoints, language),
generateRetentionSection(dataPoints, RETENTION_MATRIX, language),
]
// Art. 9 DSGVO Abschnitt nur einfügen, wenn besondere Kategorien vorhanden
const specialCategoriesSection = generateSpecialCategoriesSection(dataPoints, language)
if (specialCategoriesSection) {
sections.push(specialCategoriesSection)
}
sections.push(
generateRightsSection(language),
generateCookiesSection(dataPoints, language),
generateChangesSection(version, now, language)
)
// Abschnittsnummern neu vergeben
sections.forEach((section, index) => {
section.order = index + 1
// Titel-Nummer aktualisieren
const titleDe = section.title.de
const titleEn = section.title.en
if (titleDe.match(/^\d+[a-z]?\./)) {
section.title.de = titleDe.replace(/^\d+[a-z]?\./, `${index + 1}.`)
}
if (titleEn.match(/^\d+[a-z]?\./)) {
section.title.en = titleEn.replace(/^\d+[a-z]?\./, `${index + 1}.`)
}
})
return sections
}
/**
* Generiert die vollstaendige Privacy Policy
*/
export function generatePrivacyPolicy(
tenantId: string,
dataPoints: DataPoint[],
companyInfo: CompanyInfo,
language: SupportedLanguage,
format: ExportFormat = 'HTML'
): GeneratedPrivacyPolicy {
const version = '1.0.0'
const sections = generatePrivacyPolicySections(dataPoints, companyInfo, language, version)
// Generiere den Inhalt
const content = renderPrivacyPolicy(sections, language, format)
return {
id: `privacy-policy-${tenantId}-${Date.now()}`,
tenantId,
language,
sections,
companyInfo,
generatedAt: new Date(),
version,
format,
content,
}
}
/**
* Rendert die Privacy Policy im gewuenschten Format
*/
function renderPrivacyPolicy(
sections: PrivacyPolicySection[],
language: SupportedLanguage,
format: ExportFormat
): string {
switch (format) {
case 'HTML':
return renderAsHTML(sections, language)
case 'MARKDOWN':
return renderAsMarkdown(sections, language)
default:
return renderAsMarkdown(sections, language)
}
}
/**
* Rendert als HTML
*/
function renderAsHTML(sections: PrivacyPolicySection[], language: SupportedLanguage): string {
const title = language === 'de' ? 'Datenschutzerklaerung' : 'Privacy Policy'
const sectionsHTML = sections
.map((section) => {
const content = t(section.content, language)
.replace(/\n\n/g, '</p><p>')
.replace(/\n/g, '<br>')
.replace(/### (.+)/g, '<h3>$1</h3>')
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/- (.+)(?:<br>|$)/g, '<li>$1</li>')
return `
<section id="${section.id}">
<h2>${t(section.title, language)}</h2>
<p>${content}</p>
</section>
`
})
.join('\n')
return `<!DOCTYPE html>
<html lang="${language}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
color: #333;
}
h1 { font-size: 2rem; margin-bottom: 2rem; }
h2 { font-size: 1.5rem; margin-top: 2rem; color: #1a1a1a; }
h3 { font-size: 1.25rem; margin-top: 1.5rem; color: #333; }
p { margin: 1rem 0; }
ul, ol { margin: 1rem 0; padding-left: 2rem; }
li { margin: 0.5rem 0; }
strong { font-weight: 600; }
</style>
</head>
<body>
<h1>${title}</h1>
${sectionsHTML}
</body>
</html>`
}
/**
* Rendert als Markdown
*/
function renderAsMarkdown(sections: PrivacyPolicySection[], language: SupportedLanguage): string {
const title = language === 'de' ? 'Datenschutzerklaerung' : 'Privacy Policy'
const sectionsMarkdown = sections
.map((section) => {
return `## ${t(section.title, language)}\n\n${t(section.content, language)}`
})
.join('\n\n---\n\n')
return `# ${title}\n\n${sectionsMarkdown}`
}
// =============================================================================
// EXPORTS
// =============================================================================
export {
generateControllerSection,
generateDataCollectionSection,
generatePurposesSection,
generateLegalBasisSection,
generateRecipientsSection,
generateRetentionSection,
generateSpecialCategoriesSection,
generateRightsSection,
generateCookiesSection,
generateChangesSection,
renderAsHTML,
renderAsMarkdown,
}

View File

@@ -0,0 +1,79 @@
/**
* Datenpunktkatalog & Datenschutzinformationen-Generator
*
* Dieses Modul erweitert das SDK Einwilligungen-Modul um:
* - Datenpunktkatalog mit 28 vordefinierten + kundenspezifischen Datenpunkten
* - Automatische Privacy Policy Generierung
* - Cookie Banner Konfiguration
* - Retention Matrix Visualisierung
*
* @module lib/sdk/einwilligungen
*/
// =============================================================================
// TYPES
// =============================================================================
export * from './types'
// =============================================================================
// CATALOG
// =============================================================================
export {
PREDEFINED_DATA_POINTS,
RETENTION_MATRIX,
DEFAULT_COOKIE_CATEGORIES,
getDataPointById,
getDataPointByCode,
getDataPointsByCategory,
getDataPointsByLegalBasis,
getDataPointsByCookieCategory,
getDataPointsRequiringConsent,
getHighRiskDataPoints,
countDataPointsByCategory,
countDataPointsByRiskLevel,
createDefaultCatalog,
searchDataPoints,
} from './catalog/loader'
// =============================================================================
// CONTEXT
// =============================================================================
export {
EinwilligungenProvider,
useEinwilligungen,
initialState as einwilligungenInitialState,
einwilligungenReducer,
} from './context'
// =============================================================================
// GENERATORS (to be implemented)
// =============================================================================
// Privacy Policy Generator
export { generatePrivacyPolicy, generatePrivacyPolicySections } from './generator/privacy-policy'
// Cookie Banner Generator
export { generateCookieBannerConfig, generateEmbedCode } from './generator/cookie-banner'
// =============================================================================
// EXPORT
// =============================================================================
// PDF Export
export {
generatePDFContent as generatePrivacyPolicyPDFContent,
generatePDFBlob as generatePrivacyPolicyPDFBlob,
generatePDFFilename as generatePrivacyPolicyPDFFilename,
} from './export/pdf'
export type { PDFExportOptions as PrivacyPolicyPDFExportOptions } from './export/pdf'
// DOCX Export
export {
generateDOCXContent as generatePrivacyPolicyDOCXContent,
generateDOCXBlob as generatePrivacyPolicyDOCXBlob,
generateDOCXFilename as generatePrivacyPolicyDOCXFilename,
} from './export/docx'
export type { DOCXExportOptions as PrivacyPolicyDOCXExportOptions } from './export/docx'

View File

@@ -0,0 +1,838 @@
/**
* Datenpunktkatalog & Datenschutzinformationen-Generator
* TypeScript Interfaces
*
* Dieses Modul definiert alle Typen für:
* - Datenpunktkatalog (32 vordefinierte + kundenspezifische)
* - Privacy Policy Generator
* - Cookie Banner Configuration
* - Retention Matrix
*/
// =============================================================================
// ENUMS
// =============================================================================
/**
* Kategorien für Datenpunkte (18 Kategorien: A-R)
*/
export type DataPointCategory =
| 'MASTER_DATA' // A: Stammdaten
| 'CONTACT_DATA' // B: Kontaktdaten
| 'AUTHENTICATION' // C: Authentifizierungsdaten
| 'CONSENT' // D: Einwilligungsdaten
| 'COMMUNICATION' // E: Kommunikationsdaten
| 'PAYMENT' // F: Zahlungsdaten
| 'USAGE_DATA' // G: Nutzungsdaten
| 'LOCATION' // H: Standortdaten
| 'DEVICE_DATA' // I: Gerätedaten
| 'MARKETING' // J: Marketingdaten
| 'ANALYTICS' // K: Analysedaten
| 'SOCIAL_MEDIA' // L: Social-Media-Daten
| 'HEALTH_DATA' // M: Gesundheitsdaten (Art. 9 DSGVO)
| 'EMPLOYEE_DATA' // N: Beschäftigtendaten
| 'CONTRACT_DATA' // O: Vertragsdaten
| 'LOG_DATA' // P: Protokolldaten
| 'AI_DATA' // Q: KI-Daten
| 'SECURITY' // R: Sicherheitsdaten
/**
* Risikoniveau für Datenpunkte
*/
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH'
/**
* Rechtsgrundlagen nach DSGVO Art. 6 und Art. 9
*/
export type LegalBasis =
| 'CONTRACT' // Art. 6 Abs. 1 lit. b DSGVO
| 'CONSENT' // Art. 6 Abs. 1 lit. a DSGVO
| 'EXPLICIT_CONSENT' // Art. 9 Abs. 2 lit. a DSGVO (für Art. 9 Daten)
| 'LEGITIMATE_INTEREST' // Art. 6 Abs. 1 lit. f DSGVO
| 'LEGAL_OBLIGATION' // Art. 6 Abs. 1 lit. c DSGVO
| 'VITAL_INTERESTS' // Art. 6 Abs. 1 lit. d DSGVO
| 'PUBLIC_INTEREST' // Art. 6 Abs. 1 lit. e DSGVO
/**
* Aufbewahrungsfristen
*/
export type RetentionPeriod =
| '24_HOURS'
| '30_DAYS'
| '90_DAYS'
| '12_MONTHS'
| '24_MONTHS'
| '26_MONTHS' // Google Analytics Standard
| '36_MONTHS'
| '48_MONTHS'
| '6_YEARS'
| '10_YEARS'
| 'UNTIL_REVOCATION'
| 'UNTIL_PURPOSE_FULFILLED'
| 'UNTIL_ACCOUNT_DELETION'
/**
* Cookie-Kategorien für Cookie-Banner
*/
export type CookieCategory =
| 'ESSENTIAL' // Technisch notwendig
| 'PERFORMANCE' // Analyse & Performance
| 'PERSONALIZATION' // Personalisierung
| 'EXTERNAL_MEDIA' // Externe Medien
/**
* Export-Formate für Privacy Policy
*/
export type ExportFormat = 'HTML' | 'MARKDOWN' | 'PDF' | 'DOCX'
/**
* Sprachen
*/
export type SupportedLanguage = 'de' | 'en'
// =============================================================================
// DATA POINT
// =============================================================================
/**
* Lokalisierter Text (DE/EN)
*/
export interface LocalizedText {
de: string
en: string
}
/**
* Einzelner Datenpunkt im Katalog
*/
export interface DataPoint {
id: string
code: string // z.B. "A1", "B2", "C3"
category: DataPointCategory
name: LocalizedText
description: LocalizedText
purpose: LocalizedText
riskLevel: RiskLevel
legalBasis: LegalBasis
legalBasisJustification: LocalizedText
retentionPeriod: RetentionPeriod
retentionJustification: LocalizedText
cookieCategory: CookieCategory | null // null = kein Cookie
isSpecialCategory: boolean // Art. 9 DSGVO (sensible Daten)
requiresExplicitConsent: boolean
thirdPartyRecipients: string[]
technicalMeasures: string[]
tags: string[]
isCustom?: boolean // Kundenspezifischer Datenpunkt
isActive?: boolean // Aktiviert fuer diesen Tenant
}
/**
* YAML-Struktur fuer Datenpunkte (fuer Loader)
*/
export interface DataPointYAML {
id: string
code: string
category: string
name_de: string
name_en: string
description_de: string
description_en: string
purpose_de: string
purpose_en: string
risk_level: string
legal_basis: string
legal_basis_justification_de: string
legal_basis_justification_en: string
retention_period: string
retention_justification_de: string
retention_justification_en: string
cookie_category: string | null
is_special_category: boolean
requires_explicit_consent: boolean
third_party_recipients: string[]
technical_measures: string[]
tags: string[]
}
// =============================================================================
// CATALOG & RETENTION MATRIX
// =============================================================================
/**
* Gesamter Datenpunktkatalog eines Tenants
*/
export interface DataPointCatalog {
id: string
tenantId: string
version: string
dataPoints: DataPoint[] // Vordefinierte (32)
customDataPoints: DataPoint[] // Kundenspezifische
retentionMatrix: RetentionMatrixEntry[]
createdAt: Date
updatedAt: Date
}
/**
* Eintrag in der Retention Matrix
*/
export interface RetentionMatrixEntry {
category: DataPointCategory
categoryName: LocalizedText
standardPeriod: RetentionPeriod
legalBasis: string
exceptions: RetentionException[]
}
/**
* Ausnahme von der Standard-Loeschfrist
*/
export interface RetentionException {
condition: LocalizedText
period: RetentionPeriod
reason: LocalizedText
}
// =============================================================================
// PRIVACY POLICY GENERATION
// =============================================================================
/**
* Abschnitt in der Privacy Policy
*/
export interface PrivacyPolicySection {
id: string
order: number
title: LocalizedText
content: LocalizedText
dataPointIds: string[]
isRequired: boolean
isGenerated: boolean // true = aus Datenpunkten generiert
}
/**
* Unternehmensinfo fuer Privacy Policy
*/
export interface CompanyInfo {
name: string
address: string
city: string
postalCode: string
country: string
email: string
phone?: string
website?: string
dpoName?: string // Datenschutzbeauftragter
dpoEmail?: string
dpoPhone?: string
registrationNumber?: string // Handelsregister
vatId?: string // USt-IdNr
}
/**
* Generierte Privacy Policy
*/
export interface GeneratedPrivacyPolicy {
id: string
tenantId: string
language: SupportedLanguage
sections: PrivacyPolicySection[]
companyInfo: CompanyInfo
generatedAt: Date
version: string
format: ExportFormat
content?: string // Rendered content (HTML/MD)
}
/**
* Optionen fuer Privacy Policy Generierung
*/
export interface PrivacyPolicyGenerationOptions {
language: SupportedLanguage
format: ExportFormat
includeDataPoints: string[] // Welche Datenpunkte einschliessen
customSections?: PrivacyPolicySection[] // Zusaetzliche Abschnitte
styling?: PrivacyPolicyStyling
}
/**
* Styling-Optionen fuer PDF/HTML Export
*/
export interface PrivacyPolicyStyling {
primaryColor?: string
fontFamily?: string
fontSize?: number
headerFontSize?: number
includeTableOfContents?: boolean
includeDateFooter?: boolean
logoUrl?: string
}
// =============================================================================
// COOKIE BANNER CONFIG
// =============================================================================
/**
* Einzelner Cookie in einer Kategorie
*/
export interface CookieInfo {
name: string
provider: string
purpose: LocalizedText
expiry: string
type: 'FIRST_PARTY' | 'THIRD_PARTY'
}
/**
* Cookie-Banner Kategorie
*/
export interface CookieBannerCategory {
id: CookieCategory
name: LocalizedText
description: LocalizedText
isRequired: boolean // Essentiell = required
defaultEnabled: boolean
dataPointIds: string[] // Verknuepfte Datenpunkte
cookies: CookieInfo[]
}
/**
* Styling fuer Cookie Banner
*/
export interface CookieBannerStyling {
position: 'BOTTOM' | 'TOP' | 'CENTER'
theme: 'LIGHT' | 'DARK' | 'CUSTOM'
primaryColor?: string
secondaryColor?: string
textColor?: string
backgroundColor?: string
borderRadius?: number
maxWidth?: number
}
/**
* Texte fuer Cookie Banner
*/
export interface CookieBannerTexts {
title: LocalizedText
description: LocalizedText
acceptAll: LocalizedText
rejectAll: LocalizedText
customize: LocalizedText
save: LocalizedText
privacyPolicyLink: LocalizedText
}
/**
* Generierter Code fuer Cookie Banner
*/
export interface CookieBannerEmbedCode {
html: string
css: string
js: string
scriptTag: string // Fertiger Script-Tag zum Einbinden
}
/**
* Vollstaendige Cookie Banner Konfiguration
*/
export interface CookieBannerConfig {
id: string
tenantId: string
categories: CookieBannerCategory[]
styling: CookieBannerStyling
texts: CookieBannerTexts
embedCode?: CookieBannerEmbedCode
updatedAt: Date
}
// =============================================================================
// CONSENT MANAGEMENT
// =============================================================================
/**
* Einzelne Einwilligung eines Nutzers
*/
export interface ConsentEntry {
id: string
userId: string
dataPointId: string
granted: boolean
grantedAt: Date
revokedAt?: Date
ipAddress?: string
userAgent?: string
consentVersion: string
}
/**
* Aggregierte Consent-Statistiken
*/
export interface ConsentStatistics {
totalConsents: number
activeConsents: number
revokedConsents: number
byCategory: Record<DataPointCategory, {
total: number
active: number
revoked: number
}>
byLegalBasis: Record<LegalBasis, {
total: number
active: number
}>
conversionRate: number // Prozent der Nutzer mit Consent
}
// =============================================================================
// EINWILLIGUNGEN STATE & ACTIONS
// =============================================================================
/**
* Aktiver Tab in der Einwilligungen-Ansicht
*/
export type EinwilligungenTab =
| 'catalog'
| 'privacy-policy'
| 'cookie-banner'
| 'retention'
| 'consents'
/**
* State fuer Einwilligungen-Modul
*/
export interface EinwilligungenState {
// Data
catalog: DataPointCatalog | null
selectedDataPoints: string[]
privacyPolicy: GeneratedPrivacyPolicy | null
cookieBannerConfig: CookieBannerConfig | null
companyInfo: CompanyInfo | null
consentStatistics: ConsentStatistics | null
// UI State
activeTab: EinwilligungenTab
isLoading: boolean
isSaving: boolean
error: string | null
// Editor State
editingDataPoint: DataPoint | null
editingSection: PrivacyPolicySection | null
// Preview
previewLanguage: SupportedLanguage
previewFormat: ExportFormat
}
/**
* Actions fuer Einwilligungen-Reducer
*/
export type EinwilligungenAction =
| { type: 'SET_CATALOG'; payload: DataPointCatalog }
| { type: 'SET_SELECTED_DATA_POINTS'; payload: string[] }
| { type: 'TOGGLE_DATA_POINT'; payload: string }
| { type: 'ADD_CUSTOM_DATA_POINT'; payload: DataPoint }
| { type: 'UPDATE_DATA_POINT'; payload: { id: string; data: Partial<DataPoint> } }
| { type: 'DELETE_CUSTOM_DATA_POINT'; payload: string }
| { type: 'SET_PRIVACY_POLICY'; payload: GeneratedPrivacyPolicy }
| { type: 'SET_COOKIE_BANNER_CONFIG'; payload: CookieBannerConfig }
| { type: 'UPDATE_COOKIE_BANNER_STYLING'; payload: Partial<CookieBannerStyling> }
| { type: 'UPDATE_COOKIE_BANNER_TEXTS'; payload: Partial<CookieBannerTexts> }
| { type: 'SET_COMPANY_INFO'; payload: CompanyInfo }
| { type: 'SET_CONSENT_STATISTICS'; payload: ConsentStatistics }
| { type: 'SET_ACTIVE_TAB'; payload: EinwilligungenTab }
| { type: 'SET_LOADING'; payload: boolean }
| { type: 'SET_SAVING'; payload: boolean }
| { type: 'SET_ERROR'; payload: string | null }
| { type: 'SET_EDITING_DATA_POINT'; payload: DataPoint | null }
| { type: 'SET_EDITING_SECTION'; payload: PrivacyPolicySection | null }
| { type: 'SET_PREVIEW_LANGUAGE'; payload: SupportedLanguage }
| { type: 'SET_PREVIEW_FORMAT'; payload: ExportFormat }
| { type: 'RESET_STATE' }
// =============================================================================
// HELPER TYPES
// =============================================================================
/**
* Kategorie-Metadaten
*/
export interface CategoryMetadata {
id: DataPointCategory
code: string // A, B, C, etc.
name: LocalizedText
description: LocalizedText
icon: string // Icon name
color: string // Tailwind color class
}
/**
* Mapping von Kategorie zu Metadaten (18 Kategorien)
*/
export const CATEGORY_METADATA: Record<DataPointCategory, CategoryMetadata> = {
MASTER_DATA: {
id: 'MASTER_DATA',
code: 'A',
name: { de: 'Stammdaten', en: 'Master Data' },
description: { de: 'Grundlegende personenbezogene Daten', en: 'Basic personal data' },
icon: 'User',
color: 'blue'
},
CONTACT_DATA: {
id: 'CONTACT_DATA',
code: 'B',
name: { de: 'Kontaktdaten', en: 'Contact Data' },
description: { de: 'Kontaktinformationen und Erreichbarkeit', en: 'Contact information and availability' },
icon: 'Mail',
color: 'sky'
},
AUTHENTICATION: {
id: 'AUTHENTICATION',
code: 'C',
name: { de: 'Authentifizierungsdaten', en: 'Authentication Data' },
description: { de: 'Daten zur Benutzeranmeldung und Session-Verwaltung', en: 'Data for user login and session management' },
icon: 'Key',
color: 'slate'
},
CONSENT: {
id: 'CONSENT',
code: 'D',
name: { de: 'Einwilligungsdaten', en: 'Consent Data' },
description: { de: 'Einwilligungen und Datenschutzpraeferenzen', en: 'Consents and privacy preferences' },
icon: 'CheckCircle',
color: 'green'
},
COMMUNICATION: {
id: 'COMMUNICATION',
code: 'E',
name: { de: 'Kommunikationsdaten', en: 'Communication Data' },
description: { de: 'Kundenservice und Kommunikationsdaten', en: 'Customer service and communication data' },
icon: 'MessageSquare',
color: 'cyan'
},
PAYMENT: {
id: 'PAYMENT',
code: 'F',
name: { de: 'Zahlungsdaten', en: 'Payment Data' },
description: { de: 'Rechnungs- und Zahlungsinformationen', en: 'Billing and payment information' },
icon: 'CreditCard',
color: 'amber'
},
USAGE_DATA: {
id: 'USAGE_DATA',
code: 'G',
name: { de: 'Nutzungsdaten', en: 'Usage Data' },
description: { de: 'Daten zur Nutzung des Dienstes', en: 'Data about service usage' },
icon: 'Activity',
color: 'violet'
},
LOCATION: {
id: 'LOCATION',
code: 'H',
name: { de: 'Standortdaten', en: 'Location Data' },
description: { de: 'Geografische Standortinformationen', en: 'Geographic location information' },
icon: 'MapPin',
color: 'emerald'
},
DEVICE_DATA: {
id: 'DEVICE_DATA',
code: 'I',
name: { de: 'Geraetedaten', en: 'Device Data' },
description: { de: 'Technische Geraete- und Browserinformationen', en: 'Technical device and browser information' },
icon: 'Smartphone',
color: 'zinc'
},
MARKETING: {
id: 'MARKETING',
code: 'J',
name: { de: 'Marketingdaten', en: 'Marketing Data' },
description: { de: 'Marketing- und Werbedaten', en: 'Marketing and advertising data' },
icon: 'Megaphone',
color: 'purple'
},
ANALYTICS: {
id: 'ANALYTICS',
code: 'K',
name: { de: 'Analysedaten', en: 'Analytics Data' },
description: { de: 'Web-Analyse und Nutzungsstatistiken', en: 'Web analytics and usage statistics' },
icon: 'BarChart3',
color: 'indigo'
},
SOCIAL_MEDIA: {
id: 'SOCIAL_MEDIA',
code: 'L',
name: { de: 'Social-Media-Daten', en: 'Social Media Data' },
description: { de: 'Daten aus sozialen Netzwerken', en: 'Data from social networks' },
icon: 'Share2',
color: 'pink'
},
HEALTH_DATA: {
id: 'HEALTH_DATA',
code: 'M',
name: { de: 'Gesundheitsdaten', en: 'Health Data' },
description: { de: 'Besondere Kategorie nach Art. 9 DSGVO - Gesundheitsbezogene Daten', en: 'Special category under Art. 9 GDPR - Health-related data' },
icon: 'Heart',
color: 'rose'
},
EMPLOYEE_DATA: {
id: 'EMPLOYEE_DATA',
code: 'N',
name: { de: 'Beschaeftigtendaten', en: 'Employee Data' },
description: { de: 'Personalverwaltung und Arbeitnehmerinformationen (BDSG § 26)', en: 'HR management and employee information' },
icon: 'Briefcase',
color: 'orange'
},
CONTRACT_DATA: {
id: 'CONTRACT_DATA',
code: 'O',
name: { de: 'Vertragsdaten', en: 'Contract Data' },
description: { de: 'Vertragsinformationen und -dokumente', en: 'Contract information and documents' },
icon: 'FileText',
color: 'teal'
},
LOG_DATA: {
id: 'LOG_DATA',
code: 'P',
name: { de: 'Protokolldaten', en: 'Log Data' },
description: { de: 'System- und Zugriffsprotokolle', en: 'System and access logs' },
icon: 'FileCode',
color: 'gray'
},
AI_DATA: {
id: 'AI_DATA',
code: 'Q',
name: { de: 'KI-Daten', en: 'AI Data' },
description: { de: 'KI-Interaktionen, Prompts und generierte Inhalte (AI Act)', en: 'AI interactions, prompts and generated content (AI Act)' },
icon: 'Bot',
color: 'fuchsia'
},
SECURITY: {
id: 'SECURITY',
code: 'R',
name: { de: 'Sicherheitsdaten', en: 'Security Data' },
description: { de: 'Sicherheitsrelevante Daten und Vorfallberichte', en: 'Security-relevant data and incident reports' },
icon: 'Shield',
color: 'red'
}
}
/**
* Mapping von Rechtsgrundlage zu Beschreibung
*/
export const LEGAL_BASIS_INFO: Record<LegalBasis, { article: string; name: LocalizedText; description: LocalizedText }> = {
CONTRACT: {
article: 'Art. 6 Abs. 1 lit. b DSGVO',
name: { de: 'Vertragserfuellung', en: 'Contract Performance' },
description: {
de: 'Die Verarbeitung ist erforderlich fuer die Erfuellung eines Vertrags oder zur Durchfuehrung vorvertraglicher Massnahmen.',
en: 'Processing is necessary for the performance of a contract or pre-contractual measures.'
}
},
CONSENT: {
article: 'Art. 6 Abs. 1 lit. a DSGVO',
name: { de: 'Einwilligung', en: 'Consent' },
description: {
de: 'Die betroffene Person hat ihre Einwilligung zu der Verarbeitung gegeben.',
en: 'The data subject has given consent to the processing.'
}
},
EXPLICIT_CONSENT: {
article: 'Art. 9 Abs. 2 lit. a DSGVO',
name: { de: 'Ausdrueckliche Einwilligung', en: 'Explicit Consent' },
description: {
de: 'Die betroffene Person hat ausdruecklich in die Verarbeitung besonderer Kategorien personenbezogener Daten (Art. 9 DSGVO) eingewilligt. Dies betrifft Gesundheitsdaten, biometrische Daten, Daten zur ethnischen Herkunft, politische Meinungen, religiöse Überzeugungen etc.',
en: 'The data subject has given explicit consent to the processing of special categories of personal data (Art. 9 GDPR). This includes health data, biometric data, racial or ethnic origin, political opinions, religious beliefs, etc.'
}
},
LEGITIMATE_INTEREST: {
article: 'Art. 6 Abs. 1 lit. f DSGVO',
name: { de: 'Berechtigtes Interesse', en: 'Legitimate Interest' },
description: {
de: 'Die Verarbeitung ist zur Wahrung berechtigter Interessen des Verantwortlichen erforderlich.',
en: 'Processing is necessary for legitimate interests pursued by the controller.'
}
},
LEGAL_OBLIGATION: {
article: 'Art. 6 Abs. 1 lit. c DSGVO',
name: { de: 'Rechtliche Verpflichtung', en: 'Legal Obligation' },
description: {
de: 'Die Verarbeitung ist zur Erfuellung einer rechtlichen Verpflichtung erforderlich.',
en: 'Processing is necessary for compliance with a legal obligation.'
}
},
VITAL_INTERESTS: {
article: 'Art. 6 Abs. 1 lit. d DSGVO',
name: { de: 'Lebenswichtige Interessen', en: 'Vital Interests' },
description: {
de: 'Die Verarbeitung ist erforderlich, um lebenswichtige Interessen der betroffenen Person oder einer anderen natuerlichen Person zu schuetzen.',
en: 'Processing is necessary to protect the vital interests of the data subject or another natural person.'
}
},
PUBLIC_INTEREST: {
article: 'Art. 6 Abs. 1 lit. e DSGVO',
name: { de: 'Oeffentliches Interesse', en: 'Public Interest' },
description: {
de: 'Die Verarbeitung ist fuer die Wahrnehmung einer Aufgabe erforderlich, die im oeffentlichen Interesse liegt oder in Ausuebung oeffentlicher Gewalt erfolgt.',
en: 'Processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority.'
}
}
}
/**
* Mapping von Aufbewahrungsfrist zu Beschreibung
*/
export const RETENTION_PERIOD_INFO: Record<RetentionPeriod, { label: LocalizedText; days: number | null }> = {
'24_HOURS': { label: { de: '24 Stunden', en: '24 Hours' }, days: 1 },
'30_DAYS': { label: { de: '30 Tage', en: '30 Days' }, days: 30 },
'90_DAYS': { label: { de: '90 Tage', en: '90 Days' }, days: 90 },
'12_MONTHS': { label: { de: '12 Monate', en: '12 Months' }, days: 365 },
'24_MONTHS': { label: { de: '24 Monate', en: '24 Months' }, days: 730 },
'26_MONTHS': { label: { de: '26 Monate (Google Analytics)', en: '26 Months (Google Analytics)' }, days: 790 },
'36_MONTHS': { label: { de: '36 Monate', en: '36 Months' }, days: 1095 },
'48_MONTHS': { label: { de: '48 Monate', en: '48 Months' }, days: 1460 },
'6_YEARS': { label: { de: '6 Jahre', en: '6 Years' }, days: 2190 },
'10_YEARS': { label: { de: '10 Jahre', en: '10 Years' }, days: 3650 },
'UNTIL_REVOCATION': { label: { de: 'Bis Widerruf', en: 'Until Revocation' }, days: null },
'UNTIL_PURPOSE_FULFILLED': { label: { de: 'Bis Zweckerfuellung', en: 'Until Purpose Fulfilled' }, days: null },
'UNTIL_ACCOUNT_DELETION': { label: { de: 'Bis Kontoschliessung', en: 'Until Account Deletion' }, days: null }
}
/**
* Spezielle Hinweise für Art. 9 DSGVO Kategorien
*/
export interface Article9Warning {
title: LocalizedText
description: LocalizedText
requirements: LocalizedText[]
}
export const ARTICLE_9_WARNING: Article9Warning = {
title: {
de: 'Besondere Kategorie personenbezogener Daten (Art. 9 DSGVO)',
en: 'Special Category of Personal Data (Art. 9 GDPR)'
},
description: {
de: 'Die Verarbeitung dieser Daten unterliegt besonderen Anforderungen nach Art. 9 DSGVO. Diese Daten sind besonders schuetzenswert.',
en: 'Processing of this data is subject to special requirements under Art. 9 GDPR. This data requires special protection.'
},
requirements: [
{
de: 'Ausdrueckliche Einwilligung erforderlich (Art. 9 Abs. 2 lit. a DSGVO)',
en: 'Explicit consent required (Art. 9(2)(a) GDPR)'
},
{
de: 'Separate Einwilligungserklaerung im UI notwendig',
en: 'Separate consent declaration required in UI'
},
{
de: 'Hoehere Dokumentationspflichten',
en: 'Higher documentation requirements'
},
{
de: 'Spezielle Loeschverfahren erforderlich',
en: 'Special deletion procedures required'
},
{
de: 'Datenschutz-Folgenabschaetzung (DSFA) empfohlen',
en: 'Data Protection Impact Assessment (DPIA) recommended'
}
]
}
/**
* Spezielle Hinweise für Beschäftigtendaten (BDSG § 26)
*/
export interface EmployeeDataWarning {
title: LocalizedText
description: LocalizedText
requirements: LocalizedText[]
}
export const EMPLOYEE_DATA_WARNING: EmployeeDataWarning = {
title: {
de: 'Beschaeftigtendaten (BDSG § 26)',
en: 'Employee Data (BDSG § 26)'
},
description: {
de: 'Die Verarbeitung von Beschaeftigtendaten unterliegt besonderen Anforderungen nach § 26 BDSG.',
en: 'Processing of employee data is subject to special requirements under § 26 BDSG (German Federal Data Protection Act).'
},
requirements: [
{
de: 'Aufbewahrungspflichten fuer Lohnunterlagen (6-10 Jahre)',
en: 'Retention obligations for payroll records (6-10 years)'
},
{
de: 'Betriebsrat-Beteiligung ggf. erforderlich',
en: 'Works council involvement may be required'
},
{
de: 'Verarbeitung nur fuer Zwecke des Beschaeftigungsverhaeltnisses',
en: 'Processing only for employment purposes'
},
{
de: 'Besondere Vertraulichkeit bei Gesundheitsdaten',
en: 'Special confidentiality for health data'
}
]
}
/**
* Spezielle Hinweise für KI-Daten (AI Act)
*/
export interface AIDataWarning {
title: LocalizedText
description: LocalizedText
requirements: LocalizedText[]
}
export const AI_DATA_WARNING: AIDataWarning = {
title: {
de: 'KI-Daten (AI Act)',
en: 'AI Data (AI Act)'
},
description: {
de: 'Die Verarbeitung von KI-bezogenen Daten unterliegt den Transparenzpflichten des AI Acts.',
en: 'Processing of AI-related data is subject to AI Act transparency requirements.'
},
requirements: [
{
de: 'Transparenzpflichten bei KI-Verarbeitung',
en: 'Transparency obligations for AI processing'
},
{
de: 'Kennzeichnung von KI-generierten Inhalten',
en: 'Labeling of AI-generated content'
},
{
de: 'Dokumentation der KI-Modell-Nutzung',
en: 'Documentation of AI model usage'
},
{
de: 'Keine Verwendung fuer unerlaubtes Training ohne Einwilligung',
en: 'No use for unauthorized training without consent'
}
]
}
/**
* Risk Level Styling
*/
export const RISK_LEVEL_STYLING: Record<RiskLevel, { label: LocalizedText; color: string; bgColor: string }> = {
LOW: {
label: { de: 'Niedrig', en: 'Low' },
color: 'text-green-700',
bgColor: 'bg-green-100'
},
MEDIUM: {
label: { de: 'Mittel', en: 'Medium' },
color: 'text-yellow-700',
bgColor: 'bg-yellow-100'
},
HIGH: {
label: { de: 'Hoch', en: 'High' },
color: 'text-red-700',
bgColor: 'bg-red-100'
}
}