Commit b6f55cae authored by Yuri Mikhin's avatar Yuri Mikhin Committed by Yuri Mikhin

Add charts layout.

parent 4c77ef2d
......@@ -20,6 +20,7 @@ const paths = {
search_results: `/search-results`,
other: `/search-results`,
auth: `/auth/auth0`,
stats: '/stats',
};
module.exports = paths;
......@@ -85,6 +85,10 @@ export const ROUTES = {
pattern: PATHS.app_index,
},
stats: {
pattern: PATHS.stats,
},
// SEARCH
search_results: {
pattern: PATHS.search_results,
......
import type { NextPage } from 'next';
import Head from 'next/head';
import React from 'react';
import Stats from '../ui/pages/Stats';
const StatsPage: NextPage = () => {
return (
<>
<Head><title>Ethereum Stats</title></Head>
<Stats/>
</>
);
};
export default StatsPage;
export { getServerSideProps } from 'lib/next/getServerSideProps';
......@@ -16,4 +16,5 @@ export enum QueryKeys {
chartsMarket = 'charts-market',
indexBlocks='indexBlocks',
indexTxs='indexTxs',
jsonRpcUrl='json-rpc-url'
}
import { Box, Button, Grid, Heading, Text, useColorModeValue } from '@chakra-ui/react';
import React, { useCallback } from 'react';
import ChartWidgetGraph from './ChartWidgetGraph';
import { demoData } from './demo-data';
type Props = {
apiMethodURL: string;
title: string;
description: string;
}
const ChartWidget = ({ title, description }: Props) => {
const [ isZoomResetInitial, setIsZoomResetInitial ] = React.useState(true);
const handleZoom = useCallback(() => {
setIsZoomResetInitial(false);
}, []);
const handleZoomResetClick = useCallback(() => {
setIsZoomResetInitial(true);
}, []);
return (
<Box
padding={{ base: 3, md: 4 }}
borderRadius="md"
border="1px"
borderColor={ useColorModeValue('gray.200', 'gray.600') }
>
<Grid>
<Heading
mb={ 1 }
size={{ base: 'xs', md: 'sm' }}
>
{ title }
</Heading>
<Text
mb={ 1 }
gridColumn={ 1 }
as="p"
variant="secondary"
fontSize="xs"
>
{ description }
</Text>
{ !isZoomResetInitial && (
<Button
gridColumn={ 2 }
justifySelf="end"
alignSelf="center"
gridRow="1/3"
size="sm"
variant="outline"
onClick={ handleZoomResetClick }
>
Reset zoom
</Button>
) }
</Grid>
<ChartWidgetGraph
items={ demoData }
onZoom={ handleZoom }
isZoomResetInitial={ isZoomResetInitial }
/>
</Box>
);
};
export default ChartWidget;
import { useToken } from '@chakra-ui/react';
import React, { useEffect, useMemo } from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types';
import ChartArea from 'ui/shared/chart/ChartArea';
import ChartAxis from 'ui/shared/chart/ChartAxis';
import ChartGridLine from 'ui/shared/chart/ChartGridLine';
import ChartLine from 'ui/shared/chart/ChartLine';
import ChartOverlay from 'ui/shared/chart/ChartOverlay';
import ChartSelectionX from 'ui/shared/chart/ChartSelectionX';
import ChartTooltip from 'ui/shared/chart/ChartTooltip';
import useChartSize from 'ui/shared/chart/useChartSize';
import useTimeChartController from 'ui/shared/chart/useTimeChartController';
interface Props {
items: Array<TimeChartItem>;
onZoom: () => void;
isZoomResetInitial: boolean;
}
const CHART_MARGIN = { bottom: 20, left: 65, right: 30, top: 10 };
const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial }: Props) => {
const ref = React.useRef<SVGSVGElement>(null);
const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]);
const { width, height, innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const overlayRef = React.useRef<SVGRectElement>(null);
const color = useToken('colors', 'blue.500');
const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) =>
({ ...d, date: new Date(d.date) })), [ items, range ]);
const chartData = [ { items: items, name: 'chart', color } ];
const { yTickFormat, xScale, yScale } = useTimeChartController({
data: [ { items: displayedData, name: 'chart', color } ],
width: innerWidth,
height: innerHeight,
});
const handleRangeSelect = React.useCallback((nextRange: [ number, number ]) => {
setRange([ range[0] + nextRange[0], range[0] + nextRange[1] ]);
onZoom();
}, [ onZoom, range ]);
useEffect(() => {
if (isZoomResetInitial) {
setRange([ 0, Infinity ]);
}
}, [ isZoomResetInitial ]);
return (
<svg width={ width || '100%' } height={ height || '100%' } ref={ ref } cursor="pointer">
<g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ width ? 1 : 0 }>
<ChartGridLine
type="horizontal"
scale={ yScale }
ticks={ 3 }
size={ innerWidth }
disableAnimation
/>
<ChartArea
data={ displayedData }
color={ color }
xScale={ xScale }
yScale={ yScale }
/>
<ChartLine
data={ displayedData }
xScale={ xScale }
yScale={ yScale }
stroke={ color }
animation="left"
strokeWidth={ 3 }
/>
<ChartAxis
type="left"
scale={ yScale }
ticks={ 5 }
tickFormat={ yTickFormat }
disableAnimation
/>
<ChartOverlay ref={ overlayRef } width={ innerWidth } height={ innerHeight }>
<ChartAxis
type="bottom"
scale={ xScale }
transform={ `translate(0, ${ innerHeight })` }
ticks={ 5 }
anchorEl={ overlayRef.current }
disableAnimation
/>
<ChartTooltip
anchorEl={ overlayRef.current }
width={ innerWidth }
height={ innerHeight }
xScale={ xScale }
yScale={ yScale }
data={ chartData }
/>
<ChartSelectionX
anchorEl={ overlayRef.current }
height={ innerHeight }
scale={ xScale }
data={ chartData }
onSelect={ handleRangeSelect }
/>
</ChartOverlay>
</g>
</svg>
);
};
export default React.memo(ChartWidgetGraph);
import { Grid, GridItem, Heading, List, ListItem } from '@chakra-ui/react';
import React from 'react';
import { statisticsChartsScheme } from '../stats/constants/list';
import ChartWidget from './ChartWidget';
const WidgetsList = () => {
return (
<List>
{
statisticsChartsScheme.map((section) => (
<ListItem
key={ section.id }
mb={ 8 }
_last={{
marginBottom: 0,
}}
>
<Heading
size="md"
mb={ 4 }
>
{ section.title }
</Heading>
<Grid
templateColumns={{
sm: 'repeat(2, 1fr)',
}}
gap={ 4 }
>
{ section.charts.map((chart) => (
<GridItem key={ chart.id }>
<ChartWidget
apiMethodURL={ chart.apiMethodURL }
title={ chart.title }
description={ chart.description }
/>
</GridItem>
)) }
</Grid>
</ListItem>
))
}
</List>
);
};
export default WidgetsList;
import type { TimeChartItem } from '../shared/chart/types';
export const demoData: Array<TimeChartItem> = [ { date: new Date('2022-10-17T00:00:00.000Z'), value: 432670 }, {
date: new Date('2022-10-18T00:00:00.000Z'),
value: 370100,
}, { date: new Date('2022-10-19T00:00:00.000Z'), value: 283234 }, { date: new Date('2022-10-20T00:00:00.000Z'), value: 420910 }, {
date: new Date('2022-10-21T00:00:00.000Z'),
value: 411988,
}, { date: new Date('2022-10-22T00:00:00.000Z'), value: 356269 }, { date: new Date('2022-10-23T00:00:00.000Z'), value: 389747 }, {
date: new Date('2022-10-24T00:00:00.000Z'),
value: 387130,
}, { date: new Date('2022-10-25T00:00:00.000Z'), value: 428785 }, { date: new Date('2022-10-26T00:00:00.000Z'), value: 63809 }, {
date: new Date('2022-10-27T00:00:00.000Z'),
value: 50518,
}, { date: new Date('2022-10-28T00:00:00.000Z'), value: 39087 }, { date: new Date('2022-10-29T00:00:00.000Z'), value: 36789 }, {
date: new Date('2022-10-30T00:00:00.000Z'),
value: 48569,
}, { date: new Date('2022-10-31T00:00:00.000Z'), value: 62519 }, { date: new Date('2022-11-01T00:00:00.000Z'), value: 152059 }, {
date: new Date('2022-11-02T00:00:00.000Z'),
value: 63743,
}, { date: new Date('2022-11-03T00:00:00.000Z'), value: 83667 }, { date: new Date('2022-11-04T00:00:00.000Z'), value: 91725 }, {
date: new Date('2022-11-05T00:00:00.000Z'),
value: 82897,
}, { date: new Date('2022-11-06T00:00:00.000Z'), value: 62477 }, { date: new Date('2022-11-07T00:00:00.000Z'), value: 58131 }, {
date: new Date('2022-11-08T00:00:00.000Z'),
value: 74197,
}, { date: new Date('2022-11-09T00:00:00.000Z'), value: 43691 }, { date: new Date('2022-11-10T00:00:00.000Z'), value: 92887 }, {
date: new Date('2022-11-11T00:00:00.000Z'),
value: 79493,
}, { date: new Date('2022-11-12T00:00:00.000Z'), value: 86764 }, { date: new Date('2022-11-13T00:00:00.000Z'), value: 22338 }, {
date: new Date('2022-11-14T00:00:00.000Z'),
value: 62266,
}, { date: new Date('2022-11-15T00:00:00.000Z'), value: 84084 }, { date: new Date('2022-11-16T00:00:00.000Z'), value: 75898 } ];
......@@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { JsonRpcUrlResponse } from 'types/api/json-rpc-url';
import type { AppItemOverview } from 'types/client/apps';
import { QueryKeys } from 'types/client/queries';
import appConfig from 'configs/app/config';
import useFetch from 'lib/hooks/useFetch';
......@@ -27,7 +28,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => {
}, []);
const { data: jsonRpcUrlResponse } = useQuery<unknown, unknown, JsonRpcUrlResponse>(
[ 'json-rpc-url' ],
[ QueryKeys.jsonRpcUrl ],
async() => await fetch(`/node-api/config/json-rpc-url`),
{ refetchOnMount: false },
);
......
import React from 'react';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import WidgetsList from '../charts/WidgetsList';
const Stats = () => {
return (
<Page>
<PageTitle text="Ethereum Stats"/>
<WidgetsList/>
</Page>
);
};
export default Stats;
export const statisticsChartsScheme = [
{
id: 'blocks',
title: 'Blocks',
charts: [
{
id: 'new-blocks',
title: 'New blocks',
description: 'New blocks number per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
{
id: 'average-block-size',
title: 'Average block size',
description: 'Average size of blocks in bytes per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
],
},
{
id: 'transactions',
title: 'Transactions',
charts: [
{
id: 'transaction-fees',
title: 'Transaction fees',
description: 'Amount of tokens paid as fees per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
{
id: 'native-coin-holders-growth',
title: 'Native coin holders growth',
description: 'Total token holders number per day',
apiMethodURL: '/node-api/stats/charts/transactions',
},
],
},
];
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