diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c6f7b3be8f..49143daf4a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -20,4 +20,4 @@ jobs: release-type: node bump-minor-pre-major: true bump-patch-for-minor-pre-major: true - release-as: 1.9.0 + release-as: 1.11.0 diff --git a/package.json b/package.json index 55b6f4c6c7..e615f58edf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "explorer", - "version": "1.9.1", + "version": "1.11.0", "private": true, "dependencies": { "@cardano-foundation/cardano-connect-with-wallet": "^0.2.9", diff --git a/playwright/api/call-blockfrost/blockfrost.api.ts b/playwright/api/call-blockfrost/blockfrost.api.ts index 8073c42c17..2852043db2 100644 --- a/playwright/api/call-blockfrost/blockfrost.api.ts +++ b/playwright/api/call-blockfrost/blockfrost.api.ts @@ -230,50 +230,6 @@ export function blockfrostApi(request: APIRequestContext) { const getAssetNameByAssetToken = async (assetToken: string | null) => { return BaseApi.getData(request, Endpoint.BlockFrost.Token.Base + `/${assetToken}`); }; - const getTxCountTopADAHolder = async (address: string) => { - return BaseApi.getData( - request, - Endpoint.BlockFrost.TopADAHolder.TxCount.replace(":address", address), - {}, - { - Accept: "*/*", - "Accept-Encoding": "gzip, deflate, br", - "Content-Type": "application/json", - project_id: BLOCKFROST_TOKEN - }, - false - ); - }; - - const getDataAmountStaked = async (stakedAddress: string) => { - return BaseApi.getData( - request, - Endpoint.BlockFrost.TopADAHolder.dataTabAmountStaked.replace(":stake_address", stakedAddress), - {}, - { - Accept: "*/*", - "Accept-Encoding": "gzip, deflate, br", - "Content-Type": "application/json", - project_id: BLOCKFROST_TOKEN - }, - false - ); - }; - - const getPoolIDAcount = async (stakedAddress: string) => { - return BaseApi.getData( - request, - Endpoint.BlockFrost.TopADAHolder.poolIDAcount.replace(":stake_address", stakedAddress), - {}, - { - Accept: "*/*", - "Accept-Encoding": "gzip, deflate, br", - "Content-Type": "application/json", - project_id: BLOCKFROST_TOKEN - }, - false - ); - }; return { getLastEpochData, @@ -291,9 +247,6 @@ export function blockfrostApi(request: APIRequestContext) { getStakePools, getMetadataPools, getHistoryPools, - getAssetNameByAssetToken, - getTxCountTopADAHolder, - getDataAmountStaked, - getPoolIDAcount + getAssetNameByAssetToken }; } diff --git a/playwright/api/dtos/topADAHolders.dto.ts b/playwright/api/dtos/topADAHolders.dto.ts deleted file mode 100644 index 6fd80c9465..0000000000 --- a/playwright/api/dtos/topADAHolders.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface DataTabAddADABalance { - dataTabAddADABalance: { address: string; balance: string }; -} - -export interface DataTabAmountStake { - dataTabAmountStake: { stakeAddress: string; stakeAmount: string }; -} diff --git a/playwright/helpers/endpoints.ts b/playwright/helpers/endpoints.ts index d133a2f8b8..d7a2c81c45 100644 --- a/playwright/helpers/endpoints.ts +++ b/playwright/helpers/endpoints.ts @@ -64,20 +64,6 @@ export class BlockFrost { return `${BlockFrost.BASE_MAIN_NET_URL}/assets`; } }; - static TopADAHolder = class { - public static get Base() { - return `${BlockFrost.BASE_MAIN_NET_URL}/addresses`; - } - public static get TxCount() { - return `${BlockFrost.TopADAHolder.Base}/:address/total`; - } - public static get dataTabAmountStaked() { - return `${BlockFrost.BASE_MAIN_NET_URL}/accounts/:stake_address`; - } - public static get poolIDAcount() { - return `${BlockFrost.BASE_MAIN_NET_URL}/accounts/:stake_address/delegations`; - } - }; } export class Koios { diff --git a/playwright/pages/block-detail.page.ts b/playwright/pages/block-detail.page.ts index 1538a78e41..652f1928ba 100644 --- a/playwright/pages/block-detail.page.ts +++ b/playwright/pages/block-detail.page.ts @@ -23,9 +23,7 @@ export function blockDetailPage(page: Page) { //Block detail - Transactions Table const blocksDetailTrxTableTxhash = page.getByTestId("block.detail.trxTable.txhash"); const blocksDetailTrxTableCreatedAt = page.getByTestId("block.detail.trxTable.createdAt"); - const blocksDetailTrxTableAddress = page.getByTestId("block.detail.trxTable.address"); const blocksDetailTrxTableFees = page.getByTestId("block.detail.trxTable.fees"); - const blocksDetailTrxTableoutput = page.getByTestId("block.detail.trxTable.output"); const blocksDetailTrxTableValueTrx = page.getByTestId("block.detail.trxTable.value.txhash#0"); const blocksDetailTrxTableValueInputAddress = page.getByTestId("block.detail.trxTable.value.inputAddress#0"); @@ -106,9 +104,7 @@ export function blockDetailPage(page: Page) { const checkTransactionsTable = async () => { await expect(blocksDetailTrxTableTxhash, "Check title on transaction table").toHaveText("Tx hash"); await expect(blocksDetailTrxTableCreatedAt, "Check title on transaction table").toHaveText("Created At"); - await expect(blocksDetailTrxTableAddress, "Check title on transaction table").toHaveText("Addresses"); await expect(blocksDetailTrxTableFees, "Check title on transaction table").toHaveText("Fees"); - await expect(blocksDetailTrxTableoutput, "Check title on transaction table").toHaveText("Output in ADA"); }; const checkBlockDetail = async ({ blockNo }: { blockNo: string | null }) => { diff --git a/playwright/pages/blocks-dashboard.page.ts b/playwright/pages/blocks-dashboard.page.ts index 9ac86eb5bf..e1a1081dbb 100644 --- a/playwright/pages/blocks-dashboard.page.ts +++ b/playwright/pages/blocks-dashboard.page.ts @@ -1,7 +1,4 @@ import { expect, Page } from "@playwright/test"; -import moment from "moment"; - -import { BlockInformationDto } from "playwright/api/dtos/blockInformation.dto"; export function blocksDashboard(page: Page) { const blocksTable = page.getByTestId("blocks-card"); @@ -20,23 +17,9 @@ export function blocksDashboard(page: Page) { const blocksTableTitleSlot = page.getByTestId("blocks.table.title.slot"); const blocksTableTitleAbsSlot = page.getByTestId("blocks.table.title.absoluteSlot"); const blocksTableTitleCreateAt = page.getByTestId("blocks.table.title.createAt"); - const blocksTableTitleTransactionsCount = page.getByTestId("blocks.table.title.transactions"); - const blocksTableTitleFee = page.getByTestId("blocks.table.title.fee"); const blocksTableTitleOutput = page.getByTestId("blocks.table.title.output"); const blocksTableValueSlot = page.getByTestId("blocks.table.value.slot#2"); - //Block widget - const blocksWidgetViewDetailButton = page.getByTestId("block.detailViewEpoch.viewDetail"); - const blocksWidgetEpochNo = page.getByTestId("block.widget.epochNo"); - const blocksWidgetSlotNo = page.getByTestId("block.widget.slotNo"); - const blocksWidgetAbsSlotNo = page.getByTestId("block.widget.absSlotNo"); - const blocksWidgetCreatedAt = page.getByTestId("block.widget.createdAt"); - const blocksWidgetConfirmation = page.getByTestId("block.widget.confirmation"); - const blocksWidgetFee = page.getByTestId("block.widget.fees"); - const blocksWidgetOutput = page.getByTestId("block.widget.output"); - const blocksWidgetBlockHash = page.getByTestId("block.widget.blockHash"); - const blocksWidgetTrxTab = page.getByTestId("block.widget.trxTab"); - const goToDashboard = async () => { await page.goto("/"); }; @@ -53,14 +36,6 @@ export function blocksDashboard(page: Page) { await blocksTableValueSlot.click(); }; - const goToBlockDetailFromWidget = async () => { - await blocksWidgetViewDetailButton.click(); - }; - - const goToBlockDetailFromWidgetByBlockHash = async () => { - await blocksWidgetBlockHash.click(); - }; - const goToBlockDetailFromBlockNo = async () => { await blocksTableValueBlock.click(); }; @@ -73,10 +48,6 @@ export function blocksDashboard(page: Page) { await blocksTableValueEpoch.click(); }; - const goToBlockDetailFromWidgetByTrxTab = async () => { - await blocksWidgetTrxTab.click(); - }; - const openLastBlockDetails = async () => { await expect(blocksTable).toBeVisible(); await firstBlocksID.click(); @@ -93,46 +64,9 @@ export function blocksDashboard(page: Page) { await expect(blocksTableTitleSlot, "Check title on blocks table").toHaveText("Slot"); await expect(blocksTableTitleAbsSlot, "Check title on blocks table").toHaveText("Absolute Slot"); await expect(blocksTableTitleCreateAt, "Check title on blocks table").toHaveText("Created At"); - await expect(blocksTableTitleTransactionsCount, "Check title on blocks table").toHaveText("Transactions"); - await expect(blocksTableTitleFee, "Check title on blocks table").toHaveText("Fees"); await expect(blocksTableTitleOutput, "Check title on finished epoch table").toHaveText("Output"); }; - const checkBlockWidget = async ({ blockFrostBlock }: { blockFrostBlock: BlockInformationDto }) => { - expect(+((await blocksWidgetEpochNo.textContent()) || 0), "Check epoch on block widget").toEqual( - blockFrostBlock.epoch - ); - expect( - +((await blocksWidgetSlotNo.textContent())?.split("/")[0] || 0), - "Slot no in block widget to equal total fee in block on Blockfrost " - ).toEqual(+(blockFrostBlock?.epoch_slot || 0)); - - expect( - +((await blocksWidgetAbsSlotNo.textContent()) || 0), - "Absolute slot in block widget to equal total fee in block on Blockfrost " - ).toEqual(+(blockFrostBlock?.slot || 0)); - - expect( - moment((await blocksWidgetCreatedAt.textContent())?.replace(",", "")).unix(), - "Created time to equal create time epoch Blockfrost" - ).toEqual(blockFrostBlock?.time); - - expect( - +((await blocksWidgetConfirmation.textContent()) || 0), - "Confirmation in block widget to equal total fee in block on Blockfrost " - ).toBeGreaterThanOrEqual(+(blockFrostBlock?.confirmations || 0)); - - expect( - +((await blocksWidgetFee.textContent())?.replaceAll(",", "") || 0) * 10 ** 6, - "Total fees in block widget to equal total fee in block on Blockfrost " - ).toEqual(+(blockFrostBlock?.fees || 0)); - - expect( - +((await blocksWidgetOutput.textContent())?.replaceAll(",", "") || 0) * 10 ** 6, - "Total output in block widget to equal total fee in block on Blockfrost " - ).toEqual(+(blockFrostBlock?.output || 0)); - }; - return { openLastBlockDetails, goToDashboard, @@ -141,12 +75,8 @@ export function blocksDashboard(page: Page) { searchBarOnBlocks, checkBlocksTable, openLastestBlockWidget, - goToBlockDetailFromWidget, goToBlockDetailFromBlockNo, goToEpochDetailFromEpochNo, - goToBlockDetailFromBlockId, - checkBlockWidget, - goToBlockDetailFromWidgetByBlockHash, - goToBlockDetailFromWidgetByTrxTab + goToBlockDetailFromBlockId }; } diff --git a/playwright/pages/epoch-detail.page.ts b/playwright/pages/epoch-detail.page.ts index d1703be5ec..9c6124eaf7 100644 --- a/playwright/pages/epoch-detail.page.ts +++ b/playwright/pages/epoch-detail.page.ts @@ -31,10 +31,6 @@ export function epochDetailPage(page: Page) { const slotTitleColumnBlockTable = page.getByTestId("epochList.slotTitle"); const slotNoTitleColumnBlockTable = page.getByTestId("epochList.slotNoTitle"); const createdAtTitleColumnBlockTable = page.getByTestId("epochList.createdAtTitle"); - const txCountTitleColumnBlockTable = page.getByTestId("epochList.txCountTitle"); - const feesTitleColumnBlockTable = page.getByTestId("epochList.feesTitle"); - const outSumTitleColumnBlockTable = page.getByTestId("epochList.outSumTitle"); - const goToBlockDetailFromEpoch = async () => { await firstBlockInEpochDetail.click(); }; @@ -145,9 +141,6 @@ export function epochDetailPage(page: Page) { await expect(slotTitleColumnBlockTable, "Check title on finished blocks table").toHaveText("Slot"); await expect(slotNoTitleColumnBlockTable, "Check title on finished blocks table").toHaveText("Absolute Slot"); await expect(createdAtTitleColumnBlockTable, "Check title on finished blocks table").toHaveText("Created At"); - await expect(txCountTitleColumnBlockTable, "Check title on finished blocks table").toHaveText("Transactions"); - await expect(feesTitleColumnBlockTable, "Check title on finished blocks table").toHaveText("Fees"); - await expect(outSumTitleColumnBlockTable, "Check title on finished blocks table").toHaveText("Output"); }; const checkBlockDetailPage = async ({ blockId = "" }: { blockId: string | null }) => { diff --git a/playwright/pages/epochs-dashboard.page.ts b/playwright/pages/epochs-dashboard.page.ts index 97e9167816..bcfa11989f 100644 --- a/playwright/pages/epochs-dashboard.page.ts +++ b/playwright/pages/epochs-dashboard.page.ts @@ -23,10 +23,6 @@ export function epochsDashboardPage(page: Page) { const startTimeTitleTable = page.getByTestId("epoch.table.startTimeTitle"); const endTimeTitleTable = page.getByTestId("epoch.table.endTimeTitle"); const blocksTitleTable = page.getByTestId("epoch.table.blocksTitle"); - const uniqueAccountsTitleTable = page.getByTestId("epoch.table.uniqueAccountsTitle"); - const transactionCountTitleTable = page.getByTestId("epoch.table.transactionCountTitle"); - const rewardsDistributedTitleTable = page.getByTestId("epoch.table.rewardsDistributedTitle"); - const totalOutputTitleTable = page.getByTestId("epoch.table.totalOutputTitle"); // Epoch widget const firstEpochContainer = page.getByTestId("epoch.firstEpoch.container"); @@ -36,7 +32,6 @@ export function epochsDashboardPage(page: Page) { const startTimeInEpochWidget = page.getByTestId("epoch.detailViewEpoch.startTimeValue"); const endTimeInEpochWidget = page.getByTestId("epoch.detailViewEpoch.endTimeValue"); const txCountInEpochWidget = page.getByTestId("epoch.detailViewEpoch.txCountValue"); - const totalOutputInEpochWidget = page.getByTestId("epoch.detailViewEpoch.totalOutputValue"); const blockTabWidget = page.getByTestId("epoch.detailViewEpoch.blockLink"); const viewDetailButtonWidget = page.getByTestId("epoch.detailViewEpoch.viewDetail"); const blockTab = page.getByTestId("epoch.detailViewEpoch.blockLink"); @@ -109,10 +104,6 @@ export function epochsDashboardPage(page: Page) { await expect(startTimeTitleTable, "Check title on finished epoch table").toHaveText("Start Timestamp"); await expect(endTimeTitleTable, "Check title on finished epoch table").toHaveText("End Timestamp"); await expect(blocksTitleTable, "Check title on finished epoch table").toHaveText("Blocks"); - await expect(uniqueAccountsTitleTable, "Check title on finished epoch table").toHaveText("Unique Accounts"); - await expect(transactionCountTitleTable, "Check title on finished epoch table").toHaveText("Transaction Count"); - await expect(rewardsDistributedTitleTable, "Check title on finished epoch table").toHaveText("Rewards Distributed"); - await expect(totalOutputTitleTable, "Check title on finished epoch table").toHaveText("Total Output"); }; const checkCurrentEpochWidget = async ({ currentEpoch }: { currentEpoch?: BlockfrostEpochInformationDto }) => { @@ -167,11 +158,6 @@ export function epochsDashboardPage(page: Page) { "Total transaction in finished epoch on widget to equal total block in epoch on Blockfrost " ).toEqual(currentEpoch?.tx_count || 0); - expect( - +((await totalOutputInEpochWidget.textContent())?.replaceAll(",", "") || 0) * 10 ** 6, - "Total output in finished epoch on widget to equal total block in epoch on Blockfrost " - ).toEqual(+(currentEpoch?.output || 0)); - await expect(blockTabWidget, "Block tab on epoch widget").toHaveText("Blocks"); await expect(viewDetailButtonWidget, "View details button on epoch widget").toHaveText("View details"); diff --git a/playwright/pages/native-token.page.ts b/playwright/pages/native-token.page.ts index 263b88128e..86a2227a6d 100644 --- a/playwright/pages/native-token.page.ts +++ b/playwright/pages/native-token.page.ts @@ -21,9 +21,7 @@ export function nativeTokenPage(page: Page) { const tokenTableIcon = page.getByTestId("tokens.table.title.icon"); const tokenTableAssetName = page.getByTestId("tokens.table.title.assetName"); const tokenTableScriptHash = page.getByTestId("tokens.table.title.scriptHash"); - const tokenTableTotalTxs = page.getByTestId("tokens.table.title.totalTxs"); const tokenTableNumberOfHolders = page.getByTestId("tokens.table.title.numberOfHolders"); - const tokenTableTotalVolumn = page.getByTestId("tokens.table.title.totalVolumn"); const tokenTableVolume24h = page.getByTestId("tokens.table.title.volume24h"); const tokenTableTotalSupply = page.getByTestId("tokens.table.title.totalSupply"); const tokenTabletoCreatedAt = page.getByTestId("tokens.table.title.createdAt"); @@ -39,7 +37,6 @@ export function nativeTokenPage(page: Page) { const tokenTransactionTxhash = page.getByTestId("token.transaction.txHash#0"); const tokenTransactionBlock = page.getByTestId("token.transaction.block#0"); const tokenTransactionEpoch = page.getByTestId("token.transaction.epoch#0"); - const tokenTransactionAddress = page.getByTestId("token.transaction.address#0"); const widgetPolicyName = page.getByTestId("token.widget.policyName"); const tokenAssetName = page.getByTestId("token.asset.name"); const tokenMinting = page.getByTestId("token.detail.minting.trxHash#0"); @@ -82,9 +79,7 @@ export function nativeTokenPage(page: Page) { const goToEpochDetailFromTransactionTableByEpoch = async () => { await tokenTransactionEpoch.click(); }; - const goToAddressDetailFromTransactionTableByAddress = async () => { - await tokenTransactionAddress.click(); - }; + const goToStakeAddressDetailFromTopHolderTableByAddress = async () => { await deatailTopHolderTab.click(); await topholderTransactionAddress.click(); @@ -152,10 +147,6 @@ export function nativeTokenPage(page: Page) { const blockTrx = await tokenTransactionBlock.getAttribute("aria-label"); return blockTrx; }; - const getAddressByTableValueTrx = async () => { - const addressTrx = await tokenTransactionAddress.getAttribute("aria-label"); - return addressTrx; - }; const getMintingByTableValueTrx = async () => { const tokenMintingTrx = await tokenMinting.getAttribute("aria-label"); return tokenMintingTrx; @@ -165,9 +156,7 @@ export function nativeTokenPage(page: Page) { await expect(tokenTableIcon, "Check title on transaction table").toHaveText("Icon"); await expect(tokenTableAssetName, "Check title on transaction table").toHaveText("Asset name"); await expect(tokenTableScriptHash, "Check title on transaction table").toHaveText("Script hash"); - await expect(tokenTableTotalTxs, "Check title on transaction table").toHaveText("Total Transactions"); await expect(tokenTableNumberOfHolders, "Check title on transaction table").toHaveText("Number of holders"); - await expect(tokenTableTotalVolumn, "Check title on transaction table").toHaveText("Total volume"); await expect(tokenTableVolume24h, "Check title on transaction table").toHaveText("Volume 24H"); await expect(tokenTableTotalSupply, "Check title on transaction table").toHaveText("Total Supply"); await expect(tokenTabletoCreatedAt, "Check title on transaction table").toHaveText("Created At"); @@ -201,7 +190,6 @@ export function nativeTokenPage(page: Page) { ); }; return { - goToAddressDetailFromTransactionTableByAddress, goToStakeAddressDetailFromTopHolderTableByAddress, goToTransactionDetailFromMintingTableByTxHash, goToTransactionDetailFromTransactionTableByTxHash, @@ -231,7 +219,6 @@ export function nativeTokenPage(page: Page) { getDataPolicyName, getTokenAssetName, getEpochByTableValueTrx, - getAddressByTableValueTrx, getMintingByTableValueTrx, getBlockByTableValueTrx }; diff --git a/playwright/pages/top-ADA-holders.page.ts b/playwright/pages/top-ADA-holders.page.ts deleted file mode 100644 index 94e5ecd370..0000000000 --- a/playwright/pages/top-ADA-holders.page.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { expect, Page } from "@playwright/test"; - -import { DataTabAddADABalance, DataTabAmountStake } from "playwright/api/dtos/topADAHolders.dto"; - -export function topADAHoldersPage(page: Page) { - const topADAHoldersTab = page.getByTestId("submenu-button-top_ada_holders"); - const sidebarBlockchainButton = page.getByTestId("menu-button-blockchain"); - const tabAmountStaked = page.getByTestId("topAddresses.amount-staked"); - - //get element in tab Address ADA Holder - const firstAddressTable = page.getByTestId("topAddresses.byADABalance.addressValue#0"); - const firstBalaceTable = page.getByTestId("topAddresses.byADABalance.balance#0"); - const firstTxCountTable = page.getByTestId("topAddresses.byADABalance.transactionCountValue#0"); - - //get element in tab Amount Stake - const firstAddressStakedTable = page.getByTestId("topAddresses.byAmountStaked.addressValue#0"); - const firstStakeAmountTable = page.getByTestId("topAddresses.byAmountStake.stakeAmount0"); - - //title tab Address ADA Holder - const topADAHoldersAddress = page.getByTestId("topAddresses.byADABalance.addressTitle"); - const topADAHoldersBalance = page.getByTestId("topAddresses.byADABalance.balanceTitle"); - const topADAHoldersCount = page.getByTestId("topAddresses.byADABalance.transactionCountTitle"); - - //title tab Amount Stake - const topADAHoldersAmountAddress = page.getByTestId("topAddresses.byAmountStake.stakeAddressTitle"); - const topADAHoldersPool = page.getByTestId("topAddresses.byAmountStake.poolTitle"); - const topADAHoldersStakeAmount = page.getByTestId("topAddresses.byAmountStake.stakeAmountTitle"); - - const checkTitleTableTabAddADABalance = async () => { - await expect(topADAHoldersAddress, "Check title on top ADA holder table tab address ADA Balance").toHaveText( - "Addresses" - ); - await expect(topADAHoldersBalance, "Check title on top ADA holder table tab address ADA Balance").toHaveText( - "Balance" - ); - await expect(topADAHoldersCount, "Check title on top ADA holder table tab address ADA Balance").toHaveText( - "Transaction Count" - ); - }; - - const checkTitleTableTabAmountStake = async () => { - await expect(topADAHoldersAmountAddress, "Check title on top ADA holder table tab amount staked").toHaveText( - "Stake Address" - ); - await expect(topADAHoldersPool, "Check title on top ADA holder table tab amount staked").toHaveText("Pool"); - await expect(topADAHoldersStakeAmount, "Check title on top ADA holder table tab amount staked").toHaveText( - "Stake Amount" - ); - }; - - const goToDashboard = async () => { - await page.goto("/"); - }; - - const goToTopADAHolders = async () => { - await page.goto("/addresses"); - }; - - const goToTopADAHoldersFromSidebar = async () => { - await sidebarBlockchainButton.click(); - await topADAHoldersTab.click(); - }; - - const goToTabAmountStaked = async () => { - await tabAmountStaked.click(); - }; - - // get field address, balance and transaction count in tab Address ADA Balance - const getAttributeAddress = async () => { - const ariaLabelValue = await firstAddressTable?.getAttribute("aria-label"); - return ariaLabelValue; - }; - - const getAttributeBalance = async () => { - const balance = await firstBalaceTable?.getAttribute("aria-label"); - return Number(balance); - }; - - const getAttributeTxCount = async () => { - const txCount = await firstTxCountTable?.getAttribute("aria-label"); - return Number(txCount); - }; - - // get field stake address, stake smount - - const getAttributeAddressStake = async () => { - const ariaLabelValueStaked = await firstAddressStakedTable?.getAttribute("aria-label"); - return ariaLabelValueStaked; - }; - - const getAttributeAmountStake = async () => { - const ariaLabelValueStaked = await firstStakeAmountTable?.getAttribute("aria-label"); - return Number(ariaLabelValueStaked); - }; - - const checkAddressAndBalace = async ({ dataTabAddADABalance }: DataTabAddADABalance) => { - expect(await Number(getAttributeAddress()), "Address on explorer equal epoch no Koios").toEqual( - Number(dataTabAddADABalance?.address) - ); - expect(await getAttributeBalance(), "Balace on explorer equal epoch no Koios").toEqual( - Number(dataTabAddADABalance?.balance) - ); - }; - - const checkTxCount = async (txCount: number) => { - expect(await getAttributeTxCount(), "Tx count on explorer equal epoch no Blockfrost").toBeLessThanOrEqual(txCount); - }; - - const checkStakeAddressAndStakeAmount = async ({ dataTabAmountStake }: DataTabAmountStake) => { - expect(await Number(getAttributeAddressStake()), "Stake address on explorer equal epoch no Blockfrost").toEqual( - Number(dataTabAmountStake?.stakeAddress) - ); - expect(await getAttributeAmountStake(), "Stake amount on explorer equal epoch no Blockfrost").toEqual( - Number(dataTabAmountStake?.stakeAmount) - ); - }; - - return { - goToDashboard, - goToTopADAHolders, - goToTopADAHoldersFromSidebar, - goToTabAmountStaked, - getAttributeAddress, - checkTitleTableTabAddADABalance, - checkTitleTableTabAmountStake, - checkAddressAndBalace, - checkTxCount, - getAttributeAddressStake, - checkStakeAddressAndStakeAmount - }; -} diff --git a/playwright/pages/transaction-overview.page.ts b/playwright/pages/transaction-overview.page.ts index 4e5ec8babd..f0a19280ae 100644 --- a/playwright/pages/transaction-overview.page.ts +++ b/playwright/pages/transaction-overview.page.ts @@ -1,7 +1,4 @@ import { expect, Page } from "@playwright/test"; -import moment from "moment"; - -import { TransactionInformationDto } from "playwright/api/dtos/transactionInformation.dto"; export function transactionOverviewPage(page: Page) { const firstTransactionInTable = page.getByTestId("transaction.table.value.txhash#0"); @@ -19,20 +16,6 @@ export function transactionOverviewPage(page: Page) { const transactionTableTitleEpoch = page.getByTestId("transactions.table.title.epoch"); const transactionTableTitleSlot = page.getByTestId("transactions.table.title.slot"); const transactionTableTitlelFees = page.getByTestId("transactions.table.title.fees"); - const transactionTableTitleOutputInAda = page.getByTestId("transactions.table.title.outputInAda"); - const transactionTableTitleInputAddress = page.getByTestId("transactions.table.title.inputAddress"); - const transactionTableTitleOutputAddress = page.getByTestId("transactions.table.title.outputAddress"); - const transactionWidgeDetailViewBtn = page.getByTestId("transaction.detailViewEpoch.viewDetail"); - - const addressDetailFromWidgetByInputAddress = page.getByTestId("transaction.widget.inputAddress"); - const transactionWidgetTrxSignature = page.getByTestId("transaction.widget.signersInformation"); - const transactionWidgeSummary = page.getByTestId("transaction.widget.summary"); - const transactionWidgeUtxo = page.getByTestId("transaction.widget.utxOs"); - const transactionWidgetEpochNo = page.getByTestId("transaction.widget.epoch"); - const transactionWidgetBlockNo = page.getByTestId("transaction.widget.block"); - const transactionWidgetSlotNo = page.getByTestId("transaction.widget.slot"); - const transactionWidgetCreatedAt = page.getByTestId("transaction.widget.createdAt"); - const transactionWidgetEpochSlot = page.getByTestId("transaction.widget.epochSlot"); const getDetailBlockPageTitle = async () => { await expect(deatailPageTitle, "Check title on block detail").toHaveText("Block Details"); @@ -71,29 +54,6 @@ export function transactionOverviewPage(page: Page) { const trxHash = await firstTransactionInTable?.getAttribute("href"); return trxHash; }; - const getLinkHrefFromWidgeDetailViewBtn = async () => { - const trxHash = await transactionWidgeDetailViewBtn?.getAttribute("href"); - return trxHash; - }; - - const getLinkHrefFromWidgetTrxSignature = async () => { - const trxHash = await transactionWidgetTrxSignature?.getAttribute("href"); - return trxHash; - }; - - const getLinkHrefFromWidgetByUtxo = async () => { - const trxHash = await transactionWidgeUtxo?.getAttribute("href"); - return trxHash; - }; - const getLinkHrefFromWidgetBySummary = async () => { - const trxHash = await transactionWidgeSummary?.getAttribute("href"); - return trxHash; - }; - - const getLinkHrefFromWidgetByInputAddress = async () => { - const trxHash = await addressDetailFromWidgetByInputAddress?.getAttribute("href"); - return trxHash; - }; const searchBarOnTransaction = async () => { await expect(searchBarEpoch).toHaveText("Transactions"); @@ -112,10 +72,6 @@ export function transactionOverviewPage(page: Page) { await page.goto("/transactions"); }; - const goToTrxDetailFromWidgetDetailViewBtn = async () => { - await transactionWidgeDetailViewBtn.click(); - }; - const goToAddressDetailByInputAddreess = async () => { await firstInputAddressInTable.click(); }; @@ -124,69 +80,22 @@ export function transactionOverviewPage(page: Page) { await transactionTableValueSlot.click(); }; - const goToAddrssDetailFromWidgetByInputAddress = async () => { - await addressDetailFromWidgetByInputAddress.click(); - }; - - const goToTransactionDetailFromWidgetBySumary = async () => { - await transactionWidgeSummary.click(); - }; - - const goToTransactionDetailFromWidgetByTrxTab = async () => { - await transactionWidgetTrxSignature.click(); - }; - - const goToTransactionDetailFromWidgetByUTXO = async () => { - await transactionWidgeUtxo.click(); - }; - const openLastestTransactionWidget = async () => { await transactionTableValueSlot.click(); }; - const checkCurrenTransactionWidget = async ({ - currentTransaction - }: { - currentTransaction: TransactionInformationDto[]; - }) => { - expect(+((await transactionWidgetEpochNo.innerText()) || 0), "Check epoch on transaction widget").toEqual( - currentTransaction[0].epoch_no - ); - expect(+((await transactionWidgetBlockNo.innerText()) || 0), "Check block on transaction widget").toEqual( - currentTransaction[0].block_height - ); - expect(+((await transactionWidgetSlotNo.innerText()) || 0), "Check slot on transaction widget").toEqual( - currentTransaction[0].absolute_slot - ); - expect( - moment((await transactionWidgetCreatedAt.textContent())?.replace(",", "")).unix(), - "Check create time on transaction widget" - ).toEqual(currentTransaction[0]?.tx_timestamp); - expect(+((await transactionWidgetEpochSlot.innerText()) || 0), "Check epoch slot on transaction widget").toEqual( - currentTransaction[0].epoch_slot - ); - }; - const checkTransactionsTable = async () => { await expect(transactionTableTitleTxhash, "Check title on transaction table").toHaveText("Tx hash"); await expect(transactionTableTitleBlock, "Check title on transaction table").toHaveText("Block"); await expect(transactionTableTitleEpoch, "Check title on transaction table").toHaveText("Epoch"); await expect(transactionTableTitleSlot, "Check title on transaction table").toHaveText("Slot"); await expect(transactionTableTitlelFees, "Check title on transaction table").toHaveText("Fees"); - await expect(transactionTableTitleOutputInAda, "Check title on transaction table").toHaveText("Output in ADA"); - await expect(transactionTableTitleInputAddress, "Check title on transaction table").toHaveText("Input Address"); - await expect(transactionTableTitleOutputAddress, "Check title on transaction table").toHaveText("Output Address"); }; return { goToAddressDetailByInputAddreess, getLinkHrefByInputAddress, getDetailAddressPageTitle, - getLinkHrefFromWidgetByInputAddress, - getLinkHrefFromWidgetBySummary, - getLinkHrefFromWidgetByUtxo, - getLinkHrefFromWidgetTrxSignature, - getLinkHrefFromWidgeDetailViewBtn, getDetailEpochPageTitle, goToEpochDetailFromTable, getDetailBlockPageTitle, @@ -194,18 +103,12 @@ export function transactionOverviewPage(page: Page) { getLinkHrefTxHash, goToBlockDetailFromTable, goToTransactionDetailFromTable, - checkCurrenTransactionWidget, openWidget, searchBarOnTransaction, goToTransactionFromSidebar, goToDashboard, checkTransactionsTable, goToTransactions, - openLastestTransactionWidget, - goToAddrssDetailFromWidgetByInputAddress, - goToTrxDetailFromWidgetDetailViewBtn, - goToTransactionDetailFromWidgetBySumary, - goToTransactionDetailFromWidgetByUTXO, - goToTransactionDetailFromWidgetByTrxTab + openLastestTransactionWidget }; } diff --git a/playwright/tests/e2e/features/top-ADA-holders.feature b/playwright/tests/e2e/features/top-ADA-holders.feature deleted file mode 100644 index ca093b8b2d..0000000000 --- a/playwright/tests/e2e/features/top-ADA-holders.feature +++ /dev/null @@ -1,28 +0,0 @@ -Feature: Top ADA Holders in Cardano explorer - - Scenario: Top ADA Holders page content - Given the user is in the general dashboard page in explorer portal test top ADA holder - When the user selects the Top ADA Holders option inside the Blockchain drop down menu - Then the user should see the top ADA Holders address hash, balance and transaction count sorted by balance - - Scenario: Top ADA Holders page - By Amount Staked - Given the user is in the Top ADA Holders section - When the user selects the By amount Staked option - Then the user should see the Stake Address, pool and Stake amount of the Top ADA holders sorted by stake amount - - Scenario: By Address ADA Balance - Adress details - Given the user is in the Top ADA Holders section Address ADA Balance - When the user selects the Address hash of one the Top ADA Holders - Then the user should be redirected to the Address detail page of the selected Address hash - - Scenario: By Amount Staked - Stake Adress details - Given the user is in the Top ADA Holders section tab Address ADA Balance - And the user selects the By amount Staked option - When the user selects the Stake Address hash of one the Top ADA Holders - Then the user should be redirected to the Stake Address detail page of the selected Stake Address hash - - Scenario: By Amount Staked - Pool details - Given the user is in the page Top ADA Holders section - And the user selects the By amount Staked option - When the user selects the pool link of one the Top ADA Holders - Then the user should be redirected to the Pool detail page of the selected pool link \ No newline at end of file diff --git a/playwright/tests/e2e/steps/blocks.steps.ts b/playwright/tests/e2e/steps/blocks.steps.ts index 1026211215..badbf904c1 100644 --- a/playwright/tests/e2e/steps/blocks.steps.ts +++ b/playwright/tests/e2e/steps/blocks.steps.ts @@ -6,16 +6,10 @@ import { BlockInformationDto } from "playwright/api/dtos/blockInformation.dto"; import { blocksDashboard } from "../../../pages/blocks-dashboard.page"; import { blockDetailPage } from "../../../pages/block-detail.page"; import { epochDetailPage } from "../../../pages/epoch-detail.page"; -import { transactionDetailPage } from "../../../pages/transaction-detail.page"; -import { addressDetailPage } from "../../../pages/address-detail.page"; -import { poolDetailPage } from "../../../pages/pool-detail.page"; const { Given, When, Then } = createBdd(); let epochNo: string | null; -let transactionHash: string | null; -let address: string | null; -let poolId: string | null; Given(/^the user is in the general dashboard page in explorer portal block test$/, async ({ page }) => { await blocksDashboard(page).goToDashboard(); @@ -28,31 +22,6 @@ Then(/^the user should see the blocks info containing the Search bar and Blocks await blocksDashboard(page).checkBlocksTable(); }); -Given(/^the user is in the blocks page in explorer portal$/, async ({ page }) => { - await blocksDashboard(page).goToBlocks(); -}); -Given(/^the user open the block info detail widget of one of the blocks in the table$/, async ({ page }) => { - await blocksDashboard(page).openLastestBlockWidget(); -}); -When(/^the user selects the view details button$/, async ({ page }) => { - await blocksDashboard(page).goToBlockDetailFromWidget(); -}); -Then( - /^the user should see the block detail page of the selected block with the Search bar, blockdetails and Transactions table$/, - async ({ page, request }) => { - const blockNo = await page.getByTestId("block.detail.overview.value.block"); - if (blockNo) { - (await blockfrostApi(request).getBlockByNumber(parseInt(await blockNo.textContent()))) - .json() - .then(async (data) => { - await blockDetailPage(page).checkBlockOverviewDetailPage({ blockFrostBlock: data as BlockInformationDto }); - }); - } - await blocksDashboard(page).searchBarOnBlocks(); - await blockDetailPage(page).checkTransactionsTable(); - } -); - Given(/^the user is in the blocks page in explorer$/, async ({ page }) => { await blocksDashboard(page).goToBlocks(); }); @@ -101,138 +70,3 @@ When(/^the user selects the epoch number of one of the blocks record in the tabl Then(/^the user should see the epoch detail page of the selected epoch number in the table$/, async ({ page }) => { await epochDetailPage(page).checkEpochDetail({ epochNo }); }); - -Given(/^the user is in the blocks page in explorer portal for go to detail widget$/, async ({ page }) => { - await blocksDashboard(page).goToBlocks(); -}); -When( - /^the user selects any data of the row execpt the block number, block id or epoch number of one of the blocks record in the table$/, - async ({ page }) => { - await blocksDashboard(page).openLastestBlockWidget(); - } -); -Then( - /^the user should see the block detail info widget with the Info widget data with the same data for the block id, absolute slot, created at date, fees and the total output in Ada of the selected block in the table$/, - async ({ page, request }) => { - const blockNo = await page.getByTestId("block.widget.blockno"); - if (blockNo) { - (await blockfrostApi(request).getBlockByNumber(parseInt(await blockNo.textContent()))) - .json() - .then(async (data) => { - await blocksDashboard(page).checkBlockWidget({ blockFrostBlock: data as BlockInformationDto }); - }); - } - } -); - -Given(/^the user is in the blocks page in explorer portal for go to block detail from widget$/, async ({ page }) => { - await blocksDashboard(page).goToBlocks(); -}); -When( - /^the user open the block info detail widget of one of the blocks in the table for go to block detail from widget$/, - async ({ page }) => { - await blocksDashboard(page).openLastestBlockWidget(); - } -); - -Then( - /^the user selects the block id into the block info detail widget user should see the block detail page of the selected block id$/, - async ({ page, request }) => { - const blockHash = await page.getByTestId("block.widget.blockHash"); - const ariaLabelValue = await blockHash?.getAttribute("aria-label"); - if (ariaLabelValue) { - await blocksDashboard(page).goToBlockDetailFromWidgetByBlockHash(); - (await blockfrostApi(request).getBlockByHash(ariaLabelValue)).json().then(async (data) => { - await blockDetailPage(page).checkBlockOverviewDetailPage({ blockFrostBlock: data as BlockInformationDto }); - }); - } - } -); - -Given(/^the user is in the Blocks section in the explorer page for go to trx detail from block$/, async ({ page }) => { - await blocksDashboard(page).goToBlocks(); -}); -Given(/^the user go to the detail view page of one block for go to trx detail from block$/, async ({ page }) => { - await blocksDashboard(page).openLastestBlockWidget(); - await blocksDashboard(page).goToBlockDetailFromWidgetByBlockHash(); -}); -When( - /^the user selects the transaction hash of one record of the transactions table in the block detail view page$/, - async ({ page }) => { - const blocksDetailTrxTableValueTrx = page.getByTestId("block.detail.trxTable.value.txhash#0"); - transactionHash = await blocksDetailTrxTableValueTrx.getAttribute("aria-label"); - await blocksDetailTrxTableValueTrx.click(); - } -); -Then( - /^the user should be redirected to the transaction details page of the select transaction in the block detail view page$/, - async ({ page }) => { - await transactionDetailPage(page).checkTransactionsDetail({ transactionHash }); - } -); - -Given( - /^the user is in the Blocks section in the explorer page for go to address detail from block$/, - async ({ page }) => { - await blocksDashboard(page).goToBlocks(); - } -); -Given(/^the user go to the detail view page of one block for go to address detail from block$/, async ({ page }) => { - await blocksDashboard(page).openLastestBlockWidget(); - await blocksDashboard(page).goToBlockDetailFromWidgetByBlockHash(); -}); -When( - /^the user selects one of the input - output addresses of one record of the transactions table in the block detail view page$/, - async ({ page }) => { - const blocksDetailTrxTableValueInputAddress = page.getByTestId("block.detail.trxTable.value.inputAddress#0"); - address = await blocksDetailTrxTableValueInputAddress.getAttribute("aria-label"); - await blocksDetailTrxTableValueInputAddress.click(); - } -); -Then( - /^the user should be redirected to the address details page of the select address in the block detail view page$/, - async ({ page }) => { - await addressDetailPage(page).checkAddressDetail({ address }); - } -); - -Given( - /^the user is in the Blocks section in the explorer page for go to transaction table from widget$/, - async ({ page }) => { - await blocksDashboard(page).goToBlocks(); - } -); -Given( - /^the user selects one of the blocks to open the info widget for go to transaction table from widget$/, - async ({ page }) => { - await blocksDashboard(page).openLastestBlockWidget(); - } -); -When(/^the user selects the transactions section into the info widget of the selected block$/, async ({ page }) => { - await blocksDashboard(page).goToBlockDetailFromWidgetByTrxTab(); -}); -Then( - /^the user should see the selected block detail view page in the transactions table section$/, - async ({ page }) => { - await blockDetailPage(page).checkTransactionsTable(); - } -); - -Given( - /^the user is in the Blocks section in the explorer page for go to pool detail from producer$/, - async ({ page }) => { - await blocksDashboard(page).goToBlocks(); - } -); -Given(/^the user go to the detail view page of one block for go to pool detail from producer$/, async ({ page }) => { - await blocksDashboard(page).openLastestBlockWidget(); - await blocksDashboard(page).goToBlockDetailFromWidgetByBlockHash(); -}); -When(/^the user selects the pool link in the block producer$/, async ({ page }) => { - const blocksDetailProducer = page.getByTestId("block.detail.overview.value.producer"); - poolId = await blocksDetailProducer.getAttribute("href"); - await blocksDetailProducer.click(); -}); -Then(/^the user should be redirected to the pool details page that produce the given block$/, async ({ page }) => { - await poolDetailPage(page).checkPoolDetail({ poolId: (poolId || "")?.split("/")[3] }); -}); diff --git a/playwright/tests/e2e/steps/native-token.steps.ts b/playwright/tests/e2e/steps/native-token.steps.ts index 6a9fbec7db..3e00c82ca4 100644 --- a/playwright/tests/e2e/steps/native-token.steps.ts +++ b/playwright/tests/e2e/steps/native-token.steps.ts @@ -1,7 +1,6 @@ import { createBdd } from "playwright-bdd"; import { blockfrostApi } from "playwright/api/call-blockfrost/blockfrost.api"; -import { addressDetailPage } from "playwright/pages/address-detail.page"; import { blockDetailPage } from "playwright/pages/block-detail.page"; import { epochDetailPage } from "playwright/pages/epoch-detail.page"; import { nativeTokenPage } from "playwright/pages/native-token.page"; @@ -14,7 +13,6 @@ const { Given, When, Then } = createBdd(); let tokenID: string | null; let scriptHash: string | null; let transactionHash: string | null; -let address: string | null; let epochNo: string | null; let blockNo: string | null; @@ -183,36 +181,6 @@ Then(/^the user should be redirected to the Address details page of the selected await epochDetailPage(page).checkEpochDetail({ epochNo }); }); -Given(/^the user open the detail page of a token in the native tokens page in web$/, async ({ page }) => { - await nativeTokenPage(page).goToNativeTokens(); - await nativeTokenPage(page).goToTokensDetailPageFromTableByAssetName(); -}); -When(/^the user selects the input addres link in the transactions table in web$/, async ({ page }) => { - address = await nativeTokenPage(page).getAddressByTableValueTrx(); - await nativeTokenPage(page).goToAddressDetailFromTransactionTableByAddress(); -}); -Then( - /^the user should be redirected to the Address details page of the selected address link in web$/, - async ({ page }) => { - await addressDetailPage(page).checkAddressDetail({ address }); - } -); - -Given(/^the user open the detail page of a token in the native tokens page in web site$/, async ({ page }) => { - await nativeTokenPage(page).goToNativeTokens(); - await nativeTokenPage(page).goToTokensDetailPageFromTableByAssetName(); -}); -When(/^the user selects the addres link in the top holders section in web site$/, async ({ page }) => { - address = await nativeTokenPage(page).getAddressByTableValueTrx(); - await nativeTokenPage(page).goToStakeAddressDetailFromTopHolderTableByAddress(); -}); -Then( - /^the user should be redirected to the Address details page of the selected address link in web site$/, - async ({ page }) => { - await addressDetailPage(page).checkAddressDetail({ address }); - } -); - Given(/^the user open the detail page of a token in the native tokens in webpage$/, async ({ page }) => { await nativeTokenPage(page).goToNativeTokens(); await nativeTokenPage(page).goToTokensDetailPageFromTableByAssetName(); diff --git a/playwright/tests/e2e/steps/top-ADA-holders.step.ts b/playwright/tests/e2e/steps/top-ADA-holders.step.ts deleted file mode 100644 index feb27ad014..0000000000 --- a/playwright/tests/e2e/steps/top-ADA-holders.step.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { createBdd } from "playwright-bdd"; -import { expect } from "@playwright/test"; - -import { blockfrostApi } from "playwright/api/call-blockfrost/blockfrost.api"; -import { koiosApi } from "playwright/api/call-koios/koios.api"; -import { topADAHoldersPage } from "playwright/pages/top-ADA-holders.page"; -import { addressDetailPage } from "playwright/pages/address-detail.page"; -import { poolDetailPage } from "playwright/pages/pool-detail.page"; -import { stakeDetailPage } from "playwright/pages/stake-detail.page"; - -const { Given, When, Then } = createBdd(); - -let firstAddressValue: string | null; -let firstStakeAddress: string | null; -let firstPoolID: string | null; - -Given(/^the user is in the general dashboard page in explorer portal test top ADA holder$/, async ({ page }) => { - await topADAHoldersPage(page).goToDashboard(); -}); -When(/^the user selects the Top ADA Holders option inside the Blockchain drop down menu$/, async ({ page }) => { - await topADAHoldersPage(page).goToTopADAHoldersFromSidebar(); -}); -Then( - /^the user should see the top ADA Holders address hash, balance and transaction count sorted by balance$/, - async ({ page, request }) => { - let address; - let balance; - let txCount; - - await topADAHoldersPage(page).checkTitleTableTabAddADABalance(); - - const addressValue = await topADAHoldersPage(page).getAttributeAddress(); - - if (addressValue) { - (await koiosApi(request).getAddress(addressValue)).json().then(async (data) => { - address = data[0].address; - balance = data[0].balance; - - const dataTabAddADABalance = { address, balance }; - - await topADAHoldersPage(page).checkAddressAndBalace({ dataTabAddADABalance }); - }); - - (await blockfrostApi(request).getTxCountTopADAHolder(addressValue)).json().then(async (data) => { - txCount = data.tx_count; - await topADAHoldersPage(page).checkTxCount(txCount); - }); - } - } -); - -Given(/^the user is in the Top ADA Holders section$/, async ({ page }) => { - await topADAHoldersPage(page).goToTopADAHolders(); -}); -When(/^the user selects the By amount Staked option$/, async ({ page }) => { - await topADAHoldersPage(page).goToTabAmountStaked(); -}); -Then( - /^the user should see the Stake Address, pool and Stake amount of the Top ADA holders sorted by stake amount$/, - async ({ page, request }) => { - let stakeAddress; - let stakeAmount; - - await topADAHoldersPage(page).checkTitleTableTabAmountStake(); - const stakedAddressValue = await topADAHoldersPage(page).getAttributeAddressStake(); - if (stakedAddressValue) { - (await blockfrostApi(request).getDataAmountStaked(stakedAddressValue)).json().then(async (data) => { - stakeAddress = data.stake_address; - stakeAmount = data.controlled_amount; - const dataTabAmountStake = { stakeAddress, stakeAmount }; - await topADAHoldersPage(page).checkStakeAddressAndStakeAmount({ dataTabAmountStake }); - }); - - (await blockfrostApi(request).getPoolIDAcount(stakedAddressValue)).json().then(async (data) => { - const firstPool = page.getByTestId("topAddresses.byAmountStaked.pool#0"); - firstPoolID = await firstPool?.getAttribute("aria-label"); - const exists: boolean = data.some((item: { pool_id: string }) => item.pool_id === firstPoolID); - await expect(exists).toBe(true); - }); - } - } -); - -Given(/^the user is in the Top ADA Holders section Address ADA Balance$/, async ({ page }) => { - await topADAHoldersPage(page).goToTopADAHolders(); -}); -When(/^the user selects the Address hash of one the Top ADA Holders$/, async ({ page }) => { - const firstAddresses = page.getByTestId("topAddresses.byADABalance.addressValue#0"); - firstAddressValue = await firstAddresses.textContent(); - await firstAddresses.click(); -}); -Then(/^the user should be redirected to the Address detail page of the selected Address hash$/, async ({ page }) => { - await addressDetailPage(page).checkAddressDetail({ address: firstAddressValue }); -}); - -Given(/^the user is in the Top ADA Holders section tab Address ADA Balance$/, async ({ page }) => { - await topADAHoldersPage(page).goToTopADAHolders(); -}); -Given(/^the user selects the By amount Staked tab option$/, async ({ page }) => { - await topADAHoldersPage(page).goToTabAmountStaked(); -}); -When(/^the user selects the Stake Address hash of one the Top ADA Holders$/, async ({ page }) => { - const firstAddressStaked = page.getByTestId("topAddresses.byAmountStaked.addressValue#0"); - firstStakeAddress = await firstAddressStaked.textContent(); - await firstAddressStaked.click(); -}); -Then( - /^the user should be redirected to the Stake Address detail page of the selected Stake Address hash$/, - async ({ page }) => { - stakeDetailPage(page).checkStakeDetail({ address: firstStakeAddress }); - } -); - -Given(/^the user is in the page Top ADA Holders section$/, async ({ page }) => { - await topADAHoldersPage(page).goToTopADAHolders(); -}); -Given(/^the user selects the tab By amount Staked option$/, async ({ page }) => { - await topADAHoldersPage(page).goToTabAmountStaked(); -}); -When(/^the user selects the pool link of one the Top ADA Holders$/, async ({ page }) => { - const firstPool = page.getByTestId("topAddresses.byAmountStaked.pool#0"); - await firstPool.click(); -}); -Then(/^the user should be redirected to the Pool detail page of the selected pool link$/, async ({ page }) => { - await poolDetailPage(page).checkPoolDetail({ poolId: firstPoolID }); -}); diff --git a/playwright/tests/e2e/steps/transactions-overview.steps.ts b/playwright/tests/e2e/steps/transactions-overview.steps.ts index d1de350617..f2eb5c6b14 100644 --- a/playwright/tests/e2e/steps/transactions-overview.steps.ts +++ b/playwright/tests/e2e/steps/transactions-overview.steps.ts @@ -1,7 +1,5 @@ import { createBdd } from "playwright-bdd"; -import { koiosApi } from "playwright/api/call-koios/koios.api"; - import { transactionOverviewPage } from "../../../pages/transaction-overview.page"; import { transactionDetailPage } from "../../../pages/transaction-detail.page"; import { addressDetailPage } from "../../../pages/address-detail.page"; @@ -25,27 +23,6 @@ Then( } ); -Given(/^the user is in the transactions page in explorer portal site$/, async ({ page }) => { - await transactionOverviewPage(page).goToTransactions(); -}); -When( - /^the user selects any data of the row execpt the transaction hash, block number, epoch number or addresses hash of one of the transactions record in the table$/, - async ({ page }) => { - await transactionOverviewPage(page).openWidget(); - } -); -Then( - /^the user should see the Info widget data with the respective data of the selected transaction$/, - async ({ page, request }) => { - const ariaLabelValue = await transactionOverviewPage(page).getAttributeTxHashWidget(); - if (ariaLabelValue) { - (await koiosApi(request).getTransactionByTrxHash(ariaLabelValue)).json().then(async (data) => { - await transactionOverviewPage(page).checkCurrenTransactionWidget({ currentTransaction: data }); - }); - } - } -); - Given(/^the user is in the Transactions page$/, async ({ page }) => { await transactionOverviewPage(page).goToTransactions(); }); @@ -99,106 +76,3 @@ When( Then(/^the user should be redirected to the address details page of the selected address hash$/, async ({ page }) => { await addressDetailPage(page).checkAddressDetail({ address: addressId || "" }); }); - -Given( - /^the user is in the Transactions page for go to transaction detail from detail button in widget$/, - async ({ page }) => { - await transactionOverviewPage(page).goToTransactions(); - } -); -Given( - /^the user open the info widget of one transaction record in the transactions table of transaction page$/, - async ({ page }) => { - await transactionOverviewPage(page).openLastestTransactionWidget(); - } -); -When(/^the user selects the view details button in the transcation info widget$/, async ({ page }) => { - transactionHash = await transactionOverviewPage(page).getLinkHrefFromWidgeDetailViewBtn(); - await transactionOverviewPage(page).goToTrxDetailFromWidgetDetailViewBtn(); -}); -Then(/^the transaction details page of the selected transaction should be opened$/, async ({ page }) => { - await transactionDetailPage(page).checkTransactionsDetail({ transactionHash: transactionHash || "" }); -}); - -Given(/^the user is in the Transactions page to address detail from detail button in widget$/, async ({ page }) => { - await transactionOverviewPage(page).goToTransactions(); -}); -Given(/^the user open the info widget of one transaction record in transaction web$/, async ({ page }) => { - await transactionOverviewPage(page).openLastestTransactionWidget(); -}); -When(/^the user selects the Input or Output address hash in the transcation info widget$/, async ({ page }) => { - addressId = await transactionOverviewPage(page).getLinkHrefFromWidgetByInputAddress(); - await transactionOverviewPage(page).goToAddrssDetailFromWidgetByInputAddress(); -}); -Then( - /^the user should be redirected to the address detail page of the selected input address hash$/, - async ({ page }) => { - await addressDetailPage(page).checkAddressDetail({ address: addressId || "" }); - } -); - -Given( - /^the user is in the Transactions page for go to transaction detail from summary in widget$/, - async ({ page }) => { - await transactionOverviewPage(page).goToTransactions(); - } -); -Given( - /^the user opens the info widget of any transaction in the transactions table from widget of transaction$/, - async ({ page }) => { - await transactionOverviewPage(page).openLastestTransactionWidget(); - } -); -When(/^the user selects the summary section in the info widget$/, async ({ page }) => { - transactionHash = await transactionOverviewPage(page).getLinkHrefFromWidgetBySummary(); - await transactionOverviewPage(page).goToTransactionDetailFromWidgetBySumary(); -}); -Then( - /^the transaction detail page of the selected transaction should be opened with the summary section displayed$/, - async ({ page }) => { - await transactionDetailPage(page).checkTransactionsDetail({ transactionHash: transactionHash || "" }); - } -); - -Given(/^the user is in the Transactions page for go to transaction detail from widget$/, async ({ page }) => { - await transactionOverviewPage(page).goToTransactions(); -}); -Given(/^the user opens the info widget of any transaction in the transactions table from widget$/, async ({ page }) => { - await transactionOverviewPage(page).openLastestTransactionWidget(); -}); -When(/^the user selects the UTXOs section in the info widget of the selected transaction$/, async ({ page }) => { - transactionHash = await transactionOverviewPage(page).getLinkHrefFromWidgetByUtxo(); - await transactionOverviewPage(page).goToTransactionDetailFromWidgetByUTXO(); -}); -Then( - /^the transaction detail page of the selected transaction should be opened with the UTXOs section displayed$/, - async ({ page }) => { - await transactionDetailPage(page).checkTransactionsDetail({ transactionHash: transactionHash || "" }); - } -); - -Given( - /^the user is in the Transactions page in the explorer page for go to transaction table from widget$/, - async ({ page }) => { - await transactionOverviewPage(page).goToTransactions(); - } -); -Given( - /^the user opens the info widget of any transaction in the transactions table from widget tab$/, - async ({ page }) => { - await transactionOverviewPage(page).openLastestTransactionWidget(); - } -); -When( - /^the user selects the transaction signatories section in the info widget of the selected transaction$/, - async ({ page }) => { - transactionHash = await transactionOverviewPage(page).getLinkHrefFromWidgetTrxSignature(); - await transactionOverviewPage(page).goToTransactionDetailFromWidgetByTrxTab(); - } -); -Then( - /^the transaction detail page of the selected transaction should be opened with the transaction signatories section displayed$/, - async ({ page }) => { - await transactionDetailPage(page).checkTransactionsDetail({ transactionHash: transactionHash || "" }); - } -); diff --git a/src/Routers.tsx b/src/Routers.tsx index 118d8c10c7..30a30e1e09 100644 --- a/src/Routers.tsx +++ b/src/Routers.tsx @@ -45,7 +45,6 @@ import StakeDetail from "./pages/StakeDetail"; import StakingLifecycle from "./pages/StakingLifecycle"; import Tokens from "./pages/Token"; import TokenDetail from "./pages/TokenDetail"; -import TopAddresses from "./pages/TopAddresses"; import TopDelegators from "./pages/TopDelegators"; import TransactionDetail from "./pages/TransactionDetail"; import TransactionList from "./pages/TransactionList"; @@ -111,7 +110,6 @@ const Routes: React.FC = () => { - diff --git a/src/commons/hooks/useFetch.test.ts b/src/commons/hooks/useFetch.test.ts index c78d58de85..d3419f72c7 100644 --- a/src/commons/hooks/useFetch.test.ts +++ b/src/commons/hooks/useFetch.test.ts @@ -11,6 +11,8 @@ interface FetchReturnType { initialized: boolean; refresh: () => void; lastUpdated?: number; + statusError: undefined; + update: () => void; } describe("useFetch", () => { @@ -34,7 +36,9 @@ describe("useFetch", () => { initialized: false, loading: true, refresh: expect.any(Function), - lastUpdated: undefined + lastUpdated: undefined, + statusError: undefined, + update: expect.any(Function) }; expect(result.current).toEqual(expected); diff --git a/src/commons/hooks/useFetchList.ts b/src/commons/hooks/useFetchList.ts index d6601d166d..4ff62fd209 100644 --- a/src/commons/hooks/useFetchList.ts +++ b/src/commons/hooks/useFetchList.ts @@ -91,7 +91,7 @@ const useFetchList = ( setInitialized(true); if (error instanceof AxiosError) { setError(error?.response?.data?.message || error?.message); - setStatusError(error.response?.status || error.request.status); + setStatusError(error.response?.status || error.request?.status || undefined); } else if (typeof error === "string") setError(error); } lastFetch.current = Date.now(); diff --git a/src/commons/menus.ts b/src/commons/menus.ts index d66cb6569e..b4f093f6b6 100644 --- a/src/commons/menus.ts +++ b/src/commons/menus.ts @@ -53,8 +53,7 @@ export const menus: Menu[] = [ href: details.nativeScriptsAndSC() }, { title: "Pools", key: "head.page.pool", href: routers.DELEGATION_POOLS, isSpecialPath: true }, - { title: "Delegated Representatives", key: "head.page.drep", href: routers.DREPS, isSpecialPath: true }, - { title: "Top ADA Holders", key: "glossary.topAdaHolder", href: routers.ADDRESS_LIST } + { title: "Delegated Representatives", key: "head.page.drep", href: routers.DREPS, isSpecialPath: true } ] }, { diff --git a/src/commons/resources/icons/pots-icon-dark.svg b/src/commons/resources/icons/pots-icon-dark.svg new file mode 100644 index 0000000000..13ce04b7d6 --- /dev/null +++ b/src/commons/resources/icons/pots-icon-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/commons/resources/icons/pots-icon.svg b/src/commons/resources/icons/pots-icon.svg new file mode 100644 index 0000000000..cd802bc01a --- /dev/null +++ b/src/commons/resources/icons/pots-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/commons/resources/index.ts b/src/commons/resources/index.ts index 97e68841e4..24bd3cf140 100644 --- a/src/commons/resources/index.ts +++ b/src/commons/resources/index.ts @@ -14,6 +14,8 @@ import AdaPriceIcon from "./icons/ada-price.svg"; import AdaPriceDarkIcon from "./icons/ada-price-dark.svg"; import MarketCapIcon from "./icons/market-cap.svg"; import MarketCapDarkIcon from "./icons/market-cap-dark.svg"; +import PotsIcon from "./icons/pots-icon.svg"; +import PotsIconDark from "./icons/pots-icon-dark.svg"; import CurentEpochIcon from "./icons/curent-epoch.svg"; import CurentEpochPool from "./icons/curent-epoch-pool.svg"; import CurentEpochPoolDark from "./icons/curent-epoch-pool-dark.svg"; @@ -301,6 +303,8 @@ export { AdaPriceDarkIcon, MarketCapIcon, MarketCapDarkIcon, + PotsIcon, + PotsIconDark, CurentEpochIcon, // WalletOutlineIconComponent, LiveStakeIcon, diff --git a/src/commons/routers.ts b/src/commons/routers.ts index 6784a1c365..b965ecb3b8 100644 --- a/src/commons/routers.ts +++ b/src/commons/routers.ts @@ -18,7 +18,6 @@ export const routers = { POOL_DEREGISTRATION: "/pool-de-registrations", STORY_LIST: "/stories", STORY_DETAIL: "/story/:storyId", - ADDRESS_LIST: "/addresses", ADDRESS_DETAIL: "/address/:address", TOKEN_LIST: "/tokens", TOKEN_DETAIL: "/token/:tokenId/:tabActive?", @@ -124,7 +123,6 @@ export const listRouters = [ routers.DELEGATION_POOLS, routers.POOL_CERTIFICATE, routers.POOL_DEREGISTRATION, - routers.ADDRESS_LIST, routers.TOKEN_LIST, routers.STAKE_ADDRESS_REGISTRATION, routers.STAKE_ADDRESS_DEREGISTRATION, diff --git a/src/commons/utils/api.ts b/src/commons/utils/api.ts index ff4a5b8d00..e0c50d03d8 100644 --- a/src/commons/utils/api.ts +++ b/src/commons/utils/api.ts @@ -5,7 +5,6 @@ export const API = { ANALYTICS: "addresses/analytics", DETAIL: "addresses", MIN_MAX_BALANCE: "addresses/min-max-balance", - TOP_ADDRESS: "addresses/top-addresses", TOKENS: "addresses/:address/tokens", VIEW_ADRRESSES: (txHash: string, scriptHash: string) => `scripts/contract-executions/${txHash}/${scriptHash}` }, @@ -46,6 +45,8 @@ export const API = { DREPS_LIST: "dreps/filter", DREPS_OVERVIEW: "dreps/overview" }, + POTS_OVERVIEW: "pots/overview", + CIRCULATING_SUPPLY: "supply/circulating", TOKEN: { LIST: "tokens", TOKEN_TRX: "tokens/:tokenId/txs", diff --git a/src/commons/utils/helper.ts b/src/commons/utils/helper.ts index 26a5dc2f7d..133d6ffecb 100644 --- a/src/commons/utils/helper.ts +++ b/src/commons/utils/helper.ts @@ -99,6 +99,12 @@ export const formatADA = ( return `${newValue}${syntax ?? `x 10^${exponential}`}`; } } + + if (realAda.gte(1000) && realAda.lt(10 ** numOfUnits)) { + const valueInThousands = realAda.div(1000).toFixed(2); + return `${valueInThousands}K`; + } + return numberWithCommas(realAda.toString(), decimalDigits); }; @@ -412,6 +418,8 @@ export const getDurationUnits = (inp: DurationInputArg1, unit: DurationInputArg2 const duration = moment.duration(inp, unit); const d = duration.days(); const h = duration.hours(); + const m = duration.minutes(); + const s = duration.seconds(); let humanized = ""; if (d > 1) { @@ -429,10 +437,11 @@ export const getDurationUnits = (inp: DurationInputArg1, unit: DurationInputArg2 return { d, h, - humanized + m, + s, + humanized: humanized.trim() }; }; - type blockEpochNoType = number | null | undefined; export const handleChangeLanguage = (newLang: APP_LANGUAGES, currentLanguage: APP_LANGUAGES | undefined) => { diff --git a/src/components/AddressDetail/AddressAnalytics/index.tsx b/src/components/AddressDetail/AddressAnalytics/index.tsx index 54d2ee28f8..88c264c383 100644 --- a/src/components/AddressDetail/AddressAnalytics/index.tsx +++ b/src/components/AddressDetail/AddressAnalytics/index.tsx @@ -146,7 +146,7 @@ const AddressAnalytics: React.FC<{ address?: string }> = ({ address }) => { return ( {t("analytics")}}> - + diff --git a/src/components/AddressTransactionList/index.tsx b/src/components/AddressTransactionList/index.tsx index 9e0b7cc5ed..30a030c450 100644 --- a/src/components/AddressTransactionList/index.tsx +++ b/src/components/AddressTransactionList/index.tsx @@ -17,7 +17,7 @@ import Card from "src/components/commons/Card"; import CustomTooltip from "src/components/commons/CustomTooltip"; import DropdownTokens, { TokenLink } from "src/components/commons/DropdownTokens"; import Table, { Column } from "src/components/commons/Table"; -import { DownRedUtxoDarkmode, TransferIcon, UpGreenUtxoDarkmode } from "src/commons/resources"; +import { DownRedUtxoDarkmode, TooltipIcon, TransferIcon, UpGreenUtxoDarkmode } from "src/commons/resources"; import { Img, StyledLink } from "./styles"; import { TextCardHighlight } from "../AddressDetail/AddressAnalytics/styles"; @@ -56,6 +56,11 @@ const AddressTransactionList: React.FC = ({ if (parent) { return; } + if (e.target instanceof HTMLAnchorElement) { + e.preventDefault(); + e.stopPropagation(); + return; + } if (openDetail) return openDetail(e, transaction); history.push(details.transaction(transaction.hash)); }; @@ -138,13 +143,31 @@ const AddressTransactionList: React.FC = ({ ) }, { - title:
{t("glossary.slot")}
, + title: ( + +
{t("glossary.slot")}
+ +

+ +

+
+
+ ), key: "epochSlotNo", minWidth: "50px", render: (r) =>
{r.epochSlotNo}
}, { - title:
{t("glossary.absoluteSlot")}
, + title: ( + +
{t("glossary.absoluteSlot")}
+ +

+ +

+
+
+ ), key: "slot", minWidth: "100px", render: (r) =>
{r.slot}
diff --git a/src/components/BlockDetail/BlockOverview/index.tsx b/src/components/BlockDetail/BlockOverview/index.tsx index 939e9e0b21..101d676a3a 100644 --- a/src/components/BlockDetail/BlockOverview/index.tsx +++ b/src/components/BlockDetail/BlockOverview/index.tsx @@ -138,6 +138,18 @@ const BlockOverview: React.FC = ({ data, loading, lastUpdate {`${t("common.slot")} - ${t( "glossary.absoluteSlot" )}`} + +

Slot: {t("common.explainSlot")}

+

Absolute slot: {t("common.absoluteSlot")}

+ + } + > + + + +
), value: ( diff --git a/src/components/CIPComplianceModal/styles.ts b/src/components/CIPComplianceModal/styles.ts index 4f486d48dd..ce2d6a59b5 100644 --- a/src/components/CIPComplianceModal/styles.ts +++ b/src/components/CIPComplianceModal/styles.ts @@ -82,7 +82,7 @@ export const TokenLabel = styled(CIPModalDesc)` export const CIPLabel = styled(Box)(({ theme }) => ({ [theme.breakpoints.down("md")]: { - width: "90%" + width: "100%" }, span: { marginRight: "12px", diff --git a/src/components/Contracts/UPLCTree/styles.tsx b/src/components/Contracts/UPLCTree/styles.tsx index df4abf8d8e..efdece5b9f 100644 --- a/src/components/Contracts/UPLCTree/styles.tsx +++ b/src/components/Contracts/UPLCTree/styles.tsx @@ -49,5 +49,8 @@ export const StyledTreeItem = styled(TreeItem)(({ theme }) => ({ marginLeft: 15, paddingLeft: 18, borderLeft: `1px dashed ${theme.isDark ? alpha(theme.palette.grey[200], 0.8) : alpha(theme.palette.grey[400], 0.5)}` + }, + [`& .MuiSvgIcon-root`]: { + fill: theme.isDark ? theme.palette.secondary.main : theme.palette.secondary.light } })); diff --git a/src/components/DelegationDetail/DelegationDetailChart/index.tsx b/src/components/DelegationDetail/DelegationDetailChart/index.tsx index d147e6c662..590b7ca975 100644 --- a/src/components/DelegationDetail/DelegationDetailChart/index.tsx +++ b/src/components/DelegationDetail/DelegationDetailChart/index.tsx @@ -149,7 +149,7 @@ const DelegationDetailChart: React.FC = ({ poolId }) return ( {t("common.analytics")} - + = ({ loading }) => { const history = useHistory<{ tickerNameSearch?: string; fromPath?: SpecialPath }>(); const [expanded, setExpanded] = useState(""); const [open, setOpen] = useState(false); + const [fixMax, setFixMax] = useState(6); + const [fixMin, setFixMin] = useState(0); const [addDotMin, setAddDotMin] = useState(false); const [addDotMax, setAddDotMax] = useState(false); const { pageInfo } = usePageInfo(); @@ -122,6 +125,64 @@ const DrepFilter: React.FC<{ loading: boolean }> = ({ loading }) => { }); setDateRange({ fromDate: query?.fromDate as string, toDate: query?.toDate as string }); }, [JSON.stringify(query)]); + + useEffect(() => { + let initDecimalMin = 0; + + switch (expanded) { + case "activeStake": + initDecimalMin = ( + filterParams?.minActiveVoteStake + ? BigNumber(filterParams?.minActiveVoteStake) + : BigNumber(initParams?.minActiveVoteStake).div(10 ** 6) || 0 + ) + .toString() + .split(".")[1]?.length; + break; + case "drepParticipation": + initDecimalMin = filterParams?.minGovParticipationRate + ? (filterParams?.minGovParticipationRate * 100).toString().split(".")[1]?.length + : 0; + break; + } + if (initDecimalMin >= 6 && expanded === "activeStake") { + setFixMin(6); + } else if (initDecimalMin >= 2 && expanded === "drepParticipation") { + setFixMin(2); + } else if (initDecimalMin > 0) { + setFixMin(initDecimalMin); + } else { + setFixMin(0); + } + let initDecimalMax = 0; + + switch (expanded) { + case "activeStake": + initDecimalMax = ( + filterParams?.maxActiveVoteStake + ? BigNumber(filterParams?.maxActiveVoteStake) + : BigNumber(initParams?.maxActiveVoteStake).div(10 ** 6) || 0 + ) + .toString() + .split(".")[1]?.length; + break; + case "drepParticipation": + initDecimalMax = filterParams?.maxGovParticipationRate + ? ((filterParams.maxGovParticipationRate * 10000) / 100).toString().split(".")[1]?.length + : 0; + break; + } + if (initDecimalMax >= 6 && expanded === "activeStake") { + setFixMax(6); + } else if (initDecimalMax >= 2 && expanded === "drepParticipation") { + setFixMax(2); + } else if (initDecimalMax > 0) { + setFixMax(initDecimalMax); + } else { + setFixMax(expanded === "activeStake" ? 6 : 0); + } + }, [dataRange, expanded]); + const handleReset = () => { setExpanded(false); setOpen(false); @@ -209,7 +270,13 @@ const DrepFilter: React.FC<{ loading: boolean }> = ({ loading }) => { width: "100% !important", color: theme.isDark ? theme.palette.secondary.main : theme.palette.secondary.light }} - value={Number(minValue || 0).toString() + (addDotMin ? "," : "")} + value={ + addDotMin + ? Number(minValue || 0).toString() + "," + : ["minActiveVoteStake", "minGovParticipationRate"].includes(keyOnChangeMin) + ? Number(minValue || 0).toFixed(fixMin) + : Number(minValue || 0).toString() + } onKeyDown={(event) => { const key = event.key; @@ -244,8 +311,25 @@ const DrepFilter: React.FC<{ loading: boolean }> = ({ loading }) => { let numericValue = value.replace(/[^0-9.]/g, ""); numericValue = numericValue.replace(/^0+(?!$)/, ""); + const decimals = numericValue.split(".")[1]?.length; + + if ( + ((keyOnChangeMin === "minActiveVoteStake" && decimals <= 6) || + (keyOnChangeMin === "minGovParticipationRate" && decimals <= 2)) && + decimals > 0 + ) { + setFixMin(decimals); + } else if (keyOnChangeMin === "minActiveVoteStake" && decimals > 6) { + setFixMin(6); + } else if (keyOnChangeMin === "minGovParticipationRate" && decimals > 2) { + setFixMin(2); + } else { + setFixMin(0); + } + if (addDotMin) { numericValue = (Number(numericValue.replace(/\\,/, ".")) / 10).toString(); + setFixMin(1); setAddDotMin(false); } setFilterParams({ @@ -276,7 +360,13 @@ const DrepFilter: React.FC<{ loading: boolean }> = ({ loading }) => { width: "100% !important", color: theme.isDark ? theme.palette.secondary.main : theme.palette.secondary.light }} - value={Number(maxValue).toString() + (addDotMax ? "," : "")} + value={ + addDotMax + ? Number(maxValue).toString() + "," + : ["maxActiveVoteStake", "maxGovParticipationRate"].includes(keyOnChangeMax) + ? Number(maxValue).toFixed(fixMax) + : Number(maxValue).toString() + } onKeyDown={(event) => { const key = event.key; @@ -318,7 +408,24 @@ const DrepFilter: React.FC<{ loading: boolean }> = ({ loading }) => { setAddDotMax(false); } - Number(numericValue) <= maxValueDefault && + if (Number(numericValue) <= maxValueDefault) { + const decimals = numericValue.split(".")[1]?.length; + if ( + ((keyOnChangeMax === "maxActiveVoteStake" && decimals <= 6) || + (keyOnChangeMax === "maxGovParticipationRate" && decimals <= 2)) && + decimals > 0 + ) { + setFixMax(decimals); + } else if (keyOnChangeMax === "maxActiveVoteStake" && decimals > 6) { + setFixMax(6); + } else if (keyOnChangeMax === "maxGovParticipationRate" && decimals > 2) { + setFixMax(2); + } else if (addDotMax) { + setFixMax(1); + } else { + setFixMax(0); + } + setFilterParams({ ...filterParams, [keyOnChangeMax]: @@ -334,6 +441,7 @@ const DrepFilter: React.FC<{ loading: boolean }> = ({ loading }) => { ? truncateDecimals(+numericValue, 6) : numericValue }); + } }} onKeyPress={handleKeyPress} /> diff --git a/src/components/DynamicEllipsisText/index.tsx b/src/components/DynamicEllipsisText/index.tsx index ff070337e4..3764d3fb6d 100644 --- a/src/components/DynamicEllipsisText/index.tsx +++ b/src/components/DynamicEllipsisText/index.tsx @@ -13,6 +13,7 @@ const Container = styled(Box)<{ whiteSpace?: "nowrap" | "normal" }>` width: 100%; text-align: left; transform: translateY(1px); + margin-bottom: 10px; `; const SubPart = styled("span")` diff --git a/src/components/EpochDetail/EpochBlockList/EpochBlockList.test.tsx b/src/components/EpochDetail/EpochBlockList/EpochBlockList.test.tsx index 432d78b37b..9f90673331 100644 --- a/src/components/EpochDetail/EpochBlockList/EpochBlockList.test.tsx +++ b/src/components/EpochDetail/EpochBlockList/EpochBlockList.test.tsx @@ -26,11 +26,9 @@ describe("EpochBlockList component", () => { data: mockedData }); render(); - expect(screen.getByText("Transactions")).toBeInTheDocument(); expect(screen.getByText("Block")).toBeInTheDocument(); expect(screen.getByText("Block ID")).toBeInTheDocument(); expect(screen.getByRole("link", { name: mockedData[0].hash })).toBeInTheDocument(); - expect(screen.getByText("Output")).toBeInTheDocument(); }); it("should component render the empty table", () => { diff --git a/src/components/EpochDetail/EpochBlockList/index.tsx b/src/components/EpochDetail/EpochBlockList/index.tsx index 30b98fc2c1..e6344e9a5f 100644 --- a/src/components/EpochDetail/EpochBlockList/index.tsx +++ b/src/components/EpochDetail/EpochBlockList/index.tsx @@ -7,22 +7,16 @@ import { Box } from "@mui/material"; import Card from "src/components/commons/Card"; import Table, { Column } from "src/components/commons/Table"; -import { - formatADAFull, - formatDateTimeLocal, - formatNameBlockNo, - getPageInfo, - getShortHash -} from "src/commons/utils/helper"; +import { formatDateTimeLocal, formatNameBlockNo, getPageInfo, getShortHash } from "src/commons/utils/helper"; import { details } from "src/commons/routers"; import useFetchList from "src/commons/hooks/useFetchList"; import { API } from "src/commons/utils/api"; -import ADAicon from "src/components/commons/ADAIcon"; import CustomTooltip from "src/components/commons/CustomTooltip"; import FormNowMessage from "src/components/commons/FormNowMessage"; import DatetimeTypeTooltip from "src/components/commons/DatetimeTypeTooltip"; +import { TooltipIcon } from "src/commons/resources"; -import { StyledOutput, BlueText, StyledContainer, StyledLink, PriceWrapper, Actions, TimeDuration } from "./styles"; +import { StyledContainer, StyledLink, PriceWrapper, Actions, TimeDuration } from "./styles"; interface IEpochBlockList { epochId: string; @@ -82,13 +76,31 @@ const EpochBlockList: React.FC = ({ epochId }) => { ) }, { - title:
{t("glossary.slot")}
, + title: ( + +
{t("glossary.slot")}
+ +

+ +

+
+
+ ), key: "epochSlotNo", minWidth: "50px", render: (r, index) =>
{r.epochSlotNo}
}, { - title:
{t("glossary.absoluteSlot")}
, + title: ( + +
{t("glossary.absoluteSlot")}
+ +

+ +

+
+
+ ), key: "slotNo", minWidth: "100px", render: (r, index) =>
{r.slotNo}
@@ -102,33 +114,6 @@ const EpochBlockList: React.FC = ({ epochId }) => { {formatDateTimeLocal(r.time)} ) - }, - { - title:
{t("drawer.transactions")}
, - key: "blkCount", - minWidth: "100px", - render: (r, index) => {r.txCount || 0} - }, - { - title:
{t("common.fees")}
, - key: "fees", - render: (r, index) => ( - - {formatADAFull(r.totalFees)} - - - ) - }, - { - title:
{t("glossary.output")}
, - key: "outSum", - minWidth: "100px", - render: (r, index) => ( - - {formatADAFull(r.totalOutput)} - - - ) } ]; diff --git a/src/components/GovernanceActionDetails/VotesOverview/FilterVotesOverview/index.tsx b/src/components/GovernanceActionDetails/VotesOverview/FilterVotesOverview/index.tsx index 6e8383cb06..89e22c4862 100644 --- a/src/components/GovernanceActionDetails/VotesOverview/FilterVotesOverview/index.tsx +++ b/src/components/GovernanceActionDetails/VotesOverview/FilterVotesOverview/index.tsx @@ -9,17 +9,25 @@ import { RadioGroup, Typography } from "@mui/material"; +import BigNumber from "bignumber.js"; +import { isEmpty, pickBy } from "lodash"; +import { parse, stringify } from "qs"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import { BsFillCheckCircleFill } from "react-icons/bs"; import { IoIosArrowDown, IoIosArrowUp } from "react-icons/io"; import { useHistory, useLocation, useParams } from "react-router-dom"; -import { parse, stringify } from "qs"; -import { isEmpty, pickBy } from "lodash"; -import { BsFillCheckCircleFill } from "react-icons/bs"; -import BigNumber from "bignumber.js"; +import useFetch from "src/commons/hooks/useFetch"; +import usePageInfo from "src/commons/hooks/usePageInfo"; import { FilterIcon, GovBodycon, GovernanceIcon, GovIDIcon, ResetIcon, TimeStampIcon } from "src/commons/resources"; +import { API } from "src/commons/utils/api"; +import { formatADA, formatPercent, LARGE_NUMBER_ABBREVIATIONS, truncateDecimals } from "src/commons/utils/helper"; +import DateRangeModal from "src/components/commons/CustomFilter/DateRangeModal"; +import { StyledSlider } from "src/components/commons/CustomFilterMultiRange/styles"; import CustomIcon from "src/components/commons/CustomIcon"; +import CustomTooltip from "src/components/commons/CustomTooltip"; +import { StyledInput } from "src/components/share/styled"; import { AccordionContainer, AccordionDetailsFilter, @@ -27,14 +35,6 @@ import { FilterContainer, FilterWrapper } from "src/pages/NativeScriptsAndSC/styles"; -import { StyledInput } from "src/components/share/styled"; -import { StyledSlider } from "src/components/commons/CustomFilterMultiRange/styles"; -import CustomTooltip from "src/components/commons/CustomTooltip"; -import { formatADA, formatPercent, LARGE_NUMBER_ABBREVIATIONS, truncateDecimals } from "src/commons/utils/helper"; -import DateRangeModal from "src/components/commons/CustomFilter/DateRangeModal"; -import usePageInfo from "src/commons/hooks/usePageInfo"; -import useFetch from "src/commons/hooks/useFetch"; -import { API } from "src/commons/utils/api"; import { Input } from "./styles"; @@ -59,7 +59,14 @@ export default function FilterVotesOverview() { const [open, setOpen] = useState(false); const [filterParams, setFilterParams] = useState({}); const [showDaterange, setShowDaterange] = useState(false); + const [fixMax, setFixMax] = useState(6); + const [fixMin, setFixMin] = useState(0); + const [addDotMin, setAddDotMin] = useState(false); + const [addDotMax, setAddDotMax] = useState(false); const { search } = useLocation(); + const isIOS = + /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.userAgent.includes("Mac") && "ontouchend" in document); + const query = parse(search.split("?")[1]); const [dateRange, setDateRange] = useState<{ fromDate?: string; @@ -115,6 +122,32 @@ export default function FilterVotesOverview() { setDateRange({ fromDate: query?.fromDate as string, toDate: query?.toDate as string }); }, [JSON.stringify(query)]); + useEffect(() => { + const initDecimalMin = BigNumber( + filterParams?.activeStakeFrom ? filterParams?.activeStakeFrom : initParams?.activeStakeFrom || 0 + ) + .div(10 ** 6) + .toString() + .split(".")[1]?.length; + if (initDecimalMin > 0) { + setFixMin(initDecimalMin); + } else { + setFixMin(0); + } + + const initDecimalMax = BigNumber( + filterParams?.activeStakeTo ? filterParams?.activeStakeTo : initParams?.activeStakeTo || 0 + ) + .div(10 ** 6) + .toString() + .split(".")[1]?.length; + if (initDecimalMax > 0) { + setFixMax(initDecimalMax); + } else { + setFixMax(0); + } + }, [dataRange, expanded]); + const handleReset = () => { setExpanded(false); setOpen(false); @@ -141,6 +174,15 @@ export default function FilterVotesOverview() { return; } const [min, max] = newValue || []; + + if (maxKey === "activeStakeTo" && initParams?.activeStakeTo && initParams?.activeStakeTo > 10 ** 12) { + if (min !== filterParams.activeStakeFrom) { + fixMin > 0 && setFixMin(0); + } + if (max !== filterParams.activeStakeTo) { + fixMax > 0 && setFixMax(0); + } + } setFilterParams({ ...filterParams, [minKey]: Math.min(min), [maxKey]: Math.min(max) }); }; @@ -173,20 +215,26 @@ export default function FilterVotesOverview() { } }; + function toFixedWithoutRounding(value: number, decimals: number) { + const factor = Math.pow(10, decimals); + return (Math.floor(value * factor) / factor).toFixed(decimals); + } + const groupInputRange = ( minValue: number, maxValue: number, keyOnChangeMin: string, keyOnChangeMax: string, maxValueDefault: number, - disabled = false + disabled = false, + minValueDefault?: number ) => { return ( { const key = event.key; - if ( + if (isIOS && key === "." && !event.target.value.includes(".")) { + event.preventDefault(); + setAddDotMin(true); + } else if ( !( key === "ArrowLeft" || key === "ArrowRight" || @@ -217,18 +274,39 @@ export default function FilterVotesOverview() { ...filterParams, [keyOnChangeMin]: maxValue }); + minValueDefault && + minValue < minValueDefault && + setFilterParams({ + ...filterParams, + [keyOnChangeMin]: minValueDefault * 10 ** 6 + }); }} onChange={({ target: { value } }) => { let numericValue = value.replace(/[^0-9.]/g, ""); numericValue = numericValue.replace(/^0+(?!$)/, ""); + const decimals = numericValue.split(".")[1]?.length; + if (decimals <= 6 && decimals > 0) { + setFixMin(decimals); + } else if (decimals > 6) { + setFixMin(6); + } else { + setFixMin(0); + } + + if (addDotMin) { + numericValue = (Number(numericValue.replace(/\\,/, ".")) / 10).toString(); + setFixMin(1); + setAddDotMin(false); + } + setFilterParams({ ...filterParams, [keyOnChangeMin]: +numericValue > maxValue ? 0 : ["activeStakeFrom"].includes(keyOnChangeMin) - ? truncateDecimals(+numericValue, 6) * 10 ** 6 + ? Math.round(truncateDecimals(+numericValue, 6) * 10 ** 6) : truncateDecimals(+numericValue, 6) }); }} @@ -239,7 +317,7 @@ export default function FilterVotesOverview() { { const key = event.key; - if ( + if (isIOS && key === "." && !event.target.value.includes(".")) { + event.preventDefault(); + setAddDotMax(true); + } else if ( !( key === "ArrowLeft" || key === "ArrowRight" || @@ -273,22 +360,39 @@ export default function FilterVotesOverview() { }); }} onChange={({ target: { value } }) => { - const numericValue = value + let numericValue = value .replace(/[^0-9.]/g, "") .replace(/^0+(?!$)/, "") .replace(/^0+(?=\d)/, "") .replace("%", ""); - Number(numericValue) <= maxValueDefault && + if (addDotMax) { + numericValue = (Number(numericValue.replace(/\\,/, ".")) / 10).toString(); + setAddDotMax(false); + } + + if (Number(numericValue) <= maxValueDefault) { + const decimals = numericValue.split(".")[1]?.length; + if (decimals <= 6 && decimals > 0) { + setFixMax(decimals); + } else if (decimals > 6) { + setFixMax(6); + } else if (addDotMax) { + setFixMax(1); + } else { + setFixMax(0); + } + setFilterParams({ ...filterParams, [keyOnChangeMax]: +numericValue > maxValueDefault ? maxValueDefault : ["activeStakeTo"].includes(keyOnChangeMax) - ? truncateDecimals(+numericValue, 6) * 10 ** 6 + ? Math.round(truncateDecimals(+numericValue, 6) * 10 ** 6) : truncateDecimals(+numericValue, 6) }); + } }} onKeyPress={handleKeyPress} /> @@ -713,7 +817,10 @@ export default function FilterVotesOverview() { BigNumber(initParams.activeStakeTo || 0) .div(10 ** 6) .toNumber(), - dataRange?.maxActiveStake === dataRange?.minActiveStake + dataRange?.maxActiveStake === dataRange?.minActiveStake, + BigNumber(initParams.activeStakeFrom || 0) + .div(10 ** 6) + .toNumber() )} diff --git a/src/components/GovernanceVotes/OverallVote.tsx b/src/components/GovernanceVotes/OverallVote.tsx index d7acfd8ecf..da9b7547df 100644 --- a/src/components/GovernanceVotes/OverallVote.tsx +++ b/src/components/GovernanceVotes/OverallVote.tsx @@ -157,7 +157,6 @@ const OverallVote: React.FC<{ data: GovernanceVoteDetail | null; voteId: string; - {t("pool.actionId")} @@ -208,7 +207,6 @@ const OverallVote: React.FC<{ data: GovernanceVoteDetail | null; voteId: string; height={22.27} marginTop="15px" /> - {t("pool.actionType")} @@ -237,7 +235,6 @@ const OverallVote: React.FC<{ data: GovernanceVoteDetail | null; voteId: string; height={28} marginTop="10px" /> - @@ -252,7 +249,6 @@ const OverallVote: React.FC<{ data: GovernanceVoteDetail | null; voteId: string; - {t("pool.actionMetadata")} @@ -288,7 +284,6 @@ const OverallVote: React.FC<{ data: GovernanceVoteDetail | null; voteId: string; icon={SubmissionDateIcon} marginTop="10px" /> - {t("pool.submission")} @@ -305,7 +300,6 @@ const OverallVote: React.FC<{ data: GovernanceVoteDetail | null; voteId: string; icon={SubmissionDateIcon} marginTop="10px" /> - {t("pool.expiryDate")} @@ -371,7 +365,6 @@ const OverallVote: React.FC<{ data: GovernanceVoteDetail | null; voteId: string; sx={{ [theme.breakpoints.down("md")]: { pt: "20px" } }} > -
-
{t("pool.votingPowerADA")} diff --git a/src/components/GovernanceVotes/OverviewVote.tsx b/src/components/GovernanceVotes/OverviewVote.tsx index 43c5a3a677..fa57faba2b 100644 --- a/src/components/GovernanceVotes/OverviewVote.tsx +++ b/src/components/GovernanceVotes/OverviewVote.tsx @@ -7,7 +7,6 @@ import { ActionTypeIcon, AnchorTextIcon, BlackCircleIcon, - BlackWarningIcon, CurrentStatusIcon, GovernanceIdIcon, SubmissionDateIcon, @@ -52,7 +51,6 @@ const OverviewVote: React.FC<{ data: GovernanceVoteDetail | null }> = ({ data }) - {t("pool.actionId")} @@ -98,7 +96,6 @@ const OverviewVote: React.FC<{ data: GovernanceVoteDetail | null }> = ({ data }) - {t("pool.actionType")} @@ -121,7 +118,6 @@ const OverviewVote: React.FC<{ data: GovernanceVoteDetail | null }> = ({ data }) > - = ({ data }) > - @@ -208,7 +203,6 @@ const OverviewVote: React.FC<{ data: GovernanceVoteDetail | null }> = ({ data }) icon={VotingPowerIcon} style={{ marginTop: "5px" }} /> - {t("pool.votingPowerADA")} @@ -233,7 +227,6 @@ const OverviewVote: React.FC<{ data: GovernanceVoteDetail | null }> = ({ data }) > - {t("pool.submission")} @@ -252,7 +245,6 @@ const OverviewVote: React.FC<{ data: GovernanceVoteDetail | null }> = ({ data }) > - {t("pool.expiryDate")} @@ -264,7 +256,6 @@ const OverviewVote: React.FC<{ data: GovernanceVoteDetail | null }> = ({ data }) - {t("pool.actionMetadata")} diff --git a/src/components/Home/Statistic/index.tsx b/src/components/Home/Statistic/index.tsx index cc08d0c24a..8a3f6fd753 100644 --- a/src/components/Home/Statistic/index.tsx +++ b/src/components/Home/Statistic/index.tsx @@ -1,11 +1,9 @@ -import { useMemo } from "react"; import { Link as LinkDom } from "react-router-dom"; import { useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import moment from "moment"; import BigNumber from "bignumber.js"; -import { Box } from "@mui/material"; -import { useLocalStorage } from "react-use"; +import { Box, useTheme } from "@mui/material"; import useFetch from "src/commons/hooks/useFetch"; import { useScreen } from "src/commons/hooks/useScreen"; @@ -14,17 +12,14 @@ import { AdaPriceIcon, CurrentEpochHome, CurrentEpochHomeDark, - HomeDownIcon, - HomeUpIcon, LiveStakeDarkIcon, LiveStakeIcon, - MarketCapDarkIcon, - MarketCapIcon, - UpGreenDarkmodeIcon + PotsIcon, + PotsIconDark } from "src/commons/resources"; import { details, routers } from "src/commons/routers"; import { API } from "src/commons/utils/api"; -import { API_GECKO, EXT_ADA_PRICE_URL, MAX_SLOT_EPOCH } from "src/commons/utils/constants"; +import { MAX_SLOT_EPOCH } from "src/commons/utils/constants"; import { formatADA, formatADAFull, @@ -34,26 +29,23 @@ import { } from "src/commons/utils/helper"; import ADAicon from "src/components/commons/ADAIcon"; import CustomTooltip from "src/components/commons/CustomTooltip"; -import FormNowMessage from "src/components/commons/FormNowMessage"; -import RateWithIcon from "src/components/commons/RateWithIcon"; import { RootState } from "src/stores/types"; -import useFetchIntervalFromCoinGecko from "src/commons/hooks/useFetchIntervalFromCoinGecko"; import DatetimeTypeTooltip from "src/components/commons/DatetimeTypeTooltip"; import { - AdaPrice, + CircularLegend, Content, Item, ItemIcon, ItemSkeleton, - Link, Name, ProcessActive, Progress, ProgressPending, + ProgressRewards, + ProgressTreasury, StatisticContainer, TextPending, - TimeDuration, Title, VerticalContent, WrapCardContent, @@ -72,70 +64,75 @@ const SkeletonBox = () => ( ); -const MILION = 10 ** 6; - const MAX_PERCENT_SHOW_LAST_BAR = 89; const MIN_PERCENT_SHOW_FIRST_BAR = 9; const HomeStatistic = () => { const { t } = useTranslation(); + const theme = useTheme(); const { currentEpoch, blockNo } = useSelector(({ system }: RootState) => system); const { data } = useFetch(API.STAKE.ANALYTICS, undefined, false, blockNo); + const { data: dataPostOverview, loading: loadingPots } = useFetch(API.POTS_OVERVIEW); + const { data: dataCiculatingSupply, loading: loadingciculating } = useFetch(API.CIRCULATING_SUPPLY); + const { theme: themeMode } = useSelector(({ theme }: RootState) => theme); const { liveStake = 0, activeStake = 1 } = data || {}; - const supply = BigNumber(currentEpoch?.circulatingSupply || 0).div(10 ** 6); - const liveRate = new BigNumber(liveStake).div(MILION).div(supply).multipliedBy(100); - const circulatingSupply = new BigNumber(supply).multipliedBy(MILION); + const progress = moment.utc(currentEpoch?.endTime, "YYYY-MM-DDTHH:mm:ssZ").isAfter(moment().utc()) ? (((currentEpoch?.slot || 0) / MAX_SLOT_EPOCH) * 100).toFixed(0) : 100; + + const isShowPercentText = (percent: number) => percent >= MIN_PERCENT_SHOW_FIRST_BAR; const isShowProgressPendingText = +progress < MAX_PERCENT_SHOW_LAST_BAR; - const isShowProgressActiveText = +progress > MIN_PERCENT_SHOW_FIRST_BAR; - const isShowLiveStakePercentText = liveRate.toNumber() >= MIN_PERCENT_SHOW_FIRST_BAR; - const isShowOtherStakePercentText = liveRate.toNumber() <= MAX_PERCENT_SHOW_LAST_BAR; - const [btcDataLocal, setBtcDataLocal] = useLocalStorage("btcData", null); - const [usdDataLocal, setUsdDataLocal] = useLocalStorage("usdData", null); - const { - data: btcData, - lastUpdated: lastUpdatedBtcData, - loading: loadingBtcData - } = useFetchIntervalFromCoinGecko(`${API_GECKO}?ids=cardano&vs_currency=btc`); - const { - data: usdData, - lastUpdated: lastUpdatedUsd, - loading: loadingUsdData - } = useFetchIntervalFromCoinGecko(`${API_GECKO}?ids=cardano&vs_currency=usd`); - - const btcMarket = useMemo(() => { - if (btcData?.length === 0) return null; - if (btcData && btcData?.length > 0) { - setBtcDataLocal(btcData?.[0] || null); - } - return btcData?.[0] || null; - }, [btcData]); - - const usdMarket = useMemo(() => { - if (usdData?.length === 0) return null; - if (usdData && usdData?.length > 0) { - setUsdDataLocal(usdData?.[0]); - } - return usdData?.[0] || null; - }, [usdData]); - - const { total_supply: total = 1 } = usdMarket || usdDataLocal || {}; - - const circulatingRate = circulatingSupply.div(total).div(MILION).multipliedBy(100); const slot = (currentEpoch?.slot || 0) % MAX_SLOT_EPOCH; const countdown = MAX_SLOT_EPOCH - slot; - const { d: days, h: hours, humanized } = getDurationUnits(countdown ? countdown : 0, "second"); + const { humanized, s, m, d, h } = getDurationUnits(countdown ? countdown : 0, "second"); const { humanized: humanizedActive } = getDurationUnits(slot, "second"); const epochActiveText = `Started ${humanizedActive} ago`; const epochFinishText = `Finishes in ${humanized}`; const { isGalaxyFoldSmall } = useScreen(); - const sign = Math.sign(BigNumber((usdMarket || usdDataLocal)?.price_change_percentage_24h || 0).toNumber()); + + const formattedDateTime = moment.utc(new Date()); + const numberActiveStake = new BigNumber(activeStake); + const numberLiveStake = new BigNumber(liveStake); + + const totalStake = numberActiveStake.plus(numberLiveStake); + const percentageActiveStake = numberActiveStake.dividedBy(totalStake).multipliedBy(100).toFixed(0); + + const percentageLiveStake = new BigNumber(100).minus(percentageActiveStake).toFixed(0); + + const dataResponPost = { + depositsAndFees: new BigNumber(dataPostOverview?.depositsAndFees || 0), + rewards: new BigNumber(dataPostOverview?.rewards || 0), + treasury: new BigNumber(dataPostOverview?.treasury || 0), + reserves: new BigNumber(dataPostOverview?.reserves || 0) + }; + + const totalDataPots = dataResponPost.depositsAndFees + .plus(dataResponPost.rewards) + .plus(dataResponPost.treasury) + .plus(dataResponPost.reserves); + + const depositsPercentage = dataResponPost.depositsAndFees.dividedBy(totalDataPots).multipliedBy(100).toFixed(0); + const rewardsPercentage = dataResponPost.rewards.dividedBy(totalDataPots).multipliedBy(100).toFixed(0); + const treasuryPercentage = dataResponPost.treasury.dividedBy(totalDataPots).multipliedBy(100).toFixed(0); + const reservesPercentage = new BigNumber(100) + .minus(depositsPercentage) + .minus(rewardsPercentage) + .minus(treasuryPercentage) + .toFixed(0); + + const FIX_MAX_SUPPLY = new BigNumber(45_000_000_000_000_000); + const ADA_FIX_MAX_SUPPLY = FIX_MAX_SUPPLY.div(10 ** 6); + const circulatingSupplyNumber = new BigNumber(dataCiculatingSupply ?? 0); + + const circulatingSupplyPercentage = circulatingSupplyNumber + .dividedBy(ADA_FIX_MAX_SUPPLY) + .multipliedBy(100) + .toFixed(0); return ( { alignItems="stretch" data-testid="home-statistic" > - - {(usdMarket || usdDataLocal) && (btcMarket || btcDataLocal) && !loadingBtcData && !loadingUsdData ? ( - - - - - - {t("stats.adaPrice")} - - - 0 ? (themeMode === "light" ? HomeUpIcon : UpGreenDarkmodeIcon) : HomeDownIcon} - alt="Home up icon" - width={30} - height={30} - /> - - ${(usdMarket || usdDataLocal)?.current_price || 0} - - - - - {(btcMarket || btcDataLocal)?.current_price} BTC - - - - - - - - - - ) : ( - - )} - - - {(usdMarket || usdDataLocal) && !loadingUsdData ? ( - - - - - - {t("glossary.marketCap")} - - - ${numberWithCommas((usdMarket || usdDataLocal)?.market_cap)} - - - - - - - - - - ) : ( - - )} - {currentEpoch ? ( @@ -248,14 +172,14 @@ const HomeStatistic = () => { - {isShowProgressActiveText && `${+progress || 0}%`} + {isShowPercentText(+progress) && `${+progress || 0}%`} {isShowProgressPendingText && ( - {days}d {hours}h + {d}d {h}h )} @@ -263,14 +187,23 @@ const HomeStatistic = () => { - palette.secondary.light}> - {t("glossary.uniqueAccounts")}: {numberWithCommas(currentEpoch?.account)} - - - palette.secondary.light} fontSize={"12px"}> - {t("glossary.endTimestamp")}: {formatDateTimeLocal(currentEpoch?.endTime)} + + + + + palette.secondary.light} fontSize={"12px"}> + {t("glossary.currentTime")}: {formatDateTimeLocal(formattedDateTime.format())} + + - + + + + palette.secondary.light} fontSize={"12px"}> + {t("glossary.timeRemaining")}: {`${humanized} ${m}m ${s}s`} + + + @@ -280,9 +213,9 @@ const HomeStatistic = () => { )} - {data && (usdMarket || usdDataLocal) ? ( + {data ? ( - + @@ -301,69 +234,227 @@ const HomeStatistic = () => { {liveStake ? formatADA(liveStake) : t("common.N/A")} - {currentEpoch?.circulatingSupply !== null && ( - - - - {isShowLiveStakePercentText && `${liveRate.toFixed(0, BigNumber.ROUND_DOWN)}%`} - - - - {isShowOtherStakePercentText && ( - palette.secondary.main}> - {liveStake ? 100 - +liveRate.toFixed(0, BigNumber.ROUND_DOWN) : 0}% - - )} - - - )} + + + + {isShowPercentText(+percentageActiveStake) && ( + + {percentageActiveStake}% + + )} + + + + {isShowPercentText(+percentageLiveStake) && ( + palette.secondary.main}>{percentageLiveStake}% + )} + + - palette.secondary.light}> - {t("glossary.activeStake")} (){": "} - + + + + palette.secondary.light}> + {t("glossary.activeStake")} {": "} + + + {activeStake ? `${formatADA(activeStake)} ADA` : t("common.N/A")} + + + + + + + palette.secondary.light}> + + {t("glossary.liveStake")} {": "} + + + + {liveStake ? `${formatADA(liveStake)} ADA` : t("common.N/A")}{" "} + + + + + + + + + + ) : ( + + )} + + + {!loadingciculating ? ( + + + + + {t("glossary.circulatingSupply")} + + + + + {dataCiculatingSupply ? formatADA((dataCiculatingSupply ?? 0) * 10 ** 6) : t("common.N/A")} + + + + + + {`${circulatingSupplyPercentage}%`} + + + + + + + + + + palette.secondary.light}> + Circulating Supply {": "} + + + {dataCiculatingSupply + ? `${formatADA((dataCiculatingSupply ?? 0) * 10 ** 6)} ADA` + : t("common.N/A")} + + + + + + + palette.secondary.light}> + + Fix Max Supply {": "} + + + 45B ADA + + + + + + + + ) : ( + + )} + + + {!loadingPots ? ( + + + + + Pots + + + + {formatADA(+totalDataPots)} + + + + {isShowPercentText(+depositsPercentage) && ( + {`${depositsPercentage}%`} + )} + + + + {isShowPercentText(+rewardsPercentage) && ( + palette.secondary.main}>{rewardsPercentage}% + )} + + + + {isShowPercentText(+treasuryPercentage) && ( + palette.secondary.main}>{treasuryPercentage}% + )} + + + + {isShowPercentText(+reservesPercentage) && ( + palette.secondary.main}>{reservesPercentage}% + )} + + + + + + + palette.secondary.light}> + Deposits:{" "} + + + {activeStake ? formatADA(dataPostOverview?.depositsAndFees) : t("common.N/A")} + + + + + + + + palette.secondary.light}> + Treasury:{" "} + - {activeStake ? formatADA(activeStake) : t("common.N/A")} + {dataPostOverview?.treasury ? formatADA(dataPostOverview?.treasury) : t("common.N/A")} + + + + palette.secondary.light}> - - - {t("glossary.circulatingSupply")} (){": "} + Rewards:{" "} + + + {dataPostOverview?.rewards ? formatADA(dataPostOverview?.rewards) : t("common.N/A")} + + + + + + palette.secondary.light}> + Reserves:{" "} - {currentEpoch?.circulatingSupply === null - ? t("common.N/A") - : formatADA(circulatingSupply.toString())}{" "} + {dataPostOverview?.reserves ? formatADA(dataPostOverview?.reserves) : t("common.N/A")}{" "} - {currentEpoch?.circulatingSupply !== null && ( - - - ({circulatingRate.toFixed(0, BigNumber.ROUND_DOWN)}%) - - - )} - - - + + + ) : ( )} diff --git a/src/components/Home/Statistic/style.ts b/src/components/Home/Statistic/style.ts index 78ab6a5afa..8edc0a6256 100644 --- a/src/components/Home/Statistic/style.ts +++ b/src/components/Home/Statistic/style.ts @@ -146,7 +146,8 @@ export const Progress = styled("div")` font-weight: var(--font-weight-bold); color: ${({ theme }) => theme.palette.common.white}; color: ${(props) => props.theme.palette.primary.contrastText}; - margin-bottom: 0.5rem; + margin-bottom: 10px; + margin-top: 8px; `; export const ProcessActive = styled("div")<{ rate: number }>` @@ -163,6 +164,25 @@ export const ProgressPending = styled(ProcessActive)<{ rate: number }>` background-color: ${(props) => props.theme.palette.primary[200]}; `; +export const ProgressRewards = styled(ProcessActive)<{ rate: number }>` + width: ${(props) => props.rate}%; + background-color: ${({ theme }) => (theme.isDark ? "#E8564B" : "#FE938C")}; +`; + +export const ProgressTreasury = styled(ProcessActive)<{ rate: number }>` + width: ${(props) => props.rate}%; + background-color: ${({ theme }) => (theme.isDark ? "#1B998B" : "#1B998B")}; +`; + +export const CircularLegend = styled(Box)<{ color: string }>` + width: 10px; + height: 10px; + border-radius: 50%; + margin-right: 5px; + background-color: ${(props) => props.color}; + flex-shrink: 0; +`; + export const TextPending = styled("span")` text-overflow: ellipsis; overflow: hidden; diff --git a/src/components/Home/Trending/TransactionChart/TransactionChart.test.tsx b/src/components/Home/Trending/TransactionChart/TransactionChart.test.tsx index 4bae8b3d30..ffb20c5c59 100644 --- a/src/components/Home/Trending/TransactionChart/TransactionChart.test.tsx +++ b/src/components/Home/Trending/TransactionChart/TransactionChart.test.tsx @@ -17,44 +17,28 @@ jest.mock("react-redux", () => ({ useSelector: jest.fn() })); -const mockItemDay: TransactionChartIF = { +const mockItemOneMonth: TransactionChartIF = { date: "2023-06-02 00:00", metadata: 9187, simpleTransactions: 32908, smartContract: 45187 }; -const mockItemWeek: TransactionChartIF = { - date: "2023-06-02 00:00", - metadata: 19187, - simpleTransactions: 132908, - smartContract: 145187 -}; - -const mockItem2Week: TransactionChartIF = { +const mockItemOneYear: TransactionChartIF = { date: "2023-06-02 00:00", metadata: 29187, simpleTransactions: 232908, smartContract: 245187 }; -const mockItemMonth: TransactionChartIF = { - date: "2023-06-02 00:00", - metadata: 49187, - simpleTransactions: 432908, - smartContract: 445187 -}; - const getData = (url: string) => { switch (url.split("/")[2]) { - case "ONE_DAY": - return mockItemDay; - case "ONE_WEEK": - return mockItemWeek; - case "TWO_WEEK": - return mockItem2Week; + case "ONE_MONTH": + return mockItemOneMonth; + case "ONE_YEAR": + return mockItemOneYear; default: - return mockItemMonth; + return mockItemOneMonth; } }; @@ -74,7 +58,7 @@ describe("TransactionChart", () => { it("Test", () => { render(); - expect(screen.getByText(/Transactions in the last 24 hours/i)).toBeInTheDocument(); + expect(screen.getByText(/Transaction history/i)).toBeInTheDocument(); }); it("renders Transaction Chart", async () => { @@ -84,27 +68,29 @@ describe("TransactionChart", () => { ); - expect(screen.getByText(/Transactions in the last 24 hours/i)).toBeInTheDocument(); - const oneDay = screen.getByText("24h"); - const oneWeek = screen.getByText("1w"); - const twoWeek = screen.getByText("2w"); + expect(screen.getByText(/Transaction history/i)).toBeInTheDocument(); + const threeYear = screen.getByText("3y"); + const threeMonth = screen.getByText("3m"); + const oneYear = screen.getByText("1y"); const oneMonth = screen.getByText("1m"); - expect(oneDay).toBeInTheDocument(); - expect(oneWeek).toBeInTheDocument(); - expect(twoWeek).toBeInTheDocument(); + expect(threeYear).toBeInTheDocument(); + expect(threeMonth).toBeInTheDocument(); + expect(oneYear).toBeInTheDocument(); expect(oneMonth).toBeInTheDocument(); - expect(screen.getByTestId("trx")).toHaveTextContent(numberWithCommas(mockItemDay.metadata)); - expect(screen.getByTestId("simple")).toHaveTextContent(numberWithCommas(mockItemDay.smartContract)); - expect(screen.getByTestId("complex")).toHaveTextContent(numberWithCommas(mockItemDay.simpleTransactions)); + expect(screen.getByTestId("trx")).toHaveTextContent(numberWithCommas(mockItemOneMonth.metadata || 0)); + expect(screen.getByTestId("simple")).toHaveTextContent(numberWithCommas(mockItemOneMonth.smartContract || 0)); + expect(screen.getByTestId("complex")).toHaveTextContent(numberWithCommas(mockItemOneMonth.simpleTransactions || 0)); - await userEvent.click(twoWeek); + await userEvent.click(oneYear); await waitFor(async () => { - expect(screen.getByText("Transactions in two weeks")).toBeInTheDocument(); - expect(screen.getByTestId("trx")).toHaveTextContent(numberWithCommas(mockItem2Week.metadata)); - expect(screen.getByTestId("simple")).toHaveTextContent(numberWithCommas(mockItem2Week.smartContract)); - expect(screen.getByTestId("complex")).toHaveTextContent(numberWithCommas(mockItem2Week.simpleTransactions)); + expect(screen.getByText("Transaction history")).toBeInTheDocument(); + expect(screen.getByTestId("trx")).toHaveTextContent(numberWithCommas(mockItemOneYear.metadata || 0)); + expect(screen.getByTestId("simple")).toHaveTextContent(numberWithCommas(mockItemOneYear.smartContract || 0)); + expect(screen.getByTestId("complex")).toHaveTextContent( + numberWithCommas(mockItemOneYear.simpleTransactions || 0) + ); }); }); }); diff --git a/src/components/Overview/ChartOverview/index.tsx b/src/components/Overview/ChartOverview/index.tsx index c4fc014882..038494a487 100644 --- a/src/components/Overview/ChartOverview/index.tsx +++ b/src/components/Overview/ChartOverview/index.tsx @@ -101,7 +101,7 @@ const ChartOverview = (props: TypeProps) => { }; return ( - + {loading ? ( diff --git a/src/components/Overview/ChartPreDefined/index.tsx b/src/components/Overview/ChartPreDefined/index.tsx new file mode 100644 index 0000000000..f76dd44a86 --- /dev/null +++ b/src/components/Overview/ChartPreDefined/index.tsx @@ -0,0 +1,128 @@ +import { BarChart, Bar, XAxis, YAxis, Legend, ResponsiveContainer } from "recharts"; +import { Box, Grid, useMediaQuery } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import { t } from "i18next"; + +import useFetch from "src/commons/hooks/useFetch"; +import { API } from "src/commons/utils/api"; +import NoRecord from "src/components/commons/NoRecord"; +import { numberWithCommas } from "src/commons/utils/helper"; +import FetchDataErr from "src/components/commons/FetchDataErr"; + +import { StyledCard, StyledSkeleton } from "./styles"; + +interface PropsLegend { + registeredPool: number | null | undefined; + activePool: number | null | undefined; + color: string; + isMobile: boolean; +} + +interface REGISTERED_STAKEPOOLS { + registeredPool: number | null; + activePool: number | null; +} + +export default function PreDefinedVotesChart() { + const theme = useTheme(); + const isMobile = useMediaQuery("(max-width:600px)"); + const { data, loading, error, statusError } = useFetch( + API.NETWORK_MONITORING_API.REGISTERED_STAKEPOOLS + ); + const activePool = data?.activePool; + const registeredPool = data?.registeredPool; + + if (loading) + return ( + + + + + + ); + + if (error && (statusError || 0) < 500) + return ( + + {t("overview.page.preDefinedVotes")} + + + ); + + if (error && (statusError || 0) >= 500) + return ( + + {t("overview.page.preDefinedVotes")} + + + ); + return ( + + {error ? ( + + ) : ( + + {t("overview.page.preDefinedVotes")} + {loading ? ( + + + + ) : ( + + + + + + + renderLegend({ activePool, registeredPool, color: theme.palette.secondary.main, isMobile }) + } + verticalAlign="bottom" + align="left" + /> + + + + + + )} + + )} + + ); +} + +const renderLegend = ({ registeredPool, activePool, color, isMobile }: PropsLegend) => { + return ( + + + + Auto-abstain + + {registeredPool ? numberWithCommas(registeredPool) : t("N/A")} + + + + + No-confidence + {activePool ? numberWithCommas(activePool) : t("N/A")} + + + ); +}; diff --git a/src/components/Overview/ChartPreDefined/styles.ts b/src/components/Overview/ChartPreDefined/styles.ts new file mode 100644 index 0000000000..5b4c97e038 --- /dev/null +++ b/src/components/Overview/ChartPreDefined/styles.ts @@ -0,0 +1,32 @@ +import { styled } from "@mui/material"; +import { Link } from "react-router-dom"; + +import { CommonSkeleton } from "src/components/commons/CustomSkeleton"; + +export const StyledSkeleton = styled(CommonSkeleton)` + border-radius: var(--border-radius); + min-height: 130px; +`; +export const StyledCard = { + Container: styled("div")` + height: 100%; + background: ${(props) => props.theme.palette.secondary[0]}; + border-radius: 12px; + box-shadow: ${(props) => props.theme.shadow.card}; + padding: 12px 32px 32px 32px; + margin-top: 56px; + `, + Title: styled("p")` + font-size: 20px; + font-weight: 500; + text-align: start; + color: ${(props) => props.theme.palette.secondary.main}; + margin-bottom: 0; + `, + Link: styled(Link)` + font-size: 16px; + font-family: var(--font-family-text) !important; + color: ${(props) => props.theme.palette.primary.main} !important; + cursor: pointer; + ` +}; diff --git a/src/components/Overview/TabOverview/styles.ts b/src/components/Overview/TabOverview/styles.ts index bb83f02c32..b5fd5b7a9b 100644 --- a/src/components/Overview/TabOverview/styles.ts +++ b/src/components/Overview/TabOverview/styles.ts @@ -10,7 +10,7 @@ export const ContainerTab = styled("div")` box-shadow: ${(props) => props.theme.shadow.card}; position: relative; display: flex; - margin-top: 62px; + margin-top: 36px; `; export const StyledLink = styled(Link)` diff --git a/src/components/Overview/index.tsx b/src/components/Overview/index.tsx index eeee7b9d1b..cba7e734c7 100644 --- a/src/components/Overview/index.tsx +++ b/src/components/Overview/index.tsx @@ -11,6 +11,7 @@ import ChartOverview from "./ChartOverview"; import TabOverview from "./TabOverview"; import NoRecord from "../commons/NoRecord"; import FetchDataErr from "../commons/FetchDataErr"; +import PreDefinedVotesChart from "./ChartPreDefined"; export default function OverviewComponent() { const { data, loading, error, statusError } = useFetch(API.GOVERNANCE_OVERVIEW.OVERVIEW); @@ -34,6 +35,7 @@ export default function OverviewComponent() {
+ ); diff --git a/src/components/StakeDetail/StakeAnalytics/index.tsx b/src/components/StakeDetail/StakeAnalytics/index.tsx index c0714b898d..fbc9b2ad91 100644 --- a/src/components/StakeDetail/StakeAnalytics/index.tsx +++ b/src/components/StakeDetail/StakeAnalytics/index.tsx @@ -271,7 +271,7 @@ const StakeAnalytics: React.FC<{ stakeAddress?: string }> = ({ stakeAddress }) = {t("common.analytics")}} > - + {isMobile ? ( diff --git a/src/components/StakeDetail/StakeTab/Tabs/DelegationHistoryTab.tsx b/src/components/StakeDetail/StakeTab/Tabs/DelegationHistoryTab.tsx index f18c2a7d6d..bae81d8d9f 100644 --- a/src/components/StakeDetail/StakeTab/Tabs/DelegationHistoryTab.tsx +++ b/src/components/StakeDetail/StakeTab/Tabs/DelegationHistoryTab.tsx @@ -91,7 +91,14 @@ const DelegationHistoryTab: React.FC<{ stakeAddress?: string; isMobile?: boolean total: fetchData.total, onChange: (page, size) => history.replace({ search: stringify({ page, size }) }, history.location.state) }} - onClickRow={(e, r: DelegationHistory) => history.push(details.delegation(r.poolId))} + onClickRow={(e, r: DelegationHistory) => { + if (e.target instanceof HTMLAnchorElement) { + e.preventDefault(); + e.stopPropagation(); + return; + } + history.push(details.delegation(r.poolId)); + }} /> ); }; diff --git a/src/components/StakeDetail/StakeTab/Tabs/TransactionTab.tsx b/src/components/StakeDetail/StakeTab/Tabs/TransactionTab.tsx index e8b70976f4..f6e9b9dc36 100644 --- a/src/components/StakeDetail/StakeTab/Tabs/TransactionTab.tsx +++ b/src/components/StakeDetail/StakeTab/Tabs/TransactionTab.tsx @@ -5,7 +5,7 @@ import { useHistory, useLocation } from "react-router-dom"; import { MouseEvent } from "react"; import useFetchList from "src/commons/hooks/useFetchList"; -import { DownRedUtxoDarkmode, TransferIcon, UpGreenUtxoDarkmode } from "src/commons/resources"; +import { DownRedUtxoDarkmode, TooltipIcon, TransferIcon, UpGreenUtxoDarkmode } from "src/commons/resources"; import receiveImg from "src/commons/resources/images/receiveImg.svg"; import sendImg from "src/commons/resources/images/sendImg.svg"; import { details } from "src/commons/routers"; @@ -142,12 +142,30 @@ const TransactionListFull: React.FC = ({ render: (r) => {r.epochNo} }, { - title: t("glossary.slot"), + title: ( + +
{t("glossary.slot")}
+ +

+ +

+
+
+ ), key: "epochSlotNo", minWidth: "50px" }, { - title: t("glossary.absoluteSlot"), + title: ( + +
{t("glossary.absoluteSlot")}
+ +

+ +

+
+
+ ), key: "slot", minWidth: "100px" }, diff --git a/src/components/StakingLifeCycleSearch/StackingLifeCycleSearch.test.tsx b/src/components/StakingLifeCycleSearch/StackingLifeCycleSearch.test.tsx index 49c8024578..f47e088999 100644 --- a/src/components/StakingLifeCycleSearch/StackingLifeCycleSearch.test.tsx +++ b/src/components/StakingLifeCycleSearch/StackingLifeCycleSearch.test.tsx @@ -30,11 +30,9 @@ describe("StakingLifeCycleSearch", () => { const mockedUseHistory = useHistory as jest.Mock; mockedUseHistory.mockReturnValue({ push }); render(); - const textBox = screen.getByRole("searchbox"); const btnSearch = screen.getAllByRole("button", { name: /search/i }); - expect(textBox).toBeInTheDocument(); expect(btnSearch[0]).toBeInTheDocument(); }); }); diff --git a/src/components/TokenDetail/TokenOverview/TokenOverview.test.tsx b/src/components/TokenDetail/TokenOverview/TokenOverview.test.tsx index a271ce1cbb..a1d12bf227 100644 --- a/src/components/TokenDetail/TokenOverview/TokenOverview.test.tsx +++ b/src/components/TokenDetail/TokenOverview/TokenOverview.test.tsx @@ -43,7 +43,6 @@ describe("TokenOverview component", () => { render(); expect(screen.getByText(/total supply/i)).toBeInTheDocument(); expect(screen.getByText(/https:\/\/example\.com\/token/i)).toBeInTheDocument(); - expect(screen.getByText(/number of holders/i)).toBeInTheDocument(); }); it("should user goto token registry page", () => { diff --git a/src/components/TokenDetail/TokenOverview/index.tsx b/src/components/TokenDetail/TokenOverview/index.tsx index 4d5ae3182a..6822236c8f 100644 --- a/src/components/TokenDetail/TokenOverview/index.tsx +++ b/src/components/TokenDetail/TokenOverview/index.tsx @@ -1,27 +1,12 @@ import { Box } from "@mui/material"; import BigNumber from "bignumber.js"; -import React, { useContext, useState } from "react"; +import React, { useState } from "react"; import { useTranslation } from "react-i18next"; -import { - ExchageIcon, - FileGuard, - RewardIconComponent, - SlotIcon, - TimeIconComponent, - USDIconComponent -} from "src/commons/resources"; -import { - formatDateTimeLocal, - formatNumberDivByDecimals, - getShortHash, - numberWithCommas, - formatNumberTotalSupply, - tokenRegistry -} from "src/commons/utils/helper"; +import { FileGuard, SlotIcon, TimeIconComponent, USDIconComponent } from "src/commons/resources"; +import { formatDateTimeLocal, getShortHash, formatNumberTotalSupply, tokenRegistry } from "src/commons/utils/helper"; import CopyButton from "src/components/commons/CopyButton"; import DetailHeader from "src/components/commons/DetailHeader"; -import { OverviewMetadataTokenContext } from "src/pages/TokenDetail"; import CustomTooltip from "src/components/commons/CustomTooltip"; import DatetimeTypeTooltip from "src/components/commons/DatetimeTypeTooltip"; @@ -33,16 +18,14 @@ BigNumber.config({ DECIMAL_PLACES: 40 }); interface ITokenOverview { data: IToken | null; loading: boolean; - currentHolders: number; lastUpdated?: number; } -const TokenOverview: React.FC = ({ data, loading, currentHolders, lastUpdated }) => { +const TokenOverview: React.FC = ({ data, loading, lastUpdated }) => { const { t } = useTranslation(); const [openModal, setOpenModal] = useState(false); const [policyId, setPolicyId] = useState(""); const decimalToken = data?.decimals || data?.metadata?.decimals || 0; - const { txCountRealtime } = useContext(OverviewMetadataTokenContext); const listItem = [ { @@ -101,17 +84,6 @@ const TokenOverview: React.FC = ({ data, loading, currentHolders ) }, - { - title: ( - - - {t("common.totalTxs")} - - - ), - icon: ExchageIcon, - value: numberWithCommas(txCountRealtime || data?.txCount) - }, { title: {t("glossary.tokenType")}, icon: USDIconComponent, @@ -128,43 +100,6 @@ const TokenOverview: React.FC = ({ data, loading, currentHolders ) }, - { - title: ( - - - {t("glossary.numberOfHolders")} - - - ), - icon: RewardIconComponent, - value: numberWithCommas(currentHolders || data?.numberOfHolders || "") - }, - { - title: ( - - - - {t("glossary.totalVolumn")} - - - - ), - icon: ExchageIcon, - value: formatNumberDivByDecimals(data?.totalVolume || "", decimalToken || 0) - }, - { - title: ( - - - - {t("glossary.volume24h")} - - - - ), - icon: USDIconComponent, - value: formatNumberDivByDecimals(data?.volumeIn24h || "", data?.metadata?.decimals || 0) - }, { title: ( @@ -186,6 +121,10 @@ const TokenOverview: React.FC = ({ data, loading, currentHolders ), icon: TimeIconComponent, value: {formatDateTimeLocal(data?.tokenLastActivity || "")} + }, + { + title: <>, + value: <> } ]; diff --git a/src/components/TokenDetail/TokenTableData/TokenTableData.test.tsx b/src/components/TokenDetail/TokenTableData/TokenTableData.test.tsx index 7fd2c4ea73..96b45e0cf1 100644 --- a/src/components/TokenDetail/TokenTableData/TokenTableData.test.tsx +++ b/src/components/TokenDetail/TokenTableData/TokenTableData.test.tsx @@ -163,7 +163,7 @@ describe("TokenTransaction component", () => { it("should component render", () => { render(); expect(screen.getByRole("columnheader", { name: /tx hash/i })).toBeInTheDocument(); - expect(screen.getByRole("columnheader", { name: /addresses/i })).toBeInTheDocument(); + expect(screen.getByRole("columnheader", { name: /Block/i })).toBeInTheDocument(); screen.logTestingPlaygroundURL(); }); diff --git a/src/components/TokenDetail/TokenTableData/TokenTopHolder.tsx b/src/components/TokenDetail/TokenTableData/TokenTopHolder.tsx index ae84257fda..553a8946c1 100644 --- a/src/components/TokenDetail/TokenTableData/TokenTopHolder.tsx +++ b/src/components/TokenDetail/TokenTableData/TokenTopHolder.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React from "react"; import { useHistory, useLocation } from "react-router-dom"; import { stringify } from "qs"; import { useTranslation } from "react-i18next"; @@ -19,10 +19,9 @@ interface ITokenTopHolder { tokenId: string; totalSupply?: number; decimal?: number; - setCurrentHolder?: (holders: number) => void; } -const TokenTopHolder: React.FC = ({ tabActive, tokenId, totalSupply, decimal, setCurrentHolder }) => { +const TokenTopHolder: React.FC = ({ tabActive, tokenId, totalSupply, decimal }) => { const { t } = useTranslation(); const { search } = useLocation(); const history = useHistory(); @@ -36,11 +35,6 @@ const TokenTopHolder: React.FC = ({ tabActive, tokenId, totalSu blockKey ); const { error } = fetchData; - useEffect(() => { - if (fetchData.total && setCurrentHolder) { - setCurrentHolder(fetchData.total || 0); - } - }, [fetchData.total, setCurrentHolder]); const columns: Column[] = [ { diff --git a/src/components/TokenDetail/TokenTableData/TokenTransaction.tsx b/src/components/TokenDetail/TokenTableData/TokenTransaction.tsx index 95d86e8c9c..ab3dfd7a22 100644 --- a/src/components/TokenDetail/TokenTableData/TokenTransaction.tsx +++ b/src/components/TokenDetail/TokenTableData/TokenTransaction.tsx @@ -15,8 +15,9 @@ import { API } from "src/commons/utils/api"; import ADAicon from "src/components/commons/ADAIcon"; import FormNowMessage from "src/components/commons/FormNowMessage"; import DatetimeTypeTooltip from "src/components/commons/DatetimeTypeTooltip"; +import { TooltipIcon } from "src/commons/resources"; -import { Flex, Label, SmallText, StyledLink, PriceValue, TimeDuration } from "./styles"; +import { SmallText, StyledLink, PriceValue, TimeDuration } from "./styles"; interface ITokenTransaction { tabActive: string; @@ -77,54 +78,33 @@ const TokenTransaction: React.FC = ({ tabActive, tokenId }) = ) }, { - title: t("glossary.slot"), + title: ( + +
{t("glossary.slot")}
+ +

+ +

+
+
+ ), key: "epochSlotNo", minWidth: "50px" }, { - title: t("glossary.absoluteSlot"), + title: ( + +
{t("glossary.absoluteSlot")}
+ +

+ +

+
+
+ ), key: "slot", minWidth: "100px" }, - - { - title: t("glossary.address"), - key: "addresses", - minWidth: "200px", - render(r, index) { - return ( - <> - - -
- - - {getShortHash(r.addressesInput[0])} - - -
- {r.addressesInput.length > 1 && ...} -
-
- - -
- - - {getShortHash(r.addressesOutput[0])} - - -
- {r.addressesOutput.length > 1 && ...} -
-
- - ); - } - }, { title: t("fees"), key: "fee", @@ -137,19 +117,6 @@ const TokenTransaction: React.FC = ({ tabActive, tokenId }) = ) - }, - { - title: t("drawer.ouput"), - minWidth: "120px", - key: "outSum", - render: (r) => ( - - - {formatADAFull(r.totalOutput)}  - - - - ) } ]; diff --git a/src/components/TokenDetail/TokenTableData/index.tsx b/src/components/TokenDetail/TokenTableData/index.tsx index 2a9d60bf4a..098845b3c5 100644 --- a/src/components/TokenDetail/TokenTableData/index.tsx +++ b/src/components/TokenDetail/TokenTableData/index.tsx @@ -6,27 +6,23 @@ import { useHistory, useParams } from "react-router-dom"; import { details } from "src/commons/routers"; import CustomAccordion, { TTab } from "src/components/commons/CustomAccordion"; -import { MetadataIcon, PeopleIcon, TransactionIcon, UnionTokenIcon } from "../../../commons/resources"; +import { MetadataIcon, TransactionIcon, UnionTokenIcon } from "../../../commons/resources"; import TokenMetaData from "./TokenMetadata"; import TokenMinting from "./TokenMinting"; -import TokenTopHolder from "./TokenTopHolder"; import TokenTransaction from "./TokenTransaction"; interface ITokenTableData { totalSupply?: number; metadata?: ITokenMetadata; metadataJson?: string; - setCurrentHolder?: (holders: number) => void; loading?: boolean; metadataCIP25?: Transaction["metadata"][0]["metadataCIP25"]; metadataCIP60?: Transaction["metadata"][0]["metadataCIP25"]; } const TokenTableData: React.FC = ({ - totalSupply, metadata, metadataJson, - setCurrentHolder, loading, metadataCIP25, metadataCIP60 @@ -43,20 +39,6 @@ const TokenTableData: React.FC = ({ children: , icon: TransactionIcon }, - { - key: "topHolders", - label:
{t("glossary.topHolders")}
, - children: ( - - ), - icon: PeopleIcon - }, { key: "tokenMint", label:
{t("tab.minting")}
, diff --git a/src/components/TopAddresses/ByADABalance/TopAddressesByADABalance.test.tsx b/src/components/TopAddresses/ByADABalance/TopAddressesByADABalance.test.tsx deleted file mode 100644 index bfec72e6bf..0000000000 --- a/src/components/TopAddresses/ByADABalance/TopAddressesByADABalance.test.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { screen, cleanup, fireEvent } from "@testing-library/react"; -import { Router } from "react-router-dom"; -import { createMemoryHistory } from "history"; - -import { render } from "src/test-utils"; -import Table, { Column } from "src/components/commons/Table"; -import useFetchList from "src/commons/hooks/useFetchList"; - -import TopAddressesByADABalance from "./index"; - -const mockData = { - data: [ - { - address: "Ae2tdPwUPEYwFx4dmJheyNPPYXtvHbJLeCaA96o6Y2iiUL18cAt7AizN2zG", - balance: "2083824242810424", - txCount: "1837" - } - ] -}; - -jest.mock("src/commons/hooks/useFetchList"); - -describe("Top addresses by ADA balance view", () => { - afterEach(() => { - cleanup(); - }); - - it("should render Top addresses page", async () => { - const mockUseFetch = useFetchList as jest.Mock; - await mockUseFetch.mockReturnValue({ data: [] }); - render(); - expect(useFetchList).toBeCalled(); - }); - - it("should show correct data in table", async () => { - const mockUseFetchList = useFetchList as jest.Mock; - mockUseFetchList.mockReturnValue(mockData); - render(); - expect(screen.getByText(/Ae2td/)).toBeInTheDocument(); - }); - - it("renders the table with given column and data", () => { - const data = [ - { - test: "Test Data" - } - ]; - const columns: Column<{ test: string }>[] = [ - { - title: "Test Column", - key: "test", - render: (r) =>
{r.test}
- } - ]; - render(); - - expect(screen.getByText("Test Column")).toBeInTheDocument(); - expect(screen.getByText("Test Data")).toBeInTheDocument(); - }); - - it("renders the table with given column and data", () => { - const columns: Column<{ test: string }>[] = [ - { - title: "Test Column", - key: "test", - render: (r) =>
{r.test}
- } - ]; - - const data = [ - { - test: "Test Data" - } - ]; - render(
); - - expect(screen.getByText("Test Column")).toBeInTheDocument(); - expect(screen.getByText("Test Data")).toBeInTheDocument(); - }); - - it("should navigate to the correct route when button is clicked", async () => { - const mockUseFetchList = useFetchList as jest.Mock; - mockUseFetchList.mockReturnValue(mockData); - const history = createMemoryHistory(); - - render( - - - - ); - - const AddressesItem = screen.getByText(/Ae2td/); - fireEvent.click(AddressesItem); - expect(history.location.pathname).toBe("/address/Ae2tdPwUPEYwFx4dmJheyNPPYXtvHbJLeCaA96o6Y2iiUL18cAt7AizN2zG"); - }); -}); diff --git a/src/components/TopAddresses/ByADABalance/index.tsx b/src/components/TopAddresses/ByADABalance/index.tsx deleted file mode 100644 index 5cf3b8ce3e..0000000000 --- a/src/components/TopAddresses/ByADABalance/index.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { Box } from "@mui/material"; -import { useState } from "react"; -import { useHistory } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import { useSelector } from "react-redux"; -import { SelectChangeEvent } from "@mui/material/Select/SelectInput"; - -import useFetchList from "src/commons/hooks/useFetchList"; -import { details } from "src/commons/routers"; -import { API } from "src/commons/utils/api"; -import { formatADAFull, numberWithCommas } from "src/commons/utils/helper"; -import ADAicon from "src/components/commons/ADAIcon"; -import FormNowMessage from "src/components/commons/FormNowMessage"; -import Table, { Column } from "src/components/commons/Table"; -import DynamicEllipsisText from "src/components/DynamicEllipsisText"; - -import { Actions, PageSize, PerPage, SelectMui, StyledLink, StyledMenuItem, TimeDuration } from "./styles"; - -const perPages = [10, 20, 50, 100]; - -const TopAddressesByADABalance = () => { - const { t } = useTranslation(); - const blockKey = useSelector(({ system }: RootState) => system.blockKey); - const history = useHistory(); - const [pageSize, setPageSize] = useState("50"); - const { data, error, statusError, initialized, loading, lastUpdated } = useFetchList( - API.ADDRESS.TOP_ADDRESS, - { page: 0, size: +pageSize }, - false, - blockKey - ); - - const columns: Column
[] = [ - { - title:
{t("glossary.address")}
, - key: "address", - minWidth: 170, - maxWidth: "35vw", - render: (r, index) => ( - - - - ) - }, - { - title:
{t("common.balance")}
, - key: "balance", - minWidth: 60, - render: (r, index) => ( - - - {formatADAFull(r.balance)} - - - - ) - }, - { - title:
{t("glossary.transactionCount")}
, - minWidth: 120, - key: "transaction_count", - render: (r, index) => ( - - {numberWithCommas(r.txCount) || 0} - - ) - } - ]; - return ( - - - {!error && } - - ) => setPageSize(event.target.value as string)} - displayEmpty - inputProps={{ "aria-label": "Without label" }} - sx={{ color: ({ palette }) => palette.secondary.main }} - MenuProps={{ - MenuListProps: { - sx: { - bgcolor: ({ palette }) => `${palette.secondary[0]} !important` - } - }, - PaperProps: { - sx: { - bgcolor: ({ palette }) => `${palette.secondary[0]} !important` - } - } - }} - > - {perPages.map((item) => ( - - `${palette.secondary.main} !important`}>{item} - - ))} - - {t("glossary.address")} - - -
({ [theme.breakpoints.between("sm", "md")]: { minHeight: "55vh" } }) }} - onClickRow={(_, r) => history.push(details.address(r.address))} - /> - - ); -}; - -export default TopAddressesByADABalance; diff --git a/src/components/TopAddresses/ByADABalance/styles.ts b/src/components/TopAddresses/ByADABalance/styles.ts deleted file mode 100644 index 197a7770af..0000000000 --- a/src/components/TopAddresses/ByADABalance/styles.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { styled, Box, MenuItem } from "@mui/material"; -import { Link } from "react-router-dom"; - -import CustomSelect from "src/components/commons/CustomSelect"; - -export const Actions = styled(Box)(() => ({ - display: "flex", - justifyContent: "space-between", - alignItems: "center", - flexWrap: "wrap", - marginTop: -10 -})); - -export const PageSize = styled(Box)(() => ({ - display: "flex", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 3 -})); - -export const TimeDuration = styled("small")(({ theme }) => ({ - color: theme.palette.secondary.light, - display: "block", - margin: "12px 0px" -})); - -export const StyledLink = styled(Link)` - font-family: var(--font-family-text) !important; - color: ${(props) => props.theme.palette.primary.main} !important; -`; - -export const PerPage = styled("div")` - margin-left: 8px; - color: ${({ theme }) => theme.palette.secondary.light}; -`; - -export const SelectMui = styled(CustomSelect)(({ theme }) => ({ - borderRadius: "4px", - fontSize: 14, - minWidth: 50, - border: `1px solid ${theme.palette.primary[200]}`, - color: theme.palette.secondary.main, - "& > li": { - color: `${theme.palette.secondary.main} !important` - } -})); - -export const StyledMenuItem = styled(MenuItem)(({ theme }) => ({ - "&:hover, &.Mui-selected": { - backgroundColor: theme.palette.primary[200] + " !important" - } -})); diff --git a/src/components/TransactionDetail/TransactionMetadata/Summary/Summary.test.tsx b/src/components/TransactionDetail/TransactionMetadata/Summary/Summary.test.tsx index 69ad71cd32..33a14d7eea 100644 --- a/src/components/TransactionDetail/TransactionMetadata/Summary/Summary.test.tsx +++ b/src/components/TransactionDetail/TransactionMetadata/Summary/Summary.test.tsx @@ -6,6 +6,20 @@ import { details } from "src/commons/routers"; import Summary from "."; +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn() + })) +}); + const mockToken = { assetName: "Asset 1", assetQuantity: 100, diff --git a/src/components/TransactionDetail/TransactionMetadata/TransactionMetadata.test.tsx b/src/components/TransactionDetail/TransactionMetadata/TransactionMetadata.test.tsx index e1ef38b488..605f632a8e 100644 --- a/src/components/TransactionDetail/TransactionMetadata/TransactionMetadata.test.tsx +++ b/src/components/TransactionDetail/TransactionMetadata/TransactionMetadata.test.tsx @@ -167,19 +167,23 @@ const mockTransaction = { stakeAddress: "stake-address-1" } ], - metadataHash: "metadata-hash-2", - metadata: [ - { - label: 1, - value: "value-1", - metadataCIP20: {}, - metadataCIP83: {}, - metadataCIP25: { tokenMap: undefined }, - metadataCIP60: { tokenMap: undefined } - } - ] + metadataHash: "metadata-hash-2" } as Transaction; +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn() + })) +}); + describe("TransactionMetadata component", () => { it("should component render", () => { render(); diff --git a/src/components/TransactionDetail/TransactionOverview/index.tsx b/src/components/TransactionDetail/TransactionOverview/index.tsx index 87596c8385..ee8f65c9cb 100644 --- a/src/components/TransactionDetail/TransactionOverview/index.tsx +++ b/src/components/TransactionDetail/TransactionOverview/index.tsx @@ -12,7 +12,8 @@ import { TotalOutput, ExchageAltIcon, CubeIconComponent, - SlotIcon + SlotIcon, + TooltipIcon } from "src/commons/resources"; import { formatADAFull, formatDateTimeLocal, formatNameBlockNo } from "src/commons/utils/helper"; import { MAX_SLOT_EPOCH } from "src/commons/utils/constants"; @@ -244,9 +245,19 @@ const TransactionOverview: React.FC = ({ data, loading }) => { icon: SlotIcon, title: ( - - {`${t("common.slot")} - ${t("glossary.absoluteSlot")}`} - + {`${t("common.slot")} - ${t("glossary.absoluteSlot")}`} + +

Slot: {t("common.explainSlot")}

+

Absolute slot: {t("common.absoluteSlot")}

+
+ } + > + + + + ), value: ( diff --git a/src/components/TransactionLists/index.tsx b/src/components/TransactionLists/index.tsx index b63f42c4c9..1062cc4dc9 100644 --- a/src/components/TransactionLists/index.tsx +++ b/src/components/TransactionLists/index.tsx @@ -9,15 +9,16 @@ import { formatADAFull, formatDateTimeLocal, formatNameBlockNo, getShortHash } f import { details } from "src/commons/routers"; import useFetchList from "src/commons/hooks/useFetchList"; import usePageInfo from "src/commons/hooks/usePageInfo"; +import { TooltipIcon } from "src/commons/resources"; import CustomTooltip from "../commons/CustomTooltip"; -import SelectedIcon from "../commons/SelectedIcon"; import ADAicon from "../commons/ADAIcon"; import FormNowMessage from "../commons/FormNowMessage"; import Table, { Column } from "../commons/Table"; import Card from "../commons/Card"; import { Actions, StyledLink, TimeDuration } from "./styles"; import DatetimeTypeTooltip from "../commons/DatetimeTypeTooltip"; +import { Capitalize } from "../commons/CustomText/styles"; interface TransactionListProps { underline?: boolean; @@ -25,17 +26,9 @@ interface TransactionListProps { openDetail?: (_: MouseEvent, r: Transactions) => void; selected?: string | null; showTabView?: boolean; - handleClose: () => void; } -const TransactionList: React.FC = ({ - underline = false, - url, - openDetail, - selected, - showTabView, - handleClose -}) => { +const TransactionList: React.FC = ({ underline = false, url, selected, showTabView }) => { const { t } = useTranslation(); const history = useHistory(); const { pageInfo, setSort } = usePageInfo(); @@ -43,10 +36,15 @@ const TransactionList: React.FC = ({ const fetchData = useFetchList(url, { ...pageInfo }, false, blockKey); const mainRef = useRef(document.querySelector("#main")); - const onClickRow = (_: MouseEvent, r: Transactions) => { - if (openDetail) return openDetail(_, r); + const onClickRow = (e: MouseEvent, r: Transactions) => { + if (e.target instanceof HTMLAnchorElement) { + e.preventDefault(); + e.stopPropagation(); + return; + } history.push(details.transaction(r.hash)); }; + const { error } = fetchData; const columns: Column[] = [ { @@ -74,7 +72,10 @@ const TransactionList: React.FC = ({ render: (r, index) => { const { blockName, tooltip } = formatNameBlockNo(r.blockNo, r.epochNo) || getShortHash(r.blockHash); return ( - + {blockName} @@ -93,13 +94,31 @@ const TransactionList: React.FC = ({ ) }, { - title: {t("glossary.slot")}, + title: ( + + {t("glossary.slot")} + +

+ +

+
+
+ ), key: "epochSlotNo", minWidth: 60, render: (r, index) => {r.epochSlotNo} }, { - title: t("glossary.absoluteSlot"), + title: ( + + {t("glossary.absoluteSlot")}{" "} + +

+ +

+
+
+ ), key: "slot", minWidth: 60 }, @@ -120,63 +139,6 @@ const TransactionList: React.FC = ({ sort: ({ columnKey, sortValue }) => { sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); } - }, - { - title: ( - - {t("glossary.outputInAda")} - - ), - minWidth: 120, - key: "outSum", - render: (r, index) => ( - - {formatADAFull(r.totalOutput)} - - {selected === r.hash && } - - ), - sort: ({ columnKey, sortValue }) => { - sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); - } - }, - { - title: {t("glossary.inputAddress")}, - key: "addressesInput", - minWidth: 120, - render: (r, index) => ( - - {r?.addressesInput?.slice(0, 2).map((address, idx) => ( - - - - {getShortHash(address)} - - - - ))} - {r?.addressesInput?.length > 2 ? ... : ""} - - ) - }, - { - title: {t("glossary.outpuAddress")}, - key: "addressesOutput", - minWidth: 120, - render: (r, index) => ( - - {r?.addressesOutput?.slice(0, 2).map((address, idx) => ( - - - - {getShortHash(address)} - - - - ))} - {r?.addressesOutput?.length > 2 ? ... : ""} - - ) } ]; const { pathname } = window.location; @@ -205,7 +167,6 @@ const TransactionList: React.FC = ({ mainRef.current?.scrollTo({ top: 0, behavior: "smooth" }); history.replace({ search: stringify({ ...pageInfo, page, size }) }); }, - handleCloseDetailView: handleClose, hideLastPage: true }} onClickRow={onClickRow} diff --git a/src/components/TransactionListsFull/index.tsx b/src/components/TransactionListsFull/index.tsx index 0f6f7fadc2..c9b816d1d6 100644 --- a/src/components/TransactionListsFull/index.tsx +++ b/src/components/TransactionListsFull/index.tsx @@ -8,7 +8,7 @@ import Card from "../commons/Card"; import Table, { Column } from "../commons/Table"; import { formatADAFull, formatDateTimeLocal, getPageInfo, getShortHash } from "../../commons/utils/helper"; import { details } from "../../commons/routers"; -import { Label, StyledLink, StyledContainer } from "./styles"; +import { StyledLink, StyledContainer } from "./styles"; import CustomTooltip from "../commons/CustomTooltip"; import useFetchList from "../../commons/hooks/useFetchList"; import ADAicon from "../commons/ADAIcon"; @@ -66,59 +66,6 @@ const TransactionListFull: React.FC = ({ ) }, - { - title: {t("glossary.address")}, - key: "address", - minWidth: 120, - render(r, index) { - return ( -
- - -
- {r.addressesInput.slice(0, 1).map((tx, key) => { - return ( - - - {getShortHash(tx)} - - - ); - })} - {r.addressesInput.length > 1 && ( - - ... - - )} -
-
- - -
- {r.addressesOutput.slice(0, 1).map((tx, key) => { - return ( - - - {getShortHash(tx)} - - - ); - })} - {r.addressesOutput.length > 1 && ( - - ... - - )} -
-
-
- ); - } - }, { title: {t("common.fees")}, key: "fee", @@ -129,17 +76,6 @@ const TransactionListFull: React.FC = ({ ) - }, - { - title: {t("glossary.outputInAda")}, - minWidth: 120, - key: "ouput", - render: (r) => ( - - {formatADAFull(r.totalOutput)} - - - ) } ]; diff --git a/src/components/commons/CustomFilterMultiRange/index.tsx b/src/components/commons/CustomFilterMultiRange/index.tsx index 1f6bc57e97..881b604373 100644 --- a/src/components/commons/CustomFilterMultiRange/index.tsx +++ b/src/components/commons/CustomFilterMultiRange/index.tsx @@ -72,6 +72,8 @@ const CustomFilterMultiRange: React.FC = () => { const history = useHistory<{ tickerNameSearch?: string; fromPath?: SpecialPath }>(); const [expanded, setExpanded] = useState(""); const [open, setOpen] = useState(false); + const [fixMax, setFixMax] = useState(2); + const [fixMin, setFixMin] = useState(0); const [addDotMin, setAddDotMin] = useState(false); const [addDotMax, setAddDotMax] = useState(false); const { pageInfo } = usePageInfo(); @@ -96,7 +98,7 @@ const CustomFilterMultiRange: React.FC = () => { minBlockLifetime: +(dataRange?.minLifetimeBlock || 0), maxBlockLifetime: +(dataRange?.maxLifetimeBlock || 0), minVotingPower: +(dataRange?.minVotingPower || 0), - maxVotingPower: +(dataRange?.maxVotingPower || 0), + maxVotingPower: +(dataRange?.maxVotingPower?.toFixed(4) || 0), minGovParticipationRate: +(dataRange?.minGovParticipationRate || 0), maxGovParticipationRate: +(dataRange?.maxGovParticipationRate || 0) }; @@ -123,6 +125,74 @@ const CustomFilterMultiRange: React.FC = () => { ...(query?.maxGovParticipationRate && { maxGovParticipationRate: +(query?.maxGovParticipationRate || 0) }) }); }, [JSON.stringify(query)]); + + useEffect(() => { + let initDecimalMin = 0; + + switch (expanded) { + case "poolSaturation": + initDecimalMin = filterParams?.minSaturation ? filterParams?.minSaturation.toString().split(".")[1]?.length : 0; + break; + case "poolParticipation": + initDecimalMin = filterParams?.minGovParticipationRate + ? filterParams?.minGovParticipationRate.toString().split(".")[1]?.length + : 0; + break; + case "poolVoting": + initDecimalMin = filterParams?.minVotingPower + ? ((filterParams?.minVotingPower * 10000) / 100).toString().split(".")[1]?.length + : 2; + break; + } + + if (initDecimalMin >= 2) { + setFixMin(2); + } else if (initDecimalMin > 0) { + setFixMin(initDecimalMin); + } else { + setFixMin(0); + } + + let initDecimalMax = 0; + + switch (expanded) { + case "poolSaturation": + initDecimalMax = Number( + filterParams?.maxSaturation ? filterParams?.maxSaturation : initParams?.maxSaturation || 0 + ) + .toString() + .split(".")[1]?.length; + break; + case "poolParticipation": + initDecimalMax = Number( + filterParams?.maxGovParticipationRate + ? filterParams?.maxGovParticipationRate + : initParams?.maxGovParticipationRate || 0 + ) + .toString() + .split(".")[1]?.length; + break; + case "poolVoting": + initDecimalMax = ( + (Number(filterParams?.maxVotingPower ? filterParams?.maxVotingPower : initParams?.maxVotingPower || 0) * + 10000) / + 100 + ) + + .toString() + .split(".")[1]?.length; + break; + } + + if (initDecimalMax >= 2) { + setFixMax(2); + } else if (initDecimalMax > 0) { + setFixMax(initDecimalMax); + } else { + setFixMax(0); + } + }, [dataRange, expanded]); + const handleReset = () => { setExpanded(false); setOpen(false); @@ -178,6 +248,11 @@ const CustomFilterMultiRange: React.FC = () => { [filterParams] ); + function toFixedWithoutRounding(value: number, decimals: number) { + const factor = Math.pow(10, decimals); + return (Math.floor(value * factor) / factor).toFixed(decimals); + } + const groupInputRange = ( minValue: number, maxValue: number, @@ -198,11 +273,22 @@ const CustomFilterMultiRange: React.FC = () => { width: "100% !important", color: theme.isDark ? theme.palette.secondary.main : theme.palette.secondary.light }} - value={Number(minValue || 0).toString() + (addDotMin ? "," : "")} + value={ + addDotMin + ? Number(minValue || 0).toString() + "," + : ["minSaturation", "minGovParticipationRate", "minVotingPower"].includes(keyOnChangeMin) + ? Number(minValue || 0).toFixed(fixMin) + : Number(minValue || 0).toString() + } onKeyDown={(event) => { const key = event.key; - if (isIOS && key === "." && !event.target.value.includes(".")) { + if ( + isIOS && + key === "." && + !event.target.value.includes(".") && + ["minSaturation", "minGovParticipationRate", "minVotingPower"].includes(keyOnChangeMin) + ) { event.preventDefault(); setAddDotMin(true); } else if ( @@ -232,8 +318,18 @@ const CustomFilterMultiRange: React.FC = () => { let numericValue = value.replace(/[^0-9.]/g, ""); numericValue = numericValue.replace(/^0+(?!$)/, ""); + const decimals = numericValue.split(".")[1]?.length; + if (decimals <= 2 && decimals > 0) { + setFixMin(decimals); + } else if (decimals > 2) { + setFixMin(2); + } else { + setFixMin(0); + } + if (addDotMin) { numericValue = (Number(numericValue.replace(/\\,/, ".")) / 10).toString(); + setFixMin(1); setAddDotMin(false); } @@ -247,9 +343,11 @@ const CustomFilterMultiRange: React.FC = () => { : ["minPledge"].includes(keyOnChangeMin) ? +numericValue * 10 ** 6 : ["minSaturation"].includes(keyOnChangeMin) - ? parseFloat(numericValue).toFixed(2) + ? toFixedWithoutRounding(parseFloat(numericValue), 2) : ["minActiveStake"].includes(keyOnChangeMin) ? truncateDecimals(+numericValue, 6) * 10 ** 6 + : ["minVotingPower"].includes(keyOnChangeMin) + ? truncateDecimals(+numericValue / 100, 4) : numericValue }); }} @@ -266,11 +364,22 @@ const CustomFilterMultiRange: React.FC = () => { width: "100% !important", color: theme.isDark ? theme.palette.secondary.main : theme.palette.secondary.light }} - value={Number(maxValue).toString() + (addDotMax ? "," : "")} + value={ + addDotMax + ? Number(maxValue).toString() + "," + : ["maxSaturation", "maxGovParticipationRate", "maxVotingPower"].includes(keyOnChangeMax) + ? Number(maxValue).toFixed(fixMax) + : Number(maxValue).toString() + (addDotMax ? "," : "") + } onKeyDown={(event) => { const key = event.key; - if (isIOS && key === "." && !event.target.value.includes(".")) { + if ( + isIOS && + key === "." && + !event.target.value.includes(".") && + ["maxSaturation", "maxGovParticipationRate", "maxVotingPower"].includes(keyOnChangeMax) + ) { event.preventDefault(); setAddDotMax(true); } else if ( @@ -313,20 +422,34 @@ const CustomFilterMultiRange: React.FC = () => { setAddDotMax(false); } - Number(numericValue) <= maxValueDefault && + if ( + Number(numericValue) <= maxValueDefault || + (["maxVotingPower"].includes(keyOnChangeMax) && +numericValue / 100 <= maxValueDefault) + ) { + const decimals = numericValue.split(".")[1]?.length; + if (decimals <= 2 && decimals > 0) { + setFixMax(decimals); + } else if (decimals > 2) { + setFixMax(2); + } else if (addDotMax) { + setFixMax(1); + } else { + setFixMax(0); + } + setFilterParams({ ...filterParams, - [keyOnChangeMax]: - +numericValue > maxValueDefault - ? maxValueDefault - : ["maxGovParticipationRate"].includes(keyOnChangeMax) - ? truncateToTwoDecimals(+numericValue) / 100 - : ["maxPledge"].includes(keyOnChangeMax) - ? +numericValue * 10 ** 6 - : ["maxSaturation"].includes(keyOnChangeMax) - ? parseFloat(numericValue).toFixed(2) - : numericValue + [keyOnChangeMax]: ["maxGovParticipationRate"].includes(keyOnChangeMax) + ? truncateToTwoDecimals(+numericValue) / 100 + : ["maxPledge"].includes(keyOnChangeMax) + ? +numericValue * 10 ** 6 + : ["maxSaturation"].includes(keyOnChangeMax) + ? toFixedWithoutRounding(parseFloat(numericValue), 2) + : ["maxVotingPower"].includes(keyOnChangeMax) + ? truncateDecimals((+numericValue * 100) / 10000, 4) + : numericValue }); + } }} onKeyPress={handleKeyPress} /> @@ -1068,8 +1191,8 @@ const CustomFilterMultiRange: React.FC = () => { {formatPercent(dataRange?.maxVotingPower || 0)} {groupInputRange( - filterParams.minVotingPower || 0, - filterParams.maxVotingPower ?? (initParams.maxVotingPower || 0), + ((filterParams.minVotingPower || 0) * 10000) / 100, + ((filterParams.maxVotingPower ?? (initParams.maxVotingPower || 0)) * 10000) / 100, "minVotingPower", "maxVotingPower", initParams.maxVotingPower, diff --git a/src/components/commons/CustomModal/styles.ts b/src/components/commons/CustomModal/styles.ts index c77891a48c..5299ed04aa 100644 --- a/src/components/commons/CustomModal/styles.ts +++ b/src/components/commons/CustomModal/styles.ts @@ -18,11 +18,15 @@ export const ModalContainer = styled(Box)<{ maxHeight: "95vh", boxSizing: "border-box", [theme.breakpoints.down("md")]: { - padding: "30px 30px 40px" + padding: "30px 30px 40px", + maxHeight: "90vh", + overflowY: "hidden" }, [theme.breakpoints.down("sm")]: { padding: "20px 15px 25px", - maxWidth: "98vw" + maxWidth: "98vw", + maxHeight: "80vh", + overflowY: "hidden" } })); diff --git a/src/components/commons/DetailHeader/styles.ts b/src/components/commons/DetailHeader/styles.ts index 1b4bdc7ea2..bba1015610 100644 --- a/src/components/commons/DetailHeader/styles.ts +++ b/src/components/commons/DetailHeader/styles.ts @@ -398,11 +398,11 @@ export const CardItem = styled(Grid)(({ theme, length, wide, item [theme.breakpoints.down("sm")]: { ":nth-of-type(even)": { paddingRight: wide ? 15 : "0 !important", - paddingLeft: 15 + paddingLeft: 3 }, ":nth-of-type(odd)": { paddingLeft: wide ? 15 : "0 !important", - paddingRight: 10 + paddingRight: 3 } } })); diff --git a/src/components/commons/DetailView/DetailViewBlock.test.tsx b/src/components/commons/DetailView/DetailViewBlock.test.tsx deleted file mode 100644 index 8c6c7a2c53..0000000000 --- a/src/components/commons/DetailView/DetailViewBlock.test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { createMemoryHistory } from "history"; -import { Router } from "react-router-dom"; -import userEvent from "@testing-library/user-event"; -import { useSelector } from "react-redux"; - -import { act, render, screen } from "src/test-utils"; -import useFetch from "src/commons/hooks/useFetch"; -import { details } from "src/commons/routers"; - -import DetailViewBlock from "./DetailViewBlock"; -const mockData: BlockDetail = { - blockNo: 1234, - epochNo: 10, - epochSlotNo: 100, - slotNo: 500, - hash: "blockHash123", - slotLeader: "slotLeader123", - time: "2023-07-03 12:34:56Z", - totalFees: 1000, - totalOutput: 5000, - txCount: 10, - totalSlot: 10000, - confirmation: 3 -}; -jest.mock("src/commons/hooks/useFetch"); -jest.mock("react-redux", () => { - return { - __esModule: true, - ...jest.requireActual("react-redux"), - useSelector: jest.fn() - }; -}); - -describe("DetailViewBlock componentËť", () => { - beforeEach(() => { - (useFetch as jest.Mock).mockReturnValue({ - data: mockData, - lastUpdated: "2023-09-03 12:34:56Z" - }); - const mockUseSelector = useSelector as jest.Mock; - mockUseSelector - .mockReturnValueOnce(mockData.epochNo) - .mockReturnValueOnce(mockData.blockNo) - .mockReturnValue({ sidebar: true }); - }); - - it("should component render", () => { - render(); - const epochNo = screen.getByText(mockData.blockNo); - const blockHash = screen.getByRole("link", { name: new RegExp(mockData.hash) }); - - expect(epochNo).toBeInTheDocument(); - expect(blockHash).toBeInTheDocument(); - }); - - it("should view more button was clicked", () => { - const history = createMemoryHistory(); - render( - - - - ); - const viewDetail = screen.getByRole("link", { name: /view details/i }); - act(() => { - userEvent.click(viewDetail); - }); - expect(history.location.pathname).toBe(details.block(mockData.blockNo)); - }); -}); diff --git a/src/components/commons/DetailView/DetailViewBlock.tsx b/src/components/commons/DetailView/DetailViewBlock.tsx deleted file mode 100644 index b833425fa8..0000000000 --- a/src/components/commons/DetailView/DetailViewBlock.tsx +++ /dev/null @@ -1,282 +0,0 @@ -import { useTheme } from "@mui/material"; -import React, { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { BiChevronRight } from "react-icons/bi"; -import { CgArrowsExchange, CgClose } from "react-icons/cg"; -import { useSelector } from "react-redux"; - -import useFetch from "src/commons/hooks/useFetch"; -import { CubeIconComponent, RocketIcon } from "src/commons/resources"; -import { details } from "src/commons/routers"; -import { API } from "src/commons/utils/api"; -import { MAX_SLOT_EPOCH } from "src/commons/utils/constants"; -import { formatADAFull, formatDateTimeLocal, formatNameBlockNo, getShortHash } from "src/commons/utils/helper"; -import { RootState } from "src/stores/types"; - -import ADAicon from "../ADAIcon"; -import CopyButton from "../CopyButton"; -import CustomIcon from "../CustomIcon"; -import CustomTooltip from "../CustomTooltip"; -import FormNowMessage from "../FormNowMessage"; -import ProgressCircle from "../ProgressCircle"; -import ViewAllButton from "../ViewAllButton"; -import ViewMoreButton from "../ViewMoreButton"; -import { - BlockDefault, - CloseButton, - DetailLabel, - DetailLabelSkeleton, - DetailLink, - DetailLinkIcon, - DetailLinkName, - DetailLinkRight, - DetailValue, - DetailValueSkeleton, - DetailsInfoItem, - EpochNumber, - EpochText, - Group, - HeaderContainer, - IconSkeleton, - Item, - ItemName, - ItemValue, - ListItem, - ProgressSkeleton, - StyledLink, - TimeDuration, - ViewDetailContainer, - ViewDetailDrawer, - ViewDetailHeader, - ViewDetailScroll -} from "./styles"; -import DatetimeTypeTooltip from "../DatetimeTypeTooltip"; - -type DetailViewBlockProps = { - blockNo?: number | string; - handleClose: () => void; - open?: boolean; -}; - -const DetailViewBlock: React.FC = (props) => { - const { t } = useTranslation(); - const { blockNo, handleClose, open } = props; - const theme = useTheme(); - const currentBlockNo = useSelector(({ system }: RootState) => system.blockNo); - const epochNo = useSelector(({ system }: RootState) => system.currentEpoch?.no); - const [lastUpdated, setLastUpdated] = useState(); - const [urlFetch, setUrlFetch] = useState(""); - const { data, loading } = useFetch(urlFetch, undefined, false); - useEffect(() => { - if (data) setLastUpdated(Date.now()); - }, [data, currentBlockNo]); - - useEffect(() => { - if (!blockNo) { - setUrlFetch(""); - } else { - setUrlFetch(`${API.BLOCK.DETAIL}/${blockNo}`); - } - }, [blockNo]); - - const renderContent = () => { - if (!data || loading || !epochNo) { - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {new Array(4).fill(0).map((_, index) => { - return ( - - - - - - - - - ); - })} - - {new Array(2).fill(0).map((_, index) => { - return ( - - - - - - - - - - - ); - })} - - - - - ); - } - const { blockName, tooltip } = formatNameBlockNo(data?.blockNo, data?.epochNo); - const confirmation = Math.max(0, currentBlockNo ? currentBlockNo - (data.blockNo || 0) : data.confirmation); - return ( - <> - - - - - - - - - - - - - - - - - {data?.epochNo !== null ? data?.epochNo : "_"} - - {t("glossary.epoch")} - - - - - - {t("glossary.block")} - - - {blockName} - - - - - - {t("common.slot")} - - {data?.epochSlotNo} - /{data?.maxEpochSlot || MAX_SLOT_EPOCH} - - - - - - {t("glossary.blockId")} - - - - {getShortHash(data?.hash)} - - - - - - - {t("glossary.absoluteSlot")} - {data.slotNo} - - - {t("createdAt")} - - {formatDateTimeLocal(data.time || "")} - - - - {confirmation > 1 ? t("glossary.comfirmations") : t("glossary.comfirmation")} - {confirmation} - - - {t("glossary.transactionfees")} - - {formatADAFull(data?.totalFees)} - - - - - {t("glossary.totalOutputInAda")} - - {formatADAFull(data?.totalOutput)} - - - - - - - - - - - {t("glossary.transactions")} - - - - - - - - - - - - - ); - }; - - return ( - - {renderContent()} - - ); -}; - -export default DetailViewBlock; diff --git a/src/components/commons/DetailView/DetailViewEpoch.test.tsx b/src/components/commons/DetailView/DetailViewEpoch.test.tsx deleted file mode 100644 index 0e0fb8771d..0000000000 --- a/src/components/commons/DetailView/DetailViewEpoch.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen } from "src/test-utils"; -import useFetch from "src/commons/hooks/useFetch"; -import { formatADAFull } from "src/commons/utils/helper"; - -import DetailViewEpoch from "./DetailViewEpoch"; - -const mockedData = { - blkCount: 20947, - endTime: "2023/06/14 21:44:28", - maxSlot: 432000, - no: 417, - outSum: 229341415829835740, - rewardsDistributed: null, - startTime: "2023/06/09 21:47:26", - status: "REWARDING", - txCount: 375738 -}; - -jest.mock("src/commons/hooks/useFetch"); - -describe("DetailViewEpoch component", () => { - beforeEach(() => { - const mockedUseFetch = useFetch as jest.Mock; - mockedUseFetch.mockReturnValue({ - data: mockedData, - lastUpdated: "2023/06/14 21:44:28" - }); - }); - it("rendering component on PC", () => { - render(); - expect(screen.getAllByText(/View Details/i)[0]).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: /20947/i })).toBeInTheDocument(); - expect(screen.getByText(mockedData.txCount)).toBeInTheDocument(); - expect(screen.getByText(formatADAFull(mockedData.outSum))).toBeInTheDocument(); - }); -}); diff --git a/src/components/commons/DetailView/DetailViewEpoch.tsx b/src/components/commons/DetailView/DetailViewEpoch.tsx deleted file mode 100644 index e8c90d9d39..0000000000 --- a/src/components/commons/DetailView/DetailViewEpoch.tsx +++ /dev/null @@ -1,314 +0,0 @@ -import { Box, useTheme } from "@mui/material"; -import React, { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { BiChevronRight } from "react-icons/bi"; -import { CgClose } from "react-icons/cg"; -import { useSelector } from "react-redux"; - -import useFetch from "src/commons/hooks/useFetch"; -import { BlockIcon, CubeIconComponent, RocketIcon } from "src/commons/resources"; -import { details } from "src/commons/routers"; -import { API } from "src/commons/utils/api"; -import { MAX_SLOT_EPOCH } from "src/commons/utils/constants"; -import { formatADAFull, formatDateTimeLocal } from "src/commons/utils/helper"; - -import ADAicon from "../ADAIcon"; -import CustomIcon from "../CustomIcon"; -import CustomTooltip from "../CustomTooltip"; -import FormNowMessage from "../FormNowMessage"; -import ProgressCircle from "../ProgressCircle"; -import ViewAllButton from "../ViewAllButton"; -import ViewMoreButton from "../ViewMoreButton"; -import { - BlockDefault, - CloseButton, - DetailLabel, - DetailLabelSkeleton, - DetailLink, - DetailLinkIcon, - DetailLinkRight, - DetailValue, - DetailValueSkeleton, - DetailsInfoItem, - EpochNumber, - EpochText, - Group, - HeaderContainer, - IconSkeleton, - Item, - ItemName, - ItemValue, - ListItem, - ProgressSkeleton, - TimeDuration, - ViewDetailContainer, - ViewDetailDrawer, - ViewDetailHeader, - ViewDetailScroll -} from "./styles"; -import DatetimeTypeTooltip from "../DatetimeTypeTooltip"; - -type DetailViewEpochProps = { - epochNo?: number; - handleClose: () => void; - callback: (callback: (data: IDataEpoch[]) => IDataEpoch[]) => void; - open?: boolean; -}; - -const DetailViewEpoch: React.FC = ({ epochNo, handleClose, callback, open }) => { - const { currentEpoch, blockNo } = useSelector(({ system }: RootState) => system); - const { t } = useTranslation(); - const [key, setKey] = useState(0); - const [urlFetch, setUrlFetch] = useState(""); - const theme = useTheme(); - - const { data, lastUpdated, loading } = useFetch( - urlFetch, - undefined, - false, - epochNo === currentEpoch?.no ? blockNo : key - ); - - useEffect(() => { - if (epochNo === undefined || epochNo === null) { - setUrlFetch(""); - } else { - setUrlFetch(`${API.EPOCH.DETAIL}/${epochNo}`); - } - }, [epochNo]); - - useEffect(() => { - // Update key if this epoch don't have rewards and when new epoch distributed for api callback - if (!data?.rewardsDistributed && epochNo !== undefined && data?.no !== undefined && epochNo !== data.no) { - setKey(epochNo); - } - }, [epochNo, data?.no, data?.rewardsDistributed]); - - useEffect(() => { - if (data) { - callback((list) => { - const index = list.findIndex((item) => item.no === data?.no); - if (index >= 0) list[index] = { ...list[index], ...data }; - return [...list]; - }); - } - }, [data, callback]); - - const renderContent = () => { - if (!data || loading) { - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {new Array(4).fill(0).map((_, index) => { - return ( - - - - - - - - - ); - })} - - {new Array(2).fill(0).map((_, index) => { - return ( - - - - - - - - - - - ); - })} - - - - - ); - } - - const slot = - data.no === currentEpoch?.no && data.status !== "SYNCING" ? currentEpoch.slot : data.maxSlot || MAX_SLOT_EPOCH; - - const progress = data.no === currentEpoch?.no ? (currentEpoch?.syncingProgress || 0) * 100 : 100; - return ( - <> - - - - - - - - - - - - - - - - {epochNo} - {t("epoch")} - - - - - - {t("glossary.blocks")} - - {currentEpoch?.no === epochNo ? currentEpoch?.blkCount || data.blkCount : data.blkCount} - - - - - {t("common.slot")} - - {slot} - /{MAX_SLOT_EPOCH} - - - - - - - {t("glossary.startTimestamp")} - - - - {formatDateTimeLocal(data.startTime || "")} - - - - - {t("glossary.endTimestamp")} - - - {formatDateTimeLocal(data.endTime || "")} - - - - - {t("glossary.blocks")} - {data.blkCount} - - - - {t("glossary.uniqueAccounts")} - - {data.account} - - - {t("drawer.txCount")} - {data.txCount} - - - - {t("glossary.rewardsDistributed")} - - - {data?.rewardsDistributed ? ( - - {formatADAFull(data?.rewardsDistributed)}  - - - ) : ( - t("common.N/A") - )} - - - - - {t("glossary.totalOutput")} - - - {formatADAFull(data.outSum)} - - - - - - - - - - - {t("glossary.blocks")} - - - - - - - - - - - - - ); - }; - - return ( - - {renderContent()} - - ); -}; - -export default DetailViewEpoch; diff --git a/src/components/commons/DetailView/DetailViewToken.test.tsx b/src/components/commons/DetailView/DetailViewToken.test.tsx deleted file mode 100644 index 3ef851bb43..0000000000 --- a/src/components/commons/DetailView/DetailViewToken.test.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { createBrowserHistory } from "history"; -import { Router } from "react-router-dom"; -import userEvent from "@testing-library/user-event"; - -import { act, render, screen } from "src/test-utils"; -import { details } from "src/commons/routers"; -import useFetch from "src/commons/hooks/useFetch"; - -import DetailViewToken from "./DetailViewToken"; - -const mockToken: IToken = { - name: "Token Name", - displayName: "Token Display Name", - policy: "Token Policy", - fingerprint: "Token Fingerprint", - txCount: 10, - supply: 1000, - createdOn: "2023-07-03T12:34:56Z", - metadata: { - policy: "Token Metadata Policy", - logo: "Token Metadata Logo", - decimals: 6, - description: "Token Metadata Description", - ticker: "Token Metadata Ticker", - url: "Token Metadata URL" - }, - volumeIn24h: 500, - totalVolume: "10000", - numberOfHolders: 50, - tokenType: "Token Type", - tokenLastActivity: "2023-07-03T12:34:56Z", - metadataJson: "Token Metadata JSON", - policyIsNativeScript: false -}; - -const mockTokenId = "1123KCCTSFD"; - -jest.mock("src/commons/hooks/useFetch"); - -describe("DetailViewToken component", () => { - beforeEach(() => { - (useFetch as jest.Mock).mockReturnValue({ - data: mockToken - }); - }); - - it("should component render", () => { - render(); - expect(screen.getAllByText(/Token Display Name/i)[0]).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: /Transactions/i })).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: /top holders/i })).toBeInTheDocument(); - }); - - it("should component go to detail pages", () => { - const history = createBrowserHistory(); - render( - - - - ); - act(() => { - userEvent.click(screen.getByRole("link", { name: /view details/i })); - }); - expect(history.location.pathname).toBe(details.token(mockTokenId)); - }); -}); diff --git a/src/components/commons/DetailView/DetailViewToken.tsx b/src/components/commons/DetailView/DetailViewToken.tsx deleted file mode 100644 index 5c82a162a9..0000000000 --- a/src/components/commons/DetailView/DetailViewToken.tsx +++ /dev/null @@ -1,302 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { BiChevronRight } from "react-icons/bi"; -import { CgClose } from "react-icons/cg"; -import { useTheme } from "@mui/material"; - -import { MetadataIcon, PeopleIcon, TransactionIcon, UnionTokenIcon } from "src/commons/resources"; -import { details } from "src/commons/routers"; -import { - formatDateTimeLocal, - formatNumberDivByDecimals, - getShortHash, - numberWithCommas -} from "src/commons/utils/helper"; - -import CopyButton from "../CopyButton"; -import CustomTooltip from "../CustomTooltip"; -import ViewAllButton from "../ViewAllButton"; -import ViewMoreButton from "../ViewMoreButton"; -import { - CloseButton, - DetailLabel, - DetailLabelSkeleton, - DetailLink, - DetailLinkIcon, - DetailLinkName, - DetailLinkRight, - DetailValue, - DetailValueSkeleton, - DetailsInfoItem, - Group, - IconSkeleton, - MetaData, - StyledLink, - TokenContainer, - TokenDecimal, - TokenDetailIcon, - TokenDetailInfo, - TokenDetailName, - TokenHeaderContainer, - TokenHeaderInfo, - TokenInfo, - TokenMetaData, - TokenTotalSupply, - ViewDetailContainer, - ViewDetailDrawer, - ViewDetailHeader, - ViewDetailScroll -} from "./styles"; -import DatetimeTypeTooltip from "../DatetimeTypeTooltip"; - -type DetailViewTokenProps = { - token: IToken | null; - tokenId: string; - handleClose: () => void; - open?: boolean; -}; - -const DetailViewToken: React.FC = (props) => { - const { t } = useTranslation(); - const { token: data, handleClose, tokenId, open } = props; - const theme = useTheme(); - const renderContent = () => { - if (!data) { - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {new Array(4).fill(0).map((_, index) => { - return ( - - - - - - - - - ); - })} - - {new Array(2).fill(0).map((_, index) => { - return ( - - - - - - - - - - - ); - })} - - - - - ); - } - return ( - <> - - - - - - - - - - - - - {t("common.scriptHash")} - - - - {getShortHash(data.policy || "")} - - - - - - - {t("common.tokenID")} - - - - {getShortHash(tokenId || "")} - - - - - - - {t("glossary.assetName")} - - - - {data.displayName && data.displayName.length > 20 ? ( - -
{getShortHash(data.displayName)}
-
- ) : data.displayName ? ( -
{data.displayName}
- ) : ( - -
{getShortHash(data.fingerprint || "")}
-
- )} -
- {data.metadata?.logo ? : ""} -
-
-
- - {t("common.totalTxs")} - {data.txCount} - - - {t("glossary.numberOfHolders")} - {numberWithCommas(data.numberOfHolders || 0)} - - - {t("glossary.totalVolumn")} - - {formatNumberDivByDecimals(data.totalVolume || 0, data?.metadata?.decimals || 0)} - - - - {t("glossary.volume24h")} - - {formatNumberDivByDecimals(data.volumeIn24h || 0, data?.metadata?.decimals || 0)} - - - - {t("createdAt")} - - {formatDateTimeLocal(data.createdOn || "")} - - -
- - - - - - - {t("drawer.transactions")} - - - - - - - {" "} - - - - - - - - {t("glossary.topHolders")} - - - - - - - - - - - - - - - {t("glossary.tokentMint")} - - - - - - - - - - - - - - - {t("glossary.metadata")} - - - - - - - - -
-
- -
- - ); - }; - return ( - - {renderContent()} - - ); -}; - -export default DetailViewToken; diff --git a/src/components/commons/DetailView/DetailViewTransaction.test.tsx b/src/components/commons/DetailView/DetailViewTransaction.test.tsx deleted file mode 100644 index 3e8f9da1a0..0000000000 --- a/src/components/commons/DetailView/DetailViewTransaction.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import userEvent from "@testing-library/user-event"; -import { createMemoryHistory } from "history"; -import { Router } from "react-router-dom"; -import { useSelector } from "react-redux"; - -import { act, render, screen } from "src/test-utils"; -import useFetch from "src/commons/hooks/useFetch"; -import { details } from "src/commons/routers"; - -import DetailViewTransaction from "./DetailViewTransaction"; - -const mockHash = "f5ca3b591dfae66ab6032f6d0eb528c00eedf75c394f410ff96d6f22b01ebe9b"; -const mockTransaction = { - tx: { - hash: "transactionHash123", - time: "2023-07-03T12:34:56Z", - blockNo: 1234, - epochSlot: 100, - epochNo: 10, - status: "SUCCESS", - confirmation: 3, - fee: 1000, - totalOutput: 5000, - maxEpochSlot: 10000 - } -}; -const { tx } = mockTransaction; -jest.mock("src/commons/hooks/useFetch"); -jest.mock("react-redux", () => { - return { - __esModule: true, - ...jest.requireActual("react-redux"), - useSelector: jest.fn() - }; -}); -describe("DetailViewTransaction component", () => { - beforeEach(() => { - (useFetch as jest.Mock).mockReturnValue({ - data: mockTransaction - }); - const mockUseSelector = useSelector as jest.Mock; - mockUseSelector.mockReturnValueOnce(tx.epochNo).mockReturnValueOnce(tx.blockNo).mockReturnValue({ sidebar: true }); - }); - it("should component render", () => { - render(); - expect(screen.getByText(tx.blockNo)).toBeInTheDocument(); - expect(screen.getByText(tx.status)).toBeInTheDocument(); - expect(screen.getByRole("link", { name: mockHash })).toBeInTheDocument(); - }); - - it("should component goto detail pages", () => { - const history = createMemoryHistory(); - render( - - - - ); - act(() => { - userEvent.click(screen.getByRole("link", { name: /view details/i })); - }); - expect(history.location.pathname).toBe(details.transaction(mockHash)); - }); -}); diff --git a/src/components/commons/DetailView/DetailViewTransaction.tsx b/src/components/commons/DetailView/DetailViewTransaction.tsx deleted file mode 100644 index 706f615d06..0000000000 --- a/src/components/commons/DetailView/DetailViewTransaction.tsx +++ /dev/null @@ -1,377 +0,0 @@ -import { useTheme } from "@mui/material"; -import React, { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { BiChevronRight } from "react-icons/bi"; -import { CgArrowsExchange, CgClose } from "react-icons/cg"; -import { useSelector } from "react-redux"; - -import useFetch from "src/commons/hooks/useFetch"; -import { - CubeIconComponent, - DelegationHistoryMainIcon, - DelegationIconComponent, - FileEditIcon, - GitCommitIcon, - InstantaneousHistoryComponent, - MetadataIconTx, - MintingIconComponent, - NoteEditIcon, - ProtocolUpdateComponent, - RewardsDistributionComponent, - RocketIcon, - StakeCertificatesComponent, - USDIconComponent, - WithdrawlIconComponent -} from "src/commons/resources"; -import { details } from "src/commons/routers"; -import { API } from "src/commons/utils/api"; -import { MAX_SLOT_EPOCH } from "src/commons/utils/constants"; -import { formatADAFull, formatDateTimeLocal, getShortHash } from "src/commons/utils/helper"; -import { RootState } from "src/stores/types"; - -import { CustomNumberBadge } from "../CustomNumberBadge"; -import ADAicon from "../ADAIcon"; -import CopyButton from "../CopyButton"; -import CustomIcon from "../CustomIcon"; -import CustomTooltip from "../CustomTooltip"; -import FormNowMessage from "../FormNowMessage"; -import ProgressCircle from "../ProgressCircle"; -import ViewAllButton from "../ViewAllButton"; -import ViewMoreButton from "../ViewMoreButton"; -import { - BlockDefault, - CloseButton, - DetailLabel, - DetailLabelSkeleton, - DetailLink, - DetailLinkIcon, - DetailLinkImage, - DetailLinkName, - DetailLinkRight, - DetailValue, - DetailValueSkeleton, - DetailsInfoItem, - EpochNumber, - EpochText, - Group, - HeaderContainer, - IconSkeleton, - Item, - ItemName, - ItemValue, - ListItem, - ProgressSkeleton, - StyledLink, - TimeDuration, - TxStatus, - ViewDetailContainer, - ViewDetailDrawer, - ViewDetailHeader, - ViewDetailScroll -} from "./styles"; -import DatetimeTypeTooltip from "../DatetimeTypeTooltip"; - -type DetailViewTransactionProps = { - hash: string; - handleClose: () => void; - open?: boolean; -}; - -const DetailViewTransaction: React.FC = (props) => { - const { t } = useTranslation(); - const { hash, handleClose, open } = props; - const [urlFetch, setUrlFetch] = useState(""); - const theme = useTheme(); - const epochNo = useSelector(({ system }: RootState) => system.currentEpoch?.no); - const blockNo = useSelector(({ system }: RootState) => system.blockNo); - const [lastUpdated, setLastUpdated] = useState(); - const { data, loading } = useFetch(urlFetch); - - useEffect(() => { - if (data) setLastUpdated(Date.now()); - }, [data, blockNo]); - - useEffect(() => { - if (!hash) { - setUrlFetch(""); - } else { - setUrlFetch(`${API.TRANSACTION.DETAIL}/${hash}`); - } - }, [hash]); - - const tabs: { key: keyof Transaction; label: string; icon?: React.ReactNode }[] = [ - { key: "summary", label: t("drawer.summary"), icon: }, - { key: "utxOs", label: t("tab.utxos"), icon: }, - { key: "contracts", label: t("glossary.contracts"), icon: }, - { key: "collaterals", label: t("glossary.collateral"), icon: }, - { key: "notes", label: t("tab.notes"), icon: }, - { key: "withdrawals", label: t("tab.withdrawal"), icon: }, - { - key: "delegations", - label: t("tab.delegations"), - icon: - }, - { key: "mints", label: t("tab.minting"), icon: }, - { - key: "poolCertificates", - label: t("tab.poolCertificates"), - icon: - }, - { - key: "stakeCertificates", - label: t("tab.stakeCertificates"), - icon: - }, - { - key: "protocols", - label: t("tab.protocolUpdate"), - icon: - }, - { - key: "instantaneousRewards", - label: t("glossary.instantaneousRewards"), - icon: - }, - { key: "signersInformation", label: t("tab.signersInformation"), icon: }, - { key: "metadata", label: t("glossary.metadata"), icon: } - ]; - - const renderContent = () => { - if (!data || loading || !epochNo) { - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {new Array(4).fill(0).map((_, index) => { - return ( - - - - - - - - - ); - })} - - {new Array(2).fill(0).map((_, index) => { - return ( - - - - - - - - - - - ); - })} - - - - - ); - } - const input = data.utxOs?.inputs[0]?.address || ""; - const output = data.utxOs?.outputs[0]?.address || ""; - const confirmation = Math.max(0, blockNo ? blockNo - (data.tx?.blockNo || 0) : data.tx?.confirmation); - return ( - <> - - - - - - - - - - - - - - - - {data?.tx?.epochNo} - Epoch - - - - - - {t("glossary.block")} - {data?.tx?.blockNo} - - - - {t("common.slot")} - - {data?.tx?.epochSlot} - /{data?.tx?.maxEpochSlot || MAX_SLOT_EPOCH} - - - - - - {t("glossary.transactionHash")} - - - - {getShortHash(hash)} - - - - - - - {t("glossary.absoluteSlot")} - {data?.tx?.slotNo} - - {input && ( - - {t("glossary.input")} - - - - {getShortHash(input)} - - - - - - )} - {output && ( - - {t("glossary.output")} - - - {getShortHash(output)} - - - - - )} - - {t("createdAt")} - - - {formatDateTimeLocal(data?.tx?.time || "")} - - - - - {t("common.status")} - - {data?.tx?.status} - - - - {confirmation > 1 ? t("glossary.comfirmations") : t("glossary.comfirmation")} - {confirmation} - - - {t("glossary.transactionfees")} - - {formatADAFull(data?.tx?.fee)} - - - - - {t("glossary.totalOutput")} - - {formatADAFull(data?.tx?.totalOutput)} - - - - - {tabs.map(({ key, label, icon }) => { - const value = data[key]; - if (!value) return null; - return ( - - - - {icon} - - {label} - - - - - - - - - - - ); - })} - - - - - ); - }; - - return ( - - {renderContent()} - - ); -}; - -export default DetailViewTransaction; diff --git a/src/components/commons/Epoch/FirstEpoch/index.tsx b/src/components/commons/Epoch/FirstEpoch/index.tsx index 82b68d306f..2edc25cb82 100644 --- a/src/components/commons/Epoch/FirstEpoch/index.tsx +++ b/src/components/commons/Epoch/FirstEpoch/index.tsx @@ -15,7 +15,7 @@ import DatetimeTypeTooltip from "../../DatetimeTypeTooltip"; interface IProps { data: IDataEpoch; - onClick: (currentEpochData: IDataEpoch, r: IDataEpoch, index: number) => void; + onClick: (e: React.MouseEvent, record: IDataEpoch) => void; } export default function FirstEpoch({ data: currentEpochData, onClick }: IProps) { @@ -139,7 +139,7 @@ export default function FirstEpoch({ data: currentEpochData, onClick }: IProps) } ]; return ( - onClick(currentEpochData, currentEpochData, -1)} data-testid="epoch.firstEpoch.container"> + onClick(e, currentEpochData)} data-testid="epoch.firstEpoch.container"> Contents; - +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn() + })) +}); describe("Layout component", () => { it("should component render", () => { render( diff --git a/src/components/commons/Table/index.tsx b/src/components/commons/Table/index.tsx index 44c2fa16c2..bf577a967c 100644 --- a/src/components/commons/Table/index.tsx +++ b/src/components/commons/Table/index.tsx @@ -1,8 +1,10 @@ import { Box, CircularProgress, + Collapse, IconButton, PaginationRenderItemParams, + Paper, SelectChangeEvent, alpha, styled, @@ -25,7 +27,7 @@ import { SortTableUpDown, StartPage } from "src/commons/resources"; -import { getPageInfo, handleClicktWithoutAnchor, numberWithCommas } from "src/commons/utils/helper"; +import { formatADAFull, getPageInfo, handleClicktWithoutAnchor, numberWithCommas } from "src/commons/utils/helper"; import breakpoints from "src/themes/breakpoints"; import { ColumnType, @@ -63,9 +65,12 @@ import { TableFullWidth, TableHeaderContainer, TableTitle, + TitleExpandedRow, TotalNumber, + ValueExpandedRow, Wrapper } from "./styles"; +import ADAicon from "../ADAIcon"; const SPACING_TOP_TABLE = 300; @@ -185,7 +190,8 @@ const TableRow = ({ columns, screen, index, - onClickRow, + onClickExpandedRow, + handleOpenDetail, showTabView, selectedProps, selected = false, @@ -194,7 +200,8 @@ const TableRow = ({ toggleSelection, isSelected, isModal, - onCallBackHeight + onCallBackHeight, + expandedTable }: TableRowProps) => { const colRef = useRef(null); const theme = useTheme(); @@ -208,7 +215,16 @@ const TableRow = ({ [rowRef.current]; return ( - handleClicktWithoutAnchor(e, () => onClickRow?.(e, row))} {...selectedProps}> + { + if (!expandedTable) { + handleOpenDetail?.(e, row); + } + handleClicktWithoutAnchor(e, () => onClickExpandedRow?.(e, row)); + }} + {...selectedProps} + > {selectable && ( toggleSelection?.(row)} /> @@ -239,16 +255,18 @@ const TableRow = ({ {showTabView && ( - {!selected && ( - - )} + { + e.stopPropagation(); + handleOpenDetail?.(e, row); + }} + /> )} @@ -271,8 +289,12 @@ const TableBody = ({ toggleSelection, isSelected, isModal, - onCallBackHeight + onCallBackHeight, + onClickExpandedRow, + expandedTable, + expandedRowData }: TableProps) => { + const { t } = useTranslation(); return (
{loading && initialized && ( @@ -290,25 +312,67 @@ const TableBody = ({ )} - {data?.map((row, index) => ( - - ))} + {data?.map((row, index) => { + const renderExpandedRowData = () => { + const expandedTableRowData = expandedRowData?.map((item) => ({ + label: item.label, + value: + row[item.value] === null ? ( + t("common.N/A") + ) : item.isFormatADA ? ( + + {formatADAFull(row[item.value])} + + ) : ( + row[item.value] + ) + })); + + return ; + }; + return ( + <> + { + expandedTable && onClickExpandedRow && onClickExpandedRow(row); + }} + handleOpenDetail={onClickRow} // this event occur when click on eye icon + showTabView={showTabView} + selected={ + Array.isArray(selected) + ? selected.includes(!!rowKey && (typeof rowKey === "function" ? rowKey(row) : row[rowKey])) + : false + } + selectedProps={Array.isArray(selected) && selected?.includes(index) ? selectedProps : undefined} + selectable={selectable} + toggleSelection={toggleSelection} + isSelected={isSelected} + isModal={isModal} + onCallBackHeight={onCallBackHeight} + expandedTable={expandedTable} + /> + {expandedTable && !!rowKey && ( + + + + )} + + ); + })} ); }; @@ -323,6 +387,36 @@ const TableSekeleton = () => { ); }; +export const ExpandedRowContent: React.FC<{ + data: { label: string; value: string | number }[] | undefined; +}> = ({ data }) => { + const { isMobile } = useScreen(); + + return ( + + {data?.map((item, index) => ( + ({ + flex: 1, + padding: isMobile ? 2 : 3, + textAlign: "start", + borderRadius: 4, + marginRight: "10px", + backgroundColor: theme.isDark ? theme.palette.secondary[100] : theme.palette.background.paper + })} + > + <> + {item.label} + {item.value} + + + ))} + + ); +}; + export const FooterTable: React.FC = ({ total, pagination, @@ -356,7 +450,6 @@ export const FooterTable: React.FC = ({ useEffect(() => { trigger && setOpen(false); }, [trigger, setOpen]); - return ( @@ -416,7 +509,7 @@ export const FooterTable: React.FC = ({ "" )} - {pagination?.total && pagination.total > pagination.size ? ( + {pagination?.total && pagination.size && pagination.total > (pagination.size || 10) ? ( = ({ isModal, height, minHeight, - isFullTableHeight = false + isFullTableHeight = false, + expandedTable, + onClickExpandedRow, + expandedRowData }) => { const { selectedItems, toggleSelection, isSelected, clearSelection, selectAll } = useSelection({ onSelectionChange @@ -518,7 +614,6 @@ const Table: React.FC = ({ clearSelection(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [data]); - const isSelectAll = useMemo(() => data?.length === selectedItems.length, [data, selectedItems]); return ( @@ -569,6 +664,9 @@ const Table: React.FC = ({ isSelected={isSelected} isModal={isModal} onCallBackHeight={onCallBackHeight} + expandedTable={expandedTable} + onClickExpandedRow={onClickExpandedRow} + expandedRowData={expandedRowData} /> {loading && !initialized && } diff --git a/src/components/commons/Table/styles.ts b/src/components/commons/Table/styles.ts index c418777244..148b87c56d 100644 --- a/src/components/commons/Table/styles.ts +++ b/src/components/commons/Table/styles.ts @@ -1,6 +1,7 @@ import { Box, Checkbox, Typography, styled, Pagination, MenuItem, alpha } from "@mui/material"; import CustomSelect from "../CustomSelect"; +import { CommonSkeleton } from "../CustomSkeleton"; export const Empty = styled(Box)<{ ismodal?: number }>` text-align: center; @@ -266,6 +267,20 @@ export const ShowedResults = styled(Typography)` line-height: 16px; color: rgba(102, 112, 133, 1); `; +export const TitleExpandedRow = styled(Typography)` + font-size: 14px; + line-height: 16px; + color: ${(props) => props.theme.palette.secondary.main}; + font-weight: 700; + white-space: nowrap; +`; +export const ValueExpandedRow = styled(Typography)` + font-size: 14px; + line-height: 30px; + color: ${(props) => props.theme.palette.secondary.light}; + font-weight: 400; + white-space: nowrap; +`; export const TableCustomTitle = styled(Box)` flex: 1; @@ -329,3 +344,9 @@ export const StyledPerPage = styled(Typography)(({ theme }) => ({ marginLeft: "4px" } })); +export const Skeleton = styled(CommonSkeleton)` + height: 1em; + width: 100%; + min-width: 10px; + border-radius: 4px; +`; diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index afe5e07c41..5dc4b3b89d 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -28,10 +28,12 @@ "glossary.currentEpoch": "Current Epoch", "glossary.slot": "Slot", "glossary.uniqueAccounts": "Unique Accounts", + "glossary.currentTime": "Current Time", + "glossary.timeRemaining": "Time Remaining", "glossary.endTimestamp": "End Timestamp", "glossary.liveStake": "Live Stake", "glossary.activeStake": "Active Stake", - "glossary.circulatingSupply": "Circulating supply", + "glossary.circulatingSupply": "Circulating Supply", "glossary.offTheMaxSupply": "Of the max supply", "glossary.txLastTime": "Transactions in the last {{hoursValue}} hours", "option.tx.in24h": "in the last 24 hours", @@ -91,6 +93,7 @@ "constitutionalCommittee.page.description": "Explore the role, composition, and current status of the Constitutional Committee responsible for Cardano's governance. Review member details, governance actions, and the proposal policy to understand the decision-making processes that shape the network.", "overview.page.description": "Governance actions include types like protocol changes, treasury withdrawals, and hard-fork initiations. Each action has specific approval thresholds, requiring consensus from bodies such as the Constitutional Committee, Delegated Representatives (DReps), and Stake Pool Operators (SPOs), ensuring transparent and effective governance.", "govAction.govActionID": "Governance Action ID: ", + "overview.page.preDefinedVotes": "Pre-defined Votes", "govAction.actionType": "Action Type", "govAction.dateCreated": "Date Created", "govAction.status": "Status", @@ -444,6 +447,8 @@ "drawer.transactionsHistory": "Transaction history", "head.page.stakeAddressDeregistration": "Stake Address Deregistration", "common.lastUpdatedSecondsAgo": "Last updated {{sec}} seconds ago", + "common.explainSlot": "Slot within Epoch", + "common.absoluteSlot": "Total Slot since Genesis", "drawer.rewardWithdrawn": "Reward withdrawn", "drawer.notDelegatedToAnyPool": "Not delegated to any pool", "drawer.delegationHistory": "Delegation History", diff --git a/src/pages/BlockList/index.tsx b/src/pages/BlockList/index.tsx index ddb744e82d..2a87bf7f79 100644 --- a/src/pages/BlockList/index.tsx +++ b/src/pages/BlockList/index.tsx @@ -8,22 +8,19 @@ import { useTranslation } from "react-i18next"; import { Column } from "src/types/table"; import CustomTooltip from "src/components/commons/CustomTooltip"; import { details } from "src/commons/routers"; -import { formatADAFull, formatDateTimeLocal, formatNameBlockNo, getShortHash } from "src/commons/utils/helper"; -import { setOnDetailView } from "src/stores/user"; -import DetailViewBlock from "src/components/commons/DetailView/DetailViewBlock"; +import { formatDateTimeLocal, formatNameBlockNo, getShortHash } from "src/commons/utils/helper"; import Card from "src/components/commons/Card"; import Table from "src/components/commons/Table"; import { API } from "src/commons/utils/api"; -import SelectedIcon from "src/components/commons/SelectedIcon"; import Link from "src/components/commons/Link"; -import ADAicon from "src/components/commons/ADAIcon"; import useFetchList from "src/commons/hooks/useFetchList"; import { Capitalize } from "src/components/commons/CustomText/styles"; import FormNowMessage from "src/components/commons/FormNowMessage"; import usePageInfo from "src/commons/hooks/usePageInfo"; import DatetimeTypeTooltip from "src/components/commons/DatetimeTypeTooltip"; +import { TooltipIcon } from "src/commons/resources"; -import { PriceWrapper, BlueText, StyledContainer, StyledLink, Actions, TimeDuration } from "./styles"; +import { PriceWrapper, StyledContainer, StyledLink, Actions, TimeDuration } from "./styles"; const BlockList = () => { const { t } = useTranslation(); @@ -31,7 +28,7 @@ const BlockList = () => { const { onDetailView } = useSelector(({ user }: RootState) => user); const blockNo = useSelector(({ system }: RootState) => system.blockNo); const { pageInfo, setSort } = usePageInfo(); - const [selected, setSelected] = useState(null); + const [selected, setSelected] = useState<(number | string | null)[]>([]); const fetchData = useFetchList(API.BLOCK.LIST, { ...pageInfo }, false, blockNo); const mainRef = useRef(document.querySelector("#main")); @@ -40,6 +37,12 @@ const BlockList = () => { document.title = `Blocks List | Cardano Blockchain Explorer`; }, []); + const expandedBlockRowData = [ + { label: "Transactions", value: "txCount" }, + { label: "Fees", value: "totalFees", isFormatADA: true }, + { label: "Output", value: "totalOutput", isFormatADA: true } + ]; + const columns: Column[] = [ { title: {t("glossary.block")}, @@ -79,13 +82,31 @@ const BlockList = () => { ) }, { - title: {t("glossary.slot")}, + title: ( + + {t("glossary.slot")} + +

+ +

+
+
+ ), key: "epochSlotNo", minWidth: "100px", render: (r, index) => {r.epochSlotNo} }, { - title: {t("glossary.absoluteSlot")}, + title: ( + + {t("glossary.absoluteSlot")}{" "} + +

+ +

+
+
+ ), key: "slotNo", minWidth: "100px", render: (r, index) => {r.slotNo} @@ -104,54 +125,32 @@ const BlockList = () => { sort: ({ columnKey, sortValue }) => { sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); } - }, - { - title: {t("glossary.transactions")}, - key: "txCount", - minWidth: "50px", - render: (r, index) => {r.txCount}, - sort: ({ columnKey, sortValue }) => { - sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); - } - }, - { - title: {t("common.fees")}, - key: "fees", - render: (r, index) => ( - - {formatADAFull(r.totalFees)} - - - ) - }, - { - title: {t("glossary.output")}, - key: "output", - minWidth: "100px", - render: (r, index) => ( - - {formatADAFull(r.totalOutput)} - - {selected === (r.blockNo || r.hash) && } - - ) } ]; - const openDetail = (_: MouseEvent, r: Block) => { - setOnDetailView(true); - setSelected(r.blockNo || r.hash); + const handleOpenDetail = (_: MouseEvent, r: Block) => { + history.push(details.block(r.blockNo)); }; const handleClose = () => { - setOnDetailView(false); - setSelected(null); + setSelected([]); }; useEffect(() => { if (!onDetailView) handleClose(); }, [onDetailView]); + const handleExpandedRow = (data: Block) => { + setSelected((prev) => { + const isSelected = prev.includes(Number(data.blockNo)); + + if (isSelected) { + return prev.filter((blockNo) => blockNo !== Number(data.blockNo)); + } else { + return [...prev, Number(data.blockNo)]; + } + }); + }; return ( @@ -175,14 +174,16 @@ const BlockList = () => { }, handleCloseDetailView: handleClose }} - onClickRow={openDetail} + onClickRow={handleOpenDetail} rowKey={(r: Block) => r.blockNo || r.hash} selected={selected} showTabView tableWrapperProps={{ sx: (theme) => ({ [theme.breakpoints.between("sm", "md")]: { minHeight: "60vh" } }) }} + onClickExpandedRow={handleExpandedRow} + expandedTable + expandedRowData={expandedBlockRowData} /> - ); }; diff --git a/src/pages/BolnisiLanding/index.tsx b/src/pages/BolnisiLanding/index.tsx index 2589f15f91..ffaf70fe79 100644 --- a/src/pages/BolnisiLanding/index.tsx +++ b/src/pages/BolnisiLanding/index.tsx @@ -275,6 +275,7 @@ const BolnisiTrx = () => { const theme = useTheme(); const history = useHistory(); const [cert, setCert] = useState("SCM"); + const [open, setOpen] = useState(false); const { data: bolnisiData } = useFetch(API.BOLNISI.OVERVIEW, undefined, false); @@ -407,21 +408,21 @@ const BolnisiTrx = () => { - + - + - + @@ -470,10 +471,19 @@ const BolnisiTrx = () => { [theme.breakpoints.down("md")]: { mt: theme.spacing(1) } }} value={cert} + open={open} + onClose={() => setOpen(false)} + onOpen={() => setOpen(true)} onChange={(e) => setCert(e.target?.value)} - inputProps={{ style: { border: "transparent" } }} + inputProps={{ style: { border: "transparent" }, id: "" }} IconComponent={() => ( - + { + setOpen(true); + }} + /> )} MenuProps={{ MenuListProps: { style: { background: theme.palette.secondary[0] } } }} > @@ -485,6 +495,7 @@ const BolnisiTrx = () => {
+ i.cert === cert)} @@ -525,6 +536,20 @@ const BolnisiTrx = () => { + + + + + ); }; diff --git a/src/pages/DrepDetail/index.tsx b/src/pages/DrepDetail/index.tsx index 47ae9cf62c..af9dc3042a 100644 --- a/src/pages/DrepDetail/index.tsx +++ b/src/pages/DrepDetail/index.tsx @@ -43,7 +43,7 @@ import FormNowMessage from "src/components/commons/FormNowMessage"; import { StyledAccordion } from "src/components/commons/CustomAccordion/styles"; import useFetchList from "src/commons/hooks/useFetchList"; import { API } from "src/commons/utils/api"; -import { formatADA, formatDateTimeLocal, formatPercent, getPageInfo } from "src/commons/utils/helper"; +import { formatADA, formatADAFull, formatDateTimeLocal, formatPercent, getPageInfo } from "src/commons/utils/helper"; import useFetch from "src/commons/hooks/useFetch"; import { BackButton, @@ -163,7 +163,7 @@ const DrepDetail = () => { ), value: ( - {data?.activeVoteStake !== null ? `${formatADA(data?.activeVoteStake || 0)} ADA` : t("common.N/A")}{" "} + {data?.activeVoteStake !== null ? `${formatADAFull(data?.activeVoteStake || 0)} ADA` : t("common.N/A")}{" "} ) }, diff --git a/src/pages/Epoch/index.tsx b/src/pages/Epoch/index.tsx index a73720421d..ed913ca1c5 100644 --- a/src/pages/Epoch/index.tsx +++ b/src/pages/Epoch/index.tsx @@ -8,25 +8,22 @@ import { Box } from "@mui/material"; import useFetchList from "src/commons/hooks/useFetchList"; import { API } from "src/commons/utils/api"; import { EPOCH_STATUS } from "src/commons/utils/constants"; -import { formatADAFull, formatDateTimeLocal } from "src/commons/utils/helper"; -import ADAicon from "src/components/commons/ADAIcon"; +import { formatDateTimeLocal } from "src/commons/utils/helper"; import Card from "src/components/commons/Card"; -import DetailViewEpoch from "src/components/commons/DetailView/DetailViewEpoch"; import FirstEpoch from "src/components/commons/Epoch/FirstEpoch"; -import SelectedIcon from "src/components/commons/SelectedIcon"; import Table, { Column } from "src/components/commons/Table"; -import { setOnDetailView } from "src/stores/user"; import { Capitalize } from "src/components/commons/CustomText/styles"; import usePageInfo from "src/commons/hooks/usePageInfo"; import DatetimeTypeTooltip from "src/components/commons/DatetimeTypeTooltip"; import NoRecord from "src/components/commons/NoRecord"; import FetchDataErr from "src/components/commons/FetchDataErr"; +import { details } from "src/commons/routers"; -import { Blocks, BlueText, EpochNumber, Output, StatusTableRow, StyledBox, StyledContainer } from "./styles"; +import { Blocks, BlueText, EpochNumber, StatusTableRow, StyledContainer, StyledLink } from "./styles"; const Epoch: React.FC = () => { const { t } = useTranslation(); - const [selected, setSelected] = useState(null); + const [selected, setSelected] = useState<(number | string | null)[]>([]); const history = useHistory(); const { onDetailView } = useSelector(({ user }: RootState) => user); const epochNo = useSelector(({ system }: RootState) => system.currentEpoch?.no); @@ -53,7 +50,9 @@ const Epoch: React.FC = () => { render: (r, idx) => ( - {r.no || 0} + + {r.no} + {EPOCH_STATUS_MAPPING[EPOCH_STATUS[r.status]]} @@ -79,10 +78,7 @@ const Epoch: React.FC = () => { minWidth: "100px", render: (r, idx) => ( - - {formatDateTimeLocal(r.endTime || "")} - {selected === r.no && } - + {formatDateTimeLocal(r.endTime || "")} ) }, @@ -94,51 +90,6 @@ const Epoch: React.FC = () => { sort: ({ columnKey, sortValue }) => { sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); } - }, - { - title: {t("glossary.uniqueAccounts")}, - key: "account", - minWidth: "100px", - render: (r, idx) => {r.account} - }, - { - title: {t("glossary.transactionCount")}, - key: "transactionCount", - minWidth: "100px", - render: (r, idx) => {r.txCount} - }, - { - title: ( - {t("glossary.rewardsDistributed")} - ), - key: "rDistributed", - minWidth: "100px", - render: (r, idx) => ( -
- {r.rewardsDistributed ? ( - - {formatADAFull(r.rewardsDistributed)} - - - ) : ( - t("common.N/A") - )} -
- ) - }, - { - title: {t("glossary.totalOutput")}, - key: "outSum", - minWidth: "100px", - render: (r, idx) => ( - - {formatADAFull(r.outSum)} - - - ), - sort: ({ columnKey, sortValue }) => { - sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); - } } ]; @@ -147,14 +98,24 @@ const Epoch: React.FC = () => { document.title = t("head.page.epochsList"); }, [t]); - const openDetail = (_: IDataEpoch, r: IDataEpoch) => { - setOnDetailView(true); - setSelected(r.no); + const handleOpenDetail = (_: React.MouseEvent, r: IDataEpoch) => { + history.push(details.epoch(r.no)); + }; + + const handleExpandedRow = (data: IDataEpoch) => { + setSelected((prev) => { + const isSelected = prev.includes(Number(data.no)); + + if (isSelected) { + return prev.filter((no) => no !== Number(data.no)); + } else { + return [...prev, Number(data.no)]; + } + }); }; const handleClose = () => { - setOnDetailView(false); - setSelected(null); + setSelected([]); }; useEffect(() => { @@ -170,10 +131,18 @@ const Epoch: React.FC = () => { if (error && (statusError || 0) < 500) return ; if (error && (statusError || 0) >= 500) return ; + + const expandedEpochRowData = [ + { label: "Unique Accounts", value: "account" }, + { label: "Transaction Count", value: "txCount" }, + { label: "Rewards Distributed", value: "rewardsDistributed", isFormatADA: true }, + { label: "Total Output", value: "outSum", isFormatADA: true } + ]; + return ( - {latestEpoch && } + {latestEpoch && }
+ + {renderExpandedRowData()} + +
{ }, handleCloseDetailView: handleClose }} - onClickRow={(_, r) => openDetail(r, r)} + onClickRow={handleOpenDetail} + onClickExpandedRow={handleExpandedRow} rowKey="no" selected={selected} showTabView + expandedTable + expandedRowData={expandedEpochRowData} /> - ); }; diff --git a/src/pages/Epoch/styles.ts b/src/pages/Epoch/styles.ts index 98d35214b9..33ede4be49 100644 --- a/src/pages/Epoch/styles.ts +++ b/src/pages/Epoch/styles.ts @@ -1,4 +1,5 @@ import { Box, styled, Container } from "@mui/material"; +import { Link } from "react-router-dom"; import { EPOCH_STATUS } from "src/commons/utils/constants"; @@ -83,3 +84,9 @@ export const EpochNumber = styled(Box)` align-items: center; justify-content: flex-start; `; + +export const StyledLink = styled(Link)` + font-family: var(--font-family-text) !important; + color: ${(props) => props.theme.palette.primary.main} !important; + margin-right: 8px; +`; diff --git a/src/pages/Micar/MicarChart/index.tsx b/src/pages/Micar/MicarChart/index.tsx index 1146f144cd..ab42ff6d15 100644 --- a/src/pages/Micar/MicarChart/index.tsx +++ b/src/pages/Micar/MicarChart/index.tsx @@ -88,7 +88,7 @@ const EmissionsAreaChart = () => { return filteredData; } - const step = Math.floor(dataLength / totalPoints); + const step = Math.ceil(dataLength / totalPoints); const result = filteredData.filter((_, index) => index % step === 0).slice(0, totalPoints); return result; diff --git a/src/pages/Micar/styles.ts b/src/pages/Micar/styles.ts index 365b5f5e7e..761bdb620b 100644 --- a/src/pages/Micar/styles.ts +++ b/src/pages/Micar/styles.ts @@ -1,7 +1,7 @@ import { Box, styled } from "@mui/material"; export const WrapHeading = styled(Box)(({ theme }) => ({ - marginBottom: "132px", + marginBottom: "102px", padding: "0 24px", [theme.breakpoints.down(1024)]: { diff --git a/src/pages/NativeScriptsAndSC/Card.tsx b/src/pages/NativeScriptsAndSC/Card.tsx index d0400c4620..cd9d894513 100644 --- a/src/pages/NativeScriptsAndSC/Card.tsx +++ b/src/pages/NativeScriptsAndSC/Card.tsx @@ -50,7 +50,6 @@ const NativeScriptCard: React.FC<{ data: NativeScriptsList; hasBeforeAndAfter: b {t("common.scriptHash")}: @@ -68,10 +67,6 @@ const NativeScriptCard: React.FC<{ data: NativeScriptsList; hasBeforeAndAfter: b - - {t("nativeScript.assetHolders")}: - {data.numberOfAssetHolders || 0} - {t("nativeScript.tokens")}: diff --git a/src/pages/NativeScriptsAndSC/TabNativeScripts.tsx b/src/pages/NativeScriptsAndSC/TabNativeScripts.tsx index 78416bd296..bc27b66dfe 100644 --- a/src/pages/NativeScriptsAndSC/TabNativeScripts.tsx +++ b/src/pages/NativeScriptsAndSC/TabNativeScripts.tsx @@ -392,19 +392,6 @@ const FilterComponent: React.FC = ({ {sort.includes("numberOfTokens") && } - setSort("numberOfAssetHolders,DESC")} - > - - - {t("NumberOfAssetHolders")} - - {sort.includes("numberOfAssetHolders") && ( - - )} - { it("should be render page", () => { render(); - expect(screen.getByText("Updatable Parameters")).toBeInTheDocument(); - expect(screen.getByText("Global Constants")).toBeInTheDocument(); - expect(screen.getByText(/View update activity/i)).toBeInTheDocument(); + expect(screen.getByText("Protocol Parameters")).toBeInTheDocument(); + expect(screen.getByText("Network group")).toBeInTheDocument(); + expect(screen.getByText(/View update history/i)).toBeInTheDocument(); }); - it("renders the table with given columns and data", () => { - const columns: Column<{ test: string }>[] = [ - { - title: "Test Column", - key: "test", - render: (r) =>
{r.test}
- } - ]; - const data = [ - { - test: "Test Data" - } - ]; - - render(); - - expect(screen.getByText("Test Column")).toBeInTheDocument(); - expect(screen.getByText("Test Data")).toBeInTheDocument(); - }); it("renders data in the table", async () => { render(); - expect(screen.getByText(/Updatable Parameters/)).toBeInTheDocument(); - expect(screen.getByText(/Global Constants/)).toBeInTheDocument(); + expect(screen.getByText(/Economic Group/)).toBeInTheDocument(); + expect(screen.getByText(/Technical Group/)).toBeInTheDocument(); }); }); diff --git a/src/pages/SmartContractDetail/TabAssociated.tsx b/src/pages/SmartContractDetail/TabAssociated.tsx index 82f0ca996d..f2fc1c664c 100644 --- a/src/pages/SmartContractDetail/TabAssociated.tsx +++ b/src/pages/SmartContractDetail/TabAssociated.tsx @@ -1,3 +1,4 @@ +import { useState } from "react"; import { Box } from "@mui/material"; import { get } from "lodash"; import { useTranslation } from "react-i18next"; @@ -5,7 +6,7 @@ import { useTranslation } from "react-i18next"; import { details } from "src/commons/routers"; import DynamicEllipsisText from "src/components/DynamicEllipsisText"; import { CommonSkeleton } from "src/components/commons/CustomSkeleton"; -import { EmptyRecord } from "src/components/commons/Table"; +import { EmptyRecord, FooterTable } from "src/components/commons/Table"; import { StyledLink, StyledSubNameTab } from "./styles"; @@ -17,17 +18,39 @@ export type TTabAssociatedProps = { const TabAssociated: React.FC = ({ data, loading }) => { const { t } = useTranslation(); + const associatedAddresses = get(data, "associatedAddresses", []); + const totalRecords = associatedAddresses.length; + + const [page, setPage] = useState(1); + const [rowsPerPage, setRowsPerPage] = useState(50); + + const handlePageChange = (page: number, size: number) => { + setPage(page); + setRowsPerPage(size); + }; + + const paginatedAddresses = associatedAddresses.slice((page - 1) * rowsPerPage, page * rowsPerPage); + const renderData = () => { if (loading) { return ; } - if (get(data, "associatedAddresses", []).length > 0) { - return data?.associatedAddresses.map((address: string) => ( - - - - )); + + if (associatedAddresses.length > 0) { + return ( + + {paginatedAddresses.map((address: string) => ( + + + + ))} + + ); } + return ; }; @@ -35,6 +58,18 @@ const TabAssociated: React.FC = ({ data, loading }) => { {t("AssociatedAddresses")}: {renderData()} + + ); }; diff --git a/src/pages/StakeDetail/StakeDetail.test.tsx b/src/pages/StakeDetail/StakeDetail.test.tsx deleted file mode 100644 index 05747e9251..0000000000 --- a/src/pages/StakeDetail/StakeDetail.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { render, screen } from "src/test-utils"; - -import StakeDetail from "."; - -describe("StakeDetail page", () => { - it("should component render", async () => { - render(); - await new Promise((r) => setTimeout(r, 1000)); - expect(screen.getByTestId("stake-address-detail-title")).toBeInTheDocument(); - }); -}); diff --git a/src/pages/Token/index.tsx b/src/pages/Token/index.tsx index dded1b058c..474baf4ffe 100644 --- a/src/pages/Token/index.tsx +++ b/src/pages/Token/index.tsx @@ -1,27 +1,18 @@ import { stringify } from "qs"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; import { useHistory, useLocation } from "react-router-dom"; import { useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { Box } from "@mui/material"; import { details } from "src/commons/routers"; -import { - formatDateTimeLocal, - formatNumberDivByDecimals, - formatNumberTotalSupply, - getShortHash, - numberWithCommas -} from "src/commons/utils/helper"; +import { formatDateTimeLocal, formatNumberTotalSupply, getShortHash } from "src/commons/utils/helper"; import Card from "src/components/commons/Card"; import Table, { Column } from "src/components/commons/Table"; -import { setOnDetailView } from "src/stores/user"; import FormNowMessage from "src/components/commons/FormNowMessage"; import useFetchList from "src/commons/hooks/useFetchList"; import { API } from "src/commons/utils/api"; import CustomTooltip from "src/components/commons/CustomTooltip"; -import DetailViewToken from "src/components/commons/DetailView/DetailViewToken"; -import SelectedIcon from "src/components/commons/SelectedIcon"; import usePageInfo from "src/commons/hooks/usePageInfo"; import DatetimeTypeTooltip from "src/components/commons/DatetimeTypeTooltip"; @@ -29,10 +20,9 @@ import { AssetName, Logo, StyledContainer, TimeDuration } from "./styles"; const Tokens = () => { const { t } = useTranslation(); - const { onDetailView } = useSelector(({ user }: RootState) => user); + const blockKey = useSelector(({ system }: RootState) => system.blockKey); - const [selected, setSelected] = useState(null); const { search } = useLocation(); const history = useHistory(); const { pageInfo, setSort } = usePageInfo(); @@ -96,38 +86,11 @@ const Tokens = () => { ) }, { - title: {t("common.totalTxs")}, - key: "txCount", - minWidth: "150px", - render: (r) => numberWithCommas(r?.txCount), - sort: ({ columnKey, sortValue }) => { - sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); - } - }, - { - title: {t("glossary.numberOfHolders")}, - key: "numberOfHolders", - minWidth: "150px", - render: (r) => numberWithCommas(r?.numberOfHolders) - }, - { - title: {t("glossary.totalVolumn")}, - key: "TotalVolume", - minWidth: "150px", - render: (r) => formatNumberDivByDecimals(r?.totalVolume, r.metadata?.decimals || 0) - }, - { - title: {t("glossary.volume24h")}, - key: "volumeIn24h", - minWidth: "150px", - render: (r, index) => ( - - {formatNumberDivByDecimals(r?.volumeIn24h, r.metadata?.decimals || 0)} + title: ( + + {t("common.totalSupply")} - ) - }, - { - title: {t("common.totalSupply")}, + ), key: "supply", minWidth: "150px", render: (r) => { @@ -139,34 +102,25 @@ const Tokens = () => { } }, { - title: {t("createdAt")}, + title: ( + + {t("createdAt")} + + ), key: "time", minWidth: "150px", - render: (r) => ( - - {formatDateTimeLocal(r.createdOn || "")} {JSON.stringify(selected) === JSON.stringify(r) && } - - ), + render: (r) => {formatDateTimeLocal(r.createdOn || "")}, sort: ({ columnKey, sortValue }) => { sortValue ? setSort(`${columnKey},${sortValue}`) : setSort(""); } } ]; - const openDetail = (_: React.MouseEvent, r: IToken) => { - setOnDetailView(true); - setSelected(r || null); - }; - - const handleClose = () => { - setOnDetailView(false); - setSelected(null); + const toTokenDetail = (_: React.MouseEvent, r: IToken) => { + if (!r.fingerprint) return; + history.push(details.token(r.fingerprint ?? "")); }; - useEffect(() => { - if (!onDetailView) handleClose(); - }, [onDetailView]); - return ( @@ -191,22 +145,14 @@ const Tokens = () => { search: stringify({ ...pageInfo, page, size, tokenName: queries.get("tokenName") || "" }) }); }, - handleCloseDetailView: handleClose, hideLastPage: true }} - onClickRow={openDetail} + onClickRow={toTokenDetail} rowKey="fingerprint" - selected={selected?.fingerprint} showTabView tableWrapperProps={{ sx: (theme) => ({ [theme.breakpoints.between("sm", "md")]: { minHeight: "60vh" } }) }} /> - ); }; diff --git a/src/pages/TokenDetail/TokenDetail.test.tsx b/src/pages/TokenDetail/TokenDetail.test.tsx index 4d0756e549..713f1531ef 100644 --- a/src/pages/TokenDetail/TokenDetail.test.tsx +++ b/src/pages/TokenDetail/TokenDetail.test.tsx @@ -42,9 +42,6 @@ describe("TokenDetail page", () => { it("should component render", () => { render(); - expect(screen.getByText(/number of holders/i)).toBeInTheDocument(); - expect(screen.getByText(/total volume/i)).toBeInTheDocument(); - expect(screen.getByText(/volume 24h/i)).toBeInTheDocument(); expect(screen.getByText(/token last activity/i)).toBeInTheDocument(); expect(screen.getByText(/analytics/i)).toBeInTheDocument(); ["1d", "1w", "1m", "3m"].forEach((item) => { diff --git a/src/pages/TokenDetail/index.tsx b/src/pages/TokenDetail/index.tsx index fd695300bc..58a0d30df0 100644 --- a/src/pages/TokenDetail/index.tsx +++ b/src/pages/TokenDetail/index.tsx @@ -25,7 +25,6 @@ export const OverviewMetadataTokenContext = createContext { const mainRef = useRef(document.querySelector("#main")); - const [currentHolders, setCurrentHolder] = useState(0); const { tokenId } = useParams<{ tokenId: string }>(); const { state } = useLocation<{ data?: IToken }>(); const blockKey = useSelector(({ system }: RootState) => system.blockKey); @@ -56,10 +55,9 @@ const TokenDetail: React.FC = () => { }} > - + { - it("should component render", () => { - render(); - expect(screen.getByRole("heading", { name: /top ada holders/i })).toBeInTheDocument(); - expect(screen.getByRole("tab", { name: /by address ada balance/i })).toBeInTheDocument(); - expect(screen.getByRole("tab", { name: /by amount staked/i })).toBeInTheDocument(); - expect(screen.getByRole("textbox", { hidden: true })).toBeInTheDocument(); - }); - - it("should user change tab", () => { - render(); - fireEvent.click(screen.getByRole("tab", { name: /by amount staked/i })); - expect(screen.getByText(/delegators/i)).toBeInTheDocument(); - }); -}); diff --git a/src/pages/TopAddresses/index.tsx b/src/pages/TopAddresses/index.tsx deleted file mode 100644 index b9d4b6c280..0000000000 --- a/src/pages/TopAddresses/index.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { TabContext, TabList, TabPanel } from "@mui/lab"; -import { Box, Tab, useTheme } from "@mui/material"; -import React, { useEffect } from "react"; -import { useTranslation } from "react-i18next"; - -import Card from "src/components/commons/Card"; -import TopAddressesByADABalance from "src/components/TopAddresses/ByADABalance"; -import TopAddressesByAmountStaked from "src/components/TopAddresses/ByAmountStaked"; -import { DelegationHistoryIcon, StakeKeyHistoryIcon } from "src/commons/resources"; - -import { StyledContainer, TabTitle } from "./styles"; - -const TopAddresses = () => { - const { t } = useTranslation(); - const [tabActive, setTabActive] = React.useState<"ada-balance" | "amount-staked">("ada-balance"); - const theme = useTheme(); - useEffect(() => { - document.title = `Top Addresses | Cardano Blockchain Explorer`; - }, []); - - const handleChange = (event: React.SyntheticEvent, tab: "ada-balance" | "amount-staked") => { - setTabActive(tab); - }; - const tabs: { - label: string; - key: string; - icon: React.FC>; - children: React.ReactNode; - }[] = [ - { - label: t("adrress.byAddressADABalance"), - key: "ada-balance", - icon: DelegationHistoryIcon, - children: - }, - { - label: t("address.byAmountStaked"), - key: "amount-staked", - icon: StakeKeyHistoryIcon, - children: - } - ]; - - return ( - - - - - theme.palette.primary.main, height: 3 } }} - > - {tabs?.map(({ key, icon: Icon, label }) => ( - - - - {label} - - - } - value={key} - sx={{ - padding: "12px 0px", - marginRight: "24px" - }} - /> - ))} - - - {tabs.map((item) => ( - `1px solid ${theme.palette.primary[200]}` }} - key={item.key} - value={item.key} - > - {item.children} - - ))} - - - - ); -}; - -export default TopAddresses; diff --git a/src/pages/TopAddresses/styles.ts b/src/pages/TopAddresses/styles.ts deleted file mode 100644 index 8dfcca05ac..0000000000 --- a/src/pages/TopAddresses/styles.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Container, Box, styled } from "@mui/material"; - -export const TabTitle = styled(Box)` - margin-bottom: 0px; - padding-left: 8px; - color: ${({ theme }) => theme.palette.secondary.light}; - text-align: left; - text-transform: capitalize !important; - font-size: 18px; - line-height: 21px; - ${({ theme }) => theme.breakpoints.down("sm")} { - font-size: 14px; - line-height: 16px; - } - &.active { - color: ${({ theme }) => theme.palette.primary.main}; - } -`; - -export const StyledContainer = styled(Container)` - .MuiSelect-select.MuiSelect-outlined { - padding-top: 10px; - padding-bottom: 10px; - } - - @media screen and (max-width: ${(props) => props.theme.breakpoints.values.sm}px) { - margin-top: 0px !important; - } -`; diff --git a/src/pages/TransactionDetail/TransactionDetail.test.tsx b/src/pages/TransactionDetail/TransactionDetail.test.tsx index 4def8715dc..9aec2fdfcc 100644 --- a/src/pages/TransactionDetail/TransactionDetail.test.tsx +++ b/src/pages/TransactionDetail/TransactionDetail.test.tsx @@ -6,7 +6,19 @@ import useFetch from "src/commons/hooks/useFetch"; import { details } from "src/commons/routers"; import Transaction from "."; - +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn() + })) +}); const transaction = { tx: { hash: "transaction-hash", diff --git a/src/pages/TransactionList/TransactionList.test.tsx b/src/pages/TransactionList/TransactionList.test.tsx index 88c636a354..f0b2aebeeb 100644 --- a/src/pages/TransactionList/TransactionList.test.tsx +++ b/src/pages/TransactionList/TransactionList.test.tsx @@ -61,8 +61,8 @@ describe("Transactions view", () => { ); + const transactionsItem = screen.getByTestId(`transactions.table.block#${mockItem.data[0].blockNo}`); - const transactionsItem = screen.getByText(8902162); fireEvent.click(transactionsItem); await waitFor(() => { expect(history.location.pathname).toBe(details.block(mockItem.data[0].blockNo.toString())); diff --git a/src/pages/TransactionList/index.tsx b/src/pages/TransactionList/index.tsx index 8afbe3fba2..bbbeed22a2 100644 --- a/src/pages/TransactionList/index.tsx +++ b/src/pages/TransactionList/index.tsx @@ -1,11 +1,9 @@ import styled from "@emotion/styled"; import { Container } from "@mui/material"; import { useEffect, useState } from "react"; -import { useSelector } from "react-redux"; import { API } from "src/commons/utils/api"; import TransactionList from "src/components/TransactionLists"; -import DetailViewTransaction from "src/components/commons/DetailView/DetailViewTransaction"; import { setOnDetailView } from "src/stores/user"; const StyledContainer = styled(Container)` @@ -17,7 +15,6 @@ const StyledContainer = styled(Container)` const Transactions = () => { const [selected, setSelected] = useState(null); - const { onDetailView } = useSelector(({ user }: RootState) => user); useEffect(() => { window.history.replaceState({}, document.title); @@ -29,27 +26,11 @@ const Transactions = () => { setSelected(r.hash); }; - const handleClose = () => { - setOnDetailView(false); - setSelected(null); - }; - - useEffect(() => { - if (!onDetailView) handleClose(); - }, [onDetailView]); - return ( <> - + - ); }; diff --git a/src/types/scripts.d.ts b/src/types/scripts.d.ts index 71b2e5a432..6596c6f337 100644 --- a/src/types/scripts.d.ts +++ b/src/types/scripts.d.ts @@ -8,7 +8,6 @@ type NativeScriptsList = { before: string; after: string; isMultiSig: boolean; - numberOfAssetHolders: number; numberOfTokens: number; scriptHash: string; isOpen: boolean; diff --git a/src/types/stakeKey.d.ts b/src/types/stakeKey.d.ts index 4000d629a9..6300414ff9 100644 --- a/src/types/stakeKey.d.ts +++ b/src/types/stakeKey.d.ts @@ -81,6 +81,14 @@ interface StakeAnalytics { liveStake: number; } +interface PostOverview { + depositsAndFees: number; + rewards: number; + treasury: number; + reserves: number; + epoch: number; +} + interface RegistrationItem { txHash: string; fee: number; diff --git a/src/types/table.d.ts b/src/types/table.d.ts index 11188a6da4..b8b879d457 100644 --- a/src/types/table.d.ts +++ b/src/types/table.d.ts @@ -37,7 +37,8 @@ export type TableRowProps = Pick & screen?: string; dataLength?: number; index: number; - onClickRow?: (e: React.MouseEvent, record: T) => void; + onClickExpandedRow?: (e: React.MouseEvent, record: T) => void; + handleOpenDetail?: (e: React.MouseEvent, record: T) => void; showTabView?: boolean; selected?: boolean; selectedProps?: { @@ -49,6 +50,7 @@ export type TableRowProps = Pick & isSelected?: (item: T) => boolean; isModal?: boolean; onCallBackHeight?: (height: number) => void; + expandedTable?: boolean; }; export interface TableProps { @@ -79,13 +81,16 @@ export interface TableProps { }; allowSelect?: boolean; onClickRow?: (e: React.MouseEvent, record: T) => void; + onClickExpandedRow?: (data: T) => void; + expandedRowData?: { label: string; value: string; isFormatADA?: boolean }[]; + expandedTable?: boolean; showTabView?: boolean; /** * @default This props default is row index. If value is string, key of row is row[rowKey]. * If rowKey is function, key is result of that fuction */ rowKey?: string | ((record: T) => string | number | symbol); - selected?: string | number | symbol | null; + selected?: (string | number | symbol | null)[]; selectedProps?: { className?: string; style?: React.CSSProperties; diff --git a/src/types/transactions.d.ts b/src/types/transactions.d.ts index 7932a01345..8ac85c2f02 100644 --- a/src/types/transactions.d.ts +++ b/src/types/transactions.d.ts @@ -262,7 +262,7 @@ interface Transaction { stakeAddress: string; }[]; metadataHash: string; - metadata: { + metadata?: { label: number; value: string; metadataCIP20: { valid?: boolean; requiredProperties?: TTCIP25Properties[] };