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:
1084
admin-compliance/lib/sdk/einwilligungen/catalog/data-points.yml
Normal file
1084
admin-compliance/lib/sdk/einwilligungen/catalog/data-points.yml
Normal file
File diff suppressed because it is too large
Load Diff
308
admin-compliance/lib/sdk/einwilligungen/catalog/loader.ts
Normal file
308
admin-compliance/lib/sdk/einwilligungen/catalog/loader.ts
Normal 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))
|
||||
)
|
||||
}
|
||||
669
admin-compliance/lib/sdk/einwilligungen/context.tsx
Normal file
669
admin-compliance/lib/sdk/einwilligungen/context.tsx
Normal 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 }
|
||||
493
admin-compliance/lib/sdk/einwilligungen/export/docx.ts
Normal file
493
admin-compliance/lib/sdk/einwilligungen/export/docx.ts
Normal 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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 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`
|
||||
}
|
||||
8
admin-compliance/lib/sdk/einwilligungen/export/index.ts
Normal file
8
admin-compliance/lib/sdk/einwilligungen/export/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Einwilligungen Export Module
|
||||
*
|
||||
* PDF and DOCX export functionality for Privacy Policy documents.
|
||||
*/
|
||||
|
||||
export * from './pdf'
|
||||
export * from './docx'
|
||||
505
admin-compliance/lib/sdk/einwilligungen/export/pdf.ts
Normal file
505
admin-compliance/lib/sdk/einwilligungen/export/pdf.ts
Normal 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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 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`
|
||||
}
|
||||
@@ -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'
|
||||
@@ -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,
|
||||
}
|
||||
79
admin-compliance/lib/sdk/einwilligungen/index.ts
Normal file
79
admin-compliance/lib/sdk/einwilligungen/index.ts
Normal 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'
|
||||
838
admin-compliance/lib/sdk/einwilligungen/types.ts
Normal file
838
admin-compliance/lib/sdk/einwilligungen/types.ts
Normal 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'
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user