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';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
const { from, to } = req.query;
return `/v1/blocks/new?${ from ? `&from=${ from }&to=${ to }` : '' }`;
const { name, from, to } = req.query;
return `/v1/charts/line?name=${ name }${ from ? `&from=${ from }&to=${ to }` : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ], appConfig.statsApi.endpoint);
......
......@@ -23,10 +23,4 @@ export type StatsChart = {
id: string;
title: 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 React, { useCallback, useState } from 'react';
......@@ -6,7 +6,8 @@ import type { Charts } from 'types/api/stats';
import { QueryKeys } from 'types/client/queries';
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 ChartWidgetGraph from './ChartWidgetGraph';
......@@ -16,7 +17,6 @@ import FullscreenChartModal from './FullscreenChartModal';
type Props = {
id: string;
apiMethodURL: string;
title: string;
description: string;
interval: StatsIntervalIds;
......@@ -26,7 +26,7 @@ function formatDate(date: Date) {
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 selectedInterval = STATS_INTERVALS[interval];
......@@ -40,7 +40,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
const menuButtonColor = useColorModeValue('black', 'white');
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>(
[ QueryKeys.charts, id, startDate ],
......@@ -58,7 +58,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
const showChartFullscreen = useCallback(() => {
setIsFullscreen(true);
if (!document.fullscreenElement) {
if (!document.fullscreenElement && document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen();
}
}, []);
......@@ -91,7 +91,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
>
<Grid
gridTemplateColumns="auto auto 36px"
gridColumnGap={ 4 }
gridColumnGap={ 2 }
>
<Heading
mb={ 1 }
......@@ -110,19 +110,22 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
{ description }
</Text>
{ !isZoomResetInitial && (
<Button
<IconButton
hidden={ isZoomResetInitial }
aria-label="Reset zoom"
title="Reset zoom"
colorScheme="blue"
w={ 9 }
h={ 8 }
gridColumn={ 2 }
justifySelf="end"
alignSelf="top"
gridRow="1/3"
size="sm"
variant="outline"
variant="ghost"
onClick={ handleZoomResetClick }
>
Reset zoom
</Button>
) }
icon={ <Icon as={ repeatArrow } w={ 4 } h={ 4 } color="blue.700"/> }
/>
<Menu>
<MenuButton
......@@ -152,6 +155,7 @@ const ChartWidget = ({ id, title, description, apiMethodURL, interval }: Props)
title={ title }
/>
</Box>
<FullscreenChartModal
isOpen={ isFullscreen }
items={ items }
......
......@@ -3,6 +3,7 @@ import React, { useEffect, useMemo } from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types';
import useIsMobile from 'lib/hooks/useIsMobile';
import ChartArea from 'ui/shared/chart/ChartArea';
import ChartAxis from 'ui/shared/chart/ChartAxis';
import ChartGridLine from 'ui/shared/chart/ChartGridLine';
......@@ -23,12 +24,13 @@ interface Props {
const CHART_MARGIN = { bottom: 20, left: 52, right: 30, top: 10 };
const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) => {
const isMobile = useIsMobile();
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 overlayRef = React.useRef<SVGRectElement>(null);
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) =>
({ ...d, date: new Date(d.date) })), [ items, range ]);
......@@ -52,9 +54,9 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
}, [ isZoomResetInitial ]);
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
type="horizontal"
scale={ yScale }
......@@ -93,7 +95,7 @@ const ChartWidgetGraph = ({ items, onZoom, isZoomResetInitial, title }: Props) =
type="bottom"
scale={ xScale }
transform={ `translate(0, ${ innerHeight })` }
ticks={ 5 }
ticks={ isMobile ? 1 : 3 }
anchorEl={ overlayRef.current }
disableAnimation
/>
......
......@@ -5,7 +5,7 @@ const ChartWidgetSkeleton = () => {
return (
<Box
height="235px"
padding={{ base: 3, md: 4 }}
paddingY={{ base: 3, md: 4 }}
>
<Skeleton w="75%" h="24px" mb={ 1 }/>
<Skeleton w="50%" h="18px" mb={ 5 }/>
......
......@@ -39,7 +39,7 @@ const ChartsWidgetsList = ({ charts, interval }: Props) => {
<Grid
templateColumns={{
sm: 'repeat(2, 1fr)',
lg: 'repeat(2, 1fr)',
}}
gap={ 4 }
>
......@@ -50,7 +50,6 @@ const ChartsWidgetsList = ({ charts, interval }: Props) => {
>
<ChartWidget
id={ chart.id }
apiMethodURL={ chart.apiMethodURL }
title={ chart.title }
description={ chart.description }
interval={ interval }
......
......@@ -74,7 +74,7 @@ const FullscreenChartModal = ({
<ModalCloseButton/>
<ModalBody
h="100%"
h="75%"
>
<ChartWidgetGraph
items={ items }
......
......@@ -17,7 +17,7 @@ const NumberWidgetsList = () => {
const { data, isLoading } = useQuery<unknown, unknown, Stats>(
[ QueryKeys.stats ],
async() => await fetch(`/node-api/stats`),
async() => await fetch(`/node-api/stats/counters`),
);
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 eastMiniArrowIcon from 'icons/arrows/east-mini.svg';
......@@ -31,9 +31,15 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec
as="span"
display="flex"
alignItems="center"
>
<Text
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
>
{ selectedCategory?.title }
<Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
</Text>
<Icon transform="rotate(-90deg)" ml="auto" as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
</Box>
</MenuButton>
......
......@@ -48,7 +48,7 @@ const StatsFilters = ({
"section interval"`,
lg: `"input section interval"`,
}}
gridTemplateColumns={{ lg: '1fr auto auto' }}
gridTemplateColumns={{ base: 'repeat(2, minmax(0, 1fr))', lg: '1fr auto auto' }}
>
<GridItem
w="100%"
......
......@@ -9,7 +9,6 @@ export const statsChartsScheme: Array<StatsSection> = [
id: 'new-blocks',
title: 'New blocks',
description: 'New blocks number per day',
apiMethodURL: '/node-api/stats/charts/blocks/new',
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