// 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 = { 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 = {} 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), })) }