diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx
index ed5725e43fa..8ff2d38e9f0 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx
@@ -1,8 +1,6 @@
"use client";
-import { FormControl, Input, Select, Skeleton, Spacer } from "@chakra-ui/react";
import { useMutation, useQuery } from "@tanstack/react-query";
-import { FormErrorMessage, FormLabel } from "chakra/form";
import { useMemo } from "react";
import { FormProvider, type UseFormReturn, useForm } from "react-hook-form";
import { toast } from "sonner";
@@ -26,8 +24,18 @@ import {
toFunctionSelector,
} from "thirdweb/utils";
import type { Account } from "thirdweb/wallets";
+import { FormFieldSetup } from "@/components/blocks/FormFieldSetup";
import { TransactionButton } from "@/components/tx-button";
+import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/Spinner/Spinner";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { Skeleton } from "@/components/ui/skeleton";
import {
useAllVersions,
usePublishedContractsQuery,
@@ -65,7 +73,7 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
version: "latest",
},
});
- const { register, watch, formState, resetField, reset } = form;
+ const { register, watch, formState, resetField, reset, setValue } = form;
const { contract, account } = props;
const { errors } = formState;
@@ -144,10 +152,6 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
installMutation.mutate();
};
- const moduleContractInputProps = register("moduleContract", {
- required: "Module name is required",
- });
-
const selectedModule = modulesOnly?.find(
(x) => x.contractId === watch("moduleContract"),
);
@@ -267,62 +271,65 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
}}
>
-
- Publisher
+
-
- {errors.publisherAddress?.message}
-
-
-
-
+
+
- Module Name
-
+ {!modulesOnly.length && isFetching ? (
+
+ ) : (
-
-
- {!isModuleCompatibleQuery.isFetching &&
- isModuleCompatibleQuery.data === false &&
- "Module is not compatible"}
- {errors.moduleContract?.message}
-
+ )}
{isModuleCompatibleQuery.isFetching && selectedModule && (
@@ -338,13 +345,18 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
)}
-
+
-
- Module Version
-
+
+ {allVersions.isFetching ? (
+
+ ) : (
-
- {errors.version?.message}
-
+ )}
+
{moduleInstallParams.isFetching ? (
-
+
) : (
moduleInstallParams.data &&
!isModuleCompatibleQuery.isFetching &&
@@ -383,7 +403,7 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
)
)}
-
+
{/* Submit */}
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/install-module-params.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/install-module-params.tsx
index 49349dd8d07..cf6dd03a7a9 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/install-module-params.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/install-module-params.tsx
@@ -1,8 +1,7 @@
-import { FormControl } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
-import { FormErrorMessage, FormLabel } from "chakra/form";
import type { ThirdwebClient } from "thirdweb";
import invariant from "tiny-invariant";
+import { FormFieldSetup } from "@/components/blocks/FormFieldSetup";
import { SolidityInput } from "@/components/solidity-inputs";
import { getModuleInstalledParams } from "./getModuleInstalledParams";
import type { InstallModuleForm } from "./ModuleForm";
@@ -48,14 +47,14 @@ export function ModuleInstallParams(props: {
const formFieldKey = `moduleInstallFormParams.${param.name}` as const;
return (
-
- {param.name}
-
- {
- form.getFieldState(formFieldKey, form.formState).error
- ?.message
- }
-
-
+
);
})}
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/burn-tab.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/burn-tab.tsx
index 27349554858..e9b069dd8d1 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/burn-tab.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/burn-tab.tsx
@@ -1,8 +1,5 @@
"use client";
-import { FormControl, Input } from "@chakra-ui/react";
-import { FormErrorMessage, FormHelperText, FormLabel } from "chakra/form";
-import { Text } from "chakra/text";
import { useForm } from "react-hook-form";
import type { ThirdwebContract } from "thirdweb";
import { burn as burn721, isERC721 } from "thirdweb/extensions/erc721";
@@ -14,24 +11,28 @@ import {
useSendAndConfirmTransaction,
} from "thirdweb/react";
import { TransactionButton } from "@/components/tx-button";
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
import { useTxNotifications } from "@/hooks/useTxNotifications";
-interface BurnTabProps {
+type BurnTabProps = {
tokenId: string;
contract: ThirdwebContract;
isLoggedIn: boolean;
-}
+};
-const BurnTab: React.FC = ({ contract, tokenId, isLoggedIn }) => {
+function BurnTab({ contract, tokenId, isLoggedIn }: BurnTabProps) {
const account = useActiveAccount();
- const {
- register,
- handleSubmit,
- watch,
- formState: { errors },
- reset,
- } = useForm<{ to: string; amount: string }>({
+ const form = useForm<{ to: string; amount: string }>({
defaultValues: { amount: "1" },
});
const invalidateContractQuery = useInvalidateContractQuery();
@@ -49,64 +50,72 @@ const BurnTab: React.FC = ({ contract, tokenId, isLoggedIn }) => {
);
const { mutate, isPending } = useSendAndConfirmTransaction();
+ const onSubmit = (data: { to: string; amount: string }) => {
+ const transaction = isErc721
+ ? burn721({ contract, tokenId: BigInt(tokenId) })
+ : burn1155({
+ account: account?.address ?? "",
+ contract,
+ id: BigInt(tokenId),
+ value: BigInt(data.amount),
+ });
+ mutate(transaction, {
+ onError: (error) => {
+ onError(error);
+ },
+ onSuccess: () => {
+ onSuccess();
+ if (contract) {
+ invalidateContractQuery({
+ chainId: contract.chain.id,
+ contractAddress: contract.address,
+ });
+ }
+ form.reset();
+ },
+ });
+ };
+
return (
);
-};
+}
export default BurnTab;
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx
index 6079cd47e40..b990f6c4bae 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx
@@ -1,16 +1,43 @@
"use client";
-import { Flex, FormControl, Input } from "@chakra-ui/react";
-import { FormErrorMessage, FormHelperText, FormLabel } from "chakra/form";
+import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
-import { type ThirdwebContract, ZERO_ADDRESS } from "thirdweb";
+import { isAddress, type ThirdwebContract } from "thirdweb";
import { getApprovalForTransaction } from "thirdweb/extensions/erc20";
import { claimTo } from "thirdweb/extensions/erc1155";
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
+import { z } from "zod";
import { TransactionButton } from "@/components/tx-button";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
import { parseError } from "@/utils/errorParser";
+const claimFormSchema = z.object({
+ to: z
+ .string()
+ .min(1, "To address is required")
+ .refine((val) => {
+ if (isAddress(val)) {
+ return true;
+ }
+ return false;
+ }, "Invalid address"),
+ amount: z.string().refine((value) => {
+ const valueNum = Number(value);
+ return Number.isInteger(valueNum) && valueNum > 0;
+ }, "Amount must be a positive integer"),
+});
+
+type ClaimFormData = z.infer;
+
interface ClaimTabProps {
contract: ThirdwebContract;
tokenId: string;
@@ -23,108 +50,110 @@ const ClaimTabERC1155: React.FC = ({
isLoggedIn,
}) => {
const address = useActiveAccount()?.address;
- const form = useForm<{ to: string; amount: string }>({
+ const form = useForm({
+ resolver: zodResolver(claimFormSchema),
defaultValues: { amount: "1", to: address },
});
const sendAndConfirmTx = useSendAndConfirmTransaction();
const account = useActiveAccount();
+
return (
- {
- if (!account) {
- return toast.error("No account detected");
- }
- try {
- const transaction = claimTo({
- contract,
- from: account.address,
- quantity: BigInt(data.amount),
- to: data.to,
- tokenId: BigInt(tokenId),
- });
- const approveTx = await getApprovalForTransaction({
- account,
- transaction,
- });
- if (approveTx) {
- const approvalPromise = sendAndConfirmTx.mutateAsync(approveTx);
- toast.promise(approvalPromise, {
- error: "Failed to approve ERC20",
- success: "Approved successfully",
+
+
);
};
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx
index 202c9ba60c5..9bee8d76b66 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/transfer-tab.tsx
@@ -1,9 +1,7 @@
"use client";
-import { FormControl, Input } from "@chakra-ui/react";
-import { FormErrorMessage, FormHelperText, FormLabel } from "chakra/form";
import { useForm } from "react-hook-form";
-import { type ThirdwebContract, ZERO_ADDRESS } from "thirdweb";
+import type { ThirdwebContract } from "thirdweb";
import { transferFrom } from "thirdweb/extensions/erc721";
import { isERC1155, safeTransferFrom } from "thirdweb/extensions/erc1155";
import {
@@ -14,19 +12,24 @@ import {
import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage";
import { SolidityInput } from "@/components/solidity-inputs";
import { TransactionButton } from "@/components/tx-button";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
import { useTxNotifications } from "@/hooks/useTxNotifications";
-interface TransferTabProps {
+type TransferTabProps = {
contract: ThirdwebContract;
tokenId: string;
isLoggedIn: boolean;
-}
+};
-const TransferTab: React.FC = ({
- contract,
- tokenId,
- isLoggedIn,
-}) => {
+function TransferTab({ contract, tokenId, isLoggedIn }: TransferTabProps) {
const account = useActiveAccount();
const form = useForm<{ to: string; amount: string }>({
@@ -50,89 +53,90 @@ const TransferTab: React.FC = ({
return ;
}
+ function onSubmit(data: { to: string; amount: string }) {
+ const transaction = isErc1155
+ ? safeTransferFrom({
+ contract,
+ data: "0x",
+ from: account?.address ?? "",
+ to: data.to,
+ tokenId: BigInt(tokenId),
+ value: BigInt(data.amount),
+ })
+ : transferFrom({
+ contract,
+ from: account?.address ?? "",
+ to: data.to,
+ tokenId: BigInt(tokenId),
+ });
+ sendTxAndConfirm.mutate(transaction, {
+ onError: (error) => {
+ onError(error);
+ },
+ onSuccess: () => {
+ onSuccess();
+ form.reset();
+ },
+ });
+ }
+
return (
-
-
-
+
);
-};
+}
export default TransferTab;
diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx
index 4f5681bd7b1..1087e0090db 100644
--- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx
+++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/claim-button.tsx
@@ -1,17 +1,27 @@
"use client";
-import { FormControl, Input } from "@chakra-ui/react";
-import { FormErrorMessage, FormHelperText, FormLabel } from "chakra/form";
+import { zodResolver } from "@hookform/resolvers/zod";
import { GemIcon } from "lucide-react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
-import { isAddress, type ThirdwebContract, ZERO_ADDRESS } from "thirdweb";
+import { isAddress, type ThirdwebContract } from "thirdweb";
import { getApprovalForTransaction } from "thirdweb/extensions/erc20";
import { claimTo } from "thirdweb/extensions/erc721";
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
+import { z } from "zod";
import { TransactionButton } from "@/components/tx-button";
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 {
Sheet,
SheetContent,
@@ -19,9 +29,22 @@ import {
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
-import { useTxNotifications } from "@/hooks/useTxNotifications";
+import { parseError } from "@/utils/errorParser";
+
+const claimFormSchema = z.object({
+ amount: z.string().refine((val) => {
+ const num = Number(val);
+ return Number.isInteger(num) && num > 0;
+ }, "Amount must be a positive integer"),
+ to: z.string().refine((val) => {
+ if (isAddress(val)) {
+ return true;
+ }
+ return false;
+ }, "Invalid address"),
+});
-const CLAIM_FORM_ID = "nft-claim-form";
+type ClaimFormData = z.infer;
interface NFTClaimButtonProps {
contract: ThirdwebContract;
@@ -32,22 +55,63 @@ interface NFTClaimButtonProps {
* This button is used for claiming NFT Drop contract (erc721) only!
* For Edition Drop we have a dedicated ClaimTabERC1155 inside each Edition's page
*/
-export const NFTClaimButton: React.FC = ({
- contract,
- isLoggedIn,
-}) => {
+export function NFTClaimButton({ contract, isLoggedIn }: NFTClaimButtonProps) {
const address = useActiveAccount()?.address;
- const { register, handleSubmit, formState, setValue } = useForm({
+ const form = useForm({
+ resolver: zodResolver(claimFormSchema),
defaultValues: { amount: "1", to: address },
});
- const { errors } = formState;
const sendAndConfirmTx = useSendAndConfirmTransaction();
const account = useActiveAccount();
const [open, setOpen] = useState(false);
- const claimNFTNotifications = useTxNotifications(
- "NFT claimed successfully",
- "Failed to claim NFT",
- );
+
+ async function onSubmit(data: ClaimFormData) {
+ try {
+ if (!account) {
+ return toast.error("No account detected");
+ }
+
+ const transaction = claimTo({
+ contract,
+ from: account.address,
+ quantity: BigInt(data.amount),
+ to: data.to.trim(),
+ });
+
+ const approveTx = await getApprovalForTransaction({
+ account,
+ transaction,
+ });
+
+ if (approveTx) {
+ const approveTxPromise = sendAndConfirmTx.mutateAsync(approveTx, {
+ onError: (error) => {
+ console.error(error);
+ },
+ });
+ toast.promise(approveTxPromise, {
+ error: (err) => ({
+ message: "Failed to approve token",
+ description: parseError(err),
+ }),
+ loading: "Approving ERC20 tokens for this claim",
+ success: "Tokens approved successfully",
+ });
+
+ await approveTxPromise;
+ }
+
+ await sendAndConfirmTx.mutateAsync(transaction);
+
+ toast.success("NFT claimed successfully");
+ setOpen(false);
+ } catch (error) {
+ console.error(error);
+ toast.error("Failed to claim NFT", {
+ description: parseError(error),
+ });
+ }
+ }
return (
@@ -56,120 +120,79 @@ export const NFTClaimButton: React.FC = ({
Claim
-
+
+
Claim NFTs
-
-
- {
- try {
- if (!account) {
- return toast.error("No account detected");
- }
- if (!d.to) {
- return toast.error(
- "Please enter the address that will receive the NFT",
- );
- }
-
- const transaction = claimTo({
- contract,
- from: account.address,
- quantity: BigInt(d.amount),
- to: d.to.trim(),
- });
-
- const approveTx = await getApprovalForTransaction({
- account,
- transaction,
- });
-
- if (approveTx) {
- const promise = sendAndConfirmTx.mutateAsync(approveTx, {
- onError: (error) => {
- console.error(error);
- },
- });
- toast.promise(promise, {
- error: "Failed to approve token",
- loading: "Approving ERC20 tokens for this claim",
- success: "Tokens approved successfully",
- });
-
- await promise;
- }
-
- await sendAndConfirmTx.mutateAsync(transaction, {
- onError: (error) => {
- console.error(error);
- },
- onSuccess: () => {
- setOpen(false);
- },
- });
-
- claimNFTNotifications.onSuccess();
- } catch (error) {
- console.error(error);
- claimNFTNotifications.onError(error);
- }
- })}
- transactionCount={1}
- txChainID={contract.chain.id}
- type="submit"
+
-
+ (
+
+ To Address
+
+ {
+ field.onChange(e.target.value.trim());
+ }}
+ />
+
+
+ Enter the address to claim to.
+
+
+
+ )}
+ />
+ (
+
+ Amount
+
+ {
+ field.onChange(e.target.value.trim());
+ }}
+ />
+
+
+ How many would you like to claim?
+
+
+
+ )}
+ />
+
+
+ {}}
+ transactionCount={1}
+ txChainID={contract.chain.id}
+ type="submit"
+ >
+ Claim NFT
+
+
+
+
);
-};
+}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/partners-table.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/partners-table.tsx
index d95866aa142..5c52011ba4a 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/partners-table.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/components/server/partners-table.tsx
@@ -1,5 +1,5 @@
-import { Link } from "chakra/link";
import { PencilIcon, Trash2Icon } from "lucide-react";
+import Link from "next/link";
import { toast } from "sonner";
import type { Ecosystem, Partner } from "@/api/ecosystems";
import { Button } from "@/components/ui/button";
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx
index 3dfdd2e0f1e..8b5234d31c2 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/components/RecentPaymentsSection.client.tsx
@@ -1,6 +1,5 @@
"use client";
-import { Skeleton } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { ArrowRightIcon, CreditCardIcon } from "lucide-react";
import Link from "next/link";
@@ -12,6 +11,7 @@ import {
} from "@/api/universal-bridge/developer";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
+import { Skeleton } from "@/components/ui/skeleton";
import { TableData, TableHeading, TableHeadingRow } from "./common";
import { EmptyState } from "./EmptyState";
import { TableRow } from "./PaymentsTableRow";