Repository navigation
Admin console redesign with Growth and People analytics - #372
Conversation
admin/console:getGrowthSummary reads only the growth tables: 90 days of totals, new paid accounts per day, the last 12 signup cohorts and the top workspaces by spend. GET /api/v1/admin/growth serves it to admin, auditor and billing operators; support has no access. The panel shows headline numbers against the prior week, weekly trends per group, W1/W4 retention and the top workspaces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Growth is counted from Convex now, so the client and server metrics pipelines, retention.engaged, the signed-in ping, feature-level captures (chat, Work, integrations, auth callback, sign-out) and the billing, automation and workspace lifecycle mappings are removed. PostHog keeps pageviews, identify/reset, feature flags and one signup event, renamed user.signed_up. The unused Convex withMetrics wrapper and its aggregation query are deleted; the functionMetrics table and cleanup cron stay until the table is empty. The growth plan is retired into admin-console.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…edesign # Conflicts: # CHANGELOG.md # internal/plans/GROWTH_ANALYTICS_PLAN.md # src/features/chat/components/chat/chatTransport.ts # src/features/chat/components/chat/useChatRetryController.ts # src/features/chat/components/chat/useChatSendController.ts
Bordered panels on the chrome background, grouped sidebar (Insights, Operations, Access), light and dark themes via a cookie, and @overlay/ui controls. Growth gets KPI tiles, a weekly-active chart, shaded retention, Created/Work/Money cards and a 4/12/26-week range (the summary now returns 182 days). People shows name, email, user ID, sign-up date, last active day and plan, newest first, with search by name, email or ID. Audit log is its own page. Also fixes the Tailwind @source paths, which pointed one directory too shallow, so package classes were missing from the admin build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42dc5f2dc0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| name: displayName(row), | ||
| createdAt: row._creationTime, | ||
| lastActiveDay: lastDay?.day, | ||
| plan: await planOf(ctx, row.billingAccountId), |
There was a problem hiding this comment.
Resolve the workspace plan instead of the personal billing account
For a person using a paid workspace, this passes their personal billing account ID to planOf, so the People directory incorrectly labels them as Free. The subscriptions schema explicitly identifies this field as the personal account link, while paid plans belong to workspace billing accounts; convex/billing/workspacePayer.ts resolves those through the workspace instead. Derive the displayed plan from the relevant workspace account, or remove the person-level plan badge rather than reporting paid customers as free.
Useful? React with 👍 / 👎.
[High impact] The PR changes an operator console and removes analytics across the wider app. The PR needs fixes before merging because paid users can appear as Free and changing the Growth range can crash the page.
|
| name: displayName(row), | ||
| createdAt: row._creationTime, | ||
| lastActiveDay: lastDay?.day, | ||
| plan: await planOf(ctx, row.billingAccountId), |
There was a problem hiding this comment.
listUserDirectory reads the plan from row.billingAccountId, but normal profile sync never sets that field. Paid plans belong to workspace billing accounts, which the app finds through workspace preferences. A person using a paid workspace therefore appears as Free in People.
Resolve the person's relevant workspace billing account before checking its subscription. Test with records created through the normal signup and billing paths.
Prompt To Fix With AI
This is a comment left during a code review.
Path: convex/admin/console.ts
Line: 836
Comment:
**Paid people appear free**
`listUserDirectory` reads the plan from `row.billingAccountId`, but normal profile sync never sets that field. Paid plans belong to workspace billing accounts, which the app finds through workspace preferences. A person using a paid workspace therefore appears as Free in People.
Resolve the person's relevant workspace billing account before checking its subscription. Test with records created through the normal signup and billing paths.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| top: `calc((100% - 22px) * ${1 - (hoveredValue ?? 0) / max} - 8px)`, | ||
| }} | ||
| > | ||
| <span>Week to {shortDay(weeks[hovered].endDay)}</span> |
There was a problem hiding this comment.
WeeklyChart keeps hovered when the range changes, but the tooltip reads weeks[hovered].endDay without checking that the week still exists. Hover a later bar in the 12-week view, then use the keyboard to activate the 4-week option without moving the pointer. The next render reads past the shorter array and throws, breaking the page.
Clear or bound the hovered index when the weeks change.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/admin/src/features/admin/growth/GrowthPanel.tsx
Line: 178
Comment:
**Changing range crashes Growth**
`WeeklyChart` keeps `hovered` when the range changes, but the tooltip reads `weeks[hovered].endDay` without checking that the week still exists. Hover a later bar in the 12-week view, then use the keyboard to activate the 4-week option without moving the pointer. The next render reads past the shorter array and throws, breaking the page.
Clear or bound the hovered index when the weeks change.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| const rows = await ctx.db.query('growthWorkspaceUsage') | ||
| .withIndex('by_day_cents', (q) => q.eq('day', day)).order('desc').take(GROWTH_WORKSPACE_ROWS_PER_DAY) |
There was a problem hiding this comment.
Monthly spend drops daily rows
If more than 200 workspaces spend in a day, taking only that day's top 200 can make the 30-day ranking wrong. A steady spender can stay below the daily cutoff while rotating one-day spenders rank above it. That workspace is omitted even if its monthly total belongs in the top ten. Included workspaces also lose spend from days below the cutoff.
Sum all daily workspace totals, or maintain a complete 30-day rollup.
Prompt To Fix With AI
This is a comment left during a code review.
Path: convex/admin/console.ts
Line: 931-932
Comment:
**Monthly spend drops daily rows**
If more than 200 workspaces spend in a day, taking only that day's top 200 can make the 30-day ranking wrong. A steady spender can stay below the daily cutoff while rotating one-day spenders rank above it. That workspace is omitted even if its monthly total belongs in the top ten. Included workspaces also lose spend from days below the cutoff.
Sum all daily workspace totals, or maintain a complete 30-day rollup.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Integration review and QA handoffReviewed and tested head
No remaining code blocker found. Production verification and Preview cleanup follow the native-main release. |
Production verificationMerged as
Remaining limitation: saved PostHog dashboard/alert dependencies were not audited because the connector requires reauthentication. Source/runtime checks verify the retained tracking boundaries; this does not establish external dashboard compatibility. |
Supersedes #366 (Growth panel) and #367 (PostHog cleanup) and adds the redesign approved from the HTML mockup. Everything ships together in this PR.
What changes
Redesign (
apps/admin)@overlay/uicontrols.overlay_admin_themecookie and read on the server, because the nonce CSP doesn't allow an inline theme script.@sourcepaths. They pointed atapps/packages, so@overlay/uipackage classes were missing from the admin build.Growth
getGrowthSummarynow returns 182 days.People
admin/console:listUserDirectoryreturns people newest first, with name, email, user ID, sign-up time, plan (paid when the billing account has an active or trialing paid subscription) and last active day (the latestgrowthUserDaysrow).?q=matches an exact user ID or email, otherwise part of a name or email among the newest 5,000 accounts.Audit log is now its own page, with an allowed/denied pill on each row.
From #367: stops sending usage events to PostHog. The merge with main kept main's new chat preflight code and removed only the PostHog calls from it. The growth analytics plan doc is deleted, since this is its last phase.
Checks
apps/admintypecheck, ESLint and unit tests: 31 tests pass.npx tsc -p convex --noEmitpasses.adminConsole,growthAnalytics,adminAuthorizationParityandadminSeparationpass. This includes a new test for the People fields, ordering and search.Not yet verified: the pages against real data with a signed-in operator session. The Roles, Groups and Catalog API calls were checked only for layout.
After merge
mainonce the matching web build is live, perinternal/develop/convex-workflow.md.🤖 Generated with Claude Code