Public/Export-Dashboard.ps1

# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0
# https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/
# AI/LLM use: see AI-USAGE.md for required attribution
function Export-Dashboard {
    <#
    .SYNOPSIS
        Generates a unified HTML dashboard across all theaters.
    .DESCRIPTION
        Produces a single-file HTML dashboard with the Guerrilla Score ring at top,
        theater-specific cards with mini-scores, and a consolidated findings table.
    .PARAMETER Findings
        Array of audit finding objects. If not provided, reads from latest state.
    .PARAMETER ScanResults
        Array of scan result objects. If not provided, reads from latest state.
    .PARAMETER OutputPath
        File path for the HTML output. Default: PSGuerrilla-Dashboard.html
    .PARAMETER OrganizationName
        Organization name for the report header.
    .EXAMPLE
        Export-Dashboard -OrganizationName 'Springfield USD'
    .EXAMPLE
        Export-Dashboard -OutputPath ./dashboard.html
    #>

    [CmdletBinding()]
    param(
        [PSCustomObject[]]$Findings,
        [PSCustomObject[]]$ScanResults,
        [string]$OutputPath,
        [string]$OrganizationName = 'Organization'
    )

    if (-not $OutputPath) { $OutputPath = Join-Path (Get-Location) 'PSGuerrilla-Dashboard.html' }

    $dataDir = Get-PSGuerrillaDataRoot

    # Load findings if not provided
    if (-not $Findings -or $Findings.Count -eq 0) {
        if (Test-Path $dataDir) {
            foreach ($f in (Get-ChildItem -Path $dataDir -Filter '*.findings.json' -ErrorAction SilentlyContinue)) {
                try { $Findings += @(Get-Content $f.FullName -Raw | ConvertFrom-Json) } catch { }
            }
        }
    }

    # Load scan results if not provided
    if (-not $ScanResults -or $ScanResults.Count -eq 0) {
        if (Test-Path $dataDir) {
            foreach ($f in (Get-ChildItem -Path $dataDir -Filter '*.state.json' -ErrorAction SilentlyContinue)) {
                try { $ScanResults += (Get-Content $f.FullName -Raw | ConvertFrom-Json) } catch { }
            }
        }
    }

    # Calculate Guerrilla Score
    $scoreResult = $null
    try { $scoreResult = Get-GuerrillaScoreCalculation -AuditFindings $Findings -ScanResults $ScanResults } catch { }
    if (-not $scoreResult) {
        $scoreResult = [PSCustomObject]@{ Score = 0; Label = 'N/A'; Components = $null }
    }

    # Generate the HTML
    $generatedPath = Export-DashboardHtml `
        -ScoreResult $scoreResult `
        -Findings $Findings `
        -ScanResults $ScanResults `
        -OutputPath $OutputPath `
        -OrganizationName $OrganizationName

    return [PSCustomObject]@{
        PSTypeName = 'PSGuerrilla.Dashboard'
        Success    = $true
        Path       = (Resolve-Path $OutputPath).Path
        Message    = "Dashboard exported to $OutputPath"
        Score      = $scoreResult.Score
        Label      = $scoreResult.Label
    }
}