Skip to content

Commit 72bfd7c

Browse files
committed
[pinpoint-apm#12240] Heatmap > experimental.enableHeatmap
1 parent 17a175c commit 72bfd7c

File tree

15 files changed

+508
-168
lines changed

15 files changed

+508
-168
lines changed

web-frontend/src/main/v3/packages/ui/src/components/Heatmap/HeatmapChartCore.tsx

Lines changed: 128 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import {
1010
GraphicComponent,
1111
} from 'echarts/components';
1212
import { ECharts, EChartsOption } from 'echarts';
13-
import { mockData } from './mockData';
14-
import { colors } from '@pinpoint-fe/ui/src/constants';
15-
import { capitalize, set } from 'lodash';
13+
import { colors, GetHeatmapAppData } from '@pinpoint-fe/ui/src/constants';
14+
import { capitalize, debounce } from 'lodash';
1615
import { defaultTickFormatter } from '@pinpoint-fe/ui/src/components/ReChart';
1716
import { HeatmapSettingType } from './HeatmapSetting';
1817

@@ -30,24 +29,29 @@ echarts.use([HeatmapChartEcharts, CanvasRenderer]);
3029
export const HeatmapColor = {
3130
success: '#34b994',
3231
failed: '#eb4748',
32+
selected: 'blue',
3333
};
3434

3535
type HeatmapChartCoreProps = {
36+
isLoading?: boolean;
37+
data?: GetHeatmapAppData.Response;
3638
setting: HeatmapSettingType;
3739
};
3840

3941
const HeatmapChartCore = React.forwardRef(
40-
({ setting }: HeatmapChartCoreProps, ref: React.Ref<ReactEChartsCore>) => {
42+
({ data, setting }: HeatmapChartCoreProps, ref: React.Ref<ReactEChartsCore>) => {
4143
const containerRef = React.useRef<HTMLDivElement>(null);
4244
const [containerSize, setContainerSize] = React.useState({
4345
width: 0,
4446
height: 0,
4547
});
48+
const chartRef = React.useRef<ReactEChartsCore>(null);
4649

47-
const successData: [string, string, number][] = [];
48-
const failedData: [string, string, number][] = [];
49-
let maxFailedCount = 0;
50-
let maxSuccessCount = 0;
50+
const [successRange, setSuccessRange] = React.useState(); // 성공 범위: [시작, 끝]
51+
const [failRange, setFailRange] = React.useState(); // 성공 범위: [시작, 끝]
52+
53+
const [startCell, setStartCell] = React.useState(''); // 시작 셀: x-y
54+
const [endCell, setEndCell] = React.useState(''); // 끝 셀: x-y
5155

5256
React.useEffect(() => {
5357
const wrapperElement = containerRef.current;
@@ -65,45 +69,90 @@ const HeatmapChartCore = React.forwardRef(
6569
};
6670
}, []);
6771

68-
const { matrixData } = mockData;
69-
matrixData.forEach((row) => {
70-
row.cellData.forEach((cell) => {
71-
successData.push([String(row.timestamp), String(cell.elapsedTime), cell.successCount]);
72-
failedData.push([String(row.timestamp), String(cell.elapsedTime), cell.failCount]);
72+
const isSelectedCell = React.useCallback(
73+
(x: number, y: number) => {
74+
const [startX, startY] = startCell.split('-').map(Number);
75+
const [endX, endY] = endCell.split('-').map(Number);
76+
77+
if (!startX || !startY || !endX || !endY) {
78+
return false;
79+
}
80+
81+
const left = Math.min(startX, endX);
82+
const right = Math.max(startX, endX);
83+
const top = Math.max(startY, endY);
84+
const bottom = Math.min(startY, endY);
85+
86+
return x >= left && x <= right && y <= top && y >= bottom;
87+
},
88+
[startCell, endCell],
89+
);
90+
91+
const dataForRender = React.useMemo(() => {
92+
const successData: [string, string, number][] = [];
93+
const failData: {
94+
value: [string, string, number];
95+
itemStyle?: {
96+
color: string;
97+
opacity: number;
98+
};
99+
}[] = [];
100+
let maxFailCount = 0;
101+
let maxSuccessCount = 0;
73102

74-
maxSuccessCount = Math.max(maxSuccessCount, cell.successCount);
75-
maxFailedCount = Math.max(maxFailedCount, cell.failCount);
103+
const { heatmapData } = data || {};
104+
heatmapData?.forEach((row) => {
105+
row?.cellDataList?.forEach((cell) => {
106+
successData.push([String(row.timestamp), String(cell.elapsedTime), cell.successCount]);
107+
failData.push({
108+
value: [String(row.timestamp), String(cell.elapsedTime), cell.failCount],
109+
itemStyle: isSelectedCell(row.timestamp, cell.elapsedTime)
110+
? {
111+
color: HeatmapColor.selected,
112+
opacity: 0.8,
113+
}
114+
: undefined,
115+
});
116+
117+
maxSuccessCount = Math.max(maxSuccessCount, cell.successCount);
118+
maxFailCount = Math.max(maxFailCount, cell.failCount);
119+
});
76120
});
77-
});
78121

79-
const totalSuccessCount = setting.yMax;
80-
const totalFailedCount = setting.yMax;
122+
return { successData, failData, maxFailCount, maxSuccessCount };
123+
}, [data, startCell, endCell]);
81124

82-
const xAxisData = matrixData.map((row) => String(row.timestamp));
83-
const yAxisData = matrixData[0].cellData.map((cell) => String(cell.elapsedTime));
125+
const xAxisData = React.useMemo(() => {
126+
return data?.heatmapData?.map((row) => String(row.timestamp)) || [];
127+
}, [data]);
128+
const yAxisData = React.useMemo(() => {
129+
return data?.heatmapData?.[0].cellDataList?.map((cell) => String(cell.elapsedTime)) || [];
130+
}, [data]);
84131

85-
// console.log('successData', successData);
86-
// console.log('failedData', failedData);
132+
const totalSuccessCount = React.useMemo(() => {
133+
return data?.summary?.totalSuccessCount || 0;
134+
}, [data]);
135+
const totalFailedCount = React.useMemo(() => {
136+
return data?.summary?.totalFailCount || 0;
137+
}, [data]);
87138

88139
const option: EChartsOption = {
89140
tooltip: {
141+
show: !!startCell ? false : true,
90142
borderColor: colors.gray[300],
91143
textStyle: {
92144
fontFamily: 'inherit',
93145
fontSize: 8,
94146
},
95147
formatter: (params: any) => {
96148
const { data } = params;
97-
const [timestamp, elapsedTime, failedCount] = data;
98-
const date = new Date(timestamp);
99-
const successCount =
100-
successData.find(
101-
(item: [string, string, number]) => item[0] === timestamp && item[1] === elapsedTime,
102-
)?.[2] || 'N/A';
103-
149+
const [timestamp, elapsedTime, failedCount] = data?.value;
150+
const successCount = dataForRender?.successData.find((item: [string, string, number]) => {
151+
return item[0] === timestamp && item[1] === elapsedTime;
152+
})?.[2];
104153
return `
105154
<div style="display: flex; flex-direction: column; gap: 5px; padding: 2px;">
106-
<div style="margin-bottom: 5px;"><strong>${defaultTickFormatter(date.getTime())}</strong></div>
155+
<div style="margin-bottom: 5px;"><strong>${defaultTickFormatter(Number(timestamp))}</strong></div>
107156
${['success', 'failed']
108157
.map((type) => {
109158
const count = type === 'success' ? successCount : failedCount;
@@ -114,7 +163,7 @@ const HeatmapChartCore = React.forwardRef(
114163
<div style="display: flex; gap: 6px; align-items: center;">
115164
<div style="width: 8px; height: 8px; background: ${color}"></div>${capitalize(type)}
116165
</div>
117-
<div>${Number(count).toLocaleString()}</div>
166+
<div>${count === undefined ? 'N/A' : Number(count).toLocaleString()}</div>
118167
</div>
119168
`;
120169
})
@@ -127,18 +176,17 @@ const HeatmapChartCore = React.forwardRef(
127176
left: setting.yMax.toString().length * 10,
128177
right: '10px',
129178
top: '2%',
130-
bottom: '20%',
179+
bottom: '100px',
131180
},
132181
xAxis: {
133182
type: 'category',
134-
data: xAxisData.sort((a, b) => new Date(a).getTime() - new Date(b).getTime()),
183+
data: xAxisData.sort((a, b) => Number(a) - Number(b)),
135184
axisLabel: {
136185
interval: 'auto',
137186
showMaxLabel: true,
138187
showMinLabel: true,
139188
formatter: (value: string) => {
140-
const date = new Date(value);
141-
return defaultTickFormatter(date.getTime());
189+
return defaultTickFormatter(Number(value));
142190
},
143191
},
144192
},
@@ -173,14 +221,16 @@ const HeatmapChartCore = React.forwardRef(
173221
},
174222
visualMap: [
175223
{
224+
id: 'success',
176225
min: 0,
177-
max: maxSuccessCount,
226+
max: dataForRender?.maxSuccessCount,
178227
calculable: true,
179228
seriesIndex: 0,
180229
orient: 'horizontal',
230+
itemWidth: 14,
181231
itemHeight: (containerSize.width || 100) * 0.3,
182232
right: '45%',
183-
bottom: '4%',
233+
bottom: '5%',
184234
hoverLink: false,
185235
formatter: (value) => {
186236
if (value === setting.yMax) {
@@ -189,25 +239,29 @@ const HeatmapChartCore = React.forwardRef(
189239
return Math.floor(Number(value)).toLocaleString();
190240
},
191241
inRange: {
192-
color: ['#ffffff', HeatmapColor.success],
242+
color: ['#ffffff', dataForRender?.maxFailCount ? HeatmapColor.success : '#ffffff'],
193243
},
244+
range: successRange,
194245
},
195246
{
247+
id: 'fail',
196248
min: 0,
197-
max: maxFailedCount,
249+
max: dataForRender?.maxFailCount,
198250
calculable: true,
199251
seriesIndex: 1,
200252
orient: 'horizontal',
253+
itemWidth: 14,
201254
itemHeight: (containerSize.width || 100) * 0.3,
202255
left: '55%',
203-
bottom: '4%',
256+
bottom: '5%',
204257
hoverLink: false,
205258
formatter: (value) => {
206259
return Math.floor(Number(value)).toLocaleString();
207260
},
208261
inRange: {
209-
color: ['#ffffff', HeatmapColor.failed],
262+
color: ['#ffffff', dataForRender?.maxFailCount ? HeatmapColor.failed : '#ffffff'],
210263
},
264+
range: failRange,
211265
},
212266
],
213267
graphic: [
@@ -236,12 +290,12 @@ const HeatmapChartCore = React.forwardRef(
236290
{
237291
name: 'success',
238292
type: 'heatmap',
239-
data: successData,
293+
data: dataForRender?.successData,
240294
},
241295
{
242296
name: 'failed',
243297
type: 'heatmap',
244-
data: failedData,
298+
data: dataForRender?.failData,
245299
itemStyle: {
246300
opacity: 0.5,
247301
},
@@ -261,7 +315,37 @@ const HeatmapChartCore = React.forwardRef(
261315
ref={ref}
262316
echarts={echarts}
263317
option={option}
264-
style={{ height: '100%', width: '100%', minHeight: 500 }}
318+
style={{ height: '100%', width: '100%' }}
319+
onEvents={{
320+
mousedown: (params: any, echartsInstance: ECharts) => {
321+
console.log('mousedown', params);
322+
setStartCell(`${params.value[0]}-${params.value[1]}`);
323+
setEndCell(`${params.value[0]}-${params.value[1]}`);
324+
},
325+
mousemove: (params: any) => {
326+
if (!startCell) {
327+
return;
328+
}
329+
setEndCell(`${params.value[0]}-${params.value[1]}`);
330+
},
331+
mouseup: (params: any) => {
332+
console.log('mouseup', params, startCell, endCell);
333+
setStartCell('');
334+
setEndCell('');
335+
},
336+
datarangeselected: debounce((params: any) => {
337+
if (params.visualMapId === 'success') {
338+
setSuccessRange(params.selected);
339+
} else if (params.visualMapId === 'fail') {
340+
setFailRange(params.selected);
341+
}
342+
}, 300),
343+
// click: (params: any, echartsInstance: ECharts) => {
344+
// console.log('click', params);
345+
// setStartCell(`${params.value[0]}-${params.value[1]}`);
346+
// // setRange([1000, 3000]);
347+
// },
348+
}}
265349
/>
266350
</div>
267351
);

0 commit comments

Comments
 (0)