From 1eaf6d9ac51965f196d07dff805e46f587b92738 Mon Sep 17 00:00:00 2001
From: mayor
Date: Fri, 6 Feb 2026 18:35:55 -0600
Subject: [PATCH] feat: build recent activity timeline with color-coded events
Co-Authored-By: Claude Sonnet 4.5
---
lib/towerops/job_monitoring.ex | 1 +
.../live/admin/monitoring_live.ex | 16 +++++++++
.../live/admin/monitoring_live.html.heex | 35 +++++++++++++++++++
3 files changed, 52 insertions(+)
diff --git a/lib/towerops/job_monitoring.ex b/lib/towerops/job_monitoring.ex
index c8a683ed..e216e8d8 100644
--- a/lib/towerops/job_monitoring.ex
+++ b/lib/towerops/job_monitoring.ex
@@ -88,6 +88,7 @@ defmodule Towerops.JobMonitoring do
limit: ^limit
)
|> Repo.all()
+ |> preload_device_context()
end
# Add helper to fetch device context
diff --git a/lib/towerops_web/live/admin/monitoring_live.ex b/lib/towerops_web/live/admin/monitoring_live.ex
index 8bd0a54a..bfbede0b 100644
--- a/lib/towerops_web/live/admin/monitoring_live.ex
+++ b/lib/towerops_web/live/admin/monitoring_live.ex
@@ -31,6 +31,7 @@ defmodule ToweropsWeb.Admin.MonitoringLive do
|> assign(:failed_jobs, [])
|> assign(:failed_jobs_count, 0)
|> assign(:recent_jobs, [])
+ |> assign(:recent_events, [])
|> assign(:metrics, %{})
|> assign(:health_metrics, %{})
|> load_monitoring_data()
@@ -76,6 +77,7 @@ defmodule ToweropsWeb.Admin.MonitoringLive do
|> assign(:failed_jobs, failed_jobs)
|> assign(:failed_jobs_count, length(failed_jobs))
|> assign(:recent_jobs, recent_jobs)
+ |> assign(:recent_events, recent_jobs)
|> assign(:metrics, metrics)
|> assign(:health_metrics, metrics)
end
@@ -94,4 +96,18 @@ defmodule ToweropsWeb.Admin.MonitoringLive do
true -> "#{div(minutes, 60)}h #{rem(minutes, 60)}m"
end
end
+
+ defp event_border_color("completed"), do: "border-green-500"
+ defp event_border_color("cancelled"), do: "border-red-500"
+ defp event_border_color("discarded"), do: "border-gray-400"
+ defp event_border_color(_), do: "border-gray-300"
+
+ defp event_outcome(job) do
+ case job.state do
+ "completed" -> "Completed"
+ "cancelled" -> "Failed"
+ "discarded" -> "Discarded"
+ _ -> String.capitalize(job.state)
+ end
+ end
end
diff --git a/lib/towerops_web/live/admin/monitoring_live.html.heex b/lib/towerops_web/live/admin/monitoring_live.html.heex
index c24d5e19..8815d726 100644
--- a/lib/towerops_web/live/admin/monitoring_live.html.heex
+++ b/lib/towerops_web/live/admin/monitoring_live.html.heex
@@ -269,5 +269,40 @@
+
+
+
+
+ Recent Activity
+
+
+ <%= if length(@recent_events) == 0 do %>
+
No recent activity
+ <% else %>
+
+ <%= for job <- Enum.take(@recent_events, 50) do %>
+
+
+
+
+ <%= if job.device do %>
+ <%= job.device.name %>
+ <% else %>
+ Device #{get_in(job.args, ["device_id"])}
+ <% end %>
+
+
+ <%= worker_name(job.worker) %> ยท <%= event_outcome(job) %>
+
+
+
+ <%= ToweropsWeb.TimeHelpers.format_time_ago(job.completed_at || job.updated_at) %>
+
+
+
+ <% end %>
+
+ <% end %>
+