Commit 2eb8c2c3 authored by tom's avatar tom

Merge branch 'main' of github.com:blockscout/frontend into contract-creation-code

parents f32bcfe6 d0f5a962
export default function formatNumberToMetricPrefix(number: number) {
return Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: 3,
}).format(number);
}
......@@ -9,7 +9,7 @@ import Stats from '../ui/pages/Stats';
const StatsPage: NextPage = () => {
return (
<>
<Head><title>{ appConfig.network.name } Stats</title></Head>
<Head><title>{ appConfig.network.name } stats</title></Head>
<Stats/>
</>
);
......
......@@ -4,6 +4,7 @@ export enum StatsSectionId {
'all',
'accounts',
'blocks',
'tokens',
'transactions',
'gas',
}
......@@ -19,7 +20,7 @@ export enum StatsIntervalId {
}
export type StatsChart = {
id: string;
apiId: string;
title: string;
description: string;
}
......@@ -4,6 +4,7 @@ import React from 'react';
import appConfig from 'configs/app/config';
import useApiQuery from 'lib/api/useApiQuery';
import ChartWidget from 'ui/shared/chart/ChartWidget';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
interface Props {
addressHash: string;
......@@ -19,6 +20,10 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
value: BigNumber(value).div(10 ** appConfig.network.currency.decimals).toNumber(),
})), [ data ]);
if (isError) {
return <DataFetchAlert/>;
}
if (!items?.length) {
return null;
}
......@@ -28,7 +33,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
chartHeight="200px"
title="Balances"
items={ items }
isLoading={ isLoading || isError }
isLoading={ isLoading }
/>
);
};
......
......@@ -22,7 +22,7 @@ const Stats = () => {
return (
<Page>
<PageTitle text={ `${ appConfig.network.name } Stats` }/>
<PageTitle text={ `${ appConfig.network.name } stats` }/>
<Box mb={{ base: 6, sm: 8 }}>
<NumberWidgetsList/>
......
......@@ -51,6 +51,8 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop
}, []);
const handleFileSaveClick = useCallback(() => {
// wait for context menu to close
setTimeout(() => {
if (ref.current) {
domToImage.toPng(ref.current,
{
......@@ -73,6 +75,7 @@ const ChartWidget = ({ items, title, description, isLoading, chartHeight }: Prop
link.remove();
});
}
}, 100);
}, [ pngBackgroundColor, title ]);
const handleSVGSavingClick = useCallback(() => {
......
......@@ -29,9 +29,9 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
const ref = React.useRef<SVGSVGElement>(null);
const color = useToken('colors', 'blue.200');
const overlayRef = React.useRef<SVGRectElement>(null);
const chartId = useMemo(() => `chart-${ title.split(' ').join('') }`, [ title ]);
const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]);
const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const chartId = `chart-${ title.split(' ').join('') }-${ isEnlarged ? 'fullscreen' : 'small' }`;
const displayedData = useMemo(() => items.slice(range[0], range[1]), [ items, range ]);
const chartData = [ { items: items, name: 'Value', color } ];
......
......@@ -3,6 +3,8 @@ import { useMemo } from 'react';
import type { TimeChartData } from 'ui/shared/chart/types';
import formatNumberToMetricPrefix from 'lib/formatNumberToMetricPrefix';
interface Props {
data: TimeChartData;
width: number;
......@@ -50,7 +52,7 @@ export default function useTimeChartController({ data, width, height }: Props) {
);
const xTickFormat = (d: d3.AxisDomain) => d.toLocaleString();
const yTickFormat = (d: d3.AxisDomain) => d.toLocaleString();
const yTickFormat = (d: d3.AxisDomain) => formatNumberToMetricPrefix(Number(d));
return {
xTickFormat,
......
......@@ -44,10 +44,10 @@ const ChartsWidgetsList = ({ charts, interval }: Props) => {
>
{ section.charts.map((chart) => (
<GridItem
key={ chart.id }
key={ chart.apiId }
>
<ChartWidgetContainer
id={ chart.id }
id={ chart.apiId }
title={ chart.title }
description={ chart.description }
interval={ interval }
......
......@@ -2,6 +2,7 @@ import { Grid } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import formatNumberToMetricPrefix from 'lib/formatNumberToMetricPrefix';
import { numberWidgetsScheme } from './constants/number-widgets-scheme';
import NumberWidget from './NumberWidget';
......@@ -12,15 +13,32 @@ const skeletonsCount = 8;
const NumberWidgetsList = () => {
const { data, isLoading } = useApiQuery('stats_counters');
const skeletonElement = [ ...Array(skeletonsCount) ]
.map((e, i) => <NumberWidgetSkeleton key={ i }/>);
return (
<Grid
gridTemplateColumns={{ base: 'repeat(2, 1fr)', lg: 'repeat(4, 1fr)' }}
gridGap={ 4 }
>
{ isLoading ? [ ...Array(skeletonsCount) ]
.map((e, i) => <NumberWidgetSkeleton key={ i }/>) :
numberWidgetsScheme.map(({ id, title }) =>
data?.counters[id] ? <NumberWidget key={ id } label={ title } value={ Number(data.counters[id]).toLocaleString() }/> : null) }
{ isLoading ? skeletonElement :
numberWidgetsScheme.map(({ id, title, formatFn }) => {
if (!data?.counters[id]) {
return null;
}
const value = formatNumberToMetricPrefix(Number(data.counters[id]));
return (
<NumberWidget
key={ id }
label={ title }
value={ formatFn ?
formatFn(value) :
value }
/>
);
}) }
</Grid>
);
};
......
import type { StatsSection } from 'types/client/stats';
export const statsChartsScheme: Array<StatsSection> = [
{
id: 'accounts',
title: 'Accounts',
charts: [
{
apiId: 'activeAccounts',
title: 'Active accounts',
description: 'Active accounts number per period',
},
{
apiId: 'accountsGrowth',
title: 'Accounts growth',
description: 'Cumulative accounts number per period',
},
],
},
{
id: 'transactions',
title: 'Transactions',
charts: [
{
apiId: 'averageTxnFee',
title: 'Average transaction fee',
description: 'The average amount in USD spent per transaction',
},
{
apiId: 'txnsFee',
title: 'Transactions fees',
description: 'Amount of tokens paid as fees',
},
{
apiId: 'newTxns',
title: 'New transactions',
description: 'New transactions number',
},
{
apiId: 'txnsGrowth',
title: 'Transactions growth',
description: 'Cumulative transactions number',
},
],
},
{
id: 'blocks',
title: 'Blocks',
charts: [
{
id: 'newBlocksPerDay',
apiId: 'newBlocksPerDay',
title: 'New blocks',
description: 'New blocks number per day',
description: 'New blocks number',
},
{
apiId: 'averageBlockSize',
title: 'Average block size',
description: 'Average size of blocks in bytes',
},
],
},
{
id: 'tokens',
title: 'Tokens',
charts: [
{
apiId: 'nativeCoinHoldersGrowth',
title: 'Native coin holders growth',
description: 'Cumulative token holders number for the period',
},
{
apiId: 'newNativeCoinTransfers',
title: 'New native coins transfers',
description: 'New token transfers number for the period',
},
{
apiId: 'nativeCoinSupply',
title: 'Native coin circulating supply',
description: 'Amount of token circulating supply for the period',
},
],
},
{
id: 'gas',
title: 'Gas',
charts: [
{
apiId: 'averageGasLimit',
title: 'Average gas limit',
description: 'Average gas limit per block for the period',
},
{
apiId: 'gasUsedGrowth',
title: 'Gas used growth',
description: 'Cumulative gas used for the period',
},
{
apiId: 'averageGasPrice',
title: 'Average gas price',
description: 'Average gas price for the period',
},
// {
// id: 'average-block-size',
// title: 'Average block size',
// description: 'Average size of blocks in bytes',
// },
],
},
// {
// id: 'transactions',
// title: 'Transactions',
// charts: [
// {
// id: 'average-transaction-fee',
// title: 'Average transaction fee',
// description: 'The average amount in USD spent per transaction',
// },
// {
// id: 'transactions-fees',
// title: 'Transactions fees',
// description: 'Amount of tokens paid as fees',
// },
// {
// id: 'new-transactions',
// title: 'Transactions fees',
// description: 'New transactions number per period',
// },
// {
// id: 'transactions-growth',
// title: 'Transactions growth',
// description: 'Cumulative transactions number per period',
// },
// ],
// },
// {
// id: 'accounts',
// title: 'Accounts',
// charts: [
// {
// id: 'active-accounts',
// title: 'Active accounts',
// description: 'Active accounts number per period',
// },
// {
// id: 'accounts-growth',
// title: 'Accounts growth',
// description: 'Cumulative accounts number per period',
// },
// ],
// },
];
......@@ -2,7 +2,7 @@ import type { Stats } from 'types/api/stats';
type Key = keyof Stats['counters'];
export const numberWidgetsScheme: Array<{id: Key; title: string}> = [
export const numberWidgetsScheme: Array<{id: Key; title: string; formatFn?: (n: string) => string}> = [
{
id: 'totalBlocks',
title: 'Total blocks',
......@@ -10,6 +10,7 @@ export const numberWidgetsScheme: Array<{id: Key; title: string}> = [
{
id: 'averageBlockTime',
title: 'Average block time',
formatFn: (n) => `${ n } s`,
},
{
id: 'totalTransactions',
......@@ -35,8 +36,4 @@ export const numberWidgetsScheme: Array<{id: Key; title: string}> = [
id: 'totalNativeCoinTransfers',
title: 'Total native coin transfers',
},
{
id: 'totalAccounts',
title: 'Total accounts',
},
];
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