diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 942fb1de5b0..68c347994c1 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -34,7 +34,6 @@ "@vercel/functions": "2.2.2", "@vercel/og": "^0.6.8", "abitype": "1.0.8", - "chakra-react-select": "^4.7.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "color": "5.0.0", diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/abi-selector.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/abi-selector.tsx index 8361fdecfec..6163a74e06e 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/abi-selector.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/abi-selector.tsx @@ -1,20 +1,24 @@ import type { Abi } from "abitype"; -import { Select } from "chakra-react-select"; import { useMemo } from "react"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; -interface AbiSelectorProps { - abi: Abi; - defaultValue: string; - value: string; - onChange: (fn: string) => void; -} - -export const AbiSelector: React.FC = ({ +export function AbiSelector({ abi, value, defaultValue, onChange, -}) => { +}: { + abi: Abi; + defaultValue: string; + value: string; + onChange: (fn: string) => void; +}) { const options = useMemo(() => { return abi .filter((f) => f.type === "function") @@ -26,24 +30,17 @@ export const AbiSelector: React.FC = ({ }, [abi]); return ( -
- + + + + + {options.map((option) => ( + + {option.label} + + ))} + + ); -}; +} diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/contract-params-fieldset.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/contract-params-fieldset.tsx index 8b9204f8887..2dcb4eb10ad 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/contract-params-fieldset.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/contract-params-fieldset.tsx @@ -1,4 +1,3 @@ -import { FormControl, useBreakpointValue } from "@chakra-ui/react"; import type { AbiParameter } from "abitype"; import { useId, useState } from "react"; import { useFormContext } from "react-hook-form"; @@ -12,6 +11,7 @@ import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; +import { useIsMobile } from "@/hooks/use-mobile"; import { getTemplateValuesForType } from "@/lib/deployment/template-values"; import { DecodedInputArrayFieldset } from "./decoded-bytes-input/decoded-input-array-fieldset"; import { RefInputFieldset } from "./ref-contract-input/ref-input-fieldset"; @@ -25,7 +25,7 @@ export const ContractParamsFieldset: React.FC = ({ client, }) => { const form = useFormContext(); - const isMobile = useBreakpointValue({ base: true, md: false }); + const isMobile = useIsMobile(); const displayNameId = useId(); const descriptionId = useId(); const [isCustomInputEnabledArray, setIsCustomInputEnabledArray] = useState( @@ -70,22 +70,20 @@ export const ContractParamsFieldset: React.FC = ({ return (
-

+

Contract Parameters

-

+

These are the parameters users will need to fill in when deploying this contract.

-
-
{deployParams.map((param, idx) => { const paramTemplateValues = getTemplateValuesForType(param.type); return (
{/* Title + Type */} @@ -100,12 +98,12 @@ export const ContractParamsFieldset: React.FC = ({ )}
-
+
{/* Display Name */} = ({ /> -
+
{/* Description */} = ({ {/* Inputs */} {!isCustomInputEnabledArray[idx] ? ( - - - + ) : param.type === "address" || param.type === "address[]" ? ( ) : ( diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/custom-factory.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/custom-factory.tsx index 8a3a6852f69..20f1145f478 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/custom-factory.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/custom-factory.tsx @@ -1,33 +1,26 @@ -import { - Box, - Flex, - FormControl, - IconButton, - Input, - ListItem, - UnorderedList, -} from "@chakra-ui/react"; import type { Abi } from "abitype"; -import { Button } from "chakra/button"; -import { Heading } from "chakra/heading"; -import { Text } from "chakra/text"; import { PlusIcon, TrashIcon } from "lucide-react"; import { type Dispatch, type SetStateAction, useEffect } from "react"; -import { Controller, useFieldArray, useFormContext } from "react-hook-form"; +import { useFieldArray, useFormContext } from "react-hook-form"; import type { ThirdwebClient } from "thirdweb"; import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors"; +import { Button } from "@/components/ui/button"; +import { + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { useCustomFactoryAbi } from "@/hooks/contract-hooks"; import { AbiSelector } from "./abi-selector"; -interface CustomFactoryProps { +export function CustomFactory(props: { setCustomFactoryAbi: Dispatch>; client: ThirdwebClient; -} - -export const CustomFactory: React.FC = ({ - setCustomFactoryAbi, - client, -}) => { +}) { + const { setCustomFactoryAbi, client } = props; const form = useFormContext(); const customFactoryAbi = useCustomFactoryAbi( @@ -35,7 +28,6 @@ export const CustomFactory: React.FC = ({ form.watch("customFactoryAddresses[0].value"), form.watch("customFactoryAddresses[0].key"), ); - // FIXME: all of this logic needs to be reworked // eslint-disable-next-line no-restricted-syntax useEffect(() => { @@ -58,102 +50,130 @@ export const CustomFactory: React.FC = ({ }, [fields, append]); return ( - - - +
+ {/* Description */} +
+

Use this if you want to invoke your own function with custom logic when users deploy your contract. - - +

+ +

You need to have factory contracts pre-deployed to all networks that you want to support. - - - - - Factory contract addresses - - Paste the contract address of your factory contracts. Your contract - can be deployed only to networks with valid factory address. - - - {fields.map((field, index) => ( -

- - - { - return ( - { - _field.onChange(value); - }} +

+
+ + {/* Factory contract addresses */} +
+

+ Factory contract addresses +

+ +

+ Paste the contract address of your factory contracts. Your contract + can be deployed only to networks with valid factory address. +

+ +
+ {fields.map((field, index) => ( +
+ ( + + Network + + + )} + /> + + ( + + Factory contract address + + - ); - }} - /> - - - - - + + + )} + /> + +
- ))} + > + + +
+ ))} +
+
- - - - Factory function - Choose the factory function to deploy your contracts. - - - {customFactoryAbi?.data ? ( - - form.setValue( - "factoryDeploymentData.customFactoryInput.factoryFunction", - selectedFn, - ) - } - value={form.watch( +
+ + {/* Factory function */} +
+

+ Factory function +

+ +

+ Choose the factory function to deploy your contracts. +

+ + {customFactoryAbi?.data ? ( + + form.setValue( "factoryDeploymentData.customFactoryInput.factoryFunction", - )} - /> - ) : ( - - Custom factory ABI not found. Please provide a valid imported - contract on the previous step. - - )} - - - + selectedFn, + ) + } + value={form.watch( + "factoryDeploymentData.customFactoryInput.factoryFunction", + )} + /> + ) : ( +

+ Custom factory ABI not found. Please provide a valid imported + contract on the previous step. +

+ )} +
+
); -}; +} diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/default-factory.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/default-factory.tsx index 5280c8c8f7f..a0be58a7333 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/default-factory.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/default-factory.tsx @@ -1,10 +1,7 @@ -import { Flex, FormControl, ListItem, UnorderedList } from "@chakra-ui/react"; import type { Abi } from "abitype"; -import { Heading } from "chakra/heading"; -import { Link } from "chakra/link"; -import { Text } from "chakra/text"; import { useFormContext } from "react-hook-form"; import type { ThirdwebClient } from "thirdweb"; +import { UnderlineLink } from "../../ui/UnderlineLink"; import { AbiSelector } from "./abi-selector"; import { NetworksFieldset } from "./networks-fieldset"; @@ -13,63 +10,62 @@ interface DefaultFactoryProps { client: ThirdwebClient; } -export const DefaultFactory: React.FC = ({ - abi, - client, -}) => { +export function DefaultFactory({ abi, client }: DefaultFactoryProps) { const form = useFormContext(); return ( - - - +
+ {/* Description */} +
+

Default factory lets users deploy your contract to{" "} - any EVM network. - - + any EVM network. +

+

The factory deploys EIP-1167 minimal proxies of your contract. This makes it much cheaper to deploy. - - +

+

The factory is{" "} - open-source - + , permissionless, and does not alter contract ownership. - - +

+

Your contract needs to be written in the upgradeable/initializable pattern. It needs to contain an initializer function. - - - - - Initializer function - - Choose the initializer function to invoke on your proxy contracts. - - - - - form.setValue( - "factoryDeploymentData.implementationInitializerFunction", - selectedFn, - ) - } - value={form.watch( +

+
+ + {/* Initializer function */} +
+

Initializer function

+

+ Choose the initializer function to invoke on your proxy contracts. +

+ + + form.setValue( "factoryDeploymentData.implementationInitializerFunction", - )} - /> - - + selectedFn, + ) + } + value={form.watch( + "factoryDeploymentData.implementationInitializerFunction", + )} + /> +
+ + {/* Networks */} - +
); -}; +} diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-fieldset.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-fieldset.tsx index 5aa04019775..1b6c1df274f 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-fieldset.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-fieldset.tsx @@ -1,5 +1,4 @@ import { PlusIcon } from "lucide-react"; -import { useEffect } from "react"; import { useFieldArray, useFormContext } from "react-hook-form"; import { Button } from "@/components/ui/button"; import { ExternalLinksInput } from "./external-links-input"; @@ -12,35 +11,26 @@ export const ExternalLinksFieldset = () => { name: "externalLinks", }); - // FIXME: all of this logic needs to be reworked - // eslint-disable-next-line no-restricted-syntax - useEffect(() => { - if (fields.length === 0) { - append( - { - name: "", - url: "", - }, - { shouldFocus: false }, - ); - } - }, [fields, append]); - return ( -
-
-

Resources

-

+

+
+

Resources

+

Provide links to docs, usage guides etc. for the contract.

{fields.map((item, index) => ( - + ))}
diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-input.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-input.tsx index 0c5241653e2..7c9c3af085d 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-input.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/external-links-input.tsx @@ -3,82 +3,82 @@ import { useFormContext } from "react-hook-form"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { Separator } from "@/components/ui/separator"; interface ExternalLinksInputProps { index: number; remove: (index: number) => void; + disableRemove: boolean; } export const ExternalLinksInput: React.FC = ({ index, remove, + disableRemove, }) => { const form = useFormContext(); return ( -
-
-
- - - {form.getFieldState(`externalLinks.${index}.name`, form.formState) - .error?.message && ( -

- { - form.getFieldState( - `externalLinks.${index}.name`, - form.formState, - ).error?.message - } -

- )} -
-
- - { - if (value.match(new RegExp(/^https:\/\/[^\s/$.?#].[^\s]*$/))) { - return true; - } - return "Provide a valid URL"; - }, - })} - /> - {form.getFieldState(`externalLinks.${index}.url`, form.formState) - .error?.message && ( -

- { - form.getFieldState(`externalLinks.${index}.url`, form.formState) - .error?.message +

+ {/* Resource name */} +
+ + + {form.getFieldState(`externalLinks.${index}.name`, form.formState).error + ?.message && ( +

+ { + form.getFieldState(`externalLinks.${index}.name`, form.formState) + .error?.message + } +

+ )} +
+ + {/* Resource URL */} +
+ + { + if (value.match(new RegExp(/^https:\/\/[^\s/$.?#].[^\s]*$/))) { + return true; } -

- )} -
- + return "Provide a valid URL"; + }, + })} + /> + {form.getFieldState(`externalLinks.${index}.url`, form.formState).error + ?.message && ( +

+ { + form.getFieldState(`externalLinks.${index}.url`, form.formState) + .error?.message + } +

+ )}
- + +
); }; diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/factory-fieldset.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/factory-fieldset.tsx index 33a040c06f0..61ff780938e 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/factory-fieldset.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/factory-fieldset.tsx @@ -1,10 +1,8 @@ -import { ButtonGroup, Flex } from "@chakra-ui/react"; import type { Abi } from "abitype"; -import { Button } from "chakra/button"; -import { Heading } from "chakra/heading"; import type { Dispatch, SetStateAction } from "react"; import { useFormContext } from "react-hook-form"; import type { ThirdwebClient } from "thirdweb"; +import { TabButtons } from "../../ui/tabs"; import { CustomFactory } from "./custom-factory"; import { DefaultFactory } from "./default-factory"; @@ -20,47 +18,45 @@ export const FactoryFieldset: React.FC = ({ client, }) => { const form = useFormContext(); + const tab = form.watch("deployType"); return ( - - - Factory deploy settings - - - - - {form.watch("deployType") === "autoFactory" && ( - - )} - {form.watch("deployType") === "customFactory" && ( - - )} - - +
+

+ Factory deploy settings +

+

+ Choose how you want to deploy your contract +

+ + form.setValue("deployType", "autoFactory"), + isActive: tab === "autoFactory", + }, + { + name: "Custom Factory (Advanced)", + onClick: () => form.setValue("deployType", "customFactory"), + isActive: tab === "customFactory", + }, + ]} + /> + +
+ + {form.watch("deployType") === "autoFactory" && ( + + )} + + {form.watch("deployType") === "customFactory" && ( + + )} +
); }; diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/impl-params-fieldset.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/impl-params-fieldset.tsx index 743bd80cd1d..f17a3409cd5 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/impl-params-fieldset.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/impl-params-fieldset.tsx @@ -1,4 +1,3 @@ -import { FormControl, useBreakpointValue } from "@chakra-ui/react"; import type { AbiParameter } from "abitype"; import { useState } from "react"; import { useFormContext } from "react-hook-form"; @@ -7,6 +6,7 @@ import { SolidityInput } from "@/components/solidity-inputs"; import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox"; import { InlineCode } from "@/components/ui/inline-code"; import { Switch } from "@/components/ui/switch"; +import { useIsMobile } from "@/hooks/use-mobile"; import { getTemplateValuesForType } from "@/lib/deployment/template-values"; import { RefInputImplFieldset } from "./ref-contract-impl-input/ref-input-impl-fieldset"; @@ -19,7 +19,7 @@ export const ImplementationParamsFieldset: React.FC< > = ({ implParams, client }) => { const form = useFormContext(); - const isMobile = useBreakpointValue({ base: true, md: false }); + const isMobile = useIsMobile(); const [isCustomInputEnabled, setIsCustomInputEnabled] = useState( Array(implParams.length).fill(false), @@ -119,25 +119,23 @@ export const ImplementationParamsFieldset: React.FC<
{!isCustomInputEnabled[idx] ? ( - - - + ) : ( )} diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/index.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/index.tsx index e1fd22191be..3414ce1ec0a 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/index.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/index.tsx @@ -1,9 +1,6 @@ "use client"; -import { Box, Divider, Flex, IconButton } from "@chakra-ui/react"; import type { Abi } from "abitype"; -import { Button } from "chakra/button"; -import { Text } from "chakra/text"; -import { ChevronFirstIcon } from "lucide-react"; +import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"; import { useId, useMemo, useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import type { ThirdwebClient } from "thirdweb"; @@ -15,6 +12,8 @@ import { import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react"; import { reportContractPublished } from "@/analytics/report"; import { CustomConnectWallet } from "@/components/connect-wallet"; +import { Button } from "@/components/ui/button"; +import { Spinner } from "@/components/ui/Spinner/Spinner"; import { DASHBOARD_ENGINE_RELAYER_URL, DASHBOARD_FORWARDER_ADDRESS, @@ -22,7 +21,6 @@ import { import { useEns, useFunctionParamsFromABI } from "@/hooks/contract-hooks"; import { useTxNotifications } from "@/hooks/useTxNotifications"; import { useDashboardRouter } from "@/lib/DashboardRouter"; -import { useIsomorphicLayoutEffect } from "@/lib/useIsomorphicLayoutEffect"; import { ContractParamsFieldset } from "./contract-params-fieldset"; import { FactoryFieldset } from "./factory-fieldset"; import { ImplementationParamsFieldset } from "./impl-params-fieldset"; @@ -183,284 +181,238 @@ export function ContractPublishForm(props: { // during loading and after success we should stay in loading state const isPending = sendTx.isPending || sendTx.isSuccess; - useIsomorphicLayoutEffect(() => { - window?.scrollTo({ - behavior: "smooth", - top: 0, - }); - }, []); - const formId = useId(); return ( -
- { - if (!account) { - // no account, do nothing - return; - } - const addressArray = - Object.keys(data?.customFactoryAddresses || {}).length > 0 - ? (data?.customFactoryAddresses as unknown as { - key: number; - value: string; - }[]) - : []; +
{ + if (!account) { + // no account, do nothing + return; + } + const addressArray = + Object.keys(data?.customFactoryAddresses || {}).length > 0 + ? (data?.customFactoryAddresses as unknown as { + key: number; + value: string; + }[]) + : []; - const addressObj = addressArray.reduce>( - (obj, item) => { - obj[item.key] = item.value; - return obj; - }, - {}, - ); + const addressObj = addressArray.reduce>( + (obj, item) => { + obj[item.key] = item.value; + return obj; + }, + {}, + ); - const metadata = { - ...data, - factoryDeploymentData: { - ...data.factoryDeploymentData, - customFactoryInput: { - customFactoryAddresses: - addressObj || - data.factoryDeploymentData?.customFactoryInput - ?.customFactoryAddresses, - factoryFunction: - data.factoryDeploymentData?.customFactoryInput - ?.factoryFunction || "deployProxyByImplementation", - params: - data.factoryDeploymentData?.customFactoryInput?.params || - [], - }, - implementationAddresses: - data.factoryDeploymentData?.implementationAddresses || {}, - implementationInitializerFunction: - data.factoryDeploymentData - ?.implementationInitializerFunction || "initialize", - }, - networksForDeployment: { - allNetworks: - data.deployType === "customFactory" - ? false - : data.networksForDeployment?.allNetworks, - networksEnabled: - addressArray.length > 0 - ? Object.keys(addressObj).map((val) => Number(val)) - : data.networksForDeployment?.networksEnabled, + const metadata = { + ...data, + factoryDeploymentData: { + ...data.factoryDeploymentData, + customFactoryInput: { + customFactoryAddresses: + addressObj || + data.factoryDeploymentData?.customFactoryInput + ?.customFactoryAddresses, + factoryFunction: + data.factoryDeploymentData?.customFactoryInput + ?.factoryFunction || "deployProxyByImplementation", + params: + data.factoryDeploymentData?.customFactoryInput?.params || [], }, - } satisfies FetchDeployMetadataResult & { - name: string; - version: string; - }; + implementationAddresses: + data.factoryDeploymentData?.implementationAddresses || {}, + implementationInitializerFunction: + data.factoryDeploymentData?.implementationInitializerFunction || + "initialize", + }, + networksForDeployment: { + allNetworks: + data.deployType === "customFactory" + ? false + : data.networksForDeployment?.allNetworks, + networksEnabled: + addressArray.length > 0 + ? Object.keys(addressObj).map((val) => Number(val)) + : data.networksForDeployment?.networksEnabled, + }, + } satisfies FetchDeployMetadataResult & { + name: string; + version: string; + }; - const tx = publishContract({ - account, - contract: getContractPublisher(props.client), - metadata, - previousMetadata: props.publishMetadata, - }); + const tx = publishContract({ + account, + contract: getContractPublisher(props.client), + metadata, + previousMetadata: props.publishMetadata, + }); - sendTx.mutate(tx, { - onError: (err) => { - console.error("Failed to publish contract", err); - onError(err); - }, - onSuccess: async () => { - onSuccess(); + sendTx.mutate(tx, { + onError: (err) => { + console.error("Failed to publish contract", err); + onError(err); + }, + onSuccess: async () => { + onSuccess(); - reportContractPublished({ - contractName: props.publishMetadata.name, - deployType: data.deployType, - publisher: ensNameOrAddress ?? "", - version: data.version, - }); - await props.onPublishSuccess().catch((err) => { - console.error("Failed to run onPublishSuccess", err); - }); - if (successRedirectUrl) { - router.push(successRedirectUrl, { scroll: true }); - } - }, - }); - })} - > - {fieldsetToShow !== "landing" && ( -
- } - onClick={() => - fieldsetToShow === "contractParams" && - (form.watch("deployType") === "autoFactory" || - form.watch("deployType") === "customFactory") - ? setFieldsetToShow("factory") - : fieldsetToShow === "contractParams" && - form.watch("deployType") === "standard" - ? setFieldsetToShow("networks") - : fieldsetToShow === "implParams" - ? setFieldsetToShow("contractParams") - : setFieldsetToShow("landing") - } - variant="ghost" - w="inherit" - > - Back - -
- )} + reportContractPublished({ + contractName: props.publishMetadata.name, + deployType: data.deployType, + publisher: ensNameOrAddress ?? "", + version: data.version, + }); + await props.onPublishSuccess().catch((err) => { + console.error("Failed to run onPublishSuccess", err); + }); + if (successRedirectUrl) { + router.push(successRedirectUrl, { scroll: true }); + } + }, + }); + })} + > + {fieldsetToShow !== "landing" && ( +
+ +
+ )} - {fieldsetToShow === "landing" && ( - - )} + {fieldsetToShow === "landing" && ( + + )} - {fieldsetToShow === "contractParams" && ( - + )} + + {fieldsetToShow === "implParams" && implDeployParams?.length > 0 && ( + + )} + + {fieldsetToShow === "factory" && ( +
+ - )} +
+ )} + + {fieldsetToShow === "networks" && ( + + )} - {fieldsetToShow === "implParams" && implDeployParams?.length > 0 && ( - + {!account ? ( + - )} - - {fieldsetToShow === "factory" && ( - - - - )} + ) : fieldsetToShow === "landing" && + form.watch("deployType") === "standard" ? ( + setFieldsetToShow("networks")} + /> + ) : fieldsetToShow === "landing" && + (form.watch("deployType") === "autoFactory" || + form.watch("deployType") === "customFactory") ? ( + setFieldsetToShow("factory")} + /> + ) : fieldsetToShow !== "contractParams" && + fieldsetToShow !== "implParams" && + deployParams?.length > 0 ? ( + setFieldsetToShow("contractParams")} + /> + ) : fieldsetToShow !== "contractParams" && + fieldsetToShow !== "implParams" && + deployParams?.length === 0 && + implDeployParams?.length > 0 ? ( + setFieldsetToShow("implParams")} + /> + ) : fieldsetToShow === "contractParams" && + implDeployParams?.length > 0 ? ( + setFieldsetToShow("implParams")} + /> + ) : ( +
+ + Publishing your contract is free, we cover all gas costs + - {fieldsetToShow === "networks" && ( - - - + +
)} - - - - - {!account ? ( - <> - - - - ) : fieldsetToShow === "landing" && - form.watch("deployType") === "standard" ? ( - <> - - - - ) : fieldsetToShow === "landing" && - (form.watch("deployType") === "autoFactory" || - form.watch("deployType") === "customFactory") ? ( - <> - - - - ) : fieldsetToShow !== "contractParams" && - fieldsetToShow !== "implParams" && - deployParams?.length > 0 ? ( - <> - - - - ) : fieldsetToShow !== "contractParams" && - fieldsetToShow !== "implParams" && - deployParams?.length === 0 && - implDeployParams?.length > 0 ? ( - <> - - - - ) : fieldsetToShow === "contractParams" && - implDeployParams?.length > 0 ? ( - <> - - - - ) : ( - <> - - Publishing your contract is free, we cover all gas costs. - - - - )} - - - -
+
+ ); } + +function NextButton(props: { disabled: boolean; onClick: () => void }) { + return ( + + ); +} diff --git a/apps/dashboard/src/@/components/contract-components/contract-publish-form/landing-fieldset.tsx b/apps/dashboard/src/@/components/contract-components/contract-publish-form/landing-fieldset.tsx index 37e735f620e..2a18ba94336 100644 --- a/apps/dashboard/src/@/components/contract-components/contract-publish-form/landing-fieldset.tsx +++ b/apps/dashboard/src/@/components/contract-components/contract-publish-form/landing-fieldset.tsx @@ -1,41 +1,30 @@ -import { - Box, - Flex, - FormControl, - Input, - Tab, - TabList, - TabPanel, - TabPanels, - Tabs, - Textarea, -} from "@chakra-ui/react"; -import { Card } from "chakra/card"; -import { FormErrorMessage, FormHelperText, FormLabel } from "chakra/form"; -import { Heading } from "chakra/heading"; -import { Link } from "chakra/link"; -import { Text } from "chakra/text"; import { compare, validate } from "compare-versions"; +import { useState } from "react"; import { useFormContext } from "react-hook-form"; import type { ThirdwebClient } from "thirdweb"; import type { ExtendedMetadata } from "thirdweb/utils"; import { SelectOption } from "@/components/batch-upload/lazy-mint-form/select-option"; import { FileInput } from "@/components/blocks/FileInput"; +import { FormFieldSetup } from "@/components/blocks/FormFieldSetup"; import { SolidityInput } from "@/components/solidity-inputs"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; import { MarkdownRenderer } from "../../blocks/markdown-renderer"; +import { TabButtons } from "../../ui/tabs"; +import { UnderlineLink } from "../../ui/UnderlineLink"; import { ExternalLinksFieldset } from "./external-links-fieldset"; -interface LandingFieldsetProps { +type LandingFieldsetProps = { latestVersion: string | undefined; placeholderVersion: string; client: ThirdwebClient; -} +}; -export const LandingFieldset: React.FC = ({ +export function LandingFieldset({ latestVersion, placeholderVersion, client, -}) => { +}: LandingFieldsetProps) { const form = useFormContext(); const logoUrl = form.watch("logo"); @@ -66,205 +55,235 @@ export const LandingFieldset: React.FC = ({ } }; + const [readmeTab, setReadmeTab] = useState<"write" | "preview">("write"); + const [changelogTab, setChangelogTab] = useState<"write" | "preview">( + "write", + ); + return ( - - - +
+ {/* header */} +
+

{!latestVersion ? "Publish" : "Edit"} your contract - - +

+

Publishing your contract makes it shareable, discoverable, and deployable in a single click.{" "} - Learn more - - - - - - Image - + +

+
+ + {/* logo, name, description */} +
+ +
form.setValue("logo", file)} value={logoUrl} /> - - - {form.formState.errors?.logo?.message as unknown as string} - - - - + + +
+ - Contract Name form.setValue("displayName", e.target.value)} placeholder="Ex. MyContract" + className="bg-card" value={form.watch("displayName")} /> - - {form.formState.errors?.displayName?.message} - - - - Description + + +