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

Improve stats charts.

parent 441f62af
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.485 7.012h1.446a.918.918 0 1 1 0 1.84H4.253a.919.919 0 0 1-.92-.92V4.254a.919.919 0 1 1 1.84 0v1.47l.505-.505a6.436 6.436 0 0 1 9.103 0 6.436 6.436 0 0 1 0 9.103 6.436 6.436 0 0 1-9.103 0 .92.92 0 0 1 1.302-1.301 4.597 4.597 0 1 0 0-6.503l-.495.494Z" fill="currentColor"/>
</svg>
...@@ -4,8 +4,8 @@ import appConfig from 'configs/app/config'; ...@@ -4,8 +4,8 @@ import appConfig from 'configs/app/config';
import handler from 'lib/api/handler'; import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => { const getUrl = (req: NextApiRequest) => {
const { from, to } = req.query; const { name, from, to } = req.query;
return `/v1/blocks/new?${ from ? `&from=${ from }&to=${ to }` : '' }`; return `/v1/charts/line?name=${ name }${ from ? `&from=${ from }&to=${ to }` : '' }`;
}; };
const requestHandler = handler(getUrl, [ 'GET' ], appConfig.statsApi.endpoint); const requestHandler = handler(getUrl, [ 'GET' ], appConfig.statsApi.endpoint);
......
...@@ -23,10 +23,4 @@ export type StatsChart = { ...@@ -23,10 +23,4 @@ export type StatsChart = {
id: string; id: string;
title: string; title: string;
description: string; description: string;
apiMethodURL: string;
}
export interface ModalChart {
id: string;
title: string;
} }
import { Box, Button, Grid, Heading, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Text, useColorModeValue, VisuallyHidden } from '@chakra-ui/react'; import { Box, Grid, Heading, Icon, IconButton, Menu, MenuButton, MenuItem, MenuList, Text, useColorModeValue, VisuallyHidden } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
...@@ -6,7 +6,8 @@ import type { Charts } from 'types/api/stats'; ...@@ -6,7 +6,8 @@ import type { Charts } from 'types/api/stats';
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
import type { StatsIntervalIds } from 'types/client/stats'; import type { StatsIntervalIds } from 'types/client/stats';
import dotsIcon from 'icons/vertical-dots.svg'; import repeatArrow from 'icons/repeat_arrow.svg';
import dotsIcon from 'icons/vertical_dots.svg';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import ChartWidgetGraph from './ChartWidgetGraph'; import ChartWidgetGraph from './ChartWidgetGraph';
...@@ -16,7 +17,6 @@ import FullscreenChartModal from './FullscreenChartModal'; ...@@ -16,7 +17,6 @@ import FullscreenChartModal from './FullscreenChartModal';
type Props = { type Props = {
id: string; id: string;
apiMethodURL: string;
title: string; title: string;
description: string; description: string;
interval: StatsIntervalIds; interval: StatsIntervalIds;
...@@ -26,7 +26,7 @@ function formatDate(date: Date) { ...@@ -26,7 +26,7 @@ function formatDate(date: Date) {
return date.toISOString().substring(0, 10); return date.toISOString().substring(0, 10);
} }
const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props) => { const ChartWidget = ({ id, title, description, interval }: Props) => {
const fetch = useFetch(); const fetch = useFetch();
const selectedInterval = STATS_INTERVALS[interval]; const selectedInterval = STATS_INTERVALS[interval];
...@@ -40,7 +40,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props) ...@@ -40,7 +40,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
const menuButtonColor = useColorModeValue('black', 'white'); const menuButtonColor = useColorModeValue('black', 'white');
const borderColor = useColorModeValue('gray.200', 'gray.600'); const borderColor = useColorModeValue('gray.200', 'gray.600');
const url = `${ apiMethodURL }${ startDate ? `?from=${ startDate }&to=${ endDate }` : '' }`; const url = `/node-api/stats/charts?name=${ id }${ startDate ? `&from=${ startDate }&to=${ endDate }` : '' }`;
const { data, isLoading } = useQuery<unknown, unknown, Charts>( const { data, isLoading } = useQuery<unknown, unknown, Charts>(
[ QueryKeys.charts, id, startDate ], [ QueryKeys.charts, id, startDate ],
...@@ -58,7 +58,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props) ...@@ -58,7 +58,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
const showChartFullscreen = useCallback(() => { const showChartFullscreen = useCallback(() => {
setIsFullscreen(true); setIsFullscreen(true);
if (!document.fullscreenElement) { if (!document.fullscreenElement && document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen(); document.documentElement.requestFullscreen();
} }
}, []); }, []);
...@@ -91,7 +91,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props) ...@@ -91,7 +91,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
> >
<Grid <Grid
gridTemplateColumns="auto auto 36px" gridTemplateColumns="auto auto 36px"
gridColumnGap={ 4 } gridColumnGap={ 2 }
> >
<Heading <Heading
mb={ 1 } mb={ 1 }
...@@ -110,19 +110,22 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props) ...@@ -110,19 +110,22 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
{ description } { description }
</Text> </Text>
{ !isZoomResetInitial && ( <IconButton
<Button hidden={ isZoomResetInitial }
gridColumn={ 2 } aria-label="Reset zoom"
justifySelf="end" title="Reset zoom"
alignSelf="top" colorScheme="blue"
gridRow="1/3" w={ 9 }
size="sm" h={ 8 }
variant="outline" gridColumn={ 2 }
onClick={ handleZoomResetClick } justifySelf="end"
> alignSelf="top"
Reset zoom gridRow="1/3"
</Button> size="sm"
) } variant="ghost"
onClick={ handleZoomResetClick }
icon={ <Icon as={ repeatArrow } w={ 4 } h={ 4 } color="blue.700"/> }
/>
<Menu> <Menu>
<MenuButton <MenuButton
...@@ -152,6 +155,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props) ...@@ -152,6 +155,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
title={ title } title={ title }
/> />
</Box> </Box>
<FullscreenChartModal <FullscreenChartModal
isOpen={ isFullscreen } isOpen={ isFullscreen }
items={ items } items={ items }
......
...@@ -3,6 +3,7 @@ import React, { useEffect, useMemo } from 'react'; ...@@ -3,6 +3,7 @@ import React, { useEffect, useMemo } from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types'; import type { TimeChartItem } from 'ui/shared/chart/types';
import useIsMobile from 'lib/hooks/useIsMobile';
import ChartArea from 'ui/shared/chart/ChartArea'; import ChartArea from 'ui/shared/chart/ChartArea';
import ChartAxis from 'ui/shared/chart/ChartAxis'; import ChartAxis from 'ui/shared/chart/ChartAxis';
import ChartGridLine from 'ui/shared/chart/ChartGridLine'; import ChartGridLine from 'ui/shared/chart/ChartGridLine';
...@@ -23,12 +24,13 @@ interface Props { ...@@ -23,12 +24,13 @@ interface Props {
const CHART_MARGIN = { bottom: 20, left: 52, right: 30, top: 10 }; const CHART_MARGIN = { bottom: 20, left: 52, right: 30, top: 10 };
const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) => { const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) => {
const isMobile = useIsMobile();
const ref = React.useRef<SVGSVGElement>(null); 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.200'); const color = useToken('colors', 'blue.200');
const overlayRef = React.useRef<SVGRectElement>(null);
const chartId = useMemo(() => `chart-${ crypto.randomUUID() }`, []); const chartId = useMemo(() => `chart-${ crypto.randomUUID() }`, []);
const [ range, setRange ] = React.useState<[ number, number ]>([ 0, Infinity ]);
const { innerWidth, innerHeight } = useChartSize(ref.current, CHART_MARGIN);
const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) => const displayedData = useMemo(() => items.slice(range[0], range[1]).map((d) =>
({ ...d, date: new Date(d.date) })), [ items, range ]); ({ ...d, date: new Date(d.date) })), [ items, range ]);
...@@ -52,9 +54,9 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -52,9 +54,9 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
}, [ isZoomResetInitial ]); }, [ isZoomResetInitial ]);
return ( return (
<svg width={ width || '100%' } height={ height || '100%' } ref={ ref } cursor="pointer" id={ chartId }> <svg width="100%" height="100%" ref={ ref } cursor="pointer" id={ chartId }>
<g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ width ? 1 : 0 }> <g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ 1 }>
<ChartGridLine <ChartGridLine
type="horizontal" type="horizontal"
scale={ yScale } scale={ yScale }
...@@ -93,7 +95,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) = ...@@ -93,7 +95,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
type="bottom" type="bottom"
scale={ xScale } scale={ xScale }
transform={ `translate(0, ${ innerHeight })` } transform={ `translate(0, ${ innerHeight })` }
ticks={ 5 } ticks={ isMobile ? 1 : 3 }
anchorEl={ overlayRef.current } anchorEl={ overlayRef.current }
disableAnimation disableAnimation
/> />
......
...@@ -5,7 +5,7 @@ const ChartWidgetSkeleton = () => { ...@@ -5,7 +5,7 @@ const ChartWidgetSkeleton = () => {
return ( return (
<Box <Box
height="235px" height="235px"
padding={{ base: 3, md: 4 }} paddingY={{ base: 3, md: 4 }}
> >
<Skeleton w="75%" h="24px" mb={ 1 }/> <Skeleton w="75%" h="24px" mb={ 1 }/>
<Skeleton w="50%" h="18px" mb={ 5 }/> <Skeleton w="50%" h="18px" mb={ 5 }/>
......
...@@ -39,7 +39,7 @@ const ChartsWidgetsList = ({ charts, interval }: Props) => { ...@@ -39,7 +39,7 @@ const ChartsWidgetsList = ({ charts, interval }: Props) => {
<Grid <Grid
templateColumns={{ templateColumns={{
sm: 'repeat(2, 1fr)', lg: 'repeat(2, 1fr)',
}} }}
gap={ 4 } gap={ 4 }
> >
...@@ -50,7 +50,6 @@ const ChartsWidgetsList = ({ charts, interval }: Props) => { ...@@ -50,7 +50,6 @@ const ChartsWidgetsList = ({ charts, interval }: Props) => {
> >
<ChartWidget <ChartWidget
id={ chart.id } id={ chart.id }
apiMethodURL={ chart.apiMethodURL }
title={ chart.title } title={ chart.title }
description={ chart.description } description={ chart.description }
interval={ interval } interval={ interval }
......
...@@ -74,7 +74,7 @@ const FullscreenChartModal = ({ ...@@ -74,7 +74,7 @@ const FullscreenChartModal = ({
<ModalCloseButton/> <ModalCloseButton/>
<ModalBody <ModalBody
h="100%" h="75%"
> >
<ChartWidgetGraph <ChartWidgetGraph
items={ items } items={ items }
......
...@@ -17,7 +17,7 @@ const NumberWidgetsList = () => { ...@@ -17,7 +17,7 @@ const NumberWidgetsList = () => {
const { data, isLoading } = useQuery<unknown, unknown, Stats>( const { data, isLoading } = useQuery<unknown, unknown, Stats>(
[ QueryKeys.stats ], [ QueryKeys.stats ],
async() => await fetch(`/node-api/stats`), async() => await fetch(`/node-api/stats/counters`),
); );
return ( return (
......
import { Box, Button, Icon, Menu, MenuButton, MenuItemOption, MenuList, MenuOptionGroup } from '@chakra-ui/react'; import { Box, Button, Icon, Menu, MenuButton, MenuItemOption, MenuList, MenuOptionGroup, Text } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import eastMiniArrowIcon from 'icons/arrows/east-mini.svg'; import eastMiniArrowIcon from 'icons/arrows/east-mini.svg';
...@@ -32,8 +32,14 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec ...@@ -32,8 +32,14 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec
display="flex" display="flex"
alignItems="center" alignItems="center"
> >
{ selectedCategory?.title } <Text
<Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/> whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
>
{ selectedCategory?.title }
</Text>
<Icon transform="rotate(-90deg)" ml="auto" as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
</Box> </Box>
</MenuButton> </MenuButton>
......
...@@ -48,7 +48,7 @@ const StatsFilters = ({ ...@@ -48,7 +48,7 @@ const StatsFilters = ({
"section interval"`, "section interval"`,
lg: `"input section interval"`, lg: `"input section interval"`,
}} }}
gridTemplateColumns={{ lg: '1fr auto auto' }} gridTemplateColumns={{ base: 'repeat(2, minmax(0, 1fr))', lg: '1fr auto auto' }}
> >
<GridItem <GridItem
w="100%" w="100%"
......
...@@ -9,7 +9,6 @@ export const statsChartsScheme: Array<StatsSection> = [ ...@@ -9,7 +9,6 @@ export const statsChartsScheme: Array<StatsSection> = [
id: 'new-blocks', id: 'new-blocks',
title: 'New blocks', title: 'New blocks',
description: 'New blocks number per day', description: 'New blocks number per day',
apiMethodURL: '/node-api/stats/charts/blocks/new',
visible: true, visible: true,
}, },
], ],
......
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