Skip to content

Commit b780255

Browse files
committed
[pinpoint-apm#12240] Heatmap > Show Apdex & HelpPopover
1 parent 8c9c455 commit b780255

File tree

8 files changed

+188
-40
lines changed

8 files changed

+188
-40
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { defaultTickFormatter } from '@pinpoint-fe/ui/src/components/ReChart';
44
import { capitalize } from 'lodash';
55
import { HeatmapSettingType } from './HeatmapSetting';
66

7+
import { format } from 'date-fns';
78
import * as echarts from 'echarts/core';
89
import { CanvasRenderer } from 'echarts/renderers';
910
import { HeatmapChart as HeatmapChartEcharts } from 'echarts/charts';
@@ -169,11 +170,11 @@ const HeatmapChart = React.forwardRef(
169170
type: 'category',
170171
data: xAxisData?.sort((a, b) => Number(a) - Number(b)),
171172
axisLabel: {
172-
interval: 'auto',
173+
interval: '1',
173174
showMaxLabel: true,
174175
showMinLabel: true,
175176
formatter: (value: string) => {
176-
return defaultTickFormatter(Number(value));
177+
return `${format(Number(value), 'yyyy.MM.dd')}\n${format(Number(value), 'HH:mm:ss')}`;
177178
},
178179
},
179180
},
@@ -406,6 +407,7 @@ const HeatmapChart = React.forwardRef(
406407
borderWidth: 1,
407408
},
408409
},
410+
cursor: 'crosshair',
409411
},
410412
],
411413
});

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ import html2canvas from 'html2canvas';
33
import {
44
APP_SETTING_KEYS,
55
BASE_PATH,
6-
colors,
76
GetHeatmapAppData,
87
GetServerMap,
98
ApplicationType,
109
} from '@pinpoint-fe/ui/src/constants';
1110
import HeatmapChart from './HeatmapChart';
1211
import {
1312
HelpPopover,
14-
cn,
1513
convertParamsToQueryString,
1614
getFormattedDateRange,
1715
getHeatmapFullScreenPath,
@@ -124,15 +122,13 @@ const HeatmapChartCore = ({
124122

125123
return (
126124
<div className="relative flex flex-col w-full h-full gap-2">
127-
<div className={cn('flex flex-row justify-end')}>
128-
<div className="flex flex-row items-center justify-end gap-2 px-4 font-normal text-gray-400">
129-
<BsGearFill className="text-base cursor-pointer" onClick={() => setShowSetting(true)} />
130-
<FaDownload className="text-base cursor-pointer" onClick={handleCaptureImage} />
131-
{!toolbarOption?.expand?.hide && (
132-
<FaExpandArrowsAlt className="text-base cursor-pointer" onClick={handleExpand} />
133-
)}
134-
{/* <HelpPopover helpKey="HELP_VIEWER.SCATTER" /> */}
135-
</div>
125+
<div className="absolute flex flex-row items-center justify-end gap-2 px-4 font-normal text-gray-400 -top-5 right-3">
126+
<BsGearFill className="text-base cursor-pointer" onClick={() => setShowSetting(true)} />
127+
<FaDownload className="text-base cursor-pointer" onClick={handleCaptureImage} />
128+
{!toolbarOption?.expand?.hide && (
129+
<FaExpandArrowsAlt className="text-base cursor-pointer" onClick={handleExpand} />
130+
)}
131+
<HelpPopover helpKey="HELP_VIEWER.HEATMAP" />
136132
</div>
137133
<HeatmapChart
138134
ref={chartContainerRef}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { cn } from '@pinpoint-fe/ui';
2+
3+
const HeatmapVisualMapIcon = ({ type }: { type?: 'success' | 'fail' }) => {
4+
return (
5+
<div className="flex flex-col items-center justify-center text-xs text-gray-700">
6+
<div className="w-[70px] flex items-center justify-between">
7+
<span>min ~</span>
8+
<span>~ max</span>
9+
</div>
10+
<div
11+
className={cn('w-[50px] relative h-3 mx-1 rounded-full bg-gradient-to-r', {
12+
'from-green-50 to-green-800': type === 'success',
13+
'from-red-50 to-red-800': type === 'fail',
14+
})}
15+
>
16+
{/* 왼쪽 핸들 */}
17+
<div className="absolute w-2 h-4 -translate-y-1/2 border-2 border-gray-300 rounded shadow-sm top-1/2" />
18+
{/* 오른쪽 핸들 */}
19+
<div className="absolute right-0 w-2 h-4 -translate-y-1/2 border-2 border-gray-300 rounded shadow-sm top-1/2" />
20+
</div>
21+
</div>
22+
);
23+
};
24+
export default HeatmapVisualMapIcon;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ import {
1616
FaSmileBeam,
1717
FaCalendarAlt,
1818
FaClock,
19+
FaRegHandPointer,
1920
} from 'react-icons/fa';
2021
import { BsGearFill } from 'react-icons/bs';
2122
import * as PopoverPrimitive from '@radix-ui/react-popover';
2223
import { Trans, useTranslation } from 'react-i18next';
2324
import { Separator } from '../../components/ui/separator';
2425
import { BiSolidServer } from 'react-icons/bi';
2526
import { PiHardDriveFill } from 'react-icons/pi';
27+
import HeatmapVisualMapIcon from './HeatmapVisualMapIcon';
2628

2729
export type HelpContent = {
2830
TITLE?: string;
@@ -56,6 +58,8 @@ const components = {
5658
FaClock: <FaClock />,
5759
FaCalendarAlt: <FaCalendarAlt />,
5860
Lt: <>{'<'}</>,
61+
FaRegHandPointer: <FaRegHandPointer />,
62+
HeatmapVisualMapIcon: <HeatmapVisualMapIcon />,
5963
};
6064

6165
export const HelpPopover = ({

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

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '@pinpoint-fe/ui/src/atoms';
3131
import { APP_SETTING_KEYS, ApplicationType, GetServerMap } from '@pinpoint-fe/ui/src/constants';
3232
import { getServerImagePath } from '@pinpoint-fe/ui/src/utils';
33+
import { cn } from '@pinpoint-fe/ui/src/lib';
3334

3435
export interface RealtimeProps {}
3536

@@ -154,27 +155,32 @@ export const Realtime = () => {
154155
) : null}
155156
{!shouldHideScatter() && isFocus && (
156157
<>
157-
{chartType === 'scatter' ? (
158-
<div className="w-full p-5 mb-12 aspect-[1.618]">
159-
<div className="h-7">
160-
<ApdexScore
161-
shouldPoll={true}
162-
nodeData={currentTargetData || application}
163-
></ApdexScore>
164-
</div>
158+
<div
159+
className={cn('w-full p-5', {
160+
'aspect-[1.618] mb-12 ': chartType === 'scatter',
161+
'aspect-[1.4]': chartType === 'heatmap',
162+
})}
163+
>
164+
<div className="h-7">
165+
<ApdexScore
166+
shouldPoll={true}
167+
nodeData={currentTargetData || application}
168+
></ApdexScore>
169+
</div>
170+
{chartType === 'scatter' ? (
165171
<ScatterChart
166172
node={serverMapCurrentTarget || (application as ApplicationType)}
167173
realtime={true}
168174
/>
169-
</div>
170-
) : (
171-
<div className="w-full pl-3 pt-5 pr-10 pb-8 aspect-[1.3]">
175+
) : (
176+
// <div className="w-full pl-3 pt-5 pr-10 pb-8 aspect-[1.3]">
172177
<Heatmap
173178
nodeData={currentTargetData || (application as ApplicationType)}
174179
realtime={true}
175180
/>
176-
</div>
177-
)}
181+
// </div>
182+
)}
183+
</div>
178184
<Separator />
179185
</>
180186
)}

web-frontend/src/main/v3/packages/ui/src/constants/locales/en.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,62 @@
343343
}
344344
]
345345
},
346+
"HEATMAP": {
347+
"TITLE": "Response Time Heatmap Chart",
348+
"DESC": "",
349+
"CATEGORY": [
350+
{
351+
"TITLE": "Legend",
352+
"ITEMS": [
353+
{
354+
"NAME": "<FaCircle color=\"#2EB089\" size=\"15\"/>",
355+
"DESC": "Successful Transaction"
356+
},
357+
{
358+
"NAME": "<FaCircle color=\"#E64A4F\" size=\"15\"/>",
359+
"DESC": "Failed Transaction"
360+
},
361+
{
362+
"NAME": "X-Axis",
363+
"DESC": "Transaction Timestamp"
364+
},
365+
{
366+
"NAME": "Y-Axis",
367+
"DESC": "Response Time"
368+
}
369+
]
370+
},
371+
{
372+
"TITLE": "Usage",
373+
"ITEMS": [
374+
{
375+
"NAME": "<FaRegHandPointer/>",
376+
"DESC": "Drag on the scatter chart to show detailed information on selected transactions."
377+
},
378+
{
379+
"NAME": "<BsGearFill/>",
380+
"DESC": "Set the min/max value of the Y-axis (Response Time)."
381+
},
382+
{
383+
"NAME": "<FaDownload/>",
384+
"DESC": "Download the chart as an image file."
385+
},
386+
{
387+
"NAME": "<FaExpandArrowsAlt/>",
388+
"DESC": "Open the chart in a new window."
389+
},
390+
{
391+
"NAME": "<HeatmapVisualMapIcon type=\"success\"/>",
392+
"DESC": "Among all cells in the heatmap, the maximum and minimum success count values are displayed.<br/>Use the handlebar to filter cells by specifying a success count range."
393+
},
394+
{
395+
"NAME": "<HeatmapVisualMapIcon type=\"fail\"/>",
396+
"DESC": "Among all cells in the heatmap, the maximum and minimum fail count values are displayed.<br/>Use the handlebar to filter cells by specifying a fail count range."
397+
}
398+
]
399+
}
400+
]
401+
},
346402
"RESPONSE_SUMMARY": {
347403
"TITLE": "Response Summary Chart",
348404
"DESC": "",

web-frontend/src/main/v3/packages/ui/src/constants/locales/ko.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,62 @@
343343
}
344344
]
345345
},
346+
"HEATMAP": {
347+
"TITLE": "Response Time Heatmap Chart",
348+
"DESC": "수집된 트랜잭션의 응답시간 분포도입니다.",
349+
"CATEGORY": [
350+
{
351+
"TITLE": "범례",
352+
"ITEMS": [
353+
{
354+
"NAME": "<FaCircle color=\"#2EB089\" size=\"15\"/>",
355+
"DESC": "성공한 트랜잭션"
356+
},
357+
{
358+
"NAME": "<FaCircle color=\"#E64A4F\" size=\"15\"/>",
359+
"DESC": "실패한 트랜잭션"
360+
},
361+
{
362+
"NAME": "X축",
363+
"DESC": "트랜잭션이 실행된 시간"
364+
},
365+
{
366+
"NAME": "Y축",
367+
"DESC": "트랜잭션의 응답 속도"
368+
}
369+
]
370+
},
371+
{
372+
"TITLE": "기능",
373+
"ITEMS": [
374+
{
375+
"NAME": "<FaRegHandPointer/>",
376+
"DESC": "마우스로 영역을 드래그하여 드래그 된 영역에 속한 트랜잭션의 상세정보를 조회할 수 있습니다."
377+
},
378+
{
379+
"NAME": "<BsGearFill/>",
380+
"DESC": "응답시간(Y축)의 최소 또는 최대값을 변경할 수 있습니다."
381+
},
382+
{
383+
"NAME": "<FaDownload/>",
384+
"DESC": "차트를 이미지 파일로 저장할 수 있습니다."
385+
},
386+
{
387+
"NAME": "<FaExpandArrowsAlt/>",
388+
"DESC": "차트를 새창으로 볼 수 있습니다."
389+
},
390+
{
391+
"NAME": "<HeatmapVisualMapIcon type=\"success\"/>",
392+
"DESC": "heatmap의 모든 셀에서, success count값의 최대 최소값을 표현합니다.<br/>handleBar 위치를 조정하여 cell 표시 범위를 지정할 수 있습니다."
393+
},
394+
{
395+
"NAME": "<HeatmapVisualMapIcon type=\"fail\"/>",
396+
"DESC": "heatmap의 모든 셀에서, fail count값의 최대 최소값을 표현합니다.<br/>handleBar 위치를 조정하여 cell 표시 범위를 지정할 수 있습니다."
397+
}
398+
]
399+
}
400+
]
401+
},
346402
"RESPONSE_SUMMARY": {
347403
"TITLE": "Response Summary Chart",
348404
"DESC": "응답결과 요약입니다.",

web-frontend/src/main/v3/packages/ui/src/pages/ServerMap.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
HelpPopover,
5959
ApplicationCombinedListProps,
6060
ChartTypeButtons,
61+
cn,
6162
} from '@pinpoint-fe/ui';
6263
import { Edge, Node } from '@pinpoint-fe/server-map';
6364
import { PiTreeStructureDuotone, PiArrowSquareOut } from 'react-icons/pi';
@@ -440,26 +441,29 @@ export const ServerMapPage = ({
440441
) : null}
441442
{!shouldHideScatter() && (
442443
<>
443-
{chartType === 'scatter' ? (
444-
<div className="w-full p-5 mb-12 aspect-[1.618]">
445-
<div className="h-7">
446-
<ApdexScore
447-
nodeData={
448-
(currentTargetData as GetServerMap.NodeData) || application
449-
}
450-
/>
451-
</div>
452-
<ScatterChart node={serverMapCurrentTarget || application} />
444+
<div
445+
className={cn('w-full p-5', {
446+
'mb-12 aspect-[1.618]': chartType === 'scatter',
447+
'aspect-[1.4]': chartType === 'heatmap',
448+
})}
449+
>
450+
<div className="h-7">
451+
<ApdexScore
452+
nodeData={
453+
(currentTargetData as GetServerMap.NodeData) || application
454+
}
455+
/>
453456
</div>
454-
) : (
455-
<div className="w-full p-5 aspect-[1.3]">
457+
{chartType === 'scatter' ? (
458+
<ScatterChart node={serverMapCurrentTarget || application} />
459+
) : (
456460
<Heatmap
457461
nodeData={
458462
(currentTargetData as GetServerMap.NodeData) || application
459463
}
460464
/>
461-
</div>
462-
)}
465+
)}
466+
</div>
463467
<Separator />
464468
</>
465469
)}

0 commit comments

Comments
 (0)