'use client' 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, getProfileInfoForBlock, getAutoFilledScoringAnswers, getUnansweredRequiredQuestions } from '@/lib/sdk/compliance-scope-profiling' import { DEPARTMENT_DATA_CATEGORIES } from '@/lib/sdk/vvt-profiling' import type { ScopeQuestionBlockId } from '@/lib/sdk/compliance-scope-types' import { useSDK } from '@/lib/sdk' interface ScopeWizardTabProps { answers: ScopeProfilingAnswer[] onAnswersChange: (answers: ScopeProfilingAnswer[]) => void onEvaluate: () => void canEvaluate: boolean isEvaluating: boolean completionStats: { total: number; answered: number; percentage: number; isComplete: boolean } } export function ScopeWizardTab({ answers, onAnswersChange, 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) // Also inject auto-filled scoring answers for questions removed from UI const autoFilled = getAutoFilledScoringAnswers(companyProfile) const allPrefilled = [...prefilled, ...autoFilled] if (allPrefilled.length > 0) { onAnswersChange(allPrefilled) setPrefilledIds(new Set(allPrefilled.map(a => a.questionId))) } } // Only run on mount // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const handleAnswerChange = useCallback( (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 } onAnswersChange(newAnswers) } else { 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, prefilledIds] ) const handlePrefillFromProfile = useCallback(() => { if (!companyProfile) return 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) for (const pa of allPrefilled) { 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 if (canEvaluate) { onEvaluate() } }, [currentBlockIndex, canEvaluate, onEvaluate]) const handleBack = useCallback(() => { 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) } 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 */}

Fortschritt

{SCOPE_QUESTION_BLOCKS.map((block, idx) => { const progress = getBlockProgress(answers, block.id) const isActive = idx === currentBlockIndex 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 return ( ) })}
{/* Main Content Area */}
{/* Progress Bar */}
Gesamtfortschritt
{completionStats.answered} / {completionStats.total} Fragen {totalProgress}%
{/* Clickable unanswered required questions summary */} {(() => { 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)) { const blockIndex = SCOPE_QUESTION_BLOCKS.findIndex(b => b.id === item.blockId) byBlock.set(item.blockId, { blockTitle: item.blockTitle, blockIndex, count: 0 }) } byBlock.get(item.blockId)!.count++ } return (
⚠ Offene Pflichtfragen: {Array.from(byBlock.entries()).map(([blockId, info], i) => ( {i > 0 && ·} ))}
) })()}
{/* Current Block */}

{currentBlock.title}

{currentBlock.description}

{companyProfile && ( )}
{/* "Aus Profil" Info Box — shown for blocks that have auto-filled data */} {companyProfile && (() => { const profileItems = getProfileInfoForBlock(companyProfile, currentBlock.id as ScopeQuestionBlockId) if (profileItems.length === 0) return null return (

Aus Unternehmensprofil

{profileItems.map(item => ( {item.label}: {item.value} ))}
Profil bearbeiten
) })()} {/* 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' : '' return (
{renderQuestion(question)}
) }) )}
{/* Navigation Buttons */}
Block {currentBlockIndex + 1} von {SCOPE_QUESTION_BLOCKS.length} {currentBlockIndex === SCOPE_QUESTION_BLOCKS.length - 1 ? ( ) : ( )}
) } // ============================================================================= // 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).

)}
)}
) })}
) }