refactor: Integrate Modules into Scope-Decision (Option C)

- RegulationsPanel: added enable/disable toggles per regulation
- ScopeDecisionTab: passes enabledModules + onToggleModule
- Scope page: auto-enables all applicable regulations when loaded
- Modules step: isOptional=true, moved to Zusatzmodule
- Requirements: now depends on compliance-scope, not modules
- Source-policy: now depends on use-case-assessment, not modules

Flow: Profile → Scope → Scope-Decision shows applicable regulations
with toggles → Requirements derived from enabled regulations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-04 14:29:53 +02:00
parent 7ab1476d8f
commit f737bfc4db
4 changed files with 44 additions and 7 deletions
@@ -78,6 +78,14 @@ export default function ComplianceScopePage() {
const [supervisoryAuthorities, setSupervisoryAuthorities] = useState<SupervisoryAuthorityInfo[]>([]) const [supervisoryAuthorities, setSupervisoryAuthorities] = useState<SupervisoryAuthorityInfo[]>([])
const [regulationAssessmentLoading, setRegulationAssessmentLoading] = useState(false) const [regulationAssessmentLoading, setRegulationAssessmentLoading] = useState(false)
// Enabled compliance modules (derived from applicable regulations)
const [enabledModules, setEnabledModules] = useState<string[]>([])
// Auto-enable all applicable regulations when they load
const handleToggleModule = (moduleId: string, enabled: boolean) => {
setEnabledModules(prev => enabled ? [...prev, moduleId] : prev.filter(id => id !== moduleId))
}
// Sync from SDK context when it becomes available (handles async loading). // Sync from SDK context when it becomes available (handles async loading).
// The SDK context loads state from server/localStorage asynchronously, so // The SDK context loads state from server/localStorage asynchronously, so
// sdkState.complianceScope may arrive AFTER this page has already mounted. // sdkState.complianceScope may arrive AFTER this page has already mounted.
@@ -159,6 +167,10 @@ export default function ComplianceScopePage() {
// Set applicable regulations from response // Set applicable regulations from response
const regs: ApplicableRegulation[] = data.overview?.applicable_regulations || data.applicable_regulations || [] const regs: ApplicableRegulation[] = data.overview?.applicable_regulations || data.applicable_regulations || []
setApplicableRegulations(regs) setApplicableRegulations(regs)
// Auto-enable all applicable regulations as modules
if (enabledModules.length === 0) {
setEnabledModules(regs.map(r => r.id))
}
// Derive supervisory authorities // Derive supervisory authorities
const regIds = regs.map(r => r.id) const regIds = regs.map(r => r.id)
@@ -375,6 +387,8 @@ export default function ComplianceScopePage() {
supervisoryAuthorities={supervisoryAuthorities} supervisoryAuthorities={supervisoryAuthorities}
regulationAssessmentLoading={regulationAssessmentLoading} regulationAssessmentLoading={regulationAssessmentLoading}
onGoToObligations={() => { window.location.href = '/sdk/obligations' }} onGoToObligations={() => { window.location.href = '/sdk/obligations' }}
enabledModules={enabledModules}
onToggleModule={handleToggleModule}
/> />
)} )}
@@ -110,6 +110,8 @@ interface RegulationsPanelProps {
supervisoryAuthorities?: SupervisoryAuthorityInfo[] supervisoryAuthorities?: SupervisoryAuthorityInfo[]
regulationAssessmentLoading?: boolean regulationAssessmentLoading?: boolean
onGoToObligations?: () => void onGoToObligations?: () => void
enabledModules?: string[]
onToggleModule?: (moduleId: string, enabled: boolean) => void
} }
export function RegulationsPanel({ export function RegulationsPanel({
@@ -117,6 +119,8 @@ export function RegulationsPanel({
supervisoryAuthorities, supervisoryAuthorities,
regulationAssessmentLoading, regulationAssessmentLoading,
onGoToObligations, onGoToObligations,
enabledModules,
onToggleModule,
}: RegulationsPanelProps) { }: RegulationsPanelProps) {
if (!applicableRegulations && !regulationAssessmentLoading) return null if (!applicableRegulations && !regulationAssessmentLoading) return null
return ( return (
@@ -149,9 +153,22 @@ export function RegulationsPanel({
)} )}
</div> </div>
</div> </div>
<div className="text-right text-sm text-gray-600"> <div className="flex items-center gap-3">
<span>{reg.obligation_count} Pflichten</span> <div className="text-right text-sm text-gray-600">
{reg.control_count > 0 && <span className="ml-2">{reg.control_count} Controls</span>} <span>{reg.obligation_count} Pflichten</span>
{reg.control_count > 0 && <span className="ml-2">{reg.control_count} Controls</span>}
</div>
{onToggleModule && (
<label className="flex items-center gap-1.5 cursor-pointer">
<input
type="checkbox"
checked={enabledModules?.includes(reg.id) ?? true}
onChange={e => onToggleModule(reg.id, e.target.checked)}
className="w-4 h-4 text-purple-600 rounded"
/>
<span className="text-xs text-gray-500">Aktiv</span>
</label>
)}
</div> </div>
</div> </div>
))} ))}
@@ -25,6 +25,8 @@ interface ScopeDecisionTabProps {
supervisoryAuthorities?: SupervisoryAuthorityInfo[] supervisoryAuthorities?: SupervisoryAuthorityInfo[]
regulationAssessmentLoading?: boolean regulationAssessmentLoading?: boolean
onGoToObligations?: () => void onGoToObligations?: () => void
enabledModules?: string[]
onToggleModule?: (moduleId: string, enabled: boolean) => void
} }
export function ScopeDecisionTab({ export function ScopeDecisionTab({
@@ -38,6 +40,8 @@ export function ScopeDecisionTab({
supervisoryAuthorities, supervisoryAuthorities,
regulationAssessmentLoading, regulationAssessmentLoading,
onGoToObligations, onGoToObligations,
enabledModules,
onToggleModule,
}: ScopeDecisionTabProps) { }: ScopeDecisionTabProps) {
const [expandedTrigger, setExpandedTrigger] = useState<number | null>(null) const [expandedTrigger, setExpandedTrigger] = useState<number | null>(null)
const [showAuditTrail, setShowAuditTrail] = useState(false) const [showAuditTrail, setShowAuditTrail] = useState(false)
@@ -71,6 +75,8 @@ export function ScopeDecisionTab({
applicableRegulations={applicableRegulations} applicableRegulations={applicableRegulations}
supervisoryAuthorities={supervisoryAuthorities} supervisoryAuthorities={supervisoryAuthorities}
regulationAssessmentLoading={regulationAssessmentLoading} regulationAssessmentLoading={regulationAssessmentLoading}
enabledModules={enabledModules}
onToggleModule={onToggleModule}
onGoToObligations={onGoToObligations} onGoToObligations={onGoToObligations}
/> />
+4 -4
View File
@@ -78,11 +78,11 @@ export const SDK_STEPS: SDKStep[] = [
order: 6, order: 6,
name: 'Compliance Modules', name: 'Compliance Modules',
nameShort: 'Module', nameShort: 'Module',
description: 'Abgleich welche Regulierungen gelten', description: 'Manuelle Modul-Verwaltung (Experten)',
url: '/sdk/modules', url: '/sdk/modules',
checkpointId: 'CP-MOD', checkpointId: 'CP-MOD',
prerequisiteSteps: ['use-case-assessment'], prerequisiteSteps: ['use-case-assessment'],
isOptional: false }, isOptional: true },
{ {
id: 'source-policy', id: 'source-policy',
seq: 700, seq: 700,
@@ -94,7 +94,7 @@ export const SDK_STEPS: SDKStep[] = [
description: 'RAG Quellen-Whitelist (Enterprise)', description: 'RAG Quellen-Whitelist (Enterprise)',
url: '/sdk/source-policy', url: '/sdk/source-policy',
checkpointId: 'CP-SPOL', checkpointId: 'CP-SPOL',
prerequisiteSteps: ['modules'], prerequisiteSteps: ['use-case-assessment'],
isOptional: true }, isOptional: true },
// PAKET 2: ANALYSE (Assessment) // PAKET 2: ANALYSE (Assessment)
@@ -109,7 +109,7 @@ export const SDK_STEPS: SDKStep[] = [
description: 'Pr\u00fcfaspekte aus Regulierungen ableiten', description: 'Pr\u00fcfaspekte aus Regulierungen ableiten',
url: '/sdk/requirements', url: '/sdk/requirements',
checkpointId: 'CP-REQ', checkpointId: 'CP-REQ',
prerequisiteSteps: ['modules'], prerequisiteSteps: ['compliance-scope'],
isOptional: false }, isOptional: false },
{ {
id: 'controls', id: 'controls',