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
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.
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:
Acceptance criteria
Out of scope
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_ComparisonAdd
includes/class-mailchimp-form-comparison.phpthat uses theMailchimp_Analytics_Bucketingtrait, and register it inmailchimp.phpsame way the other analytics classes are wired (require_once, then construct and callinit()). Ininit(), register twowp_ajax_actions. Both must reuse the existing guard fromMailchimp_Form_Performance::handle_get(): checkMCSF_CAP_THRESHOLD, then verify themailchimp_sf_analytics_admin_noncenonce.Endpoint A —
mailchimp_sf_get_list_forms(fills the form selector). Takes alist_id. Reads forms registry for that list and returns each form'sform_idandtitle.Endpoint B —
mailchimp_sf_get_form_comparison(chart data). Takeslist_id,date_from,date_to, and a list ofform_ids(max 5). Validate the same way the form-performance handler does: require alist_id, run both dates throughis_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 (viaMailchimp_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'sconversion_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 anaria-describedbypointing 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
formComparisonModuleIn
assets/js/analytics.js, add a fourth self-contained module next toformPerformanceModulethat listens for the page's existingmailchimp-analytics-refreshevent. 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:
Both fetches follow the existing
fetchPerformanceapproach: 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:
0/ "no data" label, never a missing or broken series.