Skip to content

feat(insights): audience and engagement UI with fixture mode (NPPD-1649)#240

Draft
kmwilkerson wants to merge 11 commits into
nppd-1648-audience-and-engagement-metric-orchestrators-with-ga4-dispatchfrom
nppd-1649-tabs-1-and-2-audience-and-engagement-ui-implementation
Draft

feat(insights): audience and engagement UI with fixture mode (NPPD-1649)#240
kmwilkerson wants to merge 11 commits into
nppd-1648-audience-and-engagement-metric-orchestrators-with-ga4-dispatchfrom
nppd-1649-tabs-1-and-2-audience-and-engagement-ui-implementation

Conversation

@kmwilkerson

@kmwilkerson kmwilkerson commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

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

  • AudienceTab at src/wizards/insights/tabs/audience/. Sections per specs/audience.md: Reach, Audience composition, Time trends, Traffic sources, Geographic, Content performance.
  • EngagementTab at src/wizards/insights/tabs/engagement/. Sections per specs/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 additive overlay, error, and duration props. Every existing call site (Gates, Subscribers, Donors) unchanged.
  • Scorecard, MetricTable, ChartCard, ConnectBanner
  • MetricNote — 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 / toSeries mappers — 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 LineChart from 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.ts and api/engagement.ts — REST clients for /newspack-insights/v1/audience and /engagement
  • useAudienceData and useEngagementData hooks — 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:

  • Tables: use the canonical .newspack-insights__table class from sections.scss. Borders, dividers, header treatment match Subscribers / Donors / Gates.
  • Chart hover: hand-rolled SVG approach (matches reference). Pies use native <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.
  • Date labels: formatShortDate helper in format.ts formats YYYYMMDD → "May 10" / "Jun 8" on the time-series chart axes. No date-fns dependency added.
  • Card layout: max 4 cards per row constraint codified in CSS. Section grids use --cols-N modifiers.
  • Section header spacing: consistent top padding above every .newspack-insights__section heading, matching Gates.
  • Pie charts: card max-width capped so pies render at contained size regardless of available row width. Traffic sources renders as a two-column layout (pie left ~40%, Top Campaigns table right ~60%) so the channel breakdown sits next to the campaigns driving each channel.
  • Failure states: no placeholder dash above the MetricNote. Card shows only icon + note when in any overlay or error state.

Fixture mode (smoke testing without GA4)

NEWSPACK_INSIGHTS_FIXTURE_MODE PHP constant. When set, REST controllers return canned data from includes/wizards/insights/fixtures/audience-fixture.php and engagement-fixture.php instead of dispatching to the orchestrators.

Fixtures are date-relative (never stale) and exercise every render path: populated scorecards / tables / charts, the custom_dimension_missing overlay state (Newsletter Subscriber Rate, Engagement by Newsletter Status), the hidden_in_v1 skip, 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

  1. Set define( 'NEWSPACK_INSIGHTS_FIXTURE_MODE', true ); in wp-config.php (with NEWSPACK_INSIGHTS_ENABLED also true).
  2. Hard-reload wp-admin → Insights → Audience and Engagement. Every section populates with fixture data.
  3. Verify graceful states:
    • Custom dimension missing overlay shows on the affected scorecards with a clear note + setup-docs link
    • hidden_in_v1 metrics don't render (no empty space)
    • Generic error state shows on the affected cards
  4. Toggle "Compare to previous period" on and confirm scorecards show green and red deltas. Toggle off and confirm deltas disappear.
  5. Hover the time-trend line and bar charts on both Audience and Engagement (and Gates) — confirm the custom dark-bg popover shows label + value.
  6. Hover pies (Traffic Sources, Device Breakdown, Logged-In vs Anonymous) — minimal native tooltip behavior, no custom popover (intentional).
  7. Verify visual parity side-by-side with Gates / Subscribers / Donors tabs: table styling, section spacing, card layouts, hover treatments.
  8. Set the constant to false on a site with no Google connection — both tabs render the full-tab connect banner instead of sections.
  9. With a real GA4 connection (constant off), confirm live metrics populate with no UI changes.
  10. Run npm test (component tests under src/wizards/insights/ + the pure-logic metrics.test.ts). Confirm npm run build, npm run lint:js, and npm run tsc are clean.

Out of scope

  • BigQuery catalog and the v1.1 backend swap (NPPD-1630)
  • v1.1 hidden_in_v1 metrics (Top Categories, Reader-Author Affinity, Mobile vs Desktop Content Preferences, Top Authors by Repeat Reader Rate, Article Freshness, Returning Reader Rate strict definition)
  • v1.1 boot-time custom dimension probe (partial folding into v1 via per-call check)
  • Promoting tab-local viz components to a shared library (NPPD-1594, triggers when Tab 3 needs them)
  • Sortable tables (v1.1 ticket across all tabs)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_MODE is 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.

Comment thread plugins/newspack-plugin/src/wizards/insights/tabs/components/ConnectBanner.tsx Outdated
Comment thread plugins/newspack-plugin/src/wizards/insights/tabs/components/MetricTable.tsx Outdated
kmwilkerson and others added 8 commits June 8, 2026 19:47
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>
@kmwilkerson kmwilkerson force-pushed the nppd-1648-audience-and-engagement-metric-orchestrators-with-ga4-dispatch branch from 189b452 to facc9e3 Compare June 9, 2026 00:57
@kmwilkerson kmwilkerson force-pushed the nppd-1649-tabs-1-and-2-audience-and-engagement-ui-implementation branch from c4b4ec4 to a5fe239 Compare June 9, 2026 00:57
kmwilkerson and others added 3 commits June 8, 2026 20:31
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants