From 0453acc4dd40910fb645c731df0c4b80cdc9dd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Fri, 28 Jun 2024 19:18:38 +0200 Subject: [PATCH 01/22] Add base web interfaces --- lib/error_tracker/web.ex | 42 +++++++++++++++++++ lib/error_tracker/web/components/layouts.ex | 5 +++ .../web/components/layouts/live.html.heex | 3 ++ .../web/components/layouts/root.html.heex | 15 +++++++ 4 files changed, 65 insertions(+) create mode 100644 lib/error_tracker/web.ex create mode 100644 lib/error_tracker/web/components/layouts.ex create mode 100644 lib/error_tracker/web/components/layouts/live.html.heex create mode 100644 lib/error_tracker/web/components/layouts/root.html.heex diff --git a/lib/error_tracker/web.ex b/lib/error_tracker/web.ex new file mode 100644 index 0000000..f343a49 --- /dev/null +++ b/lib/error_tracker/web.ex @@ -0,0 +1,42 @@ +defmodule ErrorTracker.Web do + @moduledoc false + + def html do + quote do + import Phoenix.Controller, only: [get_csrf_token: 0] + + unquote(html_helpers()) + end + end + + def live_view do + quote do + use Phoenix.LiveView, layout: {ErrorTracker.Web.Layouts, :live} + + unquote(html_helpers()) + end + end + + def live_component do + quote do + use Phoenix.LiveComponent + + unquote(html_helpers()) + end + end + + defp html_helpers do + quote do + use Phoenix.Component + + import Phoenix.HTML + import Phoenix.LiveView.Helpers + + alias Phoenix.LiveView.JS + end + end + + defmacro __using__(which) when is_atom(which) do + apply(__MODULE__, which, []) + end +end diff --git a/lib/error_tracker/web/components/layouts.ex b/lib/error_tracker/web/components/layouts.ex new file mode 100644 index 0000000..86ed657 --- /dev/null +++ b/lib/error_tracker/web/components/layouts.ex @@ -0,0 +1,5 @@ +defmodule ErrorTracker.Web.Layouts do + use ErrorTracker.Web, :html + + embed_templates "layouts/*" +end diff --git a/lib/error_tracker/web/components/layouts/live.html.heex b/lib/error_tracker/web/components/layouts/live.html.heex new file mode 100644 index 0000000..c753bc6 --- /dev/null +++ b/lib/error_tracker/web/components/layouts/live.html.heex @@ -0,0 +1,3 @@ +
+ <%= @inner_content %> +
diff --git a/lib/error_tracker/web/components/layouts/root.html.heex b/lib/error_tracker/web/components/layouts/root.html.heex new file mode 100644 index 0000000..9cca1e4 --- /dev/null +++ b/lib/error_tracker/web/components/layouts/root.html.heex @@ -0,0 +1,15 @@ + + + + + + + + + <%= assigns[:page_title] || "ErrorTracker" %> + + + + <%= @inner_content %> + + From 155d54564fd8bb8d6da437d68528ab9826408f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Fri, 28 Jun 2024 19:19:10 +0200 Subject: [PATCH 02/22] WIP Dashboard interface --- lib/error_tracker/web/live/dashboard.ex | 20 +++++++++++++++++++ .../web/live/dashboard.html.heex | 1 + 2 files changed, 21 insertions(+) create mode 100644 lib/error_tracker/web/live/dashboard.ex create mode 100644 lib/error_tracker/web/live/dashboard.html.heex diff --git a/lib/error_tracker/web/live/dashboard.ex b/lib/error_tracker/web/live/dashboard.ex new file mode 100644 index 0000000..bf76f7d --- /dev/null +++ b/lib/error_tracker/web/live/dashboard.ex @@ -0,0 +1,20 @@ +defmodule ErrorTracker.Web.Live.Dashboard do + use ErrorTracker.Web, :live_view + + @impl Phoenix.LiveView + def mount(_params, _session, socket) do + {:ok, socket} + end + + @impl Phoenix.LiveView + def terminate(_reason, %{assigns: %{timer: timer}}) do + if is_reference(timer), do: Process.cancel_timer(timer) + + :ok + end + + @impl Phoenix.LiveView + def handle_params(_params, _uri, socket) do + {:noreply, socket} + end +end diff --git a/lib/error_tracker/web/live/dashboard.html.heex b/lib/error_tracker/web/live/dashboard.html.heex new file mode 100644 index 0000000..cd08755 --- /dev/null +++ b/lib/error_tracker/web/live/dashboard.html.heex @@ -0,0 +1 @@ +Hello world! From c3e1f1bdd6860ef45670f23c3b18de1893c37c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Fri, 28 Jun 2024 19:19:23 +0200 Subject: [PATCH 03/22] Router helper and use it in dev --- dev.exs | 4 ++++ lib/error_tracker/web/router.ex | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 lib/error_tracker/web/router.ex diff --git a/dev.exs b/dev.exs index b02d8e6..05e7eb0 100644 --- a/dev.exs +++ b/dev.exs @@ -95,6 +95,8 @@ end defmodule ErrorTrackerDevWeb.Router do use Phoenix.Router + import ErrorTracker.Web.Router + pipeline :browser do plug :fetch_session plug :protect_from_forgery @@ -106,6 +108,8 @@ defmodule ErrorTrackerDevWeb.Router do get "/noroute", ErrorTrackerDevWeb.PageController, :noroute get "/exception", ErrorTrackerDevWeb.PageController, :exception get "/exit", ErrorTrackerDevWeb.PageController, :exit + + error_tracker_dashboard("/error_tracker") end end diff --git a/lib/error_tracker/web/router.ex b/lib/error_tracker/web/router.ex new file mode 100644 index 0000000..52136df --- /dev/null +++ b/lib/error_tracker/web/router.ex @@ -0,0 +1,35 @@ +defmodule ErrorTracker.Web.Router do + @moduledoc false + + @doc """ + Creates the routes needed to use the `ErrorTracker` web interface. + + It requires a path in which you are going to serve the web interface. + """ + defmacro error_tracker_dashboard(path, opts \\ []) do + {session_name, session_opts} = parse_options(opts) + + quote do + scope unquote(path), alias: false, as: false do + import Phoenix.LiveView.Router, only: [live: 4, live_session: 3] + + live_session unquote(session_name), unquote(session_opts) do + live "/", ErrorTracker.Web.Live.Dashboard, :index, as: unquote(session_name) + end + end + end + end + + @doc false + def parse_options(opts) do + on_mount = Keyword.get(opts, :on_mount, []) + session_name = Keyword.get(opts, :as, :error_tracker_dashboard) + + session_opts = [ + on_mount: on_mount, + root_layout: {ErrorTracker.Web.Layouts, :root} + ] + + {session_name, session_opts} + end +end From 4ab26c88200852c297fcdedb157a19891647e1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Fri, 28 Jun 2024 19:20:41 +0200 Subject: [PATCH 04/22] Update route --- dev.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev.exs b/dev.exs index 05e7eb0..1cd1541 100644 --- a/dev.exs +++ b/dev.exs @@ -109,7 +109,7 @@ defmodule ErrorTrackerDevWeb.Router do get "/exception", ErrorTrackerDevWeb.PageController, :exception get "/exit", ErrorTrackerDevWeb.PageController, :exit - error_tracker_dashboard("/error_tracker") + error_tracker_dashboard("/errors") end end From 41b202f97e945e44bdbf6297cc070c4f15ac6d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Fri, 28 Jun 2024 19:22:11 +0200 Subject: [PATCH 05/22] Happy credo --- lib/error_tracker/web/live/dashboard.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/error_tracker/web/live/dashboard.ex b/lib/error_tracker/web/live/dashboard.ex index bf76f7d..2bc4fe6 100644 --- a/lib/error_tracker/web/live/dashboard.ex +++ b/lib/error_tracker/web/live/dashboard.ex @@ -1,4 +1,6 @@ defmodule ErrorTracker.Web.Live.Dashboard do + @moduledoc false + use ErrorTracker.Web, :live_view @impl Phoenix.LiveView From 9856d4e1fb9459aaf3801dddc7d90c503b424d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 11:27:57 +0200 Subject: [PATCH 06/22] Tailwind install --- assets/css/app.css | 4 ++++ assets/tailwind.config.js | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 assets/css/app.css create mode 100644 assets/tailwind.config.js diff --git a/assets/css/app.css b/assets/css/app.css new file mode 100644 index 0000000..71a77f0 --- /dev/null +++ b/assets/css/app.css @@ -0,0 +1,4 @@ +@import "tailwindcss/base"; +@import "tailwindcss/components"; +@import "tailwindcss/utilities"; + diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js new file mode 100644 index 0000000..1447c42 --- /dev/null +++ b/assets/tailwind.config.js @@ -0,0 +1,22 @@ +// See the Tailwind configuration guide for advanced usage +// https://tailwindcss.com/docs/configuration + +let plugin = require('tailwindcss/plugin') + +module.exports = { + content: [ + './js/**/*.js', + '../lib/error_tracker/web.ex', + '../lib/error_tracker/web/**/*.*ex' + ], + theme: { + extend: {}, + }, + plugins: [ + require('@tailwindcss/forms'), + plugin(({addVariant}) => addVariant('phx-no-feedback', ['&.phx-no-feedback', '.phx-no-feedback &'])), + plugin(({addVariant}) => addVariant('phx-click-loading', ['&.phx-click-loading', '.phx-click-loading &'])), + plugin(({addVariant}) => addVariant('phx-submit-loading', ['&.phx-submit-loading', '.phx-submit-loading &'])), + plugin(({addVariant}) => addVariant('phx-change-loading', ['&.phx-change-loading', '.phx-change-loading &'])) + ] +} From 2ad8c725e2d112c377e51749852c4d38fba67e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 11:28:04 +0200 Subject: [PATCH 07/22] Add esbuild --- mix.exs | 4 +++- mix.lock | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 00ee6a7..3e085f1 100644 --- a/mix.exs +++ b/mix.exs @@ -51,6 +51,7 @@ defmodule ErrorTracker.MixProject do {:postgrex, ">= 0.0.0"}, # Dev dependencies {:credo, "~> 1.7", only: [:dev, :test]}, + {:esbuild, "~> 0.8", only: :dev}, {:ex_doc, "~> 0.33", only: :dev}, {:phoenix_live_reload, ">= 0.0.0", only: :dev}, {:plug_cowboy, ">= 0.0.0", only: :dev}, @@ -62,7 +63,8 @@ defmodule ErrorTracker.MixProject do [ setup: ["deps.get", "cmd --cd assets npm install"], dev: "run --no-halt dev.exs", - "assets.build": ["esbuild default --minify"] + "assets.watch": ["tailwind default --watch"], + "assets.build": ["esbuild default --minify", "tailwind default --minify"] ] end end diff --git a/mix.lock b/mix.lock index b24680c..b18e732 100644 --- a/mix.lock +++ b/mix.lock @@ -10,6 +10,7 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "ecto_sql": {:hex, :ecto_sql, "3.11.2", "c7cc7f812af571e50b80294dc2e535821b3b795ce8008d07aa5f336591a185a8", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "73c07f995ac17dbf89d3cfaaf688fcefabcd18b7b004ac63b0dc4ef39499ed6b"}, + "esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"}, "ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, From b51c9c843d9bf1cd11c9ad2e793fe2f6b582ca7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 11:28:18 +0200 Subject: [PATCH 08/22] Configure assets building --- .gitignore | 3 +++ assets/js/app.js | 0 config/config.exs | 10 +++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 assets/js/app.js diff --git a/.gitignore b/.gitignore index 28dc7b0..6956b31 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,7 @@ error_tracker-*.tar # Temporary files, for example, from tests. /tmp/ +/priv/static/app.js +/priv/static/app.css + dev.local.exs diff --git a/assets/js/app.js b/assets/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/config/config.exs b/config/config.exs index 0845f95..88d4312 100644 --- a/config/config.exs +++ b/config/config.exs @@ -7,8 +7,16 @@ if config_env() == :dev do args: ~w( --config=tailwind.config.js --input=css/app.css - --output=../priv/static/assets/app.css + --output=../priv/static/app.css ), cd: Path.expand("../assets", __DIR__) ] + + config :esbuild, + version: "0.21.5", + default: [ + args: ~w(app.js --bundle --target=es2016 --outdir=../../priv/static), + cd: Path.expand("../assets/js", __DIR__), + env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} + ] end From 686b6d2b356f615ace5dd6c75ef004f35c664e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 11:28:25 +0200 Subject: [PATCH 09/22] Test adding a class --- lib/error_tracker/web/live/dashboard.html.heex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/error_tracker/web/live/dashboard.html.heex b/lib/error_tracker/web/live/dashboard.html.heex index cd08755..4a4319b 100644 --- a/lib/error_tracker/web/live/dashboard.html.heex +++ b/lib/error_tracker/web/live/dashboard.html.heex @@ -1 +1 @@ -Hello world! +

Hello world!

From 69c5262d0cab2b9994d71ba08e9574b00d582d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 11:55:57 +0200 Subject: [PATCH 10/22] Read assets from disk and inject it on the HTML --- lib/error_tracker/web/components/layouts.ex | 6 ++++++ lib/error_tracker/web/components/layouts/root.html.heex | 3 +++ 2 files changed, 9 insertions(+) diff --git a/lib/error_tracker/web/components/layouts.ex b/lib/error_tracker/web/components/layouts.ex index 86ed657..472b256 100644 --- a/lib/error_tracker/web/components/layouts.ex +++ b/lib/error_tracker/web/components/layouts.ex @@ -1,5 +1,11 @@ defmodule ErrorTracker.Web.Layouts do use ErrorTracker.Web, :html + @css_path :code.priv_dir(:error_tracker) |> Path.join("static/app.css") + @js_path :code.priv_dir(:error_tracker) |> Path.join("static/app.js") + embed_templates "layouts/*" + + def get_content(:css), do: File.read!(@css_path) + def get_content(:js), do: File.read!(@js_path) end diff --git a/lib/error_tracker/web/components/layouts/root.html.heex b/lib/error_tracker/web/components/layouts/root.html.heex index 9cca1e4..8d87fbd 100644 --- a/lib/error_tracker/web/components/layouts/root.html.heex +++ b/lib/error_tracker/web/components/layouts/root.html.heex @@ -7,6 +7,9 @@ <%= assigns[:page_title] || "ErrorTracker" %> + + + From 9f16ff78b665e30e5fdc27946dcd3e77f52c510e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 12:57:09 +0200 Subject: [PATCH 11/22] Move to bun --- .gitignore | 2 ++ assets/bun.lockb | Bin 0 -> 3607 bytes assets/js/app.js | 16 ++++++++++++++++ assets/package.json | 10 ++++++++++ config/config.exs | 8 ++++---- mix.exs | 4 ++-- mix.lock | 2 +- 7 files changed, 35 insertions(+), 7 deletions(-) create mode 100755 assets/bun.lockb create mode 100644 assets/package.json diff --git a/.gitignore b/.gitignore index 6956b31..21d1524 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ error_tracker-*.tar # Temporary files, for example, from tests. /tmp/ +/assets/node_modules + /priv/static/app.js /priv/static/app.css diff --git a/assets/bun.lockb b/assets/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..dd7898a176ac5b3efa9572e4d734c9f446fcb6a2 GIT binary patch literal 3607 zcmY#Z)GsYA(of3F(@)JSQ%EY!;{sycoc!eMw9K4T-L(9o+{6;yG6OCq1_lP@$*X>A zYNgd&>f9W$Icf>}6xoaSCwngCX8pI9-R;2bgYCRPML@s_p%^&O=msc16{Y~nXJ9DF zFGxx(Vqow9iU=|=H1q;#0U$jWNOJ+{wBppV)Vz{nMMee&ejvXfBR@4Svw}ecD9;1r z11T_IO=LUNcp+)tUENo9+XL4G>|46|sK?qZ9_v$9#4Snqy>iEdCh^vH9rq?W{eQLF z`qq2?2Qi#F3>3@egmMz7C`$!=7GWhW-iPgTr^Bw22j5{8SaOLp90VV zP&&b74=!<-evmvcZ8hLhhfkaZC<($K0HQ&0zzU_=fHcUD96$^LxM+|Zby@|eyYw_g9a%+O$G z=?QD=*kS}kJ8UM(*2=y9uujpp;P4HH#!w`4Vd;P>&PFm9IX|-ahUwItbYol-7Zm9` zSx5TdORG26 z);c4Z3yW`%ds!aa-YTw2<^5(XA-DP>?`iSF=WJ!Sq#yW_S#Fp!`&rEiA+NQG6Hn-i zik_T&>&ohh{|)AHZCzRO`bMJVCHu23_jI7Q@(%gDsQme& z;v7~?jYi6i_RptxtNiXeA%QrY|55o54r>4d z6&7!xbO-}1Q)jkM2{8$6S;=(Hyi{uIzoJ^{f0H_mtV4b+DhsdmF`uu0JN)yu%Njmm zsZ&{>e4lt~b@Y8VU7kQ5jMN}I5HDLE%o zFRK`mr_sf*Spm`utHWS=v8khm7%=!~3}|lyjXgk)@g#eiaRs!wvj<9Z*%TKg>t*H@ zmn7!o=)u}rdLc!rsdfrR1`5TQRjK)DItnHVi8-0+dHHF;77!5p`wsyiHYh*5fEvdE zk!IksDK$2-1M0$|ALMXQ`U91Lu(AbI4uJBk1=NfPBFzHX57GxR4`eRPOptk?GOGh- z9ygE!1Y9=JMrN_47C`@*!QE$w@E@!l2Wy8ZfD0&44Ge4P!CGoeYzjd0Ky|wZkOs9C zVQoDoHdw0()|!O12ALS+jP(rk3>X+-?Mhg?kb^PKP|w5=C}4Q_0YC8FqVO#XoxKkf%fX@>8GR?6zfAnEk2_pHwT%GCXdFA&&e!HjW5eg zEr;oW3uKg(6ck(O>ldY_XBL+fRqEvxFdX+;#PGV(w zQD%BZiGE3HaS4Kp$24?JAaj9wbs@0 topbar.show(300)); +window.addEventListener("phx:page-loading-stop", (_info) => topbar.hide()); + +// connect if there are any LiveViews on the page +liveSocket.connect(); +window.liveSocket = liveSocket; diff --git a/assets/package.json b/assets/package.json new file mode 100644 index 0000000..31b8464 --- /dev/null +++ b/assets/package.json @@ -0,0 +1,10 @@ +{ + "workspaces": [ + "../deps/*" + ], + "dependencies": { + "phoenix": "workspace:*", + "phoenix_live_view": "workspace:*", + "topbar": "^3.0.0" + } +} diff --git a/config/config.exs b/config/config.exs index 88d4312..782b5de 100644 --- a/config/config.exs +++ b/config/config.exs @@ -12,11 +12,11 @@ if config_env() == :dev do cd: Path.expand("../assets", __DIR__) ] - config :esbuild, - version: "0.21.5", + config :bun, + version: "1.1.6", default: [ - args: ~w(app.js --bundle --target=es2016 --outdir=../../priv/static), + args: ~w(build app.js --outdir=../../priv/static), cd: Path.expand("../assets/js", __DIR__), - env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} + env: %{} ] end diff --git a/mix.exs b/mix.exs index 3e085f1..72b0e0f 100644 --- a/mix.exs +++ b/mix.exs @@ -50,8 +50,8 @@ defmodule ErrorTracker.MixProject do {:plug, "~> 1.10"}, {:postgrex, ">= 0.0.0"}, # Dev dependencies + {:bun, "~> 1.3", only: :dev}, {:credo, "~> 1.7", only: [:dev, :test]}, - {:esbuild, "~> 0.8", only: :dev}, {:ex_doc, "~> 0.33", only: :dev}, {:phoenix_live_reload, ">= 0.0.0", only: :dev}, {:plug_cowboy, ">= 0.0.0", only: :dev}, @@ -64,7 +64,7 @@ defmodule ErrorTracker.MixProject do setup: ["deps.get", "cmd --cd assets npm install"], dev: "run --no-halt dev.exs", "assets.watch": ["tailwind default --watch"], - "assets.build": ["esbuild default --minify", "tailwind default --minify"] + "assets.build": ["bun default --minify", "tailwind default --minify"] ] end end diff --git a/mix.lock b/mix.lock index b18e732..0f48b67 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,5 @@ %{ + "bun": {:hex, :bun, "1.3.0", "6833722da5b073777e043aec42091b0cf8bbacb84262ec6d348a914dda4c6a98", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "dde1b8116ba57269a9f398b4b28492b16fb29a78800c9533b7c9fb036793d62a"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, @@ -10,7 +11,6 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "ecto_sql": {:hex, :ecto_sql, "3.11.2", "c7cc7f812af571e50b80294dc2e535821b3b795ce8008d07aa5f336591a185a8", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "73c07f995ac17dbf89d3cfaaf688fcefabcd18b7b004ac63b0dc4ef39499ed6b"}, - "esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"}, "ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, From 4238600ace2ae5598b1a6c8453690424b3553808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 12:57:16 +0200 Subject: [PATCH 12/22] Proper render of assets --- lib/error_tracker/web/components/layouts/root.html.heex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/error_tracker/web/components/layouts/root.html.heex b/lib/error_tracker/web/components/layouts/root.html.heex index 8d87fbd..1aea091 100644 --- a/lib/error_tracker/web/components/layouts/root.html.heex +++ b/lib/error_tracker/web/components/layouts/root.html.heex @@ -8,8 +8,8 @@ <%= assigns[:page_title] || "ErrorTracker" %> - - + + From 15a952323baec82f0b45be861c0f5cc702d5d1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 12:57:22 +0200 Subject: [PATCH 13/22] WIP dashboard example --- lib/error_tracker/web/live/dashboard.ex | 12 +++++------- lib/error_tracker/web/live/dashboard.html.heex | 2 ++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/error_tracker/web/live/dashboard.ex b/lib/error_tracker/web/live/dashboard.ex index 2bc4fe6..caf12e6 100644 --- a/lib/error_tracker/web/live/dashboard.ex +++ b/lib/error_tracker/web/live/dashboard.ex @@ -5,18 +5,16 @@ defmodule ErrorTracker.Web.Live.Dashboard do @impl Phoenix.LiveView def mount(_params, _session, socket) do - {:ok, socket} + {:ok, assign(socket, :counter, 0)} end @impl Phoenix.LiveView - def terminate(_reason, %{assigns: %{timer: timer}}) do - if is_reference(timer), do: Process.cancel_timer(timer) - - :ok + def handle_params(_params, _uri, socket) do + {:noreply, socket} end @impl Phoenix.LiveView - def handle_params(_params, _uri, socket) do - {:noreply, socket} + def handle_event("increment", _params, socket) do + {:noreply, assign(socket, :counter, socket.assigns.counter + 1)} end end diff --git a/lib/error_tracker/web/live/dashboard.html.heex b/lib/error_tracker/web/live/dashboard.html.heex index 4a4319b..e523c7b 100644 --- a/lib/error_tracker/web/live/dashboard.html.heex +++ b/lib/error_tracker/web/live/dashboard.html.heex @@ -1 +1,3 @@

Hello world!

+

Number of presses: <%= @counter %>

+ From cb32568060dd92e82b5125bc58756541b69897fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 12:59:14 +0200 Subject: [PATCH 14/22] Update formatter config --- .formatter.exs | 5 +++-- lib/error_tracker/web/components/layouts/root.html.heex | 8 ++++++-- lib/error_tracker/web/live/dashboard.html.heex | 7 ++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.formatter.exs b/.formatter.exs index ecde7b6..73d29a7 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,5 +1,6 @@ # Used by "mix format" [ - inputs: ["{mix,.formatter,dev,dev.*}.exs", "{config,lib,test}/**/*.{ex,exs}"], - import_deps: [:ecto, :ecto_sql, :plug, :phoenix] + import_deps: [:ecto, :ecto_sql, :plug, :phoenix], + inputs: ["{mix,.formatter,dev,dev.*}.exs", "{config,lib,test}/**/*.{heex,ex,exs}"], + plugins: [Phoenix.LiveView.HTMLFormatter] ] diff --git a/lib/error_tracker/web/components/layouts/root.html.heex b/lib/error_tracker/web/components/layouts/root.html.heex index 1aea091..14365fb 100644 --- a/lib/error_tracker/web/components/layouts/root.html.heex +++ b/lib/error_tracker/web/components/layouts/root.html.heex @@ -8,8 +8,12 @@ <%= assigns[:page_title] || "ErrorTracker" %> - - + + diff --git a/lib/error_tracker/web/live/dashboard.html.heex b/lib/error_tracker/web/live/dashboard.html.heex index e523c7b..07f5708 100644 --- a/lib/error_tracker/web/live/dashboard.html.heex +++ b/lib/error_tracker/web/live/dashboard.html.heex @@ -1,3 +1,8 @@

Hello world!

Number of presses: <%= @counter %>

- + From 109cc36b0cf39f70578924e935cddb51367588aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 16:58:05 +0200 Subject: [PATCH 15/22] Allow configuring socket path and transport --- assets/js/app.js | 8 ++++++-- lib/error_tracker/web/components/layouts.ex | 3 +++ lib/error_tracker/web/components/layouts/root.html.heex | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 5b5abac..5faf26a 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,10 +1,14 @@ // Establish Phoenix Socket and LiveView configuration. -import { Socket } from "phoenix"; +import { Socket, LongPoll } from "phoenix"; import { LiveSocket } from "phoenix_live_view"; import topbar from "topbar"; let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content"); -let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken }}); +let socketPath = document.querySelector("meta[name='socket-path']").getAttribute("content"); +let socketTransport = document.querySelector("meta[name='socket-transport']").getAttribute("content"); +let normalizedTransport = (socketTransport == "longpoll") ? LongPoll : WebSocket; + +let liveSocket = new LiveSocket(socketPath, Socket, { transport: normalizedTransport, params: { _csrf_token: csrfToken }}); // Show progress bar on live navigation and form submits topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" }); diff --git a/lib/error_tracker/web/components/layouts.ex b/lib/error_tracker/web/components/layouts.ex index 472b256..9ac8e38 100644 --- a/lib/error_tracker/web/components/layouts.ex +++ b/lib/error_tracker/web/components/layouts.ex @@ -8,4 +8,7 @@ defmodule ErrorTracker.Web.Layouts do def get_content(:css), do: File.read!(@css_path) def get_content(:js), do: File.read!(@js_path) + + def get_config(:socket_path), do: "/live" + def get_config(:socket_transport), do: :websocket end diff --git a/lib/error_tracker/web/components/layouts/root.html.heex b/lib/error_tracker/web/components/layouts/root.html.heex index 14365fb..d3cd7af 100644 --- a/lib/error_tracker/web/components/layouts/root.html.heex +++ b/lib/error_tracker/web/components/layouts/root.html.heex @@ -5,6 +5,8 @@ + + <%= assigns[:page_title] || "ErrorTracker" %> From cf05eca28bef78fc43d020b635e167703b9676d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 17:02:21 +0200 Subject: [PATCH 16/22] Load socket config from app config --- lib/error_tracker/web/components/layouts.ex | 9 +++++++-- lib/error_tracker/web/components/layouts/root.html.heex | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/error_tracker/web/components/layouts.ex b/lib/error_tracker/web/components/layouts.ex index 9ac8e38..33ecade 100644 --- a/lib/error_tracker/web/components/layouts.ex +++ b/lib/error_tracker/web/components/layouts.ex @@ -4,11 +4,16 @@ defmodule ErrorTracker.Web.Layouts do @css_path :code.priv_dir(:error_tracker) |> Path.join("static/app.css") @js_path :code.priv_dir(:error_tracker) |> Path.join("static/app.js") + @default_docket_config %{path: "/live", transport: :websocket} + embed_templates "layouts/*" def get_content(:css), do: File.read!(@css_path) def get_content(:js), do: File.read!(@js_path) - def get_config(:socket_path), do: "/live" - def get_config(:socket_transport), do: :websocket + def get_socket_config(key) do + default = Map.get(@default_docket_config, key) + config = Application.get_env(:error_tracker, :live_view_socket, []) + Keyword.get(config, key, default) + end end diff --git a/lib/error_tracker/web/components/layouts/root.html.heex b/lib/error_tracker/web/components/layouts/root.html.heex index d3cd7af..a802f2e 100644 --- a/lib/error_tracker/web/components/layouts/root.html.heex +++ b/lib/error_tracker/web/components/layouts/root.html.heex @@ -5,8 +5,8 @@ - - + + <%= assigns[:page_title] || "ErrorTracker" %> From 37b8af01a753cb0b842650435d0198ede747ee4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 17:10:03 +0200 Subject: [PATCH 17/22] Add some documentation --- lib/error_tracker/web.ex | 43 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/error_tracker/web.ex b/lib/error_tracker/web.ex index f343a49..f866fb5 100644 --- a/lib/error_tracker/web.ex +++ b/lib/error_tracker/web.ex @@ -1,5 +1,40 @@ defmodule ErrorTracker.Web do - @moduledoc false + @moduledoc """ + ErrorTracker includes a Web UI to view and inspect errors occurred on your + application and already stored on the database. + + In order to use it, you need to add the following to your Phoenix's ` + router.ex` file: + + ```elixir + defmodule YourAppWeb.Router do + use Phoenix.Router + use ErrorTracker.Web, :router + + ... + + error_tracker_dashboard "/errors" + end + ``` + + This will add the routes needed for the ErrorTracker LiveView UI to work. + + ## LiveView socket options + + By default the library expects you to have your LiveView socket at `/live` and + using `websocket` transport. + + If that's not the case, you can configure it adding the following + configuration to your app's config files: + + ```elixir + config :error_tracker, + socket: [ + path: "/my-custom-socket-path" + transport: :longpoll # (accepted values are :longpoll or :websocket) + ] + ``` + """ def html do quote do @@ -25,6 +60,12 @@ defmodule ErrorTracker.Web do end end + def router do + quote do + import ErrorTracker.Web.Router + end + end + defp html_helpers do quote do use Phoenix.Component From 3a6c5ce3b0e93311038b022e8dfe80972f459645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 17:10:43 +0200 Subject: [PATCH 18/22] Update dev script --- dev.exs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev.exs b/dev.exs index 1cd1541..55ea57c 100644 --- a/dev.exs +++ b/dev.exs @@ -94,8 +94,7 @@ end defmodule ErrorTrackerDevWeb.Router do use Phoenix.Router - - import ErrorTracker.Web.Router + use ErrorTracker.Web, :router pipeline :browser do plug :fetch_session From 2dc07844be6aeb644c6ea2f5a0ad4212c9bf397a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Sun, 30 Jun 2024 17:22:39 +0200 Subject: [PATCH 19/22] Update README --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index 3024954..1c5b089 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,42 @@ defmodule MyApp.Endpoint do use ErrorTracker.Integrations.Plug end ``` + +## Development + +### Development server + +We have a `dev.exs` script that starts a development server. + +To run it together with an `IEx` console you can do: + +``` +iex -S mix dev +``` + +### Assets + +In ortder to participate in the development of this library, you may need to +know how to compile the assets needed to use the Web UI. + +To do so, you need to first make a clean build: + +``` +mix assets.build +``` + +That task will build the JS and CSS of the project. + +The JS is not expected to change too much because we rely in LiveView, but if +you make any change just execute that command again and you are good to go. + +In the case of CSS, as it is automatically generated by Tailwind, you need to +start the watcher when your intention is to modify the classes used. + +To do so you can execute this task in a separate terminal: + +``` +mix assets.watch +``` + + From ac02cd7a640d34378db8920ea29c995fe4d8ad8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Tue, 9 Jul 2024 16:58:19 +0200 Subject: [PATCH 20/22] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1c5b089..a401f82 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ know how to compile the assets needed to use the Web UI. To do so, you need to first make a clean build: ``` +(cd assets && ../_build/bun install) mix assets.build ``` From 6efbc149b0745c2d97cc378b73e0a7b16adc8b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Tue, 9 Jul 2024 17:00:32 +0200 Subject: [PATCH 21/22] Update bun version --- config/config.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index 782b5de..3c99d05 100644 --- a/config/config.exs +++ b/config/config.exs @@ -13,7 +13,7 @@ if config_env() == :dev do ] config :bun, - version: "1.1.6", + version: "1.1.18", default: [ args: ~w(build app.js --outdir=../../priv/static), cd: Path.expand("../assets/js", __DIR__), From 98f14c5956825b7f2ef84a6da22822a60fc9fb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20de=20Arriba?= Date: Tue, 9 Jul 2024 17:03:15 +0200 Subject: [PATCH 22/22] Add new task for installing deps --- README.md | 3 +-- assets/bun.lockb | Bin 3607 -> 4128 bytes mix.exs | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a401f82..1430ec5 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,7 @@ know how to compile the assets needed to use the Web UI. To do so, you need to first make a clean build: ``` -(cd assets && ../_build/bun install) -mix assets.build +mix do assets.install, assets.build ``` That task will build the JS and CSS of the project. diff --git a/assets/bun.lockb b/assets/bun.lockb index dd7898a176ac5b3efa9572e4d734c9f446fcb6a2..b0a82aef6f8b36d72b88f81ba61b60b0431c611d 100755 GIT binary patch delta 741 zcmbO(vp`{jAJYW>i2((S${P=gGjT8g0Y3vn!(>55gUJ&ZIXHkE1`&vWNj)2o1HvEx zq8Wjh9ZG|=G669c5Q6|N8YD-iIS{M4Hs55HW|L)wngg;QWH%=eTR<&Mm@LSp&IyxY znrz7>&p81qH)V1nm%4XBMt*8uW(5QPgBVVo@())Pz-EJdt_(DX2T0qW3tqmrwkAh` z4Hg6*Kp8=x%w64AcH0Bj1SqiS14Se!&*u_lWSG2_%bQVSG9!05=RIzqFc93EoXG9S z$pJJB1h!9J&8;~31GoI-2i)?T`FJWB>&yL%yo&=8oqzB`lnC%aXfB&lGZQ-n3x(p$ zs?_{69Uv<)Co?@SKMm>!n5$uKgt;gJYQPJ)LB&PMdYO5}C5bsXdc~=^WvNAaVW~yM znfZBk3d$xpZGc5A4=^35GqOxJ;523hrUC{&5Wijm6e2)i0Hq1J7ngG@p!z4k&B5Z_ l;F6-uymaKy&@@B~4zOc@W;B4z0VP&_AT0@`Cp&UX0036yi`oDH delta 344 zcmZ3WFkNPXA5%N;#DD@uk&Oq%nK%>}7#R2&7#b!EG8#<2z{JBM0^yj{vjAm47z98x zBM`GfX*M9u1jHOb3<9`lkQ|xjfUE!-y!j@xG@C3l)EtogAPIILw%`I1KoBumkV~DB zW3nTcJZA@7ER#!J7w8oR9-w#Z&jl~vTU(Q(05%ik!GetZ)V$0J2L1;zoI2$nt}?MH zO#aB_4K&4;JDhV7H`ri?MU&@pJ91ut^6Do)=T-y<$YgOIeIORt?9NlcIN6h90ssxI BE8hSB diff --git a/mix.exs b/mix.exs index 72b0e0f..38a3fe7 100644 --- a/mix.exs +++ b/mix.exs @@ -63,6 +63,7 @@ defmodule ErrorTracker.MixProject do [ setup: ["deps.get", "cmd --cd assets npm install"], dev: "run --no-halt dev.exs", + "assets.install": ["bun.install", "cmd _build/bun install --cwd assets/"], "assets.watch": ["tailwind default --watch"], "assets.build": ["bun default --minify", "tailwind default --minify"] ]