From 90df4b8d1edd8535bb15bedcc3ecf960ac369075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sza=C5=82owski?= Date: Mon, 25 Mar 2024 09:46:32 +0000 Subject: [PATCH 01/21] [#543] Update frontend package readme --- CHANGELOG.md | 1 + govtool/frontend/README.md | 65 ++++++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9fea525b..07b9c6d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ changes. - Fix all the existing eslint errors [Issue 514](https://github.com/IntersectMBO/govtool/issues/514) - Fix all the existing typescript errors [Issue 514](https://github.com/IntersectMBO/govtool/issues/514) - Fix endless spinner on a dashboard [Issue 539](https://github.com/IntersectMBO/govtool/issues/539) +- Update frontend package readme to reflect recent changes [Issue 543](https://github.com/IntersectMBO/govtool/issues/543) ### Added diff --git a/govtool/frontend/README.md b/govtool/frontend/README.md index ec8be6bdb..b66001800 100644 --- a/govtool/frontend/README.md +++ b/govtool/frontend/README.md @@ -3,7 +3,7 @@ Installed on your machine: 1. Node.js >= 18 ([official website](https://nodejs.org/en)) -2. npm or yarn - for package management +2. npm or yarn (recommended) - for package management Clone the project @@ -16,7 +16,7 @@ Fill .env based on env.example file Go to the project directory ```bash -cd voltaire-era/govtool/frontend +cd govtool/frontend ``` Install dependencies @@ -25,7 +25,7 @@ Install dependencies npm install ``` -or +or (recommended) ```bash yarn install @@ -37,7 +37,7 @@ Start the server npm run dev ``` -or +or (recommended) ```bash yarn dev @@ -59,12 +59,30 @@ yarn dev 10. Yup - ^1.3.2 11. Keen-Slider - ^6.8.5 12. Sentry - ^7.77.0 -13. Cardano serialization lib - 12.0.0-alpha.13 +13. Cardano serialization lib - 12.0.0-alpha.19 +14. i18next - ^23.7.19 + +### Code Quality and Checks Are Handled BY + +1. eslint - ^8.38.0 +2. vitest - ^1.1.0 +3. chromatic - ^10.0.0 ### Prerequisites -Install [`Git`](https://git-scm.com/) - version control -Recommendetd [`React developer tools`](https://react.dev/learn/react-developer-tools) +Install [`Git`](https://git-scm.com/) - version control. +Recommended [`React developer tools`](https://react.dev/learn/react-developer-tools). + +To automatically set correct node version: + +1. Install [`nvm`](https://github.com/nvm-sh/nvm) +2. Install `lts/hydrogen` version of node + +```bash + nvm install lts/hydrogen +``` + +3. Having that every time you enter the `govtool/frontend` package [`nvm`](https://github.com/nvm-sh/nvm) automatically sets the correct version of node. ## To Develop @@ -76,12 +94,24 @@ Recommendetd [`React developer tools`](https://react.dev/learn/react-developer-t npm install ``` +or (recommended) + +```bash +yarn install +``` + 2. Launch Server ```bash npm run dev ``` +or (recommended) + +```bash +yarn dev +``` + #### Using Nix and Direnv 1. Get [Nix](https://nixos.org/download). @@ -108,17 +138,26 @@ direnv allow yarn dev ``` +## After development + +Check our [Contributing Documentation](../../CONTRIBUTING.md) on how to submit a PR. + ### Users The GovTool application can read and display data from the Cardano chain using REST API. We distinguish two types of users: #### without a connected wallet who can: -1. see the governance actions along with their details and the number of votes + +1. See the governance actions along with their details and the number of votes + #### with connected wallet who can: -1. see the governance actions along with their details and the number of votes. -2. display the wallet status -3. delegate his or her voting power in a form of ADA to dReps, -4. register as DRrep -5. vote for the governance actions of his or her choice (if the user is registered) + +1. See the governance actions along with their details and the number of votes. +2. Display the wallet status. +3. Delegate his or her voting power in a form of ADA to dReps. +4. Register as DRrep or Sole Voter. +5. Vote for the Governance Actions of his or her choice (if the user is registered). +6. Create their own Governance Action. + From bb26945935ec65cf7e71928c66802bcf820de842 Mon Sep 17 00:00:00 2001 From: Joanna Dyczka Date: Mon, 25 Mar 2024 19:03:58 +0100 Subject: [PATCH 02/21] [#522] fix bugs in usePendingTransaction --- .../src/context/pendingTransaction/types.ts | 4 +- .../usePendingTransaction.ts | 26 ++++++------- .../src/context/pendingTransaction/utils.tsx | 11 ++---- govtool/frontend/src/context/wallet.tsx | 38 ++++++++----------- .../useGetAdaHolderCurrentDelegationQuery.ts | 4 +- .../src/hooks/queries/useGetDRepListQuery.ts | 4 +- .../src/hooks/queries/useGetDRepVotesQuery.ts | 2 +- .../queries/useGetProposalsInfiniteQuery.ts | 2 +- .../src/hooks/queries/useGetProposalsQuery.ts | 2 +- .../src/hooks/queries/useGetVoterInfoQuery.ts | 4 +- 10 files changed, 42 insertions(+), 55 deletions(-) diff --git a/govtool/frontend/src/context/pendingTransaction/types.ts b/govtool/frontend/src/context/pendingTransaction/types.ts index f6aae4ade..a44c5f6e0 100644 --- a/govtool/frontend/src/context/pendingTransaction/types.ts +++ b/govtool/frontend/src/context/pendingTransaction/types.ts @@ -15,14 +15,14 @@ export type TransactionType = export type TransactionStateWithoutResource = { type: TransactionTypeWithoutResource; transactionHash: string; - time: Date; + time: string; resourceId?: never; }; export type TransactionStateWithResource = { type: TransactionTypeWithResource; transactionHash: string; - time: Date; + time: string; resourceId: string; }; diff --git a/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts b/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts index 18f1e4a7c..1621a3362 100644 --- a/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts +++ b/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts @@ -20,7 +20,6 @@ const DB_SYNC_REFRESH_TIME = 3 * 1000; // 3 SECONDS const DB_SYNC_MAX_ATTEMPTS = 10; type UsePendingTransactionProps = { - dRepID: string; isEnabled: boolean; stakeKey: string | undefined; }; @@ -28,7 +27,6 @@ type UsePendingTransactionProps = { export const usePendingTransaction = ({ isEnabled, stakeKey, - dRepID, }: UsePendingTransactionProps) => { const { t } = useTranslation(); const { openModal, closeModal } = useModal(); @@ -57,11 +55,10 @@ export const usePendingTransaction = ({ const fromLocalStorage = getItemFromLocalStorage( `${PENDING_TRANSACTION_KEY}_${stakeKey}` ); - setTransaction( - fromLocalStorage - ? { ...fromLocalStorage, time: new Date(fromLocalStorage.time) } - : null - ); + setTransaction({ + ...fromLocalStorage, + resourceId: fromLocalStorage?.resourceId ?? undefined, + }); } }, [isEnabled, stakeKey]); @@ -84,9 +81,7 @@ export const usePendingTransaction = ({ if (isEnabled) { const desiredResult = getDesiredResult( type, - dRepID, resourceId, - stakeKey ); const queryKey = getQueryKey(type, transaction); @@ -120,7 +115,7 @@ export const usePendingTransaction = ({ if (isEnabled && transaction) { addWarningAlert(t("alerts.transactionInProgress"), 10000); } - }, [transaction]); + }, [isEnabled, transaction]); const isPendingTransaction = useCallback(() => { if (transaction) { @@ -144,14 +139,17 @@ export const usePendingTransaction = ({ const updateTransaction = (data: Omit) => { const newTransaction = { - time: new Date(), + time: new Date().toISOString(), ...data, } as TransactionState; setTransaction(newTransaction); setItemToLocalStorage( `${PENDING_TRANSACTION_KEY}_${stakeKey}`, - JSON.stringify(newTransaction) + { + ...newTransaction, + resourceId: newTransaction.resourceId || null, + } ); }; @@ -162,5 +160,5 @@ export const usePendingTransaction = ({ }; }; -const isTransactionExpired = (time: Date): boolean => - Date.now() - time.getTime() > TIME_TO_EXPIRE_TRANSACTION; +const isTransactionExpired = (time: string): boolean => + Date.now() - new Date(time).getTime() > TIME_TO_EXPIRE_TRANSACTION; diff --git a/govtool/frontend/src/context/pendingTransaction/utils.tsx b/govtool/frontend/src/context/pendingTransaction/utils.tsx index 916a994ff..ed7195b3d 100644 --- a/govtool/frontend/src/context/pendingTransaction/utils.tsx +++ b/govtool/frontend/src/context/pendingTransaction/utils.tsx @@ -4,17 +4,14 @@ import { TransactionType, TransactionState } from "./types"; export const getDesiredResult = ( type: TransactionType, - dRepID: string, resourceId: string | undefined, - stakeKey?: string ) => { switch (type) { case "delegate": { // current delegation - if (resourceId === dRepID) return dRepID; if (resourceId === "no confidence") return "drep_always_no_confidence"; if (resourceId === "abstain") return "drep_always_abstain"; - return stakeKey; + return resourceId; } case "registerAsDrep": case "registerAsSoleVoter": @@ -38,9 +35,9 @@ export const getQueryKey = ( case "retireAsDrep": case "registerAsSoleVoter": case "retireAsSoleVoter": - return [QUERY_KEYS.useGetDRepInfoKey, transaction]; + return [QUERY_KEYS.useGetDRepInfoKey, transaction?.transactionHash]; case "delegate": - return [QUERY_KEYS.getAdaHolderCurrentDelegationKey, transaction]; + return [QUERY_KEYS.getAdaHolderCurrentDelegationKey, transaction?.transactionHash]; default: return undefined; } @@ -58,7 +55,7 @@ export const refetchData = async ( // eslint-disable-next-line @typescript-eslint/no-explicit-any const data = await queryClient.getQueryData(queryKey); - if (type === "delegate") return data.currentDelegation; + if (type === "delegate") return data; if (type === "registerAsDrep" || type === "retireAsDrep") return data.isRegisteredAsDRep; if (type === "registerAsSoleVoter" || type === "retireAsSoleVoter") return data.isRegisteredAsSoleVoter; return undefined; diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 75eb48eb0..21d00ac21 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -1,6 +1,5 @@ // TODO: enable eslint and fix all the errors with wallet refactor /* eslint-disable */ -// @ts-nocheck import { createContext, useCallback, @@ -71,7 +70,8 @@ import { getUtxos } from './getUtxos'; import { useModal, useSnackbar } from '.'; import { PendingTransaction, - TransactionType, + TransactionStateWithResource, + TransactionStateWithoutResource, usePendingTransaction, } from './pendingTransaction'; @@ -97,6 +97,16 @@ type TreasuryProps = { url: string; }; +type BuildSignSubmitConwayCertTxArgs = { + certBuilder?: CertificatesBuilder; + govActionBuilder?: VotingProposalBuilder; + votingBuilder?: VotingBuilder; + voterDeposit?: string; +} & ( + | Pick + | Pick + ); + interface CardanoContext { address?: string; disconnectWallet: () => Promise; @@ -119,24 +129,15 @@ interface CardanoContext { type, votingBuilder, voterDeposit, - }: { - certBuilder?: CertificatesBuilder; - govActionBuilder?: VotingProposalBuilder; - resourceId?: string; - type: TransactionType; - votingBuilder?: VotingBuilder; - voterDeposit?: string; - }) => Promise; + }: BuildSignSubmitConwayCertTxArgs) => Promise; buildDRepRegCert: ( url?: string, hash?: string, - hash?: string, ) => Promise; buildVoteDelegationCert: (vote: string) => Promise; buildDRepUpdateCert: ( url?: string, hash?: string, - hash?: string, ) => Promise; buildDRepRetirementCert: ( voterDeposit: string, @@ -147,7 +148,6 @@ interface CardanoContext { index: number, cip95MetadataURL?: string, cip95MetadataHash?: string, - cip95MetadataHash?: string, ) => Promise; pendingTransaction: PendingTransaction; isPendingTransaction: () => boolean; @@ -186,7 +186,6 @@ const CardanoProvider = (props: Props) => { const [stakeKey, setStakeKey] = useState(undefined); const [stakeKeys, setStakeKeys] = useState([]); const [isMainnet, setIsMainnet] = useState(false); - const { openModal, closeModal } = useModal(); const [registeredStakeKeysListState, setRegisteredPubStakeKeysState] = useState([]); const [error, setError] = useState(undefined); @@ -201,7 +200,7 @@ const CardanoProvider = (props: Props) => { const epochParams = getItemFromLocalStorage(PROTOCOL_PARAMS_KEY); const { isPendingTransaction, updateTransaction, pendingTransaction } = - usePendingTransaction({ dRepID, isEnabled, stakeKey }); + usePendingTransaction({ isEnabled, stakeKey }); const getChangeAddress = async (enabledApi: CardanoApiWallet) => { try { @@ -430,14 +429,7 @@ const CardanoProvider = (props: Props) => { type, votingBuilder, voterDeposit, - }: { - certBuilder?: CertificatesBuilder; - govActionBuilder?: VotingProposalBuilder; - resourceId?: string; - type: TransactionType; - votingBuilder?: VotingBuilder; - voterDeposit?: string; - }) => { + }: BuildSignSubmitConwayCertTxArgs) => { await checkIsMaintenanceOn(); const isPendingTx = isPendingTransaction(); if (isPendingTx) return; diff --git a/govtool/frontend/src/hooks/queries/useGetAdaHolderCurrentDelegationQuery.ts b/govtool/frontend/src/hooks/queries/useGetAdaHolderCurrentDelegationQuery.ts index 94dc2621d..ba2b170af 100644 --- a/govtool/frontend/src/hooks/queries/useGetAdaHolderCurrentDelegationQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetAdaHolderCurrentDelegationQuery.ts @@ -4,13 +4,13 @@ import { getAdaHolderCurrentDelegation } from "@services"; import { QUERY_KEYS } from "@consts"; import { useCardano } from "@context"; -export const useGetAdaHolderCurrentDelegationQuery = (stakeKey?: string) => { +export const useGetAdaHolderCurrentDelegationQuery = (stakeKey: string | undefined) => { const { pendingTransaction } = useCardano(); const { data, isLoading } = useQuery({ queryKey: [ QUERY_KEYS.getAdaHolderCurrentDelegationKey, - pendingTransaction.delegate, + pendingTransaction.delegate?.transactionHash, ], queryFn: () => getAdaHolderCurrentDelegation({ stakeKey }), enabled: !!stakeKey, diff --git a/govtool/frontend/src/hooks/queries/useGetDRepListQuery.ts b/govtool/frontend/src/hooks/queries/useGetDRepListQuery.ts index 4e9aa0c26..806d55072 100644 --- a/govtool/frontend/src/hooks/queries/useGetDRepListQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetDRepListQuery.ts @@ -10,10 +10,10 @@ export const useGetDRepListQuery = () => { const { data, isLoading } = useQuery({ queryKey: [ QUERY_KEYS.useGetDRepListKey, - pendingTransaction.registerAsSoleVoter || + (pendingTransaction.registerAsSoleVoter || pendingTransaction.registerAsDrep || pendingTransaction.retireAsSoleVoter || - pendingTransaction.retireAsDrep, + pendingTransaction.retireAsDrep)?.transactionHash, ], queryFn: getDRepList, }); diff --git a/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts b/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts index 034423c29..304c39cff 100644 --- a/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetDRepVotesQuery.ts @@ -11,7 +11,7 @@ export const useGetDRepVotesQuery = (filters: string[], sorting: string) => { const { data, isLoading, refetch, isRefetching } = useQuery({ queryKey: [ QUERY_KEYS.useGetDRepVotesKey, - pendingTransaction.vote, + pendingTransaction.vote?.transactionHash, filters, sorting, ], diff --git a/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts b/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts index 90e2ac016..7e141842e 100644 --- a/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetProposalsInfiniteQuery.ts @@ -33,7 +33,7 @@ export const useGetProposalsInfiniteQuery = ({ dRepID, filters, isEnabled, - pendingTransaction.vote, + pendingTransaction.vote?.transactionHash, sorting, ], fetchProposals, diff --git a/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts b/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts index 3d0434acd..242add713 100644 --- a/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetProposalsQuery.ts @@ -28,7 +28,7 @@ export const useGetProposalsQuery = ({ filters, sorting, dRepID, - pendingTransaction.vote, + pendingTransaction.vote?.transactionHash, ], fetchProposals, ); diff --git a/govtool/frontend/src/hooks/queries/useGetVoterInfoQuery.ts b/govtool/frontend/src/hooks/queries/useGetVoterInfoQuery.ts index 5eab75e42..a21a5cf55 100644 --- a/govtool/frontend/src/hooks/queries/useGetVoterInfoQuery.ts +++ b/govtool/frontend/src/hooks/queries/useGetVoterInfoQuery.ts @@ -10,10 +10,10 @@ export const useGetVoterInfo = () => { const { data } = useQuery({ queryKey: [ QUERY_KEYS.useGetDRepInfoKey, - pendingTransaction.registerAsDrep || + (pendingTransaction.registerAsDrep || pendingTransaction.registerAsSoleVoter || pendingTransaction.retireAsDrep || - pendingTransaction.retireAsSoleVoter, + pendingTransaction.retireAsSoleVoter)?.transactionHash, ], enabled: !!dRepID, queryFn: () => getVoterInfo(dRepID), From a189d87b1585bd471212eec0597360541e29a292 Mon Sep 17 00:00:00 2001 From: Joanna Dyczka Date: Mon, 25 Mar 2024 19:06:32 +0100 Subject: [PATCH 03/21] [#512] fix ts and eslint errors in wallet --- govtool/frontend/.eslintrc.cjs | 1 + govtool/frontend/src/context/getUtxos.ts | 1 - govtool/frontend/src/context/wallet.tsx | 49 ++++++++++--------- .../forms/useCreateGovernanceActionForm.ts | 1 - .../src/hooks/forms/useRegisterAsdRepForm.tsx | 1 - govtool/frontend/src/utils/getDRepID.ts | 1 - 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/govtool/frontend/.eslintrc.cjs b/govtool/frontend/.eslintrc.cjs index 89a5aff94..07d27ff79 100644 --- a/govtool/frontend/.eslintrc.cjs +++ b/govtool/frontend/.eslintrc.cjs @@ -51,6 +51,7 @@ module.exports = { "operator-linebreak": "off", "implicit-arrow-linebreak": "off", "consistent-return": "off", + "no-console": ["error", { allow: ["warn", "error"] }], "no-shadow": "off", "function-paren-newline": "off", "object-curly-newline": "off", diff --git a/govtool/frontend/src/context/getUtxos.ts b/govtool/frontend/src/context/getUtxos.ts index 23fc0a198..6888846a8 100644 --- a/govtool/frontend/src/context/getUtxos.ts +++ b/govtool/frontend/src/context/getUtxos.ts @@ -77,7 +77,6 @@ export const getUtxos = async ( return utxos; } catch (err) { Sentry.captureException(err); - // eslint-disable-next-line no-console console.error(err); } }; diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 21d00ac21..a6eb929e9 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -1,5 +1,3 @@ -// TODO: enable eslint and fix all the errors with wallet refactor -/* eslint-disable */ import { createContext, useCallback, @@ -107,7 +105,7 @@ type BuildSignSubmitConwayCertTxArgs = { | Pick ); -interface CardanoContext { +interface CardanoContextType { address?: string; disconnectWallet: () => Promise; enable: (walletName: string) => Promise; @@ -160,7 +158,7 @@ interface CardanoContext { } type Utxos = { - txid: any; + txid: unknown; txindx: number; amount: string; str: string; @@ -168,9 +166,9 @@ type Utxos = { TransactionUnspentOutput: TransactionUnspentOutput; }[]; -const NETWORK = import.meta.env.VITE_NETWORK_FLAG; +const NETWORK = +import.meta.env.VITE_NETWORK_FLAG; -const CardanoContext = createContext({} as CardanoContext); +const CardanoContext = createContext({} as CardanoContextType); CardanoContext.displayName = 'CardanoContext'; const CardanoProvider = (props: Props) => { @@ -248,7 +246,7 @@ const CardanoProvider = (props: Props) => { throw new Error(t('errors.walletNoCIP30Nor90Support')); } // Enable wallet connection - const enabledApi = await window.cardano[walletName] + const enabledApi: CardanoApiWallet = await window.cardano[walletName] .enable({ extensions: [{ cip: 95 }], }) @@ -266,15 +264,15 @@ const CardanoProvider = (props: Props) => { throw new Error(t('errors.walletNoCIP90FunctionsEnabled')); } const network = await enabledApi.getNetworkId(); - if (network != NETWORK) { + if (network !== NETWORK) { throw new Error( t('errors.tryingConnectTo', { - networkFrom: network == 1 ? 'mainnet' : 'testnet', - networkTo: network != 1 ? 'mainnet' : 'testnet', + networkFrom: network === 1 ? 'mainnet' : 'testnet', + networkTo: network !== 1 ? 'mainnet' : 'testnet', }), ); } - setIsMainnet(network == 1); + setIsMainnet(network === 1); // Check and set wallet address const usedAddresses = await enabledApi.getUsedAddresses(); const unusedAddresses = await enabledApi.getUnusedAddresses(); @@ -296,8 +294,8 @@ const CardanoProvider = (props: Props) => { let stakeKeysList; if (registeredStakeKeysList.length > 0) { - stakeKeysList = registeredStakeKeysList.map((stakeKey) => { - const stakeKeyHash = PublicKey.from_hex(stakeKey).hash(); + stakeKeysList = registeredStakeKeysList.map((key) => { + const stakeKeyHash = PublicKey.from_hex(key).hash(); const stakeCredential = Credential.from_keyhash(stakeKeyHash); if (network === 1) { return RewardAddress.new(1, stakeCredential) @@ -310,8 +308,8 @@ const CardanoProvider = (props: Props) => { }); } else { console.warn(t('warnings.usingUnregisteredStakeKeys')); - stakeKeysList = unregisteredStakeKeysList.map((stakeKey) => { - const stakeKeyHash = PublicKey.from_hex(stakeKey).hash(); + stakeKeysList = unregisteredStakeKeysList.map((key) => { + const stakeKeyHash = PublicKey.from_hex(key).hash(); const stakeCredential = Credential.from_keyhash(stakeKeyHash); if (network === 1) { return RewardAddress.new(1, stakeCredential) @@ -361,14 +359,16 @@ const CardanoProvider = (props: Props) => { setPubDRepKey(''); setStakeKey(undefined); setIsEnabled(false); + // eslint-disable-next-line no-throw-literal throw { status: 'ERROR', - error: `${e == undefined ? t('errors.somethingWentWrong') : e}`, + error: `${e ?? t('errors.somethingWentWrong')}`, }; } finally { setIsEnableLoading(null); } } + // eslint-disable-next-line no-throw-literal throw { status: 'ERROR', error: t('errors.somethingWentWrong') }; }, [isEnabled, stakeKeys], @@ -414,6 +414,7 @@ const CardanoProvider = (props: Props) => { const getTxUnspentOutputs = async (utxos: Utxos) => { const txOutputs = TransactionUnspentOutputs.new(); + // eslint-disable-next-line no-restricted-syntax for (const utxo of utxos) { txOutputs.add(utxo.TransactionUnspentOutput); } @@ -531,9 +532,11 @@ const CardanoProvider = (props: Props) => { resourceId, }); + // eslint-disable-next-line no-console console.log(signedTx.to_hex(), 'signed tx cbor'); return resultHash; - // TODO: type error + // TODO: type error + // eslint-disable-next-line @typescript-eslint/no-shadow, @typescript-eslint/no-explicit-any } catch (error: any) { const walletName = getItemFromLocalStorage(`${WALLET_LS_KEY}_name`); const isWalletConnected = await window.cardano[walletName].isEnabled(); @@ -589,7 +592,7 @@ const CardanoProvider = (props: Props) => { return certBuilder; } catch (e) { Sentry.captureException(e); - console.log(e); + console.error(e); throw e; } }, @@ -621,7 +624,7 @@ const CardanoProvider = (props: Props) => { anchor, ); } else { - console.log(t('errors.notUsingAnchor')); + console.error(t('errors.notUsingAnchor')); dRepRegCert = DrepRegistration.new( dRepCred, BigNum.from_str(`${epochParams.drep_deposit}`), @@ -667,7 +670,7 @@ const CardanoProvider = (props: Props) => { return certBuilder; } catch (e) { Sentry.captureException(e); - console.log(e); + console.error(e); throw e; } }, @@ -695,7 +698,7 @@ const CardanoProvider = (props: Props) => { return certBuilder; } catch (e) { Sentry.captureException(e); - console.log(e); + console.error(e); throw e; } }, @@ -748,7 +751,7 @@ const CardanoProvider = (props: Props) => { return votingBuilder; } catch (e) { Sentry.captureException(e); - console.log(e); + console.error(e); throw e; } }, @@ -955,6 +958,8 @@ function useCardano() { } return result; } + // TODO: type error + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { Sentry.captureException(e); await context.disconnectWallet(); diff --git a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts index 22843aba7..ff0d505b6 100644 --- a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts +++ b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts @@ -183,7 +183,6 @@ export const useCreateGovernanceActionForm = ( } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - // eslint-disable-next-line no-console console.error(error); throw error; } diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index 7afe6084a..9f7ac6e37 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -37,7 +37,6 @@ export const useRegisterAsdRepForm = () => { console.log(values); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { - // eslint-disable-next-line no-console console.error(e); } finally { setIsLoading(false); diff --git a/govtool/frontend/src/utils/getDRepID.ts b/govtool/frontend/src/utils/getDRepID.ts index ccc23d8bd..05561eaf5 100644 --- a/govtool/frontend/src/utils/getDRepID.ts +++ b/govtool/frontend/src/utils/getDRepID.ts @@ -28,7 +28,6 @@ export const getPubDRepID = async (walletApi: CardanoApiWallet) => { dRepIDBech32, }; } catch (err) { - // eslint-disable-next-line no-console console.error(err); return { dRepKey: undefined, From 28d782c12f260eaad9ae235f3d63e62f427b93bd Mon Sep 17 00:00:00 2001 From: Joanna Dyczka Date: Tue, 26 Mar 2024 09:51:28 +0100 Subject: [PATCH 04/21] [#522] fix pending transaction expiration logic --- .../pendingTransaction/usePendingTransaction.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts b/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts index 1621a3362..a2c5af53a 100644 --- a/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts +++ b/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts @@ -100,13 +100,13 @@ export const usePendingTransaction = ({ await wait(DB_SYNC_REFRESH_TIME); } } - - if (isTransactionExpired(transaction.time)) { - addErrorAlert(t(`alerts.${type}.failed`)); - resetTransaction(); - } } } + + if (isTransactionExpired(transaction.time)) { + addErrorAlert(t(`alerts.${type}.failed`)); + resetTransaction(); + } }; let interval = setInterval(checkTransaction, TRANSACTION_REFRESH_TIME); From 6d59394a8c92d160420319579a42fffe2a553f91 Mon Sep 17 00:00:00 2001 From: Joanna Dyczka Date: Tue, 26 Mar 2024 10:39:33 +0100 Subject: [PATCH 05/21] [#522] fix reading pending transaction from localStorage --- .../src/context/pendingTransaction/usePendingTransaction.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts b/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts index a2c5af53a..bd537754b 100644 --- a/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts +++ b/govtool/frontend/src/context/pendingTransaction/usePendingTransaction.ts @@ -55,9 +55,10 @@ export const usePendingTransaction = ({ const fromLocalStorage = getItemFromLocalStorage( `${PENDING_TRANSACTION_KEY}_${stakeKey}` ); - setTransaction({ + if (!fromLocalStorage) setTransaction(null); + else setTransaction({ ...fromLocalStorage, - resourceId: fromLocalStorage?.resourceId ?? undefined, + resourceId: fromLocalStorage.resourceId ?? undefined, }); } }, [isEnabled, stakeKey]); From 221318ed24bd1a648cf7e5ea5c8389cab73ada05 Mon Sep 17 00:00:00 2001 From: jankun4 Date: Mon, 25 Mar 2024 21:57:39 +0100 Subject: [PATCH 06/21] introduce stack Signed-off-by: jankun4 --- .gitignore | 1 + govtool/backend/app/Main.hs | 6 ++++-- govtool/backend/stack.yaml | 6 ++++++ govtool/backend/vva-be.cabal | 7 +++++-- 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 govtool/backend/stack.yaml diff --git a/.gitignore b/.gitignore index 60ee619a1..3d712a194 100644 --- a/.gitignore +++ b/.gitignore @@ -120,6 +120,7 @@ local/ # used by haskell govtool/backend/dist-newstyle/ +govtool/backend/.stack-work/ # target environment config dir scripts/govtool/config/target diff --git a/govtool/backend/app/Main.hs b/govtool/backend/app/Main.hs index 111a142ab..8de817ea3 100644 --- a/govtool/backend/app/Main.hs +++ b/govtool/backend/app/Main.hs @@ -80,7 +80,7 @@ import Data.Monoid (mempty) import qualified Data.Cache as Cache import VVA.API.Types import System.Clock (TimeSpec(TimeSpec)) -import Data.Pool (newPool, defaultPoolConfig) +import Data.Pool (createPool) import Database.PostgreSQL.Simple (connectPostgreSQL, close) import Data.Text.Encoding (encodeUtf8) import Data.Has (getter) @@ -138,7 +138,7 @@ startApp vvaConfig = do , dRepListCache , networkMetricsCache } - connectionPool <- newPool $ defaultPoolConfig (connectPostgreSQL (encodeUtf8 (dbSyncConnectionString $ getter vvaConfig))) close 1 60 + connectionPool <- createPool (connectPostgreSQL (encodeUtf8 (dbSyncConnectionString $ getter vvaConfig))) close 1 1 60 let appEnv = AppEnv {vvaConfig=vvaConfig, vvaCache=cacheEnv, vvaConnectionPool=connectionPool} server' <- mkVVAServer appEnv @@ -161,6 +161,8 @@ exceptionHandler vvaConfig mRequest exception = do (formatMessage mRequest exception) (recordUpdate mRequest exception) + + formatMessage :: Maybe Request -> SomeException -> String formatMessage Nothing exception = "Exception before request could be parsed: " ++ show exception formatMessage (Just request) exception = "Exception " ++ show exception ++ " while handling request " ++ show request diff --git a/govtool/backend/stack.yaml b/govtool/backend/stack.yaml new file mode 100644 index 000000000..31940eb29 --- /dev/null +++ b/govtool/backend/stack.yaml @@ -0,0 +1,6 @@ +resolver: lts-20.24 +packages: + - . + +extra-deps: +- raven-haskell-0.1.4.1@sha256:9187272adc064197528645b5ad9b89163b668f386f34016d97fa646d5c790784 \ No newline at end of file diff --git a/govtool/backend/vva-be.cabal b/govtool/backend/vva-be.cabal index e126e1aa2..fb1643b8e 100644 --- a/govtool/backend/vva-be.cabal +++ b/govtool/backend/vva-be.cabal @@ -60,11 +60,13 @@ executable vva-be , lens , cache , clock - , resource-pool >= 0.4.0.0 + , resource-pool , postgresql-simple , data-has - , raven-haskell >= 0.1.4.1 , bytestring + , http-client + , http-client-tls + , raven-haskell hs-source-dirs: app default-language: Haskell2010 @@ -97,6 +99,7 @@ library , resource-pool , swagger2 + exposed-modules: VVA.Config , VVA.CommandLine , VVA.API From 7c547583052b74b4adc6c8d3307235c9161cef7f Mon Sep 17 00:00:00 2001 From: jankun4 Date: Mon, 25 Mar 2024 21:59:50 +0100 Subject: [PATCH 07/21] [#372] provide governance action details for TreasuryWithdrawals provide basic details (title, about, motivation and rationale) for all GA types, and extra details for TreasuryWithdrawals Signed-off-by: jankun4 --- govtool/backend/sql/list-proposals.sql | 32 ++++++++++++++++-- govtool/backend/src/VVA/API.hs | 8 ++++- govtool/backend/src/VVA/API/Types.hs | 14 +++++++- govtool/backend/src/VVA/Proposal.hs | 45 ++++++++++++++++++++++++-- govtool/backend/src/VVA/Types.hs | 8 ++++- govtool/backend/stack.yaml.lock | 19 +++++++++++ 6 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 govtool/backend/stack.yaml.lock diff --git a/govtool/backend/sql/list-proposals.sql b/govtool/backend/sql/list-proposals.sql index 5c8694134..94cd4849e 100644 --- a/govtool/backend/sql/list-proposals.sql +++ b/govtool/backend/sql/list-proposals.sql @@ -38,12 +38,27 @@ SELECT encode(creator_tx.hash, 'hex'), gov_action_proposal.index, gov_action_proposal.type::text, - gov_action_proposal.description::json, + ( + case when gov_action_proposal.type = 'TreasuryWithdrawals' then + json_build_object('Reward Address', stake_address.view, 'Amount', treasury_withdrawal.amount) + + when gov_action_proposal.type::text = 'InfoAction' then + json_build_object() + else + null + end + ) as description, epoch_utils.last_epoch_end_time + epoch_utils.epoch_duration *(gov_action_proposal.expiration - epoch_utils.last_epoch_no), + gov_action_proposal.expiration, creator_block.time, + creator_block.epoch_no, /* created date */ voting_anchor.url, encode(voting_anchor.data_hash, 'hex'), + off_chain_vote_data.title, + off_chain_vote_data.abstract, + off_chain_vote_data.motivation, + off_chain_vote_data.rationale, coalesce(Sum(ldd.amount) FILTER (WHERE voting_procedure.vote::text = 'Yes'), 0) +( CASE WHEN gov_action_proposal.type = 'NoConfidence' THEN always_no_confidence_voting_power.amount @@ -59,12 +74,18 @@ SELECT coalesce(Sum(ldd.amount) FILTER (WHERE voting_procedure.vote::text = 'Abstain'), 0) + always_abstain_voting_power.amount "abstain_votes" FROM gov_action_proposal + LEFT JOIN treasury_withdrawal + on gov_action_proposal.id = treasury_withdrawal.gov_action_proposal_id + LEFT JOIN stake_address + on stake_address.id = treasury_withdrawal.stake_address_id + CROSS JOIN EpochUtils AS epoch_utils CROSS JOIN always_no_confidence_voting_power CROSS JOIN always_abstain_voting_power JOIN tx AS creator_tx ON creator_tx.id = gov_action_proposal.tx_id JOIN block AS creator_block ON creator_block.id = creator_tx.block_id - JOIN voting_anchor ON voting_anchor.id = gov_action_proposal.voting_anchor_id + LEFT JOIN voting_anchor ON voting_anchor.id = gov_action_proposal.voting_anchor_id + LEFT JOIN off_chain_vote_data ON off_chain_vote_data.voting_anchor_id = voting_anchor.id LEFT JOIN voting_procedure ON voting_procedure.gov_action_proposal_id = gov_action_proposal.id LEFT JOIN LatestDrepDistr ldd ON ldd.hash_id = voting_procedure.drep_voter AND ldd.rn = 1 @@ -81,6 +102,13 @@ AND gov_action_proposal.expired_epoch IS NULL AND gov_action_proposal.dropped_epoch IS NULL GROUP BY (gov_action_proposal.id, + stake_address.view, + treasury_withdrawal.amount, + creator_block.epoch_no, + off_chain_vote_data.title, + off_chain_vote_data.abstract, + off_chain_vote_data.motivation, + off_chain_vote_data.rationale, gov_action_proposal.index, creator_tx.hash, creator_block.time, diff --git a/govtool/backend/src/VVA/API.hs b/govtool/backend/src/VVA/API.hs index c303fe32f..aba121ba6 100644 --- a/govtool/backend/src/VVA/API.hs +++ b/govtool/backend/src/VVA/API.hs @@ -116,11 +116,17 @@ proposalToResponse Types.Proposal {..} = proposalResponseTxHash = HexText proposalTxHash, proposalResponseIndex = proposalIndex, proposalResponseType = fromMaybe InfoAction $ readMaybe $ unpack proposalType, - proposalResponseDetails = GovernanceActionDetails proposalDetails, + proposalResponseDetails = GovernanceActionDetails <$> proposalDetails, proposalResponseExpiryDate = proposalExpiryDate, + proposalResponseExpiryEpochNo = proposalExpiryEpochNo, proposalResponseCreatedDate = proposalCreatedDate, + proposalResponseCreatedEpochNo = proposalCreatedEpochNo, proposalResponseUrl = proposalUrl, proposalResponseMetadataHash = HexText proposalDocHash, + proposalResponseTitle = proposalTitle, + proposalResponseAbout = proposalAbout, + proposalResponseMotivation = proposalMotivaiton, + proposalResponseRationale = proposalRationale, proposalResponseYesVotes = proposalYesVotes, proposalResponseNoVotes = proposalNoVotes, proposalResponseAbstainVotes = proposalAbstainVotes diff --git a/govtool/backend/src/VVA/API/Types.hs b/govtool/backend/src/VVA/API/Types.hs index efc79857d..5080cd339 100644 --- a/govtool/backend/src/VVA/API/Types.hs +++ b/govtool/backend/src/VVA/API/Types.hs @@ -238,11 +238,17 @@ data ProposalResponse = ProposalResponse proposalResponseTxHash :: HexText, proposalResponseIndex :: Integer, proposalResponseType :: GovernanceActionType, - proposalResponseDetails :: GovernanceActionDetails, + proposalResponseDetails :: Maybe GovernanceActionDetails, proposalResponseExpiryDate :: Maybe UTCTime, + proposalResponseExpiryEpochNo :: Maybe Integer, proposalResponseCreatedDate :: UTCTime, + proposalResponseCreatedEpochNo :: Integer, proposalResponseUrl :: Text, proposalResponseMetadataHash :: HexText, + proposalResponseTitle :: Maybe Text, + proposalResponseAbout :: Maybe Text, + proposalResponseMotivation :: Maybe Text, + proposalResponseRationale :: Maybe Text, proposalResponseYesVotes :: Integer, proposalResponseNoVotes :: Integer, proposalResponseAbstainVotes :: Integer @@ -258,9 +264,15 @@ exampleProposalResponse = "{ \"id\": \"proposalId123\"," <> "\"type\": \"InfoAction\"," <> "\"details\": \"some details\"," <> "\"expiryDate\": \"1970-01-01T00:00:00Z\"," + <> "\"expiryEpochNo\": 0," <> "\"createdDate\": \"1970-01-01T00:00:00Z\"," + <> "\"createdEpochNo\": 0," <> "\"url\": \"https://proposal.metadata.xyz\"," <> "\"metadataHash\": \"9af10e89979e51b8cdc827c963124a1ef4920d1253eef34a1d5cfe76438e3f11\"," + <> "\"title\": \"Proposal Title\"," + <> "\"about\": \"Proposal About\"," + <> "\"motivation\": \"Proposal Motivation\"," + <> "\"rationale\": \"Proposal Rationale\"," <> "\"yesVotes\": 0," <> "\"noVotes\": 0," <> "\"abstainVotes\": 0}" diff --git a/govtool/backend/src/VVA/Proposal.hs b/govtool/backend/src/VVA/Proposal.hs index d47e7af50..45980af7d 100644 --- a/govtool/backend/src/VVA/Proposal.hs +++ b/govtool/backend/src/VVA/Proposal.hs @@ -31,6 +31,9 @@ import VVA.Pool (ConnectionPool, withPool) import Control.Exception (throw) import VVA.Types (Proposal(..), AppError(..)) +import Data.Aeson +import Data.Aeson.Types (parseMaybe, Parser) +import Data.Text (Text) sqlFrom :: ByteString -> SQL.Query sqlFrom bs = fromString $ unpack $ Text.decodeUtf8 bs @@ -67,10 +70,46 @@ getProposals mProposalIds = withPool $ \conn -> do timeZone <- liftIO getCurrentTimeZone return $ map - ( \(id', txHash', index', type', details', expiryDate', createdDate', url', docHash', yesVotes', noVotes', abstainVotes') -> + ( \( id' + , txHash' + , index' + , type' + , details' + , expiryDate' + , expiryEpochNo' + , createdDate' + , createdEpochNo' + , url' + , docHash' + , title' + , about' + , motivation' + , rationale' + , yesVotes' + , noVotes' + , abstainVotes' + ) -> let eDate = localTimeToUTC timeZone <$> expiryDate' cDate = localTimeToUTC timeZone createdDate' in - Proposal id' txHash' (floor @Scientific index') type' details' eDate cDate url' docHash' (floor @Scientific yesVotes') (floor @Scientific noVotes') (floor @Scientific abstainVotes') + Proposal + id' + txHash' + (floor @Scientific index') + type' + details' + eDate + expiryEpochNo' + cDate + createdEpochNo' + url' + docHash' + title' + about' + motivation' + rationale' + (floor @Scientific yesVotes') + (floor @Scientific noVotes') + (floor @Scientific abstainVotes') ) - proposalResults + proposalResults \ No newline at end of file diff --git a/govtool/backend/src/VVA/Types.hs b/govtool/backend/src/VVA/Types.hs index e316f34e0..499bac20a 100644 --- a/govtool/backend/src/VVA/Types.hs +++ b/govtool/backend/src/VVA/Types.hs @@ -94,11 +94,17 @@ data Proposal = Proposal proposalTxHash :: Text, proposalIndex :: Integer, proposalType :: Text, - proposalDetails :: Value, + proposalDetails :: Maybe Value, proposalExpiryDate :: Maybe UTCTime, + proposalExpiryEpochNo :: Maybe Integer, proposalCreatedDate :: UTCTime, + proposalCreatedEpochNo :: Integer, proposalUrl :: Text, proposalDocHash :: Text, + proposalTitle :: Maybe Text, + proposalAbout :: Maybe Text, + proposalMotivaiton :: Maybe Text, + proposalRationale :: Maybe Text, proposalYesVotes :: Integer, proposalNoVotes :: Integer, proposalAbstainVotes :: Integer diff --git a/govtool/backend/stack.yaml.lock b/govtool/backend/stack.yaml.lock new file mode 100644 index 000000000..985ab39a4 --- /dev/null +++ b/govtool/backend/stack.yaml.lock @@ -0,0 +1,19 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: +- completed: + hackage: raven-haskell-0.1.4.1@sha256:9187272adc064197528645b5ad9b89163b668f386f34016d97fa646d5c790784,1479 + pantry-tree: + sha256: e8f14bfed6b055dc95933257441ba97d3d779cbe08a0e82c3c2dff5d69c8c48f + size: 632 + original: + hackage: raven-haskell-0.1.4.1@sha256:9187272adc064197528645b5ad9b89163b668f386f34016d97fa646d5c790784 +snapshots: +- completed: + sha256: e019cd29e3f7f9dbad500225829a3f7a50f73c674614f2f452e21bb8bf5d99ea + size: 650253 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/24.yaml + original: lts-20.24 From 2dd1bee52d8259772bf0eb0ac6ceb2e17cce1ea9 Mon Sep 17 00:00:00 2001 From: jankun4 Date: Mon, 25 Mar 2024 22:08:14 +0100 Subject: [PATCH 08/21] [#372] update changelog Signed-off-by: jankun4 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07b9c6d47..4001e6244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,8 @@ changes. ### Changed +- `proposal/list` returns additional data such ass `expiryEpochNo`, `createdEpochNo`, `title`, `about`, `motivation`, + `rationale`. `TreasuryWithdrawals` GAs also got nicely formated details. [Issue 372](https://github.com/IntersectMBO/govtool/issues/372) - `drep/list` now return also `status` and `type` fields. Also it now returns the retired dreps, and you can search for given drep by name using optional query parameter. If the drep name is passed exactly, then you can even find a drep that's sole voter. [Issue 446](https://github.com/IntersectMBO/govtool/issues/446) - `drep/list` and `drep/info` endpoints now return additional data such as metadata url and hash, and voting power [Issue 223](https://github.com/IntersectMBO/govtool/issues/223) - `drep/info` now does not return sole voters (dreps without metadata) [Issue 317](https://github.com/IntersectMBO/govtool/issues/317) From e4d4b81a03f411f48385413df5870514ec896221 Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Mon, 4 Mar 2024 19:24:44 +0100 Subject: [PATCH 09/21] New Gov Actions --- .../frontend/public/icons/CopyBlueThin.svg | 11 + govtool/frontend/public/icons/Share.svg | 5 + .../src/components/atoms/CopyButton.tsx | 6 +- .../components/atoms/ExternalModalButton.tsx | 48 +++ .../src/components/atoms/IconLink.tsx | 45 +++ .../frontend/src/components/atoms/Radio.tsx | 44 ++- .../src/components/atoms/SliderArrow.tsx | 40 +++ .../frontend/src/components/atoms/Tooltip.tsx | 2 +- .../src/components/atoms/VotePill.tsx | 3 +- .../frontend/src/components/atoms/index.ts | 3 + .../components/molecules/DataActionsBar.tsx | 4 +- .../molecules/DataMissingInfoBox.tsx | 60 ++++ .../molecules/GovernanceActionCard.tsx | 235 ++++----------- .../molecules/GovernanceActionCardElement.tsx | 140 +++++++++ .../molecules/GovernanceActionCardHeader.tsx | 62 ++++ .../molecules/GovernanceActionCardMyVote.tsx | 56 ++++ .../GovernanceActionCardStatePill.tsx | 50 +++ .../GovernanceActionDetailsCardHeader.tsx | 56 ++++ .../GovernanceActionDetailsCardLinks.tsx | 47 +++ ...GovernanceActionDetailsCardOnChainData.tsx | 79 +++++ .../GovernanceActionDetailsCardVotes.tsx | 61 ++++ .../molecules/GovernanceActionsDatesBox.tsx | 124 ++++++++ .../molecules/GovernanceVotedOnCard.tsx | 285 +++++------------- .../src/components/molecules/Share.tsx | 99 ++++++ .../src/components/molecules/SliderArrows.tsx | 44 +++ .../components/molecules/VoteActionForm.tsx | 187 ++++++++---- .../components/molecules/VotesSubmitted.tsx | 57 ++-- .../src/components/molecules/index.ts | 12 + .../DashboardGovernanceActionDetails.tsx | 17 +- .../organisms/DashboardGovernanceActions.tsx | 40 +-- .../DashboardGovernanceActionsVotedOn.tsx | 25 +- .../organisms/GovernanceActionDetailsCard.tsx | 266 ++++------------ .../GovernanceActionDetailsCardData.tsx | 121 ++++++++ .../organisms/GovernanceActionsToVote.tsx | 70 ++--- .../src/components/organisms/Slider.tsx | 185 +++++------- .../src/components/organisms/index.ts | 1 + govtool/frontend/src/consts/icons.ts | 2 + govtool/frontend/src/hooks/useSlider.ts | 27 +- govtool/frontend/src/i18n/locales/en.ts | 20 +- .../DashboardGovernanceActionsCategory.tsx | 2 + .../src/pages/GovernanceActionDetails.tsx | 8 +- .../src/pages/GovernanceActionsCategory.tsx | 2 + govtool/frontend/src/theme.ts | 55 ++++ 43 files changed, 1796 insertions(+), 910 deletions(-) create mode 100644 govtool/frontend/public/icons/CopyBlueThin.svg create mode 100644 govtool/frontend/public/icons/Share.svg create mode 100644 govtool/frontend/src/components/atoms/ExternalModalButton.tsx create mode 100644 govtool/frontend/src/components/atoms/IconLink.tsx create mode 100644 govtool/frontend/src/components/atoms/SliderArrow.tsx create mode 100644 govtool/frontend/src/components/molecules/DataMissingInfoBox.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx create mode 100644 govtool/frontend/src/components/molecules/Share.tsx create mode 100644 govtool/frontend/src/components/molecules/SliderArrows.tsx create mode 100644 govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx diff --git a/govtool/frontend/public/icons/CopyBlueThin.svg b/govtool/frontend/public/icons/CopyBlueThin.svg new file mode 100644 index 000000000..2050c9abe --- /dev/null +++ b/govtool/frontend/public/icons/CopyBlueThin.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/govtool/frontend/public/icons/Share.svg b/govtool/frontend/public/icons/Share.svg new file mode 100644 index 000000000..a8b9eac2b --- /dev/null +++ b/govtool/frontend/public/icons/Share.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/govtool/frontend/src/components/atoms/CopyButton.tsx b/govtool/frontend/src/components/atoms/CopyButton.tsx index 65601c718..2783d0fb7 100644 --- a/govtool/frontend/src/components/atoms/CopyButton.tsx +++ b/govtool/frontend/src/components/atoms/CopyButton.tsx @@ -7,7 +7,7 @@ import { useTranslation } from "@hooks"; interface Props { isChecked?: boolean; text: string; - variant?: string; + variant?: "blueThin" | "blue"; } export const CopyButton = ({ isChecked, text, variant }: Props) => { @@ -19,6 +19,10 @@ export const CopyButton = ({ isChecked, text, variant }: Props) => { return ICONS.copyBlueIcon; } + if (variant === "blueThin") { + return ICONS.copyBlueThinIcon; + } + if (isChecked) { return ICONS.copyWhiteIcon; } diff --git a/govtool/frontend/src/components/atoms/ExternalModalButton.tsx b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx new file mode 100644 index 000000000..c4debddc5 --- /dev/null +++ b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx @@ -0,0 +1,48 @@ +import { Typography } from "@mui/material"; + +import { Button } from "@atoms"; +import { ICONS } from "@consts"; +import { useModal } from "@context"; + +export const ExternalModalButton = ({ + label, + url, +}: { + label: string; + url: string; +}) => { + const { openModal } = useModal(); + + return ( + + ); +}; diff --git a/govtool/frontend/src/components/atoms/IconLink.tsx b/govtool/frontend/src/components/atoms/IconLink.tsx new file mode 100644 index 000000000..0c12fcd1e --- /dev/null +++ b/govtool/frontend/src/components/atoms/IconLink.tsx @@ -0,0 +1,45 @@ +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; +import { openInNewTab } from "@utils"; +import { ICONS } from "@consts"; + +type IconLinkProps = { + label: string; + navTo: string; + isSmall?: boolean; +}; + +export const IconLink = ({ label, navTo, isSmall }: IconLinkProps) => { + const openLink = () => openInNewTab(navTo); + + return ( + + link + + {label} + + + ); +}; diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index 538fa7deb..bf4184681 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Box, Typography } from "@mui/material"; +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; import { UseFormRegister, UseFormSetValue } from "react-hook-form"; type RadioProps = { @@ -10,11 +12,20 @@ type RadioProps = { setValue: UseFormSetValue; register: UseFormRegister; dataTestId?: string; + disabled?: boolean; }; export const Radio = ({ ...props }: RadioProps) => { - const { isChecked, name, setValue, title, value, dataTestId, register } = - props; + const { + isChecked, + name, + setValue, + title, + value, + dataTestId, + register, + disabled, + } = props; const handleClick = () => { setValue(name, value); @@ -23,13 +34,17 @@ export const Radio = ({ ...props }: RadioProps) => { return ( { + if (!disabled) handleClick(); + }} + borderRadius={isChecked ? "15px" : "12px"} + p={isChecked ? "2px" : 0} + border={isChecked ? 2 : 0} + borderColor={isChecked ? "specialCyanBorder" : undefined} sx={[{ "&:hover": { color: "blue", cursor: "pointer" } }]} - flex={1} + boxShadow={ + "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" + } > { checked={isChecked} /> {title} diff --git a/govtool/frontend/src/components/atoms/SliderArrow.tsx b/govtool/frontend/src/components/atoms/SliderArrow.tsx new file mode 100644 index 000000000..2ab59999a --- /dev/null +++ b/govtool/frontend/src/components/atoms/SliderArrow.tsx @@ -0,0 +1,40 @@ +import { Box } from "@mui/material"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; + +import { theme } from "@/theme"; + +interface Props { + disabled: boolean; + onClick: (e: any) => void; + left?: boolean; +} + +export const SliderArrow = ({ disabled, onClick, left }: Props) => { + const { + palette: { primaryBlue, arcticWhite, lightBlue }, + } = theme; + + return ( + + + + ); +}; diff --git a/govtool/frontend/src/components/atoms/Tooltip.tsx b/govtool/frontend/src/components/atoms/Tooltip.tsx index 4b8b7a90a..4e2ccdc51 100644 --- a/govtool/frontend/src/components/atoms/Tooltip.tsx +++ b/govtool/frontend/src/components/atoms/Tooltip.tsx @@ -2,7 +2,7 @@ import { styled } from "@mui/material"; import * as TooltipMUI from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; -type TooltipProps = Omit & { +export type TooltipProps = Omit & { heading?: string; paragraphOne?: string; paragraphTwo?: string; diff --git a/govtool/frontend/src/components/atoms/VotePill.tsx b/govtool/frontend/src/components/atoms/VotePill.tsx index d28ed9da4..de29ac900 100644 --- a/govtool/frontend/src/components/atoms/VotePill.tsx +++ b/govtool/frontend/src/components/atoms/VotePill.tsx @@ -1,6 +1,7 @@ -import { Vote } from "@models"; import { Box, Typography } from "@mui/material"; +import { Vote } from "@models"; + export const VotePill = ({ vote, width, diff --git a/govtool/frontend/src/components/atoms/index.ts b/govtool/frontend/src/components/atoms/index.ts index 7072b7635..1939a0971 100644 --- a/govtool/frontend/src/components/atoms/index.ts +++ b/govtool/frontend/src/components/atoms/index.ts @@ -5,9 +5,11 @@ export * from "./Checkbox"; export * from "./ClickOutside"; export * from "./CopyButton"; export * from "./DrawerLink"; +export * from "./ExternalModalButton"; export * from "./FormErrorMessage"; export * from "./FormHelpfulText"; export * from "./HighlightedText"; +export * from "./IconLink"; export * from "./InfoText"; export * from "./Input"; export * from "./Link"; @@ -19,6 +21,7 @@ export * from "./modal/ModalWrapper"; export * from "./Radio"; export * from "./ScrollToManage"; export * from "./ScrollToTop"; +export * from "./SliderArrow"; export * from "./Spacer"; export * from "./StakeRadio"; export * from "./TextArea"; diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index 86613a8bc..c54010f5a 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -50,7 +50,7 @@ export const DataActionsBar: FC = ({ ...props }) => { return ( <> - + setSearchText(e.target.value)} @@ -76,7 +76,7 @@ export const DataActionsBar: FC = ({ ...props }) => { fontWeight: 500, height: 48, padding: "16px 24px", - width: 231, + width: 500, }} /> { + const { t } = useTranslation(); + + return ( + + + {/* TODO: Text to confirm/change */} + The data that was originally used when this Governance Action was + created has been formatted incorrectly. + + + {/* TODO: Text to confirm/change */} + GovTool uses external sources for Governance Action data, and these + sources are maintained by the proposers of the Actions. This error means + that GovTool cannot locate the data on the URL specified when the + Governance Action was originally posted. + + + // TODO: Add the correct link + openInNewTab( + "https://docs.sanchogov.tools/how-to-use-the-govtool/getting-started/get-a-compatible-wallet" + ) + } + sx={{ + fontFamily: "Poppins", + fontSize: "16px", + lineHeight: "24px", + cursor: "pointer", + }} + > + {t("learnMore")} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx index 567a3fb50..c9f6bbeb3 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx @@ -1,16 +1,22 @@ import { FC } from "react"; import { Box } from "@mui/material"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { Button, Typography, Tooltip } from "@atoms"; +import { Button } from "@atoms"; +import { + GovernanceActionCardElement, + GovernanceActionCardHeader, + GovernanceActionsDatesBox, +} from "@molecules"; + import { useScreenDimension, useTranslation } from "@hooks"; import { formatDisplayDate, getFullGovActionId, getProposalTypeLabel, - getShortenedGovActionId, } from "@utils"; -import { theme } from "@/theme"; + +const mockedLongText = + "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; interface ActionTypeProps extends Omit< @@ -29,6 +35,7 @@ interface ActionTypeProps inProgress?: boolean; txHash: string; index: number; + isDataMissing: boolean; } export const GovernanceActionCard: FC = ({ ...props }) => { @@ -40,14 +47,11 @@ export const GovernanceActionCard: FC = ({ ...props }) => { createdDate, txHash, index, + isDataMissing, } = props; const { isMobile, screenWidth } = useScreenDimension(); const { t } = useTranslation(); - const { - palette: { lightBlue }, - } = theme; - const govActionId = getFullGovActionId(txHash, index); const proposalTypeNoEmptySpaces = getProposalTypeLabel(type).replace( / /g, @@ -56,188 +60,79 @@ export const GovernanceActionCard: FC = ({ ...props }) => { return ( - {inProgress && ( - - - {t("inProgress")} - - - )} - - - {t("govActions.governanceActionType")} - - - - - {getProposalTypeLabel(type)} - - - - - - - {t("govActions.governanceActionId")} - - - - - {getShortenedGovActionId(txHash, index)} - - - - + + + + + - {createdDate ? ( - - - {t("govActions.submissionDate")} - - - {formatDisplayDate(createdDate)} - - - - - - ) : null} - {expiryDate ? ( - - - {t("govActions.expiryDate")} - - - {formatDisplayDate(expiryDate)} - - - - - - ) : null} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx new file mode 100644 index 000000000..fe6baf8c2 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx @@ -0,0 +1,140 @@ +import { Box } from "@mui/material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; + +import { Typography, Tooltip, CopyButton, TooltipProps } from "@atoms"; + +type BaseProps = { + label: string; + text: string; + dataTestId?: string; + isSliderCard?: boolean; + tooltipProps?: Omit; + marginBottom?: number; +}; + +type PillVariantProps = BaseProps & { + textVariant: "pill"; + isCopyButton?: false; +}; + +type OtherVariantsProps = BaseProps & { + textVariant?: "oneLine" | "twoLines" | "longText"; + isCopyButton?: boolean; +}; + +type GovernanceActionCardElementProps = PillVariantProps | OtherVariantsProps; + +export const GovernanceActionCardElement = ({ + label, + text, + dataTestId, + isSliderCard, + textVariant = "oneLine", + isCopyButton, + tooltipProps, + marginBottom, +}: GovernanceActionCardElementProps) => { + return ( + + + + {label} + + {tooltipProps && ( + + + + )} + + + {textVariant === "pill" ? ( + + + {text} + + + ) : ( + + + {text} + + {isCopyButton && ( + + + + )} + + )} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx new file mode 100644 index 000000000..148377b7e --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx @@ -0,0 +1,62 @@ +import { Box } from "@mui/material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; + +import { Tooltip, Typography } from "@atoms"; +import { useTranslation } from "@hooks"; + +const mockedLongText = + "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; + +type GovernanceActionCardHeaderProps = { + title: string; + isDataMissing: boolean; +}; + +export const GovernanceActionCardHeader = ({ + title, + isDataMissing, +}: GovernanceActionCardHeaderProps) => { + const { t } = useTranslation(); + + return ( + + + {isDataMissing ? t("govActions.dataMissing") : title} + + {isDataMissing && ( + + + + )} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx new file mode 100644 index 000000000..2beba72c8 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx @@ -0,0 +1,56 @@ +import { Box } from "@mui/material"; + +import { Button, Typography, VotePill } from "@atoms"; +import { openInNewTab } from "@utils"; +import { useTranslation } from "@hooks"; +import { Vote } from "@models"; + +export const GovernanceActionCardMyVote = ({ vote }: { vote: Vote }) => { + const { t } = useTranslation(); + + return ( + + + {t("govActions.myVote")} + + + + + + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx new file mode 100644 index 000000000..d2144ea85 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx @@ -0,0 +1,50 @@ +import { Box } from "@mui/material"; +import CheckIcon from "@mui/icons-material/Check"; + +import { Typography } from "@atoms"; +import { useTranslation } from "@hooks"; + +export const GovernanceActionCardStatePill = ({ + variant = "voteSubmitted", +}: { + variant?: "inProgress" | "voteSubmitted"; +}) => { + const { t } = useTranslation(); + + return ( + + + {variant === "voteSubmitted" && ( + + )} + {variant === "inProgress" + ? t("inProgress") + : t("govActions.voteSubmitted")} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx new file mode 100644 index 000000000..a54b65431 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx @@ -0,0 +1,56 @@ +import { useLocation } from "react-router-dom"; +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; +import { Share } from "@molecules"; +import { useTranslation } from "@hooks"; + +type GovernanceActionDetailsCardHeaderProps = { + title: string; + isDataMissing: boolean; +}; + +export const GovernanceActionDetailsCardHeader = ({ + title, + isDataMissing, +}: GovernanceActionDetailsCardHeaderProps) => { + const { t } = useTranslation(); + const { pathname, hash } = useLocation(); + + const govActionLinkToShare = `${window.location.protocol}//${ + window.location.hostname + }${window.location.port ? `:${window.location.port}` : ""}${pathname}${hash}`; + + return ( + + + + {isDataMissing ? t("govActions.dataMissing") : title} + + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx new file mode 100644 index 000000000..795205e36 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx @@ -0,0 +1,47 @@ +import { Box } from "@mui/material"; + +import { IconLink, Typography } from "@atoms"; +import { useScreenDimension, useTranslation } from "@hooks"; + +const LINKS = [ + "https://www.google.com", + "https://www.google.com", + "https://www.google.com", + "https://www.google.com", +]; + +export const GovernanceActionDetailsCardLinks = () => { + const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); + + return ( + <> + + {t("govActions.supportingLinks")} + + + {LINKS.map((link, index) => ( + + ))} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx new file mode 100644 index 000000000..ae8872ad4 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx @@ -0,0 +1,79 @@ +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; +import { useTranslation } from "@hooks"; + +type GovernanceActionDetailsCardOnChainDataProps = { + data: { + label: string; + content: string; + }[]; +}; + +export const GovernanceActionDetailsCardOnChainData = ({ + data, +}: GovernanceActionDetailsCardOnChainDataProps) => { + const { t } = useTranslation(); + + return ( + + + + {t("govActions.onChainTransactionDetails")} + + + {data.map(({ label, content }) => ( + + + {label} + {":"} + + + {content} + + + ))} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx new file mode 100644 index 000000000..2abf51ca9 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx @@ -0,0 +1,61 @@ +import { Dispatch, SetStateAction } from "react"; +import { Box } from "@mui/material"; + +import { useScreenDimension } from "@hooks"; +import { VoteActionForm, VotesSubmitted } from "@molecules"; + +type GovernanceActionCardVotesProps = { + setIsVoteSubmitted: Dispatch>; + abstainVotes: number; + noVotes: number; + yesVotes: number; + isOneColumn: boolean; + isVoter?: boolean; + voteFromEP?: string; + isDashboard?: boolean; + isInProgress?: boolean; +}; + +export const GovernanceActionDetailsCardVotes = ({ + setIsVoteSubmitted, + abstainVotes, + noVotes, + yesVotes, + isOneColumn, + isVoter, + voteFromEP, + isDashboard, + isInProgress, +}: GovernanceActionCardVotesProps) => { + const { screenWidth } = useScreenDimension(); + + const isModifiedPadding = + (isDashboard && screenWidth < 1368) ?? screenWidth < 1100; + + return ( + + {isVoter ? ( + + ) : ( + + )} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx new file mode 100644 index 000000000..8db7ef999 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx @@ -0,0 +1,124 @@ +import { Box } from "@mui/material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; +import { Trans } from "react-i18next"; + +import { Tooltip, Typography } from "@atoms"; +import { useScreenDimension, useTranslation } from "@hooks"; + +type GovernanceActionsDatesBoxProps = { + createdDate: string; + expiryDate: string; + isSliderCard?: boolean; +}; + +export const GovernanceActionsDatesBox = ({ + createdDate, + expiryDate, + isSliderCard, +}: GovernanceActionsDatesBoxProps) => { + const { t } = useTranslation(); + const { screenWidth } = useScreenDimension(); + + const isFontSizeSmaller = screenWidth < 420; + + return ( + + + + , + , + ]} + /> + + + + + + + + , + , + ]} + /> + + + + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx index 817c8495d..a6e56f200 100644 --- a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx @@ -1,9 +1,7 @@ import { useNavigate } from "react-router-dom"; import { Box } from "@mui/material"; -import CheckIcon from "@mui/icons-material/Check"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { Button, VotePill, Typography, Tooltip } from "@atoms"; +import { Button } from "@atoms"; import { PATHS } from "@consts"; import { useScreenDimension, useTranslation } from "@hooks"; import { VotedProposal } from "@models"; @@ -11,23 +9,34 @@ import { formatDisplayDate, getFullGovActionId, getProposalTypeLabel, - getShortenedGovActionId, - openInNewTab, } from "@utils"; -import { theme } from "@/theme"; +import { + GovernanceActionCardElement, + GovernanceActionCardHeader, + GovernanceActionCardMyVote, + GovernanceActionCardStatePill, + GovernanceActionsDatesBox, +} from "@molecules"; + +const mockedLongText = + "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; interface Props { votedProposal: VotedProposal; + isDataMissing: boolean; + searchPhrase?: string; inProgress?: boolean; } -export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { +export const GovernanceVotedOnCard = ({ + votedProposal, + isDataMissing, + inProgress, +}: Props) => { const navigate = useNavigate(); const { proposal, vote } = votedProposal; - const { - palette: { lightBlue }, - } = theme; - const { isMobile } = useScreenDimension(); + + const { isMobile, screenWidth } = useScreenDimension(); const { t } = useTranslation(); const proposalTypeNoEmptySpaces = getProposalTypeLabel(proposal.type).replace( @@ -37,213 +46,72 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { return ( + - - {inProgress ? ( - t("inProgress") - ) : ( - <> - - {t("govActions.voteSubmitted")} - - )} - - - - - - {t("govActions.governanceActionType")} - - - - - {getProposalTypeLabel(proposal.type)} - - - - - - - {t("govActions.governanceActionId")} - - - - - {getShortenedGovActionId(proposal.txHash, proposal.index)} - - - - - - - {t("govActions.myVote")} - - - - - - - - + + + + + + - {proposal.createdDate ? ( - - - {t("govActions.submissionDate")} - - - {formatDisplayDate(proposal.createdDate)} - - - - - - ) : null} - {proposal.expiryDate ? ( - - - {t("govActions.expiryDate")} - - - {formatDisplayDate(proposal.expiryDate)} - - - - - - ) : null} diff --git a/govtool/frontend/src/components/molecules/Share.tsx b/govtool/frontend/src/components/molecules/Share.tsx new file mode 100644 index 000000000..1165c9c24 --- /dev/null +++ b/govtool/frontend/src/components/molecules/Share.tsx @@ -0,0 +1,99 @@ +import { useState } from "react"; +import { Box, Popover } from "@mui/material"; + +import { Typography } from "@atoms"; +import { ICONS } from "@consts"; +import { useSnackbar } from "@context"; +import { useTranslation } from "@hooks"; + +export const Share = ({ link }: { link: string }) => { + const { addSuccessAlert } = useSnackbar(); + const { t } = useTranslation(); + const [anchorEl, setAnchorEl] = useState(null); + const [isActive, setIsActive] = useState(true); + + const handleClick = (event: any) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const onCopy = (e: any) => { + navigator.clipboard.writeText(link); + addSuccessAlert(t("alerts.copiedToClipboard")); + setIsActive(false); + e.stopPropagation(); + }; + + const open = Boolean(anchorEl); + const id = open ? "simple-popover" : undefined; + + return ( + <> + + + + + + Share + + link + + Click to copy + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/SliderArrows.tsx b/govtool/frontend/src/components/molecules/SliderArrows.tsx new file mode 100644 index 000000000..f38a45b0e --- /dev/null +++ b/govtool/frontend/src/components/molecules/SliderArrows.tsx @@ -0,0 +1,44 @@ +import { KeenSliderHooks, KeenSliderInstance } from "keen-slider/react"; +import { SliderArrow } from "@atoms"; +import { Box } from "@mui/material"; + +interface ArrowsProps { + currentSlide: number; + instanceRef: React.MutableRefObject | null>; +} + +export const SliderArrows = ({ currentSlide, instanceRef }: ArrowsProps) => { + return ( + <> + {instanceRef.current && ( + + + e.stopPropagation() || instanceRef.current?.prev() + } + disabled={currentSlide === 0} + /> + + e.stopPropagation() || instanceRef.current?.next() + } + disabled={ + currentSlide === + instanceRef.current.track.details.slides.length - 1 + } + /> + + )} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/VoteActionForm.tsx b/govtool/frontend/src/components/molecules/VoteActionForm.tsx index 4c7413400..2cecfbc2a 100644 --- a/govtool/frontend/src/components/molecules/VoteActionForm.tsx +++ b/govtool/frontend/src/components/molecules/VoteActionForm.tsx @@ -1,4 +1,11 @@ -import { useState, useEffect, useMemo, useCallback } from "react"; +import { + useState, + useEffect, + useMemo, + useCallback, + Dispatch, + SetStateAction, +} from "react"; import { useLocation } from "react-router-dom"; import { Box, Link } from "@mui/material"; @@ -14,21 +21,32 @@ import { import { openInNewTab } from "@utils"; import { ControlledField } from "../organisms"; +import { Trans } from "react-i18next"; + +// TODO: Change into props when BE is ready +const castVoteDate = undefined; +const castVoteChangeDeadline = "20.06.2024 (Epoch 445)"; + +type VoteActionFormProps = { + setIsVoteSubmitted: Dispatch>; + voteFromEP?: string; + yesVotes: number; + noVotes: number; + abstainVotes: number; + isInProgress?: boolean; +}; export const VoteActionForm = ({ + setIsVoteSubmitted, voteFromEP, yesVotes, noVotes, abstainVotes, -}: { - voteFromEP?: string; - yesVotes: number; - noVotes: number; - abstainVotes: number; -}) => { - const { state } = useLocation(); + isInProgress, +}: VoteActionFormProps) => { const [isContext, setIsContext] = useState(false); - const { isMobile, screenWidth } = useScreenDimension(); + const { state } = useLocation(); + const { isMobile } = useScreenDimension(); const { openModal } = useModal(); const { t } = useTranslation(); const { voter } = useGetVoterInfo(); @@ -49,8 +67,10 @@ export const VoteActionForm = ({ useEffect(() => { if (state && state.vote) { setValue("vote", state.vote); + setIsVoteSubmitted(true); } else if (voteFromEP) { setValue("vote", voteFromEP); + setIsVoteSubmitted(true); } }, [state, voteFromEP, setValue]); @@ -104,56 +124,97 @@ export const VoteActionForm = ({ ); return ( - - - - {t("govActions.chooseHowToVote")} - - - - - - - - - - + + + {castVoteDate ? ( + <> + + ]} + /> + + + {t("govActions.castVoteDeadline", { + date: castVoteChangeDeadline, + })} + + + ) : ( + + {t("govActions.chooseHowToVote")} + + )} + + + + {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && ( - - - {isDRep ? ( - - ) : ( - - )} - + {(isVoteSubmitted || isInProgress) && ( + + )} + + ); }; diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx new file mode 100644 index 000000000..7ac215c13 --- /dev/null +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx @@ -0,0 +1,121 @@ +import { Box } from "@mui/material"; + +import { ExternalModalButton } from "@atoms"; +import { + GovernanceActionCardElement, + GovernanceActionDetailsCardLinks, + DataMissingInfoBox, + GovernanceActionDetailsCardHeader, + GovernanceActionsDatesBox, + GovernanceActionDetailsCardOnChainData, +} from "@molecules"; +import { useScreenDimension, useTranslation } from "@hooks"; + +const mockedLongDescription = + "I am the Cardano crusader carving his path in the blockchain battleground. With a mind sharper than a Ledger Nano X, this fearless crypto connoisseur fearlessly navigates the volatile seas of Cardano, turning code into currency. Armed with a keyboard and a heart pumping with blockchain beats, Mister Big Bad fearlessly champions decentralization, smart contracts, and the Cardano community. His Twitter feed is a mix of market analysis that rivals CNBC and memes that could break the internet."; + +const mockedOnChainData = [ + { + label: "Reward Address", + content: "Lorem ipsum dolor sit amet consectetur.", + }, + { label: "Amount", content: "₳ 12,350" }, +]; + +type GovernanceActionDetailsCardDataProps = { + type: string; + govActionId: string; + createdDate: string; + expiryDate: string; + isDataMissing: boolean; + isOneColumn: boolean; + isDashboard?: boolean; +}; + +export const GovernanceActionDetailsCardData = ({ + type, + govActionId, + createdDate, + expiryDate, + isDataMissing, + isOneColumn, + isDashboard, +}: GovernanceActionDetailsCardDataProps) => { + const { t } = useTranslation(); + const { screenWidth } = useScreenDimension(); + + const isModifiedPadding = + (isDashboard && screenWidth < 1168) ?? screenWidth < 900; + + return ( + + + {isDataMissing && } + + + {isDataMissing && ( + + )} + + + + + + + + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx index 41eaa2bb2..794a33dc3 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx @@ -2,13 +2,13 @@ import { useNavigate, generatePath } from "react-router-dom"; import { Box } from "@mui/material"; -import { Typography } from '@atoms'; -import { PATHS } from '@consts'; -import { useCardano } from '@context'; -import { useScreenDimension, useTranslation } from '@hooks'; -import { GovernanceActionCard } from '@molecules'; -import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from '@utils'; -import { Slider } from './Slider'; +import { Typography } from "@atoms"; +import { PATHS } from "@consts"; +import { useCardano } from "@context"; +import { useScreenDimension, useTranslation } from "@hooks"; +import { GovernanceActionCard } from "@molecules"; +import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from "@utils"; +import { Slider } from "@organisms"; type GovernanceActionsToVoteProps = { filters: string[]; @@ -34,7 +34,7 @@ export const GovernanceActionsToVote = ({ <> {!proposals.length ? ( - {t('govActions.noResultsForTheSearch')} + {t("govActions.noResultsForTheSearch")} ) : ( <> @@ -46,49 +46,51 @@ export const GovernanceActionsToVote = ({ className="keen-slider__slide" key={action.id} style={{ - overflow: 'visible', - width: 'auto', + overflow: "visible", + width: "auto", }} > onDashboard && - pendingTransaction.vote?.resourceId === + pendingTransaction.vote?.resourceId === action?.txHash + action?.index ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) + "https://adanordic.com/latest_transactions", + ) : navigate( - onDashboard - ? generatePath( - PATHS.dashboardGovernanceActionsAction, - { - proposalId: getFullGovActionId( - action.txHash, - action.index, + onDashboard + ? generatePath( + PATHS.dashboardGovernanceActionsAction, + { + proposalId: getFullGovActionId( + action.txHash, + action.index, + ), + }, + ) + : PATHS.governanceActionsAction.replace( + ":proposalId", + getFullGovActionId( + action.txHash, + action.index, + ), ), - }, - ) - : PATHS.governanceActionsAction.replace( - ":proposalId", - getFullGovActionId( - action.txHash, - action.index, - ), - ), - { - state: { ...action }, - }, - ) + { + state: { ...action }, + }, + ) } /> diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index 577ca56a6..f4f0499c5 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -1,13 +1,15 @@ -import { useCallback, useEffect, useMemo } from "react"; -import { Box, Link, Typography } from "@mui/material"; +import { useEffect } from "react"; +import { generatePath, useNavigate } from "react-router-dom"; +import { Box } from "@mui/material"; import { KeenSliderOptions } from "keen-slider"; import "keen-slider/keen-slider.min.css"; -import { ICONS, PATHS } from "@consts"; import { useCardano } from "@context"; -import { useScreenDimension, useSlider, useTranslation } from "@hooks"; -import { generatePath, useNavigate } from "react-router-dom"; -import styles from "./slider.module.css"; +import { useScreenDimension, useTranslation, useSlider } from "@hooks"; +import { Button, Typography } from "@atoms"; +import { SliderArrows } from "@molecules"; +import { theme } from "@/theme"; +import { PATHS } from "@consts"; const SLIDER_MAX_LENGTH = 1000; @@ -36,39 +38,34 @@ export const Slider = ({ searchPhrase, sorting, }: SliderProps) => { - const { isMobile, screenWidth, pagePadding } = useScreenDimension(); + const { isMobile, screenWidth } = useScreenDimension(); const navigate = useNavigate(); const { pendingTransaction } = useCardano(); const { t } = useTranslation(); + const { + palette: { primaryBlue, arcticWhite, lightBlue }, + } = theme; + const DEFAULT_SLIDER_CONFIG = { mode: "free", initial: 0, slides: { perView: "auto", - spacing: 24, + spacing: 20, }, } as KeenSliderOptions; - const { - currentRange, - sliderRef, - setPercentageValue, - instanceRef, - setCurrentRange, - } = useSlider({ + const isShowArrows = + screenWidth < 268 + 28 + dataLength * 350 + 20 * dataLength - 5; + + const { sliderRef, instanceRef, currentSlide } = useSlider({ config: DEFAULT_SLIDER_CONFIG, sliderMaxLength: SLIDER_MAX_LENGTH, }); - const paddingOffset = useMemo(() => { - const padding = onDashboard ? (isMobile ? 2 : 3.5) : pagePadding; - return padding * 8 * 2; - }, [isMobile, pagePadding]); - const refresh = () => { instanceRef.current?.update(instanceRef.current?.options); - setCurrentRange(0); instanceRef.current?.track.to(0); instanceRef.current?.moveToIdx(0); }; @@ -77,58 +74,66 @@ export const Slider = ({ refresh(); }, [filters, sorting, searchPhrase, pendingTransaction.vote?.resourceId, data]); - const rangeSliderCalculationElement = - dataLength < notSlicedDataLength - ? (screenWidth + - (onDashboard ? -290 - paddingOffset : -paddingOffset + 250)) / - 437 - : (screenWidth + (onDashboard ? -280 - paddingOffset : -paddingOffset)) / - 402; - - const handleLinkPress = useCallback(() => { - if (onDashboard) { - navigate( - generatePath(PATHS.dashboardGovernanceActionsCategory, { - category: navigateKey, - }), - ); - } else { - navigate( - generatePath(PATHS.governanceActionsCategory, { - category: navigateKey, - }), - ); - } - }, [onDashboard]); - return ( - - - {title} - - {isMobile && isShowAll && ( - + + - + {(notSlicedDataLength > 6 || (isMobile && isShowAll)) && ( + + )} + + {isShowArrows && dataLength > 1 && !isMobile && ( + )}
{data} - {!isMobile && isShowAll && dataLength < notSlicedDataLength && ( -
- - - {t("slider.viewAll")} - - arrow - -
- )}
- {!isMobile && Math.floor(rangeSliderCalculationElement) < dataLength && ( - - - - )}
); }; diff --git a/govtool/frontend/src/components/organisms/index.ts b/govtool/frontend/src/components/organisms/index.ts index bbded8e90..cccbc8400 100644 --- a/govtool/frontend/src/components/organisms/index.ts +++ b/govtool/frontend/src/components/organisms/index.ts @@ -17,6 +17,7 @@ export * from "./DrawerMobile"; export * from "./ExternalLinkModal"; export * from "./Footer"; export * from "./GovernanceActionDetailsCard"; +export * from "./GovernanceActionDetailsCardData"; export * from "./GovernanceActionsToVote"; export * from "./Hero"; export * from "./HomeCards"; diff --git a/govtool/frontend/src/consts/icons.ts b/govtool/frontend/src/consts/icons.ts index 2a5fc394d..dfabf260d 100644 --- a/govtool/frontend/src/consts/icons.ts +++ b/govtool/frontend/src/consts/icons.ts @@ -8,6 +8,7 @@ export const ICONS = { closeIcon: "/icons/Close.svg", closeWhiteIcon: "/icons/CloseWhite.svg", copyBlueIcon: "/icons/CopyBlue.svg", + copyBlueThinIcon: "/icons/CopyBlueThin.svg", copyIcon: "/icons/Copy.svg", copyWhiteIcon: "/icons/CopyWhite.svg", dashboardActiveIcon: "/icons/DashboardActive.svg", @@ -25,6 +26,7 @@ export const ICONS = { guidesIcon: "/icons/Guides.svg", helpIcon: "/icons/Help.svg", link: "/icons/Link.svg", + share: "/icons/Share.svg", sortActiveIcon: "/icons/SortActive.svg", sortIcon: "/icons/Sort.svg", sortWhiteIcon: "/icons/SortWhite.svg", diff --git a/govtool/frontend/src/hooks/useSlider.ts b/govtool/frontend/src/hooks/useSlider.ts index 78788791e..e73341422 100644 --- a/govtool/frontend/src/hooks/useSlider.ts +++ b/govtool/frontend/src/hooks/useSlider.ts @@ -1,4 +1,4 @@ -import { ChangeEvent, useState } from "react"; +import { useState } from "react"; import { KeenSliderOptions, useKeenSlider } from "keen-slider/react"; import type { KeenSliderInstance } from "keen-slider"; @@ -49,51 +49,26 @@ const WheelControls = (slider: KeenSliderInstance) => { export const useSlider = ({ config, - sliderMaxLength, }: { config: KeenSliderOptions; sliderMaxLength: number; }) => { const [currentSlide, setCurrentSlide] = useState(0); - const [currentRange, setCurrentRange] = useState(0); const [sliderRef, instanceRef] = useKeenSlider( { ...config, rubberband: false, detailsChanged: (slider) => { - setCurrentRange(slider.track.details.progress * sliderMaxLength); setCurrentSlide(slider.track.details.rel); }, }, [WheelControls], ); - const DATA_LENGTH = instanceRef?.current?.slides?.length ?? 10; - const ITEMS_PER_VIEW = - DATA_LENGTH - (instanceRef?.current?.track?.details?.maxIdx ?? 2); - - const setPercentageValue = (e: ChangeEvent) => { - const target = e?.target; - const currentIndexOfSlide = Math.floor( - +(target?.value ?? 0) / - (sliderMaxLength / (DATA_LENGTH - Math.floor(ITEMS_PER_VIEW))), - ); - - instanceRef.current?.track.add( - (+(target?.value ?? 0) - currentRange) * - (instanceRef.current.track.details.length / sliderMaxLength), - ); - setCurrentRange(+(target?.value ?? 0)); - setCurrentSlide(currentIndexOfSlide); - }; - return { sliderRef, instanceRef, currentSlide, - currentRange, - setCurrentRange, - setPercentageValue, }; }; diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index f70098f2a..28a2756db 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -314,24 +314,39 @@ export const en = { }, }, govActions: { + about: "About", + abstract: "Abstract:", + castVote: "<0>You voted {{vote}} for this proposal\nat {{date}}", + castVoteDeadline: + "You can change your vote up to the deadline of {{date}}", changeVote: "Change vote", changeYourVote: "Change your vote", chooseHowToVote: "Choose how you want to vote:", + dataMissing: "Data Missing", details: "Governance Details:", + expiresDateWithEpoch: "Expires: <0>{{date}} <1>(Epoch {{epoch}})", expiryDate: "Expiry date:", filterTitle: "Governance Action Type", forGovAction: "for this Governance Action", governanceActionId: "Governance Action ID:", governanceActionType: "Governance Action Type:", + motivation: "Motivation", myVote: "My Vote:", noResultsForTheSearch: "No results for the search.", + onChainTransactionDetails: "On-chain Transaction Details", optional: "(optional)", provideContext: "Provide context about your vote", + rationale: "Rationale", + seeExternalData: "See external data", selectDifferentOption: "Select a different option to change your vote", showVotes: "Show votes", submissionDate: "Submission date:", + submittedDateWithEpoch: + "Submitted: <0>{{date}} <1>(Epoch {{epoch}})", + supportingLinks: "Supporting links", title: "Governance Actions", toVote: "To vote", + viewDetails: "View Details", viewOtherDetails: "View other details", viewProposalDetails: "View proposal details", vote: "Vote", @@ -496,7 +511,7 @@ export const en = { }, }, slider: { - showAll: "Show all", + showAll: "Show All", viewAll: "View all", }, soleVoter: { @@ -590,7 +605,7 @@ export const en = { continue: "Continue", delegate: "Delegate", here: "here", - inProgress: "In Progress", + inProgress: "In progress", learnMore: "Learn more", loading: "Loading...", myDRepId: "My DRep ID:", @@ -602,6 +617,7 @@ export const en = { required: "required", seeTransaction: "See transaction", select: "Select", + showMore: "Show more", skip: "Skip", sortBy: "Sort by", submit: "Submit", diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index 8685bfbf8..cf6a0bbdc 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -191,6 +191,8 @@ export const DashboardGovernanceActionsCategory = () => { pendingTransaction.vote?.resourceId === item.txHash + item.index } + // TODO: Add data validation + isDataMissing={false} onClick={() => { saveScrollPosition(); diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index 34378488d..376e4156e 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -130,11 +130,7 @@ export const GovernanceActionDetails = () => {
) : data || state ? ( - + { } url={state ? state.url : data.proposal.url} yesVotes={state ? state.yesVotes : data.proposal.yesVotes} - shortenedGovActionId={shortenedGovActionId} + govActionId={fullProposalId} /> ) : ( diff --git a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx index e29cbe1c3..4538cb006 100644 --- a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx @@ -218,6 +218,8 @@ export const GovernanceActionsCategory = () => { {...item} txHash={item.txHash} index={item.index} + // TODO: Add data validation + isDataMissing={false} onClick={() => { saveScrollPosition(); diff --git a/govtool/frontend/src/theme.ts b/govtool/frontend/src/theme.ts index 8a8736f17..83cd35489 100644 --- a/govtool/frontend/src/theme.ts +++ b/govtool/frontend/src/theme.ts @@ -9,6 +9,59 @@ import { successGreen, } from "./consts"; +declare module "@mui/material/styles" { + interface Palette { + accentOrange: string; + accentYellow: string; + arcticWhite: string; + boxShadow1: string; + boxShadow2: string; + errorRed: string; + highlightBlue: string; + inputRed: string; + negativeRed: string; + neutralGray: string; + orangeDark: string; + neutralWhite: string; + positiveGreen: string; + primaryBlue: string; + secondaryBlue: string; + specialCyan: string; + specialCyanBorder: string; + lightBlue: string; + textBlack: string; + textGray: string; + lightOrange: string; + fadedPurple: string; + } + interface PaletteOptions { + accentOrange: string; + accentYellow: string; + arcticWhite: string; + boxShadow1: string; + boxShadow2: string; + errorRed: string; + highlightBlue: string; + orangeDark: string; + inputRed: string; + negativeRed: string; + neutralGray: string; + neutralWhite: string; + positiveGreen: string; + primaryBlue: string; + secondaryBlue: string; + specialCyan: string; + specialCyanBorder: string; + lightBlue: string; + textBlack: string; + textGray: string; + lightOrange: string; + fadedPurple: string; + } +} + +export type Theme = typeof theme; + export const theme = createTheme({ breakpoints: { values: { @@ -114,8 +167,10 @@ export const theme = createTheme({ palette: { accentOrange: "#F29339", accentYellow: "#F2D9A9", + arcticWhite: "#FBFBFF", boxShadow1: "rgba(0, 18, 61, 0.37)", boxShadow2: "rgba(47, 98, 220, 0.2)", + errorRed: "#9E2323", fadedPurple: "#716E88", highlightBlue: "#C2EFF299", inputRed: "#FAEAEB", From 471927736dc4dc5ee42b853fd2e64cf2aede06b6 Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Sat, 23 Mar 2024 13:45:34 +0100 Subject: [PATCH 10/21] Lint fixes --- .../frontend/src/components/atoms/Radio.tsx | 6 +- .../src/components/atoms/SliderArrow.tsx | 2 +- .../molecules/GovernanceActionCard.tsx | 4 +- .../molecules/GovernanceActionCardElement.tsx | 80 ++--- .../molecules/GovernanceActionCardHeader.tsx | 4 +- .../molecules/GovernanceActionCardMyVote.tsx | 2 +- .../GovernanceActionDetailsCardLinks.tsx | 4 +- ...GovernanceActionDetailsCardOnChainData.tsx | 2 +- .../molecules/GovernanceActionsDatesBox.tsx | 4 +- .../molecules/GovernanceVotedOnCard.tsx | 5 +- .../src/components/molecules/Share.tsx | 12 +- .../src/components/molecules/SliderArrows.tsx | 63 ++-- .../components/molecules/VoteActionForm.tsx | 8 +- .../components/molecules/VotesSubmitted.tsx | 2 +- .../DashboardGovernanceActionDetails.tsx | 6 +- .../DashboardGovernanceActionsVotedOn.tsx | 2 +- .../organisms/GovernanceActionDetailsCard.tsx | 8 +- .../GovernanceActionDetailsCardData.tsx | 4 +- .../organisms/GovernanceActionsToVote.tsx | 74 ++-- .../src/components/organisms/Slider.tsx | 6 +- .../DashboardGovernanceActionsCategory.tsx | 52 +-- .../src/pages/GovernanceActionDetails.tsx | 8 +- .../src/stories/GovernanceAction.stories.ts | 107 +++--- .../GovernanceActionDetailsCard.stories.ts | 127 +++---- .../stories/GovernanceActionVoted.stories.ts | 339 +++++++++--------- 25 files changed, 475 insertions(+), 456 deletions(-) diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index bf4184681..9695dd30c 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -42,9 +42,7 @@ export const Radio = ({ ...props }: RadioProps) => { border={isChecked ? 2 : 0} borderColor={isChecked ? "specialCyanBorder" : undefined} sx={[{ "&:hover": { color: "blue", cursor: "pointer" } }]} - boxShadow={ - "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" - } + boxShadow="0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" > { checked={isChecked} /> diff --git a/govtool/frontend/src/components/atoms/SliderArrow.tsx b/govtool/frontend/src/components/atoms/SliderArrow.tsx index 2ab59999a..aee0bf94e 100644 --- a/govtool/frontend/src/components/atoms/SliderArrow.tsx +++ b/govtool/frontend/src/components/atoms/SliderArrow.tsx @@ -5,7 +5,7 @@ import { theme } from "@/theme"; interface Props { disabled: boolean; - onClick: (e: any) => void; + onClick: (e: React.MouseEvent) => void; left?: boolean; } diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx index c9f6bbeb3..159cd8a73 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx @@ -32,7 +32,7 @@ interface ActionTypeProps | "index" > { onClick?: () => void; - inProgress?: boolean; + // inProgress?: boolean; txHash: string; index: number; isDataMissing: boolean; @@ -41,7 +41,7 @@ interface ActionTypeProps export const GovernanceActionCard: FC = ({ ...props }) => { const { type, - inProgress = false, + // inProgress = false, expiryDate, onClick, createdDate, diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx index fe6baf8c2..51186d699 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx @@ -33,21 +33,20 @@ export const GovernanceActionCardElement = ({ isCopyButton, tooltipProps, marginBottom, -}: GovernanceActionCardElementProps) => { - return ( +}: GovernanceActionCardElementProps) => ( + - - + - {label} - - {tooltipProps && ( - - - + > + {label} + + {tooltipProps && ( + + + )} - - - {textVariant === "pill" ? ( - + + {textVariant === "pill" ? ( + - + - {text} - - + > + {text} + + ) : ( )} - + ); -}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx index 148377b7e..3d0c023ca 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx @@ -43,9 +43,9 @@ export const GovernanceActionCardHeader = ({ {isDataMissing && ( { const { t } = useTranslation(); return ( - + { rowGap: 2, }} > - {LINKS.map((link, index) => ( - + {LINKS.map((link) => ( + ))} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx index ae8872ad4..1a4077ce0 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx @@ -57,7 +57,7 @@ export const GovernanceActionDetailsCardOnChainData = ({ }} > {label} - {":"} + : { const navigate = useNavigate(); @@ -104,7 +105,7 @@ export const GovernanceVotedOnCard = ({ { const { addSuccessAlert } = useSnackbar(); const { t } = useTranslation(); - const [anchorEl, setAnchorEl] = useState(null); - const [isActive, setIsActive] = useState(true); + const [anchorEl, setAnchorEl] = useState(null); - const handleClick = (event: any) => { - setAnchorEl(event.currentTarget); + const handleClick = (e: React.MouseEvent) => { + setAnchorEl(e.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; - const onCopy = (e: any) => { + const onCopy = (e: React.MouseEvent) => { navigator.clipboard.writeText(link); addSuccessAlert(t("alerts.copiedToClipboard")); - setIsActive(false); e.stopPropagation(); }; @@ -46,7 +44,7 @@ export const Share = ({ link }: { link: string }) => { padding: 1.5, }} > - + share icon | null>; } -export const SliderArrows = ({ currentSlide, instanceRef }: ArrowsProps) => { - return ( - <> - {instanceRef.current && ( - ( + <> + {instanceRef.current && ( + + ) => { + e.stopPropagation(); + instanceRef.current?.prev(); }} - > - - e.stopPropagation() || instanceRef.current?.prev() - } - disabled={currentSlide === 0} - /> - - e.stopPropagation() || instanceRef.current?.next() - } - disabled={ - currentSlide === - instanceRef.current.track.details.slides.length - 1 - } - /> - - )} - - ); -}; + disabled={currentSlide === 0} + /> + ) => { + e.stopPropagation(); + instanceRef.current?.next(); + }} + disabled={ + currentSlide === instanceRef.current.track.details.slides.length - 1 + } + /> + + )} + +); diff --git a/govtool/frontend/src/components/molecules/VoteActionForm.tsx b/govtool/frontend/src/components/molecules/VoteActionForm.tsx index 2cecfbc2a..26a6bb484 100644 --- a/govtool/frontend/src/components/molecules/VoteActionForm.tsx +++ b/govtool/frontend/src/components/molecules/VoteActionForm.tsx @@ -20,8 +20,8 @@ import { } from "@hooks"; import { openInNewTab } from "@utils"; -import { ControlledField } from "../organisms"; import { Trans } from "react-i18next"; +import { ControlledField } from "../organisms"; // TODO: Change into props when BE is ready const castVoteDate = undefined; @@ -237,8 +237,8 @@ export const VoteActionForm = ({ flexDirection="row" flexWrap="wrap" justifyContent="center" - mb={"15px"} - mt={"58px"} + mb="15px" + mt="58px" onClick={handleContext} >

{ > {t("govActions.forGovAction")} - + {t("govActions.votesSubmittedOnChain")} { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } - details={state ? state.details : data.proposal.details} + // TODO: To decide if we want to keep it when metadate BE is ready + // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) @@ -145,7 +146,8 @@ export const DashboardGovernanceActionDetails = () => { ? getProposalTypeLabel(state.type) : getProposalTypeLabel(data.proposal.type) } - url={state ? state.url : data.proposal.url} + // TODO: To decide if we want to keep it when metadate BE is ready + // url={state ? state.url : data.proposal.url} yesVotes={state ? state.yesVotes : data.proposal.yesVotes} voteFromEP={data?.vote?.vote} govActionId={fullProposalId} diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx index a958fc570..e14f42f35 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx @@ -76,7 +76,7 @@ export const DashboardGovernanceActionsVotedOn = ({ > {isDataMissing && } @@ -72,7 +72,7 @@ export const GovernanceActionDetailsCardData = ({ /> {isDataMissing && ( )} diff --git a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx index 794a33dc3..e8862e026 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx @@ -10,6 +10,13 @@ import { GovernanceActionCard } from "@molecules"; import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from "@utils"; import { Slider } from "@organisms"; +type GroupedActions = { + [key: string]: { + title: string; + actions: ActionType[]; + }; +}; + type GovernanceActionsToVoteProps = { filters: string[]; sorting: string; @@ -54,44 +61,47 @@ export const GovernanceActionsToVote = ({ {...action} txHash={action.txHash} index={action.index} + // inProgress={ + // onDashboard && + // pendingTransaction.vote?.resourceId === + // action?.txHash + action?.index + // } // TODO: Add data validation isDataMissing={false} - inProgress={ - onDashboard && - pendingTransaction.vote?.resourceId === - action?.txHash + action?.index - } - // eslint-disable-next-line no-confusing-arrow - onClick={() => - onDashboard && - pendingTransaction.vote?.resourceId === - action?.txHash + action?.index - ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) - : navigate( - onDashboard - ? generatePath( - PATHS.dashboardGovernanceActionsAction, - { - proposalId: getFullGovActionId( - action.txHash, - action.index, - ), - }, - ) - : PATHS.governanceActionsAction.replace( - ":proposalId", - getFullGovActionId( + onClick={() => { + if ( + onDashboard && + pendingTransaction.vote?.resourceId === + `${action.txHash ?? ""}${action.index ?? ""}` + ) { + openInNewTab( + "https://adanordic.com/latest_transactions", + ); + } else { + navigate( + onDashboard + ? generatePath( + PATHS.dashboardGovernanceActionsAction, + { + proposalId: getFullGovActionId( action.txHash, action.index, ), + }, + ) + : PATHS.governanceActionsAction.replace( + ":proposalId", + getFullGovActionId( + action.txHash, + action.index, ), - { - state: { ...action }, - }, - ) - } + ), + { + state: { ...action }, + }, + ); + } + }} /> ))} diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index f4f0499c5..545bb8c47 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -8,8 +8,8 @@ import { useCardano } from "@context"; import { useScreenDimension, useTranslation, useSlider } from "@hooks"; import { Button, Typography } from "@atoms"; import { SliderArrows } from "@molecules"; -import { theme } from "@/theme"; import { PATHS } from "@consts"; +import { theme } from "@/theme"; const SLIDER_MAX_LENGTH = 1000; @@ -115,7 +115,7 @@ export const Slider = ({ "&:hover": { backgroundColor: arcticWhite }, }} onClick={() => - onDashboard + (onDashboard ? navigate( generatePath(PATHS.dashboardGovernanceActionsCategory, { category: navigateKey, @@ -125,7 +125,7 @@ export const Slider = ({ generatePath(PATHS.governanceActionsCategory, { category: navigateKey, }), - ) + )) } > {t("slider.showAll")} diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index cf6a0bbdc..16187ab72 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -175,49 +175,51 @@ export const DashboardGovernanceActionsCategory = () => { {mappedData.map((item) => ( { saveScrollPosition(); // eslint-disable-next-line no-unused-expressions - pendingTransaction.vote?.resourceId === item.txHash + item.index + pendingTransaction.vote?.resourceId === + item.txHash + item.index ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) + "https://adanordic.com/latest_transactions", + ) : navigate( - generatePath( - PATHS.dashboardGovernanceActionsAction, + generatePath( + PATHS.dashboardGovernanceActionsAction, + { + proposalId: getFullGovActionId( + item.txHash, + item.index, + ), + }, + ), { - proposalId: getFullGovActionId( - item.txHash, - item.index, - ), - }, - ), - { - state: { - ...item, - openedFromCategoryPage: true, + state: { + ...item, + openedFromCategoryPage: true, + }, }, - }, - ); + ); }} txHash={item.txHash} /> diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index 376e4156e..fe916ca4d 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -130,7 +130,7 @@ export const GovernanceActionDetails = () => { ) : data || state ? ( - + { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } - details={state ? state.details : data.proposal.details} + // TODO: To decide if we want to keep it when metadate BE is ready + // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) @@ -152,7 +153,8 @@ export const GovernanceActionDetails = () => { ? getProposalTypeLabel(state.type) : getProposalTypeLabel(data.proposal.type) } - url={state ? state.url : data.proposal.url} + // TODO: To decide if we want to keep it when metadate BE is ready + // url={state ? state.url : data.proposal.url} yesVotes={state ? state.yesVotes : data.proposal.yesVotes} govActionId={fullProposalId} /> diff --git a/govtool/frontend/src/stories/GovernanceAction.stories.ts b/govtool/frontend/src/stories/GovernanceAction.stories.ts index 8ed28776b..770b603c0 100644 --- a/govtool/frontend/src/stories/GovernanceAction.stories.ts +++ b/govtool/frontend/src/stories/GovernanceAction.stories.ts @@ -1,58 +1,61 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { within, userEvent, waitFor, screen } from "@storybook/testing-library"; -import { expect, jest } from "@storybook/jest"; +// Story to be updated when new Gov Actions are finished +/* eslint-disable storybook/default-exports */ -import { formatDisplayDate } from "@utils"; -import { GovernanceActionCard } from "@/components/molecules"; +// import type { Meta, StoryObj } from "@storybook/react"; +// import { within, userEvent, waitFor, screen } from "@storybook/testing-library"; +// import { expect, jest } from "@storybook/jest"; -const meta = { - title: "Example/GovernanceActionCard", - component: GovernanceActionCard, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], -} satisfies Meta; +// import { formatDisplayDate } from "@utils"; +// import { GovernanceActionCard } from "@/components/molecules"; -export default meta; -type Story = StoryObj; +// const meta = { +// title: "Example/GovernanceActionCard", +// component: GovernanceActionCard, +// parameters: { +// layout: "centered", +// }, +// tags: ["autodocs"], +// } satisfies Meta; -export const GovernanceActionCardComponent: Story = { - args: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - type: "exampleType", - txHash: "sad78afdsf7jasd98d", - index: 2, - onClick: jest.fn(), - }, - play: async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); - expect(canvas.getByTestId("sad78afdsf7jasd98d#2-id")).toBeInTheDocument(); - expect( - canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), - ).toBeInTheDocument(); - expect( - canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), - ).toBeInTheDocument(); +// export default meta; +// type Story = StoryObj; - const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); - await userEvent.hover(tooltips[0]); - await waitFor(async () => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); - await userEvent.unhover(tooltips[0]); - }); - await userEvent.hover(tooltips[1]); - await waitFor(() => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); - }); +// export const GovernanceActionCardComponent: Story = { +// args: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// type: "exampleType", +// txHash: "sad78afdsf7jasd98d", +// index: 2, +// onClick: jest.fn(), +// }, +// play: async ({ canvasElement, args }) => { +// const canvas = within(canvasElement); +// expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); +// expect(canvas.getByTestId("sad78afdsf7jasd98d#2-id")).toBeInTheDocument(); +// expect( +// canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), +// ).toBeInTheDocument(); +// expect( +// canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), +// ).toBeInTheDocument(); - await userEvent.click( - canvas.getByTestId("govaction-sad78afdsf7jasd98d#2-view-detail"), - ); - await expect(args.onClick).toHaveBeenCalled(); - }, -}; +// const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); +// await userEvent.hover(tooltips[0]); +// await waitFor(async () => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); +// await userEvent.unhover(tooltips[0]); +// }); +// await userEvent.hover(tooltips[1]); +// await waitFor(() => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); +// }); + +// await userEvent.click( +// canvas.getByTestId("govaction-sad78afdsf7jasd98d#2-view-detail"), +// ); +// await expect(args.onClick).toHaveBeenCalled(); +// }, +// }; diff --git a/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts b/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts index 8f7d8793d..454db3a52 100644 --- a/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts +++ b/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts @@ -1,62 +1,65 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { screen, userEvent, waitFor, within } from "@storybook/testing-library"; -import { GovernanceActionDetailsCard } from "@organisms"; -import { expect } from "@storybook/jest"; - -const meta = { - title: "Example/GovernanceActionDetailsCard", - component: GovernanceActionDetailsCard, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const GovernanceActionDetailsCardComponent: Story = { - args: { - abstainVotes: 1000000, - createdDate: new Date().toLocaleDateString(), - expiryDate: new Date().toLocaleDateString(), - shortenedGovActionId: "Example id", - noVotes: 1000000, - type: "Gov Type", - url: "https://exampleurl.com", - yesVotes: 1000000, - details: { - key: "value", - key2: ["key-list", "key-list", "key-list"], - }, - }, - - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const todayDate = new Date().toLocaleDateString(); - await expect(canvas.getAllByText(todayDate)).toHaveLength(2); - - const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); - await userEvent.hover(tooltips[0]); - await waitFor(async () => { - await expect(screen.getByRole("tooltip")).toBeInTheDocument(); - await expect(screen.getByRole("tooltip")).toHaveTextContent( - /Submission Date/i, - ); - await userEvent.unhover(tooltips[0]); - }); - await userEvent.hover(tooltips[1]); - - await expect(canvas.getByText("Gov Type")).toBeInTheDocument(); - await expect(canvas.getByText("Example id")).toBeInTheDocument(); - - await expect(canvas.getByText("key: value")).toBeInTheDocument(); - await expect(canvas.getAllByText("key-list")).toHaveLength(3); - - await expect(canvas.getByText(/Yes/i)).toBeInTheDocument(); - await expect(canvas.getByText(/Abstain/i)).toBeInTheDocument(); - await expect(canvas.getByText(/No/i)).toBeInTheDocument(); - - await userEvent.click(canvas.getByTestId("view-other-details-button")); - }, -}; +// Story to be updated when new Gov Actions are finished +/* eslint-disable storybook/default-exports */ + +// import type { Meta, StoryObj } from "@storybook/react"; +// import { screen, userEvent, waitFor, within } from "@storybook/testing-library"; +// import { GovernanceActionDetailsCard } from "@organisms"; +// import { expect } from "@storybook/jest"; + +// const meta = { +// title: "Example/GovernanceActionDetailsCard", +// component: GovernanceActionDetailsCard, +// parameters: { +// layout: "centered", +// }, +// tags: ["autodocs"], +// } satisfies Meta; + +// export default meta; +// type Story = StoryObj; + +// export const GovernanceActionDetailsCardComponent: Story = { +// args: { +// abstainVotes: 1000000, +// createdDate: new Date().toLocaleDateString(), +// expiryDate: new Date().toLocaleDateString(), +// shortenedGovActionId: "Example id", +// noVotes: 1000000, +// type: "Gov Type", +// url: "https://exampleurl.com", +// yesVotes: 1000000, +// details: { +// key: "value", +// key2: ["key-list", "key-list", "key-list"], +// }, +// }, + +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// const todayDate = new Date().toLocaleDateString(); +// await expect(canvas.getAllByText(todayDate)).toHaveLength(2); + +// const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); +// await userEvent.hover(tooltips[0]); +// await waitFor(async () => { +// await expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// await expect(screen.getByRole("tooltip")).toHaveTextContent( +// /Submission Date/i, +// ); +// await userEvent.unhover(tooltips[0]); +// }); +// await userEvent.hover(tooltips[1]); + +// await expect(canvas.getByText("Gov Type")).toBeInTheDocument(); +// await expect(canvas.getByText("Example id")).toBeInTheDocument(); + +// await expect(canvas.getByText("key: value")).toBeInTheDocument(); +// await expect(canvas.getAllByText("key-list")).toHaveLength(3); + +// await expect(canvas.getByText(/Yes/i)).toBeInTheDocument(); +// await expect(canvas.getByText(/Abstain/i)).toBeInTheDocument(); +// await expect(canvas.getByText(/No/i)).toBeInTheDocument(); + +// await userEvent.click(canvas.getByTestId("view-other-details-button")); +// }, +// }; diff --git a/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts b/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts index 4fdbc5790..3547562ab 100644 --- a/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts +++ b/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts @@ -1,180 +1,183 @@ -import type { Meta, StoryObj } from "@storybook/react"; +// Story to be updated when new Gov Actions are finished +/* eslint-disable storybook/default-exports */ -import { GovernanceVotedOnCard } from "@molecules"; -import { userEvent, waitFor, within, screen } from "@storybook/testing-library"; -import { expect } from "@storybook/jest"; -import { formatDisplayDate } from "@/utils"; +// import type { Meta, StoryObj } from "@storybook/react"; -const meta = { - title: "Example/GovernanceVotedOnCard", - component: GovernanceVotedOnCard, - parameters: { - layout: "centered", - }, +// import { GovernanceVotedOnCard } from "@molecules"; +// import { userEvent, waitFor, within, screen } from "@storybook/testing-library"; +// import { expect } from "@storybook/jest"; +// import { formatDisplayDate } from "@/utils"; - tags: ["autodocs"], -} satisfies Meta; +// const meta = { +// title: "Example/GovernanceVotedOnCard", +// component: GovernanceVotedOnCard, +// parameters: { +// layout: "centered", +// }, -export default meta; -type Story = StoryObj; +// tags: ["autodocs"], +// } satisfies Meta; -async function checkGovActionVisibility(canvas: ReturnType) { - expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); - expect(canvas.getByTestId("exampleHash#1-id")).toBeInTheDocument(); - expect(canvas.getByText(/vote submitted/i)).toBeInTheDocument(); +// export default meta; +// type Story = StoryObj; - expect( - canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), - ).toBeInTheDocument(); - expect( - canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), - ).toBeInTheDocument(); +// async function checkGovActionVisibility(canvas: ReturnType) { +// expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); +// expect(canvas.getByTestId("exampleHash#1-id")).toBeInTheDocument(); +// expect(canvas.getByText(/vote submitted/i)).toBeInTheDocument(); - const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); - await userEvent.hover(tooltips[0]); - await waitFor(async () => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); - await userEvent.unhover(tooltips[0]); - }); - await userEvent.hover(tooltips[1]); - await waitFor(() => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); - }); +// expect( +// canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), +// ).toBeInTheDocument(); +// expect( +// canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), +// ).toBeInTheDocument(); - await expect( - canvas.getByTestId("govaction-exampleHash#1-change-your-vote"), - ).toBeInTheDocument(); -} +// const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); +// await userEvent.hover(tooltips[0]); +// await waitFor(async () => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); +// await userEvent.unhover(tooltips[0]); +// }); +// await userEvent.hover(tooltips[1]); +// await waitFor(() => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); +// }); -export const GovernanceVotedOnCardComponent: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "no", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - }, -}; +// await expect( +// canvas.getByTestId("govaction-exampleHash#1-change-your-vote"), +// ).toBeInTheDocument(); +// } -export const GovernanceVotedOnCardAbstain: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "abstain", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - expect(canvas.getByText(/abstain/i)).toBeInTheDocument(); - }, -}; +// export const GovernanceVotedOnCardComponent: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "no", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// }, +// }; -export const GovernanceVotedOnCardYes: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "yes", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - expect(canvas.getByText(/yes/i)).toBeInTheDocument(); - }, -}; +// export const GovernanceVotedOnCardAbstain: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "abstain", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// expect(canvas.getByText(/abstain/i)).toBeInTheDocument(); +// }, +// }; -export const GovernanceVotedOnCardNo: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "no", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - expect(canvas.getByText(/no/i)).toBeInTheDocument(); - }, -}; +// export const GovernanceVotedOnCardYes: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "yes", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// expect(canvas.getByText(/yes/i)).toBeInTheDocument(); +// }, +// }; + +// export const GovernanceVotedOnCardNo: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "no", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// expect(canvas.getByText(/no/i)).toBeInTheDocument(); +// }, +// }; From 2aa8b89f50e98920aa4911caa5150da70410702e Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Sun, 24 Mar 2024 16:31:30 +0100 Subject: [PATCH 11/21] Further changes --- govtool/frontend/public/icons/Separator.svg | 3 + .../components/atoms/ExternalModalButton.tsx | 1 + .../frontend/src/components/atoms/Radio.tsx | 13 +- .../src/components/atoms/SliderArrow.tsx | 9 +- .../src/components/molecules/Breadcrumbs.tsx | 57 ++++++++ .../components/molecules/DataActionsBar.tsx | 3 +- .../molecules/GovernanceActionCard.tsx | 25 ++-- .../molecules/GovernanceActionCardHeader.tsx | 8 +- .../GovernanceActionCardStatePill.tsx | 1 + .../GovernanceActionDetailsCardHeader.tsx | 1 + .../GovernanceActionDetailsCardLinks.tsx | 10 +- ...GovernanceActionDetailsCardOnChainData.tsx | 6 +- .../molecules/GovernanceActionsDatesBox.tsx | 2 + .../molecules/GovernanceVotedOnCard.tsx | 28 ++-- .../components/molecules/OrderActionsChip.tsx | 79 ++++++++--- .../src/components/molecules/Share.tsx | 4 +- .../src/components/molecules/SliderArrows.tsx | 14 +- .../components/molecules/VoteActionForm.tsx | 68 ++++----- .../src/components/molecules/index.ts | 1 + .../DashboardGovernanceActionDetails.tsx | 48 ++----- .../organisms/DashboardGovernanceActions.tsx | 2 +- .../DashboardGovernanceActionsVotedOn.tsx | 2 +- .../organisms/GovernanceActionDetailsCard.tsx | 10 +- .../GovernanceActionDetailsCardData.tsx | 12 +- .../organisms/GovernanceActionsToVote.tsx | 18 +-- .../src/components/organisms/Slider.tsx | 77 +++++----- govtool/frontend/src/consts/icons.ts | 1 + govtool/frontend/src/context/wallet.tsx | 130 ++++++++--------- govtool/frontend/src/hooks/useSlider.ts | 12 +- govtool/frontend/src/i18n/locales/en.ts | 9 +- .../DashboardGovernanceActionsCategory.tsx | 133 ++++++++---------- .../src/pages/GovernanceActionDetails.tsx | 60 ++++---- .../src/pages/GovernanceActionsCategory.tsx | 71 ++-------- .../src/utils/getProposalTypeLabel.ts | 2 + 34 files changed, 497 insertions(+), 423 deletions(-) create mode 100644 govtool/frontend/public/icons/Separator.svg create mode 100644 govtool/frontend/src/components/molecules/Breadcrumbs.tsx diff --git a/govtool/frontend/public/icons/Separator.svg b/govtool/frontend/public/icons/Separator.svg new file mode 100644 index 000000000..e6edeb0dd --- /dev/null +++ b/govtool/frontend/public/icons/Separator.svg @@ -0,0 +1,3 @@ + + + diff --git a/govtool/frontend/src/components/atoms/ExternalModalButton.tsx b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx index c4debddc5..8584c15b0 100644 --- a/govtool/frontend/src/components/atoms/ExternalModalButton.tsx +++ b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx @@ -32,6 +32,7 @@ export const ExternalModalButton = ({ }} disableRipple variant="text" + data-testid="external-modal-button" > {label} diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index 9695dd30c..f93e68ec3 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -41,8 +41,17 @@ export const Radio = ({ ...props }: RadioProps) => { p={isChecked ? "2px" : 0} border={isChecked ? 2 : 0} borderColor={isChecked ? "specialCyanBorder" : undefined} - sx={[{ "&:hover": { color: "blue", cursor: "pointer" } }]} - boxShadow="0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" + sx={[ + { + boxShadow: + "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)", + + "&:hover": { + color: "blue", + cursor: disabled ? "default" : "pointer", + }, + }, + ]} > ) => void; left?: boolean; } -export const SliderArrow = ({ disabled, onClick, left }: Props) => { +export const SliderArrow = ({ disabled, onClick, left }: SliderArrowProps) => { const { palette: { primaryBlue, arcticWhite, lightBlue }, } = theme; @@ -27,6 +27,11 @@ export const SliderArrow = ({ disabled, onClick, left }: Props) => { justifyContent: "center", alignItems: "center", cursor: "pointer", + transition: "0.3s", + + "&:hover": { + boxShadow: disabled ? 0 : 2, + }, }} > { + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + + return ( + + + + {elementOne} + + + separator + + {isDataMissing ? t("govActions.dataMissing") : elementTwo} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index c54010f5a..297d58bab 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -2,10 +2,9 @@ import { Dispatch, FC, SetStateAction } from "react"; import { Box, InputBase } from "@mui/material"; import Search from "@mui/icons-material/Search"; -import { GovernanceActionsFilters, GovernanceActionsSorting } from "."; +import { GovernanceActionsFilters, GovernanceActionsSorting } from "@molecules"; import { OrderActionsChip } from "./OrderActionsChip"; import { ClickOutside } from "../atoms"; - import { theme } from "@/theme"; type DataActionsBarProps = { diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx index 159cd8a73..29bb4dad4 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx @@ -5,6 +5,7 @@ import { Button } from "@atoms"; import { GovernanceActionCardElement, GovernanceActionCardHeader, + GovernanceActionCardStatePill, GovernanceActionsDatesBox, } from "@molecules"; @@ -13,6 +14,7 @@ import { formatDisplayDate, getFullGovActionId, getProposalTypeLabel, + getProposalTypeNoEmptySpaces, } from "@utils"; const mockedLongText = @@ -31,17 +33,17 @@ interface ActionTypeProps | "txHash" | "index" > { - onClick?: () => void; - // inProgress?: boolean; txHash: string; index: number; isDataMissing: boolean; + onClick?: () => void; + inProgress?: boolean; } export const GovernanceActionCard: FC = ({ ...props }) => { const { type, - // inProgress = false, + inProgress = false, expiryDate, onClick, createdDate, @@ -53,10 +55,6 @@ export const GovernanceActionCard: FC = ({ ...props }) => { const { t } = useTranslation(); const govActionId = getFullGovActionId(txHash, index); - const proposalTypeNoEmptySpaces = getProposalTypeLabel(type).replace( - / /g, - "", - ); return ( = ({ ...props }) => { ...(isDataMissing && { border: "1px solid #F6D5D5", }), + ...(inProgress && { + border: "1px solid #FFCBAD", + }), }} - data-testid={`govaction-${proposalTypeNoEmptySpaces}-card`} + data-testid={`govaction-${getProposalTypeNoEmptySpaces(type)}-card`} > + {inProgress && } = ({ ...props }) => { label={t("govActions.governanceActionType")} text={getProposalTypeLabel(type)} textVariant="pill" - dataTestId={`${proposalTypeNoEmptySpaces}-type`} + dataTestId={`${getProposalTypeNoEmptySpaces(type)}-type`} isSliderCard /> = ({ ...props }) => { > )} + {/* TODO: Change below into new voting context */} {(state?.vote && state?.vote !== vote) || - (voteFromEP && voteFromEP !== vote) ? ( - - {isMobile ? renderChangeVoteButton : renderCancelButton} - - {isMobile ? renderCancelButton : renderChangeVoteButton} - + (voteFromEP && voteFromEP !== vote) ? ( + + {isMobile ? renderChangeVoteButton : renderCancelButton} + + {isMobile ? renderCancelButton : renderChangeVoteButton} + ) : ( { const { voter } = useGetVoterInfo(); const { pendingTransaction } = useCardano(); const { state, hash } = useLocation(); const navigate = useNavigate(); - const { isMobile, screenWidth } = useScreenDimension(); + const { isMobile } = useScreenDimension(); const { t } = useTranslation(); const { proposalId } = useParams(); const fullProposalId = proposalId + hash; @@ -45,21 +42,6 @@ export const DashboardGovernanceActionDetails = () => { state ? state.index : data?.proposal.index ?? "", ); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {t("govActions.voteOnGovActions")} - , - ]; - return ( { flex={1} > - {breadcrumbs} - + elementOne={t("govActions.title")} + elementOnePath={PATHS.dashboardGovernanceActions} + elementTwo="Fund our project" + isDataMissing={false} + /> { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - {t("backToList")} + {t("back")} @@ -130,6 +108,8 @@ export const DashboardGovernanceActionDetails = () => { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } + // TODO: Add data validation + isDataMissing={isDataMissing} // TODO: To decide if we want to keep it when metadate BE is ready // details={state ? state.details : data.proposal.details} expiryDate={ diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx index cf07ab14e..2ff29ba3e 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx @@ -164,7 +164,7 @@ export const DashboardGovernanceActions = () => { /> (

{ const [isVoteSubmitted, setIsVoteSubmitted] = useState(false); const { screenWidth, isMobile } = useScreenDimension(); - // TODO: Add as a prop when BE is ready - const isDataMissing = false; - const isOneColumn = (isDashboard && screenWidth < 1036) ?? isMobile; return ( @@ -57,9 +56,12 @@ export const GovernanceActionDetailsCard = ({ position: "relative", boxShadow: isInProgress ? "2px 2px 20px 0px rgba(245, 90, 0, 0.20)" - : isVoteSubmitted + : isVoteSubmitted && !isDataMissing ? "2px 2px 20px 0px rgba(98, 188, 82, 0.20)" : "2px 2px 20px 0px rgba(47, 98, 220, 0.20)", + ...(isDataMissing && { + border: "1px solid #F6D5D5", + }), }} data-testid="governance-action-details-card" > diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx index 25240ecf4..9a6641d41 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx @@ -10,6 +10,7 @@ import { GovernanceActionDetailsCardOnChainData, } from "@molecules"; import { useScreenDimension, useTranslation } from "@hooks"; +import { getProposalTypeNoEmptySpaces } from "@utils"; const mockedLongDescription = "I am the Cardano crusader carving his path in the blockchain battleground. With a mind sharper than a Ledger Nano X, this fearless crypto connoisseur fearlessly navigates the volatile seas of Cardano, turning code into currency. Armed with a keyboard and a heart pumping with blockchain beats, Mister Big Bad fearlessly champions decentralization, smart contracts, and the Cardano community. His Twitter feed is a mix of market analysis that rivals CNBC and memes that could break the internet."; @@ -57,7 +58,10 @@ export const GovernanceActionDetailsCardData = ({ }} > {isDataMissing && } @@ -65,6 +69,7 @@ export const GovernanceActionDetailsCardData = ({ label={t("govActions.governanceActionType")} text={type} textVariant="pill" + dataTestId={`${getProposalTypeNoEmptySpaces(type)}-type`} /> + {/* TODO: To add option display of on-chain data when BE is ready */} diff --git a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx index e8862e026..b9ce93be9 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-unsafe-optional-chaining */ import { useNavigate, generatePath } from "react-router-dom"; import { Box } from "@mui/material"; @@ -10,13 +9,6 @@ import { GovernanceActionCard } from "@molecules"; import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from "@utils"; import { Slider } from "@organisms"; -type GroupedActions = { - [key: string]: { - title: string; - actions: ActionType[]; - }; -}; - type GovernanceActionsToVoteProps = { filters: string[]; sorting: string; @@ -61,11 +53,11 @@ export const GovernanceActionsToVote = ({ {...action} txHash={action.txHash} index={action.index} - // inProgress={ - // onDashboard && - // pendingTransaction.vote?.resourceId === - // action?.txHash + action?.index - // } + inProgress={ + onDashboard && + pendingTransaction.vote?.resourceId === + `${action.txHash ?? ""}${action.index ?? ""}` + } // TODO: Add data validation isDataMissing={false} onClick={() => { diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index 545bb8c47..1394a70b5 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useMemo, useState } from "react"; import { generatePath, useNavigate } from "react-router-dom"; import { Box } from "@mui/material"; import { KeenSliderOptions } from "keen-slider"; @@ -11,8 +11,6 @@ import { SliderArrows } from "@molecules"; import { PATHS } from "@consts"; import { theme } from "@/theme"; -const SLIDER_MAX_LENGTH = 1000; - type SliderProps = { title: string; navigateKey: string; @@ -38,6 +36,8 @@ export const Slider = ({ searchPhrase, sorting, }: SliderProps) => { + const [isSliderInitialized, setIsSliderInitialized] = useState(false); + const { isMobile, screenWidth } = useScreenDimension(); const navigate = useNavigate(); const { pendingTransaction } = useCardano(); @@ -56,12 +56,15 @@ export const Slider = ({ }, } as KeenSliderOptions; - const isShowArrows = - screenWidth < 268 + 28 + dataLength * 350 + 20 * dataLength - 5; + const isShowArrows = useMemo( + () => + screenWidth < + (onDashboard ? 268 : 40) + 28 + dataLength * 350 + 20 * dataLength - 5, + [screenWidth, dataLength], + ); - const { sliderRef, instanceRef, currentSlide } = useSlider({ + const { sliderRef, instanceRef, currentSlide, itemsPerView } = useSlider({ config: DEFAULT_SLIDER_CONFIG, - sliderMaxLength: SLIDER_MAX_LENGTH, }); const refresh = () => { @@ -70,9 +73,21 @@ export const Slider = ({ instanceRef.current?.moveToIdx(0); }; + useEffect(() => { + if (instanceRef.current) { + setIsSliderInitialized(true); + } + }, [instanceRef.current]); + useEffect(() => { refresh(); - }, [filters, sorting, searchPhrase, pendingTransaction.vote?.resourceId, data]); + }, [ + filters, + sorting, + searchPhrase, + pendingTransaction.vote?.resourceId, + data, + ]); return ( @@ -93,15 +108,7 @@ export const Slider = ({ gap: 2, }} > - - {title} - + {title} {(notSlicedDataLength > 6 || (isMobile && isShowAll)) && ( )} - {isShowArrows && dataLength > 1 && !isMobile && ( - + {isSliderInitialized && isShowArrows && dataLength > 1 && !isMobile && ( + )}
{ undefined, ); const [address, setAddress] = useState(undefined); - const [pubDRepKey, setPubDRepKey] = useState(''); - const [dRepID, setDRepID] = useState(''); - const [dRepIDBech32, setDRepIDBech32] = useState(''); + const [pubDRepKey, setPubDRepKey] = useState(""); + const [dRepID, setDRepID] = useState(""); + const [dRepIDBech32, setDRepIDBech32] = useState(""); const [stakeKey, setStakeKey] = useState(undefined); const [stakeKeys, setStakeKeys] = useState([]); const [isMainnet, setIsMainnet] = useState(false); @@ -204,7 +204,7 @@ const CardanoProvider = (props: Props) => { try { const raw = await enabledApi.getChangeAddress(); const changeAddress = Address.from_bytes( - Buffer.from(raw, 'hex'), + Buffer.from(raw, "hex"), ).to_bech32(); setWalletState((prev) => ({ ...prev, changeAddress })); } catch (err) { @@ -218,7 +218,7 @@ const CardanoProvider = (props: Props) => { const raw = await enabledApi.getUsedAddresses(); const rawFirst = raw[0]; const usedAddress = Address.from_bytes( - Buffer.from(rawFirst, 'hex'), + Buffer.from(rawFirst, "hex"), ).to_bech32(); setWalletState((prev) => ({ ...prev, usedAddress })); } catch (err) { @@ -237,13 +237,13 @@ const CardanoProvider = (props: Props) => { try { // Check that this wallet supports CIP-95 connection if (!window.cardano[walletName].supportedExtensions) { - throw new Error(t('errors.walletNoCIP30Support')); + throw new Error(t("errors.walletNoCIP30Support")); } else if ( !window.cardano[walletName].supportedExtensions.some( (item) => item.cip === 95, ) ) { - throw new Error(t('errors.walletNoCIP30Nor90Support')); + throw new Error(t("errors.walletNoCIP30Nor90Support")); } // Enable wallet connection const enabledApi: CardanoApiWallet = await window.cardano[walletName] @@ -261,7 +261,7 @@ const CardanoProvider = (props: Props) => { // Check if wallet has enabled the CIP-95 extension const enabledExtensions = await enabledApi.getExtensions(); if (!enabledExtensions.some((item) => item.cip === 95)) { - throw new Error(t('errors.walletNoCIP90FunctionsEnabled')); + throw new Error(t("errors.walletNoCIP90FunctionsEnabled")); } const network = await enabledApi.getNetworkId(); if (network !== NETWORK) { @@ -277,7 +277,7 @@ const CardanoProvider = (props: Props) => { const usedAddresses = await enabledApi.getUsedAddresses(); const unusedAddresses = await enabledApi.getUnusedAddresses(); if (!usedAddresses.length && !unusedAddresses.length) { - throw new Error(t('errors.noAddressesFound')); + throw new Error(t("errors.noAddressesFound")); } if (!usedAddresses.length) { setAddress(unusedAddresses[0]); @@ -341,22 +341,22 @@ const CardanoProvider = (props: Props) => { stakeKeySet = true; } const dRepIDs = await getPubDRepID(enabledApi); - setPubDRepKey(dRepIDs?.dRepKey || ''); - setDRepID(dRepIDs?.dRepID || ''); - setDRepIDBech32(dRepIDs?.dRepIDBech32 || ''); + setPubDRepKey(dRepIDs?.dRepKey || ""); + setDRepID(dRepIDs?.dRepID || ""); + setDRepIDBech32(dRepIDs?.dRepIDBech32 || ""); setItemToLocalStorage(`${WALLET_LS_KEY}_name`, walletName); const protocol = await getEpochParams(); setItemToLocalStorage(PROTOCOL_PARAMS_KEY, protocol); - return { status: t('ok'), stakeKey: stakeKeySet }; + return { status: t("ok"), stakeKey: stakeKeySet }; } catch (e) { Sentry.captureException(e); console.error(e); setError(`${e}`); setAddress(undefined); setWalletApi(undefined); - setPubDRepKey(''); + setPubDRepKey(""); setStakeKey(undefined); setIsEnabled(false); // eslint-disable-next-line no-throw-literal @@ -439,7 +439,7 @@ const CardanoProvider = (props: Props) => { const txBuilder = await initTransactionBuilder(); if (!txBuilder) { - throw new Error(t('errors.appCannotCreateTransaction')); + throw new Error(t("errors.appCannotCreateTransaction")); } if (certBuilder) { @@ -468,10 +468,10 @@ const CardanoProvider = (props: Props) => { ); // Add output of 1 ADA to the address of our wallet - let outputValue = BigNum.from_str('1000000'); + let outputValue = BigNum.from_str("1000000"); if ( - (type === 'retireAsDrep' || type === 'retireAsSoleVoter') && + (type === "retireAsDrep" || type === "retireAsSoleVoter") && voterDeposit ) { outputValue = outputValue.checked_add(BigNum.from_str(voterDeposit)); @@ -484,7 +484,7 @@ const CardanoProvider = (props: Props) => { const utxos = await getUtxos(walletApi); if (!utxos) { - throw new Error(t('errors.appCannotGetUtxos')); + throw new Error(t("errors.appCannotGetUtxos")); } // Find the available UTXOs in the wallet and use them as Inputs for the transaction const txUnspentOutputs = await getTxUnspentOutputs(utxos); @@ -511,11 +511,11 @@ const CardanoProvider = (props: Props) => { // Create witness set object using the witnesses provided by the wallet txVkeyWitnesses = TransactionWitnessSet.from_bytes( - Buffer.from(txVkeyWitnesses, 'hex'), + Buffer.from(txVkeyWitnesses, "hex"), ); const vkeys = txVkeyWitnesses.vkeys(); - if (!vkeys) throw new Error(t('errors.appCannotGetVkeys')); + if (!vkeys) throw new Error(t("errors.appCannotGetVkeys")); transactionWitnessSet.set_vkeys(vkeys); // Build transaction with witnesses @@ -546,7 +546,7 @@ const CardanoProvider = (props: Props) => { } Sentry.captureException(error); - console.error(error, 'error'); + console.error(error, "error"); throw error?.info ?? error; } }, @@ -560,7 +560,7 @@ const CardanoProvider = (props: Props) => { const certBuilder = CertificatesBuilder.new(); let stakeCred; if (!stakeKey) { - throw new Error(t('errors.noStakeKeySelected')); + throw new Error(t("errors.noStakeKeySelected")); } // Remove network tag from stake key hash const stakeKeyHash = Ed25519KeyHash.from_hex(stakeKey.substring(2)); @@ -575,11 +575,11 @@ const CardanoProvider = (props: Props) => { // Create correct DRep let targetDRep; - if (target === 'abstain') { + if (target === "abstain") { targetDRep = DRep.new_always_abstain(); - } else if (target === 'no confidence') { + } else if (target === "no confidence") { targetDRep = DRep.new_always_no_confidence(); - } else if (target.includes('drep')) { + } else if (target.includes("drep")) { targetDRep = DRep.new_key_hash(Ed25519KeyHash.from_bech32(target)); } else { targetDRep = DRep.new_key_hash(Ed25519KeyHash.from_hex(target)); @@ -725,9 +725,9 @@ const CardanoProvider = (props: Props) => { ); let votingChoice; - if (voteChoice === 'yes') { + if (voteChoice === "yes") { votingChoice = 1; - } else if (voteChoice === 'no') { + } else if (voteChoice === "no") { votingChoice = 0; } else { votingChoice = 2; @@ -761,11 +761,11 @@ const CardanoProvider = (props: Props) => { const getRewardAddress = useCallback(async () => { const addresses = await walletApi?.getRewardAddresses(); if (!addresses) { - throw new Error('Can not get reward addresses from wallet.'); + throw new Error("Can not get reward addresses from wallet."); } const firstAddress = addresses[0]; const bech32Address = Address.from_bytes( - Buffer.from(firstAddress, 'hex'), + Buffer.from(firstAddress, "hex"), ).to_bech32(); return RewardAddress.from_address(Address.from_bech32(bech32Address)); @@ -783,7 +783,7 @@ const CardanoProvider = (props: Props) => { const anchor = generateAnchor(url, hash); const rewardAddr = await getRewardAddress(); - if (!rewardAddr) throw new Error('Can not get reward address'); + if (!rewardAddr) throw new Error("Can not get reward address"); // Create voting proposal const votingProposal = VotingProposal.new( @@ -811,7 +811,7 @@ const CardanoProvider = (props: Props) => { Address.from_bech32(receivingAddress), ); - if (!treasuryTarget) throw new Error('Can not get tresasury target'); + if (!treasuryTarget) throw new Error("Can not get tresasury target"); const myWithdrawal = BigNum.from_str(amount); const withdrawals = TreasuryWithdrawals.new(); @@ -825,7 +825,7 @@ const CardanoProvider = (props: Props) => { const rewardAddr = await getRewardAddress(); - if (!rewardAddr) throw new Error('Can not get reward address'); + if (!rewardAddr) throw new Error("Can not get reward address"); // Create voting proposal const votingProposal = VotingProposal.new( treasuryGovAct, @@ -909,7 +909,7 @@ function useCardano() { const { t } = useTranslation(); if (context === undefined) { - throw new Error(t('errors.useCardano')); + throw new Error(t("errors.useCardano")); } const enable = useCallback( @@ -922,22 +922,22 @@ function useCardano() { if (!result.error) { closeModal(); if (result.stakeKey) { - addSuccessAlert(t('alerts.walletConnected'), 3000); + addSuccessAlert(t("alerts.walletConnected"), 3000); } if (!isSanchoInfoShown) { openModal({ - type: 'statusModal', + type: "statusModal", state: { - status: 'info', - dataTestId: 'info-about-sancho-net-modal', + status: "info", + dataTestId: "info-about-sancho-net-modal", message: (

- {t('system.sanchoNetIsBeta')} + {t("system.sanchoNetIsBeta")} openInNewTab('https://sancho.network/')} - sx={{ cursor: 'pointer' }} + onClick={() => openInNewTab("https://sancho.network/")} + sx={{ cursor: "pointer" }} > - {t('system.sanchoNet')} + {t("system.sanchoNet")} .
@@ -950,8 +950,8 @@ function useCardano() { />

), - title: t('system.toolConnectedToSanchonet'), - buttonText: t('ok'), + title: t("system.toolConnectedToSanchonet"), + buttonText: t("ok"), }, }); setItemToLocalStorage(`${SANCHO_INFO_KEY}_${walletName}`, true); @@ -965,15 +965,15 @@ function useCardano() { await context.disconnectWallet(); navigate(PATHS.home); openModal({ - type: 'statusModal', + type: "statusModal", state: { - status: 'warning', - message: e?.error?.replace('Error: ', ''), + status: "warning", + message: e?.error?.replace("Error: ", ""), onSubmit: () => { closeModal(); }, - title: t('modals.common.oops'), - dataTestId: 'wallet-connection-error-modal', + title: t("modals.common.oops"), + dataTestId: "wallet-connection-error-modal", }, }); throw e; diff --git a/govtool/frontend/src/hooks/useSlider.ts b/govtool/frontend/src/hooks/useSlider.ts index e73341422..b2f64adf5 100644 --- a/govtool/frontend/src/hooks/useSlider.ts +++ b/govtool/frontend/src/hooks/useSlider.ts @@ -47,12 +47,7 @@ const WheelControls = (slider: KeenSliderInstance) => { }); }; -export const useSlider = ({ - config, -}: { - config: KeenSliderOptions; - sliderMaxLength: number; -}) => { +export const useSlider = ({ config }: { config: KeenSliderOptions }) => { const [currentSlide, setCurrentSlide] = useState(0); const [sliderRef, instanceRef] = useKeenSlider( @@ -66,9 +61,14 @@ export const useSlider = ({ [WheelControls], ); + const dataLength = instanceRef?.current?.slides?.length ?? 10; + const itemsPerView = + dataLength - (instanceRef?.current?.track?.details?.maxIdx ?? 2); + return { sliderRef, instanceRef, currentSlide, + itemsPerView, }; }; diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 28a2756db..19e49e5f0 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -316,6 +316,7 @@ export const en = { govActions: { about: "About", abstract: "Abstract:", + backToGovActions: "Back to Governance Actions", castVote: "<0>You voted {{vote}} for this proposal\nat {{date}}", castVoteDeadline: "You can change your vote up to the deadline of {{date}}", @@ -323,6 +324,8 @@ export const en = { changeYourVote: "Change your vote", chooseHowToVote: "Choose how you want to vote:", dataMissing: "Data Missing", + dataMissingTooltipExplanation: + "Please click “View Details” for more information.", details: "Governance Details:", expiresDateWithEpoch: "Expires: <0>{{date}} <1>(Epoch {{epoch}})", expiryDate: "Expiry date:", @@ -350,7 +353,7 @@ export const en = { viewOtherDetails: "View other details", viewProposalDetails: "View proposal details", vote: "Vote", - voted: "Voted", + votedOnByMe: "Voted on by me", voteOnGovActions: "Vote on Governance Action", voteSubmitted: "Vote submitted", voteTransaction: "Vote transaction", @@ -601,9 +604,11 @@ export const en = { backToList: "Back to the list", cancel: "Cancel", clear: "Clear", + clickToCopyLink: "Click to copy link", confirm: "Confirm", continue: "Continue", delegate: "Delegate", + filter: "Filter", here: "here", inProgress: "In progress", learnMore: "Learn more", @@ -617,8 +622,10 @@ export const en = { required: "required", seeTransaction: "See transaction", select: "Select", + share: "Share", showMore: "Show more", skip: "Skip", + sort: "Sort", sortBy: "Sort by", submit: "Submit", thisLink: "this link", diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index 16187ab72..47384da36 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -1,19 +1,8 @@ import { useCallback, useMemo, useRef, useState } from "react"; -import { - generatePath, - NavLink, - useNavigate, - useParams, -} from "react-router-dom"; -import { - Box, - Breadcrumbs, - CircularProgress, - Link, - Typography, -} from "@mui/material"; +import { generatePath, useNavigate, useParams } from "react-router-dom"; +import { Box, CircularProgress, Link } from "@mui/material"; -import { Background } from "@atoms"; +import { Background, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; import { useCardano } from "@context"; import { DataActionsBar, GovernanceActionCard } from "@molecules"; @@ -68,21 +57,6 @@ export const DashboardGovernanceActionsCategory = () => { isProposalsFetching, ); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {getProposalTypeLabel(category ?? "")} - , - ]; - const mappedData = useMemo(() => { const uniqueProposals = removeDuplicatedProposals(proposals); @@ -112,23 +86,13 @@ export const DashboardGovernanceActionsCategory = () => { > - - {breadcrumbs} - navigate(PATHS.dashboardGovernanceActions)} > @@ -137,8 +101,8 @@ export const DashboardGovernanceActionsCategory = () => { alt="arrow" style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - - {t("backToList")} + + {t("govActions.backToGovActions")} { sortingActive={Boolean(chosenSorting)} sortOpen={sortOpen} /> - + + {getProposalTypeLabel(category ?? "")} + {!mappedData || isEnableLoading || isProposalsLoading ? ( ) : !mappedData?.length ? ( - + {t("govActions.withCategoryNotExist.partOne")}   - {` ${category} `} + + {` ${category} `} +   {t("govActions.withCategoryNotExist.partTwo")} @@ -173,14 +155,10 @@ export const DashboardGovernanceActionsCategory = () => { ) : ( {mappedData.map((item) => ( @@ -188,38 +166,41 @@ export const DashboardGovernanceActionsCategory = () => { { saveScrollPosition(); - // eslint-disable-next-line no-unused-expressions - pendingTransaction.vote?.resourceId === - item.txHash + item.index - ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) - : navigate( - generatePath( - PATHS.dashboardGovernanceActionsAction, - { - proposalId: getFullGovActionId( - item.txHash, - item.index, - ), - }, - ), + if ( + pendingTransaction.vote?.resourceId === + item.txHash + item.index + ) { + openInNewTab( + "https://adanordic.com/latest_transactions", + ); + } else { + navigate( + generatePath( + PATHS.dashboardGovernanceActionsAction, { - state: { - ...item, - openedFromCategoryPage: true, - }, + proposalId: getFullGovActionId( + item.txHash, + item.index, + ), + }, + ), + { + state: { + ...item, + openedFromCategoryPage: true, }, - ); + }, + ); + } }} txHash={item.txHash} /> diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index fe916ca4d..8fc1055bc 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -3,10 +3,9 @@ import { useNavigate, useLocation, useParams, - NavLink, generatePath, } from "react-router-dom"; -import { Box, Breadcrumbs, CircularProgress, Link } from "@mui/material"; +import { Box, CircularProgress, Link } from "@mui/material"; import { Background, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; @@ -24,11 +23,15 @@ import { getItemFromLocalStorage, getShortenedGovActionId, } from "@utils"; +import { Breadcrumbs } from "@molecules"; + +// TODO: Remove when data validation is ready +const isDataMissing = false; export const GovernanceActionDetails = () => { const { state, hash } = useLocation(); const navigate = useNavigate(); - const { pagePadding, screenWidth } = useScreenDimension(); + const { pagePadding, isMobile } = useScreenDimension(); const { isEnabled } = useCardano(); const { t } = useTranslation(); const { proposalId } = useParams(); @@ -48,21 +51,6 @@ export const GovernanceActionDetails = () => { } }, [isEnabled]); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {t("govActions.voteOnGovActions")} - , - ]; - return ( { px={pagePadding} > - {screenWidth >= 1024 ? ( - - {t("govActions.title")} - + {isMobile ? ( + + + {t("govActions.title")} + + ) : null} - {breadcrumbs} - + elementOne={t("govActions.title")} + elementOnePath={PATHS.dashboardGovernanceActions} + elementTwo="Fund our project" + isDataMissing={false} + /> { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - {t("backToList")} + {t("back")} {isLoading ? ( @@ -140,6 +134,8 @@ export const GovernanceActionDetails = () => { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } + // TODO: Add data validation + isDataMissing={isDataMissing} // TODO: To decide if we want to keep it when metadate BE is ready // details={state ? state.details : data.proposal.details} expiryDate={ diff --git a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx index 4538cb006..82b67ce46 100644 --- a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx @@ -1,12 +1,6 @@ import { useState, useCallback, useEffect, useMemo, useRef } from "react"; -import { NavLink, useNavigate, useParams } from "react-router-dom"; -import { - Box, - Breadcrumbs, - CircularProgress, - Divider, - Link, -} from "@mui/material"; +import { useNavigate, useParams } from "react-router-dom"; +import { Box, CircularProgress, Link } from "@mui/material"; import { Background, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; @@ -65,21 +59,6 @@ export const GovernanceActionsCategory = () => { isProposalsFetching, ); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {getProposalTypeLabel(category ?? "")} - , - ]; - const mappedData = useMemo(() => { const uniqueProposals = removeDuplicatedProposals(proposals); @@ -116,32 +95,7 @@ export const GovernanceActionsCategory = () => { > - - {t("govActions.title")} - - {isMobile && ( - - )} - - {breadcrumbs} - { alt="arrow" style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - - {t("backToList")} + + {t("govActions.backToGovActions")} { closeSorts={closeSorts} setChosenSorting={setChosenSorting} /> - + + {getProposalTypeLabel(category ?? "")} + {!isProposalsLoading ? ( !mappedData?.length ? ( @@ -202,14 +163,10 @@ export const GovernanceActionsCategory = () => { ) : ( {mappedData.map((item) => ( diff --git a/govtool/frontend/src/utils/getProposalTypeLabel.ts b/govtool/frontend/src/utils/getProposalTypeLabel.ts index e44f7905c..0064dd602 100644 --- a/govtool/frontend/src/utils/getProposalTypeLabel.ts +++ b/govtool/frontend/src/utils/getProposalTypeLabel.ts @@ -4,3 +4,5 @@ export const getProposalTypeLabel = (type: string) => { const label = GOVERNANCE_ACTIONS_FILTERS.find((i) => i.key === type)?.label; return label || type; }; +export const getProposalTypeNoEmptySpaces = (type: string) => + getProposalTypeLabel(type).replace(/ /g, ""); From de9dffb92cab72533e72fdc3500eb0f1ffe313a6 Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Mon, 25 Mar 2024 16:54:09 +0100 Subject: [PATCH 12/21] Fixes after CR --- govtool/frontend/public/icons/Separator.svg | 3 - .../src/components/atoms/CopyButton.tsx | 4 +- .../src/components/atoms/IconLink.tsx | 45 ------ .../frontend/src/components/atoms/Radio.tsx | 4 +- .../frontend/src/components/atoms/Tooltip.tsx | 7 +- .../frontend/src/components/atoms/index.ts | 2 - .../frontend/src/components/atoms/types.ts | 7 + .../src/components/molecules/Breadcrumbs.tsx | 9 +- .../molecules/GovernanceActionCard.tsx | 27 ++-- .../molecules/GovernanceActionCardElement.tsx | 140 +++++++++--------- .../molecules/GovernanceActionCardMyVote.tsx | 6 +- .../GovernanceActionDetailsCardLinks.tsx | 13 +- .../molecules/GovernanceActionsFilters.tsx | 5 +- .../molecules/GovernanceActionsSorting.tsx | 5 +- .../molecules/GovernanceVotedOnCard.tsx | 4 +- .../src/components/molecules/LinkWithIcon.tsx | 14 +- .../components/molecules/OrderActionsChip.tsx | 89 +++++++---- .../{atoms => molecules}/SliderArrow.tsx | 4 +- .../src/components/molecules/SliderArrows.tsx | 59 ++++---- .../components/molecules/VotesSubmitted.tsx | 4 +- .../src/components/molecules/index.ts | 1 + .../src/components/molecules/types.ts | 1 + .../DashboardGovernanceActionDetails.tsx | 2 - .../organisms/DashboardGovernanceActions.tsx | 4 +- .../DashboardGovernanceActionsVotedOn.tsx | 4 +- .../organisms/GovernanceActionDetailsCard.tsx | 2 - .../src/components/organisms/Slider.tsx | 4 + govtool/frontend/src/consts/icons.ts | 1 - .../src/pages/GovernanceActionDetails.tsx | 2 - govtool/frontend/src/theme.ts | 51 ------- govtool/frontend/src/types/@mui.d.ts | 2 + .../src/utils/getProposalTypeLabel.ts | 1 + .../getProposalTypeNoEmptySpaces.test.ts | 25 ++++ 33 files changed, 269 insertions(+), 282 deletions(-) delete mode 100644 govtool/frontend/public/icons/Separator.svg delete mode 100644 govtool/frontend/src/components/atoms/IconLink.tsx rename govtool/frontend/src/components/{atoms => molecules}/SliderArrow.tsx (97%) create mode 100644 govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts diff --git a/govtool/frontend/public/icons/Separator.svg b/govtool/frontend/public/icons/Separator.svg deleted file mode 100644 index e6edeb0dd..000000000 --- a/govtool/frontend/public/icons/Separator.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/govtool/frontend/src/components/atoms/CopyButton.tsx b/govtool/frontend/src/components/atoms/CopyButton.tsx index 2783d0fb7..021d5fcc0 100644 --- a/govtool/frontend/src/components/atoms/CopyButton.tsx +++ b/govtool/frontend/src/components/atoms/CopyButton.tsx @@ -4,11 +4,11 @@ import { ICONS } from "@consts"; import { useSnackbar } from "@context"; import { useTranslation } from "@hooks"; -interface Props { +type Props = { isChecked?: boolean; text: string; variant?: "blueThin" | "blue"; -} +}; export const CopyButton = ({ isChecked, text, variant }: Props) => { const { addSuccessAlert } = useSnackbar(); diff --git a/govtool/frontend/src/components/atoms/IconLink.tsx b/govtool/frontend/src/components/atoms/IconLink.tsx deleted file mode 100644 index 0c12fcd1e..000000000 --- a/govtool/frontend/src/components/atoms/IconLink.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Box } from "@mui/material"; - -import { Typography } from "@atoms"; -import { openInNewTab } from "@utils"; -import { ICONS } from "@consts"; - -type IconLinkProps = { - label: string; - navTo: string; - isSmall?: boolean; -}; - -export const IconLink = ({ label, navTo, isSmall }: IconLinkProps) => { - const openLink = () => openInNewTab(navTo); - - return ( - - link - - {label} - - - ); -}; diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index f93e68ec3..d997c1b58 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -3,6 +3,7 @@ import { Box } from "@mui/material"; import { Typography } from "@atoms"; import { UseFormRegister, UseFormSetValue } from "react-hook-form"; +import { theme } from "@/theme"; type RadioProps = { isChecked: boolean; @@ -43,8 +44,7 @@ export const Radio = ({ ...props }: RadioProps) => { borderColor={isChecked ? "specialCyanBorder" : undefined} sx={[ { - boxShadow: - "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)", + boxShadow: theme.shadows[1], "&:hover": { color: "blue", diff --git a/govtool/frontend/src/components/atoms/Tooltip.tsx b/govtool/frontend/src/components/atoms/Tooltip.tsx index 4e2ccdc51..dd8d8dc23 100644 --- a/govtool/frontend/src/components/atoms/Tooltip.tsx +++ b/govtool/frontend/src/components/atoms/Tooltip.tsx @@ -1,12 +1,7 @@ import { styled } from "@mui/material"; import * as TooltipMUI from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; - -export type TooltipProps = Omit & { - heading?: string; - paragraphOne?: string; - paragraphTwo?: string; -}; +import { TooltipProps } from "@atoms"; const StyledTooltip = styled( ({ className, ...props }: TooltipMUI.TooltipProps) => ( diff --git a/govtool/frontend/src/components/atoms/index.ts b/govtool/frontend/src/components/atoms/index.ts index 1939a0971..6fa848bac 100644 --- a/govtool/frontend/src/components/atoms/index.ts +++ b/govtool/frontend/src/components/atoms/index.ts @@ -9,7 +9,6 @@ export * from "./ExternalModalButton"; export * from "./FormErrorMessage"; export * from "./FormHelpfulText"; export * from "./HighlightedText"; -export * from "./IconLink"; export * from "./InfoText"; export * from "./Input"; export * from "./Link"; @@ -21,7 +20,6 @@ export * from "./modal/ModalWrapper"; export * from "./Radio"; export * from "./ScrollToManage"; export * from "./ScrollToTop"; -export * from "./SliderArrow"; export * from "./Spacer"; export * from "./StakeRadio"; export * from "./TextArea"; diff --git a/govtool/frontend/src/components/atoms/types.ts b/govtool/frontend/src/components/atoms/types.ts index 34f0fdf14..ff2e603b8 100644 --- a/govtool/frontend/src/components/atoms/types.ts +++ b/govtool/frontend/src/components/atoms/types.ts @@ -7,6 +7,7 @@ import { TextareaAutosizeProps, SxProps, } from "@mui/material"; +import * as TooltipMUI from "@mui/material/Tooltip"; export type ButtonProps = Omit & { size?: "small" | "medium" | "large" | "extraLarge"; @@ -71,3 +72,9 @@ export type InfoTextProps = { label: string; sx?: SxProps; }; + +export type TooltipProps = Omit & { + heading?: string; + paragraphOne?: string; + paragraphTwo?: string; +}; diff --git a/govtool/frontend/src/components/molecules/Breadcrumbs.tsx b/govtool/frontend/src/components/molecules/Breadcrumbs.tsx index 435d56e3d..8a9e564a2 100644 --- a/govtool/frontend/src/components/molecules/Breadcrumbs.tsx +++ b/govtool/frontend/src/components/molecules/Breadcrumbs.tsx @@ -1,7 +1,7 @@ import { NavLink, To } from "react-router-dom"; import { Box } from "@mui/material"; +import Divider from "@mui/material/Divider"; -import { ICONS } from "@consts"; import { useScreenDimension, useTranslation } from "@hooks"; import { Typography } from "@atoms"; @@ -40,7 +40,12 @@ export const Breadcrumbs = ({ {elementOne} - separator + { +type ActionTypeProps = Omit< + ActionType, + | "yesVotes" + | "noVotes" + | "abstainVotes" + | "metadataHash" + | "url" + | "details" + | "id" + | "txHash" + | "index" +> & { txHash: string; index: number; isDataMissing: boolean; onClick?: () => void; inProgress?: boolean; -} +}; export const GovernanceActionCard: FC = ({ ...props }) => { const { diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx index 51186d699..94517a9ca 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx @@ -40,99 +40,97 @@ export const GovernanceActionCardElement = ({ > {label} {tooltipProps && ( - - - - )} + + + + )} {textVariant === "pill" ? ( {text} - ) : ( - + - - {text} - - {isCopyButton && ( - - - - )} - - )} + {text} + + {isCopyButton && ( + + + + )} + + )} - ); +); diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx index 0a53fbfe6..c1dde83ec 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx @@ -5,7 +5,11 @@ import { openInNewTab } from "@utils"; import { useTranslation } from "@hooks"; import { Vote } from "@models"; -export const GovernanceActionCardMyVote = ({ vote }: { vote: Vote }) => { +type Props = { + vote: Vote; +}; + +export const GovernanceActionCardMyVote = ({ vote }: Props) => { const { t } = useTranslation(); return ( diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx index 8fdb99397..8028668be 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx @@ -1,7 +1,10 @@ import { Box } from "@mui/material"; -import { IconLink, Typography } from "@atoms"; +import { Typography } from "@atoms"; import { useScreenDimension, useTranslation } from "@hooks"; +import { LinkWithIcon } from "@molecules"; +import { openInNewTab } from "@/utils"; +import { ICONS } from "@/consts"; // TODO: When BE is ready, pass links as props const LINKS = [ @@ -41,7 +44,13 @@ export const GovernanceActionDetailsCardLinks = () => { }} > {LINKS.map((link) => ( - + openInNewTab(link)} + icon={link} + cutWithEllipsis + /> ))} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx index 05746f111..89542217d 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx @@ -8,7 +8,7 @@ import { } from "@mui/material"; import { GOVERNANCE_ACTIONS_FILTERS } from "@consts"; -import { useTranslation } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; interface Props { chosenFilters: string[]; @@ -36,6 +36,7 @@ export const GovernanceActionsFilters = ({ ); const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); return ( { const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); return ( diff --git a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx index 7e9f140be..7e5a6aa2b 100644 --- a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx @@ -22,11 +22,11 @@ import { const mockedLongText = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; -interface Props { +type Props = { votedProposal: VotedProposal; isDataMissing: boolean; inProgress?: boolean; -} +}; export const GovernanceVotedOnCard = ({ votedProposal, diff --git a/govtool/frontend/src/components/molecules/LinkWithIcon.tsx b/govtool/frontend/src/components/molecules/LinkWithIcon.tsx index ab12e106c..ecfa97b16 100644 --- a/govtool/frontend/src/components/molecules/LinkWithIcon.tsx +++ b/govtool/frontend/src/components/molecules/LinkWithIcon.tsx @@ -10,6 +10,7 @@ export const LinkWithIcon = ({ onClick, icon, sx, + cutWithEllipsis, }: LinkWithIconProps) => ( {label} diff --git a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx index 3bddbd6dd..037a68cd4 100644 --- a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx +++ b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx @@ -1,12 +1,12 @@ import { Dispatch, SetStateAction } from "react"; import { Box } from "@mui/material"; -import { useTranslation } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { Typography } from "@atoms"; import { ICONS } from "@consts"; import { theme } from "@/theme"; -interface Props { +type Props = { filtersOpen?: boolean; setFiltersOpen?: Dispatch>; chosenFiltersLength?: number; @@ -14,10 +14,11 @@ interface Props { setSortOpen: Dispatch>; sortingActive: boolean; isFiltering?: boolean; -} +}; export const OrderActionsChip = (props: Props) => { const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); const { palette: { secondary }, @@ -34,7 +35,13 @@ export const OrderActionsChip = (props: Props) => { } = props; return ( - + {isFiltering && ( { alignItems: "center", justifyContent: "center", cursor: "pointer", + ...(!isMobile && { + background: filtersOpen ? secondary.main : "transparent", + borderRadius: "99px", + padding: "12px 14px", + }), }} onClick={() => { setSortOpen(false); @@ -56,24 +68,30 @@ export const OrderActionsChip = (props: Props) => { alt="filter" src={filtersOpen ? ICONS.filterWhiteIcon : ICONS.filterIcon} style={{ - background: filtersOpen ? secondary.main : "transparent", borderRadius: "100%", - padding: "14px", + marginRight: "8px", overflow: "visible", height: 20, width: 20, objectFit: "contain", + ...(isMobile && { + background: filtersOpen ? secondary.main : "transparent", + padding: "14px", + marginRight: "0", + }), }} /> - - {t("filter")} - + {!isMobile && ( + + {t("filter")} + + )} {!filtersOpen && chosenFiltersLength > 0 && ( { height: "16px", justifyContent: "center", position: "absolute", - left: "32px", + right: "-3px", top: "0", width: "16px", }} @@ -105,6 +123,11 @@ export const OrderActionsChip = (props: Props) => { alignItems: "center", justifyContent: "center", cursor: "pointer", + ...(!isMobile && { + background: sortOpen ? secondary.main : "transparent", + borderRadius: "99px", + padding: "12px 14px", + }), }} onClick={() => { if (isFiltering) { @@ -118,23 +141,29 @@ export const OrderActionsChip = (props: Props) => { alt="sort" src={sortOpen ? ICONS.sortWhiteIcon : ICONS.sortIcon} style={{ - background: sortOpen ? secondary.main : "transparent", borderRadius: "100%", - padding: "14px", - height: 24, - width: 24, + marginRight: "8px", + height: 20, + width: 20, objectFit: "contain", + ...(isMobile && { + background: sortOpen ? secondary.main : "transparent", + padding: "14px", + marginRight: "0", + }), }} /> - - {t("sort")} - + {!isMobile && ( + + {t("sort")} + + )} {!sortOpen && sortingActive && ( { height: "16px", justifyContent: "center", position: "absolute", - left: "36px", + right: "-3px", top: "0", width: "16px", }} diff --git a/govtool/frontend/src/components/atoms/SliderArrow.tsx b/govtool/frontend/src/components/molecules/SliderArrow.tsx similarity index 97% rename from govtool/frontend/src/components/atoms/SliderArrow.tsx rename to govtool/frontend/src/components/molecules/SliderArrow.tsx index d48fcbec8..b4b079d48 100644 --- a/govtool/frontend/src/components/atoms/SliderArrow.tsx +++ b/govtool/frontend/src/components/molecules/SliderArrow.tsx @@ -3,11 +3,11 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import { theme } from "@/theme"; -interface SliderArrowProps { +type SliderArrowProps = { disabled: boolean; onClick: (e: React.MouseEvent) => void; left?: boolean; -} +}; export const SliderArrow = ({ disabled, onClick, left }: SliderArrowProps) => { const { diff --git a/govtool/frontend/src/components/molecules/SliderArrows.tsx b/govtool/frontend/src/components/molecules/SliderArrows.tsx index c481836ae..6d5f9e7a6 100644 --- a/govtool/frontend/src/components/molecules/SliderArrows.tsx +++ b/govtool/frontend/src/components/molecules/SliderArrows.tsx @@ -1,5 +1,5 @@ import { KeenSliderHooks, KeenSliderInstance } from "keen-slider/react"; -import { SliderArrow } from "@atoms"; +import { SliderArrow } from "@molecules"; import { Box } from "@mui/material"; type SliderArrowsProps = { @@ -16,34 +16,31 @@ export const SliderArrows = ({ currentSlide, instanceRef, itemsPerView, -}: SliderArrowsProps) => ( - <> - {instanceRef.current && ( - + instanceRef.current && ( + + ) => { + e.stopPropagation(); + instanceRef.current?.prev(); }} - > - ) => { - e.stopPropagation(); - instanceRef.current?.prev(); - }} - disabled={currentSlide === 0} - /> - ) => { - e.stopPropagation(); - instanceRef.current?.next(); - }} - disabled={ - currentSlide + itemsPerView >= - instanceRef.current.track.details.slides.length - } - /> - - )} - -); + disabled={currentSlide === 0} + /> + ) => { + e.stopPropagation(); + instanceRef.current?.next(); + }} + disabled={ + currentSlide + itemsPerView >= + instanceRef.current.track.details.slides.length + } + /> + + ); diff --git a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx index f24b11c73..1e4ba2347 100644 --- a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx +++ b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx @@ -5,11 +5,11 @@ import { VotePill } from "@atoms"; import { useTranslation } from "@hooks"; import { correctAdaFormat } from "@/utils/adaFormat"; -interface Props { +type Props = { yesVotes: number; noVotes: number; abstainVotes: number; -} +}; export const VotesSubmitted = ({ yesVotes, noVotes, abstainVotes }: Props) => { const { t } = useTranslation(); diff --git a/govtool/frontend/src/components/molecules/index.ts b/govtool/frontend/src/components/molecules/index.ts index 0947666fb..78d4f0563 100644 --- a/govtool/frontend/src/components/molecules/index.ts +++ b/govtool/frontend/src/components/molecules/index.ts @@ -25,6 +25,7 @@ export * from "./GovernanceVotedOnCard"; export * from "./LinkWithIcon"; export * from "./OrderActionsChip"; export * from "./Share"; +export * from "./SliderArrow"; export * from "./SliderArrows"; export * from "./Step"; export * from "./VoteActionForm"; diff --git a/govtool/frontend/src/components/molecules/types.ts b/govtool/frontend/src/components/molecules/types.ts index 0ed7df185..dbe0db33c 100644 --- a/govtool/frontend/src/components/molecules/types.ts +++ b/govtool/frontend/src/components/molecules/types.ts @@ -5,6 +5,7 @@ export type LinkWithIconProps = { onClick: () => void; icon?: JSX.Element; sx?: SxProps; + cutWithEllipsis?: boolean; }; export type StepProps = { diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx index 8db0e5603..480de270c 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx @@ -110,8 +110,6 @@ export const DashboardGovernanceActionDetails = () => { } // TODO: Add data validation isDataMissing={isDataMissing} - // TODO: To decide if we want to keep it when metadate BE is ready - // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx index 2ff29ba3e..f9b22bc1e 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx @@ -16,11 +16,11 @@ import { DashboardGovernanceActionsVotedOn, } from "@organisms"; -interface TabPanelProps { +type TabPanelProps = { children?: React.ReactNode; index: number; value: number; -} +}; const defaultCategories = GOVERNANCE_ACTIONS_FILTERS.map( (category) => category.key, diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx index 6c46c7f5b..5f8758cd2 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx @@ -12,11 +12,11 @@ import { getProposalTypeLabel } from "@/utils/getProposalTypeLabel"; import { getFullGovActionId } from "@/utils"; import { useCardano } from "@/context"; -interface DashboardGovernanceActionsVotedOnProps { +type DashboardGovernanceActionsVotedOnProps = { filters: string[]; searchPhrase?: string; sorting: string; -} +}; export const DashboardGovernanceActionsVotedOn = ({ filters, diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx index 4c3cdec67..f6fabab7b 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx @@ -10,7 +10,6 @@ import { useState } from "react"; type GovernanceActionDetailsCardProps = { abstainVotes: number; createdDate: string; - // details: unknown; expiryDate: string; noVotes: number; type: string; @@ -27,7 +26,6 @@ type GovernanceActionDetailsCardProps = { export const GovernanceActionDetailsCard = ({ abstainVotes, createdDate, - // details, expiryDate, noVotes, type, diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index 1394a70b5..6be504364 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -58,6 +58,10 @@ export const Slider = ({ const isShowArrows = useMemo( () => + // Arrows are to be show only on desktop view. + // 268 - side menu width; 40 - distance needed from the left on + // disconnected wallet (no side menu); 350 - gov action card width; + // other values are for paddings and margins screenWidth < (onDashboard ? 268 : 40) + 28 + dataLength * 350 + 20 * dataLength - 5, [screenWidth, dataLength], diff --git a/govtool/frontend/src/consts/icons.ts b/govtool/frontend/src/consts/icons.ts index d1d237ced..dfabf260d 100644 --- a/govtool/frontend/src/consts/icons.ts +++ b/govtool/frontend/src/consts/icons.ts @@ -26,7 +26,6 @@ export const ICONS = { guidesIcon: "/icons/Guides.svg", helpIcon: "/icons/Help.svg", link: "/icons/Link.svg", - separator: "/icons/Separator.svg", share: "/icons/Share.svg", sortActiveIcon: "/icons/SortActive.svg", sortIcon: "/icons/Sort.svg", diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index 8fc1055bc..c914ce978 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -136,8 +136,6 @@ export const GovernanceActionDetails = () => { } // TODO: Add data validation isDataMissing={isDataMissing} - // TODO: To decide if we want to keep it when metadate BE is ready - // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) diff --git a/govtool/frontend/src/theme.ts b/govtool/frontend/src/theme.ts index 83cd35489..7b9bd2331 100644 --- a/govtool/frontend/src/theme.ts +++ b/govtool/frontend/src/theme.ts @@ -9,57 +9,6 @@ import { successGreen, } from "./consts"; -declare module "@mui/material/styles" { - interface Palette { - accentOrange: string; - accentYellow: string; - arcticWhite: string; - boxShadow1: string; - boxShadow2: string; - errorRed: string; - highlightBlue: string; - inputRed: string; - negativeRed: string; - neutralGray: string; - orangeDark: string; - neutralWhite: string; - positiveGreen: string; - primaryBlue: string; - secondaryBlue: string; - specialCyan: string; - specialCyanBorder: string; - lightBlue: string; - textBlack: string; - textGray: string; - lightOrange: string; - fadedPurple: string; - } - interface PaletteOptions { - accentOrange: string; - accentYellow: string; - arcticWhite: string; - boxShadow1: string; - boxShadow2: string; - errorRed: string; - highlightBlue: string; - orangeDark: string; - inputRed: string; - negativeRed: string; - neutralGray: string; - neutralWhite: string; - positiveGreen: string; - primaryBlue: string; - secondaryBlue: string; - specialCyan: string; - specialCyanBorder: string; - lightBlue: string; - textBlack: string; - textGray: string; - lightOrange: string; - fadedPurple: string; - } -} - export type Theme = typeof theme; export const theme = createTheme({ diff --git a/govtool/frontend/src/types/@mui.d.ts b/govtool/frontend/src/types/@mui.d.ts index c8ea5da12..e83f05e7e 100644 --- a/govtool/frontend/src/types/@mui.d.ts +++ b/govtool/frontend/src/types/@mui.d.ts @@ -16,8 +16,10 @@ declare module "@mui/material/styles" { interface PaletteOptions extends MuiPalette { accentOrange: string; accentYellow: string; + arcticWhite: string; boxShadow1: string; boxShadow2: string; + errorRed: string; highlightBlue: string; inputRed: string; negativeRed: string; diff --git a/govtool/frontend/src/utils/getProposalTypeLabel.ts b/govtool/frontend/src/utils/getProposalTypeLabel.ts index 0064dd602..7a3dcccdc 100644 --- a/govtool/frontend/src/utils/getProposalTypeLabel.ts +++ b/govtool/frontend/src/utils/getProposalTypeLabel.ts @@ -4,5 +4,6 @@ export const getProposalTypeLabel = (type: string) => { const label = GOVERNANCE_ACTIONS_FILTERS.find((i) => i.key === type)?.label; return label || type; }; + export const getProposalTypeNoEmptySpaces = (type: string) => getProposalTypeLabel(type).replace(/ /g, ""); diff --git a/govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts b/govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts new file mode 100644 index 000000000..4ec64fe92 --- /dev/null +++ b/govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts @@ -0,0 +1,25 @@ +import { getProposalTypeNoEmptySpaces } from ".."; + +describe("getProposalTypeNoEmptySpaces", () => { + it("returns correct label with no spaces for a known type", () => { + const type = "NoConfidence"; + const expectedLabel = "NoConfidence"; + expect(getProposalTypeNoEmptySpaces(type)).toBe(expectedLabel); + }); + + it("returns correct label with no spaces for another known type", () => { + const type = "ParameterChange"; + const expectedLabel = "ProtocolParameterChanges"; + expect(getProposalTypeNoEmptySpaces(type)).toBe(expectedLabel); + }); + + it("returns the type itself with no spaces removed when no matching key is found", () => { + const type = "UnknownType"; + expect(getProposalTypeNoEmptySpaces(type)).toBe(type); + }); + + it("returns an empty string when given an empty string", () => { + const type = ""; + expect(getProposalTypeNoEmptySpaces(type)).toBe(type); + }); +}); From 2f64bf1ca2ca9f9f8ce151b7c0f67ffe54c18a7f Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Mon, 25 Mar 2024 19:39:51 +0100 Subject: [PATCH 13/21] Filter and sort dropdowns fixes --- .../src/components/atoms/ClickOutside.tsx | 30 ---------------- .../frontend/src/components/atoms/index.ts | 1 - .../components/molecules/DataActionsBar.tsx | 34 +++++++++---------- .../molecules/GovernanceActionsFilters.tsx | 18 ++++++---- .../molecules/GovernanceActionsSorting.tsx | 17 ++++++---- .../components/molecules/OrderActionsChip.tsx | 5 +++ govtool/frontend/src/hooks/index.ts | 5 +-- .../frontend/src/hooks/useOutsideClick.tsx | 25 ++++++++++++++ 8 files changed, 72 insertions(+), 63 deletions(-) delete mode 100644 govtool/frontend/src/components/atoms/ClickOutside.tsx create mode 100644 govtool/frontend/src/hooks/useOutsideClick.tsx diff --git a/govtool/frontend/src/components/atoms/ClickOutside.tsx b/govtool/frontend/src/components/atoms/ClickOutside.tsx deleted file mode 100644 index 86266bf2a..000000000 --- a/govtool/frontend/src/components/atoms/ClickOutside.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { useRef, useEffect, RefObject } from "react"; - -const useOutsideClick = (ref: RefObject, onClick: () => void) => { - useEffect(() => { - document.addEventListener("mousedown", (e) => { - if (ref.current && !ref.current.contains(e.target as Node)) { - onClick(); - } - }); - - return () => { - document.removeEventListener("mousedown", (e) => { - if (ref.current && !ref.current.contains(e.target as Node)) { - onClick(); - } - }); - }; - }, [ref]); -}; - -interface Props { - children: React.ReactElement; - onClick: () => void; -} - -export const ClickOutside = ({ children, onClick }: Props) => { - const wrapperRef = useRef(null); - useOutsideClick(wrapperRef, onClick); - return
{children}
; -}; diff --git a/govtool/frontend/src/components/atoms/index.ts b/govtool/frontend/src/components/atoms/index.ts index 6fa848bac..186752f80 100644 --- a/govtool/frontend/src/components/atoms/index.ts +++ b/govtool/frontend/src/components/atoms/index.ts @@ -2,7 +2,6 @@ export * from "./ActionRadio"; export * from "./Background"; export * from "./Button"; export * from "./Checkbox"; -export * from "./ClickOutside"; export * from "./CopyButton"; export * from "./DrawerLink"; export * from "./ExternalModalButton"; diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index 297d58bab..08e27a1db 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -4,7 +4,6 @@ import Search from "@mui/icons-material/Search"; import { GovernanceActionsFilters, GovernanceActionsSorting } from "@molecules"; import { OrderActionsChip } from "./OrderActionsChip"; -import { ClickOutside } from "../atoms"; import { theme } from "@/theme"; type DataActionsBarProps = { @@ -86,24 +85,23 @@ export const DataActionsBar: FC = ({ ...props }) => { setSortOpen={setSortOpen} sortingActive={sortingActive} sortOpen={sortOpen} - /> + > + {filtersOpen && ( + + )} + {sortOpen && ( + + )} +
- {filtersOpen && ( - - - - )} - {sortOpen && ( - - - - )} ); }; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx index 89542217d..29bf5cc99 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; +import { Dispatch, SetStateAction, useCallback, useRef } from "react"; import { Box, Checkbox, @@ -8,16 +8,18 @@ import { } from "@mui/material"; import { GOVERNANCE_ACTIONS_FILTERS } from "@consts"; -import { useScreenDimension, useTranslation } from "@hooks"; +import { useOnClickOutside, useScreenDimension, useTranslation } from "@hooks"; interface Props { chosenFilters: string[]; setChosenFilters: Dispatch>; + closeFilters: () => void; } export const GovernanceActionsFilters = ({ chosenFilters, setChosenFilters, + closeFilters, }: Props) => { const handleFilterChange = useCallback( (e: React.ChangeEvent) => { @@ -36,7 +38,10 @@ export const GovernanceActionsFilters = ({ ); const { t } = useTranslation(); - const { isMobile } = useScreenDimension(); + const { isMobile, screenWidth } = useScreenDimension(); + + const wrapperRef = useRef(null); + useOnClickOutside(wrapperRef, closeFilters); return ( >; + closeSorts: () => void; } export const GovernanceActionsSorting = ({ chosenSorting, setChosenSorting, + closeSorts, }: Props) => { const { t } = useTranslation(); - const { isMobile } = useScreenDimension(); + + const wrapperRef = useRef(null); + useOnClickOutside(wrapperRef, closeSorts); return ( diff --git a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx index 037a68cd4..006133653 100644 --- a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx +++ b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx @@ -13,6 +13,7 @@ type Props = { sortOpen: boolean; setSortOpen: Dispatch>; sortingActive: boolean; + children?: React.ReactNode; isFiltering?: boolean; }; @@ -32,6 +33,7 @@ export const OrderActionsChip = (props: Props) => { setSortOpen, sortingActive, isFiltering = true, + children, } = props; return ( @@ -41,6 +43,8 @@ export const OrderActionsChip = (props: Props) => { alignItems="center" ml="8px" gap={isMobile ? "8px" : "24px"} + position="relative" + sx={{ alignSelf: "end" }} > {isFiltering && ( { )} + {children} ); }; diff --git a/govtool/frontend/src/hooks/index.ts b/govtool/frontend/src/hooks/index.ts index 84e427cfa..126c2d0c9 100644 --- a/govtool/frontend/src/hooks/index.ts +++ b/govtool/frontend/src/hooks/index.ts @@ -1,8 +1,9 @@ export { useTranslation } from "react-i18next"; +export * from "./useFetchNextPageDetector"; +export * from "./useOutsideClick"; +export * from "./useSaveScrollPosition"; export * from "./useScreenDimension"; export * from "./useSlider"; -export * from "./useSaveScrollPosition"; -export * from "./useFetchNextPageDetector"; export * from "./useWalletConnectionListener"; export * from "./forms"; diff --git a/govtool/frontend/src/hooks/useOutsideClick.tsx b/govtool/frontend/src/hooks/useOutsideClick.tsx new file mode 100644 index 000000000..619086632 --- /dev/null +++ b/govtool/frontend/src/hooks/useOutsideClick.tsx @@ -0,0 +1,25 @@ +import { MutableRefObject, useEffect } from "react"; + +export function useOnClickOutside( + ref: MutableRefObject, + handler: (event: MouseEvent | TouchEvent) => void, +) { + useEffect(() => { + const listener = (event: MouseEvent | TouchEvent) => { + const target = event.target as Node; + + if (!ref.current || ref.current.contains(target)) { + return; + } + handler(event); + }; + + document.addEventListener("mousedown", listener as EventListener); + document.addEventListener("touchstart", listener as EventListener); + + return () => { + document.removeEventListener("mousedown", listener as EventListener); + document.removeEventListener("touchstart", listener as EventListener); + }; + }, [ref, handler]); +} From d7c349435c02941b4c5c8d470e4a8394801bc254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Fri, 22 Mar 2024 09:21:21 +0100 Subject: [PATCH 14/21] add logic to handle drep metadata --- .../StorageInformation.tsx | 2 +- .../DRepStorageInformation.tsx | 20 +- .../RegisterAsDRepSteps/DRepStoreDataInfo.tsx | 7 +- .../RegisterAsDRepForm.tsx | 23 +-- .../frontend/src/consts/dRepActions/fields.ts | 41 ++++ .../frontend/src/consts/dRepActions/index.ts | 2 + .../src/consts/dRepActions/jsonContext.ts | 50 +++++ .../src/consts/externalDataModalConfig.ts | 41 ++++ .../src/consts/governanceAction/index.ts | 2 - .../metadataHashValidationErrors.ts | 6 - .../consts/governanceAction/modalConfig.ts | 50 ----- govtool/frontend/src/consts/index.ts | 2 + .../forms/useCreateGovernanceActionForm.ts | 2 +- .../src/hooks/forms/useRegisterAsdRepForm.tsx | 194 ++++++++++++++++-- govtool/frontend/src/i18n/locales/en.ts | 39 ++-- govtool/frontend/src/utils/isValidFormat.ts | 2 + 16 files changed, 365 insertions(+), 118 deletions(-) create mode 100644 govtool/frontend/src/consts/dRepActions/fields.ts create mode 100644 govtool/frontend/src/consts/dRepActions/index.ts create mode 100644 govtool/frontend/src/consts/dRepActions/jsonContext.ts create mode 100644 govtool/frontend/src/consts/externalDataModalConfig.ts delete mode 100644 govtool/frontend/src/consts/governanceAction/metadataHashValidationErrors.ts delete mode 100644 govtool/frontend/src/consts/governanceAction/modalConfig.ts diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx index 55cd6d46f..200b59e22 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx @@ -37,7 +37,7 @@ export const StorageInformation = ({ setStep }: StorageInformationProps) => { const openGuideAboutStoringInformation = () => openInNewTab("https://sancho.network/"); - const isActionButtonDisabled = !watch("storingURL"); + const isActionButtonDisabled = !watch("storingURL") || !!errors["storingURL"]; const onClickBack = () => setStep(5); diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx index 238e0f0a8..23f95b354 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; +import { Dispatch, SetStateAction } from "react"; import { Box } from "@mui/material"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; @@ -24,24 +24,23 @@ export const DRepStorageInformation = ({ const { control, errors, + getValues, isRegistrationAsDRepLoading, + onClickDownloadJson, registerAsDrep, watch, } = useRegisterAsdRepForm(); const { screenWidth } = useScreenDimension(); - // TODO: change on correct file name - const fileName = "fileName"; + const fileName = getValues("dRepName"); // TODO: Change link to correct - const openGuideAboutStoringInformation = useCallback( - () => openInNewTab("https://sancho.network/"), - [], - ); + const openGuideAboutStoringInformation = () => + openInNewTab("https://sancho.network/"); - const isActionButtonDisabled = !watch("storingURL"); + const isActionButtonDisabled = !watch("storingURL") || !!errors["storingURL"]; - const onClickBack = useCallback(() => setStep(3), []); + const onClickBack = () => setStep(3); return ( } sx={{ diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx index 1e6839512..a8a22e110 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx @@ -18,13 +18,10 @@ export const DRepStoreDataInfo = ({ }) => { const { t } = useTranslation(); const { isMobile } = useScreenDimension(); - const { control, errors, isRegistrationAsDRepLoading, resetField, watch } = + const { control, errors, isRegistrationAsDRepLoading, watch } = useRegisterAsdRepForm(); - const onClickBackButton = () => { - setStep(2); - resetField("storeData"); - }; + const onClickBackButton = () => setStep(2); const isContinueDisabled = !watch("storeData"); diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx index d2295b79f..bea469874 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx @@ -4,17 +4,16 @@ 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 { useRegisterAsdRepForm, useScreenDimension, useTranslation, } from "@hooks"; -import { URL_REGEX } from "@utils"; import { BgCard, ControlledField } from ".."; -import { Placeholders } from "@/consts"; -const MAX_NUMBER_OF_LINKS = 8; +const MAX_NUMBER_OF_LINKS = 7; export const RegisterAsDRepForm = ({ setStep, @@ -23,7 +22,7 @@ export const RegisterAsDRepForm = ({ }) => { const { t } = useTranslation(); const { isMobile } = useScreenDimension(); - const { control, errors, register, watch } = useRegisterAsdRepForm(); + const { control, errors, isError, register, watch } = useRegisterAsdRepForm(); const { append, fields: links, @@ -41,7 +40,7 @@ export const RegisterAsDRepForm = ({ const removeLink = useCallback((index: number) => remove(index), [remove]); - const isContinueButtonDisabled = !watch("dRepName"); + const isContinueButtonDisabled = !watch("dRepName") || isError; const renderLinks = useCallback( () => @@ -65,12 +64,7 @@ export const RegisterAsDRepForm = ({ layoutStyles={{ mb: 3 }} placeholder={Placeholders.LINK} name={`links.${index}.link`} - rules={{ - pattern: { - value: URL_REGEX, - message: t("createGovernanceAction.fields.validations.url"), - }, - }} + rules={Rules.LINK} /> )), [links], @@ -98,6 +92,7 @@ export const RegisterAsDRepForm = ({ helpfulText={t("forms.registerAsDRep.dRepNameHelpfulText")} label={t("forms.registerAsDRep.dRepName")} name="dRepName" + rules={Rules.DREP_NAME} placeholder={t("forms.registerAsDRep.dRepNamePlaceholder")} /> @@ -115,6 +110,7 @@ export const RegisterAsDRepForm = ({ label={t("forms.registerAsDRep.email")} name="email" placeholder={t("forms.registerAsDRep.emailPlaceholder")} + rules={Rules.EMAIL} />

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

diff --git a/govtool/frontend/src/consts/dRepActions/fields.ts b/govtool/frontend/src/consts/dRepActions/fields.ts new file mode 100644 index 000000000..025e7c72c --- /dev/null +++ b/govtool/frontend/src/consts/dRepActions/fields.ts @@ -0,0 +1,41 @@ +import i18n from "@/i18n"; +import { EMAIL_REGEX, NICKNAME_REGEX, URL_REGEX } from "@/utils"; + +export const Rules = { + BIO: { + maxLength: { + value: 500, + message: i18n.t("registration.fields.validations.maxLength", { + maxLength: 500, + }), + }, + }, + DREP_NAME: { + required: { + value: true, + message: i18n.t("registration.fields.validations.required"), + }, + pattern: { + value: NICKNAME_REGEX, + message: i18n.t("registration.fields.validations.nickname"), + }, + maxLength: { + value: 80, + message: i18n.t("registration.fields.validations.maxLength", { + maxLength: 80, + }), + }, + }, + EMAIL: { + pattern: { + value: EMAIL_REGEX, + message: i18n.t("registration.fields.validations.email"), + }, + }, + LINK: { + pattern: { + value: URL_REGEX, + message: i18n.t("registration.fields.validations.url"), + }, + }, +}; diff --git a/govtool/frontend/src/consts/dRepActions/index.ts b/govtool/frontend/src/consts/dRepActions/index.ts new file mode 100644 index 000000000..68294a70f --- /dev/null +++ b/govtool/frontend/src/consts/dRepActions/index.ts @@ -0,0 +1,2 @@ +export * from "./fields"; +export * from "./jsonContext"; diff --git a/govtool/frontend/src/consts/dRepActions/jsonContext.ts b/govtool/frontend/src/consts/dRepActions/jsonContext.ts new file mode 100644 index 000000000..78a6040df --- /dev/null +++ b/govtool/frontend/src/consts/dRepActions/jsonContext.ts @@ -0,0 +1,50 @@ +export const CIP_QQQ = + "https://github.com/cardano-foundation/CIPs/blob/master/CIP-QQQ/README.md#"; + +export const DREP_CONTEXT = { + "@language": "en-us", + CIP100: + "https://github.com/cardano-foundation/CIPs/blob/master/CIP-0100/README.md#", + CIPQQQ: CIP_QQQ, + hashAlgorithm: "CIP100:hashAlgorithm", + body: { + "@id": "CIPQQQ:body", + "@context": { + references: { + "@id": "CIPQQQ:references", + "@container": "@set" as const, + "@context": { + GovernanceMetadata: "CIP100:GovernanceMetadataReference", + Other: "CIP100:OtherReference", + label: "CIP100:reference-label", + uri: "CIP100:reference-uri", + referenceHash: { + "@id": "CIPQQQ:referenceHash", + "@context": { + hashDigest: "CIPQQQ:hashDigest", + hashAlgorithm: "CIP100:hashAlgorithm", + }, + }, + }, + }, + dRepName: "CIPQQQ:dRepName", + bio: "CIPQQQ:bio", + email: "CIPQQQ:email", + }, + }, + authors: { + "@id": "CIP100:authors", + "@container": "@set" as const, + "@context": { + name: "http://xmlns.com/foaf/0.1/name", + witness: { + "@id": "CIP100:witness", + "@context": { + witnessAlgorithm: "CIP100:witnessAlgorithm", + publicKey: "CIP100:publicKey", + signature: "CIP100:signature", + }, + }, + }, + }, +}; diff --git a/govtool/frontend/src/consts/externalDataModalConfig.ts b/govtool/frontend/src/consts/externalDataModalConfig.ts new file mode 100644 index 000000000..1508004f4 --- /dev/null +++ b/govtool/frontend/src/consts/externalDataModalConfig.ts @@ -0,0 +1,41 @@ +import { ModalState } from "@/context"; +import I18n from "@/i18n"; + +export enum MetadataHashValidationErrors { + INVALID_URL = "Invalid URL", + INVALID_JSON = "Invalid JSON", + INVALID_HASH = "Invalid hash", + FETCH_ERROR = "Error fetching data", +} + +const externalDataDoesntMatchModal = { + status: "warning", + title: I18n.t("modals.externalDataDoesntMatch.title"), + message: I18n.t("modals.externalDataDoesntMatch.message"), + buttonText: I18n.t("modals.externalDataDoesntMatch.buttonText"), + cancelText: I18n.t("modals.externalDataDoesntMatch.cancelRegistrationText"), + feedbackText: I18n.t("modals.externalDataDoesntMatch.feedbackText"), +} as const; + +const urlCannotBeFound = { + status: "warning", + title: I18n.t("modals.urlCannotBeFound.title"), + message: I18n.t("modals.urlCannotBeFound.message"), + link: "https://docs.sanchogov.tools", + linkText: I18n.t("modals.urlCannotBeFound.linkText"), + buttonText: I18n.t("modals.urlCannotBeFound.buttonText"), + cancelText: I18n.t("modals.urlCannotBeFound.cancelRegistrationText"), + feedbackText: I18n.t("modals.urlCannotBeFound.feedbackText"), +}; + +export const storageInformationErrorModals: Record< + MetadataHashValidationErrors, + ModalState< + typeof externalDataDoesntMatchModal | typeof urlCannotBeFound + >["state"] +> = { + [MetadataHashValidationErrors.INVALID_URL]: urlCannotBeFound, + [MetadataHashValidationErrors.FETCH_ERROR]: urlCannotBeFound, + [MetadataHashValidationErrors.INVALID_JSON]: externalDataDoesntMatchModal, + [MetadataHashValidationErrors.INVALID_HASH]: externalDataDoesntMatchModal, +}; diff --git a/govtool/frontend/src/consts/governanceAction/index.ts b/govtool/frontend/src/consts/governanceAction/index.ts index 288b6cd15..f25d9d5b2 100644 --- a/govtool/frontend/src/consts/governanceAction/index.ts +++ b/govtool/frontend/src/consts/governanceAction/index.ts @@ -1,5 +1,3 @@ export * from "./fields"; export * from "./filters"; export * from "./sorting"; -export * from "./modalConfig"; -export * from "./metadataHashValidationErrors"; diff --git a/govtool/frontend/src/consts/governanceAction/metadataHashValidationErrors.ts b/govtool/frontend/src/consts/governanceAction/metadataHashValidationErrors.ts deleted file mode 100644 index 0d3479ba4..000000000 --- a/govtool/frontend/src/consts/governanceAction/metadataHashValidationErrors.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum MetadataHashValidationErrors { - INVALID_URL = "Invalid URL", - INVALID_JSON = "Invalid JSON", - INVALID_HASH = "Invalid hash", - FETCH_ERROR = "Error fetching data", -} diff --git a/govtool/frontend/src/consts/governanceAction/modalConfig.ts b/govtool/frontend/src/consts/governanceAction/modalConfig.ts deleted file mode 100644 index e38aae9c4..000000000 --- a/govtool/frontend/src/consts/governanceAction/modalConfig.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ModalState } from "@/context"; -import I18n from "@/i18n"; - -import { MetadataHashValidationErrors } from "./metadataHashValidationErrors"; - -const externalDataDoesntMatchModal = { - status: "warning", - title: I18n.t("createGovernanceAction.modals.externalDataDoesntMatch.title"), - message: I18n.t( - "createGovernanceAction.modals.externalDataDoesntMatch.message", - ), - buttonText: I18n.t( - "createGovernanceAction.modals.externalDataDoesntMatch.buttonText", - ), - cancelText: I18n.t( - "createGovernanceAction.modals.externalDataDoesntMatch.cancelRegistrationText", - ), - feedbackText: I18n.t( - "createGovernanceAction.modals.externalDataDoesntMatch.feedbackText", - ), -} as const; - -const urlCannotBeFound = { - status: "warning", - title: I18n.t("createGovernanceAction.modals.urlCannotBeFound.title"), - message: I18n.t("createGovernanceAction.modals.urlCannotBeFound.message"), - link: "https://docs.sanchogov.tools", - linkText: I18n.t("createGovernanceAction.modals.urlCannotBeFound.linkText"), - buttonText: I18n.t( - "createGovernanceAction.modals.urlCannotBeFound.buttonText", - ), - cancelText: I18n.t( - "createGovernanceAction.modals.urlCannotBeFound.cancelRegistrationText", - ), - feedbackText: I18n.t( - "createGovernanceAction.modals.urlCannotBeFound.feedbackText", - ), -}; - -export const storageInformationErrorModals: Record< - MetadataHashValidationErrors, - ModalState< - typeof externalDataDoesntMatchModal | typeof urlCannotBeFound - >["state"] -> = { - [MetadataHashValidationErrors.INVALID_URL]: urlCannotBeFound, - [MetadataHashValidationErrors.FETCH_ERROR]: urlCannotBeFound, - [MetadataHashValidationErrors.INVALID_JSON]: externalDataDoesntMatchModal, - [MetadataHashValidationErrors.INVALID_HASH]: externalDataDoesntMatchModal, -}; diff --git a/govtool/frontend/src/consts/index.ts b/govtool/frontend/src/consts/index.ts index 201aacbb1..22a9c3bbc 100644 --- a/govtool/frontend/src/consts/index.ts +++ b/govtool/frontend/src/consts/index.ts @@ -1,4 +1,6 @@ +export * from "./externalDataModalConfig"; export * from "./colors"; +export * from "./dRepActions"; export * from "./governanceAction"; export * from "./icons"; export * from "./images"; diff --git a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts index ff0d505b6..e8ff4f660 100644 --- a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts +++ b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts @@ -135,7 +135,7 @@ export const useCreateGovernanceActionForm = ( type: "statusModal", state: { ...storageInformationErrorModals[ - error.message as MetadataHashValidationErrors + error.message as MetadataHashValidationErrors ], onSubmit: backToForm, onCancel: backToDashboard, diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index 9f7ac6e37..c2c672352 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -1,5 +1,26 @@ -import { useCallback, useState } from "react"; +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 { + CIP_100, + CIP_QQQ, + DREP_CONTEXT, + MetadataHashValidationErrors, + PATHS, + storageInformationErrorModals, +} from "@consts"; +import { useCardano, useModal } from "@context"; +import { + canonizeJSON, + downloadJson, + generateJsonld, + validateMetadataHash, +} from "@utils"; +import { useGetVoterInfo } from ".."; export type RegisterAsDRepValues = { bio?: string; @@ -19,11 +40,31 @@ export const defaultRegisterAsDRepValues: RegisterAsDRepValues = { storingURL: "", }; -export const useRegisterAsdRepForm = () => { +export const useRegisterAsdRepForm = ( + setStep?: Dispatch>, +) => { + const { t } = useTranslation(); + const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); + const [hash, setHash] = useState(null); + const { closeModal, openModal } = useModal(); + const { buildDRepRegCert, buildDRepUpdateCert, buildSignSubmitConwayCertTx } = + useCardano(); + const { voter } = useGetVoterInfo(); + + const backToForm = useCallback(() => { + setStep?.(3); + closeModal(); + }, [setStep]); + + const backToDashboard = useCallback(() => { + navigate(PATHS.dashboard); + closeModal(); + }, []); const { control, + getValues, handleSubmit, formState: { errors, isValid }, register, @@ -31,23 +72,152 @@ export const useRegisterAsdRepForm = () => { watch, } = useFormContext(); - const onSubmit = useCallback(async (values: RegisterAsDRepValues) => { - try { - // eslint-disable-next-line no-console - console.log(values); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { - console.error(e); - } finally { - setIsLoading(false); - } + const dRepName = watch("dRepName"); + const isError = Object.keys(errors).length > 0; + + const generateMetadata = async (data: RegisterAsDRepValues) => { + 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 RegisterAsDRepValues).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); + + const canonizedJson = await canonizeJSON(jsonld); + const hash = blake2bHex(canonizedJson, undefined, 32); + + setHash(hash); + + return jsonld; + }; + + const onClickDownloadJson = async () => { + const data = getValues(); + const json = await generateMetadata(data); + + downloadJson(json, dRepName); + }; + + const validateHash = useCallback( + async (storingUrl: string, hash: string | null) => { + try { + if (!hash) throw new Error(MetadataHashValidationErrors.INVALID_HASH); + + await validateMetadataHash(storingUrl, hash); + } 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], + ); + + const createCert = useCallback( + async (data: RegisterAsDRepValues) => { + // if (!hash) return; + // const url = data.storingURL; + const urlSubmitValue = + "https://raw.githubusercontent.com/Thomas-Upfield/test-metadata/main/placeholder.json"; + const hashSubmitValue = + "654e483feefc4d208ea02637a981a2046e17c73c09583e9dd0c84c25dab42749"; + try { + let certBuilder; + if (voter?.isRegisteredAsSoleVoter) { + certBuilder = await buildDRepUpdateCert( + urlSubmitValue, + hashSubmitValue, + ); + } else { + certBuilder = await buildDRepRegCert(urlSubmitValue, hashSubmitValue); + } + return certBuilder; + } catch (error: any) { + console.error(error); + throw error; + } + }, + [ + buildDRepRegCert, + buildDRepUpdateCert, + hash, + voter?.isRegisteredAsSoleVoter, + ], + ); + + 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: RegisterAsDRepValues) => { + try { + setIsLoading(true); + + // await validateHash(data.storingURL, hash); + const registerAsDRepCert = await createCert(data); + await buildSignSubmitConwayCertTx({ + certBuilder: registerAsDRepCert, + type: "registerAsDrep", + }); + + showSuccessModal(); + } catch (error: any) { + captureException(error); + console.error(error); + } finally { + setIsLoading(false); + } + }, + [buildSignSubmitConwayCertTx, createCert, hash], + ); + return { control, errors, + getValues, + isError, isRegistrationAsDRepLoading: isLoading, isValid, + onClickDownloadJson, register, registerAsDrep: handleSubmit(onSubmit), resetField, diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 19e49e5f0..5f451f5f5 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -199,28 +199,11 @@ export const en = { supportingLinks: "Supporting links", title: "Create a Governance Action", modals: { - externalDataDoesntMatch: { - buttonText: "Go to Data Edit Screen", - cancelRegistrationText: "Cancel Registration", - feedbackText: "Feedback", - message: - "GovTool checks the URL you entered to see if the JSON file that you self-host matches the one that was generated in GovTool. To complete registration, this match must be exact.\n\nIn this case, there is a mismatch. You can go back to the data edit screen and try the process again.", - title: "Your External Data Does Not Match the Original File.", - }, submitTransactionSuccess: { message: "Your Governance Action may take a little time to submit to the chain.", title: "Governance Action submitted!", }, - urlCannotBeFound: { - buttonText: "Go to Data Edit Screen", - cancelRegistrationText: "Cancel Registration", - feedbackText: "Feedback", - linkText: "Learn More about self-hosting", - message: - "GovTool cannot find the URL that you entered. Please check it and re-enter.", - title: "The URL You Entered Cannot Be Found", - }, }, }, delegation: { @@ -438,6 +421,14 @@ export const en = { "The confirmation of your actual delegation might take a bit of time but you can track it using", title: "Delegation Transaction Submitted!", }, + externalDataDoesntMatch: { + buttonText: "Go to Data Edit Screen", + cancelRegistrationText: "Cancel Registration", + feedbackText: "Feedback", + message: + "GovTool checks the URL you entered to see if the JSON file that you self-host matches the one that was generated in GovTool. To complete registration, this match must be exact.\n\nIn this case, there is a mismatch. You can go back to the data edit screen and try the process again.", + title: "Your External Data Does Not Match the Original File.", + }, externalLink: { beCareful: "Be Careful!", continueTo: "Continue to external link", @@ -460,6 +451,15 @@ export const en = { "The confirmation of your retirement might take a bit of time but you can track it using", title: "Retirement Transaction Submitted!", }, + urlCannotBeFound: { + buttonText: "Go to Data Edit Screen", + cancelRegistrationText: "Cancel Registration", + feedbackText: "Feedback", + linkText: "Learn More about self-hosting", + message: + "GovTool cannot find the URL that you entered. Please check it and re-enter.", + title: "The URL You Entered Cannot Be Found", + }, votingPower: { govActionsVotes: "Governance Action votes", votesSubmittedByDReps: "Votes submitted by DReps", @@ -486,7 +486,7 @@ export const en = { "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", linksDescription: "Links to extra content or social media contacts ", - maximumLinks: "(maximum of 7 entries)", + maximumLinks: "(maximum of {{numberOfLinks}} entries)", optional: "optional", register: "Register", required: "required", @@ -508,6 +508,9 @@ export const en = { 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", }, diff --git a/govtool/frontend/src/utils/isValidFormat.ts b/govtool/frontend/src/utils/isValidFormat.ts index 67824d6c3..4b4ccafa8 100644 --- a/govtool/frontend/src/utils/isValidFormat.ts +++ b/govtool/frontend/src/utils/isValidFormat.ts @@ -1,6 +1,8 @@ export const URL_REGEX = /^(?!.*\s)(ipfs:\/\/[a-zA-Z0-9]+|https?:\/\/(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[^/\s]+?\.[a-zA-Z]{2,})[^\s]*)/; export const HASH_REGEX = /^[0-9A-Fa-f]+$/; +export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; +export const NICKNAME_REGEX = /^\S+$/; export function isValidURLFormat(str: string) { if (!str.length) return true; From 902d4a5df8ec1c6a569db840372d08c2594c6505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Mon, 25 Mar 2024 15:11:26 +0100 Subject: [PATCH 15/21] metadata creation for DRep registration --- .../DRepStorageInformation.tsx | 7 +- .../src/hooks/forms/useRegisterAsdRepForm.tsx | 40 +- govtool/frontend/src/utils/generateJsonld.ts | 3 +- govtool/frontend/yarn.lock | 987 +++++++++++------- 4 files changed, 639 insertions(+), 398 deletions(-) diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx index 23f95b354..7768f95ce 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction } from "react"; +import { Dispatch, SetStateAction, useEffect } from "react"; import { Box } from "@mui/material"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; @@ -24,6 +24,7 @@ export const DRepStorageInformation = ({ const { control, errors, + generateMetadata, getValues, isRegistrationAsDRepLoading, onClickDownloadJson, @@ -42,6 +43,10 @@ export const DRepStorageInformation = ({ const onClickBack = () => setStep(3); + useEffect(() => { + generateMetadata(); + }, []); + return ( ; storeData?: boolean; storingURL: string; }; @@ -47,6 +48,7 @@ export const useRegisterAsdRepForm = ( const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [hash, setHash] = useState(null); + const [json, setJson] = useState(null); const { closeModal, openModal } = useModal(); const { buildDRepRegCert, buildDRepUpdateCert, buildSignSubmitConwayCertTx } = useCardano(); @@ -75,7 +77,8 @@ export const useRegisterAsdRepForm = ( const dRepName = watch("dRepName"); const isError = Object.keys(errors).length > 0; - const generateMetadata = async (data: RegisterAsDRepValues) => { + const generateMetadata = useCallback(async () => { + const data = getValues(); const acceptedKeys = ["dRepName", "bio", "email"]; const filteredData = Object.entries(data) @@ -95,19 +98,19 @@ export const useRegisterAsdRepForm = ( [`${CIP_QQQ}references`]: references, }; - const jsonld = await generateJsonld(body, DREP_CONTEXT); + const jsonld = await generateJsonld(body, DREP_CONTEXT, CIP_QQQ); const canonizedJson = await canonizeJSON(jsonld); const hash = blake2bHex(canonizedJson, undefined, 32); setHash(hash); + setJson(jsonld); return jsonld; - }; + }, []); const onClickDownloadJson = async () => { - const data = getValues(); - const json = await generateMetadata(data); + if (!json) return; downloadJson(json, dRepName); }; @@ -141,23 +144,17 @@ export const useRegisterAsdRepForm = ( [backToForm], ); - const createCert = useCallback( + const createRegistrationCert = useCallback( async (data: RegisterAsDRepValues) => { - // if (!hash) return; - // const url = data.storingURL; - const urlSubmitValue = - "https://raw.githubusercontent.com/Thomas-Upfield/test-metadata/main/placeholder.json"; - const hashSubmitValue = - "654e483feefc4d208ea02637a981a2046e17c73c09583e9dd0c84c25dab42749"; + if (!hash) return; + const url = data.storingURL; + try { let certBuilder; if (voter?.isRegisteredAsSoleVoter) { - certBuilder = await buildDRepUpdateCert( - urlSubmitValue, - hashSubmitValue, - ); + certBuilder = await buildDRepUpdateCert(url, hash); } else { - certBuilder = await buildDRepRegCert(urlSubmitValue, hashSubmitValue); + certBuilder = await buildDRepRegCert(url, hash); } return certBuilder; } catch (error: any) { @@ -192,8 +189,8 @@ export const useRegisterAsdRepForm = ( try { setIsLoading(true); - // await validateHash(data.storingURL, hash); - const registerAsDRepCert = await createCert(data); + await validateHash(data.storingURL, hash); + const registerAsDRepCert = await createRegistrationCert(data); await buildSignSubmitConwayCertTx({ certBuilder: registerAsDRepCert, type: "registerAsDrep", @@ -207,12 +204,13 @@ export const useRegisterAsdRepForm = ( setIsLoading(false); } }, - [buildSignSubmitConwayCertTx, createCert, hash], + [buildSignSubmitConwayCertTx, createRegistrationCert, hash], ); return { control, errors, + generateMetadata, getValues, isError, isRegistrationAsDRepLoading: isLoading, diff --git a/govtool/frontend/src/utils/generateJsonld.ts b/govtool/frontend/src/utils/generateJsonld.ts index e78c30af9..24ba6187f 100644 --- a/govtool/frontend/src/utils/generateJsonld.ts +++ b/govtool/frontend/src/utils/generateJsonld.ts @@ -17,9 +17,10 @@ export const generateJsonld = async < >( body: T, context: C, + bodyCip: string = CIP_108, ) => { const doc = { - [`${CIP_108}body`]: body, + [`${bodyCip}body`]: body, [`${CIP_100}hashAlgorithm`]: "blake2b-256", [`${CIP_100}authors`]: [], }; diff --git a/govtool/frontend/yarn.lock b/govtool/frontend/yarn.lock index f5f7feba5..19855528f 100644 --- a/govtool/frontend/yarn.lock +++ b/govtool/frontend/yarn.lock @@ -40,7 +40,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== -"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.18.9", "@babel/core@^7.20.12", "@babel/core@^7.22.5", "@babel/core@^7.23.0", "@babel/core@^7.23.2", "@babel/core@^7.23.5", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.7.5", "@babel/core@^7.8.0": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.18.9", "@babel/core@^7.20.12", "@babel/core@^7.22.5", "@babel/core@^7.23.0", "@babel/core@^7.23.2", "@babel/core@^7.23.5", "@babel/core@^7.7.5": version "7.23.5" resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz" integrity sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g== @@ -878,7 +878,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/preset-env@^7.1.6", "@babel/preset-env@^7.23.2": +"@babel/preset-env@^7.23.2": version "7.23.5" resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.5.tgz" integrity sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A== @@ -1131,7 +1131,7 @@ resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz" integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== -"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.11.1", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0": +"@emotion/react@^11.11.1": version "11.11.1" resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz" integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA== @@ -1161,7 +1161,7 @@ resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== -"@emotion/styled@^11.11.0", "@emotion/styled@^11.3.0": +"@emotion/styled@^11.11.0": version "11.11.0" resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz" integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng== @@ -1198,6 +1198,56 @@ resolved "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-asmjs/-/cardano-serialization-lib-asmjs-12.0.0-alpha.19.tgz" integrity sha512-fF3e5qPgJeYDDmPVcyZ7nCuqYI6H3JJ5cSjxnKRRKK/FzYxEsHRoxpLCuZ604A4j/4mYNXVlUYeV4KhgecgUBw== +"@esbuild/aix-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz#2acd20be6d4f0458bc8c784103495ff24f13b1d3" + integrity sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g== + +"@esbuild/android-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" + integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== + +"@esbuild/android-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz#b45d000017385c9051a4f03e17078abb935be220" + integrity sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q== + +"@esbuild/android-arm64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.8.tgz#fb7130103835b6d43ea499c3f30cfb2b2ed58456" + integrity sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA== + +"@esbuild/android-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" + integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== + +"@esbuild/android-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.11.tgz#f46f55414e1c3614ac682b29977792131238164c" + integrity sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw== + +"@esbuild/android-arm@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.8.tgz#b46e4d9e984e6d6db6c4224d72c86b7757e35bcb" + integrity sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA== + +"@esbuild/android-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" + integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== + +"@esbuild/android-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.11.tgz#bfc01e91740b82011ef503c48f548950824922b2" + integrity sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg== + +"@esbuild/android-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.8.tgz#a13db9441b5a4f4e4fec4a6f8ffacfea07888db7" + integrity sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A== + "@esbuild/darwin-arm64@0.18.20": version "0.18.20" resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz" @@ -1213,6 +1263,276 @@ resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.8.tgz" integrity sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw== +"@esbuild/darwin-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" + integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== + +"@esbuild/darwin-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz#62f3819eff7e4ddc656b7c6815a31cf9a1e7d98e" + integrity sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g== + +"@esbuild/darwin-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.8.tgz#75c5c88371eea4bfc1f9ecfd0e75104c74a481ac" + integrity sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q== + +"@esbuild/freebsd-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" + integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== + +"@esbuild/freebsd-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz#d478b4195aa3ca44160272dab85ef8baf4175b4a" + integrity sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA== + +"@esbuild/freebsd-arm64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.8.tgz#9d7259fea4fd2b5f7437b52b542816e89d7c8575" + integrity sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw== + +"@esbuild/freebsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" + integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== + +"@esbuild/freebsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz#7bdcc1917409178257ca6a1a27fe06e797ec18a2" + integrity sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw== + +"@esbuild/freebsd-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.8.tgz#abac03e1c4c7c75ee8add6d76ec592f46dbb39e3" + integrity sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg== + +"@esbuild/linux-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" + integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== + +"@esbuild/linux-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz#58ad4ff11685fcc735d7ff4ca759ab18fcfe4545" + integrity sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg== + +"@esbuild/linux-arm64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.8.tgz#c577932cf4feeaa43cb9cec27b89cbe0df7d9098" + integrity sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ== + +"@esbuild/linux-arm@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" + integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== + +"@esbuild/linux-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz#ce82246d873b5534d34de1e5c1b33026f35e60e3" + integrity sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q== + +"@esbuild/linux-arm@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.8.tgz#d6014d8b98b5cbc96b95dad3d14d75bb364fdc0f" + integrity sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ== + +"@esbuild/linux-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" + integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== + +"@esbuild/linux-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz#cbae1f313209affc74b80f4390c4c35c6ab83fa4" + integrity sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA== + +"@esbuild/linux-ia32@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.8.tgz#2379a0554307d19ac4a6cdc15b08f0ea28e7a40d" + integrity sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ== + +"@esbuild/linux-loong64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" + integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== + +"@esbuild/linux-loong64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz#5f32aead1c3ec8f4cccdb7ed08b166224d4e9121" + integrity sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg== + +"@esbuild/linux-loong64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.8.tgz#e2a5bbffe15748b49356a6cd7b2d5bf60c5a7123" + integrity sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ== + +"@esbuild/linux-mips64el@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" + integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== + +"@esbuild/linux-mips64el@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz#38eecf1cbb8c36a616261de858b3c10d03419af9" + integrity sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg== + +"@esbuild/linux-mips64el@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.8.tgz#1359331e6f6214f26f4b08db9b9df661c57cfa24" + integrity sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q== + +"@esbuild/linux-ppc64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" + integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== + +"@esbuild/linux-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz#9c5725a94e6ec15b93195e5a6afb821628afd912" + integrity sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA== + +"@esbuild/linux-ppc64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.8.tgz#9ba436addc1646dc89dae48c62d3e951ffe70951" + integrity sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg== + +"@esbuild/linux-riscv64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" + integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== + +"@esbuild/linux-riscv64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz#2dc4486d474a2a62bbe5870522a9a600e2acb916" + integrity sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ== + +"@esbuild/linux-riscv64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.8.tgz#fbcf0c3a0b20f40b5fc31c3b7695f0769f9de66b" + integrity sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg== + +"@esbuild/linux-s390x@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" + integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== + +"@esbuild/linux-s390x@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz#4ad8567df48f7dd4c71ec5b1753b6f37561a65a8" + integrity sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q== + +"@esbuild/linux-s390x@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.8.tgz#989e8a05f7792d139d5564ffa7ff898ac6f20a4a" + integrity sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg== + +"@esbuild/linux-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" + integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== + +"@esbuild/linux-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz#b7390c4d5184f203ebe7ddaedf073df82a658766" + integrity sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA== + +"@esbuild/linux-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.8.tgz#b187295393a59323397fe5ff51e769ec4e72212b" + integrity sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg== + +"@esbuild/netbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" + integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== + +"@esbuild/netbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz#d633c09492a1721377f3bccedb2d821b911e813d" + integrity sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ== + +"@esbuild/netbsd-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.8.tgz#c1ec0e24ea82313cb1c7bae176bd5acd5bde7137" + integrity sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw== + +"@esbuild/openbsd-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" + integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== + +"@esbuild/openbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz#17388c76e2f01125bf831a68c03a7ffccb65d1a2" + integrity sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw== + +"@esbuild/openbsd-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.8.tgz#0c5b696ac66c6d70cf9ee17073a581a28af9e18d" + integrity sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ== + +"@esbuild/sunos-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" + integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== + +"@esbuild/sunos-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz#e320636f00bb9f4fdf3a80e548cb743370d41767" + integrity sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ== + +"@esbuild/sunos-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.8.tgz#2a697e1f77926ff09fcc457d8f29916d6cd48fb1" + integrity sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w== + +"@esbuild/win32-arm64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" + integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== + +"@esbuild/win32-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz#c778b45a496e90b6fc373e2a2bb072f1441fe0ee" + integrity sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ== + +"@esbuild/win32-arm64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.8.tgz#ec029e62a2fca8c071842ecb1bc5c2dd20b066f1" + integrity sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg== + +"@esbuild/win32-ia32@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" + integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== + +"@esbuild/win32-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz#481a65fee2e5cce74ec44823e6b09ecedcc5194c" + integrity sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg== + +"@esbuild/win32-ia32@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.8.tgz#cbb9a3146bde64dc15543e48afe418c7a3214851" + integrity sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw== + +"@esbuild/win32-x64@0.18.20": + version "0.18.20" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" + integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== + +"@esbuild/win32-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz#a5d300008960bb39677c46bf16f53ec70d8dee04" + integrity sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw== + +"@esbuild/win32-x64@0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.8.tgz#c8285183dbdb17008578dbacb6e22748709b4822" + integrity sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -1436,7 +1756,7 @@ jest-mock "^29.7.0" jest-util "^29.7.0" -"@jest/globals@^29.7.0", "@jest/globals@>= 28": +"@jest/globals@^29.7.0": version "29.7.0" resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz" integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== @@ -1654,7 +1974,7 @@ dependencies: "@babel/runtime" "^7.23.4" -"@mui/material@^5.0.0", "@mui/material@^5.14.4": +"@mui/material@^5.14.4": version "5.14.20" resolved "https://registry.npmjs.org/@mui/material/-/material-5.14.20.tgz" integrity sha512-SUcPZnN6e0h1AtrDktEl76Dsyo/7pyEUQ+SAVe9XhHg/iliA0b4Vo+Eg4HbNkELsMbpDsUF4WHp7rgflPG7qYQ== @@ -1737,7 +2057,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -2069,11 +2389,76 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@rollup/rollup-android-arm-eabi@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.2.tgz#ccb02257556bacbc1e756ab9b0b973cea2c7a664" + integrity sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA== + +"@rollup/rollup-android-arm64@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.2.tgz#21bd0fbafdf442c6a17645b840f6a94556b0e9bb" + integrity sha512-yZ+MUbnwf3SHNWQKJyWh88ii2HbuHCFQnAYTeeO1Nb8SyEiWASEi5dQUygt3ClHWtA9My9RQAYkjvrsZ0WK8Xg== + "@rollup/rollup-darwin-arm64@4.9.2": version "4.9.2" resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.2.tgz" integrity sha512-vqJ/pAUh95FLc/G/3+xPqlSBgilPnauVf2EXOQCZzhZJCXDXt/5A8mH/OzU6iWhb3CNk5hPJrh8pqJUPldN5zw== +"@rollup/rollup-darwin-x64@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.2.tgz#1b06291ff1c41af94d2786cd167188c5bf7caec9" + integrity sha512-otPHsN5LlvedOprd3SdfrRNhOahhVBwJpepVKUN58L0RnC29vOAej1vMEaVU6DadnpjivVsNTM5eNt0CcwTahw== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.2.tgz#147069948bba00f435122f411210624e72638ebf" + integrity sha512-ewG5yJSp+zYKBYQLbd1CUA7b1lSfIdo9zJShNTyc2ZP1rcPrqyZcNlsHgs7v1zhgfdS+kW0p5frc0aVqhZCiYQ== + +"@rollup/rollup-linux-arm64-gnu@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.2.tgz#3a50f0e7ae6e444d11c61fce12783196454a4efb" + integrity sha512-pL6QtV26W52aCWTG1IuFV3FMPL1m4wbsRG+qijIvgFO/VBsiXJjDPE/uiMdHBAO6YcpV4KvpKtd0v3WFbaxBtg== + +"@rollup/rollup-linux-arm64-musl@4.12.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz#3882a4e3a564af9e55804beeb67076857b035ab7" + integrity sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ== + +"@rollup/rollup-linux-arm64-musl@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.2.tgz#82b5e75484d91c25d4e649d018d9523e72d6dac2" + integrity sha512-On+cc5EpOaTwPSNetHXBuqylDW+765G/oqB9xGmWU3npEhCh8xu0xqHGUA+4xwZLqBbIZNcBlKSIYfkBm6ko7g== + +"@rollup/rollup-linux-riscv64-gnu@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.2.tgz#ca96f2d43a553d73aec736e991c07010561bc7a9" + integrity sha512-Wnx/IVMSZ31D/cO9HSsU46FjrPWHqtdF8+0eyZ1zIB5a6hXaZXghUKpRrC4D5DcRTZOjml2oBhXoqfGYyXKipw== + +"@rollup/rollup-linux-x64-gnu@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.2.tgz#db1cece244ea46706c0e1a522ec19ca0173abc55" + integrity sha512-ym5x1cj4mUAMBummxxRkI4pG5Vht1QMsJexwGP8547TZ0sox9fCLDHw9KCH9c1FO5d9GopvkaJsBIOkTKxksdw== + +"@rollup/rollup-linux-x64-musl@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.2.tgz#c15b26b86827f75977bf59ebd41ce5d788713936" + integrity sha512-m0hYELHGXdYx64D6IDDg/1vOJEaiV8f1G/iO+tejvRCJNSwK4jJ15e38JQy5Q6dGkn1M/9KcyEOwqmlZ2kqaZg== + +"@rollup/rollup-win32-arm64-msvc@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.2.tgz#60152948f9fb08e8c50c1555e334ca9f9f1f53aa" + integrity sha512-x1CWburlbN5JjG+juenuNa4KdedBdXLjZMp56nHFSHTOsb/MI2DYiGzLtRGHNMyydPGffGId+VgjOMrcltOksA== + +"@rollup/rollup-win32-ia32-msvc@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.2.tgz#657288cff10311f997d8dbd648590441760ae6d9" + integrity sha512-VVzCB5yXR1QlfsH1Xw1zdzQ4Pxuzv+CPr5qpElpKhVxlxD3CRdfubAG9mJROl6/dmj5gVYDDWk8sC+j9BI9/kQ== + +"@rollup/rollup-win32-x64-msvc@4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.2.tgz#830f3a3fba67f6216a5884368431918029045afe" + integrity sha512-SYRedJi+mweatroB+6TTnJYLts0L0bosg531xnQWtklOI6dezEagx4Q0qDyvRdK+qgdA3YZpjjGuPFtxBmddBA== + "@sentry-internal/feedback@7.85.0": version "7.85.0" resolved "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.85.0.tgz" @@ -2338,7 +2723,7 @@ dependencies: memoizerific "^1.11.3" -"@storybook/blocks@^7.4.5", "@storybook/blocks@7.6.3": +"@storybook/blocks@7.6.3", "@storybook/blocks@^7.4.5": version "7.6.3" resolved "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.3.tgz" integrity sha512-EyjyNNCZMcV9UnBSujwduiq+F1VLVX/f16fTTPqqZOHigyfrG5LoEYC6dwOC4yO/xfWY+h3qJ51yiugMxVl0Vg== @@ -2521,7 +2906,7 @@ "@storybook/client-logger" "7.6.3" "@storybook/preview-api" "7.6.3" -"@storybook/core-common@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0", "@storybook/core-common@7.6.3": +"@storybook/core-common@7.6.3", "@storybook/core-common@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": version "7.6.3" resolved "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.3.tgz" integrity sha512-/ZE4BEyGwBHCQCOo681GyBKF4IqCiwVV/ZJCHTMTHFCPLJT2r+Qwv4tnI7xt1kwflOlbBlG6B6CvAqTjjVw/Ew== @@ -2612,7 +2997,7 @@ "@storybook/csf-tools" "7.6.3" unplugin "^1.3.1" -"@storybook/csf-tools@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0", "@storybook/csf-tools@7.6.3": +"@storybook/csf-tools@7.6.3", "@storybook/csf-tools@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": version "7.6.3" resolved "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.3.tgz" integrity sha512-Zi3pg2pg88/mvBKewkfWhFUR1J4uYpHI5fSjOE+J/FeZObX/DIE7r+wJxZ0UBGyrk0Wy7Jajlb2uSP56Y0i19w== @@ -2722,7 +3107,7 @@ resolved "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.3.tgz" integrity sha512-WpgdpJpY6rionluxjFZLbKiSDjvQJ5cPgufjvBRuXTsnVOsH3JNRWnPdkQkJLT9uTUMoNcyBMxbjYkK3vU6wSg== -"@storybook/preview-api@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0", "@storybook/preview-api@7.6.3": +"@storybook/preview-api@7.6.3", "@storybook/preview-api@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": version "7.6.3" resolved "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.3.tgz" integrity sha512-uPaK7yLE1P++F+IOb/1j9pgdCwfMYZrUPHogF/Mf9r4cfEjDCcIeKgGMcsbU1KnkzNQQGPh8JRzRr/iYnLjswg== @@ -2765,7 +3150,7 @@ magic-string "^0.30.0" react-docgen "^7.0.0" -"@storybook/react@^7.4.5", "@storybook/react@7.6.3": +"@storybook/react@7.6.3", "@storybook/react@^7.4.5": version "7.6.3" resolved "https://registry.npmjs.org/@storybook/react/-/react-7.6.3.tgz" integrity sha512-W+530cC0BAU+yBc7NzSXYWR3e8Lo5qMsmFJjWYK7zGW/YZGhSG3mjhF9pDzNM+cMtHvUS6qf5PJPQM8jePpPhg== @@ -2801,7 +3186,7 @@ memoizerific "^1.11.3" qs "^6.10.0" -"@storybook/telemetry@^7.1.0", "@storybook/telemetry@7.6.3": +"@storybook/telemetry@7.6.3", "@storybook/telemetry@^7.1.0": version "7.6.3" resolved "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.6.3.tgz" integrity sha512-NDCZWhVIUI3M6Lq4M/HPOvZqDXqANDNbI3kyHr4pFGoVaCUXuDPokL9wR+CZcMvATkJ1gHrfLPBdcRq6Biw3Iw== @@ -2883,7 +3268,47 @@ resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.100.tgz" integrity sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw== -"@swc/core@*", "@swc/core@^1.3.18": +"@swc/core-darwin-x64@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.100.tgz#d84f5c0bb4603c252884d011a698ed7c634b1505" + integrity sha512-KF/MXrnH1nakm1wbt4XV8FS7kvqD9TGmVxeJ0U4bbvxXMvzeYUurzg3AJUTXYmXDhH/VXOYJE5N5RkwZZPs5iA== + +"@swc/core-linux-arm64-gnu@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.100.tgz#1ed4b92b373882d8f338c4e0a0aa64cdaa6106f1" + integrity sha512-p8hikNnAEJrw5vHCtKiFT4hdlQxk1V7vqPmvUDgL/qe2menQDK/i12tbz7/3BEQ4UqUPnvwpmVn2d19RdEMNxw== + +"@swc/core-linux-arm64-musl@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.100.tgz#9db560f7459e42e65ec02670d6a8316e7c850cfc" + integrity sha512-BWx/0EeY89WC4q3AaIaBSGfQxkYxIlS3mX19dwy2FWJs/O+fMvF9oLk/CyJPOZzbp+1DjGeeoGFuDYpiNO91JA== + +"@swc/core-linux-x64-gnu@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.100.tgz#228826ea48879bf1e73683fbef4373e3e762e424" + integrity sha512-XUdGu3dxAkjsahLYnm8WijPfKebo+jHgHphDxaW0ovI6sTdmEGFDew7QzKZRlbYL2jRkUuuKuDGvD6lO5frmhA== + +"@swc/core-linux-x64-musl@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.100.tgz#09a234dbbf625d071ecb663680e997a62d230d49" + integrity sha512-PhoXKf+f0OaNW/GCuXjJ0/KfK9EJX7z2gko+7nVnEA0p3aaPtbP6cq1Ubbl6CMoPL+Ci3gZ7nYumDqXNc3CtLQ== + +"@swc/core-win32-arm64-msvc@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.100.tgz#add1c82884c10a9054ed6a48f884097aa85c6d2b" + integrity sha512-PwLADZN6F9cXn4Jw52FeP/MCLVHm8vwouZZSOoOScDtihjY495SSjdPnlosMaRSR4wJQssGwiD/4MbpgQPqbAw== + +"@swc/core-win32-ia32-msvc@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.100.tgz#e0b6c5ae7f3250adeeb88dae83558d3f45148c56" + integrity sha512-0f6nicKSLlDKlyPRl2JEmkpBV4aeDfRQg6n8mPqgL7bliZIcDahG0ej+HxgNjZfS3e0yjDxsNRa6sAqWU2Z60A== + +"@swc/core-win32-x64-msvc@1.3.100": + version "1.3.100" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.100.tgz#34721dff151d7dcf165675f18aeed0a12264d88c" + integrity sha512-b7J0rPoMkRTa3XyUGt8PwCaIBuYWsL2DqbirrQKRESzgCvif5iNpqaM6kjIjI/5y5q1Ycv564CB51YDpiS8EtQ== + +"@swc/core@^1.3.18": version "1.3.100" resolved "https://registry.npmjs.org/@swc/core/-/core-1.3.100.tgz" integrity sha512-7dKgTyxJjlrMwFZYb1auj3Xq0D8ZBe+5oeIgfMlRU05doXZypYJe0LAk0yjj3WdbwYzpF+T1PLxwTWizI0pckw== @@ -2919,7 +3344,7 @@ resolved "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz" integrity sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw== -"@testing-library/dom@^9.0.0", "@testing-library/dom@>=7.21.4": +"@testing-library/dom@^9.0.0": version "9.3.3" resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz" integrity sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw== @@ -3149,7 +3574,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@>= 28", "@types/jest@28.1.3": +"@types/jest@28.1.3": version "28.1.3" resolved "https://registry.npmjs.org/@types/jest/-/jest-28.1.3.tgz" integrity sha512-Tsbjk8Y2hkBaY/gJsataeb4q9Mubw9EOz7+4RjPkzD5KjTvHHs7cpws22InaoXxAVAhF5HfFbzJjo6oKWqSZLw== @@ -3210,7 +3635,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@^18.0.0 || >=20.0.0", "@types/node@^20.4.8", "@types/node@>= 14": +"@types/node@*", "@types/node@^20.4.8": version "20.10.3" resolved "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz" integrity sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg== @@ -3254,13 +3679,6 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@*", "@types/react-dom@^18.0.11": - version "18.2.17" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.17.tgz" - integrity sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg== - dependencies: - "@types/react" "*" - "@types/react-dom@^18.0.0": version "18.2.18" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz" @@ -3268,6 +3686,13 @@ dependencies: "@types/react" "*" +"@types/react-dom@^18.0.11": + version "18.2.17" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.17.tgz" + integrity sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg== + dependencies: + "@types/react" "*" + "@types/react-gtm-module@^2.0.2": version "2.0.3" resolved "https://registry.npmjs.org/@types/react-gtm-module/-/react-gtm-module-2.0.3.tgz" @@ -3280,7 +3705,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react@^16.9.0 || ^17.0.0 || ^18.0.0", "@types/react@^17.0.0 || ^18.0.0", "@types/react@^18.2.12", "@types/react@>=16": +"@types/react@*", "@types/react@>=16", "@types/react@^18.2.12": version "18.2.42" resolved "https://registry.npmjs.org/@types/react/-/react-18.2.42.tgz" integrity sha512-c1zEr96MjakLYus/wPnuWDo1/zErfdU9rNsIGmE+NV71nx88FG9Ttgo5dqorXTu/LImX2f63WBP986gJkMPNbA== @@ -3362,7 +3787,7 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.0.0 || ^6.0.0 || ^7.0.0", "@typescript-eslint/eslint-plugin@^7.3.1": +"@typescript-eslint/eslint-plugin@^7.3.1": version "7.3.1" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz" integrity sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw== @@ -3379,7 +3804,7 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^7.0.0", "@typescript-eslint/parser@^7.3.1": +"@typescript-eslint/parser@^7.3.1": version "7.3.1" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz" integrity sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw== @@ -3453,6 +3878,19 @@ semver "^7.5.4" ts-api-utils "^1.0.1" +"@typescript-eslint/utils@7.3.1": + version "7.3.1" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz" + integrity sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "7.3.1" + "@typescript-eslint/types" "7.3.1" + "@typescript-eslint/typescript-estree" "7.3.1" + semver "^7.5.4" + "@typescript-eslint/utils@^5.10.0", "@typescript-eslint/utils@^5.45.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" @@ -3467,19 +3905,6 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@7.3.1": - version "7.3.1" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz" - integrity sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "7.3.1" - "@typescript-eslint/types" "7.3.1" - "@typescript-eslint/typescript-estree" "7.3.1" - semver "^7.5.4" - "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz" @@ -3557,7 +3982,7 @@ dependencies: tinyspy "^2.2.0" -"@vitest/ui@^1.0.0", "@vitest/ui@^1.1.0": +"@vitest/ui@^1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@vitest/ui/-/ui-1.1.0.tgz" integrity sha512-7yU1QRFBplz0xJqcgt+agcbrNFdBmLo8UUppdKkFmYx+Ih0+yMYQOyr7kOB+YoggJY/p5ZzXxdbiOz7NBX2y+w== @@ -3604,7 +4029,7 @@ "@yarnpkg/libzip" "^2.3.0" tslib "^1.13.0" -"@yarnpkg/libzip@^2.3.0", "@yarnpkg/libzip@2.3.0": +"@yarnpkg/libzip@2.3.0", "@yarnpkg/libzip@^2.3.0": version "2.3.0" resolved "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.3.0.tgz" integrity sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg== @@ -3647,7 +4072,7 @@ acorn-walk@^8.3.2: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^7.4.1: +acorn@^7.4.1: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -3657,12 +4082,7 @@ acorn@^8.10.0: resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -acorn@^8.11.2: - version "8.11.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz" - integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== - -acorn@^8.9.0: +acorn@^8.11.2, acorn@^8.9.0: version "8.11.2" resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz" integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== @@ -3672,6 +4092,11 @@ address@^1.0.1: resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== +agent-base@5: + version "5.1.1" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz" @@ -3679,11 +4104,6 @@ agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" -agent-base@5: - version "5.1.1" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz" - integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== - aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" @@ -3697,7 +4117,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.12.4, ajv@^6.9.1: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3799,13 +4219,6 @@ aria-hidden@^1.1.1: dependencies: tslib "^2.0.0" -aria-query@^5.0.0, aria-query@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== - dependencies: - dequal "^2.0.3" - aria-query@5.1.3: version "5.1.3" resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" @@ -3813,6 +4226,13 @@ aria-query@5.1.3: dependencies: deep-equal "^2.0.5" +aria-query@^5.0.0, aria-query@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" @@ -4224,7 +4644,7 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" -browserslist@^4.21.9, browserslist@^4.22.1, "browserslist@>= 4.21.0": +browserslist@^4.21.9, browserslist@^4.22.1: version "4.22.2" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz" integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== @@ -4520,16 +4940,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-string@^1.6.0: version "1.9.1" resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" @@ -4635,12 +5055,7 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.5.0: - version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^1.7.0: +convert-source-map@^1.5.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -4787,33 +5202,26 @@ date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" -debug@^2.6.9: +debug@2.6.9, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: - ms "2.0.0" + ms "^2.1.1" decamelize@^1.2.0: version "1.2.0" @@ -5049,7 +5457,7 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -domelementtype@^1.3.1, domelementtype@1: +domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -5328,35 +5736,7 @@ esbuild-register@^3.5.0: dependencies: debug "^4.3.4" -esbuild@^0.18.0, esbuild@>=0.10.0, "esbuild@>=0.12 <1": - version "0.18.20" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz" - integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== - optionalDependencies: - "@esbuild/android-arm" "0.18.20" - "@esbuild/android-arm64" "0.18.20" - "@esbuild/android-x64" "0.18.20" - "@esbuild/darwin-arm64" "0.18.20" - "@esbuild/darwin-x64" "0.18.20" - "@esbuild/freebsd-arm64" "0.18.20" - "@esbuild/freebsd-x64" "0.18.20" - "@esbuild/linux-arm" "0.18.20" - "@esbuild/linux-arm64" "0.18.20" - "@esbuild/linux-ia32" "0.18.20" - "@esbuild/linux-loong64" "0.18.20" - "@esbuild/linux-mips64el" "0.18.20" - "@esbuild/linux-ppc64" "0.18.20" - "@esbuild/linux-riscv64" "0.18.20" - "@esbuild/linux-s390x" "0.18.20" - "@esbuild/linux-x64" "0.18.20" - "@esbuild/netbsd-x64" "0.18.20" - "@esbuild/openbsd-x64" "0.18.20" - "@esbuild/sunos-x64" "0.18.20" - "@esbuild/win32-arm64" "0.18.20" - "@esbuild/win32-ia32" "0.18.20" - "@esbuild/win32-x64" "0.18.20" - -esbuild@^0.18.10: +esbuild@^0.18.0, esbuild@^0.18.10: version "0.18.20" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz" integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== @@ -5512,7 +5892,7 @@ eslint-module-utils@^2.8.0: dependencies: debug "^3.2.7" -eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.29.1: +eslint-plugin-import@^2.29.1: version "2.29.1" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== @@ -5542,7 +5922,7 @@ eslint-plugin-jest@^27.9.0: dependencies: "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.8.0: +eslint-plugin-jsx-a11y@^6.8.0: version "6.8.0" resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz" integrity sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA== @@ -5564,7 +5944,7 @@ eslint-plugin-jsx-a11y@^6.5.1, eslint-plugin-jsx-a11y@^6.8.0: object.entries "^1.1.7" object.fromentries "^2.0.7" -eslint-plugin-react-hooks@^4.3.0, eslint-plugin-react-hooks@^4.6.0: +eslint-plugin-react-hooks@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== @@ -5574,7 +5954,7 @@ eslint-plugin-react-refresh@^0.3.4: resolved "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.3.5.tgz" integrity sha512-61qNIsc7fo9Pp/mju0J83kzvLm0Bsayu7OQSLEoJxLDCBjIIyb87bkzufoOvdDxLkSlMfkF7UxomC4+eztUBSA== -eslint-plugin-react@^7.28.0, eslint-plugin-react@^7.34.1: +eslint-plugin-react@^7.34.1: version "7.34.1" resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz" integrity sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw== @@ -5629,7 +6009,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.32.0 || ^8.2.0", eslint@^8.38.0, eslint@^8.56.0, eslint@>=6, eslint@>=7: +eslint@^8.38.0: version "8.57.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -6120,6 +6500,15 @@ fs-exists-sync@^0.1.0: resolved "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz" integrity sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg== +fs-extra@11.1.1: + version "11.1.1" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^11.1.0: version "11.2.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz" @@ -6139,15 +6528,6 @@ fs-extra@^9.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@11.1.1: - version "11.1.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" @@ -6160,16 +6540,16 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - fsevents@2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -6299,18 +6679,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^10.0.0: - version "10.3.10" - resolved "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz" - integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.3.5" - minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" - -glob@^10.2.2: +glob@^10.0.0, glob@^10.2.2: version "10.3.10" resolved "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz" integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== @@ -6577,7 +6946,7 @@ human-signals@^5.0.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz" integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== -i18next@^23.7.19, "i18next@>= 23.2.3": +i18next@^23.7.19: version "23.7.19" resolved "https://registry.npmjs.org/i18next/-/i18next-23.7.19.tgz" integrity sha512-1aP+YSJl+nLxr42ZJtNhpWpNWYsc6nCbVCf2x4uizIX1BYfcigiRMlb3vOkE1p3+qrI+aD6h5G2Fg+2d2oMIOQ== @@ -6642,7 +7011,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6860,6 +7229,11 @@ is-path-inside@^3.0.2, is-path-inside@^3.0.3: resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-object@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" @@ -6867,11 +7241,6 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-plain-object@5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" @@ -7017,18 +7386,7 @@ istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^5.0.4: - version "5.2.1" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - -istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.2.1" resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -7039,18 +7397,7 @@ istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" -istanbul-lib-instrument@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz" - integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^7.5.4" - -istanbul-lib-instrument@^6.0.1: +istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz" integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== @@ -7138,7 +7485,7 @@ jest-changed-files@^29.7.0: jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^29.3.1, jest-circus@^29.6.4, jest-circus@^29.7.0: +jest-circus@^29.6.4, jest-circus@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz" integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== @@ -7247,7 +7594,7 @@ jest-each@^29.7.0: jest-util "^29.7.0" pretty-format "^29.7.0" -jest-environment-node@^29.3.1, jest-environment-node@^29.6.4, jest-environment-node@^29.7.0: +jest-environment-node@^29.6.4, jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz" integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== @@ -7341,15 +7688,7 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.0.6: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - -jest-mock@^27.3.0: +jest-mock@^27.0.6, jest-mock@^27.3.0: version "27.5.1" resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== @@ -7412,7 +7751,7 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@*, jest-resolve@^29.7.0: +jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== @@ -7427,7 +7766,7 @@ jest-resolve@*, jest-resolve@^29.7.0: resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.3.1, jest-runner@^29.6.4, jest-runner@^29.7.0: +jest-runner@^29.6.4, jest-runner@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz" integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== @@ -7576,7 +7915,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@*, "jest@^27.0.0 || ^28.0.0 || ^29.0.0", jest@^29.3.1, jest@^29.6.4, "jest@>= 28": +jest@^29.6.4: version "29.7.0" resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -7648,7 +7987,7 @@ jscodeshift@^0.15.1: temp "^0.8.4" write-file-atomic "^2.3.0" -jsdom@*, jsdom@^23.0.1: +jsdom@^23.0.1: version "23.0.1" resolved "https://registry.npmjs.org/jsdom/-/jsdom-23.0.1.tgz" integrity sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ== @@ -7803,7 +8142,7 @@ ky-universal@^0.11.0: abort-controller "^3.0.0" node-fetch "^3.2.10" -ky@^0.33.3, ky@>=0.31.4: +ky@^0.33.3: version "0.33.3" resolved "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz" integrity sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== @@ -8082,7 +8421,7 @@ microseconds@0.2.0: resolved "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz" integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== -"mime-db@>= 1.43.0 < 2", mime-db@1.52.0: +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== @@ -8094,16 +8433,16 @@ mime-types@^2.1.12, mime-types@^2.1.25, mime-types@~2.1.24, mime-types@~2.1.34: dependencies: mime-db "1.52.0" -mime@^2.0.3: - version "2.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^2.0.3: + version "2.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" @@ -8119,7 +8458,7 @@ min-indent@^1.0.0, min-indent@^1.0.1: resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@*: +minimatch@*, minimatch@9.0.3, minimatch@^9.0.1: version "9.0.3" resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== @@ -8140,20 +8479,6 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.1: - version "9.0.3" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" @@ -8166,16 +8491,16 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.4" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" - integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== - minipass@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.4" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" @@ -8221,16 +8546,16 @@ mrmime@^2.0.0: resolved "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz" integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== -ms@^2.1.1, ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -8550,14 +8875,7 @@ os-tmpdir@~1.0.2: resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -8810,7 +9128,7 @@ pkg-types@^1.0.3: mlly "^1.2.0" pathe "^1.1.0" -playwright-core@>=1.2.0, playwright-core@1.40.1: +playwright-core@1.40.1, playwright-core@>=1.2.0: version "1.40.1" resolved "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz" integrity sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ== @@ -9001,13 +9319,6 @@ pure-rand@^6.0.0: resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz" integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== -qs@^6.10.0: - version "6.11.2" - resolved "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz" - integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== - dependencies: - side-channel "^1.0.4" - qs@6.11.0: version "6.11.0" resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" @@ -9015,6 +9326,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@^6.10.0: + version "6.11.2" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -9098,7 +9416,7 @@ react-docgen@^7.0.0: resolve "^1.22.1" strip-indent "^4.0.0" -"react-dom@^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^18.0.0, react-dom@^18.2.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, "react-dom@16.8.0 - 18": +react-dom@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== @@ -9120,7 +9438,7 @@ react-gtm-module@^2.0.11: resolved "https://registry.npmjs.org/react-gtm-module/-/react-gtm-module-2.0.11.tgz" integrity sha512-8gyj4TTxeP7eEyc2QKawEuQoAZdjKvMY4pgWfycGmqGByhs17fR+zEBs0JUDq4US/l+vbTl+6zvUIx27iDo/Vw== -react-hook-form@^7.0.0, react-hook-form@^7.47.0: +react-hook-form@^7.47.0: version "7.48.2" resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.48.2.tgz" integrity sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A== @@ -9133,12 +9451,12 @@ react-i18next@^14.0.1: "@babel/runtime" "^7.22.5" html-parse-stringify "^3.0.1" -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@18.1.0: + version "18.1.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz" + integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== -react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -9153,11 +9471,6 @@ react-is@^18.0.0, react-is@^18.2.0: resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-is@18.1.0: - version "18.1.0" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz" - integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== - react-json-tree@^0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.18.0.tgz" @@ -9234,7 +9547,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -"react@^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0", "react@^16.3.0 || ^17.0.1 || ^18.0.0", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.2.0, "react@>= 0.14.0", "react@>= 16.8.0", react@>=16, react@>=16.6.0, react@>=16.8, react@>=16.8.0, "react@15.x || 16.x || 17.x || 18.x", "react@16.8.0 - 18": +react@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== @@ -9273,16 +9586,7 @@ readable-stream@^2.0.0, readable-stream@^2.2.2, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1: - version "3.6.2" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^3.4.0: +readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -9496,27 +9800,20 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.6.1: - version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -rimraf@^2.6.3: +rimraf@^2.6.1, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2, rimraf@3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rimraf@~2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" @@ -9524,7 +9821,7 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0, "rollup@^2.25.0 || ^3.3.0", rollup@^3.27.1: +"rollup@^2.25.0 || ^3.3.0", rollup@^3.27.1: version "3.29.4" resolved "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz" integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== @@ -9580,17 +9877,12 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@5.1.2: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@5.2.1: +safe-buffer@5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -9632,22 +9924,12 @@ schema-utils@^2.7.0: ajv "^6.12.4" ajv-keywords "^3.5.2" -semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.6.0: version "5.7.2" resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.1: +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -9659,11 +9941,6 @@ semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: dependencies: lru-cache "^6.0.0" -"semver@2 || 3 || 4 || 5": - version "5.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - send@0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" @@ -9769,12 +10046,7 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -signal-exit@^4.1.0: +signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== @@ -9827,14 +10099,6 @@ source-map-js@^1.0.2: resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-support@^0.5.16: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" @@ -9843,6 +10107,14 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@^0.5.16: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" @@ -9975,20 +10247,6 @@ stream-shift@^1.0.0: resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -10005,16 +10263,7 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10078,14 +10327,21 @@ string.prototype.trimstart@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: - ansi-regex "^5.0.1" + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10387,12 +10643,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.13.0: - version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^1.8.1: +tslib@^1.13.0, tslib@^1.8.1: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -10421,7 +10672,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.8, type-detect@4.0.8: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -10446,12 +10697,7 @@ type-fest@^0.6.0: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.0: - version "0.8.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-fest@^0.8.1: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -10530,7 +10776,7 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@*, typescript@^5.0.2, "typescript@>= 4.3.x", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@>=4.2.0: +typescript@^5.0.2: version "5.3.2" resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz" integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== @@ -10637,7 +10883,7 @@ unload@2.2.0: "@babel/runtime" "^7.6.2" detect-node "^2.0.4" -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -10776,7 +11022,7 @@ vite-plugin-istanbul@^3.0.1: picocolors "^1.0.0" test-exclude "^6.0.0" -"vite@^3.0.0 || ^4.0.0 || ^5.0.0", vite@^4, vite@^4.1.0-beta.0, "vite@^4.2.0 || ^5.0.0", vite@^4.3.9: +vite@^4.3.9: version "4.5.1" resolved "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz" integrity sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA== @@ -10798,7 +11044,7 @@ vite@^5.0.0: optionalDependencies: fsevents "~2.3.3" -vitest@^1.0.0, vitest@^1.1.0, "vitest@>= 0.32": +vitest@^1.1.0: version "1.2.2" resolved "https://registry.npmjs.org/vitest/-/vitest-1.2.2.tgz" integrity sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw== @@ -10879,7 +11125,7 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-streams-polyfill@^3.0.3, web-streams-polyfill@>=3.2.1: +web-streams-polyfill@^3.0.3: version "3.3.3" resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz" integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== @@ -11014,7 +11260,7 @@ wordwrap@^1.0.0: resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11032,15 +11278,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" From c62334f1e38f884cd124ff760c3e3a9cd272070c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Mon, 25 Mar 2024 15:35:25 +0100 Subject: [PATCH 16/21] lint fixes --- .../StorageInformation.tsx | 2 +- .../DRepStorageInformation.tsx | 2 +- .../src/hooks/forms/useRegisterAsdRepForm.tsx | 25 ++++++++++--------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx index 200b59e22..015ae75d1 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx @@ -37,7 +37,7 @@ export const StorageInformation = ({ setStep }: StorageInformationProps) => { const openGuideAboutStoringInformation = () => openInNewTab("https://sancho.network/"); - const isActionButtonDisabled = !watch("storingURL") || !!errors["storingURL"]; + const isActionButtonDisabled = !watch("storingURL") || !!errors.storingURL; const onClickBack = () => setStep(5); diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx index 7768f95ce..7fbed215e 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx @@ -39,7 +39,7 @@ export const DRepStorageInformation = ({ const openGuideAboutStoringInformation = () => openInNewTab("https://sancho.network/"); - const isActionButtonDisabled = !watch("storingURL") || !!errors["storingURL"]; + const isActionButtonDisabled = !watch("storingURL") || !!errors.storingURL; const onClickBack = () => setStep(3); diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index 59ad47096..d407b9c38 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -101,9 +101,9 @@ export const useRegisterAsdRepForm = ( const jsonld = await generateJsonld(body, DREP_CONTEXT, CIP_QQQ); const canonizedJson = await canonizeJSON(jsonld); - const hash = blake2bHex(canonizedJson, undefined, 32); + const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32); - setHash(hash); + setHash(canonizedJsonHash); setJson(jsonld); return jsonld; @@ -116,11 +116,12 @@ export const useRegisterAsdRepForm = ( }; const validateHash = useCallback( - async (storingUrl: string, hash: string | null) => { + 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) @@ -141,7 +142,7 @@ export const useRegisterAsdRepForm = ( throw error; } }, - [backToForm], + [backToForm, hash], ); const createRegistrationCert = useCallback( @@ -150,14 +151,14 @@ export const useRegisterAsdRepForm = ( const url = data.storingURL; try { - let certBuilder; if (voter?.isRegisteredAsSoleVoter) { - certBuilder = await buildDRepUpdateCert(url, hash); - } else { - certBuilder = await buildDRepRegCert(url, hash); + return await buildDRepUpdateCert(url, hash); } - return certBuilder; + + return await buildDRepRegCert(url, hash); + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { + // eslint-disable-next-line no-console console.error(error); throw error; } @@ -189,7 +190,7 @@ export const useRegisterAsdRepForm = ( try { setIsLoading(true); - await validateHash(data.storingURL, hash); + await validateHash(data.storingURL); const registerAsDRepCert = await createRegistrationCert(data); await buildSignSubmitConwayCertTx({ certBuilder: registerAsDRepCert, @@ -197,14 +198,14 @@ export const useRegisterAsdRepForm = ( }); showSuccessModal(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { captureException(error); - console.error(error); } finally { setIsLoading(false); } }, - [buildSignSubmitConwayCertTx, createRegistrationCert, hash], + [buildSignSubmitConwayCertTx, createRegistrationCert, hash, validateHash], ); return { From 0798723301bb3465d4417d222a6a0d17b77b64a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Tue, 26 Mar 2024 08:01:32 +0100 Subject: [PATCH 17/21] add to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4001e6244..6a4ce807a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ changes. ## [Unreleased] +- DRep metadata builder [Issue 497](https://github.com/IntersectMBO/govtool/issues/497) - Update Cardano Serialization Lib to 12.0.0-alpha.19 [Issue 521](https://github.com/IntersectMBO/govtool/issues/521) - Add generate jsonld function [Issue 451](https://github.com/IntersectMBO/govtool/issues/451) - Create GA review subbmision page [Issue 362](https://github.com/IntersectMBO/govtool/issues/362) From fa6c7771d5d0c0cd41a925e11eefdd1a12348c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Tue, 26 Mar 2024 12:50:04 +0100 Subject: [PATCH 18/21] eslint fix --- govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index d407b9c38..12d5422d6 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -158,7 +158,6 @@ export const useRegisterAsdRepForm = ( return await buildDRepRegCert(url, hash); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - // eslint-disable-next-line no-console console.error(error); throw error; } From 354b5392c8ac41e19d7c5d889ebab83817c82768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Tue, 26 Mar 2024 12:55:29 +0100 Subject: [PATCH 19/21] add errors into render linds dependencies --- .../organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx index bea469874..88d11a3f3 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RegisterAsDRepForm.tsx @@ -67,7 +67,7 @@ export const RegisterAsDRepForm = ({ rules={Rules.LINK} /> )), - [links], + [errors, links], ); return ( From 16aae05f1416c12201b2acdc69d580645f9ccbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Sworze=C5=84?= Date: Mon, 25 Mar 2024 15:57:21 +0100 Subject: [PATCH 20/21] [#550] new retire as drep flow --- govtool/frontend/src/App.tsx | 26 ++-- .../components/organisms/DashboardCards.tsx | 74 ++---------- .../WhatRetirementMeans.tsx | 112 ++++++++++++++++++ .../organisms/RegisterAsDRepSteps/index.ts | 1 + .../organisms/RetireAsSoleVoterBox.tsx | 4 +- govtool/frontend/src/consts/paths.ts | 7 +- govtool/frontend/src/i18n/locales/en.ts | 8 ++ govtool/frontend/src/pages/RetireAsDrep.tsx | 44 +++++++ govtool/frontend/src/pages/index.ts | 3 + 9 files changed, 196 insertions(+), 83 deletions(-) create mode 100644 govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx create mode 100644 govtool/frontend/src/pages/RetireAsDrep.tsx diff --git a/govtool/frontend/src/App.tsx b/govtool/frontend/src/App.tsx index 7db190366..9e7b9300e 100644 --- a/govtool/frontend/src/App.tsx +++ b/govtool/frontend/src/App.tsx @@ -4,6 +4,7 @@ import { Route, Routes, useNavigate } from "react-router-dom"; import { Modal, ScrollToTop } from "@atoms"; import { PATHS } from "@consts"; import { useCardano, useModal } from "@context"; +import { useWalletConnectionListener } from "@hooks"; import { DashboardCards, DashboardGovernanceActions, @@ -11,28 +12,28 @@ import { } from "@organisms"; import { ChooseStakeKey, + CreateGovernanceAction, Dashboard, - ErrorPage, - Home, - GovernanceActions, + DashboardGovernanceActionsCategory, DelegateTodRep, - RegisterAsdRep, + ErrorPage, GovernanceActionDetails, - UpdatedRepMetadata, + GovernanceActions, GovernanceActionsCategory, - DashboardGovernanceActionsCategory, + Home, + RegisterAsdRep, + RegisterAsSoleVoter, + RetireAsDrep, RetireAsSoleVoter, + UpdatedRepMetadata, } from "@pages"; +import { SetupInterceptors } from "@services"; import { callAll, getItemFromLocalStorage, WALLET_LS_KEY, removeItemFromLocalStorage, } from "@utils"; -import { SetupInterceptors } from "./services"; -import { useWalletConnectionListener } from "./hooks"; -import { RegisterAsSoleVoter } from "./pages/RegisterAsSoleVoter"; -import { CreateGovernanceAction } from "./pages/CreateGovernanceAction"; export default () => { const { enable } = useCardano(); @@ -110,6 +111,7 @@ export default () => { /> } /> } /> + } /> } @@ -126,8 +128,8 @@ export default () => { handleClose={ !modals[modal.type].preventDismiss ? callAll(modals[modal.type]?.onClose, () => - openModal({ type: "none", state: null }), - ) + openModal({ type: "none", state: null }), + ) : undefined } > diff --git a/govtool/frontend/src/components/organisms/DashboardCards.tsx b/govtool/frontend/src/components/organisms/DashboardCards.tsx index fd684fd6b..98a99630d 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards.tsx @@ -1,10 +1,10 @@ -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { Box, CircularProgress } from "@mui/material"; import { Trans } from "react-i18next"; import { IMAGES, PATHS } from "@consts"; -import { useCardano, useModal } from "@context"; +import { useCardano } from "@context"; import { useGetAdaHolderVotingPowerQuery, useScreenDimension, @@ -17,8 +17,6 @@ import { correctAdaFormat, formHexToBech32, openInNewTab } from "@utils"; export const DashboardCards = () => { const { - buildDRepRetirementCert, - buildSignSubmitConwayCertTx, dRepID, dRepIDBech32, isPendingTransaction, @@ -28,67 +26,10 @@ export const DashboardCards = () => { const navigate = useNavigate(); const { currentDelegation } = useGetAdaHolderCurrentDelegationQuery(stakeKey); const { screenWidth } = useScreenDimension(); - const { openModal } = useModal(); - const [isRetirementLoading, setIsRetirementLoading] = - useState(false); const { votingPower } = useGetAdaHolderVotingPowerQuery(stakeKey); const { t } = useTranslation(); const { voter } = useGetVoterInfo(); - const retireAsDrep = useCallback(async () => { - try { - setIsRetirementLoading(true); - const isPendingTx = isPendingTransaction(); - - if (isPendingTx) return; - if (!voter?.deposit) throw new Error("Can not get deposit"); - - const certBuilder = await buildDRepRetirementCert( - voter.deposit.toString(), - ); - const result = await buildSignSubmitConwayCertTx({ - certBuilder, - type: "retireAsDrep", - voterDeposit: voter.deposit.toString(), - }); - if (result) { - openModal({ - type: "statusModal", - state: { - status: "success", - title: t("modals.retirement.title"), - message: t("modals.retirement.message"), - link: `https://adanordic.com/latest_transactions`, - buttonText: t("modals.common.goToDashboard"), - dataTestId: "retirement-transaction-submitted-modal", - }, - }); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - const errorMessage = error.info ? error.info : error; - - openModal({ - type: "statusModal", - state: { - status: "warning", - message: errorMessage, - buttonText: t("modals.common.goToDashboard"), - title: t("modals.common.oops"), - dataTestId: "retirement-transaction-error-modal", - }, - }); - } finally { - setIsRetirementLoading(false); - } - }, [ - buildDRepRetirementCert, - buildSignSubmitConwayCertTx, - isPendingTransaction, - openModal, - voter?.deposit, - ]); - const delegationDescription = useMemo(() => { const correctAdaRepresentation = correctAdaFormat(votingPower); if (currentDelegation === dRepID) { @@ -416,12 +357,13 @@ export const DashboardCards = () => { : "register-learn-more-button" } description={registrationCardDescription} - firstButtonAction={ - voter?.isRegisteredAsDRep - ? retireAsDrep - : () => navigateTo(PATHS.registerAsdRep) + firstButtonAction={() => + navigateTo( + voter?.isRegisteredAsDRep + ? PATHS.retireAsDrep + : PATHS.registerAsdRep, + ) } - firstButtonIsLoading={isRetirementLoading} firstButtonLabel={ pendingTransaction.registerAsDrep || pendingTransaction.retireAsDrep ? "" diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx new file mode 100644 index 000000000..23f0fe766 --- /dev/null +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx @@ -0,0 +1,112 @@ +import { useCallback, useState } from "react"; + +import { Typography } from "@atoms"; +import { useCardano, useModal } from "@context"; +import { useGetVoterInfo, useScreenDimension, useTranslation } from "@hooks"; + +import { BgCard } from ".."; + +export const WhatRetirementMeans = ({ + onClickCancel, +}: { + onClickCancel: () => void; +}) => { + const { + isPendingTransaction, + buildDRepRetirementCert, + buildSignSubmitConwayCertTx, + } = useCardano(); + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + const { closeModal, openModal } = useModal(); + const [isRetirementLoading, setIsRetirementLoading] = + useState(false); + const { voter } = useGetVoterInfo(); + + const onSubmit = () => { + onClickCancel(); + closeModal(); + }; + + const retireAsDrep = useCallback(async () => { + try { + setIsRetirementLoading(true); + const isPendingTx = isPendingTransaction(); + if (isPendingTx) return; + if (!voter?.deposit) throw new Error(t("errors.appCannotGetDeposit")); + + const certBuilder = await buildDRepRetirementCert( + voter?.deposit.toString(), + ); + const result = await buildSignSubmitConwayCertTx({ + certBuilder, + type: "retireAsDrep", + voterDeposit: voter?.deposit.toString(), + }); + if (result) { + openModal({ + type: "statusModal", + state: { + buttonText: t("modals.common.goToDashboard"), + dataTestId: "retirement-transaction-submitted-modal", + link: "https://adanordic.com/latest_transactions", + message: t("modals.retirement.message"), + onSubmit, + status: "success", + title: t("modals.retirement.title"), + }, + }); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + const errorMessage = error.info ? error.info : error; + + openModal({ + type: "statusModal", + state: { + buttonText: t("modals.common.goToDashboard"), + dataTestId: "retirement-transaction-error-modal", + message: errorMessage, + onSubmit, + status: "warning", + title: t("modals.common.oops"), + }, + }); + } finally { + setIsRetirementLoading(false); + } + }, [ + buildDRepRetirementCert, + buildSignSubmitConwayCertTx, + isPendingTransaction, + openModal, + voter?.deposit, + ]); + + return ( + + + {t("retirement.whatRetirementMeansTitle")} + + + {t("retirement.whatRetirementMeansDescription")} + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts index 00566b6d4..29b5cb726 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/index.ts @@ -2,3 +2,4 @@ export * from "./DRepStorageInformation"; export * from "./DRepStoreDataInfo"; export * from "./RegisterAsDRepForm"; export * from "./RolesAndResponsibilities"; +export * from "./WhatRetirementMeans"; diff --git a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx index f691d3d72..081e05d40 100644 --- a/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx +++ b/govtool/frontend/src/components/organisms/RetireAsSoleVoterBox.tsx @@ -27,10 +27,10 @@ export const RetireAsSoleVoterBox = () => { const isPendingTx = isPendingTransaction(); if (isPendingTx) return; if (!voter?.deposit) { - throw new Error("Can not fetch deposit"); + throw new Error(t("errors.appCannotGetDeposit")); } const certBuilder = await buildDRepRetirementCert( - voter?.deposit?.toString() + voter?.deposit?.toString(), ); const result = await buildSignSubmitConwayCertTx({ certBuilder, diff --git a/govtool/frontend/src/consts/paths.ts b/govtool/frontend/src/consts/paths.ts index 679962a36..1e2eddda5 100644 --- a/govtool/frontend/src/consts/paths.ts +++ b/govtool/frontend/src/consts/paths.ts @@ -1,22 +1,23 @@ export const PATHS = { createGovernanceAction: "/create_governance_action", + dashboard: "/dashboard", + dashboardGovernanceActions: "/connected/governance_actions", dashboardGovernanceActionsAction: "/connected/governance_actions/:proposalId", dashboardGovernanceActionsCategory: "/connected/governance_actions/category/:category", - dashboardGovernanceActions: "/connected/governance_actions", - dashboard: "/dashboard", delegateTodRep: "/delegate", error: "/error", faqs: "/faqs", governanceActions: "/governance_actions", governanceActionsAction: "/governance_actions/:proposalId", + governanceActionsCategory: "/governance_actions/category/:category", governanceActionsCategoryAction: "/governance_actions/category/:category/:proposalId", - governanceActionsCategory: "/governance_actions/category/:category", guides: "/guides", home: "/", registerAsdRep: "/register", registerAsSoleVoter: "/register_sole_voter", + retireAsDrep: "/retire_drep", retireAsSoleVoter: "/retire_sole_voter", stakeKeys: "/stake_keys", updateMetadata: "/update_metadata", diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 5f451f5f5..73e9c4217 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -243,6 +243,7 @@ export const en = { }, errors: { appCannotCreateTransaction: "Application can not create transaction.", + appCannotGetDeposit: "Can not fetch deposit", appCannotGetUtxos: "Application can not get utxos", appCannotGetVkeys: "Application can not get vkey", checkIsWalletConnected: "Check if the wallet is connected.", @@ -516,6 +517,13 @@ export const en = { }, }, }, + retirement: { + continue: "Continue to Retirement", + retireAsDrep: "Retire as a Drep", + whatRetirementMeansTitle: "What Retirement Means", + whatRetirementMeansDescription: + "By retiring you are giving up your voting rights. Voting Power that is delegated to you will remain in place.\n\nADA Holders that have delegated to be able to see that you are retired in the DRep directory. They will be able to re-delegate their Voting Power to another DRep.\n\nYou can still participate in Governance by proposing Governance Actions, by delegating your personal Voting Power to another DRep, or by coming out of retirement, and assuming your previous role as a DRep.\n\nIf you come out of retirement, your DRep ID will be the same as it was before retirement, and your Voting Power will consist of your own ADA balance and what delegated power that remains associated\nto your DRep ID.", + }, slider: { showAll: "Show All", viewAll: "View all", diff --git a/govtool/frontend/src/pages/RetireAsDrep.tsx b/govtool/frontend/src/pages/RetireAsDrep.tsx new file mode 100644 index 000000000..f0349b0f1 --- /dev/null +++ b/govtool/frontend/src/pages/RetireAsDrep.tsx @@ -0,0 +1,44 @@ +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { Box } from "@mui/material"; + +import { Background } from "@atoms"; +import { PATHS } from "@consts"; +import { DashboardTopNav, WhatRetirementMeans } from "@organisms"; +import { useScreenDimension, useTranslation } from "@hooks"; +import { LinkWithIcon } from "@molecules"; +import { checkIsWalletConnected } from "@utils"; + +export const RetireAsDrep = () => { + const navigate = useNavigate(); + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + + useEffect(() => { + if (checkIsWalletConnected()) { + navigate(PATHS.home); + } + }, []); + + const onClickBackToDashboard = () => navigate(PATHS.dashboard); + + return ( + + + + + + + + ); +}; diff --git a/govtool/frontend/src/pages/index.ts b/govtool/frontend/src/pages/index.ts index c2f422300..3739fa117 100644 --- a/govtool/frontend/src/pages/index.ts +++ b/govtool/frontend/src/pages/index.ts @@ -1,4 +1,5 @@ export * from "./ChooseStakeKey"; +export * from "./CreateGovernanceAction"; export * from "./Dashboard"; export * from "./DashboardGovernanceActionsCategory"; export * from "./DelegateTodRep"; @@ -8,5 +9,7 @@ export * from "./GovernanceActions"; export * from "./GovernanceActionsCategory"; export * from "./Home"; export * from "./RegisterAsdRep"; +export * from "./RegisterAsSoleVoter"; +export * from "./RetireAsDrep"; export * from "./RetireAsSoleVoter"; export * from "./UpdatedRepMetadata"; From aa63ed729b3f3983c993b061bf1d8e17e784ccce Mon Sep 17 00:00:00 2001 From: jankun4 Date: Tue, 26 Mar 2024 14:54:13 +0100 Subject: [PATCH 21/21] update backend config --- govtool/backend/Dockerfile.base | 2 +- govtool/backend/vva-be.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/govtool/backend/Dockerfile.base b/govtool/backend/Dockerfile.base index e9b93f093..8f4e4c356 100644 --- a/govtool/backend/Dockerfile.base +++ b/govtool/backend/Dockerfile.base @@ -4,7 +4,7 @@ # is a common practice in Haskell projects, as it can significantly reduce the # time it takes to build the project. -FROM haskell:9.2-buster +FROM haskell:9.2.7-buster WORKDIR /src COPY . . RUN cabal update && cabal configure && cabal install --only-dependencies && rm -rf /src/* diff --git a/govtool/backend/vva-be.cabal b/govtool/backend/vva-be.cabal index fb1643b8e..2df658d4e 100644 --- a/govtool/backend/vva-be.cabal +++ b/govtool/backend/vva-be.cabal @@ -66,7 +66,7 @@ executable vva-be , bytestring , http-client , http-client-tls - , raven-haskell + , raven-haskell >= 0.1.4.1 hs-source-dirs: app default-language: Haskell2010