From f02a529b2d832136ee0338f5f7c541a9031e2da5 Mon Sep 17 00:00:00 2001 From: DinhTran Date: Thu, 28 Mar 2024 10:09:03 +0700 Subject: [PATCH 1/3] feat: integrate api chart pool --- src/commons/utils/api.ts | 1 + src/commons/utils/helper.ts | 4 + src/components/GovernanceVotes/index.tsx | 202 ++++++++++++++++++++--- src/locales/en/translation.json | 3 + src/pages/DelegationDetail/index.tsx | 4 +- 5 files changed, 194 insertions(+), 20 deletions(-) diff --git a/src/commons/utils/api.ts b/src/commons/utils/api.ts index 54e678c4a3..f6119257e4 100644 --- a/src/commons/utils/api.ts +++ b/src/commons/utils/api.ts @@ -32,6 +32,7 @@ export const API = { POOL: "pools", POOL_CERTIFICATE: { POOL: "gov-actions", + POOL_CHART: "gov-actions/voting-chart", POOL_DETAIL: (poolId: string) => `gov-actions/${poolId}/voting-procedure-detail` }, POOL_CERTIFICATES_HISTORY: "pools/certificates-history", diff --git a/src/commons/utils/helper.ts b/src/commons/utils/helper.ts index e7f836e232..c7b3a2b190 100644 --- a/src/commons/utils/helper.ts +++ b/src/commons/utils/helper.ts @@ -34,6 +34,10 @@ export const getShortValue = (address = "", length = 50) => { return address.slice(0, length); }; +export const getShortNumber = (number = 0, length = 3) => { + return Number(number.toFixed(length)); +}; + export const LARGE_NUMBER_ABBREVIATIONS = ["", "K", "M", "B", "T", "q", "Q", "s", "S"]; export const formatPrice = (value?: string | number, abbreviations: string[] = LARGE_NUMBER_ABBREVIATIONS): string => { diff --git a/src/components/GovernanceVotes/index.tsx b/src/components/GovernanceVotes/index.tsx index 3c2dddcf95..59313ee425 100644 --- a/src/components/GovernanceVotes/index.tsx +++ b/src/components/GovernanceVotes/index.tsx @@ -27,11 +27,13 @@ import { styled, tooltipClasses, useTheme, - ClickAwayListener + ClickAwayListener, + Link } from "@mui/material"; import { IoIosArrowDown, IoIosArrowUp } from "react-icons/io"; import moment from "moment"; import { isUndefined, omitBy } from "lodash"; +import { JsonViewer } from "@textea/json-viewer"; import { ActionTypeIcon, @@ -54,7 +56,7 @@ import { } from "src/commons/resources"; import { API } from "src/commons/utils/api"; import { POOLS_ACTION_TYPE, VOTE_TYPE, STATUS_VOTE } from "src/commons/utils/constants"; -import { getShortHash } from "src/commons/utils/helper"; +import { getShortHash, getShortNumber } from "src/commons/utils/helper"; import CardGovernanceVotes, { GovernanceStatus, VoteStatus } from "src/components/commons/CardGovernanceVotes"; import CopyButton from "src/components/commons/CopyButton"; import CustomIcon from "src/components/commons/CustomIcon"; @@ -85,12 +87,29 @@ import { } from "../DelegationDetail/DelegationDetailInfo/styles"; import { TimeDuration } from "../TransactionLists/styles"; import NoRecord from "../commons/NoRecord"; +import { ViewJson } from "../ScriptModal/styles"; interface DelegationGovernanceVotesProps { hash: string; type: VOTE_TYPE.DREP_KEY_HASH | VOTE_TYPE.STAKING_POOL_KEY_HASH; } +interface GovernanceVoteChart { + txHash: string | null; + index: number | null; + numberOfYesVote: number; + numberOfNoVotes: number; + numberOfAbstainVotes: number; + votingChartsList: VotingChart[]; +} + +interface VotingChart { + voterType: string; + numberOfYesVote: number; + numberOfNoVotes: number; + numberOfAbstainVotes: number; +} + const DelegationGovernanceVotes: React.FC = ({ hash, type }) => { const { search } = useLocation(); const history = useHistory(); @@ -111,11 +130,13 @@ const DelegationGovernanceVotes: React.FC = ({ h voterType: type }); }, [JSON.stringify(query)]); + const { data, total, lastUpdated, loading, initialized } = useFetchList( `${API.POOL_CERTIFICATE.POOL}/${hash}`, omitBy(params, isUndefined), false ); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const setQuery = (query: any) => { history.replace({ search: stringify(query) }, history.location.state); @@ -198,6 +219,7 @@ const GovernanceVotesDetail: React.FC<{ }> = ({ hash, voteId, type }) => { const theme = useTheme(); const [openHistoryVoteModal, setOpenHistoryVoteModal] = useState(false); + const [openActionMetadataModal, setOpenActionMetadataModal] = useState(false); const { t } = useTranslation(); const history = useHistory(); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -213,9 +235,26 @@ const GovernanceVotesDetail: React.FC<{ voterType: type })}` ); - const [tab, setTab] = useState("pool"); - const handleTabChange = (newTab: string) => { - setTab(newTab); + + const { data: dataChart } = useFetch( + `${API.POOL_CERTIFICATE.POOL_CHART}?${stringify({ + txHash: voteId, + index: 0 + })}` + ); + + const filterDataChart = (selectVote: string) => { + switch (selectVote) { + case "SPOs": + return dataChart?.votingChartsList.filter((i) => i.voterType === "STAKING_POOL_KEY_HASH")[0]; + case "DRops": + return dataChart?.votingChartsList.filter((i) => i.voterType === "DREP_KEY_HASH")[0]; + case "CC": + return dataChart?.votingChartsList.filter((i) => i.voterType === "CONSTITUTIONAL_COMMITTEE_HOT_KEY_HASH")[0]; + + default: + return dataChart; + } }; const actionType = (type: string) => { @@ -234,6 +273,11 @@ const GovernanceVotesDetail: React.FC<{ } }; + const [tab, setTab] = useState("pool"); + const handleTabChange = (newTab: string) => { + setTab(newTab); + }; + const TabButton: React.FC = ({ tabName, title }) => { return ( {t("pool.actionType")} - {data?.govActionType} + {actionType(data?.govActionType || "")} @@ -376,7 +421,7 @@ const GovernanceVotesDetail: React.FC<{ color: theme.isDark ? theme.palette.secondary.main : theme.palette.secondary.light }} label={selectVote || i} - onClick={() => setSelectVote(i)} + onClick={() => setSelectVote(selectVote ? "" : i)} /> ))} {selectVote && ( @@ -404,7 +449,7 @@ const GovernanceVotesDetail: React.FC<{ ) : ( - + )} @@ -418,7 +463,7 @@ const GovernanceVotesDetail: React.FC<{ {t("pool.currentStatus")} - + @@ -442,21 +487,32 @@ const GovernanceVotesDetail: React.FC<{ {t("pool.submission")} - {data?.submissionDate} + {moment(data?.submissionDate).format("MM/DD/YYYY HH:mm:ss")} {t("pool.expiryDate")} - {data?.expiryDate} + {moment(data?.expiryDate).format("DD/MM/YYYY")} {t("pool.anchorText")} - Whatever the anchor text string is for this action + + + setOpenHistoryVoteModal(false)} /> + setOpenActionMetadataModal(false)} + /> ); @@ -494,7 +557,7 @@ const VoteBar = ({ return ( - {percentage}% + {!percentage ? "0" : percentage}% - + {label} @@ -520,20 +583,37 @@ const VoteBar = ({ ); }; -const VoteRate = () => { +const VoteRate = ({ data }: { data?: GovernanceVoteChart | VotingChart | null }) => { const { t } = useTranslation(); const theme = useTheme(); + const totalVotes = Number( + (data?.numberOfYesVote || 0) + (data?.numberOfNoVotes || 0) + (data?.numberOfAbstainVotes || 0) + ); + const yesPercentage = ((data?.numberOfYesVote || 0) / totalVotes) * 100; + const noPercentage = ((data?.numberOfNoVotes || 0) / totalVotes) * 100; + const abstainPercentage = ((data?.numberOfAbstainVotes || 0) / totalVotes) * 100; + return ( - } label={t("common.yes")} /> } + label={t("common.yes")} + /> + } label={t("common.abstain")} /> - } label={t("common.no")} /> + } + label={t("common.no")} + /> ); }; @@ -661,6 +741,89 @@ const VoteHistoryModal: React.FC = ({ onClose, open, data }) = ); }; +interface ActionMetadataProps { + onClose?: () => void; + open: boolean; + anchorHash?: string; + anchorUrl?: string; + data?: { + type: string; + govActionId: string | null; + quorumThreshold: number; + membersForRemoval: string[]; + newMembersAndTerms: Record; + }; +} + +const ActionMetadataModal: React.FC = ({ onClose, open, data, anchorHash, anchorUrl }) => { + const { t } = useTranslation(); + const theme = useTheme(); + + return ( + onClose?.()} + title={t("pool.actionMetadata")} + width={500} + sx={{ position: "relative", height: "70vh" }} + > + + + {t("pool.anchor")}: + {" "} + + + {anchorHash} + + + {anchorUrl} + + + + + {t("pool.metadata")}:{" "} + + + + + + + + ); +}; + interface TabButtonProps { tabName: string; title: string; @@ -714,6 +877,7 @@ const FilterGovernanceVotes: React.FC = ({ query, setQuer page: 1, size: 6, tab: "governanceVotes", + governanceActionTxHash: "", actionType: STATUS_VOTE.ALL, actionStatus: STATUS_VOTE.ANY, voteType: STATUS_VOTE.ANY, @@ -729,7 +893,7 @@ const FilterGovernanceVotes: React.FC = ({ query, setQuer isRepeatVote: params?.isRepeatVote, page: 1, size: 6, - id: params?.id, + governanceActionTxHash: params?.id, anchorText: params?.anchorText, actionType: params?.actionType, actionStatus: params?.currentStatus, diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 93962111af..b1d2c50fd3 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -1030,6 +1030,9 @@ "pool.Infor": "Infor", "pool.tabPool": "The vote cast on the blockchain by Pool Name.", "pool.overall": "Votes cast on the blockchain by Delegated Representatives (DReps), Stake Pool Operators (SPOs), and members of the Constitutional Committee.", + "pool.actionMetadata": "Governance Action Metadata", + "pool.anchor": "Anchor", + "pool.metadata": "Metadata", "smartContract.plutusDes.title": "What Are Plutus Scripts?", "smartContract.plutusDes.desc1": "Plutus is the native smart contract language for Cardano.", "smartContract.plutusDes.desc2": "is the initial version of Plutus, introduced in the Alonzo hard fork", diff --git a/src/pages/DelegationDetail/index.tsx b/src/pages/DelegationDetail/index.tsx index 019d30f6a2..e68659077b 100644 --- a/src/pages/DelegationDetail/index.tsx +++ b/src/pages/DelegationDetail/index.tsx @@ -38,6 +38,7 @@ interface Query { voteId?: string | number; id?: string | number; anchorText?: string | number; + governanceActionTxHash?: string | number; actionType?: string; currentStatus?: string; vote?: string; @@ -175,7 +176,7 @@ const DelegationDetail: React.FC = () => { key: "governanceVotes", component: (
- +
) } @@ -207,6 +208,7 @@ const DelegationDetail: React.FC = () => { tab: newExpanded ? panel : "", page: 0, size: panel === "governanceVotes" ? 6 : 50, + governanceActionTxHash: "", actionType: STATUS_VOTE.ALL, actionStatus: STATUS_VOTE.ANY, voteType: STATUS_VOTE.ANY, From a4ca19421bca7402e8b39df14d7abedcdf5e0136 Mon Sep 17 00:00:00 2001 From: DinhTran Date: Thu, 28 Mar 2024 11:22:04 +0700 Subject: [PATCH 2/3] fix: change function pool name --- src/commons/utils/constants.ts | 5 ++- src/commons/utils/helper.ts | 4 +++ src/components/GovernanceVotes/index.tsx | 6 ++-- .../commons/CardGovernanceVotes/index.tsx | 32 +++++++++---------- src/locales/en/translation.json | 4 +++ src/pages/DelegationDetail/index.tsx | 2 +- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/commons/utils/constants.ts b/src/commons/utils/constants.ts index f42482cdcd..1d6406018e 100644 --- a/src/commons/utils/constants.ts +++ b/src/commons/utils/constants.ts @@ -299,7 +299,10 @@ export enum POOLS_ACTION_TYPE { UPDATE_COMMITTEE = "UPDATE_COMMITTEE", HARD_FORK_INITIATION_ACTION = "HARD_FORK_INITIATION_ACTION", NO_CONFIDENCE = "NO_CONFIDENCE", - INFO_ACTION = "INFO_ACTION" + INFO_ACTION = "INFO_ACTION", + NEW_CONSTITUTION = "NEW_CONSTITUTION", + TREASURY_WITHDRAWALS_ACTION = "TREASURY_WITHDRAWALS_ACTION", + PARAMETER_CHANGE_ACTION = "PARAMETER_CHANGE_ACTION" } export enum VOTE_TYPE { diff --git a/src/commons/utils/helper.ts b/src/commons/utils/helper.ts index c7b3a2b190..1c2d00c3a3 100644 --- a/src/commons/utils/helper.ts +++ b/src/commons/utils/helper.ts @@ -204,6 +204,10 @@ export const formatDateTimeLocal = (date: string) => { return moment(moment(`${date} GMT+0000`).local(true)).format("MM/DD/YYYY HH:mm:ss"); }; +export const formatDate = (date: string) => { + return moment(date).format("DD/MM/YYYY"); +}; + export const getEpochSlotNo = (data: IDataEpoch) => { if (data.status === "FINISHED") { return MAX_SLOT_EPOCH; diff --git a/src/components/GovernanceVotes/index.tsx b/src/components/GovernanceVotes/index.tsx index 59313ee425..4a7ba838fc 100644 --- a/src/components/GovernanceVotes/index.tsx +++ b/src/components/GovernanceVotes/index.tsx @@ -56,7 +56,7 @@ import { } from "src/commons/resources"; import { API } from "src/commons/utils/api"; import { POOLS_ACTION_TYPE, VOTE_TYPE, STATUS_VOTE } from "src/commons/utils/constants"; -import { getShortHash, getShortNumber } from "src/commons/utils/helper"; +import { formatDate, formatDateTime, getShortHash, getShortNumber } from "src/commons/utils/helper"; import CardGovernanceVotes, { GovernanceStatus, VoteStatus } from "src/components/commons/CardGovernanceVotes"; import CopyButton from "src/components/commons/CopyButton"; import CustomIcon from "src/components/commons/CustomIcon"; @@ -487,14 +487,14 @@ const GovernanceVotesDetail: React.FC<{ {t("pool.submission")} - {moment(data?.submissionDate).format("MM/DD/YYYY HH:mm:ss")} + {formatDateTime(data?.submissionDate || "")} {t("pool.expiryDate")} - {moment(data?.expiryDate).format("DD/MM/YYYY")} + {formatDate(data?.expiryDate || "")} diff --git a/src/components/commons/CardGovernanceVotes/index.tsx b/src/components/commons/CardGovernanceVotes/index.tsx index f6a52dabf1..e70eda1de4 100644 --- a/src/components/commons/CardGovernanceVotes/index.tsx +++ b/src/components/commons/CardGovernanceVotes/index.tsx @@ -1,6 +1,7 @@ import { Box, Stack, Tooltip, Typography, useTheme } from "@mui/material"; import React from "react"; import { useTranslation } from "react-i18next"; +import { t } from "i18next"; import { VotesAbstainIcon, VotesNoIcon, VotesNoneIcon, VotesYesIcon } from "src/commons/resources"; import { ChipContainer } from "src/pages/NativeScriptsAndSC/Card"; @@ -13,25 +14,22 @@ interface ICardGovernanceVotes { data: GovernanceVote; } +export const actionTypeListDrep = [ + { value: POOLS_ACTION_TYPE.ALL, text: t("pool.any") }, + { value: POOLS_ACTION_TYPE.NO_CONFIDENCE, text: t("pool.typeMotion") }, + { value: POOLS_ACTION_TYPE.UPDATE_COMMITTEE, text: t("pool.typeConstitutional") }, + { value: POOLS_ACTION_TYPE.NEW_CONSTITUTION, text: t("drep.updateConstitution") }, + { value: POOLS_ACTION_TYPE.HARD_FORK_INITIATION_ACTION, text: t("pool.typeHardFork") }, + { value: POOLS_ACTION_TYPE.PARAMETER_CHANGE_ACTION, text: t("drep.protocolChange") }, + { value: POOLS_ACTION_TYPE.TREASURY_WITHDRAWALS_ACTION, text: t("drep.treasuryWithdrawals") }, + { value: POOLS_ACTION_TYPE.INFO_ACTION, text: t("pool.typeInfo") } +]; + const CardGovernanceVotes: React.FC = ({ data }) => { - const { index, status, txHash, type, vote, votingPower } = data; + const { status, txHash, type, vote, votingPower } = data; const theme = useTheme(); const { t } = useTranslation(); - const actionType = (type: string) => { - switch (type) { - case POOLS_ACTION_TYPE.UPDATE_COMMITTEE: - return t("pool.normalState"); - case POOLS_ACTION_TYPE.HARD_FORK_INITIATION_ACTION: - return t("pool.harkFork"); - case POOLS_ACTION_TYPE.NO_CONFIDENCE: - return t("pool.typeMotion"); - case POOLS_ACTION_TYPE.INFO_ACTION: - return t("pool.Infor"); - default: - break; - } - }; return ( @@ -45,7 +43,7 @@ const CardGovernanceVotes: React.FC = ({ data }) => { lineHeight="28px" color={theme.isDark ? theme.palette.secondary.main : theme.palette.secondary.main} > - {actionType(type)} #{index} + {actionTypeListDrep.find((action) => action.value === type)?.text}
@@ -65,7 +63,7 @@ const CardGovernanceVotes: React.FC = ({ data }) => { {t("pool.actionType")}: - {actionType(type)} + {actionTypeListDrep.find((action) => action.value === type)?.text} diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index b1d2c50fd3..4c541c02c3 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -1018,6 +1018,7 @@ "pool.typeUpdate": "Update to the Constitutional or proposal policy", "pool.typeHardFork": "Hard-Fork Initiation", "pool.typeProtocol": "Protocol Parameter Changes", + "pool.typeTreasury": "Treasury Withdrawals", "pool.typeInfo": "Info", "pool.dateRange": "Date Range", @@ -1060,6 +1061,9 @@ "bolnisi.verifyErrorTooltipTryAgain": "Please, refresh the page to try again.", "bolnisi.verifyError": "Verification Unavailable", "account.myProfile": "My Profile", + "drep.updateConstitution": "Update to the Constitution", + "drep.protocolChange": "Protocol Parameter Changes", + "drep.treasuryWithdrawals": "Treasury Withdrawals", "drep.des": "DRep Metadata", "drep.activeVoteStake": "Active Voting Stake", "drep.liveStake": "Live Stake", diff --git a/src/pages/DelegationDetail/index.tsx b/src/pages/DelegationDetail/index.tsx index e68659077b..0891ae6077 100644 --- a/src/pages/DelegationDetail/index.tsx +++ b/src/pages/DelegationDetail/index.tsx @@ -176,7 +176,7 @@ const DelegationDetail: React.FC = () => { key: "governanceVotes", component: (
- +
) } From 9d2df7947f808bd6b605e873a99cb9ccb2dc763a Mon Sep 17 00:00:00 2001 From: DinhTran Date: Thu, 28 Mar 2024 11:35:52 +0700 Subject: [PATCH 3/3] fix: change modal metadata --- src/components/GovernanceVotes/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/GovernanceVotes/index.tsx b/src/components/GovernanceVotes/index.tsx index 4a7ba838fc..0801236d27 100644 --- a/src/components/GovernanceVotes/index.tsx +++ b/src/components/GovernanceVotes/index.tsx @@ -765,7 +765,7 @@ const ActionMetadataModal: React.FC = ({ onClose, open, dat onClose={() => onClose?.()} title={t("pool.actionMetadata")} width={500} - sx={{ position: "relative", height: "70vh" }} + sx={{ maxHeight: "70vh" }} > @@ -803,7 +803,7 @@ const ActionMetadataModal: React.FC = ({ onClose, open, dat gap="24px" mt="20px" p="24px" - sx={{ background: theme.isDark ? "" : theme.palette.secondary[0], position: "absolute" }} + sx={{ background: theme.isDark ? "" : theme.palette.secondary[0] }} >