diff --git a/admin-compliance/app/sdk/company-profile/page.tsx b/admin-compliance/app/sdk/company-profile/page.tsx index 9c58cef..ee58e22 100644 --- a/admin-compliance/app/sdk/company-profile/page.tsx +++ b/admin-compliance/app/sdk/company-profile/page.tsx @@ -2608,6 +2608,8 @@ export default function CompanyProfilePage() { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(buildProfilePayload(false)), }) + // Sync draft to Redux so it persists across navigation + setCompanyProfile({ ...formData, isComplete: false, completedAt: null } as CompanyProfile) setDraftSaveStatus('saved') // Reset status after 3 seconds if (draftSaveTimerRef.current) clearTimeout(draftSaveTimerRef.current) diff --git a/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx b/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx index 75c0606..aa339a5 100644 --- a/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx +++ b/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx @@ -580,7 +580,13 @@ const DEPT_VALUE_TO_KEY: Record = { finanzen: ['dept_finance'], vertrieb: ['dept_sales'], marketing: ['dept_marketing'], + it: ['dept_it'], + recht: ['dept_recht'], kundenservice: ['dept_support'], + produktion: ['dept_produktion'], + logistik: ['dept_logistik'], + einkauf: ['dept_einkauf'], + facility: ['dept_facility'], } /** Mapping department key → scope question ID for Block 9 */ @@ -591,6 +597,12 @@ const DEPT_KEY_TO_QUESTION: Record = { dept_sales: 'dk_dept_sales', dept_marketing: 'dk_dept_marketing', dept_support: 'dk_dept_support', + dept_it: 'dk_dept_it', + dept_recht: 'dk_dept_recht', + dept_produktion: 'dk_dept_produktion', + dept_logistik: 'dk_dept_logistik', + dept_einkauf: 'dk_dept_einkauf', + dept_facility: 'dk_dept_facility', } function DatenkategorienBlock9({ diff --git a/admin-compliance/lib/sdk/compliance-scope-engine.ts b/admin-compliance/lib/sdk/compliance-scope-engine.ts index 5ae3600..97b93ab 100644 --- a/admin-compliance/lib/sdk/compliance-scope-engine.ts +++ b/admin-compliance/lib/sdk/compliance-scope-engine.ts @@ -1062,32 +1062,32 @@ export class ComplianceScopeEngine { * Bestimmt den Multiplikator für eine Antwort (0.0 - 1.0) */ private getAnswerMultiplier(answer: ScopeProfilingAnswer): number { - const { questionId, answerValue } = answer + const { questionId, value } = answer // Boolean - if (typeof answerValue === 'boolean') { - return answerValue ? 1.0 : 0.0 + if (typeof value === 'boolean') { + return value ? 1.0 : 0.0 } // Number - if (typeof answerValue === 'number') { - return this.normalizeNumericAnswer(questionId, answerValue) + if (typeof value === 'number') { + return this.normalizeNumericAnswer(questionId, value) } // Single choice - if (typeof answerValue === 'string') { + if (typeof value === 'string') { const multipliers = ANSWER_MULTIPLIERS[questionId] - if (multipliers && multipliers[answerValue] !== undefined) { - return multipliers[answerValue] + if (multipliers && multipliers[value] !== undefined) { + return multipliers[value] } return 0.5 // Fallback } // Multi choice - if (Array.isArray(answerValue)) { - if (answerValue.length === 0) return 0.0 + if (Array.isArray(value)) { + if (value.length === 0) return 0.0 // Simplified: count selected items - return Math.min(answerValue.length / 5, 1.0) + return Math.min(value.length / 5, 1.0) } return 0.0 @@ -1110,7 +1110,7 @@ export class ComplianceScopeEngine { */ evaluateHardTriggers(answers: ScopeProfilingAnswer[], companyProfile?: CompanyProfile | null): TriggeredHardTrigger[] { const triggered: TriggeredHardTrigger[] = [] - const answerMap = new Map(answers.map((a) => [a.questionId, a.answerValue])) + const answerMap = new Map(answers.map((a) => [a.questionId, a.value])) for (const rule of HARD_TRIGGER_RULES) { const isTriggered = this.checkTriggerCondition(rule, answerMap, answers, companyProfile) @@ -1186,39 +1186,39 @@ export class ComplianceScopeEngine { } // Standard answer-based triggers - const answerValue = answerMap.get(rule.questionId) - if (answerValue === undefined) return false + const value = answerMap.get(rule.questionId) + if (value === undefined) return false // Basis-Check let baseCondition = false switch (rule.condition) { case 'EQUALS': - baseCondition = answerValue === rule.conditionValue + baseCondition = value === rule.conditionValue break case 'CONTAINS': - if (Array.isArray(answerValue)) { - baseCondition = answerValue.includes(rule.conditionValue) - } else if (typeof answerValue === 'string') { - baseCondition = answerValue.includes(rule.conditionValue) + if (Array.isArray(value)) { + baseCondition = value.includes(rule.conditionValue) + } else if (typeof value === 'string') { + baseCondition = value.includes(rule.conditionValue) } break case 'IN': if (Array.isArray(rule.conditionValue)) { - baseCondition = rule.conditionValue.includes(answerValue) + baseCondition = rule.conditionValue.includes(value) } break case 'GREATER_THAN': - if (typeof answerValue === 'number' && typeof rule.conditionValue === 'number') { - baseCondition = answerValue > rule.conditionValue - } else if (typeof answerValue === 'string') { + if (typeof value === 'number' && typeof rule.conditionValue === 'number') { + baseCondition = value > rule.conditionValue + } else if (typeof value === 'string') { // Parse employee count from string like "1000+" - const parsed = this.parseEmployeeCount(answerValue) + const parsed = this.parseEmployeeCount(value) baseCondition = parsed > (rule.conditionValue as number) } break case 'NOT_EQUALS': - baseCondition = answerValue !== rule.conditionValue + baseCondition = value !== rule.conditionValue break } @@ -1404,7 +1404,7 @@ export class ComplianceScopeEngine { level: ComplianceDepthLevel ): RiskFlag[] { const flags: RiskFlag[] = [] - const answerMap = new Map(answers.map((a) => [a.questionId, a.answerValue])) + const answerMap = new Map(answers.map((a) => [a.questionId, a.value])) // Process Maturity Gaps (Kategorie I Trigger) const maturityRules = HARD_TRIGGER_RULES.filter((r) => r.category === 'process_maturity') @@ -1503,7 +1503,7 @@ export class ComplianceScopeEngine { level: ComplianceDepthLevel ): ScopeGap[] { const gaps: ScopeGap[] = [] - const answerMap = new Map(answers.map((a) => [a.questionId, a.answerValue])) + const answerMap = new Map(answers.map((a) => [a.questionId, a.value])) // DSFA Gap (bei L3+) if (getDepthLevelNumeric(level) >= 3) { diff --git a/admin-compliance/lib/sdk/compliance-scope-profiling.ts b/admin-compliance/lib/sdk/compliance-scope-profiling.ts index a9ce4e0..0a80501 100644 --- a/admin-compliance/lib/sdk/compliance-scope-profiling.ts +++ b/admin-compliance/lib/sdk/compliance-scope-profiling.ts @@ -21,14 +21,16 @@ export const PROFILE_AUTOFILL_QUESTION_IDS = [ 'org_industry', 'org_business_model', 'org_has_dsb', + 'org_cert_target', + 'data_volume', 'prod_type', 'prod_webshop', ] as const const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { id: 'organisation', - title: 'Organisation & Reife', - description: 'Grundlegende Informationen zu Ihrer Organisation und Compliance-Zielen', + title: 'Kunden & Nutzer', + description: 'Informationen zu Ihren Kunden und Nutzern', order: 1, questions: [ { @@ -46,22 +48,6 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { ], scoreWeights: { risk: 6, complexity: 7, assurance: 6 }, }, - { - id: 'org_cert_target', - type: 'multi', - question: 'Welche Zertifizierungen streben Sie an oder besitzen Sie bereits?', - helpText: 'Mehrfachauswahl möglich. Zertifizierungen erhöhen den Assurance-Bedarf', - required: false, - options: [ - { value: 'ISO27001', label: 'ISO 27001 (Informationssicherheit)' }, - { value: 'ISO27701', label: 'ISO 27701 (Datenschutz-Erweiterung)' }, - { value: 'TISAX', label: 'TISAX (Automotive)' }, - { value: 'SOC2', label: 'SOC 2 (US-Standard)' }, - { value: 'BSI-Grundschutz', label: 'BSI IT-Grundschutz' }, - { value: 'Keine', label: 'Keine Zertifizierung geplant' }, - ], - scoreWeights: { risk: 3, complexity: 5, assurance: 10 }, - }, ], } @@ -70,7 +56,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { */ const BLOCK_2_DATA: ScopeQuestionBlock = { id: 'data', - title: 'Daten & Betroffene', + title: 'Datenverarbeitung', description: 'Art und Umfang der verarbeiteten personenbezogenen Daten', order: 2, questions: [ @@ -131,21 +117,6 @@ const BLOCK_2_DATA: ScopeQuestionBlock = { mapsToVVTQuestion: 'dept_finance', mapsToLFQuestion: 'data-buchhaltung', }, - { - id: 'data_volume', - type: 'single', - question: 'Wie viele Personendatensätze verarbeiten Sie insgesamt?', - helpText: 'Schätzen Sie die Gesamtzahl betroffener Personen', - required: true, - options: [ - { value: '<1000', label: 'Unter 1.000' }, - { value: '1000-10000', label: '1.000 bis 10.000' }, - { value: '10000-100000', label: '10.000 bis 100.000' }, - { value: '100000-1000000', label: '100.000 bis 1 Mio.' }, - { value: '>1000000', label: 'Über 1 Mio.' }, - ], - scoreWeights: { risk: 7, complexity: 6, assurance: 6 }, - }, ], } @@ -224,7 +195,7 @@ const BLOCK_3_PROCESSING: ScopeQuestionBlock = { */ const BLOCK_4_TECH: ScopeQuestionBlock = { id: 'tech', - title: 'Technik, Hosting & Transfers', + title: 'Hosting & Verarbeitung', description: 'Technische Infrastruktur und Datenübermittlung', order: 4, questions: [ @@ -354,7 +325,7 @@ const BLOCK_5_PROCESSES: ScopeQuestionBlock = { */ const BLOCK_6_PRODUCT: ScopeQuestionBlock = { id: 'product', - title: 'Produktkontext', + title: 'Website und Services', description: 'Spezifische Merkmale Ihrer Produkte und Services', order: 6, questions: [ @@ -433,6 +404,20 @@ export const HIDDEN_SCORING_QUESTIONS: ScopeProfilingQuestion[] = [ required: false, scoreWeights: { risk: 5, complexity: 3, assurance: 6 }, }, + { + id: 'org_cert_target', + type: 'multi', + question: 'Zertifizierungen (aus Profil)', + required: false, + scoreWeights: { risk: 3, complexity: 5, assurance: 10 }, + }, + { + id: 'data_volume', + type: 'single', + question: 'Personendatensaetze (aus Profil)', + required: false, + scoreWeights: { risk: 7, complexity: 6, assurance: 6 }, + }, { id: 'prod_type', type: 'multi', @@ -494,10 +479,15 @@ const BLOCK_7_AI_SYSTEMS: ScopeQuestionBlock = { }, { id: 'ai_risk_assessment', - type: 'boolean', + type: 'single', question: 'Haben Sie eine KI-Risikobewertung nach EU AI Act durchgeführt?', helpText: 'Risikoeinstufung der KI-Systeme (verboten / hochriskant / begrenzt / minimal)', required: false, + options: [ + { value: 'yes', label: 'Ja' }, + { value: 'no', label: 'Nein' }, + { value: 'not_yet', label: 'Noch nicht' }, + ], scoreWeights: { risk: -5, complexity: 3, assurance: 8 }, }, ], @@ -668,6 +658,84 @@ const BLOCK_9_DATENKATEGORIEN: ScopeQuestionBlock = { scoreWeights: { risk: 5, complexity: 3, assurance: 4 }, mapsToVVTQuestion: 'dept_support_categories', }, + { + id: 'dk_dept_it', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihre IT-Abteilung?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer IT / Administration', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_it.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 7, complexity: 5, assurance: 6 }, + mapsToVVTQuestion: 'dept_it_categories', + }, + { + id: 'dk_dept_recht', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihre Rechtsabteilung?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Recht / Compliance', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_recht.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 6, complexity: 4, assurance: 6 }, + mapsToVVTQuestion: 'dept_recht_categories', + }, + { + id: 'dk_dept_produktion', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihre Produktion?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Produktion / Fertigung', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_produktion.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 6, complexity: 4, assurance: 5 }, + mapsToVVTQuestion: 'dept_produktion_categories', + }, + { + id: 'dk_dept_logistik', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihre Logistik?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Logistik / Versand', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_logistik.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 5, complexity: 3, assurance: 4 }, + mapsToVVTQuestion: 'dept_logistik_categories', + }, + { + id: 'dk_dept_einkauf', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihr Einkauf?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Einkauf / Beschaffung', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_einkauf.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 4, complexity: 3, assurance: 4 }, + mapsToVVTQuestion: 'dept_einkauf_categories', + }, + { + id: 'dk_dept_facility', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihr Facility Management?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Facility Management', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_facility.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 5, complexity: 3, assurance: 4 }, + mapsToVVTQuestion: 'dept_facility_categories', + }, ], } diff --git a/admin-compliance/lib/sdk/vvt-profiling.ts b/admin-compliance/lib/sdk/vvt-profiling.ts index be129b7..294e211 100644 --- a/admin-compliance/lib/sdk/vvt-profiling.ts +++ b/admin-compliance/lib/sdk/vvt-profiling.ts @@ -380,6 +380,76 @@ export const DEPARTMENT_DATA_CATEGORIES: Record = { id: 'TECHNICAL_DATA', label: 'Technische Daten', info: 'Systeminfos, Logdateien, Screenshots bei Fehlermeldungen' }, ] }, + dept_it: { + label: 'IT / Administration', + icon: '💻', + categories: [ + { id: 'USER_ACCOUNTS', label: 'Benutzerkonten', info: 'Benutzernamen, Passwort-Hashes, Rollen, Berechtigungen', isTypical: true }, + { id: 'LOG_DATA', label: 'Log-/Protokolldaten', info: 'System-Logs, Zugriffsprotokolle, Fehlerprotokolle, IP-Adressen', isTypical: true }, + { id: 'DEVICE_DATA', label: 'Geraetedaten', info: 'Inventar, Seriennummern, MAC-Adressen, zugewiesene Geraete', isTypical: true }, + { id: 'NETWORK_DATA', label: 'Netzwerkdaten', info: 'IP-Adressen, VPN-Verbindungen, Firewall-Logs', isTypical: true }, + { id: 'EMAIL_DATA', label: 'E-Mail-/Kommunikation', info: 'E-Mail-Konten, Verteiler, Archivierung', isTypical: true }, + { id: 'BACKUP_DATA', label: 'Backup-Daten', info: 'Sicherungskopien mit personenbezogenen Inhalten' }, + { id: 'MONITORING_DATA', label: 'Monitoring-Daten', info: 'Systemueberwachung, Performance-Metriken mit Nutzerbezug' }, + ] + }, + dept_recht: { + label: 'Recht / Compliance', + icon: '⚖️', + categories: [ + { id: 'CONTRACT_DATA', label: 'Vertragsdaten', info: 'Vertraege, NDAs, AVVs, Rahmenvereinbarungen', isTypical: true }, + { id: 'NAME', label: 'Ansprechpartner', info: 'Namen, Kontaktdaten von Vertragspartnern und Anwaelten', isTypical: true }, + { id: 'COMPLIANCE_DATA', label: 'Compliance-Daten', info: 'Datenschutzanfragen, Meldungen, Audit-Ergebnisse', isTypical: true }, + { id: 'INCIDENT_DATA', label: 'Vorfallsdaten', info: 'Datenschutzvorfaelle, Beschwerden, Meldungen an Aufsichtsbehoerden' }, + { id: 'CONSENT_DATA', label: 'Einwilligungsdaten', info: 'Consent-Nachweise, Widerrufe, Opt-in/Opt-out-Protokolle' }, + { id: 'CRIMINAL_DATA', label: 'Strafrechtliche Daten', info: 'Fuehrungszeugnisse, Compliance-Pruefungen (Art. 10 DSGVO)', isArt9: true }, + ] + }, + dept_produktion: { + label: 'Produktion / Fertigung', + icon: '🏭', + categories: [ + { id: 'EMPLOYMENT_DATA', label: 'Schichtplaene', info: 'Schichtzuordnung, Arbeitszeiten, Anwesenheitslisten', isTypical: true }, + { id: 'NAME', label: 'Mitarbeiterstammdaten', info: 'Name, Personalnummer, Qualifikation, Maschinenberechtigungen', isTypical: true }, + { id: 'HEALTH_DATA', label: 'Arbeitsschutzdaten', info: 'Arbeitsmedizinische Vorsorge, Unfallmeldungen, Gefahrstoff-Expositionen', isArt9: true }, + { id: 'ACCESS_DATA', label: 'Zugangsdaten', info: 'Zutrittskontrolle, Badge-Protokolle, Bereichsberechtigungen', isTypical: true }, + { id: 'QUALITY_DATA', label: 'Qualitaetsdaten', info: 'Pruefprotokolle mit Pruefernamen, Fehlerberichte' }, + { id: 'PHOTO_VIDEO', label: 'Bild-/Videodaten', info: 'Kameraueberwachung in Produktionsbereichen' }, + ] + }, + dept_logistik: { + label: 'Logistik / Versand', + icon: '🚚', + categories: [ + { id: 'NAME', label: 'Empfaengerdaten', info: 'Name, Lieferadresse, Telefon fuer Zustellung', isTypical: true }, + { id: 'ADDRESS', label: 'Versandadressen', info: 'Liefer-/Abholadressen, Paketshop-Zuordnung', isTypical: true }, + { id: 'TRACKING_DATA', label: 'Sendungsverfolgung', info: 'Tracking-Nummern, Zustellstatus, Lieferzeitfenster', isTypical: true }, + { id: 'DRIVER_DATA', label: 'Fahrerdaten', info: 'Fahrerlaubnis, Touren, GPS-Standortdaten', isTypical: true }, + { id: 'CUSTOMS_DATA', label: 'Zolldaten', info: 'Zollerklaerungen, EORI-Nummern bei internationalem Versand' }, + ] + }, + dept_einkauf: { + label: 'Einkauf / Beschaffung', + icon: '🛒', + categories: [ + { id: 'NAME', label: 'Lieferantenkontakte', info: 'Ansprechpartner, E-Mail, Telefon der Lieferanten', isTypical: true }, + { id: 'CONTRACT_DATA', label: 'Vertragsdaten', info: 'Rahmenvertraege, Bestellungen, Konditionen, Laufzeiten', isTypical: true }, + { id: 'BANK_ACCOUNT', label: 'Bankverbindungen', info: 'IBAN, BIC der Lieferanten fuer Zahlungsabwicklung', isTypical: true }, + { id: 'TAX_ID', label: 'Steuer-IDs', info: 'USt-IdNr., Steuernummer der Lieferanten', isTypical: true }, + { id: 'COMPLIANCE_DATA', label: 'Lieferantenbewertung', info: 'Qualitaetsbewertungen, Audit-Ergebnisse, Zertifizierungen' }, + ] + }, + dept_facility: { + label: 'Facility Management', + icon: '🏢', + categories: [ + { id: 'ACCESS_DATA', label: 'Zutrittsdaten', info: 'Schluesselausgaben, Badge-Protokolle, Zutrittslisten', isTypical: true }, + { id: 'NAME', label: 'Dienstleisterkontakte', info: 'Reinigung, Wartung, Sicherheitsdienst — Namen und Kontaktdaten', isTypical: true }, + { id: 'PHOTO_VIDEO', label: 'Videoueberwachung', info: 'Kameraaufnahmen in/an Gebaeuden, Parkplaetzen', isTypical: true }, + { id: 'VISITOR_DATA', label: 'Besucherdaten', info: 'Name, Firma, Besuchsgrund, Ein-/Austrittszeiten', isTypical: true }, + { id: 'HEALTH_DATA', label: 'Gesundheits-/Sicherheitsdaten', info: 'Unfallmeldungen, Evakuierungslisten, Ersthelfer-Register', isArt9: true }, + ] + }, } // =============================================================================