Skip to content

Dashboard: Add NFT creation wizard in Asset page #7315

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 10, 2025

Conversation

MananTank
Copy link
Member

@MananTank MananTank commented Jun 10, 2025


PR-Codex overview

This PR focuses on enhancing the handling of file uploads, particularly images, and improves the integration of the ThirdwebClient across various components. It also introduces support for blob URLs and refines error handling in several areas.

Detailed summary

  • Added fileToBlobUrl function to create blob URLs from files.
  • Enhanced FileInput components to accept client prop for ThirdwebClient.
  • Improved error handling in several components, including MediaRenderer and FaucetButton.
  • Updated form schemas to include validation for URLs and addresses.
  • Reorganized file structures and imports for better clarity.
  • Introduced new tracking functions for NFT actions.
  • Added DownloadFileButton component for downloading files.
  • Updated various UI components to utilize the new client prop for better integration with Thirdweb.

The following files were skipped due to too many changes: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/upload-nfts.tsx, apps/dashboard/src/core-ui/batch-upload/batch-table.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/page.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/sales/sales-settings.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/collection-info/nft-collection-info-fieldset.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/snapshot-upload.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token-info-fieldset.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-upload.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/NFTMediaFormGroup.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page-ui.tsx, apps/dashboard/src/components/shared/FileInput.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/distribution/token-airdrop.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/process-files.ts, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/single-upload/single-upload-nft.tsx, apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/batch-upload-instructions.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/launch/launch-token.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/batch-upload-nfts.tsx, apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch/launch-nft.tsx

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

Summary by CodeRabbit

  • New Features

    • Added logic to always display the selected token in token selectors.
    • Introduced a styled drag-and-drop file upload area with error handling and reset options.
    • Added reusable components: file preview for files and URLs, step cards with navigation and tracking, social URLs fieldset, downloadable file button, batch upload instructions, and NFT attribute management.
    • Launched a multi-step NFT creation workflow covering collection info, asset upload (single and batch), sales settings, and launch steps.
    • Added schema validation for NFT metadata including pricing, social URLs, and blockchain addresses.
    • Enabled batch uploading and inline editing of NFTs with price, currency, and supply management.
    • Added advanced sales and royalty settings with real-time validation and analytics tracking.
  • Enhancements

    • Improved error reporting in toast notifications with detailed parsed messages.
    • Centralized and streamlined media file handling by removing redundant hooks and consolidating upload logic.
    • Enhanced navigation and event tracking across multi-step asset creation flows.
    • Updated UI components to accept client context for consistent file handling.
    • Refined form validation by reusing common schemas.
  • Bug Fixes

    • Corrected NFT supply column label to "Circulating Supply".
    • Improved loading placeholder text in token selectors.
  • Refactor

    • Consolidated CSV upload logic and replaced custom drag-and-drop UIs with a unified DropZone component.
    • Replaced multiple media preview components with a single FilePreview component.
    • Updated prop types and component interfaces for better consistency and maintainability.
    • Removed deprecated hooks and unused imports to reduce technical debt.
    • Reorganized and standardized import paths across modules.
    • Simplified step execution logic in launch flows and enhanced retry capabilities.
  • Documentation

    • Added Storybook stories for DropZone, multi-step status, and NFT upload components to support visual testing and documentation.
  • Chores

    • Exported utility functions and constants for broader use.
    • Improved styling flexibility by enhancing component className handling.

@MananTank MananTank requested review from a team as code owners June 10, 2025 00:17
Copy link

changeset-bot bot commented Jun 10, 2025

🦋 Changeset detected

Latest commit: 9b57877

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

vercel bot commented Jun 10, 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 10, 2025 9:58pm
login ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 10, 2025 9:58pm
thirdweb_playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 10, 2025 9:58pm
thirdweb-www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 10, 2025 9:58pm
wallet-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 10, 2025 9:58pm

Copy link
Contributor

graphite-app bot commented Jun 10, 2025

How to use the Graphite Merge Queue

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

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

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

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

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

Copy link
Contributor

coderabbitai bot commented Jun 10, 2025

Walkthrough

This update introduces a comprehensive NFT creation workflow for a dashboard application, including new multi-step UI components, batch and single NFT upload modules, and detailed schema validation using Zod. The refactor centralizes and standardizes media file handling, enhances error reporting, and introduces reusable components for file previews, downloads, and social URL management. Numerous components now require a ThirdwebClient prop for consistent blockchain interactions. Storybook stories and schema utilities are added for testing and validation. Several UI components are updated or replaced to use these new shared modules, and legacy or redundant logic is removed.

Changes

File(s) / Group Change Summary
NFT Creation Workflow:
.../nft/create-nft-page-ui.tsx, .../nft/create-nft-page.tsx, .../nft/page.tsx
Added new multi-step NFT creation UI and logic, including contract deployment, lazy minting, and claim condition setup.
NFT Upload & Batch Processing:
.../nft/upload-nfts/upload-nfts.tsx, .../nft/upload-nfts/single-upload/single-upload-nft.tsx, .../nft/upload-nfts/batch-upload/batch-upload-nfts.tsx, .../nft/upload-nfts/batch-upload/process-files.ts, .../nft/upload-nfts/schema.ts, .../nft/upload-nfts/batch-upload/batch-upload-instructions.tsx, .../nft/upload-nfts/single-upload/attributes.tsx, .../nft/upload-nfts/upload-nfts.stories.tsx
Implemented single and batch NFT upload components, batch file processing (CSV/JSON), attribute management, validation schemas, and Storybook stories.
NFT Creation Shared & Common:
.../nft/_common/form.ts, .../nft/_common/pages.ts, .../nft/_common/tracking.ts, .../nft/collection-info/nft-collection-info-fieldset.tsx, .../nft/sales/sales-settings.tsx
Added shared form schemas, tracking utilities, and collection/sales settings components.
NFT Media Handling:
.../nft/handleNFTMediaUpload.ts, .../nft/components/nft/NFTMediaFormGroup.tsx, .../nft/components/update-metadata-form.tsx, .../nft/components/lazy-mint-form.tsx, .../nft/components/mint-form.tsx, .../nft/components/shared-metadata-form.tsx
Centralized NFT media file upload logic, refactored components to use new helpers, removed legacy hooks.
File Preview & Upload Utilities:
.../file-preview.tsx, .../download-file-button.tsx, .../file-to-url.ts
Introduced reusable file preview and download button components, and a utility for generating blob URLs from files.
Social URLs & Schema Utilities:
.../SocialUrls.tsx, .../schema.ts
Added dynamic social URL fieldset component and reusable Zod schemas for social URLs and addresses.
NFT Creation Step Card:
.../step-card.tsx, .../create-token-card.tsx
Added and refactored step card components for navigation and analytics tracking.
DropZone & CSV Upload Refactor:
.../drop-zone/drop-zone.tsx, .../drop-zone/drop-zone.stories.tsx, .../useCsvUpload.ts, .../airdrop-upload.tsx, .../snapshot-upload.tsx, .../token-airdrop.tsx
Introduced DropZone component, refactored CSV upload logic to use react-dropzone, updated related components and stories.
Multi-Step Status Refactor:
.../multi-step-status/multi-step-status.tsx, .../multi-step-status/multi-step-status.stories.tsx
Refactored step status logic to be generic and accept retry callbacks, updated story.
FileInput & Media Preview Updates:
.../shared/FileInput.tsx, .../Img.tsx, .../tw-components/nft-media.tsx, .../MediaRenderer.tsx, .../resolveMimeType.ts, .../BasisPointsInput.tsx
Updated file input and media preview logic, standardized client prop usage, improved styling and fallback handling.
Generalized Client Prop:
Numerous files across components and forms
Added or updated client: ThirdwebClient prop to various components for consistent blockchain SDK access.
Contract Deploy/Publish/Properties Forms:
.../contract-deploy-form/contract-metadata-fieldset.tsx, .../contract-deploy-form/custom-contract.tsx, .../contract-publish-form/landing-fieldset.tsx, .../contract-pages/forms/properties.shared.tsx
Updated to require and use client prop, simplified image/file handling.
Miscellaneous Refactors:
.../cards.tsx, .../chain-overview.tsx, .../mintable.tsx, .../BatchMetadata.tsx, .../account/settings/AccountSettingsPageUI.tsx, .../team-onboarding/TeamInfoForm.tsx, .../team-onboarding/team-onboarding.tsx, .../team-onboarding/TeamInfoForm.stories.tsx, .../ProjectGeneralSettingsPage.tsx, .../Configure/index.tsx, .../contract-components/tables/contract-table.tsx, .../contract-components/contract-deploy-form/contract-metadata-fieldset.tsx, .../contract-components/contract-deploy-form/custom-contract.tsx, .../contract-components/contract-publish-form/landing-fieldset.tsx, .../contract-pages/forms/properties.shared.tsx, .../embedded-wallets/Configure/index.tsx, .../ipfs-upload/button.tsx, .../solidity-inputs/string-input.tsx, .../useImageFileOrUrl.ts, .../batch-upload/batch-table.tsx, .../batch-upload/batch-lazy-mint.tsx, .../utils/batch.ts, .../token/_common/form.ts, .../token/_common/tracking.ts, .../token/create-token-page.client.tsx, .../token/distribution/token-distribution.tsx, .../token/distribution/token-sale.tsx, .../token/page.tsx, .../token/token-info/token-info-fieldset.tsx, .../token/create-token-page-impl.tsx, .../token/launch/launch-token.tsx, .../blocks/TokenSelector.tsx, .../blocks/Img.tsx, .../blocks/decimal-input.tsx, .../blocks/multi-step-status/multi-step-status.tsx, .../blocks/multi-step-status/multi-step-status.stories.tsx, .../blocks/drop-zone/drop-zone.tsx, .../blocks/drop-zone/drop-zone.stories.tsx
Miscellaneous refactors, prop updates, import path changes, and removal of legacy/unused code across many files.
Error Handling Improvements:
.../claim-tab.tsx, .../FaucetButton.tsx, .../create-token-page-impl.tsx
Enhanced error handling and reporting using a new parseError utility.
Types & Schema Updates:
.../project-contracts.ts, .../contract-table.tsx, .../utils/batch.ts
Extended types for contract types, exported utility functions, and updated asset type mappings.
Token Form Refactor:
.../token/_common/form.ts, .../token/token-info/token-info-fieldset.tsx
Replaced inline validation with imported shared schemas; removed local SocialUrls component in favor of shared component.
Launch Token Flow Refactor:
.../token/launch/launch-token.tsx
Replaced embedded step execution with centralized step runner; added retry support; replaced custom file preview with shared component; removed unused imports and hooks.
Storybook Additions:
.../drop-zone/drop-zone.stories.tsx, .../multi-step-status/multi-step-status.stories.tsx, .../upload-nfts/upload-nfts.stories.tsx, .../team-onboarding/TeamInfoForm.stories.tsx
Added stories for DropZone, MultiStepStatus, UploadNFTsFieldset, and TeamInfoForm with client prop injection.

Sequence Diagram(s)

NFT Creation Multi-Step Flow

sequenceDiagram
    participant User
    participant CreateNFTPage
    participant CreateNFTPageUI
    participant NFTCollectionInfoFieldset
    participant UploadNFTsFieldset
    participant SalesSettings
    participant LaunchNFT

    User->>CreateNFTPage: Navigates to NFT creation page
    CreateNFTPage->>CreateNFTPageUI: Renders with props
    User->>NFTCollectionInfoFieldset: Fills collection info, selects chain
    NFTCollectionInfoFieldset->>CreateNFTPageUI: onNext
    CreateNFTPageUI->>UploadNFTsFieldset: Renders NFT upload step
    User->>UploadNFTsFieldset: Uploads NFT(s) (single or batch)
    UploadNFTsFieldset->>CreateNFTPageUI: onNext
    CreateNFTPageUI->>SalesSettings: Renders sales settings step
    User->>SalesSettings: Enters royalty and sales info
    SalesSettings->>CreateNFTPageUI: onNext
    CreateNFTPageUI->>LaunchNFT: Renders launch step with overview
    User->>LaunchNFT: Clicks "Launch"
    LaunchNFT->>CreateNFTPage: Calls deploy, lazy mint, set claim conditions
    CreateNFTPage->>LaunchNFT: Returns result, updates status
    LaunchNFT->>User: Shows progress, errors, or success
Loading

Batch NFT Upload Processing

sequenceDiagram
    participant User
    participant BatchUploadNFTs
    participant processBatchUploadFiles
    participant FilePreview

    User->>BatchUploadNFTs: Drops files into DropZone
    BatchUploadNFTs->>processBatchUploadFiles: Parses files (CSV/JSON, images, assets)
    processBatchUploadFiles-->>BatchUploadNFTs: Returns parsed NFT metadata or error
    BatchUploadNFTs->>FilePreview: Renders previews for each NFT row
    User->>BatchUploadNFTs: Edits price, currency, supply, attributes
    User->>BatchUploadNFTs: Clicks Next to continue workflow
Loading

Media File Upload and Preview

sequenceDiagram
    participant User
    participant FileInput
    participant handleNFTMediaUpload
    participant getUploadedNFTMediaMeta
    participant FilePreview

    User->>FileInput: Selects or drops file
    FileInput->>handleNFTMediaUpload: Processes file, updates form
    FileInput->>getUploadedNFTMediaMeta: Extracts media state from form
    FileInput->>FilePreview: Renders preview (image, video, etc.)
Loading

DropZone File Upload (Reusable)

sequenceDiagram
    participant User
    participant DropZone
    participant useDropzone
    participant onDropHandler

    User->>DropZone: Drags and drops file(s)
    DropZone->>useDropzone: Handles drag/drop event
    useDropzone->>onDropHandler: Passes accepted files
    onDropHandler-->>DropZone: Updates state, triggers UI feedback
Loading

<!-- 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 (1)</summary>
> 
> * TEAM-0000: 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=7315):

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

Copy link
Contributor

github-actions bot commented Jun 10, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 62.57 KB (0%) 1.3 s (0%) 293 ms (+153.82% 🔺) 1.6 s
thirdweb (cjs) 345.55 KB (0%) 7 s (0%) 1.1 s (+12.18% 🔺) 8 s
thirdweb (minimal + tree-shaking) 5.7 KB (0%) 114 ms (0%) 81 ms (+863.66% 🔺) 195 ms
thirdweb/chains (tree-shaking) 531 B (0%) 11 ms (0%) 34 ms (+928.27% 🔺) 44 ms
thirdweb/react (minimal + tree-shaking) 19.56 KB (0%) 392 ms (0%) 83 ms (+370.53% 🔺) 475 ms

Copy link

codecov bot commented Jun 10, 2025

Codecov Report

Attention: Patch coverage is 83.33333% with 1 line in your changes missing coverage. Please review.

Project coverage is 55.57%. Comparing base (81c66f4) to head (99d6b3b).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...es/thirdweb/src/react/web/utils/resolveMimeType.ts 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7315      +/-   ##
==========================================
- Coverage   55.57%   55.57%   -0.01%     
==========================================
  Files         909      909              
  Lines       58673    58675       +2     
  Branches     4158     4157       -1     
==========================================
+ Hits        32607    32608       +1     
- Misses      25959    25960       +1     
  Partials      107      107              
Flag Coverage Δ
packages 55.57% <83.33%> (-0.01%) ⬇️
Files with missing lines Coverage Δ
...b/src/react/web/ui/MediaRenderer/MediaRenderer.tsx 40.33% <100.00%> (ø)
...es/thirdweb/src/react/web/utils/resolveMimeType.ts 89.47% <80.00%> (-4.65%) ⬇️
🚀 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 (16)
apps/dashboard/src/@/components/blocks/TokenSelector.tsx (1)

162-171: Good defensive programming for missing selected tokens.

The logic correctly handles cases where the selected token isn't in the fetched options list, preventing UI inconsistencies.

Consider using shortenAddress for the fallback label to maintain consistency with the address display format used elsewhere:

- label: props.selectedToken?.address || "Unknown",
+ label: props.selectedToken?.address ? shortenAddress(props.selectedToken.address, 4) : "Unknown",
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/NFTMediaFormGroup.tsx (1)

13-14: Address the TODO comment for the external_url field.

The TODO comment indicates that this field is currently unused. Consider either implementing the usage or removing the field entirely if it's no longer needed.

Would you like me to help determine the intended usage of this field or generate code to implement it?

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/chain-overview.tsx (1)

1-24: Well-structured reusable component.

The ChainOverview component is well-implemented with proper fallbacks for missing chain metadata. The use of useAllChainsData hook and ChainIconClient component follows good separation of concerns.

Consider improving type safety for the chainId prop:

export function ChainOverview(props: {
-  chainId: string;
+  chainId: number | string;
  client: ThirdwebClient;
}) {

This would make the component more flexible while maintaining type safety.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/upload-nfts.tsx (1)

42-53: Consider preserving user data when switching modes.

Currently, switching between "Create Single" and "Create Multiple" modes resets the data to null. This could frustrate users who accidentally switch tabs and lose their work.

Consider implementing a confirmation dialog or temporary data preservation:

onClick: () => {
+  if (props.nftData.nft && !confirm("Switching modes will clear your current data. Continue?")) {
+    return;
+  }
  props.setNFTData({
    type: "single",
    nft: null,
  });
}
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/single-upload/single-upload-nft.tsx (1)

129-145: Improve price input validation error display.

The price input error handling is implemented correctly, but consider consolidating the error display pattern used throughout the form for consistency.

Consider using a reusable error display component:

const ErrorMessage = ({ error }: { error?: string }) => 
  error ? <p className="text-destructive-text text-sm">{error}</p> : null;

// Then use:
<ErrorMessage error={form.formState.errors.price_amount?.message} />
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/batch-upload-instructions.tsx (1)

182-385: Consider extracting example code constants to improve maintainability.

The hardcoded example strings are quite large and would be better maintained in a separate constants file or as individual template files. This would also make it easier to update examples and add syntax highlighting in editors.

Consider creating a separate file for example templates:

+// batch-upload-examples.ts
+export const CSV_EXAMPLES = {
+  basic: `name,description,background_color,foo,bar,bazz
+Token 0 Name,Token 0 Description,#0098EE,value1,value2,value3
+Token 1 Name,Token 1 Description,#0098EE,value1,value2,value3`,
+  // ... other examples
+} as const;
+
+export const JSON_EXAMPLES = {
+  basic: [...],
+  // ... other examples
+} as const;

Then import and use:

-const csv_example_basic = `\
-name,description,background_color,foo,bar,bazz
-...
-`;
+import { CSV_EXAMPLES, JSON_EXAMPLES } from './batch-upload-examples';
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/batch-upload-nfts.tsx (2)

267-296: Type safety concern with dynamic attribute access.

The attribute rendering logic uses manual type checking with runtime validation. Consider defining a proper type for attributes to improve type safety.

+type NFTAttribute = {
+  trait_type: string;
+  value: string;
+};
+
+function isValidAttribute(attr: unknown): attr is NFTAttribute {
+  return (
+    typeof attr === "object" &&
+    attr !== null &&
+    "trait_type" in attr &&
+    "value" in attr &&
+    typeof attr.trait_type === "string" &&
+    typeof attr.value === "string"
+  );
+}

 {Array.isArray(item.attributes) && (
   <div className="flex flex-wrap gap-2">
-    {item.attributes.map((property: unknown) => {
-      if (
-        typeof property === "object" &&
-        property !== null &&
-        "trait_type" in property &&
-        "value" in property &&
-        typeof property.trait_type === "string" &&
-        typeof property.value === "string"
-      ) {
+    {item.attributes.filter(isValidAttribute).map((property) => {
         return (
           <Badge
             key={`${property.trait_type}-${property.value}`}

133-426: Consider breaking down the large BatchUploadNFTs component.

This component handles multiple responsibilities including file upload, data display, editing, and validation. Consider extracting sub-components for better maintainability.

Consider extracting these sub-components:

  1. NFTTableRow - for individual NFT row rendering
  2. NFTMediaDisplay - for image/animation rendering logic
  3. NFTAttributes - for attribute badge display
  4. PriceSupplyEditor - for price and supply editing

This would improve:

  • Code readability and maintainability
  • Component testing in isolation
  • Performance through memoization opportunities
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/schema.ts (1)

60-71: Consider simplifying the attributes transformation logic.

The current filter logic in the transform function could be clearer about its intention and behavior.

Consider this more explicit approach:

   attributes: z
     .array(z.object({ trait_type: z.string(), value: z.string() }))
     .transform((value) => {
-      if (!value) {
-        return value;
-      }
-
-      return value.filter((item) => {
-        return item.trait_type && item.value;
-      });
+      // Filter out attributes with empty trait_type or value
+      return value?.filter((item) => 
+        item.trait_type.trim() !== "" && item.value.trim() !== ""
+      ) ?? [];
     })
     .optional(),

This version:

  • Handles the null/undefined case more explicitly
  • Trims whitespace to catch empty strings with spaces
  • Makes the filtering intention clearer with a comment
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page-ui.tsx (1)

62-65: Complete the TODO for chain update handling.

The TODO comment indicates incomplete functionality for updating price address when the chain changes. This could affect the pricing configuration in the NFT creation flow.

Do you want me to help implement the chain update logic or create an issue to track this task?

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/process-files.ts (2)

293-297: Debug console.log left in production path

Stray logging inside tight CSV-parsing loop will spam the console and leak metadata.

-                console.log({
-                  parsed,
-                  result,
-                });

Please drop before merge.


126-129: CSV / JSON paths do not checksum or validate provided price_currency

You normalise only the default (NATIVE_TOKEN_ADDRESS) but leave user-supplied addresses untouched. Wrap the user value with getAddress (and optionally reject invalid ones) to keep casing consistent across rows.

Also applies to: 165-166

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/distribution/token-airdrop.tsx (1)

331-345: DropZone error-reset UX

When an invalid CSV is detected you expose a “Remove Invalid CSV” reset. Great UX, but the flag csvUpload.noCsv is cleared only by handleReset which also nukes manual textarea input. Consider exposing a lighter csvUpload.clearFiles() to avoid wiping other progress.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/launch/launch-token.tsx (1)

61-72: updateStatus mutates by index – consider functional update to avoid stale closure traps

Inside long-running loops the prev snapshot might be outdated after earlier setSteps calls.
Safer pattern:

-    setSteps((prev) => {
-      return [
-        ...prev.slice(0, index),
-        { ...prev[index], status: newStatus },
-        ...prev.slice(index + 1),
-      ];
-    });
+    setSteps((prev) =>
+      prev.map((s, idx) =>
+        idx === index ? { ...s, status: newStatus } : s,
+      ),
+    );
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/tracking.ts (2)

31-58: contractType is hard-coded to NFTCollection – loses fidelity for 721 vs 1155

You already return ercType; consider mapping it to the concrete contract (e.g. DropERC721 / DropERC1155) for consistency with getNFTStepTrackingData.

-    contractType: "NFTCollection",
-    ercType: params.ercType,
+    contractType:
+      params.ercType === "erc721" ? "DropERC721" : "DropERC1155",

60-86: Three nearly identical helpers – extract common builder to DRY up code

All three functions share the same conditional errorMessage handling and many keys. A small internal util would reduce duplication and future drift.

function withError<T extends { type?: string; status?: string }>(
  base: Record<string, unknown>,
  params: T,
) {
  return params.type === "error" || params.status === "error"
    ? { ...base, errorMessage: (params as any).errorMessage }
    : base;
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 56d27d4 and 5bad90c.

📒 Files selected for processing (53)
  • apps/dashboard/src/@/components/blocks/TokenSelector.tsx (2 hunks)
  • apps/dashboard/src/@/components/blocks/drop-zone/drop-zone.stories.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/drop-zone/drop-zone.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx (1 hunks)
  • apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx (3 hunks)
  • apps/dashboard/src/@/components/ui/decimal-input.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/snapshot-upload.tsx (6 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/AdvancedNFTMetadataFormGroup.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/NFTMediaFormGroup.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-upload.tsx (5 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/cards.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/PageHeader.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/SocialUrls.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/chain-overview.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/download-file-button.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/file-image.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/schema.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/create-token-card.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/create-token-page-impl.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/distribution/token-airdrop.tsx (9 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/distribution/token-distribution.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/form.ts (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/launch/launch-token.tsx (4 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page-ui.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/form.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/nft-collection-info-fieldset.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/tracking.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/batch-upload-instructions.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/batch-upload-nfts.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/process-files.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/schema.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/single-upload/attributes.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/single-upload/single-upload-nft.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/upload-nfts.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/page.tsx (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token-info-fieldset.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/tracking.ts (2 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/hooks/project-contracts.ts (1 hunks)
  • apps/dashboard/src/components/contract-components/tables/contract-table.tsx (1 hunks)
  • apps/dashboard/src/hooks/useCsvUpload.ts (2 hunks)
  • apps/dashboard/src/utils/batch.ts (2 hunks)
  • packages/thirdweb/src/utils/nft/parseNft.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (13)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx (1)
apps/dashboard/src/utils/errorParser.tsx (1)
  • parseError (29-69)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx (1)
apps/dashboard/src/utils/errorParser.tsx (1)
  • parseError (29-69)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/create-token-page-impl.tsx (2)
apps/dashboard/src/utils/errorParser.tsx (1)
  • parseError (29-69)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/tracking.ts (2)
  • getTokenStepTrackingData (26-52)
  • getTokenDeploymentTrackingData (1-23)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/page.tsx (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/PageHeader.tsx (1)
  • CreateAssetPageHeader (12-56)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/form.ts (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/schema.ts (1)
  • socialUrlsSchema (6-23)
apps/dashboard/src/@/components/blocks/drop-zone/drop-zone.stories.tsx (1)
apps/dashboard/src/@/components/blocks/drop-zone/drop-zone.tsx (1)
  • DropZone (6-81)
apps/dashboard/src/utils/batch.ts (1)
packages/thirdweb/src/utils/nft/parseNft.ts (1)
  • NFTInput (11-22)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/schema.ts (1)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • isAddress (308-308)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-upload.tsx (1)
apps/dashboard/src/hooks/useCsvUpload.ts (1)
  • useCsvUpload (93-184)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/process-files.ts (3)
apps/dashboard/src/utils/batch.ts (2)
  • jsonMimeTypes (25-31)
  • csvMimeTypes (13-23)
packages/thirdweb/src/exports/thirdweb.ts (3)
  • getAddress (307-307)
  • NATIVE_TOKEN_ADDRESS (17-17)
  • isAddress (308-308)
packages/thirdweb/src/utils/nft/parseNft.ts (1)
  • NFTInput (11-22)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/form.ts (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/schema.ts (1)
  • socialUrlsSchema (6-23)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/process-files.ts (1)
  • NFTMetadataWithPrice (57-68)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token-info-fieldset.tsx (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/SocialUrls.tsx (1)
  • SocialUrlsFieldset (21-106)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/schema.ts (1)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • isAddress (308-308)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Size
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (79)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx (1)

142-142: Improved terminology for ERC1155 supply display.

The change from "Supply" to "Circulating Supply" provides better clarity for users, as it accurately reflects that this column shows the current circulating amount of ERC1155 tokens rather than the total possible supply.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/file-image.tsx (1)

6-59: Well-implemented file preview component.

The component follows React best practices:

  • Proper object URL cleanup in useEffect to prevent memory leaks
  • Muted autoplay for videos (accessibility compliance)
  • Clear type-based rendering logic
  • Good fallback handling for unsupported file types
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/distribution/token-distribution.tsx (1)

36-39: Enhanced tracking structure for better analytics.

The change from a simple string to a structured tracking object improves event tracking granularity by including both the page context and contract type. This enables better analytics and more precise tracking of user interactions across different asset creation workflows.

apps/dashboard/src/components/contract-components/tables/contract-table.tsx (1)

297-298: Logical extension of contract type mappings for NFT collections.

The addition of DropERC721 and DropERC1155 mappings to "NFT Collection" is consistent with the existing pattern and supports the new NFT creation workflow. This provides clear asset type categorization for users in the dashboard.

packages/thirdweb/src/utils/nft/parseNft.ts (1)

17-17: Simplified external_url type aligns with semantic expectations.

The change from FileOrBufferOrString to string for the external_url property is semantically correct, as external URLs should be string references to external resources rather than uploadable files. This simplification improves type safety and aligns with the broader codebase refactoring mentioned in the AI summary.

apps/dashboard/src/@/components/ui/decimal-input.tsx (1)

9-9: LGTM! Clean addition of disabled prop.

The addition of the optional disabled prop enhances the component's flexibility and follows standard UI component patterns.

Also applies to: 19-19

apps/dashboard/src/@/components/blocks/TokenSelector.tsx (1)

189-189: LGTM! Clean up of loading text.

Removing the ellipsis standardizes the loading state text across the application.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/BatchMetadata.tsx (1)

42-42:

✅ Verification successful

Verify external_url usage consistency across the codebase.

The schema change standardizes external_url as a string-only field, which simplifies the data model. Ensure all components that handle this field have been updated accordingly.


🏁 Script executed:

#!/bin/bash
# Description: Verify that external_url is consistently treated as string across the codebase
# Expected: No references to external_url being handled as File or buffer

# Search for external_url usage patterns
rg -A 3 -B 3 "external_url.*[Ff]ile|[Ff]ile.*external_url" --type ts --type tsx

# Search for external_url in form handling and validation
rg -A 5 "external_url" --type ts --type tsx | grep -E "(File|Buffer|blob)"

Length of output: 212


Here’s a refined search that avoids the unsupported --type tsx flag and uses glob patterns to scan all TS/TSX files:


🏁 Script executed:

#!/bin/bash
# Refined verify: look for any handling of external_url as File, Buffer, or blob in .ts/.tsx files
rg -i -C3 'external_url.*(File|Buffer|blob)|(File|Buffer|blob).*external_url' -g '*.ts' -g '*.tsx'

Length of output: 812


external_url string-only schema is consistent across the codebase.

The change to z.string().optional() in BatchMetadata.tsx aligns with all existing usage:

  • No occurrences of external_url being treated as File, Buffer, or blob were found in the app code.
  • The only other reference is in packages/thirdweb/src/extensions/erc1155/write/lazyMint.ts, where external_url?: FileOrBufferOrString still permits string URLs.

No further updates are needed.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx (2)

13-13: LGTM! Importing parseError utility for consistent error handling.

This import enables standardized error parsing across the application.


71-76: Excellent error handling improvement.

The enhanced error handling provides better user experience by:

  • Offering a consistent error message format
  • Including detailed error descriptions parsed by the centralized utility
  • Following the established pattern across other components
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx (2)

53-53: LGTM! Consistent import of parseError utility.

This maintains consistency with error handling patterns across the application.


238-243: Great enhancement to error feedback.

The improved error handling provides:

  • Consistent error message structure
  • User-friendly error descriptions via parseError utility
  • Better alignment with application-wide error handling patterns
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/hooks/project-contracts.ts (1)

14-14: LGTM! Contract type expansion for NFT support.

The addition of "DropERC721" and "DropERC1155" to the contract type union correctly enables support for NFT collections alongside the existing ERC20 tokens. This change is backward compatible and aligns with the NFT creation wizard implementation.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/create-token-page-impl.tsx (4)

29-30: LGTM! Import improvements.

Good addition of the parseError utility for better error handling and shortened import path for pollWithTimeout.


140-148: Excellent error handling enhancement.

The implementation correctly uses parseError to provide more user-friendly error messages for event tracking, with proper type checking to ensure the error message is a string.


155-157: Consistent error handling pattern.

Good continuation of the same error parsing pattern for deployment tracking events.


416-416: Good fix for URL generation.

Correctly uses props.teamSlug instead of props.projectId in the URL path, which aligns with the expected route structure.

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

42-44: LGTM! NFT creation feature activation.

The card updates correctly enable the NFT creation feature with:

  • Clear, concise title "Create NFT"
  • Descriptive text about launching NFT collections
  • Working href that routes to the NFT creation wizard

This aligns well with the broader NFT creation workflow implementation.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/AdvancedNFTMetadataFormGroup.tsx (1)

94-106: Good simplification of external_url handling.

The refactor correctly standardizes the external_url field as a string-only input, removing the complex File object handling. The implementation uses:

  • Proper form field setup with validation
  • URL input type for browser validation
  • Clear helper text explaining the field's purpose

This aligns with the broader standardization effort across NFT metadata forms.

apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.stories.tsx (2)

21-21: LGTM! Added required onRetry callback.

The addition of the onRetry prop correctly satisfies the new component API requirements.


26-46: Good API adaptation for step definitions.

The updates correctly replace the execute functions with id strings, reflecting the new component API where step execution is handled externally via the onRetry callback. This separation of concerns is a good design pattern.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/NFTMediaFormGroup.tsx (2)

27-27: Good removal of external_url file handling.

The simplification correctly removes the logic that cleared external_url when setting an image file, aligning with the broader refactor to treat external_url as a string-only field.


59-74: Logic simplification looks correct.

The changes properly simplify the media file handling logic by removing external_url file considerations. The showCoverImageUpload, mediaFileUrl, and mediaFileError logic now correctly focus only on animation_url and image fields.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx (2)

57-57: Consistent removal of external_url file handling.

This change aligns with the broader refactor to standardize external_url as a string field. The removal of the clearing logic is appropriate.


90-101: Properly simplified media file logic.

The logic correctly removes external_url file considerations while maintaining the proper handling of animation_url and image fields. The simplification is consistent and correct.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx (3)

72-72: Consistent external_url file handling removal.

This change aligns with the coordinated refactor across NFT components to treat external_url as a string-only field.


105-116: Properly simplified media handling logic.

The logic correctly removes external_url file considerations while maintaining proper handling of animation_url and image fields.


282-295: Good UX improvement - always show External URL field.

Making the External URL field always visible is a good UX decision since it's now purely a string input field. This removes unnecessary conditional rendering complexity.

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

10-10: Good refactoring to use shared component.

The import of the shared CreateAssetPageHeader component promotes code reuse and consistency across asset creation pages.


46-52: Props correctly passed to shared header component.

The props are properly passed to the CreateAssetPageHeader component, maintaining the same functionality while benefiting from the shared implementation. The title "Create Coin" and description are appropriate for this page.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx (5)

66-95: LGTM! Simplified file handling by removing external_url logic.

The setFile function has been correctly updated to remove the handling of external_url as a file input source. This aligns with the broader changes across the codebase to treat external_url purely as a string URL input.


101-106: Simplified media file URL calculation is correct.

The mediaFileUrl calculation now correctly excludes external_url and only considers animation_url and image files, which is consistent with the simplified file handling approach.


108-113: Media file error calculation properly updated.

The mediaFileError calculation has been correctly simplified to exclude external_url error handling, maintaining consistency with the file handling changes.


115-115: Simplified cover image upload logic is appropriate.

The showCoverImageUpload flag now depends solely on whether animation_url is a file, which is the correct behavior after removing external_url file handling.


256-269: External URL form control is now always rendered correctly.

The External URL input is now unconditionally rendered as a string input field, which is the expected behavior after removing the file upload functionality for this field.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx (5)

98-127: LGTM! Consistent removal of external_url file handling.

The setFile function has been properly updated to remove external_url file handling logic, maintaining consistency with the broader refactor across NFT components.


133-138: Media file URL calculation correctly simplified.

The simplification of mediaFileUrl to exclude external_url is appropriate and consistent with the removal of file handling for this field.


140-145: Media file error calculation properly updated.

The mediaFileError calculation has been correctly updated to exclude external_url, maintaining consistency with the file handling changes.


147-147: Cover image upload logic appropriately simplified.

The showCoverImageUpload logic now correctly depends only on animation_url being a file, which is the expected behavior after the refactor.


330-343: External URL form control correctly rendered as string input.

The External URL input is now properly rendered as an unconditional string input field, which aligns with treating external_url as a string-only field.

apps/dashboard/src/hooks/useCsvUpload.ts (1)

105-132: Excellent refactor to decouple CSV processing from UI concerns.

The replacement of useDropzone with a simple setFiles callback successfully separates CSV parsing logic from drag-and-drop UI concerns. The CSV validation and parsing logic is properly preserved, including:

  • CSV file type validation
  • Address column presence checks
  • Papa Parse integration for CSV processing
  • Error handling for invalid files

This refactor enables the use of a reusable DropZone component while keeping the hook focused on CSV processing.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/page.tsx (4)

19-24: Efficient parallel data fetching implementation.

The use of Promise.all() to fetch authentication token, team data, project data, and account address in parallel is an excellent performance optimization that reduces overall loading time.


26-38: Proper authentication and data validation with appropriate redirects.

The authentication checks and data validation logic are well-implemented:

  • Redirects to login when auth/wallet is missing
  • Falls back to team list when team not found
  • Falls back to team page when project not found

This provides a good user experience and prevents broken states.


40-43: Correct thirdweb client initialization.

The client initialization with JWT token and team ID is properly configured for authenticated API access.


45-66: Well-structured component composition.

The component structure is clean and follows good practices:

  • Proper use of shared CreateAssetPageHeader component
  • Appropriate container styling and layout
  • All necessary props passed to the CreateNFTPage component
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/PageHeader.tsx (3)

12-18: Well-defined component interface.

The props interface is clear and provides appropriate customization options for team/project context, content, and styling.


20-39: Proper breadcrumb navigation implementation.

The breadcrumb implementation correctly uses the UI components and provides clear navigation back to the assets list. The use of Next.js Link component is appropriate for client-side navigation.


41-55: Good responsive layout and styling.

The header layout uses appropriate Tailwind classes for responsive design and proper spacing. The use of the cn utility for conditional styling is a good practice.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/tracking.ts (2)

55-55: LGTM: Function rename improves clarity.

The rename from getLaunchTrackingData to getTokenLaunchTrackingData makes the function's purpose more explicit and aligns with the broader NFT support being added.


88-96: LGTM: Excellent generalization for multi-contract support.

The changes properly generalize the tracking system:

  • step parameter now accepts any string for flexibility
  • contractType parameter enables differentiation between "DropERC20" and "NFTCollection"
  • Dynamic contractType usage replaces the hardcoded value

This maintains backward compatibility while enabling NFT tracking support.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/single-upload/attributes.tsx (1)

32-106: LGTM: Clean implementation of dynamic attribute management.

The component provides a clean interface for managing NFT attributes with proper form validation, accessibility labels, and intuitive add/remove functionality. The UI layout adapts well to different screen sizes.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/airdrop-upload.tsx (3)

40-43: LGTM: Clean integration of consolidated CSV upload state.

The refactor properly consolidates CSV upload functionality using the useCsvUpload hook and integrates useDropzone for file handling. This follows the same pattern used in other CSV upload components throughout the codebase.


47-47: LGTM: Consistent state access pattern.

Using csvUpload.normalizeQuery.data maintains consistency with the consolidated state management approach.


106-184: LGTM: Proper state management throughout the component.

All references to CSV upload state and methods have been correctly updated to use the consolidated csvUpload object:

  • csvUpload.rawData for data access
  • csvUpload.reset() and csvUpload.removeInvalid() for actions
  • csvUpload.noCsv for error state
  • dropzone props for file input handling

The refactor maintains all existing functionality while improving code organization.

apps/dashboard/src/@/components/blocks/drop-zone/drop-zone.stories.tsx (1)

1-53: LGTM: Comprehensive Storybook coverage for DropZone component.

The stories file provides excellent coverage of the DropZone component states:

  • Default: Normal operation
  • ErrorState: Error handling without reset
  • ErrorStateWithResetButton: Full error recovery flow

The container decorator provides appropriate visual context, and all necessary props are properly configured for effective visual testing.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/SocialUrls.tsx (1)

32-106: LGTM: Well-designed social URL management component.

The component provides an intuitive interface for managing social URLs with:

  • Clear platform and URL input fields
  • Proper form validation and error messaging
  • Responsive design that adapts to different screen sizes
  • Accessible controls with screen reader support

The UI patterns are consistent with the AttributesFieldset component, creating a cohesive user experience.

apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx (3)

13-25: LGTM! Well-designed generic type refactoring.

The refactoring from embedded execute methods to a generic type with external retry handling is excellent. This improves reusability and separation of concerns. The addition of the optional description field and the change to React.ReactNode for error messages enhances flexibility.


27-30: Good component API design.

The generic MultiStepStatus<T> component with the required onRetry callback provides a clean separation between UI and business logic. This allows consumers to handle retry logic externally while the component focuses on presentation.


60-67: Improve accessibility and UX with the description display.

The conditional rendering of descriptions for pending and error states provides helpful user feedback. The muted text styling appropriately de-emphasizes supplementary information.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/_common/schema.ts (1)

25-35: LGTM! Robust address validation.

The address validation using isAddress from the thirdweb library is appropriate and should handle blockchain address validation correctly. The error message is clear and user-friendly.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/upload-nfts.tsx (2)

11-19: Excellent use of discriminated union for type safety.

The NFTData discriminated union type provides excellent type safety by ensuring that the data structure matches the selected mode. This prevents runtime errors and improves developer experience with proper type checking.


57-88: Clean conditional rendering with proper props passing.

The conditional rendering based on NFT data type is clean and passes appropriate props to child components. The state update callbacks properly maintain the discriminated union structure.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/single-upload/single-upload-nft.tsx (3)

37-47: Good form setup with proper validation and data persistence.

The form configuration is well-structured with proper default values, validation, and revalidation mode. Using the values prop ensures the form stays in sync with external state changes.


49-52: Excellent UX pattern for data preservation.

Saving form data before navigation (props.setNFTData(form.getValues())) is an excellent UX pattern that prevents users from losing their work when navigating back.


248-250: Good use of URL input type for external URL.

Using type="url" provides built-in browser validation and improved mobile keyboard support for URL input.

apps/dashboard/src/utils/batch.ts (3)

25-31: Good utility export for JSON MIME type handling.

Exporting jsonMimeTypes allows for consistent MIME type handling across the application, which is especially useful for file upload components.


105-115: Excellent type safety improvement with generic constraints.

The generic type constraint <T extends Record<string, unknown>> improves type safety while preserving the function's behavior. The return type T ensures the caller gets back the same object type they passed in.


117-124: Good utility export for OpenSea metadata standardization.

Exporting convertToOsStandard enables consistent metadata formatting across the application, which is valuable for NFT-related components.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/form.ts (3)

3-3: LGTM! Good refactoring for code reuse.

The extraction of shared validation schemas to a common module improves maintainability and consistency across the codebase.


14-14: Type safety improvement looks good.

Changing from a generic optional type to z.instanceof(File).optional() provides better type safety and runtime validation for the image field.


15-15: Clean schema reuse.

Using the imported socialUrlsSchema directly instead of inline validation reduces code duplication and ensures consistency.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/batch-upload/batch-upload-instructions.tsx (1)

404-409:

✅ Verification successful

Verify the download file format parameter.

The file format parameter uses text/csv for CSV but application/json for JSON. While these are correct MIME types, ensure the handleDownload utility properly sets the Content-Type header.


🏁 Script executed:

#!/bin/bash
# Check the handleDownload implementation to verify it handles MIME types correctly
ast-grep --pattern 'export function handleDownload($_) {
  $$$
}'

Length of output: 2017


No changes needed for download file format

The handleDownload function already applies the passed fileFormat to the Blob’s type option, so both "text/csv" and "application/json" are correctly set on the downloaded file.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/snapshot-upload.tsx (1)

16-16: Clean refactoring to use react-dropzone.

The consolidation of CSV upload logic and adoption of the standard react-dropzone library improves consistency with other file upload components in the codebase.

Also applies to: 56-65

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/token-info-fieldset.tsx (2)

11-11: Excellent separation of concerns.

Moving the social URLs logic to a shared component reduces duplication and improves maintainability across the asset creation flow.

Also applies to: 126-126


26-29:

✅ Verification successful

Verify the StepCard tracking prop change is backward compatible.

The tracking prop structure has changed from a simple string to an object. Ensure all consumers of the StepCard component have been updated.


🏁 Script executed:

#!/bin/bash
# Search for other StepCard usages to verify tracking prop compatibility
ast-grep --pattern 'StepCard
  $$$
  page=$_
  $$$
'

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Find all StepCard component usages to verify tracking prop updates
rg -n "<StepCard" .

Length of output: 831


🏁 Script executed:

#!/bin/bash
# List all tracking prop usages in StepCard components
# Search for lines containing both StepCard and tracking across the repo
rg -n "StepCard" . -A5 | rg "tracking"

Length of output: 2985


🏁 Script executed:

#!/bin/bash
# Inspect StepCard usage and tracking prop in create-token-card.tsx
sed -n '1,200p' 'apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/create-token-card.tsx' | rg -n -C5 '<StepCard'

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Locate the create-token-card.tsx file to inspect StepCard usage
fd create-token-card.tsx .

Length of output: 137


All StepCard tracking props updated to the new object format – no changes required

I’ve verified that every <StepCard> consumer now passes tracking as an object ({ page, contractType }) rather than a string. No instances of the old string-based prop remain.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/nft-collection-info-fieldset.tsx (1)

1-131: LGTM! Well-structured NFT collection form component.

The component demonstrates good practices with proper form validation, SSR-safe chain selection using ClientOnly, and consistent tracking integration. The form structure is clear and accessibility is maintained through proper labeling and error message display.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/upload-nfts/schema.ts (1)

1-72: LGTM! Comprehensive validation schema for NFT metadata.

The schema properly validates all required NFT fields including pricing and supply. The use of isAddress for currency validation and the numeric validations for price and supply are appropriate.

apps/dashboard/src/@/components/blocks/drop-zone/drop-zone.tsx (1)

1-81: LGTM! Well-designed reusable dropzone component.

The component properly handles drag-and-drop functionality with good error state management and accessibility. The use of stopPropagation on the reset button prevents unwanted file selection triggers, and the conditional rendering provides clear visual feedback.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/create-nft-page-ui.tsx (1)

19-114: LGTM! Well-structured multi-step NFT creation orchestrator.

The component effectively manages the NFT creation workflow with proper state management and form integration. The step-by-step approach provides a clear user experience.

This was referenced Jul 22, 2025
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