From 04c7243994edecbf243e15c14b82d8c2209bb418 Mon Sep 17 00:00:00 2001 From: MananTank Date: Tue, 29 Jul 2025 18:15:59 +0000 Subject: [PATCH] Dashboard: Migrate various components from chakra to tailwind #2 (#7749) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ## PR-Codex overview This PR focuses on refactoring components for better structure and performance, particularly in the forms and tables related to NFTs. It also removes unused components and improves the user experience with updated UI elements and error handling. ### Detailed summary - Removed `FormHelperText` and `FormErrorMessage` from `chakra/form`. - Refactored `NFTSharedMetadataButton` to use a function component instead of a React.FC. - Updated `ContractPermissionsPage` to use a function component and improved prop handling. - Replaced `FormControl` and `FormErrorMessage` with a new form structure in `NFTRevealButton`. - Refactored `SharedMetadataForm` to enhance form handling and error messaging. - Updated `NFTGetAllTable` to streamline pagination and data fetching logic. - Removed unused imports and components to clean up the codebase. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` ## Summary by CodeRabbit * **Refactor** * Updated multiple NFT dashboard components to use a new custom UI library, replacing Chakra UI and simplifying form and table logic. * Improved form validation and error handling with integrated schema validation and toast notifications. * Streamlined table rendering and pagination for NFT listings with a more explicit and user-friendly approach. * Simplified permission and metadata management pages for improved clarity and consistency. * **Chores** * Removed deprecated Chakra UI-based components and helper utilities, including custom link and button components no longer in use. --- .../nfts/components/reveal-button.tsx | 209 ++++--- .../components/shared-metadata-button.tsx | 17 +- .../nfts/components/shared-metadata-form.tsx | 271 +++++---- .../nfts/components/table.tsx | 572 +++++++----------- .../permissions/ContractPermissionsPage.tsx | 79 +-- apps/dashboard/src/chakra/button.tsx | 46 -- apps/dashboard/src/chakra/form.tsx | 23 - apps/dashboard/src/chakra/link.tsx | 58 -- 8 files changed, 536 insertions(+), 739 deletions(-) delete mode 100644 apps/dashboard/src/chakra/link.tsx diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx index 9460ad40433..042b19bce8c 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx @@ -1,7 +1,6 @@ "use client"; -import { FormControl, Input, Select } from "@chakra-ui/react"; -import { FormErrorMessage, FormLabel } from "chakra/form"; +import { zodResolver } from "@hookform/resolvers/zod"; import { EyeIcon } from "lucide-react"; import { useState } from "react"; import { useForm } from "react-hook-form"; @@ -9,9 +8,26 @@ import { toast } from "sonner"; import type { ThirdwebContract } from "thirdweb"; import { getBatchesToReveal, reveal } from "thirdweb/extensions/erc721"; import { useReadContract, useSendAndConfirmTransaction } from "thirdweb/react"; +import * as z from "zod"; import { MinterOnly } from "@/components/contracts/roles/minter-only"; import { TransactionButton } from "@/components/tx-button"; import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { Sheet, SheetContent, @@ -20,29 +36,35 @@ import { SheetTrigger, } from "@/components/ui/sheet"; import { ToolTipLabel } from "@/components/ui/tooltip"; +import { parseError } from "@/utils/errorParser"; -interface NFTRevealButtonProps { - contract: ThirdwebContract; - isLoggedIn: boolean; -} +const revealFormSchema = z.object({ + batchId: z.string().min(1, "Please select a batch"), + password: z.string().min(1, "Password is required"), +}); -const REVEAL_FORM_ID = "reveal-form"; +type RevealFormData = z.infer; -export const NFTRevealButton: React.FC = ({ +export function NFTRevealButton({ contract, isLoggedIn, -}) => { +}: { + contract: ThirdwebContract; + isLoggedIn: boolean; +}) { const batchesQuery = useReadContract(getBatchesToReveal, { contract, }); const sendTxMutation = useSendAndConfirmTransaction(); - const { - register, - handleSubmit, - formState: { errors, isDirty }, - } = useForm<{ batchId: string; password: string }>(); + const form = useForm({ + resolver: zodResolver(revealFormSchema), + defaultValues: { + batchId: "", + password: "", + }, + }); const [open, setOpen] = useState(false); @@ -67,6 +89,27 @@ export const NFTRevealButton: React.FC = ({ ); } + const onSubmit = async (data: RevealFormData) => { + const tx = reveal({ + batchId: BigInt(data.batchId), + contract, + password: data.password, + }); + + await sendTxMutation.mutateAsync(tx, { + onError: (error) => { + toast.error("Failed to reveal batch", { + description: parseError(error), + }); + console.error(error); + }, + onSuccess: () => { + toast.success("Batch revealed successfully"); + setOpen(false); + }, + }); + }; + return ( @@ -75,80 +118,80 @@ export const NFTRevealButton: React.FC = ({ Reveal NFTs - + Reveal batch -
{ - const tx = reveal({ - batchId: BigInt(data.batchId), - contract, - password: data.password, - }); - - const promise = sendTxMutation.mutateAsync(tx, { - onError: (error) => { - console.error(error); - }, - onSuccess: () => { - setOpen(false); - }, - }); - - toast.promise(promise, { - error: "Failed to reveal batch", - loading: "Revealing batch", - success: "Batch revealed successfully", - }); - })} - > - - Select a batch - - {errors?.password?.message} - - - Password - - {errors?.password?.message} - -
-
- +
- Reveal NFTs - -
+ ( + + Select a batch + + + + )} + /> + ( + + Password + + + + + + )} + /> + +
+ + Reveal NFTs + +
+ +
); -}; +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-button.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-button.tsx index c33b4ad1716..c06a3dca958 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-button.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-button.tsx @@ -13,24 +13,23 @@ import { } from "@/components/ui/sheet"; import { SharedMetadataForm } from "./shared-metadata-form"; -interface NFTSharedMetadataButtonProps { +export function NFTSharedMetadataButton({ + contract, + isLoggedIn, +}: { contract: ThirdwebContract; isLoggedIn: boolean; -} - -export const NFTSharedMetadataButton: React.FC< - NFTSharedMetadataButtonProps -> = ({ contract, isLoggedIn, ...restButtonProps }) => { +}) { const [open, setOpen] = useState(false); return ( - - + Set NFT Metadata @@ -43,4 +42,4 @@ export const NFTSharedMetadataButton: React.FC< ); -}; +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx index d3789d3f868..2489756145d 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx @@ -1,19 +1,5 @@ "use client"; -import { - Accordion, - AccordionButton, - AccordionIcon, - AccordionItem, - AccordionPanel, - Divider, - 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, SetStateAction } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -22,30 +8,44 @@ import { setSharedMetadata } from "thirdweb/extensions/erc721"; import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react"; import { FileInput } from "@/components/blocks/FileInput"; import { TransactionButton } from "@/components/tx-button"; -import { useTxNotifications } from "@/hooks/useTxNotifications"; +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 type { NFTMetadataInputLimited } from "@/types/modified-types"; +import { parseError } from "@/utils/errorParser"; import { parseAttributes } from "@/utils/parseAttributes"; import { getUploadedNFTMediaMeta, handleNFTMediaUpload, } from "../../modules/components/nft/handleNFTMediaUpload"; -const SHARED_METADATA_FORM_ID = "shared-metadata-form"; - -export const SharedMetadataForm: React.FC<{ +export function SharedMetadataForm({ + contract, + setOpen, + isLoggedIn, +}: { contract: ThirdwebContract; setOpen: Dispatch>; isLoggedIn: boolean; -}> = ({ contract, setOpen, isLoggedIn }) => { +}) { const address = useActiveAccount()?.address; const sendAndConfirmTx = useSendAndConfirmTransaction(); const form = useForm(); - const { - setValue, - register, - handleSubmit, - formState: { errors, isDirty }, - } = form; const setFile = (file: File) => { handleNFTMediaUpload({ file, form }); @@ -54,17 +54,11 @@ export const SharedMetadataForm: React.FC<{ const { media, image, mediaFileError, showCoverImageUpload, animation_url } = getUploadedNFTMediaMeta(form); - const setSharedMetaNotifications = useTxNotifications( - "Shared metadata updated successfully", - "Failed to update shared metadata", - ); - return ( - <> +
{ + className="mt-4 space-y-6" + onSubmit={form.handleSubmit(async (data) => { if (!address) { toast.error("Please connect your wallet."); return; @@ -76,41 +70,42 @@ export const SharedMetadataForm: React.FC<{ image: data.image, }; - try { - const transaction = setSharedMetadata({ - contract, - nft: parseAttributes(dataWithCustom), - }); - await sendAndConfirmTx.mutateAsync(transaction, { - onError: (error) => { - console.error(error); - }, - onSuccess: () => { - setOpen(false); - }, - }); - - setSharedMetaNotifications.onSuccess(); - } catch (err) { - console.error(err); - setSharedMetaNotifications.onError(err); - } + const transaction = setSharedMetadata({ + contract, + nft: parseAttributes(dataWithCustom), + }); + await sendAndConfirmTx.mutateAsync(transaction, { + onError: (error) => { + toast.error("Failed to update shared metadata", { + description: parseError(error), + }); + }, + onSuccess: () => { + toast.success("Shared metadata updated successfully"); + setOpen(false); + }, + }); })} > -
- Metadata - -
- - Name - - {errors?.name?.message} - - + ( + + Name + + + + + + )} + /> + + Media
- + You can upload image, audio, video, html, text, pdf, and 3d model files here. - - - {mediaFileError?.message as unknown as string} - -
+ + {mediaFileError && ( + {mediaFileError.message} + )} + + {showCoverImageUpload && ( - + Cover Image setValue("image", file)} + setValue={(file) => + form.setValue("image", file, { + shouldValidate: true, + }) + } showUploadButton value={image} /> - + You can optionally upload an image as the cover of your NFT. - - - {errors?.image?.message as unknown as string} - - + + )} - - Description -