[MNY-139] Dashboard, Portal, Playground: Add Bridge Product#7984
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Out-of-scope changes
Possibly related PRs
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing Touches
🧪 Generate unit tests
Comment |
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7984 +/- ##
=======================================
Coverage 56.63% 56.63%
=======================================
Files 904 904
Lines 58677 58677
Branches 4161 4161
=======================================
Hits 33231 33231
Misses 25340 25340
Partials 106 106
🚀 New features to boost your workflow:
|
size-limit report 📦
|
8226c74 to
4b84bb3
Compare
29be72e to
17d40af
Compare
4b84bb3 to
965d8c7
Compare
965d8c7 to
f1534fe
Compare
bc6470b to
2fd101e
Compare
Merge activity
|
There was a problem hiding this comment.
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
tocan 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 componentAlign with repo guidelines: explicit return types and a
classNameprop 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
classNameimproves 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 exportAlso 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 pathDashboard 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 typesAdd
server-onlyand 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
teamIdin the key to avoid cross-team cache bleed; setstaleTime ≥ 60sper 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,
isEmptyignores 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
includePricesa 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?: stringprop 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→/paymentsredirect 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
!accountearly 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 === trueto 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 ( <CodeExampleapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/settings/payments/page.tsx (1)
7-7: Mark as server component and annotate return typeAdd 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 typeKeep 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-onlyPer 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 ruleRe-export follows our icon guidelines (lucide source, “Icon” suffix). Minor nit: the file contains no JSX; consider
.tsextension 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
classNameand merge viacn().keyinside 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 variantAdd explicit return types per guidelines and default
variantto"default"to avoid accidentalundefined.-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-locateActionelsewhereIf this grows, consider moving
Actioninto./header/action.tsxto 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 stateEnable 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 variantMinor: 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 rootMatches 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 typeKeeps 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 mismatchThere 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 copyCopy reads fine; optional period at end if you want consistency.
125-128: Button style tweak — optionalRounded-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 staleTimeAligns 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 typeType 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-hiddenImproves 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 renderingPassing 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
fileNamefor 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
<!--
## 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 -->
2fd101e to
2e46ad6
Compare
<!--
## 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 -->
2e46ad6 to
4f1f5ec
Compare
<!--
## 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 -->
There was a problem hiding this comment.
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: Renamethirdweb-client.client.tsto a server module
The.client.tssuffix forces this helper to be treated as a Client Component, so importing it inapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx(a Server Component) will break under Next’s RSC rules. SincegetClientThirdwebClientwraps a server‐only import (./thirdweb.server), rename the file tothirdweb-client.server.ts(or remove the.clientsuffix) 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 pragmaPages 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 theapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx (4)
24-26: EnsureloginRedirectshort-circuits (return/never) to satisfy narrowingIf
loginRedirectdoes not returnnever, the type ofauthTokenmay remainstring | null, risking misuse below and potential runtime issues. Either makeloginRedirectreturnnever(internally callsredirect()) or explicitlyreturnhere.- 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 destinationDestination is “ui-components”; label should be plural for consistency.
- label: "UI Component", + label: "UI Components",
15-20: Add explicit return type for PageKeep 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 sectionsThese 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 rowThe 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 componentThis 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 chartsCharts 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, reuseYou 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 blockPaymentHistory 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 rootRepo 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 keyAvoid 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 rootTo 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
cnimport from "@/lib/utils".
77-123: Minor: consistent empty/loading UXYou 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.
📒 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@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{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@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless 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.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/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.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/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
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/playground-web/src/app/payments/components/LeftSection.tsxapps/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)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/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
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PaymentsTableRow.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/dashboard/src/@/components/blocks/project-page/project-page-header.tsxapps/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.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/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.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/PayAnalytics.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsxapps/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.tsxapps/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.tsxapps/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
PlusIconandWebhookIconfollow the repo’s lint rule for lucide-react icon names. LGTM.
63-67: Webhooks secondary action: path looks correctLinking to
/webhooks/paymentsmatches 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 goodSwapping 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 improvementPlacing 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 consistentTableContainer/Table/TableHeader/TableBody adoption is consistent with the design system. Skeleton rows also fit well.
Also applies to: 77-109
| <TableHeading> Sent </TableHeading> | ||
| <TableHeading> Received </TableHeading> | ||
| <TableHeading>Type</TableHeading> | ||
| <TableHeading>Status</TableHeading> | ||
| <TableHeading>Recipient</TableHeading> | ||
| <TableHeading>Date</TableHeading> | ||
| </TableHeadingRow> |
There was a problem hiding this comment.
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.
| <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.
| Payments allows developers accept crypto payments for goods and | ||
| services | ||
| </> |
There was a problem hiding this comment.
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.
| 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.

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
RouteDiscoveryCard.tsx.BridgeIconfromlucide-react.next.config.mjs.navLinks.ts.useTokensData.ts.CreatePaymentWebhookButtonto a regular function.code-example.tsx.transaction-button.tsxfor better account handling.ProjectSettingsBreadcrumbfor better navigation.QuickStartSectionandRouteDiscoverycomponents.PaymentHistoryto use new table components.FeatureCardfor improved layout and styling.Summary by CodeRabbit
New Features
Navigation
UI/UX Improvements