import type { ReportDomainSummary, ReportProjection } from './report-projection'; import { ASSESSOR_PRODUCT_VERSION } from '../domain/assessor-release'; import { escapeHtml } from '../shared/html'; import { statementText } from './report-statement-text'; import { buildContextManagementHtml } from './context-management-report'; /** Offline management report: a factual view of the shared projection. */ export const MANAGEMENT_OVERVIEW_RENDERER_VERSION = '3.3.0'; const DASH = '—'; const count = (n: number): string => Number.isFinite(n) ? n.toLocaleString('de-DE') : DASH; function date(iso: string): string { const parsed = Date.parse(iso); return Number.isFinite(parsed) ? new Date(parsed).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) : DASH; } function tile(item: ReportDomainSummary): string { const assessed = item.assessed > 0 && Number.isFinite(item.assessed) && Number.isFinite(item.deviations); const share = assessed ? Math.round(100 * item.deviations / item.assessed) : undefined; const level = share === undefined ? 'unknown' : `h${share < 25 ? 0 : share < 50 ? 1 : share < 75 ? 2 : 3}`; return `
  • ${escapeHtml(item.domainLabel)} ${share === undefined ? DASH : `${count(share)} %`} ${assessed ? `${count(item.deviations)} von ${count(item.assessed)} bewertet` : 'Keine bewerteten Ergebnisse'}
  • `; } function comparisonRow(item: ReportDomainSummary): string { const assessed = Number.isFinite(item.assessed) && item.assessed > 0; const passed = assessed && Number.isFinite(item.passed) ? Math.max(0, Math.min(item.passed, item.assessed)) : 0; const deviations = assessed && Number.isFinite(item.deviations) ? Math.max(0, Math.min(item.deviations, item.assessed - passed)) : 0; const other = assessed ? Math.max(0, item.assessed - passed - deviations) : 0; const passPercent = assessed ? Math.round(100 * passed / item.assessed) : undefined; return `
  • ${escapeHtml(item.domainLabel)} ${other ? `` : ''} ${passPercent === undefined ? DASH : `${count(passPercent)} %`} · ${assessed ? `${count(item.deviations)} von ${count(item.assessed)}` : DASH}
  • `; } function keyStatements(projection: ReportProjection): string { const statements = projection.statements ?? []; if (!statements.length) return ''; return `

    Kernaussagen

    `; } export function buildManagementOverviewHtml(projection: ReportProjection): string { // Retain the historical renderer for archived projections; new management runs require Context. if (projection.context) return buildContextManagementHtml(projection); const domain = projection.environment.find(fact => fact.key === 'Domain')?.value; const dc = projection.environment.find(fact => fact.key === 'Domain Controller')?.value; const gpo = projection.gpo; const gpoNote = gpo && projection.domains.some(item => item.domainId === 'gpo') ? `

    Die GPO-Kachel zählt zugeordnete Ergebniszeilen. Die gesonderte GPO-Evaluation zählt ${count(gpo.counts.Fail)} abweichende Vergleiche zu ${count(gpo.affectedSettings)} Einstellungen. Diese Zähleinheiten sind nicht gleichzusetzen.${gpo.scopeLimited ? ' Die Evaluation bezieht sich auf die importierten GPO-Backups.' : ''}

    ` : ''; return ` ASSESSOR · Management Report · ${escapeHtml(projection.project.name)}
    ASSESSOR
    Management Report

    Gespeicherter Assessment-Stand

    Technische Ergebnisse im Überblick

    ${escapeHtml(projection.project.name)}

    ${domain ? `Domain: ${escapeHtml(domain)}` : ''}Berichtsdatum: ${date(projection.generatedAt)}
    ${dc ? escapeHtml(dc) : DASH}Domain Controller erfasst
    ${count(projection.coverage.assessed)}Ergebnisse bewertet
    ${count(projection.coverage.deviations)}Abweichende Ergebnisse
    ${keyStatements(projection)}

    Abweichungsanteil je Security Domain

    Abweichende von bewerteten Ergebnissen innerhalb des jeweiligen Bereichs.

      ${projection.domains.length ? projection.domains.map(tile).join('') : '
    1. Keine Bereiche verfügbar—Keine bewerteten Ergebnisse
    2. '}
    Farbstufe:0–24 %25–49 %50–74 %75–100 %

    Bestandene und abweichende Ergebnisse

      ${projection.domains.map(comparisonRow).join('')}
    BestandenAbweichung

    Pass Rate je Security Domain; rechts: Anzahl der Abweichungen von den bewerteten Ergebnissen.

    `; }