From 66a93b03989277beae1005ad67fecd45f4665c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Fri, 15 Mar 2024 16:02:14 +0100 Subject: [PATCH 01/32] refactor step1 and step2 for drep registration --- .../CreateGovernanceActionForm.tsx | 11 +- .../RegisterAsDRepForm.tsx | 160 ++++++++++++++++++ .../RolesAndResponsibilities.tsx} | 42 +++-- .../organisms/RegisterAsDRepSteps/index.ts | 2 + .../organisms/RegisterAsdRepStepThree.tsx | 14 +- .../organisms/RegisterAsdRepStepTwo.tsx | 85 ---------- .../src/components/organisms/index.ts | 3 +- govtool/frontend/src/hooks/forms/index.ts | 2 +- .../src/hooks/forms/useRegisterAsdRepForm.tsx | 54 ++++++ .../forms/useRegisterAsdRepFormContext.tsx | 107 ------------ govtool/frontend/src/i18n/locales/en.ts | 23 ++- govtool/frontend/src/pages/RegisterAsdRep.tsx | 29 ++-- 12 files changed, 283 insertions(+), 249 deletions(-) create mode 100644 govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx rename govtool/frontend/src/components/organisms/{RegisterAsdRepStepOne.tsx => RegisterAsDRepSteps/RolesAndResponsibilities.tsx} (51%) create mode 100644 govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts delete mode 100644 govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx create mode 100644 govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx delete mode 100644 govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx index 65c868313..ab0b8c695 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/CreateGovernanceActionForm.tsx @@ -2,9 +2,7 @@ import { Dispatch, SetStateAction, useCallback } from 'react'; import { useFieldArray } from 'react-hook-form'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; -import { - Button, InfoText, Spacer, Typography -} from '@atoms'; +import { Button, InfoText, Spacer, Typography } from '@atoms'; import { GOVERNANCE_ACTION_FIELDS } from '@consts'; import { useCreateGovernanceActionForm, useTranslation } from '@hooks'; import { Field } from '@molecules'; @@ -14,7 +12,6 @@ import { GovernanceActionField } from '@/types/governanceAction'; import { BgCard } from '../BgCard'; import { ControlledField } from '../ControlledField'; -const LINK_PLACEHOLDER = 'https://website.com/'; const MAX_NUMBER_OF_LINKS = 8; type CreateGovernanceActionFormProps = { @@ -25,9 +22,7 @@ export const CreateGovernanceActionForm = ({ setStep, }: CreateGovernanceActionFormProps) => { const { t } = useTranslation(); - const { - control, errors, getValues, register, reset, watch - } = + const { control, errors, getValues, register, reset, watch } = useCreateGovernanceActionForm(); const isError = Object.keys(errors).length > 0; @@ -112,7 +107,7 @@ export const CreateGovernanceActionForm = ({ key={field.id} label={`${t('forms.link')} ${index + 1}`} layoutStyles={{ mb: 3 }} - placeholder={LINK_PLACEHOLDER} + placeholder={t('forms.linkPlaceholder')} name={`links.${index}.link`} rules={{ pattern: { diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx new file mode 100644 index 000000000..afebab1e0 --- /dev/null +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx @@ -0,0 +1,160 @@ +import { Dispatch, SetStateAction, useCallback } from "react"; +import { useFieldArray } from "react-hook-form"; +import { Box } from "@mui/material"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; + +import { Button, InfoText, Spacer, Typography } from "@atoms"; +import { + useRegisterAsdRepForm, + useScreenDimension, + useTranslation, +} from "@hooks"; +import { URL_REGEX } from "@utils"; + +import { BgCard, ControlledField } from ".."; + +const MAX_NUMBER_OF_LINKS = 8; + +export const RegisterAsDRepForm = ({ + setStep, +}: { + setStep: Dispatch>; +}) => { + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + const { control, errors, register, watch } = useRegisterAsdRepForm(); + const { + append, + fields: links, + remove, + } = useFieldArray({ + control, + name: "links", + }); + + const onClickContinue = () => { + setStep(3); + }; + + const onClickBack = () => { + setStep(1); + }; + + const addLink = useCallback(() => { + append({ link: "" }); + }, [append]); + + const removeLink = useCallback( + (index: number) => { + remove(index); + }, + [remove] + ); + + const isContinueButtonDisabled = !watch("dRepName"); + + const renderLinks = useCallback(() => { + return links.map((field, index) => { + return ( + 1 ? ( + removeLink(index)} + /> + ) : null + } + key={field.id} + label={t("forms.link") + ` ${index + 1}`} + layoutStyles={{ mb: 3 }} + placeholder={t("forms.linkPlaceholder")} + name={`links.${index}.link`} + rules={{ + pattern: { + value: URL_REGEX, + message: t("createGovernanceAction.fields.validations.url"), + }, + }} + /> + ); + }); + }, [links]); + + return ( + + + + + {t("registration.dRepName")} + + + {t("registration.dRepNameDescription")} + + + + + + + + {t("registration.aboutYou")} + + + {t("registration.aboutYouDescription")} + + + + + + +

+ {t("registration.linksDescription")} + + {t("registration.maximumLinks")} + +

+ + {renderLinks()} + {links?.length < MAX_NUMBER_OF_LINKS ? ( + + ) : null} + +
+ ); +}; diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx similarity index 51% rename from govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx rename to govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx index e1fde2b6a..95f8bbea2 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepOne.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx @@ -1,19 +1,19 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; -import { Trans } from "react-i18next"; -import { Link } from "@mui/material"; +import { Dispatch, SetStateAction } from 'react'; +import { Trans } from 'react-i18next'; +import { Link } from '@mui/material'; -import { Typography } from "@atoms"; -import { useScreenDimension, useTranslation } from "@hooks"; +import { Typography } from '@atoms'; +import { useScreenDimension, useTranslation } from '@hooks'; import { correctAdaFormat, getItemFromLocalStorage, openInNewTab, PROTOCOL_PARAMS_KEY, -} from "@utils"; +} from '@utils'; -import { BgCard } from "."; +import { BgCard } from '..'; -export const RegisterAsdRepStepOne = ({ +export const RolesAndResponsibilities = ({ onClickCancel, setStep, }: { @@ -25,31 +25,29 @@ export const RegisterAsdRepStepOne = ({ const deposit = getItemFromLocalStorage(PROTOCOL_PARAMS_KEY); - const onClickContinue = useCallback(() => setStep(2), []); + const onClickContinue = () => setStep(2); - const openLearnMoreAboutDrep = useCallback( - () => openInNewTab("https://sancho.network/roles/drep"), - [], - ); + const openLearnMoreAboutDrep = () => + openInNewTab('https://sancho.network/roles/drep'); return ( - - {t("registration.rolesAndResponsibilitiesTitle")} + + {t('registration.rolesAndResponsibilitiesTitle')} @@ -58,7 +56,7 @@ export const RegisterAsdRepStepOne = ({ , ]} i18nKey="registration.rolesAndResponsibilitiesDescription" diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts new file mode 100644 index 000000000..c09542813 --- /dev/null +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts @@ -0,0 +1,2 @@ +export * from "./RolesAndResponsibilities"; +export * from "./RegisterAsDRepForm"; diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx b/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx index 6b5a3df4d..ef29526f1 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsdRepStepThree.tsx @@ -3,9 +3,9 @@ import { Box, Link } from "@mui/material"; import { Spacer, Typography } from "@atoms"; import { - useRegisterAsdRepFormContext, useScreenDimension, useTranslation, + useRegisterAsdRepForm, } from "@hooks"; import { openInNewTab } from "@utils"; @@ -18,14 +18,8 @@ export const RegisterAsdRepStepThree = ({ }) => { const { t } = useTranslation(); const { isMobile } = useScreenDimension(); - const { - control, - errors, - isRegistrationAsDRepLoading, - resetField, - submitForm, - watch, - } = useRegisterAsdRepFormContext(); + const { control, errors, isRegistrationAsDRepLoading, resetField, watch } = + useRegisterAsdRepForm(); const onClickBackButton = () => { setStep(2); @@ -42,7 +36,7 @@ export const RegisterAsdRepStepThree = ({ actionButtonLabel={t("register")} isActionButtonDisabled={isContinueDisabled} isLoadingActionButton={isRegistrationAsDRepLoading} - onClickActionButton={submitForm} + onClickActionButton={() => {}} onClickBackButton={onClickBackButton} > diff --git a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx b/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx deleted file mode 100644 index a9766dcfb..000000000 --- a/govtool/frontend/src/components/organisms/RegisterAsdRepStepTwo.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; -import { Box, Link } from "@mui/material"; - -import { Spacer, Typography } from "@atoms"; -import { - useScreenDimension, - useRegisterAsdRepFormContext, - useTranslation, -} from "@hooks"; -import { openInNewTab } from "@utils"; - -import { BgCard, ControlledField } from "."; - -interface Props { - setStep: Dispatch>; -} - -export const RegisterAsdRepStepTwo = ({ setStep }: Props) => { - const { t } = useTranslation(); - const { isMobile } = useScreenDimension(); - const { - control, errors, isContinueButtonDisabled, isSkipButton, - } = useRegisterAsdRepFormContext(); - - const onClickContinue = useCallback(() => setStep(3), []); - - const onClickBackButton = useCallback(() => setStep(1), []); - - return ( - - - {t("registration.optional")} - - - {t("registration.addInformationTitle")} - - - {t("registration.addInformationDescription")} - - - - - - openInNewTab( - "https://docs.sanchogov.tools/faqs/how-to-create-a-metadata-anchor", - )} - alignSelf="center" - my={5} - sx={{ cursor: "pointer" }} - > - - {t("forms.howCreateUrlAndHash")} - - - - - ); -}; diff --git a/govtool/frontend/src/components/organisms/index.ts b/govtool/frontend/src/components/organisms/index.ts index 57b806873..2bdfd5de0 100644 --- a/govtool/frontend/src/components/organisms/index.ts +++ b/govtool/frontend/src/components/organisms/index.ts @@ -20,9 +20,8 @@ export * from "./GovernanceActionDetailsCard"; export * from "./GovernanceActionsToVote"; export * from "./Hero"; export * from "./HomeCards"; -export * from "./RegisterAsdRepStepOne"; +export * from "./RegisterAsDRepSteps"; export * from "./RegisterAsdRepStepThree"; -export * from "./RegisterAsdRepStepTwo"; export * from "./RegisterAsSoleVoterBox"; export * from "./RegisterAsSoleVoterBoxContent"; export * from "./RetireAsSoleVoterBox"; diff --git a/govtool/frontend/src/hooks/forms/index.ts b/govtool/frontend/src/hooks/forms/index.ts index fe994e70b..a1b5ca4e4 100644 --- a/govtool/frontend/src/hooks/forms/index.ts +++ b/govtool/frontend/src/hooks/forms/index.ts @@ -1,6 +1,6 @@ export * from "./useCreateGovernanceActionForm"; export * from "./useDelegateTodRepForm"; -export * from "./useRegisterAsdRepFormContext"; +export * from "./useRegisterAsdRepForm"; export * from "./useUpdatedRepMetadataForm"; export * from "./useUrlAndHashFormController"; export * from "./useVoteActionForm"; diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx new file mode 100644 index 000000000..69dfd3411 --- /dev/null +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -0,0 +1,54 @@ +import { useCallback, useState } from "react"; +import { useFormContext } from "react-hook-form"; + +export type RegisterAsDRepValues = { + bio?: string; + dRepName: string; + email?: string; + links?: { link: string }[]; + storeData?: boolean; + storingURL: string; +}; + +export const defaultRegisterAsDRepValues: RegisterAsDRepValues = { + bio: "", + dRepName: "", + email: "", + links: [{ link: "" }], + storeData: false, + storingURL: "", +}; + +export const useRegisterAsdRepForm = () => { + const [isLoading, setIsLoading] = useState(false); + + const { + control, + handleSubmit, + formState: { errors, isValid }, + register, + resetField, + watch, + } = useFormContext(); + + const onSubmit = useCallback(async (values: RegisterAsDRepValues) => { + try { + console.log(values); + } catch (e: any) { + console.error(e); + } finally { + setIsLoading(false); + } + }, []); + + return { + control, + errors, + isRegistrationAsDRepLoading: isLoading, + isValid, + register, + registerAsDrep: handleSubmit(onSubmit), + resetField, + watch, + }; +}; diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx deleted file mode 100644 index 7daae0406..000000000 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { useCallback, useState } from "react"; -import { useNavigate } from "react-router-dom"; -import { useFormContext } from "react-hook-form"; - -import { PATHS } from "@consts"; -import { useCardano, useModal } from "@context"; -import { UrlAndHashFormValues, useTranslation } from "@hooks"; - -export const useRegisterAsdRepFormContext = () => { - const { - buildSignSubmitConwayCertTx, - buildDRepRegCert, - buildDRepUpdateCert, - voter, - } = useCardano(); - const { openModal, closeModal } = useModal(); - const [isLoading, setIsLoading] = useState(false); - const navigate = useNavigate(); - const { t } = useTranslation(); - - const { - control, - handleSubmit, - formState: { errors, isValid }, - resetField, - watch, - } = useFormContext(); - - const isSkipButton = !watch("hash")?.trim() && !watch("url")?.trim(); - - const isContinueButtonDisabled = !!Object.keys(errors).length; - - const onSubmit = useCallback( - async (values: UrlAndHashFormValues) => { - const { url, hash } = values; - - // Temporary solution. To modify later. - const urlSubmitValue = !url - ? "https://raw.githubusercontent.com/Thomas-Upfield/test-metadata/main/placeholder.json" - : url; - const hashSubmitValue = !hash - ? "654e483feefc4d208ea02637a981a2046e17c73c09583e9dd0c84c25dab42749" - : hash; - setIsLoading(true); - - try { - const certBuilder = voter?.isRegisteredAsSoleVoter - ? await buildDRepUpdateCert(urlSubmitValue, hashSubmitValue) - : await buildDRepRegCert(urlSubmitValue, hashSubmitValue); - const result = await buildSignSubmitConwayCertTx({ - certBuilder, - type: "registration", - registrationType: "registration", - }); - if (result) { - openModal({ - type: "statusModal", - state: { - status: "success", - title: t("modals.registration.title"), - message: t("modals.registration.message"), - link: "https://adanordic.com/latest_transactions", - buttonText: t("modals.common.goToDashboard"), - onSubmit: () => { - navigate(PATHS.dashboard); - closeModal(); - }, - dataTestId: "registration-transaction-submitted-modal", - }, - }); - } - } catch (e: any) { - const errorMessage = e.info ? e.info : e; - - openModal({ - type: "statusModal", - state: { - status: "warning", - title: t("modals.common.oops"), - message: errorMessage, - buttonText: t("modals.common.goToDashboard"), - onSubmit: () => { - navigate(PATHS.dashboard); - closeModal(); - }, - dataTestId: "registration-transaction-error-modal", - }, - }); - } finally { - setIsLoading(false); - } - }, - [buildSignSubmitConwayCertTx, buildDRepRegCert, openModal], - ); - - return { - control, - errors, - isContinueButtonDisabled, - isRegistrationAsDRepLoading: isLoading, - isSkipButton, - isValid, - resetField, - submitForm: handleSubmit(onSubmit), - watch, - }; -}; diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 166d05af5..98484fbf4 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -285,6 +285,7 @@ export const en = { hashPlaceholder: "The hash of metadata at URL", howCreateUrlAndHash: "How to create URL and hash?", link: "Link", + linkPlaceholder: "https://website.com/", urlWithContextPlaceholder: "Your URL with with your context", urlWithInfoPlaceholder: "Your URL with extra info about you", createGovernanceAction: { @@ -298,6 +299,17 @@ export const en = { urlTooLong: "Url must be less than 65 characters", urlInvalidFormat: "Invalid URL format", }, + registerAsDRep: { + bio: "Bio", + bioHelpfulText: "Some sentence about yourself", + bioPlaceholder: "Enter your Bio ...", + dRepName: "DRep Name", + dRepNameHelpfulText: + "This is name that will be shown on your DRep profile", + dRepNamePlaceholder: "ex. JohnDRep", + email: "Email", + emailPlaceholder: "john.smith@email.com", + }, }, govActions: { changeVote: "Change vote", @@ -440,15 +452,24 @@ export const en = { }, }, registration: { + aboutYou: "About You", + aboutYouDescription: + "Some extra info about you to provide context to delegators.", addInformationDescription: "You can include extra information about yourself by adding a URL and its hash.", addInformationTitle: "Add Information", becomeADRep: "Become a DRep", descriptionStepTwo: "By clicking register you create your DRep ID within your wallet and become a DRep.\n\nOnce the registration has completed your DRep ID will be shown on your dashboard. You will be able to share your DRep ID so that other ada holders can delegate their voting power to you.", + dRepName: "DRep Name", + dRepNameDescription: + "This is the name that will be displayed in the DRep Directory and it will be used also by delegators to find your profile.", headingStepTwo: "Confirm DRep registration", - optional: "OPTIONAL", + linksDescription: "Links to extra content or social media contacts ", + maximumLinks: "(maximum of 7 entries)", + optional: "optional", register: "Register", + required: "required", rolesAndResponsibilitiesDescription: "DReps are fundamental users that govern the Cardano network. This is an important role which requires work and dedication to fulfil.\n\nA DRep is expected to actively participate in governance and act as a representative of other Cardano members in governance matters. Therefore, DReps will be expected to keep abreast of Governance Actions so they can make informed and wise decisions.\n<0>Learn More about DRep.\n\nPlease register as a DRep if you have time to dedicate to making Cardano a better and more well-governed place.\n\nBecoming a DRep will require a refundable deposit of ₳{{deposit}}.\n\nYou will be refunded your deposit when you retire.", rolesAndResponsibilitiesTitle: "Roles & Responsibilities", diff --git a/govtool/frontend/src/pages/RegisterAsdRep.tsx b/govtool/frontend/src/pages/RegisterAsdRep.tsx index 29323d1f2..5a96f49ee 100644 --- a/govtool/frontend/src/pages/RegisterAsdRep.tsx +++ b/govtool/frontend/src/pages/RegisterAsdRep.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { FormProvider } from "react-hook-form"; +import { FormProvider, useForm } from "react-hook-form"; import { Box } from "@mui/material"; import { Background } from "@atoms"; @@ -8,16 +8,16 @@ import { PATHS } from "@consts"; import { useModal } from "@context"; import { useScreenDimension, - useUrlAndHashFormController as useRegisterAsdRepFormController, useTranslation, + defaultRegisterAsDRepValues, } from "@hooks"; import { LinkWithIcon } from "@molecules"; import { DashboardTopNav, Footer, - RegisterAsdRepStepOne, + RolesAndResponsibilities, RegisterAsdRepStepThree, - RegisterAsdRepStepTwo, + RegisterAsDRepForm, } from "@organisms"; import { checkIsWalletConnected } from "@utils"; @@ -28,7 +28,10 @@ export const RegisterAsdRep = () => { const { t } = useTranslation(); const { closeModal, openModal } = useModal(); - const registerAsdRepFormMethods = useRegisterAsdRepFormController(); + const methods = useForm({ + mode: "onChange", + defaultValues: defaultRegisterAsDRepValues, + }); useEffect(() => { if (checkIsWalletConnected()) { @@ -68,14 +71,14 @@ export const RegisterAsdRep = () => { mt: isMobile ? 3 : 1.5, }} /> - - {step === 1 && ( - - )} - {step === 2 && } + {step === 1 && ( + + )} + + {step === 2 && } {step === 3 && } {isMobile &&