{t("Job Monitoring Dashboard")}

{t("Real-time monitoring of polling and discovery jobs")}

Completed (1h)

{Map.get(@health_metrics, :completed_last_hour, 0)}

jobs completed

Failed (1h)

{Map.get(@health_metrics, :failed_last_hour, 0)}

failures

{t("Avg Duration")}

<%= if Map.get(@health_metrics, :avg_execution_time_seconds) do %> {Map.get(@health_metrics, :avg_execution_time_seconds) |> Float.round(1)}s <% else %> {t("N/A")} <% end %>

last hour

{t("Active Jobs")}

{length(@active_jobs)}

executing now

Active Operations ({length(@executing_jobs)})

<%= if length(@executing_jobs) == 0 do %>

No jobs currently executing

<% else %>
<%= for job <- @executing_jobs do %>

<%= if job.device do %> {job.device.name} <% else %> Device #{get_in(job.args, ["device_id"])} <% end %>

{worker_name(job.worker)}

Started {ToweropsWeb.TimeHelpers.format_time_ago(job.attempted_at)}

<.icon name="hero-arrow-path" class="w-4 h-4 animate-spin text-blue-600" />
<% end %>
<% end %>
<%= if length(@stuck_jobs) > 0 or length(@failed_jobs) > 0 do %>

⚠️ Problems Detected

<%= if length(@stuck_jobs) > 0 do %>

Stuck Jobs ({length(@stuck_jobs)})

<%= for job <- @stuck_jobs do %>

<%= if job.device do %> {job.device.name} <% else %> Device #{get_in(job.args, ["device_id"])} <% end %>

{worker_name(job.worker)}

Running for {duration_in_words(job.attempted_at)}

<% end %>
<% end %> <%= if length(@failed_jobs) > 0 do %>

Failed Jobs ({length(@failed_jobs)})

<%= for job <- Enum.take(@failed_jobs, 5) do %>

<%= if job.device do %> {job.device.name} <% else %> Device #{get_in(job.args, ["device_id"])} <% end %>

{worker_name(job.worker)} - Attempt {job.attempt}/{job.max_attempts}

<%= if job.errors && length(job.errors) > 0 do %>

{hd(job.errors)["error"] || "Unknown error"}

<% end %>
<% end %>
<% end %>
<% end %>

{t("Health Metrics")}

{t("Current Activity")}

Executing

{@metrics.executing_count}

Queued

{@metrics.queued_count}

Completed (1h)

{@metrics.completed_last_hour}

Failed (1h)

{@metrics.failed_last_hour}

{t("Success Rates")}

Polling (1h) {Float.round(@metrics.polling_success_rate_1h * 100, 1)}%
Discovery (1h) {Float.round(@metrics.discovery_success_rate_1h * 100, 1)}%

{t("Performance")}

Avg Execution Time
<%= if @metrics.avg_execution_time_seconds do %> {Float.round(@metrics.avg_execution_time_seconds, 1)}s <% else %> {t("N/A")} <% end %>
Jobs/Hour
{@metrics.completed_last_hour}

{t("Queue Depths")}

Pollers
{@metrics.queue_depths.pollers}
Discovery
{@metrics.queue_depths.discovery}

{t("Updates every 10 seconds")}

{t("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.cancelled_at || job.discarded_at || job.inserted_at )}
<% end %>
<% end %>