Skip to content

[MNY-139] Dashboard, Portal, Playground: Add Bridge Product#7984

Merged
graphite-app[bot] merged 1 commit into
mainfrom
09-04-dashboard_portal_playground_add_bridge_product
Sep 5, 2025
Merged

[MNY-139] Dashboard, Portal, Playground: Add Bridge Product#7984
graphite-app[bot] merged 1 commit into
mainfrom
09-04-dashboard_portal_playground_add_bridge_product

Conversation

@MananTank

@MananTank MananTank commented Sep 4, 2025

Copy link
Copy Markdown
Contributor

PR-Codex overview

This PR focuses on enhancing the payment components and bridge functionality within the application, including UI updates, new features, and code refactoring for better maintainability.

Detailed summary

  • Deleted RouteDiscoveryCard.tsx.
  • Exported BridgeIcon from lucide-react.
  • Updated routing in next.config.mjs.
  • Modified navigation links in navLinks.ts.
  • Adjusted token data fetching in useTokensData.ts.
  • Refactored CreatePaymentWebhookButton to a regular function.
  • Enhanced styling in code-example.tsx.
  • Updated payment feature cards metadata.
  • Improved transaction-button.tsx for better account handling.
  • Added ProjectSettingsBreadcrumb for better navigation.
  • Introduced new QuickStartSection and RouteDiscovery components.
  • Refactored payment links table for better UI.
  • Updated PaymentHistory to use new table components.
  • Enhanced FeatureCard for improved layout and styling.
  • Added new sections for bridge functionality in the sidebar.
  • Streamlined payment components and improved code organization.

The following files were skipped due to too many changes: apps/playground-web/src/app/payments/transactions/page.tsx

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

Summary by CodeRabbit

  • New Features

    • Bridge page with analytics, route discovery, Quickstart section; CSV export for transaction history; Buy, Checkout, and Transaction playgrounds.
  • Navigation

    • Added "Bridge" to project sidebar; removed Payments UI Components page and added redirect /payments/ui-components → /payments; Webhooks and Payments navigation simplified.
  • UI/UX Improvements

    • Redesigned analytics and payment-history table; updated FeatureCard and EmptyState visuals; removed "Swap Tokens" card; settings use breadcrumb navigation; assorted copy and spacing tweaks.

@vercel vercel Bot temporarily deployed to Preview – nebula September 4, 2025 14:04 Inactive
@vercel

vercel Bot commented Sep 4, 2025

Copy link
Copy Markdown

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

Project Deployment Preview Comments Updated (UTC)
thirdweb_playground Ready Ready Preview Comment Sep 5, 2025 8:26pm
thirdweb-www Ready Ready Preview Comment Sep 5, 2025 8:26pm
3 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
docs-v2 Skipped Skipped Sep 5, 2025 8:26pm
nebula Skipped Skipped Sep 5, 2025 8:26pm
wallet-ui Skipped Skipped Sep 5, 2025 8:26pm

@changeset-bot

changeset-bot Bot commented Sep 4, 2025

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 4ca407d

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

@vercel vercel Bot temporarily deployed to Preview – wallet-ui September 4, 2025 14:04 Inactive
@coderabbitai

coderabbitai Bot commented Sep 4, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a Bridge area (icon, sidebar link, server Bridge page, RouteDiscovery, QuickStartSection) and Webhooks layout; refactors Payments UI/components and playgrounds; updates several component APIs/props; removes the Playground "UI Components" page and adds a redirect.

Changes

Cohort / File(s) Summary
Project header action typing & styles
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
Made actions.primary optional; changed Button secondaryoutline; simplified button classes; adjusted description truncation.
Bridge feature (dashboard)
apps/dashboard/src/@/icons/BridgeIcon.tsx, apps/dashboard/src/app/(app)/team/.../(sidebar)/bridge/page.tsx, .../RouteDiscovery.tsx, .../QuickstartSection.client.tsx, .../components/ProjectSidebarLayout.tsx
Added BridgeIcon alias; new server Bridge page composing analytics, RouteDiscovery, QuickStartSection; switched to SingleNetworkSelector API; added "Bridge" sidebar link.
Payments UI components & cards
apps/dashboard/src/app/.../payments/components/FeatureCard.client.tsx, .../EmptyState.tsx, .../AdvancedSection.client.tsx, .../QuickstartSection.client.tsx, .../PaymentLinksTable.client.tsx
Removed color and link target; redesigned FeatureCard and EmptyState (fixed XIcon); removed Swap Tokens card; cleaned unused imports; adjusted button/link behaviors.
Payments analytics & tables
apps/dashboard/src/app/.../payments/components/PayAnalytics.tsx, .../PaymentHistory.client.tsx, .../PaymentsTableRow.tsx, .../payments/page.tsx
Reworked PayAnalytics layout and data filtering (completed); migrated PaymentHistory to Table primitives; added CSV export support; updated row UI; simplified Payments page (removed analytics/searchParams; added Webhooks action).
Webhooks layout & pages
apps/dashboard/src/app/.../webhooks/layout.tsx, .../webhooks/payments/page.tsx, .../webhooks/contracts/page.tsx, .../payments/webhooks/components/webhooks.client.tsx
Added server Webhooks layout with ProjectPage header/tabs; simplified per-page renders to content-only; removed some client wrappers; made CreatePaymentWebhookButton internal.
Settings breadcrumb integration
apps/dashboard/src/app/.../settings/_components/project-settings-breadcrumb.tsx, .../settings/account-abstraction/page.tsx, .../settings/payments/page.tsx, .../settings/wallets/page.tsx
Added ProjectSettingsBreadcrumb and replaced/back-button headers on multiple settings pages; adjusted spacing and wallets props.
Playground: remove UI Components, nav/redirects
apps/playground-web/next.config.mjs, apps/playground-web/src/app/payments/ui-components/page.tsx (deleted), apps/playground-web/src/app/navLinks.ts, apps/playground-web/src/app/data/pages-metadata.ts
Deleted UI Components page and metadata/nav entry; added redirect /payments/ui-components/payments; renamed payments card.
Playground: shared Left/Right controls (lockable)
apps/playground-web/src/app/payments/components/LeftSection.tsx, .../RightSection.tsx
Added optional lockedWidget prop and effectiveWidget logic; hide widget selector when locked and gate subsections accordingly.
Playground: Buy/Checkout/Transaction playbooks
apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx, .../commerce/CheckoutPlayground.tsx, .../transactions/TransactionPlayground.tsx, plus page updates
Introduced Checkout and Transaction playground components; refactored BuyPlayground to named export; pages now use new playgrounds and locked widgets.
Playground misc UI/data tweaks
apps/playground-web/src/components/code/code-example.tsx, .../components/pay/transaction-button.tsx, .../hooks/useTokensData.ts
Minor typography/spacing tweaks; PayTransactionButtonPreview early-returns connect button and shows static "Price: 50 USDC"; tokens fetch includes includePrices=false.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Next as Next.js Server
  participant Auth as Auth Service
  participant Projects as Projects Service
  participant Third as Thirdweb Client
  participant Page as Bridge Page

  User->>Next: GET /team/:team_slug/:project_slug/.../bridge
  Next->>Auth: getAuthToken()
  Next->>Projects: getProject(team_slug, project_slug)
  alt missing auth
    Next-->>User: redirect to login
  else if project not found
    Next-->>User: redirect to team page / notFound
  else
    Next->>Third: getClientThirdwebClient(jwt, project.teamId)
    Next->>Page: render Bridge Page (header, analytics, route discovery, quickstart)
    Page-->>User: HTML/JS for Bridge UI
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
Move analytics from /payments to new /bridge page (MNY-139)
Add bridge cards: Earn Fees, UI components, Swap Tokens, Webhooks (MNY-139) Swap Tokens card was removed; UI Components entry was removed from playground and not added to Bridge quickstart.
Playground: change Payments menu to Monetize (MNY-139) UI Components page removed and redirect added, but nav label rename to "Monetize" is not present in modified nav links.
Portal: rename payments tab to monetize (MNY-139) No portal code changes in this diff.

Out-of-scope changes

Code Change Explanation
CSV export integration added to PaymentHistory (apps/.../PaymentHistory.client.tsx) CSV export props for ExportToCSVButton (fileName/getData) and CSV transformation were added; not required by MNY-139 objectives.
ProjectSettingsBreadcrumb added and settings header refactors (apps/.../settings/_components/project-settings-breadcrumb.tsx) Breadcrumb and multiple settings page header replacements are UI/UX refactors unrelated to adding Bridge or playground monetization.
FeatureCard public API changes (apps/.../FeatureCard.client.tsx) Removal of color and link.target alters a shared component API beyond the linked issue scope.

Possibly related PRs

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 09-04-dashboard_portal_playground_add_bridge_product

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added Dashboard Involves changes to the Dashboard. Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase. labels Sep 4, 2025

MananTank commented Sep 4, 2025

Copy link
Copy Markdown
Contributor Author

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

codecov Bot commented Sep 4, 2025

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7984   +/-   ##
=======================================
  Coverage   56.63%   56.63%           
=======================================
  Files         904      904           
  Lines       58677    58677           
  Branches     4161     4161           
=======================================
  Hits        33231    33231           
  Misses      25340    25340           
  Partials      106      106           
Flag Coverage Δ
packages 56.63% <ø> (ø)
🚀 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

github-actions Bot commented Sep 4, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 64.01 KB (0%) 1.3 s (0%) 2.5 s (+1.76% 🔺) 3.8 s
thirdweb (cjs) 357.32 KB (0%) 7.2 s (0%) 17.7 s (+1.34% 🔺) 24.9 s
thirdweb (minimal + tree-shaking) 5.73 KB (0%) 115 ms (0%) 204 ms (+53.3% 🔺) 318 ms
thirdweb/chains (tree-shaking) 526 B (0%) 11 ms (0%) 57 ms (+25.11% 🔺) 68 ms
thirdweb/react (minimal + tree-shaking) 19.15 KB (0%) 383 ms (0%) 331 ms (+18.79% 🔺) 714 ms

@MananTank MananTank force-pushed the 09-04-dashboard_portal_playground_add_bridge_product branch from 8226c74 to 4b84bb3 Compare September 4, 2025 14:26
@vercel vercel Bot temporarily deployed to Preview – nebula September 4, 2025 14:26 Inactive
@vercel vercel Bot temporarily deployed to Preview – wallet-ui September 4, 2025 14:26 Inactive
@graphite-app graphite-app Bot changed the base branch from mnn/fix-bridge-crash to graphite-base/7984 September 4, 2025 14:45
@graphite-app graphite-app Bot force-pushed the graphite-base/7984 branch from 29be72e to 17d40af Compare September 4, 2025 14:56
@graphite-app graphite-app Bot force-pushed the 09-04-dashboard_portal_playground_add_bridge_product branch from 4b84bb3 to 965d8c7 Compare September 4, 2025 14:56
@vercel vercel Bot temporarily deployed to Preview – nebula September 4, 2025 14:56 Inactive
@vercel vercel Bot temporarily deployed to Preview – wallet-ui September 4, 2025 14:56 Inactive
@graphite-app graphite-app Bot changed the base branch from graphite-base/7984 to main September 4, 2025 14:56
@graphite-app graphite-app Bot force-pushed the 09-04-dashboard_portal_playground_add_bridge_product branch from 965d8c7 to f1534fe Compare September 4, 2025 14:56
@vercel vercel Bot temporarily deployed to Preview – wallet-ui September 4, 2025 14:56 Inactive
@MananTank MananTank force-pushed the 09-04-dashboard_portal_playground_add_bridge_product branch from bc6470b to 2fd101e Compare September 5, 2025 19:22
@vercel vercel Bot temporarily deployed to Preview – docs-v2 September 5, 2025 19:22 Inactive
@vercel vercel Bot temporarily deployed to Preview – wallet-ui September 5, 2025 19:22 Inactive
@vercel vercel Bot temporarily deployed to Preview – nebula September 5, 2025 19:22 Inactive
@MananTank MananTank marked this pull request as ready for review September 5, 2025 19:28
@MananTank MananTank requested review from a team as code owners September 5, 2025 19:28
@graphite-app

graphite-app Bot commented Sep 5, 2025

Copy link
Copy Markdown
Contributor

Merge activity

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (14)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/webhooks/components/webhooks.client.tsx (2)

191-194: Fix query invalidation key to include teamId (stale list bug).

The list query key includes teamId, but the create mutation invalidates only by clientId. Newly created webhooks may not appear until a full reload.

Apply:

-      return queryClient.invalidateQueries({
-        queryKey: ["webhooks", props.clientId],
-      });
+      return queryClient.invalidateQueries({
+        queryKey: ["webhooks", props.clientId, props.teamId],
+      });

339-341: Also align delete invalidations with the list query key.

Both invalidations should include teamId to refresh the correct cache entry.

-      return queryClient.invalidateQueries({
-        queryKey: ["webhooks", props.clientId],
-      });
+      return queryClient.invalidateQueries({
+        queryKey: ["webhooks", props.clientId, props.teamId],
+      });
-                  return queryClient.invalidateQueries({
-                    queryKey: ["webhooks", props.clientId],
-                  });
+                  return queryClient.invalidateQueries({
+                    queryKey: ["webhooks", props.clientId, props.teamId],
+                  });

Also applies to: 370-372

apps/playground-web/src/app/payments/commerce/page.tsx (1)

8-11: Fix grammar in user-facing description (“set up”, not “setup”).

Visible copy on the page; please correct.

-const description =
-  "Enable purchase of any service or goods with fiat or cryptocurrency and setup notifications on every sale to ship goods, activate services, and more";
+const description =
+  "Enable purchase of any service or goods with fiat or cryptocurrency and set up notifications on every sale to ship goods, activate services, and more";
apps/playground-web/src/app/payments/components/RightSection.tsx (1)

107-129: Guard transaction preview when no wallet is connected.

Passing an empty string as to can produce invalid transactions; show a prompt instead.

-  if (effectiveWidget === "transaction") {
-    embed = (
-      <TransactionWidget
-        client={THIRDWEB_CLIENT}
-        description={props.options.payOptions.description}
-        image={props.options.payOptions.image}
-        paymentMethods={props.options.payOptions.paymentMethods}
-        theme={themeObj}
-        title={props.options.payOptions.title}
-        transaction={claimTo({
-          contract: nftContract,
-          quantity: 1n,
-          to: account?.address || "",
-          tokenId: 2n,
-        })}
-        currency={props.options.payOptions.currency}
-        showThirdwebBranding={props.options.payOptions.showThirdwebBranding}
-        {...(props.options.payOptions.buttonLabel && {
-          buttonLabel: props.options.payOptions.buttonLabel,
-        })}
-      />
-    );
-  }
+  if (effectiveWidget === "transaction" && !account?.address) {
+    embed = (
+      <div className="text-sm text-muted-foreground">
+        Connect a wallet to preview a transaction.
+      </div>
+    );
+  } else if (effectiveWidget === "transaction" && account?.address) {
+    embed = (
+      <TransactionWidget
+        client={THIRDWEB_CLIENT}
+        description={props.options.payOptions.description}
+        image={props.options.payOptions.image}
+        paymentMethods={props.options.payOptions.paymentMethods}
+        theme={themeObj}
+        title={props.options.payOptions.title}
+        transaction={claimTo({
+          contract: nftContract,
+          quantity: 1n,
+          to: account.address,
+          tokenId: 2n,
+        })}
+        currency={props.options.payOptions.currency}
+        showThirdwebBranding={props.options.payOptions.showThirdwebBranding}
+        {...(props.options.payOptions.buttonLabel && {
+          buttonLabel: props.options.payOptions.buttonLabel,
+        })}
+      />
+    );
+  }
apps/playground-web/src/app/payments/transactions/page.tsx (1)

27-33: Replace docsLink with TransactionWidget documentation URL.
In apps/playground-web/src/app/payments/transactions/page.tsx, update the docsLink prop:

- docsLink="https://portal.thirdweb.com/wallets/sponsor-gas?utm_source=playground"
+ docsLink="https://portal.thirdweb.com/payments/transactions?utm_source=playground"
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx (1)

13-16: Add explicit return type and expose className on the root component

Align with repo guidelines: explicit return types and a className prop on the root element.

Apply:

-export function TableRow(props: {
-  purchase: BridgePayment;
-  client: ThirdwebClient;
-}) {
+export function TableRow(props: {
+  purchase: BridgePayment;
+  client: ThirdwebClient;
+  className?: string;
+}): JSX.Element {
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (2)

52-71: Expose className on the header and merge with cn()

This component is widely used; exposing className improves layout flexibility.

 export type ProjectPageHeaderProps = {
   client: ThirdwebClient;
   title: string;
   description?: React.ReactNode;
   imageUrl?: string | null;
   icon: React.FC<{ className?: string }>;
   isProjectIcon?: boolean;
+  className?: string;
   actions: {
     primary?: Action;
     secondary?: Action;
   } | null;

73-76: Wire className onto the root element and type the export

Also add explicit return type for the exported component.

-export function ProjectPageHeader(props: ProjectPageHeaderProps) {
+export function ProjectPageHeader(props: ProjectPageHeaderProps): JSX.Element {
   return (
-    <header className="container max-w-7xl py-6 relative">
+    <header className={cn("container max-w-7xl py-6 relative", props.className)}>
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/QuickstartSection.client.tsx (1)

3-3: Use app UI primitive import path

Dashboard components should import UI primitives from "@/components/ui/*".

Apply:

-import { Button } from "@workspace/ui/components/button";
+import { Button } from "@/components/ui/button";
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/PaymentLinksTable.client.tsx (1)

316-318: Fix mislabeled destructive action in PaymentLinksTable.client.tsx Change the button label from “Delete Webhook” to “Delete Payment”.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx (1)

19-27: Server component pragma and explicit return types

Add server-only and explicit return types for consistency and tooling.

+import "server-only";
...
-export async function PayAnalytics(props: {
+export async function PayAnalytics(props: {
   projectClientId: string;
   client: ThirdwebClient;
   projectId: string;
   teamId: string;
   range: Range;
   interval: "day" | "week";
   authToken: string;
-}) {
+}): Promise<JSX.Element | null> {

Also type helpers:

-function GridWithSeparator(props: { children: React.ReactNode }) {
+function GridWithSeparator(props: { children: React.ReactNode }): JSX.Element {
...
-function CardContainer(props: { children: React.ReactNode }) {
-  return <Card className="flex p-4 lg:p-6">{props.children}</Card>;
-}
+function CardContainer(props: { children: React.ReactNode }): JSX.Element {
+  return <Card className="flex p-4 lg:p-6">{props.children}</Card>;
+}
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (3)

132-171: CSV header/order mismatch (rows don’t align with header).

Header is ["Type","Bought","Paid","Status","Recipient","Date"] but rows start with “Bought”. This will generate misaligned CSVs.

Apply:

   const rows: string[][] = data.map((purchase) => {
@@
-    return [
-      // bought
-      `${formatTokenAmount(toAmount)} ${purchase.destinationToken.symbol}`,
-      // paid
-      `${formatTokenAmount(fromAmount)} ${purchase.originToken.symbol}`,
-      // type
-      type,
-      // status
-      purchase.status,
-      // sender
-      purchase.sender,
-      // date
-      format(new Date(purchase.createdAt), "LLL dd y h:mm a"),
-    ];
+    return [
+      // type
+      type,
+      // bought
+      `${formatTokenAmount(toAmount)} ${purchase.destinationToken.symbol}`,
+      // paid
+      `${formatTokenAmount(fromAmount)} ${purchase.originToken.symbol}`,
+      // status
+      purchase.status,
+      // recipient
+      purchase.sender,
+      // date (match table format)
+      format(new Date(purchase.createdAt), "LLL dd, y h:mm a"),
+    ];

46-48: Strengthen React Query cache key and freshness.

Include teamId in the key to avoid cross-team cache bleed; set staleTime ≥ 60s per guidelines.

Apply:

-    queryKey: ["payments", props.projectClientId, page],
-    refetchInterval: 10_000,
+    queryKey: ["payments", props.projectClientId, props.teamId, page],
+    refetchInterval: 10_000,
+    staleTime: 60_000,

49-53: Base emptiness and rendering on filtered data.

Currently, isEmpty ignores the .filter(isBridgePayment) used later, causing empty tables with pagination when only onramp rows exist.

Apply:

-  const isEmpty = useMemo(
-    () => !payPurchaseData?.data.length,
-    [payPurchaseData],
-  );
+  const filtered = useMemo(
+    () => payPurchaseData?.data.filter(isBridgePayment) || [],
+    [payPurchaseData],
+  );
+  const isEmpty = filtered.length === 0;
-            return getCSVData(
-              payPurchaseData?.data.filter(isBridgePayment) || [],
-            );
+            return getCSVData(filtered);
-                ? payPurchaseData.data
-                    .filter(isBridgePayment)
-                    .map((purchase) => {
+                ? filtered.map((purchase) => {

Also applies to: 68-71, 91-103

🧹 Nitpick comments (49)
apps/playground-web/src/hooks/useTokensData.ts (1)

3-10: Type safety + abort support + safer URL build + future-proof includePrices.

Adds explicit return types, passes React Query’s abort signal to fetch, hardens URL construction (avoids double protocol), and makes includePrices a first-class option keyed in cache.

Apply:

-import { useQuery } from "@tanstack/react-query";
+import { useQuery, type UseQueryResult } from "@tanstack/react-query";
 import type { TokenMetadata } from "@/lib/types";

-async function fetchTokensFromApi(chainId?: number) {
-  const domain = process.env.NEXT_PUBLIC_BRIDGE_URL || "bridge.thirdweb.com";
-  const url = new URL(
-    `${domain.includes("localhost") ? "http" : "https"}://${domain}/v1/tokens`,
-  );
+async function fetchTokensFromApi(
+  chainId?: number,
+  includePrices = false,
+  signal?: AbortSignal,
+): Promise<Array<TokenMetadata>> {
+  const rawDomain = process.env.NEXT_PUBLIC_BRIDGE_URL || "bridge.thirdweb.com";
+  const domain = rawDomain.replace(/^https?:\/\//, "");
+  const protocol = domain.includes("localhost") ? "http" : "https";
+  const url = new URL("/v1/tokens", `${protocol}://${domain}`);

   if (chainId) {
     url.searchParams.append("chainId", String(chainId));
   }
   url.searchParams.append("limit", "1000");
-  url.searchParams.append("includePrices", "false");
+  url.searchParams.append("includePrices", includePrices ? "true" : "false");

-  const res = await fetch(url.toString(), {
+  const res = await fetch(url.toString(), {
+    signal,
     headers: {
       "Content-Type": "application/json",
       "x-client-id": process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID || "",
     },
   });
@@
-export function useTokensData({
-  chainId,
-  enabled = true,
-}: {
-  chainId?: number;
-  enabled?: boolean;
-}) {
-  return useQuery({
+export function useTokensData({
+  chainId,
+  enabled = true,
+  includePrices = false,
+}: {
+  chainId?: number;
+  enabled?: boolean;
+  includePrices?: boolean;
+}): UseQueryResult<Array<TokenMetadata>, Error> {
+  return useQuery<Array<TokenMetadata>, Error>({
     enabled,
-    queryFn: () => fetchTokensFromApi(chainId),
-    queryKey: ["tokens", chainId], // 2 minutes
-    staleTime: 1000 * 60 * 2,
+    queryFn: ({ signal }) => fetchTokensFromApi(chainId, includePrices, signal),
+    queryKey: ["tokens", chainId, includePrices],
+    staleTime: 1000 * 60 * 2, // 2 minutes
   });
 }

Also applies to: 16-16, 18-18, 37-49

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/webhooks/components/webhooks.client.tsx (5)

65-73: Set a sensible staleTime for React Query (≥60s per guidelines).

Prevents unnecessary refetch thrash and aligns with app defaults.

   const webhooksQuery = useQuery({
     queryFn: async () => {
       return await getWebhooks({
         clientId: props.clientId,
         teamId: props.teamId,
       });
     },
     queryKey: ["webhooks", props.clientId, props.teamId],
+    staleTime: 60_000,
   });

103-103: Typo casing: “URL” (all caps).

-              <TableHead>Url</TableHead>
+              <TableHead>URL</TableHead>

115-116: Ensure createdAt is formatted from a Date.

If createdAt is string/number, wrap in Date to avoid locale/env inconsistencies.

-                  {formatDistanceToNow(webhook.createdAt, { addSuffix: true })}
+                  {formatDistanceToNow(new Date(webhook.createdAt), { addSuffix: true })}

If createdAt is already a Date, this remains safe.


264-271: Use controlled props for shadcn Select to avoid passing extraneous field props.

-                  <Select {...field} onValueChange={field.onChange}>
+                  <Select value={field.value} onValueChange={field.onChange}>

152-157: Constrain version via Zod enum instead of free-form string.

-  version: z.string(),
+  version: z.enum(["1", "2"]),
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/layout.tsx (1)

1-1: Mark server component explicitly.

Add server-only pragma per dashboard guidelines for server files.

+import "server-only";
 import { WebhookIcon } from "lucide-react";
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx (1)

1-1: Add server-only pragma.

Consistent with server component conventions.

+import "server-only";
 import { notFound } from "next/navigation";
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx (1)

1-1: Add server-only pragma.

Declare server-only for this route segment file.

+import "server-only";
 import { notFound } from "next/navigation";
apps/playground-web/src/components/code/code-example.tsx (1)

27-31: Expose className on root for overrides.

Playground/Dashboard components should allow root-level class overrides. Add a className?: string prop and merge it on the outer div.

Apply outside-range changes like:

-import type { JSX } from "react";
+import type { JSX } from "react";
+import { cn } from "@/lib/utils";

 type CodeExampleProps = {
+  className?: string;
   preview: JSX.Element;
   code: string;
   lang: BundledLanguage;
   header?: {
     title: React.ReactNode;
     description?: React.ReactNode;
   };
 };

-export const CodeExample: React.FC<CodeExampleProps> = ({
+export const CodeExample: React.FC<CodeExampleProps> = ({
   code,
   lang,
   preview,
   header,
+  className,
 }) => {
   return (
-    <div className="relative z-0">
+    <div className={cn("relative z-0", className)}>
       {header && (
         <div className="mb-4">
apps/playground-web/next.config.mjs (1)

160-164: Avoid double redirect: point /connect/pay directly to /payments.

Right now, /connect/pay/payments/ui-components (Line 61-64) and then /payments/ui-components/payments (Lines 160-164). Collapse the first hop to reduce latency and preserve query params.

Apply outside-range change at Lines 61-64:

-      {
-        source: "/connect/pay",
-        destination: "/payments/ui-components",
-        permanent: false,
-      },
+      {
+        source: "/connect/pay",
+        destination: "/payments",
+        permanent: false,
+      },

Keep the new /payments/ui-components/payments redirect for backward compatibility.

apps/playground-web/src/app/data/pages-metadata.ts (1)

183-183: Hyphenate “Onchain” → “On‑chain”.

Minor copy nit for consistency with common usage elsewhere.

-    title: "Onchain Transaction",
+    title: "On-chain Transaction",
apps/playground-web/src/components/pay/transaction-button.tsx (3)

67-113: Remove redundant conditional; we already early-returned.

After the !account early return, {account && (...)} is unnecessary. Return the content directly to simplify.

-  return (
-    <>
-      {account && (
-        <div className="flex flex-col items-center justify-center gap-2">
+  return (
+    <div className="flex flex-col items-center justify-center gap-2">
           <div className="flex items-center gap-2">Price: 50 USDC</div>
           <TransactionButton
             onError={(e) => {
               console.error(e);
             }}
             payModal={{
               theme: theme === "light" ? "light" : "dark",
             }}
             transaction={() => {
               if (!account) throw new Error("No active account");
               return transfer({
                 amount: "50",
                 contract: usdcContract,
                 to: account?.address || "",
               });
             }}
           >
             Buy VIP Pass
           </TransactionButton>
           <div className="h-10" />
           <div className="flex items-center gap-2">Price: 0.1 ETH</div>
           <TransactionButton
             onError={(e) => {
               console.error(e);
             }}
             payModal={{
               theme: theme === "light" ? "light" : "dark",
             }}
             transaction={() => {
               if (!account) throw new Error("No active account");
               return prepareTransaction({
                 chain: baseSepolia,
                 client: THIRDWEB_CLIENT,
                 to: account.address,
                 value: toWei("0.1"),
               });
             }}
           >
             Send 0.1 ETH
           </TransactionButton>
-        </div>
-      )}
-    </>
+    </div>

71-71: Avoid hard-coded price label; derive from token metadata.

Rendering symbol dynamically prevents drift if the token changes.

<div className="flex items-center gap-2">
  Price: 50 {USDC?.symbol ?? "USDC"}
</div>

Optionally format with decimals if needed.


34-57: Add explicit return types to exported components.

Aligns with repo TS guidelines.

Outside-range example:

export function PayTransactionPreview(): JSX.Element { ... }
export function PayTransactionButtonPreview(): JSX.Element { ... }
apps/playground-web/src/app/payments/commerce/page.tsx (1)

1-5: Mark this page as a Server Component explicitly.

Per guidelines, add the server-only directive to RSC pages.

+import "server-only";
 import { CreditCardIcon } from "lucide-react";
apps/playground-web/src/app/payments/components/RightSection.tsx (2)

29-33: Add an explicit return type.

Follow the TS guideline for exported functions.

 export function RightSection(props: {
   options: BridgeComponentsPlaygroundOptions;
   tab?: string;
   lockedWidget?: "buy" | "checkout" | "transaction";
-}) {
+}): JSX.Element {

55-56: Provide a safe fallback for effectiveWidget.

Without a fallback, embed can be empty if widget is unset.

-  const effectiveWidget = props.lockedWidget || props.options.payOptions.widget;
+  const effectiveWidget =
+    props.lockedWidget || props.options.payOptions.widget || "buy";
apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx (1)

32-48: Add an explicit return type to the exported component.

-export function TransactionPlayground() {
+export function TransactionPlayground(): JSX.Element {
apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx (1)

32-48: Add an explicit return type to the exported component.

-export function CheckoutPlayground() {
+export function CheckoutPlayground(): JSX.Element {
apps/playground-web/src/app/payments/components/LeftSection.tsx (2)

34-40: Add an explicit return type to the exported component.

 export function LeftSection(props: {
   options: BridgeComponentsPlaygroundOptions;
   setOptions: React.Dispatch<
     React.SetStateAction<BridgeComponentsPlaygroundOptions>
   >;
   lockedWidget?: "buy" | "checkout" | "transaction";
-}) {
+}): JSX.Element {

257-280: Avoid tri-state pitfalls in onCheckedChange.

Radix Checkbox passes boolean | "indeterminate". Use checked === true to avoid treating "indeterminate" as truthy.

-                              paymentMethods: checked
+                              paymentMethods: checked === true
                                 ? [
                                     ...v.payOptions.paymentMethods.filter(
                                       (m) => m !== "crypto",
                                     ),
                                     "crypto",
                                   ]
                                 : v.payOptions.paymentMethods.filter(
                                     (m) => m !== "crypto",
                                   ),
-                              paymentMethods: checked
+                              paymentMethods: checked === true
                                 ? [
                                     ...v.payOptions.paymentMethods.filter(
                                       (m) => m !== "card",
                                     ),
                                     "card",
                                   ]
                                 : v.payOptions.paymentMethods.filter(
                                     (m) => m !== "card",
                                   ),
-                              paymentMethods: checked
+                              paymentMethods: checked === true
                                 ? [
                                     ...v.payOptions.paymentMethods.filter(
                                       (m) => m !== "crypto",
                                     ),
                                     "crypto",
                                   ]
                                 : v.payOptions.paymentMethods.filter(
                                     (m) => m !== "crypto",
                                   ),
-                              paymentMethods: checked
+                              paymentMethods: checked === true
                                 ? [
                                     ...v.payOptions.paymentMethods.filter(
                                       (m) => m !== "card",
                                     ),
                                     "card",
                                   ]
                                 : v.payOptions.paymentMethods.filter(
                                     (m) => m !== "card",
                                   ),

Also applies to: 282-305, 357-381, 384-407

apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx (1)

32-35: Annotate component return type.

Add an explicit return type per our TS/TSX guidelines.

-export function BuyPlayground() {
+export function BuyPlayground(): JSX.Element {
   const [options, setOptions] =
     useState<BridgeComponentsPlaygroundOptions>(defaultOptions);
apps/playground-web/src/app/payments/transactions/page.tsx (1)

51-126: Rename function to reflect content + add return type.

The helper renders a code example, not a “No funds popup.” Rename for clarity and add an explicit return type.

-export default function Page() {
+export default function Page() {
   return (
     <ThirdwebProvider>
       <PageLayout
         icon={ArrowLeftRightIcon}
         containerClassName="space-y-12"
         description={description}
         docsLink="https://portal.thirdweb.com/wallets/sponsor-gas?utm_source=playground"
         title={title}
       >
-        <div>
+        <div>
           <h2 className="text-xl font-semibold tracking-tight mb-1">
             Transaction Widget
           </h2>
           <p className="text-base text-muted-foreground mb-6">
             Render a prebuilt UI for performing transactions using any token or
             fiat. <br /> It handles the complete payment flow, supporting both
             crypto and fiat payments across 50+ chains.
           </p>
           <TransactionPlayground />
         </div>
-        <NoFundsPopup />
+        <TransactionButtonExample />
       </PageLayout>
     </ThirdwebProvider>
   );
 }
 
-function NoFundsPopup() {
+function TransactionButtonExample(): JSX.Element {
   return (
     <CodeExample
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/payments/page.tsx (1)

7-7: Mark as server component and annotate return type

Add server-only marker at top and annotate the Page return type to satisfy our TS guidelines.

Add:

// very top of file
import "server-only";

And change signature:

export default async function Page(props: {
  params: Promise<{ team_slug: string; project_slug: string }>;
}): Promise<JSX.Element> {

Also optional cleanup: you already return a default object in the getFees catch; the subsequent !fees guard (Lines 46–53) is redundant—consider removing it.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/wallets/page.tsx (1)

8-8: Add server-only marker and explicit return type

Keep pages aligned with our server component and TS return type rules.

Add:

// very top of file
import "server-only";

Update signature:

export default async function Page(props: {
  params: Promise<{ team_slug: string; project_slug: string }>;
}): Promise<JSX.Element> {
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/account-abstraction/page.tsx (1)

1-1: Mark page as server-only

Per dashboard server component rule, add the marker at the top.

Apply:

+import "server-only";
 import { CircleAlertIcon } from "lucide-react";
apps/dashboard/src/@/icons/BridgeIcon.tsx (1)

1-1: LGTM: aliasing lucide icon with the required “Icon” suffix rule

Re-export follows our icon guidelines (lucide source, “Icon” suffix). Minor nit: the file contains no JSX; consider .ts extension for faster TS transforms.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx (1)

37-41: Merge external className and drop internal key prop

  • Accept className and merge via cn().
  • key inside a leaf component has no effect; parents should set keys on list items.
-  return (
-    <TableRowComponent
-      className="fade-in-0 border-border border-b duration-300"
-      key={purchase.id}
-    >
+  return (
+    <TableRowComponent
+      className={cn("fade-in-0 border-border border-b duration-300", props.className)}
+    >
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (2)

26-37: Type the helpers and set a sane default variant

Add explicit return types per guidelines and default variant to "default" to avoid accidental undefined.

-function Action(props: { action: Action; variant?: "default" | "outline" }) {
+function Action(props: { action: Action; variant?: "default" | "outline" }): React.ReactNode {
   const action = props.action;
 
   return "component" in action ? (
     action.component
   ) : (
     <Button
       asChild
-      className={cn("rounded-full")}
+      className={cn("rounded-full")}
       size="sm"
-      variant={props.variant}
+      variant={props.variant ?? "default"}
     >

1-1: Optional: co-locate Action elsewhere

If this grows, consider moving Action into ./header/action.tsx to keep single-responsibility per file.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx (1)

31-37: Improve UX: validate-on-change and button disabled state

Enable live validation and disable submit until the form is valid and dirty.

-const form = useForm<RouteDiscoveryValidationSchema>({
+const form = useForm<RouteDiscoveryValidationSchema>({
   defaultValues: {
     chainId: 1,
     tokenAddress: undefined,
   },
-  resolver: zodResolver(routeDiscoveryValidationSchema),
+  resolver: zodResolver(routeDiscoveryValidationSchema),
+  mode: "onChange",
 });
 saveButton={{
-  disabled: !form.formState.isDirty,
+  disabled: !form.formState.isDirty || !form.formState.isValid,
   isPending: submitDiscoveryMutation.isPending,
   type: "submit",
   variant: "outline",
 }}

Also applies to: 88-95

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/QuickstartSection.client.tsx (2)

49-51: Button color overrides on outline variant

Minor: overriding text/background on an outline button can fight the DS theme. Consider dropping explicit colors unless needed.


13-21: Expose className and explicit return type on component root

Matches repo guidelines for dashboard *.tsx components.

Apply:

+import { cn } from "@/lib/utils";
@@
-export function QuickStartSection(props: {
-  teamSlug: string;
-  projectSlug: string;
-  clientId: string;
-  teamId: string;
-}) {
+export function QuickStartSection(props: {
+  teamSlug: string;
+  projectSlug: string;
+  clientId: string;
+  teamId: string;
+  className?: string;
+}): JSX.Element {
   return (
-    <section>
+    <section className={cn(props.className)}>
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/AdvancedSection.client.tsx (2)

6-12: Add className prop and explicit return type

Keeps components flexible and typed per guidelines.

Apply:

-export function AdvancedSection({
+export function AdvancedSection({
   teamSlug,
   projectSlug,
-}: {
+}: {
   teamSlug: string;
   projectSlug: string;
-}) {
+  className?: string;
+}): JSX.Element {

And wrap the root:

-  return (
-    <section>
+  return (
+    <section className={className}>

21-21: Grid columns mismatch

There are 2 cards; set lg:grid-cols-2 to avoid empty space.

-      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
+      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/PaymentLinksTable.client.tsx (4)

47-50: Header/description copy

Copy reads fine; optional period at end if you want consistency.


125-128: Button style tweak — optional

Rounded-full pill looks good; ensure it matches DS button shapes elsewhere.


294-301: Use mutate when leveraging callbacks (or await mutateAsync)

You’re passing onError/onSuccess, so mutate fits better and avoids unhandled promise warnings.

-              deleteMutation.mutateAsync(props.paymentLinkId, {
+              deleteMutation.mutate(props.paymentLinkId, {
                 onError(err) {
                   toast.error("Failed to delete payment link.", {
                     description: err instanceof Error ? err.message : undefined,
                   });
                 },
                 onSuccess: () => {

58-66: Set sensible React Query staleTime

Aligns with repo guidance (≥60s) and reduces refetch churn.

   const paymentLinksQuery = useQuery({
     queryFn: async () => {
       return getPaymentLinks({
         clientId: props.clientId,
         teamId: props.teamId,
       });
     },
     queryKey: ["payment-links", props.clientId, props.teamId],
+    staleTime: 60_000,
   });
@@
   const paymentLinkUsagesQuery = useQuery({
     queryFn: async () => {
       const paymentLinks = paymentLinksQuery.data || [];
       return await Promise.all(
         paymentLinks.map(async (paymentLink) => {
           const { data } = await getPayments({
             clientId: props.clientId,
             teamId: props.teamId,
             paymentLinkId: paymentLink.id,
           });
           return {
             paymentLink,
             usages: data,
           };
         }),
       );
     },
     queryKey: [
       "payment-link-usages",
       paymentLinksQuery.dataUpdatedAt,
       props.clientId,
       props.teamId,
     ],
+    staleTime: 60_000,
   });

Also applies to: 68-91

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/page.tsx (2)

15-25: Add explicit return type

Type the page to return a JSX element.

-export default async function Page(props: {
+export default async function Page(props: {
   params: Promise<{
     team_slug: string;
     project_slug: string;
   }>;
   searchParams: Promise<{
     from?: string | undefined | string[];
     to?: string | undefined | string[];
     interval?: string | undefined | string[];
   }>;
-}) {
+}): Promise<JSX.Element> {

87-90: Pluralize “UI Components”

Matches destination and other labels.

-              label: "UI Component",
+              label: "UI Components",
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/EmptyState.tsx (2)

11-13: Mark decorative icon as aria-hidden

Improves a11y for screen readers.

-      <div className="rounded-full p-2.5 border bg-background">
-        <XIcon className="size-5 text-muted-foreground" />
-      </div>
+      <div className="rounded-full p-2.5 border bg-background">
+        <XIcon aria-hidden="true" className="size-5 text-muted-foreground" focusable="false" />
+      </div>

22-22: Avoid key-less list rendering

Passing arrays without keys can cause React warnings. Either accept a single ReactNode or add keys while mapping.

Option A (preferred) — accept a single node and render directly:

-  buttons: React.ReactNode[];
+  buttons: React.ReactNode;
...
-      <div className="flex gap-4">{props.buttons.map((button) => button)}</div>
+      <div className="flex gap-4">{props.buttons}</div>

Option B — keep array and add stable keys:

-      <div className="flex gap-4">{props.buttons.map((button) => button)}</div>
+      <div className="flex gap-4">
+        {props.buttons.map((button, i) => (
+          <span key={i}>{button}</span>
+        ))}
+      </div>
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (1)

91-94: Fix label grammar

“UI Component” → “UI Components”.

-              label: "UI Component",
+              label: "UI Components",
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx (1)

112-120: Avoid repeated filtering for status==="completed"

Compute once and reuse.

-  const [volumeData, walletData] = await Promise.all([
+  const [volumeData, walletData] = await Promise.all([
     volumeDataPromise,
     walletDataPromise,
   ]);
+  const completedVolumeData = (volumeData || []).filter((x) => x.status === "completed");
...
-                <TotalVolumePieChart
-                  data={
-                    volumeData?.filter((x) => x.status === "completed") || []
-                  }
-                />
+                <TotalVolumePieChart data={completedVolumeData} />
...
-              <CardContainer>
-                <Payouts
-                  data={
-                    volumeData?.filter((x) => x.status === "completed") || []
-                  }
-                  dateFormat={dateFormat}
-                />
-              </CardContainer>
+              <CardContainer>
+                <Payouts data={completedVolumeData} dateFormat={dateFormat} />
+              </CardContainer>

Also applies to: 123-131

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (2)

81-86: Align column labels with row semantics.

Rows render “Paid” then “Bought”; headers read “Sent/Received”. Pick one vocabulary for both.

Apply:

-              <TableHeading> Sent </TableHeading>
-              <TableHeading> Received </TableHeading>
+              <TableHeading>Paid</TableHeading>
+              <TableHeading>Bought</TableHeading>

55-73: Optional: CSV filename context.

Consider including project slug or date in fileName for uniqueness, e.g., transaction_history_${new Date().toISOString().slice(0,10)}.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/FeatureCard.client.tsx (1)

10-18: Expose className on FeatureCard and merge with cn().

Improves composability and matches app-wide conventions.

Apply:

+import { cn } from "@/lib/utils";
@@
 type FeatureCardProps = {
   title: string;
   description: string;
   badge?: { label: string; variant?: "outline" | "success" | "default" };
   id: string;
-  icon: React.FC<{ className?: string }>;
+  icon: React.ComponentType<{ className?: string }>;
   setupTime?: number;
   features?: string[];
+  className?: string;
 } & (
@@
-export function FeatureCard(props: FeatureCardProps) {
+export function FeatureCard(props: FeatureCardProps) {
   return (
-    <Card className="p-4 flex flex-col gap-6 rounded-xl">
+    <Card className={cn("p-4 flex flex-col gap-6 rounded-xl", props.className)}>

Also applies to: 31-31

@MananTank MananTank changed the title Dashboard, Portal, Playground: Add Bridge Product [MNY-139] Dashboard, Portal, Playground: Add Bridge Product Sep 5, 2025
@linear

linear Bot commented Sep 5, 2025

Copy link
Copy Markdown

graphite-app Bot pushed a commit that referenced this pull request Sep 5, 2025
<!--

## 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 enhancing the user interface and functionality of the payments and bridge components within the application. It includes updates to various components, new features, and improvements in the overall layout.

### Detailed summary
- Removed `page.tsx` from `apps/playground-web/src/app/payments/ui-components`.
- Exported `BringToFrontIcon` as `BridgeIcon` in `BridgeIcon.tsx`.
- Added URL rewrites for `/payments/ui-components`.
- Updated navigation links in `navLinks.ts`.
- Modified `useTokensData.ts` to include prices.
- Changed `CreatePaymentWebhookButton` to a plain function.
- Adjusted styles in `code-example.tsx` for headers and paragraphs.
- Updated `paymentsFeatureCards` to remove UI Components and rename title.
- Simplified `TransactionButton` rendering in `transaction-button.tsx`.
- Added new `ProjectSettingsBreadcrumb` component for breadcrumbs in settings.
- Introduced `RouteDiscovery` component for token addition in bridge.
- Enhanced layout and structure in various settings and payments components.
- Updated `PaymentLinksTable` to improve the display of payment information.
- Introduced new `CheckoutPlayground` and `TransactionPlayground` components.
- Improved user feedback mechanisms and error handling across components.

> The following files were skipped due to too many changes: `apps/playground-web/src/app/payments/transactions/page.tsx`

> ✨ 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
  - Introduced Bridge page with analytics, route discovery, and quickstart.
  - Added CSV export in Payment History.
  - Added dedicated Buy, Checkout, and Transaction playgrounds.

- Navigation
  - Added “Bridge” to the project sidebar.
  - Webhooks now has a unified layout with tabs for “Contracts” and “Payments & Bridge”.
  - Payments page simplified with a Webhooks quick action.
  - Removed “UI Components” page; redirect /payments/ui-components → /payments; updated playground nav/cards.

- UI/UX Improvements
  - Redesigned analytics layout and Payment History table.
  - Updated FeatureCard and EmptyState visuals; removed “Swap Tokens” card.
  - Settings pages now use breadcrumbs.
  - Various copy and spacing refinements.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@graphite-app graphite-app Bot force-pushed the 09-04-dashboard_portal_playground_add_bridge_product branch from 2fd101e to 2e46ad6 Compare September 5, 2025 19:43
@vercel vercel Bot temporarily deployed to Preview – nebula September 5, 2025 19:43 Inactive
@vercel vercel Bot temporarily deployed to Preview – docs-v2 September 5, 2025 19:43 Inactive
@vercel vercel Bot temporarily deployed to Preview – wallet-ui September 5, 2025 19:43 Inactive
MananTank added a commit that referenced this pull request Sep 5, 2025
<!--

## 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 enhancing the user interface and functionality of the payments and bridge components within the application. It includes updates to various components, new features, and improvements in the overall layout.

### Detailed summary
- Removed `page.tsx` from `apps/playground-web/src/app/payments/ui-components`.
- Exported `BringToFrontIcon` as `BridgeIcon` in `BridgeIcon.tsx`.
- Added URL rewrites for `/payments/ui-components`.
- Updated navigation links in `navLinks.ts`.
- Modified `useTokensData.ts` to include prices.
- Changed `CreatePaymentWebhookButton` to a plain function.
- Adjusted styles in `code-example.tsx` for headers and paragraphs.
- Updated `paymentsFeatureCards` to remove UI Components and rename title.
- Simplified `TransactionButton` rendering in `transaction-button.tsx`.
- Added new `ProjectSettingsBreadcrumb` component for breadcrumbs in settings.
- Introduced `RouteDiscovery` component for token addition in bridge.
- Enhanced layout and structure in various settings and payments components.
- Updated `PaymentLinksTable` to improve the display of payment information.
- Introduced new `CheckoutPlayground` and `TransactionPlayground` components.
- Improved user feedback mechanisms and error handling across components.

> The following files were skipped due to too many changes: `apps/playground-web/src/app/payments/transactions/page.tsx`

> ✨ 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
  - Introduced Bridge page with analytics, route discovery, and quickstart.
  - Added CSV export in Payment History.
  - Added dedicated Buy, Checkout, and Transaction playgrounds.

- Navigation
  - Added “Bridge” to the project sidebar.
  - Webhooks now has a unified layout with tabs for “Contracts” and “Payments & Bridge”.
  - Payments page simplified with a Webhooks quick action.
  - Removed “UI Components” page; redirect /payments/ui-components → /payments; updated playground nav/cards.

- UI/UX Improvements
  - Redesigned analytics layout and Payment History table.
  - Updated FeatureCard and EmptyState visuals; removed “Swap Tokens” card.
  - Settings pages now use breadcrumbs.
  - Various copy and spacing refinements.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@MananTank MananTank force-pushed the 09-04-dashboard_portal_playground_add_bridge_product branch from 2e46ad6 to 4f1f5ec Compare September 5, 2025 19:51
@vercel vercel Bot temporarily deployed to Preview – docs-v2 September 5, 2025 19:51 Inactive
@vercel vercel Bot temporarily deployed to Preview – nebula September 5, 2025 19:51 Inactive
@vercel vercel Bot temporarily deployed to Preview – wallet-ui September 5, 2025 19:51 Inactive
MananTank added a commit that referenced this pull request Sep 5, 2025
<!--

## 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 enhancing the user interface and functionality of the payments and bridge components within the application. It includes updates to various components, new features, and improvements in the overall layout.

### Detailed summary
- Removed `page.tsx` from `apps/playground-web/src/app/payments/ui-components`.
- Exported `BringToFrontIcon` as `BridgeIcon` in `BridgeIcon.tsx`.
- Added URL rewrites for `/payments/ui-components`.
- Updated navigation links in `navLinks.ts`.
- Modified `useTokensData.ts` to include prices.
- Changed `CreatePaymentWebhookButton` to a plain function.
- Adjusted styles in `code-example.tsx` for headers and paragraphs.
- Updated `paymentsFeatureCards` to remove UI Components and rename title.
- Simplified `TransactionButton` rendering in `transaction-button.tsx`.
- Added new `ProjectSettingsBreadcrumb` component for breadcrumbs in settings.
- Introduced `RouteDiscovery` component for token addition in bridge.
- Enhanced layout and structure in various settings and payments components.
- Updated `PaymentLinksTable` to improve the display of payment information.
- Introduced new `CheckoutPlayground` and `TransactionPlayground` components.
- Improved user feedback mechanisms and error handling across components.

> The following files were skipped due to too many changes: `apps/playground-web/src/app/payments/transactions/page.tsx`

> ✨ 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
  - Introduced Bridge page with analytics, route discovery, and quickstart.
  - Added CSV export in Payment History.
  - Added dedicated Buy, Checkout, and Transaction playgrounds.

- Navigation
  - Added “Bridge” to the project sidebar.
  - Webhooks now has a unified layout with tabs for “Contracts” and “Payments & Bridge”.
  - Payments page simplified with a Webhooks quick action.
  - Removed “UI Components” page; redirect /payments/ui-components → /payments; updated playground nav/cards.

- UI/UX Improvements
  - Redesigned analytics layout and Payment History table.
  - Updated FeatureCard and EmptyState visuals; removed “Swap Tokens” card.
  - Settings pages now use breadcrumbs.
  - Various copy and spacing refinements.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--

## 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 enhancing the payment and bridge functionalities in the dashboard and playground applications. It includes UI improvements, new components, and refactoring for better organization and usability.

### Detailed summary
- Deleted `RouteDiscoveryCard.tsx`.
- Replaced `BridgeIcon` export.
- Added route rewrites in `next.config.mjs`.
- Updated navigation links in `navLinks.ts`.
- Modified `useTokensData.ts` to include price parameters.
- Refactored `CreatePaymentWebhookButton` to a function.
- Enhanced layout and styling in various components, including `ProjectSidebarLayout`, `EmptyState`, and `QuickstartSection`.
- Introduced `ProjectSettingsBreadcrumb` for better navigation.
- Added support for locked widgets in payment components.
- Improved `PaymentLinksTable` and `PaymentHistory` components with new table structures.
- Enhanced `FeatureCard` and `PayAnalytics` with updated styling and structure.
- Created `CheckoutPlayground` and `TransactionPlayground` components for better payment handling.
- Reorganized the bridge section with improved UI and functionality.

> The following files were skipped due to too many changes: `apps/playground-web/src/app/payments/transactions/page.tsx`

> ✨ 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**
  * Bridge page with analytics, route discovery, and Quickstart; CSV export for payment history; new Buy, Checkout, and Transaction playgrounds.

* **Navigation**
  * Added "Bridge" to project sidebar; Webhooks consolidated with tabs; Payments page streamlined; redirect from /payments/ui-components → /payments and removed UI Components page.

* **UI/UX Improvements**
  * Redesigned analytics and payment history table; updated FeatureCard and EmptyState visuals; removed "Swap Tokens" card; settings pages use breadcrumbs; assorted copy and spacing tweaks.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (6)
apps/playground-web/src/app/payments/components/LeftSection.tsx (2)

258-277: Handle tri-state Checkbox correctly and dedupe toggle logic.

shadcn Checkbox onCheckedChange can emit "indeterminate" which is truthy; compare with === true to avoid accidental additions. Also, the crypto/card toggles are duplicated between Buy and Checkout; extract a small helper to reduce drift.

Apply this diff for the boolean guard (repeat for both crypto/card and both modes):

-                              paymentMethods: checked
+                              paymentMethods: checked === true
                                 ? [
                                     ...v.payOptions.paymentMethods.filter(
                                       (m) => m !== "crypto",
                                     ),
                                     "crypto",
                                   ]
                                 : v.payOptions.paymentMethods.filter(
                                     (m) => m !== "crypto",
                                   ),

Optional: extract a local helper

const toggleMethod = (
  methods: ("crypto" | "card")[],
  method: "crypto" | "card",
  checked: boolean | "indeterminate",
) => (checked === true ? [...methods.filter((m) => m !== method), method] : methods.filter((m) => m !== method));

Then use:

paymentMethods: toggleMethod(v.payOptions.paymentMethods, "crypto", checked)

Also applies to: 283-302, 361-377, 388-404


441-447: Fix stale-closure risk in setOptions merges.

Inside functional setState, spreading outer-scoped payOptions can drop concurrent updates. Spread v.payOptions instead.

Apply this diff:

-                    payOptions: {
-                      ...payOptions,
+                    payOptions: {
+                      ...v.payOptions,
                       title: e.target.value,
                     },
-                    payOptions: {
-                      ...payOptions,
+                    payOptions: {
+                      ...v.payOptions,
                       image: e.target.value,
                     },
-                  payOptions: {
-                    ...payOptions,
+                  payOptions: {
+                    ...v.payOptions,
                     description: e.target.value,
                   },
-                  payOptions: {
-                    ...payOptions,
+                  payOptions: {
+                    ...v.payOptions,
                     buttonLabel: e.target.value,
                   },

Also applies to: 461-467, 481-489, 501-509

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (1)

32-35: Rename thirdweb-client.client.ts to a server module
The .client.ts suffix forces this helper to be treated as a Client Component, so importing it in apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (a Server Component) will break under Next’s RSC rules. Since getClientThirdwebClient wraps a server‐only import (./thirdweb.server), rename the file to thirdweb-client.server.ts (or remove the .client suffix) and update the import accordingly.

apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (2)

96-107: Fix invalid anchor > button nesting (accessibility/semantics).

Use Button asChild to avoid interactive-element nesting and ensure correct focus/activation behavior.

-            {props.settings && (
-              <Link href={props.settings.href}>
-                <Button
-                  variant="outline"
-                  size="sm"
-                  className="rounded-full gap-2 bg-card"
-                >
-                  <Settings2Icon className="size-4 text-muted-foreground" />
-                  Settings
-                </Button>
-              </Link>
-            )}
+            {props.settings && (
+              <Button asChild variant="outline" size="sm" className="rounded-full gap-2 bg-card">
+                <Link href={props.settings.href}>
+                  <Settings2Icon className="size-4 text-muted-foreground" />
+                  Settings
+                </Link>
+              </Button>
+            )}

1-1: Replace direct @workspace/ui import with local UI wrapper
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx:1 – import Button from "@/components/ui/button" instead of "@workspace/ui/components/button" to align with dashboard app conventions.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (1)

132-171: CSV header/order mismatch (and date format)

Header says ["Type","Bought","Paid",...] but rows start with Bought, then Paid, then Type. This misaligns exported columns. Also, the date format lacks a comma after the day (inconsistent with the table).

-  const header = ["Type", "Bought", "Paid", "Status", "Recipient", "Date"];
+  const header = ["Type", "Bought", "Paid", "Status", "Sender", "Date"];
...
-    return [
-      // bought
-      `${formatTokenAmount(toAmount)} ${purchase.destinationToken.symbol}`,
-      // paid
-      `${formatTokenAmount(fromAmount)} ${purchase.originToken.symbol}`,
-      // type
-      type,
-      // status
-      purchase.status,
-      // sender
-      purchase.sender,
-      // date
-      format(new Date(purchase.createdAt), "LLL dd y h:mm a"),
-    ];
+    return [
+      // type
+      type,
+      // bought
+      `${formatTokenAmount(toAmount)} ${purchase.destinationToken.symbol}`,
+      // paid
+      `${formatTokenAmount(fromAmount)} ${purchase.originToken.symbol}`,
+      // status
+      purchase.status,
+      // sender
+      purchase.sender,
+      // date
+      format(new Date(purchase.createdAt), "LLL dd, y h:mm a"),
+    ];
♻️ Duplicate comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (1)

1-1: Use local UI primitive and add server-only pragma

Pages under dashboard should import UI from "@/components/ui/*" and start with import "server-only"; to keep this route a Server Component and avoid pulling client bundles.

+import "server-only";
-import { Button } from "@workspace/ui/components/button";
+import { Button } from "@/components/ui/button";
🧹 Nitpick comments (21)
apps/playground-web/src/app/payments/components/LeftSection.tsx (5)

34-41: Add an explicit return type to the component.

Coding guideline requires explicit return types for TSX. Add React.ReactElement to the signature.

Apply this diff:

-export function LeftSection(props: {
+export function LeftSection(props: {
   options: BridgeComponentsPlaygroundOptions;
   setOptions: React.Dispatch<
     React.SetStateAction<BridgeComponentsPlaygroundOptions>
   >;
   lockedWidget?: "buy" | "checkout" | "transaction";
-}) {
+}): React.ReactElement {

43-43: Type-safe and nullish-coalesced effectiveWidget.

Prefer ?? over || and annotate to the exact union type for clarity.

Apply this diff:

-  const effectiveWidget = props.lockedWidget || payOptions.widget || "buy";
+  const effectiveWidget =
+    (props.lockedWidget ?? payOptions.widget ?? "buy") as NonNullable<
+      BridgeComponentsPlaygroundOptions["payOptions"]["widget"]
+    >;

122-147: Widget selector gating is correct; use effectiveWidget for value.

Minor consistency: bind the radio group’s value to effectiveWidget.

Apply this diff:

-                value={payOptions.widget || "buy"}
+                value={effectiveWidget}

151-166: Narrow Select value type for currency.

onValueChange provides string; BridgeComponentsPlaygroundOptions expects a SupportedFiatCurrency union. Add a precise cast to avoid type widening.

Apply this diff:

   <Select
     value={payOptions.currency || "USD"}
     onValueChange={(value) => {
       setOptions((v) => ({
         ...v,
         payOptions: {
           ...v.payOptions,
-          currency: value,
+          currency:
+            value as BridgeComponentsPlaygroundOptions["payOptions"]["currency"],
         },
       }));
     }}
   >

418-423: User-facing text: article and hyphenation.

“a ERC1155” → “an ERC-1155” for correctness.

Apply this diff:

-                    This demo uses a ERC1155 NFT claim transaction. Check the
+                    This demo uses an ERC-1155 NFT claim transaction. Check the
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (4)

24-26: Ensure loginRedirect short-circuits (return/never) to satisfy narrowing

If loginRedirect does not return never, the type of authToken may remain string | null, risking misuse below and potential runtime issues. Either make loginRedirect return never (internally calls redirect()) or explicitly return here.

-  if (!authToken) {
-    loginRedirect(`/team/${params.team_slug}/${params.project_slug}/payments`);
-  }
+  if (!authToken) {
+    return loginRedirect(
+      `/team/${params.team_slug}/${params.project_slug}/payments`,
+    );
+  }

91-94: Nit: pluralize label to match destination

Destination is “ui-components”; label should be plural for consistency.

-              label: "UI Component",
+              label: "UI Components",

15-20: Add explicit return type for Page

Keep to repo TS conventions by annotating the return type.

-export default async function Page(props: {
+export default async function Page(props: {
   params: Promise<{
     team_slug: string;
     project_slug: string;
   }>;
-}) {
+}): Promise<JSX.Element> {

141-157: Optional: add Suspense fallbacks for client sections

These client components fetch/render data; a lightweight fallback improves perceived performance.

+import { Suspense } from "react";
@@
-      <div className="flex flex-col gap-12">
-        <PaymentLinksTable
+      <div className="flex flex-col gap-12">
+        <Suspense fallback={<div className="text-sm text-muted-foreground">Loading payment links…</div>}>
+          <PaymentLinksTable
           clientId={project.publishableKey}
           teamId={project.teamId}
-        />
-        <QuickStartSection
+          />
+        </Suspense>
+        <Suspense fallback={<div className="text-sm text-muted-foreground">Loading quick start…</div>}>
+          <QuickStartSection
           projectSlug={params.project_slug}
           teamSlug={params.team_slug}
           clientId={project.publishableKey}
           teamId={project.teamId}
-        />
+          />
+        </Suspense>
@@
-        <AdvancedSection
+        <Suspense fallback={<div className="text-sm text-muted-foreground">Loading advanced features…</div>}>
+          <AdvancedSection
           teamSlug={params.team_slug}
           projectSlug={params.project_slug}
-        />
+          />
+        </Suspense>
       </div>
apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (3)

26-37: Add explicit return type to Action.

Keeps types tight and matches guidelines for explicit return types.

-function Action(props: { action: Action; variant?: "default" | "outline" }) {
+function Action(
+  props: { action: Action; variant?: "default" | "outline" },
+): ReactNode {

Add supporting type import at top:

import type { ReactNode } from "react";

52-71: Expose className on ProjectPageHeader and merge with cn().

Improves composability and aligns with app component conventions.

 export type ProjectPageHeaderProps = {
   client: ThirdwebClient;
   title: string;
   description?: React.ReactNode;
   imageUrl?: string | null;
   icon: React.FC<{ className?: string }>;
   isProjectIcon?: boolean;
   actions: {
     primary?: Action;
     secondary?: Action;
   } | null;

   links?: ActionLink[];
   settings?: {
     href: string;
   };

   // TODO: add task card component
   task?: never;
+  className?: string;
 };
 
-export function ProjectPageHeader(props: ProjectPageHeaderProps) {
+export function ProjectPageHeader(props: ProjectPageHeaderProps) {
   return (
-    <header className="container max-w-7xl py-6 relative">
+    <header className={cn("container max-w-7xl py-6 relative", props.className)}>

Also applies to: 73-76


132-134: Confirm removal of line clamp is intentional; consider responsive clamp.

Long descriptions may overflow on small screens. Optionally clamp on small breakpoints.

-          <p className="text-sm text-muted-foreground text-pretty">
+          <p className="text-sm text-muted-foreground text-pretty sm:line-clamp-3">
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx (3)

37-40: Remove redundant key on inner row

The list key is already applied at the callsite when mapping . The key on the inner is unnecessary.

Apply:

-    <TableRowComponent
-      className="fade-in-0 border-border border-b duration-300"
-      key={purchase.id}
-    >
+    <TableRowComponent className="fade-in-0 border-border border-b duration-300">

6-9: Mark as client component

This component is rendered from a client component and uses interactive child components. Add 'use client' for clarity and to satisfy repo guidelines.

+ 'use client';
  import { format } from "date-fns";

86-89: Timezone consistency with charts

Charts format dates in UTC while the table uses local time. Consider standardizing (UTC or project tz) to avoid confusion across views.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx (3)

111-116: Filter once, reuse

You filter to completed three times. Compute once to reduce duplication.

-              <TotalVolumePieChart
-                data={
-                  volumeData?.filter((x) => x.status === "completed") || []
-                }
-              />
+              {/*
+                Compute once above:
+                const completedVolume = volumeData?.filter((x) => x.status === "completed") || [];
+              */}
+              <TotalVolumePieChart data={completedVolume} />
...
-              <TotalPayVolume
-                data={volumeData?.filter((x) => x.status === "completed") || []}
-                dateFormat={dateFormat}
-              />
+              <TotalPayVolume data={completedVolume} dateFormat={dateFormat} />
...
-                <Payouts
-                  data={
-                    volumeData?.filter((x) => x.status === "completed") || []
-                  }
-                  dateFormat={dateFormat}
-                />
+                <Payouts data={completedVolume} dateFormat={dateFormat} />

Also applies to: 118-121, 125-134


92-106: Fallback skeleton still includes history block

PaymentHistory moved below and outside this suspense but the fallback retains a large (500px) skeleton likely intended for history. Trim it to only cover the analytics grid.

-              <div className="grid grid-cols-1 gap-6 xl:grid-cols-2">
-                <Skeleton className="h-[350px] border rounded-xl" />
-                <Skeleton className="h-[350px] border rounded-xl" />
-              </div>
-              <Skeleton className="h-[500px] border rounded-xl" />
+              <div className="grid grid-cols-1 gap-6 xl:grid-cols-2">
+                <Skeleton className="h-[350px] border rounded-xl" />
+                <Skeleton className="h-[350px] border rounded-xl" />
+              </div>

79-81: Expose className on root

Repo guideline: components should accept className on the root. Add an optional prop and merge with cn().

-export async function PayAnalytics(props: {
+export async function PayAnalytics(props: {
   projectClientId: string;
   client: ThirdwebClient;
   projectId: string;
   teamId: string;
   range: Range;
   interval: "day" | "week";
   authToken: string;
+  className?: string;
 }) {
...
-  return (
-    <div>
+  return (
+    <div className={props.className}>
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (3)

46-48: Include teamId in the query key

Avoid cross-team cache bleed by keying queries on teamId as well.

-    queryKey: ["payments", props.projectClientId, page],
+    queryKey: ["payments", props.teamId, props.projectClientId, page],

58-63: Add className passthrough on root

To match repo convention, allow callers to style the wrapper.

-export function PaymentHistory(props: {
+export function PaymentHistory(props: {
   client: ThirdwebClient;
   projectClientId: string;
   teamId: string;
+  className?: string;
 }) {
...
-  return (
-    <div className="w-full">
+  return (
+    <div className={cn("w-full", props.className)}>

Note: add cn import from "@/lib/utils".


77-123: Minor: consistent empty/loading UX

You already gate the body with isEmpty/isLoading. Consider also disabling the Export button while loading to avoid exporting partial data.

📜 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 2fd101e and 4ca407d.

📒 Files selected for processing (38)
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (5 hunks)
  • apps/dashboard/src/@/icons/BridgeIcon.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/QuickstartSection.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectSidebarLayout.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/AdvancedSection.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/EmptyState.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/FeatureCard.client.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/QuickstartSection.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/PaymentLinksTable.client.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/webhooks/components/webhooks.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/_components/project-settings-breadcrumb.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/account-abstraction/page.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/payments/page.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/wallets/page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/contracts/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx (2 hunks)
  • apps/playground-web/next.config.mjs (1 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (1 hunks)
  • apps/playground-web/src/app/navLinks.ts (0 hunks)
  • apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx (1 hunks)
  • apps/playground-web/src/app/payments/commerce/page.tsx (2 hunks)
  • apps/playground-web/src/app/payments/components/LeftSection.tsx (9 hunks)
  • apps/playground-web/src/app/payments/components/RightSection.tsx (4 hunks)
  • apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx (2 hunks)
  • apps/playground-web/src/app/payments/fund-wallet/page.tsx (2 hunks)
  • apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx (1 hunks)
  • apps/playground-web/src/app/payments/transactions/page.tsx (2 hunks)
  • apps/playground-web/src/app/payments/ui-components/page.tsx (0 hunks)
  • apps/playground-web/src/components/code/code-example.tsx (1 hunks)
  • apps/playground-web/src/components/pay/transaction-button.tsx (1 hunks)
  • apps/playground-web/src/hooks/useTokensData.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/playground-web/src/app/payments/ui-components/page.tsx
  • apps/playground-web/src/app/navLinks.ts
🚧 Files skipped from review as they are similar to previous changes (30)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/QuickstartSection.client.tsx
  • apps/dashboard/src/@/icons/BridgeIcon.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/components/code/code-example.tsx
  • apps/playground-web/src/app/payments/components/RightSection.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/EmptyState.tsx
  • apps/playground-web/src/app/payments/transactions/TransactionPlayground.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/_components/project-settings-breadcrumb.tsx
  • apps/playground-web/src/hooks/useTokensData.ts
  • 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)/payments/webhooks/components/webhooks.client.tsx
  • apps/playground-web/src/app/payments/commerce/CheckoutPlayground.tsx
  • apps/playground-web/next.config.mjs
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/QuickstartSection.client.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/PaymentLinksTable.client.tsx
  • apps/playground-web/src/components/pay/transaction-button.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/AdvancedSection.client.tsx
  • apps/playground-web/src/app/payments/fund-wallet/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/payments/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/wallets/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/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)/settings/account-abstraction/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx
  • apps/playground-web/src/app/payments/transactions/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/FeatureCard.client.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/layout.tsx
  • apps/playground-web/src/app/payments/commerce/page.tsx
  • apps/playground-web/src/app/payments/fund-wallet/BuyPlayground.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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

**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from @/types where applicable
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size

Files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.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)/payments/components/PaymentsTableRow.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.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)/payments/components/PaymentsTableRow.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/playground-web/src/app/payments/components/LeftSection.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from @/components/ui/_ (e.g., Button, Input, Tabs, Card)
Use NavLink for internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names with cn() from @/lib/utils for conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start with import "server-only"; use next/headers, server‑only env, heavy data fetching, and redirect() where appropriate
Client Components must start with 'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: call getAuthToken() from cookies, send Authorization: Bearer <token> header, and return typed results (avoid any)
Client-side data fetching: wrap calls in React Query with descriptive, stable queryKeys and set sensible staleTime/cacheTime (≥ 60s default); keep tokens secret via internal routes or server actions
Do not import posthog-js in server components (client-side only)

Files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx
apps/{dashboard,playground}/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Expose a className prop on the root element of every component

Files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
  • apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx
🧠 Learnings (17)
📓 Common learnings
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.
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.
📚 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)/payments/components/PaymentsTableRow.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.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)/payments/components/PaymentHistory.client.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)/payments/components/PaymentsTableRow.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.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)/payments/components/PaymentHistory.client.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} : Server Components (Node edge): Start files with `import "server-only";`

Applied to files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.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} : 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)/payments/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Server Components must start with `import "server-only"`; use `next/headers`, server‑only env, heavy data fetching, and `redirect()` where appropriate

Applied to files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/_` (e.g., Button, Input, Tabs, Card)

Applied to files:

  • 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)/payments/components/PaymentHistory.client.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/**/api/**/*.{ts,tsx} : Prefix files with `import "server-only";` so they never end up in the client bundle.

Applied to files:

  • 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} : Reuse core UI primitives; avoid re-implementing buttons, cards, modals.

Applied to files:

  • 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} : 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)/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} : Always import from the central UI library under `@/components/ui/*` – e.g. `import { Button } from "@/components/ui/button"`.

Applied to files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.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} : Client Components (browser): Begin files with `'use client';`

Applied to files:

  • 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/**/api/**/*.{ts,tsx} : Co-locate data helpers under `@/api/**` and mark them with "server-only".

Applied to files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.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)/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} : 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)/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 : 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)/payments/page.tsx
📚 Learning: 2025-05-21T05:17:31.283Z
Learnt from: jnsdls
PR: thirdweb-dev/js#6929
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/insight/webhooks/page.tsx:14-19
Timestamp: 2025-05-21T05:17:31.283Z
Learning: In Next.js server components, the `params` object can sometimes be a Promise that needs to be awaited, despite type annotations suggesting otherwise. In apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/insight/webhooks/page.tsx, it's necessary to await the params object before accessing its properties.

Applied to files:

  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
🧬 Code graph analysis (5)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/format.ts (1)
  • formatTokenAmount (1-9)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx (8)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalyticsFilter.tsx (1)
  • PayAnalyticsFilter (29-126)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/TotalVolumePieChart.tsx (1)
  • TotalVolumePieChart (15-129)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/TotalPayVolume.tsx (1)
  • TotalPayVolume (19-159)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/Payouts.tsx (1)
  • Payouts (14-136)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsSuccessRate.tsx (1)
  • PaymentsSuccessRate (18-109)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayNewCustomers.tsx (1)
  • PayNewCustomers (13-138)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayCustomersTable.tsx (1)
  • PayCustomersTable (22-81)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (1)
  • PaymentHistory (27-126)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (3)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/links/components/PaymentLinksTable.client.tsx (1)
  • PaymentLinksTable (43-55)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/QuickstartSection.client.tsx (1)
  • QuickStartSection (13-93)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/AdvancedSection.client.tsx (1)
  • AdvancedSection (6-49)
apps/playground-web/src/app/payments/components/LeftSection.tsx (1)
apps/playground-web/src/app/payments/components/types.ts (1)
  • BridgeComponentsPlaygroundOptions (33-62)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (3)
apps/portal/src/components/Document/Table.tsx (1)
  • Table (3-13)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/common.tsx (2)
  • TableHeadingRow (17-19)
  • TableHeading (21-27)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx (1)
  • TableRow (13-93)
⏰ 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). (2)
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (13)
apps/playground-web/src/app/payments/components/LeftSection.tsx (4)

32-32: Import path update looks good.

Using a local types barrel for BridgeComponentsPlaygroundOptions improves cohesion with the co-located playground components.


195-227: Shared chain/token section looks good.

Conditional rendering on effectiveWidget is correct, and resetting token on chain change is handled.


313-349: Reuse of buyTokenAmount for Checkout “Price”.

If intentional, fine. If a distinct field is desired for checkout price vs buy amount, consider separating for clarity.

Would you like me to split this into payOptions.checkoutAmount to avoid cross-mode state coupling?


545-561: Branding checkbox uses the safe boolean guard—nice.

Pattern checked === true avoids tri-state surprises.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (3)

2-2: Icon imports are correct with Icon-suffix

PlusIcon and WebhookIcon follow the repo’s lint rule for lucide-react icon names. LGTM.


63-67: Webhooks secondary action: path looks correct

Linking to /webhooks/payments matches the established route in this layout. Good addition for discoverability.


72-85: No updates needed for external docs/playground URLs All verified links remain under /payments; no slug changes required.

apps/dashboard/src/@/components/blocks/project-page/project-page-header.tsx (3)

60-62: Primary action optionality — LGTM.

Rendering guards handle absence correctly across desktop/mobile.


114-115: Use outline for secondary actions — consistent and good.

Matches the updated design tokens and button variants.


145-146: Mobile secondary as outline — LGTM.

Consistent with desktop variant and design guidance.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsx (1)

6-9: UI table migration looks good

Swapping to the shared Table components and aliasing TableRow avoids naming conflicts and aligns with the design system.

Also applies to: 37-91

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsx (1)

79-91: Header-first layout is a UX improvement

Placing the Analytics header above filters reads better and matches other sections.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx (1)

15-20: Migration to shared Table UI looks consistent

TableContainer/Table/TableHeader/TableBody adoption is consistent with the design system. Skeleton rows also fit well.

Also applies to: 77-109

Comment on lines +81 to +87
<TableHeading> Sent </TableHeading>
<TableHeading> Received </TableHeading>
<TableHeading>Type</TableHeading>
<TableHeading>Status</TableHeading>
<TableHeading>Recipient</TableHeading>
<TableHeading>Date</TableHeading>
</TableHeadingRow>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Column label is “Recipient” but you render the sender address

UI shows sender (purchase.sender). Rename column to “Sender” to avoid misleading users, or switch to the recipient field if available.

-              <TableHeading>Recipient</TableHeading>
+              <TableHeading>Sender</TableHeading>

If you intended the recipient, update PaymentsTableRow to use that field instead. Happy to provide a follow-up diff.

📝 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
<TableHeading> Sent </TableHeading>
<TableHeading> Received </TableHeading>
<TableHeading>Type</TableHeading>
<TableHeading>Status</TableHeading>
<TableHeading>Recipient</TableHeading>
<TableHeading>Date</TableHeading>
</TableHeadingRow>
<TableHeadingRow>
<TableHeading> Sent </TableHeading>
<TableHeading> Received </TableHeading>
<TableHeading>Type</TableHeading>
<TableHeading>Status</TableHeading>
<TableHeading>Sender</TableHeading>
- <TableHeading>Recipient</TableHeading>
<TableHeading>Date</TableHeading>
</TableHeadingRow>
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentHistory.client.tsx
around lines 81-87, the table column label reads "Recipient" but the row renders
the sender address; either rename this TableHeading to "Sender" to match the
rendered value, or update the PaymentsTableRow to render the actual recipient
field (e.g., purchase.recipient) instead of purchase.sender; make the
corresponding change in the TableHeading or the row rendering so label and value
match and ensure the used field exists on the data type.

Comment on lines +45 to 47
Payments allows developers accept crypto payments for goods and
services
</>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix user-facing grammar in header description

Add “to” and end with a period.

-            Payments allows developers accept crypto payments for goods and
-            services
+            Payments allows developers to accept crypto payments for goods and
+            services.
📝 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
Payments allows developers accept crypto payments for goods and
services
</>
Payments allows developers to accept crypto payments for goods and
services.
</>
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx
around lines 45 to 47, the header description currently reads "Payments allows
developers accept crypto payments for goods and services"; update the text to
"Payments allows developers to accept crypto payments for goods and services."
by inserting the missing "to" after "developers" and adding a period at the end
to fix the grammar and punctuation.

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. Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants