Commit 9c53fbaf authored by tom's avatar tom Committed by isstuev

stats info

parent 02b84b3b
// todo_tom leave only one api endpoint
import handler from 'lib/api/handler';
const getUrl = () => '/v2/stats';
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
export type Stats = {
total_blocks: string;
total_addresses: string;
total_transactions: string;
average_block_time: number;
coin_price: string;
total_gas_used: string;
transactions_today: string;
gas_used_today: string;
gas_prices: {average: number; fast: number; slow: number};
static_gas_price: string;
market_cap: string;
}
...@@ -3,6 +3,7 @@ export enum QueryKeys { ...@@ -3,6 +3,7 @@ export enum QueryKeys {
profile = 'profile', profile = 'profile',
txsValidate = 'txs-validated', txsValidate = 'txs-validated',
txsPending = 'txs-pending', txsPending = 'txs-pending',
stats='stats',
tx = 'tx', tx = 'tx',
txInternals = 'tx-internals', txInternals = 'tx-internals',
txLogs = 'tx-logs', txLogs = 'tx-logs',
......
import { Text, Flex, Box, useColorModeValue } from '@chakra-ui/react'; import { Text, Flex, Box, Skeleton, useColorModeValue } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { ChainIndicatorId } from './types'; import type { ChainIndicatorId } from './types';
import type { Stats } from 'types/api/stats';
interface Props { interface Props {
id: ChainIndicatorId; id: ChainIndicatorId;
title: string; title: string;
value: string; value: (stats: Stats) => string;
icon: React.ReactNode; icon: React.ReactNode;
isSelected: boolean; isSelected: boolean;
onClick: (id: ChainIndicatorId) => void; onClick: (id: ChainIndicatorId) => void;
stats: UseQueryResult<Stats>;
} }
const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick }: Props) => { const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick, stats }: Props) => {
const bgColor = useColorModeValue('white', 'gray.900'); const bgColor = useColorModeValue('white', 'gray.900');
const handleClick = React.useCallback(() => { const handleClick = React.useCallback(() => {
onClick(id); onClick(id);
}, [ id, onClick ]); }, [ id, onClick ]);
const valueContent = (() => {
if (stats.isLoading) {
return <Skeleton h={ 3 } w="70px" my={ 1.5 }/>;
}
if (stats.isError) {
return <Text variant="secondary" fontWeight={ 400 }>no data</Text>;
}
return <Text variant="secondary" fontWeight={ 600 }>{ value(stats.data) }</Text>;
})();
return ( return (
<Flex <Flex
alignItems="center" alignItems="center"
...@@ -39,7 +54,7 @@ const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick }: Pro ...@@ -39,7 +54,7 @@ const ChainIndicatorItem = ({ id, title, value, icon, isSelected, onClick }: Pro
{ icon } { icon }
<Box> <Box>
<Text fontFamily="heading" fontWeight={ 500 }>{ title }</Text> <Text fontFamily="heading" fontWeight={ 500 }>{ title }</Text>
<Text variant="secondary" fontWeight={ 600 }>{ value }</Text> { valueContent }
</Box> </Box>
</Flex> </Flex>
); );
......
import { Box, Flex, Icon, Text, Tooltip, useColorModeValue } from '@chakra-ui/react'; import { Box, Flex, Icon, Skeleton, Text, Tooltip, useColorModeValue } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { Stats } from 'types/api/stats';
import { QueryKeys } from 'types/client/queries';
import infoIcon from 'icons/info.svg'; import infoIcon from 'icons/info.svg';
import useFetch from 'lib/hooks/useFetch';
import ChainIndicatorChartContainer from './ChainIndicatorChartContainer'; import ChainIndicatorChartContainer from './ChainIndicatorChartContainer';
import ChainIndicatorItem from './ChainIndicatorItem'; import ChainIndicatorItem from './ChainIndicatorItem';
...@@ -11,11 +16,34 @@ import INDICATORS from './utils/indicators'; ...@@ -11,11 +16,34 @@ import INDICATORS from './utils/indicators';
const ChainIndicators = () => { const ChainIndicators = () => {
const [ selectedIndicator, selectIndicator ] = React.useState(INDICATORS[0].id); const [ selectedIndicator, selectIndicator ] = React.useState(INDICATORS[0].id);
const indicator = INDICATORS.find(({ id }) => id === selectedIndicator); const indicator = INDICATORS.find(({ id }) => id === selectedIndicator);
const queryResult = useFetchChartData(indicator); const queryResult = useFetchChartData(indicator);
const fetch = useFetch();
const statsQueryResult = useQuery<unknown, unknown, Stats>(
[ QueryKeys.stats ],
() => fetch('/node-api/stats'),
);
const bgColor = useColorModeValue('white', 'gray.900'); const bgColor = useColorModeValue('white', 'gray.900');
const listBgColor = useColorModeValue('gray.50', 'black'); const listBgColor = useColorModeValue('gray.50', 'black');
const valueTitle = (() => {
if (statsQueryResult.isLoading) {
return <Skeleton h="48px" w="215px" mt={ 3 } mb={ 4 }/>;
}
if (statsQueryResult.isError) {
return <Text mt={ 3 } mb={ 4 }>There is no data</Text>;
}
return (
<Text fontWeight={ 600 } fontFamily="heading" fontSize="48px" lineHeight="48px" mt={ 3 } mb={ 4 }>
{ indicator?.value(statsQueryResult.data) }
</Text>
);
})();
return ( return (
<Flex p={ 8 } borderRadius="lg" boxShadow="lg" bgColor={ bgColor } columnGap={ 12 } w="100%" alignItems="stretch"> <Flex p={ 8 } borderRadius="lg" boxShadow="lg" bgColor={ bgColor } columnGap={ 12 } w="100%" alignItems="stretch">
<Flex flexGrow={ 1 } flexDir="column"> <Flex flexGrow={ 1 } flexDir="column">
...@@ -29,7 +57,7 @@ const ChainIndicators = () => { ...@@ -29,7 +57,7 @@ const ChainIndicators = () => {
</Tooltip> </Tooltip>
) } ) }
</Flex> </Flex>
<Text fontWeight={ 600 } fontFamily="heading" fontSize="48px" lineHeight="48px" mt={ 3 } mb={ 4 }>{ indicator?.value }</Text> { valueTitle }
<ChainIndicatorChartContainer { ...queryResult }/> <ChainIndicatorChartContainer { ...queryResult }/>
</Flex> </Flex>
<Flex flexShrink={ 0 } flexDir="column" as="ul" p={ 3 } borderRadius="lg" bgColor={ listBgColor } rowGap={ 3 }> <Flex flexShrink={ 0 } flexDir="column" as="ul" p={ 3 } borderRadius="lg" bgColor={ listBgColor } rowGap={ 3 }>
...@@ -39,6 +67,7 @@ const ChainIndicators = () => { ...@@ -39,6 +67,7 @@ const ChainIndicators = () => {
{ ...indicator } { ...indicator }
isSelected={ selectedIndicator === indicator.id } isSelected={ selectedIndicator === indicator.id }
onClick={ selectIndicator } onClick={ selectIndicator }
stats={ statsQueryResult }
/> />
)) } )) }
</Flex> </Flex>
......
import type { ChartTransactionResponse, ChartMarketResponse } from 'types/api/charts'; import type { ChartTransactionResponse, ChartMarketResponse } from 'types/api/charts';
import type { Stats } from 'types/api/stats';
import type { QueryKeys } from 'types/client/queries'; import type { QueryKeys } from 'types/client/queries';
import type { TimeChartDataItem } from 'ui/shared/chart/types'; import type { TimeChartDataItem } from 'ui/shared/chart/types';
...@@ -9,7 +10,7 @@ export type ChainIndicatorId = 'daily_txs' | 'coin_price' | 'market_cup'; ...@@ -9,7 +10,7 @@ export type ChainIndicatorId = 'daily_txs' | 'coin_price' | 'market_cup';
export interface TChainIndicator<Q extends ChartsQueryKeys> { export interface TChainIndicator<Q extends ChartsQueryKeys> {
id: ChainIndicatorId; id: ChainIndicatorId;
title: string; title: string;
value: string; value: (stats: Stats) => string;
icon: React.ReactNode; icon: React.ReactNode;
hint?: string; hint?: string;
api: { api: {
......
...@@ -14,7 +14,7 @@ const COLOR = '#439AE2'; ...@@ -14,7 +14,7 @@ const COLOR = '#439AE2';
const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = { const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = {
id: 'daily_txs', id: 'daily_txs',
title: 'Daily transactions', title: 'Daily transactions',
value: '1,531.14 M', value: (stats) => shortenNumberWithLetter(Number(stats.transactions_today), undefined, { maximumFractionDigits: 2 }),
icon: <Icon as={ txIcon } boxSize={ 6 } bgColor="#56ACD1" borderRadius="base" color="white"/>, icon: <Icon as={ txIcon } boxSize={ 6 } bgColor="#56ACD1" borderRadius="base" color="white"/>,
hint: `The total daily number of transactions on the blockchain for the last month.`, hint: `The total daily number of transactions on the blockchain for the last month.`,
api: { api: {
...@@ -26,7 +26,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = { ...@@ -26,7 +26,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = {
.sort(sortByDateDesc), .sort(sortByDateDesc),
name: 'Tx/day', name: 'Tx/day',
color: COLOR, color: COLOR,
valueFormatter: (x) => '$' + shortenNumberWithLetter(x), valueFormatter: (x) => shortenNumberWithLetter(x, undefined, { maximumFractionDigits: 2 }),
} ]), } ]),
}, },
}; };
...@@ -34,7 +34,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = { ...@@ -34,7 +34,7 @@ const dailyTxsIndicator: TChainIndicator<QueryKeys.chartsTxs> = {
const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
id: 'coin_price', id: 'coin_price',
title: `${ appConfig.network.currency.symbol } price`, title: `${ appConfig.network.currency.symbol } price`,
value: '$0.998566', value: (stats) => '$' + Number(stats.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
// todo_tom change to TokenLogo after token-transfers branch merge // todo_tom change to TokenLogo after token-transfers branch merge
icon: <Icon as={ globeIcon } boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>, icon: <Icon as={ globeIcon } boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>,
hint: `${ appConfig.network.currency.symbol } daily price in USD.`, hint: `${ appConfig.network.currency.symbol } daily price in USD.`,
...@@ -55,7 +55,7 @@ const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { ...@@ -55,7 +55,7 @@ const coinPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
id: 'market_cup', id: 'market_cup',
title: 'Market cap', title: 'Market cap',
value: '$379M', value: (stats) => '$' + shortenNumberWithLetter(Number(stats.market_cap), undefined, { maximumFractionDigits: 0 }),
icon: <Icon as={ globeIcon } boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>, icon: <Icon as={ globeIcon } boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
hint: 'The total market value of a cryptocurrency\'s circulating supply. It is analogous to the free-float capitalization in the stock market. Market Cap = Current Price x Circulating Supply.', hint: 'The total market value of a cryptocurrency\'s circulating supply. It is analogous to the free-float capitalization in the stock market. Market Cap = Current Price x Circulating Supply.',
...@@ -68,7 +68,7 @@ const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = { ...@@ -68,7 +68,7 @@ const marketPriceIndicator: TChainIndicator<QueryKeys.chartsMarket> = {
.sort(sortByDateDesc), .sort(sortByDateDesc),
name: 'Market cap', name: 'Market cap',
color: COLOR, color: COLOR,
valueFormatter: (x) => '$' + x.toLocaleString(undefined, { maximumFractionDigits: 0 }), valueFormatter: (x) => '$' + shortenNumberWithLetter(x, undefined, { maximumFractionDigits: 0 }),
} ]), } ]),
}, },
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment