Admin Events

A product analytics system built on top of the admin panel. Track user events from your core application, then view them in a real-time feed and dashboard with bar charts, stacked charts, and funnels. Events are stored in the database and can be extended with custom handlers.

v1.8.3
Level: Advanced
18
1616
8

Related

Admin JS

The Bar and Funnel hooks are registered alongside the existing CopyToClipboard hook in the admin LiveSocket.

Bar chart hook

Handles tooltip show/hide/position for bar and stacked bar charts. Formats date labels into human-readable strings (e.g. "Mon, 3 Mar 2026" for days, "March 2026" for months). Reads data-bar-label , data-bar-count , and data-bar-dataset attributes from each bar section.

Funnel chart hook

Similar tooltip handling for funnel steps. Reads data-funnel-label , data-funnel-count , and data-funnel-rate attributes.

Tracking events in the app

Two events are wired into the registration flow:

register_user/1 tracks "user.signed_up" after a successful insert, using with to preserve the original {:ok, user} return value.

login_user_by_magic_link/1 tracks "user.confirmed" in the branch where a previously unconfirmed user clicks their magic link. Already-confirmed users skip the tracking.

Events context

The public API is intentionally small: track/2 , subscribe/0 , and broadcast/1 . All query and analytics functions live in the admin layer.

track/2 inserts the event, broadcasts it over PubSub for the real-time feed, and notifies any registered handlers. Failures are logged rather than raised, so tracking never breaks the calling code path.

Handlers are notified synchronously after each event insert. Register them in config:

Event catalog

Maps event names to display properties: a human-readable title, an emoji icon, and a description function that receives the event struct. The catalog is used by both the event feed and the dashboard charts. Unknown events get a nil fallback so the UI always renders gracefully.

Event schema

A minimal schema with a @name_format regex that enforces lowercase dotted names like user.signed_up . The metadata field defaults to an empty map and stores arbitrary key-value data about the event. The belongs_to :user association links events to the user who triggered them.

Handler behaviour

A simple behaviour with a single handle_event/1 callback. Implement this to react to events, for example sending a welcome email when a user signs up. Handlers run synchronously after insert, keeping the architecture simple while allowing extension.

Admin event queries

All read queries and analytics live in the admin layer since they're only used by admin views.

list_latest/1 powers the event feed with optional name and user filters. Events are preloaded with their user association and ordered newest-first.

aggregate/3 computes a count for a date range and its previous period of the same length, returning the current count, previous count, and percentage change. This drives the stat cards on the dashboard.

bar_chart/4 wraps aggregate for daily data and adds a monthly mode using date_trunc('month', ...) . Returns data shaped for the <.bar_chart> component.

stacked_bar_chart/3 runs a single grouped query for multiple event names and zero-fills each series. Uses catalog titles as dataset labels.

funnel_chart/3 counts distinct users per event and intersects the sets at each step, so each step shows users who completed that step and all previous steps.

Chart components

A set of function components for rendering analytics charts, imported into admin views.

stat/1 renders a card with a label, value, and color-coded percentage change indicator.

bar_chart/1 renders a horizontal bar chart with CSS-based bars (no JS charting library). Each bar's height is computed as a percentage of the max value. The Bar phx-hook handles tooltip positioning on hover.

stacked_bar_chart/1 extends the bar chart for multiple series stacked per day. Colors are auto-assigned from a shared @series_colors palette with a legend.

funnel_chart/1 renders horizontal bars that taper from 100% down based on conversion rates. Shows counts and percentages at each step.

All chart types share a chart_card wrapper component for consistent titles, icons, and subtitles, plus a bar_tooltip component for hover states.

Dashboard

The admin landing page renders four stat cards (30d, 90d, 6mo, 1yr registrations), two bar charts (daily and quarterly), a stacked bar chart comparing sign-ups vs confirmations, and a registration funnel. All data is computed on mount from the query module.

Event feed

A real-time event feed using LiveView streams. New events arrive via PubSub subscription and are prepended to the stream. URL-based filters for event name and user ID are applied both to the initial query and to incoming broadcasts via matches_filter?/2 . Filter pills with clear buttons make it easy to drill into specific events or users.

Event detail

Displays the event's catalog title, icon, description, user email, IDs, and metadata key-value pairs. Each property is a click-to-copy button using the CopyToClipboard phx-hook with a data-copied attribute for visual feedback.