Skip to content

Commit dcff57f

Browse files
committed
feat: add daily monitor data display for public
1 parent 316b954 commit dcff57f

File tree

7 files changed

+299
-90
lines changed

7 files changed

+299
-90
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { useTheme } from '@/hooks/useTheme';
2+
import { get } from 'lodash-es';
3+
import React from 'react';
4+
import { useMemo } from 'react';
5+
import { Rectangle } from 'recharts';
6+
7+
export const CustomizedErrorArea: React.FC = (props) => {
8+
const { colors } = useTheme();
9+
const y = get(props, 'offset.top', 10);
10+
const height = get(props, 'offset.height', 160);
11+
const points = get(props, 'formattedGraphicalItems.0.props.points', []) as {
12+
x: number;
13+
y: number | null;
14+
}[];
15+
16+
const errorArea = useMemo(() => {
17+
const _errorArea: { x: number; width: number }[] = [];
18+
let prevX: number | null = null;
19+
points.forEach((item, i, arr) => {
20+
if (i === 0 && !item.y) {
21+
prevX = 0;
22+
} else if (!item.y && prevX === null && arr[i - 1].y) {
23+
prevX = arr[i - 1].x;
24+
} else if (item.y && prevX !== null) {
25+
_errorArea.push({
26+
x: prevX,
27+
width: item.x - prevX,
28+
});
29+
prevX = null;
30+
}
31+
});
32+
33+
return _errorArea;
34+
}, [points]);
35+
36+
return errorArea.map((area, i) => {
37+
return (
38+
<Rectangle
39+
key={i}
40+
width={area.width}
41+
height={height}
42+
x={area.x}
43+
y={y}
44+
fill={colors.chart.error}
45+
/>
46+
);
47+
});
48+
};
49+
CustomizedErrorArea.displayName = 'CustomizedErrorArea';

src/client/components/monitor/MonitorDataChart.tsx

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -195,47 +195,3 @@ export const MonitorDataChart: React.FC<{ monitorId: string }> = React.memo(
195195
}
196196
);
197197
MonitorDataChart.displayName = 'MonitorDataChart';
198-
199-
const CustomizedErrorArea: React.FC = (props) => {
200-
const { colors } = useTheme();
201-
const y = get(props, 'offset.top', 10);
202-
const height = get(props, 'offset.height', 160);
203-
const points = get(props, 'formattedGraphicalItems.0.props.points', []) as {
204-
x: number;
205-
y: number | null;
206-
}[];
207-
208-
const errorArea = useMemo(() => {
209-
const _errorArea: { x: number; width: number }[] = [];
210-
let prevX: number | null = null;
211-
points.forEach((item, i, arr) => {
212-
if (i === 0 && !item.y) {
213-
prevX = 0;
214-
} else if (!item.y && prevX === null && arr[i - 1].y) {
215-
prevX = arr[i - 1].x;
216-
} else if (item.y && prevX !== null) {
217-
_errorArea.push({
218-
x: prevX,
219-
width: item.x - prevX,
220-
});
221-
prevX = null;
222-
}
223-
});
224-
225-
return _errorArea;
226-
}, [points]);
227-
228-
return errorArea.map((area, i) => {
229-
return (
230-
<Rectangle
231-
key={i}
232-
width={area.width}
233-
height={height}
234-
x={area.x}
235-
y={y}
236-
fill={colors.chart.error}
237-
/>
238-
);
239-
});
240-
};
241-
CustomizedErrorArea.displayName = 'CustomizedErrorArea';
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import dayjs from 'dayjs';
2+
import { get } from 'lodash-es';
3+
import React, { useMemo } from 'react';
4+
import { trpc } from '../../api/trpc';
5+
import { getMonitorProvider, getProviderDisplay } from './provider';
6+
import { useTranslation } from '@i18next-toolkit/react';
7+
import {
8+
ChartConfig,
9+
ChartContainer,
10+
ChartTooltip,
11+
ChartTooltipContent,
12+
} from '../ui/chart';
13+
import {
14+
Area,
15+
AreaChart,
16+
CartesianGrid,
17+
Customized,
18+
XAxis,
19+
YAxis,
20+
} from 'recharts';
21+
import { useTheme } from '@/hooks/useTheme';
22+
import { CustomizedErrorArea } from './CustomizedErrorArea';
23+
24+
const chartConfig = {
25+
value: {
26+
label: <span className="text-sm font-bold">Result</span>,
27+
},
28+
} satisfies ChartConfig;
29+
30+
interface MonitorPublicDataChartProps {
31+
workspaceId: string;
32+
monitorId: string;
33+
className?: string;
34+
}
35+
36+
export const MonitorPublicDataChart: React.FC<MonitorPublicDataChartProps> =
37+
React.memo((props) => {
38+
const { t } = useTranslation();
39+
const { workspaceId, monitorId } = props;
40+
const { colors } = useTheme();
41+
42+
const { data: monitorInfo } = trpc.monitor.getPublicInfo.useQuery(
43+
{
44+
monitorIds: [monitorId],
45+
},
46+
{
47+
select(data) {
48+
return data[0];
49+
},
50+
}
51+
);
52+
53+
const { data: _data = [] } = trpc.monitor.publicData.useQuery({
54+
workspaceId,
55+
monitorId,
56+
});
57+
58+
const providerInfo = getMonitorProvider(monitorInfo?.type ?? '');
59+
60+
const { data } = useMemo(() => {
61+
const data = _data.map((d, i, arr) => {
62+
const value = d.value > 0 ? d.value : null;
63+
const time = dayjs(d.createdAt).valueOf();
64+
65+
return {
66+
value,
67+
time,
68+
};
69+
});
70+
71+
return { data };
72+
}, [_data]);
73+
74+
const isTrendingMode = monitorInfo?.trendingMode ?? false; // if true, y axis not start from 0
75+
76+
return (
77+
<div>
78+
<ChartContainer className="h-[120px] w-full" config={chartConfig}>
79+
<AreaChart
80+
data={data}
81+
margin={{ top: 10, right: 0, left: 0, bottom: 0 }}
82+
>
83+
<defs>
84+
<linearGradient id="color" x1="0" y1="0" x2="0" y2="1">
85+
<stop
86+
offset="5%"
87+
stopColor={colors.chart.monitor}
88+
stopOpacity={0.3}
89+
/>
90+
<stop
91+
offset="95%"
92+
stopColor={colors.chart.monitor}
93+
stopOpacity={0}
94+
/>
95+
</linearGradient>
96+
</defs>
97+
<XAxis
98+
dataKey="time"
99+
type="number"
100+
domain={['dataMin', 'dataMax']}
101+
tickFormatter={(date) => dayjs(date).format('HH:mm')}
102+
/>
103+
<YAxis
104+
mirror
105+
domain={[isTrendingMode ? 'dataMin' : 0, 'dataMax']}
106+
/>
107+
<CartesianGrid vertical={false} />
108+
<ChartTooltip
109+
labelFormatter={(label, payload) =>
110+
dayjs(get(payload, [0, 'payload', 'time'])).format(
111+
'YYYY-MM-DD HH:mm:ss'
112+
)
113+
}
114+
formatter={(value, defaultText, item, index, payload) => {
115+
if (typeof value !== 'number') {
116+
return defaultText;
117+
}
118+
const { name, text } = getProviderDisplay(
119+
Number(value),
120+
providerInfo
121+
);
122+
123+
return (
124+
<div>
125+
<span className="mr-2">{name}:</span>
126+
<span>{text}</span>
127+
</div>
128+
);
129+
}}
130+
content={<ChartTooltipContent />}
131+
/>
132+
133+
<Customized component={CustomizedErrorArea} />
134+
135+
<Area
136+
type="monotone"
137+
dataKey="value"
138+
stroke={colors.chart.monitor}
139+
fillOpacity={1}
140+
fill="url(#color)"
141+
strokeWidth={2}
142+
isAnimationActive={false}
143+
/>
144+
</AreaChart>
145+
</ChartContainer>
146+
</div>
147+
);
148+
});
149+
MonitorPublicDataChart.displayName = 'MonitorPublicDataChart';

0 commit comments

Comments
 (0)