include browser/os/screen_size in e-commerce analytics events
All checks were successful
deploy / deploy (push) Successful in 1m37s

Event call sites (product_view, add_to_cart, checkout_start, purchase)
were only passing visitor_hash and pathname, leaving browser, OS, screen
size and country nil. Add AnalyticsHook.attrs/1 helper to extract common
analytics fields from socket assigns, and use it in all LiveView event
call sites. Checkout controller reads the same fields from the session.

Also fix plug analytics test to clear stale events before assertions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey 2026-02-22 21:13:47 +00:00
parent e26a02a0fb
commit f91b47f0c3
5 changed files with 37 additions and 14 deletions

View File

@ -79,6 +79,20 @@ defmodule BerrypodWeb.AnalyticsHook do
end end
end end
@doc """
Extracts common analytics attributes from socket assigns.
Call sites can merge their own fields (pathname, revenue, etc.) on top.
"""
def attrs(socket) do
%{
visitor_hash: socket.assigns[:analytics_visitor_hash],
browser: socket.assigns[:analytics_browser],
os: socket.assigns[:analytics_os],
screen_size: socket.assigns[:analytics_screen_size],
country_code: socket.assigns[:analytics_country_code]
}
end
defp handle_analytics_event("analytics:screen", %{"width" => width}, socket) defp handle_analytics_event("analytics:screen", %{"width" => width}, socket)
when is_integer(width) do when is_integer(width) do
screen_size = classify_screen(width) screen_size = classify_screen(width)

View File

@ -134,7 +134,11 @@ defmodule BerrypodWeb.CheckoutController do
if visitor_hash do if visitor_hash do
Analytics.track_event("checkout_start", %{ Analytics.track_event("checkout_start", %{
pathname: "/checkout", pathname: "/checkout",
visitor_hash: visitor_hash visitor_hash: visitor_hash,
browser: get_session(conn, "analytics_browser"),
os: get_session(conn, "analytics_os"),
screen_size: get_session(conn, "analytics_screen_size"),
country_code: get_session(conn, "country_code")
}) })
end end
end end

View File

@ -14,11 +14,11 @@ defmodule BerrypodWeb.Shop.CheckoutSuccess do
# Track purchase event # Track purchase event
if order && connected?(socket) && socket.assigns[:analytics_visitor_hash] do if order && connected?(socket) && socket.assigns[:analytics_visitor_hash] do
Analytics.track_event("purchase", %{ attrs =
pathname: "/checkout/success", BerrypodWeb.AnalyticsHook.attrs(socket)
visitor_hash: socket.assigns.analytics_visitor_hash, |> Map.merge(%{pathname: "/checkout/success", revenue: order.total})
revenue: order.total
}) Analytics.track_event("purchase", attrs)
end end
# Clear the cart after successful checkout # Clear the cart after successful checkout

View File

@ -42,10 +42,10 @@ defmodule BerrypodWeb.Shop.ProductShow do
gallery_images = filter_gallery_images(all_images, selected_options["Color"]) gallery_images = filter_gallery_images(all_images, selected_options["Color"])
if connected?(socket) and socket.assigns[:analytics_visitor_hash] do if connected?(socket) and socket.assigns[:analytics_visitor_hash] do
Analytics.track_event("product_view", %{ Analytics.track_event(
pathname: "/products/#{slug}", "product_view",
visitor_hash: socket.assigns.analytics_visitor_hash Map.put(BerrypodWeb.AnalyticsHook.attrs(socket), :pathname, "/products/#{slug}")
}) )
end end
socket = socket =
@ -185,10 +185,14 @@ defmodule BerrypodWeb.Shop.ProductShow do
cart = Cart.add_item(socket.assigns.raw_cart, variant.id, socket.assigns.quantity) cart = Cart.add_item(socket.assigns.raw_cart, variant.id, socket.assigns.quantity)
if socket.assigns[:analytics_visitor_hash] do if socket.assigns[:analytics_visitor_hash] do
Analytics.track_event("add_to_cart", %{ Analytics.track_event(
pathname: "/products/#{socket.assigns.product.slug}", "add_to_cart",
visitor_hash: socket.assigns.analytics_visitor_hash Map.put(
}) BerrypodWeb.AnalyticsHook.attrs(socket),
:pathname,
"/products/#{socket.assigns.product.slug}"
)
)
end end
socket = socket =

View File

@ -9,6 +9,7 @@ defmodule BerrypodWeb.Plugs.AnalyticsTest do
setup do setup do
send(Buffer, :flush) send(Buffer, :flush)
:timer.sleep(50) :timer.sleep(50)
Repo.delete_all(Event)
:ok :ok
end end