← Back to task

Commit 0356aac5

commit 0356aac5ad591172967f688e71d1e4351c99c7fe
Author: Ben Sima <ben@bensima.com>
Date:   Thu Nov 27 17:14:33 2025

    Implement multi-color progress bar component
    
    The build and test passed with no errors. The multi-color progress
    bar c
    
    **Implementation summary:** -
    **[Omni/Jr/Web.hs](file:///home/ben/omni/Omni/Jr/Web.hs#L234-L275)**: -
    **[Omni/Jr/Web/Style.hs](file:///home/ben/omni/Omni/Jr/Web/Style.hs#L3
    
    The progress bar is used on the dashboard (line 324 in Web.hs)
    to visual
    
    Task-Id: t-155.3

diff --git a/Omni/Jr/Web.hs b/Omni/Jr/Web.hs
index 72de7dbb..d3130cee 100644
--- a/Omni/Jr/Web.hs
+++ b/Omni/Jr/Web.hs
@@ -231,6 +231,49 @@ statusBadge status =
         TaskCore.Done -> ("badge badge-done", "Done")
    in Lucid.span_ [Lucid.class_ cls] label
 
+multiColorProgressBar :: (Monad m) => TaskCore.TaskStats -> Lucid.HtmlT m ()
+multiColorProgressBar stats =
+  let total = TaskCore.totalTasks stats
+      doneCount = TaskCore.doneTasks stats
+      inProgressCount = TaskCore.inProgressTasks stats
+      openCount = TaskCore.openTasks stats + TaskCore.reviewTasks stats + TaskCore.approvedTasks stats
+      donePct = if total == 0 then 0 else (doneCount * 100) `div` total
+      inProgressPct = if total == 0 then 0 else (inProgressCount * 100) `div` total
+      openPct = if total == 0 then 0 else (openCount * 100) `div` total
+   in Lucid.div_ [Lucid.class_ "multi-progress-container"] <| do
+        Lucid.div_ [Lucid.class_ "multi-progress-bar"] <| do
+          when (donePct > 0)
+            <| Lucid.div_
+              [ Lucid.class_ "multi-progress-segment progress-done",
+                Lucid.style_ ("width: " <> tshow donePct <> "%"),
+                Lucid.title_ (tshow doneCount <> " done")
+              ]
+              ""
+          when (inProgressPct > 0)
+            <| Lucid.div_
+              [ Lucid.class_ "multi-progress-segment progress-inprogress",
+                Lucid.style_ ("width: " <> tshow inProgressPct <> "%"),
+                Lucid.title_ (tshow inProgressCount <> " in progress")
+              ]
+              ""
+          when (openPct > 0)
+            <| Lucid.div_
+              [ Lucid.class_ "multi-progress-segment progress-open",
+                Lucid.style_ ("width: " <> tshow openPct <> "%"),
+                Lucid.title_ (tshow openCount <> " open")
+              ]
+              ""
+        Lucid.div_ [Lucid.class_ "progress-legend"] <| do
+          Lucid.span_ [Lucid.class_ "legend-item"] <| do
+            Lucid.span_ [Lucid.class_ "legend-dot legend-done"] ""
+            Lucid.toHtml ("Done " <> tshow doneCount)
+          Lucid.span_ [Lucid.class_ "legend-item"] <| do
+            Lucid.span_ [Lucid.class_ "legend-dot legend-inprogress"] ""
+            Lucid.toHtml ("In Progress " <> tshow inProgressCount)
+          Lucid.span_ [Lucid.class_ "legend-item"] <| do
+            Lucid.span_ [Lucid.class_ "legend-dot legend-open"] ""
+            Lucid.toHtml ("Open " <> tshow openCount)
+
 statusBadgeWithForm :: (Monad m) => TaskCore.Status -> Text -> Lucid.HtmlT m ()
 statusBadgeWithForm status tid =
   Lucid.div_ [Lucid.id_ "status-badge-container", Lucid.class_ "status-badge-container"] <| do
@@ -278,6 +321,7 @@ instance Lucid.ToHtml HomePage where
           Lucid.h1_ "Jr Dashboard"
 
           Lucid.h2_ "Task Status"
+          multiColorProgressBar stats
           Lucid.div_ [Lucid.class_ "stats-grid"] <| do
             statCard "Open" (TaskCore.openTasks stats) "badge-open" "/tasks?status=Open"
             statCard "In Progress" (TaskCore.inProgressTasks stats) "badge-inprogress" "/tasks?status=InProgress"
@@ -979,6 +1023,7 @@ instance Lucid.ToHtml StatsPage where
               Lucid.a_ [Lucid.href_ "/stats", Lucid.class_ "clear-btn"] "Clear"
 
           Lucid.h2_ "By Status"
+          multiColorProgressBar stats
           Lucid.div_ [Lucid.class_ "stats-grid"] <| do
             statCard "Open" (TaskCore.openTasks stats) (TaskCore.totalTasks stats)
             statCard "In Progress" (TaskCore.inProgressTasks stats) (TaskCore.totalTasks stats)
diff --git a/Omni/Jr/Web/Style.hs b/Omni/Jr/Web/Style.hs
index dd5bc404..8b1946a4 100644
--- a/Omni/Jr/Web/Style.hs
+++ b/Omni/Jr/Web/Style.hs
@@ -369,6 +369,39 @@ cardStyles = do
     backgroundColor "#0066cc"
     borderRadius (px 2) (px 2) (px 2) (px 2)
     transition "width" (ms 300) ease (sec 0)
+  ".multi-progress-container" ? do
+    marginBottom (px 12)
+  ".multi-progress-bar" ? do
+    display flex
+    height (px 8)
+    backgroundColor "#e5e7eb"
+    borderRadius (px 4) (px 4) (px 4) (px 4)
+    overflow hidden
+    marginTop (px 6)
+  ".multi-progress-segment" ? do
+    height (pct 100)
+    transition "width" (ms 300) ease (sec 0)
+  ".progress-done" ? backgroundColor "#10b981"
+  ".progress-inprogress" ? backgroundColor "#f59e0b"
+  ".progress-open" ? backgroundColor "#3b82f6"
+  ".progress-legend" ? do
+    display flex
+    Stylesheet.key "gap" ("16px" :: Text)
+    marginTop (px 6)
+    fontSize (px 12)
+    color "#6b7280"
+  ".legend-item" ? do
+    display flex
+    alignItems center
+    Stylesheet.key "gap" ("4px" :: Text)
+  ".legend-dot" ? do
+    display inlineBlock
+    width (px 10)
+    height (px 10)
+    borderRadius (px 2) (px 2) (px 2) (px 2)
+  ".legend-done" ? backgroundColor "#10b981"
+  ".legend-inprogress" ? backgroundColor "#f59e0b"
+  ".legend-open" ? backgroundColor "#3b82f6"
   ".stats-section" ? do
     backgroundColor white
     borderRadius (px 2) (px 2) (px 2) (px 2)
@@ -996,6 +1029,8 @@ darkModeStyles =
     ".stats-row" ? borderBottomColor "#374151"
     ".progress-bar" ? backgroundColor "#374151"
     ".progress-fill" ? backgroundColor "#60a5fa"
+    ".multi-progress-bar" ? backgroundColor "#374151"
+    ".progress-legend" ? color "#9ca3af"
     ".activity-section" ? do
       backgroundColor "#1f2937"
       borderColor "#374151"