-
Notifications
You must be signed in to change notification settings - Fork 620
Dashboard: Fix No Funds PayEmbed in TransactionButton #7856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dashboard: Fix No Funds PayEmbed in TransactionButton #7856
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
WalkthroughReplaced PayEmbed with CheckoutWidget in MismatchButton’s Not Enough Funds flow (render gated by account), removed DynamicHeight wrapper/import and widened dialog styling; DynamicHeight's default height transition shortened from 250ms to 220ms. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant MismatchButton
participant Account
participant CheckoutWidget
participant Chain
User->>MismatchButton: Trigger Not Enough Funds flow
MismatchButton->>Account: Check account presence
alt Account exists
MismatchButton->>CheckoutWidget: Render with {name, seller, amount, chain, theme}
User->>CheckoutWidget: Complete purchase
CheckoutWidget->>Chain: Process payment
else No account
MismatchButton-->>User: Do not render CheckoutWidget
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
f7acb72 to
bd5a9c5
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7856 +/- ##
=======================================
Coverage 56.34% 56.34%
=======================================
Files 905 905
Lines 58834 58834
Branches 4150 4150
=======================================
Hits 33151 33151
Misses 25577 25577
Partials 106 106
🚀 New features to boost your workflow:
|
size-limit report 📦
|
Merge activity
|
<!--
## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"
If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):
## Notes for the reviewer
Anything important to call out? Be sure to also clarify these in your comments.
## How to test
Unit tests, playground, etc.
-->
<!-- start pr-codex -->
---
## PR-Codex overview
This PR focuses on updating the `MismatchButton` component by replacing the `PayEmbed` with `CheckoutWidget`, adjusting the transition duration in `DynamicHeight`, and streamlining the dialog content structure.
### Detailed summary
- Changed transition duration in `DynamicHeight` from `250ms` to `220ms`.
- Replaced `PayEmbed` with `CheckoutWidget` in the `MismatchButton` component.
- Simplified the dialog content structure by removing the `DynamicHeight` wrapper.
- Adjusted class names for consistency and clarity.
> ✨ 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
- “Not enough funds” flow now uses a new checkout widget and only appears when your account is connected.
- Style
- Payment dialog is wider and uses full available width for improved readability.
- Spacing and layout refined for a cleaner experience.
- Slightly faster height transition animations for snappier UI feedback.
- Refactor
- Simplified dialog content by removing an extra height wrapper.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
bd5a9c5 to
98eefdf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/dashboard/src/@/components/tx-button/MismatchButton.tsx (3)
21-28: Lazy-load CheckoutWidget to keep the dashboard bundle lean
CheckoutWidgetis likely heavy. Since it only renders in the “pay” dialog, prefer a dynamic import to avoid shipping it in the initial bundle.Apply this diff to remove the static import:
-import { - CheckoutWidget, - useActiveAccount, +import { + useActiveAccount, useActiveWallet, useActiveWalletChain, useActiveWalletConnectionStatus, useSwitchActiveWalletChain, useWalletBalance, } from "thirdweb/react";Then add the dynamic import (outside the selected range, near other imports):
import dynamic from "next/dynamic"; // Split out the widget and ensure it never SSRs in the dialog const CheckoutWidget = dynamic( () => import("thirdweb/react").then((m) => m.CheckoutWidget), { ssr: false }, );
253-255: Make dialog width conditional per mode (pay vs. no-funds)Right now
md:!max-w-[480px]applies to both modes. If the intent is to widen only the “pay” view, conditionally set widths to maintain prior sizing for “no-funds”.- className={cn( - "gap-0 p-0 md:!max-w-[480px]", - dialog === "pay" && "border-none bg-transparent", - )} + className={cn( + "gap-0 p-0", + dialog === "pay" + ? "md:!max-w-[480px] border-none bg-transparent" + : "md:!max-w-[360px]", + )}
258-268: Removal of DynamicHeight: verify UX for height changes between viewsDropping the height animator may introduce a perceptible jump when switching from “no-funds” to “pay”. If this is intended, ignore. If not, consider a lightweight height transition on the container or per-section fade to smooth the swap.
📜 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 settings in your CodeRabbit configuration.
📒 Files selected for processing (2)
apps/dashboard/src/@/components/tx-button/MismatchButton.tsx(3 hunks)apps/dashboard/src/@/components/ui/DynamicHeight.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/dashboard/src/@/components/ui/DynamicHeight.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/dashboard/src/@/components/tx-button/MismatchButton.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/@/components/tx-button/MismatchButton.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/@/components/tx-button/MismatchButton.tsx
🧠 Learnings (1)
📚 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/@/components/tx-button/MismatchButton.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Size
- GitHub Check: Unit Tests
- GitHub Check: Build Packages
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/dashboard/src/@/components/tx-button/MismatchButton.tsx (1)
359-360: LGTM: Clear CTA to open the pay flowButton wiring to
openPayModalis straightforward and consistent with the surrounding dialog logic.
| {dialog === "pay" && account && ( | ||
| <CheckoutWidget | ||
| className="!w-full" | ||
| client={props.client} | ||
| chain={txChain} | ||
| amount="0.01" | ||
| name="Buy Funds" | ||
| seller={account.address as `0x${string}`} | ||
| theme={getSDKTheme(theme === "dark" ? "dark" : "light")} | ||
| /> | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
CheckoutWidget usage: verify prop semantics; add resilience and reset behavior
- Verify that
seller={account.address}is the correct prop to direct funds to the connected wallet for “Buy Funds” use-cases. If the prop represents a merchant/recipient (not the buyer), confirm it aligns with your intended flow. - Consider forcing a remount when the chain or address changes to avoid stale internal state.
- If the widget exposes success/close callbacks, wire them to
setDialog(undefined)so the dialog closes on completion/cancel.
Apply this small resilience tweak (safe regardless of API details):
- <CheckoutWidget
+ <CheckoutWidget
+ key={`${txChain.id}-${account.address}`}
className="!w-full"
client={props.client}
chain={txChain}
amount="0.01"
name="Buy Funds"
seller={account.address as `0x${string}`}
theme={getSDKTheme(theme === "dark" ? "dark" : "light")}
/>To confirm the prop semantics, please run this web query:
🌐 Web query:
What are the props and semantics of thirdweb’s React CheckoutWidget (particularly the meaning of the `seller` prop and expected format of `amount`)? Provide the relevant docs link.
💡 Result:
Short answer
- seller — the recipient wallet (an Address) that will receive the payment. Pass the seller’s wallet address (e.g. "0x1234...abcd"). (portal.thirdweb.com)
- amount — the purchase amount as a human‑readable string (e.g. "99" or "0.0002"). The CheckoutWidget TypeScript prop is typed as string, so prefer passing a string (examples in the docs sometimes show numeric literals, but the typed prop is string). The widget will interpret the amount relative to the other props you pass (currency / tokenAddress / supportedTokens). (portal.thirdweb.com)
Key props / semantics (most useful ones)
- client: ThirdwebClient (required). (portal.thirdweb.com)
- chain: Chain to use (required/commonly passed). (portal.thirdweb.com)
- amount: string — human‑readable amount (fiat or token units). Prefer "99", "0.01", etc. Not the wei/base‑units. (portal.thirdweb.com)
- seller: Address — recipient wallet address for payouts. (portal.thirdweb.com)
- currency: SupportedFiatCurrency — if you want to fix a fiat currency. (portal.thirdweb.com)
- tokenAddress / supportedTokens: set if you want to charge in a specific token (ERC20). (portal.thirdweb.com)
- purchaseData: arbitrary metadata you can attach to the purchase (productId, orderId, etc.). (portal.thirdweb.com)
- onSuccess / onError / onCancel callbacks. (portal.thirdweb.com)
- feePayer: "user" | "seller" — who pays gas/fees. (portal.thirdweb.com)
Recommended usage (TypeScript-friendly)
- Pass amount as a string to match the prop type and avoid TS errors.
- Pass seller as an address string (checksummed address string).
Example:
<CheckoutWidget
client={client}
chain={arbitrum}
amount="99" // e.g. $99 (or "0.01" ETH depending on configuration)
seller="0x1234...abcDEF" // wallet that should receive payouts
name="Premium Course"
description="Complete guide to web3 development"
purchaseData={{ productId: "premium-course" }}
onSuccess={() => { /* ... */ }}
/>
Relevant docs
- CheckoutWidget API / props (TypeScript reference). (portal.thirdweb.com)
- "Sell a Product" guide (CheckoutWidget example showing amount="99" and seller comment). (portal.thirdweb.com)
- Payments changelog / example (shows another example with amount numeric). (portal.thirdweb.com)
If you want, I can:
- paste the full prop type list from the API reference, or
- show example permutations (fiat vs. token checkout) and how amount is interpreted in each case. Which would you prefer?
Citations:
- 1: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 2: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 3: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 4: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 5: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 6: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 7: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 8: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 9: https://portal.thirdweb.com/payments/products?utm_source=chatgpt.com
- 10: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 11: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 12: https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget?utm_source=chatgpt.com
- 13: https://portal.thirdweb.com/payments/products?utm_source=chatgpt.com
- 14: https://portal.thirdweb.com/changelog/payment-widgets-buy-checkout-and-transact?utm_source=chatgpt.com
Confirm CheckoutWidget props; implement remount key and close callbacks
seller={account.address}is correct: this prop directs payouts to the provided wallet.amount="0.01"is the expected human-readable string format.- To avoid stale internal state, add a
keyprop that changes whentxChainoraccount.addresschanges. - Wire the widget’s completion/cancel callbacks to
setDialog(undefined)so the modal closes on success, error, or cancellation.
Proposed diff:
{dialog === "pay" && account && (
- <CheckoutWidget
+ <CheckoutWidget
+ key={`${txChain.id}-${account.address}`}
className="!w-full"
client={props.client}
chain={txChain}
amount="0.01"
name="Buy Funds"
seller={account.address as `0x${string}`}
+ onSuccess={() => setDialog(undefined)}
+ onError={() => setDialog(undefined)}
+ onCancel={() => setDialog(undefined)}
theme={getSDKTheme(theme === "dark" ? "dark" : "light")}
/>
)}Reference: CheckoutWidget API – props and semantics (seller = recipient address, amount = human-readable string)
https://portal.thirdweb.com/references/typescript/v5/widgets/CheckoutWidget [1]
📝 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.
| {dialog === "pay" && account && ( | |
| <CheckoutWidget | |
| className="!w-full" | |
| client={props.client} | |
| chain={txChain} | |
| amount="0.01" | |
| name="Buy Funds" | |
| seller={account.address as `0x${string}`} | |
| theme={getSDKTheme(theme === "dark" ? "dark" : "light")} | |
| /> | |
| )} | |
| {dialog === "pay" && account && ( | |
| <CheckoutWidget | |
| key={`${txChain.id}-${account.address}`} | |
| className="!w-full" | |
| client={props.client} | |
| chain={txChain} | |
| amount="0.01" | |
| name="Buy Funds" | |
| seller={account.address as `0x${string}`} | |
| onSuccess={() => setDialog(undefined)} | |
| onError={() => setDialog(undefined)} | |
| onCancel={() => setDialog(undefined)} | |
| theme={getSDKTheme(theme === "dark" ? "dark" : "light")} | |
| /> | |
| )} |
🤖 Prompt for AI Agents
In apps/dashboard/src/@/components/tx-button/MismatchButton.tsx around lines 270
to 280, the CheckoutWidget needs a remount key and close callbacks: add a key
prop that changes when txChain or account.address changes (e.g.,
`${txChain}-${account.address}`) to force remounts when either changes, and pass
callbacks (onSuccess, onError, onCancel or the widget's equivalent) that call
setDialog(undefined) so the modal closes on completion, error, or cancellation;
keep seller as account.address and amount as "0.01".

PR-Codex overview
This PR primarily focuses on updating the
MismatchButtoncomponent by replacing thePayEmbedwithCheckoutWidget, adjusting transition timing inDynamicHeight, and refining the layout and styling in the dialog content.Detailed summary
DynamicHeightfrom 250ms to 220ms.PayEmbedwithCheckoutWidgetinMismatchButton.DynamicHeightwrapper around conditional content inMismatchButton.Summary by CodeRabbit
New Features
Style
Refactor