hide system events by default on activity page
All checks were successful
deploy / deploy (push) Successful in 1h1m58s
All checks were successful
deploy / deploy (push) Successful in 1h1m58s
Adds a toggle to show/hide internal system events (syncs, job failures, abandoned cart creation) on the activity feed. Hidden by default so shop owners only see actionable items. Toggle is ephemeral — resets on page load. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
820714a04f
commit
bf06ee051a
@ -11,6 +11,11 @@ defmodule Berrypod.ActivityLog do
|
|||||||
alias Berrypod.Repo
|
alias Berrypod.Repo
|
||||||
alias Berrypod.ActivityLog.Entry
|
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 ──
|
# ── Write ──
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@ -71,6 +76,7 @@ defmodule Berrypod.ActivityLog do
|
|||||||
* `:tab` - "all" (default) or "attention" (unresolved warnings/errors)
|
* `:tab` - "all" (default) or "attention" (unresolved warnings/errors)
|
||||||
* `:category` - "orders", "syncs", "emails", "carts", or nil for all
|
* `:category` - "orders", "syncs", "emails", "carts", or nil for all
|
||||||
* `:search` - order number substring search
|
* `:search` - order number substring search
|
||||||
|
* `:hide_system` - exclude system event types (default false)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def list_recent(opts \\ []) do
|
def list_recent(opts \\ []) do
|
||||||
@ -78,6 +84,7 @@ defmodule Berrypod.ActivityLog do
|
|||||||
|> order_by([a], desc: a.occurred_at)
|
|> order_by([a], desc: a.occurred_at)
|
||||||
|> maybe_filter_tab(opts[:tab])
|
|> maybe_filter_tab(opts[:tab])
|
||||||
|> maybe_filter_category(opts[:category])
|
|> maybe_filter_category(opts[:category])
|
||||||
|
|> maybe_hide_system(opts[:hide_system])
|
||||||
|> maybe_search_order_number(opts[:search])
|
|> maybe_search_order_number(opts[:search])
|
||||||
|> Berrypod.Pagination.paginate(page: opts[:page], per_page: 50)
|
|> Berrypod.Pagination.paginate(page: opts[:page], per_page: 50)
|
||||||
end
|
end
|
||||||
@ -165,6 +172,12 @@ defmodule Berrypod.ActivityLog do
|
|||||||
|
|
||||||
defp maybe_filter_category(query, _), do: query
|
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
|
defp maybe_search_order_number(query, search) when is_binary(search) and search != "" do
|
||||||
query
|
query
|
||||||
|> join(:inner, [a], o in "orders", on: a.order_id == o.id)
|
|> join(:inner, [a], o in "orders", on: a.order_id == o.id)
|
||||||
|
|||||||
@ -15,6 +15,7 @@ defmodule BerrypodWeb.Admin.Activity do
|
|||||||
socket
|
socket
|
||||||
|> assign(:page_title, "Activity")
|
|> assign(:page_title, "Activity")
|
||||||
|> assign(:attention_count, ActivityLog.count_needing_attention())
|
|> assign(:attention_count, ActivityLog.count_needing_attention())
|
||||||
|
|> assign(:show_system, false)
|
||||||
|
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
end
|
end
|
||||||
@ -31,7 +32,8 @@ defmodule BerrypodWeb.Admin.Activity do
|
|||||||
page: page_num,
|
page: page_num,
|
||||||
tab: tab,
|
tab: tab,
|
||||||
category: category,
|
category: category,
|
||||||
search: search
|
search: search,
|
||||||
|
hide_system: !socket.assigns.show_system
|
||||||
)
|
)
|
||||||
|
|
||||||
socket =
|
socket =
|
||||||
@ -85,6 +87,11 @@ defmodule BerrypodWeb.Admin.Activity do
|
|||||||
{:noreply, push_patch(socket, to: ~p"/admin/activity?#{params}")}
|
{:noreply, push_patch(socket, to: ~p"/admin/activity?#{params}")}
|
||||||
end
|
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
|
def handle_event("resolve", %{"id" => id}, socket) do
|
||||||
ActivityLog.resolve(id)
|
ActivityLog.resolve(id)
|
||||||
{:noreply, refetch_and_reassign(socket)}
|
{: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="syncs" active={@category} label="Syncs" />
|
||||||
<.category_chip category="emails" active={@category} label="Emails" />
|
<.category_chip category="emails" active={@category} label="Emails" />
|
||||||
<.category_chip category="carts" active={@category} label="Carts" />
|
<.category_chip category="carts" active={@category} label="Carts" />
|
||||||
|
<label class="admin-toggle-label admin-text-secondary">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="admin-toggle admin-toggle-sm"
|
||||||
|
checked={@show_system}
|
||||||
|
phx-click="toggle_system"
|
||||||
|
/> System events
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<.form
|
<.form
|
||||||
for={%{}}
|
for={%{}}
|
||||||
@ -380,7 +395,10 @@ defmodule BerrypodWeb.Admin.Activity do
|
|||||||
_ -> true
|
_ -> true
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
defp build_params(opts) do
|
defp build_params(opts) do
|
||||||
@ -397,7 +415,8 @@ defmodule BerrypodWeb.Admin.Activity do
|
|||||||
page: assigns.pagination.page,
|
page: assigns.pagination.page,
|
||||||
tab: assigns.tab,
|
tab: assigns.tab,
|
||||||
category: assigns.category,
|
category: assigns.category,
|
||||||
search: assigns.search
|
search: assigns.search,
|
||||||
|
hide_system: !assigns.show_system
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -34,13 +34,13 @@ defmodule BerrypodWeb.Admin.ActivityTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "renders activity entries", %{conn: conn} do
|
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.created", "Order placed")
|
||||||
|
ActivityLog.log_event("order.shipped", "Order shipped")
|
||||||
|
|
||||||
{:ok, _view, html} = live(conn, ~p"/admin/activity")
|
{:ok, _view, html} = live(conn, ~p"/admin/activity")
|
||||||
|
|
||||||
assert html =~ "Product sync finished"
|
|
||||||
assert html =~ "Order placed"
|
assert html =~ "Order placed"
|
||||||
|
assert html =~ "Order shipped"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "links to order when order_id present", %{conn: conn} do
|
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")
|
{: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"})
|
html = render_click(view, "category", %{"category" => "syncs"})
|
||||||
|
|
||||||
assert html =~ "Sync done"
|
assert html =~ "Sync done"
|
||||||
@ -129,7 +131,11 @@ defmodule BerrypodWeb.Admin.ActivityTest do
|
|||||||
payload: %{connection_id: Ecto.UUID.generate()}
|
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"
|
assert html =~ "Retry sync"
|
||||||
end
|
end
|
||||||
@ -137,10 +143,26 @@ defmodule BerrypodWeb.Admin.ActivityTest do
|
|||||||
test "updates via PubSub", %{conn: conn} do
|
test "updates via PubSub", %{conn: conn} do
|
||||||
{:ok, view, _html} = live(conn, ~p"/admin/activity")
|
{: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)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user