summaryrefslogtreecommitdiff
path: root/Omni/Jr/Web.hs
diff options
context:
space:
mode:
authorBen Sima <ben@bensima.com>2025-11-27 09:59:42 -0500
committerBen Sima <ben@bensima.com>2025-11-27 09:59:42 -0500
commitdbe2b82d2f53761d9504fb4e5cf37f64a2000b7a (patch)
tree505a236b1de090a411b0ce5b6ced034d42530ec7 /Omni/Jr/Web.hs
parentf151fbf764670369c54d2b9c03a5d1c8c1bb82fb (diff)
Show activity timeline on task detail page in web UI
All builds pass. Here's a summary of the changes I made: **Omni/Task/Core.hs:** - Added `getActivitiesForTask` function to query task_activity table for **Omni/Jr/Web.hs:** - Added `Data.Time` import for timestamp formatting - Updated `TaskDetailPage` type to include `[TaskCore.TaskActivity]` - Updated `taskDetailHandler` to fetch activities for the task - Added activity timeline rendering in `toHtml` instance for InProgress - Added helper functions: `renderActivity`, `stageClass`, `stageIcon`, ` **Omni/Jr/Web/Style.hs:** - Added `activityTimelineStyles` function with vertical timeline CSS - Added stage-specific colors (claiming=blue, running=yellow, reviewing= - Added dark mode styles for the activity timeline Task-Id: t-148.3
Diffstat (limited to 'Omni/Jr/Web.hs')
-rw-r--r--Omni/Jr/Web.hs54
1 files changed, 51 insertions, 3 deletions
diff --git a/Omni/Jr/Web.hs b/Omni/Jr/Web.hs
index 28f42a2..d117169 100644
--- a/Omni/Jr/Web.hs
+++ b/Omni/Jr/Web.hs
@@ -21,6 +21,7 @@ import qualified Data.List as List
import qualified Data.Text as Text
import qualified Data.Text.Lazy as LazyText
import qualified Data.Text.Lazy.Encoding as LazyText
+import Data.Time (UTCTime, defaultTimeLocale, formatTime)
import qualified Lucid
import qualified Network.Wai.Handler.Warp as Warp
import qualified Omni.Jr.Web.Style as Style
@@ -74,7 +75,7 @@ newtype ReadyQueuePage = ReadyQueuePage [TaskCore.Task]
data TaskListPage = TaskListPage [TaskCore.Task] TaskFilters
data TaskDetailPage
- = TaskDetailFound TaskCore.Task [TaskCore.Task]
+ = TaskDetailFound TaskCore.Task [TaskCore.Task] [TaskCore.TaskActivity]
| TaskDetailNotFound Text
data TaskReviewPage
@@ -271,7 +272,7 @@ instance Lucid.ToHtml TaskDetailPage where
Lucid.code_ (Lucid.toHtml tid)
" could not be found."
Lucid.p_ [Lucid.class_ "back-link"] <| Lucid.a_ [Lucid.href_ "/tasks"] "← Back to Tasks"
- toHtml (TaskDetailFound task allTasks) =
+ toHtml (TaskDetailFound task allTasks activities) =
Lucid.doctypehtml_ <| do
pageHead (TaskCore.taskId task <> " - Jr")
Lucid.body_ <| do
@@ -342,6 +343,12 @@ instance Lucid.ToHtml TaskDetailPage where
Lucid.ul_ [Lucid.class_ "child-list"] <| do
traverse_ renderChild children
+ when (TaskCore.taskStatus task == TaskCore.InProgress && not (null activities)) <| do
+ Lucid.div_ [Lucid.class_ "activity-section"] <| do
+ Lucid.h3_ "Activity Timeline"
+ Lucid.div_ [Lucid.class_ "activity-timeline"] <| do
+ traverse_ renderActivity activities
+
when (TaskCore.taskStatus task == TaskCore.Review) <| do
Lucid.div_ [Lucid.class_ "review-link-section"] <| do
Lucid.a_
@@ -387,6 +394,45 @@ instance Lucid.ToHtml TaskDetailPage where
Lucid.span_ [Lucid.class_ "child-title"] <| Lucid.toHtml (" - " <> TaskCore.taskTitle child)
Lucid.span_ [Lucid.class_ "child-status"] <| Lucid.toHtml (" [" <> tshow (TaskCore.taskStatus child) <> "]")
+ renderActivity :: (Monad m) => TaskCore.TaskActivity -> Lucid.HtmlT m ()
+ renderActivity act =
+ Lucid.div_ [Lucid.class_ ("activity-item " <> stageClass (TaskCore.activityStage act))] <| do
+ Lucid.div_ [Lucid.class_ "activity-icon"] (stageIcon (TaskCore.activityStage act))
+ Lucid.div_ [Lucid.class_ "activity-content"] <| do
+ Lucid.div_ [Lucid.class_ "activity-header"] <| do
+ Lucid.span_ [Lucid.class_ "activity-stage"] (Lucid.toHtml (tshow (TaskCore.activityStage act)))
+ Lucid.span_ [Lucid.class_ "activity-time"] (Lucid.toHtml (formatTimestamp (TaskCore.activityTimestamp act)))
+ case TaskCore.activityMessage act of
+ Nothing -> pure ()
+ Just msg -> Lucid.p_ [Lucid.class_ "activity-message"] (Lucid.toHtml msg)
+ case TaskCore.activityMetadata act of
+ Nothing -> pure ()
+ Just meta ->
+ Lucid.details_ [Lucid.class_ "activity-metadata"] <| do
+ Lucid.summary_ "Metadata"
+ Lucid.pre_ [Lucid.class_ "metadata-json"] (Lucid.toHtml meta)
+
+ stageClass :: TaskCore.ActivityStage -> Text
+ stageClass stage = case stage of
+ TaskCore.Claiming -> "stage-claiming"
+ TaskCore.Running -> "stage-running"
+ TaskCore.Reviewing -> "stage-reviewing"
+ TaskCore.Retrying -> "stage-retrying"
+ TaskCore.Completed -> "stage-completed"
+ TaskCore.Failed -> "stage-failed"
+
+ stageIcon :: (Monad m) => TaskCore.ActivityStage -> Lucid.HtmlT m ()
+ stageIcon stage = case stage of
+ TaskCore.Claiming -> "●"
+ TaskCore.Running -> "▶"
+ TaskCore.Reviewing -> "◎"
+ TaskCore.Retrying -> "↻"
+ TaskCore.Completed -> "✓"
+ TaskCore.Failed -> "✗"
+
+ formatTimestamp :: UTCTime -> Text
+ formatTimestamp = Text.pack <. formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S"
+
instance Lucid.ToHtml TaskReviewPage where
toHtmlRaw = Lucid.toHtml
toHtml (ReviewPageNotFound tid) =
@@ -645,7 +691,9 @@ server =
tasks <- liftIO TaskCore.loadTasks
case TaskCore.findTask tid tasks of
Nothing -> pure (TaskDetailNotFound tid)
- Just task -> pure (TaskDetailFound task tasks)
+ Just task -> do
+ activities <- liftIO (TaskCore.getActivitiesForTask tid)
+ pure (TaskDetailFound task tasks activities)
taskStatusHandler :: Text -> StatusForm -> Servant.Handler (Headers '[Header "Location" Text] NoContent)
taskStatusHandler tid (StatusForm newStatus) = do