From cd26dfb8003ec8c041d363ed4af5cc902c5db559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Wed, 27 Mar 2024 15:14:48 +0100 Subject: [PATCH] [#122] update drep metadata/ re-register as drep --- govtool/frontend/src/App.tsx | 4 +- .../DashboardCards/DRepDashboardCard.tsx | 10 +- .../EditDRepInfoSteps/EditDRepForm.tsx | 152 +++++++++++++ .../EditDRepStorageInformation.tsx | 129 +++++++++++ .../EditDRepStoreDataInfo.tsx | 65 ++++++ .../organisms/EditDRepInfoSteps/index.ts | 3 + .../DRepStorageInformation.tsx | 17 +- .../RegisterAsDRepSteps/DRepStoreDataInfo.tsx | 4 +- .../RegisterAsDRepForm.tsx | 10 +- .../src/components/organisms/index.ts | 1 + .../frontend/src/consts/dRepActions/fields.ts | 10 + govtool/frontend/src/consts/paths.ts | 4 +- govtool/frontend/src/hooks/forms/index.ts | 1 + .../src/hooks/forms/useEditDRepInfoForm.ts | 200 ++++++++++++++++++ .../src/hooks/forms/useRegisterAsdRepForm.tsx | 2 +- govtool/frontend/src/i18n/locales/en.ts | 47 ++++ .../frontend/src/pages/EditDRepMetadata.tsx | 89 ++++++++ govtool/frontend/src/pages/RegisterAsdRep.tsx | 62 ++++-- .../frontend/src/pages/UpdatedRepMetadata.tsx | 160 -------------- govtool/frontend/src/pages/index.ts | 2 +- 20 files changed, 769 insertions(+), 203 deletions(-) create mode 100644 govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx create mode 100644 govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStorageInformation.tsx create mode 100644 govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx create mode 100644 govtool/frontend/src/components/organisms/EditDRepInfoSteps/index.ts create mode 100644 govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts create mode 100644 govtool/frontend/src/pages/EditDRepMetadata.tsx delete mode 100644 govtool/frontend/src/pages/UpdatedRepMetadata.tsx diff --git a/govtool/frontend/src/App.tsx b/govtool/frontend/src/App.tsx index 9e7b9300e..c73ca3bfb 100644 --- a/govtool/frontend/src/App.tsx +++ b/govtool/frontend/src/App.tsx @@ -25,7 +25,7 @@ import { RegisterAsSoleVoter, RetireAsDrep, RetireAsSoleVoter, - UpdatedRepMetadata, + EditDRepMetadata, } from "@pages"; import { SetupInterceptors } from "@services"; import { @@ -118,7 +118,7 @@ export default () => { /> } /> } /> - } /> + } /> } /> } /> diff --git a/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx index 4c9298d3e..52a491541 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx @@ -70,7 +70,7 @@ export const DRepDashboardCard = ({ { children: t("dashboard.registration.changeMetadata"), dataTestId: "change-metadata-button", - onClick: () => navigate(PATHS.updateMetadata), + onClick: () => navigate(PATHS.editDrepMetadata), variant: "text", }, ], @@ -84,7 +84,11 @@ export const DRepDashboardCard = ({ const wasRegisteredOrNotRegisteredButtons: DashboardActionCardProps["buttons"] = [ { - children: t("dashboard.registration.register"), + children: t( + voter.wasRegisteredAsDRep + ? "dashboard.registration.reRegister" + : "dashboard.registration.register", + ), dataTestId: "register-button", onClick: () => navigate(PATHS.registerAsdRep), variant: "contained", @@ -94,7 +98,7 @@ export const DRepDashboardCard = ({ dataTestId: "register-learn-more-button", onClick: () => openInNewTab( - "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep" + "https://docs.sanchogov.tools/faqs/what-does-it-mean-to-register-as-a-drep", ), }, ]; diff --git a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx new file mode 100644 index 000000000..fcc821727 --- /dev/null +++ b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx @@ -0,0 +1,152 @@ +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 { Placeholders, Rules } from "@consts"; +import { + useEditDRepInfoForm, + useScreenDimension, + useTranslation, +} from "@hooks"; + +import { BgCard, ControlledField } from ".."; + +const MAX_NUMBER_OF_LINKS = 7; + +export const EditDRepForm = ({ + onClickCancel, + setStep, +}: { + onClickCancel: () => void; + setStep: Dispatch>; +}) => { + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + const { control, errors, isError, register, watch } = useEditDRepInfoForm(); + const { + append, + fields: links, + remove, + } = useFieldArray({ + control, + name: "links", + }); + + const onClickContinue = () => setStep(2); + + const addLink = useCallback(() => append({ link: "" }), [append]); + + const removeLink = useCallback((index: number) => remove(index), [remove]); + + const isContinueButtonDisabled = !watch("dRepName") || isError; + + const renderLinks = useCallback( + () => + links.map((field, index) => ( + 1 ? ( + removeLink(index)} + /> + ) : null + } + key={field.id} + // prefer-template rule for that label makes no sense + // eslint-disable-next-line prefer-template + label={t("forms.link") + ` ${index + 1}`} + layoutStyles={{ mb: 3 }} + placeholder={Placeholders.LINK} + name={`links.${index}.link`} + rules={Rules.LINK} + /> + )), + [errors, links], + ); + + return ( + + + + + {t("editMetadata.dRepName")} + + + {t("editMetadata.dRepNameDescription")} + + + + + + + + {t("editMetadata.aboutYou")} + + + {t("editMetadata.aboutYouDescription")} + + + + + + +

+ {t("editMetadata.linksDescription")} + + {t("editMetadata.maximumLinks", { + numberOfLinks: MAX_NUMBER_OF_LINKS, + })} + +

+ + {renderLinks()} + {links?.length < MAX_NUMBER_OF_LINKS ? ( + + ) : null} + +
+ ); +}; diff --git a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStorageInformation.tsx b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStorageInformation.tsx new file mode 100644 index 000000000..5a7b6293c --- /dev/null +++ b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStorageInformation.tsx @@ -0,0 +1,129 @@ +import { Dispatch, SetStateAction, useEffect } from "react"; +import { Box } from "@mui/material"; +import OpenInNewIcon from "@mui/icons-material/OpenInNew"; + +import { Button, Spacer, Typography } from "@atoms"; +import { ICONS, Rules } from "@consts"; +import { + useEditDRepInfoForm, + useTranslation, + useScreenDimension, +} from "@hooks"; +import { Step } from "@molecules"; +import { BgCard, ControlledField } from "@organisms"; +import { openInNewTab } from "@utils"; + +type StorageInformationProps = { + setStep: Dispatch>; +}; + +export const EditDRepStorageInformation = ({ + setStep, +}: StorageInformationProps) => { + const { t } = useTranslation(); + const { + control, + errors, + generateMetadata, + getValues, + isEditDRepMetadataLoading, + onClickDownloadJson, + editDRepInfo, + watch, + } = useEditDRepInfoForm(); + const { screenWidth } = useScreenDimension(); + + const fileName = getValues("dRepName"); + + // TODO: Change link to correct + const openGuideAboutStoringInformation = () => + openInNewTab("https://sancho.network/"); + + const isActionButtonDisabled = !watch("storingURL") || !!errors.storingURL; + + const onClickBack = () => setStep(2); + + useEffect(() => { + generateMetadata(); + }, []); + + return ( + + + {t("editMetadata.storingInformationTitle")} + + + + {t("editMetadata.storingInformationDescription")} + + + } + sx={{ + width: "fit-content", + ml: screenWidth < 1024 ? 0 : 1.75, + mt: screenWidth < 1024 ? 1.5 : 0, + }} + variant="outlined" + > + {`${fileName}.jsonld`} + + } + label={t("editMetadata.storingInformationStep1Label")} + componentsLayoutStyles={{ + alignItems: screenWidth < 1024 ? undefined : "center", + flexDirection: screenWidth < 1024 ? "column" : "row", + }} + stepNumber={1} + /> + + + + + } + label={t("editMetadata.storingInformationStep3Label")} + stepNumber={3} + /> + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx new file mode 100644 index 000000000..24afb4120 --- /dev/null +++ b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx @@ -0,0 +1,65 @@ +import { Dispatch, SetStateAction } from "react"; +import { Box, Link } from "@mui/material"; + +import { Spacer, Typography } from "@atoms"; +import { + useScreenDimension, + useTranslation, + useEditDRepInfoForm, +} from "@hooks"; +import { openInNewTab } from "@utils"; + +import { BgCard, ControlledField } from ".."; + +export const EditDRepStoreDataInfo = ({ + setStep, +}: { + setStep: Dispatch>; +}) => { + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + const { control, errors, watch } = useEditDRepInfoForm(); + + const onClickBackButton = () => setStep(1); + + const onClickContinue = () => setStep(3); + + const isContinueDisabled = !watch("storeData"); + + // TODO: Add link about store data when available + const openLink = () => openInNewTab("https://sancho.network/get-started"); + + return ( + + + {t("editMetadata.storeDataTitle")} + + + {t("editMetadata.storeDataLink")} + + + + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/index.ts b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/index.ts new file mode 100644 index 000000000..65ee7cc1f --- /dev/null +++ b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/index.ts @@ -0,0 +1,3 @@ +export * from "./EditDRepForm"; +export * from "./EditDRepStorageInformation"; +export * from "./EditDRepStoreDataInfo"; diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx index 7fbed215e..e021583ff 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx @@ -3,7 +3,7 @@ import { Box } from "@mui/material"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import { Button, Spacer, Typography } from "@atoms"; -import { ICONS } from "@consts"; +import { ICONS, Rules } from "@consts"; import { useRegisterAsdRepForm, useTranslation, @@ -11,7 +11,7 @@ import { } from "@hooks"; import { Step } from "@molecules"; import { BgCard, ControlledField } from "@organisms"; -import { URL_REGEX, openInNewTab } from "@utils"; +import { openInNewTab } from "@utils"; type StorageInformationProps = { setStep: Dispatch>; @@ -52,9 +52,9 @@ export const DRepStorageInformation = ({ actionButtonLabel={t("submit")} backButtonLabel={t("back")} isActionButtonDisabled={isActionButtonDisabled} + isLoadingActionButton={isRegistrationAsDRepLoading} onClickActionButton={registerAsDrep} onClickBackButton={onClickBack} - isLoadingActionButton={isRegistrationAsDRepLoading} > {t("registration.storingInformationTitle")} @@ -117,16 +117,7 @@ export const DRepStorageInformation = ({ layoutStyles={{ mt: 1.5 }} name="storingURL" placeholder={t("registration.storingInformationURLPlaceholder")} - rules={{ - required: { - value: true, - message: t("registration.fields.validations.required"), - }, - pattern: { - value: URL_REGEX, - message: t("registration.fields.validations.url"), - }, - }} + rules={Rules.STORING_LINK} /> } label={t("registration.storingInformationStep3Label")} diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx index a8a22e110..51fed7e13 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx @@ -18,8 +18,7 @@ export const DRepStoreDataInfo = ({ }) => { const { t } = useTranslation(); const { isMobile } = useScreenDimension(); - const { control, errors, isRegistrationAsDRepLoading, watch } = - useRegisterAsdRepForm(); + const { control, errors, watch } = useRegisterAsdRepForm(); const onClickBackButton = () => setStep(2); @@ -34,7 +33,6 @@ export const DRepStoreDataInfo = ({ diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx index 88d11a3f3..f6f561bb4 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx @@ -10,15 +10,20 @@ import { useScreenDimension, useTranslation, } from "@hooks"; +import { VoterInfo } from "@models"; import { BgCard, ControlledField } from ".."; const MAX_NUMBER_OF_LINKS = 7; export const RegisterAsDRepForm = ({ + onClickCancel, setStep, + voter, }: { + onClickCancel: () => void; setStep: Dispatch>; + voter?: VoterInfo; }) => { const { t } = useTranslation(); const { isMobile } = useScreenDimension(); @@ -73,8 +78,11 @@ export const RegisterAsDRepForm = ({ return ( diff --git a/govtool/frontend/src/components/organisms/index.ts b/govtool/frontend/src/components/organisms/index.ts index f8da1ecb0..f4931d142 100644 --- a/govtool/frontend/src/components/organisms/index.ts +++ b/govtool/frontend/src/components/organisms/index.ts @@ -14,6 +14,7 @@ export * from "./DelegateTodRepStepOne"; export * from "./DelegateTodRepStepTwo"; export * from "./Drawer"; export * from "./DrawerMobile"; +export * from "./EditDRepInfoSteps"; export * from "./ExternalLinkModal"; export * from "./Footer"; export * from "./GovernanceActionDetailsCard"; diff --git a/govtool/frontend/src/consts/dRepActions/fields.ts b/govtool/frontend/src/consts/dRepActions/fields.ts index 025e7c72c..155ac6b87 100644 --- a/govtool/frontend/src/consts/dRepActions/fields.ts +++ b/govtool/frontend/src/consts/dRepActions/fields.ts @@ -38,4 +38,14 @@ export const Rules = { message: i18n.t("registration.fields.validations.url"), }, }, + STORING_LINK: { + required: { + value: true, + message: i18n.t("registration.fields.validations.required"), + }, + pattern: { + value: URL_REGEX, + message: i18n.t("registration.fields.validations.url"), + }, + }, }; diff --git a/govtool/frontend/src/consts/paths.ts b/govtool/frontend/src/consts/paths.ts index 1e2eddda5..3361f31a3 100644 --- a/govtool/frontend/src/consts/paths.ts +++ b/govtool/frontend/src/consts/paths.ts @@ -6,6 +6,7 @@ export const PATHS = { dashboardGovernanceActionsCategory: "/connected/governance_actions/category/:category", delegateTodRep: "/delegate", + editDrepMetadata: "/edit_drep", error: "/error", faqs: "/faqs", governanceActions: "/governance_actions", @@ -15,10 +16,9 @@ export const PATHS = { "/governance_actions/category/:category/:proposalId", guides: "/guides", home: "/", - registerAsdRep: "/register", + registerAsdRep: "/register_drep", registerAsSoleVoter: "/register_sole_voter", retireAsDrep: "/retire_drep", retireAsSoleVoter: "/retire_sole_voter", stakeKeys: "/stake_keys", - updateMetadata: "/update_metadata", }; diff --git a/govtool/frontend/src/hooks/forms/index.ts b/govtool/frontend/src/hooks/forms/index.ts index f86aeba6c..eee66a733 100644 --- a/govtool/frontend/src/hooks/forms/index.ts +++ b/govtool/frontend/src/hooks/forms/index.ts @@ -1,5 +1,6 @@ export * from "./useCreateGovernanceActionForm"; export * from "./useDelegateTodRepForm"; +export * from "./useEditDRepInfoForm"; export * from "./useRegisterAsdRepForm"; export * from "./useUpdatedRepMetadataForm"; export * from "./useUrlAndHashFormController"; diff --git a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts new file mode 100644 index 000000000..89795269c --- /dev/null +++ b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts @@ -0,0 +1,200 @@ +import { Dispatch, SetStateAction, useCallback, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { useFormContext } from "react-hook-form"; +import { blake2bHex } from "blakejs"; +import { captureException } from "@sentry/react"; +import { NodeObject } from "jsonld"; + +import { + CIP_100, + CIP_QQQ, + DREP_CONTEXT, + MetadataHashValidationErrors, + PATHS, + storageInformationErrorModals, +} from "@consts"; +import { useCardano, useModal } from "@context"; +import { + canonizeJSON, + downloadJson, + generateJsonld, + validateMetadataHash, +} from "@utils"; + +export type EditDRepInfoValues = { + bio?: string; + dRepName: string; + email?: string; + links?: Array<{ link: string }>; + storeData?: boolean; + storingURL: string; +}; + +export const defaultEditDRepInfoValues: EditDRepInfoValues = { + bio: "", + dRepName: "", + email: "", + links: [{ link: "" }], + storeData: false, + storingURL: "", +}; + +export const useEditDRepInfoForm = ( + setStep?: Dispatch>, +) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const [isLoading, setIsLoading] = useState(false); + const [hash, setHash] = useState(null); + const [json, setJson] = useState(null); + const { closeModal, openModal } = useModal(); + const { buildDRepUpdateCert, buildSignSubmitConwayCertTx } = useCardano(); + + const backToForm = useCallback(() => { + setStep?.(1); + closeModal(); + }, [setStep]); + + const backToDashboard = useCallback(() => { + navigate(PATHS.dashboard); + closeModal(); + }, []); + + const { + control, + getValues, + handleSubmit, + formState: { errors, isValid }, + register, + resetField, + watch, + } = useFormContext(); + + const dRepName = watch("dRepName"); + const isError = Object.keys(errors).length > 0; + + const generateMetadata = useCallback(async () => { + const data = getValues(); + const acceptedKeys = ["dRepName", "bio", "email"]; + + const filteredData = Object.entries(data) + .filter(([key]) => acceptedKeys.includes(key)) + .map(([key, value]) => [CIP_QQQ + key, value]); + + const references = (data as EditDRepInfoValues).links + ?.filter((link) => link.link) + .map((link) => ({ + "@type": "Other", + [`${CIP_100}reference-label`]: "Label", + [`${CIP_100}reference-uri`]: link.link, + })); + + const body = { + ...Object.fromEntries(filteredData), + [`${CIP_QQQ}references`]: references, + }; + + const jsonld = await generateJsonld(body, DREP_CONTEXT, CIP_QQQ); + + const canonizedJson = await canonizeJSON(jsonld); + const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32); + + setHash(canonizedJsonHash); + setJson(jsonld); + + return jsonld; + }, []); + + const onClickDownloadJson = async () => { + if (!json) return; + + downloadJson(json, dRepName); + }; + + const validateHash = useCallback( + async (storingUrl: string) => { + try { + if (!hash) throw new Error(MetadataHashValidationErrors.INVALID_HASH); + + await validateMetadataHash(storingUrl, hash); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + if ( + Object.values(MetadataHashValidationErrors).includes(error.message) + ) { + openModal({ + type: "statusModal", + state: { + ...storageInformationErrorModals[ + error.message as MetadataHashValidationErrors + ], + onSubmit: backToForm, + onCancel: backToDashboard, + // TODO: Open usersnap feedback + onFeedback: backToDashboard, + }, + }); + } + throw error; + } + }, + [backToForm, hash], + ); + + const showSuccessModal = useCallback(() => { + openModal({ + type: "statusModal", + state: { + status: "success", + title: t("modals.registration.title"), + message: t("modals.registration.message"), + buttonText: t("modals.common.goToDashboard"), + dataTestId: "governance-action-submitted-modal", + onSubmit: backToDashboard, + }, + }); + }, []); + + const onSubmit = useCallback( + async (data: EditDRepInfoValues) => { + const url = data.storingURL; + + if (!hash) return; + + try { + setIsLoading(true); + + await validateHash(url); + const updateDRepMetadataCert = await buildDRepUpdateCert(url, hash); + await buildSignSubmitConwayCertTx({ + certBuilder: updateDRepMetadataCert, + type: "updateMetaData", + }); + + showSuccessModal(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + captureException(error); + } finally { + setIsLoading(false); + } + }, + [buildDRepUpdateCert, buildSignSubmitConwayCertTx, hash, validateHash], + ); + + return { + control, + errors, + generateMetadata, + getValues, + isError, + isEditDRepMetadataLoading: isLoading, + isValid, + onClickDownloadJson, + register, + editDRepInfo: handleSubmit(onSubmit), + resetField, + watch, + }; +}; diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index 12d5422d6..22c2aa4cd 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -55,7 +55,7 @@ export const useRegisterAsdRepForm = ( const { voter } = useGetVoterInfo(); const backToForm = useCallback(() => { - setStep?.(3); + setStep?.(2); closeModal(); }, [setStep]); diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 9f84a3429..e8143f5ed 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -108,6 +108,7 @@ export const en = { registerAsDRep: "Register as a DRep", registrationInProgress: "The registration process is ongoing. This may take several minutes.", + reRegister: "Re-register as a DRep", retire: "Retire as a DRep", retirementInProgress: "The retirement process is ongoing. This may take several minutes.", @@ -279,6 +280,17 @@ export const en = { typeTip: "To change the Governance Action Type go back to the previous page.", }, + editMetadata: { + 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", + }, errors: { hashInvalidFormat: "Invalid hash format", hashInvalidLength: "Hash must be exactly 64 characters long", @@ -478,6 +490,41 @@ export const en = { "Before performing a new action please wait for the previous action transaction to be completed.", }, }, + editMetadata: { + pageTitle: "Edit DRep Info", + aboutYou: "About You", + aboutYouDescription: + "Some extra info about you to provide context to delegators.", + 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.", + linksDescription: "Links to extra content or social media contacts ", + maximumLinks: "(maximum of {{numberOfLinks}} entries)", + optional: "optional", + required: "required", + storeDataCheckboxLabel: + "I agree to store correctly this information and to maintain them over the years", + storeDataLink: "Learn more about storing information", + storeDataTitle: "Store and maintain the data yourself", + storingInformationDescription: + "Download your file, save it to your chosen location, and enter the URL of that location in step 3", + storingInformationStep1Label: "Download this file", + storingInformationStep2Label: + "Save this file in a location that provides a public URL (ex. github)", + storingInformationStep2Link: "Read full guide", + storingInformationStep3Label: "Paste the URL here", + storingInformationTitle: "Information Storage Steps", + storingInformationURLPlaceholder: "URL", + fields: { + validations: { + email: "Invalid email address", + maxLength: "Max {{maxLength}} characters", + nickname: "Nickname can not contain whitespaces", + required: "This field is required", + url: "Invalid URL", + }, + }, + }, registration: { aboutYou: "About You", aboutYouDescription: diff --git a/govtool/frontend/src/pages/EditDRepMetadata.tsx b/govtool/frontend/src/pages/EditDRepMetadata.tsx new file mode 100644 index 000000000..e8296ee45 --- /dev/null +++ b/govtool/frontend/src/pages/EditDRepMetadata.tsx @@ -0,0 +1,89 @@ +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { FormProvider, useForm } from "react-hook-form"; +import { Box } from "@mui/material"; + +import { Background } from "@atoms"; +import { PATHS } from "@consts"; +import { useModal } from "@context"; +import { + useScreenDimension, + useTranslation, + defaultEditDRepInfoValues, +} from "@hooks"; +import { LinkWithIcon } from "@molecules"; +import { + DashboardTopNav, + EditDRepStorageInformation, + EditDRepStoreDataInfo, + Footer, + EditDRepForm, +} from "@organisms"; +import { checkIsWalletConnected } from "@utils"; + +export const EditDRepMetadata = () => { + const [step, setStep] = useState(1); + const { isMobile } = useScreenDimension(); + const navigate = useNavigate(); + const { t } = useTranslation(); + const { closeModal, openModal } = useModal(); + + const methods = useForm({ + mode: "onChange", + defaultValues: defaultEditDRepInfoValues, + }); + + const backToDashboard = () => { + navigate(PATHS.dashboard); + closeModal(); + }; + + const onClickBackToDashboard = () => + openModal({ + type: "statusModal", + state: { + status: "warning", + message: t("modals.registration.cancelDescription"), + buttonText: t("modals.common.goToDashboard"), + title: t("modals.registration.cancelTitle"), + dataTestId: "cancel-edit-drep-info-modal", + onSubmit: backToDashboard, + }, + }); + + useEffect(() => { + if (checkIsWalletConnected()) { + navigate(PATHS.home); + } + }, []); + + return ( + + + + + + {step === 1 && ( + + )} + {step === 2 && } + {step === 3 && } + + {isMobile &&