defmodule Oban.Web.Crons.TableComponent do use Oban.Web, :live_component import Oban.Web.Crons.Helpers, only: [maybe_to_unix: 1, show_name?: 1, state_icon: 1] alias Oban.Web.{Colors, Cron} @sparkline_count 60 @sparkline_height 16 @sparkline_bar_width 4 @sparkline_gap 1 @impl Phoenix.LiveComponent def render(assigns) do ~H"""

No crons

Crons run jobs on a schedule. Configure them in your Oban supervisor or create them dynamically.

""" end attr :label, :string, required: true attr :class, :string, default: "" defp header(assigns) do ~H""" {@label} """ end attr :history, :list, required: true attr :id, :string, required: true defp sparkline(assigns) do history = Enum.take(assigns.history, -@sparkline_count) offset = @sparkline_count - length(history) bars = for {job, index} <- Enum.with_index(history) do x = (offset + index) * (@sparkline_bar_width + @sparkline_gap) %{x: x, color: state_color(job.state)} end tooltip_data = for job <- history do unix = (job.finished_at || job.attempted_at || job.scheduled_at) |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_unix(:millisecond) %{timestamp: unix, state: job.state} end placeholders = for slot <- 0..(@sparkline_count - 1) do %{x: slot * (@sparkline_bar_width + @sparkline_gap)} end width = @sparkline_count * (@sparkline_bar_width + @sparkline_gap) assigns = assigns |> assign(bars: bars, placeholders: placeholders, width: width, offset: offset) |> assign(height: @sparkline_height, bar_width: @sparkline_bar_width) |> assign(tooltip_data: tooltip_data) ~H""" """ end defp state_color(state), do: Colors.state_hex(state) attr :cron, Cron attr :id, :string defp cron_row(assigns) do ~H"""
  • <.link patch={oban_path([:crons, @cron.name])} class="pl-3 py-3.5 flex flex-grow items-center">
    {@cron.worker} ({@cron.name})
    dynamic {tag} {format_opts(@cron.opts)}
    <.sparkline id={"sparkline-#{@cron.name}"} history={@cron.history} />
    {@cron.expression} - -
    <.state_icon state={@cron.last_state} paused={@cron.paused?} />
  • """ end defp state_title(%{paused?: true}), do: "Paused" defp state_title(cron) do case cron.last_state do nil -> "Unknown, no previous runs" state -> "#{String.capitalize(state)} as of #{NaiveDateTime.truncate(cron.last_at, :second)}" end end defp format_opts(opts) when map_size(opts) == 0, do: nil defp format_opts(opts) do opts |> Map.drop(["tags"]) |> case do filtered when map_size(filtered) == 0 -> nil filtered -> filtered |> Enum.map_join(", ", fn {key, val} -> "#{key}: #{inspect(val)}" end) |> truncate(0..98) end end defp has_tags?(opts), do: Map.has_key?(opts, "tags") and opts["tags"] != [] defp get_tags(opts), do: Map.get(opts, "tags", []) end