mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Improve stats charts.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 367 B |
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
@@ -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;
|
||||
}
|
||||
|
||||
+24
-20
@@ -1,4 +1,4 @@
|
||||
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
|
||||
gridColumn={ 2 }
|
||||
justifySelf="end"
|
||||
alignSelf="top"
|
||||
gridRow="1/3"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={ handleZoomResetClick }
|
||||
>
|
||||
Reset zoom
|
||||
</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="ghost"
|
||||
onClick={ handleZoomResetClick }
|
||||
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 (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
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';
|
||||
@@ -32,8 +32,14 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
>
|
||||
{ selectedCategory?.title }
|
||||
<Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
|
||||
<Text
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
>
|
||||
{ selectedCategory?.title }
|
||||
</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,
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user