From d5f4165d051ce2e5c2096013216e37f579d9baab Mon Sep 17 00:00:00 2001 From: pinkhoodie Date: Thu, 1 May 2025 21:20:28 -0400 Subject: [PATCH 01/17] Implement asset creation flows with standardized components --- .../assets-implementation-summary.md | 84 ++ .../[project_slug]/assets/create-nft/page.tsx | 747 ++++++++++++++++++ .../assets/create-token/page.tsx | 726 +++++++++++++++++ .../[project_slug]/assets/layout.tsx | 9 + .../[project_slug]/assets/page.tsx | 282 +++++++ 5 files changed, 1848 insertions(+) create mode 100644 apps/dashboard/assets-implementation-summary.md create mode 100644 apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create-nft/page.tsx create mode 100644 apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create-token/page.tsx create mode 100644 apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/layout.tsx create mode 100644 apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/page.tsx diff --git a/apps/dashboard/assets-implementation-summary.md b/apps/dashboard/assets-implementation-summary.md new file mode 100644 index 00000000000..c44436103e1 --- /dev/null +++ b/apps/dashboard/assets-implementation-summary.md @@ -0,0 +1,84 @@ +# Assets Module Implementation Summary + +## Overview + +The Assets module has been revised to leverage the existing contract deployment components and patterns from the contracts module in the thirdweb dashboard. This ensures consistency in design, form validation, and user experience across the platform. + +## Key Improvements + +### 1. Component Reuse + +- **Fieldset Component**: Replaced generic Card components with the specialized Fieldset component from the contracts module, providing consistent section styling +- **FormFieldSetup**: Used the FormFieldSetup component for consistent form field layout and error handling +- **FileInput**: Integrated the shared FileInput component for image uploads +- **BasisPointsInput**: Adopted the specialized BasisPointsInput component for percentage inputs (royalties, fees) +- **SolidityInput**: Used SolidityInput for address inputs to ensure proper validation of blockchain addresses + +### 2. Form Validation Improvements + +- Standardized form validation patterns using zod schemas +- Added detailed validation: + - Symbol limited to 10 characters max + - Proper validation for numeric inputs + - Required field validation with clear error messages +- Added validation feedback indicators for token allocation percentages + +### 3. Error Handling and User Feedback + +- Consistent display of validation errors using FormFieldSetup +- Visual indicators for invalid states +- Proper error message placement under relevant fields + +### 4. Responsive Design + +- Improved grid layouts for different screen sizes +- Responsive form sections that adapt to small screens +- Better organized form fields with appropriate spacing + +### 5. User Experience Improvements + +- Consistent step indicator component with clear progression visualization +- Collapsible sections for advanced settings +- Improved file upload and preview for token/collection images +- Better organized form sections with logical grouping +- Consistent button styling and placement + +### 6. Design System Compliance + +- Maintained consistent spacing, margins, and padding +- Used standard color tokens from the design system +- Ensured proper typography and visual hierarchy + +## Technical Notes + +1. **Shared Form Components**: + + - Reused common form field components across both token and NFT creation flows + - Standardized form layout patterns + - Used `FormFieldSetup` to maintain consistency + +2. **State Management**: + + - Used React Hook Form for form state management + - Implemented multi-step form navigation + - Maintained consistent form validation with zod + +3. **Deployment Integration**: + - Updated deployment action buttons to match the contract deployment flow + - Prepared for integration with actual deployment functions + +## Future Improvements + +1. **Network Selection Integration**: + + - Connect to actual chain selection component with network icons + - Add chain-specific configuration options + +2. **Project Integration**: + + - Integrate with project selection for adding assets to projects + - Implement actual deployment to selected chains + +3. **Asset Preview**: + - Add visual preview of tokens/NFTs in the review step + - Preview metadata in standardized format diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create-nft/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create-nft/page.tsx new file mode 100644 index 00000000000..1473a14994a --- /dev/null +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/assets/create-nft/page.tsx @@ -0,0 +1,747 @@ +"use client"; + +import { FormFieldSetup } from "@/components/blocks/FormFieldSetup"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { Textarea } from "@/components/ui/textarea"; +import { Switch } from "@/components/ui/switch"; +import { cn } from "@/lib/utils"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + ArrowLeftIcon, + ArrowRightIcon, + ImageIcon, + CheckIcon, + ChevronDownIcon, + ChevronUpIcon, +} from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import { Fieldset } from "components/contract-components/contract-deploy-form/common"; +import { FileInput } from "components/shared/FileInput"; +import { BasisPointsInput } from "components/inputs/BasisPointsInput"; +import { SolidityInput } from "contract-ui/components/solidity-inputs"; +import { Form } from "@/components/ui/form"; + +// Form schemas +const collectionInfoSchema = z.object({ + name: z.string().min(1, "Name is required"), + symbol: z + .string() + .min(1, "Symbol is required") + .max(10, "Symbol must be 10 characters or less"), + chain: z.string().min(1, "Chain is required"), + description: z.string().optional(), + asCollection: z.boolean().default(true), + image: z.any().optional(), +}); + +const mintSettingsSchema = z.object({ + price: z.string().default("0.1"), + supply: z.string().min(1, "Supply is required"), + initialMint: z.string().default("1"), + // Royalty settings + royaltyPercentage: z.string().default("0"), + royaltyAddress: z.string().optional(), + collectionType: z.enum(["new", "existing", "project"]).default("new"), + platformFeeBps: z.string().default("250"), // Using basis points (2.5% = 250 basis points) +}); + +type CollectionInfoValues = z.infer; +type MintSettingsValues = z.infer; + +// Step indicator component +const StepIndicator = ({ + step, + currentStep, + label, +}: { + step: number; + currentStep: number; + label: string; +}) => ( +
+
step + ? "bg-primary/20 text-primary" + : "bg-muted text-muted-foreground" + )} + > + {currentStep > step ? ( + + ) : ( + {step} + )} +
+ {label} +
+); + +export default function CreateNFTPage() { + const [step, setStep] = useState(1); + const [collectionInfo, setCollectionInfo] = useState(); + const [mintSettings, setMintSettings] = useState({ + price: "0.1", + supply: "10000", + initialMint: "1", + royaltyPercentage: "0", + royaltyAddress: "", + collectionType: "new", + platformFeeBps: "250", + }); + const [showRoyaltySettings, setShowRoyaltySettings] = useState(false); + + // Forms + const collectionInfoForm = useForm({ + resolver: zodResolver(collectionInfoSchema), + defaultValues: { + name: "", + symbol: "", + chain: "Ethereum", + description: "", + asCollection: true, + image: undefined, + }, + }); + + const mintSettingsForm = useForm({ + resolver: zodResolver(mintSettingsSchema), + defaultValues: { + price: "0.1", + supply: "10000", + initialMint: "1", + royaltyPercentage: "0", + royaltyAddress: "", + collectionType: "new", + platformFeeBps: "250", + }, + }); + + // Step handlers + const onCollectionInfoSubmit = (data: CollectionInfoValues) => { + setCollectionInfo(data); + setStep(2); + }; + + const onMintSettingsSubmit = (data: MintSettingsValues) => { + setMintSettings(data); + setStep(3); + }; + + const goBack = () => { + if (step > 1) { + setStep(step - 1); + } + }; + + // Render functions + const renderStepIndicators = () => ( +
+
+ {/* Connect line between circles */} +
+ + + + +
+
+ ); + + const renderStep1 = () => ( +
+

Create NFT Collection

+

+ Create a collection of NFTs with shared properties +

+ + {renderStepIndicators()} + +
+
+ + collectionInfoForm.setValue("asCollection", checked) + } + /> + +
+ +
+ +
+ + + collectionInfoForm.setValue("image", file, { + shouldTouch: true, + }) + } + className="rounded border-border bg-background transition-all duration-200 hover:border-active-border hover:bg-background" + /> + + +
+
+ + + + + + + +
+ + + + + + +