/** * Privacy Policy Section Generators (Sections 1-4) * * Generiert die ersten 4 Abschnitte der Datenschutzerklaerung (DSI). * Sections 5-7 live in privacy-policy-sections-part2.ts. */ import { DataPoint, DataPointCategory, CompanyInfo, PrivacyPolicySection, SupportedLanguage, LocalizedText, RetentionMatrixEntry, LegalBasis, CATEGORY_METADATA, LEGAL_BASIS_INFO, RETENTION_PERIOD_INFO, } from '../types' // Re-export sections 5-7 for backward compatibility export { generateRecipientsSection, generateRetentionSection, generateSpecialCategoriesSection, generateRightsSection, } from './privacy-policy-sections-part2' // ============================================================================= // KONSTANTEN // ============================================================================= const ALL_CATEGORIES: DataPointCategory[] = [ 'MASTER_DATA', 'CONTACT_DATA', 'AUTHENTICATION', 'CONSENT', 'COMMUNICATION', 'PAYMENT', 'USAGE_DATA', 'LOCATION', 'DEVICE_DATA', 'MARKETING', 'ANALYTICS', 'SOCIAL_MEDIA', 'HEALTH_DATA', 'EMPLOYEE_DATA', 'CONTRACT_DATA', 'LOG_DATA', 'AI_DATA', 'SECURITY', ] const ALL_LEGAL_BASES: LegalBasis[] = [ 'CONTRACT', 'CONSENT', 'EXPLICIT_CONSENT', 'LEGITIMATE_INTEREST', 'LEGAL_OBLIGATION', 'VITAL_INTERESTS', 'PUBLIC_INTEREST', ] // ============================================================================= // HELPER FUNCTIONS // ============================================================================= function t(text: LocalizedText, language: SupportedLanguage): string { return text[language] } function groupByCategory(dataPoints: DataPoint[]): Map { const grouped = new Map() for (const dp of dataPoints) { const existing = grouped.get(dp.category) || [] grouped.set(dp.category, [...existing, dp]) } return grouped } function groupByLegalBasis(dataPoints: DataPoint[]): Map { const grouped = new Map() for (const dp of dataPoints) { const existing = grouped.get(dp.legalBasis) || [] grouped.set(dp.legalBasis, [...existing, dp]) } return grouped } function extractThirdParties(dataPoints: DataPoint[]): string[] { const thirdParties = new Set() for (const dp of dataPoints) { for (const recipient of dp.thirdPartyRecipients) { thirdParties.add(recipient) } } return Array.from(thirdParties).sort() } export function formatDate(date: Date, language: SupportedLanguage): string { return date.toLocaleDateString(language === 'de' ? 'de-DE' : 'en-US', { year: 'numeric', month: 'long', day: 'numeric', }) } // ============================================================================= // SECTION GENERATORS // ============================================================================= export 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, } } export 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[] = [] 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) 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:', } 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, } } export function generatePurposesSection( dataPoints: DataPoint[], language: SupportedLanguage ): PrivacyPolicySection { const title: LocalizedText = { de: '3. Zwecke der Datenverarbeitung', en: '3. Purposes of Data Processing', } const purposes = new Map() 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, } } export 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[] = [] 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) 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, } }