From 9ec5a88af95024e1a1570b27a5cd65be006e764f Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 10 Feb 2026 13:42:31 +0100 Subject: [PATCH] =?UTF-8?q?fix(sdk):=20Fix=20compliance=20scope=20wizard?= =?UTF-8?q?=20=E2=80=94=20missing=20labels,=20broken=20prefill,=20invisibl?= =?UTF-8?q?e=20helpText?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename `label` to `question` in profiling data (35 questions) to match ScopeProfilingQuestion type — fixes missing question headings - Sync ScopeWizardTab props with page.tsx (onEvaluate/canEvaluate/isEvaluating instead of onComplete/companyProfile/currentLevel) - Load companyProfile from SDK context instead of expecting it as prop - Auto-prefill from company profile on mount when answers are empty - Add "Aus Profil" badge for prefilled questions - Replace title-only helpText tooltip with click-to-expand visible info box - Fix ScopeQuestionBlockId to match actual block IDs in data - Add `order` field to ScopeQuestionBlock type - Fix completionStats to count against total required questions Co-Authored-By: Claude Opus 4.6 --- .../app/(sdk)/sdk/compliance-scope/page.tsx | 15 +- .../sdk/compliance-scope/ScopeWizardTab.tsx | 335 ++++++++++-------- .../lib/sdk/compliance-scope-profiling.ts | 70 ++-- admin-v2/lib/sdk/compliance-scope-types.ts | 16 +- 4 files changed, 243 insertions(+), 193 deletions(-) diff --git a/admin-v2/app/(sdk)/sdk/compliance-scope/page.tsx b/admin-v2/app/(sdk)/sdk/compliance-scope/page.tsx index 0f68b8f..73c8872 100644 --- a/admin-v2/app/(sdk)/sdk/compliance-scope/page.tsx +++ b/admin-v2/app/(sdk)/sdk/compliance-scope/page.tsx @@ -19,6 +19,7 @@ import { STORAGE_KEY } from '@/lib/sdk/compliance-scope-types' import { complianceScopeEngine } from '@/lib/sdk/compliance-scope-engine' +import { getAllQuestions } from '@/lib/sdk/compliance-scope-profiling' type TabId = 'overview' | 'wizard' | 'decision' | 'export' @@ -78,7 +79,7 @@ export default function ComplianceScopePage() { }, [scopeState, isLoading, dispatch]) // Handle answers change from wizard - const handleAnswersChange = useCallback((answers: Record) => { + const handleAnswersChange = useCallback((answers: ScopeProfilingAnswer[]) => { setScopeState(prev => ({ ...prev, answers, @@ -125,11 +126,11 @@ export default function ComplianceScopePage() { // Calculate completion statistics const completionStats = useMemo(() => { - const answers = scopeState.answers - const totalQuestions = Object.keys(answers).length - const answeredQuestions = Object.values(answers).filter( - answer => answer.value !== null && answer.value !== undefined - ).length + const allQuestions = getAllQuestions() + const requiredQuestions = allQuestions.filter(q => q.required) + const totalQuestions = requiredQuestions.length + const answeredIds = new Set(scopeState.answers.map(a => a.questionId)) + const answeredQuestions = requiredQuestions.filter(q => answeredIds.has(q.id)).length const completionPercentage = totalQuestions > 0 ? Math.round((answeredQuestions / totalQuestions) * 100) @@ -350,7 +351,7 @@ export default function ComplianceScopePage() { Active Tab: {activeTab}
- Total Answers: {Object.keys(scopeState.answers).length} + Total Answers: {scopeState.answers.length}
Answered: {completionStats.answered} ({completionStats.percentage}%) diff --git a/admin-v2/components/sdk/compliance-scope/ScopeWizardTab.tsx b/admin-v2/components/sdk/compliance-scope/ScopeWizardTab.tsx index f34c420..d060ce9 100644 --- a/admin-v2/components/sdk/compliance-scope/ScopeWizardTab.tsx +++ b/admin-v2/components/sdk/compliance-scope/ScopeWizardTab.tsx @@ -1,57 +1,97 @@ 'use client' -import React, { useState, useCallback } from 'react' -import type { ScopeProfilingAnswer, ComplianceDepthLevel } from '@/lib/sdk/compliance-scope-types' -import { SCOPE_QUESTION_BLOCKS, getBlockProgress, getTotalProgress, getAnswerValue, getAllQuestions } from '@/lib/sdk/compliance-scope-profiling' -import type { CompanyProfile } from '@/lib/sdk/types' -import { prefillFromCompanyProfile } from '@/lib/sdk/compliance-scope-profiling' -import { DEPTH_LEVEL_LABELS, DEPTH_LEVEL_COLORS } from '@/lib/sdk/compliance-scope-types' +import React, { useState, useCallback, useEffect, useMemo } from 'react' +import type { ScopeProfilingAnswer, ScopeProfilingQuestion } from '@/lib/sdk/compliance-scope-types' +import { SCOPE_QUESTION_BLOCKS, getBlockProgress, getTotalProgress, getAnswerValue, prefillFromCompanyProfile } from '@/lib/sdk/compliance-scope-profiling' +import { useSDK } from '@/lib/sdk' interface ScopeWizardTabProps { answers: ScopeProfilingAnswer[] onAnswersChange: (answers: ScopeProfilingAnswer[]) => void - onComplete: () => void - companyProfile: CompanyProfile | null - currentLevel: ComplianceDepthLevel | null + onEvaluate: () => void + canEvaluate: boolean + isEvaluating: boolean + completionStats: { total: number; answered: number; percentage: number; isComplete: boolean } } export function ScopeWizardTab({ answers, onAnswersChange, - onComplete, - companyProfile, - currentLevel, + onEvaluate, + canEvaluate, + isEvaluating, + completionStats, }: ScopeWizardTabProps) { const [currentBlockIndex, setCurrentBlockIndex] = useState(0) + const [expandedHelp, setExpandedHelp] = useState>(new Set()) const currentBlock = SCOPE_QUESTION_BLOCKS[currentBlockIndex] const totalProgress = getTotalProgress(answers) + // Load companyProfile from SDK context + const { state: sdkState } = useSDK() + const companyProfile = sdkState.companyProfile + + // Track which question IDs were prefilled from profile + const [prefilledIds, setPrefilledIds] = useState>(new Set()) + + // Auto-prefill from company profile on mount if answers are empty + useEffect(() => { + if (companyProfile && answers.length === 0) { + const prefilled = prefillFromCompanyProfile(companyProfile) + if (prefilled.length > 0) { + onAnswersChange(prefilled) + setPrefilledIds(new Set(prefilled.map(a => a.questionId))) + } + } + // Only run on mount + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + const handleAnswerChange = useCallback( - (questionId: string, value: any) => { + (questionId: string, value: string | string[] | boolean | number) => { const existingIndex = answers.findIndex((a) => a.questionId === questionId) if (existingIndex >= 0) { const newAnswers = [...answers] - newAnswers[existingIndex] = { questionId, value, answeredAt: new Date().toISOString() } + newAnswers[existingIndex] = { questionId, value } onAnswersChange(newAnswers) } else { - onAnswersChange([...answers, { questionId, value, answeredAt: new Date().toISOString() }]) + onAnswersChange([...answers, { questionId, value }]) + } + // Remove from prefilled set when user manually changes + if (prefilledIds.has(questionId)) { + setPrefilledIds(prev => { + const next = new Set(prev) + next.delete(questionId) + return next + }) } }, - [answers, onAnswersChange] + [answers, onAnswersChange, prefilledIds] ) const handlePrefillFromProfile = useCallback(() => { if (!companyProfile) return - const prefilledAnswers = prefillFromCompanyProfile(companyProfile, answers) - onAnswersChange(prefilledAnswers) - }, [companyProfile, answers, onAnswersChange]) + const prefilled = prefillFromCompanyProfile(companyProfile) + // Merge with existing answers: prefilled values for questions not yet answered + const existingIds = new Set(answers.map(a => a.questionId)) + const newAnswers = [...answers] + const newPrefilledIds = new Set(prefilledIds) + for (const pa of prefilled) { + if (!existingIds.has(pa.questionId)) { + newAnswers.push(pa) + newPrefilledIds.add(pa.questionId) + } + } + onAnswersChange(newAnswers) + setPrefilledIds(newPrefilledIds) + }, [companyProfile, answers, onAnswersChange, prefilledIds]) const handleNext = useCallback(() => { if (currentBlockIndex < SCOPE_QUESTION_BLOCKS.length - 1) { setCurrentBlockIndex(currentBlockIndex + 1) - } else { - onComplete() + } else if (canEvaluate) { + onEvaluate() } - }, [currentBlockIndex, onComplete]) + }, [currentBlockIndex, canEvaluate, onEvaluate]) const handleBack = useCallback(() => { if (currentBlockIndex > 0) { @@ -59,34 +99,87 @@ export function ScopeWizardTab({ } }, [currentBlockIndex]) - const renderQuestion = (question: any) => { + const toggleHelp = useCallback((questionId: string) => { + setExpandedHelp(prev => { + const next = new Set(prev) + if (next.has(questionId)) { + next.delete(questionId) + } else { + next.add(questionId) + } + return next + }) + }, []) + + // Check if a question was prefilled from company profile + const isPrefilledFromProfile = useCallback((questionId: string) => { + return prefilledIds.has(questionId) + }, [prefilledIds]) + + const renderHelpText = (question: ScopeProfilingQuestion) => { + if (!question.helpText) return null + + return ( + <> + + {expandedHelp.has(question.id) && ( +
+ + + + {question.helpText} +
+ )} + + ) + } + + const renderPrefilledBadge = (questionId: string) => { + if (!isPrefilledFromProfile(questionId)) return null + return ( + + Aus Profil + + ) + } + + const renderQuestion = (question: ScopeProfilingQuestion) => { const currentValue = getAnswerValue(answers, question.id) switch (question.type) { case 'boolean': return (
-
-
diff --git a/admin-v2/lib/sdk/compliance-scope-profiling.ts b/admin-v2/lib/sdk/compliance-scope-profiling.ts index a6548c2..d32b50a 100644 --- a/admin-v2/lib/sdk/compliance-scope-profiling.ts +++ b/admin-v2/lib/sdk/compliance-scope-profiling.ts @@ -19,7 +19,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { { id: 'org_employee_count', type: 'number', - label: 'Wie viele Mitarbeiter hat Ihre Organisation?', + question: 'Wie viele Mitarbeiter hat Ihre Organisation?', helpText: 'Geben Sie die Gesamtzahl aller Beschäftigten an (inkl. Teilzeit, Minijobs)', required: true, scoreWeights: { risk: 5, complexity: 8, assurance: 6 }, @@ -28,7 +28,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { { id: 'org_customer_count', type: 'single', - label: 'Wie viele Kunden/Nutzer betreuen Sie?', + question: 'Wie viele Kunden/Nutzer betreuen Sie?', helpText: 'Schätzen Sie die Anzahl aktiver Kunden oder Nutzer', required: true, options: [ @@ -43,7 +43,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { { id: 'org_annual_revenue', type: 'single', - label: 'Wie hoch ist Ihr jährlicher Umsatz?', + question: 'Wie hoch ist Ihr jährlicher Umsatz?', helpText: 'Wählen Sie die zutreffende Umsatzklasse', required: true, options: [ @@ -58,7 +58,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { { id: 'org_cert_target', type: 'multi', - label: 'Welche Zertifizierungen streben Sie an oder besitzen Sie bereits?', + question: 'Welche Zertifizierungen streben Sie an oder besitzen Sie bereits?', helpText: 'Mehrfachauswahl möglich. Zertifizierungen erhöhen den Assurance-Bedarf', required: false, options: [ @@ -74,7 +74,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { { id: 'org_industry', type: 'single', - label: 'In welcher Branche sind Sie tätig?', + question: 'In welcher Branche sind Sie tätig?', helpText: 'Ihre Branche beeinflusst Risikobewertung und regulatorische Anforderungen', required: true, options: [ @@ -96,7 +96,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { { id: 'org_business_model', type: 'single', - label: 'Was ist Ihr primäres Geschäftsmodell?', + question: 'Was ist Ihr primäres Geschäftsmodell?', helpText: 'B2C-Modelle haben höhere Datenschutzanforderungen', required: true, options: [ @@ -113,7 +113,7 @@ const BLOCK_1_ORGANISATION: ScopeQuestionBlock = { { id: 'org_has_dsb', type: 'boolean', - label: 'Haben Sie einen Datenschutzbeauftragten bestellt?', + question: 'Haben Sie einen Datenschutzbeauftragten bestellt?', helpText: 'Ein DSB ist bei mehr als 20 Personen mit regelmäßiger Datenverarbeitung Pflicht', required: true, scoreWeights: { risk: 5, complexity: 3, assurance: 6 }, @@ -133,7 +133,7 @@ const BLOCK_2_DATA: ScopeQuestionBlock = { { id: 'data_minors', type: 'boolean', - label: 'Verarbeiten Sie Daten von Minderjährigen?', + question: 'Verarbeiten Sie Daten von Minderjährigen?', helpText: 'Besondere Schutzpflichten für unter 16-Jährige (bzw. 13-Jährige bei Online-Diensten)', required: true, scoreWeights: { risk: 10, complexity: 5, assurance: 7 }, @@ -142,7 +142,7 @@ const BLOCK_2_DATA: ScopeQuestionBlock = { { id: 'data_art9', type: 'multi', - label: 'Verarbeiten Sie besondere Kategorien personenbezogener Daten (Art. 9 DSGVO)?', + question: 'Verarbeiten Sie besondere Kategorien personenbezogener Daten (Art. 9 DSGVO)?', helpText: 'Diese Daten unterliegen erhöhten Schutzanforderungen', required: true, options: [ @@ -162,7 +162,7 @@ const BLOCK_2_DATA: ScopeQuestionBlock = { { id: 'data_hr', type: 'boolean', - label: 'Verarbeiten Sie Personaldaten (HR)?', + question: 'Verarbeiten Sie Personaldaten (HR)?', helpText: 'Bewerberdaten, Gehälter, Leistungsbeurteilungen etc.', required: true, scoreWeights: { risk: 6, complexity: 4, assurance: 5 }, @@ -172,7 +172,7 @@ const BLOCK_2_DATA: ScopeQuestionBlock = { { id: 'data_communication', type: 'boolean', - label: 'Verarbeiten Sie Kommunikationsdaten (E-Mail, Chat, Telefonie)?', + question: 'Verarbeiten Sie Kommunikationsdaten (E-Mail, Chat, Telefonie)?', helpText: 'Inhalte oder Metadaten von Kommunikationsvorgängen', required: true, scoreWeights: { risk: 7, complexity: 5, assurance: 6 }, @@ -180,7 +180,7 @@ const BLOCK_2_DATA: ScopeQuestionBlock = { { id: 'data_financial', type: 'boolean', - label: 'Verarbeiten Sie Finanzdaten (Konten, Zahlungen)?', + question: 'Verarbeiten Sie Finanzdaten (Konten, Zahlungen)?', helpText: 'Bankdaten, Kreditkartendaten, Buchhaltungsdaten', required: true, scoreWeights: { risk: 8, complexity: 6, assurance: 7 }, @@ -190,7 +190,7 @@ const BLOCK_2_DATA: ScopeQuestionBlock = { { id: 'data_volume', type: 'single', - label: 'Wie viele Personendatensätze verarbeiten Sie insgesamt?', + question: 'Wie viele Personendatensätze verarbeiten Sie insgesamt?', helpText: 'Schätzen Sie die Gesamtzahl betroffener Personen', required: true, options: [ @@ -217,7 +217,7 @@ const BLOCK_3_PROCESSING: ScopeQuestionBlock = { { id: 'proc_tracking', type: 'boolean', - label: 'Setzen Sie Tracking oder Profiling ein?', + question: 'Setzen Sie Tracking oder Profiling ein?', helpText: 'Web-Analytics, Werbe-Tracking, Nutzungsprofile etc.', required: true, scoreWeights: { risk: 7, complexity: 6, assurance: 6 }, @@ -225,7 +225,7 @@ const BLOCK_3_PROCESSING: ScopeQuestionBlock = { { id: 'proc_adm_scoring', type: 'boolean', - label: 'Treffen Sie automatisierte Entscheidungen (Art. 22 DSGVO)?', + question: 'Treffen Sie automatisierte Entscheidungen (Art. 22 DSGVO)?', helpText: 'Scoring, Bonitätsprüfung, automatische Ablehnung ohne menschliche Beteiligung', required: true, scoreWeights: { risk: 9, complexity: 8, assurance: 8 }, @@ -233,7 +233,7 @@ const BLOCK_3_PROCESSING: ScopeQuestionBlock = { { id: 'proc_ai_usage', type: 'multi', - label: 'Setzen Sie KI-Systeme ein?', + question: 'Setzen Sie KI-Systeme ein?', helpText: 'KI-Einsatz kann zusätzliche Anforderungen (EU AI Act) auslösen', required: true, options: [ @@ -249,7 +249,7 @@ const BLOCK_3_PROCESSING: ScopeQuestionBlock = { { id: 'proc_data_combination', type: 'boolean', - label: 'Führen Sie Daten aus verschiedenen Quellen zusammen?', + question: 'Führen Sie Daten aus verschiedenen Quellen zusammen?', helpText: 'Data Matching, Anreicherung aus externen Quellen', required: true, scoreWeights: { risk: 7, complexity: 7, assurance: 6 }, @@ -257,7 +257,7 @@ const BLOCK_3_PROCESSING: ScopeQuestionBlock = { { id: 'proc_employee_monitoring', type: 'boolean', - label: 'Überwachen Sie Mitarbeiter (Zeiterfassung, Standort, IT-Nutzung)?', + question: 'Überwachen Sie Mitarbeiter (Zeiterfassung, Standort, IT-Nutzung)?', helpText: 'Beschäftigtendatenschutz nach § 26 BDSG', required: true, scoreWeights: { risk: 8, complexity: 6, assurance: 7 }, @@ -265,7 +265,7 @@ const BLOCK_3_PROCESSING: ScopeQuestionBlock = { { id: 'proc_video_surveillance', type: 'boolean', - label: 'Setzen Sie Videoüberwachung ein?', + question: 'Setzen Sie Videoüberwachung ein?', helpText: 'Kameras in Büros, Produktionsstätten, Verkaufsräumen etc.', required: true, scoreWeights: { risk: 8, complexity: 5, assurance: 7 }, @@ -287,7 +287,7 @@ const BLOCK_4_TECH: ScopeQuestionBlock = { { id: 'tech_hosting_location', type: 'single', - label: 'Wo werden Ihre Daten primär gehostet?', + question: 'Wo werden Ihre Daten primär gehostet?', helpText: 'Standort bestimmt anwendbares Datenschutzrecht', required: true, options: [ @@ -302,7 +302,7 @@ const BLOCK_4_TECH: ScopeQuestionBlock = { { id: 'tech_subprocessors', type: 'boolean', - label: 'Nutzen Sie Auftragsverarbeiter (externe Dienstleister)?', + question: 'Nutzen Sie Auftragsverarbeiter (externe Dienstleister)?', helpText: 'Cloud-Anbieter, Hosting, E-Mail-Service, CRM etc. – erfordert AVV nach Art. 28 DSGVO', required: true, scoreWeights: { risk: 6, complexity: 7, assurance: 7 }, @@ -310,7 +310,7 @@ const BLOCK_4_TECH: ScopeQuestionBlock = { { id: 'tech_third_country', type: 'boolean', - label: 'Übermitteln Sie Daten in Drittländer?', + question: 'Übermitteln Sie Daten in Drittländer?', helpText: 'Transfer außerhalb EU/EWR erfordert Schutzmaßnahmen (SCC, BCR etc.)', required: true, scoreWeights: { risk: 9, complexity: 8, assurance: 8 }, @@ -319,7 +319,7 @@ const BLOCK_4_TECH: ScopeQuestionBlock = { { id: 'tech_encryption_rest', type: 'boolean', - label: 'Sind Daten im Ruhezustand verschlüsselt (at rest)?', + question: 'Sind Daten im Ruhezustand verschlüsselt (at rest)?', helpText: 'Datenbank-, Dateisystem- oder Volume-Verschlüsselung', required: true, scoreWeights: { risk: -5, complexity: 3, assurance: 7 }, @@ -327,7 +327,7 @@ const BLOCK_4_TECH: ScopeQuestionBlock = { { id: 'tech_encryption_transit', type: 'boolean', - label: 'Sind Daten bei Übertragung verschlüsselt (in transit)?', + question: 'Sind Daten bei Übertragung verschlüsselt (in transit)?', helpText: 'TLS/SSL für alle Verbindungen', required: true, scoreWeights: { risk: -5, complexity: 2, assurance: 7 }, @@ -335,7 +335,7 @@ const BLOCK_4_TECH: ScopeQuestionBlock = { { id: 'tech_cloud_providers', type: 'multi', - label: 'Welche Cloud-Anbieter nutzen Sie?', + question: 'Welche Cloud-Anbieter nutzen Sie?', helpText: 'Mehrfachauswahl möglich', required: false, options: [ @@ -365,7 +365,7 @@ const BLOCK_5_PROCESSES: ScopeQuestionBlock = { { id: 'proc_dsar_process', type: 'boolean', - label: 'Haben Sie einen Prozess für Betroffenenrechte (DSAR)?', + question: 'Haben Sie einen Prozess für Betroffenenrechte (DSAR)?', helpText: 'Auskunft, Löschung, Berichtigung, Widerspruch etc. – Art. 15-22 DSGVO', required: true, scoreWeights: { risk: 6, complexity: 5, assurance: 8 }, @@ -373,7 +373,7 @@ const BLOCK_5_PROCESSES: ScopeQuestionBlock = { { id: 'proc_deletion_concept', type: 'boolean', - label: 'Haben Sie ein Löschkonzept?', + question: 'Haben Sie ein Löschkonzept?', helpText: 'Definierte Löschfristen und automatisierte Löschroutinen', required: true, scoreWeights: { risk: 7, complexity: 6, assurance: 8 }, @@ -381,7 +381,7 @@ const BLOCK_5_PROCESSES: ScopeQuestionBlock = { { id: 'proc_incident_response', type: 'boolean', - label: 'Haben Sie einen Notfallplan für Datenschutzvorfälle?', + question: 'Haben Sie einen Notfallplan für Datenschutzvorfälle?', helpText: 'Incident Response Plan, 72h-Meldepflicht an Aufsichtsbehörde (Art. 33 DSGVO)', required: true, scoreWeights: { risk: 8, complexity: 6, assurance: 9 }, @@ -389,7 +389,7 @@ const BLOCK_5_PROCESSES: ScopeQuestionBlock = { { id: 'proc_regular_audits', type: 'boolean', - label: 'Führen Sie regelmäßige Datenschutz-Audits durch?', + question: 'Führen Sie regelmäßige Datenschutz-Audits durch?', helpText: 'Interne oder externe Prüfungen mindestens jährlich', required: true, scoreWeights: { risk: 5, complexity: 4, assurance: 9 }, @@ -397,7 +397,7 @@ const BLOCK_5_PROCESSES: ScopeQuestionBlock = { { id: 'proc_training', type: 'boolean', - label: 'Schulen Sie Ihre Mitarbeiter im Datenschutz?', + question: 'Schulen Sie Ihre Mitarbeiter im Datenschutz?', helpText: 'Awareness-Trainings, Onboarding, jährliche Auffrischung', required: true, scoreWeights: { risk: 6, complexity: 3, assurance: 7 }, @@ -417,7 +417,7 @@ const BLOCK_6_PRODUCT: ScopeQuestionBlock = { { id: 'prod_type', type: 'multi', - label: 'Welche Art von Produkten/Services bieten Sie an?', + question: 'Welche Art von Produkten/Services bieten Sie an?', helpText: 'Mehrfachauswahl möglich', required: true, options: [ @@ -435,7 +435,7 @@ const BLOCK_6_PRODUCT: ScopeQuestionBlock = { { id: 'prod_cookies_consent', type: 'boolean', - label: 'Benötigen Sie Cookie-Consent (Tracking-Cookies)?', + question: 'Benötigen Sie Cookie-Consent (Tracking-Cookies)?', helpText: 'Nicht-essenzielle Cookies erfordern opt-in Einwilligung', required: true, scoreWeights: { risk: 5, complexity: 4, assurance: 6 }, @@ -443,7 +443,7 @@ const BLOCK_6_PRODUCT: ScopeQuestionBlock = { { id: 'prod_webshop', type: 'boolean', - label: 'Betreiben Sie einen Online-Shop?', + question: 'Betreiben Sie einen Online-Shop?', helpText: 'E-Commerce mit Zahlungsabwicklung, Bestellverwaltung', required: true, scoreWeights: { risk: 7, complexity: 6, assurance: 6 }, @@ -451,7 +451,7 @@ const BLOCK_6_PRODUCT: ScopeQuestionBlock = { { id: 'prod_api_external', type: 'boolean', - label: 'Bieten Sie externe APIs an (Daten-Weitergabe an Dritte)?', + question: 'Bieten Sie externe APIs an (Daten-Weitergabe an Dritte)?', helpText: 'Programmierschnittstellen für Partner, Entwickler etc.', required: true, scoreWeights: { risk: 7, complexity: 7, assurance: 7 }, @@ -459,7 +459,7 @@ const BLOCK_6_PRODUCT: ScopeQuestionBlock = { { id: 'prod_data_broker', type: 'boolean', - label: 'Handeln Sie mit Daten (Data Brokerage, Adresshandel)?', + question: 'Handeln Sie mit Daten (Data Brokerage, Adresshandel)?', helpText: 'Verkauf oder Vermittlung personenbezogener Daten', required: true, scoreWeights: { risk: 10, complexity: 8, assurance: 9 }, diff --git a/admin-v2/lib/sdk/compliance-scope-types.ts b/admin-v2/lib/sdk/compliance-scope-types.ts index 6b1d338..d05d6a3 100644 --- a/admin-v2/lib/sdk/compliance-scope-types.ts +++ b/admin-v2/lib/sdk/compliance-scope-types.ts @@ -42,12 +42,12 @@ export interface ComplianceScores { * IDs der Fragenblöcke für das Scope-Profiling */ export type ScopeQuestionBlockId = - | 'org_reife' // Organisatorische Reife - | 'daten_betroffene' // Daten & Betroffene - | 'verarbeitung_zweck' // Verarbeitung & Zweck - | 'technik_hosting' // Technik & Hosting - | 'rechte_prozesse' // Rechte & Prozesse - | 'produktkontext'; // Produktkontext + | 'organisation' // Organisation & Reife + | 'data' // Daten & Betroffene + | 'processing' // Verarbeitung & Zweck + | 'tech' // Technik & Hosting + | 'processes' // Rechte & Prozesse + | 'product'; // Produktkontext /** * Eine einzelne Frage im Scope-Profiling @@ -55,8 +55,6 @@ export type ScopeQuestionBlockId = export interface ScopeProfilingQuestion { /** Eindeutige ID der Frage */ id: string; - /** Zugehöriger Block */ - block: ScopeQuestionBlockId; /** Fragetext */ question: string; /** Optional: Hilfetext/Erklärung */ @@ -103,6 +101,8 @@ export interface ScopeQuestionBlock { title: string; /** Block-Beschreibung */ description: string; + /** Reihenfolge des Blocks */ + order: number; /** Fragen in diesem Block */ questions: ScopeProfilingQuestion[]; }