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:
@@ -12,6 +12,7 @@ import {
|
||||
VERIFICATION_METHODS, CATEGORY_OPTIONS, EVIDENCE_TYPE_OPTIONS,
|
||||
} from './helpers'
|
||||
import { ControlsMeta } from './useControlLibraryState'
|
||||
import { useCaseLabel, mcVerificationLabel } from './mcMappingLabels'
|
||||
import { GeneratorModal } from './GeneratorModal'
|
||||
|
||||
interface ControlListViewProps {
|
||||
@@ -34,6 +35,10 @@ interface ControlListViewProps {
|
||||
domainFilter: string
|
||||
stateFilter: string
|
||||
verificationFilter: string
|
||||
useCaseFilter: string
|
||||
primaryOnly: boolean
|
||||
regulationFilter: string
|
||||
mappedFilter: string
|
||||
categoryFilter: string
|
||||
evidenceTypeFilter: string
|
||||
audienceFilter: string
|
||||
@@ -46,6 +51,10 @@ interface ControlListViewProps {
|
||||
setDomainFilter: (v: string) => void
|
||||
setStateFilter: (v: string) => void
|
||||
setVerificationFilter: (v: string) => void
|
||||
setUseCaseFilter: (v: string) => void
|
||||
setPrimaryOnly: (v: boolean) => void
|
||||
setRegulationFilter: (v: string) => void
|
||||
setMappedFilter: (v: string) => void
|
||||
setCategoryFilter: (v: string) => void
|
||||
setEvidenceTypeFilter: (v: string) => void
|
||||
setAudienceFilter: (v: string) => void
|
||||
@@ -71,10 +80,12 @@ export function ControlListView({
|
||||
reviewCount, bulkProcessing, showStats, processedStats,
|
||||
showGenerator, currentPage, totalPages, sortBy,
|
||||
searchQuery, severityFilter, domainFilter, stateFilter,
|
||||
verificationFilter, categoryFilter, evidenceTypeFilter, audienceFilter,
|
||||
verificationFilter, useCaseFilter, primaryOnly, regulationFilter, mappedFilter,
|
||||
categoryFilter, evidenceTypeFilter, audienceFilter,
|
||||
sourceFilter, typeFilter, hideDuplicates,
|
||||
setSearchQuery, setSeverityFilter, setDomainFilter, setStateFilter,
|
||||
setVerificationFilter, setCategoryFilter, setEvidenceTypeFilter, setAudienceFilter,
|
||||
setVerificationFilter, setUseCaseFilter, setPrimaryOnly, setRegulationFilter, setMappedFilter,
|
||||
setCategoryFilter, setEvidenceTypeFilter, setAudienceFilter,
|
||||
setSourceFilter, setTypeFilter, setHideDuplicates, setSortBy,
|
||||
setShowStats, setShowGenerator, setCurrentPage,
|
||||
onSelectControl, onCreateMode, onEnterReview, onBulkReject, onRefresh, onLoadStats, onFullReload,
|
||||
@@ -176,18 +187,60 @@ export function ControlListView({
|
||||
className="rounded border-gray-300 text-purple-600 focus:ring-purple-500" />
|
||||
Duplikate ausblenden
|
||||
</label>
|
||||
{meta?.use_case_counts && (
|
||||
<select value={useCaseFilter} onChange={e => setUseCaseFilter(e.target.value)}
|
||||
className="text-sm border border-purple-300 bg-purple-50 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-purple-500 max-w-[260px]">
|
||||
<option value="">Use Case (alle)</option>
|
||||
{Object.entries(meta.use_case_counts).sort((a, b) => b[1] - a[1]).map(([k, c]) => (
|
||||
<option key={k} value={k}>{useCaseLabel(k)} ({c})</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
{meta?.use_case_counts && useCaseFilter && (
|
||||
<label className="flex items-center gap-1.5 text-xs text-gray-600 cursor-pointer whitespace-nowrap"
|
||||
title="Nur Master Controls, deren Primärzweck dieser Use Case ist (blendet über-geclusterte Mehrfachzwecke aus)">
|
||||
<input type="checkbox" checked={primaryOnly} onChange={e => setPrimaryOnly(e.target.checked)}
|
||||
className="rounded border-gray-300 text-purple-600 focus:ring-purple-500" />
|
||||
nur Primärzweck
|
||||
</label>
|
||||
)}
|
||||
{meta?.regulations && meta.regulations.length > 0 && (
|
||||
<select value={regulationFilter} onChange={e => setRegulationFilter(e.target.value)}
|
||||
className="text-sm border border-blue-300 bg-blue-50 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-purple-500 max-w-[260px]">
|
||||
<option value="">Regulierung (alle)</option>
|
||||
{meta.regulations.map(rg => (
|
||||
<option key={rg.source_regulation} value={rg.source_regulation}>{rg.source_regulation} ({rg.count})</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<select value={verificationFilter} onChange={e => setVerificationFilter(e.target.value)}
|
||||
className="text-sm border border-gray-300 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-purple-500">
|
||||
<option value="">Nachweis</option>
|
||||
{Object.entries(VERIFICATION_METHODS).map(([k, v]) => (
|
||||
<option key={k} value={k}>{v.label}{meta?.verification_method_counts?.[k] ? ` (${meta.verification_method_counts[k]})` : ''}</option>
|
||||
))}
|
||||
{Object.keys(meta?.verification_method_counts || {})
|
||||
.filter(k => k !== '__none__' && !(k in VERIFICATION_METHODS))
|
||||
.map(k => (
|
||||
<option key={k} value={k}>{mcVerificationLabel(k)} ({meta!.verification_method_counts![k]})</option>
|
||||
))}
|
||||
{meta?.verification_method_counts?.['__none__'] ? <option value="__none__">Ohne Nachweis ({meta.verification_method_counts['__none__']})</option> : null}
|
||||
</select>
|
||||
{meta?.mapped_total != null && (
|
||||
<select value={mappedFilter} onChange={e => setMappedFilter(e.target.value)}
|
||||
className="text-sm border border-gray-300 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-purple-500">
|
||||
<option value="">Coverage: alle</option>
|
||||
<option value="mapped">Zugeordnet ({meta.mapped_total})</option>
|
||||
<option value="unmapped">Offen ({meta.unmapped_count ?? 0})</option>
|
||||
</select>
|
||||
)}
|
||||
<select value={categoryFilter} onChange={e => setCategoryFilter(e.target.value)}
|
||||
className="text-sm border border-gray-300 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-purple-500">
|
||||
<option value="">Kategorie</option>
|
||||
{CATEGORY_OPTIONS.map(c => <option key={c.value} value={c.value}>{c.label}{meta?.category_counts?.[c.value] ? ` (${meta.category_counts[c.value]})` : ''}</option>)}
|
||||
{Object.keys(meta?.category_counts || {})
|
||||
.filter(k => k !== '__none__' && !CATEGORY_OPTIONS.some(c => c.value === k))
|
||||
.map(k => <option key={k} value={k}>{k} ({meta!.category_counts![k]})</option>)}
|
||||
{meta?.category_counts?.['__none__'] ? <option value="__none__">Ohne Kategorie ({meta.category_counts['__none__']})</option> : null}
|
||||
</select>
|
||||
<select value={evidenceTypeFilter} onChange={e => setEvidenceTypeFilter(e.target.value)}
|
||||
|
||||
Reference in New Issue
Block a user