Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a widget-driven, persistent metrics dashboard with time-range-aware charts and an idle-gated Suspense globe; large UI rewrites across apps management, sidebar/drawer, settings, shared app icon/ui, new hooks, components, types, and runtime dependencies (three, phosphor-icons). Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant MetricsPage
participant LocalStorage
participant MetricsContent
participant Widget
participant MetricsAPI
User->>MetricsPage: open metrics page
MetricsPage->>LocalStorage: loadConfig(STORAGE_KEY)
LocalStorage-->>MetricsPage: dashboardConfig
MetricsPage->>MetricsContent: render(dashboardConfig, timeRange)
MetricsContent->>Widget: mount (per enabled widget)
Widget->>MetricsAPI: fetch data (timeRange)
MetricsAPI-->>Widget: return data
Widget-->>MetricsContent: render visuals
User->>Widget: interact (change timeRange / click)
Widget->>MetricsAPI: additional requests or navigation
MetricsContent->>LocalStorage: saveConfig(updatedLayout)
LocalStorage-->>MetricsContent: ack
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Potential hotspots to inspect:
Possibly related PRs
Suggested reviewers
Poem
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 |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Pull request overview
This PR implements a comprehensive dashboard redesign with significant UI/UX improvements across the entire application. The changes include new components, layout redesigns, performance optimizations, and a new design guide to ensure consistency.
Key Changes:
- Complete redesign of the dashboard layout with collapsible sidebar and improved navigation
- New component patterns with React.memo for performance optimization
- Enhanced dark mode support with theme-aware colors throughout
- New interactive elements including app warning modals, copyable text components, and redesigned app cards
- Major refactor of metrics/analytics pages with new chart components and improved data visualization
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| DESIGN_GUIDE.md | New comprehensive design guide documenting patterns, colors, typography, and dark mode requirements |
| user-button.tsx | Removed unused imports (DropdownMenuLabel, DropdownMenuSeparator, SunMoon) and color mode toggle feature |
| credential-sign-up.tsx | Added React import and explicit type annotations for event handlers |
| apps-ui.tsx | Complete redesign of AppIcon component with simplified styling and enabled state |
| apps-config.ts | Added shortName field to all apps for compact display |
| theme-toggle.tsx | Updated button styling to use ghost variant and consistent hover states |
| stack-companion.tsx | Complete redesign as floating drawer with improved UX and touch support |
| settings.tsx | Major refactor with React.memo optimization and improved component structure |
| project-switcher.tsx | Redesigned with compact display and removed SelectValue import |
| link.tsx | Updated onClick signature to receive MouseEvent parameter |
| copyable-text.tsx | New component for displaying copyable text with visual feedback |
| app-warning-modal.tsx | New modal component for alpha/beta app warnings |
| app-store-entry.tsx | Complete redesign with hero section, feature grid, and improved layout |
| app-square.tsx | Major refactor with new variants, hover states, and modal integration |
| vercel/page-client.tsx | Complete redesign with collapsible steps, progress tracking, and confetti animation |
| sidebar-layout.tsx | Major layout overhaul with collapsible sidebar, tooltips, and improved navigation |
| project-settings/page-client.tsx | Performance optimization with useCallback/useMemo and new SettingCopyableText component |
| page-layout.tsx | Updated with improved typography and responsive spacing |
| auth-methods/providers.tsx | Changed Link to anchor tag for external documentation |
| apps/page-client.tsx | Complete redesign with search, categories, and grid layout |
| apps/[appId]/page-client.tsx | Updated error handling and removed wait function |
| metrics-page.tsx | Major redesign with widget system, flexible layout, and globe integration |
| line-chart.tsx | Complete refactor with tabbed views, user lists, and time range support |
| globe.tsx | Redesigned with improved positioning, tooltips, and auto-rotate |
| charts-section-with-data.tsx | Updated layout and improved card designs |
Comments suppressed due to low confidence (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx:1
- Using
as anybypasses TypeScript's type safety. This should be properly typed to catch potential type errors at compile time.
'use client';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/template/src/components/user-button.tsx (1)
83-131:colorModeToggleprop is unused and not rendered in the menu; consumers pass it but it has no effectConfirmed:
UserButtonPropsexposescolorModeToggleat line 24, but the prop is never used inUserButtonInnerInner's render logic (lines 83–131). Despite this, multiple call sites actively pass the prop:
examples/demo/src/components/header.tsx:33apps/dashboard/src/components/navbar.tsx:26examples/docs-examples/src/app/components/user-button/page.tsx:9This means the prop exists in the public API, consumers provide callbacks, but those callbacks are never invoked since no menu item renders them. Either:
- Wire
colorModeToggleback into the menu as a conditionalItem(safer, non-breaking), or- Remove the prop from
UserButtonPropsand all call sites (breaking change, but cleaner).apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (1)
35-40: Remove unusedglobeWindowSizeandsectionContainerSizevariables;globeWindowRefis actually used at line 290.The review comment conflates two different variables.
globeWindowRefis correctly assigned to a div at line 290, so it should not be removed. However,globeWindowSize(defined at line 36) is genuinely unused and can be safely removed. Additionally,sectionContainerSize(defined at line 40) is also unused and should be removed.
🧹 Nitpick comments (27)
packages/template/src/components/user-button.tsx (1)
72-83: Ensure visible focus styling when usingoutline-noneon the triggerAdding
outline-noneonDropdownMenuTriggerrisks removing the native focus indicator for keyboard users unless the underlying component or other classes add a custom:focus-visiblestyle (e.g.,focus-visible:ring-*). Please confirm that a visible focus state is still present, and if not, add one to keep the trigger keyboard‑accessible.Example adjustment (if needed):
- <DropdownMenuTrigger className="outline-none stack-scope rounded-lg hover:bg-muted/50 transition-colors p-1 -m-1"> + <DropdownMenuTrigger className="outline-none stack-scope rounded-lg hover:bg-muted/50 transition-colors p-1 -m-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">apps/dashboard/src/components/project-switcher.tsx (1)
30-35: Add consistent fallback for undefined currentProject.When
currentProjectis undefined, line 31 shows an empty avatar (first letter of empty string) while line 33 renders nothing. This creates an inconsistent UI state.Consider either hiding the entire block or showing a fallback message like "Select a project".
Apply this diff:
<div className="flex items-center gap-1"> - <ProjectAvatar displayName={currentProject?.displayName || ""} /> - <span className="truncate text-sm font-medium"> - {currentProject?.displayName} - </span> + {currentProject ? ( + <> + <ProjectAvatar displayName={currentProject.displayName} /> + <span className="truncate text-sm font-medium"> + {currentProject.displayName} + </span> + </> + ) : ( + <span className="truncate text-sm font-medium text-muted-foreground"> + Select a project + </span> + )} </div>packages/template/src/components/credential-sign-up.tsx (2)
8-8: React import for event typing is correct; consider type-only import (optional).Importing
Reactto useReact.ChangeEventis fine and should typecheck cleanly. If you want to keep the value import surface minimal, you could instead use a type-only import:-import React, { useState } from "react"; +import React, { useState, type ChangeEvent } from "react";and then switch the handlers to
ChangeEvent<HTMLInputElement>(see next comment). This is purely an optional cleanliness tweak.
73-77: ExplicitChangeEventtyping on password fields looks good; small optional cleanups.The explicit
React.ChangeEvent<HTMLInputElement>annotations on bothPasswordInputhandlers are correct and align with howregisterPassword.onChange/registerPasswordRepeat.onChangeare typically typed. UsingrunAsynchronouslyaround these calls also matches the repo’s async-handling pattern (and avoids voiding promises), which is consistent with prior learnings. Based on learnings, this is the intended pattern.If you’d like, you could make two small optional refinements:
- Use the imported type alias instead of the namespace-qualified type:
-onChange={(e: React.ChangeEvent<HTMLInputElement>) => { +onChange={(e: ChangeEvent<HTMLInputElement>) => {
- Since the two handlers are nearly identical, you could factor out a tiny helper to reduce duplication, but that’s non-essential given the current size.
Also applies to: 87-91
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsx (3)
6-6: Consider also importingrunAsynchronouslyWithAlertfor async button handlers.Based on learnings,
runAsynchronouslyWithAlertshould be used for async button click handlers to automatically handle errors and show alerts to users. This will be needed forhandleGenerateKeys.-import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises"; +import { runAsynchronously, runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
309-312: UnuseditemIdparameter.The
itemIdparameter is declared but never used in the function body.- const handleItemClick = (stepId: StepId, itemId: string) => { - // Toggle the step completion when clicking on an item - toggleStepCompletion(stepId); + const handleItemClick = (stepId: StepId, _itemId: string) => { + toggleStepCompletion(stepId); };
518-528: Overly restrictive type annotation on keyboard event.The inline type annotation
(e: { key: string, preventDefault: () => void })loses React's full keyboard event type. Consider usingReact.KeyboardEventor letting TypeScript infer the type.- onKeyDown={(e: { key: string, preventDefault: () => void }) => { + onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); props.onToggle(); } }}apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/providers.tsx (1)
178-186: Switching to a plain<a>for external docs link looks goodUsing a regular anchor for the external documentation URL avoids router-specific behaviors while keeping the existing styling and target behavior; this is appropriate here. As an optional hardening step, you could extend
relto"noopener noreferrer"to also suppress referrer leakage, but the current setup is already safe fromwindow.openerissues.apps/dashboard/src/components/app-warning-modal.tsx (1)
7-13: Consider async support for onConfirm.The
onConfirmcallback is typed as() => void, but enabling an app may involve async operations (API calls). If callers pass async functions, they won't be awaited, and the modal will close immediately without waiting for completion or handling errors.Consider:
- Updating the type to
() => void | Promise<void>- Adding loading state while confirmation is in progress
- Using
runAsynchronouslyWithAlertfor error handling (as per coding guidelines)Based on learnings, use
runAsynchronouslyWithAlertfor async operations instead of manual try/catch blocks.Apply this pattern:
+import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises"; type AppWarningModalProps = { isOpen: boolean, onClose: () => void, - onConfirm: () => void, + onConfirm: () => void | Promise<void>, appName: string, stage: "alpha" | "beta", };Then update the button handler:
+ const [isConfirming, setIsConfirming] = useState(false); + // ... in the Enable Anyway button: <button - onClick={() => { - onConfirm(); - onClose(); + onClick={() => runAsynchronouslyWithAlert(async () => { + setIsConfirming(true); + try { + await onConfirm(); + onClose(); + } finally { + setIsConfirming(false); + } })} + disabled={isConfirming}apps/dashboard/src/components/app-store-entry.tsx (2)
100-106: Consider explicit error handling for async button action.The
onEnableprop is async (() => Promise<void>), and while theButtoncomponent may support async callbacks with built-in loading state, it's best practice to explicitly wrap async handlers withrunAsynchronouslyWithAlertfor consistent error handling across the dashboard.Based on learnings, use
runAsynchronouslyWithAlertfor async button handlers instead of relying on implicit behavior.+import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises"; <Button - onClick={onEnable} + onClick={() => runAsynchronouslyWithAlert(onEnable)} size="lg" className="px-8 bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white font-medium shadow-lg shadow-blue-500/20" > Enable App </Button>
23-28: Consider making features configurable per app.The features array is currently hardcoded with generic items. As the comment on line 23 suggests, this could be customized per app in the future for more relevant feature highlights.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx (1)
33-33: Redundantasyncwrapper.The
asynckeyword is unnecessary here sincerunAsynchronouslyWithAlertalready handles the promise. The current code works correctly, but can be simplified.- onEnable={async () => runAsynchronouslyWithAlert(handleEnable())} + onEnable={() => runAsynchronouslyWithAlert(handleEnable())}apps/dashboard/src/components/copyable-text.tsx (1)
22-29: Avoid try-catch withconsole.errorfor silent failures.Per coding guidelines, avoid
.catch(console.error)or similar patterns. The clipboard write failure is silently logged without user feedback. Consider usingrunAsynchronouslyWithAlertor providing visual feedback on failure.+import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises"; + const handleCopy = useCallback(async () => { - try { - await navigator.clipboard.writeText(props.value); - setCopied(true); - } catch (err) { - console.error("Failed to copy:", err); - } + await navigator.clipboard.writeText(props.value); + setCopied(true); }, [props.value]);Then update the Button's onClick to:
-onClick={handleCopy} +onClick={() => runAsynchronouslyWithAlert(handleCopy())}Based on coding guidelines: "never use try-catch-all... use
runAsynchronouslyWithAlertfor error handling."apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx (3)
21-21: Add proper typing formemberparameter.Using
anyloses type safety. Consider defining or importing the proper team member type.-function TeamMemberItem({ member }: { member: any }) { +function TeamMemberItem({ member }: { member: { id: string; teamProfile: { displayName?: string; profileImageUrl?: string | null } } }) {
138-141: Remove unnecessaryasyncfrom synchronous callback.
handleTeamSwitcherChangeonly callssetSelectedTeamId, which is synchronous. Theasynckeyword is unnecessary.-const handleTeamSwitcherChange = useCallback(async (team: any) => { +const handleTeamSwitcherChange = useCallback((team: { id: string }) => { setSelectedTeamId(team.id); }, []);
230-245: Inconsistent memoization for logo change handlers.The handlers for
logoUrlandfullLogoUrl(lines 125-131) are memoized withuseCallback, but the handlers forlogoDarkModeUrlandlogoFullDarkModeUrlare inline arrow functions. Consider memoizing all logo handlers for consistency.+const handleLogoDarkModeChange = useCallback(async (logoDarkModeUrl: string | null) => { + await project.update({ logoDarkModeUrl }); +}, [project]); + +const handleLogoFullDarkModeChange = useCallback(async (logoFullDarkModeUrl: string | null) => { + await project.update({ logoFullDarkModeUrl }); +}, [project]);Then use these in the
onValueChangeprops.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (2)
172-172: Remove emptystyleobject.The empty
style={{}}prop has no effect and can be removed.-<div - className='relative w-full h-full' - style={{}} - onMouseEnter={() => { +<div + className='relative w-full h-full' + onMouseEnter={() => {
45-61: Consider extracting magic numbers as named constants.The camera distance calculation uses several magic numbers (484, 0.35, 355, 360). Consider extracting these as named constants for better maintainability and self-documentation.
+const GLOBE_MIN_WIDTH = 350; +const GLOBE_ZOOM_THRESHOLD = 355; +const ZOOM_BASE = 484; +const ZOOM_SLOPE = 0.35; +const ZOOM_DEFAULT = 360; + const canvasWidth = globeContainerSize?.width ?? 0; -const GLOBE_MIN_WIDTH = 350; const shouldShowGlobe = canvasWidth >= GLOBE_MIN_WIDTH; -const cameraDistance = canvasWidth >= 355 - ? 484 - 0.35 * canvasWidth - : 360; +const cameraDistance = canvasWidth >= GLOBE_ZOOM_THRESHOLD + ? ZOOM_BASE - ZOOM_SLOPE * canvasWidth + : ZOOM_DEFAULT;apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/page-client.tsx (2)
55-57: UseinstalledAppsSetfor O(1) lookup instead ofArray.includes().You've already created
installedAppsSetfor O(1) lookups at line 44, but here you're usinginstalledApps.includes(appId)which is O(n).// Apply category filter if (selectedCategory === "installed") { - apps = apps.filter(appId => installedApps.includes(appId)); + apps = apps.filter(appId => installedAppsSet.has(appId)); } else if (selectedCategory !== "all") {
88-102: Consider memoizinggetCategoryCountto avoid redundant recalculations.This function is called for each category tab on every render. While the computation isn't expensive for a small number of apps, wrapping it with
useMemooruseCallbackwould be more consistent with the optimization approach already applied elsewhere in this component.- // Get count for each category - const getCategoryCount = (categoryId: string) => { + // Get count for each category - memoized to prevent recalculation + const getCategoryCount = useCallback((categoryId: string) => { if (categoryId === "installed") return installedApps.length; if (categoryId === "all") return Object.keys(ALL_APPS).filter(appId => process.env.NODE_ENV === "development" || ALL_APPS[appId as AppId].stage !== "alpha" ).length; const category = CATEGORIES.find(c => c.id === categoryId); if (!category) return 0; return (Object.entries(ALL_APPS) as [AppId, typeof ALL_APPS[AppId]][]).filter(([appId, app]) => { if (process.env.NODE_ENV !== "development" && app.stage === "alpha") return false; return app.tags.some((tag: string) => category.tags.includes(tag)); }).length; - }; + }, [installedApps.length]);apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (3)
80-88: UnusedsaveConfigfunction with TODO comment.This function is defined but never called. The TODO suggests it will be used when widget configuration UI is implemented. Consider removing it until it's needed, or if you want to keep it, ensure it's actually wired up.
Would you like me to help implement the widget configuration UI, or should this function be removed for now to avoid dead code?
122-129: Improve type safety incalculateTotalUsers.The
reducecallback usesanyfor both parameters. Consider adding proper types to avoid potential runtime issues.const calculateTotalUsers = () => { if (timeRange === 'all') { return data.total_users || 0; } const dailyUsers = data.daily_users || []; const filteredData = timeRange === '7d' ? dailyUsers.slice(-7) : dailyUsers.slice(-30); - return filteredData.reduce((sum: any, point: { activity: any }) => sum + point.activity, 0); + return filteredData.reduce((sum: number, point: { activity: number }) => sum + point.activity, 0); };
393-400: Hardcoded gap value assumes 16px root font size.The calculation uses
20forgap-5(1.25rem), which assumes a 16px root font size. While this is typically correct, consider using a CSS variable or calculating this dynamically for robustness.apps/dashboard/src/components/app-square.tsx (1)
84-98: Consider usingrunAsynchronouslyWithAlertinstead of try-catch with manual alert.Per coding guidelines,
runAsynchronouslyWithAlertis preferred for async operations that need error handling. This would simplify the error handling and ensure consistent behavior.+import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises"; const performToggle = async () => { setIsProcessing(true); - - try { - await project.updateConfig({ - [`apps.installed.${appId}.enabled`]: !isEnabled, - }); - onToggleEnabled?.(!isEnabled); - } catch (error) { - console.error(`Failed to ${isEnabled ? 'disable' : 'enable'} app:`, error); - alert(`Failed to ${isEnabled ? 'disable' : 'enable'} ${app.displayName}. Please try again.`); - } finally { - setIsProcessing(false); - } + await runAsynchronouslyWithAlert(async () => { + await project.updateConfig({ + [`apps.installed.${appId}.enabled`]: !isEnabled, + }); + onToggleEnabled?.(!isEnabled); + }); + setIsProcessing(false); };Note: If you need to preserve the
setIsProcessing(false)call even on error, you may need to adjust the pattern slightly. Based on learnings,runAsynchronouslyWithAlertis the preferred approach for async button handlers.apps/dashboard/src/components/stack-companion.tsx (3)
170-173: Unreachable code path.This branch can never execute. The guard at line 121 (
if (!activeItem) return;) preventsisResizingfrom being set to true when there's no active item, sohandleMouseUpwill never run with!activeItembeing true.Consider removing this dead code:
} else { - // Keep current width but ensure item is active - if (!activeItem) { - // If dragged open from closed state without clicking specific item, default to docs - setActiveItem('docs'); - } + // Keep current width, item is guaranteed to be active }
183-184: Consider addingpreventDefault()for touch drag.The
touchmovelistener uses{ passive: false }but doesn't calle.preventDefault(). Without it, the page may still scroll on touch devices during drawer resize, causing a jarring UX.const handleMouseMove = (e: MouseEvent | TouchEvent) => { + if ('touches' in e) { + e.preventDefault(); // Prevent page scroll during resize + } const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
218-221: Consider removing empty comment and unnecessary fragment.The backdrop comment block is empty, and the outer fragment (
<>...</>) is unnecessary since there's only one child element (the main container div).return ( - <> - {/* Backdrop - transparent but blocks interaction with content if dragging? - User asked for "work along side it", so NO backdrop pointer-events. - We only use it for closing on outside click if we really wanted, but requested behavior implies modeless. - */} - - {/* Main Container - Fixed Right Edge, Flex Reverse to push handle left */} - <div className="fixed inset-y-0 right-0 z-50 flex flex-row-reverse items-center pointer-events-none"> + <div className="fixed inset-y-0 right-0 z-50 flex flex-row-reverse items-center pointer-events-none"> ... - </div> - </> + </div> );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
apps/dashboard/DESIGN_GUIDE.md(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsx(3 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx(4 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx(2 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/page-client.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/providers.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsx(2 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx(10 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx(7 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsx(5 hunks)apps/dashboard/src/components/app-square.tsx(2 hunks)apps/dashboard/src/components/app-store-entry.tsx(2 hunks)apps/dashboard/src/components/app-warning-modal.tsx(1 hunks)apps/dashboard/src/components/copyable-text.tsx(1 hunks)apps/dashboard/src/components/link.tsx(2 hunks)apps/dashboard/src/components/project-switcher.tsx(2 hunks)apps/dashboard/src/components/settings.tsx(5 hunks)apps/dashboard/src/components/stack-companion.tsx(1 hunks)apps/dashboard/src/components/theme-toggle.tsx(1 hunks)apps/dashboard/src/lib/apps-frontend.tsx(3 hunks)packages/stack-shared/src/apps/apps-config.ts(5 hunks)packages/stack-shared/src/apps/apps-ui.tsx(3 hunks)packages/template/src/components/credential-sign-up.tsx(3 hunks)packages/template/src/components/user-button.tsx(3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
packages/template/src/components/credential-sign-up.tsxapps/dashboard/src/components/app-store-entry.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/providers.tsxapps/dashboard/src/components/copyable-text.tsxapps/dashboard/src/components/app-warning-modal.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsxapps/dashboard/src/components/project-switcher.tsxapps/dashboard/src/components/stack-companion.tsxpackages/stack-shared/src/apps/apps-ui.tsxpackages/template/src/components/user-button.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsxapps/dashboard/src/components/link.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsxapps/dashboard/src/components/app-square.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/page-client.tsxapps/dashboard/src/components/theme-toggle.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsxpackages/stack-shared/src/apps/apps-config.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/components/settings.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/components/app-store-entry.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/providers.tsxapps/dashboard/src/components/copyable-text.tsxapps/dashboard/src/components/app-warning-modal.tsxapps/dashboard/src/lib/apps-frontend.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsxapps/dashboard/src/components/project-switcher.tsxapps/dashboard/src/components/stack-companion.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsxapps/dashboard/src/components/link.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsxapps/dashboard/src/components/app-square.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/page-client.tsxapps/dashboard/src/components/theme-toggle.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/components/settings.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx
**/apps-{frontend,config}.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
To update available apps, edit
apps-frontend.tsxandapps-config.ts
Files:
apps/dashboard/src/lib/apps-frontend.tsxpackages/stack-shared/src/apps/apps-config.ts
🧠 Learnings (8)
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
packages/template/src/components/credential-sign-up.tsxapps/dashboard/src/components/app-warning-modal.tsxapps/dashboard/src/components/project-switcher.tsxpackages/template/src/components/user-button.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsxapps/dashboard/src/components/app-square.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsxapps/dashboard/src/components/settings.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/apps-{frontend,config}.{ts,tsx} : To update available apps, edit `apps-frontend.tsx` and `apps-config.ts`
Applied to files:
apps/dashboard/src/components/app-store-entry.tsxapps/dashboard/src/lib/apps-frontend.tsxpackages/stack-shared/src/apps/apps-ui.tsxapps/dashboard/src/components/app-square.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/page-client.tsxpackages/stack-shared/src/apps/apps-config.tsapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/components/settings.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to apps/**/*.{ts,tsx} : Prefer client components and dynamic functions like `usePathname` over Next.js dynamic functions to keep pages static; avoid using `await params` or similar dynamic patterns
Applied to files:
apps/dashboard/src/components/app-store-entry.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Never use `toast` for blocking alerts and errors; use alerts instead as they are more visible to users
Applied to files:
apps/dashboard/src/components/app-warning-modal.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: When making dashboard changes, provide deep links to the dashboard page in the format `http://localhost:<$NEXT_PUBLIC_STACK_PORT_PREFIX>01/projects/-selector-/...` (or use a.localhost, b.localhost, c.localhost for port prefixes 91, 92, 93)
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; use `runAsynchronously` or `runAsynchronouslyWithAlert` for error handling instead of try-catch blocks
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to packages/stack-shared/src/config/** : When making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`
Applied to files:
packages/stack-shared/src/apps/apps-config.ts
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to apps/e2e/**/*.{ts,tsx} : Always add new E2E tests when you change the API or SDK interface
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx
🧬 Code graph analysis (11)
apps/dashboard/src/components/app-store-entry.tsx (3)
docs/src/components/icons.tsx (2)
Zap(345-347)Check(163-165)apps/dashboard/src/components/app-square.tsx (1)
AppIcon(16-43)packages/stack-shared/src/apps/apps-config.ts (1)
ALL_APP_TAGS(5-39)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/providers.tsx (2)
apps/dashboard/src/lib/utils.tsx (1)
cn(7-9)packages/stack-ui/src/index.ts (1)
cn(53-53)
apps/dashboard/src/components/copyable-text.tsx (1)
docs/src/components/icons.tsx (2)
Check(163-165)Copy(185-205)
apps/dashboard/src/lib/apps-frontend.tsx (1)
packages/stack-shared/src/apps/apps-config.ts (2)
AppId(49-49)ALL_APPS(51-152)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsx (5)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (3)
LineChartDisplayConfig(14-18)LineChartDisplay(432-485)DonutChartDisplay(517-644)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (2)
useAdminApp(29-44)useProjectId(46-53)apps/dashboard/src/components/router.tsx (1)
useRouter(15-33)packages/stack-ui/src/components/ui/typography.tsx (1)
Typography(47-47)packages/stack-shared/src/utils/dates.tsx (1)
fromNow(28-30)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (3)
packages/stack-shared/src/utils/react.tsx (2)
current(133-139)current(187-193)apps/e2e/tests/helpers.ts (1)
value(82-86)packages/stack-shared/src/utils/unicode.tsx (1)
getFlagEmoji(3-10)
apps/dashboard/src/components/app-square.tsx (8)
packages/stack-shared/src/apps/apps-ui.tsx (1)
AppIcon(33-79)apps/dashboard/src/lib/apps-frontend.tsx (3)
AppId(13-13)ALL_APPS_FRONTEND(67-228)getAppPath(40-43)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useAdminApp(29-44)docs/src/middleware.ts (1)
config(58-63)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronously(343-366)docs/src/components/icons.tsx (1)
Check(163-165)apps/dashboard/src/components/app-warning-modal.tsx (1)
AppWarningModal(15-201)apps/dashboard/src/components/link.tsx (1)
Link(22-43)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsx (2)
apps/dashboard/src/components/env-keys.tsx (1)
EnvKeys(24-62)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronously(343-366)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsx (2)
packages/stack-ui/src/lib/utils.tsx (1)
cn(4-6)packages/stack-ui/src/components/ui/typography.tsx (1)
Typography(47-47)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (4)
apps/dashboard/src/lib/apps-frontend.tsx (6)
DUMMY_ORIGIN(15-15)AppId(13-13)getItemPath(45-48)testItemPath(60-65)getAppPath(40-43)testAppPath(50-58)packages/stack-shared/src/apps/apps-config.ts (2)
AppId(49-49)ALL_APPS(51-152)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (2)
useAdminApp(29-44)useProjectId(46-53)packages/stack-shared/src/utils/objects.tsx (1)
typedEntries(263-265)
apps/dashboard/src/components/settings.tsx (3)
packages/stack-shared/src/utils/react.tsx (1)
forwardRefIfNeeded(47-57)packages/stack-shared/src/utils/promises.tsx (2)
runAsynchronouslyWithAlert(312-328)runAsynchronously(343-366)apps/dashboard/src/components/copyable-text.tsx (1)
CopyableText(8-49)
⏰ 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). (8)
- GitHub Check: CodeQL analysis (javascript-typescript)
- GitHub Check: Vercel Agent Review
- GitHub Check: setup-tests
- GitHub Check: all-good
- GitHub Check: restart-dev-and-test
- GitHub Check: lint_and_build (latest)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: check_prisma_migrations (22.x)
🔇 Additional comments (40)
packages/template/src/components/user-button.tsx (1)
4-5: Imports and async utilities align with project conventionsThe updated imports are minimal and all used, and the pattern of routing menu actions through
runAsynchronouslyWithAlertinItemmatches the repo’s async‑handling guidance for UI interactions. No issues from a type‑checking or guideline perspective here.
Based on learnings, this correctly usesrunAsynchronouslyWithAlertfor menu actions.apps/dashboard/src/components/project-switcher.tsx (1)
36-40: Based on my verification of the codebase, I can now provide the rewritten review comment.The original concerns are not valid issues given the codebase context.
The SelectTrigger component from
@stackframe/stack-uiexplicitly accepts optional children and renders just the caret icon when empty. The empty SelectTrigger with compact styling (h-6 w-6 p-0) is an intentional icon-only design pattern. Additionally, usingdefaultValuewith Next.js app router is correct here—whenprojectIdchanges via URL navigation, the component re-mounts and re-evaluatesdefaultValuewith the newcurrentProjectId.However, there is an actual issue on line 33:
currentProject?.displayNamecould render as undefined whencurrentProjectis not found. Add a fallback like line 31 does.{currentProject?.displayName || ""}apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/vercel/page-client.tsx (3)
42-61: All status types share identicalcardClassvalues.The
cardClassproperty is identical fordone,action, andblockedstatuses, making the differentiation ineffective. Either this is intentional (and thecardClassproperty could be removed from the mapping), or distinct styling was intended but not implemented.
260-303: Confetti animation handles async correctly.Good use of
runAsynchronouslywith{ noErrorLogging: true }for the optional confetti animation, and proper cleanup viaclearIntervalin the effect return.
447-460: Clickable list item accessibility is implemented well.Good implementation of keyboard navigation with
onKeyDown, properrole="button", andtabIndexattributes for clickable items.apps/dashboard/src/components/link.tsx (1)
10-20: Type narrowing ofLinkPropsis safe and intentional; no existing usages depend on NextLink-only props.Verification confirms that while
LinkPropswas narrowed fromReact.ComponentProps<typeof NextLink>to a custom type (excludingreplace,shallow,locale,legacyBehavior,passHref), there are no existing<Link>or<StyledLink>usages in the codebase that pass these NextLink-specific props. All sampled usage patterns (20+ import sites across the dashboard) only use the supported properties:href,children,className,target,onClick,style,scroll,prefetch, andtitle. The narrowing is type-safe and consistent.apps/dashboard/src/components/theme-toggle.tsx (1)
9-11: LGTM! Theme-aware styling update.The change from
outlinetoghostvariant with the hover styling (hover:bg-muted/50) provides a more subtle, theme-aware appearance that aligns well with the dashboard redesign.apps/dashboard/src/components/app-warning-modal.tsx (1)
24-30: Animation pattern is acceptable.The
setTimeoutwith a 10ms delay is a common pattern for triggering CSS transitions after initial render. This ensures the transition actually plays rather than the element appearing instantly in its final state.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsx (1)
1-46: LGTM! Responsive layout improvements.The refactored layout enhances responsiveness with proper breakpoints (
sm:) and improved header structure. The use ofcnutility and theme-aware classes aligns well with the dashboard redesign goals.packages/stack-shared/src/apps/apps-ui.tsx (1)
18-79: LGTM! Streamlined icon rendering with state support.The refactored
AppIconcomponent simplifies rendering by removing complex gradients and addsenabledstate support with appropriate visual indicators (green stroke for enabled, grayscale for disabled). The theme-aware classes ensure dark mode compatibility.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (2)
514-515: Excellent use of ES6 Map!Using
MapforBRAND_CONFIG_MAPfollows the coding guidelines to prefer ES6 Maps over records for efficient lookups. This provides better performance for the brand color lookups used throughout the chart components.As per coding guidelines, "Use ES6 maps instead of records wherever possible."
12-644: LGTM! Well-architected chart component system.The refactored chart components introduce a clean, modular architecture with:
- Reusable building blocks (
ChartCard,TimeRangeToggle,ActivityBarChart)- Type-safe interfaces (
TimeRange,DataPoint,UserListItem)- Theme-aware styling with proper dark mode support
- Performance optimizations (
isAnimationActive={false})- Weekend highlighting with brand-specific colors
The components maintain consistency with the design guide and follow best practices.
apps/dashboard/src/lib/apps-frontend.tsx (2)
3-13: LGTM! Type-safe AppId derivation.Deriving
AppIdaskeyof typeof ALL_APPSensures type safety and single source of truth. This aligns with the coding guideline that apps should be updated inapps-frontend.tsxandapps-config.ts.As per coding guidelines, "To update available apps, edit
apps-frontend.tsxandapps-config.ts."
218-220: LGTM! Improved Vercel logo rendering.The updated container properly centers the logo with flex utilities and applies appropriate styling (
object-contain,p-2) for consistent presentation.apps/dashboard/DESIGN_GUIDE.md (1)
1-814: Excellent comprehensive design guide!This design guide provides clear, actionable guidance for maintaining visual and functional consistency across the dashboard. The emphasis on theme-aware colors, dark mode support, WCAG accessibility standards, and concrete code examples will help ensure high-quality implementations.
Key strengths:
- Explicit WCAG AA contrast requirements (4.5:1 normal text, 3:1 large text)
- Theme-aware color patterns with dark mode variants
- Comprehensive verification checklist
- Concrete code examples for common patterns
- Best practices section
This documentation significantly reduces the risk of accessibility and consistency issues in future dashboard development.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx (2)
21-26: Good defensive guard for app lookup.The explicit check with
StackAssertionErrorprovides clear error messaging if the app frontend is not found. This follows best practices for handling unexpected states.
14-14: Non-null assertion is safe; parent layout wraps withAdminAppProvider.The
AdminAppProvidercontext provider wraps all children in the[projectId]/layout.tsx(line 10), ensuringuseAdminApp()always receives a non-null value from context. When called without parameters,useAdminApp()either returns the provided app or throws an error (line 42 ofuse-admin-app.tsx)—it never returns null. The non-null assertion is therefore guaranteed not to fail at runtime in normal usage.apps/dashboard/src/components/copyable-text.tsx (1)
15-20: Good cleanup pattern for timeout.The useEffect correctly cleans up the timeout on unmount or when
copiedchanges, preventing memory leaks and stale state updates.packages/stack-shared/src/apps/apps-config.ts (1)
41-47: Clean addition of optionalshortNamefield.The optional
shortNameproperty is well-structured and provides useful display abbreviations for apps. The type definition is backward-compatible.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx (1)
103-122: Good use ofrunAsynchronouslyWithAlertfor transfer action.The transfer handler correctly relies on
runAsynchronouslyWithAlertat the call site (line 328) to handle errors, following the coding guidelines. Thetry-finallyensuresisTransferringis reset regardless of outcome.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (2)
101-109: Camera controls set in bothuseEffectandonGlobeReady.The camera distance and control limits are set in both the
useEffect(lines 101-109) andonGlobeReady(lines 206-214). This is intentional—onGlobeReadyhandles initial setup whileuseEffecthandles resize updates—but ensure the duplication is necessary. TheuseEffectdependency onshouldShowGlobeis good for skipping updates when the globe is hidden.Also applies to: 206-214
294-325: Fixed-position tooltip follows cursor well.The tooltip implementation with
fixedpositioning and mouse tracking provides a smooth UX. The transform calculation for positioning above the cursor is appropriate.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/page-client.tsx (1)
189-215: Clean implementation of empty state and grid rendering.The conditional rendering for empty state with contextual messaging (search vs. category) and the responsive grid layout are well-implemented.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (1)
253-304: Widget components are well-structured.The
DailyActiveUsersWidgetandDailySignUpsWidgetcomponents provide clean abstractions for the metrics cards. Thedata: anytyping is consistent with the internal API usage pattern.apps/dashboard/src/components/app-square.tsx (2)
203-279:AppListItemprovides a clean compact list variant.The component correctly handles enabled/disabled states and provides appropriate navigation paths. The stage badge styling is consistent with
AppSquare.
100-200: Well-structured hover interaction and modal flow.The hover-triggered enable/disable button with the warning modal for non-stable apps is a nice UX pattern. The
runAsynchronouslywrapper for click handlers follows the coding guidelines.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsx (2)
47-67: Good use ofuseProjectIdhook for static page compatibility.Using
useProjectId()from the client-side hook rather thanawait paramskeeps the page static, which aligns with the coding guidelines for apps directory. The chart rendering with theme-aware colors is well-implemented.
86-127: Clean implementation of scrollable user list with proper height constraints.The
max-h-[340px]withoverflow-y-autoandpr-1for scrollbar padding creates a good scrollable container. The empty state handling is also well done.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (3)
328-375: Well-memoizedAppNavItemcomponent.The
useMemofornavItemDataprevents unnecessary re-renders ofNavItemwhen the parent re-renders. The dependency array correctly includes all relevant values.
562-583: Clean responsive layout with sticky sidebar.The flex-based layout with sticky positioning for the sidebar and proper overflow handling provides a solid foundation. The transition animation for the collapsed state is smooth.
169-175: Duplicatetransition-colorsclass.The
caretClassesstring contains bothtransition-colorsandtransition-transform. The firsttransition-colorsappears to be a copy-paste error.const caretClasses = cn( - "h-4 w-4 flex-shrink-0 transition-colors transition-transform duration-[50ms]", + "h-4 w-4 flex-shrink-0 transition-transform duration-[50ms]", isHighlighted ? "text-blue-600 dark:text-blue-400" : "text-muted-foreground group-hover:text-foreground", isSection && isExpanded && "rotate-180" );⛔ Skipped due to learnings
Learnt from: CR Repo: stack-auth/stack-auth PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-25T02:09:03.104Z Learning: Applies to **/*.{ts,tsx} : Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UIapps/dashboard/src/components/settings.tsx (3)
114-148: LGTM! Excellent error handling.The component correctly uses
runAsynchronouslyWithAlertto handle async operations in the change handler, following the coding guidelines.
173-192: LGTM! Well-structured new component.The new
SettingCopyableTextcomponent follows consistent patterns with other Setting components and properly delegates to theCopyableTextutility component.
194-249: LGTM! Proper async handling.The component correctly uses
runAsynchronouslyWithAlertinhandleValueChangeto handle async operations, following the coding guidelines.apps/dashboard/src/components/stack-companion.tsx (6)
14-56: LGTM!The type definition and constants are well-organized. The threshold values (100px for close, 400px min, 800px max) provide sensible boundaries for the drawer behavior.
58-82: LGTM!State initialization and version check effect are properly structured with cleanup.
103-114: LGTM!The click vs drag distinction using
dragThresholdRefis a clean pattern, and the three-way branching logic correctly handles all interaction states.
194-207: LGTM!Proper cleanup of body styles both when
isDraggingbecomes false and on unmount.
209-216: LGTM!The content opacity calculation provides a smooth fade-out effect as the drawer approaches the close threshold—nice touch for UX polish.
296-321: LGTM!The sidebar item buttons with tooltips, per-item hover colors, and active state styling are well implemented. The 50ms transition duration keeps interactions snappy per the coding guidelines.
Greptile OverviewGreptile SummaryThis PR implements a comprehensive dashboard redesign with significant improvements to user experience and visual consistency. The changes introduce a collapsible sidebar navigation, a widget-based customizable dashboard with time-range filtering, an app discovery interface with category tabs, a resizable companion drawer, and an interactive Vercel integration checklist. Key Changes:
Code Quality:
Minor Observations:
Confidence Score: 5/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant User
participant Browser
participant MetricsPage
participant SidebarLayout
participant StackCompanion
participant AdminApp
participant API
User->>Browser: Navigate to dashboard
Browser->>SidebarLayout: Render layout
SidebarLayout->>SidebarLayout: Load collapsed state
SidebarLayout->>AdminApp: useProject()
AdminApp->>API: Fetch project config
API-->>AdminApp: Return config with installed apps
AdminApp-->>SidebarLayout: Config data
SidebarLayout->>SidebarLayout: Filter enabled apps
SidebarLayout->>Browser: Display collapsible sidebar
Browser->>MetricsPage: Render metrics dashboard
MetricsPage->>MetricsPage: loadConfig() from localStorage
MetricsPage->>AdminApp: useMetrics()
AdminApp->>API: Fetch metrics data
API-->>AdminApp: Return user metrics
AdminApp-->>MetricsPage: Metrics data
MetricsPage->>MetricsPage: Filter widgets by config
MetricsPage->>Browser: Display widgets (globe, charts, apps)
User->>SidebarLayout: Click sidebar toggle
SidebarLayout->>SidebarLayout: Toggle collapsed state
SidebarLayout->>Browser: Animate sidebar width
User->>StackCompanion: Click companion icon
StackCompanion->>StackCompanion: openDrawer()
StackCompanion->>Browser: Animate drawer to DEFAULT_DRAWER_WIDTH
User->>StackCompanion: Drag resize handle
StackCompanion->>StackCompanion: handleMouseMove()
StackCompanion->>StackCompanion: Calculate newWidth
StackCompanion->>Browser: Update drawer width dynamically
User->>StackCompanion: Drag below threshold
StackCompanion->>StackCompanion: handleMouseUp()
StackCompanion->>StackCompanion: closeDrawer()
StackCompanion->>Browser: Animate drawer to 0 width
User->>MetricsPage: Change time range
MetricsPage->>MetricsPage: setTimeRange()
MetricsPage->>AdminApp: useMetrics() with new filter
AdminApp-->>MetricsPage: Filtered metrics
MetricsPage->>Browser: Update charts and totals
|
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
apps/dashboard/src/components/stack-companion.tsx (1)
114-131: Missing cleanup for animation timeouts.The
setTimeoutcalls inopenDrawerandcloseDrawerare not cleaned up if the component unmounts during animation. This could cause state updates on unmounted components.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (1)
284-325: NavSubItem structure looks good.The component follows the same URL matching pattern as
NavItem. The try-catch concern was already flagged in the previous comment.
🧹 Nitpick comments (3)
apps/dashboard/src/components/stack-companion.tsx (2)
186-209: Dead code: unreachable branch.The check
if (!activeItem)at lines 200-203 is unreachable. SincehandleMouseDownreturns early when!activeItem(line 151), dragging can only start whenactiveItemis truthy. Additionally, ifdrawerWidth < CLOSE_THRESHOLD,closeDrawer()is called before reaching this branch.if (drawerWidth < CLOSE_THRESHOLD) { closeDrawer(); } else if (drawerWidth < MIN_DRAWER_WIDTH) { setIsAnimating(true); setDrawerWidth(MIN_DRAWER_WIDTH); setTimeout(() => setIsAnimating(false), 200); - } else { - // Keep current width but ensure item is active - if (!activeItem) { - // If dragged open from closed state without clicking specific item, default to docs - setActiveItem('docs'); - } }
343-343: Memoize context value to prevent unnecessary re-renders.Creating a new object on every render causes all
useStackCompanion()consumers to re-render even when the values haven't changed.- const contextValue = { drawerWidth, isSplitScreenMode }; + const contextValue = useMemo(() => ({ drawerWidth, isSplitScreenMode }), [drawerWidth, isSplitScreenMode]);apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (1)
483-594: SidebarLayout implementation is solid with good responsive design.The layout properly handles:
- Mobile (Sheet-based menu) vs desktop (collapsible sidebar) navigation
- TooltipProvider wrapper for collapsed mode tooltips
- Responsive breakpoints and smooth transitions
The component is quite large (~110 lines). Consider extracting
HeaderandSidebarinto separate components if the file grows further, but the current organization is acceptable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx(7 hunks)apps/dashboard/src/components/stack-companion.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/components/stack-companion.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/components/stack-companion.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Applied to files:
apps/dashboard/src/components/stack-companion.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/apps-{frontend,config}.{ts,tsx} : To update available apps, edit `apps-frontend.tsx` and `apps-config.ts`
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to apps/**/*.{ts,tsx} : Prefer client components and dynamic functions like `usePathname` over Next.js dynamic functions to keep pages static; avoid using `await params` or similar dynamic patterns
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
⏰ 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). (11)
- GitHub Check: Vercel Agent Review
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: all-good
- GitHub Check: restart-dev-and-test
- GitHub Check: restart-dev-and-test-with-custom-base-port
🔇 Additional comments (9)
apps/dashboard/src/components/stack-companion.tsx (5)
1-21: LGTM!Imports and type definitions are well-organized. The
SidebarItemtype properly captures the newhoverBgfield for per-item hover styling.
22-51: LGTM!The sidebar items configuration is well-structured with consistent styling patterns across all items.
53-74: LGTM!Constants are well-documented with the breakpoint comment, and the context/hook pattern is correctly implemented.
224-237: LGTM!The body style manipulation for drag state is properly implemented with correct cleanup.
345-399: LGTM!The dual rendering modes (split-screen vs overlay) are cleanly implemented with proper positioning strategies and transition controls.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (4)
40-97: LGTM! Type definitions and constants are well-structured.The addition of
firstItemHrefto theAppSectiontype cleanly supports the collapsed navigation behavior where clicking a collapsed section navigates to its first item.
119-140: Verify the try-catch pattern for URL parsing.The coding guidelines state "never use try-catch-all" for error handling. While these try-catch blocks are for synchronous URL parsing (not async operations), silently returning
nullorfalseon error could mask issues if the pathname is malformed.Consider whether:
- URL parsing failures indicate a deeper routing issue that should be reported
- A more explicit error boundary or validation approach would be better
- This defensive pattern is acceptable for URL parsing specifically
Based on coding guidelines, which prohibit try-catch-all patterns.
327-375: Excellent use of memoization to prevent unnecessary re-renders.The
AppNavItemcomponent properly memoizes the navigation item data with correct dependencies, which will improve performance when app configuration changes.
377-481: SidebarContent is well-structured with good memoization.The component properly uses
useMemoanduseCallbackto optimize performance. ThegetDefaultExpandedSectionscallback includespathnamein dependencies, which changes frequently, but since it's only used foruseStateinitialization (which runs once), this is acceptable.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx (5)
20-20: Consider adding proper type formemberparameter.Using
anytype loses TypeScript's type safety benefits. Consider defining a proper type or using the actual member type from the SDK.-function TeamMemberItem({ member }: { member: any }) { +function TeamMemberItem({ member }: { member: { id: string; teamProfile: { displayName?: string | null; profileImageUrl?: string | null } } }) {
102-121: Error handling is incomplete in the transfer flow.If
user.transferProjectthrows an error, the user won't see any feedback since the toast and reload are only reached on success. Per coding guidelines, consider usingrunAsynchronouslyWithAlertfor proper error handling, or ensure errors are surfaced to the user.+ import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises"; ... const handleTransfer = useCallback(async () => { if (!selectedTeamId || selectedTeamId === project.ownerTeamId) return; if (isTransferring) return; setIsTransferring(true); - try { - await user.transferProject(project.id, selectedTeamId); - - toast({ - title: 'Project transferred successfully', - variant: 'success' - }); - - // Reload the page to reflect changes - // we don't actually need this, but it's a nicer UX as it clearly indicates to the user that a "big" change was made - window.location.reload(); - } finally { - setIsTransferring(false); - } + await user.transferProject(project.id, selectedTeamId); + toast({ + title: 'Project transferred successfully', + variant: 'success' + }); + setIsTransferring(false); + window.location.reload(); }, [selectedTeamId, project.ownerTeamId, project.id, user, toast, isTransferring]);Note: The
Buttoncomponent used as the trigger could handle the async operation with built-in loading state if refactored. Based on learnings,runAsynchronouslyWithAlertis the preferred pattern for async handlers.
138-145: Replaceanytypes with proper type definitions.Both
handleTeamSwitcherChangeandhandleProjectDetailsSubmituseanytypes, losing type safety. Consider using the proper types:- const handleTeamSwitcherChange = useCallback(async (team: any) => { + const handleTeamSwitcherChange = useCallback(async (team: { id: string }) => { setSelectedTeamId(team.id); }, []); - const handleProjectDetailsSubmit = useCallback(async (values: any) => { + const handleProjectDetailsSubmit = useCallback(async (values: yup.InferType<typeof projectInformationSchema>) => { await project.update(values); }, [project]);
229-241: Inconsistent memoization - dark mode logo handlers are inline while others are memoized.
handleLogoChangeandhandleFullLogoChangeare memoized, but the dark mode variants use inline async callbacks. For consistency and to avoid unnecessary re-renders, memoize these as well:+ const handleLogoDarkModeChange = useCallback(async (logoDarkModeUrl: string | null) => { + await project.update({ logoDarkModeUrl }); + }, [project]); + + const handleFullLogoDarkModeChange = useCallback(async (logoFullDarkModeUrl: string | null) => { + await project.update({ logoFullDarkModeUrl }); + }, [project]);Then update the JSX:
<LogoUpload label="Logo (Dark Mode)" value={project.logoDarkModeUrl} - onValueChange={async (logoDarkModeUrl) => { - await project.update({ logoDarkModeUrl }); - }} + onValueChange={handleLogoDarkModeChange} ... /> <LogoUpload label="Full Logo (Dark Mode)" value={project.logoFullDarkModeUrl} - onValueChange={async (logoFullDarkModeUrl) => { - await project.update({ logoFullDarkModeUrl }); - }} + onValueChange={handleFullLogoDarkModeChange} ... />
69-77: TherenderInfoLabelcallback has an empty dependency array but doesn't need memoization.This function doesn't close over any component state or props, so
useCallbackwith an empty dependency array is equivalent to defining it outside the component. Consider moving it outsidePageClientto avoid the hook overhead entirely.+const renderInfoLabel = (label: string, tooltip: string) => ( + <div className="flex items-center gap-2"> + <span>{label}</span> + <SimpleTooltip type="info" tooltip={tooltip}> + <span className="sr-only">{`More info about ${label}`}</span> + </SimpleTooltip> + </div> +); + export default function PageClient() { ... - // Memoize renderInfoLabel callback - const renderInfoLabel = useCallback((label: string, tooltip: string) => ( - <div className="flex items-center gap-2"> - <span>{label}</span> - <SimpleTooltip type="info" tooltip={tooltip}> - <span className="sr-only">{`More info about ${label}`}</span> - </SimpleTooltip> - </div> - ), []);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx(10 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx
🧠 Learnings (2)
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to apps/**/*.{ts,tsx} : Prefer client components and dynamic functions like `usePathname` over Next.js dynamic functions to keep pages static; avoid using `await params` or similar dynamic patterns
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx
⏰ 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). (12)
- GitHub Check: CodeQL analysis (javascript-typescript)
- GitHub Check: setup-tests
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: docker
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: build (22.x)
🔇 Additional comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/project-settings/page-client.tsx (1)
46-54: Good use of memoization for derived values and callbacks.The extensive use of
useMemofor computed URLs anduseCallbackfor handlers follows React best practices for performance optimization. The integration ofuseToastfor success notifications is appropriate here since it's for non-blocking success feedback (not errors/blocking alerts per coding guidelines).
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (1)
116-120: Using internal DO-NOT-USE API is fragile and should be replaced.This issue was previously flagged but remains unresolved. Accessing
stackAppInternalsSymboland using(adminApp as any)[stackAppInternalsSymbol].useMetrics()bypasses the public API and may break with library updates.Consider exposing a proper public API method like
adminApp.useMetrics(includeAnonymous)in the Stack library, or work with the library maintainers to provide an official way to access metrics data.Also applies to: 362-362
🧹 Nitpick comments (10)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (2)
43-62: Extract magic numbers as named constants.The sizing and zoom calculations use several magic numbers (484, 0.35, 360, 350, 355) that make the logic harder to understand and maintain. Consider extracting these as named constants to document their purpose.
Apply this diff:
+ // Globe sizing constants + const GLOBE_DEFAULT_SIZE = 400; + const GLOBE_MIN_WIDTH = 350; + const GLOBE_MIN_VISIBLE_WIDTH = 355; + const CAMERA_BASE_DISTANCE = 484; + const CAMERA_DISTANCE_SLOPE = 0.35; + const CAMERA_DISTANCE_FALLBACK = 360; + // Simplified sizing for the new layout - only use width - const globeSize = globeContainerSize?.width ?? 400; + const globeSize = globeContainerSize?.width ?? GLOBE_DEFAULT_SIZE; // Calculate camera distance (zoom) based on canvas width // Linear interpolation: zoom decreases as width increases (less aggressive slope) // Lower zoom values = larger globe size - // - Canvas width 350: Hide globe - // - Canvas width 355: zoom = 360 - // - Canvas width 500: zoom = 309 - // Formula: zoom = 484 - 0.35 * width (for width >= 355) + // Formula: zoom = CAMERA_BASE_DISTANCE - CAMERA_DISTANCE_SLOPE * width (for width >= GLOBE_MIN_VISIBLE_WIDTH) const canvasWidth = globeContainerSize?.width ?? 0; - const GLOBE_MIN_WIDTH = 350; const shouldShowGlobe = canvasWidth >= GLOBE_MIN_WIDTH; // Calculate zoom based on width - // For widths >= 355, use linear formula: zoom = 484 - 0.35 * width - // For widths between 350-355, use 360 (same as at 355px) - const cameraDistance = canvasWidth >= 355 - ? 484 - 0.35 * canvasWidth - : 360; // For 350-355 range, use 360 + // For widths >= GLOBE_MIN_VISIBLE_WIDTH, use linear formula + // For widths between GLOBE_MIN_WIDTH and GLOBE_MIN_VISIBLE_WIDTH, use fallback + const cameraDistance = canvasWidth >= GLOBE_MIN_VISIBLE_WIDTH + ? CAMERA_BASE_DISTANCE - CAMERA_DISTANCE_SLOPE * canvasWidth + : CAMERA_DISTANCE_FALLBACK;
234-241: Consider defining types for Globe event handlers.Multiple event handlers use
anytype for parameters (e.g.,d: any,country: any,polygon: any). While this might be due to react-globe.gl's type definitions, consider defining interfaces for better type safety:interface CountryFeature { properties: { ISO_A2_EH: string; NAME: string; POP_EST: number; }; // Add other properties as needed }Then use
CountryFeatureinstead ofanyin the callbacks.Also applies to: 246-250, 273-280, 284-297
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (3)
80-88: Implementation ready but not yet wired up.The
saveConfigfunction is complete but marked as TODO since the widget configuration UI isn't implemented yet. This is fine for now but ensure it's used when the configuration UI is added.Would you like me to help draft the widget configuration UI to connect this persistence layer?
128-128: Define proper types for metrics data structures.Multiple locations use
anytype, which bypasses TypeScript's type checking. Consider defining interfaces for the metrics data structure:interface DailyMetricPoint { activity: number; date?: string; // Add other properties as needed } interface MetricsData { total_users: number; daily_users: DailyMetricPoint[]; daily_active_users: DailyMetricPoint[]; recently_active: any[]; // Define proper type recently_registered: any[]; // Define proper type }Then use these types instead of
anyin function parameters and reduce callbacks.Also applies to: 256-256, 282-282
390-430: Extract magic numbers for grid calculations.The grid sizing logic uses several magic numbers (352.5, 180, 1.75, 1024, 20) that could be extracted as named constants for better maintainability:
const GRID_GAP_LG = 20; // gap-5 in pixels const GRID_COLUMNS = 12; const GLOBE_COLUMNS = 5; const GLOBE_MIN_WIDTH = 352.5; const VIEW_HEIGHT_OFFSET = 180; // Header/padding offset const GLOBE_MAX_HEIGHT_RATIO = 1.75; const LG_BREAKPOINT = 1024; // Tailwind lg breakpointThis makes the calculations more self-documenting and easier to maintain if design requirements change.
packages/stack-shared/src/apps/apps-ui.tsx (1)
18-25: Clarify the semantic difference betweenenabledanddisabledprops.The
enabledanddisabledprops control different visual aspects but their semantics may overlap in consumer understanding. Consider documenting when to use each:
enabled: Controls "active/installed" visual state (green icon, full opacity logo)disabled: Controls "interaction disabled" state (dashed border, reduced opacity)This is clear in the implementation but could benefit from a brief note in the docstrings about their independent use cases.
apps/dashboard/src/components/app-store-entry.tsx (1)
23-28: Move staticfeaturesarray outside the component.The
featuresarray is static and doesn't depend on props or state. Moving it outside the component avoids recreating the array on every render.+// Feature highlights (can be customized per app in the future) +const features = [ + { icon: Shield, label: "Secure & Compliant" }, + { icon: Zap, label: "Quick Setup" }, + { icon: Check, label: "Production Ready" }, +]; + export function AppStoreEntry({ appId, onEnable, titleComponent: TitleComponent = "h1", }: { appId: AppId, onEnable: () => Promise<void>, titleComponent?: FunctionComponent<any> | string, }) { const app = ALL_APPS[appId]; const appFrontend = ALL_APPS_FRONTEND[appId]; - - // Feature highlights (can be customized per app in the future) - const features = [ - { icon: Shield, label: "Secure & Compliant" }, - { icon: Zap, label: "Quick Setup" }, - { icon: Check, label: "Production Ready" }, - ];apps/dashboard/src/components/app-square.tsx (2)
186-191: Use async button callback instead ofrunAsynchronouslywrapper.For the warning modal confirm action, consider making
onConfirmaccept an async callback and using aButtoncomponent with loading state for consistency with the CTA patterns used elsewhere.
197-272: AppListItem: Consider extracting shared hook logic.
AppListItemandAppSquareboth calluseAdminApp()!,project.useProject(), andproject.useConfig()with the same pattern. Consider extracting this into a shared hook likeuseAppInstallState(appId)to reduce duplication.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (1)
400-411:getDefaultExpandedSectionsasuseCallbackis unnecessary.The function is only used as the useState initializer and never called again. The
useCallbackwrapper adds overhead without benefit. Use a plain function or inline the logic.- // Memoize getDefaultExpandedSections to prevent recreating the function - const getDefaultExpandedSections = useCallback((): Set<AppId> => { + const [expandedSections, setExpandedSections] = useState<Set<AppId>>(() => { const currentUrl = new URL(pathname, DUMMY_ORIGIN); for (const enabledApp of enabledApps) { const appFrontend = ALL_APPS_FRONTEND[enabledApp]; if (testAppPath(projectId, appFrontend, currentUrl)) { return new Set([enabledApp]); } } return new Set(["authentication"]); - }, [enabledApps, pathname, projectId]); - - const [expandedSections, setExpandedSections] = useState<Set<AppId>>(() => getDefaultExpandedSections()); + });Note: This changes behavior slightly since the current code captures
enabledAppsat initialization time, but sinceuseStateinitializer only runs once, this is equivalent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx(4 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx(7 hunks)apps/dashboard/src/app/layout.tsx(1 hunks)apps/dashboard/src/components/app-square.tsx(2 hunks)apps/dashboard/src/components/app-store-entry.tsx(2 hunks)packages/stack-shared/src/apps/apps-ui.tsx(3 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/dashboard/src/app/layout.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/components/app-store-entry.tsxpackages/stack-shared/src/apps/apps-ui.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/components/app-square.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/components/app-store-entry.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/components/app-square.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/apps-{frontend,config}.{ts,tsx} : To update available apps, edit `apps-frontend.tsx` and `apps-config.ts`
Applied to files:
apps/dashboard/src/components/app-store-entry.tsxpackages/stack-shared/src/apps/apps-ui.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/components/app-square.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to apps/**/*.{ts,tsx} : Prefer client components and dynamic functions like `usePathname` over Next.js dynamic functions to keep pages static; avoid using `await params` or similar dynamic patterns
Applied to files:
apps/dashboard/src/components/app-store-entry.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
apps/dashboard/src/components/app-square.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/components/app-store-entry.tsx (4)
docs/src/components/icons.tsx (2)
Zap(345-347)Check(163-165)apps/dashboard/src/components/app-square.tsx (1)
AppIcon(16-39)packages/stack-shared/src/apps/apps-ui.tsx (1)
AppIcon(33-79)packages/stack-shared/src/apps/apps-config.ts (1)
ALL_APP_TAGS(5-39)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (2)
examples/lovable-react-18-example/src/lib/utils.ts (1)
cn(4-6)packages/stack-shared/src/utils/unicode.tsx (1)
getFlagEmoji(3-10)
apps/dashboard/src/components/app-square.tsx (9)
packages/stack-shared/src/apps/apps-ui.tsx (1)
AppIcon(33-79)packages/stack-shared/src/apps/apps-config.ts (2)
AppId(48-48)ALL_APPS(50-141)apps/dashboard/src/lib/apps-frontend.tsx (2)
AppId(13-13)ALL_APPS_FRONTEND(67-228)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useAdminApp(29-44)docs/src/middleware.ts (1)
config(58-63)packages/stack-shared/src/utils/promises.tsx (1)
runAsynchronously(343-366)docs/src/components/icons.tsx (1)
Check(163-165)apps/dashboard/src/components/app-warning-modal.tsx (1)
AppWarningModal(15-201)apps/dashboard/src/components/link.tsx (1)
Link(22-43)
⏰ 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). (12)
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: all-good
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: build (22.x)
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: build (22.x)
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: restart-dev-and-test-with-custom-base-port
🔇 Additional comments (17)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (3)
67-74: Nice UX improvement!The
previousSelectedCountrytracking allows the tooltip to persist with the last selected country data even when not hovering, providing a smoother user experience.
110-118: Verify camera update performance.The effect depends on
cameraDistance, which is recalculated on every render whenglobeContainerSizechanges. This will trigger camera updates during window resizes. While this appears intentional for responsive behavior, verify that the frequent camera position updates don't cause performance issues or visual jitter during continuous resizing.
303-334: Tooltip implementation looks good.The tooltip positioning and transition logic is well-implemented:
- Uses
transformfor smooth positioning updates- Applies
transition-opacityonly on fade-out (whenselectedCountrybecomes null), keeping the UI snappy as per coding guidelines- Displays flag emoji with proper validation
- Clean data presentation with proper formatting
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (3)
146-248: Well-implemented responsive widget.The
AppsWidgetimplementation handles dynamic grid layout effectively:
- Responsive item sizing with proper resize observation
- Smart overflow handling with expand/collapse
- Hover transitions applied correctly (post-action fade, not pre-action delay) per coding guidelines
- Clean empty state handling
432-462: Widget rendering looks clean.The
renderWidgetfunction provides a clean abstraction for widget rendering with proper type safety via the switch statement. The grouping of widgets into chart and stat categories (lines 465-466) is logical and supports the grid layout effectively.
556-564: Good mobile UX with informative notice.The mobile globe notice provides clear feedback to users on smaller screens, explaining why the globe visualization isn't visible. This is good UX design.
packages/stack-shared/src/apps/apps-ui.tsx (1)
53-75: LGTM - Conditional rendering logic is correct.The rendering logic correctly handles both LogoComponent and IconComponent cases with appropriate styling based on the
enabledanddisabledstates. The conditional grayscale/opacity for logos and stroke colors for icons provide clear visual feedback.apps/dashboard/src/components/app-store-entry.tsx (3)
98-104: LGTM - Button correctly handles async callback.The
Buttoncomponent withonClick={onEnable}correctly leverages the async callback support with built-in loading state per the coding guidelines.
149-176: LGTM - Screenshots section with proper conditional rendering.The screenshots gallery correctly checks for non-empty array before rendering and uses appropriate
ScrollAreafor horizontal scrolling. Image alt text is descriptive.
66-78: I'll verify the type assertion concern aboutapp.tagsandALL_APP_TAGS. Let me start by exploring the relevant code.
<function_calls>
#!/bin/bashFind the definition of ALL_APP_TAGS
rg -n "ALL_APP_TAGS" apps/dashboard/src/components/app-store-entry.tsx -A 3 -B 3
</function_calls>
#!/bin/bashFind where ALL_APP_TAGS is defined (likely imported or defined elsewhere)
rg -n "export.*ALL_APP_TAGS|const ALL_APP_TAGS|const ALL_APP_TAGS" --type ts --type tsx
</function_calls>
#!/bin/bashCheck the file structure to find imports in app-store-entry.tsx
head -30 apps/dashboard/src/components/app-store-entry.tsx
</function_calls>apps/dashboard/src/components/app-square.tsx (2)
143-146: Simplify async handling -handleToggleEnabledis already async.The
handleToggleEnabledfunction is alreadyasyncand returns aPromise<void>. Wrapping it withrunAsynchronouslyis correct for button click handlers per the guidelines.
58-58: I'll help you verify this review comment about the non-null assertion onuseAdminApp()!. Let me investigate the codebase to understand the context and usage patterns.
<function_calls>
<invoke_name>shell
#!/bin/bashFirst, let's explore the file structure and find the file in question
fd "app-square.tsx" --type f
</invoke_name>
</function_calls>Now let me examine the file and understand the context:
<function_calls>
<invoke_name>shell
#!/bin/bashRead the app-square.tsx file to see the full context
cat -n "apps/dashboard/src/components/app-square.tsx"
</invoke_name>
</function_calls>Now let me search for where AppSquare is imported and used:
<function_calls>
<invoke_name>shell
#!/bin/bashSearch for AppSquare imports and usages
rg -n "AppSquare" --type=tsx -A 3 -B 3
</invoke_name>
</function_calls>Let me also check the useAdminApp hook implementation:
<function_calls>
<invoke_name>shell
#!/bin/bashSearch for useAdminApp hook definition
rg -n "useAdminApp" --type=ts --type=tsx -A 5 -B 2
</invoke_name>
</function_calls>Let me search for AdminApp provider to understand the context setup:
<function_calls>
<invoke_name>shell
#!/bin/bashSearch for AdminApp context/provider
rg -n "AdminApp" --type=tsx --type=ts -A 3 -B 3 | head -100
</invoke_name>
</function_calls><budget_exceeded />
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (5)
119-125: LGTM - Defensive URL parsing with graceful fallback.The try-catch for URL parsing is appropriate here since
pathnamecould theoretically be invalid, and returningnullallows for safe downstream handling.
327-375: Good use of memoization forAppNavItem.The
useMemofornavItemDatacorrectly memoizes the navigation item object to prevent unnecessary re-renders ofNavItem. The dependency array is complete.
391-397: LGTM - MemoizedenabledAppsfilter.Memoizing the filtered enabled apps list with proper dependencies prevents unnecessary recalculations on every render.
177-225: LGTM - Collapsed navigation with tooltip support.The collapsed sidebar navigation correctly renders icon-only buttons with tooltips for accessibility. The conditional href handling for sections vs items is correct.
565-575: Verify sidebar transition doesn't cause layout shift.The sidebar width transition uses
transition-[width] duration-200 ease-in-outwhich could cause content reflow. Consider usingtransformor ensuring the main content area handles the width change gracefully.
There was a problem hiding this comment.
This is the final PR Bugbot will review for you during this billing cycle
Your free Bugbot reviews will reset on December 10
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (5)
apps/dashboard/DESIGN_GUIDE.md (2)
529-534: Hardcodedbg-white/40conflicts with dark mode guidance.This example uses
bg-white/40which contradicts the guide's own dark mode principles documented in lines 90-219. Consider using a theme-aware alternative likebg-muted/40orbg-background/40.
547-558: Progress Summary Section also uses hardcodedbg-white/40.Line 558 repeats the same
bg-white/40pattern that conflicts with the dark mode guidance. For consistency with the guide's principles, this should also use a theme-aware token.- <Progress value={progress} className="mt-4 h-2 bg-white/40" /> + <Progress value={progress} className="mt-4 h-2 bg-muted/40" />apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (2)
34-39: Duplicate isWeekend function.This function duplicates logic from
isWeekendin@stackframe/stack-shared/dist/utils/dates, which is already imported in this file (line 4 importsfromNowfrom the same module).Consider refactoring to reuse the shared function:
+import { fromNow, isWeekend as isWeekendShared } from '@stackframe/stack-shared/dist/utils/dates'; -import { fromNow } from '@stackframe/stack-shared/dist/utils/dates'; -const isWeekend = (dateString: string): boolean => { +const isWeekend = (dateString: string): boolean => { const date = new Date(dateString); - if (isNaN(date.getTime())) return false; - const dayOfWeek = date.getDay(); - return dayOfWeek === 0 || dayOfWeek === 6; + return isNaN(date.getTime()) ? false : isWeekendShared(date); };
627-627: Critical: Unsafe property access will crash on unknown auth methods.This line accesses
BRAND_CONFIG[item.method as keyof typeof BRAND_CONFIG].colorwithout checking if the key exists. Sinceitem.methodis typed asstring(line 488), if the backend returns an auth method not inBRAND_CONFIG, this throws "Cannot read property 'color' of undefined". The type cast provides no runtime protection, and the?? "var(--color-other)"fallback won't prevent the exception.Line 630 demonstrates the correct safe pattern using
BRAND_CONFIG_MAP.get()with optional chaining. Apply the same pattern here:- style={{ backgroundColor: BRAND_CONFIG[item.method as keyof typeof BRAND_CONFIG].color ?? "var(--color-other)" }} + style={{ backgroundColor: BRAND_CONFIG_MAP.get(item.method)?.color ?? "var(--color-other)" }}apps/dashboard/src/components/stack-companion.tsx (1)
114-131: Clean up animation timeouts and rAF to avoid state updates after unmount.
openDrawer,closeDrawer, andhandleMouseUpschedulerequestAnimationFrameand multiplesetTimeoutcallbacks that can still fire after the component unmounts, causing React “state update on unmounted component” warnings and minor leaks. Track these IDs in refs, clear them in a cleanup effect, and route all timeouts through a helper instead of rawsetTimeout.Example refactor:
@@ - const startXRef = useRef(0); - const startWidthRef = useRef(0); - const dragThresholdRef = useRef(false); + const startXRef = useRef(0); + const startWidthRef = useRef(0); + const dragThresholdRef = useRef(false); + const animationFrameRef = useRef<number | null>(null); + const timeoutsRef = useRef<Array<ReturnType<typeof setTimeout>>>([]); + + const registerTimeout = useCallback((cb: () => void, delay: number) => { + const id = window.setTimeout(cb, delay); + timeoutsRef.current.push(id); + return id; + }, []); @@ - const openDrawer = useCallback((itemId: string) => { - setActiveItem(itemId); - setIsAnimating(true); - // Start animation - requestAnimationFrame(() => { - setDrawerWidth(DEFAULT_DRAWER_WIDTH); - setTimeout(() => setIsAnimating(false), 300); - }); - }, []); + const openDrawer = useCallback((itemId: string) => { + setActiveItem(itemId); + setIsAnimating(true); + animationFrameRef.current = requestAnimationFrame(() => { + setDrawerWidth(DEFAULT_DRAWER_WIDTH); + registerTimeout(() => setIsAnimating(false), 300); + }); + }, [registerTimeout]); @@ - const closeDrawer = useCallback(() => { - setIsAnimating(true); - setDrawerWidth(0); - setTimeout(() => { - setActiveItem(null); - setIsAnimating(false); - }, 300); - }, []); + const closeDrawer = useCallback(() => { + setIsAnimating(true); + setDrawerWidth(0); + registerTimeout(() => { + setActiveItem(null); + setIsAnimating(false); + }, 300); + }, [registerTimeout]); @@ - const handleMouseUp = () => { - setIsResizing(false); - setTimeout(() => setIsDragging(false), 0); + const handleMouseUp = () => { + setIsResizing(false); + registerTimeout(() => setIsDragging(false), 0); @@ - } else if (drawerWidth < MIN_DRAWER_WIDTH) { - setIsAnimating(true); - setDrawerWidth(MIN_DRAWER_WIDTH); - setTimeout(() => setIsAnimating(false), 200); + } else if (drawerWidth < MIN_DRAWER_WIDTH) { + setIsAnimating(true); + setDrawerWidth(MIN_DRAWER_WIDTH); + registerTimeout(() => setIsAnimating(false), 200); @@ - }; + }; @@ - document.addEventListener('touchend', handleMouseUp); + document.addEventListener('touchend', handleMouseUp); @@ - document.removeEventListener('touchend', handleMouseUp); - }; - }, [isResizing, drawerWidth, closeDrawer, activeItem]); + document.removeEventListener('touchend', handleMouseUp); + }; + }, [isResizing, drawerWidth, closeDrawer, activeItem, registerTimeout]); + + useEffect(() => { + return () => { + if (animationFrameRef.current !== null) { + cancelAnimationFrame(animationFrameRef.current); + } + timeoutsRef.current.forEach(clearTimeout); + timeoutsRef.current = []; + }; + }, []);Please verify with your existing ESLint/hooks rules and run-time behavior in dev to ensure no new warnings.
Also applies to: 161-205
🧹 Nitpick comments (9)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx (1)
33-33: Remove unnecessaryasyncwrapper.The
asynckeyword is unnecessary here sincerunAsynchronouslyWithAlertalready handles the promise returned byhandleEnable().Apply this diff:
- onEnable={async () => runAsynchronouslyWithAlert(handleEnable())} + onEnable={() => runAsynchronouslyWithAlert(handleEnable())}apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (2)
119-125: Silent try-catch suppresses errors without handling.Per coding guidelines, avoid try-catch-all patterns. This catch block silently returns
nullwithout logging or handling the error. If URL parsing fails, this could mask underlying issues.Consider using a safer URL validation approach or at minimum log unexpected errors:
const currentUrl = useMemo(() => { try { return new URL(pathname, DUMMY_ORIGIN); - } catch { - return null; + } catch (e) { + console.warn('Failed to parse pathname as URL:', pathname, e); + return null; } }, [pathname]);
131-140: Silent error suppression in matchesCurrentUrl.This try-catch-all pattern suppresses all errors from
sectionItem.match(). Ifmatchthrows due to a bug (not just invalid URLs), this will silently returnfalseand hide the issue.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsx (1)
95-96: Consider typing user data instead ofany.The user objects from
data.recently_registeredanddata.recently_activeare typed asany. If a shared type exists for these user objects, applying it would improve type safety.Also applies to: 159-160
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (2)
154-158: Avoid blockingalert()even in development mode.Per coding guidelines, never use
toastfor blocking alerts. While thisalert()is for development debugging only, consider usingconsole.erroror a non-blocking notification mechanism instead.if (process.env.NODE_ENV === "development") { - setTimeout(() => { - alert("Globe rendering error — it has now been refreshed. TODO let's fix this"); - }, 1000); + console.error("Globe rendering error — it has now been refreshed. TODO: fix this root cause"); }
177-199: Consider extracting mouse event handlers.The inline event handlers (
onMouseEnter,onMouseMoveCapture,onMouseLeave) could be extracted to named callbacks to improve readability and potentially reduce re-renders, though the performance impact is likely minimal for a globe component.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (2)
492-515: Consolidate to use BRAND_CONFIG_MAP consistently for runtime lookups.The file defines both
BRAND_CONFIG(record) andBRAND_CONFIG_MAP(Map), but uses them inconsistently. Line 630 uses the safe patternBRAND_CONFIG_MAP.get(item.method)?.label, while lines 574 and 627 use unsafe bracket notation onBRAND_CONFIG. This inconsistency led to the critical bug at line 627.Apply the safe Map pattern consistently for all runtime lookups:
Line 574:
- const brandConfig = key ? BRAND_CONFIG[key as keyof typeof BRAND_CONFIG] : undefined; + const brandConfig = key ? BRAND_CONFIG_MAP.get(key) : undefined;Note:
BRAND_CONFIGis still needed for theChartContainerconfig prop (line 560), so both can coexist, but runtime lookups should use the Map exclusively.Also applies to: 574-574, 627-627, 630-630
514-515: Clarify "Memoized" comment.The comment states "Memoized Map" but this is a module-level constant, not a memoized value. While it's efficiently created once at module load time, "memoized" typically refers to React's
useMemoor similar caching patterns.Consider updating the comment for clarity:
-// Memoized Map for efficient lookups +// Map for efficient runtime lookups const BRAND_CONFIG_MAP = new Map(Object.entries(BRAND_CONFIG));apps/dashboard/src/components/stack-companion.tsx (1)
146-159: Unreachable “drag to open from closed” path—consider simplifying or enabling the behavior.
handleMouseDownbails out when!activeItem, so the branch inhandleMouseUpthat sets a default'docs'item when!activeItemanddragThresholdRef.currentis true can never be hit. Either allow drag-resize from a closed state (and keep the default-docs logic), or remove that branch/comment to avoid dead code and confusion.Also applies to: 186-205
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/dashboard/DESIGN_GUIDE.md(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsx(3 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx(4 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx(2 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsx(2 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx(7 hunks)apps/dashboard/src/components/stack-companion.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsxapps/dashboard/src/components/stack-companion.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsxapps/dashboard/src/components/stack-companion.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsx
🧠 Learnings (4)
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/apps-{frontend,config}.{ts,tsx} : To update available apps, edit `apps-frontend.tsx` and `apps-config.ts`
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to apps/**/*.{ts,tsx} : Prefer client components and dynamic functions like `usePathname` over Next.js dynamic functions to keep pages static; avoid using `await params` or similar dynamic patterns
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Applied to files:
apps/dashboard/src/components/stack-companion.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (2)
examples/lovable-react-18-example/src/lib/utils.ts (1)
cn(4-6)packages/stack-shared/src/utils/unicode.tsx (1)
getFlagEmoji(3-10)
apps/dashboard/src/components/stack-companion.tsx (5)
apps/dashboard/src/lib/version-check.ts (2)
VersionCheckResult(5-8)checkVersion(44-109)apps/dashboard/src/components/stack-companion/unified-docs-widget.tsx (1)
UnifiedDocsWidget(159-404)apps/dashboard/src/components/stack-companion/feature-request-board.tsx (1)
FeatureRequestBoard(50-487)apps/dashboard/src/components/stack-companion/changelog-widget.tsx (1)
ChangelogWidget(24-285)apps/dashboard/src/components/feedback-form.tsx (1)
FeedbackForm(11-177)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (2)
packages/stack-shared/src/utils/dates.tsx (2)
isWeekend(5-7)fromNow(28-30)apps/dashboard/src/components/router.tsx (1)
useRouter(15-33)
⏰ 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). (12)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: restart-dev-and-test
- GitHub Check: setup-tests
- GitHub Check: lint_and_build (latest)
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: docker
- GitHub Check: check_prisma_migrations (22.x)
🔇 Additional comments (16)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/apps/[appId]/page-client.tsx (2)
6-8: LGTM: Error handling utilities properly imported.The addition of
StackAssertionErrorandrunAsynchronouslyWithAlertaligns with the coding guidelines for proper error handling.
14-14: The non-null assertion is safe;useAdminApp()cannot return null.The
useAdminApp()hook returns either aStackAdminApp<false>value or callsthrowErr()(which has return typenever, meaning it always throws and never returns). TypeScript correctly infers this cannot produce a null value, making the non-null assertion!technically redundant but not unsafe.apps/dashboard/DESIGN_GUIDE.md (1)
1-814: Solid design guide establishing consistent patterns.This comprehensive documentation provides valuable guidance for dashboard consistency, dark mode support, and accessibility. The patterns and examples will help maintain design coherence across the dashboard.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (4)
348-363: Well-structured memoization for navigation items.The
navItemDatamemoization with proper dependencies prevents unnecessary re-renders of the navigation structure. Good use ofuseMemohere.
391-397: Good use of memoization and callbacks for performance.The
enabledApps,getDefaultExpandedSections, andtoggleSectionare properly memoized with appropriate dependency arrays, preventing unnecessary recalculations on re-renders.Also applies to: 400-409, 413-423
483-593: Clean layout implementation with responsive design.The new header/sidebar/content structure with collapsible sidebar, TooltipProvider wrapper, and responsive breakpoints is well-implemented. The transition animations and sticky positioning provide a polished UX.
105-106: I'll help you verify this review comment about the unusedonExpandSidebarparameter. Let me search the codebase to understand how this parameter is used.
<function_calls>
#!/bin/bashSearch for onExpandSidebar usage across the codebase
rg -n "onExpandSidebar" --type=ts --type=tsx -C3
</function_calls>
shellLet me also read the specific file to understand the component structure and parameter declarations:
<function_calls>
#!/bin/bashGet line count first
wc -l apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
</function_calls>
shell<function_calls>
#!/bin/bashRead the sidebar-layout.tsx file
cat -n "apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx"
</function_calls>
shellapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/page-layout.tsx (1)
1-49: Clean responsive layout implementation.The refactored PageLayout properly handles responsive design with appropriate breakpoints, maintains a consistent header structure with optional actions, and uses the
cnutility correctly for className composition.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/charts-section-with-data.tsx (2)
19-45: Good theme-aware color configuration.The chart configs properly use theme objects with separate light/dark values, ensuring charts display correctly in both modes.
86-127: Well-structured interactive user list with empty state.The Recent Sign Ups section handles empty state gracefully and provides clickable navigation to user details. Good use of responsive styling and accessibility with proper button semantics.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (4)
46-62: Well-documented camera distance calculation.The inline comments clearly explain the linear interpolation formula for camera distance based on canvas width. This makes the magic numbers understandable and maintainable.
67-74: Good pattern for tooltip fade-out.Tracking
previousSelectedCountryseparately allows the tooltip to fade out smoothly rather than disappearing abruptly when the user moves away from a country.
250-272: Theme-aware hex polygon coloring.The color calculation properly handles both light and dark themes with appropriate HSL values, ensuring the globe is visually appealing in both modes.
302-334: Clean tooltip implementation with theme-aware styling.The tooltip uses
bg-background/95,text-foreground, andtext-muted-foregroundtokens ensuring proper dark mode support. The flag emoji fallback to 🌍 handles edge cases gracefully.apps/dashboard/src/components/stack-companion.tsx (2)
14-51: Context, sidebar config, and version-check wiring look solid.SidebarItem’s new
hoverBgfield, the per-item color/hover classes, drawer sizing constants, theStackCompanionContext/useStackCompanionAPI, the split-screen breakpoint effect, and the delayed, silentcheckVersioncall are all cohesive and type-safe. I don’t see logical issues here.Also applies to: 53-75, 94-103, 105-112
247-341: Handle UI, content container, and split-screen/overlay layouts look coherent and performant.The shared
drawerContentandhandleComponentreuse is clean, width transitions are short (150–300ms) and applied post-action, tooltip/item hover effects are snappy, and the split-screen vs overlay layouts integrate well with the new context. This aligns with the design guide’s emphasis on fast interactions and feels maintainable.Based on learnings, the brief transition durations and action-first behavior match the DESIGN_GUIDE guidance.
Also applies to: 343-399
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (5)
apps/dashboard/src/app/layout.tsx (1)
74-78: Clarification on production guard + development concerns.The previous review flagged this as loading in production, but the
process.env.NODE_ENV === 'development'guard should prevent that—Next.js evaluates this at build time, so production builds won't include this script.However, there are still concerns for the development environment:
- No version pinning: The unpkg URL lacks a version specifier, meaning different developers could pull different versions of react-scan, leading to inconsistent behavior.
- Synchronous script: Loading this synchronously in the head blocks rendering, even in development.
- Protocol-relative URL: The
//prefix is outdated; preferhttps://.Consider pinning to a specific version and documenting why this tool is needed:
- {/* eslint-disable-next-line @next/next/no-sync-scripts */} - {process.env.NODE_ENV === 'development' && <script - crossOrigin="anonymous" - src="//unpkg.com/react-scan/dist/auto.global.js" - />} + {/* React performance profiling in development */} + {process.env.NODE_ENV === 'development' && <script + async + crossOrigin="anonymous" + src="https://unpkg.com/react-scan@0.0.37/dist/auto.global.js" + />}apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (2)
34-39: LocalisWeekenddiffers from shared utility.The shared
isWeekendinpackages/stack-shared/src/utils/dates.tsxtakes aDateobject, while this local version takes a date string and handles invalid dates. If you need string handling, this is reasonable, but consider extracting to a shared utility or using the existing one with proper date parsing upstream.
631-636: Unsafe property access will crash on unknown auth methods.Line 633 accesses
BRAND_CONFIG[item.method as keyof typeof BRAND_CONFIG].colorwithout checking if the key exists. If the backend returns an auth method not inBRAND_CONFIG(e.g.,magiclink,passkey,otp), this throws "Cannot read property 'color' of undefined". Line 636 correctly usesBRAND_CONFIG_MAP.get()with a fallback—apply the same pattern to line 633.<span className="h-2 w-2 rounded-full shrink-0" - style={{ backgroundColor: BRAND_CONFIG[item.method as keyof typeof BRAND_CONFIG].color ?? "var(--color-other)" }} + style={{ backgroundColor: BRAND_CONFIG_MAP.get(item.method)?.color ?? "var(--color-other)" }} />apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (1)
116-120: Using internal symbols is fragile and may break.This accesses an internal API marked with "DO-NOT-USE-OR-YOU-WILL-BE-FIRED". While it works now, this pattern is brittle and could break without notice on library updates. Consider exposing a proper public API for accessing metrics data.
apps/dashboard/src/components/stack-companion.tsx (1)
114-131: Critical: Animation timeouts still lack cleanup (previously reported).This issue was already flagged in a previous review but remains unfixed. The
requestAnimationFrame(line 118) andsetTimeoutcalls (lines 120, 127-130) can execute after the component unmounts, causing state updates on an unmounted component.As suggested in the previous review, track timeout and animation frame IDs in refs and clear them on unmount:
+ const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null); + const animationFrameRef = useRef<number | null>(null); + + useEffect(() => { + return () => { + if (animationTimeoutRef.current) { + clearTimeout(animationTimeoutRef.current); + } + if (animationFrameRef.current) { + cancelAnimationFrame(animationFrameRef.current); + } + }; + }, []); const openDrawer = useCallback((itemId: string) => { setActiveItem(itemId); setIsAnimating(true); - requestAnimationFrame(() => { + animationFrameRef.current = requestAnimationFrame(() => { setDrawerWidth(DEFAULT_DRAWER_WIDTH); - setTimeout(() => setIsAnimating(false), 300); + animationTimeoutRef.current = setTimeout(() => setIsAnimating(false), 300); }); }, []); const closeDrawer = useCallback(() => { setIsAnimating(true); setDrawerWidth(0); - setTimeout(() => { + animationTimeoutRef.current = setTimeout(() => { setActiveItem(null); setIsAnimating(false); }, 300); }, []);
🧹 Nitpick comments (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (3)
51-78: Consider using structured error handling instead of console.error.Per coding guidelines, avoid try-catch-all patterns with
console.error. For localStorage operations that can fail (quota exceeded, private browsing), consider usingrunAsynchronouslyor at minimum, ensure the error is surfaced appropriately rather than silently logged.function loadConfig(): DashboardConfig { if (typeof window === 'undefined') return DEFAULT_CONFIG; - try { - const stored = localStorage.getItem(STORAGE_KEY); - if (stored) { - const parsed = JSON.parse(stored); - // Validate and merge with defaults for any new widgets - const validWidgetIds = new Set(AVAILABLE_WIDGETS.map(w => w.id)); - const enabledWidgets = (parsed.enabledWidgets || []).filter((id: string) => validWidgetIds.has(id as WidgetId)); - const widgetOrder = (parsed.widgetOrder || []).filter((id: string) => validWidgetIds.has(id as WidgetId)); - - // Add any new widgets that aren't in the stored config - for (const widget of AVAILABLE_WIDGETS) { - if (!widgetOrder.includes(widget.id)) { - widgetOrder.push(widget.id); - if (widget.defaultEnabled) { - enabledWidgets.push(widget.id); - } - } - } - - return { enabledWidgets, widgetOrder }; - } - } catch (e) { - console.error('Failed to load dashboard config:', e); + const stored = localStorage.getItem(STORAGE_KEY); + if (!stored) return DEFAULT_CONFIG; + + const parsed = JSON.parse(stored) as Partial<DashboardConfig> | null; + if (!parsed) return DEFAULT_CONFIG; + + // Validate and merge with defaults for any new widgets + const validWidgetIds = new Set(AVAILABLE_WIDGETS.map(w => w.id)); + const enabledWidgets = (parsed.enabledWidgets || []).filter((id: string) => validWidgetIds.has(id as WidgetId)); + const widgetOrder = (parsed.widgetOrder || []).filter((id: string) => validWidgetIds.has(id as WidgetId)); + + // Add any new widgets that aren't in the stored config + for (const widget of AVAILABLE_WIDGETS) { + if (!widgetOrder.includes(widget.id)) { + widgetOrder.push(widget.id); + if (widget.defaultEnabled) { + enabledWidgets.push(widget.id); + } + } } + + return { enabledWidgets, widgetOrder }; return DEFAULT_CONFIG; }Alternatively, if you need to handle malformed JSON gracefully, extract the JSON parsing into a helper that returns
nullon failure rather than using try-catch at this level.
390-397: Grid width calculation assumes fixed gap value.The calculation hardcodes
gap = 20forgap-5, but the comment mentionssm:gap-5which means on smaller screens (belowsmbreakpoint),gap-4(16px) applies. Since this calculation only runs onlgscreens (isLgScreen), the value is correct, but consider adding a comment clarifying this.const calculateGlobeColumnWidth = () => { if (!gridContainerSize?.width) return 0; - const gap = 20; // gap-5 = 1.25rem = 20px on lg screens (sm:gap-5 applies) + const gap = 20; // gap-5 = 1.25rem = 20px; this calculation only runs on lg screens where sm:gap-5 is active const totalGaps = gap * 11; // 11 gaps between 12 columns
409-424: Multiple resize event listeners could be consolidated.The component uses both
useResizeObserver(line 385) and a manualresizeevent listener for viewport height. Consider using a single approach or extracting this into a custom hook for consistency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
AGENTS.md(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx(4 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx(2 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx(1 hunks)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx(7 hunks)apps/dashboard/src/app/layout.tsx(1 hunks)apps/dashboard/src/components/stack-companion.tsx(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- AGENTS.md
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/components/stack-companion.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/app/layout.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsxapps/dashboard/src/components/stack-companion.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
🧠 Learnings (4)
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Applied to files:
apps/dashboard/src/components/stack-companion.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: When making dashboard changes, provide deep links to the dashboard page in the format `http://localhost:<$NEXT_PUBLIC_STACK_PORT_PREFIX>01/projects/-selector-/...` (or use a.localhost, b.localhost, c.localhost for port prefixes 91, 92, 93)
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/apps-{frontend,config}.{ts,tsx} : To update available apps, edit `apps-frontend.tsx` and `apps-config.ts`
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to apps/**/*.{ts,tsx} : Prefer client components and dynamic functions like `usePathname` over Next.js dynamic functions to keep pages static; avoid using `await params` or similar dynamic patterns
Applied to files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx
🧬 Code graph analysis (4)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (2)
examples/lovable-react-18-example/src/lib/utils.ts (1)
cn(4-6)packages/stack-shared/src/utils/unicode.tsx (1)
getFlagEmoji(3-10)
apps/dashboard/src/components/stack-companion.tsx (1)
apps/dashboard/src/lib/version-check.ts (2)
VersionCheckResult(5-8)checkVersion(44-109)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (3)
packages/stack-shared/src/utils/dates.tsx (2)
isWeekend(5-7)fromNow(28-30)apps/dashboard/src/components/router.tsx (1)
useRouter(15-33)packages/stack-ui/src/components/ui/typography.tsx (1)
Typography(47-47)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (4)
apps/dashboard/src/lib/apps-frontend.tsx (6)
DUMMY_ORIGIN(15-15)ALL_APPS_FRONTEND(67-228)getItemPath(45-48)testItemPath(60-65)getAppPath(40-43)testAppPath(50-58)apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/use-admin-app.tsx (1)
useAdminApp(29-44)packages/stack-shared/src/utils/objects.tsx (1)
typedEntries(263-265)apps/dashboard/src/lib/env.tsx (1)
getPublicEnvVar(69-79)
⏰ 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). (12)
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: build (22.x)
- GitHub Check: all-good
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: setup-tests
- GitHub Check: docker
🔇 Additional comments (11)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/globe.tsx (4)
42-62: Sizing and camera distance logic looks good.The width-based globe sizing with linear interpolation for camera distance is well-documented with clear comments explaining the formula and thresholds. The
GLOBE_MIN_WIDTHconstant at 350px provides a sensible cutoff.
67-74: Good UX pattern for tooltip persistence.Tracking
previousSelectedCountryallows the tooltip to fade out gracefully rather than disappearing abruptly when the user moves away from a country. The effect correctly updates only when there's an active selection.
111-119: Verify camera controls access timing.The effect correctly updates camera distance on resize. However, consider adding a null check for
controls()return value, as the globe may not have fully initialized its controls in all scenarios.useEffect(() => { if (!globeRef.current || !shouldShowGlobe) return; const controls = globeRef.current.controls(); + if (!controls) return; controls.maxDistance = cameraDistance; controls.minDistance = cameraDistance; globeRef.current.camera().position.z = cameraDistance; }, [cameraDistance, shouldShowGlobe]);
304-335: Tooltip implementation is clean.The tooltip correctly uses
lastSelectedCountryfor content while usingselectedCountryfor opacity transitions. The flag emoji rendering with regex validation is a nice defensive touch. The fixed positioning with transform-based mouse tracking works well for this use case.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx (1)
146-248: AppsWidget implementation is well-structured.The responsive grid calculation with
itemsPerRowbased on container width is a good approach. The expand/collapse logic with "See all" / "Less" buttons provides good UX for many installed apps.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (4)
50-82: CustomTooltip is clean and well-formatted.Good defensive handling for invalid dates with the
isNaNcheck before formatting.
96-186: ActivityBarChart reduces duplication effectively.Good extraction of the shared bar chart logic. The weekend coloring per chart name and compact mode support are well implemented.
517-518: Good use of ES6 Map for efficient lookups.Using a
MapforBRAND_CONFIG_MAPaligns with the coding guideline to prefer ES6 Maps over records where possible.
260-433: TabbedMetricsCard is well-structured with good tab state management.The component handles chart/list view switching cleanly, with proper empty states and time range filtering. The color theming system with
gradientColorprop provides good flexibility.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sidebar-layout.tsx (2)
391-479: SidebarContent memoization and section expansion logic look solid.The
enabledAppsmemo, URL-basedgetDefaultExpandedSections, andSet<AppId>-backed expand/collapse logic are clean and should scale well with more apps. UsingtypedEntriesandusePathnamealigns with the apps/* guidelines to avoid dynamic route handlers.
483-593: Overall layout composition and responsive behavior look good.The header/sheet + sticky sidebar + main content +
StackCompanionsplit-screen arrangement is cohesive. Mobile vs desktop behavior viaSheetandlg/xlbreakpoints, plus thePanelLeft-driven collapse, all read cleanly and follow the client-component +usePathnameguidance.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (2)
34-48: Consider reusing the sharedisWeekendutility instead of duplicating logicYou’re reimplementing
isWeekendlocally even though there’s already a shared helper inpackages/stack-shared/src/utils/dates.tsx(and you’re importingfromNowfrom the same module). To keep a single source of truth and avoid subtle divergence over time, consider delegating to the shared function:-import { fromNow } from '@stackframe/stack-shared/dist/utils/dates'; +import { fromNow, isWeekend as isWeekendDate } from '@stackframe/stack-shared/dist/utils/dates'; @@ -const isWeekend = (dateString: string): boolean => { - const date = new Date(dateString); - if (isNaN(date.getTime())) return false; - const dayOfWeek = date.getDay(); - return dayOfWeek === 0 || dayOfWeek === 6; // Sunday (0) or Saturday (6) -}; +const isWeekend = (dateString: string): boolean => { + const date = new Date(dateString); + if (isNaN(date.getTime())) return false; + return isWeekendDate(date); +};This keeps the string-parsing behavior local while centralizing the “weekend” definition.
490-494: Fix unsafe BRAND_CONFIG access for auth methods and harden legend mappingIn the auth‑methods donut,
AuthMethodDatapoint.methodis now typed asstring, so it can legitimately contain values that aren’t keys inBRAND_CONFIG. The legend color currently does:style={{ backgroundColor: BRAND_CONFIG[item.method as keyof typeof BRAND_CONFIG].color ?? "var(--color-other)" }}If
item.methodis not defined inBRAND_CONFIG,BRAND_CONFIG[item.method]isundefined, and accessing.colorthrows at runtime. This is the same issue that was previously flagged. You already haveBRAND_CONFIG_MAPfor safe lookups; you can use it (with a fallback) for both color and label:- <div className={cn("flex w-full flex-wrap justify-center gap-2 shrink-0", compact ? "mt-3" : "mt-4")}> - {datapoints.map((item) => { - const percentage = total > 0 ? ((item.count / total) * 100).toFixed(0) : 0; - return ( + <div className={cn("flex w-full flex-wrap justify-center gap-2 shrink-0", compact ? "mt-3" : "mt-4")}> + {datapoints.map((item) => { + const percentage = total > 0 ? ((item.count / total) * 100).toFixed(0) : 0; + const brandConfig = BRAND_CONFIG_MAP.get(item.method) ?? BRAND_CONFIG.other; + return ( @@ - <span - className="h-2 w-2 rounded-full shrink-0" - style={{ backgroundColor: BRAND_CONFIG[item.method as keyof typeof BRAND_CONFIG].color ?? "var(--color-other)" }} - /> - <span className="font-medium text-foreground"> - {BRAND_CONFIG_MAP.get(item.method)?.label ?? item.method} - </span> + <span + className="h-2 w-2 rounded-full shrink-0" + style={{ backgroundColor: brandConfig.color ?? "var(--color-other)" }} + /> + <span className="font-medium text-foreground"> + {brandConfig.label ?? item.method} + </span>This removes the crash for unknown methods and keeps color/label logic centralized. Optionally, if upstream data can emit arbitrary new methods, you might also normalize them to an
'other'bucket earlier so colors and labels stay fully consistent withBRAND_CONFIG.Also applies to: 495-519, 533-647
🧹 Nitpick comments (3)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (3)
50-82: Time‑range filtering and bar chart rendering look solid; be aware of ordering assumptions
CustomTooltip,filterDatapointsByTimeRange, andActivityBarChartare well-structured: date parsing is defensive, empty data is handled, and weekend coloring is cleanly isolated.One small assumption to keep in mind:
filterDatapointsByTimeRangeusesslice(-7)/slice(-30), so it relies ondatapointsbeing chronologically ordered and roughly one point per day. If upstream data can be unsorted or have gaps, you may eventually want to switch to a true date-based filter rather than “last N points.”Also applies to: 84-93, 95-186
226-258: Time range toggle is clear; consider minor accessibility semanticsThe 7d / 30d / All toggle is visually clear and easy to use. For keyboard and screen‑reader users, you might consider adding tab/segment semantics, e.g.
role="tablist"on the wrapper androle="tab"+aria-selectedoraria-pressedon the buttons, to better convey the current selection state. This is a nice‑to‑have, not blocking.
260-432: Avoid coupling behavior toconfig.name === 'Daily Active Users'Using the display name string to drive behavior:
config.name === 'Daily Active Users' ? user.last_active_at_millis ... : user.signed_up_at_millis ...makes the component fragile to copy changes—if the name is reworded, the logic silently flips branches. Consider adding a stable field to
LineChartDisplayConfig(e.g.kind: 'dau' | 'signups') or a boolean likeusesLastActiveand branching on that instead, keepingnamepurely presentational. The rest of the component (time‑range totals, empty states, routing) looks good.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx(2 hunks)apps/dashboard/src/app/layout.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/dashboard/src/app/layout.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (3)
packages/stack-shared/src/utils/dates.tsx (2)
isWeekend(5-7)fromNow(28-30)apps/dashboard/src/components/router.tsx (1)
useRouter(15-33)packages/stack-ui/src/components/ui/typography.tsx (1)
Typography(47-47)
⏰ 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). (12)
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: setup-tests
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
🔇 Additional comments (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (2)
188-224:ChartCardimplementation matches the visual/interaction guidelinesThe glassmorphic layering, subtle hover tint, and short
duration-150transitions (withhover:transition-none) fit the “snappy, non-blocking transitions” guideline and provide a nice reusable shell for charts. No changes needed here.
435-488:LineChartDisplayrefactor reuses shared pieces cleanlyThe refactored
LineChartDisplaynicely reusesActivityBarChart, centralizes the time‑range filtering, and provides reasonable empty‑state messaging. The requiredtimeRangeprop keeps the behavior explicit. No issues from this component on its own.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
apps/dashboard/src/components/app-square.tsx (2)
79-89: Error handling remains incomplete—no user feedback on failure.While
console.errorandalertwere removed per the previous review, errors thrown byproject.updateConfigstill propagate without being caught. When called from line 183 withvoid performToggle(), these errors are silently swallowed, leaving users with no feedback.Per coding guidelines and learnings, either:
- Let the
Buttoncomponent handle async operations with its built-in error handling (if it usesrunAsynchronouslyWithAlertinternally), or- Wrap the call in
runAsynchronouslyWithAlertto show alerts on failure.Consider simplifying by removing manual
isProcessingstate if theButtoncomponent'sloadingprop is driven by its internal async handling:+import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises"; + - const performToggle = async () => { - setIsProcessing(true); - try { - await project.updateConfig({ - [`apps.installed.${appId}.enabled`]: !isEnabled, - }); - onToggleEnabled?.(!isEnabled); - } finally { - setIsProcessing(false); - } + const performToggle = async () => { + await project.updateConfig({ + [`apps.installed.${appId}.enabled`]: !isEnabled, + }); + onToggleEnabled?.(!isEnabled); };Then update the
Buttonat lines 138-153 to userunAsynchronouslyWithAlert:<Button - onClick={handleToggleEnabled} - loading={isProcessing} + onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + if (!isEnabled && app.stage !== "stable") { + setShowWarningModal(true); + return; + } + runAsynchronouslyWithAlert(performToggle); + }}Based on learnings, the
Buttoncomponent supports async callbacks with built-in loading state.
214-227: Remove pointless try-catch-rethrow pattern (regression).The
catchblock only re-throws, making the try-catch semantically useless. This was previously flagged and marked as addressed in commit a8b2dff, but the pattern has reappeared.Per coding guidelines: "Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state."
Simplify to:
const handleEnable = async (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); - - setIsLoading(true); - try { - await onEnable?.(); - } catch (error) { - // Re-throw to let Button component handle it via runAsynchronouslyWithAlert - throw error; - } finally { - setIsLoading(false); - } + return onEnable?.(); };If the
Buttoncomponent internally usesrunAsynchronouslyWithAlertfor itsonClickhandler, you can also remove the manualisLoadingstate andloadingprop. Otherwise, wrap the button's onClick withrunAsynchronouslyWithAlertas shown in the earlier comment.Based on learnings, the
Buttoncomponent should handle async operations and loading state automatically.apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (1)
568-599: Handle unknown auth methods safely and avoid potential crashes in tooltip.The tooltip formatter has three robustness issues that can cause crashes or incorrect rendering:
Missing optional chaining on
item(Line 569): The code accessesitem.payloadwithout checking ifitemexists. If Recharts passesundefined, this will crash.Unsafe property access (Line 570): Using
BRAND_CONFIG[key as keyof typeof BRAND_CONFIG]provides no runtime protection whenkeyis an unknown method. The type cast only satisfies TypeScript.CSS variable without fallback (Lines 581, 598): Using
var(--color-${key})andvar(--color-${x.method})assumes CSS variables exist for all methods. Unknown methods from the backend will render incorrectly.Your legend (lines 623, 626) already demonstrates the correct pattern:
BRAND_CONFIG_MAP.get(item.method)?.color ?? "var(--color-other)".Apply these changes to use the same safe pattern throughout:
formatter={(value, _name, item) => { - const key = (item.payload as AuthMethodDatapoint | undefined)?.method; - const brandConfig = key ? BRAND_CONFIG[key as keyof typeof BRAND_CONFIG] : undefined; + const key = (item?.payload as AuthMethodDatapoint | undefined)?.method; + const brandConfig = key ? BRAND_CONFIG_MAP.get(key) : undefined; const label = brandConfig?.label || _name; if (typeof value !== "number" || !key) { return null; } return ( <div className="flex items-center gap-2.5"> <span className="h-2 w-2 rounded-full ring-2 ring-white/20" - style={{ backgroundColor: `var(--color-${key})` }} + style={{ backgroundColor: brandConfig?.color ?? BRAND_CONFIG.other.color }} />And for the pie segments:
<Pie - data={datapoints.map(x => ({ + data={datapoints.map((x) => { + const brand = BRAND_CONFIG_MAP.get(x.method); + return { ...x, - fill: `var(--color-${x.method})` - }))} + fill: brand?.color ?? BRAND_CONFIG.other.color, + }; + })}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx(2 hunks)apps/dashboard/src/components/app-square.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Ensure code passespnpm typecheckfor TypeScript type validation
Never usetoastfor blocking alerts and errors; use alerts instead as they are more visible to users
Keep hover/click transitions snappy and fast; apply transitions after the action (e.g., smooth fade-out on hover end) rather than delaying actions with pre-transitions to avoid sluggish UI
Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; userunAsynchronouslyorrunAsynchronouslyWithAlertfor error handling instead of try-catch blocks
Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like support async callbacks with built-in loading state
Use ES6 maps instead of records wherever possible
Files:
apps/dashboard/src/components/app-square.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer client components and dynamic functions like
usePathnameover Next.js dynamic functions to keep pages static; avoid usingawait paramsor similar dynamic patterns
Files:
apps/dashboard/src/components/app-square.tsxapps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx
🧠 Learnings (5)
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/apps-{frontend,config}.{ts,tsx} : To update available apps, edit `apps-frontend.tsx` and `apps-config.ts`
Applied to files:
apps/dashboard/src/components/app-square.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
Repo: stack-auth/stack-auth PR: 943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.
Applied to files:
apps/dashboard/src/components/app-square.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Never use try-catch-all, never void a promise, never use .catch(console.error) or similar; use `runAsynchronously` or `runAsynchronouslyWithAlert` for error handling instead of try-catch blocks
Applied to files:
apps/dashboard/src/components/app-square.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Never use `toast` for blocking alerts and errors; use alerts instead as they are more visible to users
Applied to files:
apps/dashboard/src/components/app-square.tsx
📚 Learning: 2025-11-25T02:09:03.104Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T02:09:03.104Z
Learning: Applies to **/*.{ts,tsx} : Use loading indicators instead of try-catch blocks for asynchronous UI operations; button components like <Button> support async callbacks with built-in loading state
Applied to files:
apps/dashboard/src/components/app-square.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx (3)
packages/stack-shared/src/utils/dates.tsx (2)
isWeekend(5-7)fromNow(28-30)apps/dashboard/src/components/router.tsx (1)
useRouter(15-33)packages/stack-ui/src/components/ui/typography.tsx (1)
Typography(47-47)
⏰ 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). (11)
- GitHub Check: Vercel Agent Review
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: restart-dev-and-test
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: build (22.x)
- GitHub Check: all-good
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: build (22.x)
Summary by CodeRabbit
New Features
Documentation
Accessibility
✏️ Tip: You can customize this high-level summary in your review settings.
Note
Major dashboard overhaul: widget-based metrics with globe and time ranges, new collapsible sidebar with resizable companion drawer, revamped app explorer/store and settings, plus a comprehensive design guide and UI utilities.
ChartCard,TimeRangeToggle,TabbedMetricsCard; improved tooltips, weekend styling, and revamped auth methods donut.PageLayoutheader/actions; collapsible left sidebar with tooltips and polished nav; theme toggle styling.AppIcon, enable/disable with alpha/beta warning modal, installed badges.SettingCopyableTextand memoized setting components.Linkclick handling (passes event),use-wait-for-idlehook, phosphor icons for app frontend, threejs typings.apps/dashboard/DESIGN_GUIDE.md; updatedAGENTS.md(hover-exit transition guidance).@phosphor-icons/react,three/@types/three; minor dependency/version updates.Written by Cursor Bugbot for commit bc1ed3f. This will update automatically on new commits. Configure here.