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:
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in New Issue
Block a user