Skip to content

[TOOL-4802] NFT Asset Page #7332

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

Merged
merged 1 commit into from
Jun 16, 2025
Merged

Conversation

MananTank
Copy link
Member

@MananTank MananTank commented Jun 12, 2025


PR-Codex overview

This PR focuses on enhancing the NFT and ERC20 functionalities within the dashboard, improving type definitions, and refining components for better usability and integration.

Detailed summary

  • Updated NFTMetadata type to include slug.
  • Added slug property in several components.
  • Introduced supplyFormatter for consistent supply formatting.
  • Enhanced BuyNFTDrop and TokenDropClaim components for better NFT purchase handling.
  • Improved pagination and UI for NFT displays.
  • Refactored ClaimTokenCardUI to use SupplyClaimedProgress and TokenPrice.
  • Added new NFTPublicPage and layout components for better organization.
  • Implemented better error handling and tracking for NFT purchases.

The following files were skipped due to too many changes: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx

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

Summary by CodeRabbit

  • New Features

    • Introduced public NFT pages for ERC-721 and ERC-1155 contracts with responsive layouts, NFT grids, token viewer dialogs, and buy functionality.
    • Added components for token price display, supply claimed progress, and enhanced pagination controls.
    • Enabled NFT purchases from drops with dedicated UI and transaction handling for ERC-721 and ERC-1155 standards.
    • Added tabbed navigation for public NFT pages and improved media rendering with loading placeholders.
    • Introduced a responsive layout component for device-specific rendering and customizable wallet address focus behavior.
    • Added new components for buying NFTs and editions with form validation, pricing display, and transaction feedback.
    • Added a token price component with loading skeleton and strikethrough styling.
    • Added a supply claimed progress bar with detailed percentage display.
    • Introduced a token viewer dialog with dynamic data fetching and rich metadata display.
    • Added a paginated NFT grid with detailed claim condition info for ERC-1155 tokens.
    • Added buy NFT drop card components with server and client rendering support.
    • Added a custom media renderer component with loading state management.
  • Enhancements

    • Improved contract and page header components with customizable styling options.
    • Expanded project and team data structures to include project slugs for better navigation.
    • Updated wallet address and pagination components with additional props for accessibility and styling.
    • Refined tracking of click and keyboard events for asset cards.
    • Simplified purchase UI code by delegating price and supply display to shared components.
    • Enhanced pagination to handle two-page scenarios with simplified controls.
    • Updated public page rendering logic to support multiple ERC standards with unified detection.
    • Improved purchase flows with analytics tracking and toast notifications.
    • Added support for special user pricing and dynamic claim condition fetching.
  • Bug Fixes

    • Fixed conditional rendering and redirection logic for public NFT pages based on contract type and metadata presence.
  • Documentation

    • Added Storybook stories for new UI components including supply claimed progress and NFT drop purchase flows.
  • Refactor

    • Delegated price and supply display logic to shared components for consistency.
    • Updated types and props to support new features and improve maintainability.
  • Chores

    • Broadened NFT metadata field types for improved compatibility with diverse data formats.
    • Added fallback for React experimental API to prevent errors in unsupported environments.

Copy link

vercel bot commented Jun 12, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
docs-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 16, 2025 6:04pm
login ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 16, 2025 6:04pm
thirdweb_playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 16, 2025 6:04pm
thirdweb-www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 16, 2025 6:04pm
wallet-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 16, 2025 6:04pm

Copy link

linear bot commented Jun 12, 2025

Copy link

changeset-bot bot commented Jun 12, 2025

🦋 Changeset detected

Latest commit: 913ea98

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
thirdweb Patch
@thirdweb-dev/wagmi-adapter Patch

Not sure what this means? Click here to learn what changesets are.

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

Copy link
Contributor

coderabbitai bot commented Jun 12, 2025

## Walkthrough

This update introduces a comprehensive NFT Asset Page supporting both NFT Drop Claim (ERC-721) and Edition Drop Claim (ERC-1155). It adds public NFT pages, paginated grid views, token viewers, claim/buy flows, and related UI components. The implementation includes new hooks, utilities, and refactors for displaying pricing, supply progress, and responsive layouts, as well as storybook stories for new components.

## Changes

| Files/Paths (grouped)                                                                                                                                                                                                                                  | Change Summary                                                                                                                                                                                                                   |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx, .../overview/nfts-grid.tsx, .../overview/tabs.tsx, .../overview/buy-nft-drop/buy-nft-drop-card.client.tsx, .../buy-nft-drop-card.server.tsx, .../buy-nft-drop-ui.client.tsx, .../buy-nft-drop.client.tsx, .../buy-edition-drop/buy-edition-drop.client.tsx, .../token-viewer/token-viewer.tsx, .../nft-page-layout.tsx, .../utils.ts, .../client-utils.ts, .../format.ts | Added and integrated NFT public page components: main page, paginated grid, tabbed navigation, buy/claim flows for ERC-721/1155, token viewer dialog, layout, hooks, utilities, and formatting.                                   |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.tsx, .../supply-claimed-progress.stories.tsx                                                                           | Added supply claimed progress component and Storybook stories.                                                                                                                            |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.stories.tsx                                                               | Added Storybook stories for BuyNFTDropUI with multiple claim and pricing scenarios.                                                                                                       |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop.client.tsx                                                                   | Added BuyNFTDrop client component, claim logic, and helper functions/types.                                                                                                               |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.server.tsx                                                              | Added server-side logic for buy NFT drop card and claim parameter fetching.                                                                                                               |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-edition-drop/buy-edition-drop.client.tsx                                                           | Added BuyEditionDrop client component for ERC-1155 claims.                                                                                                                               |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx                                                                                   | Added TokenViewerSheet and PageLoadTokenViewerSheet components for token details.                                                                                                         |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx                                                                                             | Added NFTPublicPageLayout for consistent layout of NFT public pages.                                                                                                                     |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts                                                                                                         | Added utility to get total NFT count for ERC-721/1155.                                                                                                                                    |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts                                                                                                  | Added hook for ERC-1155 claim condition and pricing.                                                                                                                                      |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/format.ts                                                                                                        | Added supplyFormatter for compact number formatting.                                                                                                                                      |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx                                                                                          | Added TokenPrice component for displaying formatted price.                                                                                                                               |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx                                                                  | Refactored: renamed ClaimTokenCardUI to TokenDropClaim, removed local price/supply components, used new TokenPrice and SupplyClaimedProgress.                                             |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.stories.tsx, .../erc20.tsx                                           | Updated to use TokenDropClaim instead of ClaimTokenCardUI.                                                                                                                               |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx                                                                                | Added imageClassName prop for image styling customization.                                                                                                                               |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.stories.tsx                                                                     | Added Storybook story for supply claimed progress component.                                                                                                                             |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx                                                                                          | Added containerClassName prop for header container styling.                                                                                                                              |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts                                                                    | Refactored public page type detection to support erc721/erc1155, updated type.                                                                                                           |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx, .../nfts/[tokenId]/shared-nfts-token-page.tsx, .../shared-overview-page.tsx                          | Integrated new NFT public page logic and routing.                                                                                                                                        |
| apps/dashboard/src/@/components/Responsive.tsx                                                                                                                          | Added ResponsiveLayout component for device-specific rendering.                                                                                                                          |
| apps/dashboard/src/@/components/blocks/media-renderer.tsx                                                                                                               | Added CustomMediaRenderer component with loading skeleton.                                                                                                                               |
| apps/dashboard/src/@/components/pagination-buttons.tsx                                                                                                                  | Enhanced pagination: added className prop, special case for 2 pages.                                                                                                                     |
| apps/dashboard/src/@/components/blocks/wallet-address.tsx                                                                                                               | Added preventOpenOnFocus prop to WalletAddress component.                                                                                                                                |
| apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx, .../add-to-project-card.stories.tsx                                     | Updated MinimalProject type and story data to include slug property.                                                                                                                     |
| apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx                                                                              | Updated contract view link logic to use team/project slugs when available.                                                                                                               |
| apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx, .../published-contract/components/uri-based-deploy.tsx                  | Added slug property to project objects in teamsAndProjects data.                                                                                                                         |
| packages/thirdweb/src/utils/nft/parseNft.ts                                                                                                                            | Broadened NFTMetadata properties/attributes fields to allow arrays or objects.                                                                                                           |
| apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx                                                                                 | Refactored CardLink tracking and keyboard event logic for consistency.                                                                                                                   |
| apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/_common/tracking.ts                                                           | Changed contractType tracking to "NFTCollection", added ercType field.                                                                                                                   |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant NFTPublicPage
    participant NFTsGrid
    participant BuyNFTDropCard
    participant BuyNFTDrop
    participant TokenViewerSheet
    participant ThirdwebContract

    User->>NFTPublicPage: Visit NFT asset page
    NFTPublicPage->>ThirdwebContract: Fetch metadata, selectors, NFT count
    NFTPublicPage->>NFTsGrid: Render NFT grid with contract/type
    NFTPublicPage->>BuyNFTDropCard: Render buy/claim UI if available
    User->>NFTsGrid: Browse NFTs, select token
    NFTsGrid->>TokenViewerSheet: Open token viewer modal
    TokenViewerSheet->>ThirdwebContract: Fetch token metadata/details
    User->>BuyNFTDropCard: Initiate NFT claim/purchase
    BuyNFTDropCard->>BuyNFTDrop: Render claim UI
    BuyNFTDrop->>ThirdwebContract: Prepare claim transaction, check approval
    BuyNFTDrop->>ThirdwebContract: Execute claim transaction
    BuyNFTDrop->>NFTPublicPage: On success, refresh data

Assessment against linked issues

Objective Addressed Explanation
Add NFT Asset Page with support for NFT Drop Claim and Edition Drop Claim (TOOL-4802)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Added slug property to project objects in teamsAndProjects data (shared-layout.tsx, uri-based-deploy.tsx) This change is unrelated to NFT asset page or claim functionality. It modifies project metadata.
Updated contract view link logic to use team/project slugs (custom-contract.tsx) This is a navigation enhancement, not related to NFT asset page or claims.
Broadened NFTMetadata properties/attributes fields to allow arrays (parseNft.ts) This is a general metadata type improvement, not specific to NFT asset pages.
Refactored CardLink tracking and keyboard event logic (cards.tsx) This is a UI/UX tracking update, not related to NFT asset page or claims.
Changed contractType tracking to "NFTCollection", added ercType field (tracking.ts) This is an analytics/tracking change, not directly related to NFT asset page or claims.

Possibly related PRs

Suggested reviewers

  • joaquim-verges

<!-- walkthrough_end -->

<!-- This is an auto-generated comment: resource warnings by coderabbit.ai -->

> [!WARNING]
> ## Review ran into problems
> 
> <details>
> <summary>🔥 Problems</summary>
> 
> Errors were encountered while retrieving linked issues.
> 
> <details>
> <summary>Errors (3)</summary>
> 
> * TEAM-0000: Entity not found: Issue - Could not find referenced Issue.
> * ERC-721: Entity not found: Issue - Could not find referenced Issue.
> * ERC-1155: Entity not found: Issue - Could not find referenced Issue.
> 
> </details>
> 
> </details>

<!-- end of auto-generated comment: resource warnings by coderabbit.ai -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=thirdweb-dev/js&utm_content=7332):

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

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

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

### CodeRabbit Configuration File (`.coderabbit.yaml`)

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

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@github-actions github-actions bot added Dashboard Involves changes to the Dashboard. packages SDK Involves changes to the thirdweb SDK labels Jun 12, 2025
Copy link
Member Author


How to use the Graphite Merge Queue

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

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

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Contributor

github-actions bot commented Jun 12, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 62.44 KB (0%) 1.3 s (0%) 266 ms (+255.38% 🔺) 1.6 s
thirdweb (cjs) 350.62 KB (0%) 7.1 s (0%) 718 ms (+17.38% 🔺) 7.8 s
thirdweb (minimal + tree-shaking) 5.7 KB (0%) 114 ms (0%) 132 ms (+3004.56% 🔺) 246 ms
thirdweb/chains (tree-shaking) 531 B (0%) 11 ms (0%) 65 ms (+2469.85% 🔺) 76 ms
thirdweb/react (minimal + tree-shaking) 19.59 KB (0%) 392 ms (0%) 135 ms (+924.16% 🔺) 527 ms

Copy link

codecov bot commented Jun 12, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 55.58%. Comparing base (5ce8a56) to head (913ea98).
Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7332   +/-   ##
=======================================
  Coverage   55.58%   55.58%           
=======================================
  Files         909      909           
  Lines       58683    58683           
  Branches     4158     4163    +5     
=======================================
  Hits        32617    32617           
  Misses      25959    25959           
  Partials      107      107           
Flag Coverage Δ
packages 55.58% <ø> (ø)
Files with missing lines Coverage Δ
packages/thirdweb/src/utils/nft/parseNft.ts 83.52% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🧹 Nitpick comments (19)
packages/thirdweb/src/utils/nft/parseNft.ts (2)

20-22: Update accompanying JSDoc / TODO

properties can now be either a record or an array, but the comment at line 19 (“// TODO check if we truly need both of these?”) still refers to the old single-record assumption.
Consider updating the TODO (or removing it) and expanding the inline docs so future readers know why both shapes are accepted.


32-33: Add note about attributes shape in documentation

The new union type for attributes mirrors properties, but the file-level doc-block doesn’t mention it. A short description (and maybe an example) would prevent confusion for integrators parsing the metadata.

apps/dashboard/src/@/components/pagination-buttons.tsx (2)

22-23: Prop should be documented on the component signature

className is a welcome addition, but there’s no TSDoc comment explaining what gets applied. A one-liner keeps the public API self-documenting.


74-75: Avoid duplicating props.className logic

className={props.className} is repeated in three branches. Consider extracting a rootClass = props.className variable (or just spreading { ...props }) to keep all branches consistent.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx (1)

70-83: Missing alt attribute on the <Img> element

Screen-readers will announce this image as an unnamed graphic. Passing an alt prop (even an empty string when the image is purely decorative) is considered an accessibility baseline.

 <Img
   className={cn(
     "size-20 shrink-0 rounded-full border bg-muted",
     props.imageClassName,
   )}
+  alt={props.name}
   src={ ... }
   ...
 />
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.client.tsx (1)

5-16: Stabilise the onSuccess handler

Every render recreates the arrow function, causing a prop change on NFTDropClaimUI and a potential unnecessary re-render. Wrap it in useCallback.

-import { useDashboardRouter } from "../../../../../../../../../@/lib/DashboardRouter";
+import { useCallback } from "react";
+import { useDashboardRouter } from "../../../../../../../../../@/lib/DashboardRouter";
 ...
   const router = useDashboardRouter();
+  const handleSuccess = useCallback(() => router.refresh(), [router]);
 ...
-      <NFTDropClaimUI {...props} onSuccess={() => router.refresh()} />
+      <NFTDropClaimUI {...props} onSuccess={handleSuccess} />
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx (1)

14-30: Guard against buyPage removal after mount

If buyPage becomes null after the tab is already set to "buy", the component will render nothing.
Consider auto-resetting the tab when buyPage toggles to falsy.

-const [tab, setTab] = useState<"nfts" | "buy">("nfts");
+const [tab, setTab] = useState<"nfts" | "buy">(
+  props.buyPage ? "nfts" : "nfts",
+);
 ...
 useEffect(() => {
   if (!props.buyPage && tab === "buy") {
     setTab("nfts");
   }
 }, [props.buyPage, tab]);
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (2)

17-21: loadedData could leak very large numbers to the UI

compactNumberFormatter is configured with maximumFractionDigits: 10, but no upper bound is set on priceInTokens. If the contract returns a value with a very large magnitude (e.g. 1e24 wei formatted to tokens), the compact formatter will happily display something like “1e21 ETH”, which is not a usable price for end-users and may break layouts.

-            : `${compactNumberFormatter.format(props.data.priceInTokens)} ${props.data.symbol}`
+            : `${compactNumberFormatter.format(
+                Math.min(props.data.priceInTokens, 1_000_000_000),
+              )} ${props.data.symbol}`

Clamp or round the value (or fall back to scientific notation) before calling .format so the UI remains predictable.


36-39: Reuse a shared formatter instead of recreating it per bundle

Intl.NumberFormat carries non-trivial instantiation overhead. Consider exporting a singleton from a utils/format module so every price/text component can reuse it instead of creating its own copy.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (1)

415-432: Total price calculation is vulnerable to FP rounding

Multiplying two JS Numbers that originated from bigints may introduce floating-point error (e.g. 0.1 * 3 = 0.30000000000000004).
Use bigint arithmetic up to the final UI formatting step.

- priceInTokens:
-   Number(toTokens(claimParamsData.pricePerTokenWei, claimParamsData.decimals)) *
-   Number(quantity),
+ priceInTokens: Number(
+   (
+     BigInt(claimParamsData.pricePerTokenWei) *
+     BigInt(quantity)
+   ).toString() /
+     10n ** BigInt(claimParamsData.decimals),
+ ),

Alternatively leverage ethers.js formatUnits & parseUnits.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (1)

23-41: Extension feature gates can be merged for readability

Three nested ifs duplicate the same pattern (ERC check + isGetNFTsSupported).
Consider factoring into a helper to make future additions easier.

- if (_supportedERCs.isERC1155 && ERC1155Ext.isGetNFTsSupported(functionSelectors)) {
-   return { type: "erc1155" };
- }
- if (_supportedERCs.isERC721 && ERC721Ext.isGetNFTsSupported(functionSelectors)) {
-   return { type: "erc721" };
- }
+ const ercChecks: Array<[boolean, () => boolean, NewPublicPageType]> = [
+   [_supportedERCs.isERC1155, () => ERC1155Ext.isGetNFTsSupported(functionSelectors), "erc1155"],
+   [_supportedERCs.isERC721, () => ERC721Ext.isGetNFTsSupported(functionSelectors), "erc721"],
+ ];
+ for (const [supported, guard, type] of ercChecks) {
+   if (supported && guard()) return { type };
+ }

No functional change, but the intent becomes clearer.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/edition-drop-claim.tsx (1)

351-355: decimals prop is unused

SupplyRemaining receives decimals but never references it, creating dead-code noise and misleading callers.

Either remove the parameter or implement formatting that actually needs it.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nft-drop-claim.tsx (1)

512-516: Unused decimals parameter in SupplyRemaining

The component never touches decimals; drop the prop to keep the API minimal.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (1)

92-99: Minor: publicPrice can reuse the fetched meta data

claimConditionQuery.data already contains decimals and symbol; cloning those fields into publicPrice is fine but costs an unnecessary object allocation every render.
Consider memoising or just re-exporting claimConditionQuery.data to keep the hook light.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.tsx (1)

11-24: async keyword is redundant here

NFTDropClaimEmbed never awaits, so the function always returns a resolved Promise<JSX.Element>.
Unless you intentionally rely on React’s Server Components to stream the result, you can drop the async keyword to avoid an unnecessary micro-task and type widening.

-export async function NFTDropClaimEmbed(props: { /* … */ }) {
+export function NFTDropClaimEmbed(props: { /* … */ }) {
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (2)

150-167: Extract attributes with useMemo to avoid repeated parsing

getAttributes is called on every render – even while the dialog is open but nothing changes.
Parsing large metadata arrays repeatedly can be surprisingly expensive on mobile devices.

-  const attributes = props.data ? getAttributes(props.data) : [];
+  const attributes = useMemo(
+    () => (props.data ? getAttributes(props.data) : []),
+    [props.data],
+  );

109-122: Error message conflates “not found” with all other errors

nftQuery.isError fires for network errors, RPC issues, and user-rejected requests, yet the dialog always says “No NFT found”.
Consider inspecting nftQuery.error and showing a generic fallback or the actual error message.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (2)

170-176: Loading skeleton count drifts frompageSize

NFTGridSkeleton renders 50 placeholders while the actual page size is 48.
Keeping them in sync prevents layout shifts.

- {Array.from({ length: 50 }).map((_, idx) => (
+ {Array.from({ length: pageSize }).map((_, idx) => (

318-327: supplyClaimed may be undefined during skeleton render

When data hasn’t arrived yet the JSX prints “undefined of … tokens bought”.
Guard the value or fall back to a skeleton placeholder.

- {claimConditionQuery.data?.supplyClaimed} of{" "}
+ {(claimConditionQuery.data?.supplyClaimed ?? "—")} of{" "}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f57300 and 1c2a044.

📒 Files selected for processing (27)
  • apps/dashboard/src/@/components/Responsive.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx (2 hunks)
  • apps/dashboard/src/@/components/pagination-buttons.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/edition-drop-claim.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nft-drop-claim.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • packages/thirdweb/src/utils/nft/parseNft.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (1)
  • shouldRenderNewPublicPage (9-53)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/utils.ts (1)
  • redirectToContractLandingPage (6-18)
apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx (1)
apps/dashboard/src/@/api/projects.ts (1)
  • Project (6-6)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1)
  • NFTPublicPage (19-138)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (1)
  • shouldRenderNewPublicPage (9-53)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1)
  • NFTPublicPage (19-138)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx (1)
apps/dashboard/src/@/components/ui/tabs.tsx (1)
  • TabButtons (85-159)
apps/dashboard/src/@/components/Responsive.tsx (1)
apps/dashboard/src/components/ClientOnly/ClientOnly.tsx (1)
  • ClientOnly (18-43)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_utils/getCurrencyMeta.ts (1)
  • getCurrencyMeta (8-44)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1)
  • TokenPrice (3-34)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • toTokens (190-190)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/edition-drop-claim.tsx (6)
packages/thirdweb/src/exports/thirdweb.ts (2)
  • ThirdwebContract (71-71)
  • toTokens (190-190)
apps/dashboard/src/hooks/analytics/useTrack.ts (1)
  • useTrack (13-45)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (2)
  • useERC1155ClaimCondition (46-153)
  • ASSET_PAGE_ERC1155_QUERIES_ROOT_KEY (44-44)
apps/dashboard/src/utils/errorParser.tsx (1)
  • parseError (29-69)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1)
  • TokenPrice (3-34)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PublicPageConnectButton.tsx (1)
  • PublicPageConnectButton (11-40)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.tsx (3)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • ThirdwebContract (71-71)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.client.tsx (1)
  • NFTDropClaimEmbedUI (5-16)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_utils/getCurrencyMeta.ts (1)
  • getCurrencyMeta (8-44)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: Lint Packages
  • GitHub Check: Unit Tests
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (16)
apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx (1)

46-51: slug addition looks correct – thanks for keeping the stub in sync
No functional issues spotted.

apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx (1)

42-47: Verify that getProjects() always returns slug
slug: x.slug assumes the REST/GraphQL response already contains this field. If any existing environment still omits slug, this will explode at runtime with undefined paths.

- slug: x.slug,
+ slug: x.slug ?? "",           // defensive default

or add a runtime guard / type-level guarantee.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx (1)

170-175: Same runtime guarantee concern for slug
If x.slug can be null/undefined, links further downstream will render /team/foo//contract/..., breaking navigation.

Consider asserting presence early or filtering out projects lacking a slug.

apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx (1)

22-24: Type extension approved
MinimalProject now mirrors the backend contract. Good catch keeping the pick list in one place.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx (1)

6-6: Import path check

The helper lives in ../_utils/newPublicPage, but we already import other helpers from the same folder using the same pattern. Good catch keeping paths consistent.

apps/dashboard/src/@/components/blocks/wallet-address.tsx (2)

27-28: Nice quality-of-life prop

preventOpenOnFocus solves the common UX annoyance when tabbing through the page—good addition.


68-71: Consider forwarding the prop downstream

Even with tabIndex={-1}, the nested <Button> is still focusable and may reopen the card on key events depending on Radix behaviour.
If that proves noisy, you might need to forward the prop to HoverCard’s openDelay or add onFocus={(e)=>e.preventDefault()} at the trigger layer.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx (2)

7-17: LGTM – flexible container classes

Allowing containerClassName gives downstream layouts breathing room without cloning this component. Implementation looks correct.


20-21: Minor visual tweak acknowledged

Logo height change from h-6 to h-5 is non-breaking and keeps vertical rhythm tighter.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx (1)

44-45: Prop addition looks good

Introducing imageClassName as an optional prop keeps the component backwards-compatible while allowing style overrides.
No issues spotted.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx (1)

104-115: Support for ERC-721 / ERC-1155 public pages added correctly

The new switch branch cleanly re-uses NFTPublicPage; type-safety is preserved and tokenId is sensibly set to undefined.
No further action required.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx (1)

45-59: Public token page routing logic is solid

The additional branch neatly defers to NFTPublicPage for ERC-721/1155 contracts when no project metadata is present. Good defensive check on shouldRenderNewPublicPage.

apps/dashboard/src/@/components/Responsive.tsx (1)

18-23: Potential double evaluation of useIsMobile

useIsMobile() runs before we know whether we are on the client (because it is outside ClientOnly).
If the hook internally touches window during SSR, hydration warnings or runtime exceptions may surface.

Verify that useIsMobile is SSR-safe (i.e. guards every window/matchMedia access with a typeof window !== "undefined" check).

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1)

126-133: Guard BigInt parsing of tokenId

BigInt(props.tokenId) will throw if tokenId is not a valid integer (e.g. query param tampering).

Add a safe parse with try/catch or pre-validate via /^\d+$/.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.tsx (1)

52-66: Verify the meaning of erc721NextTokenIdToClaim

erc721NextTokenIdToClaim is populated with ERC721Ext.getTotalClaimedSupply.
If the intention is to surface the next token id (often ≙ claimed supply, but not always, e.g. when tokens are burned), consider switching to ERC721Ext.nextTokenIdToClaim for clarity:

-    ERC721Ext.getTotalClaimedSupply({
+    ERC721Ext.nextTokenIdToClaim({

Please double-check the SDK semantics to ensure the UI shows an accurate next-id.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (1)

51-52: Potential overflow converting BigInt to Number

Math.ceil(Number(props.totalNFTCount) / pageSize) silently loses precision for collections larger than ≈9 quadrillion tokens.
While unlikely, using BigInt throughout avoids foot-guns:

- const totalPages = Math.ceil(Number(props.totalNFTCount) / pageSize);
+ const totalPages = Number(
+   (props.totalNFTCount + BigInt(pageSize) - 1n) / BigInt(pageSize),
+ );

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (7)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (2)

54-61: 🛠️ Refactor suggestion

Unstable React-Query cache keys — still embedding full contract objects

Prior review already covered this. params.contract is still stored inside both query keys, making the key identity change whenever a new signer recreates the SDK instance and blowing away the cache.
Use a stable primitive (address and optionally chain id) instead.

-      {
-        contract: params.contract,
+      {
+        contractAddress: params.contract.address,

Apply the same change to the claimParamsQuery key (lines 102-110).

Also applies to: 102-110


101-139: 🛠️ Refactor suggestion

claimParamsQuery still fires unconditionally

The query is mounted even when params.enabled is false or while claimConditionQuery is still loading, causing an avoidable extra RPC round-trip.
Prior review already highlighted this – add an enabled guard that mirrors the first query:

   const claimParamsQuery = useQuery({
     queryKey: [/* … */],
     queryFn: async () => { /* … */ },
-  });
+    enabled: params.enabled && (!!publicPrice || !!account),
+  });
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1)

284-286: ⚠️ Potential issue

Duplicate React keys risk runtime warnings & incorrect diffing

Using only attribute.trait_type as a React key will collide when multiple traits share the same name (e.g. two “color” traits).
Generate a stable per-element key.

- {attributes.map((attribute) => (
-   <TraitCard key={attribute.trait_type} {...attribute} />
- ))}
+ {attributes.map((attribute, idx) => (
+   <TraitCard key={`${attribute.trait_type}-${idx}`} {...attribute} />
+ ))}
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/edition-drop-claim.tsx (2)

94-98: ⚠️ Potential issue

Incorrect contractType sent to analytics – still hard-coded

contractType is hard-coded as "DropERC20" but this component handles an ERC-1155 Edition Drop. Analytics will be mis-categorised.

-      contractType: "DropERC20",
+      contractType: "EditionDrop",

149-160: ⚠️ Potential issue

Abort flow on failed approval

If approveTxPromise rejects, execution still proceeds to the claim transaction, almost guaranteeing a revert and double toast noise.
Return early (or wrap claim inside the same try) after tracking the error.

         } catch (err) {
           const errorMessage = parseError(err);
           trackAssetBuy({
             type: "error",
             errorMessage:
               typeof errorMessage === "string" ? errorMessage : "Unknown error",
           });
+          return; // stop – approval failed
         }
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nft-drop-claim.tsx (2)

173-178: ⚠️ Potential issue

Analytics mis-categorisation

contractType again reports "DropERC20" inside an ERC-721 drop component. Use the proper type ("NFTDrop" or similar) so downstream dashboards remain accurate.

-      contractType: "DropERC20",
+      contractType: "NFTDrop",

234-244: ⚠️ Potential issue

Continue-on-error after failed approval

Same logical flaw as in the ERC-1155 component: if approval fails, the claim still fires. Insert an early return.

         } catch (err) {
           const errorMessage = parseError(err);
           trackAssetBuy({
             type: "error",
             errorMessage:
               typeof errorMessage === "string" ? errorMessage : "Unknown error",
           });
+          return; // abort – approval failed
         }
🧹 Nitpick comments (4)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (4)

3-6: Avoid star-imports from large extension packages to reduce bundle size

import * as ERC721Ext and ERC1155Ext will pull every export from the extension packages into the bundle, even though you only need isGetNFTsSupported. Tree-shaking of * as namespaces is unreliable with some bundlers.

-import * as ERC721Ext from "thirdweb/extensions/erc721";
-import * as ERC1155Ext from "thirdweb/extensions/erc1155";
+import { isGetNFTsSupported as isERC721GetNFTsSupported } from "thirdweb/extensions/erc721";
+import { isGetNFTsSupported as isERC1155GetNFTsSupported } from "thirdweb/extensions/erc1155";

Update the usages accordingly:

-  _supportedERCs.isERC1155 && ERC1155Ext.isGetNFTsSupported(functionSelectors)
+  _supportedERCs.isERC1155 && isERC1155GetNFTsSupported(functionSelectors)

and

-  _supportedERCs.isERC721 && ERC721Ext.isGetNFTsSupported(functionSelectors)
+  _supportedERCs.isERC721 && isERC721GetNFTsSupported(functionSelectors)

7-8: Add a default/never branch to future-proof the union type

NewPublicPageType is now a closed union. Consider adding an exhaustive check (e.g. in a switch) or a never fallback wherever you consume this type so TypeScript will alert you when a new token standard is added.


21-22: Drop the leading underscore for consistency

Local variables are camel-cased elsewhere in the codebase (functionSelectors). Renaming _supportedERCs to supported (or similar) keeps naming conventions consistent and removes the unused private-like prefix.


43-47: Minor: guard clause can exit earlier

For slight readability, consider converting the final ERC-20 check into a guard clause at the top (after computing supportedERCs) so the positive paths are short-circuited and the default return false sits at the bottom without nested ifs.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2a044 and 324a08e.

📒 Files selected for processing (27)
  • apps/dashboard/src/@/components/Responsive.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx (2 hunks)
  • apps/dashboard/src/@/components/pagination-buttons.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/edition-drop-claim.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nft-drop-claim.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • packages/thirdweb/src/utils/nft/parseNft.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (22)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.client.tsx
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx
  • apps/dashboard/src/@/components/pagination-buttons.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
  • apps/dashboard/src/@/components/Responsive.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-embed.tsx
  • packages/thirdweb/src/utils/nft/parseNft.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx
🧰 Additional context used
🧬 Code Graph Analysis (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/edition-drop-claim.tsx (6)
packages/thirdweb/src/exports/thirdweb.ts (2)
  • ThirdwebContract (71-71)
  • toTokens (190-190)
apps/dashboard/src/hooks/analytics/useTrack.ts (1)
  • useTrack (13-45)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (2)
  • useERC1155ClaimCondition (46-153)
  • ASSET_PAGE_ERC1155_QUERIES_ROOT_KEY (44-44)
apps/dashboard/src/utils/errorParser.tsx (1)
  • parseError (29-69)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1)
  • TokenPrice (3-34)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PublicPageConnectButton.tsx (1)
  • PublicPageConnectButton (11-40)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (2)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • ThirdwebContract (71-71)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_utils/getCurrencyMeta.ts (1)
  • getCurrencyMeta (8-44)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Unit Tests
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (1)

23-41: Verify behaviour when a contract advertises multiple standards

A single contract can technically expose both ERC-721 and ERC-1155 interfaces (e.g. multi-token wrappers). With the current ordering, such a contract will always be treated as erc1155 because that check appears first.

Please confirm that this precedence is intentional; otherwise, add explicit disambiguation logic or allow the caller to decide.

if (_supportedERCs.isERC1155 && _supportedERCs.isERC721) {
  // decide which page to show or surface a selector to the user
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (2)

320-326: Still possible to generate duplicate React keys

Using trait_type and value drastically reduces the likelihood of clashes, yet duplicates are still possible whenever an NFT has the same trait/value pair repeated (seen in some collections that encode rarity weight).
Relying on deterministic data for keys can silently break React reconciliation.

- {attributes.map((attribute) => (
-   <TraitCard
-     key={`${attribute.trait_type}-${attribute.value}`}
-     ...
- ))}
+ {attributes.map((attribute, idx) => (
+   <TraitCard
+     key={`${attribute.trait_type}-${attribute.value}-${idx}`}
+     ...
+ ))}

157-164: Recompute-heavy values not memoised

attributes and isClaimable are recalculated on every re-render. Wrapping them in useMemo (and useMemo on the BigInt comparison) will avoid unnecessary work when parent state toggles (e.g. during claim mutations).

Not critical but improves perf in large grids.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c57b036 and 417b970.

📒 Files selected for processing (40)
  • .changeset/red-cooks-juggle.md (1 hunks)
  • apps/dashboard/src/@/components/Responsive.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/media-renderer.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx (2 hunks)
  • apps/dashboard/src/@/components/pagination-buttons.tsx (3 hunks)
  • apps/dashboard/src/@/constants/server-envs.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.stories.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (5 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/format.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-edition-drop/buy-edition-drop.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.server.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/_common/tracking.ts (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • packages/thirdweb/src/utils/nft/parseNft.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (39)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/format.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx
  • apps/dashboard/src/@/constants/server-envs.ts
  • .changeset/red-cooks-juggle.md
  • packages/thirdweb/src/utils/nft/parseNft.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/_common/tracking.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx
  • apps/dashboard/src/@/components/Responsive.tsx
  • apps/dashboard/src/@/components/blocks/media-renderer.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx
  • apps/dashboard/src/@/components/pagination-buttons.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-edition-drop/buy-edition-drop.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.server.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1)

165-169: Available For Purchase message may be misleading for ERC-721

isClaimable logic only applies to ERC-1155. For ERC-721 the UI labels every ownerless token as “Available For Purchase”, even though the contract may not implement marketplace/sale logic. Consider:

  1. Querying a sales module / marketplace extension when present.
  2. Hiding the banner unless price or listing information can be fetched.

This prevents confusing users with a CTA that might not exist.

Also applies to: 251-269

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts (1)

19-26: React-Query key still embeds the whole contract object

Non-serialisable objects in a query key break referential equality and will miss cache hits between renders. Pass stable primitives (address + chain id) instead.

-      {
-        contract: params.contract,
-        tokenId: params.tokenId.toString(),
-      },
+      {
+        contractAddress: params.contract.address,
+        chainId: params.contract.chain.id,
+        tokenId: params.tokenId.toString(),
+      },

Also applies to: 67-75

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (2)

55-55: Number(totalNFTCount) can lose precision for big collections

Bigints beyond 2^53-1 silently truncate. Keep the maths in bigint and cast only the final page count.


200-215: role="button" declared even when card isn’t clickable

Screen readers announce every card as a button, but without onClick it’s not operable—accessibility mismatch. Make role conditional like tabIndex.

🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts (2)

57-65: Duplicate price-mapping logic – extract helper

publicPriceData is built twice with identical code. Consolidating this in a small util (or at least a useMemo) removes repetition and keeps future refactors DRY.

Also applies to: 107-114


66-76: Skip claimParamsQuery when no connected wallet

enabled ignores account, so the query fires (and instantly returns null) on every rerender until a wallet connects—unnecessary work and noise in React-Query DevTools.

-enabled: params.enabled,
+enabled: params.enabled && !!account,

Also applies to: 77-80, 104-105

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 417b970 and a50d520.

📒 Files selected for processing (40)
  • .changeset/red-cooks-juggle.md (1 hunks)
  • apps/dashboard/src/@/components/Responsive.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/media-renderer.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx (2 hunks)
  • apps/dashboard/src/@/components/pagination-buttons.tsx (3 hunks)
  • apps/dashboard/src/@/constants/server-envs.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.stories.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (5 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/format.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-edition-drop/buy-edition-drop.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.server.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/_common/tracking.ts (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • packages/thirdweb/src/utils/nft/parseNft.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (38)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx
  • .changeset/red-cooks-juggle.md
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/format.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/_common/tracking.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
  • packages/thirdweb/src/utils/nft/parseNft.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx
  • apps/dashboard/src/@/components/pagination-buttons.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx
  • apps/dashboard/src/@/components/blocks/media-renderer.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx
  • apps/dashboard/src/@/components/Responsive.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-edition-drop/buy-edition-drop.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx
  • apps/dashboard/src/@/constants/server-envs.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.server.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx
🧰 Additional context used
🧠 Learnings (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (2)
Learnt from: MananTank
PR: thirdweb-dev/js#7332
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx:347-351
Timestamp: 2025-06-13T21:59:58.910Z
Learning: Intl.NumberFormat.prototype.format supports bigint values in modern JavaScript (ES2020+), so bigint values can be passed directly to formatter.format() without conversion to number.
Learnt from: MananTank
PR: thirdweb-dev/js#7332
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx:347-351
Timestamp: 2025-06-13T21:59:58.910Z
Learning: Intl.NumberFormat.prototype.format supports bigint values in modern JavaScript (ES2020+), so bigint values can be passed directly to formatter.format() without conversion to number.
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Size
  • GitHub Check: Unit Tests
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)

Copy link
Contributor

graphite-app bot commented Jun 16, 2025

Merge activity

<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the handling of NFTs and contract components within the application, including updates to types, UI components, and functionality related to NFT collections and claims.

### Detailed summary
- Updated `NFTMetadata` type to include `slug`.
- Added `slug` property in various components.
- Introduced `supplyFormatter` for better NFT supply formatting.
- Changed contract type handling to `NFTCollection`.
- Enhanced `MinimalProject` type to include `slug`.
- Updated UI components for better user interaction.
- Improved pagination and NFT display logic.
- Added new utilities for NFT claim conditions and total counts.
- Refactored claim token UI to utilize new components and structures.
- Implemented responsive layouts for better mobile support.

> The following files were skipped due to too many changes: `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx`, `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx`, `apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.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 public NFT pages for ERC-721 and ERC-1155 contracts with responsive layouts, NFT grids, token viewer dialogs, and buy functionality.
  - Added components for token price display, supply claimed progress, and enhanced pagination controls.
  - Enabled NFT purchases from drops with dedicated UI and transaction handling for ERC-721 and ERC-1155 standards.
  - Added tabbed navigation for public NFT pages and improved media rendering with loading placeholders.
  - Introduced a responsive layout component for device-specific rendering and customizable wallet address focus behavior.
  - Added new components for buying NFTs and editions with form validation, pricing display, and transaction feedback.
  - Added a token price component with loading skeleton and strikethrough styling.
  - Added a supply claimed progress bar with detailed percentage display.
  - Introduced a token viewer dialog with dynamic data fetching and rich metadata display.
  - Added a paginated NFT grid with detailed claim condition info for ERC-1155 tokens.
  - Added buy NFT drop card components with server and client rendering support.
  - Added a custom media renderer component with loading state management.

- **Enhancements**
  - Improved contract and page header components with customizable styling options.
  - Expanded project and team data structures to include project slugs for better navigation.
  - Updated wallet address and pagination components with additional props for accessibility and styling.
  - Refined tracking of click and keyboard events for asset cards.
  - Simplified purchase UI code by delegating price and supply display to shared components.
  - Enhanced pagination to handle two-page scenarios with simplified controls.
  - Updated public page rendering logic to support multiple ERC standards with unified detection.
  - Improved purchase flows with analytics tracking and toast notifications.
  - Added support for special user pricing and dynamic claim condition fetching.

- **Bug Fixes**
  - Fixed conditional rendering and redirection logic for public NFT pages based on contract type and metadata presence.

- **Documentation**
  - Added Storybook stories for new UI components including supply claimed progress and NFT drop purchase flows.

- **Refactor**
  - Delegated price and supply display logic to shared components for consistency.
  - Updated types and props to support new features and improve maintainability.

- **Chores**
  - Broadened NFT metadata field types for improved compatibility with diverse data formats.
  - Added fallback for React experimental API to prevent errors in unsupported environments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1)

377-414: Numeric/boolean attribute values are still silently skipped

The safeguard keeps the exact logic that was previously flagged and therefore continues to ignore non-string values coming from on-chain metadata (numbers, booleans, nullables), so those traits never appear in the UI.
Convert everything to String(attribute.value) (array branch) / String(nft.metadata.attributes[key]) (object branch) instead of filtering them out.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts (2)

19-26: Avoid embedding non-serialisable objects in React-Query keys

params.contract is a live object; a new instance is passed on every render, breaking referential equality and cache hits. Use stable primitives such as params.contract.address and params.contract.chain.id instead.

-      {
-        contract: params.contract,
-        tokenId: params.tokenId.toString(),
-      },
+      {
+        contractAddress: params.contract.address,
+        chainId: params.contract.chain.id,
+        tokenId: params.tokenId.toString(),
+      },

67-75: Same key-stability issue as above for ERC1155_getClaimParams query

Please apply the same primitive-only pattern here to keep cache behaviour predictable.

🧹 Nitpick comments (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (2)

320-327: React key may still collide for duplicate traits

key={${attribute.trait_type}-${attribute.value}} reduces the risk but does not remove it – duplicate metadata entries with identical trait_type and value will still clash. The safest low-cost fix is to suffix the index:

- {attributes.map((attribute) => (
-   <TraitCard
-     key={`${attribute.trait_type}-${attribute.value}`}
-     ...
-   />
- ))}
+ {attributes.map((attribute, idx) => (
+   <TraitCard
+     key={`${attribute.trait_type}-${attribute.value}-${idx}`}
+     ...
+   />
+ ))}

157-164: Repeated re-parsing of attributes on every render

getAttributes is called on each render of TokenInfoUI, even when nft hasn’t changed. Wrapping the call in useMemo keyed by props.data keeps renders cheap, especially for NFTs with many traits.

- const attributes = props.data ? getAttributes(props.data) : [];
+ const attributes = React.useMemo(
+   () => (props.data ? getAttributes(props.data) : []),
+   [props.data],
+ );
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts (1)

57-65: Duplicate price-mapping logic – factor out to a helper

The blocks that map claimConditionQuery.data into { pricePerTokenWei, … } are duplicated. Consolidating them into a small helper will reduce maintenance overhead and the risk of inconsistent changes.

Also applies to: 107-114

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a50d520 and 913ea98.

📒 Files selected for processing (40)
  • .changeset/red-cooks-juggle.md (1 hunks)
  • apps/dashboard/src/@/components/Responsive.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/media-renderer.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx (2 hunks)
  • apps/dashboard/src/@/components/pagination-buttons.tsx (3 hunks)
  • apps/dashboard/src/@/constants/server-envs.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.stories.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx (5 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/client-utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/format.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-edition-drop/buy-edition-drop.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.server.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.stories.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/token-viewer/token-viewer.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/_common/tracking.ts (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx (1 hunks)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx (1 hunks)
  • packages/thirdweb/src/utils/nft/parseNft.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (38)
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.stories.tsx
  • .changeset/red-cooks-juggle.md
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/format.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-layout.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/shared-overview-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/shared-nfts-page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/_common/tracking.ts
  • apps/dashboard/src/components/contract-components/contract-deploy-form/add-to-project-card.tsx
  • packages/thirdweb/src/utils/nft/parseNft.ts
  • apps/dashboard/src/@/constants/server-envs.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/PageHeader.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/shared-nfts-token-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/ContractHeader.tsx
  • apps/dashboard/src/@/components/blocks/wallet-address.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page-layout.tsx
  • apps/dashboard/src/@/components/pagination-buttons.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/uri-based-deploy.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/token-price.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/_components/supply-claimed-progress.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/utils.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/tabs.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx
  • apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/newPublicPage.ts
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.client.tsx
  • apps/dashboard/src/@/components/blocks/media-renderer.tsx
  • apps/dashboard/src/@/components/Responsive.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-ui.stories.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/nft-page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop-card.server.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-nft-drop/buy-nft-drop.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_components/claim-tokens/claim-tokens-ui.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/buy-edition-drop/buy-edition-drop.client.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/nft/overview/nfts-grid.tsx
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Lint Packages
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Analyze (javascript)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dashboard Involves changes to the Dashboard. packages SDK Involves changes to the thirdweb SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants