Premium Analytics: add Traffic chart stats widget#49518
Conversation
Port Jetpack Stats' stats-chart-tabs traffic card as the jpa/traffic-chart
dashboard widget. v1 charts site Views and Visitors over the last 30 days as a
line chart.
Data comes from the existing jetpack-stats-admin proxy
(/jetpack/v4/stats-app/sites/{id}/stats/visits) via a new useReportStatsVisits
hook (plain useQuery; stats has no comparison period), with a Config_Data PHP
class emitting window.jpaConfig (site ID, REST root, nonce) for the client.
The widget reuses widget-local copies of the chart toolkit pieces (WidgetRoot,
ComparativeLineChart, tooltip, overlays) adapted from the average-items-per-order
widget; these are deletable once the shared widgets-toolkit lands.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! Premium Analytics plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. |
…fig semantics - traffic-chart-widget: move console.error into a useEffect keyed on the error transition so it no longer fires on every render (claude[bot]). - build-visits-series: document that the toolkit's index-based comparison semantics are inert for two parallel primary metrics. - Config_Data: note that apiRoot/nonce are reserved (apiFetch resolves the REST root and nonce from the wp-api-fetch script); only siteId is read today.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The earlier install ran before the workspace store was fully warmed and pruned ~3300 unrelated lock entries, breaking the monorepo install and cascading failures across CI. Reset to the base lock and re-resolved so the only change is the premium-analytics importer (charts + the three internal link: packages).
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
🤖 Review-cycle summary —
|
| Source | Comment | Resolution |
|---|---|---|
| claude[bot] | console.error in render | Moved into a useEffect keyed on [ isError, error ] (43e42d8f) |
| claude[bot] | Visitors treated as comparison series | Documented that the toolkit's index-based comparison semantics are inert for two identical-date primary series (43e42d8f) |
| claude[bot] | apiRoot/nonce dead config |
Documented they're reserved (client uses wp-api-fetch's root+nonce); kept identical to #49508 for clean reconciliation (43e42d8f) |
claude[bot] re-reviewed 43e42d8f and confirmed all three resolved (LGTM). Acknowledged-no-change nits (double Number() coercion, deliberate chart-toolkit carry-over, widget.json/widget.ts string split, day-only weekly normalization) were accepted as reasonable.
CI fix: 8f78fc0c — an earlier pnpm install (run before the workspace store was warmed) had pruned ~3,300 unrelated lines from pnpm-lock.yaml, breaking the monorepo install and cascading failures across CI. Reset to the base lock and re-resolved so the only lock change is the premium-analytics importer (charts + the three internal link: packages). CI went from ~30 failures to the 3 inherited ones below.
Unaddressed (flagged for owner): None.
CI: No required checks are failing. The remaining red checks are inherited from the base branch #49502 (which fails the identical set) and not introduced here:
- Type checking — pre-existing errors in the vendored
widget-primitives/widget-dashboardpackages; zero in this PR's files. - Project structure —
noEmit/ missingchangelog/.gitkeepon the vendoredjs-packages/grid|widget-dashboard|widget-primitives; this PR'spackages/+plugins/premium-analyticscheck clean. - Static analysis — phan flags
src/widget-modules.php(a base-branch file, untouched here) calling the build-generatedjpa_get_registered_widget_modules(); this PR's new PHP produced no phan errors. - Code coverage requirement — expected: coverage can't compute against a feature-branch base (no trunk baseline), per jp-launch-control.
Open (non-blocking, on the manual-test checklist): full-page-app happy-path auth (does apiFetch get a valid wp_rest nonce on the wp-build app?) — gated on the widget-dashboard rendering branch + a connected site.
Screenshot — Traffic chart widgetThe
|

Fixes WOOA7S-1504
Proposed changes
Premium Analytics users get a Traffic widget for their customizable dashboard: a line chart of the site's Views and Visitors over the last 30 days — the Jetpack Stats traffic card brought into the new dashboard.
This is the second real dashboard widget (after Average items per order, #49505) and the second Stats card port. It follows the self-contained widget pattern: widget-local copies of the chart toolkit pieces that are deletable once the shared
widgets-toolkit(#49422) lands.widgets/traffic-chart/— newjpa/traffic-chartwidget (statscategory). A providers-onlyWidgetRoot(query client + chart theme) wrapsTrafficChartWidget, which fetches visits and renders Views + Visitors as two line series via aComparativeLineChart(adapted from Premium Analytics: port the Average items per order widget #49505). Loading/refetch overlay and an inline error notice with Retry. Period/quantity are hardcoded for v1 via attribute defaults (unit: day,quantity: 30); a shared date-range context can drive them later.packages/data):useReportStatsVisits— usesuseQuerydirectly (stats has no comparison concept in v1) and returns the sanitized time series.fetchReportStatsVisitsagainst the existingjetpack/v4/stats-app/sites/{site_id}/stats/visitsproxy from thejetpack-stats-adminpackage — no new REST proxy, no new composer dependency.fields+dataarrays into typed period rows. It parses all of views/visitors/likes/comments even though v1 charts only the first two, so adding the Calypso tabs later is a frontend-only change.getJpaConfig()readingwindow.jpaConfig, plusgetStatsApiPath()building the proxy base path from the runtime site ID.Config_DataPHP class — emitswindow.jpaConfig = { siteId, apiRoot, nonce }ahead of the boot script (a small take on stats-admin'sOdyssey_Config_Data), wired through the*_boot_dependenciesfilters.Deviations from upstream (documented inline)
WidgetErrorNoticewith Retry) — the hostWidgetRenderPropscontract has nosetErrorchannel.WidgetRootis providers-only: Stats widgets are driven by stats period/quantity attributes, not WooCommerce-Analytics report params, so there's no report-params context.Notes for reviewers
jpaConfig/Config_Datastats infra on the widgets-toolkit branch; whichever merges second reconciles the duplication. This PR is self-contained on the dashboard base so it doesn't depend on the toolkit stack.Related product discussion/links
Does this pull request change what data or activity we track or use?
No. It reads existing Jetpack Stats visit data through the existing
jetpack-stats-adminREST proxy.Testing instructions
Automated:
cd projects/packages/premium-analytics && pnpm test— fetcher URL-composition, normalizer, and series-builder tests (113 total).pnpm typecheck— only the pre-existingwidget-dashboard/widget-primitiveserrors remain (present on the base branch).pnpm build— thetraffic-chartwidget builds andbuild/widgets/registry.phpregistersjpa/traffic-chart.Manual (on a site with both Jetpack — connected, with stats — and Premium Analytics active):
window.jpaConfig = { siteId, apiRoot, nonce }appears before the boot module import.fetchthe proxy path (<apiRoot>jetpack/v4/stats-app/sites/<siteId>/stats/visits?unit=day&quantity=30&stat_fields=views,visitorswith theX-WP-Nonceheader) and confirm it returns afields/datapayload.