feat(use-case-controls): relevant als Stufe statt Hard-Filter + Provenance
CI / test-python-backend (push) Successful in 30s
CI / test-python-document-crawler (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / test-python-dsms-gateway (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) Successful in 12s
CI / validate-canonical-controls (push) Successful in 12s
CI / loc-budget (push) Successful in 25s
CI / go-lint (push) Has been skipped
CI / detect-changes (push) Successful in 15s
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / branch-name (push) Has been skipped
CI / nodejs-build (push) Successful in 3m9s
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 30s
CI / test-python-document-crawler (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / test-python-dsms-gateway (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) Successful in 12s
CI / validate-canonical-controls (push) Successful in 12s
CI / loc-budget (push) Successful in 25s
CI / go-lint (push) Has been skipped
CI / detect-changes (push) Successful in 15s
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / branch-name (push) Has been skipped
CI / nodejs-build (push) Successful in 3m9s
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
Der harte relevant=true-Filter versteckte ~25% des Korpus (40.926 Atome), ~70% davon echte Pflichten (500er-Validierung). relevant wird zur Stufe: - Service: tier-Param (core=Default schuetzt Agent/CRA; all=alles inkl. review), ORDER BY relevant DESC; pro Control relevant/tier/source_type (own_library bei license_rule=3, sonst derived) + source_regulation/article; core_count/review_count. Pure Helper tier_label + source_type (+ Tests). - Route: optionaler tier-Query (default core) — contract-safe (additiv). - Frontend: Coverage-Drill-down /sdk/coverage/[useCase] — Kern-Pflichten vs. "zur fachlichen Pruefung", je mit Herkunfts-Badge; Uebersicht zeigt Delta. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,82 @@ export function commercialBadgeClass(commercial: string | null): string {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Controls drill-down (#80 Stufe-Flip + Provenance) ---
|
||||
|
||||
export interface ControlItem {
|
||||
id: string
|
||||
control_id?: string | null
|
||||
title: string
|
||||
objective?: string | null
|
||||
severity?: string | null
|
||||
sub_topic?: string | null
|
||||
canonical_obligation?: string | null
|
||||
source_regulation?: string | null
|
||||
source_article?: string | null
|
||||
relevant: boolean
|
||||
tier: 'core' | 'review'
|
||||
source_type: 'derived' | 'own_library'
|
||||
}
|
||||
|
||||
export interface ControlsResponse {
|
||||
use_case: string
|
||||
label: string
|
||||
group: string
|
||||
granularity: string
|
||||
tier: string
|
||||
total: number
|
||||
core_count: number
|
||||
review_count: number
|
||||
limit: number
|
||||
offset: number
|
||||
sub_topic: string | null
|
||||
subtopic_counts: Record<string, number>
|
||||
controls: ControlItem[]
|
||||
}
|
||||
|
||||
// Provenance line: own library vs derived-from-document (with the document, and
|
||||
// article when known). The user wants to see WHERE a derived control came from.
|
||||
export function provenanceLabel(
|
||||
c: Pick<ControlItem, 'source_type' | 'source_regulation' | 'source_article'>,
|
||||
): string {
|
||||
if (c.source_type === 'own_library') return 'Eigene Bibliothek'
|
||||
const doc = c.source_regulation?.trim()
|
||||
if (!doc) return 'Abgeleitet'
|
||||
const art = c.source_article?.trim()
|
||||
return art ? `Abgeleitet · ${doc} ${art}` : `Abgeleitet · ${doc}`
|
||||
}
|
||||
|
||||
export function provenanceBadgeClass(sourceType: string): string {
|
||||
return sourceType === 'own_library'
|
||||
? 'bg-amber-100 text-amber-800'
|
||||
: 'bg-blue-100 text-blue-800'
|
||||
}
|
||||
|
||||
export function severityBadgeClass(sev: string | null | undefined): string {
|
||||
switch ((sev || '').toLowerCase()) {
|
||||
case 'critical':
|
||||
return 'bg-red-100 text-red-800'
|
||||
case 'high':
|
||||
return 'bg-orange-100 text-orange-800'
|
||||
case 'medium':
|
||||
return 'bg-yellow-100 text-yellow-800'
|
||||
default:
|
||||
return 'bg-gray-100 text-gray-600'
|
||||
}
|
||||
}
|
||||
|
||||
// Split into the two display tiers: Kern-Pflichten (relevant) and the
|
||||
// 'zur Prüfung' tier (shown but flagged) — never hidden.
|
||||
export function splitByTier(controls: ControlItem[]): {
|
||||
core: ControlItem[]
|
||||
review: ControlItem[]
|
||||
} {
|
||||
const core: ControlItem[] = []
|
||||
const review: ControlItem[] = []
|
||||
for (const c of controls) (c.relevant ? core : review).push(c)
|
||||
return { core, review }
|
||||
}
|
||||
|
||||
export interface UseCaseGroup {
|
||||
group: string
|
||||
label: string
|
||||
|
||||
Reference in New Issue
Block a user