deps: bump oban 2.22.0 → 2.22.1, oban_web (vendored) 2.12.1 → 2.12.3

Latest-version sweep across the Oban stack. Final state:

  oban     2.22.1   (hexpm)
  oban_pro 1.7.0    (vendored, latest in licensed repo)
  oban_met 1.1.0    (vendored, latest in licensed repo and hexpm)
  oban_web 2.12.3   (vendored from public hexpm)

oban_web was re-vendored via `mix hex.package fetch oban_web 2.12.3
--unpack` and dropped into vendor/oban_web. No local mods to
preserve — vendor commit history shows the dir was a vanilla
import.
This commit is contained in:
Graham McIntire 2026-04-30 09:34:45 -05:00
parent 355a9df829
commit e6ce8be98c
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
111 changed files with 291 additions and 579 deletions

View file

@ -41,7 +41,7 @@
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"}, "nx": {:hex, :nx, "0.11.0", "d37723dbd6cfa274a5def6d6664f5680c32e2eb8a1ce25ec6d91751967fa0abf", [:mix], [{:complex, "~> 0.6", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36157b21239aeb251d6cbac23eb0eb3495a5e1e0cbc2e6df16afd2ede1575205"},
"oban": {:hex, :oban, "2.22.0", "d50fb8be22a58ca8839dbc69610e271773d851b7deae01acd8b62e7197bcd318", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.20", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6675164c11eadd82044732dd7b6e448aa6d6f429a41a1e7d6dbf5fd358f52f26"}, "oban": {:hex, :oban, "2.22.1", "9d2a38cec95070b31c1e274fae55f3925089f62159d8f3facabee0454d55b257", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.20", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "af2508c156c5b0ec30b21b0883babf7e2716af35ed5d264095896103fe3cea37"},
"octo_fetch": {:hex, :octo_fetch, "0.5.0", "f50701568b9fc752656367f82cc134d5fbefff37c5a0e8ddfcceb02ceee3f5fc", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6226cc3c14ca948ee9f25fb0446322e5c288e215da9beba7899b6b5f4cd3ccb0"}, "octo_fetch": {:hex, :octo_fetch, "0.5.0", "f50701568b9fc752656367f82cc134d5fbefff37c5a0e8ddfcceb02ceee3f5fc", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6226cc3c14ca948ee9f25fb0446322e5c288e215da9beba7899b6b5f4cd3ccb0"},
"owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"}, "owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"},
"peep": {:hex, :peep, "3.5.0", "9f6ead7b0f2c684494200c8fc02e7e62e8c459afe861b29bd859e4c96f402ed8", [:mix], [{:nimble_options, "~> 1.1", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:plug, "~> 1.16", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry_metrics, "~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5a73a99c6e60062415efeb7e536a663387146463a3d3df1417da31fd665ac210"}, "peep": {:hex, :peep, "3.5.0", "9f6ead7b0f2c684494200c8fc02e7e62e8c459afe861b29bd859e4c96f402ed8", [:mix], [{:nimble_options, "~> 1.1", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:plug, "~> 1.16", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry_metrics, "~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5a73a99c6e60062415efeb7e536a663387146463a3d3df1417da31fd665ac210"},

BIN
vendor/oban_web/.hex vendored

Binary file not shown.

View file

@ -15,7 +15,8 @@ workflow progress, activity counts, duration, and nested sub-workflows. Workflow
by properties like name, workers, or status. by properties like name, workers, or status.
<video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;"> <video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;">
<source src="https://media.oban.pro/web-2-12-workflows-av1.mp4" type="video/mp4"> <source src="https://media.oban.pro/web-2-12-workflows-av1.mp4" type="video/mp4" codecs="av01">
<source src="https://media.oban.pro/web-2-12-workflows.mp4" type="video/mp4">
</video> </video>
Clicking into a workflow brings you to a detail view with an interactive graph showing jobs as Clicking into a workflow brings you to a detail view with an interactive graph showing jobs as
@ -34,7 +35,8 @@ There's also a new page for viewing and managing cron entries. The table display
dynamic entries with history sparklines and activity details. dynamic entries with history sparklines and activity details.
<video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;"> <video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;">
<source src="https://media.oban.pro/web-2-12-crons-av1.mp4" type="video/mp4"> <source src="https://media.oban.pro/web-2-12-crons-av1.mp4" type="video/mp4" codecs="av01">
<source src="https://media.oban.pro/web-2-12-crons.mp4" type="video/mp4">
</video> </video>
The cron detail view includes natural language expressions like "Daily at 8:00 and 9:00" or The cron detail view includes natural language expressions like "Daily at 8:00 and 9:00" or
@ -49,7 +51,8 @@ The job detail page is rebuilt with a full-width layout and a new timeline compo
the job state machine as a branching diagram rather than a linear progression. the job state machine as a branching diagram rather than a linear progression.
<video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;"> <video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;">
<source src="https://media.oban.pro/web-2-12-jobs-details-av1.mp4" type="video/mp4"> <source src="https://media.oban.pro/web-2-12-jobs-details-av1.mp4" type="video/mp4" codecs="av01">
<source src="https://media.oban.pro/web-2-12-jobs-details.mp4" type="video/mp4">
</video> </video>
A scoped chart displays execution history for that worker's previous jobs and jobs in an A scoped chart displays execution history for that worker's previous jobs and jobs in an
@ -66,13 +69,45 @@ throughput for each queue. The queue sidebar provided minimal value, and it was
room for the additional data displayed per-row. room for the additional data displayed per-row.
<video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;"> <video autoplay loop muted playsinline loading="lazy" preload="none" style="width: 100%; border-radius: 12px;">
<source src="https://media.oban.pro/web-2-12-queues-av1.mp4" type="video/mp4"> <source src="https://media.oban.pro/web-2-12-queues-av1.mp4" type="video/mp4" codecs="av01">
<source src="https://media.oban.pro/web-2-12-queues.mp4" type="video/mp4">
</video> </video>
The queue detail page adds status badges for paused, partial, and terminating states, with The queue detail page adds status badges for paused, partial, and terminating states, with
pause/resume, stop, and edit buttons in the header. Partitioning controls are expanded with meta pause/resume, stop, and edit buttons in the header. Partitioning controls are expanded with meta
options and burst mode configuration. options and burst mode configuration.
## v2.12.3 - 2026-04-15
### Bug Fixes
- [Dashboard] Switch icons from inline SVG to CSS masks
CSP doesn't allow inline styles, which includes the url-masks that our app was using for icons.
This removes the icons/asset pipeline in favor of the standard Tailwind plugin approach used by
modern Phoenix apps
## v2.12.2 - 2026-03-31
### Enhancements
- [Jobs] Allow editing jobs in any state except `executing`
`Oban.update_job/3` allows editing any non-executing job, so the detail component's edit form
should as well.
- [Standalone] Ship inetrc in standalone Docker image for native DNS resolution (#173)
The BEAM VM's built-in DNS resolver ignores `/etc/resolv.conf`, which prevents the standalone
image from resolving internal hostnames on platforms like Fly.io and Kubernetes with CoreDNS.
### Bug Fixes
- [Query] Prevent table check with non-PostgreSQL engines
The crons and workflows table checks only apply to Pro, and only Postgres engines should check
whether the tables exist.
## v2.12.1 - 2026-03-25 ## v2.12.1 - 2026-03-25
### Bug Fixes ### Bug Fixes

View file

@ -4,7 +4,7 @@
<<"https://github.com/oban-bg/oban_web/blob/main/CHANGELOG.md">>}, <<"https://github.com/oban-bg/oban_web/blob/main/CHANGELOG.md">>},
{<<"GitHub">>,<<"https://github.com/oban-bg/oban_web">>}]}. {<<"GitHub">>,<<"https://github.com/oban-bg/oban_web">>}]}.
{<<"name">>,<<"oban_web">>}. {<<"name">>,<<"oban_web">>}.
{<<"version">>,<<"2.12.1">>}. {<<"version">>,<<"2.12.3">>}.
{<<"description">>,<<"Dashboard for the Oban job orchestration framework">>}. {<<"description">>,<<"Dashboard for the Oban job orchestration framework">>}.
{<<"elixir">>,<<"~> 1.15">>}. {<<"elixir">>,<<"~> 1.15">>}.
{<<"files">>, {<<"files">>,
@ -65,79 +65,9 @@
<<"lib/oban/web/helpers/queue_helper.ex">>,<<"lib/oban/web/assets.ex">>, <<"lib/oban/web/helpers/queue_helper.ex">>,<<"lib/oban/web/assets.ex">>,
<<"lib/oban/web/search.ex">>,<<"lib/oban/web/exceptions.ex">>, <<"lib/oban/web/search.ex">>,<<"lib/oban/web/exceptions.ex">>,
<<"lib/oban/web.ex">>,<<"priv/static">>,<<"priv/static/app.css">>, <<"lib/oban/web.ex">>,<<"priv/static">>,<<"priv/static/app.css">>,
<<"priv/static/icons">>,<<"priv/static/icons/solid">>, <<"priv/static/fonts">>,<<"priv/static/fonts/Inter.woff2">>,
<<"priv/static/icons/solid/pause-circle.svg">>, <<"priv/static/app.js">>,<<"priv/timezones.txt">>,<<".formatter.exs">>,
<<"priv/static/icons/solid/play-circle.svg">>, <<"mix.exs">>,<<"README.md">>,<<"CHANGELOG.md">>,<<"LICENSE.txt">>]}.
<<"priv/static/icons/solid/crossbones-circle.svg">>,
<<"priv/static/icons/special">>,<<"priv/static/icons/special/spinner.svg">>,
<<"priv/static/icons/outline">>,
<<"priv/static/icons/outline/arrow-top-right-on-square.svg">>,
<<"priv/static/icons/outline/queue-list.svg">>,
<<"priv/static/icons/outline/pause-circle.svg">>,
<<"priv/static/icons/outline/computer-desktop.svg">>,
<<"priv/static/icons/outline/cog.svg">>,
<<"priv/static/icons/outline/play-circle.svg">>,
<<"priv/static/icons/outline/x-circle.svg">>,
<<"priv/static/icons/outline/square-2x2.svg">>,
<<"priv/static/icons/outline/no-symbol.svg">>,
<<"priv/static/icons/outline/chevron-down.svg">>,
<<"priv/static/icons/outline/adjustments-horizontal.svg">>,
<<"priv/static/icons/outline/power.svg">>,
<<"priv/static/icons/outline/cog-8-tooth.svg">>,
<<"priv/static/icons/outline/sparkles.svg">>,
<<"priv/static/icons/outline/chevron-up.svg">>,
<<"priv/static/icons/outline/table-cells.svg">>,
<<"priv/static/icons/outline/arrow-path.svg">>,
<<"priv/static/icons/outline/ellipsis-horizontal-circle.svg">>,
<<"priv/static/icons/outline/chevron-right.svg">>,
<<"priv/static/icons/outline/clipboard.svg">>,
<<"priv/static/icons/outline/link.svg">>,
<<"priv/static/icons/outline/bars-arrow-down.svg">>,
<<"priv/static/icons/outline/arrow-right.svg">>,
<<"priv/static/icons/outline/pencil-square.svg">>,
<<"priv/static/icons/outline/bolt-circle.svg">>,
<<"priv/static/icons/outline/arrow-turn-down-right.svg">>,
<<"priv/static/icons/outline/arrow-path-rounded.svg">>,
<<"priv/static/icons/outline/indeterminate.svg">>,
<<"priv/static/icons/outline/check.svg">>,
<<"priv/static/icons/outline/percent-square.svg">>,
<<"priv/static/icons/outline/rectangle-group.svg">>,
<<"priv/static/icons/outline/x-mark.svg">>,
<<"priv/static/icons/outline/info-circle.svg">>,
<<"priv/static/icons/outline/arrow-right-circle.svg">>,
<<"priv/static/icons/outline/check-circle.svg">>,
<<"priv/static/icons/outline/camera.svg">>,
<<"priv/static/icons/outline/life-buoy.svg">>,
<<"priv/static/icons/outline/view-columns.svg">>,
<<"priv/static/icons/outline/bolt-slash.svg">>,
<<"priv/static/icons/outline/exclamation-circle.svg">>,
<<"priv/static/icons/outline/play-pause-circle.svg">>,
<<"priv/static/icons/outline/trash.svg">>,
<<"priv/static/icons/outline/hashtag.svg">>,
<<"priv/static/icons/outline/sun.svg">>,
<<"priv/static/icons/outline/bars-arrow-up.svg">>,
<<"priv/static/icons/outline/plus-circle.svg">>,
<<"priv/static/icons/outline/user-circle.svg">>,
<<"priv/static/icons/outline/user-group.svg">>,
<<"priv/static/icons/outline/clock.svg">>,
<<"priv/static/icons/outline/command-line.svg">>,
<<"priv/static/icons/outline/adjustments-vertical.svg">>,
<<"priv/static/icons/outline/question-mark-circle.svg">>,
<<"priv/static/icons/outline/map-pin.svg">>,
<<"priv/static/icons/outline/globe.svg">>,
<<"priv/static/icons/outline/arrow-left.svg">>,
<<"priv/static/icons/outline/magnifying-glass.svg">>,
<<"priv/static/icons/outline/minus-circle.svg">>,
<<"priv/static/icons/outline/lock-closed.svg">>,
<<"priv/static/icons/outline/arrow-trending-down.svg">>,
<<"priv/static/icons/outline/chevron-left.svg">>,
<<"priv/static/icons/outline/moon.svg">>,
<<"priv/static/icons/outline/chart-bar-square.svg">>,
<<"priv/static/icons/outline/square-stack.svg">>,
<<"priv/static/icons/outline/calendar-days.svg">>,<<"priv/static/fonts">>,
<<"priv/static/fonts/Inter.woff2">>,<<"priv/static/app.js">>,
<<"priv/timezones.txt">>,<<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,
<<"CHANGELOG.md">>,<<"LICENSE.txt">>]}.
{<<"app">>,<<"oban_web">>}. {<<"app">>,<<"oban_web">>}.
{<<"licenses">>,[<<"Apache-2.0">>]}. {<<"licenses">>,[<<"Apache-2.0">>]}.
{<<"requirements">>, {<<"requirements">>,

View file

@ -7,20 +7,6 @@ defmodule Oban.Web.Assets do
@static_path Application.app_dir(:oban_web, ["priv", "static"]) @static_path Application.app_dir(:oban_web, ["priv", "static"])
# Icons
icon_files =
for dir <- ["outline", "solid", "special"],
base = Path.join([@static_path, "icons", dir]),
file <- File.ls!(base),
String.ends_with?(file, ".svg") do
path = Path.join(base, file)
Module.put_attribute(__MODULE__, :external_resource, path)
{Path.join(dir, file), File.read!(path)}
end
@icons Map.new(icon_files)
# Font # Font
@external_resource font_path = Path.join(@static_path, "fonts/Inter.woff2") @external_resource font_path = Path.join(@static_path, "fonts/Inter.woff2")
@ -65,21 +51,6 @@ defmodule Oban.Web.Assets do
serve_asset(conn, @font, "font/woff2") serve_asset(conn, @font, "font/woff2")
end end
def call(conn, :icon) do
icon_path = Enum.join(conn.path_params["path"], "/")
case Map.get(@icons, icon_path) do
nil ->
conn
|> put_resp_header("content-type", "text/plain")
|> send_resp(404, "Icon not found")
|> halt()
contents ->
serve_asset(conn, contents, "image/svg+xml")
end
end
defp serve_asset(conn, contents, content_type) do defp serve_asset(conn, contents, content_type) do
conn conn
|> put_resp_header("content-type", content_type) |> put_resp_header("content-type", content_type)

View file

@ -106,7 +106,7 @@ defmodule Oban.Web.Components.Core do
phx-value-choice={@value} phx-value-choice={@value}
> >
<%= if to_string(@value) == to_string(@selected) do %> <%= if to_string(@value) == to_string(@selected) do %>
<Icons.check class="w-5 h-5" /> <Icons.icon name="icon-check" class="w-5 h-5" />
<% else %> <% else %>
<span class="block w-5 h-5"></span> <span class="block w-5 h-5"></span>
<% end %> <% end %>
@ -142,7 +142,7 @@ defmodule Oban.Web.Components.Core do
rel="check" rel="check"
> >
<div class={["w-4 h-4 flex items-center justify-center rounded border", @style]}> <div class={["w-4 h-4 flex items-center justify-center rounded border", @style]}>
<Icons.check class="w-3 h-3 text-white dark:text-gray-900" /> <Icons.icon name="icon-check" class="w-3 h-3 text-white dark:text-gray-900" />
</div> </div>
</button> </button>
""" """
@ -174,9 +174,9 @@ defmodule Oban.Web.Components.Core do
> >
<div class={["w-4 h-4 flex items-center justify-center rounded border", @style]}> <div class={["w-4 h-4 flex items-center justify-center rounded border", @style]}>
<%= if @checked == :some do %> <%= if @checked == :some do %>
<Icons.indeterminate class="w-3 h-3 text-white dark:text-gray-900" /> <Icons.icon name="icon-indeterminate" class="w-3 h-3 text-white dark:text-gray-900" />
<% else %> <% else %>
<Icons.check class="w-3 h-3 text-white dark:text-gray-900" /> <Icons.icon name="icon-check" class="w-3 h-3 text-white dark:text-gray-900" />
<% end %> <% end %>
</div> </div>
</button> </button>
@ -203,40 +203,41 @@ defmodule Oban.Web.Components.Core do
""" """
end end
defp badge_icon(%{name: "camera"} = assigns), do: ~H[<Icons.camera class="h-4 w-4 shrink-0" />] defp badge_icon(%{name: "camera"} = assigns),
do: ~H[<Icons.icon name="icon-camera" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "life_buoy"} = assigns), defp badge_icon(%{name: "life_buoy"} = assigns),
do: ~H[<Icons.life_buoy class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-life-buoy" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "lock_closed"} = assigns), defp badge_icon(%{name: "lock_closed"} = assigns),
do: ~H[<Icons.lock_closed class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-lock-closed" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "sparkles"} = assigns), defp badge_icon(%{name: "sparkles"} = assigns),
do: ~H[<Icons.sparkles class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-sparkles" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "table_cells"} = assigns), defp badge_icon(%{name: "table_cells"} = assigns),
do: ~H[<Icons.table_cells class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-table-cells" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "square_2x2"} = assigns), defp badge_icon(%{name: "square_2x2"} = assigns),
do: ~H[<Icons.square_2x2 class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-square-2x2" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "rectangle_group"} = assigns), defp badge_icon(%{name: "rectangle_group"} = assigns),
do: ~H[<Icons.rectangle_group class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-rectangle-group" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "user_group"} = assigns), defp badge_icon(%{name: "user_group"} = assigns),
do: ~H[<Icons.user_group class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-user-group" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "link"} = assigns), defp badge_icon(%{name: "link"} = assigns),
do: ~H[<Icons.link class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-link" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "power"} = assigns), defp badge_icon(%{name: "power"} = assigns),
do: ~H[<Icons.power class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-power" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "pause_circle"} = assigns), defp badge_icon(%{name: "pause_circle"} = assigns),
do: ~H[<Icons.pause_circle class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-pause-circle" class="h-4 w-4 shrink-0" />]
defp badge_icon(%{name: "play_pause_circle"} = assigns), defp badge_icon(%{name: "play_pause_circle"} = assigns),
do: ~H[<Icons.play_pause_circle class="h-4 w-4 shrink-0" />] do: ~H[<Icons.icon name="icon-play-pause-circle" class="h-4 w-4 shrink-0" />]
@doc """ @doc """
An icon-only button that expands to show label on hover. Supports disabled state. An icon-only button that expands to show label on hover. Supports disabled state.
@ -309,17 +310,23 @@ defmodule Oban.Web.Components.Core do
""" """
end end
defp button_icon(%{name: "arrow_path"} = assigns), do: ~H[<Icons.arrow_path class={@class} />] defp button_icon(%{name: "arrow_path"} = assigns),
do: ~H[<Icons.icon name="icon-arrow-path" class={@class} />]
defp button_icon(%{name: "pause_circle"} = assigns), defp button_icon(%{name: "pause_circle"} = assigns),
do: ~H[<Icons.pause_circle class={@class} />] do: ~H[<Icons.icon name="icon-pause-circle" class={@class} />]
defp button_icon(%{name: "pencil_square"} = assigns), defp button_icon(%{name: "pencil_square"} = assigns),
do: ~H[<Icons.pencil_square class={@class} />] do: ~H[<Icons.icon name="icon-pencil-square" class={@class} />]
defp button_icon(%{name: "play_circle"} = assigns), do: ~H[<Icons.play_circle class={@class} />] defp button_icon(%{name: "play_circle"} = assigns),
defp button_icon(%{name: "trash"} = assigns), do: ~H[<Icons.trash class={@class} />] do: ~H[<Icons.icon name="icon-play-circle" class={@class} />]
defp button_icon(%{name: "x_circle"} = assigns), do: ~H[<Icons.x_circle class={@class} />]
defp button_icon(%{name: "trash"} = assigns),
do: ~H[<Icons.icon name="icon-trash" class={@class} />]
defp button_icon(%{name: "x_circle"} = assigns),
do: ~H[<Icons.icon name="icon-x-circle" class={@class} />]
# Sparkline # Sparkline

View file

@ -30,7 +30,7 @@ defmodule Oban.Web.FormComponents do
phx-hook="Tippy" phx-hook="Tippy"
class="translate-y-px" class="translate-y-px"
> >
<Icons.info_circle class="w-4 h-4 text-gray-400 dark:text-gray-500" /> <Icons.icon name="icon-info-circle" class="w-4 h-4 text-gray-400 dark:text-gray-500" />
</span> </span>
</label> </label>
<%= if @type == "textarea" do %> <%= if @type == "textarea" do %>
@ -110,7 +110,10 @@ defmodule Oban.Web.FormComponents do
class="sr-only peer" class="sr-only peer"
/> />
<span class="block w-4 h-4 rounded border border-gray-300 dark:border-gray-600 peer-checked:border-blue-500 peer-checked:bg-blue-500" /> <span class="block w-4 h-4 rounded border border-gray-300 dark:border-gray-600 peer-checked:border-blue-500 peer-checked:bg-blue-500" />
<Icons.check class="w-3 h-3 text-white absolute top-0.5 left-0.5 hidden peer-checked:block" /> <Icons.icon
name="icon-check"
class="w-3 h-3 text-white absolute top-0.5 left-0.5 hidden peer-checked:block"
/>
</div> </div>
{@label} {@label}
<span <span
@ -120,7 +123,7 @@ defmodule Oban.Web.FormComponents do
phx-hook="Tippy" phx-hook="Tippy"
class="ml-1 translate-y-px" class="ml-1 translate-y-px"
> >
<Icons.info_circle class="w-4 h-4 text-gray-400 dark:text-gray-500" /> <Icons.icon name="icon-info-circle" class="w-4 h-4 text-gray-400 dark:text-gray-500" />
</span> </span>
</label> </label>
</div> </div>

View file

@ -3,60 +3,17 @@ defmodule Oban.Web.Components.Icons do
use Phoenix.Component use Phoenix.Component
@icons_path Application.app_dir(:oban_web, ["priv", "static", "icons"]) attr :name, :string, required: true
attr :class, :any, default: nil
attr :rest, :global
@outline_path Path.join(@icons_path, "outline") def icon(assigns) do
@solid_path Path.join(@icons_path, "solid") ~H"""
<span class={[@name, @class]} aria-hidden="true" {@rest} />
@outline_icons @outline_path """
|> File.ls!()
|> Enum.filter(&String.ends_with?(&1, ".svg"))
|> Enum.map(&String.trim_trailing(&1, ".svg"))
@solid_icons @solid_path
|> File.ls!()
|> Enum.filter(&String.ends_with?(&1, ".svg"))
|> Enum.map(&String.trim_trailing(&1, ".svg"))
for icon <- @outline_icons do
@external_resource Path.join(@outline_path, "#{icon}.svg")
func_name = icon |> String.replace("-", "_") |> String.to_atom()
@doc false
attr :class, :any, default: "w-6 h-6"
attr :rest, :global
def unquote(func_name)(assigns) do
assigns = assign(assigns, :icon_path, "outline/#{unquote(icon)}.svg")
render_icon(assigns)
end
end end
for icon <- @solid_icons do attr :rest, :global, default: %{class: "size-6"}
@external_resource Path.join(@solid_path, "#{icon}.svg")
func_name =
icon
|> String.replace("-", "_")
|> Kernel.<>("_solid")
|> String.to_atom()
@doc false
attr :class, :any, default: "w-6 h-6"
attr :rest, :global
def unquote(func_name)(assigns) do
assigns = assign(assigns, :icon_path, "solid/#{unquote(icon)}.svg")
render_icon(assigns)
end
end
# Inline Icons
attr :rest, :global, default: %{class: "w-6 h-6"}
def oban_pro_logo(assigns) do def oban_pro_logo(assigns) do
~H""" ~H"""
@ -116,7 +73,7 @@ defmodule Oban.Web.Components.Icons do
""" """
end end
attr :rest, :global, default: %{class: "w-6 h-6"} attr :rest, :global, default: %{class: "size-6"}
def spinner(assigns) do def spinner(assigns) do
~H""" ~H"""
@ -126,37 +83,4 @@ defmodule Oban.Web.Components.Icons do
</svg> </svg>
""" """
end end
defp render_icon(assigns) do
icon_url = icon_url(assigns.icon_path)
class = normalize_class(assigns.class)
assigns = assigns |> assign(:icon_url, icon_url) |> assign(:normalized_class, class)
~H"""
<span
class={["block bg-current", @normalized_class]}
style={"mask-image: url(#{@icon_url}); -webkit-mask-image: url(#{@icon_url}); mask-size: contain; mask-repeat: no-repeat; mask-position: center;"}
aria-hidden="true"
{@rest}
/>
"""
end
defp icon_url(path) do
case Process.get(:routing) do
{socket, prefix} ->
Phoenix.VerifiedRoutes.unverified_path(
socket,
socket.router,
"#{prefix}/icons/#{path}"
)
_ ->
"/icons/#{path}"
end
end
defp normalize_class(class) when is_binary(class), do: class
defp normalize_class(class) when is_list(class), do: class
defp normalize_class(nil), do: "w-6 h-6"
end end

View file

@ -121,7 +121,7 @@ defmodule Oban.Web.Layouts do
<div class="p-4"> <div class="p-4">
<div class="flex items-center"> <div class="flex items-center">
<div class="flex-shrink-0 text-green-400"> <div class="flex-shrink-0 text-green-400">
<Icons.check_circle /> <Icons.icon name="icon-check-circle" />
</div> </div>
<div class="ml-3 w-0 flex-1"> <div class="ml-3 w-0 flex-1">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100"> <p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
@ -133,7 +133,7 @@ defmodule Oban.Web.Layouts do
phx-click="lv:clear-flash" phx-click="lv:clear-flash"
class="inline-flex text-gray-400 dark:text-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 focus:text-gray-500 transition ease-in-out duration-150" class="inline-flex text-gray-400 dark:text-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 focus:text-gray-500 transition ease-in-out duration-150"
> >
<Icons.x_mark class="w-5 h-5" /> <Icons.icon name="icon-x-mark" class="w-5 h-5" />
</button> </button>
</div> </div>
</div> </div>

View file

@ -41,7 +41,10 @@ defmodule Oban.Web.SidebarComponents do
phx-click={toggle(@name)} phx-click={toggle(@name)}
phx-hook="Tippy" phx-hook="Tippy"
> >
<Icons.chevron_right class="w-4 h-4 mt-1 mr-1 transition-transform rotate-90" /> <Icons.icon
name="icon-chevron-right"
class="w-4 h-4 mt-1 mr-1 transition-transform rotate-90"
/>
</button> </button>
<h3 class="dark:text-gray-200 font-bold">{String.capitalize(@name)}</h3> <h3 class="dark:text-gray-200 font-bold">{String.capitalize(@name)}</h3>

View file

@ -22,9 +22,9 @@ defmodule Oban.Web.SortComponent do
type="button" type="button"
> >
<%= if @params.sort_dir == "asc" do %> <%= if @params.sort_dir == "asc" do %>
<Icons.bars_arrow_down class="w-4 h-4" /> <Icons.icon name="icon-bars-arrow-down" class="w-4 h-4" />
<% else %> <% else %>
<Icons.bars_arrow_up class="w-4 h-4" /> <Icons.icon name="icon-bars-arrow-up" class="w-4 h-4" />
<% end %> <% end %>
<span class="ml-1 block capitalize"> <span class="ml-1 block capitalize">
{String.replace(@params.sort_by, "_", " ")} {String.replace(@params.sort_by, "_", " ")}
@ -71,7 +71,7 @@ defmodule Oban.Web.SortComponent do
role="option" role="option"
> >
<%= if @value == @selected do %> <%= if @value == @selected do %>
<Icons.check class="w-4 h-4 text-blue-500" /> <Icons.icon name="icon-check" class="w-4 h-4 text-blue-500" />
<% else %> <% else %>
<span class="block w-4 h-4"></span> <span class="block w-4 h-4"></span>
<% end %> <% end %>

View file

@ -20,6 +20,7 @@ defmodule Oban.Web.DashboardLive do
page = resolve_page(params) page = resolve_page(params)
Process.put(:routing, {socket, prefix}) Process.put(:routing, {socket, prefix})
Process.put(:csp_nonce_style, csp_nonces.style)
socket = socket =
socket socket

View file

@ -47,10 +47,15 @@ defmodule Oban.Web.ConnectivityComponent do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div id="connectivity" data-title={@title} phx-hook="Tippy"> <div id="connectivity" data-title={@title} phx-hook="Tippy">
<Icons.bolt_circle :if={@status == :solitary} class="w-6 h-6 text-yellow-500" /> <Icons.icon :if={@status == :solitary} name="icon-bolt-circle" class="w-6 h-6 text-yellow-500" />
<Icons.bolt_slash :if={@status == :isolated} class="w-6 h-6 animate-pulse text-red-500" /> <Icons.icon
<Icons.exclamation_circle :if={@status == :isolated}
name="icon-bolt-slash"
class="w-6 h-6 animate-pulse text-red-500"
/>
<Icons.icon
:if={@status == :disconnected} :if={@status == :disconnected}
name="icon-exclamation-circle"
class="w-6 h-6 animate-pulse text-red-500" class="w-6 h-6 animate-pulse text-red-500"
/> />
</div> </div>

View file

@ -22,7 +22,7 @@ defmodule Oban.Web.Crons.DetailComponent do
data-title="Back to crons" data-title="Back to crons"
phx-hook="Tippy" phx-hook="Tippy"
> >
<Icons.arrow_left class="w-5 h-5" /> <Icons.icon name="icon-arrow-left" class="w-5 h-5" />
<span class="text-lg font-bold ml-2"> <span class="text-lg font-bold ml-2">
{@cron.worker} {@cron.worker}
<span :if={show_name?(@cron)} class="font-normal text-gray-500 dark:text-gray-400"> <span :if={show_name?(@cron)} class="font-normal text-gray-500 dark:text-gray-400">
@ -140,7 +140,7 @@ defmodule Oban.Web.Crons.DetailComponent do
<div class="px-3 py-6 border-t border-gray-200 dark:border-gray-700"> <div class="px-3 py-6 border-t border-gray-200 dark:border-gray-700">
<h3 class="flex font-semibold mb-3 space-x-2 text-gray-400"> <h3 class="flex font-semibold mb-3 space-x-2 text-gray-400">
<Icons.pencil_square /> <Icons.icon name="icon-pencil-square" />
<span>Edit Configuration</span> <span>Edit Configuration</span>
</h3> </h3>
@ -230,7 +230,7 @@ defmodule Oban.Web.Crons.DetailComponent do
class="text-xs text-gray-500 dark:text-gray-400 hover:underline" class="text-xs text-gray-500 dark:text-gray-400 hover:underline"
> >
Editing requires DynamicCron Editing requires DynamicCron
<Icons.arrow_top_right_on_square class="w-3 h-3 inline-block" /> <Icons.icon name="icon-arrow-top-right-on-square" class="w-3 h-3 inline-block" />
</a> </a>
<.save_button disabled={not @changed?} /> <.save_button disabled={not @changed?} />
</div> </div>
@ -257,7 +257,7 @@ defmodule Oban.Web.Crons.DetailComponent do
navigate={oban_path(:jobs, %{meta: [["cron_name"], @cron.name], state: "completed"})} navigate={oban_path(:jobs, %{meta: [["cron_name"], @cron.name], state: "completed"})}
class="absolute right-4 top-4 flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-blue-100 hover:text-blue-600 dark:hover:bg-blue-900 dark:hover:text-blue-300 opacity-0 group-hover:opacity-100 transition-opacity" class="absolute right-4 top-4 flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-blue-100 hover:text-blue-600 dark:hover:bg-blue-900 dark:hover:text-blue-300 opacity-0 group-hover:opacity-100 transition-opacity"
> >
View all jobs <Icons.arrow_right class="w-3 h-3" /> View all jobs <Icons.icon name="icon-arrow-right" class="w-3 h-3" />
</.link> </.link>
</div> </div>
""" """

View file

@ -11,7 +11,7 @@ defmodule Oban.Web.Crons.Helpers do
def state_icon(%{paused: true} = assigns) do def state_icon(%{paused: true} = assigns) do
~H""" ~H"""
<Icons.pause_circle class="w-5 h-5 text-gray-400" /> <Icons.icon name="icon-pause-circle" class="w-5 h-5 text-gray-400" />
""" """
end end
@ -21,21 +21,21 @@ defmodule Oban.Web.Crons.Helpers do
~H""" ~H"""
<%= case @state do %> <%= case @state do %>
<% "available" -> %> <% "available" -> %>
<Icons.ellipsis_horizontal_circle class={["w-5 h-5", @color_class]} /> <Icons.icon name="icon-ellipsis-horizontal-circle" class={["w-5 h-5", @color_class]} />
<% "cancelled" -> %> <% "cancelled" -> %>
<Icons.x_circle class={["w-5 h-5", @color_class]} /> <Icons.icon name="icon-x-circle" class={["w-5 h-5", @color_class]} />
<% "completed" -> %> <% "completed" -> %>
<Icons.check_circle class={["w-5 h-5", @color_class]} /> <Icons.icon name="icon-check-circle" class={["w-5 h-5", @color_class]} />
<% "discarded" -> %> <% "discarded" -> %>
<Icons.exclamation_circle class={["w-5 h-5", @color_class]} /> <Icons.icon name="icon-exclamation-circle" class={["w-5 h-5", @color_class]} />
<% "executing" -> %> <% "executing" -> %>
<Icons.play_circle class={["w-5 h-5", @color_class]} /> <Icons.icon name="icon-play-circle" class={["w-5 h-5", @color_class]} />
<% "retryable" -> %> <% "retryable" -> %>
<Icons.arrow_path class={["w-5 h-5", @color_class]} /> <Icons.icon name="icon-arrow-path" class={["w-5 h-5", @color_class]} />
<% "scheduled" -> %> <% "scheduled" -> %>
<Icons.clock class={["w-5 h-5", @color_class]} /> <Icons.icon name="icon-clock" class={["w-5 h-5", @color_class]} />
<% _ -> %> <% _ -> %>
<Icons.minus_circle class="w-5 h-5 text-gray-400" /> <Icons.icon name="icon-minus-circle" class="w-5 h-5 text-gray-400" />
<% end %> <% end %>
""" """
end end

View file

@ -48,7 +48,7 @@ defmodule Oban.Web.Crons.NewComponent do
phx-target={@myself} phx-target={@myself}
aria-label="Close" aria-label="Close"
> >
<Icons.x_mark class="h-6 w-6" /> <Icons.icon name="icon-x-mark" class="h-6 w-6" />
</button> </button>
</div> </div>

View file

@ -26,7 +26,7 @@ defmodule Oban.Web.Crons.TableComponent do
</ul> </ul>
<div :if={Enum.empty?(@crontab)} class="py-16 px-6 text-center"> <div :if={Enum.empty?(@crontab)} class="py-16 px-6 text-center">
<Icons.clock class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" /> <Icons.icon name="icon-clock" class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" />
<h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100">No crons</h3> <h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100">No crons</h3>
<p class="mt-2 text-base text-gray-500 dark:text-gray-400 max-w-md mx-auto"> <p class="mt-2 text-base text-gray-500 dark:text-gray-400 max-w-md mx-auto">
Crons run jobs on a schedule. Configure them in your Oban supervisor or create them dynamically. Crons run jobs on a schedule. Configure them in your Oban supervisor or create them dynamically.

View file

@ -15,7 +15,7 @@ defmodule Oban.Web.HelpComponent do
phx-click={JS.toggle(to: "#help-menu")} phx-click={JS.toggle(to: "#help-menu")}
type="button" type="button"
> >
<Icons.question_mark_circle /> <Icons.icon name="icon-question-mark-circle" />
</button> </button>
<ul <ul
@ -35,7 +35,7 @@ defmodule Oban.Web.HelpComponent do
rel="noopener noreferrer" rel="noopener noreferrer"
class="flex items-center space-x-2 w-full" class="flex items-center space-x-2 w-full"
> >
<Icons.arrow_top_right_on_square class="w-5 h-5" /> <Icons.icon name="icon-arrow-top-right-on-square" class="w-5 h-5" />
<span class="text-gray-800 dark:text-gray-200">Documentation</span> <span class="text-gray-800 dark:text-gray-200">Documentation</span>
</a> </a>
</li> </li>
@ -45,7 +45,7 @@ defmodule Oban.Web.HelpComponent do
phx-click={JS.hide(to: "#help-menu") |> JS.exec("data-shortcut", to: "#shortcuts")} phx-click={JS.hide(to: "#help-menu") |> JS.exec("data-shortcut", to: "#shortcuts")}
role="option" role="option"
> >
<Icons.command_line class="w-5 h-5" /> <Icons.icon name="icon-command-line" class="w-5 h-5" />
<span class="text-gray-800 dark:text-gray-200">Keyboard shortcuts</span> <span class="text-gray-800 dark:text-gray-200">Keyboard shortcuts</span>
</li> </li>
</ul> </ul>

View file

@ -66,7 +66,7 @@ defmodule Oban.Web.InstancesComponent do
phx-value-name={name} phx-value-name={name}
> >
<%= if name == @active do %> <%= if name == @active do %>
<Icons.check class="w-4 h-4 text-blue-500" /> <Icons.icon name="icon-check" class="w-4 h-4 text-blue-500" />
<% else %> <% else %>
<span class="block w-4 h-4"></span> <span class="block w-4 h-4"></span>
<% end %> <% end %>

View file

@ -56,10 +56,13 @@ defmodule Oban.Web.Jobs.ChartComponent do
phx-click={toggle_chart(@myself)} phx-click={toggle_chart(@myself)}
phx-hook="Tippy" phx-hook="Tippy"
> >
<Icons.chevron_right class={[ <Icons.icon
"w-5 h-5 mr-2 transition-transform", name="icon-chevron-right"
if(@visible, do: "rotate-90") class={[
]} /> "w-5 h-5 mr-2 transition-transform",
if(@visible, do: "rotate-90")
]}
/>
</button> </button>
<h3 class="text-base font-semibold"> <h3 class="text-base font-semibold">
@ -89,7 +92,7 @@ defmodule Oban.Web.Jobs.ChartComponent do
target={@myself} target={@myself}
title="Change metric series" title="Change metric series"
> >
<Icons.chart_bar_square /> <Icons.icon name="icon-chart-bar-square" />
</Core.dropdown_button> </Core.dropdown_button>
<Core.dropdown_button <Core.dropdown_button
@ -100,7 +103,7 @@ defmodule Oban.Web.Jobs.ChartComponent do
target={@myself} target={@myself}
title="Change slice period" title="Change slice period"
> >
<Icons.clock /> <Icons.icon name="icon-clock" />
</Core.dropdown_button> </Core.dropdown_button>
<Core.dropdown_button <Core.dropdown_button
@ -111,7 +114,7 @@ defmodule Oban.Web.Jobs.ChartComponent do
target={@myself} target={@myself}
title="Change metric grouping" title="Change metric grouping"
> >
<Icons.rectangle_group /> <Icons.icon name="icon-rectangle-group" />
</Core.dropdown_button> </Core.dropdown_button>
<Core.dropdown_button <Core.dropdown_button
@ -122,7 +125,7 @@ defmodule Oban.Web.Jobs.ChartComponent do
target={@myself} target={@myself}
title="Change percentile" title="Change percentile"
> >
<Icons.percent_square /> <Icons.icon name="icon-percent-square" />
</Core.dropdown_button> </Core.dropdown_button>
</div> </div>
</div> </div>

View file

@ -54,7 +54,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
phx-hook="HistoryBack" phx-hook="HistoryBack"
type="button" type="button"
> >
<Icons.arrow_left class="w-5 h-5" /> <Icons.icon name="icon-arrow-left" class="w-5 h-5" />
<span class="text-lg font-bold ml-2">{job_title(@job)}</span> <span class="text-lg font-bold ml-2">{job_title(@job)}</span>
</button> </button>
@ -109,7 +109,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
label="Edit" label="Edit"
color="violet" color="violet"
tooltip="Edit this job" tooltip="Edit this job"
disabled={not editable?(@job)} disabled={executing?(@job)}
phx-click={scroll_to_edit()} phx-click={scroll_to_edit()}
/> />
</div> </div>
@ -223,7 +223,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
Workflow Workflow
</span> </span>
<span class="text-base text-gray-800 dark:text-gray-200 flex items-center"> <span class="text-base text-gray-800 dark:text-gray-200 flex items-center">
<Icons.rectangle_group class="w-4 h-4 mr-1.5 text-violet-500" /> <Icons.icon name="icon-rectangle-group" class="w-4 h-4 mr-1.5 text-violet-500" />
<span class="truncate">{workflow_display_name(@job)}</span> <span class="truncate">{workflow_display_name(@job)}</span>
</span> </span>
</.link> </.link>
@ -241,7 +241,8 @@ defmodule Oban.Web.Jobs.DetailComponent do
phx-click="toggle-diagnostics" phx-click="toggle-diagnostics"
phx-target={@myself} phx-target={@myself}
> >
<Icons.chevron_right <Icons.icon
name="icon-chevron-right"
id="diagnostics-chevron" id="diagnostics-chevron"
class={["w-5 h-5 transition-transform", if(@diagnostics_open?, do: "rotate-90")]} class={["w-5 h-5 transition-transform", if(@diagnostics_open?, do: "rotate-90")]}
/> />
@ -324,7 +325,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
phx-hook="Tippy" phx-hook="Tippy"
phx-click={copy_to_clipboard(@diagnostics["info"]["current_stacktrace"])} phx-click={copy_to_clipboard(@diagnostics["info"]["current_stacktrace"])}
> >
<Icons.clipboard class="w-4 h-4" /> <Icons.icon name="icon-clipboard" class="w-4 h-4" />
</button> </button>
</div> </div>
<%= if @diagnostics["info"]["current_stacktrace"] do %> <%= if @diagnostics["info"]["current_stacktrace"] do %>
@ -345,7 +346,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
</div> </div>
<% else %> <% else %>
<div class="flex items-center space-x-2 px-2 text-gray-400 dark:text-gray-500"> <div class="flex items-center space-x-2 px-2 text-gray-400 dark:text-gray-500">
<Icons.clock class="w-5 h-5" /> <Icons.icon name="icon-clock" class="w-5 h-5" />
<span class="text-sm"> <span class="text-sm">
<%= if executing?(@job) do %> <%= if executing?(@job) do %>
Waiting for diagnostics... Waiting for diagnostics...
@ -374,7 +375,8 @@ defmodule Oban.Web.Jobs.DetailComponent do
class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer" class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
phx-click={toggle_errors()} phx-click={toggle_errors()}
> >
<Icons.chevron_right <Icons.icon
name="icon-chevron-right"
id="errors-chevron" id="errors-chevron"
class={["w-5 h-5 transition-transform", if(Enum.any?(@job.errors), do: "rotate-90")]} class={["w-5 h-5 transition-transform", if(Enum.any?(@job.errors), do: "rotate-90")]}
/> />
@ -440,7 +442,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
) )
]} ]}
> >
<Icons.chevron_left class="w-5 h-5" /> <Icons.icon name="icon-chevron-left" class="w-5 h-5" />
</button> </button>
<span class="text-sm text-gray-500 dark:text-gray-400 tabular min-w-[4rem] text-center"> <span class="text-sm text-gray-500 dark:text-gray-400 tabular min-w-[4rem] text-center">
{@error_index + 1} of {length(@job.errors)} {@error_index + 1} of {length(@job.errors)}
@ -460,7 +462,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
) )
]} ]}
> >
<Icons.chevron_right class="w-5 h-5" /> <Icons.icon name="icon-chevron-right" class="w-5 h-5" />
</button> </button>
</div> </div>
</div> </div>
@ -468,7 +470,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
<.error_entry errors={@job.errors} index={@error_index} sort={@error_sort} /> <.error_entry errors={@job.errors} index={@error_index} sort={@error_sort} />
<% else %> <% else %>
<div class="flex items-center space-x-2 px-2 text-gray-400 dark:text-gray-500"> <div class="flex items-center space-x-2 px-2 text-gray-400 dark:text-gray-500">
<Icons.check_circle class="w-5 h-5" /> <Icons.icon name="icon-check-circle" class="w-5 h-5" />
<span class="text-sm">No errors recorded</span> <span class="text-sm">No errors recorded</span>
</div> </div>
<% end %> <% end %>
@ -482,24 +484,25 @@ defmodule Oban.Web.Jobs.DetailComponent do
class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer" class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
phx-click={toggle_edit()} phx-click={toggle_edit()}
> >
<Icons.chevron_right <Icons.icon
name="icon-chevron-right"
id="edit-chevron" id="edit-chevron"
class={["w-5 h-5 transition-transform", if(editable?(@job), do: "rotate-90")]} class={["w-5 h-5 transition-transform", if(not executing?(@job), do: "rotate-90")]}
/> />
<span class="font-semibold">Edit Job</span> <span class="font-semibold">Edit Job</span>
<span <span
:if={not editable?(@job)} :if={executing?(@job)}
id="edit-hint" id="edit-hint"
class="flex items-center" class="flex items-center"
data-title="Only available, scheduled, and retryable jobs can be edited" data-title="Executing jobs can't be edited"
phx-hook="Tippy" phx-hook="Tippy"
> >
<Icons.info_circle class="w-4 h-4 text-gray-400" /> <Icons.icon name="icon-info-circle" class="w-4 h-4 text-gray-400" />
</span> </span>
</button> </button>
<div id="edit-content" class={["mt-3", unless(editable?(@job), do: "hidden")]}> <div id="edit-content" class={["mt-3", if(executing?(@job), do: "hidden")]}>
<fieldset disabled={not editable?(@job)}> <fieldset disabled={executing?(@job)}>
<form <form
id="job-edit-form" id="job-edit-form"
class="grid grid-cols-4 gap-4 bg-gray-50 dark:bg-gray-800 rounded-md p-4" class="grid grid-cols-4 gap-4 bg-gray-50 dark:bg-gray-800 rounded-md p-4"
@ -626,7 +629,11 @@ defmodule Oban.Web.Jobs.DetailComponent do
class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer" class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
phx-click={toggle_job_data()} phx-click={toggle_job_data()}
> >
<Icons.chevron_right id="job-data-chevron" class="w-5 h-5 transition-transform rotate-90" /> <Icons.icon
name="icon-chevron-right"
id="job-data-chevron"
class="w-5 h-5 transition-transform rotate-90"
/>
<span class="font-semibold">Job Data</span> <span class="font-semibold">Job Data</span>
</button> </button>
@ -645,7 +652,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
phx-hook="Tippy" phx-hook="Tippy"
phx-click={copy_to_clipboard(format_args(@job, @resolver))} phx-click={copy_to_clipboard(format_args(@job, @resolver))}
> >
<Icons.clipboard class="w-4 h-4" /> <Icons.icon name="icon-clipboard" class="w-4 h-4" />
</button> </button>
</div> </div>
<pre class="font-mono text-sm text-gray-600 dark:text-gray-400 whitespace-pre-wrap break-all">{format_args(@job, @resolver)}</pre> <pre class="font-mono text-sm text-gray-600 dark:text-gray-400 whitespace-pre-wrap break-all">{format_args(@job, @resolver)}</pre>
@ -664,7 +671,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
phx-hook="Tippy" phx-hook="Tippy"
phx-click={copy_to_clipboard(format_meta(@job, @resolver))} phx-click={copy_to_clipboard(format_meta(@job, @resolver))}
> >
<Icons.clipboard class="w-4 h-4" /> <Icons.icon name="icon-clipboard" class="w-4 h-4" />
</button> </button>
</div> </div>
<pre class="font-mono text-sm text-gray-500 dark:text-gray-400 whitespace-pre-wrap break-all">{format_meta(@job, @resolver)}</pre> <pre class="font-mono text-sm text-gray-500 dark:text-gray-400 whitespace-pre-wrap break-all">{format_meta(@job, @resolver)}</pre>
@ -688,7 +695,7 @@ defmodule Oban.Web.Jobs.DetailComponent do
phx-hook="Tippy" phx-hook="Tippy"
phx-click={copy_to_clipboard(format_recorded(@job, @resolver))} phx-click={copy_to_clipboard(format_recorded(@job, @resolver))}
> >
<Icons.clipboard class="w-4 h-4" /> <Icons.icon name="icon-clipboard" class="w-4 h-4" />
</button> </button>
</div> </div>
<pre class="font-mono text-sm text-gray-600 dark:text-gray-400 whitespace-pre-wrap break-all">{format_recorded(@job, @resolver)}</pre> <pre class="font-mono text-sm text-gray-600 dark:text-gray-400 whitespace-pre-wrap break-all">{format_recorded(@job, @resolver)}</pre>
@ -919,10 +926,6 @@ defmodule Oban.Web.Jobs.DetailComponent do
JS.dispatch("phx:copy-to-clipboard", detail: %{text: text}) JS.dispatch("phx:copy-to-clipboard", detail: %{text: text})
end end
defp editable?(%{state: state}) do
state in ~w(scheduled retryable available)
end
defp executing?(%{state: state}), do: state == "executing" defp executing?(%{state: state}), do: state == "executing"
defp format_bytes(nil), do: "" defp format_bytes(nil), do: ""

View file

@ -11,7 +11,11 @@ defmodule Oban.Web.Jobs.HistoryChartComponent do
class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer" class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
phx-click={toggle_section()} phx-click={toggle_section()}
> >
<Icons.chevron_right id="history-chevron" class="w-5 h-5 transition-transform rotate-90" /> <Icons.icon
name="icon-chevron-right"
id="history-chevron"
class="w-5 h-5 transition-transform rotate-90"
/>
<span class="font-semibold">Recent Executions</span> <span class="font-semibold">Recent Executions</span>
</button> </button>
@ -28,7 +32,7 @@ defmodule Oban.Web.Jobs.HistoryChartComponent do
navigate={all_jobs_path(@job)} navigate={all_jobs_path(@job)}
class="absolute right-4 top-4 flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-blue-100 hover:text-blue-600 dark:hover:bg-blue-900 dark:hover:text-blue-300 opacity-0 group-hover:opacity-100 transition-opacity" class="absolute right-4 top-4 flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-blue-100 hover:text-blue-600 dark:hover:bg-blue-900 dark:hover:text-blue-300 opacity-0 group-hover:opacity-100 transition-opacity"
> >
View all jobs <Icons.arrow_right class="w-3 h-3" /> View all jobs <Icons.icon name="icon-arrow-right" class="w-3 h-3" />
</.link> </.link>
</div> </div>
</div> </div>

View file

@ -43,7 +43,7 @@ defmodule Oban.Web.Jobs.NewComponent do
phx-target={@myself} phx-target={@myself}
aria-label="Close" aria-label="Close"
> >
<Icons.x_mark class="h-6 w-6" /> <Icons.icon name="icon-x-mark" class="h-6 w-6" />
</button> </button>
</div> </div>

View file

@ -49,7 +49,7 @@ defmodule Oban.Web.Jobs.TableComponent do
<div :if={Enum.empty?(@jobs)} class="text-lg text-center py-12"> <div :if={Enum.empty?(@jobs)} class="text-lg text-center py-12">
<div class="flex items-center justify-center space-x-2 text-gray-600 dark:text-gray-300"> <div class="flex items-center justify-center space-x-2 text-gray-600 dark:text-gray-300">
<Icons.no_symbol /> <span>No jobs match the current set of filters.</span> <Icons.icon name="icon-no-symbol" /> <span>No jobs match the current set of filters.</span>
</div> </div>
<p :if={is_integer(@query_limit)} class="mt-2 text-xs text-gray-500 dark:text-gray-400"> <p :if={is_integer(@query_limit)} class="mt-2 text-xs text-gray-500 dark:text-gray-400">
Filtering limited to latest {integer_to_delimited(@query_limit)} jobs. See <a Filtering limited to latest {integer_to_delimited(@query_limit)} jobs. See <a
@ -111,16 +111,18 @@ defmodule Oban.Web.Jobs.TableComponent do
</div> </div>
<div class="ml-auto flex items-center space-x-1"> <div class="ml-auto flex items-center space-x-1">
<Icons.life_buoy <Icons.icon
:if={Map.has_key?(@job.meta, "rescued")} :if={Map.has_key?(@job.meta, "rescued")}
name="icon-life-buoy"
class="h-5 w-5 text-gray-500 dark:text-gray-300" class="h-5 w-5 text-gray-500 dark:text-gray-300"
id={"job-rescued-#{assigns.job.id}"} id={"job-rescued-#{assigns.job.id}"}
phx-hook="Tippy" phx-hook="Tippy"
data-title="Rescued by lifeline" data-title="Rescued by lifeline"
/> />
<Icons.crossbones_circle_solid <Icons.icon
:if={orphaned?(@job, @producers)} :if={orphaned?(@job, @producers)}
name="icon-crossbones-circle-solid"
class="h-5 w-5 text-gray-500 dark:text-gray-300" class="h-5 w-5 text-gray-500 dark:text-gray-300"
id={"job-orphaned-#{assigns.job.id}"} id={"job-orphaned-#{assigns.job.id}"}
phx-hook="Tippy" phx-hook="Tippy"

View file

@ -102,7 +102,7 @@ defmodule Oban.Web.Jobs.TimelineComponent do
defp state_icon(%{icon: :pending} = assigns) do defp state_icon(%{icon: :pending} = assigns) do
~H""" ~H"""
<Icons.ellipsis_horizontal_circle class="w-5 h-5" /> <Icons.icon name="icon-ellipsis-horizontal-circle" class="w-5 h-5" />
""" """
end end
@ -114,7 +114,7 @@ defmodule Oban.Web.Jobs.TimelineComponent do
defp state_icon(%{icon: :done} = assigns) do defp state_icon(%{icon: :done} = assigns) do
~H""" ~H"""
<Icons.check_circle class="w-5 h-5" /> <Icons.icon name="icon-check-circle" class="w-5 h-5" />
""" """
end end

View file

@ -133,7 +133,7 @@ defmodule Oban.Web.Queues.DetailComponent do
phx-hook="HistoryBack" phx-hook="HistoryBack"
type="button" type="button"
> >
<Icons.arrow_left class="w-5 h-5" /> <Icons.icon name="icon-arrow-left" class="w-5 h-5" />
<span class="text-lg capitalize font-bold ml-2">{@queue} Queue</span> <span class="text-lg capitalize font-bold ml-2">{@queue} Queue</span>
</button> </button>
@ -210,7 +210,7 @@ defmodule Oban.Web.Queues.DetailComponent do
navigate={oban_path(:jobs, %{queues: @queue, state: "completed"})} navigate={oban_path(:jobs, %{queues: @queue, state: "completed"})}
class="absolute right-4 top-4 flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-blue-100 hover:text-blue-600 dark:hover:bg-blue-900 dark:hover:text-blue-300 opacity-0 group-hover:opacity-100 transition-opacity" class="absolute right-4 top-4 flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-blue-100 hover:text-blue-600 dark:hover:bg-blue-900 dark:hover:text-blue-300 opacity-0 group-hover:opacity-100 transition-opacity"
> >
View all jobs <Icons.arrow_right class="w-3 h-3" /> View all jobs <Icons.icon name="icon-arrow-right" class="w-3 h-3" />
</.link> </.link>
</div> </div>
""" """
@ -339,7 +339,8 @@ defmodule Oban.Web.Queues.DetailComponent do
class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer" class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
phx-click={toggle_instances(@myself)} phx-click={toggle_instances(@myself)}
> >
<Icons.chevron_right <Icons.icon
name="icon-chevron-right"
id="instances-chevron" id="instances-chevron"
class={["w-5 h-5 transition-transform", if(@instances_open?, do: "rotate-90")]} class={["w-5 h-5 transition-transform", if(@instances_open?, do: "rotate-90")]}
/> />
@ -421,7 +422,8 @@ defmodule Oban.Web.Queues.DetailComponent do
class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer" class="flex items-center w-full space-x-2 px-2 py-1.5 rounded-md text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
phx-click={toggle_config(@myself)} phx-click={toggle_config(@myself)}
> >
<Icons.chevron_right <Icons.icon
name="icon-chevron-right"
id="config-chevron" id="config-chevron"
class={["w-5 h-5 transition-transform", if(@config_open?, do: "rotate-90")]} class={["w-5 h-5 transition-transform", if(@config_open?, do: "rotate-90")]}
/> />
@ -437,7 +439,7 @@ defmodule Oban.Web.Queues.DetailComponent do
phx-submit="local-submit" phx-submit="local-submit"
> >
<h3 class="flex items-center mb-4"> <h3 class="flex items-center mb-4">
<Icons.map_pin class="w-5 h-5 mr-1 text-gray-500" /> <Icons.icon name="icon-map-pin" class="w-5 h-5 mr-1 text-gray-500" />
<span class="text-base font-medium">Local Limit</span> <span class="text-base font-medium">Local Limit</span>
</h3> </h3>
@ -467,7 +469,7 @@ defmodule Oban.Web.Queues.DetailComponent do
> >
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h3 class="flex items-center"> <h3 class="flex items-center">
<Icons.globe class="w-5 h-5 mr-1 text-gray-500" /> <Icons.icon name="icon-globe" class="w-5 h-5 mr-1 text-gray-500" />
<span class="text-base font-medium">Global Limit</span> <span class="text-base font-medium">Global Limit</span>
<span <span
id="global-limit-info" id="global-limit-info"
@ -475,7 +477,10 @@ defmodule Oban.Web.Queues.DetailComponent do
phx-hook="Tippy" phx-hook="Tippy"
class="ml-1" class="ml-1"
> >
<Icons.info_circle class="w-4 h-4 text-gray-400 dark:text-gray-500" /> <Icons.icon
name="icon-info-circle"
class="w-4 h-4 text-gray-400 dark:text-gray-500"
/>
</span> </span>
</h3> </h3>
@ -565,7 +570,7 @@ defmodule Oban.Web.Queues.DetailComponent do
class="text-xs text-gray-500 dark:text-gray-400 hover:underline" class="text-xs text-gray-500 dark:text-gray-400 hover:underline"
> >
Requires Smart Engine Requires Smart Engine
<Icons.arrow_top_right_on_square class="w-3 h-3 inline-block" /> <Icons.icon name="icon-arrow-top-right-on-square" class="w-3 h-3 inline-block" />
</a> </a>
</div> </div>
</form> </form>
@ -579,7 +584,7 @@ defmodule Oban.Web.Queues.DetailComponent do
> >
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h3 class="flex items-center"> <h3 class="flex items-center">
<Icons.arrow_trending_down class="w-5 h-5 mr-1 text-gray-500" /> <Icons.icon name="icon-arrow-trending-down" class="w-5 h-5 mr-1 text-gray-500" />
<span class="text-base font-medium">Rate Limit</span> <span class="text-base font-medium">Rate Limit</span>
<span <span
id="rate-limit-info" id="rate-limit-info"
@ -587,7 +592,10 @@ defmodule Oban.Web.Queues.DetailComponent do
phx-hook="Tippy" phx-hook="Tippy"
class="ml-1" class="ml-1"
> >
<Icons.info_circle class="w-4 h-4 text-gray-400 dark:text-gray-500" /> <Icons.icon
name="icon-info-circle"
class="w-4 h-4 text-gray-400 dark:text-gray-500"
/>
</span> </span>
</h3> </h3>
@ -677,7 +685,7 @@ defmodule Oban.Web.Queues.DetailComponent do
class="text-xs text-gray-500 dark:text-gray-400 hover:underline" class="text-xs text-gray-500 dark:text-gray-400 hover:underline"
> >
Requires Smart Engine Requires Smart Engine
<Icons.arrow_top_right_on_square class="w-3 h-3 inline-block" /> <Icons.icon name="icon-arrow-top-right-on-square" class="w-3 h-3 inline-block" />
</a> </a>
</div> </div>
</form> </form>
@ -913,13 +921,13 @@ defmodule Oban.Web.Queues.DetailComponent do
class={"#{if @enabled, do: "opacity-0 ease-out duration-100", else: "opacity-100 ease-in duration-200"} absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"} class={"#{if @enabled, do: "opacity-0 ease-out duration-100", else: "opacity-100 ease-in duration-200"} absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"}
aria-hidden="true" aria-hidden="true"
> >
<Icons.x_mark class="h-3 w-3 text-gray-400" /> <Icons.icon name="icon-x-mark" class="h-3 w-3 text-gray-400" />
</span> </span>
<span <span
class={"#{if @enabled, do: "opacity-100 ease-in duration-200", else: "opacity-0 ease-out duration-100"} absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"} class={"#{if @enabled, do: "opacity-100 ease-in duration-200", else: "opacity-0 ease-out duration-100"} absolute inset-0 h-full w-full flex items-center justify-center transition-opacity"}
aria-hidden="true" aria-hidden="true"
> >
<Icons.check class="h-3 w-3 text-blue-500" /> <Icons.icon name="icon-check" class="h-3 w-3 text-blue-500" />
</span> </span>
</span> </span>
</button> </button>
@ -930,7 +938,7 @@ defmodule Oban.Web.Queues.DetailComponent do
~H""" ~H"""
<div class="flex items-center justify-end mt-4 space-x-2"> <div class="flex items-center justify-end mt-4 space-x-2">
<%= if @locked do %> <%= if @locked do %>
<Icons.lock_closed class="w-5 h-5 text-gray-600 dark:text-gray-400" /> <Icons.icon name="icon-lock-closed" class="w-5 h-5 text-gray-600 dark:text-gray-400" />
<% end %> <% end %>
<button <button

View file

@ -115,13 +115,13 @@ defmodule Oban.Web.Queues.DetailInstanceComponent do
:if={@terminating?} :if={@terminating?}
class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300" class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"
> >
<Icons.power class="w-3 h-3 mr-0.5" /> Stopping <Icons.icon name="icon-power" class="w-3 h-3 mr-0.5" /> Stopping
</span> </span>
<span <span
:if={@paused and not @terminating?} :if={@paused and not @terminating?}
class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300" class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300"
> >
<Icons.pause_circle class="w-3 h-3 mr-0.5" /> Paused <Icons.icon name="icon-pause-circle" class="w-3 h-3 mr-0.5" /> Paused
</span> </span>
""" """
end end
@ -147,9 +147,9 @@ defmodule Oban.Web.Queues.DetailInstanceComponent do
type="button" type="button"
> >
<%= if @paused do %> <%= if @paused do %>
<Icons.play_circle class="w-5 h-5" /> <Icons.icon name="icon-play-circle" class="w-5 h-5" />
<% else %> <% else %>
<Icons.pause_circle class="w-5 h-5" /> <Icons.icon name="icon-pause-circle" class="w-5 h-5" />
<% end %> <% end %>
</button> </button>
""" """
@ -168,7 +168,7 @@ defmodule Oban.Web.Queues.DetailInstanceComponent do
phx-target={@target} phx-target={@target}
type="button" type="button"
> >
<Icons.pencil_square class="w-5 h-5" /> <Icons.icon name="icon-pencil-square" class="w-5 h-5" />
</button> </button>
""" """
end end

View file

@ -23,7 +23,7 @@ defmodule Oban.Web.Queues.TableComponent do
</ul> </ul>
<div :if={Enum.empty?(@queues) and Enum.empty?(@checks)} class="py-16 px-6 text-center"> <div :if={Enum.empty?(@queues) and Enum.empty?(@checks)} class="py-16 px-6 text-center">
<Icons.queue_list class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" /> <Icons.icon name="icon-queue-list" class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" />
<h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100">No queues</h3> <h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100">No queues</h3>
<p class="mt-2 text-base text-gray-500 dark:text-gray-400 max-w-md mx-auto"> <p class="mt-2 text-base text-gray-500 dark:text-gray-400 max-w-md mx-auto">
Queues process jobs concurrently. They'll appear here once your Oban instance starts with queues configured. Queues process jobs concurrently. They'll appear here once your Oban instance starts with queues configured.
@ -44,7 +44,7 @@ defmodule Oban.Web.Queues.TableComponent do
:if={Enum.empty?(@queues) and not Enum.empty?(@checks)} :if={Enum.empty?(@queues) and not Enum.empty?(@checks)}
class="flex items-center justify-center py-12 space-x-2 text-lg text-gray-600 dark:text-gray-300" class="flex items-center justify-center py-12 space-x-2 text-lg text-gray-600 dark:text-gray-300"
> >
<Icons.no_symbol /> <span>No queues match the current filters.</span> <Icons.icon name="icon-no-symbol" /> <span>No queues match the current filters.</span>
</div> </div>
<ul class="divide-y divide-gray-100 dark:divide-gray-800"> <ul class="divide-y divide-gray-100 dark:divide-gray-800">
@ -115,22 +115,25 @@ defmodule Oban.Web.Queues.TableComponent do
<span class="w-14 text-left tabular pl-2">{exec}/{limit}</span> <span class="w-14 text-left tabular pl-2">{exec}/{limit}</span>
</span> </span>
<div class="w-14 flex items-center justify-start space-x-1 text-gray-400 dark:text-gray-500"> <div class="w-14 flex items-center justify-start space-x-1 text-gray-400 dark:text-gray-500">
<Icons.globe <Icons.icon
:if={Queue.global_limit?(@queue)} :if={Queue.global_limit?(@queue)}
name="icon-globe"
class="w-4 h-4" class="w-4 h-4"
data-title="Global limit" data-title="Global limit"
id={"#{@queue.name}-has-global"} id={"#{@queue.name}-has-global"}
phx-hook="Tippy" phx-hook="Tippy"
/> />
<Icons.arrow_trending_down <Icons.icon
:if={Queue.rate_limit?(@queue)} :if={Queue.rate_limit?(@queue)}
name="icon-arrow-trending-down"
class="w-4 h-4" class="w-4 h-4"
data-title="Rate limit" data-title="Rate limit"
id={"#{@queue.name}-has-rate"} id={"#{@queue.name}-has-rate"}
phx-hook="Tippy" phx-hook="Tippy"
/> />
<Icons.view_columns <Icons.icon
:if={Queue.partitioned?(@queue)} :if={Queue.partitioned?(@queue)}
name="icon-view-columns"
class="w-4 h-4" class="w-4 h-4"
data-title="Partitioned" data-title="Partitioned"
id={"#{@queue.name}-has-partition"} id={"#{@queue.name}-has-partition"}
@ -203,24 +206,27 @@ defmodule Oban.Web.Queues.TableComponent do
</span> </span>
<div class="w-20 pr-3 flex justify-center items-center space-x-1"> <div class="w-20 pr-3 flex justify-center items-center space-x-1">
<Icons.pause_circle <Icons.icon
:if={Queue.all_paused?(@queue)} :if={Queue.all_paused?(@queue)}
name="icon-pause-circle"
class="w-5 h-5" class="w-5 h-5"
data-title="All paused" data-title="All paused"
id={"#{@queue.name}-is-paused"} id={"#{@queue.name}-is-paused"}
phx-hook="Tippy" phx-hook="Tippy"
rel="is-paused" rel="is-paused"
/> />
<Icons.play_pause_circle <Icons.icon
:if={Queue.any_paused?(@queue) and not Queue.all_paused?(@queue)} :if={Queue.any_paused?(@queue) and not Queue.all_paused?(@queue)}
name="icon-play-pause-circle"
class="w-5 h-5" class="w-5 h-5"
data-title="Some paused" data-title="Some paused"
id={"#{@queue.name}-is-some-paused"} id={"#{@queue.name}-is-some-paused"}
phx-hook="Tippy" phx-hook="Tippy"
rel="has-some-paused" rel="has-some-paused"
/> />
<Icons.power <Icons.icon
:if={Queue.terminating?(@queue)} :if={Queue.terminating?(@queue)}
name="icon-power"
class="w-5 h-5" class="w-5 h-5"
data-title="Terminating" data-title="Terminating"
id={"#{@queue.name}-is-terminating"} id={"#{@queue.name}-is-terminating"}

View file

@ -36,7 +36,7 @@ defmodule Oban.Web.RefreshComponent do
phx-click={JS.toggle(to: "#refresh-menu")} phx-click={JS.toggle(to: "#refresh-menu")}
type="button" type="button"
> >
<Icons.arrow_path_rounded /> <Icons.icon name="icon-arrow-path-rounded" />
<span class="ml-1 leading-6 text-sm">{:proplists.get_value(@refresh, @options)}</span> <span class="ml-1 leading-6 text-sm">{:proplists.get_value(@refresh, @options)}</span>
</button> </button>
@ -78,7 +78,7 @@ defmodule Oban.Web.RefreshComponent do
phx-value-interval={@value} phx-value-interval={@value}
> >
<%= if @value == @refresh do %> <%= if @value == @refresh do %>
<Icons.check class="w-5 h-5" /> <Icons.icon name="icon-check" class="w-5 h-5" />
<% else %> <% else %>
<span class="block w-5 h-5"></span> <span class="block w-5 h-5"></span>
<% end %> <% end %>

View file

@ -43,7 +43,7 @@ defmodule Oban.Web.SearchComponent do
<%= if @loading do %> <%= if @loading do %>
<Icons.spinner class="w-5 h-5 text-gray-200 animate-spin dark:text-gray-600 fill-violet-500" /> <Icons.spinner class="w-5 h-5 text-gray-200 animate-spin dark:text-gray-600 fill-violet-500" />
<% else %> <% else %>
<Icons.magnifying_glass class="w-5 h-5 text-gray-500" /> <Icons.icon name="icon-magnifying-glass" class="w-5 h-5 text-gray-500" />
<% end %> <% end %>
</div> </div>
@ -84,7 +84,7 @@ defmodule Oban.Web.SearchComponent do
phx-click="clear" phx-click="clear"
type="reset" type="reset"
> >
<Icons.x_circle class="w-5 h-5" /> <Icons.icon name="icon-x-circle" class="w-5 h-5" />
</button> </button>
<nav <nav
@ -102,7 +102,7 @@ defmodule Oban.Web.SearchComponent do
/> />
<div :if={Enum.empty?(@suggestions)} class="w-full flex items-center space-x-2 p-1"> <div :if={Enum.empty?(@suggestions)} class="w-full flex items-center space-x-2 p-1">
<Icons.exclamation_circle class="w-5 h-5 text-gray-400" /> <Icons.icon name="icon-exclamation-circle" class="w-5 h-5 text-gray-400" />
<span class="text-gray-700">No suggestions matching <b>"{@buffer}"</b></span> <span class="text-gray-700">No suggestions matching <b>"{@buffer}"</b></span>
</div> </div>
</div> </div>
@ -113,7 +113,7 @@ defmodule Oban.Web.SearchComponent do
title="Read the filtering and searching guide" title="Read the filtering and searching guide"
target="_blank" target="_blank"
> >
<Icons.info_circle class="w-4 h-4" /> <Icons.icon name="icon-info-circle" class="w-4 h-4" />
<span class="text-xs">Filtering Tips</span> <span class="text-xs">Filtering Tips</span>
</a> </a>
</nav> </nav>
@ -139,7 +139,7 @@ defmodule Oban.Web.SearchComponent do
phx-value-terms={@terms} phx-value-terms={@terms}
phx-target="#search" phx-target="#search"
> >
<Icons.x_mark class="w-5 h-5" /> <Icons.icon name="icon-x-mark" class="w-5 h-5" />
</button> </button>
</div> </div>
""" """

View file

@ -33,7 +33,7 @@ defmodule Oban.Web.ShortcutsComponent do
class="absolute top-4 right-4 text-gray-400 hover:text-gray-600" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600"
aria-label="close" aria-label="close"
> >
<Icons.x_mark /> <Icons.icon name="icon-x-mark" />
</button> </button>
<div> <div>

View file

@ -71,11 +71,11 @@ defmodule Oban.Web.ThemeComponent do
~H""" ~H"""
<%= case @theme do %> <%= case @theme do %>
<% "light" -> %> <% "light" -> %>
<Icons.sun /> <Icons.icon name="icon-sun" />
<% "dark" -> %> <% "dark" -> %>
<Icons.moon /> <Icons.icon name="icon-moon" />
<% "system" -> %> <% "system" -> %>
<Icons.computer_desktop /> <Icons.icon name="icon-computer-desktop" />
<% end %> <% end %>
""" """
end end

View file

@ -64,7 +64,10 @@ defmodule Oban.Web.Workflows.DetailComponent do
<% else %> <% else %>
<div class="flex items-center justify-center py-16"> <div class="flex items-center justify-center py-16">
<div class="text-center"> <div class="text-center">
<Icons.rectangle_group class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" /> <Icons.icon
name="icon-rectangle-group"
class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500"
/>
<h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100"> <h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100">
Workflow not found Workflow not found
</h3> </h3>
@ -97,7 +100,7 @@ defmodule Oban.Web.Workflows.DetailComponent do
phx-hook="HistoryBack" phx-hook="HistoryBack"
type="button" type="button"
> >
<Icons.arrow_left class="w-5 h-5" /> <Icons.icon name="icon-arrow-left" class="w-5 h-5" />
<span class="text-lg font-bold ml-2">{@workflow.name || @workflow.id}</span> <span class="text-lg font-bold ml-2">{@workflow.name || @workflow.id}</span>
</button> </button>
@ -142,7 +145,7 @@ defmodule Oban.Web.Workflows.DetailComponent do
defp parent_breadcrumb(assigns) do defp parent_breadcrumb(assigns) do
~H""" ~H"""
<div class="flex items-center ml-3 text-sm text-gray-500 dark:text-gray-400"> <div class="flex items-center ml-3 text-sm text-gray-500 dark:text-gray-400">
<Icons.arrow_turn_down_right class="w-4 h-4 mr-1" /> <Icons.icon name="icon-arrow-turn-down-right" class="w-4 h-4 mr-1" />
<span>sub-workflow of</span> <span>sub-workflow of</span>
<.link <.link
navigate={oban_path([:workflows, @parent.id])} navigate={oban_path([:workflows, @parent.id])}
@ -233,10 +236,13 @@ defmodule Oban.Web.Workflows.DetailComponent do
phx-click="toggle-graph" phx-click="toggle-graph"
phx-target={@myself} phx-target={@myself}
> >
<Icons.chevron_right class={[ <Icons.icon
"w-5 h-5 transition-transform", name="icon-chevron-right"
@graph_open? && "rotate-90" class={[
]} /> "w-5 h-5 transition-transform",
@graph_open? && "rotate-90"
]}
/>
<span class="font-semibold">Workflow Graph</span> <span class="font-semibold">Workflow Graph</span>
</button> </button>
@ -419,10 +425,13 @@ defmodule Oban.Web.Workflows.DetailComponent do
phx-click="toggle-subs" phx-click="toggle-subs"
phx-target={@myself} phx-target={@myself}
> >
<Icons.chevron_right class={[ <Icons.icon
"w-5 h-5 transition-transform", name="icon-chevron-right"
@subs_open? && "rotate-90" class={[
]} /> "w-5 h-5 transition-transform",
@subs_open? && "rotate-90"
]}
/>
<span class="font-semibold"> <span class="font-semibold">
Sub-workflows Sub-workflows
<span class="text-gray-400 font-normal"> <span class="text-gray-400 font-normal">
@ -522,17 +531,17 @@ defmodule Oban.Web.Workflows.DetailComponent do
<span class="inline-flex"> <span class="inline-flex">
<%= case @state do %> <%= case @state do %>
<% "executing" -> %> <% "executing" -> %>
<Icons.play_circle class="w-5 h-5 text-emerald-400" /> <Icons.icon name="icon-play-circle" class="w-5 h-5 text-emerald-400" />
<% "completed" -> %> <% "completed" -> %>
<Icons.check_circle class="w-5 h-5 text-cyan-400" /> <Icons.icon name="icon-check-circle" class="w-5 h-5 text-cyan-400" />
<% "retryable" -> %> <% "retryable" -> %>
<Icons.arrow_path class="w-5 h-5 text-yellow-400" /> <Icons.icon name="icon-arrow-path" class="w-5 h-5 text-yellow-400" />
<% "cancelled" -> %> <% "cancelled" -> %>
<Icons.x_circle class="w-5 h-5 text-violet-400" /> <Icons.icon name="icon-x-circle" class="w-5 h-5 text-violet-400" />
<% "discarded" -> %> <% "discarded" -> %>
<Icons.exclamation_circle class="w-5 h-5 text-rose-400" /> <Icons.icon name="icon-exclamation-circle" class="w-5 h-5 text-rose-400" />
<% _ -> %> <% _ -> %>
<Icons.minus_circle class="w-5 h-5 text-gray-400" /> <Icons.icon name="icon-minus-circle" class="w-5 h-5 text-gray-400" />
<% end %> <% end %>
</span> </span>
""" """

View file

@ -21,7 +21,10 @@ defmodule Oban.Web.Workflows.TableComponent do
</ul> </ul>
<div :if={Enum.empty?(@workflows)} class="py-16 px-6 text-center"> <div :if={Enum.empty?(@workflows)} class="py-16 px-6 text-center">
<Icons.rectangle_group class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" /> <Icons.icon
name="icon-rectangle-group"
class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500"
/>
<h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100">No workflows</h3> <h3 class="mt-4 text-xl font-semibold text-gray-900 dark:text-gray-100">No workflows</h3>
<p class="mt-2 text-base text-gray-500 dark:text-gray-400 max-w-md mx-auto"> <p class="mt-2 text-base text-gray-500 dark:text-gray-400 max-w-md mx-auto">
Workflows coordinate jobs with dependencies. They'll appear here once jobs with workflow metadata are enqueued. Workflows coordinate jobs with dependencies. They'll appear here once jobs with workflow metadata are enqueued.
@ -249,15 +252,15 @@ defmodule Oban.Web.Workflows.TableComponent do
<span data-title={status_title(@state)} id={"workflow-state-#{@id}"} phx-hook="Tippy"> <span data-title={status_title(@state)} id={"workflow-state-#{@id}"} phx-hook="Tippy">
<%= case @state do %> <%= case @state do %>
<% :executing -> %> <% :executing -> %>
<Icons.play_circle class="w-5 h-5 text-emerald-400" /> <Icons.icon name="icon-play-circle" class="w-5 h-5 text-emerald-400" />
<% :completed -> %> <% :completed -> %>
<Icons.check_circle class="w-5 h-5 text-cyan-400" /> <Icons.icon name="icon-check-circle" class="w-5 h-5 text-cyan-400" />
<% :cancelled -> %> <% :cancelled -> %>
<Icons.x_circle class="w-5 h-5 text-violet-400" /> <Icons.icon name="icon-x-circle" class="w-5 h-5 text-violet-400" />
<% :discarded -> %> <% :discarded -> %>
<Icons.exclamation_circle class="w-5 h-5 text-rose-400" /> <Icons.icon name="icon-exclamation-circle" class="w-5 h-5 text-rose-400" />
<% _ -> %> <% _ -> %>
<Icons.minus_circle class="w-5 h-5 text-gray-400" /> <Icons.icon name="icon-minus-circle" class="w-5 h-5 text-gray-400" />
<% end %> <% end %>
</span> </span>
""" """

View file

@ -71,7 +71,7 @@ defmodule Oban.Web.CronsPage do
"text-gray-400 dark:text-gray-500 border-gray-200 dark:border-gray-800 cursor-not-allowed opacity-50" "text-gray-400 dark:text-gray-500 border-gray-200 dark:border-gray-800 cursor-not-allowed opacity-50"
]} ]}
> >
<Icons.plus_circle class="mr-1 h-4 w-4" /> New <Icons.icon name="icon-plus-circle" class="mr-1 h-4 w-4" /> New
</.link> </.link>
</div> </div>
</div> </div>

View file

@ -89,7 +89,7 @@ defmodule Oban.Web.JobsPage do
click="cancel-jobs" click="cancel-jobs"
target={@myself} target={@myself}
> >
<:icon><Icons.x_circle class="w-5 h-5" /></:icon> <:icon><Icons.icon name="icon-x-circle" class="w-5 h-5" /></:icon>
<:title>Cancel Jobs</:title> <:title>Cancel Jobs</:title>
</Core.action_button> </Core.action_button>
@ -99,7 +99,7 @@ defmodule Oban.Web.JobsPage do
click="retry-jobs" click="retry-jobs"
target={@myself} target={@myself}
> >
<:icon><Icons.arrow_right_circle class="w-5 h-5" /></:icon> <:icon><Icons.icon name="icon-arrow-right-circle" class="w-5 h-5" /></:icon>
<:title>Retry Jobs</:title> <:title>Retry Jobs</:title>
</Core.action_button> </Core.action_button>
@ -109,7 +109,7 @@ defmodule Oban.Web.JobsPage do
click="retry-jobs" click="retry-jobs"
target={@myself} target={@myself}
> >
<:icon><Icons.arrow_right_circle class="w-5 h-5" /></:icon> <:icon><Icons.icon name="icon-arrow-right-circle" class="w-5 h-5" /></:icon>
<:title>Run Jobs Now</:title> <:title>Run Jobs Now</:title>
</Core.action_button> </Core.action_button>
@ -120,7 +120,7 @@ defmodule Oban.Web.JobsPage do
target={@myself} target={@myself}
danger={true} danger={true}
> >
<:icon><Icons.trash class="w-5 h-5" /></:icon> <:icon><Icons.icon name="icon-trash" class="w-5 h-5" /></:icon>
<:title>Delete Jobs</:title> <:title>Delete Jobs</:title>
</Core.action_button> </Core.action_button>
</div> </div>
@ -162,7 +162,7 @@ defmodule Oban.Web.JobsPage do
"text-gray-400 dark:text-gray-500 border-gray-200 dark:border-gray-800 cursor-not-allowed opacity-50" "text-gray-400 dark:text-gray-500 border-gray-200 dark:border-gray-800 cursor-not-allowed opacity-50"
]} ]}
> >
<Icons.plus_circle class="mr-1 h-4 w-4" /> New <Icons.icon name="icon-plus-circle" class="mr-1 h-4 w-4" /> New
</.link> </.link>
</div> </div>
</div> </div>

View file

@ -56,7 +56,7 @@ defmodule Oban.Web.QueuesPage do
click="pause-queues" click="pause-queues"
target={@myself} target={@myself}
> >
<:icon><Icons.pause_circle class="w-5 h-5" /></:icon> <:icon><Icons.icon name="icon-pause-circle" class="w-5 h-5" /></:icon>
<:title>Pause Queues</:title> <:title>Pause Queues</:title>
</Core.action_button> </Core.action_button>
@ -66,7 +66,7 @@ defmodule Oban.Web.QueuesPage do
click="resume-queues" click="resume-queues"
target={@myself} target={@myself}
> >
<:icon><Icons.play_circle class="w-5 h-5" /></:icon> <:icon><Icons.icon name="icon-play-circle" class="w-5 h-5" /></:icon>
<:title>Resume Queues</:title> <:title>Resume Queues</:title>
</Core.action_button> </Core.action_button>
@ -77,7 +77,7 @@ defmodule Oban.Web.QueuesPage do
target={@myself} target={@myself}
danger={true} danger={true}
> >
<:icon><Icons.x_circle class="w-5 h-5" /></:icon> <:icon><Icons.icon name="icon-x-circle" class="w-5 h-5" /></:icon>
<:title>Stop Queues</:title> <:title>Stop Queues</:title>
</Core.action_button> </Core.action_button>
</div> </div>

View file

@ -121,7 +121,7 @@ defmodule Oban.Web.WorkflowsPage do
~H""" ~H"""
<div class="flex flex-col items-center justify-center py-16 px-6"> <div class="flex flex-col items-center justify-center py-16 px-6">
<div class="flex items-center justify-center w-16 h-16 rounded-full bg-violet-100 dark:bg-violet-900/30 mb-6"> <div class="flex items-center justify-center w-16 h-16 rounded-full bg-violet-100 dark:bg-violet-900/30 mb-6">
<Icons.rectangle_group class="w-8 h-8 text-violet-500 dark:text-violet-400" /> <Icons.icon name="icon-rectangle-group" class="w-8 h-8 text-violet-500 dark:text-violet-400" />
</div> </div>
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-3"> <h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-3">
@ -136,21 +136,21 @@ defmodule Oban.Web.WorkflowsPage do
<ul class="text-left text-gray-600 dark:text-gray-400 space-y-3 mb-8"> <ul class="text-left text-gray-600 dark:text-gray-400 space-y-3 mb-8">
<li class="flex items-start"> <li class="flex items-start">
<Icons.check class="w-5 h-5 text-violet-500 mr-2 mt-0.5 shrink-0" /> <Icons.icon name="icon-check" class="w-5 h-5 text-violet-500 mr-2 mt-0.5 shrink-0" />
<span> <span>
<span class="font-medium text-gray-700 dark:text-gray-300">Fully Distributed</span> <span class="font-medium text-gray-700 dark:text-gray-300">Fully Distributed</span>
high availability and scalability across your infrastructure high availability and scalability across your infrastructure
</span> </span>
</li> </li>
<li class="flex items-start"> <li class="flex items-start">
<Icons.check class="w-5 h-5 text-violet-500 mr-2 mt-0.5 shrink-0" /> <Icons.icon name="icon-check" class="w-5 h-5 text-violet-500 mr-2 mt-0.5 shrink-0" />
<span> <span>
<span class="font-medium text-gray-700 dark:text-gray-300">Cascading Context</span> <span class="font-medium text-gray-700 dark:text-gray-300">Cascading Context</span>
pass cumulative context between jobs for seamless data flow pass cumulative context between jobs for seamless data flow
</span> </span>
</li> </li>
<li class="flex items-start"> <li class="flex items-start">
<Icons.check class="w-5 h-5 text-violet-500 mr-2 mt-0.5 shrink-0" /> <Icons.icon name="icon-check" class="w-5 h-5 text-violet-500 mr-2 mt-0.5 shrink-0" />
<span> <span>
<span class="font-medium text-gray-700 dark:text-gray-300">Nested Sub-Workflows</span> <span class="font-medium text-gray-700 dark:text-gray-300">Nested Sub-Workflows</span>
compose hierarchically for better organization and reusability compose hierarchically for better organization and reusability
@ -163,7 +163,7 @@ defmodule Oban.Web.WorkflowsPage do
target="_blank" target="_blank"
class="inline-flex items-center px-5 py-2.5 rounded-lg bg-violet-600 hover:bg-violet-700 text-white font-medium transition-colors" class="inline-flex items-center px-5 py-2.5 rounded-lg bg-violet-600 hover:bg-violet-700 text-white font-medium transition-colors"
> >
Learn about Oban Pro <Icons.arrow_top_right_on_square class="w-4 h-4 ml-2" /> Learn about Oban Pro <Icons.icon name="icon-arrow-top-right-on-square" class="w-4 h-4 ml-2" />
</.link> </.link>
</div> </div>
""" """

View file

@ -228,6 +228,8 @@ defmodule Oban.Web.CronQuery do
query = select(Oban.Pro.Cron, [c], {c.expression, c.worker, c.opts, c.name, true, c.paused}) query = select(Oban.Pro.Cron, [c], {c.expression, c.worker, c.opts, c.name, true, c.paused})
Repo.all(conf, query) Repo.all(conf, query)
else
[]
end end
end end

View file

@ -200,7 +200,6 @@ defmodule Oban.Web.Router do
get "/css-:md5", Oban.Web.Assets, :css, as: :oban_web_asset get "/css-:md5", Oban.Web.Assets, :css, as: :oban_web_asset
get "/js-:md5", Oban.Web.Assets, :js, as: :oban_web_asset get "/js-:md5", Oban.Web.Assets, :js, as: :oban_web_asset
get "/fonts/inter.woff2", Oban.Web.Assets, :font, as: :oban_web_font get "/fonts/inter.woff2", Oban.Web.Assets, :font, as: :oban_web_font
get "/icons/*path", Oban.Web.Assets, :icon, as: :oban_web_icon
live "/", Oban.Web.DashboardLive, :home, route_opts live "/", Oban.Web.DashboardLive, :home, route_opts
live "/:page", Oban.Web.DashboardLive, :index, route_opts live "/:page", Oban.Web.DashboardLive, :index, route_opts

View file

@ -5,9 +5,9 @@ defmodule Oban.Web.Utils do
alias Oban.Repo alias Oban.Repo
def has_crons?(conf), do: has_table?(conf, "oban_crons") def has_crons?(conf), do: has_table?("oban_crons", conf)
def has_workflows?(conf), do: has_table?(conf, "oban_workflows") def has_workflows?(conf), do: has_table?("oban_workflows", conf)
def has_pro? do def has_pro? do
persistent_cache(:pro?, fn -> Code.ensure_loaded?(Oban.Pro) end) persistent_cache(:pro?, fn -> Code.ensure_loaded?(Oban.Pro) end)
@ -20,10 +20,13 @@ defmodule Oban.Web.Utils do
end end
end end
defp has_table?(conf, table_name) do defp has_table?(_table_name, %{engine: Oban.Engines.Dolphin}), do: false
%{name: name, prefix: prefix} = conf defp has_table?(_table_name, %{engine: Oban.Engines.Lite}), do: false
persistent_cache({:table?, name, table_name}, fn -> defp has_table?(table_name, conf) do
%{name: oban_name, prefix: prefix} = conf
persistent_cache({:table?, oban_name, table_name}, fn ->
query = query =
from("tables") from("tables")
|> put_query_prefix("information_schema") |> put_query_prefix("information_schema")

View file

@ -2,7 +2,7 @@ defmodule Oban.Web.MixProject do
use Mix.Project use Mix.Project
@source_url "https://github.com/oban-bg/oban_web" @source_url "https://github.com/oban-bg/oban_web"
@version "2.12.1" @version "2.12.3"
def project do def project do
[ [

File diff suppressed because one or more lines are too long

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75"/>
</svg>

Before

Width:  |  Height:  |  Size: 380 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 13.5V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m12-3V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m-6-9V3.75m0 3.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 9.75V10.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 373 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"/>
</svg>

Before

Width:  |  Height:  |  Size: 211 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12c0-1.232-.046-2.453-.138-3.662a4.006 4.006 0 00-3.7-3.7 48.678 48.678 0 00-7.324 0 4.006 4.006 0 00-3.7 3.7c-.017.22-.032.441-.046.662M19.5 12l3-3m-3 3l-3-3m-12 3c0 1.232.046 2.453.138 3.662a4.006 4.006 0 003.7 3.7 48.656 48.656 0 007.324 0 4.006 4.006 0 003.7-3.7c.017-.22.032-.441.046-.662M4.5 12l3 3m-3-3l-3 3"/>
</svg>

Before

Width:  |  Height:  |  Size: 496 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"/>
</svg>

Before

Width:  |  Height:  |  Size: 339 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12.75 15l3-3m0 0l-3-3m3 3h-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 241 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3"/>
</svg>

Before

Width:  |  Height:  |  Size: 211 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"/>
</svg>

Before

Width:  |  Height:  |  Size: 302 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 6L9 12.75l4.286-4.286a11.948 11.948 0 014.306 6.43l.776 2.898m0 0l3.182-5.511m-3.182 5.51l-5.511-3.181"/>
</svg>

Before

Width:  |  Height:  |  Size: 284 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="m16.49 12 3.75 3.75m0 0-3.75 3.75m3.75-3.75H3.74V4.499" />
</svg>

Before

Width:  |  Height:  |  Size: 253 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 4.5h14.25M3 9h9.75M3 13.5h9.75m4.5-4.5v12m0 0l-3.75-3.75M17.25 21L21 17.25"/>
</svg>

Before

Width:  |  Height:  |  Size: 253 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12"/>
</svg>

Before

Width:  |  Height:  |  Size: 250 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.317 6.066a.648.648 0 0 1 .337.703l-.683 3.517h3.404c.245 0 .467.147.568.375a.657.657 0 0 1-.096.688l-5.416 6.43a.613.613 0 0 1-.747.155.648.648 0 0 1-.338-.702l.683-3.518H7.625a.624.624 0 0 1-.568-.375.657.657 0 0 1 .096-.688l5.416-6.43a.613.613 0 0 1 .748-.155ZM21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 479 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.412 15.655 9.75 21.75l3.745-4.012M9.257 13.5H3.75l2.659-2.849m2.048-2.194L14.25 2.25 12 10.5h8.25l-4.707 5.043M8.457 8.457 3 3m5.457 5.457 7.086 7.086m0 0L21 21"/>
</svg>

Before

Width:  |  Height:  |  Size: 340 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5m-9-6h.008v.008H12v-.008zM12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zM9.75 15h.008v.008H9.75V15zm0 2.25h.008v.008H9.75v-.008zM7.5 15h.008v.008H7.5V15zm0 2.25h.008v.008H7.5v-.008zm6.75-4.5h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V15zm0 2.25h.008v.008h-.008v-.008zm2.25-4.5h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5V15z"/>
</svg>

Before

Width:  |  Height:  |  Size: 719 B

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM18.75 10.5h.008v.008h-.008V10.5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 669 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 14.25v2.25m3-4.5v4.5m3-6.75v6.75m3-9v9M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z"/>
</svg>

Before

Width:  |  Height:  |  Size: 326 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 235 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 197 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 203 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
</svg>

Before

Width:  |  Height:  |  Size: 226 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 201 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 15.75l7.5-7.5 7.5 7.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 202 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184" />
</svg>

Before

Width:  |  Height:  |  Size: 567 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 221 B

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 0 1 1.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.559.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.894.149c-.424.07-.764.383-.929.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 0 1-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.398.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 0 1-.12-1.45l.527-.737c.25-.35.272-.806.108-1.204-.165-.397-.506-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.108-1.204l-.526-.738a1.125 1.125 0 0 1 .12-1.45l.773-.773a1.125 1.125 0 0 1 1.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894Z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12a7.5 7.5 0 0015 0m-15 0a7.5 7.5 0 1115 0m-15 0H3m16.5 0H21m-1.5 0H12m-8.457 3.077l1.41-.513m14.095-5.13l1.41-.513M5.106 17.785l1.15-.964m11.49-9.642l1.149-.964M7.501 19.795l.75-1.3m7.5-12.99l.75-1.3m-6.063 16.658l.26-1.477m2.605-14.772l.26-1.477m0 17.726l-.26-1.477M10.698 4.614l-.26-1.477M16.5 19.794l-.75-1.299M7.5 4.205L12 12m6.894 5.785l-1.149-.964M6.256 7.178l-1.15-.964m15.352 8.864l-1.41-.513M4.954 9.435l-1.41-.514M12.002 12l-3.75 6.495"/>
</svg>

Before

Width:  |  Height:  |  Size: 627 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z"/>
</svg>

Before

Width:  |  Height:  |  Size: 318 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 17.25v1.007a3 3 0 01-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0115 18.257V17.25m6-12V15a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 15V5.25m18 0A2.25 2.25 0 0018.75 3H5.25A2.25 2.25 0 003 5.25m18 0V12a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 12V5.25"/>
</svg>

Before

Width:  |  Height:  |  Size: 431 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 393 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"/>
</svg>

Before

Width:  |  Height:  |  Size: 248 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"/>
</svg>

Before

Width:  |  Height:  |  Size: 637 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 8.25h15m-16.5 7.5h15m-1.8-13.5l-3.9 19.5m-2.1-19.5l-3.9 19.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 242 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<line stroke-linecap="round" stroke-linejoin="round" x1="5" x2="19" y1="12" y2="12"/>
</svg>

Before

Width:  |  Height:  |  Size: 202 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"/>
</svg>

Before

Width:  |  Height:  |  Size: 322 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.712 4.33a9.027 9.027 0 0 1 1.652 1.306c.51.51.944 1.064 1.306 1.652M16.712 4.33l-3.448 4.138m3.448-4.138a9.014 9.014 0 0 0-9.424 0M19.67 7.288l-4.138 3.448m4.138-3.448a9.014 9.014 0 0 1 0 9.424m-4.138-5.976a3.736 3.736 0 0 0-.88-1.388 3.737 3.737 0 0 0-1.388-.88m2.268 2.268a3.765 3.765 0 0 1 0 2.528m-2.268-4.796a3.765 3.765 0 0 0-2.528 0m4.796 4.796c-.181.506-.475.982-.88 1.388a3.736 3.736 0 0 1-1.388.88m2.268-2.268 4.138 3.448m0 0a9.027 9.027 0 0 1-1.306 1.652c-.51.51-1.064.944-1.652 1.306m0 0-3.448-4.138m3.448 4.138a9.014 9.014 0 0 1-9.424 0m5.976-4.138a3.765 3.765 0 0 1-2.528 0m0 0a3.736 3.736 0 0 1-1.388-.88 3.737 3.737 0 0 1-.88-1.388m2.268 2.268L7.288 19.67m0 0a9.024 9.024 0 0 1-1.652-1.306 9.027 9.027 0 0 1-1.306-1.652m0 0 4.138-3.448M4.33 16.712a9.014 9.014 0 0 1 0-9.424m4.138 5.976a3.765 3.765 0 0 1 0-2.528m0 0c.181-.506.475-.982.88-1.388a3.736 3.736 0 0 1 1.388-.88m-2.268 2.268L4.33 7.288m6.406 1.18L7.288 4.33m0 0a9.024 9.024 0 0 0-1.652 1.306A9.025 9.025 0 0 0 4.33 7.288"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
</svg>

Before

Width:  |  Height:  |  Size: 368 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"/>
</svg>

Before

Width:  |  Height:  |  Size: 344 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"/>
</svg>

Before

Width:  |  Height:  |  Size: 250 B

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 348 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 217 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 257 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/>
</svg>

Before

Width:  |  Height:  |  Size: 270 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.25 9v6m-4.5 0V9M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 232 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"/>
</svg>

Before

Width:  |  Height:  |  Size: 432 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path d="M6 4h12.5a2 2 0 0 1 2 2v12.5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Zm2 13 9-9m-6.313 2.125a.563.563 0 1 1-1.125 0 .563.563 0 0 1 1.126 0Zm4.75 4.75a.562.562 0 1 1-1.124 0 .562.562 0 0 1 1.124 0Z" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 377 B

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 392 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path d="M17 9v6m-2.455-6v6M7 14.32V9.68c0-.494.509-.803.918-.558l3.877 2.32c.191.115.31.328.31.559 0 .23-.119.443-.31.558l-3.877 2.32a.59.59 0 0 1-.613-.002.65.65 0 0 1-.305-.556h0ZM12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 386 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 222 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M5.636 5.636a9 9 0 1 0 12.728 0M12 3v9"/>
</svg>

Before

Width:  |  Height:  |  Size: 214 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" />
</svg>

Before

Width:  |  Height:  |  Size: 398 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" />
</svg>

Before

Width:  |  Height:  |  Size: 316 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75zM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-8.25zM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-2.25z"/>
</svg>

Before

Width:  |  Height:  |  Size: 623 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 787 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z"/>
</svg>

Before

Width:  |  Height:  |  Size: 650 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.429 9.75L2.25 12l4.179 2.25m0-4.5l5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0l4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0l-5.571 3-5.571-3"/>
</svg>

Before

Width:  |  Height:  |  Size: 372 B

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 369 B

Some files were not shown because too many files have changed in this diff Show more