module Web.View.Hubs.TriageDashboard where import Web.Types import Generated.Types import IHP.Prelude import IHP.ViewPrelude import Web.Routes () data TriageDashboardView = TriageDashboardView { hub :: !Hub , widgets :: ![Widget] , allCandidates :: ![RequirementCandidate] , triageQueue :: ![RequirementCandidate] , recentEscalations :: ![RequirementCandidate] , allAnnotations :: ![Annotation] } instance View TriageDashboardView where html TriageDashboardView { .. } = [hsx| {autoRefreshMeta}
Hubs / {hub.name} / Triage Dashboard

Triage Dashboard โ€” {hub.name}

All Candidates
{renderKpi "Open" "open" allCandidates "bg-blue-50 border-blue-200 text-blue-800"} {renderKpi "In Review" "in_review" allCandidates "bg-yellow-50 border-yellow-200 text-yellow-800"} {renderKpi "Accepted" "accepted" allCandidates "bg-green-50 border-green-200 text-green-800"} {renderKpi "Rejected" "rejected" allCandidates "bg-red-50 border-red-200 text-red-800"} {renderKpi "Deferred" "deferred" allCandidates "bg-gray-50 border-gray-200 text-gray-700"}

Triage Queue (Open)

{renderTriageQueue triageQueue widgets}

Recent Escalations

{renderEscalationsSection recentEscalations widgets}

Annotation Category Breakdown

{renderCategoryBreakdown allAnnotations}
|] renderKpi :: Text -> Text -> [RequirementCandidate] -> Text -> Html renderKpi label status candidates colorClass = let n = length $ filter (\c -> c.status == status) candidates in [hsx|
colorClass}>

{label}

{show n}

|] renderTriageQueue :: [RequirementCandidate] -> [Widget] -> Html renderTriageQueue [] _ = [hsx|

Queue empty.

|] renderTriageQueue items ws = [hsx|
{forEach items (renderQueueItem ws)}
|] renderEscalationsSection :: [RequirementCandidate] -> [Widget] -> Html renderEscalationsSection [] _ = [hsx|

No escalations yet.

|] renderEscalationsSection items ws = [hsx|
{forEach items (renderEscalationItem ws)}
|] renderQueueItem :: [Widget] -> RequirementCandidate -> Html renderQueueItem widgets c = let mWidget = find (\w -> w.id == c.sourceWidgetId) widgets age = show c.createdAt in [hsx|
{c.title} {age}
{maybe "โ€”" (.name) mWidget} ยท {c.category}
|] renderEscalationItem :: [Widget] -> RequirementCandidate -> Html renderEscalationItem widgets c = let mWidget = find (\w -> w.id == c.sourceWidgetId) widgets in [hsx|
" text-xs px-2 py-0.5 rounded"}>{c.status} {maybe "โ€”" (.name) mWidget}
{c.title}
|] renderCategoryBreakdown :: [Annotation] -> Html renderCategoryBreakdown annotations = let categories = ["friction", "defect", "wish", "policy_concern", "doc_gap", "trust", "other"] counts = map (\cat -> (cat, length $ filter (\a -> a.category == cat) annotations)) categories nonZero = filter (\(_, n) -> n > 0) counts total = length annotations in if total == 0 then [hsx|

No annotations yet.

|] else [hsx|
{forEach nonZero (renderCategoryBar total)}
|] renderCategoryBar :: Int -> (Text, Int) -> Html renderCategoryBar total (cat, n) = let pct = if total > 0 then (n * 100) `div` total else 0 in [hsx|
{cat}
show pct <> "%"}>
{show n}
|] statusClass :: Text -> Text statusClass "open" = "bg-blue-100 text-blue-700" statusClass "in_review" = "bg-yellow-100 text-yellow-800" statusClass "accepted" = "bg-green-100 text-green-800" statusClass "rejected" = "bg-red-100 text-red-800" statusClass "deferred" = "bg-gray-100 text-gray-600" statusClass _ = "bg-gray-100 text-gray-600"