199 lines
5.8 KiB
TypeScript
199 lines
5.8 KiB
TypeScript
import { Select } from 'antd';
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
import { get, takeRight, uniqBy } from 'lodash-es';
|
|
import React, { useState, useMemo } from 'react';
|
|
import { useSocketSubscribeList } from '../../api/socketio';
|
|
import { trpc } from '../../api/trpc';
|
|
import { useCurrentWorkspaceId } from '../../store/user';
|
|
import { getMonitorProvider, getProviderDisplay } from './provider';
|
|
import { useTranslation } from '@i18next-toolkit/react';
|
|
import {
|
|
ChartConfig,
|
|
ChartContainer,
|
|
ChartTooltip,
|
|
ChartTooltipContent,
|
|
} from '../ui/chart';
|
|
import {
|
|
Area,
|
|
AreaChart,
|
|
CartesianGrid,
|
|
Customized,
|
|
Rectangle,
|
|
XAxis,
|
|
YAxis,
|
|
} from 'recharts';
|
|
import { useTheme } from '@/hooks/useTheme';
|
|
import { CustomizedErrorArea } from './CustomizedErrorArea';
|
|
|
|
const chartConfig = {
|
|
value: {
|
|
label: <span className="text-sm font-bold">Result</span>,
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
export const MonitorDataChart: React.FC<{ monitorId: string }> = React.memo(
|
|
(props) => {
|
|
const { t } = useTranslation();
|
|
const workspaceId = useCurrentWorkspaceId();
|
|
const { monitorId } = props;
|
|
const [rangeType, setRangeType] = useState('recent');
|
|
const { colors } = useTheme();
|
|
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: monitorInfo } = trpc.monitor.get.useQuery({
|
|
workspaceId,
|
|
monitorId,
|
|
});
|
|
|
|
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 providerInfo = getMonitorProvider(monitorInfo?.type ?? '');
|
|
|
|
const { data } = useMemo(() => {
|
|
let fetchedData = rangeType === 'recent' ? _recentData : _data;
|
|
const data = takeRight(
|
|
uniqBy([...fetchedData, ...subscribedDataList], 'createdAt'),
|
|
fetchedData.length
|
|
).map((d, i, arr) => {
|
|
const value = d.value > 0 ? d.value : null;
|
|
const time = dayjs(d.createdAt).valueOf();
|
|
|
|
return {
|
|
value,
|
|
time,
|
|
};
|
|
});
|
|
|
|
return { data };
|
|
}, [_recentData, _data, subscribedDataList]);
|
|
|
|
const isTrendingMode = monitorInfo?.trendingMode ?? false; // if true, y axis not start from 0
|
|
|
|
return (
|
|
<div>
|
|
<div className="mb-4 text-right">
|
|
<Select
|
|
className="w-20 text-center"
|
|
size="small"
|
|
value={rangeType}
|
|
onChange={(val) => setRangeType(val)}
|
|
>
|
|
<Select.Option value="recent">{t('Recent')}</Select.Option>
|
|
<Select.Option value="3h">{t('3h')}</Select.Option>
|
|
<Select.Option value="6h">{t('6h')}</Select.Option>
|
|
<Select.Option value="24h">{t('24h')}</Select.Option>
|
|
<Select.Option value="1w">{t('1w')}</Select.Option>
|
|
</Select>
|
|
</div>
|
|
|
|
<ChartContainer className="h-[200px] w-full" config={chartConfig}>
|
|
<AreaChart
|
|
data={data}
|
|
margin={{ top: 10, right: 0, left: 0, bottom: 0 }}
|
|
>
|
|
<defs>
|
|
<linearGradient id="color" x1="0" y1="0" x2="0" y2="1">
|
|
<stop
|
|
offset="5%"
|
|
stopColor={colors.chart.monitor}
|
|
stopOpacity={0.3}
|
|
/>
|
|
<stop
|
|
offset="95%"
|
|
stopColor={colors.chart.monitor}
|
|
stopOpacity={0}
|
|
/>
|
|
</linearGradient>
|
|
</defs>
|
|
<XAxis
|
|
dataKey="time"
|
|
type="number"
|
|
domain={['dataMin', 'dataMax']}
|
|
tickFormatter={(date) =>
|
|
dayjs(date).format(rangeType === '1w' ? 'MM-DD HH:mm' : 'HH:mm')
|
|
}
|
|
/>
|
|
<YAxis
|
|
mirror
|
|
domain={[isTrendingMode ? 'dataMin' : 0, 'dataMax']}
|
|
/>
|
|
<CartesianGrid vertical={false} />
|
|
<ChartTooltip
|
|
labelFormatter={(label, payload) =>
|
|
dayjs(get(payload, [0, 'payload', 'time'])).format(
|
|
'YYYY-MM-DD HH:mm:ss'
|
|
)
|
|
}
|
|
formatter={(value, defaultText, item, index, payload) => {
|
|
if (typeof value !== 'number') {
|
|
return defaultText;
|
|
}
|
|
const { name, text } = getProviderDisplay(
|
|
Number(value),
|
|
providerInfo
|
|
);
|
|
|
|
return (
|
|
<div>
|
|
<span className="mr-2">{name}:</span>
|
|
<span>{text}</span>
|
|
</div>
|
|
);
|
|
}}
|
|
content={<ChartTooltipContent />}
|
|
/>
|
|
|
|
<Customized component={CustomizedErrorArea} />
|
|
|
|
<Area
|
|
type="monotone"
|
|
dataKey="value"
|
|
stroke={colors.chart.monitor}
|
|
fillOpacity={1}
|
|
fill="url(#color)"
|
|
strokeWidth={2}
|
|
isAnimationActive={false}
|
|
/>
|
|
</AreaChart>
|
|
</ChartContainer>
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
MonitorDataChart.displayName = 'MonitorDataChart';
|