+
{labelRow}
+
+ {([true, false] as const).map(val => (
+
+ ))}
+
+
+ )
+
+ case 'single':
+ return (
+
+ {labelRow}
+
+ {question.options?.map((option) => (
+
+ ))}
+
+
+ )
+
+ case 'multi': {
+ const selectedValues = Array.isArray(currentValue) ? currentValue as string[] : []
+ return (
+
+ {labelRow}
+
+ {question.options?.map((option) => {
+ const isChecked = selectedValues.includes(option.value)
+ return (
+
+ )
+ })}
+
+
+ )
+ }
+
+ case 'number':
+ return (
+ >(new Set())
// Auto-prefill from company profile on mount if answers are empty
useEffect(() => {
if (companyProfile && answers.length === 0) {
const prefilled = prefillFromCompanyProfile(companyProfile)
- // Also inject auto-filled scoring answers for questions removed from UI
const autoFilled = getAutoFilledScoringAnswers(companyProfile)
const allPrefilled = [...prefilled, ...autoFilled]
if (allPrefilled.length > 0) {
@@ -47,7 +45,6 @@ export function ScopeWizardTab({
setPrefilledIds(new Set(allPrefilled.map(a => a.questionId)))
}
}
- // Only run on mount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
@@ -61,7 +58,6 @@ export function ScopeWizardTab({
} else {
onAnswersChange([...answers, { questionId, value }])
}
- // Remove from prefilled set when user manually changes
if (prefilledIds.has(questionId)) {
setPrefilledIds(prev => {
const next = new Set(prev)
@@ -78,7 +74,6 @@ export function ScopeWizardTab({
const prefilled = prefillFromCompanyProfile(companyProfile)
const autoFilled = getAutoFilledScoringAnswers(companyProfile)
const allPrefilled = [...prefilled, ...autoFilled]
- // 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)
@@ -101,242 +96,18 @@ export function ScopeWizardTab({
}, [currentBlockIndex, canEvaluate, onEvaluate])
const handleBack = useCallback(() => {
- if (currentBlockIndex > 0) {
- setCurrentBlockIndex(currentBlockIndex - 1)
- }
+ if (currentBlockIndex > 0) setCurrentBlockIndex(currentBlockIndex - 1)
}, [currentBlockIndex])
const toggleHelp = useCallback((questionId: string) => {
setExpandedHelp(prev => {
const next = new Set(prev)
- if (next.has(questionId)) {
- next.delete(questionId)
- } else {
- next.add(questionId)
- }
+ 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 (
-
-
-
-
- {question.question}
-
- {question.required && *}
- {renderPrefilledBadge(question.id)}
- {renderHelpText(question)}
-
-
-
-
-
-
-
- )
-
- case 'single':
- return (
-
-
-
- {question.question}
-
- {question.required && *}
- {renderPrefilledBadge(question.id)}
- {renderHelpText(question)}
-
-
- {question.options?.map((option) => (
-
- ))}
-
-
- )
-
- case 'multi':
- return (
-
-
-
- {question.question}
-
- {question.required && *}
- {renderPrefilledBadge(question.id)}
- {renderHelpText(question)}
-
-
- {question.options?.map((option) => {
- const selectedValues = Array.isArray(currentValue) ? currentValue as string[] : []
- const isChecked = selectedValues.includes(option.value)
- return (
-
- )
- })}
-
-
- )
-
- case 'number':
- return (
-
-
-
- {question.question}
-
- {question.required && *}
- {renderPrefilledBadge(question.id)}
- {renderHelpText(question)}
-
-
handleAnswerChange(question.id, parseInt(e.target.value, 10))}
- className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
- placeholder="Zahl eingeben"
- />
-
- )
-
- case 'text':
- return (
-
-
-
- {question.question}
-
- {question.required && *}
- {renderPrefilledBadge(question.id)}
- {renderHelpText(question)}
-
-
handleAnswerChange(question.id, e.target.value)}
- className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
- placeholder="Text eingeben"
- />
-
- )
-
- default:
- return null
- }
- }
-
return (
{/* Left Sidebar - Block Navigation */}
@@ -350,7 +121,6 @@ export function ScopeWizardTab({
const unanswered = getUnansweredRequiredQuestions(answers, block.id)
const hasRequired = block.questions.some(q => q.required)
const allRequiredDone = hasRequired && unanswered.length === 0
- // For optional-only blocks: check if any questions were answered
const answeredIds = new Set(answers.map(a => a.questionId))
const hasAnyAnswer = block.questions.some(q => answeredIds.has(q.id))
const optionalDone = !hasRequired && hasAnyAnswer
@@ -380,19 +150,13 @@ export function ScopeWizardTab({
) : !hasRequired ? (
(nur optional)
) : (
-
- {unanswered.length} offen
-
+ {unanswered.length} offen
)}
@@ -428,8 +192,6 @@ export function ScopeWizardTab({
{(() => {
const allUnanswered = getUnansweredRequiredQuestions(answers)
if (allUnanswered.length === 0) return null
-
- // Group by block
const byBlock = new Map
()
for (const item of allUnanswered) {
if (!byBlock.has(item.blockId)) {
@@ -438,7 +200,6 @@ export function ScopeWizardTab({
}
byBlock.get(item.blockId)!.count++
}
-
return (
⚠ Offene Pflichtfragen:
@@ -477,7 +238,7 @@ export function ScopeWizardTab({
)}
- {/* "Aus Profil" Info Box — shown for blocks that have auto-filled data */}
+ {/* "Aus Profil" Info Box */}
{companyProfile && (() => {
const profileItems = getProfileInfoForBlock(companyProfile, currentBlock.id as ScopeQuestionBlockId)
if (profileItems.length === 0) return null
@@ -516,21 +277,23 @@ export function ScopeWizardTab({
{/* Questions */}
{currentBlock.id === 'datenkategorien_detail' ? (
-
+
) : (
currentBlock.questions.map((question) => {
const isAnswered = answers.some(a => a.questionId === question.id)
const borderClass = question.required
- ? isAnswered
- ? 'border-l-4 border-l-green-400 pl-4'
- : 'border-l-4 border-l-orange-400 pl-4'
+ ? isAnswered ? 'border-l-4 border-l-green-400 pl-4' : 'border-l-4 border-l-orange-400 pl-4'
: ''
return (
- {renderQuestion(question)}
+
)
})
@@ -574,221 +337,3 @@ export function ScopeWizardTab({
)
}
-
-// =============================================================================
-// BLOCK 9: Datenkategorien pro Abteilung (aufklappbare Kacheln)
-// =============================================================================
-
-/** Mapping Block 8 vvt_departments values → DEPARTMENT_DATA_CATEGORIES keys */
-const DEPT_VALUE_TO_KEY: Record = {
- personal: ['dept_hr', 'dept_recruiting'],
- 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 */
-const DEPT_KEY_TO_QUESTION: Record = {
- dept_hr: 'dk_dept_hr',
- dept_recruiting: 'dk_dept_recruiting',
- dept_finance: 'dk_dept_finance',
- 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({
- answers,
- onAnswerChange,
-}: {
- answers: ScopeProfilingAnswer[]
- onAnswerChange: (questionId: string, value: string | string[] | boolean | number) => void
-}) {
- const [expandedDepts, setExpandedDepts] = useState>(new Set())
- const [initializedDepts, setInitializedDepts] = useState>(new Set())
-
- // Get selected departments from Block 8
- const deptAnswer = answers.find(a => a.questionId === 'vvt_departments')
- const selectedDepts = Array.isArray(deptAnswer?.value) ? (deptAnswer.value as string[]) : []
-
- // Resolve which department keys are active
- const activeDeptKeys: string[] = []
- for (const deptValue of selectedDepts) {
- const keys = DEPT_VALUE_TO_KEY[deptValue]
- if (keys) {
- for (const k of keys) {
- if (!activeDeptKeys.includes(k)) activeDeptKeys.push(k)
- }
- }
- }
-
- const toggleDept = (deptKey: string) => {
- setExpandedDepts(prev => {
- const next = new Set(prev)
- if (next.has(deptKey)) {
- next.delete(deptKey)
- } else {
- next.add(deptKey)
- // Prefill typical categories on first expand
- if (!initializedDepts.has(deptKey)) {
- const config = DEPARTMENT_DATA_CATEGORIES[deptKey]
- const questionId = DEPT_KEY_TO_QUESTION[deptKey]
- if (config && questionId) {
- const existing = answers.find(a => a.questionId === questionId)
- if (!existing) {
- const typicalIds = config.categories.filter(c => c.isTypical).map(c => c.id)
- onAnswerChange(questionId, typicalIds)
- }
- }
- setInitializedDepts(p => new Set(p).add(deptKey))
- }
- }
- return next
- })
- }
-
- const handleCategoryToggle = (deptKey: string, catId: string) => {
- const questionId = DEPT_KEY_TO_QUESTION[deptKey]
- if (!questionId) return
- const existing = answers.find(a => a.questionId === questionId)
- const current = Array.isArray(existing?.value) ? (existing.value as string[]) : []
- const updated = current.includes(catId)
- ? current.filter(id => id !== catId)
- : [...current, catId]
- onAnswerChange(questionId, updated)
- }
-
- if (activeDeptKeys.length === 0) {
- return (
-
-
- Bitte waehlen Sie zuerst in Block 8 (Verarbeitungstaetigkeiten) die
- Abteilungen aus, in denen personenbezogene Daten verarbeitet werden.
-
-
- )
- }
-
- return (
-
- {activeDeptKeys.map(deptKey => {
- const config = DEPARTMENT_DATA_CATEGORIES[deptKey]
- if (!config) return null
- const questionId = DEPT_KEY_TO_QUESTION[deptKey]
- const isExpanded = expandedDepts.has(deptKey)
- const existing = answers.find(a => a.questionId === questionId)
- const selectedCategories = Array.isArray(existing?.value) ? (existing.value as string[]) : []
- const hasArt9Selected = config.categories
- .filter(c => c.isArt9)
- .some(c => selectedCategories.includes(c.id))
-
- return (
-
- {/* Header */}
-
-
- {/* Expandable categories panel */}
- {isExpanded && (
-
-
- Datenkategorien
-
-
- {config.categories.map(cat => {
- const isChecked = selectedCategories.includes(cat.id)
- return (
-
- )
- })}
-
-
- {/* Art. 9 warning */}
- {hasArt9Selected && (
-
-
- Art. 9 DSGVO: Sie verarbeiten besondere Kategorien
- personenbezogener Daten. Eine zusaetzliche Rechtsgrundlage nach Art. 9 Abs. 2 DSGVO ist
- erforderlich (z.B. § 26 Abs. 3 BDSG fuer Beschaeftigtendaten).
-
-
- )}
-
- )}
-
- )
- })}
-
- )
-}