();
const { addSuccessAlert } = useSnackbar();
const navigate = useNavigate();
+ const { t } = useTranslation();
if (context === undefined) {
- throw new Error("useCardano must be used within a CardanoProvider");
+ throw new Error(t("errors.useCardano"));
}
const enable = useCallback(
@@ -1210,7 +1200,7 @@ function useCardano() {
if (!result.error) {
closeModal();
if (result.stakeKey) {
- addSuccessAlert(`Wallet connected`, 3000);
+ addSuccessAlert(t("alerts.walletConnected"), 3000);
}
if (!isSanchoInfoShown) {
openModal({
@@ -1220,23 +1210,26 @@ function useCardano() {
dataTestId: "info-about-sancho-net-modal",
message: (
- The SanchoNet GovTool is currently in beta and it connects
- to{" "}
+ {t("system.sanchoNetIsBeta")}
openInNewTab("https://sancho.network/")}
sx={{ cursor: "pointer" }}
>
- SanchoNet
+ {t("system.sanchoNet")}
.
- Please note, this tool uses ‘Test ada’
- NOT real ada . All
- governance actions and related terms pertain to SanchoNet."
+
+ ,
+ ]}
+ />
),
- title: "This tool is connected to SanchoNet",
- buttonText: "Ok",
+ title: t("system.toolConnectedToSanchonet"),
+ buttonText: t("ok"),
},
});
setItemToLocalStorage(SANCHO_INFO_KEY + `_${walletName}`, true);
@@ -1255,7 +1248,7 @@ function useCardano() {
onSubmit: () => {
closeModal();
},
- title: "Oops!",
+ title: t("modals.common.oops"),
dataTestId: "wallet-connection-error-modal",
},
});
diff --git a/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx b/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx
index e4ff1861b..8065db233 100644
--- a/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx
+++ b/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx
@@ -4,16 +4,16 @@ import { useForm, useWatch } from "react-hook-form";
import { PATHS } from "@consts";
import { useCardano, useModal } from "@context";
-import { useGetDRepListQuery } from "@hooks";
+import { useGetDRepListQuery, useTranslation } from "@hooks";
import { formHexToBech32 } from "@utils";
export interface DelegateTodrepFormValues {
- dRepId: string;
+ dRepID: string;
}
export const useDelegateTodRepForm = () => {
const {
- setDelegatedDRepId,
+ setDelegatedDRepID,
buildSignSubmitConwayCertTx,
buildVoteDelegationCert,
} = useCardano();
@@ -21,31 +21,32 @@ export const useDelegateTodRepForm = () => {
const { openModal, closeModal, modal } = useModal();
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
+ const { t } = useTranslation();
const { control, handleSubmit } = useForm();
const watch = useWatch({
control,
- name: "dRepId",
+ name: "dRepID",
});
const isDelegateButtonDisabled = !watch;
const delegate = useCallback(
- async ({ dRepId }: DelegateTodrepFormValues) => {
+ async ({ dRepID }: DelegateTodrepFormValues) => {
setIsLoading(true);
try {
- setDelegatedDRepId(dRepId);
+ setDelegatedDRepID(dRepID);
let isValidDrep = false;
if (drepList?.length) {
isValidDrep = drepList.some((i) => {
- return i.drepId === dRepId || formHexToBech32(i.drepId) === dRepId;
+ return i.drepId === dRepID || formHexToBech32(i.drepId) === dRepID;
});
}
if (!drepList?.length || !isValidDrep) {
- throw new Error("DrepId not found");
+ throw new Error(t("errors.dRepIdNotFound"));
}
- const certBuilder = await buildVoteDelegationCert(dRepId);
+ const certBuilder = await buildVoteDelegationCert(dRepID);
const result = await buildSignSubmitConwayCertTx({
certBuilder,
type: "delegation",
@@ -55,11 +56,10 @@ export const useDelegateTodRepForm = () => {
type: "statusModal",
state: {
status: "success",
- title: "Delegation Transaction Submitted!",
- message:
- "The confirmation of your actual delegation might take a bit of time but you can track it using.",
+ title: t("modals.delegation.title"),
+ message: t("modals.delegation.message"),
link: "https://adanordic.com/latest_transactions",
- buttonText: "Go to dashboard",
+ buttonText: t("modals.common.goToDashboard"),
onSubmit: () => {
navigate(PATHS.dashboard);
closeModal();
@@ -76,7 +76,7 @@ export const useDelegateTodRepForm = () => {
onSubmit: () => {
closeModal();
},
- title: "Oops!",
+ title: t("modals.common.oops"),
dataTestId: "delegation-transaction-error-modal",
},
});
@@ -92,6 +92,6 @@ export const useDelegateTodRepForm = () => {
isDelegateButtonDisabled,
delegate: handleSubmit(delegate),
modal,
- isLoading,
+ isDelegationLoading: isLoading,
};
};
diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx
index a68b89519..27a9fc6a5 100644
--- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx
+++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepFormContext.tsx
@@ -4,13 +4,14 @@ import { useFormContext, useWatch } from "react-hook-form";
import { PATHS } from "@consts";
import { useCardano, useModal } from "@context";
-import { UrlAndHashFormValues } from "@hooks";
+import { UrlAndHashFormValues, useTranslation } from "@hooks";
export const useRegisterAsdRepFormContext = () => {
const { buildSignSubmitConwayCertTx, buildDRepRegCert } = useCardano();
const { openModal, closeModal } = useModal();
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
+ const { t } = useTranslation();
const {
control,
@@ -49,11 +50,10 @@ export const useRegisterAsdRepFormContext = () => {
type: "statusModal",
state: {
status: "success",
- title: "Registration Transaction Submitted!",
- message:
- "The confirmation of your registration might take a bit of time but you can track it using.",
+ title: t("modals.registration.title"),
+ message: t("modals.registration.message"),
link: "https://adanordic.com/latest_transactions",
- buttonText: "Go to dashboard",
+ buttonText: t("modals.common.goToDashboard"),
onSubmit: () => {
navigate(PATHS.dashboard);
closeModal();
@@ -68,9 +68,9 @@ export const useRegisterAsdRepFormContext = () => {
type: "statusModal",
state: {
status: "warning",
- title: "Oops!",
+ title: t("modals.common.oops"),
message: errorMessage,
- buttonText: "Go to dashboard",
+ buttonText: t("modals.common.goToDashboard"),
onSubmit: () => {
navigate(PATHS.dashboard);
closeModal();
diff --git a/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx b/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx
index da5adae9f..da48b2250 100644
--- a/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx
+++ b/govtool/frontend/src/hooks/forms/useUpdatedRepMetadataForm.tsx
@@ -7,12 +7,14 @@ import { useNavigate } from "react-router-dom";
import { PATHS } from "@consts";
import { useCardano, useSnackbar } from "@context";
+import { useTranslation } from "@hooks";
export const useUpdatedRepMetadataForm = () => {
const { buildSignSubmitConwayCertTx, buildDRepUpdateCert } = useCardano();
const { addSuccessAlert, addErrorAlert } = useSnackbar();
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
+ const { t } = useTranslation();
const {
handleSubmit,
@@ -37,10 +39,10 @@ export const useUpdatedRepMetadataForm = () => {
type: "registration",
registrationType: "update",
});
- if (result) addSuccessAlert("Metadata update submitted");
+ if (result) addSuccessAlert(t("alerts.metadataUpdate.success"));
navigate(PATHS.dashboard);
} catch (e) {
- addErrorAlert("Something went wrong while updating metadata");
+ addErrorAlert(t("alerts.metadataUpdate.failed"));
} finally {
setIsLoading(false);
}
diff --git a/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx b/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx
index 437354e8a..062f8b566 100644
--- a/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx
+++ b/govtool/frontend/src/hooks/forms/useUrlAndHashFormController.tsx
@@ -3,6 +3,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import * as Yup from "yup";
import { HASH_REGEX, URL_REGEX } from "@utils";
+import { useTranslation } from "@hooks";
export interface UrlAndHashFormValues {
url?: string;
@@ -10,27 +11,37 @@ export interface UrlAndHashFormValues {
}
export const useUrlAndHashFormController = () => {
+ const { t } = useTranslation();
+
const validationSchema = useMemo(
() =>
Yup.object().shape({
url: Yup.string()
.trim()
- .max(64, "Url must be less than 65 characters")
- .test("url-validation", "Invalid URL format", (value) => {
- return !value || URL_REGEX.test(value);
- }),
+ .max(64, t("forms.errors.urlTooLong"))
+ .test(
+ "url-validation",
+ t("forms.errors.urlInvalidFormat"),
+ (value) => {
+ return !value || URL_REGEX.test(value);
+ }
+ ),
hash: Yup.string()
.trim()
.test(
"hash-length-validation",
- "Hash must be exactly 64 characters long",
+ t("forms.errors.hashInvalidLength"),
(value) => {
return !value || value.length === 64;
}
)
- .test("hash-format-validation", "Invalid hash format", (value) => {
- return !value || HASH_REGEX.test(value);
- }),
+ .test(
+ "hash-format-validation",
+ t("forms.errors.hashInvalidFormat"),
+ (value) => {
+ return !value || HASH_REGEX.test(value);
+ }
+ ),
}),
[]
);
diff --git a/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx b/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx
index 0dc5f768f..cf1a0cbe0 100644
--- a/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx
+++ b/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx
@@ -9,34 +9,45 @@ import { UrlAndHashFormValues } from "./useUrlAndHashFormController";
import { PATHS } from "@consts";
import { useCardano, useSnackbar } from "@context";
import { HASH_REGEX, URL_REGEX } from "@utils";
+import { useTranslation } from "@hooks";
export interface VoteActionFormValues extends UrlAndHashFormValues {
vote: string;
}
export const useVoteActionFormController = () => {
+ const { t } = useTranslation();
+
const validationSchema = useMemo(
() =>
Yup.object().shape({
vote: Yup.string().oneOf(["yes", "no", "abstain"]).required(),
url: Yup.string()
.trim()
- .max(64, "Url must be less than 65 characters")
- .test("url-validation", "Invalid URL format", (value) => {
- return !value || URL_REGEX.test(value);
- }),
+ .max(64, t("forms.errors.urlTooLong"))
+ .test(
+ "url-validation",
+ t("forms.errors.urlInvalidFormat"),
+ (value) => {
+ return !value || URL_REGEX.test(value);
+ }
+ ),
hash: Yup.string()
.trim()
.test(
"hash-length-validation",
- "Hash must be exactly 64 characters long",
+ t("forms.errors.hashInvalidLength"),
(value) => {
return !value || value.length === 64;
}
)
- .test("hash-format-validation", "Invalid hash format", (value) => {
- return !value || HASH_REGEX.test(value);
- }),
+ .test(
+ "hash-format-validation",
+ t("forms.errors.hashInvalidFormat"),
+ (value) => {
+ return !value || HASH_REGEX.test(value);
+ }
+ ),
}),
[]
);
@@ -124,6 +135,6 @@ export const useVoteActionForm = () => {
isDirty,
clearErrors,
areFormErrors,
- isLoading,
+ isVoteLoading: isLoading,
};
};
diff --git a/govtool/frontend/src/hooks/index.ts b/govtool/frontend/src/hooks/index.ts
index 04d84bc47..e17b56d3a 100644
--- a/govtool/frontend/src/hooks/index.ts
+++ b/govtool/frontend/src/hooks/index.ts
@@ -1,3 +1,4 @@
+export { useTranslation } from "react-i18next";
export * from "./useScreenDimension";
export * from "./useSlider";
export * from "./useSaveScrollPosition";
diff --git a/govtool/frontend/src/hooks/queries/useGetDRepInfoQuery.ts b/govtool/frontend/src/hooks/queries/useGetDRepInfoQuery.ts
index 2ac0e85a2..217aecde8 100644
--- a/govtool/frontend/src/hooks/queries/useGetDRepInfoQuery.ts
+++ b/govtool/frontend/src/hooks/queries/useGetDRepInfoQuery.ts
@@ -5,7 +5,7 @@ import { useCardano } from "@context";
import { getDRepInfo } from "@services";
export const useGetDRepInfo = () => {
- const { registerTransaction, dRepID } = useCardano();
+ const { dRepID, registerTransaction } = useCardano();
const { data, isLoading } = useQuery({
queryKey: [
diff --git a/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts b/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts
index 24667b2f9..cb85c8271 100644
--- a/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts
+++ b/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts
@@ -6,7 +6,7 @@ import { getDRepVotes } from "@services";
import { VotedProposal } from "@/models/api";
export const useGetDRepVotesQuery = (filters: string[], sorting: string) => {
- const { dRepID: dRepId, voteTransaction } = useCardano();
+ const { dRepID, voteTransaction } = useCardano();
const { data, isLoading, refetch, isRefetching } = useQuery({
queryKey: [
@@ -16,9 +16,9 @@ export const useGetDRepVotesQuery = (filters: string[], sorting: string) => {
sorting,
],
queryFn: async () => {
- return await getDRepVotes({ dRepId, filters, sorting });
+ return await getDRepVotes({ dRepID, filters, sorting });
},
- enabled: !!dRepId,
+ enabled: !!dRepID,
});
const groupedByType = data?.reduce((groups, item) => {
@@ -41,7 +41,7 @@ export const useGetDRepVotesQuery = (filters: string[], sorting: string) => {
title: string;
actions: VotedProposal[];
}[],
- dRepVotesAreLoading: isLoading,
+ areDRepVotesLoading: isLoading,
refetch,
isRefetching,
};
diff --git a/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts b/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts
index 554a81688..aade95029 100644
--- a/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts
+++ b/govtool/frontend/src/hooks/queries/useGetDRepVotingPowerQuery.ts
@@ -5,15 +5,15 @@ import { useCardano } from "@context";
import { getDRepVotingPower } from "@services";
export const useGetDRepVotingPowerQuery = () => {
- const { dRepID: dRepId } = useCardano();
+ const { dRepID } = useCardano();
const { data, isLoading } = useQuery({
queryKey: QUERY_KEYS.useGetDRepVotingPowerKey,
queryFn: async () => {
- return await getDRepVotingPower({ dRepId });
+ return await getDRepVotingPower({ dRepID });
},
- enabled: !!dRepId,
+ enabled: !!dRepID,
});
- return { data, isLoading };
+ return { dRepVotingPower: data, isDRepVotingPowerLoading: isLoading };
};
diff --git a/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts b/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts
index c25b04f29..897a44355 100644
--- a/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts
+++ b/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts
@@ -2,17 +2,23 @@ import { useInfiniteQuery } from "react-query";
import { QUERY_KEYS } from "@consts";
import { useCardano } from "@context";
-import { getProposals } from "@services";
+import { getProposals, getProposalsArguments } from "@services";
-export const useGetProposalsInfiniteQuery = (
- filters: string[],
- sorting: string,
- pageSize: number = 10
-) => {
- const { voteTransaction, isEnabled } = useCardano();
+export const useGetProposalsInfiniteQuery = ({
+ filters = [],
+ pageSize = 10,
+ sorting = "",
+}: getProposalsArguments) => {
+ const { dRepID, isEnabled, voteTransaction } = useCardano();
const fetchProposals = async ({ pageParam = 0 }) => {
- return await getProposals(filters, sorting, pageParam, pageSize);
+ return await getProposals({
+ dRepID,
+ filters,
+ page: pageParam,
+ pageSize,
+ sorting,
+ });
};
const {
@@ -29,6 +35,7 @@ export const useGetProposalsInfiniteQuery = (
sorting,
voteTransaction.proposalId,
isEnabled,
+ dRepID,
],
fetchProposals,
{
@@ -47,11 +54,11 @@ export const useGetProposalsInfiniteQuery = (
) as ActionType[];
return {
+ proposalsfetchNextPage: fetchNextPage,
+ proposalsHaveNextPage: hasNextPage,
+ isProposalsFetching: isFetching,
+ isProposalsFetchingNextPage: isFetchingNextPage,
+ isProposalsLoading: isLoading,
proposals,
- isLoading,
- fetchNextPage,
- hasNextPage,
- isFetching,
- isFetchingNextPage,
};
};
diff --git a/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts b/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts
index 8273dc1b3..88c4a301e 100644
--- a/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts
+++ b/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts
@@ -2,40 +2,38 @@ import { useQuery } from "react-query";
import { QUERY_KEYS } from "@consts";
import { useCardano } from "@context";
-import { getProposals } from "@services";
+import { getProposals, getProposalsArguments } from "@services";
-export const useGetProposalsQuery = (
- filters: string[],
- sorting: string
-): {
- proposals: ActionType[];
- isLoading: boolean;
-} => {
- const { voteTransaction, isEnabled } = useCardano();
+export const useGetProposalsQuery = ({
+ filters = [],
+ sorting,
+}: getProposalsArguments) => {
+ const { dRepID, isEnabled, voteTransaction } = useCardano();
- const fetchProposals = async () => {
+ const fetchProposals = async (): Promise => {
const allProposals = await Promise.all(
- filters.map((filter) => getProposals([filter], sorting))
+ filters.map((filter) =>
+ getProposals({ dRepID, filters: [filter], sorting })
+ )
);
- return allProposals.flatMap((proposal) => proposal.elements || []);
+ return allProposals.flatMap((proposal) => proposal.elements);
};
- const request = useQuery(
+ const { data, isLoading } = useQuery(
[
QUERY_KEYS.useGetProposalsKey,
filters,
sorting,
voteTransaction.proposalId,
isEnabled,
+ dRepID,
],
fetchProposals
);
- const proposals = request.data as ActionType[];
-
return {
- proposals,
- isLoading: request.isLoading,
+ isProposalsLoading: isLoading,
+ proposals: data,
};
};
diff --git a/govtool/frontend/src/i18n/index.ts b/govtool/frontend/src/i18n/index.ts
new file mode 100644
index 000000000..035c38ccc
--- /dev/null
+++ b/govtool/frontend/src/i18n/index.ts
@@ -0,0 +1,16 @@
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+
+import { en } from "./locales/en";
+
+i18n.use(initReactI18next).init({
+ resources: {
+ en,
+ },
+ fallbackLng: "en",
+ interpolation: {
+ escapeValue: false,
+ },
+});
+
+export default i18n;
diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts
new file mode 100644
index 000000000..876dd71b0
--- /dev/null
+++ b/govtool/frontend/src/i18n/locales/en.ts
@@ -0,0 +1,380 @@
+export const en = {
+ translation: {
+ alerts: {
+ delegation: {
+ failed: "Delegation transaction failed",
+ refreshPage:
+ "Your voting power has been successfully delegated! Please refresh the page.",
+ success: "Your voting power has been successfully delegated!",
+ },
+ metadataUpdate: {
+ failed: "Update DRep metadata transaction failed",
+ success: "You have successfully updated DRep metadata!",
+ },
+ registration: {
+ failed: "Registration transaction failed",
+ refreshPage:
+ "You have successfully registered as a DRep! Please refresh the page.",
+ success: "You have successfully registered as a DRep!",
+ },
+ retirement: {
+ failed: "Retirement transaction failed",
+ refreshPage:
+ "You have successfully retired from being a DRep! Please refresh the page.",
+ success: "You have successfully retired from being a DRep!",
+ },
+ voting: {
+ failed: "Vote transaction failed",
+ success: "You have successfully voted!",
+ },
+ changesSaved: "Your changes have been saved",
+ copiedToClipboard: "Copied to clipboard",
+ transactionInProgress: "Transaction in progress. Please wait.",
+ walletConnected: "Wallet connected",
+ },
+ dashboard: {
+ headingOne: "Your Participation",
+ headingTwo: "See Active Governance Actions",
+ delegation: {
+ changeDelegation: "Change delegation",
+ connectToDelegate: "Connect to delegate",
+ delegateOwnPower:
+ "If you want to delegate your own voting power of ₳{{ada}} .",
+ description:
+ "If you want to delegate to a DRep or select a default option.",
+ dRepDelegatedTo: "DRep you delegated to",
+ toDRep:
+ "You have delegated your voting power of ₳{{ada}} to a selected DRep.",
+ toYourself:
+ "You have delegated your voting power of ₳{{ada}} to yourself.",
+ useYourVotingPower: "Use your Voting Power",
+ voteAbstain:
+ "You have delegated your voting power of ₳{{ada}} . You are going to vote 'ABSTAIN' as default.",
+ voteNo:
+ "You have delegated your voting power of ₳{{ada}} . You are going to vote 'NO' as default.",
+ votingPowerDelegation: "Voting Power Delegation",
+ yourVotingPowerIsDelegated:
+ "Your Voting Power is Delegated ",
+ inProgress: {
+ toDRep:
+ "Your own voting power of ₳{{ada}} is progress of being delegated. You are going to delegate your voting power to a selected DRep.",
+ toYourself:
+ "Your own voting power of ₳{{ada}} is in progress of being delegated. You are going to delegate your voting power to yourself.",
+ voteAbstain:
+ "Your own voting power of ₳{{ada}} is in progress of being delegated. You are going to vote ‘ABSTAIN’ as default.",
+ voteNo:
+ "Your own voting power of ₳{{ada}} is in progress of being delegated. You are going to vote ‘NO’ as default.",
+ },
+ },
+ govActions: {
+ description: "Review governance actions submitted on-chain.",
+ reviewAndVote: "Review and vote",
+ title: "Governance Actions",
+ view: "View governance actions",
+ },
+ registration: {
+ changeMetadata: "Change metadata",
+ connectToRegister: "Connect to register",
+ dRepRegistration: "DRep Registration",
+ dRepRetirement: "DRep Retirement",
+ dRepUpdate: "DRep Update",
+ description:
+ "If you want to directly participate in voting and have other ada holders delegate their voting power to you.",
+ holdersCanDelegate:
+ "Ada holders can delegate their voting power to you.",
+ ifYouWant:
+ "If you want to directly participate in voting and have other ada holders delegate their voting power to you.",
+ metadataUpdateInProgress:
+ "The update DRep metadata is ongoing. This may take several minutes.",
+ register: "Register",
+ registerAgain: "Register Again as a dRep",
+ registerAsDRep: "Register as a DRep",
+ registrationInProgress:
+ "The registration process is ongoing. This may take several minutes.",
+ retire: "Retire as a DRep",
+ retirementInProgress:
+ "The retirement process is ongoing. This may take several minutes.",
+ youAreRegistered: "You are Registered as a DRep",
+ },
+ },
+ delegation: {
+ description:
+ "You can delegate your voting power to a DRep or to a pre-defined voting option.",
+ dRepIdDescription: "The DRep ID is the identifier of a DRep.",
+ heading: "Use your Voting Power",
+ otherOptions: "Other options",
+ pasteDRepId: "Paste DRep ID",
+ votingPowerToDelegate: "Voting power to delegate:",
+ whereFindDRepId: "The DRep ID is the identifier of a DRep.",
+ abstain: {
+ subtitle: "Select this to vote ABSTAIN to every vote.",
+ title: "Vote ABSTAIN as default",
+ },
+ noConfidence: {
+ subtitle:
+ "Select this to signal no confidence in the current constitutional committee by voting NO on every proposal and voting YES to no-confidence proposals.",
+ title: "Signal no confidence",
+ },
+ toDRep: {
+ subtitle:
+ "Select this to delegate to a DRep using their related DRep ID.",
+ title: "Delegate to DRep",
+ },
+ toMyself: {
+ subtitle: "Select this to delegate your own voting power to yourself.",
+ title: "Delegate to myself",
+ },
+ },
+ errorPage: {
+ backToDashboard: "Back to dashboard",
+ backToHomepage: "Back to homepage",
+ error: "Error ",
+ serverError: "We have an internal server error.",
+ whoops: "Whoops!",
+ },
+ errors: {
+ appCannotCreateTransaction: "Application can not create transaction.",
+ appCannotGetUtxos: "Application can not get utxos",
+ checkIsWalletConnected: "Check if the wallet is connected.",
+ dRepIdNotFound: "DrepId not found",
+ notUsingAnchor: "DRep Registration - not using anchor",
+ noAddressesFound: "No addresses found",
+ noStakeKeySelected: "No stake key selected",
+ registeringStakeKey: "Registering stake key",
+ somethingWentWrong: "Something went wrong",
+ useCardano: "useCardano must be used within a CardanoProvider",
+ tryingConnectTo:
+ "You are trying to connect with a wallet connected to {{networkFrom}}. Please adjust your wallet settings to connect to {{networkTo}} or select a different wallet.",
+ walletNoCIP30Nor90Support:
+ "Your wallet does not support the required CIP-30 extension, CIP-95.",
+ walletNoCIP30Support: "Your wallet does not support CIP-30 extensions.",
+ walletNoCIP90FunctionsEnabled:
+ "Your wallet did not enable the needed CIP-95 functions during connection.",
+ },
+ footer: {
+ copyright: "© 2023 Voltaire Gov Tool",
+ privacyPolicy: "Privacy policy",
+ },
+ forms: {
+ hashPlaceholder: "The hash of your URL",
+ howCreateUrlAndHash: "How to create URL and hash?",
+ urlWithContextPlaceholder: "Your URL with with your context",
+ urlWithInfoPlaceholder: "Your URL with extra info about you",
+ errors: {
+ hashInvalidFormat: "Invalid hash format",
+ hashInvalidLength: "Hash must be exactly 64 characters long",
+ urlTooLong: "Url must be less than 65 characters",
+ urlInvalidFormat: "Invalid URL format",
+ },
+ },
+ govActions: {
+ changeVote: "Change vote",
+ changeYourVote: "Change your vote",
+ chooseHowToVote: "Choose how you want to vote:",
+ details: "Governance Details:",
+ expiryDate: "Expiry date:",
+ filterTitle: "Governance Action Type",
+ forGovAction: "for this Governance Action",
+ governanceActionId: "Governance Action ID:",
+ governanceActionType: "Governance Action Type:",
+ myVote: "My Vote:",
+ noResultsForTheSearch: "No results for the search.",
+ optional: "(optional)",
+ provideContext: "Provide context about your vote",
+ selectDifferentOption: "Select a different option to change your vote",
+ showVotes: "Show votes",
+ submissionDate: "Submission date:",
+ title: "Governance Actions",
+ toVote: "To vote",
+ viewOtherDetails: "View other details",
+ viewProposalDetails: "View proposal details",
+ vote: "Vote",
+ voted: "Voted",
+ voteOnGovActions: "Vote on Governance Action",
+ voteSubmitted: "Vote submitted",
+ voteTransaction: "Vote transaction",
+ votes: "Votes:",
+ votesSubmitted: "Votes submitted",
+ votesSubmittedOnChain:
+ "Votes submitted on-chain by DReps, SPOs and Constitutional Committee members.",
+ youHaventVotedYet:
+ "You haven't voted on any Governance Actions yet. Check the 'To vote on' section to vote on Governance Actions.",
+ withCategoryNotExist: {
+ partOne: "Governnance actions with category",
+ optional: "and search phrase",
+ partTwo: "don't exist.",
+ },
+ withIdNotExist: {
+ partOne: "Governance action with id",
+ partTwo: "does not exist.",
+ },
+ },
+ hero: {
+ connectWallet: "Connect your wallet",
+ description:
+ "Interact with SanchoNet using GovTool - a friendly user\ninterface connected to SanchoNet. You can delegate\nyour voting power (tAda) or become a SanchoNet DRep\nto allow people to delegate voting power to you.",
+ headline: "SanchoNet \n Governance Tool",
+ },
+ menu: {
+ faqs: "FAQs",
+ guides: "Guides",
+ help: "Help",
+ myDashboard: "My Dashboard",
+ viewGovActions: "View Governance Actions",
+ },
+ metadataUpdate: {
+ description:
+ "You can include extra information about yourself by adding a URL and its hash.",
+ info: "Update Information",
+ title: "Update DRep Metadata",
+ },
+ modals: {
+ common: {
+ goToDashboard: "Go to Dashboard",
+ oops: "Oops!",
+ },
+ delegation: {
+ message:
+ "The confirmation of your actual delegation might take a bit of time but you can track it using",
+ title: "Delegation Transaction Submitted!",
+ },
+ externalLink: {
+ beCareful: "Be Careful!",
+ continueTo: "Continue to external link",
+ description:
+ "Exercise caution and verify the website's authenticity before sharing personal information. To proceed, click 'Continue'. To stay on Voltaire, click 'Cancel'.",
+ safety: "External Link Safety",
+ thisIs: "This is an external link:",
+ youAreAboutToOpen: "You are about to open an external link to:",
+ },
+ registration: {
+ message:
+ "The confirmation of your registration might take a bit of time but you can track it using",
+ title: "Registration Transaction Submitted!",
+ },
+ retirement: {
+ message:
+ "The confirmation of your retirement might take a bit of time but you can track it using",
+ title: "Retirement Transaction Submitted!",
+ },
+ votingPower: {
+ govActionsVotes: "Governance Action votes",
+ votesSubmittedByDReps: "Votes submitted by DReps",
+ yourVote: "Your vote",
+ },
+ waitForTransaction: {
+ title: "Please wait for your previous transaction to be completed.",
+ message:
+ "Before performing a new action please wait for the previous action transaction to be completed.",
+ },
+ },
+ registration: {
+ descriptionStepOne:
+ "You can include extra information about yourself by adding a URL and its hash.",
+ 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.",
+ headingStepOne: "Add Information",
+ headingStepTwo: "Confirm DRep registration",
+ optional: "OPTIONAL",
+ register: "Register",
+ registerAsDRep: "Register as a DRep",
+ },
+ slider: {
+ showAll: "Show all",
+ viewAll: "View all",
+ },
+ system: {
+ sanchoNet: "SanchoNet",
+ sanchoNetIsBeta:
+ "The SanchoNet GovTool is currently in beta and it connects to ",
+ testAdaNote:
+ "Please note, this tool uses ‘Test ada’ <0>NOT real ada0>. All governance actions and related terms pertain to SanchoNet.",
+ toolConnectedToSanchonet: "This tool is connected to SanchoNet",
+ },
+ tooltips: {
+ delegateTodRep: {
+ abstain: {
+ heading: "Abstaining",
+ paragraphOne:
+ "Select this to signal no confidence in the current constitutional committee by voting NO on every proposal and voting YES to no-confidence proposals.",
+ },
+ noConfidence: {
+ heading: "No confidence",
+ paragraphOne:
+ "If you don’t have trust in the current constitutional committee you signal ‘No-confidence’. By voting ‘No’ means you don’t want governance actions to be ratified.",
+ },
+ todRep: {
+ heading: "Delegation to DRep",
+ paragraphOne:
+ "DReps are representatives of the ada holders that can vote on governance actions.",
+ },
+ toMyself: {
+ heading: "Delegate to myself",
+ paragraphOne:
+ "If you are registered as DRep you can delegate your voting power on yourself.",
+ },
+ },
+ expiryDate: {
+ heading: "Expiry Date",
+ paragraphOne:
+ "The date when the governance action will expiry if it doesn’t reach ratification thresholds.",
+ paragraphTwo:
+ "IMPORTANT: If the governance action is ratified before the expiry date it will be considered ratified and it will not be available to vote on afterwards.",
+ },
+ submissionDate: {
+ heading: "Submission Date",
+ paragraphOne:
+ "The date when the governance action was submitted on-chain.",
+ },
+ votingPower: {
+ heading: "DRep Voting Power",
+ paragraphOne:
+ "This is the voting power delegated to you as a DRep and it is calculated at the end of every epoch for the epoch that just ended.",
+ paragraphTwo:
+ "IMPORTANT: When voting, the voting power provides an indication and not the exact number.",
+ },
+ },
+ wallet: {
+ cantSeeWalletQuestion:
+ "Can’t see your wallet? Check what wallets are currently compatible with GovTool ",
+ chooseWallet: "Choose the wallet you want to connect with:",
+ connect: "Connect",
+ connectWallet: "Connect wallet",
+ connectYourWallet: "Connect your Wallet",
+ connectYourWalletButton: "Connect your wallet",
+ connectedWallet: "Connected Wallet:",
+ disconnect: "Disconnect",
+ noWalletsToConnect:
+ "You don't have wallets to connect, install a wallet and refresh the page and try again",
+ pickStakeKey: "Pick Stake Key",
+ selectStakeKey: "Select the stake key you want to use:",
+ },
+ warnings: {
+ usingUnregisteredStakeKeys:
+ "Warning, no registered stake keys, using unregistered stake keys",
+ },
+ abstain: "Abstain",
+ back: "Back",
+ backToList: "Back to the list",
+ cancel: "Cancel",
+ clear: "Clear",
+ confirm: "Confirm",
+ continue: "Continue",
+ delegate: "Delegate",
+ here: "here",
+ inProgress: "In progress",
+ learnMore: "Learn more",
+ loading: "Loading...",
+ myDRepId: "My DRep ID:",
+ nextStep: "Next step",
+ no: "No",
+ ok: "Ok",
+ select: "Select",
+ seeTransaction: "See transaction",
+ skip: "Skip",
+ sortBy: "Sort by",
+ thisLink: "this link",
+ votingPower: "Voting power:",
+ yes: "Yes",
+ },
+};
diff --git a/govtool/frontend/src/main.tsx b/govtool/frontend/src/main.tsx
index 206c03e80..6997c7a12 100644
--- a/govtool/frontend/src/main.tsx
+++ b/govtool/frontend/src/main.tsx
@@ -1,9 +1,5 @@
import React from "react";
import ReactDOM from "react-dom/client";
-import App from "./App.tsx";
-import { ThemeProvider } from "@emotion/react";
-import { ContextProviders } from "@context";
-import { theme } from "./theme.ts";
import {
BrowserRouter,
createRoutesFromChildren,
@@ -12,8 +8,15 @@ import {
useNavigationType,
} from "react-router-dom";
import { QueryClient, QueryClientProvider } from "react-query";
-import * as Sentry from "@sentry/react";
import TagManager from "react-gtm-module";
+import { ThemeProvider } from "@emotion/react";
+import * as Sentry from "@sentry/react";
+
+import { ContextProviders } from "@context";
+
+import App from "./App.tsx";
+import { theme } from "./theme.ts";
+import "./i18n";
const queryClient = new QueryClient();
diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx
index c6b27dadc..5097fd091 100644
--- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx
+++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx
@@ -1,7 +1,7 @@
-import { useState, useCallback, useMemo, useRef } from "react";
+import { useCallback, useMemo, useRef, useState } from "react";
import {
- NavLink,
generatePath,
+ NavLink,
useNavigate,
useParams,
} from "react-router-dom";
@@ -18,59 +18,61 @@ import { ICONS, PATHS } from "@consts";
import { useCardano } from "@context";
import { DataActionsBar, GovernanceActionCard } from "@molecules";
import {
- useGetDRepVotesQuery,
- useGetProposalsInfiniteQuery,
useFetchNextPageDetector,
+ useGetProposalsInfiniteQuery,
useSaveScrollPosition,
useScreenDimension,
+ useTranslation,
} from "@hooks";
import {
getFullGovActionId,
- openInNewTab,
getProposalTypeLabel,
+ openInNewTab,
removeDuplicatedProposals,
} from "@utils";
-export const DashboardGovernanceActionsCategory = ({}) => {
+export const DashboardGovernanceActionsCategory = () => {
const { category } = useParams();
const [searchText, setSearchText] = useState("");
const [sortOpen, setSortOpen] = useState(false);
const [chosenSorting, setChosenSorting] = useState("");
const { isMobile, screenWidth } = useScreenDimension();
const navigate = useNavigate();
- const { dRep, voteTransaction } = useCardano();
-
- const { data: dRepVotes } = useGetDRepVotesQuery([], "");
+ const { dRep, isDrepLoading, voteTransaction } = useCardano();
+ const { t } = useTranslation();
const {
+ isProposalsFetching,
+ isProposalsFetchingNextPage,
+ isProposalsLoading,
proposals,
- fetchNextPage,
- hasNextPage,
- isFetching,
- isFetchingNextPage,
- isLoading,
- } = useGetProposalsInfiniteQuery(
- [category?.replace(/ /g, "") ?? ""],
- chosenSorting
- );
+ proposalsfetchNextPage,
+ proposalsHaveNextPage,
+ } = useGetProposalsInfiniteQuery({
+ filters: [category?.replace(/ /g, "") ?? ""],
+ sorting: chosenSorting,
+ });
const loadNextPageRef = useRef(null);
useFetchNextPageDetector(
- fetchNextPage,
- isLoading || isFetchingNextPage,
- hasNextPage
+ proposalsfetchNextPage,
+ isProposalsLoading || isProposalsFetchingNextPage,
+ proposalsHaveNextPage
);
- const saveScrollPosition = useSaveScrollPosition(isLoading, isFetching);
+ const saveScrollPosition = useSaveScrollPosition(
+ isProposalsLoading,
+ isProposalsFetching
+ );
const breadcrumbs = [
- Governance Actions
+ {t("govActions.title")}
,
@@ -81,33 +83,12 @@ export const DashboardGovernanceActionsCategory = ({}) => {
const mappedData = useMemo(() => {
const uniqueProposals = removeDuplicatedProposals(proposals);
- if (dRep?.isRegistered && dRepVotes) {
- const filteredBySearchPhrase = uniqueProposals?.filter((i) =>
- getFullGovActionId(i.txHash, i.index)
- .toLowerCase()
- .includes(searchText.toLowerCase())
- );
-
- const filteredData = filteredBySearchPhrase?.filter((i) => {
- return !dRepVotes
- .flatMap((item) => item.actions.map((item) => item.proposal.id))
- .includes(i.id);
- });
-
- return filteredData;
- }
return uniqueProposals?.filter((i) =>
getFullGovActionId(i.txHash, i.index)
.toLowerCase()
.includes(searchText.toLowerCase())
);
- }, [
- proposals,
- dRep?.isRegistered,
- dRepVotes,
- searchText,
- isFetchingNextPage,
- ]);
+ }, [proposals, dRep?.isRegistered, searchText, isProposalsFetchingNextPage]);
const closeSorts = useCallback(() => {
setSortOpen(false);
@@ -116,16 +97,16 @@ export const DashboardGovernanceActionsCategory = ({}) => {
return (
{
style={{ marginRight: "12px", transform: "rotate(180deg)" }}
/>
- Back to the list
+ {t("backToList")}
- {!isLoading ? (
- !mappedData?.length ? (
-
-
-
- Governnance actions with category
-
- {` ${category} `}
- don't exist.
-
-
- ) : (
-
- {mappedData.map((item) => (
-
- {
- saveScrollPosition();
+ {isProposalsLoading || isDrepLoading ? (
+
+
+
+ ) : !mappedData?.length ? (
+
+
+
+ {t("govActions.withCategoryNotExist.partOne")}
+
+ {` ${category} `}
+
+ {t("govActions.withCategoryNotExist.partTwo")}
+
+
+
+ ) : (
+
+ {mappedData.map((item) => (
+
+ {
+ saveScrollPosition();
- voteTransaction.proposalId ===
- item.txHash + item.index
- ? openInNewTab(
- "https://adanordic.com/latest_transactions"
- )
- : navigate(
- generatePath(
- PATHS.dashboard_governance_actions_action,
- {
- proposalId: getFullGovActionId(
- item.txHash,
- item.index
- ),
- }
- ),
+ voteTransaction.proposalId === item.txHash + item.index
+ ? openInNewTab(
+ "https://adanordic.com/latest_transactions"
+ )
+ : navigate(
+ generatePath(
+ PATHS.dashboard_governance_actions_action,
{
- state: {
- ...item,
- openedFromCategoryPage: true,
- },
+ proposalId: getFullGovActionId(
+ item.txHash,
+ item.index
+ ),
}
- );
- }}
- />
-
- ))}
- {hasNextPage && isFetchingNextPage && (
-
-
-
- )}
-
- )
- ) : (
-
-
+ ),
+ {
+ state: {
+ ...item,
+ openedFromCategoryPage: true,
+ },
+ }
+ );
+ }}
+ txHash={item.txHash}
+ />
+
+ ))}
+ {proposalsHaveNextPage && isProposalsFetchingNextPage && (
+
+
+
+ )}
)}
diff --git a/govtool/frontend/src/pages/DelegateTodRep.tsx b/govtool/frontend/src/pages/DelegateTodRep.tsx
index 1c2c20332..8749bb225 100644
--- a/govtool/frontend/src/pages/DelegateTodRep.tsx
+++ b/govtool/frontend/src/pages/DelegateTodRep.tsx
@@ -9,7 +9,7 @@ import {
DelegateTodRepStepTwo,
Footer,
} from "@organisms";
-import { useScreenDimension } from "@hooks";
+import { useScreenDimension, useTranslation } from "@hooks";
import { WALLET_LS_KEY, getItemFromLocalStorage } from "@/utils/localStorage";
import { useNavigate } from "react-router-dom";
@@ -17,6 +17,7 @@ export const DelegateTodRep = () => {
const [step, setStep] = useState(1);
const { isMobile } = useScreenDimension();
const navigate = useNavigate();
+ const { t } = useTranslation();
useEffect(() => {
if (
@@ -34,7 +35,7 @@ export const DelegateTodRep = () => {
imageSRC={ICONS.appLogoIcon}
imageWidth={isMobile ? undefined : 42}
imageHeight={isMobile ? 24 : 35}
- title={"Delegate to DRep"}
+ title={t("delegation.toDRep.title")}
/>
@@ -40,19 +41,22 @@ export const ErrorPage = ({
lineHeight={"64px"}
sx={{ whiteSpace: "nowrap" }}
>
- Whoops!
+ {t("errorPage.whoops")}
{state && state.errorCode === 500
- ? "We have an internal server error."
+ ? t("errorPage.serverError")
: errorDescription}
- Error {state ? state.errorCode : errorCode}
+ {t("errorPage.error")}
+ {state ? state.errorCode : errorCode}
{isButton && (
navigate(PATHS.home)}>
- {isEnabled ? "Back to dashboard" : "Back to homepage"}
+ {isEnabled
+ ? t("errorPage.backToDashboard")
+ : t("errorPage.backToHomepage")}
)}