module Web.View.Hubs.OperationalReviewBoard where
import Web.Types
import Generated.Types
import IHP.Prelude
import IHP.ViewPrelude
import Application.Helper.HubHealth (healthScoreBadge)
import Application.Helper.FrictionScore (scoreBand)
import Web.View.Hubs.BottleneckDashboard (severityBadge)
data OperationalReviewBoardView = OperationalReviewBoardView
{ hubs :: ![Hub]
, allSnapshots :: ![HubHealthSnapshot]
, topFrictionScores :: ![FrictionScore]
, topWidgets :: ![Widget]
, bottlenecks :: ![BottleneckRecord]
, openPropagations :: ![CrossHubPropagation]
}
instance View OperationalReviewBoardView where
html OperationalReviewBoardView { .. } = [hsx|
Operational Review Board
Hub Health Matrix
{if null hubs
then [hsx|
No hubs registered.
|]
else [hsx|
| Hub |
Health |
Snapshot |
|
{forEach hubs renderHubRow}
|]}
Top Friction Widgets
{if null topFrictionScores
then [hsx|
No friction scores computed yet.
|]
else [hsx|
| Widget |
Score |
Type |
{forEach (zip topFrictionScores topWidgets) renderFrictionRow}
|]}
Active Bottlenecks by Stage
{if null bottlenecks
then [hsx|
No active bottlenecks.
|]
else [hsx|
{forEach stages renderBottleneckStage}
|]}
Open Cross-Hub Propagations
{if null openPropagations
then [hsx|
No open propagation events.
|]
else [hsx|
{forEach openPropagations renderPropagationRow}
|]}
|]
where
stages = ["candidate", "requirement", "decision", "observation"] :: [Text]
stageLabel s = case s of
"candidate" -> "Candidate"
"requirement" -> "Requirement"
"decision" -> "Decision"
"observation" -> "Observation"
_ -> s
latestSnapshotFor hub =
find (\s -> s.hubId == hub.id) allSnapshots
renderHubRow :: Hub -> Html
renderHubRow h =
let mSnap = latestSnapshotFor h
in [hsx|
|
{h.name}
|
{case mSnap of
Nothing -> [hsx|–|]
Just s -> [hsx|
healthScoreBadge s.healthScore}>
{show s.healthScore}
|]}
|
{maybe "never" (\s -> show s.computedAt) mSnap}
|
History
|
|]
renderFrictionRow :: (FrictionScore, Widget) -> Html
renderFrictionRow (fs, w) = [hsx|
|
{w.name}
|
scoreBand fs.score}>
{show fs.score}
|
{w.widgetType} |
|]
renderBottleneckStage :: Text -> Html
renderBottleneckStage stage =
let stageBNs = filter (\b -> b.stage == stage) bottlenecks
cnt = length stageBNs
hasCrit = any (\b -> b.severity == "critical") stageBNs
colourCls = if cnt == 0 then "bg-gray-50 text-gray-400"
else if hasCrit then "bg-red-50 text-red-700"
else "bg-orange-50 text-orange-700"
in [hsx|
colourCls}>
{show cnt}
{stageLabel stage}
|]
renderPropagationRow :: CrossHubPropagation -> Html
renderPropagationRow p = [hsx|
{p.patternType}
{p.summary}
{show p.detectedAt}
|]