add activity log with order timeline and global feed
All checks were successful
deploy / deploy (push) Successful in 4m22s
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:
102
priv/repo/migrations/20260301133937_create_activity_log.exs
Normal file
102
priv/repo/migrations/20260301133937_create_activity_log.exs
Normal file
@@ -0,0 +1,102 @@
|
||||
defmodule Berrypod.Repo.Migrations.CreateActivityLog do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
create table(:activity_log, primary_key: false) do
|
||||
add :id, :binary_id, primary_key: true
|
||||
add :event_type, :string, null: false
|
||||
add :level, :string, null: false, default: "info"
|
||||
add :order_id, references(:orders, type: :binary_id, on_delete: :nilify_all)
|
||||
add :payload, :map, default: %{}
|
||||
add :message, :string, null: false
|
||||
add :resolved_at, :utc_datetime
|
||||
add :occurred_at, :utc_datetime, null: false
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
create index(:activity_log, [:order_id])
|
||||
create index(:activity_log, [:occurred_at])
|
||||
create index(:activity_log, [:level, :resolved_at])
|
||||
|
||||
# Backfill from existing orders
|
||||
flush()
|
||||
backfill_from_orders()
|
||||
end
|
||||
|
||||
def down do
|
||||
drop table(:activity_log)
|
||||
end
|
||||
|
||||
defp backfill_from_orders do
|
||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||
|
||||
# order.created for all paid orders
|
||||
execute("""
|
||||
INSERT INTO activity_log (id, event_type, level, order_id, payload, message, occurred_at, inserted_at, updated_at)
|
||||
SELECT
|
||||
lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))),
|
||||
'order.created',
|
||||
'info',
|
||||
id,
|
||||
'{}',
|
||||
'Order placed',
|
||||
inserted_at,
|
||||
'#{now}',
|
||||
'#{now}'
|
||||
FROM orders
|
||||
WHERE payment_status = 'paid'
|
||||
""")
|
||||
|
||||
# order.submitted
|
||||
execute("""
|
||||
INSERT INTO activity_log (id, event_type, level, order_id, payload, message, occurred_at, inserted_at, updated_at)
|
||||
SELECT
|
||||
lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))),
|
||||
'order.submitted',
|
||||
'info',
|
||||
id,
|
||||
'{}',
|
||||
'Submitted to provider',
|
||||
submitted_at,
|
||||
'#{now}',
|
||||
'#{now}'
|
||||
FROM orders
|
||||
WHERE submitted_at IS NOT NULL
|
||||
""")
|
||||
|
||||
# order.shipped
|
||||
execute("""
|
||||
INSERT INTO activity_log (id, event_type, level, order_id, payload, message, occurred_at, inserted_at, updated_at)
|
||||
SELECT
|
||||
lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))),
|
||||
'order.shipped',
|
||||
'info',
|
||||
id,
|
||||
'{}',
|
||||
'Shipped',
|
||||
shipped_at,
|
||||
'#{now}',
|
||||
'#{now}'
|
||||
FROM orders
|
||||
WHERE shipped_at IS NOT NULL
|
||||
""")
|
||||
|
||||
# order.delivered
|
||||
execute("""
|
||||
INSERT INTO activity_log (id, event_type, level, order_id, payload, message, occurred_at, inserted_at, updated_at)
|
||||
SELECT
|
||||
lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))),
|
||||
'order.delivered',
|
||||
'info',
|
||||
id,
|
||||
'{}',
|
||||
'Delivered',
|
||||
delivered_at,
|
||||
'#{now}',
|
||||
'#{now}'
|
||||
FROM orders
|
||||
WHERE delivered_at IS NOT NULL
|
||||
""")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user