Skip to content

Conversation

@MananTank
Copy link
Member

@MananTank MananTank commented Aug 25, 2025


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

  • Updated button styles to include size and border variations.
  • Enhanced layout with consistent maximum widths for containers.
  • Improved spacing in various sections for better visual appeal.
  • Refined text descriptions to include line breaks for clarity.
  • Adjusted component structures for better responsiveness.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

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, container max-widths, and padding for a more prominent layout.
    • Icons resized/recolored; token cards stacked vertically; dialog max-widths constrained; footer width tightened; various header/footer spacing tweaks.
  • Bug Fixes / Refactor

    • Replaced visual separators with CSS borders and simplified DOM.
    • Removed certain header doc links from Webhooks and Payments pages; added an extra footer separator in the sidebar.

@vercel
Copy link

vercel bot commented Aug 25, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
thirdweb-www Ready Ready Preview Comment Aug 25, 2025 5:19pm
4 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
docs-v2 Skipped Skipped Aug 25, 2025 5:19pm
nebula Skipped Skipped Aug 25, 2025 5:19pm
thirdweb_playground Skipped Skipped Aug 25, 2025 5:19pm
wallet-ui Skipped Skipped Aug 25, 2025 5:19pm

@vercel vercel bot temporarily deployed to Preview – docs-v2 August 25, 2025 13:10 Inactive
@vercel vercel bot temporarily deployed to Preview – nebula August 25, 2025 13:10 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui August 25, 2025 13:10 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground August 25, 2025 13:10 Inactive
@changeset-bot
Copy link

changeset-bot bot commented Aug 25, 2025

⚠️ No Changeset found

Latest commit: fbe1aa2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 25, 2025

Walkthrough

Presentation-focused UI and layout updates across the dashboard: icon mapping changed to icon components for project header links, buttons standardized to secondary/size="sm" with borders, header descriptions now accept React nodes, container widths/paddings adjusted, minor DOM simplifications, and one sidebar footer separator added.

Changes

Cohort / File(s) Summary of Changes
Project header & links
apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
Replaced icon map values with component refs (React.FC props) and render icons as components; direct-link and dropdown icons now use dynamic Icon components with different color semantics; updated button variants/rounded styling and DropdownMenuContent alignment/rounding/sideOffset.
Project page core
apps/dashboard/src/@/components/blocks/project-page/project-page.tsx, apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx, apps/dashboard/src/@/components/blocks/project-page/project-page-footer.tsx
Header description prop changed to React.ReactNode; spacing, avatar size, title typography, container max widths and paddings adjusted; Separator usage removed in favor of conditional borders; imports updated to use cn.
Header descriptions as React nodes
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/page.tsx, .../payments/page.tsx, .../transactions/page.tsx, .../wallets/page.tsx
header.description converted from string to JSX fragments with responsive <br /> for line breaks; payments page also sets a button size="sm".
Button styling standardization (various)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.tsx, .../webhooks/components/CreateWebhookModal.tsx, .../engine/(general)/import/import-engine-dialog.tsx, .../engine/(general)/overview/engine-instances-table.tsx, apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx, apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
Many Buttons switched from variant="outline" to variant="secondary", added border, rounded-full, increased gaps, and set size="sm" where applicable; modal triggers and action buttons updated visually only.
Engine UI updates
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/engine/(general)/_components.tsx, .../import/import-engine-dialog.tsx, .../overview/engine-instances-table.tsx
Icon swap (ExternalLinkIcon → ArrowUpRightIcon), increased icon sizes, pill-shaped action wrappers, spacing tweaks; action icons updated to ArrowRight/ArrowUpRight; presentational only.
Tokens UI adjustments
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/cards.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/page.tsx
Import button restyled to secondary with border and size="sm"; tokens layout changed from row to column; icon padding/size reduced; dialog content constrained with !max-w-md.
Webhooks header links removal
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx, .../webhooks/payments/page.tsx
Removed header.links docs entries from ProjectPage header config; other header actions retained.
Transactions & Server Wallets UI
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/analytics/tx-table/tx-table-ui.tsx, .../transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
Removed decorative icon from transactions header and increased title typography; server wallets header restructured to stacked layout with smaller icon container—purely presentational.
RPC analytics FTUX layout
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx
FTUX branch removes the container max-w-7xl wrapper and now uses flex grow flex-col for layout; no logic changes.
Project overview simplifications
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
Removed unnecessary wrapper divs around card components (simplified DOM); behavior unchanged.
Settings layout padding
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/layout.tsx
Increased header vertical padding from py-10 to py-12.
Sidebar footer separator
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx
Added an extra separator entry at the top of the footerSidebarLinks array.
Team analytics empty state growth
apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
Added grow/flex-grow to container and inner wrappers; removed outer container width constraint to enable vertical expansion.
Account deploy card visual update
apps/dashboard/src/app/(app)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
Visual enhancements: rounded-2xl, GridPattern decorative element, ContractIcon pill, responsive line break in copy, action buttons rounded-full with size="sm", muted icon coloring; no logic changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 08-25-dashboard_various_ui_tweaks_in_project_pages

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@MananTank MananTank marked this pull request as ready for review August 25, 2025 13:10
@github-actions github-actions bot added the Dashboard Involves changes to the Dashboard. label Aug 25, 2025
@MananTank MananTank requested review from a team as code owners August 25, 2025 13:10
Copy link
Member Author

MananTank commented Aug 25, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

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
Copy link

codecov bot commented Aug 25, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.53%. Comparing base (bdcbe0e) to head (fbe1aa2).
⚠️ Report is 1 commits behind head on main.

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           
Flag Coverage Δ
packages 56.53% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Contributor

github-actions bot commented Aug 25, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 64.06 KB (0%) 1.3 s (0%) 240 ms (+240.03% 🔺) 1.6 s
thirdweb (cjs) 357.05 KB (0%) 7.2 s (0%) 711 ms (+20.25% 🔺) 7.9 s
thirdweb (minimal + tree-shaking) 5.73 KB (0%) 115 ms (0%) 68 ms (+1191.3% 🔺) 182 ms
thirdweb/chains (tree-shaking) 526 B (0%) 11 ms (0%) 60 ms (+3837.62% 🔺) 70 ms
thirdweb/react (minimal + tree-shaking) 19.15 KB (0%) 383 ms (0%) 76 ms (+437.59% 🔺) 459 ms

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 modal

Since 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 trigger

Setting 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 color

The 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/link

This 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-5

You’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 usage

Adding 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 redundant

On 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 accuracy

Lucide 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 links

For 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 screens

Switching 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 tappable

Reducing 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 href is 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 with Link when href exists; otherwise keep the div + role="button" path for onClick.

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 onClick variant; native Link handles 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 clarity

Our 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 consideration

Switching 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 a

Since 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 configurable

If 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 component

Our 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.

📥 Commits

Reviewing files that changed from the base of the PR and between bdcbe0e and 232a4ab.

📒 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 @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless 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.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/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)/import/import-engine-dialog.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)/webhooks/components/CreateWebhookModal.tsx
  • 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)/tokens/cards.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)/overview/engine-instances-table.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)/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)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.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
**/*.{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.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/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)/import/import-engine-dialog.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)/webhooks/components/CreateWebhookModal.tsx
  • 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)/tokens/cards.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)/overview/engine-instances-table.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)/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)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.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,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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() from @/lib/utils for conditional class logic
Use design system tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components (Node edge): Start files with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components

Files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.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/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)/import/import-engine-dialog.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)/webhooks/components/CreateWebhookModal.tsx
  • 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)/tokens/cards.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)/overview/engine-instances-table.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)/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)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/header/link-group.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/rpc/components/RpcAnalytics.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.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
🧠 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.tsx
  • apps/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.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/page.tsx
  • apps/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.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)/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.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/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)/webhooks/components/CreateWebhookModal.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)/transactions/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)/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.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • 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/**/*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.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)/webhooks/components/CreateWebhookModal.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)/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)/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.tsx
  • apps/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.tsx
  • apps/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.tsx
  • apps/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.tsx
  • apps/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.tsx
  • apps/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.tsx
  • apps/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.tsx
  • apps/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.tsx
  • apps/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.tsx
  • 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} : 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.tsx
  • apps/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 rhythm

The 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 in container max-w-7xl will not introduce nested containers
apps/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” wrapper

The <RpcFTUX> view is rendered inside RpcAnalytics, which lives as a child of <ProjectPage>. In project-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 pattern

Switching 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 good

Removing 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 better

The 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 tokens

The header margin and list spacing adjustments improve readability without affecting structure.


24-31: External link button: solid accessibility and token usage

Good 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-friendly

variant="secondary" with a bordered rounded-full icon button matches the new header action style across pages.


102-110: Dropdown trigger/button refinements LGTM

Centered 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-proof

Rendering 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 direction

Switching 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 win

Using 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 API

Using 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 break

This 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 solid

JSX fragment with a conditional line break improves scannability without altering semantics.


73-74: Please manually verify the Button “sm” size variant

I wasn’t able to locate the buttonVariants definition 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 the buttonVariants (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 direction

variant="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 appropriate

Replacing ad-hoc string composition with cn keeps class logic readable and consistent with our dashboard rules.


24-26: Bottom padding behavior flips compared to the summary; verify intended spacing with footers

The 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 rule

When 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 composition

Importing 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 rhythm

This aligns with the dashboard layout guidance and PR objectives for consistent page widths and spacing.


79-79: LGTM: Left column gap increase improves scan-ability

The 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 ratios

The 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 CTAs

The 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/contracts

The 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 separator

FullWidthSidebarLayout 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 of footerSidebarLinks is therefore necessary to visually separate the footer links from the main navigation. No change needed.

Likely an incorrect or invalid review comment.

@MananTank MananTank force-pushed the 08-25-dashboard_various_ui_tweaks_in_project_pages branch from 232a4ab to 0a5bc2f Compare August 25, 2025 13:24
@vercel vercel bot temporarily deployed to Preview – wallet-ui August 25, 2025 13:24 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground August 25, 2025 13:24 Inactive
@vercel vercel bot temporarily deployed to Preview – nebula August 25, 2025 13:24 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 August 25, 2025 13:24 Inactive
@MananTank MananTank force-pushed the 08-25-dashboard_various_ui_tweaks_in_project_pages branch from 0a5bc2f to 1ffe360 Compare August 25, 2025 13:27
@vercel vercel bot temporarily deployed to Preview – wallet-ui August 25, 2025 13:27 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 August 25, 2025 13:27 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground August 25, 2025 13:27 Inactive
@vercel vercel bot temporarily deployed to Preview – nebula August 25, 2025 13:27 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (grow vs h-full).

You’re signaling growth twice (parent Card has grow, wrapper has h-full grow). Within a flex parent, grow is sufficient; h-full depends on a definite parent height and is usually redundant here.

Apply this diff to drop h-full on 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 nested grow to keep sizing predictable.

EmptyStateContent doesn’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 metric in 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 EmptyStateContent internal.

-export function EmptyStateContent(props: EmptyStateProps): ReactElement {
+function EmptyStateContent(props: EmptyStateProps): ReactElement {

Also applies to: 28-33


33-37: Guard the CTA label when metric is undefined.

Currently, a provided link without a metric would 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 button

Icon-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 consistently

Next.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 TableRow

The 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 decorative

This 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 explicit bg-background on 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 secondary variant

-         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 tune

If 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 behavior

For 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 clarity

Small 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 232a4ab and 1ffe360.

📒 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 @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless 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.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)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
  • apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
  • apps/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.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)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
  • apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
  • apps/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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() from @/lib/utils for conditional class logic
Use design system tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components (Node edge): Start files with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components

Files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/contracts/import-contract-button.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)/account/contracts/_components/DeployViaCLIOrImportCard.tsx
  • apps/dashboard/src/app/(app)/team/components/Analytics/EmptyStateCard.tsx
  • apps/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.tsx
  • apps/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.tsx
  • 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 : 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.tsx
  • apps/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.tsx
  • apps/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 the container class 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 detected

I reviewed all occurrences of <EmptyStateCard> across the dashboard and checked for any direct parent wrappers using flex-row that 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 new grow class 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 useWalletBalance

I ran the ripgrep command and confirmed that this is the only call to invalidateQueries using the hard-coded ["walletBalance", selectedChainId] key, with no other occurrences in the codebase. However, the useWalletBalance hook from thirdweb/react manages 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 refreshNonce into each WalletBalanceCell and trigger balance.refetch() via a useEffect.

Please verify that the actual queryKey shape used by useWalletBalance indeed 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 ContractIcon implementation exists at apps/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 token

The Safari-only mask-image fallback is necessary—-webkit-mask-image must be added alongside mask-image for consistent clipping. The text-border color token is defined in our UI package theme (border: "hsl(var(--border))"), so both text-border and dark:text-border/70 are 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 — LGTM

Adopts 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 — LGTM

Matches our lucide-react import rule (…Icon naming).


10-12: Header spacing tweak — LGTM

mb-3 reads better with the denser list below.


14-22: List spacing and content — LGTM

space-y-1.5 tightens the bullets and the added “Securely manage backend wallets” line fits the narrative.


24-31: External “Learn More” CTA pill — LGTM

Button asChild + Link with target/_blank and rel is correct. The outline + bg-card combo achieves the intended pill aesthetic.

Also applies to: 38-39

@graphite-app
Copy link
Contributor

graphite-app bot commented Aug 25, 2025

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 -->
@graphite-app graphite-app bot force-pushed the 08-25-dashboard_various_ui_tweaks_in_project_pages branch from 1ffe360 to fbe1aa2 Compare August 25, 2025 17:09
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground August 25, 2025 17:09 Inactive
@vercel vercel bot temporarily deployed to Preview – nebula August 25, 2025 17:09 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 August 25, 2025 17:09 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui August 25, 2025 17:09 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 links

Per dashboard conventions, internal links should use NavLink for automatic active-state styling. External URLs (starting with http) can continue using next/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.

📥 Commits

Reviewing files that changed from the base of the PR and between 1ffe360 and fbe1aa2.

📒 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 @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless 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
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() from @/lib/utils for conditional class logic
Use design system tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components (Node edge): Start files with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in 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 Icon and 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" with rounded-full border matches 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" and sideOffset={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-foreground for primary icons and text-muted-foreground in 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

Comment on lines +41 to 47
const linkTypeToIcon: Record<LinkType, React.FC<{ className?: string }>> = {
api: NetworkIcon,
docs: BookTextIcon,
playground: BoxIcon,
webhooks: WebhookIcon,
settings: SettingsIcon,
};
Copy link
Contributor

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.

Suggested change
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.

Comment on lines 82 to 90
<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>
Copy link
Contributor

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.

Comment on lines +102 to 104
<Button size="icon" variant="secondary" className="rounded-full border">
<EllipsisVerticalIcon className="size-4 text-foreground" />
</Button>
Copy link
Contributor

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.

Suggested change
<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.

@graphite-app graphite-app bot merged commit fbe1aa2 into main Aug 25, 2025
25 checks passed
@graphite-app graphite-app bot deleted the 08-25-dashboard_various_ui_tweaks_in_project_pages branch August 25, 2025 17:19
@vercel vercel bot temporarily deployed to Production – thirdweb_playground August 25, 2025 17:19 Inactive
@vercel vercel bot temporarily deployed to Production – nebula August 25, 2025 17:19 Inactive
@vercel vercel bot temporarily deployed to Production – docs-v2 August 25, 2025 17:19 Inactive
@vercel vercel bot temporarily deployed to Production – wallet-ui August 25, 2025 17:19 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Dashboard Involves changes to the Dashboard.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants