add activity log with order timeline and global feed
All checks were successful
deploy / deploy (push) Successful in 4m22s

Single activity_log table powering two views: chronological timeline
on each order detail page (replacing the old fulfilment card) and a
global feed at /admin/activity with tabs, category filters, search,
and pagination. Real-time via PubSub — new entries appear instantly,
nav badge updates across all admin pages.

Instrumented across all event points: Stripe webhooks, order notifier,
submission worker, fulfilment status worker, product sync worker, and
Oban exhausted-job telemetry. Contextual action buttons (retry
submission, retry sync, dismiss) with Oban unique constraints to
prevent double-enqueue. 90-day pruning via cron.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-03-01 15:09:08 +00:00
parent b235219aee
commit 580a7203c9
23 changed files with 1716 additions and 54 deletions

View File

@@ -116,12 +116,12 @@ defmodule BerrypodWeb.Admin.OrdersTest do
live(conn, ~p"/admin/orders/#{fake_id}")
end
test "shows fulfilment card", %{conn: conn} do
test "shows timeline card", %{conn: conn} do
order = order_fixture(payment_status: "paid")
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
assert html =~ "Fulfilment"
assert html =~ "Timeline"
assert html =~ "unfulfilled"
end
@@ -172,6 +172,49 @@ defmodule BerrypodWeb.Admin.OrdersTest do
end
end
describe "order timeline" do
setup %{conn: conn, user: user} do
conn = log_in_user(conn, user)
%{conn: conn}
end
test "renders timeline entries for an order", %{conn: conn} do
order = order_fixture(payment_status: "paid")
Berrypod.ActivityLog.log_event("order.created", "Order placed", order_id: order.id)
Berrypod.ActivityLog.log_event("order.submitted", "Submitted to provider",
order_id: order.id
)
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
assert html =~ "Order placed"
assert html =~ "Submitted to provider"
end
test "shows empty state when no timeline entries", %{conn: conn} do
order = order_fixture(payment_status: "paid")
{:ok, _view, html} = live(conn, ~p"/admin/orders/#{order}")
assert html =~ "No activity recorded yet"
end
test "updates timeline via PubSub", %{conn: conn} do
order = order_fixture(payment_status: "paid")
{:ok, view, _html} = live(conn, ~p"/admin/orders/#{order}")
Berrypod.ActivityLog.log_event("order.submitted", "Submitted to provider",
order_id: order.id
)
html = render(view)
assert html =~ "Submitted to provider"
end
end
describe "order list fulfilment column" do
setup %{conn: conn, user: user} do
conn = log_in_user(conn, user)