diff --git a/lib/berrypod/activity_log.ex b/lib/berrypod/activity_log.ex index 1611829..b34aa19 100644 --- a/lib/berrypod/activity_log.ex +++ b/lib/berrypod/activity_log.ex @@ -11,6 +11,11 @@ defmodule Berrypod.ActivityLog do alias Berrypod.Repo alias Berrypod.ActivityLog.Entry + @system_event_types ~w(sync.started sync.completed sync.failed job.exhausted abandoned_cart.created) + + @doc "Returns true if the event type is internal system noise." + def system_event_type?(event_type), do: event_type in @system_event_types + # ── Write ── @doc """ @@ -71,6 +76,7 @@ defmodule Berrypod.ActivityLog do * `:tab` - "all" (default) or "attention" (unresolved warnings/errors) * `:category` - "orders", "syncs", "emails", "carts", or nil for all * `:search` - order number substring search + * `:hide_system` - exclude system event types (default false) """ def list_recent(opts \\ []) do @@ -78,6 +84,7 @@ defmodule Berrypod.ActivityLog do |> order_by([a], desc: a.occurred_at) |> maybe_filter_tab(opts[:tab]) |> maybe_filter_category(opts[:category]) + |> maybe_hide_system(opts[:hide_system]) |> maybe_search_order_number(opts[:search]) |> Berrypod.Pagination.paginate(page: opts[:page], per_page: 50) end @@ -165,6 +172,12 @@ defmodule Berrypod.ActivityLog do defp maybe_filter_category(query, _), do: query + defp maybe_hide_system(query, true) do + where(query, [a], a.event_type not in ^@system_event_types) + end + + defp maybe_hide_system(query, _), do: query + defp maybe_search_order_number(query, search) when is_binary(search) and search != "" do query |> join(:inner, [a], o in "orders", on: a.order_id == o.id) diff --git a/lib/berrypod_web/live/admin/activity.ex b/lib/berrypod_web/live/admin/activity.ex index 721ec8a..c0b4127 100644 --- a/lib/berrypod_web/live/admin/activity.ex +++ b/lib/berrypod_web/live/admin/activity.ex @@ -15,6 +15,7 @@ defmodule BerrypodWeb.Admin.Activity do socket |> assign(:page_title, "Activity") |> assign(:attention_count, ActivityLog.count_needing_attention()) + |> assign(:show_system, false) {:ok, socket} end @@ -31,7 +32,8 @@ defmodule BerrypodWeb.Admin.Activity do page: page_num, tab: tab, category: category, - search: search + search: search, + hide_system: !socket.assigns.show_system ) socket = @@ -85,6 +87,11 @@ defmodule BerrypodWeb.Admin.Activity do {:noreply, push_patch(socket, to: ~p"/admin/activity?#{params}")} end + def handle_event("toggle_system", _params, socket) do + socket = assign(socket, :show_system, !socket.assigns.show_system) + {:noreply, refetch_and_reassign(socket)} + end + def handle_event("resolve", %{"id" => id}, socket) do ActivityLog.resolve(id) {:noreply, refetch_and_reassign(socket)} @@ -171,6 +178,14 @@ defmodule BerrypodWeb.Admin.Activity do <.category_chip category="syncs" active={@category} label="Syncs" /> <.category_chip category="emails" active={@category} label="Emails" /> <.category_chip category="carts" active={@category} label="Carts" /> + <.form for={%{}} @@ -380,7 +395,10 @@ defmodule BerrypodWeb.Admin.Activity do _ -> true end - tab_match and category_match + system_match = + if assigns.show_system, do: true, else: !ActivityLog.system_event_type?(entry.event_type) + + tab_match and category_match and system_match end defp build_params(opts) do @@ -397,7 +415,8 @@ defmodule BerrypodWeb.Admin.Activity do page: assigns.pagination.page, tab: assigns.tab, category: assigns.category, - search: assigns.search + search: assigns.search, + hide_system: !assigns.show_system ) end diff --git a/test/berrypod_web/live/admin/activity_test.exs b/test/berrypod_web/live/admin/activity_test.exs index a81dc65..6ed1701 100644 --- a/test/berrypod_web/live/admin/activity_test.exs +++ b/test/berrypod_web/live/admin/activity_test.exs @@ -34,13 +34,13 @@ defmodule BerrypodWeb.Admin.ActivityTest do end test "renders activity entries", %{conn: conn} do - ActivityLog.log_event("sync.completed", "Product sync finished") ActivityLog.log_event("order.created", "Order placed") + ActivityLog.log_event("order.shipped", "Order shipped") {:ok, _view, html} = live(conn, ~p"/admin/activity") - assert html =~ "Product sync finished" assert html =~ "Order placed" + assert html =~ "Order shipped" end test "links to order when order_id present", %{conn: conn} do @@ -72,6 +72,8 @@ defmodule BerrypodWeb.Admin.ActivityTest do {:ok, view, _html} = live(conn, ~p"/admin/activity") + # Enable system events so sync.completed is visible + render_click(view, "toggle_system") html = render_click(view, "category", %{"category" => "syncs"}) assert html =~ "Sync done" @@ -129,7 +131,11 @@ defmodule BerrypodWeb.Admin.ActivityTest do payload: %{connection_id: Ecto.UUID.generate()} ) - {:ok, _view, html} = live(conn, ~p"/admin/activity?tab=attention") + {:ok, view, _html} = live(conn, ~p"/admin/activity?tab=attention") + + # sync.failed is a system event, hidden by default + render_click(view, "toggle_system") + html = render(view) assert html =~ "Retry sync" end @@ -137,10 +143,26 @@ defmodule BerrypodWeb.Admin.ActivityTest do test "updates via PubSub", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/admin/activity") - ActivityLog.log_event("sync.started", "Sync kicked off") + ActivityLog.log_event("order.created", "New order arrived") html = render(view) - assert html =~ "Sync kicked off" + assert html =~ "New order arrived" + end + + test "system events hidden by default, shown after toggle", %{conn: conn} do + ActivityLog.log_event("sync.completed", "Sync done") + ActivityLog.log_event("order.created", "Order placed") + + {:ok, view, html} = live(conn, ~p"/admin/activity") + + # System events hidden by default + refute html =~ "Sync done" + assert html =~ "Order placed" + + # Toggle shows system events + html = render_click(view, "toggle_system") + assert html =~ "Sync done" + assert html =~ "Order placed" end end