diff --git a/mix.lock b/mix.lock index 5276c875..f4bf65f5 100644 --- a/mix.lock +++ b/mix.lock @@ -3,7 +3,7 @@ "absinthe_phoenix": {:hex, :absinthe_phoenix, "2.0.4", "f36999412fbd6a2339abb5b7e24a4cc9492bbc7909d5806deeef83b06f55c508", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:absinthe_plug, "~> 1.5", [hex: :absinthe_plug, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.13 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "66617ee63b725256ca16264364148b10b19e2ecb177488cd6353584f2e6c1cf3"}, "absinthe_plug": {:hex, :absinthe_plug, "1.5.9", "4f66fd46aecf969b349dd94853e6132db6d832ae6a4b951312b6926ad4ee7ca3", [:mix], [{:absinthe, "~> 1.7", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "dcdc84334b0e9e2cd439bd2653678a822623f212c71088edf0a4a7d03f1fa225"}, "argon2_elixir": {:hex, :argon2_elixir, "4.1.3", "4f28318286f89453364d7fbb53e03d4563fd7ed2438a60237eba5e426e97785f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "7c295b8d8e0eaf6f43641698f962526cdf87c6feb7d14bd21e599271b510608c"}, - "bandit": {:hex, :bandit, "1.10.3", "1e5d168fa79ec8de2860d1b4d878d97d4fbbe2fdbe7b0a7d9315a4359d1d4bb9", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "99a52d909c48db65ca598e1962797659e3c0f1d06e825a50c3d75b74a5e2db18"}, + "bandit": {:hex, :bandit, "1.10.4", "02b9734c67c5916a008e7eb7e2ba68aaea6f8177094a5f8d95f1fb99069aac17", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "a5faf501042ac1f31d736d9d4a813b3db4ef812e634583b6a457b0928798a51d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.18", "5e43ef0ec7d31195dfa5a65a86e6131db999d074179d2ba5a8de11fe14570f55", [:mix], [], "hexpm", "f393e4fe6317829b158fb74d86eb681f737d2fe326aa61ccf6293c4104957e34"}, "cbor": {:hex, :cbor, "1.0.2", "9b0af85af291a556e10a0ffd48ba9a21a75e711828fafd3af193d56d95f0907f", [:mix], [], "hexpm", "edbc9b4a16eb93a582437b9b249c340a75af03958e338fb43d8c1be9fc65b864"}, @@ -63,16 +63,18 @@ "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "nimble_totp": {:hex, :nimble_totp, "1.0.0", "79753bae6ce59fd7cacdb21501a1dbac249e53a51c4cd22b34fa8438ee067283", [:mix], [], "hexpm", "6ce5e4c068feecdb782e85b18237f86f66541523e6bad123e02ee1adbe48eda9"}, "oban": {:hex, :oban, "2.21.1", "4b6af7b901ef9baca09e239b5a991ef2fa429cf5a13799bc429a131d610ff692", [: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", "8162a160924cf4a25905fed2a9242e7787d88e320e3b5b0dcf324eb17c51c4e6"}, - "oban_met": {:hex, :oban_met, "1.0.6", "2a5500aff496b7ac4b830b0b03b08e920625a051bb6890981fbb53b15f1cbdc0", [:mix], [{:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "15ea3303de76225878a8e6c25a9d62bd1e2e9dd1c46ac8487d873b9f99e8dcee"}, + "oban_met": {:hex, :oban_met, "1.1.0", "60eb84614ff31519c7af4c7c9114bae5ba0f3057645dccae19a9589b95fd70f8", [:mix], [{:oban, "~> 2.21", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "535af16a369f94c1ef31b15f4d5de991b07c105eb8689061b326183c52ba6793"}, + "oban_pro": {:hex, :oban_pro, "1.6.13", "35db517685f909b7a88bdc1498f6241fc78c439eec63f2ee56d7e6a565f3617c", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}], "oban", "5738c7884d740baa50565cfc348c7c6daace0c349f365dd5c4663d3fb10f854f"}, + "oban_web": {:hex, :oban_web, "2.12.1", "74b2b66a453f0072fa690126404f2bf23788f37aca373d3139ca2d8c708b18ee", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, "~> 2.21", [hex: :oban, repo: "hexpm", optional: false]}, {:oban_met, "~> 1.1", [hex: :oban_met, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "d6ee45d6e8c5ed9fbb6213a6cb22870055c0f52eee6f437281e935cf92c408d7"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.8.5", "919db335247e6d4891764dc3063415b0d2457641c5f9b3751b5df03d8e20bbcf", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "83b2bb125127e02e9f475c8e3e92736325b5b01b0b9b05407bcb4083b7a32485"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.7.0", "75c4b9dfb3efdc42aec2bd5f8bccd978aca0651dbcbc7a3f362ea5d9d43153c6", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "1d75011e4254cb4ddf823e81823a9629559a1be93b4321a6a5f11a5306fbf4cc"}, "phoenix_html": {:hex, :phoenix_html, "4.3.0", "d3577a5df4b6954cd7890c84d955c470b5310bb49647f0a114a6eeecc850f7ad", [:mix], [], "hexpm", "3eaa290a78bab0f075f791a46a981bbe769d94bc776869f4f3063a14f30497ad"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.2", "b18b0773a1ba77f28c52decbb0f10fd1ac4d3ae5b8632399bbf6986e3b665f62", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "d1f89c18114c50d394721365ffb428cce24f1c13de0467ffa773e2ff4a30d5b9"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.27", "9afcab28b0c82afdc51044e661bcd5b8de53d242593d34c964a37710b40a42af", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "415735d0b2c612c9104108b35654e977626a0cb346711e1e4f1ed16e3c827ede"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.28", "8a8e123d018025f756605a2fb02a4854f0d3cd7b207f710fef1fd5d9d72d0254", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "24faad535b65089642c3a7d84088109dc58f49c1f1c5a978659855d643466353"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.2.0", "ff3a5616e1bed6804de7773b92cbccfc0b0f473faf1f63d7daf1206c7aeaaa6f", [:mix], [], "hexpm", "adc313a5bf7136039f63cfd9668fde73bba0765e0614cba80c06ac9460ff3e96"}, - "phoenix_pubsub_redis": {:hex, :phoenix_pubsub_redis, "3.0.1", "d4d856b1e57a21358e448543e1d091e07e83403dde4383b8be04ed9d2c201cbc", [:mix], [{:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5.1 or ~> 1.6", [hex: :poolboy, repo: "hexpm", optional: false]}, {:redix, "~> 0.10.0 or ~> 1.0", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "0b36a17ff6e9a56159f8df8933d62b5c1f0695eae995a02e0c86c035ace6a309"}, + "phoenix_pubsub_redis": {:hex, :phoenix_pubsub_redis, "3.1.0", "8b95de04fdb5d86b4a0797ed5cd6c5e0cdb58bcf3badc766418f6042029c42aa", [:mix], [{:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5.1 or ~> 1.6", [hex: :poolboy, repo: "hexpm", optional: false]}, {:redix, "~> 1.0", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "d5de753365ccf1d1a405ea69accde806d5d4e3716ce0b65ed9b3eb6c3d7378f2"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"}, "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, diff --git a/vendor/README.md b/vendor/README.md index 7dc557ce..3976ece3 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -37,7 +37,7 @@ Advanced Oban features including batching, workflows, dynamic partitioning, and ### Oban Web -**Version**: 2.11.8 +**Version**: 2.12.1 **Source**: https://hex.pm/packages/oban_web **License**: Apache-2.0 @@ -60,7 +60,7 @@ end ### Oban Met -**Version**: 1.0.6 +**Version**: 1.1.0 **Source**: https://hex.pm/packages/oban_met **License**: Apache-2.0 diff --git a/vendor/oban_met/.hex b/vendor/oban_met/.hex index 78a6f8cb..67e377c4 100644 Binary files a/vendor/oban_met/.hex and b/vendor/oban_met/.hex differ diff --git a/vendor/oban_met/CHANGELOG.md b/vendor/oban_met/CHANGELOG.md index 6b9dea94..83da1147 100644 --- a/vendor/oban_met/CHANGELOG.md +++ b/vendor/oban_met/CHANGELOG.md @@ -1,10 +1,19 @@ # Changelog for Oban Met v1.0 +## v1.1.0 — 2026-03-25 + +### Enhancements + +- [Met] Require a minimum of Oban v2.21 + + Oban v2.21 introduces the `suspended` state, which is now counted by the reporter along with the + other states. + ## v1.0.6 — 2026-02-19 ### Enhancements -- Add configurable time unit for timing metrics +- [Met] Add configurable time unit for timing metrics Metrics stored in sketches now support configurable time units via the `:sketch_time_unit` compile-time option. By default, timing values remain in `:native` units for backward @@ -14,7 +23,7 @@ and consolidates timing values into fewer bins by eliminating nanosecond-level measurement noise. -- Share crontab options as a map rather than nested lists +- [Cronitor] Share crontab options as a map rather than nested lists For compatibility with entries shared by python, we convert options to a map structure rather than a list of lists. diff --git a/vendor/oban_met/README.md b/vendor/oban_met/README.md index f9031c0a..35883b60 100644 --- a/vendor/oban_met/README.md +++ b/vendor/oban_met/README.md @@ -106,6 +106,16 @@ declares an override to set the limit to 200k: {Oban.Met, reporter: [estimate_limit: 200_000]} ``` +### Customizing Check Interval + +The reporter periodically counts jobs in each state/queue combination to provide the counts +displayed in Oban Web. By default, counts are checked every 1 second. For systems where real-time +counts aren't critical, or to reduce database load, you can increase the interval: + +```elixir +{Oban.Met, reporter: [check_interval: :timer.seconds(5)]} +``` + ### Explicit Migrations Met will create the necessary estimate function automatically when possible. The migration isn't diff --git a/vendor/oban_met/hex_metadata.config b/vendor/oban_met/hex_metadata.config index 8ee7678c..c654163f 100644 --- a/vendor/oban_met/hex_metadata.config +++ b/vendor/oban_met/hex_metadata.config @@ -4,7 +4,7 @@ <<"https://github.com/oban-bg/oban_met/blob/main/CHANGELOG.md">>}, {<<"GitHub">>,<<"https://github.com/oban-bg/oban_met">>}]}. {<<"name">>,<<"oban_met">>}. -{<<"version">>,<<"1.0.6">>}. +{<<"version">>,<<"1.1.0">>}. {<<"description">>, <<"A distributed, compacting, multidimensional, telemetry-powered time series datastore">>}. {<<"elixir">>,<<"~> 1.15">>}. @@ -23,6 +23,6 @@ [[{<<"name">>,<<"oban">>}, {<<"app">>,<<"oban">>}, {<<"optional">>,false}, - {<<"requirement">>,<<"~> 2.19">>}, + {<<"requirement">>,<<"~> 2.21">>}, {<<"repository">>,<<"hexpm">>}]]}. {<<"build_tools">>,[<<"mix">>]}. diff --git a/vendor/oban_met/lib/oban/met/listener.ex b/vendor/oban_met/lib/oban/met/listener.ex index c1b86671..c05d22eb 100644 --- a/vendor/oban_met/lib/oban/met/listener.ex +++ b/vendor/oban_met/lib/oban/met/listener.ex @@ -148,7 +148,6 @@ defmodule Oban.Met.Listener do # For backward compatibility reasons, the telemetry event's state doesn't match the final job # state. Here we translate the event state to the `t:Oban.Job.state`. - defp trans_state(:cancelled), do: :cancelled defp trans_state(:discard), do: :discarded defp trans_state(:exhausted), do: :discarded defp trans_state(:failure), do: :retryable diff --git a/vendor/oban_met/lib/oban/met/reporter.ex b/vendor/oban_met/lib/oban/met/reporter.ex index 5aaa807d..b64497f8 100644 --- a/vendor/oban_met/lib/oban/met/reporter.ex +++ b/vendor/oban_met/lib/oban/met/reporter.ex @@ -26,7 +26,8 @@ defmodule Oban.Met.Reporter do "discarded" => [], "executing" => [], "retryable" => [], - "scheduled" => [] + "scheduled" => [], + "suspended" => [] } defstruct [ diff --git a/vendor/oban_met/mix.exs b/vendor/oban_met/mix.exs index 9b17ae36..fd12d413 100644 --- a/vendor/oban_met/mix.exs +++ b/vendor/oban_met/mix.exs @@ -2,7 +2,7 @@ defmodule Oban.Met.MixProject do use Mix.Project @source_url "https://github.com/oban-bg/oban_met" - @version "1.0.6" + @version "1.1.0" def project do [ @@ -97,7 +97,7 @@ defmodule Oban.Met.MixProject do defp deps do [ - {:oban, "~> 2.19"}, + {:oban, "~> 2.21"}, {:ecto_sqlite3, "~> 0.18", only: [:test, :dev]}, {:postgrex, "~> 0.20", only: [:test, :dev]}, {:stream_data, "~> 1.1", only: [:test, :dev]}, diff --git a/vendor/oban_web/.hex b/vendor/oban_web/.hex index d4c34ee5..b2dee2fe 100644 Binary files a/vendor/oban_web/.hex and b/vendor/oban_web/.hex differ diff --git a/vendor/oban_web/CHANGELOG.md b/vendor/oban_web/CHANGELOG.md index a31d1bd2..68978f96 100644 --- a/vendor/oban_web/CHANGELOG.md +++ b/vendor/oban_web/CHANGELOG.md @@ -1,349 +1,237 @@ -# Changelog for Oban Web v2.11 +# Changelog for Oban Web v2.12 -All notable changes to `Oban.Web` are documented here. +This is a major release that overhauls job details, the queues table, queue details, introduces a +crons page, adds a workflows page, and adds a job creation sidebar. -## ðŸĨ‚ Web is Free and Open Source +> #### Requirements {: .info} +> +> This release requires Oban v2.21+ and the new V14 migration due to important schema changes. For +> Pro users, v1.7+ is also required along with the v1.7.0 migration. -Oban Web is now [fully open source][fos] and free (as in champagne 😉)! From now on, Oban Web will -be [published to Hex][hex] and available for use in your Oban powered applications. See the -updated, much slimmer, [installation guide][ins] to get started! +## 🔀 Workflows Page -Web v2.11 is licensed under Apache 2.0, just like Oban and Elixir itself. Previous versions are -commercially licensed, therefore private, and won't be published to Hex. +There is a new page for viewing, filtering, and generally managing workflows. The table displays +workflow progress, activity counts, duration, and nested sub-workflows. Workflows can be filtered +by properties like name, workers, or status. -Special thanks to all of our customers that supported the project for the past 5 years and made -open source Oban, and now Web, possible 💛. + -[fos]: https://github.com/oban-bg/oban_web -[hex]: https://hex.pm/packages/oban_web -[ins]: installation.md +Clicking into a workflow brings you to a detail view with an interactive graph showing jobs as +stateful nodes with dependencies. The graph supports panning, zooming, directional layout +toggling, and a default tracking mode that follows executing nodes. Sub-workflow nodes can be +expanded inline to reveal their internal jobs, or navigated to for direct management such as +retrying or cancelling. -## 🐎ðŸŠķ MySQL and SQLite +Workflow viewing remains fast on busy systems or with _large_ workflows thanks to the new +`oban_workflows` aggregate table in Pro v1.7. It's also compatible with Python Pro, though +cancel/retry actions require Elixir. -All official Oban and Oban Pro engines are fully supported. Listing, filtering, ordering, and -searching through jobs works for Postgres, MySQL, and SQLite. That includes the particularly -gnarly issue of dynamically generating and manipulating JSON for filter auto-suggestions! Nested -args queries, such as `args.address.city:Edinburgh` work equally well with each engine. +## ⏰ Crons Page -## 🎛ïļ Instance Select +There's also a new page for viewing and managing cron entries. The table displays all static and +dynamic entries with history sparklines and activity details. -The dashboard will now support switching between Oban instances. A new instance select menu in the -header allows switching between running Oban instances at runtime. There's no need to mount -additional dashboards in your application's router to handle multiple instances. The most recently -selected instance persists between mounts. + -```diff -- oban_dashboard "/oban_a", oban_name: Oban.A -- oban_dashboard "/oban_b", oban_name: Oban.B -- oban_dashboard "/oban_c", oban_name: Oban.C -+ oban_dashboard "/oban" -``` +The cron detail view includes natural language expressions like "Daily at 8:00 and 9:00" or +"Weekdays except Monday", along with cron entry specifics, and recent job history. -This also eliminates the need for additional router configuration, as the dashboard will select -the first running Oban instance it finds (with a preference for the default `Oban`). +For Pro users, `DynamicCron` entires can be created, edited, paused, resumed, or deleted directly +from a form on the details page. -```diff -- oban_dashboard "/oban", oban_name: MyOban -+ oban_dashboard "/oban" -``` +## 🔍 Job Details -## â˜Ŋïļ Unified Tables +The job detail page is rebuilt with a full-width layout and a new timeline component that shows +the job state machine as a branching diagram rather than a linear progression. -The queue and jobs tables are fully rebuilt with shared, reusable components and matching -functionality. This makes interacting with jobs clearer while queues gain some much requested -functionality: + -- Sidebar - a new queue sidebar shows status counts and enables filtering by statuses such as - `paused` or `terminating`. +A scoped chart displays execution history for that worker's previous jobs and jobs in an +incomplete, non-executing state can now be edited directly. -- Filtering - queues are auto-complete filterable just like jobs, making it possible to find - queues running on a particular node or narrow down by status. +Executing Pro jobs display live diagnostics including process status, reductions, memory, and +current stacktrace. The diagnostics panel persists after job completion with a "Stale" indicator +showing the data is from when the job was running. -- Shared Sorting - queue sorting now behaves identically to jobs, through a shared dropdown. +## ➡ïļ Queues Table and Details -- Uniform Navigation - click on any part of the queue row to navigate to details. +The queues table is redesigned with a utilization gauge and a history sparkline showing 5-minute +throughput for each queue. The queue sidebar provided minimal value, and it was removed to make +room for the additional data displayed per-row. -- Condensed Rows - simplify the queue page by removing nested row components. Extra queue details - are in the sub-queue page. + -## ðŸ•Ŋïļ Operate on Full Selection - -Apply bulk actions to all selected jobs, not just those visible on the current page. - -This expands the select functionality to extend beyond the current page and include all filtered -jobs, up to a configurable limit. The limit defaults to 1000 and may be overridden with a [resolver -callback][rsc]. - -[rsc]: Oban.Web.Resolver.html#c:bulk_action_limit/1 - -## v2.11.8 - 2026-02-06 - -### Enhancements - -- [Dashboard] Add standalone docker image for simplified Web hosting - - A standalone Docker image is now available for situations where you want an Oban dashboard - without mounting Oban.Web within your application. - -- [Job] Safely handle missing timestamps in jobs table - - Manual table updates may forget to include a timestamp for a particular state, which crashes the - table. This provides an empty fallback for missing timestamps. - -## v2.11.7 - 2026-01-22 - -### Enhancements - -- [Router] Add configurable `:logo_path` option for dashboard - - Allow users to customize where the Oban logo links to by passing a `:logo_path` option to the - `oban_dashboard/1` router helper. When provided, the logo will link to the specified path - instead of the default jobs page. - -- [Query] Help query planner with value rather than subquery - - Since the condition uses a result of an inner query, database can't reliably estimate that it's - worth to use an index. It "thinks" that many rows will be returned in result and decides to use - a sequential scan. - - However, when a specific value is provided, database can use the collected statistics to come up - with a better plan and use the index. +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 +options and burst mode configuration. +## v2.12.1 - 2026-03-25 ### Bug Fixes -- [Jobs] Escape regex match in search highlighting to prevent exceptions +- [Dashboard] Include `priv/timezones.txt` in hex package - Unescaped values would crash the LiveView process rather than gracefully returning no matches. + The timezones file wasn't included in the package files list, which broke compilation for + downloaded packages. -- [Dashboard] Refactor inline styles to include CSP nonces +## v2.12.0 - 2026-03-25 - Avoid CSP warnings for inline styles used to control the sidebar. - -- [Dashboard] Respect custom variant for applying dark mode - - Additional configuration is required to manually apply dark mode after upgrading to Tailwind 4. - -- [Jobs] Correct detail background color for dark modes - -## v2.11.6 - 2025-10-24 - -### Bug Fixes - -- [Dashboard] Extract current prefix for correct asset routing - - The prefix was mistakenly hard coded to `/oban`, which broke the dashboard for any non-standard - paths. Now the value is extracted from the live view session. - -## v2.11.5 - 2025-10-23 +While all bug fixes are listed below, the enhancements section only covers a portion of the new +features. For enhancements, a video is worth many thousands of words. ### Enhancements -- [Dashboard] Extract CSS and JS assets into dedicated plug module +- [Dashboard] Preserve refresh changes between page changes - The changes move CSS/JS assets from layouts into a dedicated plug module, enabling cached and - immutable asset serving. + The refresh reverted to the original value between changes because there wasn't a new liveview + connection. Now the stored refresh value is synced on change, and reloaded when the component + mounts. -- [Dashboard] Upgrade to Tailwind v4 with updated styles +- [Dahboard] Add a 30s option for refreshing -- [Dashboard] Add sidebar resizing with local persistence + It's a simple addition that makes watching the cron page a bit more sensible. - The sidebar can now be resized within a small range of sizes to compensate for long queue names - or larger screens. The adjusted size is persisted as a setting within local storage, preserving - it across reloads. +- [Dashboard] Serve dynamically loaded mask based svg icons -- [Dashboard] Overhaul navigation for state retention between pages + Rather than manually defining SVG icons inline, SVG files are tracked and dynamically loaded + from a compiled assets module. - Page navigation uses patch rather than navigate to prevent re-mounting the dashboard. This was - essential to retain sidebar changes during page transitions. +- [Dashboard] Serve font as static asset instead of embedding -- [Telemetry] Add `queues` to metadata of `*_queues` telemetry events + Extract font out of the `app.css` and serve it as a stand-alone asset for better caching. - Telemetry events for `pause_queues`, `resume_queues`, and `stop_queues` now include the - selected queues. +- [Dashboard] Add help button to primary toolbar + + The keyboard shortcuts modal was only accessible via the ? key with no visual indication it + existed. Added a help dropdown to the toolbar that links to documentation and opens the + shortcuts modal. + +- [Jobs] Support suspended state in sidebar and details + + The latest Oban version adds a proper `suspended` state, so there's no more on_hold + psuedo-state. + +- [Jobs] Jobs rescued by any lifeline are detectable + + Change the language for orphans to correctly indicate that any lifeline may have rescued them. + +- [Jobs] Redesign timeline as state machine visualization + + Replace the linear horizontal timeline with a branching layout that accurately represents the + job state machine. Entry states (scheduled/retryable) flow into available, then executing, which + branches to terminal states (completed/cancelled/discarded). + +- [Jobs] Add diagnostics for executing jobs + + Actively executing `Oban.Pro.Worker` jobs now display diagnostics including process status, + reductions, memory, and the current stacktrace. + +- [Jobs] Add job editing to job detail page + + Job's in an incomplete and non-executing state can have their attributes edited. Internally, + `Oban.update_job/3` is used to perform the update, so standard validations still apply. + +- [Jobs] Refine layout and error display for job details + + Restructure information for job details to emphasize what's important (args, the most recent + error), while providing control over which information is displayed. + +- [Jobs] Show history chart component for job detail + + The new component uses exact historic information for the current job rather than the aggregate + metrics used for the primary job chart. + +- [Jobs] Add "New Job" drawer for creating jobs + + Jobs can now be created directly from the Jobs page using a slide-out drawer. The form includes + fields for worker, args, queue, priority, max attempts, scheduled time, and tags. After + creation, the user is navigated to the new job's detail page. + +- [Crons] Use name provided by crontab for entry job history + + The `cron_name` calculated for Elixir entries isn't compatible with those generated by Python. + The `oban-py` metrics now include a `name` option that is used to correctly match entries up + with historic jobs. + +- [Crons] Add cron parser for complex expressions + + Parse cron fields into structured data before describing them, enabling support for: + + - Combined DOM/DOW patterns like "The 1st, only on Mondays" + - Complement detection, "Daily except the 1st" or "except Tuesdays" + - Multiple hour values, "Daily at 8:00, 9:00, and 10:00" + - Weekday/weekend recognition + + It's also switched to a 24-hour time format for international consistency. + +- [Queues] Remove sidebar from queues page The sidebar filters (paused, terminating, modes, nodes) added little + + The sidebar filters (paused, terminating, modes, nodes) added little value for a typically small + dataset while consuming significant screen space. Filtering remains available via the search + component. + +- [Queues] Add history sparkline graph to queues table + + Display a 5-minute throughput history for each queue using a sparkline + visualization with 5-second rollups (60 data points). Hovering over bars + shows the job count and timestamp for that interval. + +- [Queues] Refine queue detail forms and layout + + Expand the queue form partitioning controls with "meta" options and burst mode. Also changes to + standard form inputs for numbers and select boxes within the edit form to simplify event + handling. + +- [Queues] Redesign queue details with actions and history + + Add status badges for paused, partial, and terminating states. Include pause/resume, stop, and + edit buttons in the header for quick access. Display queue execution history in a chart + alongside stats. + +- [Pages] Add empty states for workflows, crons, and queues + + Each page now shows a helpful message with an icon and documentation link when there's nothing + to display. The queues page distinguishes between having no queues configured versus filters + hiding all results. + +- [Pages] Improve dark mode color consistency and contrast + + Standardize border colors across form inputs and controls, align form input backgrounds, and + increase contrast for disabled elements. ### Bug Fixes -- [Router] Ensure the resolver module is loaded +- [Dahboard] Fix instance switching when resolver returns a list - Optional resolver modules are now loaded before checking whether they export a function. This - ensures functions are correctly identified, as they aren't automatically loaded locally by - Elixir (unless eager module loading is turned on). + Ensure the instance name and allowed instances are strings before comparison -- [Jobs] Display correct completed_at timestamp in details +- [Dashboard] Poll registry instead of blocking on telemetry - Completed jobs would show a relative time value that matched how long the job took rather than - when it completed. + Replace telemetry-based init that could block for 15 seconds waiting for an init event that may + have already fired. Now polls the Oban registry, avoiding the race condition that caused slow + websocket reconnections. -- [Dashboard] Use history navigation for detail back buttons +- [Dashboard] Cache sidebar counts to prevent flickering - Clicking the back arrow in job and queue detail pages removed any applied params, as it dropped - any non-id params on navigation. The new approach uses a history hook that leverages native - window history instead. + When the metric reporter's `check_interval` exceeds the 2s lookback window, counts briefly show + as zero between broadcasts. Cache previous non-empty counts in a centralized Metrics module and + return them when `Met.latest/3` returns an empty map. -## v2.11.4 - 2025-07-31 +- [Dashboard] Automatically update theme when OS theme changes -### Enhancements + Listen for prefers-color-scheme media query changes so the theme updates in real-time when the + browser or OS switches between light and dark mode. -- [Connectivity] Consider metric checks for disconnected status. +- [Standalone] Use the Postgres notifier for standalone instance - The connectivity status is now determined by `Met` output as well as pubsub connectivity. - This should make it easier to identify metric issues on solo nodes, e.g. in dev or a - staging environment. + The PG notifier can't (easily) connect to an external cluster for notifications. Connection is + possible through the Postgres notifier. -### Bug Fixes - -- [Dashboard] Read phoenix js assets at compile time. - - Stop bundling phoenix and liveview assets. Instead, read them at compile time and concatenate - with app js. - -- [Search] Trim strings when splitting to parse integers. - - This prevents "not a textual representation of an integer" errors when splitting on a comma - with an empty string. - -- [Search] Move all regexes out of module attributes. - - Regexes aren't allowed in module attributes as of OTP 28. This moves them inline rather than - hoisted at the top of the module. - -- [Sidebar] Fix column header mismatch in sidebar. - - The headers and values in the sidebar were misaligned and showed the wrong values. - -## v2.11.3 - 2025-04-21 - -### Bug Fixes - -- [Installer] Prevent compilation errors when igniter isn't installed. - -## v2.11.2 - 2025-04-21 - -### Enhancements - -- [Installer] Add igniter powered `oban_web` installer. - - It's now possible to install oban_web with a single igniter command: - - ```bash - mix igniter.install oban_web - ``` - - Or install `oban` and `oban_web` at the same time: - - ```bash - mix igniter.install oban,oban_web - ``` - -- [Resolver] Pattern match on `arg` rather than checking for `decorated` annotation. - - Matching on term encoded `arg` is more accurate than checking for decorated metadata. This makes - the default `format_job_args` compatible with workflow cascade jobs that don't have any `arg` - set. - -- [Page] Upgrade bundled assets to use the Phoenix LiveView JavaScript version to v1.10 - -- [Chart] Replace inline styles with tailwind classes to avoid inline style CSP warnings. - -## v2.11.1 — 2025-02-06 - -### Enhancements - -- [Layout] Display Oban.Met version in layout footer - - The Met version is highly relevant to how Web behaves. This also refactors the version display - for reuse with consistent conditionals. - -- [Layout] Only show Pro version number when available. - - The version footer shows that Pro isn't available rather than showing a `v` followed by a blank - space. - -- [Jobs] Auto-complete worker priorities from 0 to 9 - - Priority completion only matched values from 0..3, but the full range is 0..9. - -### Bug Fixes - -- [Jobs] Preserve quotes in `args` and `meta` searches. - - Parsing would strip quotes from args and meta queries. This prevented quoted numeric vaalues to - be treated as integers. Now quotes are preserved for `args` and `meta`, just as they are for - other qualifiers. - -- [Queue Details] Eliminate duplicate id warnings for inputs in queue details. - - The latest live_view alerts when nodes in a view have conflicting ids, which caught a number of - instances on the queue details page. - -- [Queues] Fix duplicate ids for nodes and queues in sidebar. - -- [Search] Correct assigns typo in search component handler. - - The key is `assigns`, not `asigns`. - -- [Resolver] Fix resolver access typespec. - - Access options must be a keyword list with boolean values, not just a list of option atoms. - -## v2.11.0 — 2025-01-16 - -### Enhancements - -- [Dashboard] Load using non-default Oban instance without any config. - - The dashboard now loads the first running non-default Oban instance, even without anything - configured. - -- [Jobs] Decode decorated `arg` when formatting job args. - - The decorated `arg` are term encoded and inscrutiable in the standard display. Now the value is - decoded and display as the original value. - -- [Jobs] Prevent decoding executable functions when displaying `recorded` output. - - By default, recorded content uses the `:safe` flag, which now prevents both atom creation and - executable content. This is an extra security precaution to prevent any possible exploits - through Web. - -- [Queues] Add sidebar similar to the jobs table, including filters to make bulk operations on - queues possible. - -- [Queues] Add search bar with filtering functionality to query queues. - -- [Queues] Add multi-select mode to allow operating on multiple queues at once. - -- [Queues] Expose a "Stop Queues" operation along with corresponding access controls to prevent - unintended shutdown. - -- [Resolver] Add `resolve_instances/1` callback to restrict selectable Oban instances. - - Not all instances should be available to all users. This adds the `resolve_instances/1` callback - to allow scoping instances by user: - - ```elixir - def resolve_instances(%{admin?: true}), do: :all - def resolve_instances(_user), do: [Oban] - ``` - -- [Resolver] Add `bulk_action_limit/1` to restrict the number of jobs operated on at once. - - Bulk operations now effect all filtered jobs, not just those visible on the current page. As - there may be millions of jobs that match a filter, some limiting factor is necessary. The - default of 1000 may be overridden: - - ```elixir - def bulk_action_limit(:completed), do: 10_000 - def bulk_action_limit(_state), do: :infinity - ``` - -### Bug Fixes - -- [Page] Address deprecations from recent upgrades to Elixir 1.17+, Phoenix 1.5+, and Phoenix - LiveView 1.0+ - -- [Jobs] Always treat args wrapped in quotes as a string when filtering. - - Manually wrapping values in quotes enforces searching by string, rather than as a parsed - integer. As values are cast to JSON the type matters, and `123 != "123"`. diff --git a/vendor/oban_web/README.md b/vendor/oban_web/README.md index 52e34895..d85b2e75 100644 --- a/vendor/oban_web/README.md +++ b/vendor/oban_web/README.md @@ -108,6 +108,30 @@ For development, a single file server that generates a wide variety of fake jobs iex -S mix dev ``` +### Python Development Workers + +A Python development app is available that runs alongside the Elixir dev server, sharing the same +database. Python workers use separate queues (inference, scraping, etl, webhooks, notifications, +transcoding, maintenance) and are tagged with "python" for easy filtering in the dashboard. + +First, install the Python dependencies (requires [uv](https://docs.astral.sh/uv/)): + +```bash +mix py.install +``` + +Then, with the Elixir dev server running in one terminal, start the Python workers in another: + +```bash +mix py.dev +``` + +Alternatively, run directly from the py directory: + +```bash +cd py && uv run py-dev +``` + ### Testing with Oban Pro Oban Pro is an optional dependency for development and testing. The test suite will automatically diff --git a/vendor/oban_web/hex_metadata.config b/vendor/oban_web/hex_metadata.config index 98e3b2ae..41484ccd 100644 --- a/vendor/oban_web/hex_metadata.config +++ b/vendor/oban_web/hex_metadata.config @@ -4,16 +4,20 @@ <<"https://github.com/oban-bg/oban_web/blob/main/CHANGELOG.md">>}, {<<"GitHub">>,<<"https://github.com/oban-bg/oban_web">>}]}. {<<"name">>,<<"oban_web">>}. -{<<"version">>,<<"2.11.8">>}. -{<<"description">>,<<"Dashboard for the Oban job processing framework">>}. +{<<"version">>,<<"2.12.1">>}. +{<<"description">>,<<"Dashboard for the Oban job orchestration framework">>}. {<<"elixir">>,<<"~> 1.15">>}. {<<"files">>, [<<"lib">>,<<"lib/mix">>,<<"lib/mix/tasks">>, <<"lib/mix/tasks/oban_web.install.ex">>,<<"lib/oban">>,<<"lib/oban/web">>, - <<"lib/oban/web/dashboard_live.ex">>,<<"lib/oban/web/page.ex">>, - <<"lib/oban/web/telemetry.ex">>,<<"lib/oban/web/cache.ex">>, - <<"lib/oban/web/plugins">>,<<"lib/oban/web/plugins/stats.ex">>, - <<"lib/oban/web/queue.ex">>,<<"lib/oban/web/queries">>, + <<"lib/oban/web/cron.ex">>,<<"lib/oban/web/dashboard_live.ex">>, + <<"lib/oban/web/page.ex">>,<<"lib/oban/web/telemetry.ex">>, + <<"lib/oban/web/cache.ex">>,<<"lib/oban/web/plugins">>, + <<"lib/oban/web/plugins/stats.ex">>,<<"lib/oban/web/queue.ex">>, + <<"lib/oban/web/metrics.ex">>,<<"lib/oban/web/queries">>, + <<"lib/oban/web/queries/query_helpers.ex">>, + <<"lib/oban/web/queries/workflow_query.ex">>, + <<"lib/oban/web/queries/cron_query.ex">>, <<"lib/oban/web/queries/job_query.ex">>, <<"lib/oban/web/queries/queue_query.ex">>,<<"lib/oban/web/router.ex">>, <<"lib/oban/web/components">>,<<"lib/oban/web/components/sort.ex">>, @@ -24,30 +28,115 @@ <<"lib/oban/web/components/layouts">>, <<"lib/oban/web/components/layouts/live.html.heex">>, <<"lib/oban/web/components/layouts/root.html.heex">>, + <<"lib/oban/web/components/form_components.ex">>, + <<"lib/oban/web/workflow.ex">>,<<"lib/oban/web/timezones.ex">>, <<"lib/oban/web/live">>,<<"lib/oban/web/live/search_component.ex">>, + <<"lib/oban/web/live/workflows">>, + <<"lib/oban/web/live/workflows/table_component.ex">>, + <<"lib/oban/web/live/workflows/detail_component.ex">>, <<"lib/oban/web/live/shortcuts_component.ex">>, <<"lib/oban/web/live/theme_component.ex">>,<<"lib/oban/web/live/queues">>, <<"lib/oban/web/live/queues/table_component.ex">>, - <<"lib/oban/web/live/queues/sidebar_component.ex">>, <<"lib/oban/web/live/queues/detail_component.ex">>, <<"lib/oban/web/live/queues/detail_instance_component.ex">>, - <<"lib/oban/web/live/jobs">>, + <<"lib/oban/web/live/help_component.ex">>,<<"lib/oban/web/live/crons">>, + <<"lib/oban/web/live/crons/table_component.ex">>, + <<"lib/oban/web/live/crons/new_component.ex">>, + <<"lib/oban/web/live/crons/detail_component.ex">>, + <<"lib/oban/web/live/crons/helpers.ex">>,<<"lib/oban/web/live/jobs">>, <<"lib/oban/web/live/jobs/table_component.ex">>, <<"lib/oban/web/live/jobs/sidebar_component.ex">>, + <<"lib/oban/web/live/jobs/new_component.ex">>, <<"lib/oban/web/live/jobs/detail_component.ex">>, + <<"lib/oban/web/live/jobs/history_chart_component.ex">>, <<"lib/oban/web/live/jobs/timeline_component.ex">>, <<"lib/oban/web/live/jobs/chart_component.ex">>, <<"lib/oban/web/live/connectivity_component.ex">>, <<"lib/oban/web/live/instances_component.ex">>, <<"lib/oban/web/live/refresh_component.ex">>,<<"lib/oban/web/helpers.ex">>, - <<"lib/oban/web/application.ex">>,<<"lib/oban/web/timing.ex">>, - <<"lib/oban/web/pages">>,<<"lib/oban/web/pages/jobs_page.ex">>, - <<"lib/oban/web/pages/queues_page.ex">>,<<"lib/oban/web/resolver.ex">>, + <<"lib/oban/web/colors.ex">>,<<"lib/oban/web/cron_expr.ex">>, + <<"lib/oban/web/application.ex">>,<<"lib/oban/web/utils.ex">>, + <<"lib/oban/web/timing.ex">>,<<"lib/oban/web/pages">>, + <<"lib/oban/web/pages/jobs_page.ex">>, + <<"lib/oban/web/pages/crons_page.ex">>, + <<"lib/oban/web/pages/queues_page.ex">>, + <<"lib/oban/web/pages/workflows_page.ex">>,<<"lib/oban/web/resolver.ex">>, <<"lib/oban/web/authentication.ex">>,<<"lib/oban/web/helpers">>, <<"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.ex">>,<<"priv/static">>,<<"priv/static/app.css">>, - <<"priv/static/app.js">>,<<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>, + <<"priv/static/icons">>,<<"priv/static/icons/solid">>, + <<"priv/static/icons/solid/pause-circle.svg">>, + <<"priv/static/icons/solid/play-circle.svg">>, + <<"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">>}. {<<"licenses">>,[<<"Apache-2.0">>]}. @@ -80,11 +169,11 @@ [{<<"name">>,<<"oban">>}, {<<"app">>,<<"oban">>}, {<<"optional">>,false}, - {<<"requirement">>,<<"~> 2.19">>}, + {<<"requirement">>,<<"~> 2.21">>}, {<<"repository">>,<<"hexpm">>}], [{<<"name">>,<<"oban_met">>}, {<<"app">>,<<"oban_met">>}, {<<"optional">>,false}, - {<<"requirement">>,<<"~> 1.0">>}, + {<<"requirement">>,<<"~> 1.1">>}, {<<"repository">>,<<"hexpm">>}]]}. {<<"build_tools">>,[<<"mix">>]}. diff --git a/vendor/oban_web/lib/oban/web/assets.ex b/vendor/oban_web/lib/oban/web/assets.ex index 2c0dbbf8..d523b6b8 100644 --- a/vendor/oban_web/lib/oban/web/assets.ex +++ b/vendor/oban_web/lib/oban/web/assets.ex @@ -5,6 +5,36 @@ defmodule Oban.Web.Assets do import Plug.Conn + @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 + + @external_resource font_path = Path.join(@static_path, "fonts/Inter.woff2") + + @font File.read!(font_path) + + # CSS + + @external_resource css_path = Path.join(@static_path, "app.css") + + @css File.read!(css_path) + + # JS + phoenix_js_paths = for app <- ~w(phoenix phoenix_html phoenix_live_view)a do path = Application.app_dir(app, ["priv", "static", "#{app}.js"]) @@ -12,13 +42,8 @@ defmodule Oban.Web.Assets do path end - @static_path Application.app_dir(:oban_web, ["priv", "static"]) - - @external_resource css_path = Path.join(@static_path, "app.css") @external_resource js_path = Path.join(@static_path, "app.js") - @css File.read!(css_path) - @js """ #{for path <- phoenix_js_paths, do: path |> File.read!() |> String.replace("//# sourceMappingURL=", "// ")} #{File.read!(js_path)} @@ -28,9 +53,34 @@ defmodule Oban.Web.Assets do def init(asset), do: asset @impl Plug - def call(conn, asset) do - {contents, content_type} = contents_and_type(asset) + def call(conn, :css) do + serve_asset(conn, @css, "text/css") + end + def call(conn, :js) do + serve_asset(conn, @js, "text/javascript") + end + + def call(conn, :font) do + serve_asset(conn, @font, "font/woff2") + 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 conn |> put_resp_header("content-type", content_type) |> put_resp_header("cache-control", "public, max-age=31536000, immutable") @@ -39,9 +89,6 @@ defmodule Oban.Web.Assets do |> halt() end - defp contents_and_type(:css), do: {@css, "text/css"} - defp contents_and_type(:js), do: {@js, "text/javascript"} - for {key, val} <- [css: @css, js: @js] do md5 = Base.encode16(:crypto.hash(:md5, val), case: :lower) diff --git a/vendor/oban_web/lib/oban/web/colors.ex b/vendor/oban_web/lib/oban/web/colors.ex new file mode 100644 index 00000000..446571a9 --- /dev/null +++ b/vendor/oban_web/lib/oban/web/colors.ex @@ -0,0 +1,93 @@ +defmodule Oban.Web.Colors do + @moduledoc false + + # Hex colors for SVGs and inline styles (must match assets/js/lib/colors.js) + @hex %{ + blue: "#60a5fa", + cyan: "#22d3ee", + emerald: "#34d399", + gray: "#9ca3af", + indigo: "#818cf8", + rose: "#fb7185", + violet: "#a78bfa", + yellow: "#facc15" + } + + # State to color name mapping + @state_color_names %{ + "suspended" => :gray, + "scheduled" => :indigo, + "available" => :blue, + "retryable" => :yellow, + "executing" => :emerald, + "completed" => :cyan, + "cancelled" => :violet, + "discarded" => :rose + } + + # Tailwind classes per state for templates + @state_classes %{ + "suspended" => {"border-gray-400", "bg-gray-400/10", "text-gray-600 dark:text-gray-400"}, + "scheduled" => + {"border-indigo-400", "bg-indigo-400/10", "text-indigo-600 dark:text-indigo-400"}, + "available" => {"border-blue-400", "bg-blue-400/10", "text-blue-600 dark:text-blue-400"}, + "retryable" => + {"border-yellow-400", "bg-yellow-400/10", "text-yellow-600 dark:text-yellow-400"}, + "executing" => + {"border-emerald-400", "bg-emerald-400/10", "text-emerald-600 dark:text-emerald-400"}, + "completed" => {"border-cyan-400", "bg-cyan-400/10", "text-cyan-600 dark:text-cyan-400"}, + "cancelled" => + {"border-violet-400", "bg-violet-400/10", "text-violet-600 dark:text-violet-400"}, + "discarded" => {"border-rose-400", "bg-rose-400/10", "text-rose-600 dark:text-rose-400"} + } + + @inactive_classes { + "border-gray-300 dark:border-gray-600", + "bg-gray-100 dark:bg-gray-800", + "text-gray-400 dark:text-gray-500" + } + + @doc """ + Returns the hex color for a state (for SVGs and inline styles). + """ + def state_hex(state) when is_binary(state) do + color_name = Map.get(@state_color_names, state, :gray) + Map.fetch!(@hex, color_name) + end + + def state_hex(_state), do: Map.fetch!(@hex, :gray) + + @doc """ + Returns Tailwind classes for a state: {border, background, text}. + """ + def state_classes(state) when is_binary(state) do + Map.get(@state_classes, state, @inactive_classes) + end + + def state_classes(_state), do: @inactive_classes + + @doc """ + Returns Tailwind classes for inactive state. + """ + def inactive_classes, do: @inactive_classes + + @doc """ + Returns the background class for a state (e.g., "bg-cyan-400"). + """ + def state_bg_class(state) when is_binary(state) do + color_name = Map.get(@state_color_names, state, :gray) + "bg-#{color_name}-400" + end + + def state_bg_class(_state), do: "bg-gray-400" + + @doc """ + Returns the text class for a state (e.g., "text-cyan-400"). + """ + def state_text_class(state) when is_binary(state) do + color_name = Map.get(@state_color_names, state, :gray) + "text-#{color_name}-400" + end + + def state_text_class(_state), do: "text-gray-400" +end diff --git a/vendor/oban_web/lib/oban/web/components/core.ex b/vendor/oban_web/lib/oban/web/components/core.ex index 109a2e94..80fbc0f7 100644 --- a/vendor/oban_web/lib/oban/web/components/core.ex +++ b/vendor/oban_web/lib/oban/web/components/core.ex @@ -41,65 +41,6 @@ defmodule Oban.Web.Components.Core do """ end - @doc """ - A numerical input with increment and decrement buttons. - """ - def number_input(assigns) do - ~H""" -
- <%= if @label do %> - - <% end %> - -
- - -
- - - -
-
-
- """ - end - slot :inner_block, required: true attr :name, :string, required: true attr :options, :list, required: true @@ -114,7 +55,7 @@ defmodule Oban.Web.Components.Core do """ @@ -231,14 +172,253 @@ defmodule Oban.Web.Components.Core do phx-target={@myself} type="button" > -
+
<%= if @checked == :some do %> - + <% else %> - + <% end %>
""" end + + @doc """ + A status badge with icon that expands to show label on hover. + """ + attr :icon, :string, required: true + attr :id, :string, default: nil + attr :label, :string, required: true + + def status_badge(assigns) do + ~H""" +
+ + <.badge_icon name={@icon} /> + + {@label} + + +
+ """ + end + + defp badge_icon(%{name: "camera"} = assigns), do: ~H[] + + defp badge_icon(%{name: "life_buoy"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "lock_closed"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "sparkles"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "table_cells"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "square_2x2"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "rectangle_group"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "user_group"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "link"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "power"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "pause_circle"} = assigns), + do: ~H[] + + defp badge_icon(%{name: "play_pause_circle"} = assigns), + do: ~H[] + + @doc """ + An icon-only button that expands to show label on hover. Supports disabled state. + """ + attr :id, :string, required: true + attr :icon, :string, required: true + attr :label, :string, required: true + attr :color, :string, required: true + attr :disabled, :boolean, default: false + attr :confirm, :string, default: nil + attr :tooltip, :string, default: nil + attr :rest, :global + + def icon_button(assigns) do + color_classes = + case {assigns.color, assigns.disabled} do + {_, true} -> + "text-gray-400 dark:text-gray-500 bg-gray-100 dark:bg-gray-800 border-gray-200 dark:border-gray-700 cursor-not-allowed" + + {"yellow", false} -> + "text-gray-600 dark:text-gray-400 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-700 cursor-pointer hover:text-yellow-600 hover:border-yellow-600" + + {"blue", false} -> + "text-gray-600 dark:text-gray-400 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-700 cursor-pointer hover:text-blue-500 hover:border-blue-600" + + {"red", false} -> + "text-gray-600 dark:text-gray-400 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-700 cursor-pointer hover:text-red-500 hover:border-red-600" + + {"violet", false} -> + "text-gray-600 dark:text-gray-400 bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-700 cursor-pointer hover:text-violet-500 hover:border-violet-600" + end + + icon_color = + if assigns.disabled do + "text-gray-400 dark:text-gray-500" + else + case assigns.color do + "yellow" -> "text-gray-500 group-hover:text-yellow-500" + "blue" -> "text-gray-500 group-hover:text-blue-500" + "red" -> "text-gray-500 group-hover:text-red-500" + "violet" -> "text-gray-500 group-hover:text-violet-500" + end + end + + assigns = assign(assigns, color_classes: color_classes, icon_color: icon_color) + + ~H""" + + """ + end + + defp button_icon(%{name: "arrow_path"} = assigns), do: ~H[] + + defp button_icon(%{name: "pause_circle"} = assigns), + do: ~H[] + + defp button_icon(%{name: "pencil_square"} = assigns), + do: ~H[] + + defp button_icon(%{name: "play_circle"} = assigns), do: ~H[] + defp button_icon(%{name: "trash"} = assigns), do: ~H[] + defp button_icon(%{name: "x_circle"} = assigns), do: ~H[] + + # Sparkline + + attr :id, :string, required: true + attr :history, :map, required: true + attr :max_value, :integer, default: nil + attr :bar_width, :integer, default: 4 + attr :count, :integer, default: 60 + attr :gap, :integer, default: 1 + attr :height, :integer, default: 16 + attr :class, :string, default: nil + + def sparkline(assigns) do + history = assigns.history + count = assigns.count + bar_width = assigns.bar_width + gap = assigns.gap + height = assigns.height + max_index = count - 1 + + max_value = + if assigns.max_value do + max(assigns.max_value, 1) + else + history + |> Map.values() + |> Enum.reduce(1, fn %{count: c}, acc -> max(c, acc) end) + end + + now = System.system_time(:millisecond) + + {bars, tooltip_data} = + for slot <- 0..max_index, reduce: {[], []} do + {bars_acc, tool_acc} -> + index = max_index - slot + timestamp = now - index * 5 * 1000 + x = slot * (bar_width + gap) + + case Map.get(history, index) do + %{count: c} -> + bar_height = min(c / max_value, 1.0) * height + bar = %{x: x, height: max(bar_height, 0)} + tooltip = %{timestamp: timestamp, count: c} + + {[bar | bars_acc], [tooltip | tool_acc]} + + nil -> + tooltip = %{timestamp: timestamp, count: 0} + + {bars_acc, [tooltip | tool_acc]} + end + end + + bars = Enum.reverse(bars) + tooltip_data = Enum.reverse(tooltip_data) + + placeholders = + for slot <- 0..max_index do + %{x: slot * (bar_width + gap)} + end + + width = count * (bar_width + gap) + + assigns = + assigns + |> assign(bars: bars, placeholders: placeholders, width: width) + |> assign(tooltip_data: tooltip_data) + + ~H""" + + + + + """ + end end diff --git a/vendor/oban_web/lib/oban/web/components/form_components.ex b/vendor/oban_web/lib/oban/web/components/form_components.ex new file mode 100644 index 00000000..84abf97c --- /dev/null +++ b/vendor/oban_web/lib/oban/web/components/form_components.ex @@ -0,0 +1,192 @@ +defmodule Oban.Web.FormComponents do + @moduledoc false + + use Oban.Web, :html + + attr :label, :string, required: true + attr :name, :string, required: true + attr :value, :any, default: nil + attr :type, :string, default: "text" + attr :colspan, :string, default: nil + attr :placeholder, :string, default: nil + attr :required, :boolean, default: false + attr :disabled, :boolean, default: false + attr :hint, :string, default: nil + attr :rows, :integer, default: 2 + + def form_field(assigns) do + ~H""" +
+ + <%= if @type == "textarea" do %> + + <% else %> + + <% end %> +
+ """ + end + + attr :label, :string, required: true + attr :name, :string, required: true + attr :value, :any, default: nil + attr :options, :list, required: true + attr :disabled, :boolean, default: false + + def select_field(assigns) do + ~H""" +
+ + +
+ """ + end + + attr :label, :string, required: true + attr :name, :string, required: true + attr :checked, :boolean, default: false + attr :disabled, :boolean, default: false + attr :hint, :string, default: nil + attr :colspan, :string, default: nil + + def checkbox_field(assigns) do + ~H""" +
+ + +
+ """ + end + + # Form Parsing Helpers + + @doc """ + Parses and trims a string, returning nil for empty values. + """ + def parse_string(nil), do: nil + def parse_string(""), do: nil + def parse_string(str), do: String.trim(str) + + @doc """ + Parses a string to an integer, returning nil for empty or invalid values. + """ + def parse_int(nil), do: nil + def parse_int(""), do: nil + + def parse_int(str) when is_binary(str) do + case Integer.parse(str) do + {num, ""} -> num + _ -> nil + end + end + + @doc """ + Parses a comma-separated string into a list of trimmed tags. Returns nil for empty input. + """ + def parse_tags(nil), do: nil + def parse_tags(""), do: nil + + def parse_tags(str) when is_binary(str) do + str + |> String.split(",") + |> Enum.map(&String.trim/1) + |> Enum.reject(&(&1 == "")) + |> case do + [] -> nil + tags -> tags + end + end + + @doc """ + Parses a JSON string into a map, returning nil for empty or invalid values. + """ + def parse_json(nil), do: nil + def parse_json(""), do: nil + + def parse_json(str) when is_binary(str) do + case Oban.JSON.decode!(str) do + map when is_map(map) -> map + _ -> nil + end + rescue + _ -> nil + end + + @doc """ + Builds queue options from a list of queue structs. + """ + def queue_options(queues) do + queues + |> Enum.map(fn %{name: name} -> {name, name} end) + |> Enum.sort_by(&elem(&1, 0)) + end +end diff --git a/vendor/oban_web/lib/oban/web/components/icons.ex b/vendor/oban_web/lib/oban/web/components/icons.ex index 24d04234..98997ba2 100644 --- a/vendor/oban_web/lib/oban/web/components/icons.ex +++ b/vendor/oban_web/lib/oban/web/components/icons.ex @@ -1,670 +1,64 @@ defmodule Oban.Web.Components.Icons do - use Oban.Web, :html + @moduledoc false - # Helpers + use Phoenix.Component - attr :rest, :global, - default: %{ - "aria-hidden": "true", - class: "w-4 h-4", - fill: "currentColor", - viewBox: "0 0 16 16" - } + @icons_path Application.app_dir(:oban_web, ["priv", "static", "icons"]) - slot :inner_block, required: true + @outline_path Path.join(@icons_path, "outline") + @solid_path Path.join(@icons_path, "solid") - defp svg_mini(assigns) do - ~H""" - - {render_slot(@inner_block)} - - """ + @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 - attr :rest, :global, - default: %{ - "stroke-width": "1.5", - class: "w-6 h-6", - fill: "none", - stroke: "currentColor", - viewBox: "0 0 24 24" - } + for icon <- @solid_icons do + @external_resource Path.join(@solid_path, "#{icon}.svg") - slot :inner_block, required: true + func_name = + icon + |> String.replace("-", "_") + |> Kernel.<>("_solid") + |> String.to_atom() - defp svg_outline(assigns) do - ~H""" - - {render_slot(@inner_block)} - - """ + @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 - attr :rest, :global, - default: %{ - "aria-hidden": "true", - class: "w-6 h-6", - fill: "currentColor", - viewBox: "0 0 24 24" - } + # Inline Icons - slot :inner_block, required: true + attr :rest, :global, default: %{class: "w-6 h-6"} - defp svg_solid(assigns) do - ~H""" - - {render_slot(@inner_block)} - - """ - end - - # Icons - - attr :rest, :global - - def adjustments_horizontal(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def adjustments_vertical(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def arrow_left(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def arrow_path(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def arrow_path_rounded(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def arrow_right_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def arrow_top_right_on_square(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def arrow_trending_down(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def calendar_days(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def crossbones_circle(assigns) do - ~H""" - <.svg_solid {@rest}> - - - """ - end - - attr :rest, :global - - def bars_arrow_down(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def bars_arrow_up(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def bolt_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def bolt_slash(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def camera(assigns) do - ~H""" - <.svg_outline {@rest}> - - - - """ - end - - attr :rest, :global - - def chart_bar_square(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def check(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def indeterminate(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def check_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def chevron_down(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def chevron_right(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def chevron_up(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def clock(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def cog(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def command_line(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def computer_desktop(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def exclamation_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def globe(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def info_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def hashtag(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def life_buoy(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def lock_closed(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def map_pin(assigns) do - ~H""" - <.svg_outline {@rest}> - - - - """ - end - - attr :rest, :global - - def magnifying_glass(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def minus_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def moon(assigns) do - ~H""" - <.svg_outline {@rest}> - - - - """ - end - - attr :rest, :global - - def no_symbol(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def pause_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def pause_circle_solid(assigns) do - ~H""" - <.svg_solid {@rest}> - - - """ - end - - attr :rest, :global - - def percent_square(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def play_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - - """ - end - - attr :rest, :global - - def play_circle_solid(assigns) do - ~H""" - <.svg_solid {@rest}> - - - """ - end - - attr :rest, :global - - def play_pause_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def plus_circle(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def power(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def soren_logo(assigns) do + def oban_pro_logo(assigns) do ~H""" @@ -722,156 +116,47 @@ defmodule Oban.Web.Components.Icons do """ end - attr :rest, :global - - def square_stack(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def square_2x2(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global - - def rectangle_group(assigns) do - ~H""" - <.svg_outline {@rest}> - - - """ - end - - attr :rest, :global + attr :rest, :global, default: %{class: "w-6 h-6"} def spinner(assigns) do ~H""" - - - + + + """ end - attr :rest, :global + 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) - def table_cells(assigns) do ~H""" - <.svg_outline {@rest}> - - +