module Web.View.FederatedGovernance.Dashboard where import Web.Types import Generated.Types import IHP.Prelude import IHP.ViewPrelude import Web.Routes () import qualified Data.List as List data FederatedGovernanceDashboardView = FederatedGovernanceDashboardView { hubs :: ![Hub] , widgets :: ![Widget] , ownerships :: ![WidgetOwnership] , rules :: ![HubRoutingRule] , routedCandidates :: ![RequirementCandidate] , overlays :: ![FederatedPolicyOverlay] , allDecisions :: ![DecisionRecord] , allPolicies :: ![PolicyReference] , stewards :: ![StewardshipRole] , recentArchives :: ![ArchiveRecord] } instance View FederatedGovernanceDashboardView where html FederatedGovernanceDashboardView { .. } = [hsx|

Federated Governance

Policy Compliance →
{panel1Ownership} {panel2Routing} {panel3PolicyCompliance} {panel4Stewardship} {panel5Archive}
|] where -- ── Panel 1: Ownership coverage ────────────────────────────────── totalWidgets = length widgets ownedWidgetIds = List.nub (map (.widgetId) ownerships) ownedCount = length ownedWidgetIds localCount = length (filter (\o -> o.ownershipType == "local") ownerships) delegatedCount = length (filter (\o -> o.ownershipType == "delegated") ownerships) globalCount = length (filter (\o -> o.ownershipType == "global") ownerships) ownershipPct :: Int ownershipPct = if totalWidgets == 0 then 0 else (ownedCount * 100) `div` totalWidgets panel1Ownership = [hsx|

Ownership Coverage

View all →
{show ownedCount}
of {show totalWidgets} widgets owned
{show ownershipPct}%
coverage
local: {show localCount} delegated: {show delegatedCount} global: {show globalCount}
|] -- ── Panel 2: Routing activity ───────────────────────────────────── activeRulesCount = length rules routedCount = length routedCandidates hubName hid = maybe (show hid) (.name) (find (\h -> toUUID h.id == hid) hubs) panel2Routing = [hsx|

Routing Activity

Rules →
{show activeRulesCount}
active rules
{show routedCount}
routed (30 days)
{renderRulesSection rules}
|] renderRulesSection :: [HubRoutingRule] -> Html renderRulesSection [] = [hsx|

No active routing rules.

|] renderRulesSection rs = [hsx|
{forEach (take 5 rs) renderRuleRow}
|] renderRuleRow :: HubRoutingRule -> Html renderRuleRow r = [hsx|
{hubName r.sourceHubId} {hubName r.targetHubId} {maybe mempty renderMatchCategory r.matchCategory}
|] renderMatchCategory :: Text -> Html renderMatchCategory c = [hsx|({c})|] -- ── Panel 3: Policy compliance ──────────────────────────────────── activeOverlaysCount = length overlays decisionIdsWithPolicy = List.nub $ map (Just . (.decisionId)) allPolicies coveredDecisions = length $ filter (\d -> Just d.id `elem` decisionIdsWithPolicy) allDecisions totalDecisions = length allDecisions policyPct :: Int policyPct = if totalDecisions == 0 then 0 else (coveredDecisions * 100) `div` totalDecisions panel3PolicyCompliance = [hsx|

Policy Compliance

Dashboard →
{show activeOverlaysCount}
active overlays
{show policyPct}%
decision coverage
{renderOverlaysList overlays}
|] -- ── Panel 4: Stewardship coverage ───────────────────────────────── hubsWithStewards = List.nub (map (.hubId) stewards) stewardedCount = length hubsWithStewards totalHubs = length hubs hubsWithNoSteward = filter (\h -> toUUID h.id `notElem` hubsWithStewards) hubs panel4Stewardship = [hsx|

Stewardship Coverage

Roles →
{show stewardedCount}
of {show totalHubs} hubs stewarded
{show (length hubsWithNoSteward)}
hubs unassigned
{renderUnstewarded hubsWithNoSteward}
|] -- ── Panel 5: Archive activity ───────────────────────────────────── archiveByType = List.sortBy (\a b -> compare (fst a) (fst b)) $ map (\grp -> ((head grp).subjectType, length grp)) $ List.groupBy (\a b -> a.subjectType == b.subjectType) $ List.sortBy (\a b -> compare a.subjectType b.subjectType) recentArchives panel5Archive = [hsx|

Archive Activity (90 days)

All records →
{renderArchiveActivity recentArchives archiveByType}
|] renderOverlaysList :: [FederatedPolicyOverlay] -> Html renderOverlaysList [] = [hsx|

No active policy overlays.

|] renderOverlaysList overlays = [hsx|
{forEach overlays renderOverlayTitle}
|] renderOverlayTitle :: FederatedPolicyOverlay -> Html renderOverlayTitle o = [hsx|
{o.title}
|] renderUnstewarded :: [Hub] -> Html renderUnstewarded [] = [hsx|

All hubs have active stewards.

|] renderUnstewarded hs = [hsx|

Hubs without stewards:

{forEach hs renderUnstewardedHub}
|] renderUnstewardedHub :: Hub -> Html renderUnstewardedHub h = [hsx|{h.name}|] renderArchiveActivity :: [ArchiveRecord] -> [(Text, Int)] -> Html renderArchiveActivity [] _ = [hsx|

No artifacts archived in the last 90 days.

|] renderArchiveActivity archives byType = [hsx|
{show (length archives)}
total archived artifacts
{forEach byType renderArchiveTypeChip}
|] renderArchiveTypeChip :: (Text, Int) -> Html renderArchiveTypeChip (typ, cnt) = [hsx| {typ}: {show cnt} |]