diff --git a/src/client/components/monitor/MonitorDataChart.tsx b/src/client/components/monitor/MonitorDataChart.tsx new file mode 100644 index 0000000..ff73ead --- /dev/null +++ b/src/client/components/monitor/MonitorDataChart.tsx @@ -0,0 +1,148 @@ +import { AreaConfig, Area } from '@ant-design/charts'; +import { Select } from 'antd'; +import dayjs, { Dayjs } from 'dayjs'; +import { uniqBy } from 'lodash'; +import React, { useState, useMemo } from 'react'; +import { useSocketSubscribeList } from '../../api/socketio'; +import { trpc } from '../../api/trpc'; +import { useCurrentWorkspaceId } from '../../store/user'; + +export const MonitorDataChart: React.FC<{ monitorId: string }> = React.memo( + (props) => { + const workspaceId = useCurrentWorkspaceId(); + const { monitorId } = props; + const [rangeType, setRangeType] = useState('recent'); + const subscribedDataList = useSocketSubscribeList( + 'onMonitorReceiveNewData', + { + filter: (data) => { + return data.monitorId === props.monitorId; + }, + } + ); + + const range = useMemo((): [Dayjs, Dayjs] => { + if (rangeType === '3h') { + return [dayjs().subtract(3, 'hour'), dayjs()]; + } + if (rangeType === '6h') { + return [dayjs().subtract(6, 'hour'), dayjs()]; + } + if (rangeType === '24h') { + return [dayjs().subtract(24, 'hour'), dayjs()]; + } + if (rangeType === '1w') { + return [dayjs().subtract(1, 'week'), dayjs()]; + } + + return [dayjs().subtract(0.5, 'hour'), dayjs()]; + }, [rangeType]); + + const { data: _recentData = [] } = trpc.monitor.recentData.useQuery({ + workspaceId, + monitorId, + take: 40, + }); + + const { data: _data = [] } = trpc.monitor.data.useQuery({ + workspaceId, + monitorId, + startAt: range[0].valueOf(), + endAt: range[1].valueOf(), + }); + + const { data, annotations } = useMemo(() => { + const annotations: AreaConfig['annotations'] = []; + let start: number | null = null; + let fetchedData = rangeType === 'recent' ? _recentData : _data; + const data = uniqBy( + [...fetchedData, ...subscribedDataList], + 'createdAt' + ).map((d, i, arr) => { + const value = d.value > 0 ? d.value : null; + const time = dayjs(d.createdAt).valueOf(); + + if (!value && !start && arr[i - 1]) { + start = dayjs(arr[i - 1]['createdAt']).valueOf(); + } else if (value && start) { + annotations.push({ + type: 'region', + start: [start, 'min'], + end: [time, 'max'], + style: { + fill: 'red', + fillOpacity: 0.25, + }, + }); + start = null; + } + + return { + value, + time, + }; + }); + + return { data, annotations }; + }, [_recentData, _data, subscribedDataList]); + + const config = useMemo(() => { + return { + data, + height: 200, + xField: 'time', + yField: 'value', + smooth: true, + meta: { + time: { + formatter(value) { + return dayjs(value).format( + rangeType === '1w' ? 'MM-DD HH:mm' : 'HH:mm' + ); + }, + }, + }, + color: 'rgb(34 197 94 / 0.8)', + areaStyle: () => { + return { + fill: 'l(270) 0:rgb(34 197 94 / 0.2) 0.5:rgb(34 197 94 / 0.5) 1:rgb(34 197 94 / 0.8)', + }; + }, + annotations, + tooltip: { + title: (title, datum) => { + return dayjs(datum.time).format('YYYY-MM-DD HH:mm'); + }, + formatter(datum) { + return { + name: 'usage', + value: datum.value ? datum.value + 'ms' : 'null', + }; + }, + }, + }; + }, [data, rangeType]); + + return ( +
+
+ +
+ + +
+ ); + } +); +MonitorDataChart.displayName = 'MonitorDataChart'; diff --git a/src/client/components/monitor/MonitorDataMetrics.tsx b/src/client/components/monitor/MonitorDataMetrics.tsx new file mode 100644 index 0000000..1aef262 --- /dev/null +++ b/src/client/components/monitor/MonitorDataMetrics.tsx @@ -0,0 +1,85 @@ +import React, { useMemo } from 'react'; +import { trpc } from '../../api/trpc'; +import { useCurrentWorkspaceId } from '../../store/user'; +import { ErrorTip } from '../ErrorTip'; +import { Loading } from '../Loading'; +import { getMonitorProvider } from '../modals/monitor/provider'; +import { MonitorStatsBlock } from './MonitorStatsBlock'; + +export const MonitorDataMetrics: React.FC<{ + monitorId: string; + monitorType: string; + currectResponse?: number; +}> = React.memo((props) => { + const workspaceId = useCurrentWorkspaceId(); + const { monitorId, monitorType, currectResponse } = props; + const { data, isLoading } = trpc.monitor.dataMetrics.useQuery({ + workspaceId, + monitorId, + }); + const providerOverview = useMemo(() => { + const provider = getMonitorProvider(monitorType); + if (!provider || !provider.overview) { + return null; + } + + return ( + <> + {provider.overview.map((Component) => ( + + ))} + + ); + }, [monitorId, monitorType]); + + if (isLoading) { + return ; + } + + if (!data) { + return ; + } + + return ( +
+ {typeof currectResponse === 'number' && ( + + )} + + + + + + {providerOverview} +
+ ); +}); +MonitorDataMetrics.displayName = 'MonitorDataMetrics'; diff --git a/src/client/components/monitor/MonitorInfo.tsx b/src/client/components/monitor/MonitorInfo.tsx index bff9f1c..f4779c9 100644 --- a/src/client/components/monitor/MonitorInfo.tsx +++ b/src/client/components/monitor/MonitorInfo.tsx @@ -1,6 +1,6 @@ -import { Button, Card, Select, Space, Spin } from 'antd'; -import dayjs, { Dayjs } from 'dayjs'; -import React, { useMemo, useState } from 'react'; +import { Button, Card, Space, Spin } from 'antd'; +import dayjs from 'dayjs'; +import React, { useState } from 'react'; import { defaultErrorHandler, defaultSuccessHandler, @@ -8,19 +8,17 @@ import { } from '../../api/trpc'; import { useCurrentWorkspaceId } from '../../store/user'; import { Loading } from '../Loading'; -import { getMonitorLink, getMonitorProvider } from '../modals/monitor/provider'; +import { getMonitorLink } from '../modals/monitor/provider'; import { NotFoundTip } from '../NotFoundTip'; import { MonitorInfo as MonitorInfoType } from '../../../types'; -import { Area, AreaConfig } from '@ant-design/charts'; import { MonitorHealthBar } from './MonitorHealthBar'; -import { useSocketSubscribeList } from '../../api/socketio'; -import { last, uniqBy } from 'lodash-es'; -import { ErrorTip } from '../ErrorTip'; +import { last } from 'lodash-es'; import { ColorTag } from '../ColorTag'; import { useNavigate } from 'react-router'; -import { MonitorStatsBlock } from './MonitorStatsBlock'; import { MonitorEventList } from './MonitorEventList'; import { useEvent } from '../../hooks/useEvent'; +import { MonitorDataMetrics } from './MonitorDataMetrics'; +import { MonitorDataChart } from './MonitorDataChart'; interface MonitorInfoProps { monitorId: string; @@ -175,218 +173,3 @@ export const MonitorInfo: React.FC = React.memo((props) => { ); }); MonitorInfo.displayName = 'MonitorInfo'; - -export const MonitorDataMetrics: React.FC<{ - monitorId: string; - monitorType: string; - currectResponse: number; -}> = React.memo((props) => { - const workspaceId = useCurrentWorkspaceId(); - const { monitorId, monitorType, currectResponse } = props; - const { data, isLoading } = trpc.monitor.dataMetrics.useQuery({ - workspaceId, - monitorId, - }); - const providerOverview = useMemo(() => { - const provider = getMonitorProvider(monitorType); - if (!provider || !provider.overview) { - return null; - } - - return ( - <> - {provider.overview.map((Component) => ( - - ))} - - ); - }, [monitorId, monitorType]); - - if (isLoading) { - return ; - } - - if (!data) { - return ; - } - - return ( -
- - - - - - {providerOverview} -
- ); -}); -MonitorDataMetrics.displayName = 'MonitorDataMetrics'; - -const MonitorDataChart: React.FC<{ monitorId: string }> = React.memo( - (props) => { - const workspaceId = useCurrentWorkspaceId(); - const { monitorId } = props; - const [rangeType, setRangeType] = useState('recent'); - const subscribedDataList = useSocketSubscribeList( - 'onMonitorReceiveNewData', - { - filter: (data) => { - return data.monitorId === props.monitorId; - }, - } - ); - - const range = useMemo((): [Dayjs, Dayjs] => { - if (rangeType === '3h') { - return [dayjs().subtract(3, 'hour'), dayjs()]; - } - if (rangeType === '6h') { - return [dayjs().subtract(6, 'hour'), dayjs()]; - } - if (rangeType === '24h') { - return [dayjs().subtract(24, 'hour'), dayjs()]; - } - if (rangeType === '1w') { - return [dayjs().subtract(1, 'week'), dayjs()]; - } - - return [dayjs().subtract(0.5, 'hour'), dayjs()]; - }, [rangeType]); - - const { data: _recentData = [] } = trpc.monitor.recentData.useQuery({ - workspaceId, - monitorId, - take: 40, - }); - - const { data: _data = [] } = trpc.monitor.data.useQuery({ - workspaceId, - monitorId, - startAt: range[0].valueOf(), - endAt: range[1].valueOf(), - }); - - const { data, annotations } = useMemo(() => { - const annotations: AreaConfig['annotations'] = []; - let start: number | null = null; - let fetchedData = rangeType === 'recent' ? _recentData : _data; - const data = uniqBy( - [...fetchedData, ...subscribedDataList], - 'createdAt' - ).map((d, i, arr) => { - const value = d.value > 0 ? d.value : null; - const time = dayjs(d.createdAt).valueOf(); - - if (!value && !start && arr[i - 1]) { - start = dayjs(arr[i - 1]['createdAt']).valueOf(); - } else if (value && start) { - annotations.push({ - type: 'region', - start: [start, 'min'], - end: [time, 'max'], - style: { - fill: 'red', - fillOpacity: 0.25, - }, - }); - start = null; - } - - return { - value, - time, - }; - }); - - return { data, annotations }; - }, [_recentData, _data, subscribedDataList]); - - const config = useMemo(() => { - return { - data, - height: 200, - xField: 'time', - yField: 'value', - smooth: true, - meta: { - time: { - formatter(value) { - return dayjs(value).format( - rangeType === '1w' ? 'MM-DD HH:mm' : 'HH:mm' - ); - }, - }, - }, - color: 'rgb(34 197 94 / 0.8)', - areaStyle: () => { - return { - fill: 'l(270) 0:rgb(34 197 94 / 0.2) 0.5:rgb(34 197 94 / 0.5) 1:rgb(34 197 94 / 0.8)', - }; - }, - annotations, - tooltip: { - title: (title, datum) => { - return dayjs(datum.time).format('YYYY-MM-DD HH:mm'); - }, - formatter(datum) { - return { - name: 'usage', - value: datum.value ? datum.value + 'ms' : 'null', - }; - }, - }, - }; - }, [data, rangeType]); - - return ( -
-
- -
- - -
- ); - } -); -MonitorDataChart.displayName = 'MonitorDataChart';