Skip to content

Simple Analytics Phase 3 #226

Description

@iamdharmesh

Problem & expected outcome

The problem: WordPress site owners who run multiple Mailchimp signup forms have no way to compare how those forms perform against each other. The analytics page implemented in Phase 1 shows only aggregated data across all form placements. Users cannot tell which forms are driving signups and which are underperforming — so they can't make informed decisions about where to invest time improving their forms.

The fix: As a Mailchimp subscriber list owner, when this is resolved I should be able to select two or more forms by name, view their views, submissions, and conversion rates side by side on a single chart, and switch between those three metrics without losing my selection.

Placement: This chart will live between the two created in phase one, as the second graph/chart on the page.

Designs:

⚠️ Changes to the designs:


Acceptance criteria

  • A new chart is visible between the List performance chart and the Subscriber data chart with a comparison of form performance within an individual list.
  • The date range and list selector at the top of the page applies to this chart.
  • A multi-select input lets users choose up to 5 forms available for the selected list, by title. It must support search/filter by name.
  • The comparison chart renders as a bar chart for views for all three metrics (submissions, views, conversion rate).
  • A metric selector (Views / Submissions / Conversion rate) switches all visible form data at once without resetting the form, list, or date range selections.
  • Only one metric is displayed at a time (Views / Submissions / Conversion rate) for the selected data.
  • If a form has no data for the selected date range, it appears in the selector and chart but renders as a flat zero line/bar with a visible "no data" or “0” label — not a broken or missing series.
  • Form titles truncate gracefully at display — full title is accessible via tooltip.
  • Tooltip shows data per form, clearly listed per each date range data point.
  • Edge/failure state is handled gracefully — no silent failures or broken layouts
  • (Optional) Chart is accessible: axes are labeled, color is not the only way to distinguish series (use dash patterns on lines, distinct bar patterns or spacing for bars)

Out of scope

Item Reason
Comparing forms across multiple sites or WP Multisite installs Separate data architecture problem; out of scope for this iteration
Saving or exporting a comparison view Nice-to-have; defer to a follow-on ticket once core UX is validated
Comparing forms across different subscriber lists Cross-list data introduces significant complexity and a different user intent
Date range picker Assumed to inherit the existing date range control already on the analytics page
More than 5 forms selected simultaneously Beyond 15 series the chart becomes unreadable; enforce a hard cap in the UI
Conversion rate calculation logic changes Existing calculation (submissions ÷ views) is unchanged — this ticket is display only

Implementation Brief

Two parts: the API (new admin-ajax endpoints) and the frontend (a new card plus chart module).

API — new server class Mailchimp_Form_Comparison

Add includes/class-mailchimp-form-comparison.php that uses the Mailchimp_Analytics_Bucketing trait, and register it in mailchimp.php same way the other analytics classes are wired (require_once, then construct and call init()). In init(), register two wp_ajax_ actions. Both must reuse the existing guard from Mailchimp_Form_Performance::handle_get(): check MCSF_CAP_THRESHOLD, then verify the mailchimp_sf_analytics_admin_nonce nonce.

Endpoint A — mailchimp_sf_get_list_forms (fills the form selector). Takes a list_id. Reads forms registry for that list and returns each form's form_id and title.

Endpoint B — mailchimp_sf_get_form_comparison (chart data). Takes list_id, date_from, date_to, and a list of form_ids (max 5). Validate the same way the form-performance handler does: require a list_id, run both dates through is_valid_date(), and reject if start is after end. Sanitize the form IDs and enforce the 5-form cap on the server too — don't rely on the UI. Then read the existing analytics table (via Mailchimp_Analytics_Data::get_table_name()), summing views and submissions grouped by form and date for the selected list, range, and form IDs.

For each requested form, bucket its rows into the chart interval exactly as Mailchimp_Form_Performance::aggregate(), back-filling every bucket in the range with zeros so all forms share an aligned x-axis. Compute each form's conversion rate per bucket from that form's own views and submissions using the trait's conversion_rate(). Look up titles from forms registry table. Return, per form, the title and a value array for all three metrics (views, submissions, conversion rate), plus the shared bucket labels and the interval — returning all three metrics together is what lets the metric toggle work without a refetch.

Template — new section

In includes/admin/templates/analytics.php, add a new analytics card section between the List-performance and Subscriber-activity sections. Model it on the form-performance card, a header with a live-region subtitle, a hidden error banner, a control row, a chart canvas with an accessible label and an aria-describedby pointing at a hidden data table, a loading overlay, and the hidden data-table container. The control row holds two things: the form multi-select and a metric selector. The metric selector is a simple selector with Views, Submissions, and Conversion rate (Views selected by default), all strings translated.

Frontend — new module formComparisonModule

In assets/js/analytics.js, add a fourth self-contained module next to formPerformanceModule that listens for the page's existing mailchimp-analytics-refresh event. The module tracks three things: the forms currently selected (max 5), the active metric (default Views), and the last range/list it saw. It also caches the last response so switching metrics never needs a new request.

Behaviour:

  • On refresh (date range or list change): call Endpoint A to repopulate the selector for the new list, drop any selected form that doesn't belong to that list, then call Endpoint B for the current selection and render.
  • On metric change: just re-render from the cached response with the new metric — do not refetch, and do not touch the form, list, or date selections.
  • On selection change: enforce the 5-form limit and search-by-title in the multi-select UI, then refetch from Endpoint B and render.

Both fetches follow the existing fetchPerformance approach: a POST to the relevant action with the nonce and parameters.

Rendering should reuse the form-performance chart setup — same colours, strings, reduced-motion handling, axis and tooltip conventions — but as a bar chart with one series per selected form for the single active metric. Specific requirements to honour:

  • A form with no data stays in the chart as a flat-zero bar carrying a visible 0 / "no data" label, never a missing or broken series.
  • Tooltips are keyed by form title (per the design change): the tooltip heading is the form's title and the body lists each form's value at that point. Long titles truncate in the selector and legend with an ellipsis, with the full title available on hover.
  • Accessibility: label the axes; (optional) don't rely on colour alone to tell forms apart — use distinct fill patterns or spacing plus direct labels.
  • States: reuse the module's loading / empty / error patterns for "loading", "no forms for this list", "no data in range", and request failures, so nothing fails silently.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions