import { PolicyDefaults, PrivacyPolicy } from '@/types/privacy-policy' /** * Felder, deren Text normalerweise für alle Erklärungen gleich ist und daher * zentral unter „Standardtexte“ gepflegt wird. * * In einer einzelnen Erklärung bedeutet `null`/leer: der Standard gilt. Steht * dort ein Wert, weicht genau diese Erklärung bewusst ab (Sonderfall). */ export const DEFAULTED_FIELDS = [ 'responsible', 'contactDPO', 'dataSubjectsRights', 'complaintRight', 'withdrawalRight', 'objectionRight', 'thirdCountryTransfer', 'automatedDecision', 'provisionObligation', 'provisionConsequences', ] as const /** Name eines Feldes, das aus den Standardtexten gespeist werden kann. */ export type DefaultedField = (typeof DEFAULTED_FIELDS)[number] /** * Bausteine, ohne die eine Erklärung nach Art. 13 DSGVO nicht auskommt. Sie * müssen entweder im Standard oder in der Erklärung selbst gefüllt sein. */ export const REQUIRED_DEFAULTED_FIELDS: DefaultedField[] = [ 'responsible', 'contactDPO', 'dataSubjectsRights', 'complaintRight', 'withdrawalRight', // Auf Drittländer und automatisierte Entscheidungen muss auch dann // hingewiesen werden, wenn es beides nicht gibt – dann eben mit einem // ausdrücklichen „findet nicht statt“. 'objectionRight', 'thirdCountryTransfer', 'automatedDecision', ] /** Beschriftungen der Standard-Bausteine – identisch in Formular und Ansicht. */ export const DEFAULTED_FIELD_LABELS: Record = { responsible: 'Verantwortlicher im Sinne der DSGVO', contactDPO: 'Kontaktdaten der/des Datenschutzbeauftragten', dataSubjectsRights: 'Betroffenen-Rechte', complaintRight: 'Beschwerderecht bei der Aufsichtsbehörde', withdrawalRight: 'Widerrufsrecht bei Einwilligung', objectionRight: 'Widerspruchsrecht', thirdCountryTransfer: 'Übermittlung in Drittländer', automatedDecision: 'Automatisierte Entscheidungsfindung', provisionObligation: 'Ihre Pflicht zur Bereitstellung der Daten', provisionConsequences: 'Folgen, wenn Sie die Daten nicht angeben', } /** Leerer Standarddatensatz – Fallback, solange nichts gepflegt wurde. */ export const EMPTY_POLICY_DEFAULTS: PolicyDefaults = { responsible: '', contactDPO: '', dataSubjectsRights: '', complaintRight: '', withdrawalRight: '', objectionRight: '', thirdCountryTransfer: '', automatedDecision: '', provisionObligation: '', provisionConsequences: '', updatedAt: '', } /** Gibt den getrimmten Text zurück oder `null`, wenn nichts Sinnvolles drinsteht. */ function textOrNull(value?: string | null) { const trimmed = value?.trim() return trimmed ? trimmed : null } /** * Setzt in eine gespeicherte Erklärung die Standardtexte ein, wo sie keinen * eigenen Wert hat, und vermerkt in `overrides`, welche Felder abweichen. * * Die zurückgegebenen Felder tragen immer den tatsächlich anzuzeigenden Text – * öffentliche Ansicht, Druckfassung und API-Konsumenten brauchen die * Standardtexte deshalb nicht selbst zu kennen. */ export function applyPolicyDefaults>( policy: T, defaults: PolicyDefaults ): T & { overrides: DefaultedField[] } { const merged = { ...policy } as Record const overrides: DefaultedField[] = [] for (const field of DEFAULTED_FIELDS) { const own = textOrNull(policy[field] as string | null | undefined) if (own) { overrides.push(field) merged[field] = own } else { merged[field] = textOrNull(defaults[field]) } } return { ...(merged as T), overrides } } /** * Prüft, ob alle Pflicht-Bausteine gefüllt sind – entweder in der Erklärung * selbst oder im Standard. Gibt die Namen der fehlenden Felder zurück. */ export function missingDefaultedFields( policy: Partial>, defaults: PolicyDefaults ): DefaultedField[] { return REQUIRED_DEFAULTED_FIELDS.filter( (field) => !textOrNull(policy[field]) && !textOrNull(defaults[field]) ) } /** Der Text, der für ein Feld tatsächlich gilt – eigener Wert vor Standard. */ export function effectiveValue( policy: Pick | Partial>, defaults: PolicyDefaults, field: DefaultedField ) { return textOrNull(policy[field]) ?? textOrNull(defaults[field]) }