Conversation
- introduces a `commentAnalytics` private flag that exposes a new `GET /stats/comments` admin endpoint and an analytics rail alongside the moderation list in the posts app
- backend aggregates totals, daily series, and top posts/members from `comments` + `comment_reports`; controller gates on `labs.isSet('commentAnalytics')` so it 404s when the flag is off
- ships behind a private flag so we can iterate on the analytics shape and UI without committing to an API surface or exposing an unfinished view to users
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- backend `getOverview` now returns `previousTotals` for the length-matched window immediately preceding the requested range, computed in the caller-supplied IANA timezone via the shared `getDateBoundaries` / `applyDateFilter` utilities (matches how every other stats endpoint handles `date_from` / `date_to` / `timezone`) - frontend renders an icon-only colored up/down badge next to each KPI value with a Radix tooltip explaining the comparison; "unchanged" hides the badge entirely and "All time" suppresses comparison since there is no preceding period - analytics rail is now sticky on `lg+` so the moderation list scrolls underneath without scrolling the analytics out of view - still gated behind the `commentAnalytics` private labs flag
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Adds a comment analytics overview rail to the moderation page in the posts app, behind a new
commentAnalyticsprivate labs flag. The rail summarizes commenting activity for a chosen date range and lets moderators drill in by clicking through to filtered views of the comment list.What's new:
commentAnalyticsprivate labs flag (ghost/core/core/shared/labs.js) plus a toggle in private features settings.GET /stats/commentsadmin endpoint (api/endpoints/stats.js). Returns404when the flag is off. Acceptsdate_from,date_to,timezone. Permissions:comments.browse(matches the moderation page).CommentsStatsService.getOverview()that aggregates fromcomments+comment_reports:totals— total comments, distinct commenters, distinct reported commentspreviousTotals— same shape for the length-matched window immediately preceding the selected range (used for trend badges;nullwhen the range is unbounded)series— per-day buckets of comments / commenters / reported (used by the bar chart)topPosts/topMembers— top 25 by comment count in rangegetDateBoundaries/applyDateFilterutilities — same pattern every other stats endpoint already uses.apps/posts/src/views/comments/) consumed via a newuseCommentsOverviewhook inadmin-x-framework:STATS_RANGES.reported=trueon the Reported tab). Top-post and top-commenter rows filter bypost=/author=. Bar clicks are only enabled on daily-bucket ranges (< 91 days) since weekly/monthly buckets would mislead.lg+so the moderation list scrolls underneath without scrolling the analytics out of view.KpiTabValuegained two optional props to support this view:diffTooltip(renders the diff badge inside a RadixTooltipProviderso it portals to body and escapes ancestor overflow/stacking) anddiffIconOnly(renders the badge as an icon-only circle instead of the default percent-and-icon pill).Opening as a draft so the team can review the API shape and UI before this leaves the private flag.
Test plan
ghost/core/test/unit/server/services/comments/comments-stats-service.test.js— 7 tests covering empty data, full payload shape, reports-only days,Date→YYYY-MM-DDformatting,previousTotalsreturned for bounded ranges,previousTotals = nullfor unbounded ranges, and a timezone case verifying PST→UTC bound conversion.ghost/core/test/e2e-api/admin/stats.test.js"Comments overview" — 2 tests covering the response shape and the date-range query params.pnpm --filter posts run lint:code,pnpm --filter shade run lint:code,pnpm --filter @tryghost/admin-x-framework run lint,cd ghost/core && pnpm lint:server.apps/posts,apps/shade,ghost/coreall clean.commentAnalyticslabs flag and exercise the rail across allSTATS_RANGESvalues (Today, Last 7/30/90 days, YTD, Last 12 months, All time). Verify the KPI tabs, trend badges + tooltips, sticky scroll behavior, bar-click filtering on daily ranges, and top-posts / top-commenters drill-throughs. Also verify the404when the flag is off.