-
Notifications
You must be signed in to change notification settings - Fork 619
Dashboard: Various UI tweaks in project pages #7905
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dashboard: Various UI tweaks in project pages #7905
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
WalkthroughPresentation-focused UI and layout updates across the dashboard: icon mapping changed to icon components for project header links, buttons standardized to Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7905 +/- ##
=======================================
Coverage 56.53% 56.53%
=======================================
Files 904 904
Lines 58592 58592
Branches 4143 4143
=======================================
Hits 33126 33126
Misses 25360 25360
Partials 106 106
🚀 New features to boost your workflow:
|
size-limit report 📦
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx (1)
231-241: Controlled Dialog lacks onOpenChange → overlay/Escape won’t close the modalSince Dialog is controlled via open={isOpen}, overlay click and Escape will not update state unless onOpenChange is wired. Keep your explicit “Create Webhook” click open behavior if you prefer, but add onOpenChange so users can dismiss via overlay/Escape. Also, if you add onOpenChange, you can drop the explicit onClick handler on the trigger.
Apply this diff within the changed segment:
- return ( - <Dialog open={isOpen}> + return ( + <Dialog + open={isOpen} + onOpenChange={(open) => { + // reset form state when opening + if (open) { + formHook.reset(); + setCurrentStep(WebhookFormSteps.BasicInfo); + } + setIsOpen(open); + }} + > <DialogTrigger asChild> - <Button - className="gap-2 rounded-full" - size="sm" - onClick={handleOpenModal} - > + <Button className="gap-2 rounded-full" size="sm"> <PlusIcon className="size-4" /> Create Webhook </Button> </DialogTrigger>And remove the now-unused handleOpenModal function:
- const handleOpenModal = () => { - formHook.reset(); - setCurrentStep(WebhookFormSteps.BasicInfo); - setIsOpen(true); - };
🧹 Nitpick comments (21)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsx (1)
134-138: Use larger hit-target on mobile for the CTA triggerSetting size="sm" universally may drop the tap target below ~44px on touch devices. Since you already have isMobile, consider a responsive size to preserve accessibility while keeping desktop compact.
- <Button className="gap-2 rounded-full" size="sm"> + <Button className="gap-2 rounded-full" size={isMobile ? "default" : "sm"}>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx (1)
33-41: LGTM on sizing/variant; minor token tweak for border colorThe move to size="sm", gap-2, and variant="secondary" reads well and matches the broader PR direction. To align with our design tokens, consider specifying the border color via border-border (secondary variant doesn’t necessarily set a border color).
Apply this diff locally:
- <Button - className="gap-2 rounded-full border" + <Button + className="gap-2 rounded-full border border-border" size="sm" onClick={() => { setImportModalOpen(true); }} variant="secondary" >apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx (1)
233-237: Optional: Align trigger styling with the rest (secondary + subtle border)Many triggers in this PR moved to variant="secondary" with a subtle border. If that’s the intended global direction, mirror it here for consistency.
- <Button className="gap-2 rounded-full" size="sm"> + <Button className="gap-2 rounded-full border border-border" size="sm" variant="secondary">apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx (1)
383-401: Use an anchor for external explorers instead of next/linkThis link points to a chain explorer (external). Next.js recommends using a plain for external links; Link adds overhead without benefit here. You already have target and rel set.
- <Link - href={`${explorer.url}/tx/${transactionHash}`} + <a + href={`${explorer.url}/tx/${transactionHash}`} onClick={(e) => { e.stopPropagation(); }} rel="noopener noreferrer" target="_blank" > {shortHash}{" "} <ExternalLinkIcon className="size-4 text-muted-foreground" /> - </Link> + </a>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx (1)
82-83: Minor class conflict: p-4 together with py-5You’re setting both p-4 and py-5 on the same container; py-5 overrides the vertical padding from p-4, which is harmless but noisy. Prefer explicit px + py for clarity.
- <div className="flex flex-col lg:flex-row lg:justify-between p-4 lg:px-6 py-5 lg:items-center gap-5"> + <div className="flex flex-col lg:flex-row lg:justify-between px-4 lg:px-6 py-5 lg:items-center gap-5">apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx (1)
42-51: Consider NavLink for the internal sandbox route (optional)For internal navigation, NavLink gives you automatic active states. Here it’s a pill action (not a nav tab), so it’s optional, but worth considering for consistency with dashboard guidelines.
Apply if you want active-state consistency:
- <Link className="gap-2" href={`${engineLinkPrefix}/sandbox`}> + <NavLink className="gap-2" href={`${engineLinkPrefix}/sandbox`}> Try Demo Engine <ArrowRightIcon className="size-3.5 text-muted-foreground" /> - </Link> + </NavLink>You’ll need:
import { NavLink } from "@/components/ui/NavLink";apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx (2)
16-18: Double-check parent flex context for new grow usageAdding grow on Card and its immediate wrapper relies on a flex parent to have effect. If the parent isn’t flex, grow is a no-op (fine), but if it is, this will cause the card to consume remaining space—desired for most empty states but verify in grids.
40-40: Minor nit: h-full + grow can be redundantOn the inner container, h-full combined with grow can be redundant if the parent already controls height via min-h. Not a blocker; leave as-is if it visually aligns across breakpoints.
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx (2)
41-47: Type icons as LucideIcon instead of React.FC for accuracyLucide components are forwardRef icon components; LucideIcon is the precise type. Current React.FC typing drops ref/prop accuracy. Switch to LucideIcon.
Apply within this range:
-const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = { +const linkTypeToIcon: Record<LinkType, LucideIcon> = { api: NetworkIcon, docs: BookTextIcon, playground: BoxIcon, webhooks: WebhookIcon, settings: SettingsIcon, };Add this import (outside the changed range):
import type { LucideIcon } from "lucide-react";
82-89: Optional: use NavLink for internal linksFor internal hrefs, NavLink standardizes active states. These are icon-only quick links, so active state may be unnecessary—treat as optional for consistency.
- <Link + <NavLink href={link.href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener noreferrer" : undefined} className="flex flex-row items-center gap-2" > <Icon className="size-4 text-foreground" /> - </Link> + </NavLink>import { NavLink } from "@/components/ui/NavLink";apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx (1)
70-70: Dialog max width override is reasonable; consider codifying!max-w-md nicely constrains the create-token dialog. If this becomes common, consider a Dialog size prop (e.g., size="md") to avoid per-instance overrides.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx (3)
65-65: Consider a responsive grid to keep cards side-by-side on md+ while stacking on small screensSwitching to a vertical column may feel sparse on wide viewports. A responsive grid keeps density without sacrificing readability on mobile.
Apply:
- <div className="flex flex-col gap-4"> + <div className="grid gap-4 sm:grid-cols-2">
110-112: Minor a11y nit: ensure the icon target area remains comfortably tappableReducing padding to p-2 and icon to size-4 is visually tighter. If this element is intended to be an interactive affordance, consider ensuring a ~40–44px touch target. If it’s purely decorative, current sizing is fine.
93-127: Make the entire card clickable when href is provided (improves UX and a11y)Currently only the title area is clickable when
hrefis set (the absolute overlay is scoped to the link). Users get a pointer cursor on the whole card but only part is interactive. Wrap the card withLinkwhenhrefexists; otherwise keep thediv+role="button"path foronClick.Apply:
- return ( - // biome-ignore lint/a11y/noStaticElementInteractions: FIXME - <div - className={cn( - "relative flex flex-col rounded-lg border bg-card p-4", - isClickable && "cursor-pointer hover:border-active-border ", - )} - onClick={onClick} - onKeyDown={(e) => { - if (e.key === "Enter" || e.key === " ") { - onClick?.(); - } - }} - role={onClick ? "button" : undefined} - tabIndex={onClick ? 0 : undefined} - > + if (props.href) { + return ( + <Link + href={props.href} + className={cn( + "relative flex flex-col rounded-lg border bg-card p-4 transition-colors", + "hover:border-active-border", + )} + > + <div className="mb-4 flex"> + <div className="flex items-center justify-center rounded-full border p-2"> + <props.icon className="size-4 text-muted-foreground" /> + </div> + </div> + <h3 className="mb-0.5 font-semibold text-lg tracking-tight"> + <span>{props.title}</span> + </h3> + <p className="text-muted-foreground text-sm">{props.description}</p> + </Link> + ); + } + + // biome-ignore lint/a11y/noStaticElementInteractions: FIXME + return ( + <div + className={cn( + "relative flex flex-col rounded-lg border bg-card p-4", + isClickable && "cursor-pointer hover:border-active-border ", + )} + onClick={onClick} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + onClick?.(); + } + }} + role={onClick ? "button" : undefined} + tabIndex={onClick ? 0 : undefined} + >Note: This preserves the keyboard path only for the
onClickvariant; nativeLinkhandles keyboard and semantics automatically.apps/dashboard/src/@/components/blocks/project-page/project-page.tsx (2)
22-24: Add an explicit return type to the component for clarityOur TS/TSX guideline prefers explicit return types for exported components.
Apply:
-export function ProjectPage(props: React.PropsWithChildren<ProjectPageProps>) { +export function ProjectPage( + props: React.PropsWithChildren<ProjectPageProps>, +): JSX.Element {
35-35: Minor spacing adjustment considerationSwitching from py-6 on the content container to pt-6 tightens the content-to-footer spacing. If we keep the footer spacing change above, this is fine; otherwise, consider restoring bottom padding (pb-6) on main when there’s no footer.
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (4)
33-41: Action button: verify secondary styling and align Button import with dashboard UI primitives; add explicit return types
- Styling: If our design system’s Button "secondary" variant already draws a border, the extra "border border-border" may be redundant. If it doesn’t, this is fine—just confirm with tokens to avoid double borders.
- Import path: In apps/dashboard, UI primitives should come from "@/components/ui/*" (see coding guidelines). Please switch the Button import.
- Types: Add explicit return types per our TS guidelines.
Apply:
- import { Button } from "@workspace/ui/components/button"; + import { Button } from "@/components/ui/button";And add return types:
-function Action(props: { action: Action; variant?: "default" | "secondary" }) { +function Action( + props: { action: Action; variant?: "default" | "secondary" }, +): JSX.Element {-export function ProjectPageHeader(props: ProjectPageHeaderProps) { +export function ProjectPageHeader(props: ProjectPageHeaderProps): JSX.Element {
42-51: Internal vs external links: prefer NavLink for internal; add aria-hidden to decorative icon; consider native anchor for externals
- Internal: For internal navigations we prefer NavLink in dashboard for active state handling. If active states are irrelevant for these CTAs, using Link is acceptable—please confirm.
- A11y: The ArrowUpRightIcon is decorative; hide it from assistive tech.
- Optional: For purely external URLs, rendering a native (still wrapped by Button asChild) avoids Next.js Link overhead.
Minimal a11y fix:
- {action.external && <ArrowUpRightIcon className="size-4" />} + {action.external && ( + <ArrowUpRightIcon className="size-4" aria-hidden="true" focusable="false" /> + )}
59-59: Description now accepts ReactNode: avoid invalid HTML by not wrapping arbitrary nodes in aSince description is React.ReactNode, callers could pass block elements, which would be invalid inside a
. Use a
wrapper instead (semantics remain fine for section copy), or constrain description to string/inline-only content.- description?: React.ReactNode; + description?: React.ReactNode; // callers may pass inline or block content - <p className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2"> + <div className="text-sm text-muted-foreground line-clamp-3 md:line-clamp-2"> {props.description} - </p> + </div>Also applies to: 92-94
88-91: Heading semantics: consider making the heading level configurableIf this header is the main page title on some routes, we should render an h1 there for SEO/a11y, and h2 when embedded. Expose a prop to control the heading tag.
export type ProjectPageHeaderProps = { client: ThirdwebClient; title: string; + // Controls the semantic heading tag for the title; defaults to "h2" + as?: "h1" | "h2" | "h3"; description?: React.ReactNode; imageUrl?: string | null; actions: { primary: Action; secondary?: Action; } | null;- <h2 className="text-3xl font-semibold tracking-tight line-clamp-1"> - {props.title} - </h2> + {(() => { + const Heading = (props.as ?? "h2") as keyof JSX.IntrinsicElements; + return ( + <Heading className="text-3xl font-semibold tracking-tight line-clamp-1"> + {props.title} + </Heading> + ); + })()}apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx (1)
23-27: Annotate explicit return type for the componentOur TS guidelines ask for explicit return types. Use JSX.Element to avoid importing React types.
Apply:
-export function ProjectSidebarLayout(props: { +export function ProjectSidebarLayout(props: { layoutPath: string; children: React.ReactNode; hasEngineInstances: boolean; -}) { +}): JSX.Element {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (24)
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx(3 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx(1 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx(5 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx(0 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx(0 hunks)apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx(2 hunks)
💤 Files with no reviewable changes (2)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/header/link-group.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsxapps/dashboard/src/@/components/blocks/project-page/project-page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/header/link-group.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsxapps/dashboard/src/@/components/blocks/project-page/project-page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/header/link-group.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsxapps/dashboard/src/@/components/blocks/project-page/project-page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
🧠 Learnings (32)
📓 Common learnings
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Layouts should reuse `SidebarLayout` / `FullWidthSidebarLayout` (`@/components/blocks/SidebarLayout`).
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Stick to design-tokens: background (`bg-card`), borders (`border-border`), muted text (`text-muted-foreground`) etc.
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx
📚 Learning: 2025-06-24T21:38:03.155Z
Learnt from: MananTank
PR: thirdweb-dev/js#7434
File: apps/dashboard/src/app/(app)/team/~/~/contract/[chain]/[contractAddress]/components/project-selector.tsx:62-76
Timestamp: 2025-06-24T21:38:03.155Z
Learning: In the project-selector.tsx component for contract imports, the addToProject.mutate() call is intentionally not awaited (fire-and-forget pattern) to allow immediate navigation to the contract page while the import happens in the background. This is a deliberate design choice to prioritize user experience.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout changes.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Anything that consumes hooks from `tanstack/react-query` or thirdweb SDKs.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Use React Query (`tanstack/react-query`) for all client data fetching.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Wrap client-side data fetching calls in React Query (`tanstack/react-query`)
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx
📚 Learning: 2025-07-07T21:21:47.488Z
Learnt from: saminacodes
PR: thirdweb-dev/js#7543
File: apps/portal/src/app/pay/page.mdx:4-4
Timestamp: 2025-07-07T21:21:47.488Z
Learning: In the thirdweb-dev/js repository, lucide-react icons must be imported with the "Icon" suffix (e.g., ExternalLinkIcon, RocketIcon) as required by the new linting rule, contrary to the typical lucide-react convention of importing without the suffix.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Pages requiring fast transitions where data is prefetched on the client.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
PR: thirdweb-dev/js#7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : For notices & skeletons rely on `AnnouncementBanner`, `GenericLoadingPage`, `EmptyStateCard`.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Reuse core UI primitives; avoid re-implementing buttons, cards, modals.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Layouts should reuse `SidebarLayout` / `FullWidthSidebarLayout` (`@/components/blocks/SidebarLayout`).
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/layout.tsx : Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page.tsx
📚 Learning: 2025-06-10T00:50:20.795Z
Learnt from: MananTank
PR: thirdweb-dev/js#7315
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx:153-226
Timestamp: 2025-06-10T00:50:20.795Z
Learning: In apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx, the updateStatus function correctly expects a complete MultiStepState["status"] object. For pending states, { type: "pending" } is the entire status object. For error states, { type: "error", message: React.ReactNode } is the entire status object. The current code incorrectly spreads the entire step object instead of passing just the status object.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use the `container` class with a `max-w-7xl` cap for page width consistency.
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
📚 Learning: 2025-07-10T10:18:33.238Z
Learnt from: arcoraven
PR: thirdweb-dev/js#7505
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/analytics/components/WebhookAnalyticsCharts.tsx:186-204
Timestamp: 2025-07-10T10:18:33.238Z
Learning: The ThirdwebBarChart component in apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/analytics/components/WebhookAnalyticsCharts.tsx does not accept standard accessibility props like `aria-label` and `role` in its TypeScript interface, causing compilation errors when added.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use design system tokens (e.g., `bg-card`, `border-border`, `text-muted-foreground`)
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Stick to design-tokens: background (`bg-card`), borders (`border-border`), muted text (`text-muted-foreground`) etc.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Keep tokens secret via internal API routes or server actions
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Spacing utilities (`px-*`, `py-*`, `gap-*`) are preferred over custom margins.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `NavLink` for internal navigation with automatic active states in dashboard and playground apps
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
📚 Learning: 2025-06-18T04:30:04.326Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7365
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17
Timestamp: 2025-06-18T04:30:04.326Z
Learning: Next.js Link component fully supports both internal and external URLs and works appropriately with all standard anchor attributes including target="_blank", rel="noopener noreferrer", etc. Using Link for external URLs is completely appropriate and recommended.
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Merge class names with `cn` from `@/lib/utils` to keep conditional logic readable.
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/dashboard/src/@/components/blocks/project-page/project-page.tsx
📚 Learning: 2025-05-29T00:46:09.063Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7188
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx:15-15
Timestamp: 2025-05-29T00:46:09.063Z
Learning: In the accounts component at apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx, the 3-column grid layout (md:grid-cols-3) is intentionally maintained even when rendering only one StatCard, as part of the design structure for this component.
Applied to files:
apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `cn()` from `@/lib/utils` for conditional class logic
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/project-page.tsx
📚 Learning: 2025-06-18T04:27:16.172Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7365
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17
Timestamp: 2025-06-18T04:27:16.172Z
Learning: Next.js Link component supports external URLs without throwing errors. When used with absolute URLs (like https://...), it behaves like a regular anchor tag without client-side routing, but does not cause runtime crashes or errors as previously believed.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
🧬 Code graph analysis (4)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/overview/highlights-card.tsx (1)
ProjectHighlightsCard(15-94)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/CreatePaymentLinkButton.client.tsx (1)
CreatePaymentLinkButton(49-297)
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx (1)
packages/thirdweb/src/react/web/ui/components/text.tsx (1)
Link(44-62)
apps/dashboard/src/@/components/blocks/project-page/project-page.tsx (3)
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (1)
ProjectPageHeader(72-120)apps/dashboard/src/@/components/ui/tabs.tsx (1)
TabPathLinks(227-252)apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx (1)
ProjectPageFooter(8-14)
⏰ 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). (3)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (28)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx (1)
10-10: LGTM: Header padding aligns with the new max-w-7xl page rhythmThe py-12 tweak reads well with the container max-w-7xl pattern adopted elsewhere in this PR. No logic impact.
apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx (1)
10-12: No nested containers detected in FooterLinksSection; ready to merge
- Reviewed apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/footer/FooterLinksSection.tsx
- No occurrences of
className="…container"found inside FooterLinksSection, so wrapping the footer incontainer max-w-7xlwill not introduce nested containersapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx (1)
69-71: Confirmed: FTUX is contained within an existing “container max-w-7xl” wrapperThe
<RpcFTUX>view is rendered insideRpcAnalytics, which lives as a child of<ProjectPage>. Inproject-page.tsx, the children are wrapped in:<main className="container max-w-7xl pt-6"> {props.children} </main>—so your FTUX UI will never stretch full-width on large screens, and there’s no risk of a “double container.”
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsx (1)
110-114: LGTM: Trigger button matches the small-action patternSwitching to size="sm", gap-2, rounded-full, and variant="secondary" with border aligns with the standardized button styling across the PR.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx (1)
89-95: Header simplification looks goodRemoving the leading icon and bumping the title to text-2xl cleans up the hierarchy and matches the updated project-page header patterns.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx (1)
83-94: Header restructure reads betterThe stacked icon + title/subtitle improves scanability and matches the updated page headers. Token usage (bg-card, border-border, text-muted-foreground) is consistent.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx (2)
10-14: Spacing tweaks look good and align with tokensThe header margin and list spacing adjustments improve readability without affecting structure.
24-31: External link button: solid accessibility and token usageGood use of Button asChild + Link, proper rel="noopener noreferrer" on target="_blank", and bg-card/rounded-full match our design tokens.
Also applies to: 37-39
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx (3)
76-81: Action pill styling is consistent and token-friendlyvariant="secondary" with a bordered rounded-full icon button matches the new header action style across pages.
102-110: Dropdown trigger/button refinements LGTMCentered alignment, rounded-lg content, and sideOffset improve UX and visual harmony with the header.
112-126: Icon rendering via mapped components is clean and future-proofRendering from a typed map keeps things extensible and removes JSX node leakage in the mapping. Colors follow text-muted-foreground in the menu—consistent.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx (1)
64-68: Primary action button size aligns with PR directionSwitching to size="sm" with a rounded-full pill matches the standardized small action pattern across pages.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsx (1)
45-51: JSX description with responsive line break is a good readability winUsing a fragment with a conditional
reads better on desktop without hurting mobile.apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsx (1)
95-101: Header description: JSX conversion looks good and matches the updated header APIUsing a fragment with a responsive
matches the new ReactNode description contract and reads well across breakpoints. No logic impact.apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx (1)
59-66: Consistent ReactNode description with responsive line breakThis aligns with the updated ProjectPageHeader typing and improves readability on larger screens while remaining compact on small screens.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (2)
60-65: Payments header description: presentation-only change is solidJSX fragment with a conditional line break improves scannability without altering semantics.
73-74: Please manually verify the Button “sm” size variantI wasn’t able to locate the
buttonVariantsdefinition in the UI package via automated searches. To ensure there’s no runtime style mismatch or TS error, please open your UI package (e.g.packages/ui/src/components/button.tsx) and confirm that thebuttonVariants(or equivalent variant definition) includes a"sm"size token. If it’s missing, add"sm"alongside the other supported sizes (e.g."md","lg").apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx (1)
26-29: Button styling tweaks are consistent with the PR’s UI directionvariant="secondary", size="sm", and a subtle border/rounded-full match the compact secondary action styling we’re adopting elsewhere.
apps/dashboard/src/@/components/blocks/project-page/project-page.tsx (3)
1-1: Switch to cn for conditional class composition is appropriateReplacing ad-hoc string composition with
cnkeeps class logic readable and consistent with our dashboard rules.
24-26: Bottom padding behavior flips compared to the summary; verify intended spacing with footersThe code sets pb-20 by default but overrides to pb-0 when a footer exists. This removes bottom breathing room on pages with footers. The AI summary claimed “adds conditional bottom padding when a footer exists,” which is the opposite of this behavior.
If the intent is to always have comfortable bottom spacing, either keep pb-20 regardless, or move spacing onto the footer wrapper. Example options:
Option A (always bottom padding):
- <div className={cn("flex flex-col pb-20", props.footer && "pb-0")}> + <div className="flex flex-col pb-20">Option B (apply padding to footer wrapper instead):
- <div className="border-t mt-20"> + <div className="border-t mt-20 pb-20">Please confirm which matches design.
Also applies to: 37-40
25-33: Header/tabs separator logic: ensure tabs render their own bottom ruleWhen tabs are present, the header loses its border-b (by design). Double-check that TabPathLinks provides a bottom separator (via its own bottom line) so content doesn’t butt against tabs on pages that include them. If not, pass bottomLineClassName or add a border-b here as needed.
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (5)
5-5: LGTM: Good use of cn for conditional class compositionImporting cn and applying it to compose the Button border conditionally keeps classes readable and consistent with our utilities preference.
74-74: LGTM: container with max-w-7xl and increased vertical rhythmThis aligns with the dashboard layout guidance and PR objectives for consistent page widths and spacing.
79-79: LGTM: Left column gap increase improves scan-abilityThe move to gap-4 matches the broader spacing tweaks called out in the PR.
82-82: Avatar size change: verify visual balance across common logo aspect ratiosThe shift to size-12 tightens the header. Just confirm tall or very wide logos still look balanced at this size in typical projects.
106-106: LGTM: Actions gap tightened for denser CTAsThe gap-3 choice reads better alongside sm buttons; matches the updated button sizing.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx (2)
132-138: Default Webhooks landing route confirmed as/webhooks/contractsThe root Webhooks page at
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
automatically redirects to/webhooks/contracts, making that the effective default. No change to the sidebar link is necessary.
128-130: Keep the existing footer separatorFullWidthSidebarLayout only renders a built-in
<SidebarSeparator/>between the footer menu and the theme toggle—it does not draw any divider above the footer menu itself. The{ separator: true }entry at the top offooterSidebarLinksis therefore necessary to visually separate the footer links from the main navigation. No change needed.Likely an incorrect or invalid review comment.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
Show resolved
Hide resolved
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
Show resolved
Hide resolved
232a4ab to
0a5bc2f
Compare
0a5bc2f to
1ffe360
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (17)
apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx (5)
16-17: Simplify competing height strategies (growvsh-full).You’re signaling growth twice (parent Card has
grow, wrapper hash-full grow). Within a flex parent,growis sufficient;h-fulldepends on a definite parent height and is usually redundant here.Apply this diff to drop
h-fullon the wrapper:- <div className="flex h-full grow flex-col items-center justify-center gap-2 rounded-md border border-dashed text-center"> + <div className="flex grow flex-col items-center justify-center gap-2 rounded-md border border-dashed text-center">
40-40: Avoid nestedgrowto keep sizing predictable.
EmptyStateContentdoesn’t need to grow when the immediate wrapper already does. Dropping this avoids unexpected stretching in some layouts.- <div className="flex w-full flex-col items-center justify-center gap-2 text-foreground grow"> + <div className="flex w-full flex-col items-center justify-center gap-2 text-foreground">
6-14: Add explicit return types and unify props via a shared type alias.Aligns with our TS guidelines: explicit return types, shared types, and optional field for
metricin both components.import { PlugIcon } from "lucide-react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import type { ReactElement } from "react"; -export function EmptyStateCard({ - metric, - link, - description, -}: { - metric?: string; - link?: string; - description?: string; -}) { +type EmptyStateProps = { + metric?: string; + link?: string; + description?: string; +}; + +export function EmptyStateCard({ + metric, + link, + description, +}: EmptyStateProps): ReactElement { @@ -export function EmptyStateContent(props: { - metric: string | undefined; - description?: string; - link?: string; -}) { +export function EmptyStateContent(props: EmptyStateProps): ReactElement {Optional (if you want to stick to “one exported component per file”): make
EmptyStateContentinternal.-export function EmptyStateContent(props: EmptyStateProps): ReactElement { +function EmptyStateContent(props: EmptyStateProps): ReactElement {Also applies to: 28-33
33-37: Guard the CTA label whenmetricis undefined.Currently, a provided
linkwithout ametricwould render “Configure undefined”. Add a safe fallback.const description = props.description || (props.metric ? `Your app may not be configured to use ${props.metric}` : undefined); + const ctaLabel = props.metric ? `Configure ${props.metric}` : "Configure analytics"; @@ - Configure {props.metric} + {ctaLabel}Also applies to: 65-65
42-42: Mark decorative icon as hidden from assistive tech.Improves accessibility for screen readers.
- <PlugIcon className="size-5 text-muted-foreground" /> + <PlugIcon className="size-5 text-muted-foreground" aria-hidden="true" focusable="false" />apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx (6)
82-95: Tighten header padding and remove an extraneous wrapper; mark decorative icon as hidden from SRs
- p-4 together with a separate py-5 is redundant; prefer explicit px/py to avoid overrides.
- The intermediate "flex" wrapper around the icon has no layout effect—drop it.
- The product icon here is purely decorative; add aria-hidden to reduce SR noise.
- <div className="flex flex-col lg:flex-row lg:justify-between p-4 lg:px-6 py-5 lg:items-center gap-5"> + <div className="flex flex-col lg:flex-row lg:justify-between px-4 py-5 lg:px-6 lg:items-center gap-5"> - <div> - <div className="flex mb-3"> + <div> + <div className="mb-3"> <div className="p-2 rounded-full bg-background border border-border"> - <WalletProductIcon className="size-5 text-muted-foreground" /> + <WalletProductIcon aria-hidden className="size-5 text-muted-foreground" /> </div> </div>
144-159: Add an accessible name to the refresh buttonIcon-only buttons should have an accessible name; the tooltip doesn’t provide one to AT.
- <Button + <Button + aria-label="Refresh balances" className="z-20 h-auto p-1.5 [&[data-pending='true']_svg]:animate-spin"
216-228: Remove legacyBehavior/passHref; use modern Link consistentlyNext.js Link no longer needs legacyBehavior or passHref. It’s also inconsistent that only the “Previous” link uses legacyBehavior. Drop both for all three instances.
- <Link + <Link href={`/team/${teamSlug}/${project.slug}/transactions/server-wallets?page=${ currentPage > 1 ? currentPage - 1 : 1 }`} - legacyBehavior - passHref >- <Link + <Link href={`/team/${teamSlug}/${project.slug}/transactions/server-wallets?page=${pageNumber}`} - passHref >- <Link + <Link href={`/team/${teamSlug}/${project.slug}/transactions/server-wallets?page=${ currentPage < totalPages ? currentPage + 1 : totalPages }`} - passHref >Also applies to: 234-241, 246-257
301-301: Drop the redundant key on TableRowThe key is already applied to ServerWalletTableRow at the map site; setting it again on the inner TableRow has no effect and is misleading.
- <TableRow key={wallet.id}> + <TableRow>
389-391: date-fns: ‘z’ is not a valid timezone token here; shows a literal “z”Use offset tokens supported by date-fns (e.g., OOOO for “GMT+X”) to avoid rendering a stray “z”.
- return ( - <ToolTipLabel label={format(dateObj, "PP pp z")}> + return ( + <ToolTipLabel label={format(dateObj, "PP pp OOOO")}> <p>{formatDistanceToNowStrict(dateObj, { addSuffix: true })}</p> </ToolTipLabel>
186-187: Mark empty-state icon as decorativeThis icon communicates state visually but carries no textual meaning. Hide it from screen readers.
- <div className="p-2 rounded-full bg-background border border-border"> - <XIcon className="size-5 text-muted-foreground" /> + <div className="p-2 rounded-full bg-background border border-border"> + <XIcon aria-hidden className="size-5 text-muted-foreground" /> </div>apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx (3)
55-59: Use the conventional breakpoint utility for the responsive line break.max-sm:hidden works, but hidden sm:block is the more common/readable pattern used throughout the repo.
- code to easily manage <br className="max-sm:hidden" /> permissions, + code to easily manage <br className="hidden sm:block" /> permissions,
12-18: Type the props and destructure for clarity; add an explicit return type.Keeps with our TS guidelines (explicit types, no inline structural types on exports) and reduces props. noise below.
Apply to the signature:
-export function DeployViaCLIOrImportCard(props: { - teamId: string; - projectId: string; - projectSlug: string; - teamSlug: string; - client: ThirdwebClient; -}) { +export function DeployViaCLIOrImportCard({ + teamId, + projectId, + projectSlug, + teamSlug, + client, +}: DeployViaCLIOrImportCardProps): JSX.Element {Update usages:
- <ImportModal - client={props.client} + <ImportModal + client={client} isOpen={importModalOpen} onClose={() => { setImportModalOpen(false); }} - projectId={props.projectId} - projectSlug={props.projectSlug} - teamId={props.teamId} - teamSlug={props.teamSlug} + projectId={projectId} + projectSlug={projectSlug} + teamId={teamId} + teamSlug={teamSlug} type="contract" />Add this type alias above the component:
type DeployViaCLIOrImportCardProps = { teamId: string; projectId: string; projectSlug: string; teamSlug: string; client: ThirdwebClient; };Also applies to: 28-39
61-67: Optional: Align Outline Buttons with Design Tokens
Using an explicitbg-backgroundon an outline-variant button can clash with your design system’s hover/disabled states and theming. Consider one of the following refactors to let the component rely on built-in variant styling:• File:
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
– Button #1 (lines 61–67)
– Button #2 (lines 78–86)• Option A: Keep
outline, drop the explicit background- className="gap-2 bg-background rounded-full" + className="gap-2 rounded-full" variant="outline"• Option B: Switch to a “filled” look via the
secondaryvariant- className="gap-2 bg-background rounded-full" - variant="outline" + className="gap-2 rounded-full" + variant="secondary"Both approaches remove the manual background override and ensure your buttons fully leverage the design-system tokens for consistent theming.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx (1)
33-43: Avoid hard-coding border if variant already provides it; optional icon size tuneIf the secondary variant already renders a border, the extra Tailwind border class is redundant and could double up or drift if tokens change. Also, consider 3.5 icon size for visual balance with size="sm" (as used elsewhere in this PR).
Apply this diff if your secondary variant includes a border by default:
<Button - className="gap-2 rounded-full border" + className="gap-2 rounded-full" size="sm" onClick={() => { setImportModalOpen(true); }} variant="secondary" > - <ImportIcon className="size-4" /> + <ImportIcon className="size-3.5" /> Import contract </Button>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx (2)
42-51: Use NavLink for internal navigation to inherit active-state behaviorFor internal routes, prefer NavLink so we’re consistent and get automatic active-state handling (even if inactive here, keeping usage consistent helps future refactors).
Apply this diff:
-import Link from "next/link"; +import Link from "next/link"; +import { NavLink } from "@/components/ui/NavLink"; @@ - <Button + <Button asChild size="sm" variant="outline" className="rounded-full bg-card" > - <Link className="gap-2" href={`${engineLinkPrefix}/sandbox`}> + <NavLink className="gap-2" href={`${engineLinkPrefix}/sandbox`}> Try Demo Engine <ArrowRightIcon className="size-3.5 text-muted-foreground" /> - </Link> + </NavLink> </Button>
5-7: Add explicit return types and local prop aliases for claritySmall TS hygiene: declare prop aliases and explicit JSX.Element returns per our TS guidelines.
Apply this diff:
-function EngineInfoSection(props: { team_slug: string; project_slug: string }) { +type EngineInfoSectionProps = { team_slug: string; project_slug: string }; +function EngineInfoSection(props: EngineInfoSectionProps): JSX.Element { @@ -export function EngineFooterCard(props: { - teamSlug: string; - projectSlug: string; -}) { +type EngineFooterCardProps = { teamSlug: string; projectSlug: string }; +export function EngineFooterCard(props: EngineFooterCardProps): JSX.Element {Also applies to: 58-70
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (25)
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx(3 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx(1 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx(5 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page.tsx(2 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx(4 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx(0 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx(0 hunks)apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx(2 hunks)
💤 Files with no reviewable changes (2)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx
✅ Files skipped from review due to trivial changes (1)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx
🚧 Files skipped from review as they are similar to previous changes (17)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
- apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx
- apps/dashboard/src/@/components/blocks/project-page/project-page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx
- apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
- apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
🧠 Learnings (16)
📓 Common learnings
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Layouts should reuse `SidebarLayout` / `FullWidthSidebarLayout` (`@/components/blocks/SidebarLayout`).
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use the `container` class with a `max-w-7xl` cap for page width consistency.
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
📚 Learning: 2025-06-24T21:38:03.155Z
Learnt from: MananTank
PR: thirdweb-dev/js#7434
File: apps/dashboard/src/app/(app)/team/~/~/contract/[chain]/[contractAddress]/components/project-selector.tsx:62-76
Timestamp: 2025-06-24T21:38:03.155Z
Learning: In the project-selector.tsx component for contract imports, the addToProject.mutate() call is intentionally not awaited (fire-and-forget pattern) to allow immediate navigation to the contract page while the import happens in the background. This is a deliberate design choice to prioritize user experience.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsxapps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsxapps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
📚 Learning: 2025-05-29T00:46:09.063Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7188
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx:15-15
Timestamp: 2025-05-29T00:46:09.063Z
Learning: In the accounts component at apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx, the 3-column grid layout (md:grid-cols-3) is intentionally maintained even when rendering only one StatCard, as part of the design structure for this component.
Applied to files:
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsxapps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Stick to design-tokens: background (`bg-card`), borders (`border-border`), muted text (`text-muted-foreground`) etc.
Applied to files:
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Reuse core UI primitives; avoid re-implementing buttons, cards, modals.
Applied to files:
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
PR: thirdweb-dev/js#7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.
Applied to files:
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Anything that consumes hooks from `tanstack/react-query` or thirdweb SDKs.
Applied to files:
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : For notices & skeletons rely on `AnnouncementBanner`, `GenericLoadingPage`, `EmptyStateCard`.
Applied to files:
apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
📚 Learning: 2025-07-07T21:21:47.488Z
Learnt from: saminacodes
PR: thirdweb-dev/js#7543
File: apps/portal/src/app/pay/page.mdx:4-4
Timestamp: 2025-07-07T21:21:47.488Z
Learning: In the thirdweb-dev/js repository, lucide-react icons must be imported with the "Icon" suffix (e.g., ExternalLinkIcon, RocketIcon) as required by the new linting rule, contrary to the typical lucide-react convention of importing without the suffix.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-06-18T04:30:04.326Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7365
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17
Timestamp: 2025-06-18T04:30:04.326Z
Learning: Next.js Link component fully supports both internal and external URLs and works appropriately with all standard anchor attributes including target="_blank", rel="noopener noreferrer", etc. Using Link for external URLs is completely appropriate and recommended.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-06-18T04:27:16.172Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7365
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17
Timestamp: 2025-06-18T04:27:16.172Z
Learning: Next.js Link component supports external URLs without throwing errors. When used with absolute URLs (like https://...), it behaves like a regular anchor tag without client-side routing, but does not cause runtime crashes or errors as previously believed.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx (2)
apps/playground-web/src/icons/ContractIcon.tsx (1)
ContractIcon(1-21)apps/dashboard/src/@/components/ui/background-patterns.tsx (1)
GridPattern(32-86)
⏰ 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). (1)
- GitHub Check: Size
🔇 Additional comments (13)
apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx (2)
16-16: Good call removing thecontainerclass and making the Card a flex container.This prevents nested container padding/max-width conflicts and makes vertical layout control more predictable within parent flex/grid contexts.
16-16: Grow utility on EmptyStateCard verified — no horizontal flex parents detectedI reviewed all occurrences of
<EmptyStateCard>across the dashboard and checked for any direct parent wrappers usingflex-rowthat could cause unexpected stretching. None of the 60+ usages live inside a horizontal flex container—most are in grid layouts, vertical flex columns, or rendered standalone. The newgrowclass on the card appears safe in its existing contexts.apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx (1)
145-159: Review key invalidation for useWalletBalanceI ran the ripgrep command and confirmed that this is the only call to
invalidateQueriesusing the hard-coded["walletBalance", selectedChainId]key, with no other occurrences in the codebase. However, theuseWalletBalancehook fromthirdweb/reactmanages its own React Query key (likely including extra parameters such as the user or contract address), so invalidating only["walletBalance", selectedChainId]may not match those queries.• File:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
Lines 145–159• Suggested fix: invalidate all “balance”-related queries by predicate instead of a fixed key
- await queryClient.invalidateQueries({ - queryKey: ["walletBalance", selectedChainId], - }); + await queryClient.invalidateQueries({ + // Invalidate any balance-related query, regardless of additional key segments + predicate: (query) => + Array.isArray(query.queryKey) && + typeof query.queryKey[0] === "string" && + /balance/i.test(query.queryKey[0]), + });Alternatively, for a more deterministic per-row refresh, you can pass a
refreshNonceinto eachWalletBalanceCelland triggerbalance.refetch()via auseEffect.Please verify that the actual queryKey shape used by
useWalletBalanceindeed won’t be covered by the original invalidation and confirm this change aligns with your desired refresh behavior.apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx (5)
8-8: Good reuse of a shared UI primitive (GridPattern).Using GridPattern from our UI library aligns with our “prefer composable primitives” learning for dashboard components. No issues.
22-27: Card wrapper and decorative chip look consistent with the design system.
- rounded-2xl + border + bg-card with relative/overflow-hidden is a solid pattern for decorative overlays.
- The chip for ContractIcon uses text-muted-foreground and is non-interactive—good.
Assuming ContractIcon remains aria-hidden/presentational (as in the referenced implementation), this is accessible. Nothing to change.
64-76: External link handling looks correct.asChild Link with target="_blank" and rel="noopener noreferrer" is correct and safe. ArrowUpRightIcon communicates the behavior. No action needed.
10-10: ✅ ContractIcon import resolves locally in the dashboard app
- A
ContractIconimplementation exists atapps/dashboard/src/@/icons/ContractIcon.tsx, matching the import path.- The dashboard app isn’t pulling this icon from
apps/playground-web—it’s a local copy.- No cross-app coupling detected; no shared package or wrapper needed.
41-50: Add WebKit mask for Safari compatibility and confirm Tailwind tokenThe Safari-only mask-image fallback is necessary—
-webkit-mask-imagemust be added alongsidemask-imagefor consistent clipping. Thetext-bordercolor token is defined in our UI package theme (border: "hsl(var(--border))"), so bothtext-borderanddark:text-border/70are valid Tailwind classes.Apply this minimal change:
<GridPattern width={30} height={30} strokeDasharray={"4 2"} className="text-border dark:text-border/70 hidden lg:block translate-x-5" style={{ maskImage: "linear-gradient(to left bottom,white,transparent,transparent)", + WebkitMaskImage: + "linear-gradient(to left bottom,white,transparent,transparent)", }} />apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx (1)
33-41: Button styling update aligns with dashboard spec — LGTMAdopts size="sm", variant="secondary", and pill shape. No behavioral changes; matches the PR’s cross-dashboard styling direction.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx (4)
1-1: Icon imports follow “Icon” suffix convention — LGTMMatches our lucide-react import rule (…Icon naming).
10-12: Header spacing tweak — LGTMmb-3 reads better with the denser list below.
14-22: List spacing and content — LGTMspace-y-1.5 tightens the bullets and the added “Securely manage backend wallets” line fits the narrative.
24-31: External “Learn More” CTA pill — LGTMButton asChild + Link with target/_blank and rel is correct. The outline + bg-card combo achieves the intended pill aesthetic.
Also applies to: 38-39
...ard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
Show resolved
Hide resolved
Merge activity
|
<!--
## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"
If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):
## Notes for the reviewer
Anything important to call out? Be sure to also clarify these in your comments.
## How to test
Unit tests, playground, etc.
-->
<!-- start pr-codex -->
---
## PR-Codex overview
This PR focuses on refining the UI components and improving the layout across various pages in the dashboard application. It includes adjustments to button styles, layout properties, and content formatting for better user experience.
### Detailed summary
- Updated button styles for consistency (size and variant).
- Adjusted layout properties for various components (e.g., `max-w-7xl`, padding).
- Enhanced descriptions with line breaks for clarity.
- Modified component structures for better alignment and spacing.
- Removed unnecessary links from `webhooks` and `contracts` sections.
- Improved the `ProjectPageHeader` and `ProjectPageFooter` components.
- Revised `LinkGroup` to utilize dynamic icon rendering.
- Enhanced `EmptyStateCard` and `TotalSponsoredCard` layouts.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
<!-- end pr-codex -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
- New Features
- Headers now accept rich content (React nodes) enabling responsive line breaks.
- Style
- Standardized small "secondary" bordered pill buttons across many actions and dialogs.
- Updated header typography, spacing, avatar sizing, and max-width constraints for a more prominent layout.
- Icons adjusted for size/color; token cards now stack vertically; dialog widths constrained; footer container width tightened.
- Bug Fixes / Refactor
- Replaced visual separators with CSS borders and simplified DOM structure.
- Removed certain header doc links from Webhooks and Payments pages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
1ffe360 to
fbe1aa2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx (1)
71-90: Use NavLink for internal project-page header linksPer dashboard conventions, internal links should use
NavLinkfor automatic active-state styling. External URLs (starting withhttp) can continue usingnext/link.Please apply this optional refactor in the following locations:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx, lines 71–90 (icon-only links)apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx, lines 111–127 (text-label links)Example diff (icon links at lines 71–90):
--- a/apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx +++ b/apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; +import { NavLink } from "@/components/ui/NavLink"; {orderedLinks.map((link) => { const isExternal = link.href.startsWith("http"); + const LinkCmp = isExternal ? Link : NavLink; const Icon = linkTypeToIcon[link.type]; return ( <ToolTipLabel key={link.type} label={linkTypeToLabel[link.type]}> @@ -75,12 +76,13 @@ <Button asChild size="icon" variant="secondary" className="rounded-full border" > - <Link + <LinkCmp href={link.href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener noreferrer" : undefined} className="flex flex-row items-center gap-2" aria-label={linkTypeToLabel[link.type]} title={linkTypeToLabel[link.type]} > <Icon className="size-4 text-foreground" aria-hidden="true" /> - </Link> + </LinkCmp> </Button> </ToolTipLabel>And similarly for the text-label links at lines 111–127:
@@ -111,5 +112,6 @@ {orderedLinks.map((link) => { const isExternal = link.href.startsWith("http"); + const LinkCmp = isExternal ? Link : NavLink; const Icon = linkTypeToIcon[link.type]; return ( <LinkCmp href={link.href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener noreferrer" : undefined} > <Icon className="size-4 text-muted-foreground" aria-hidden="true" /> {linkTypeToLabel[link.type]} - </Link> + </LinkCmp>This change is optional but recommended to keep navigation patterns consistent and leverage built-in active styling.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (25)
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx(3 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx(1 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx(5 hunks)apps/dashboard/src/@/components/blocks/project-page/project-page.tsx(2 hunks)apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx(4 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx(0 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx(0 hunks)apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx(2 hunks)
💤 Files with no reviewable changes (2)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx
✅ Files skipped from review due to trivial changes (2)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx
🚧 Files skipped from review as they are similar to previous changes (20)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/overview/engine-instances-table.tsx
- apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/import/import-engine-dialog.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx
- apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx
- apps/dashboard/src/@/components/blocks/project-page/project-page.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/page.tsx
- apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx
- apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Stick to design-tokens: background (`bg-card`), borders (`border-border`), muted text (`text-muted-foreground`) etc.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Reuse core UI primitives; avoid re-implementing buttons, cards, modals.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Layouts should reuse `SidebarLayout` / `FullWidthSidebarLayout` (`@/components/blocks/SidebarLayout`).
Learnt from: jnsdls
PR: thirdweb-dev/js#7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/wallets/page.tsx:1-1
Timestamp: 2025-08-20T10:37:10.123Z
Learning: In apps/dashboard, prefer importing UI components from "workspace/ui/components/*" where possible, as per jnsdls preference, rather than from "@/components/ui/*".
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Spacing utilities (`px-*`, `py-*`, `gap-*`) are preferred over custom margins.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use the `container` class with a `max-w-7xl` cap for page width consistency.
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/layout.tsx : Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data.
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `NavLink` for internal navigation with automatic active states in dashboard and playground apps
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically.
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
📚 Learning: 2025-06-18T04:30:04.326Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7365
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17
Timestamp: 2025-06-18T04:30:04.326Z
Learning: Next.js Link component fully supports both internal and external URLs and works appropriately with all standard anchor attributes including target="_blank", rel="noopener noreferrer", etc. Using Link for external URLs is completely appropriate and recommended.
Applied to files:
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx (1)
packages/thirdweb/src/react/web/ui/components/text.tsx (1)
Link(44-62)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Unit Tests
- GitHub Check: Size
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (4)
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx (4)
73-74: Nice upgrade: dynamic icon components instead of nodes.Computing
Iconand rendering it keeps things lightweight and consistent with the “no raw SVG” rule. Good move.Also applies to: 113-114
79-81: Button standardization looks good.
variant="secondary"withrounded-full bordermatches the design tokens direction and the PR’s stated goal of consistent button treatments.
106-110: Dropdown alignment change: verify no viewport clipping on narrow screens.
align="center"andsideOffset={10}can clip near edges on very small screens. If you observe clipping, consider adding/adjusting Radix collision props (e.g.,avoidCollisions,collisionPadding) or enforcing a max width with safe padding.If helpful, I can inspect other DropdownMenu usages in the repo to mirror their collision settings for consistency.
79-81: Consistency check: tokens and spacing look aligned with our dashboard rules.
- Using
text-foregroundfor primary icons andtext-muted-foregroundin menus matches token guidance.- Rounded, bordered secondary icon buttons are consistent with the current direction.
- Dropdown items keep compact vertical rhythm with
py-2.No action needed.
Also applies to: 102-104, 106-110, 118-119
| const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = { | ||
| api: NetworkIcon, | ||
| docs: BookTextIcon, | ||
| playground: BoxIcon, | ||
| webhooks: WebhookIcon, | ||
| settings: SettingsIcon, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use LucideIcon type and add a type-only import; avoid React.FC here (prevents “Cannot find name 'React'” and tightens types).
lucide-react icons are LucideIcon forwardRef components, not plain React.FC. Also, referencing React.FC requires a React type import you don’t currently have. Switching to LucideIcon fixes both correctness and type-accuracy.
Apply this diff:
@@
-import {
+import {
BookTextIcon,
BoxIcon,
EllipsisVerticalIcon,
NetworkIcon,
SettingsIcon,
WebhookIcon,
} from "lucide-react";
+import type { LucideIcon } from "lucide-react";
@@
-const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = {
+const linkTypeToIcon: Record<LinkType, LucideIcon> = {
api: NetworkIcon,
docs: BookTextIcon,
playground: BoxIcon,
webhooks: WebhookIcon,
settings: SettingsIcon,
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = { | |
| api: NetworkIcon, | |
| docs: BookTextIcon, | |
| playground: BoxIcon, | |
| webhooks: WebhookIcon, | |
| settings: SettingsIcon, | |
| }; | |
| import { | |
| BookTextIcon, | |
| BoxIcon, | |
| EllipsisVerticalIcon, | |
| NetworkIcon, | |
| SettingsIcon, | |
| WebhookIcon, | |
| } from "lucide-react"; | |
| import type { LucideIcon } from "lucide-react"; | |
| const linkTypeToIcon: Record<LinkType, LucideIcon> = { | |
| api: NetworkIcon, | |
| docs: BookTextIcon, | |
| playground: BoxIcon, | |
| webhooks: WebhookIcon, | |
| settings: SettingsIcon, | |
| }; |
🤖 Prompt for AI Agents
In apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
around lines 41 to 47, replace the current mapping type that uses React.FC with
the lucide-react forwardRef type: add a type-only import "import type {
LucideIcon } from 'lucide-react';" at the top of the file and change the mapping
signature to use Record<LinkType, LucideIcon> (remove any use of React.FC and
any runtime React type import) so the icons are typed correctly as LucideIcon
forwardRef components.
| <Link | ||
| href={link.href} | ||
| target={isExternal ? "_blank" : undefined} | ||
| rel={isExternal ? "noopener noreferrer" : undefined} | ||
| className="flex flex-row items-center gap-2" | ||
| > | ||
| {linkTypeToIcon[link.type]} | ||
| <Icon className="size-4 text-foreground" /> | ||
| </Link> | ||
| </Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add accessible names for icon-only links and hide decorative icons from SR.
The direct-link buttons render only an icon, so screen readers will announce “button” with no name. Use the existing label map for aria-label and mark icons aria-hidden.
Apply this diff:
@@
- <Link
+ <Link
href={link.href}
target={isExternal ? "_blank" : undefined}
rel={isExternal ? "noopener noreferrer" : undefined}
- className="flex flex-row items-center gap-2"
+ className="flex flex-row items-center gap-2"
+ aria-label={linkTypeToLabel[link.type]}
+ title={linkTypeToLabel[link.type]}
>
- <Icon className="size-4 text-foreground" />
+ <Icon className="size-4 text-foreground" aria-hidden="true" />
</Link>@@
- <Icon className="size-4 text-muted-foreground" />
+ <Icon className="size-4 text-muted-foreground" aria-hidden="true" />Also applies to: 125-125
🤖 Prompt for AI Agents
In apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
around lines 82-90 and also at line 125, the icon-only Link/Button elements lack
accessible names and their icons are not hidden from screen readers; add an
appropriate aria-label (using the existing label map) to the Link/Button so the
control has a name, and add aria-hidden="true" to the Icon component (or pass a
prop to render it hidden to SR) so the decorative icon is ignored by screen
readers; ensure the aria-label text comes from the same label map used elsewhere
and apply the same change at the other occurrence on line 125.
| <Button size="icon" variant="secondary" className="rounded-full border"> | ||
| <EllipsisVerticalIcon className="size-4 text-foreground" /> | ||
| </Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Name the overflow menu trigger for accessibility.
Icon-only trigger needs an accessible name.
Apply this diff:
- <Button size="icon" variant="secondary" className="rounded-full border">
+ <Button
+ size="icon"
+ variant="secondary"
+ className="rounded-full border"
+ aria-label="More project links"
+ >
<EllipsisVerticalIcon className="size-4 text-foreground" />
</Button>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Button size="icon" variant="secondary" className="rounded-full border"> | |
| <EllipsisVerticalIcon className="size-4 text-foreground" /> | |
| </Button> | |
| <Button | |
| size="icon" | |
| variant="secondary" | |
| className="rounded-full border" | |
| aria-label="More project links" | |
| > | |
| <EllipsisVerticalIcon className="size-4 text-foreground" /> | |
| </Button> |
🤖 Prompt for AI Agents
In apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
around lines 102 to 104, the icon-only Button used as the overflow menu trigger
lacks an accessible name; add an appropriate accessible name by adding an
aria-label (e.g., "More options" or "Open menu") to the Button and, if this
button toggles a menu, add aria-haspopup="menu" and manage aria-expanded when
the menu is open/closed so screen readers can identify its purpose and state.

PR-Codex overview
This PR focuses on improving the UI components and layouts within the dashboard application, enhancing user experience with better spacing, button styles, and descriptive text formatting.
Detailed summary
Summary by CodeRabbit
New Features
Style
Bug Fixes / Refactor