feat(admin): enhance analytics dashboard with improved charts and UX#488
feat(admin): enhance analytics dashboard with improved charts and UX#488saddlepaddle merged 7 commits intomainfrom
Conversation
Add custom event tracking for key user actions: - Marketing: download_clicked, waitlist_clicked - Desktop: desktop_opened, auth_started, auth_completed, workspace_created/opened/closed/deleted, terminal_opened - Fix user identity to use database user ID consistently across all apps (was using Clerk ID in web/admin which differed from desktop) Also adds posthog-node for reliable server-side tracking in desktop main process. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace separate activation and onboarding funnels with one full journey funnel: site visit → download → desktop opened → auth completed → terminal opened This provides a clearer view of where users drop off across the entire user journey from marketing to product activation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add WAU trend chart with workspace_created event filtering - Add traffic sources chart with breakdown by referring domain - Add signups trend chart with daily counts - Add revenue trend chart (stub data for now) - Add YC Demo Day countdown widget - Add time range pickers (7d/30d/90d/180d) to all charts - Add week picker for leaderboard - Add cohort size column to retention table - Fix sidebar active route highlighting - Improve empty states with consistent styling across all charts - Use Tailwind 500 colors for traffic sources bars 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WalkthroughAdds a complete admin analytics feature set: new UI components and charts, a PostHog client, TRPC analytics router, env/schema and CI wiring for PostHog keys, a small shared constant change, UI/breadcrumb updates, and a dependency on recharts. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Admin Dashboard (Browser)
participant TRPCClient as TRPC Client
participant TRPCServer as TRPC Server / analyticsRouter
participant PostHogAPI as PostHog API
participant DB as Database
rect `#F2F8FF`
Note over Browser,TRPCClient: Dashboard mounts and issues analytics queries
Browser->>TRPCClient: useQuery(analytics.getWAUTrend / getSignups / ...)
end
rect `#FFF5F0`
Note over TRPCClient,TRPCServer: TRPC invokes analytics procedures
TRPCClient->>TRPCServer: invoke analytics endpoint
TRPCServer->>PostHogAPI: executeQuery / executeHogQLQuery / executeFunnelQuery (cached)
PostHogAPI-->>TRPCServer: results (or error)
TRPCServer->>DB: enrich (e.g., join user metadata for leaderboard)
DB-->>TRPCServer: user records
TRPCServer-->>TRPCClient: formatted analytics response
end
rect `#F5FFF5`
Note over TRPCClient,Browser: Render visualizations
TRPCClient-->>Browser: responses
Browser->>Browser: render charts/tables/cards (recharts + UI primitives)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (8)
apps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsx (1)
18-18: Unusederror.messageproperty.The
errorprop is typed with{ message: string }but the actual message is not displayed—the component shows a hardcoded "Failed to load" instead. Consider either using the message or simplifying the error type.Option 1: Use the error message
- <p className="text-destructive text-sm">Failed to load</p> + <p className="text-destructive text-sm">{error.message || "Failed to load"}</p>Option 2: Simplify the error type if message is intentionally hidden
- error?: { message: string } | null; + error?: Error | null;Also applies to: 44-45
apps/admin/package.json (1)
36-36: Consider evaluating compatibility with recharts 3.x.recharts@2.15.4 is a valid, stable version with no known security vulnerabilities. However, it's significantly outdated—the latest release is 3.6.0. If your application is compatible with the 3.x series, updating would provide access to newer features and improvements.
apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx (1)
80-83: Consider using Next.jsLinkfor client-side navigation.Using raw
<a>tags triggers full page reloads. For smoother navigation within the admin dashboard, prefernext/link:🔎 Proposed fix
+import Link from "next/link"; ... - <a href={item.url}> + <Link href={item.url}> <item.icon className="size-4" /> {item.title} - </a> + </Link>Apply the same change to line 117 for section items.
apps/admin/src/app/(dashboard)/page.tsx (1)
33-37: Consider extracting the time range parsing logic.The pattern
Number.parseInt(range.slice(1, -1), 10)is repeated 5 times. A small helper would improve readability and maintainability:🔎 Suggested helper
const parseDays = (range: TimeRange): number => Number.parseInt(range.slice(1, -1), 10); // Usage: trpc.analytics.getWAUTrend.queryOptions({ days: parseDays(wauRange) })apps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsx (1)
17-24: Consider making week columns dynamic for future flexibility.The current
CohortRowinterface hardcodes week0-week4. If retention periods need to change, this requires interface updates. A more flexible approach:🔎 Alternative structure
interface CohortRow { cohort: string; weeks: WeekData[]; // Dynamic array instead of fixed properties }This would require corresponding changes to the table rendering logic. However, the current approach is acceptable if the 5-week retention view is a stable requirement.
apps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsx (1)
14-31: Consider ISO 8601 week convention for international users.The week calculation uses Sunday as the first day (US convention). For international applications, ISO 8601 specifies Monday as the week start. Consider making this configurable or documenting the US-centric behavior.
The date formatting is also hardcoded to
"en-US"locale.Optional: Support for configurable week start day
interface WeekPickerProps { /** Week offset from current week (0 = this week, -1 = last week, etc.) */ weekOffset: number; onChange: (offset: number) => void; /** Minimum offset (how far back can we go). Default -12 (12 weeks back) */ minOffset?: number; + /** First day of week (0 = Sunday, 1 = Monday). Default 0 */ + weekStartsOn?: 0 | 1; } -function getWeekLabel(offset: number): string { +function getWeekLabel(offset: number, weekStartsOn = 0): string { const now = new Date(); const startOfWeek = new Date(now); - // Go to start of current week (Sunday) - startOfWeek.setDate(now.getDate() - now.getDay() + offset * 7); + // Go to start of current week + const dayOfWeek = now.getDay(); + const daysToSubtract = (dayOfWeek + 7 - weekStartsOn) % 7; + startOfWeek.setDate(now.getDate() - daysToSubtract + offset * 7);apps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsx (1)
49-122: LGTM! Well-structured chart component with good state handling.The component correctly implements loading, error, and empty states with consistent styling. The dynamic tick interval calculation (line 58) provides reasonable spacing, and the currency formatting is properly internationalized.
Optional: Add defensive type check in formatter
Line 105 uses a type assertion that could be made safer:
<ChartTooltip content={ <ChartTooltipContent - formatter={(value) => formatCurrency(value as number)} + formatter={(value) => + typeof value === 'number' ? formatCurrency(value) : String(value) + } /> } />apps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsx (1)
42-126: LGTM! Clear funnel visualization with helpful conversion metrics.The component correctly renders funnel data with:
- Consistent loading, error, and empty states
- Custom tooltip displaying both user counts and conversion rates
- Locale-aware number formatting
- Appropriate chart dimensions and margins
Optional: Add defensive null check in tooltip formatter
Line 103 accesses
item.payload.conversionRatewithout a null check. While the TypeScript interface makes this required, adding a defensive check improves runtime safety:<ChartTooltipContent formatter={(value, _name, item) => ( <div className="flex flex-col gap-1"> <span>{value.toLocaleString()} users</span> - <span className="text-muted-foreground"> - {item.payload.conversionRate.toFixed(1)}% conversion - </span> + {item.payload.conversionRate != null && ( + <span className="text-muted-foreground"> + {item.payload.conversionRate.toFixed(1)}% conversion + </span> + )} </div> )} />
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
apps/admin/public/yc-logo.pngis excluded by!**/*.pngbun.lockis excluded by!**/*.lock
📒 Files selected for processing (34)
apps/admin/package.jsonapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsxapps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/AppSidebarHeader.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/index.tsapps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/index.tsapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/index.tsapps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsxapps/admin/src/app/(dashboard)/components/MetricCard/index.tsapps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/index.tsapps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsxapps/admin/src/app/(dashboard)/components/RevenueTrendChart/index.tsapps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsxapps/admin/src/app/(dashboard)/components/SignupsTrendChart/index.tsapps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/index.tsapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsxapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/index.tsapps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsxapps/admin/src/app/(dashboard)/components/WAUTrendChart/index.tsapps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsxapps/admin/src/app/(dashboard)/components/WeekPicker/index.tsapps/admin/src/app/(dashboard)/layout.tsxapps/admin/src/app/(dashboard)/page.tsxapps/admin/src/app/layout.tsxpackages/shared/src/constants.tspackages/trpc/src/env.tspackages/trpc/src/lib/posthog-client.tspackages/trpc/src/root.tspackages/trpc/src/router/analytics/analytics.tspackages/trpc/src/router/analytics/index.ts
💤 Files with no reviewable changes (1)
- apps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/AppSidebarHeader.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Avoid using any type in TypeScript - maintain type safety unless absolutely necessary
Files:
apps/admin/src/app/(dashboard)/components/DemoCountdown/index.tspackages/shared/src/constants.tsapps/admin/src/app/(dashboard)/components/SignupsTrendChart/index.tsapps/admin/src/app/(dashboard)/components/RevenueTrendChart/index.tsapps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsxapps/admin/src/app/(dashboard)/layout.tsxapps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/index.tsapps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsxapps/admin/src/app/(dashboard)/page.tsxapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/index.tspackages/trpc/src/router/analytics/index.tsapps/admin/src/app/(dashboard)/components/MetricCard/index.tsapps/admin/src/app/layout.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsxpackages/trpc/src/env.tsapps/admin/src/app/(dashboard)/components/RetentionCard/index.tsapps/admin/src/app/(dashboard)/components/WeekPicker/index.tsapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsxpackages/trpc/src/router/analytics/analytics.tsapps/admin/src/app/(dashboard)/components/LeaderboardTable/index.tsapps/admin/src/app/(dashboard)/components/WAUTrendChart/index.tspackages/trpc/src/root.tsapps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/index.tsapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsxpackages/trpc/src/lib/posthog-client.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Run Biome for formatting, linting, import organization, and safe fixes at the root level using bun run lint:fix
Files:
apps/admin/src/app/(dashboard)/components/DemoCountdown/index.tspackages/shared/src/constants.tsapps/admin/src/app/(dashboard)/components/SignupsTrendChart/index.tsapps/admin/src/app/(dashboard)/components/RevenueTrendChart/index.tsapps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsxapps/admin/src/app/(dashboard)/layout.tsxapps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/index.tsapps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsxapps/admin/src/app/(dashboard)/page.tsxapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/index.tspackages/trpc/src/router/analytics/index.tsapps/admin/src/app/(dashboard)/components/MetricCard/index.tsapps/admin/src/app/layout.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsxpackages/trpc/src/env.tsapps/admin/src/app/(dashboard)/components/RetentionCard/index.tsapps/admin/src/app/(dashboard)/components/WeekPicker/index.tsapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsxpackages/trpc/src/router/analytics/analytics.tsapps/admin/src/app/(dashboard)/components/LeaderboardTable/index.tsapps/admin/src/app/(dashboard)/components/WAUTrendChart/index.tspackages/trpc/src/root.tsapps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/index.tsapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsxpackages/trpc/src/lib/posthog-client.ts
**/{components,features}/**/*.{ts,tsx,test.ts,test.tsx,stories.tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Co-locate component dependencies (utils, hooks, constants, config, tests, stories) next to the file using them
Files:
apps/admin/src/app/(dashboard)/components/DemoCountdown/index.tsapps/admin/src/app/(dashboard)/components/SignupsTrendChart/index.tsapps/admin/src/app/(dashboard)/components/RevenueTrendChart/index.tsapps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsxapps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/index.tsapps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsxapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/index.tsapps/admin/src/app/(dashboard)/components/MetricCard/index.tsapps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/index.tsapps/admin/src/app/(dashboard)/components/WeekPicker/index.tsapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/index.tsapps/admin/src/app/(dashboard)/components/WAUTrendChart/index.tsapps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/index.tsapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
**/{components,features}/**/[!.]*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Organize project structure with one folder per component: ComponentName/ComponentName.tsx with index.ts barrel export
Files:
apps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsxapps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsxapps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsxapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsxapps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsxapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
**/*.{tsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use React + TailwindCSS v4 + shadcn/ui for UI development
Files:
apps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsxapps/admin/src/app/(dashboard)/layout.tsxapps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsxapps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsxapps/admin/src/app/(dashboard)/page.tsxapps/admin/src/app/layout.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsxapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsxapps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsxapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
**/{components,features}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
**/{components,features}/**/*.tsx: Nest components in parent's components/ folder if used only once, promote to highest shared parent's components/ if used 2+ times
Use one component per file - do not combine multiple components in a single file
Files:
apps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsxapps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsxapps/admin/src/app/(dashboard)/components/RevenueTrendChart/RevenueTrendChart.tsxapps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsxapps/admin/src/app/(dashboard)/components/FunnelChart/FunnelChart.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsxapps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsxapps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsxapps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsxapps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsxapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-18T23:19:10.415Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.415Z
Learning: Applies to **/*.{tsx,css} : Use React + TailwindCSS v4 + shadcn/ui for UI development
Applied to files:
apps/admin/package.json
📚 Learning: 2025-12-18T23:19:10.415Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.415Z
Learning: Applies to **/{components,features}/**/[!.]*.tsx : Organize project structure with one folder per component: ComponentName/ComponentName.tsx with index.ts barrel export
Applied to files:
apps/admin/src/app/(dashboard)/components/WeekPicker/index.ts
📚 Learning: 2025-12-18T23:19:10.415Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T23:19:10.415Z
Learning: Applies to src/components/{ui,ai-elements,react-flow}/**/*.tsx : Use kebab-case single files for shadcn/ui components in src/components/ui/, src/components/ai-elements, and src/components/react-flow/ directories
Applied to files:
apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
🧬 Code graph analysis (8)
apps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsx (2)
apps/admin/src/app/(dashboard)/components/TimeRangePicker/index.ts (2)
TimeRange(1-1)TimeRangePicker(1-1)packages/ui/src/components/ui/toggle-group.tsx (1)
ToggleGroup(83-83)
apps/admin/src/app/(dashboard)/layout.tsx (1)
packages/ui/src/components/ui/breadcrumb.tsx (4)
BreadcrumbLink(106-106)BreadcrumbItem(105-105)BreadcrumbSeparator(108-108)BreadcrumbPage(107-107)
apps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsx (2)
apps/admin/src/app/(dashboard)/components/WeekPicker/index.ts (1)
WeekPicker(1-1)packages/ui/src/components/ui/button.tsx (1)
Button(60-60)
apps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsx (2)
packages/ui/src/components/ui/card.tsx (5)
Card(85-85)CardHeader(86-86)CardTitle(88-88)CardDescription(90-90)CardContent(91-91)packages/ui/src/components/ui/skeleton.tsx (1)
Skeleton(13-13)
apps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsx (2)
apps/admin/src/app/(dashboard)/components/DemoCountdown/index.ts (1)
DemoCountdown(1-1)packages/ui/src/components/ai-elements/image.tsx (1)
Image(9-24)
apps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsx (4)
packages/ui/src/components/ui/chart.tsx (4)
ChartConfig(11-19)ChartContainer(352-352)ChartTooltip(353-353)ChartTooltipContent(354-354)apps/admin/src/app/(dashboard)/components/WAUTrendChart/index.ts (1)
WAUTrendChart(1-1)packages/ui/src/components/ui/card.tsx (5)
Card(85-85)CardHeader(86-86)CardTitle(88-88)CardDescription(90-90)CardContent(91-91)packages/ui/src/components/ui/skeleton.tsx (1)
Skeleton(13-13)
apps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsx (3)
apps/admin/src/app/(dashboard)/components/RetentionCard/index.ts (1)
RetentionCard(1-1)packages/ui/src/components/ui/card.tsx (5)
Card(85-85)CardHeader(86-86)CardTitle(88-88)CardDescription(90-90)CardContent(91-91)packages/ui/src/components/ui/skeleton.tsx (1)
Skeleton(13-13)
packages/trpc/src/lib/posthog-client.ts (1)
packages/trpc/src/env.ts (1)
env(4-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy Docs
- GitHub Check: Deploy API
- GitHub Check: Deploy Admin
- GitHub Check: Deploy Web
- GitHub Check: Build
- GitHub Check: Lint
🔇 Additional comments (32)
apps/admin/src/app/(dashboard)/components/MetricCard/index.ts (1)
1-1: LGTM!Clean barrel export following the component organization guidelines.
apps/admin/src/app/(dashboard)/components/MetricCard/MetricCard.tsx (1)
23-54: Well-structured component with proper state handling.The component correctly handles the four states (loading, error, value present, no data) in the right priority order. The default formatter using
toLocaleString()is a sensible choice, and theheaderActionslot provides good flexibility.apps/admin/src/app/(dashboard)/components/WAUTrendChart/WAUTrendChart.tsx (1)
1-110: LGTM! Well-structured chart component.The component demonstrates solid React and TypeScript practices with proper error handling, loading states, and defensive coding. The empty state styling with dashed borders aligns with the PR's UX consistency goals.
packages/trpc/src/router/analytics/analytics.ts (2)
32-54: LGTM! Helper functions are well-implemented.Both
formatFunnelResultsandformatWeekDatahandle edge cases properly with defensive null checks and correct percentage calculations.
57-96: LGTM! These endpoints are safely implemented.The
getFullJourneyFunnel,getRetention,getTrafficSources, andgetRevenueTrendendpoints use structured queries or hardcoded values, avoiding SQL injection risks. The revenue endpoint appropriately returns stub data as documented.Also applies to: 145-170, 272-318, 320-347
packages/trpc/src/lib/posthog-client.ts (2)
132-161: Verify PostHog credentials before API calls.The API authentication (lines 145-147) depends on
env.POSTHOG_API_KEYandenv.POSTHOG_PROJECT_ID. As flagged inpackages/trpc/src/env.ts, these variables currently allow empty strings. Once that validation is fixed, this implementation will be safe.The caching strategy and error handling are well-implemented.
The critical validation issue must be addressed in
packages/trpc/src/env.ts(lines 8-9) to ensure these credentials are non-empty.
163-233: LGTM! Query helper functions are well-structured.The
executeFunnelQuery,executeHogQLQuery, andexecuteRetentionQueryfunctions properly wrap queries with appropriate defaults and structure. The TypeScript typing ensures type safety throughout.packages/trpc/src/router/analytics/index.ts (1)
1-1: LGTM! Standard barrel export.apps/admin/src/app/(dashboard)/layout.tsx (1)
42-42: LGTM! Breadcrumb text updates align with rebranding.The changes from "Admin Panel" to "Superset" and "Dashboard" to "Home" are purely presentational and consistent with the dashboard enhancements in this PR.
Also applies to: 46-46
apps/admin/src/app/(dashboard)/components/WAUTrendChart/index.ts (1)
1-1: LGTM! Standard barrel export following project conventions.apps/admin/src/app/(dashboard)/components/DemoCountdown/index.ts (1)
1-1: LGTM! Clean barrel export following project conventions.The barrel export pattern aligns with the project structure guidelines and enables clean component imports. Based on learnings, this follows the expected pattern: one folder per component with index.ts barrel export.
apps/admin/src/app/(dashboard)/components/FunnelChart/index.ts (1)
1-1: LGTM! Consistent barrel export pattern.apps/admin/src/app/(dashboard)/components/WeekPicker/index.ts (1)
1-1: LGTM! Follows established component organization pattern.apps/admin/src/app/(dashboard)/components/SignupsTrendChart/index.ts (1)
1-1: LGTM! Clean barrel export.apps/admin/src/app/(dashboard)/components/LeaderboardTable/index.ts (1)
1-1: LGTM! Consistent with other dashboard components.apps/admin/src/app/(dashboard)/components/RevenueTrendChart/index.ts (1)
1-1: LGTM! Standard barrel export.apps/admin/src/app/(dashboard)/components/TimeRangePicker/index.ts (1)
1-1: LGTM! Proper use of type-only export.Correctly uses the
typekeyword for the TimeRange type export, which is a TypeScript best practice that enables better tree-shaking and clearer intent for type-only exports.apps/admin/src/app/(dashboard)/components/RetentionCard/index.ts (1)
1-1: LGTM! Clean barrel export completing the dashboard components suite.apps/admin/src/app/(dashboard)/components/TrafficSourcesChart/index.ts (1)
1-1: LGTM!Clean barrel export following the project's component organization pattern.
apps/admin/src/app/layout.tsx (1)
25-28: LGTM!Metadata updates align with the dashboard branding changes across the PR.
apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx (1)
62-65: LGTM on the active route detection logic.The
isActivehelper correctly handles the root path with an exact match while usingstartsWithfor nested routes, preventing false positives on "/".apps/admin/src/app/(dashboard)/page.tsx (1)
65-142: LGTM on the dashboard composition.Clean structure with consistent patterns across all chart components:
- Each chart receives
data,isLoading,error, andheaderActionprops uniformly.- Time range state is properly lifted and controlled at the page level.
- Loading and error states are delegated appropriately to child components.
apps/admin/src/app/(dashboard)/components/TimeRangePicker/TimeRangePicker.tsx (1)
19-35: LGTM!Clean implementation with proper handling of the empty-value case in
onValueChange. The type assertion on line 24 is acceptable here since theToggleGroupItemvalues are constrained to theTIME_RANGESarray.packages/trpc/src/root.ts (1)
4-4: LGTM!Clean integration of the analytics router following the established pattern for router registration.
apps/admin/src/app/(dashboard)/components/SignupsTrendChart/SignupsTrendChart.tsx (1)
39-111: LGTM!Well-structured chart component with:
- Proper loading, error, and empty state handling
- Consistent empty-state styling with dashed borders (matching PR objectives)
- Reasonable tick interval calculation for readability
- Clean separation of chart configuration
apps/admin/src/app/(dashboard)/components/RetentionCard/RetentionCard.tsx (2)
32-55: LGTM on RetentionCell implementation.The color scale logic is clear and the inline styles are appropriate for runtime-computed colors. Good handling of
nullrates with a dash placeholder.
57-115: LGTM on the RetentionCard structure.
- Consistent loading/error/empty state handling matching other dashboard components
- Clean table structure with proper accessibility (semantic
<table>,<thead>,<tbody>)- Empty state uses consistent dashed-border styling
apps/admin/src/app/(dashboard)/components/WeekPicker/WeekPicker.tsx (1)
41-63: LGTM! Well-implemented week navigation control.The component correctly handles boundary conditions, prevents layout shift with fixed-width labels, and provides clear visual feedback for disabled states. The suppressHydrationWarning on line 32 would be appropriate if this component displayed real-time data.
apps/admin/src/app/(dashboard)/components/TrafficSourcesChart/TrafficSourcesChart.tsx (1)
54-130: LGTM! Excellent implementation of traffic sources visualization.The component correctly:
- Handles all state conditions (loading, error, empty, data) with appropriate UI
- Uses Tailwind 500 palette colors for consistent visual design
- Implements safe color cycling with modulo operator (line 120)
- Formats numbers with locale awareness via
toLocaleString()- Provides clear tooltips with visitor counts
The vertical bar chart layout with right-aligned labels provides good readability for source names.
apps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsx (1)
40-129: Well-structured leaderboard with good UX patterns.The component implements:
- Detailed loading skeletons matching the final layout
- Consistent error and empty state messaging
- Clear ranking display with user avatars
- Flexible header action support
- Configurable count label
The table layout and styling provide good readability.
apps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsx (1)
29-39: The YC logo asset exists atapps/admin/public/yc-logo.pngand will resolve correctly.packages/shared/src/constants.ts (1)
42-42: Verify backwards compatibility if this changes an existing cookie name.The
POSTHOG_COOKIE_NAMEconstant is properly centralized and consistently used across all apps (web, admin, marketing, docs) as thepersistence_nameparameter in PostHog SDK initialization. The value"superset"is valid and compliant with cookie naming standards.However, if this constant was previously set to a different value (e.g.,
"ph_superset"), existing users will not be recognized under the new cookie name, potentially fragmenting analytics data. Verify:
- Was this constant previously set to a different value?
- Do you need to preserve user tracking continuity by reading from old cookie names if the new one doesn't exist?
| useEffect(() => { | ||
| const timer = setInterval(() => { | ||
| setDaysLeft(calculateDaysLeft()); | ||
| }, 100); | ||
|
|
||
| return () => clearInterval(timer); | ||
| }, []); |
There was a problem hiding this comment.
Excessive update frequency wastes CPU and battery.
The 100ms interval (line 20) updates the countdown 10 times per second, which is unnecessary for a day counter. Even with 6 decimal places, updating every second (1000ms) would provide smooth animation while being 10× more efficient.
This impacts performance, especially on mobile devices and battery-powered laptops.
Recommended fix: Change to 1 second interval
useEffect(() => {
const timer = setInterval(() => {
setDaysLeft(calculateDaysLeft());
- }, 100);
+ }, 1000); // Update once per second
return () => clearInterval(timer);
}, []);Alternatively, consider reducing decimal precision to 2-3 places and updating less frequently:
- const fraction = (daysLeft - wholeDays).toFixed(6).slice(1); // ".466739"
+ const fraction = (daysLeft - wholeDays).toFixed(2).slice(1); // ".47"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| const timer = setInterval(() => { | |
| setDaysLeft(calculateDaysLeft()); | |
| }, 100); | |
| return () => clearInterval(timer); | |
| }, []); | |
| useEffect(() => { | |
| const timer = setInterval(() => { | |
| setDaysLeft(calculateDaysLeft()); | |
| }, 1000); // Update once per second | |
| return () => clearInterval(timer); | |
| }, []); |
🤖 Prompt for AI Agents
In apps/admin/src/app/(dashboard)/components/DemoCountdown/DemoCountdown.tsx
around lines 18-24, the setInterval is currently 100ms which updates the day
counter 10×/sec and wastes CPU/battery; change the interval to 1000ms (1 second)
so the countdown updates once per second, keep the existing cleanup, and
optionally reduce decimal precision in calculateDaysLeft to 2-3 places (or
format the displayed value) to further lower update frequency and rendering
work.
| <Avatar className="h-8 w-8"> | ||
| <AvatarImage src={entry.avatarUrl ?? undefined} /> | ||
| <AvatarFallback> | ||
| {entry.name | ||
| .split(" ") | ||
| .map((n) => n[0]) | ||
| .join("") | ||
| .toUpperCase() | ||
| .slice(0, 2)} | ||
| </AvatarFallback> |
There was a problem hiding this comment.
Avatar fallback handles edge cases inconsistently.
The initials generation logic has edge cases that could produce poor UX:
- Empty or whitespace-only names produce no initials
- Single-word names produce one character instead of two
- Names with multiple consecutive spaces could access undefined
While these won't cause crashes, they degrade the user experience.
Recommended fix: Defensive initials generation
<AvatarFallback>
- {entry.name
- .split(" ")
- .map((n) => n[0])
- .join("")
- .toUpperCase()
- .slice(0, 2)}
+ {entry.name
+ .trim()
+ .split(/\s+/) // Split on any whitespace
+ .filter(n => n.length > 0)
+ .map((n) => n[0])
+ .join("")
+ .toUpperCase()
+ .slice(0, 2) || "?"} // Fallback for empty names
</AvatarFallback>🤖 Prompt for AI Agents
In
apps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsx
around lines 99 to 108, the AvatarFallback initials logic is brittle: it can
produce empty strings for blank/whitespace names, return only one character for
single-word names, and may read undefined when encountering extra spaces;
replace that logic with a defensive initials generator that trims the name,
splits on one-or-more whitespace, filters out empty segments, uppercases parts,
then returns either the first two letters of the first segment for single-word
names or the first letters of the first two segments for multi-word names
(fallback to a placeholder like "?" or the first available character if nothing
valid) so initials are always 1–2 characters and never undefined.
| POSTHOG_API_KEY: z.string(), | ||
| POSTHOG_PROJECT_ID: z.string(), |
There was a problem hiding this comment.
Enforce non-empty validation for PostHog credentials.
The PostHog environment variables lack .min(1) validation, allowing empty strings to pass. This is inconsistent with other required credentials (CLERK_SECRET_KEY, BLOB_READ_WRITE_TOKEN) and will cause runtime failures when the PostHog client attempts API authentication in packages/trpc/src/lib/posthog-client.ts (lines 145-147).
🔎 Proposed fix
server: {
CLERK_SECRET_KEY: z.string().min(1),
BLOB_READ_WRITE_TOKEN: z.string().min(1),
- POSTHOG_API_KEY: z.string(),
- POSTHOG_PROJECT_ID: z.string(),
+ POSTHOG_API_KEY: z.string().min(1),
+ POSTHOG_PROJECT_ID: z.string().min(1),
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| POSTHOG_API_KEY: z.string(), | |
| POSTHOG_PROJECT_ID: z.string(), | |
| POSTHOG_API_KEY: z.string().min(1), | |
| POSTHOG_PROJECT_ID: z.string().min(1), |
🤖 Prompt for AI Agents
In packages/trpc/src/env.ts around lines 8 to 9, the POSTHOG_API_KEY and
POSTHOG_PROJECT_ID schemas are missing .min(1) checks so empty strings pass
validation; update both z.string() entries to z.string().min(1) to enforce
non-empty values consistent with other secrets, rebuild types if needed, and run
tests to ensure the PostHog client will receive valid credentials.
| const { results } = await executeHogQLQuery<[[number]]>(` | ||
| SELECT count(DISTINCT person_id) as wau_users | ||
| FROM ( | ||
| SELECT person_id, count(DISTINCT toDate(timestamp)) as active_days | ||
| FROM events | ||
| WHERE timestamp >= now() - INTERVAL ${weekStart} DAY | ||
| AND timestamp < now() - INTERVAL ${weekEnd} DAY | ||
| AND event = 'workspace_created' | ||
| GROUP BY person_id | ||
| HAVING active_days >= 3 | ||
| ) | ||
| `); |
There was a problem hiding this comment.
SQL injection risk: use parameterized queries or validate integer values.
The HogQL query directly interpolates weekStart and weekEnd values (lines 123-124) into the SQL string. While these are computed from a validated numeric input, string interpolation in SQL queries is inherently risky and violates secure coding practices.
If the validation logic is modified or bypassed in the future, this becomes an exploitable SQL injection vector.
🔎 Recommended fix
Since HogQL may not support parameterized queries, explicitly validate and sanitize the integer values before interpolation:
.query(async ({ input }) => {
const days = input?.days ?? 30;
const numWeeks = Math.ceil(days / 7);
+
+ // Ensure integer values to prevent SQL injection
+ if (!Number.isInteger(days) || days < 7 || days > 180) {
+ throw new Error('Invalid days parameter');
+ }
// Calculate WAU for each week
const weeklyData: { week: string; count: number }[] = [];
for (let i = numWeeks - 1; i >= 0; i--) {
const weekEnd = i * 7;
const weekStart = weekEnd + 7;
+
+ // Verify computed values are safe integers
+ if (!Number.isSafeInteger(weekStart) || !Number.isSafeInteger(weekEnd)) {
+ throw new Error('Invalid week calculation');
+ }
// Only count workspace_created as meaningful product usage
const { results } = await executeHogQLQuery<[[number]]>(`Consider abstracting HogQL queries into a query builder that handles proper value sanitization.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/trpc/src/router/analytics/analytics.ts around lines 118 to 129, the
HogQL query interpolates weekStart and weekEnd directly, creating an SQL
injection risk; ensure these values are strictly validated and sanitized before
interpolation: coerce to Number, verify Number.isInteger(value) and value is
within an acceptable range (e.g., 0..365), or throw on invalid input, and then
use the sanitized integer variables in the template; optionally extract this
validation into a small helper or a HogQL query builder to centralize
sanitization.
| const { results } = await executeHogQLQuery<[string, number][]>(` | ||
| SELECT | ||
| distinct_id, | ||
| count() as workspaces_created | ||
| FROM events | ||
| WHERE event = 'workspace_created' | ||
| AND timestamp >= now() - INTERVAL ${weekStart + 7} DAY | ||
| AND timestamp < now() - INTERVAL ${weekStart} DAY | ||
| GROUP BY distinct_id | ||
| ORDER BY workspaces_created DESC | ||
| LIMIT ${limit} | ||
| `); |
There was a problem hiding this comment.
SQL injection risk: validate integer values before interpolation.
Similar to the issue in getWAUTrend, this HogQL query interpolates weekStart and limit directly into the SQL string (lines 192-193, 196). Apply the same integer validation pattern to prevent potential SQL injection.
🔎 Recommended fix
.query(async ({ input }) => {
const limit = input?.limit ?? 10;
const weekOffset = input?.weekOffset ?? 0;
const weekStart = weekOffset === 0 ? 0 : -weekOffset * 7;
+
+ // Validate safe integers
+ if (!Number.isSafeInteger(weekStart) || !Number.isSafeInteger(limit)) {
+ throw new Error('Invalid parameters');
+ }
const { results } = await executeHogQLQuery<[string, number][]>(`Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/trpc/src/router/analytics/analytics.ts around lines 186 to 197, the
HogQL query interpolates weekStart and limit directly which is a SQL injection
risk; validate and sanitize both values as integers before building the query by
applying the same pattern used in getWAUTrend: parse them to numbers (e.g.,
Number()/parseInt), ensure they are finite integers (Math.floor) and within
acceptable ranges (non-negative for weekStart, positive and capped for limit),
throw an error or fallback to a safe default if validation fails, then
interpolate only the validated integer values (or better, use a
parameterized/templated query API if available) so the constructed SQL contains
only safe integers.
| const { results } = await executeHogQLQuery<[string, number][]>(` | ||
| SELECT | ||
| formatDateTime(toDate(timestamp), '%Y-%m-%d') as date, | ||
| count(DISTINCT person_id) as signups | ||
| FROM events | ||
| WHERE event = 'auth_completed' | ||
| AND timestamp >= now() - INTERVAL ${days} DAY | ||
| GROUP BY date | ||
| ORDER BY date ASC | ||
| `); |
There was a problem hiding this comment.
SQL injection risk: validate integer before interpolation.
The days parameter is interpolated directly into the HogQL query (line 248). Apply integer validation as recommended in the previous endpoints.
🔎 Recommended fix
.query(async ({ input }) => {
const days = input?.days ?? 30;
+
+ // Validate safe integer
+ if (!Number.isSafeInteger(days)) {
+ throw new Error('Invalid days parameter');
+ }
const { results } = await executeHogQLQuery<[string, number][]>(`Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/trpc/src/router/analytics/analytics.ts around lines 242 to 251, the
days variable is interpolated directly into the HogQL query creating an SQL
injection risk; validate and sanitize days before use by parsing it as an
integer (e.g., Number.parseInt or equivalent), ensure it is a finite
non-negative integer within an acceptable max bound, and throw or return a
validation error if it fails validation, then use the validated integer (not the
raw input) in the interpolated query.
Add POSTHOG_API_KEY and POSTHOG_PROJECT_ID to admin deployment jobs in both preview and production workflows for server-side analytics queries. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add POSTHOG_API_KEY and POSTHOG_PROJECT_ID to turbo.jsonc globalEnv so they're passed through to all tasks during build/dev - Downgrade biome schema to 2.3.8 to match CLI version 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tRPC analytics router runs in the API app, not admin. Added POSTHOG_API_KEY and POSTHOG_PROJECT_ID to deploy-api jobs in both preview and production workflows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/deploy-preview.yml (1)
395-414: Remove unused PostHog server-side credentials from Admin deployment.The Admin app's
env.tsdoes not declarePOSTHOG_API_KEYorPOSTHOG_PROJECT_IDin either its server or client environment validation. Code search confirms these variables are not referenced anywhere in the Admin codebase. Only the client-side keys (NEXT_PUBLIC_POSTHOG_KEYandNEXT_PUBLIC_POSTHOG_HOST) are actually declared and used.Remove
POSTHOG_API_KEYandPOSTHOG_PROJECT_IDfrom lines 395-396 (environment) and lines 413-414 (deploy arguments) in the Admin deployment section.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/deploy-preview.yml.github/workflows/deploy-production.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Deploy Admin
- GitHub Check: Deploy API
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy Docs
- GitHub Check: Deploy Web
- GitHub Check: Build
🔇 Additional comments (4)
.github/workflows/deploy-preview.yml (1)
137-159: Code implementation is correct, but verify GitHub secrets are configured in repository settings.The PostHog server-side credentials are properly added to both the API and Admin deployments, following the same pattern as other secrets (added to build environment and Vercel deploy flags). Ensure that
POSTHOG_API_KEYandPOSTHOG_PROJECT_IDsecrets are configured in the repository's GitHub Settings > Secrets and variables section before deploying..github/workflows/deploy-production.yml (3)
274-275: LGTM! Server-side PostHog credentials added to admin deployment.The admin dashboard appropriately has both client-side tracking credentials (
NEXT_PUBLIC_POSTHOG_KEY/NEXT_PUBLIC_POSTHOG_HOST) and server-side API credentials (POSTHOG_API_KEY/POSTHOG_PROJECT_ID) to support the new analytics features.
292-293: LGTM! Vercel runtime environment properly configured.The PostHog credentials are correctly passed as runtime environment variables to match the build-time configuration, ensuring the admin analytics features work in production.
107-109: No action needed — server-side credentials are properly secured.The
POSTHOG_API_KEYandPOSTHOG_PROJECT_IDare correctly defined as server-only environment variables inpackages/trpc/src/env.ts(not in theclientobject), which prevents client-side exposure. The credentials are never exported fromposthog-client.ts—only query functions that use them internally are exposed. All analytics endpoints are protected withadminProcedure, and only sanitized query results are returned to clients.
| POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} | ||
| POSTHOG_PROJECT_ID: ${{ secrets.POSTHOG_PROJECT_ID }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the deploy-production.yml file around lines 87-88
cat -n .github/workflows/deploy-production.yml | sed -n '80,120p'Repository: superset-sh/superset
Length of output: 2415
🏁 Script executed:
# Search for POSTHOG_API_KEY usage in the codebase
rg -n "POSTHOG_API_KEY" --type ts --type js --type env -C 2Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
# Search for POSTHOG_PROJECT_ID usage in the codebase
rg -n "POSTHOG_PROJECT_ID" --type ts --type js --type env -C 2Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
# Check if there's env validation in trpc package or api directory
find . -type f \( -name "env.ts" -o -name "env.js" -o -name ".env*" \) | grep -E "(trpc|api|server)" | head -20Repository: superset-sh/superset
Length of output: 112
🏁 Script executed:
# Look for PostHog setup in the API codebase
rg -n "posthog\|PostHog" --type ts --type js -C 2 | head -100Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
cat -n ./apps/api/src/env.tsRepository: superset-sh/superset
Length of output: 1430
🏁 Script executed:
cat -n ./packages/trpc/src/env.tsRepository: superset-sh/superset
Length of output: 593
🏁 Script executed:
# Also search more broadly for posthog (case insensitive)
rg -i "posthog" --type ts --type js --type jsx --type tsx -C 3Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
# Look for usage of these env variables in the tRPC package
rg -n "env\.|POSTHOG" packages/trpc --type ts -C 3Repository: superset-sh/superset
Length of output: 3158
🏁 Script executed:
# Check deploy-admin section for similar setup
cat -n .github/workflows/deploy-production.yml | sed -n '265,300p'Repository: superset-sh/superset
Length of output: 2478
🏁 Script executed:
# Search for how tRPC env is imported and used
rg -n "from.*trpc.*env|import.*env" apps/api --type ts -C 2Repository: superset-sh/superset
Length of output: 2650
Ensure POSTHOG_API_KEY and POSTHOG_PROJECT_ID secrets are configured in GitHub repository settings.
These server-side credentials are required by the tRPC package for authenticated requests to PostHog's analytics API. Without these secrets configured, the deployment will fail during the build phase when the tRPC environment validation runs, or the PostHog integration will not function in production.
🤖 Prompt for AI Agents
.github/workflows/deploy-production.yml lines 87-88: the workflow references
POSTHOG_API_KEY and POSTHOG_PROJECT_ID secrets but they may not be configured in
the repository settings; add both secrets in the GitHub repository (Settings →
Secrets and variables → Actions) with the correct PostHog API key and project ID
values, then re-run the workflow to ensure the tRPC PostHog integration can
validate environment variables during build/deploy.
Summary
workspace_createdevent filtering (requires 3+ active days)usePathname()Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.