diff --git a/apps/dashboard/src/@/components/contracts/properties.shared.tsx b/apps/dashboard/src/@/components/contracts/properties.shared.tsx index f2a3bbcd0b4..f1e470a3583 100644 --- a/apps/dashboard/src/@/components/contracts/properties.shared.tsx +++ b/apps/dashboard/src/@/components/contracts/properties.shared.tsx @@ -1,15 +1,4 @@ -import { - Flex, - FormControl, - IconButton, - Input, - InputGroup, - InputRightElement, - Tooltip, -} from "@chakra-ui/react"; -import { Button } from "chakra/button"; -import { FormErrorMessage, FormLabel } from "chakra/form"; -import { BanIcon, PlusIcon, TrashIcon, UploadIcon, XIcon } from "lucide-react"; +import { PlusIcon, TrashIcon, XIcon } from "lucide-react"; import { useEffect } from "react"; import { type ArrayPath, @@ -25,7 +14,16 @@ import { type WatchObserver, } from "react-hook-form"; import type { ThirdwebClient } from "thirdweb"; -import { FileInput } from "@/components/blocks/FileInput"; +import { Button } from "@/components/ui/button"; +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; type OptionalPropertiesInput = { [key: string]: string | number; @@ -46,17 +44,15 @@ interface IPropertiesFormControlProps< client: ThirdwebClient; } -export const PropertiesFormControl = < +export function PropertiesFormControl< TFieldValues extends IPropertyFieldValues, >({ control, - register, watch, errors, setValue, - client, -}: React.PropsWithChildren>) => { - const { fields, append, remove, replace } = useFieldArray({ +}: React.PropsWithChildren>) { + const { fields, append, remove } = useFieldArray({ control, name: "attributes" as ArrayPath, }); @@ -72,19 +68,7 @@ export const PropertiesFormControl = < return (
- - Attributes - - + Attributes {fields.map((field, index) => { // biome-ignore lint/suspicious/noExplicitAny: FIXME const keyError = (errors as any)?.attributes?.[index]?.trait_type @@ -92,102 +76,108 @@ export const PropertiesFormControl = < // biome-ignore lint/suspicious/noExplicitAny: FIXME const valueError = (errors as any)?.attributes?.[index]?.value ?.message as string | undefined; - const isInvalid = !!(keyError || valueError); + const _isInvalid = !!(keyError || valueError); return (
- - - , - )} - placeholder="trait_type" - /> - {keyError} - - - {watch( - `attributes.${index}.value` as unknown as WatchObserver, - ) instanceof File ? ( - - ) - .name - } - /> - - - setValue( - `attributes.${index}.value` as Path, - "" as PathValue>, - ) - } +
+ } + render={({ field: traitField }) => ( + + + - - - ) : ( - - , + + {keyError && {keyError}} + + )} + /> + + } + render={({ field: valueField }) => ( + + + {watch( + `attributes.${index}.value` as unknown as WatchObserver, + ) instanceof File ? ( +
+ , + ).name + } + className="pr-10 bg-card" + /> + +
+ ) : ( +
+ +
)} - placeholder="value" - /> - - - { - setValue( - `attributes.${index}.value` as Path, - file as PathValue< - TFieldValues, - Path - >, - ); - }} - > - - - - - +
+ {valueError && {valueError}} +
)} - {valueError} - - - } + /> +
+
); })}
); -}; +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx index 14cb097285f..a37d86a72ca 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx @@ -1,18 +1,5 @@ "use client"; -import { - Accordion, - AccordionButton, - AccordionIcon, - AccordionItem, - AccordionPanel, - FormControl, - Input, - Textarea, -} from "@chakra-ui/react"; -import { Button } from "chakra/button"; -import { FormErrorMessage, FormHelperText, FormLabel } from "chakra/form"; -import { Heading } from "chakra/heading"; import { type Dispatch, type SetStateAction, useMemo } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -31,6 +18,24 @@ import { OpenSeaPropertyBadge } from "@/components/badges/opensea"; import { FileInput } from "@/components/blocks/FileInput"; import { PropertiesFormControl } from "@/components/contracts/properties.shared"; import { TransactionButton } from "@/components/tx-button"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; import { useTxNotifications } from "@/hooks/useTxNotifications"; import type { NFTMetadataInputLimited } from "@/types/modified-types"; import { parseAttributes } from "@/utils/parseAttributes"; @@ -53,13 +58,13 @@ type UpdateNftMetadataForm = { isLoggedIn: boolean; }; -export const UpdateNftMetadata: React.FC = ({ +export function UpdateNftMetadata({ contract, nft, useUpdateMetadata, setOpen, isLoggedIn, -}) => { +}: UpdateNftMetadataForm) { const address = useActiveAccount()?.address; const transformedQueryData = useMemo(() => { @@ -90,7 +95,7 @@ export const UpdateNftMetadata: React.FC = ({ register, watch, handleSubmit, - formState: { errors, isDirty }, + formState: { errors }, } = form; const setFile = (file: File) => { @@ -112,229 +117,304 @@ export const UpdateNftMetadata: React.FC = ({ ); return ( -
{ - if (!address) { - toast.error("Please connect your wallet to update metadata."); - return; - } + + { + if (!address) { + toast.error("Please connect your wallet to update metadata."); + return; + } - try { - const newMetadata = parseAttributes({ - ...data, - animation_url: data.animation_url || nft.metadata.animation_url, - image: data.image || nft.metadata.image, - }); + try { + const newMetadata = parseAttributes({ + ...data, + animation_url: data.animation_url || nft.metadata.animation_url, + image: data.image || nft.metadata.image, + }); - const transaction = useUpdateMetadata - ? // For Drop contracts, we need to call the `updateBatchBaseURI` method - nft.type === "ERC721" - ? updateMetadata721({ - contract, - newMetadata, - targetTokenId: BigInt(nft.id), - }) - : updateMetadata1155({ - contract, - newMetadata, - targetTokenId: BigInt(nft.id), - }) - : // For Collection contracts, we need to call the `setTokenURI` method - nft.type === "ERC721" - ? updateTokenURI721({ - contract, - newMetadata, - tokenId: BigInt(nft.id), - }) - : updateTokenURI1155({ - contract, - newMetadata, - tokenId: BigInt(nft.id), - }); - await sendAndConfirmTx.mutateAsync(transaction, { - onError: (error) => { - console.error(error); - }, - onSuccess: () => { - setOpen(false); - }, - }); + const transaction = useUpdateMetadata + ? // For Drop contracts, we need to call the `updateBatchBaseURI` method + nft.type === "ERC721" + ? updateMetadata721({ + contract, + newMetadata, + targetTokenId: BigInt(nft.id), + }) + : updateMetadata1155({ + contract, + newMetadata, + targetTokenId: BigInt(nft.id), + }) + : // For Collection contracts, we need to call the `setTokenURI` method + nft.type === "ERC721" + ? updateTokenURI721({ + contract, + newMetadata, + tokenId: BigInt(nft.id), + }) + : updateTokenURI1155({ + contract, + newMetadata, + tokenId: BigInt(nft.id), + }); + await sendAndConfirmTx.mutateAsync(transaction, { + onError: (error) => { + console.error(error); + }, + onSuccess: () => { + setOpen(false); + }, + }); - updateMetadataNotifications.onSuccess(); - } catch (err) { - console.error(err); - updateMetadataNotifications.onError(err); - } - })} - > - - Name - - {errors?.name?.message} - + updateMetadataNotifications.onSuccess(); + } catch (err) { + console.error(err); + updateMetadataNotifications.onError(err); + } + })} + > + ( + + Name + + + + + + )} + /> - - Media -
- + Media +
+ +
+ + You can upload image, audio, video, html, text, pdf, and 3d model + files here. + + {mediaFileError && ( + + {mediaFileError.message as unknown as string} + + )} + + + {showCoverImageUpload && ( + ( + + Cover Image + + { + setValue("image", file); + }} + showUploadButton + value={image} + /> + + + You can optionally upload an image as the cover of your NFT. + + + + )} /> -
+ )} - - You can upload image, audio, video, html, text, pdf, and 3d model - files here. - - - {mediaFileError?.message as unknown as string} - -
+ ( + + Description + +