diff --git a/explorer-nextjs/src/app/account/[id]/page.tsx b/explorer-nextjs/src/app/account/[id]/page.tsx index 92fc2cb796..f3029bd4bf 100644 --- a/explorer-nextjs/src/app/account/[id]/page.tsx +++ b/explorer-nextjs/src/app/account/[id]/page.tsx @@ -1,10 +1,46 @@ -import { NYM_ACCOUNT_ADDRESS } from "@/app/api/urls"; -import ExplorerCard from "@/components/cards/ExplorerCard"; +import { NYM_ACCOUNT_ADDRESS, NYM_PRICES_API } from "@/app/api/urls"; +import { AccountBalancesCard } from "@/components/accountPageComponents/AccountBalancesCard"; +import { AccountInfoCard } from "@/components/accountPageComponents/AccountInfoCard"; import { ContentLayout } from "@/components/contentLayout/ContentLayout"; import SectionHeading from "@/components/headings/SectionHeading"; -import ExplorerListItem from "@/components/list/ListItem"; import ExplorerButtonGroup from "@/components/toggleButton/ToggleButton"; -import { Box, Grid2, Stack } from "@mui/material"; +import { Box, Grid2 } from "@mui/material"; + +interface IRewardDetails { + amount_staked: IAmountDetails; + node_id: number; + node_still_fully_bonded: boolean; + rewards: IAmountDetails; +} + +interface IAmountDetails { + denom: string; + amount: string; +} + +interface IDelegationDetails { + node_id: number; + delegated: IAmountDetails; + height: number; + proxy: null | string; +} + +interface ITotalDetails { + amount: string; + denom: string; +} + +export interface IAccountInfo { + accumulated_rewards: IRewardDetails[]; + address: string; + balances: IAmountDetails[]; + claimable_rewards: IAmountDetails; + delegations: IDelegationDetails[]; + operator_rewards: null | any; + total_delegations: ITotalDetails; + total_value: ITotalDetails; + vesting_account: null | any; +} export default async function Account({ params, @@ -12,7 +48,7 @@ export default async function Account({ params: Promise<{ address: string }>; }) { // const address = (await params).address; - const address = "n17hllefp8rn3ayk23rsgp7agtxkv56mazv3w637"; + const address = "n1z0msxu8c098umdhnthpr2ac3ck2n3an97dm8pn"; const nymAccountAddress = `${NYM_ACCOUNT_ADDRESS}${address}`; const accountData = await fetch(nymAccountAddress, { @@ -23,12 +59,25 @@ export default async function Account({ next: { revalidate: 60 }, // refresh event list cache at given interval }); - const nymAccountData = await accountData.json(); + const nymAccountData: IAccountInfo = await accountData.json(); if (!nymAccountData) { return null; } + const nymPrice = await fetch(NYM_PRICES_API, { + headers: { + Accept: "application/json", + "Content-Type": "application/json; charset=utf-8", + }, + next: { revalidate: 60 }, + // refresh event list cache at given interval + }); + + const nymPriceData = await nymPrice.json(); + + console.log("nymPriceData :>> ", nymPriceData); + console.log("nymAccountData :>> ", nymAccountData); return ( @@ -51,33 +100,13 @@ export default async function Account({ - -
- + - - - - - - - - - + diff --git a/explorer-nextjs/src/app/api/urls.ts b/explorer-nextjs/src/app/api/urls.ts index 1a5f4db8af..a10204aee2 100644 --- a/explorer-nextjs/src/app/api/urls.ts +++ b/explorer-nextjs/src/app/api/urls.ts @@ -29,3 +29,6 @@ export const NYM_NODE_BONDED = export const NYM_ACCOUNT_ADDRESS = "https://explorer.nymtech.net/api/v1/tmp/unstable/account/"; + +export const NYM_PRICES_API = + "https://canary-nym-vpn-chain-payment-watcher.nymte.ch/v1/price/average"; diff --git a/explorer-nextjs/src/app/mocks/dummy_data.ts b/explorer-nextjs/src/app/mocks/dummy_data.ts index 588c5eb126..b671bb36ba 100644 --- a/explorer-nextjs/src/app/mocks/dummy_data.ts +++ b/explorer-nextjs/src/app/mocks/dummy_data.ts @@ -1,4 +1,4 @@ -import type { IAccountStatsCardProps } from "@/components/cards/AccountStatsCard"; +import type { IAccountBalancesTableProps } from "@/components/cards/AccountBalancesTable"; import type { ContentCardProps } from "@/components/cards/MonoCard"; const explorerCard: ContentCardProps = { @@ -78,7 +78,7 @@ const explorerCard: ContentCardProps = { }, }; -const accountStatsCard: IAccountStatsCardProps = { +const accountStatsCard: IAccountBalancesTableProps = { overTitle: "Total value", priceTitle: 1990.0174, rows: [ diff --git a/explorer-nextjs/src/components/accountPageComponents/AccountBalancesCard.tsx b/explorer-nextjs/src/components/accountPageComponents/AccountBalancesCard.tsx new file mode 100644 index 0000000000..6f437d10ad --- /dev/null +++ b/explorer-nextjs/src/components/accountPageComponents/AccountBalancesCard.tsx @@ -0,0 +1,131 @@ +"use client"; +import type { IAccountInfo } from "@/app/account/[id]/page"; +import ExplorerCard from "../cards/ExplorerCard"; + +import { runTurboTracing } from "next/dist/build/swc/generated-native"; +import { AccountBalancesTable } from "../cards/AccountBalancesTable"; + +export interface IAccontStatsRowProps { + type: string; + allocation: number; + amount: number; + value: number; + history?: { type: string; amount: number }[]; + isLastRow?: boolean; + progressBarColor?: string; +} + +interface IAccountBalancesCardProps { + accountInfo: IAccountInfo; + nymPrice: number; +} + +const getNymsFormated = (unyms: number) => { + const balance = unyms / 1000000; + return balance; +}; +const getPriceInUSD = (unyms: number, usdPrice: number) => { + const balanceInUSD = (unyms / 1000000) * usdPrice; + const balanceFormated = Number(balanceInUSD.toFixed(2)); + return balanceFormated; +}; + +const getAllocation = (unyms: number, totalUnyms: number): number => { + const allocationPercentage = (unyms * 100) / totalUnyms; + return Number(allocationPercentage.toFixed(2)); +}; + +export const AccountBalancesCard = (props: IAccountBalancesCardProps) => { + const { accountInfo, nymPrice } = props; + + const totalBalanceUSD = getPriceInUSD( + Number(accountInfo.total_value.amount), + nymPrice, + ); + + const spendableNYM = getNymsFormated(Number(accountInfo.balances[0].amount)); + const spendableUSD = getPriceInUSD( + Number(accountInfo.balances[0].amount), + nymPrice, + ); + const spendableAllocation = getAllocation( + Number(accountInfo.balances[0].amount), + Number(accountInfo.total_value.amount), + ); + + const delegationsNYM = getNymsFormated( + Number(accountInfo.total_delegations.amount), + ); + const delegationsUSD = getPriceInUSD( + Number(accountInfo.total_delegations.amount), + nymPrice, + ); + const delegationsAllocation = getAllocation( + Number(accountInfo.total_delegations.amount), + Number(accountInfo.total_value.amount), + ); + + const claimableNYM = getNymsFormated( + Number(accountInfo.claimable_rewards.amount), + ); + const claimableUSD = getPriceInUSD( + Number(accountInfo.claimable_rewards.amount), + nymPrice, + ); + const claimableAllocation = getAllocation( + Number(accountInfo.claimable_rewards.amount), + Number(accountInfo.total_value.amount), + ); + + const tableRows = [ + { + type: "Spendable", + allocation: spendableAllocation, + amount: spendableNYM, + value: spendableUSD, + }, + { + type: "Delegated", + allocation: delegationsAllocation, + amount: delegationsNYM, + value: delegationsUSD, + history: [ + { type: "Liquid", amount: 6900 }, + { type: "Locked", amount: 6900 }, + ], + }, + { + type: "Claimable", + allocation: claimableAllocation, + amount: claimableNYM, + value: claimableUSD, + history: [ + { type: "Unlocked", amount: 6900 }, + { type: "Staking rewards", amount: 6900 }, + { type: "Operator comission", amount: 6900 }, + ], + }, + { + type: "Self bonded", + allocation: 15.53, + amount: 12800, + value: 1200, + }, + { + type: "Locked", + allocation: 15.53, + amount: 12800, + value: 1200, + }, + ]; + + return ( + + + + ); +}; diff --git a/explorer-nextjs/src/components/accountPageComponents/AccountInfoCard.tsx b/explorer-nextjs/src/components/accountPageComponents/AccountInfoCard.tsx new file mode 100644 index 0000000000..bd3ea8ad8d --- /dev/null +++ b/explorer-nextjs/src/components/accountPageComponents/AccountInfoCard.tsx @@ -0,0 +1,48 @@ +"use client"; +import type { IAccountInfo } from "@/app/account/[id]/page"; +import { Box, Stack, Typography } from "@mui/material"; +import ExplorerCard from "../cards/ExplorerCard"; +import CopyToClipboard from "../copyToClipboard/CopyToClipboard"; +import ExplorerListItem from "../list/ListItem"; +import { CardQRCode } from "../qrCode/QrCode"; + +interface IAccountInfoCardProps { + accountInfo: IAccountInfo; +} + +export const AccountInfoCard = (props: IAccountInfoCardProps) => { + const { accountInfo } = props; + + const balance = Number(accountInfo.balances[0].amount) / 1000000; + const balanceFormated = `${balance} NYM`; + + return ( + + + + + + + + {accountInfo.address} + + + } + /> + + + ); +}; diff --git a/explorer-nextjs/src/components/cards/AccountStatsCard.tsx b/explorer-nextjs/src/components/cards/AccountBalancesTable.tsx similarity index 71% rename from explorer-nextjs/src/components/cards/AccountStatsCard.tsx rename to explorer-nextjs/src/components/cards/AccountBalancesTable.tsx index 5bb8978f13..372a1dbeb3 100644 --- a/explorer-nextjs/src/components/cards/AccountStatsCard.tsx +++ b/explorer-nextjs/src/components/cards/AccountBalancesTable.tsx @@ -235,14 +235,12 @@ const Row = (props: IAccontStatsRowProps) => { ); }; -export interface IAccountStatsCardProps { +export interface IAccountBalancesTableProps { rows: Array; - overTitle?: string; - priceTitle?: number; } -export const AccountStatsCard = (props: IAccountStatsCardProps) => { - const { rows, overTitle, priceTitle } = props; +export const AccountBalancesTable = (props: IAccountBalancesTableProps) => { + const { rows } = props; const tablet = useMediaQuery(TABLET_WIDTH); const progressBarPercentages = () => { return rows.map((row) => row.allocation); @@ -262,80 +260,63 @@ export const AccountStatsCard = (props: IAccountStatsCardProps) => { const progressValues = getProgressValues(); return ( - - - {overTitle && ( - - {overTitle} - - )} - {priceTitle && ( - - ${priceTitle} - - )} - {!tablet && } - - - - {tablet ? ( - - - - Type - - - - - Allocation - - - - - Amount - - - - - Value - - - - - ) : ( - - - - Type - - - - - Amount / Value - - - - - )} - - - {rows.map((row, i) => ( - - ))} - -
-
-
-
+ + {!tablet && } + + + + {tablet ? ( + + + + Type + + + + + Allocation + + + + + Amount + + + + + Value + + + + + ) : ( + + + + Type + + + + + Amount / Value + + + + + )} + + + {rows.map((row, i) => ( + + ))} + +
+
+
); }; diff --git a/explorer-nextjs/src/components/cards/MonoCard.tsx b/explorer-nextjs/src/components/cards/MonoCard.tsx index 24fc11b3cb..edcfaf2ccf 100644 --- a/explorer-nextjs/src/components/cards/MonoCard.tsx +++ b/explorer-nextjs/src/components/cards/MonoCard.tsx @@ -16,6 +16,7 @@ import { UpDownPriceIndicator, } from "../price/UpDownPriceIndicator"; import type { IDynamicProgressBarProps } from "../progressBars/EpochProgressBar"; +import { CardQRCode, type ICardQRCodeProps } from "../qrCode/QrCode"; import { StarRating } from "../starRating"; interface ICardTitlePriceProps { @@ -126,26 +127,6 @@ const CardCopyAddress = (props: ICardCopyAddressProps) => { ); }; -interface ICardQRCodeProps { - url: string; -} - -const CardQRCode = (props: ICardQRCodeProps) => { - const { url } = props; - return ( - - - - - - ); -}; - interface ICardRatingsProps { ratings: Array<{ title: string; numberOfStars: number }>; } diff --git a/explorer-nextjs/src/components/nymNodePageComponents/BasicInfoCard.tsx b/explorer-nextjs/src/components/nymNodePageComponents/BasicInfoCard.tsx index 71403146c1..70b003243c 100644 --- a/explorer-nextjs/src/components/nymNodePageComponents/BasicInfoCard.tsx +++ b/explorer-nextjs/src/components/nymNodePageComponents/BasicInfoCard.tsx @@ -1,7 +1,8 @@ import type { IBondInfo, INodeDescription } from "@/app/api"; -import { Stack } from "@mui/material"; +import { Stack, Typography } from "@mui/material"; import { format } from "date-fns"; import ExplorerCard from "../cards/ExplorerCard"; +import CopyToClipboard from "../copyToClipboard/CopyToClipboard"; import ExplorerListItem from "../list/ListItem"; interface IBasicInfoCardProps { @@ -25,12 +26,40 @@ export const BasicInfoCard = (props: IBasicInfoCardProps) => { + + {bondInfo.bond_information.owner} + + + + } /> + + {bondInfo.bond_information.node.identity_key} + + + + } /> { label="Host" value={nodeDescription.description.host_information.ip_address.toString()} /> - diff --git a/explorer-nextjs/src/components/qrCode/QrCode.tsx b/explorer-nextjs/src/components/qrCode/QrCode.tsx new file mode 100644 index 0000000000..409ef51c5f --- /dev/null +++ b/explorer-nextjs/src/components/qrCode/QrCode.tsx @@ -0,0 +1,22 @@ +import { Box } from "@mui/material"; +import { QRCodeCanvas } from "qrcode.react"; + +export interface ICardQRCodeProps { + url: string; +} + +export const CardQRCode = (props: ICardQRCodeProps) => { + const { url } = props; + return ( + + + + ); +};