ydb-embedded-ui 3.3.3 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +28 -0
- package/dist/components/Errors/ResponseError/ResponseError.tsx +2 -2
- package/dist/components/InfoViewer/formatters/topicStats.tsx +8 -29
- package/dist/components/LabelWithPopover/LabelWithPopover.tsx +20 -0
- package/dist/components/LabelWithPopover/index.ts +1 -0
- package/dist/components/LagImages/LagImages.tsx +205 -0
- package/dist/components/LagImages/index.ts +1 -0
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.scss +92 -0
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +120 -0
- package/dist/components/SpeedMultiMeter/i18n/en.json +6 -0
- package/dist/components/SpeedMultiMeter/i18n/index.ts +13 -0
- package/dist/components/SpeedMultiMeter/i18n/ru.json +6 -0
- package/dist/components/SpeedMultiMeter/index.ts +1 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +26 -14
- package/dist/containers/Storage/VDisk/VDisk.tsx +20 -5
- package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +34 -5
- package/dist/containers/Storage/utils/types.ts +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +32 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +62 -69
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.scss +13 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.tsx +27 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.scss +32 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +43 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/Columns.scss +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +66 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +4 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +4 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/utils/constants.ts +23 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +7 -3
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -9
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +9 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +6 -8
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.scss +33 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.tsx +76 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.scss +45 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +254 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +79 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +246 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/en.json +13 -0
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards → Partitions}/i18n/index.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/ru.json +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/constants.ts +74 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/types.ts +6 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss +8 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +56 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.scss → TopShards/TopShards.scss} +2 -10
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.tsx → TopShards/TopShards.tsx} +61 -31
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +6 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +6 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
- package/dist/containers/Tenant/utils/schema.ts +1 -16
- package/dist/services/api.d.ts +4 -0
- package/dist/services/api.js +22 -6
- package/dist/store/reducers/authentication.js +0 -15
- package/dist/store/reducers/consumer.ts +160 -0
- package/dist/store/reducers/index.ts +2 -0
- package/dist/store/reducers/settings.js +2 -0
- package/dist/store/reducers/shardsWorkload.ts +28 -2
- package/dist/store/reducers/topic.ts +82 -2
- package/dist/store/state-url-mapping.js +3 -0
- package/dist/types/store/consumer.ts +55 -0
- package/dist/types/store/shardsWorkload.ts +6 -0
- package/dist/types/store/topic.ts +23 -6
- package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +24 -0
- package/dist/utils/bytesParsers/formatBytesCustom.ts +57 -0
- package/dist/utils/bytesParsers/i18n/en.json +7 -0
- package/dist/utils/bytesParsers/i18n/index.ts +11 -0
- package/dist/utils/bytesParsers/i18n/ru.json +7 -0
- package/dist/utils/bytesParsers/index.ts +2 -0
- package/dist/utils/constants.ts +3 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/storage.ts +2 -2
- package/dist/utils/timeParsers/index.ts +2 -1
- package/dist/utils/timeParsers/parsers.ts +18 -0
- package/dist/utils/timeParsers/{protobuf.ts → protobufParsers.ts} +0 -0
- package/dist/utils/typecheckers.ts +5 -0
- package/dist/utils/utils.js +3 -3
- package/package.json +2 -2
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +0 -4
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +0 -4
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +0 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.4.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.4...v3.4.0) (2023-02-17)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* **Diagnostics:** add Partitions tab ([914702b](https://github.com/ydb-platform/ydb-embedded-ui/commit/914702be7e8aea28fcdc9f2ddf1cb7356995146a))
|
9
|
+
* **Diagnostics:** rework Consumers tab ([0dae9d8](https://github.com/ydb-platform/ydb-embedded-ui/commit/0dae9d84c254d556db2a0d18345fdc10c152172a))
|
10
|
+
|
11
|
+
|
12
|
+
### Bug Fixes
|
13
|
+
|
14
|
+
* add read and lag images ([a3f0648](https://github.com/ydb-platform/ydb-embedded-ui/commit/a3f0648fc4f23c2ac2c9e73c4078bf5f06d1a57e))
|
15
|
+
* add reducer for consumer ([4ab65e3](https://github.com/ydb-platform/ydb-embedded-ui/commit/4ab65e3fb3dd4f29b4757473275ba84bec0f5411))
|
16
|
+
* add SpeedMultiMeter component ([39acbf1](https://github.com/ydb-platform/ydb-embedded-ui/commit/39acbf1a1e234f36a090b29935872e694e1525c0))
|
17
|
+
* **ResponseError:** make error prop optional ([f706e94](https://github.com/ydb-platform/ydb-embedded-ui/commit/f706e940e51e62841e18338775b01183831761e1))
|
18
|
+
* **Storage:** display not full donors ([13f4b9f](https://github.com/ydb-platform/ydb-embedded-ui/commit/13f4b9fe9f796e8ef6fee094f7b5bc6056e2833b))
|
19
|
+
* **Topic:** use SpeedMultiMeter and utils functions ([3e0293c](https://github.com/ydb-platform/ydb-embedded-ui/commit/3e0293cc5cf69c2dee5b6c4cdcf053829960dac5))
|
20
|
+
* **utils:** add formatBytesCustom function ([2f18c22](https://github.com/ydb-platform/ydb-embedded-ui/commit/2f18c2233b37b666e16327af0ca8e20bccf01de6))
|
21
|
+
|
22
|
+
## [3.3.4](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.3...v3.3.4) (2023-02-16)
|
23
|
+
|
24
|
+
|
25
|
+
### Bug Fixes
|
26
|
+
|
27
|
+
* **OverloadedShards:** rename to top shards ([ffa4f27](https://github.com/ydb-platform/ydb-embedded-ui/commit/ffa4f27f2cf0a5e12b2800c81bf61b1d3c25912c))
|
28
|
+
* **StorageGroups:** display Erasure ([4a7ebc0](https://github.com/ydb-platform/ydb-embedded-ui/commit/4a7ebc08b87fe75af83df70a38ebd486d64d6d4e))
|
29
|
+
* **TopShards:** switch between history and immediate data ([eeb9bb0](https://github.com/ydb-platform/ydb-embedded-ui/commit/eeb9bb0911b9e889b633558c9d3c13f986f72bfe))
|
30
|
+
|
3
31
|
## [3.3.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.2...v3.3.3) (2023-02-08)
|
4
32
|
|
5
33
|
|
@@ -3,7 +3,7 @@ import type {IResponseError} from '../../../types/api/error';
|
|
3
3
|
import i18n from '../i18n';
|
4
4
|
|
5
5
|
interface ResponseErrorProps {
|
6
|
-
error
|
6
|
+
error?: IResponseError;
|
7
7
|
className?: string;
|
8
8
|
defaultMessage?: string;
|
9
9
|
}
|
@@ -13,5 +13,5 @@ export const ResponseError = ({
|
|
13
13
|
className,
|
14
14
|
defaultMessage = i18n('responseError.defaultMessage'),
|
15
15
|
}: ResponseErrorProps) => {
|
16
|
-
return <div className={`error ${className}`}>{error
|
16
|
+
return <div className={`error ${className}`}>{error?.statusText || defaultMessage}</div>;
|
17
17
|
};
|
@@ -1,45 +1,24 @@
|
|
1
|
-
import type {MultipleWindowsStat} from '../../../types/api/consumer';
|
2
1
|
import type {TopicStats} from '../../../types/api/topic';
|
3
2
|
import {formatBytes} from '../../../utils';
|
4
|
-
import {DAY_IN_SECONDS, HOUR_IN_SECONDS, MINUTE_IN_SECONDS} from '../../../utils/constants';
|
5
|
-
|
6
3
|
import {
|
7
|
-
|
8
|
-
|
4
|
+
parseLag,
|
5
|
+
parseTimestampToIdleTime,
|
9
6
|
formatDurationToShortTimeFormat,
|
10
7
|
} from '../../../utils/timeParsers';
|
8
|
+
import {convertBytesObjectToSpeed} from '../../../utils/bytesParsers';
|
11
9
|
|
12
|
-
import {
|
10
|
+
import {SpeedMultiMeter} from '../../SpeedMultiMeter';
|
13
11
|
|
14
12
|
import {createInfoFormatter} from '../utils';
|
15
13
|
|
16
|
-
export const prepareBytesWritten = (data?: MultipleWindowsStat) => {
|
17
|
-
return {
|
18
|
-
per_minute:
|
19
|
-
data && data.per_minute ? Math.floor(Number(data.per_minute) / MINUTE_IN_SECONDS) : 0,
|
20
|
-
per_hour: data && data.per_hour ? Math.floor(Number(data.per_hour) / HOUR_IN_SECONDS) : 0,
|
21
|
-
per_day: data && data.per_day ? Math.floor(Number(data.per_day) / DAY_IN_SECONDS) : 0,
|
22
|
-
};
|
23
|
-
};
|
24
|
-
|
25
14
|
export const formatTopicStats = createInfoFormatter<TopicStats>({
|
26
15
|
values: {
|
27
16
|
store_size_bytes: formatBytes,
|
28
|
-
min_last_write_time: (value) =>
|
29
|
-
|
30
|
-
|
31
|
-
}
|
32
|
-
|
33
|
-
const durationMs = Date.now() - parseProtobufTimestampToMs(value);
|
34
|
-
|
35
|
-
// Duration could be negative because of the difference between server and local time
|
36
|
-
// Usually it below 100ms, so it could be omitted
|
37
|
-
return formatDurationToShortTimeFormat(durationMs < 0 ? 0 : durationMs);
|
38
|
-
},
|
39
|
-
max_write_time_lag: (value) =>
|
40
|
-
formatDurationToShortTimeFormat(value ? parseProtobufDurationToMs(value) : 0),
|
17
|
+
min_last_write_time: (value) =>
|
18
|
+
formatDurationToShortTimeFormat(parseTimestampToIdleTime(value)),
|
19
|
+
max_write_time_lag: (value) => formatDurationToShortTimeFormat(parseLag(value)),
|
41
20
|
bytes_written: (value) =>
|
42
|
-
value && <
|
21
|
+
value && <SpeedMultiMeter data={convertBytesObjectToSpeed(value)} withValue={false} />,
|
43
22
|
},
|
44
23
|
labels: {
|
45
24
|
store_size_bytes: 'Store size',
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import type {ReactNode} from 'react';
|
2
|
+
|
3
|
+
import {HelpPopover} from '@gravity-ui/uikit';
|
4
|
+
|
5
|
+
interface LabelWithPopoverProps {
|
6
|
+
headerText: string;
|
7
|
+
popoverContent: ReactNode;
|
8
|
+
className?: string;
|
9
|
+
}
|
10
|
+
|
11
|
+
export const LabelWithPopover = ({
|
12
|
+
headerText,
|
13
|
+
popoverContent,
|
14
|
+
className,
|
15
|
+
}: LabelWithPopoverProps) => (
|
16
|
+
<div className={className}>
|
17
|
+
{headerText}
|
18
|
+
<HelpPopover content={popoverContent} />
|
19
|
+
</div>
|
20
|
+
);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './LabelWithPopover';
|
@@ -0,0 +1,205 @@
|
|
1
|
+
const STICK_START = 30;
|
2
|
+
const STICK_SPACE = 70;
|
3
|
+
const HEIGHT = 54;
|
4
|
+
const WIDTH = 268;
|
5
|
+
|
6
|
+
const READ_FILL = '#ADE8F5';
|
7
|
+
const WRITE_FILL = '#f5be9d';
|
8
|
+
|
9
|
+
interface DashArcProps {
|
10
|
+
width: number;
|
11
|
+
height: number;
|
12
|
+
transform?: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
const DashArc = ({width, height, transform}: DashArcProps) => (
|
16
|
+
<path
|
17
|
+
d={`M-${width / 2} 0 c0 -${height}, ${width} -${height}, ${width} 0`}
|
18
|
+
fill="none"
|
19
|
+
strokeDasharray="4,6"
|
20
|
+
stroke="#28f"
|
21
|
+
strokeWidth="1.6"
|
22
|
+
transform={transform}
|
23
|
+
/>
|
24
|
+
);
|
25
|
+
|
26
|
+
interface ArrowLineProps {
|
27
|
+
width: number;
|
28
|
+
}
|
29
|
+
|
30
|
+
const ArrowLine = ({width}: ArrowLineProps) => (
|
31
|
+
<path fill="none" strokeWidth="2" d={`M0 0 h${width} l-10 -5 m0 10 l10 -5`} />
|
32
|
+
);
|
33
|
+
|
34
|
+
const WriteLag = () => (
|
35
|
+
<g fill="var(--yc-color-text-primary)" fontSize="12">
|
36
|
+
<g transform={`translate(0, ${HEIGHT / 2})`} stroke={WRITE_FILL}>
|
37
|
+
<ArrowLine width={STICK_SPACE * 2.9} />
|
38
|
+
</g>
|
39
|
+
|
40
|
+
<g transform={`translate(${STICK_START}, 0)`}>
|
41
|
+
<g transform={`translate(${STICK_SPACE / 2}, ${HEIGHT / 2})`}>
|
42
|
+
<DashArc width={STICK_SPACE} height={15} />
|
43
|
+
<text x="0" y="-15" textAnchor="middle">
|
44
|
+
<tspan x="0" dy="0">
|
45
|
+
write lag
|
46
|
+
</tspan>
|
47
|
+
</text>
|
48
|
+
</g>
|
49
|
+
<g transform={`translate(${STICK_SPACE * 1.7}, ${HEIGHT / 2})`}>
|
50
|
+
<DashArc width={STICK_SPACE * 1.4} height={15} />
|
51
|
+
<text x="0" y="-15" textAnchor="middle">
|
52
|
+
<tspan x="0" dy="0">
|
53
|
+
write idle time
|
54
|
+
</tspan>
|
55
|
+
</text>
|
56
|
+
</g>
|
57
|
+
</g>
|
58
|
+
|
59
|
+
<g transform={`translate(${STICK_START}, 0)`}>
|
60
|
+
<g transform={`translate(${0}, ${HEIGHT / 2})`}>
|
61
|
+
<use y="-10" xlinkHref="#check" stroke={WRITE_FILL} />
|
62
|
+
<text x="0" y="20" textAnchor="middle">
|
63
|
+
<tspan x="0" dy="0">
|
64
|
+
create time
|
65
|
+
</tspan>
|
66
|
+
</text>
|
67
|
+
</g>
|
68
|
+
<g transform={`translate(${STICK_SPACE}, ${HEIGHT / 2})`}>
|
69
|
+
<use y="-10" xlinkHref="#check" stroke={WRITE_FILL} />
|
70
|
+
<text x="0" y="20" textAnchor="middle">
|
71
|
+
<tspan x="0" dy="0">
|
72
|
+
write time
|
73
|
+
</tspan>
|
74
|
+
</text>
|
75
|
+
</g>
|
76
|
+
<g transform={`translate(${2.4 * STICK_SPACE}, ${HEIGHT / 2})`}>
|
77
|
+
<text x="0" y="20" textAnchor="middle">
|
78
|
+
<tspan x="0" dy="0">
|
79
|
+
now
|
80
|
+
</tspan>
|
81
|
+
</text>
|
82
|
+
</g>
|
83
|
+
</g>
|
84
|
+
</g>
|
85
|
+
);
|
86
|
+
|
87
|
+
const ReadLag = () => (
|
88
|
+
<g fill="var(--yc-color-text-primary)" fontSize="12">
|
89
|
+
<g transform={`translate(0, ${HEIGHT / 2})`} stroke={READ_FILL}>
|
90
|
+
<ArrowLine width={WIDTH} />
|
91
|
+
</g>
|
92
|
+
|
93
|
+
<g transform={`translate(${STICK_START}, 0)`}>
|
94
|
+
<g transform={`translate(${STICK_SPACE * 1.5}, ${HEIGHT / 2})`}>
|
95
|
+
<DashArc width={STICK_SPACE} height={15} />
|
96
|
+
<text x="0" y="-15" textAnchor="middle">
|
97
|
+
<tspan x="0" dy="0">
|
98
|
+
read lag
|
99
|
+
</tspan>
|
100
|
+
</text>
|
101
|
+
</g>
|
102
|
+
<g transform={`translate(${STICK_SPACE / 2}, ${HEIGHT / 2})`}>
|
103
|
+
<DashArc width={STICK_SPACE} height={15} />
|
104
|
+
<text x="0" y="-15" textAnchor="middle">
|
105
|
+
<tspan x="0" dy="0">
|
106
|
+
write lag
|
107
|
+
</tspan>
|
108
|
+
</text>
|
109
|
+
</g>
|
110
|
+
<g transform={`translate(${STICK_SPACE * 2.6}, ${HEIGHT / 2})`}>
|
111
|
+
<DashArc width={STICK_SPACE * 1.3} height={15} />
|
112
|
+
<text x="0" y="-15" textAnchor="middle">
|
113
|
+
<tspan x="0" dy="0">
|
114
|
+
read idle time
|
115
|
+
</tspan>
|
116
|
+
</text>
|
117
|
+
</g>
|
118
|
+
</g>
|
119
|
+
|
120
|
+
<g transform={`translate(${STICK_START}, ${HEIGHT / 2})`}>
|
121
|
+
<g transform={`translate(${0}, 0)`}>
|
122
|
+
<use y="-10" xlinkHref="#check" stroke={READ_FILL} />
|
123
|
+
<text x="0" y="20" textAnchor="middle">
|
124
|
+
<tspan x="0" dy="0">
|
125
|
+
create time
|
126
|
+
</tspan>
|
127
|
+
</text>
|
128
|
+
</g>
|
129
|
+
<g transform={`translate(${STICK_SPACE}, 0)`}>
|
130
|
+
<use y="-10" xlinkHref="#check" stroke={READ_FILL} />
|
131
|
+
<text x="0" y="20" textAnchor="middle">
|
132
|
+
<tspan x="0" dy="0">
|
133
|
+
write time
|
134
|
+
</tspan>
|
135
|
+
</text>
|
136
|
+
</g>
|
137
|
+
<g transform={`translate(${2 * STICK_SPACE}, 0)`}>
|
138
|
+
<use x="-2" y="-10" xlinkHref="#check" stroke={READ_FILL} />
|
139
|
+
<text x="0" y="20" textAnchor="middle">
|
140
|
+
<tspan x="0" dy="0">
|
141
|
+
read time
|
142
|
+
</tspan>
|
143
|
+
</text>
|
144
|
+
</g>
|
145
|
+
<g transform={`translate(${3.2 * STICK_SPACE}, 0)`}>
|
146
|
+
<text x="0" y="20" textAnchor="middle">
|
147
|
+
<tspan x="0" dy="0">
|
148
|
+
now
|
149
|
+
</tspan>
|
150
|
+
</text>
|
151
|
+
</g>
|
152
|
+
</g>
|
153
|
+
</g>
|
154
|
+
);
|
155
|
+
|
156
|
+
interface DashPatternProps {
|
157
|
+
id: string;
|
158
|
+
fill: string;
|
159
|
+
}
|
160
|
+
|
161
|
+
const DashPattern = ({id, fill}: DashPatternProps) => (
|
162
|
+
<pattern id={id} x="0" y="0" width="8" height="8" patternUnits="userSpaceOnUse">
|
163
|
+
<path d="M0 5L5 0H8L0 8V5M5 8L8 5V8Z" fill={fill} />
|
164
|
+
</pattern>
|
165
|
+
);
|
166
|
+
|
167
|
+
export const WriteLagImage = () => (
|
168
|
+
<svg
|
169
|
+
className="paint"
|
170
|
+
xmlns="http://www.w3.org/2000/svg"
|
171
|
+
xmlnsXlink="http://www.w3.org/1999/xlink"
|
172
|
+
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
173
|
+
width={WIDTH}
|
174
|
+
height={HEIGHT}
|
175
|
+
>
|
176
|
+
<defs>
|
177
|
+
<g id="check">
|
178
|
+
<path d="M0 3 v14" strokeWidth="2" />
|
179
|
+
</g>
|
180
|
+
<DashPattern id="latest-read" fill={READ_FILL} />
|
181
|
+
<DashPattern id="latest-write" fill={WRITE_FILL} />
|
182
|
+
</defs>
|
183
|
+
<WriteLag />
|
184
|
+
</svg>
|
185
|
+
);
|
186
|
+
|
187
|
+
export const ReadLagImage = () => (
|
188
|
+
<svg
|
189
|
+
className="paint"
|
190
|
+
xmlns="http://www.w3.org/2000/svg"
|
191
|
+
xmlnsXlink="http://www.w3.org/1999/xlink"
|
192
|
+
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
193
|
+
width={WIDTH}
|
194
|
+
height={HEIGHT}
|
195
|
+
>
|
196
|
+
<defs>
|
197
|
+
<g id="check">
|
198
|
+
<path d="M0 3 v14" strokeWidth="2" />
|
199
|
+
</g>
|
200
|
+
<DashPattern id="latest-read" fill={READ_FILL} />
|
201
|
+
<DashPattern id="latest-write" fill={WRITE_FILL} />
|
202
|
+
</defs>
|
203
|
+
<ReadLag />
|
204
|
+
</svg>
|
205
|
+
);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './LagImages';
|
@@ -0,0 +1,92 @@
|
|
1
|
+
.speed-multimeter {
|
2
|
+
display: flex;
|
3
|
+
|
4
|
+
width: 100%;
|
5
|
+
|
6
|
+
&__content {
|
7
|
+
display: flex;
|
8
|
+
flex-grow: 1;
|
9
|
+
flex-direction: row;
|
10
|
+
justify-content: flex-end;
|
11
|
+
|
12
|
+
line-height: 22px;
|
13
|
+
}
|
14
|
+
|
15
|
+
&__displayed-value {
|
16
|
+
display: flex;
|
17
|
+
flex-direction: row;
|
18
|
+
justify-content: flex-end;
|
19
|
+
|
20
|
+
margin-right: 10px;
|
21
|
+
}
|
22
|
+
|
23
|
+
&__bars {
|
24
|
+
display: flex;
|
25
|
+
overflow: hidden;
|
26
|
+
flex-direction: column;
|
27
|
+
align-items: flex-start;
|
28
|
+
|
29
|
+
width: 32px;
|
30
|
+
margin-right: 5px;
|
31
|
+
}
|
32
|
+
|
33
|
+
&__bar-container {
|
34
|
+
width: 100%;
|
35
|
+
height: 6px;
|
36
|
+
|
37
|
+
&_highlighted {
|
38
|
+
background: var(--yc-color-line-generic);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
&__bar {
|
43
|
+
min-width: 2px;
|
44
|
+
height: 100%;
|
45
|
+
|
46
|
+
&_color_light {
|
47
|
+
background: var(--yc-color-infographics-info-medium);
|
48
|
+
}
|
49
|
+
|
50
|
+
&_color_dark {
|
51
|
+
background: var(--yc-color-infographics-info-heavy);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
&__bar-container + &__bar-container {
|
56
|
+
margin-top: 2px;
|
57
|
+
}
|
58
|
+
|
59
|
+
&__popover-container {
|
60
|
+
display: flex;
|
61
|
+
justify-content: center;
|
62
|
+
align-items: center;
|
63
|
+
}
|
64
|
+
|
65
|
+
&__popover-content {
|
66
|
+
padding: 10px;
|
67
|
+
}
|
68
|
+
|
69
|
+
&__popover-header {
|
70
|
+
display: block;
|
71
|
+
|
72
|
+
margin-bottom: 7px;
|
73
|
+
|
74
|
+
font-size: 18px;
|
75
|
+
line-height: 24px;
|
76
|
+
}
|
77
|
+
|
78
|
+
&__popover-row {
|
79
|
+
display: block;
|
80
|
+
|
81
|
+
font-size: 13px;
|
82
|
+
line-height: 18px;
|
83
|
+
|
84
|
+
&_color_primary {
|
85
|
+
color: var(--yc-color-text-primary);
|
86
|
+
}
|
87
|
+
|
88
|
+
&_color_secondary {
|
89
|
+
color: var(--yc-color-text-secondary);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
@@ -0,0 +1,120 @@
|
|
1
|
+
import {useState} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
|
+
import {Popover} from '@gravity-ui/uikit';
|
4
|
+
|
5
|
+
import {formatBytesCustom, IBytesSizes, IProcessSpeedStats} from '../../utils/bytesParsers';
|
6
|
+
|
7
|
+
import './SpeedMultiMeter.scss';
|
8
|
+
|
9
|
+
import i18n from './i18n';
|
10
|
+
|
11
|
+
const b = cn('speed-multimeter');
|
12
|
+
|
13
|
+
interface SpeedMultiMeterProps {
|
14
|
+
data?: IProcessSpeedStats;
|
15
|
+
speedSize?: IBytesSizes;
|
16
|
+
withValue?: boolean;
|
17
|
+
withPopover?: boolean;
|
18
|
+
}
|
19
|
+
|
20
|
+
export const SpeedMultiMeter = ({
|
21
|
+
data,
|
22
|
+
speedSize = 'kb',
|
23
|
+
withValue = true,
|
24
|
+
withPopover = true,
|
25
|
+
}: SpeedMultiMeterProps) => {
|
26
|
+
const {perMinute = 0, perHour = 0, perDay = 0} = data || {};
|
27
|
+
const rawValues = [perMinute, perHour, perDay];
|
28
|
+
|
29
|
+
const formatValue = (value: number) =>
|
30
|
+
formatBytesCustom({value: value, size: speedSize, isSpeed: true});
|
31
|
+
|
32
|
+
const formattedValues = [
|
33
|
+
{value: formatValue(perMinute), label: i18n('perMinute')},
|
34
|
+
{value: formatValue(perHour), label: i18n('perHour')},
|
35
|
+
{value: formatValue(perDay), label: i18n('perDay')},
|
36
|
+
];
|
37
|
+
|
38
|
+
const [valueToDisplay, setValueToDisplay] = useState(perMinute);
|
39
|
+
const [highlightedValueIndex, setHighlightedValueIndex] = useState(withValue ? 0 : undefined);
|
40
|
+
const [highlightedContainerIndex, setHighlightedContainerIndex] = useState<
|
41
|
+
number | undefined
|
42
|
+
>();
|
43
|
+
|
44
|
+
const onEnterDiagram = (values: number[], index: number) => {
|
45
|
+
setValueToDisplay(values[index]);
|
46
|
+
setHighlightedValueIndex(index);
|
47
|
+
setHighlightedContainerIndex(index);
|
48
|
+
};
|
49
|
+
|
50
|
+
const onLeaveDiagram = () => {
|
51
|
+
setValueToDisplay(perMinute);
|
52
|
+
setHighlightedValueIndex(withValue ? 0 : undefined);
|
53
|
+
setHighlightedContainerIndex(undefined);
|
54
|
+
};
|
55
|
+
|
56
|
+
const isValueHighlighted = (index: number) => highlightedValueIndex === index;
|
57
|
+
const isContainerHighlighted = (index: number) => highlightedContainerIndex === index;
|
58
|
+
|
59
|
+
const getModifier = (flag: boolean) => (flag ? {color: 'primary'} : {color: 'secondary'});
|
60
|
+
|
61
|
+
const renderValues = () => {
|
62
|
+
const max = Math.max(...rawValues, 0) || 1;
|
63
|
+
|
64
|
+
return rawValues.map((value, index) => (
|
65
|
+
<div
|
66
|
+
key={index}
|
67
|
+
className={b('bar-container', {
|
68
|
+
highlighted: isContainerHighlighted(index),
|
69
|
+
})}
|
70
|
+
onMouseEnter={onEnterDiagram.bind(null, rawValues, index)}
|
71
|
+
>
|
72
|
+
<div
|
73
|
+
className={b('bar', {
|
74
|
+
color: isValueHighlighted(index) ? 'dark' : 'light',
|
75
|
+
})}
|
76
|
+
style={{width: `${(100 * value) / max}%`}}
|
77
|
+
/>
|
78
|
+
</div>
|
79
|
+
));
|
80
|
+
};
|
81
|
+
|
82
|
+
const renderPopoverContent = () => {
|
83
|
+
return (
|
84
|
+
<div className={b('popover-content')}>
|
85
|
+
<span className={b('popover-header')}>{i18n('averageSpeed')}</span>
|
86
|
+
{formattedValues.map((formattedValue, index) => (
|
87
|
+
<span
|
88
|
+
key={index}
|
89
|
+
className={b('popover-row', getModifier(isValueHighlighted(index)))}
|
90
|
+
>
|
91
|
+
{`${formattedValue.label}: ${formattedValue.value}`}
|
92
|
+
</span>
|
93
|
+
))}
|
94
|
+
</div>
|
95
|
+
);
|
96
|
+
};
|
97
|
+
|
98
|
+
return (
|
99
|
+
<div className={b()}>
|
100
|
+
<div className={b('content')}>
|
101
|
+
{withValue && (
|
102
|
+
<div className={b('displayed-value')}>{formatValue(valueToDisplay)}</div>
|
103
|
+
)}
|
104
|
+
<div className={b('popover-container')}>
|
105
|
+
<Popover
|
106
|
+
content={renderPopoverContent()}
|
107
|
+
placement={'bottom'}
|
108
|
+
disabled={!withPopover}
|
109
|
+
hasArrow={true}
|
110
|
+
size="s"
|
111
|
+
>
|
112
|
+
<div className={b('bars')} onMouseLeave={onLeaveDiagram}>
|
113
|
+
{renderValues()}
|
114
|
+
</div>
|
115
|
+
</Popover>
|
116
|
+
</div>
|
117
|
+
</div>
|
118
|
+
</div>
|
119
|
+
);
|
120
|
+
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import {i18n, Lang} from '../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-components-speed-multimeter';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
12
|
+
|
13
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './SpeedMultiMeter';
|
@@ -16,7 +16,7 @@ import {VisibleEntities} from '../../../store/reducers/storage';
|
|
16
16
|
import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
|
17
17
|
//@ts-ignore
|
18
18
|
import {stringifyVdiskId} from '../../../utils';
|
19
|
-
import {getUsage,
|
19
|
+
import {getUsage, isFullVDiksData} from '../../../utils/storage';
|
20
20
|
|
21
21
|
import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
|
22
22
|
import {VDisk} from '../VDisk';
|
@@ -28,6 +28,7 @@ import './StorageGroups.scss';
|
|
28
28
|
enum TableColumnsIds {
|
29
29
|
PoolName = 'PoolName',
|
30
30
|
Type = 'Type',
|
31
|
+
ErasureSpecies = 'ErasureSpecies',
|
31
32
|
GroupID = 'GroupID',
|
32
33
|
Used = 'Used',
|
33
34
|
Limit = 'Limit',
|
@@ -53,6 +54,7 @@ interface StorageGroupsProps {
|
|
53
54
|
const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
|
54
55
|
PoolName: 'Pool Name',
|
55
56
|
Type: 'Type',
|
57
|
+
ErasureSpecies: 'Erasure',
|
56
58
|
GroupID: 'Group ID',
|
57
59
|
Used: 'Used',
|
58
60
|
Limit: 'Limit',
|
@@ -146,6 +148,12 @@ function StorageGroups({
|
|
146
148
|
</>
|
147
149
|
),
|
148
150
|
},
|
151
|
+
{
|
152
|
+
name: TableColumnsIds.ErasureSpecies,
|
153
|
+
header: tableColumnsNames[TableColumnsIds.ErasureSpecies],
|
154
|
+
render: ({row}) => (row.ErasureSpecies ? row.ErasureSpecies : '-'),
|
155
|
+
align: DataTable.LEFT,
|
156
|
+
},
|
149
157
|
{
|
150
158
|
name: TableColumnsIds.Missing,
|
151
159
|
header: tableColumnsNames[TableColumnsIds.Missing],
|
@@ -249,26 +257,30 @@ function StorageGroups({
|
|
249
257
|
render: ({value, row}) => (
|
250
258
|
<div className={b('vdisks-wrapper')}>
|
251
259
|
{_.map(value as TVDiskStateInfo[], (el) => {
|
252
|
-
const donors =
|
253
|
-
? el.Donors.filter(isFullDonorData)
|
254
|
-
: [];
|
260
|
+
const donors = el.Donors;
|
255
261
|
|
256
|
-
return donors.length > 0 ? (
|
262
|
+
return donors && donors.length > 0 ? (
|
257
263
|
<Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
|
258
264
|
<VDisk
|
259
265
|
data={el}
|
260
266
|
poolName={row[TableColumnsIds.PoolName]}
|
261
267
|
nodes={nodes}
|
262
268
|
/>
|
263
|
-
{donors.map((donor) =>
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
269
|
+
{donors.map((donor) => {
|
270
|
+
const isFullData = isFullVDiksData(donor);
|
271
|
+
|
272
|
+
return (
|
273
|
+
<VDisk
|
274
|
+
data={isFullData ? donor : {...donor, DonorMode: true}}
|
275
|
+
// donor and acceptor are always in the same group
|
276
|
+
poolName={row[TableColumnsIds.PoolName]}
|
277
|
+
nodes={nodes}
|
278
|
+
key={stringifyVdiskId(
|
279
|
+
isFullData ? donor.VDiskId : donor,
|
280
|
+
)}
|
281
|
+
/>
|
282
|
+
);
|
283
|
+
})}
|
272
284
|
</Stack>
|
273
285
|
) : (
|
274
286
|
<div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
|