-
Notifications
You must be signed in to change notification settings - Fork 565
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
Conversation
🦋 Changeset detectedLatest commit: 9b57877 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
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 |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. |
WalkthroughThis 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 Changes
Sequence Diagram(s)NFT Creation Multi-Step FlowsequenceDiagram
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
Batch NFT Upload ProcessingsequenceDiagram
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
Media File Upload and PreviewsequenceDiagram
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.)
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
|
size-limit report 📦
|
Codecov ReportAttention: Patch coverage is
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
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 ofuseAllChainsData
hook andChainIconClient
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:
NFTTableRow
- for individual NFT row renderingNFTMediaDisplay
- for image/animation rendering logicNFTAttributes
- for attribute badge displayPriceSupplyEditor
- for price and supply editingThis 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
: Debugconsole.log
left in production pathStray 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 providedprice_currency
You normalise only the default (
NATIVE_TOKEN_ADDRESS
) but leave user-supplied addresses untouched. Wrap the user value withgetAddress
(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 UXWhen an invalid CSV is detected you expose a “Remove Invalid CSV” reset. Great UX, but the flag
csvUpload.noCsv
is cleared only byhandleReset
which also nukes manual textarea input. Consider exposing a lightercsvUpload.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 trapsInside long-running loops the
prev
snapshot might be outdated after earliersetSteps
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 toNFTCollection
– loses fidelity for 721 vs 1155You already return
ercType
; consider mapping it to the concrete contract (e.g.DropERC721
/DropERC1155
) for consistency withgetNFTStepTrackingData
.- contractType: "NFTCollection", - ercType: params.ercType, + contractType: + params.ercType === "erc721" ? "DropERC721" : "DropERC1155",
60-86
: Three nearly identical helpers – extract common builder to DRY up codeAll 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
📒 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
tostring
for theexternal_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
, whereexternal_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 forpollWithTimeout
.
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 ofprops.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 withid
strings, reflecting the new component API where step execution is handled externally via theonRetry
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 treatexternal_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. TheshowCoverImageUpload
,mediaFileUrl
, andmediaFileError
logic now correctly focus only onanimation_url
andimage
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 ofanimation_url
andimage
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 ofanimation_url
andimage
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 ofexternal_url
as a file input source. This aligns with the broader changes across the codebase to treatexternal_url
purely as a string URL input.
101-106
: Simplified media file URL calculation is correct.The
mediaFileUrl
calculation now correctly excludesexternal_url
and only considersanimation_url
andimage
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 excludeexternal_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 whetheranimation_url
is a file, which is the correct behavior after removingexternal_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 removeexternal_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 excludeexternal_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 excludeexternal_url
, maintaining consistency with the file handling changes.
147-147
: Cover image upload logic appropriately simplified.The
showCoverImageUpload
logic now correctly depends only onanimation_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 simplesetFiles
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
componentapps/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
togetTokenLaunchTrackingData
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 flexibilitycontractType
parameter enables differentiation between "DropERC20" and "NFTCollection"- Dynamic
contractType
usage replaces the hardcoded valueThis 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 integratesuseDropzone
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 accesscsvUpload.reset()
andcsvUpload.removeInvalid()
for actionscsvUpload.noCsv
for error statedropzone
props for file input handlingThe 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 operationErrorState
: Error handling without resetErrorStateWithResetButton
: Full error recovery flowThe 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 optionaldescription
field and the change toReact.ReactNode
for error messages enhances flexibility.
27-30
: Good component API design.The generic
MultiStepStatus<T>
component with the requiredonRetry
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 typeT
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 butapplication/json
for JSON. While these are correct MIME types, ensure thehandleDownload
utility properly sets theContent-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 passedfileFormat
to the Blob’stype
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 passestracking
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.
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
fileToBlobUrl
function to create blob URLs from files.FileInput
components to acceptclient
prop forThirdwebClient
.MediaRenderer
andFaucetButton
.DownloadFileButton
component for downloading files.client
prop for better integration withThirdweb
.Summary by CodeRabbit
New Features
Enhancements
Bug Fixes
Refactor
Documentation
Chores