00f304fed9
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) Successful in 11s
CI / validate-canonical-controls (push) Failing after 5s
CI / loc-budget (push) Successful in 22s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / test-go (push) Successful in 1m11s
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m41s
CI / iace-gt-coverage (push) Failing after 5s
CI / test-python-backend (push) Failing after 5s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
- Registry: arbeitsrecht, gesellschaftsrecht, insolvenzrecht, csrd, bafin_it + Mapper-Regeln für zuvor ungemappte Quell-Gesetze, Machinery-Guide 2006/42 -> maschinen. Jetzt 43 Use Cases (Achse 1 / license 1+2 vollständig). - corpus_overview Service + GET /v1/controls/corpus: Quell-Dokumente mit Lizenz-Tier + atom-Count + Use-Case + kuratiertem Lizenz-Katalog. - list_use_cases trägt atom_classification-Counts (atom_total/atom_relevant). - Frontend /sdk/coverage: Use-Case-Übersicht + Korpus-Dokumente + Lizenz-Katalog. - Tests: registry-Mappings (neue Domänen), corpus tier-labels, coverage-helpers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
106 lines
2.7 KiB
TypeScript
106 lines
2.7 KiB
TypeScript
// Pure helpers for the Coverage page (#74). Kept separate so they are unit-testable
|
|
// without rendering the server component.
|
|
|
|
export interface UseCaseRow {
|
|
key: string
|
|
label: string
|
|
group: string
|
|
regulations: string[]
|
|
verification_methods: string[]
|
|
mapped_controls: number
|
|
atom_total: number
|
|
atom_relevant: number
|
|
}
|
|
|
|
export interface CorpusDoc {
|
|
source_regulation: string
|
|
license_rule: number | null
|
|
license_tier: string
|
|
atom_count: number
|
|
use_case: string | null
|
|
}
|
|
|
|
export interface LicenseSummaryRow {
|
|
license_rule: number | null
|
|
label: string
|
|
atom_count: number
|
|
}
|
|
|
|
export interface LicenseCatalogEntry {
|
|
source_id: string
|
|
title: string
|
|
publisher: string | null
|
|
url: string | null
|
|
version: string | null
|
|
license_id: string | null
|
|
license_name: string | null
|
|
commercial_use: string | null
|
|
ship_in_product: boolean | null
|
|
terms_url: string | null
|
|
}
|
|
|
|
export interface CorpusOverview {
|
|
license_summary: LicenseSummaryRow[]
|
|
documents: CorpusDoc[]
|
|
license_catalog: LicenseCatalogEntry[]
|
|
totals: { documents: number; catalog_sources: number }
|
|
}
|
|
|
|
export const USE_CASE_GROUP_LABELS: Record<string, string> = {
|
|
document: 'Dokument-Compliance',
|
|
security: 'Security',
|
|
cross_cutting: 'Querschnitt',
|
|
product: 'Produkt / Sektor',
|
|
}
|
|
|
|
export function licenseTierBadgeClass(rule: number | null): string {
|
|
switch (rule) {
|
|
case 1:
|
|
return 'bg-green-100 text-green-800'
|
|
case 2:
|
|
return 'bg-blue-100 text-blue-800'
|
|
case 3:
|
|
return 'bg-amber-100 text-amber-800'
|
|
default:
|
|
return 'bg-gray-100 text-gray-700'
|
|
}
|
|
}
|
|
|
|
export function commercialBadgeClass(commercial: string | null): string {
|
|
switch ((commercial || '').toLowerCase()) {
|
|
case 'allowed':
|
|
return 'bg-green-100 text-green-800'
|
|
case 'restricted':
|
|
return 'bg-amber-100 text-amber-800'
|
|
case 'prohibited':
|
|
return 'bg-red-100 text-red-800'
|
|
default:
|
|
return 'bg-gray-100 text-gray-700'
|
|
}
|
|
}
|
|
|
|
export interface UseCaseGroup {
|
|
group: string
|
|
label: string
|
|
rows: UseCaseRow[]
|
|
}
|
|
|
|
// Group use-cases by their registry group (stable order), each group's rows
|
|
// sorted by how many relevant obligations it carries (desc).
|
|
export function groupUseCases(rows: UseCaseRow[]): UseCaseGroup[] {
|
|
const order = ['document', 'security', 'cross_cutting', 'product']
|
|
const by: Record<string, UseCaseRow[]> = {}
|
|
for (const r of rows) {
|
|
;(by[r.group] ||= []).push(r)
|
|
}
|
|
const groups = order.filter((g) => by[g]?.length)
|
|
for (const g of Object.keys(by)) {
|
|
if (!order.includes(g)) groups.push(g)
|
|
}
|
|
return groups.map((g) => ({
|
|
group: g,
|
|
label: USE_CASE_GROUP_LABELS[g] || g,
|
|
rows: [...by[g]].sort((a, b) => b.atom_relevant - a.atom_relevant),
|
|
}))
|
|
}
|