Use-Case-Mapping-Filter für Master Controls + Mapper-Präzisionsfix
CI / detect-changes (push) Successful in 14s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 7s
CI / validate-canonical-controls (push) Successful in 13s
CI / loc-budget (push) Failing after 15s
CI / go-lint (push) Has been skipped
CI / test-go (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m23s
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 34s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / detect-changes (push) Successful in 14s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Failing after 7s
CI / validate-canonical-controls (push) Successful in 13s
CI / loc-budget (push) Failing after 15s
CI / go-lint (push) Has been skipped
CI / test-go (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m23s
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 34s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
Phase 2: Live-Filter an /sdk/master-controls (Use Case, Quell-Regulierung, Verifikations-Methode, Coverage, Primärzweck-Toggle, category via Member-EXISTS). API mit EXISTS-Filtern + gecachten Meta-Counts in master-controls/route.ts. Phase A: neue UseCase telekommunikation + Fix der Impressum-Fehlrouten im Register (TKG/AT-TKG->telekommunikation, telemedien->dse, GewO->handelsrecht); echte Impressum-Quellen (TMG/Mediengesetz) bleiben impressum. Deterministischer Seed aus source_regulation; Tests grün. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { ControlListView } from '../control-library/components/ControlListView'
|
||||
import { useControlLibraryState } from '../control-library/components/useControlLibraryState'
|
||||
import { useCaseLabel, mcVerificationLabel } from '../control-library/components/mcMappingLabels'
|
||||
|
||||
/**
|
||||
* Master Controls page — reuses the Control Library UI exactly,
|
||||
@@ -38,7 +39,7 @@ export default function MasterControlsPage() {
|
||||
if (state.mode === 'detail' && state.selectedControl) {
|
||||
return (
|
||||
<MCDetail
|
||||
mc={state.selectedControl}
|
||||
mc={state.selectedControl as unknown as Record<string, unknown>}
|
||||
onBack={() => { state.setMode('list'); state.setSelectedControl(null) }}
|
||||
/>
|
||||
)
|
||||
@@ -65,6 +66,10 @@ export default function MasterControlsPage() {
|
||||
domainFilter={state.domainFilter}
|
||||
stateFilter={state.stateFilter}
|
||||
verificationFilter={state.verificationFilter}
|
||||
useCaseFilter={state.useCaseFilter}
|
||||
primaryOnly={state.primaryOnly}
|
||||
regulationFilter={state.regulationFilter}
|
||||
mappedFilter={state.mappedFilter}
|
||||
categoryFilter={state.categoryFilter}
|
||||
evidenceTypeFilter={state.evidenceTypeFilter}
|
||||
audienceFilter={state.audienceFilter}
|
||||
@@ -76,6 +81,10 @@ export default function MasterControlsPage() {
|
||||
setDomainFilter={state.setDomainFilter}
|
||||
setStateFilter={state.setStateFilter}
|
||||
setVerificationFilter={state.setVerificationFilter}
|
||||
setUseCaseFilter={state.setUseCaseFilter}
|
||||
setPrimaryOnly={state.setPrimaryOnly}
|
||||
setRegulationFilter={state.setRegulationFilter}
|
||||
setMappedFilter={state.setMappedFilter}
|
||||
setCategoryFilter={state.setCategoryFilter}
|
||||
setEvidenceTypeFilter={state.setEvidenceTypeFilter}
|
||||
setAudienceFilter={state.setAudienceFilter}
|
||||
@@ -116,8 +125,15 @@ const SEV = {
|
||||
low: 'bg-blue-100 text-blue-800',
|
||||
} as Record<string, string>
|
||||
|
||||
interface MCMapping {
|
||||
use_cases?: Array<{ use_case: string; is_primary: boolean }>
|
||||
verification_method?: string | null
|
||||
regulations?: Array<{ source_regulation: string; is_primary: boolean; member_count: number }>
|
||||
}
|
||||
|
||||
function MCDetail({ mc, onBack }: { mc: Record<string, unknown>; onBack: () => void }) {
|
||||
const [members, setMembers] = useState<Member[]>([])
|
||||
const [mapping, setMapping] = useState<MCMapping>({})
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [phaseFilter, setPhaseFilter] = useState('')
|
||||
|
||||
@@ -131,6 +147,10 @@ function MCDetail({ mc, onBack }: { mc: Record<string, unknown>; onBack: () => v
|
||||
fetch(`/api/sdk/v1/master-controls?endpoint=control&id=${mcId}`)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(data => {
|
||||
if (data) setMapping({
|
||||
use_cases: data.use_cases, verification_method: data.verification_method,
|
||||
regulations: data.regulations,
|
||||
})
|
||||
if (data?.members) setMembers(data.members)
|
||||
else if (data?.requirements) {
|
||||
// Fallback: parse requirements strings
|
||||
@@ -164,6 +184,33 @@ function MCDetail({ mc, onBack }: { mc: Record<string, unknown>; onBack: () => v
|
||||
<h1 className="text-2xl font-bold text-gray-900">{mcName}</h1>
|
||||
<p className="text-gray-500 mt-1">{mcId} — {totalControls} Atomic Controls</p>
|
||||
|
||||
{/* Zuordnung: Use Cases + Verifikation + Quell-Regulierung */}
|
||||
{(mapping.use_cases?.length || mapping.verification_method || mapping.regulations?.length) ? (
|
||||
<div className="mt-4 flex flex-wrap items-center gap-2">
|
||||
{(mapping.use_cases || []).map(u => (
|
||||
<span key={u.use_case}
|
||||
className={`px-2 py-0.5 rounded text-xs font-medium ${u.is_primary
|
||||
? 'bg-purple-100 text-purple-800 border border-purple-300'
|
||||
: 'bg-purple-50 text-purple-600'}`}
|
||||
title={u.is_primary ? 'Primärzweck' : 'Mehrfachzweck'}>
|
||||
{useCaseLabel(u.use_case)}{u.is_primary ? ' ★' : ''}
|
||||
</span>
|
||||
))}
|
||||
{mapping.verification_method && (
|
||||
<span className="px-2 py-0.5 rounded text-xs font-medium bg-emerald-100 text-emerald-800 border border-emerald-300">
|
||||
Nachweis: {mcVerificationLabel(mapping.verification_method)}
|
||||
</span>
|
||||
)}
|
||||
{(mapping.regulations || []).slice(0, 4).map(r => (
|
||||
<span key={r.source_regulation}
|
||||
className="px-2 py-0.5 rounded text-xs bg-blue-50 text-blue-700"
|
||||
title={`${r.member_count} Member${r.is_primary ? ' · Primärquelle' : ''}`}>
|
||||
{r.source_regulation}{r.is_primary ? ' ★' : ''}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Phase badges */}
|
||||
<div className="flex flex-wrap gap-2 mt-4">
|
||||
{uniquePhases.map(p => (
|
||||
|
||||
Reference in New Issue
Block a user