import { DEFAULTED_FIELD_LABELS, DefaultedField, REQUIRED_DEFAULTED_FIELDS, } from "@/lib/policy-defaults" import { PolicyDefaults } from "@/types/privacy-policy" /** * Prüft, ob eine Erklärung die Pflichtangaben aus Art. 13/14 DSGVO enthält. * * Die Prüfung greift beim Veröffentlichen, nicht beim Speichern: Ein Entwurf * darf unvollständig sein, eine veröffentlichte Erklärung nicht. So lassen sich * bestehende Datensätze weiter bearbeiten, ohne dass die Anwendung Angaben * erfindet, die nur die Fachabteilung kennt. */ /** Eine Angabe, die zur Veröffentlichung vorliegen muss. */ interface RequiredField { field: string label: string article: string } /** * Angaben, die die Erklärung selbst mitbringen muss – sie fallen je * Verarbeitungstätigkeit unterschiedlich aus und haben deshalb keinen Standard. */ export const REQUIRED_OWN_FIELDS: RequiredField[] = [ { field: "title", label: "Titel", article: "" }, { field: "processingDescription", label: "Bezeichnung der Verarbeitungstätigkeit", article: "", }, { field: "processingPurposes", label: "Zweck der Datenerhebung", article: "Art. 13 Abs. 1 lit. c", }, { field: "legalBasis", label: "Rechtsgrundlage der Verarbeitung", article: "Art. 13 Abs. 1 lit. c", }, { field: "recipients", label: "Geplante Empfänger der Daten", article: "Art. 13 Abs. 1 lit. e" }, { field: "storageDuration", label: "Speicherdauer", article: "Art. 13 Abs. 2 lit. a", }, ] /** Artikelangabe je Standard-Baustein, für verständliche Fehlermeldungen. */ export const DEFAULTED_FIELD_ARTICLES: Partial> = { responsible: "Art. 13 Abs. 1 lit. a", contactDPO: "Art. 13 Abs. 1 lit. b", dataSubjectsRights: "Art. 13 Abs. 2 lit. b", complaintRight: "Art. 13 Abs. 2 lit. d", withdrawalRight: "Art. 13 Abs. 2 lit. c", objectionRight: "Art. 21 Abs. 4", thirdCountryTransfer: "Art. 13 Abs. 1 lit. f", automatedDecision: "Art. 13 Abs. 2 lit. f", } /** Eine fehlende Pflichtangabe. */ export interface MissingField { field: string label: string article: string /** Bei einem Baustein: Der Standardtext ist ebenfalls leer. */ fromDefaults: boolean } /** Gibt den getrimmten Text zurück oder `null`. */ function textOrNull(value: unknown) { const trimmed = typeof value === "string" ? value.trim() : "" return trimmed ? trimmed : null } /** * Ermittelt, welche Pflichtangaben einer Erklärung zur Veröffentlichung fehlen. * * `policy` enthält die Werte der Erklärung selbst – bei den Bausteinen also nur * eine etwaige Abweichung. Fehlt dort ein Wert, zählt der Standardtext. */ export function missingRequiredFields( policy: Record, defaults: PolicyDefaults ): MissingField[] { const missing: MissingField[] = [] // Angaben, die nur die Erklärung selbst liefern kann for (const { field, label, article } of REQUIRED_OWN_FIELDS) { if (!textOrNull(policy[field])) { missing.push({ field, label, article, fromDefaults: false }) } } // Bausteine: eigener Text oder Standardtext muss vorhanden sein for (const field of REQUIRED_DEFAULTED_FIELDS) { if (!textOrNull(policy[field]) && !textOrNull(defaults[field])) { missing.push({ field, label: DEFAULTED_FIELD_LABELS[field], article: DEFAULTED_FIELD_ARTICLES[field] ?? "", fromDefaults: true, }) } } // Art. 14 greift nur, wenn die Daten nicht bei der betroffenen Person selbst // erhoben wurden – erkennbar daran, dass eine Herkunft angegeben ist. if (textOrNull(policy.dataSource) && !textOrNull(policy.dataCategories)) { missing.push({ field: "dataCategories", label: "Kategorien der verarbeiteten Daten", article: "Art. 14 Abs. 1 lit. d", fromDefaults: false, }) } return missing } /** Fehlende Angabe als Zeile für eine Fehlermeldung. */ export function describeMissing(entry: MissingField) { const artikel = entry.article ? ` (${entry.article})` : "" const hinweis = entry.fromDefaults ? " – weder hier noch als Standardtext hinterlegt" : "" return `${entry.label}${artikel} fehlt${hinweis}` }