feat(insights): audience and engagement UI with fixture mode (NPPD-1649)#240
Draft
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR replaces the “Coming soon” stubs for Insights Tabs 1–2 with a GA4-backed Audience and Engagement UI, adds shared UI atoms/mappers for metric payload rendering (including graceful failure states), and introduces a backend “fixture mode” to return realistic canned data for smoke-testing without a GA4 connection.
Changes:
- Implement Audience and Engagement tab UIs (sections, charts, tables) driven by orchestrator REST endpoints with loading/error/connect-banner lifecycle.
- Add shared metric UI primitives (Scorecard/MetricTable/ChartCard) plus payload mappers (
payloadToCard,toSeries) and corresponding unit tests. - Add PHP fixture payloads and REST-controller switch to serve fixtures when
NEWSPACK_INSIGHTS_FIXTURE_MODEis enabled.
Reviewed changes
Copilot reviewed 38 out of 38 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/newspack-plugin/src/wizards/insights/tabs/EngagementTab.tsx | Replaces the Engagement stub with real tab lifecycle + section composition. |
| plugins/newspack-plugin/src/wizards/insights/tabs/engagement/sections/TimePatternsSection.tsx | Adds Engagement “Time patterns” section with a line chart. |
| plugins/newspack-plugin/src/wizards/insights/tabs/engagement/sections/ReaderSegmentsSection.tsx | Adds Engagement “Reader segments” section with segment tables. |
| plugins/newspack-plugin/src/wizards/insights/tabs/engagement/sections/QualitySection.tsx | Adds Engagement “Overall engagement quality” scorecards. |
| plugins/newspack-plugin/src/wizards/insights/tabs/engagement/sections/ContentEngagementSection.tsx | Adds Engagement “Content engagement” tables. |
| plugins/newspack-plugin/src/wizards/insights/tabs/engagement/engagement.scss | Tab-level SCSS entry importing shared Insights chart/table styles. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/Scorecard.tsx | Wrapper mapping metric payloads to MetricCard props (hidden/overlay/error handling). |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/MetricTable.tsx | Table renderer for rows payloads with overlay/error/degraded/empty handling. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/metrics.ts | Defines metric payload types + mapping helpers (payloadToCard, toSeries). |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/metrics.test.ts | Pure-logic unit tests for payload mappers and duration formatting. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/MetricCard.tsx | Extends MetricCard with overlay/error/duration support. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/insights-ui.test.tsx | Adds unit tests for MetricCard/MetricTable/Scorecard behavior. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/format.ts | Adds formatDuration used by duration metrics. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/ConnectBanner.tsx | New full-tab connect banner shown on tab-level OAuth error. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/ChartCard.tsx | Chart frame that centralizes hidden/overlay/error handling around viz children. |
| plugins/newspack-plugin/src/wizards/insights/tabs/components/_insights-charts.scss | Shared styling for chart/table grids, connect banner, and viz primitives. |
| plugins/newspack-plugin/src/wizards/insights/tabs/AudienceTab.tsx | Replaces the Audience stub with real tab lifecycle + section composition. |
| plugins/newspack-plugin/src/wizards/insights/tabs/AudienceTab.test.tsx | Adds tab-level tests for connect-banner handling and hidden metric skipping. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/viz/PieChart.tsx | New dependency-free SVG donut/pie visualization. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/viz/LineChart.tsx | New dependency-free SVG line chart used across Audience/Engagement. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/viz/BarChart.tsx | New dependency-free bar chart for categorical breakdowns. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/sections/TrafficSourcesSection.tsx | Adds Audience “Traffic sources” section (pie + campaigns table). |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/sections/TimeTrendsSection.tsx | Adds Audience “Time trends” section with line/bar charts. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/sections/ReachSection.tsx | Adds Audience “Reach” scorecards. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/sections/GeographicSection.tsx | Adds Audience “Geographic” tables. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/sections/ContentPerformanceSection.tsx | Adds Audience “Content performance” tables. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/sections/CompositionSection.tsx | Adds Audience “Composition” scorecards + pies. |
| plugins/newspack-plugin/src/wizards/insights/tabs/audience/audience.scss | Tab-level SCSS entry importing shared Insights chart/table styles. |
| plugins/newspack-plugin/src/wizards/insights/hooks/useEngagementData.ts | New data-fetch hook for Engagement endpoint with request-id guarding. |
| plugins/newspack-plugin/src/wizards/insights/hooks/useAudienceData.ts | New data-fetch hook for Audience endpoint with request-id guarding. |
| plugins/newspack-plugin/src/wizards/insights/api/engagement.ts | New Engagement REST client wrapper around @wordpress/api-fetch. |
| plugins/newspack-plugin/src/wizards/insights/api/audience.ts | New Audience REST client wrapper + shared response/query types. |
| plugins/newspack-plugin/includes/wizards/insights/metrics/class-engagement-metric.php | Adds Engagement_Metric::get_fixture() for fixture mode. |
| plugins/newspack-plugin/includes/wizards/insights/metrics/class-audience-metric.php | Adds Audience_Metric::get_fixture() for fixture mode. |
| plugins/newspack-plugin/includes/wizards/insights/fixtures/engagement-fixture.php | Adds date-relative canned Engagement fixture payload (covers overlay/error/hidden). |
| plugins/newspack-plugin/includes/wizards/insights/fixtures/audience-fixture.php | Adds date-relative canned Audience fixture payload (covers overlay/error/hidden). |
| plugins/newspack-plugin/includes/wizards/insights/api/class-engagement-rest-controller.php | Adds fixture-mode branch to return canned Engagement payload. |
| plugins/newspack-plugin/includes/wizards/insights/api/class-audience-rest-controller.php | Adds fixture-mode branch to return canned Audience payload. |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…w tables (NPPD-1649) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion (NPPD-1649) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…warmer copy (NPPD-1649) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…NPPD-1649) The IA consolidation removed the private yes_rate() helper (rate scorecards were cut); re-point its tests to the surviving yes_composition() helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
189b452 to
facc9e3
Compare
c4b4ec4 to
a5fe239
Compare
…c pie (NPPD-1649) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) Addresses Copilot: the hard-coded /wp-admin/admin.php?page=newspack-connections page does not exist and breaks on subdirectory installs. Use the localized, admin_url()-built settingsUrl from the boot config (where the Google connection lives), with a relative fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prefix the delta with an up/down glyph reflecting the factual direction of change; the tone color still conveys good/bad (lowerIsBetter-aware). No glyph for a zero delta. Shared MetricCard, so Audience/Engagement/Subscribers scorecards all get it (Gates/Donors inherit it when their UI lands). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds the React UI for the Audience (Tab 1) and Engagement (Tab 2) Insights tabs, consuming the GA4-backed orchestrator REST endpoints from NPPD-1648. Real data, no placeholder phase — the same UI serves the v1.1 BigQuery swap (the orchestrator handles backend dispatch). Replaces the "Coming soon" stubs and follows the established Gates / Subscribers / Donors tab conventions: top-level tab component, per-section subcomponents, tab-local SVG viz.
Includes the visual polish work to bring the new tabs to parity with the Gates / Subscribers / Donors reference: bordered tables, hand-rolled SVG hover treatments, formatted date labels, consistent section spacing, contained pie chart sizing, and unified failure-state treatment. Backports a rich hover panel to Gates' time-trend chart so all tabs share the same hover behavior.
Closes NPPD-1649.
Stacks on
nppd-1648(#239, the metric orchestrators). Retargets down the chain as the stack merges.What's in this PR
Tab implementations
src/wizards/insights/tabs/audience/. Sections perspecs/audience.md: Reach, Audience composition, Time trends, Traffic sources, Geographic, Content performance.src/wizards/insights/tabs/engagement/. Sections perspecs/engagement.md: Overall engagement quality, Content engagement, Reader segments, Time patterns. The three cut box-plot distributions are not rendered.Shared components
src/wizards/insights/tabs/components/:MetricCard— adds additiveoverlay,error, anddurationprops. Every existing call site (Gates, Subscribers, Donors) unchanged.Scorecard,MetricTable,ChartCard,ConnectBannerMetricNote— unified failure-state component supporting three text variants (custom-dimension-missing, not-configured, generic-error). Used across MetricCard, MetricTable, and ChartCard. Replaces ad-hoc treatments.payloadToCard/toSeriesmappers — centralize the render-rule branching so per-section components stay declarative.Tab-local SVG viz
src/wizards/insights/tabs/audience/viz/:PieChart— dependency-free SVG with contained sizing (never spans full row width). Color-matched legend swatches sized for visibility.LineChart— supports single or multi-series rendering. Custom hover panel anchored to the nearest data point.BarChart— used by Readership by Day of Week and Readership by Hour of Day. Bars flex within container.Engagement imports
LineChartfrom the audience viz directory per the v1 "build tab-local, promote when a second tab needs the same component" plan (NPPD-1594).Data layer
api/audience.tsandapi/engagement.ts— REST clients for/newspack-insights/v1/audienceand/engagementuseAudienceDataanduseEngagementDatahooks — mirror the Gates client / hook pattern. Cache key includes date range and compare flag.Render rules (uniform across both tabs)
hidden_in_v1: true→ skipped entirely. No empty card, no spacer.overlay: { type: 'custom_dimension_missing', dimensions: [...] }→ MetricCard / MetricTable renders the MetricNote overlay with the param name (in<code>) and a setup-docs link. Sans-serif, no code-block background.error: <message>→ MetricCard renders error state via MetricNote.tab_error: 'oauth_not_connected'→ entire tab renders ConnectBanner replacing all sections. CTA links to Newspack → Connections.compare: <prior period payload>→ scorecards render green / red deltas when comparison toggle is on. Suppressed when toggle is off.Visual polish
All Audience and Engagement components match the established reference-tab design language:
.newspack-insights__tableclass fromsections.scss. Borders, dividers, header treatment match Subscribers / Donors / Gates.<title>+ CSS hover treatment; line and bar charts in time trends use a custom dark-bg popover panel positioned near the cursor showing label + value.formatShortDatehelper informat.tsformats YYYYMMDD → "May 10" / "Jun 8" on the time-series chart axes. No date-fns dependency added.--cols-Nmodifiers..newspack-insights__sectionheading, matching Gates.Fixture mode (smoke testing without GA4)
NEWSPACK_INSIGHTS_FIXTURE_MODEPHP constant. When set, REST controllers return canned data fromincludes/wizards/insights/fixtures/audience-fixture.phpandengagement-fixture.phpinstead of dispatching to the orchestrators.Fixtures are date-relative (never stale) and exercise every render path: populated scorecards / tables / charts, the
custom_dimension_missingoverlay state (Newsletter Subscriber Rate, Engagement by Newsletter Status), thehidden_in_v1skip, the generic error state (Local Reader Rate, Top Authors by Avg Engagement Time), and comparison deltas in both directions. Documented in~/Sites/insights-docs/dev-notes.md.How to test
define( 'NEWSPACK_INSIGHTS_FIXTURE_MODE', true );inwp-config.php(withNEWSPACK_INSIGHTS_ENABLEDalso true).wp-admin → Insights → AudienceandEngagement. Every section populates with fixture data.hidden_in_v1metrics don't render (no empty space)falseon a site with no Google connection — both tabs render the full-tab connect banner instead of sections.npm test(component tests undersrc/wizards/insights/+ the pure-logicmetrics.test.ts). Confirmnpm run build,npm run lint:js, andnpm run tscare clean.Out of scope
hidden_in_v1metrics (Top Categories, Reader-Author Affinity, Mobile vs Desktop Content Preferences, Top Authors by Repeat Reader Rate, Article Freshness, Returning Reader Rate strict definition)