ydb-embedded-ui 3.3.3 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/components/Errors/ResponseError/ResponseError.tsx +2 -2
  3. package/dist/components/InfoViewer/formatters/topicStats.tsx +8 -29
  4. package/dist/components/LabelWithPopover/LabelWithPopover.tsx +20 -0
  5. package/dist/components/LabelWithPopover/index.ts +1 -0
  6. package/dist/components/LagImages/LagImages.tsx +205 -0
  7. package/dist/components/LagImages/index.ts +1 -0
  8. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.scss +92 -0
  9. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +120 -0
  10. package/dist/components/SpeedMultiMeter/i18n/en.json +6 -0
  11. package/dist/components/SpeedMultiMeter/i18n/index.ts +13 -0
  12. package/dist/components/SpeedMultiMeter/i18n/ru.json +6 -0
  13. package/dist/components/SpeedMultiMeter/index.ts +1 -0
  14. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +26 -14
  15. package/dist/containers/Storage/VDisk/VDisk.tsx +20 -5
  16. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +34 -5
  17. package/dist/containers/Storage/utils/types.ts +5 -0
  18. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +32 -3
  19. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +62 -69
  20. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.scss +13 -0
  21. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.tsx +27 -0
  22. package/dist/containers/Tenant/Diagnostics/Consumers/Headers/index.ts +1 -0
  23. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.scss +32 -0
  24. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +43 -0
  25. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/index.ts +1 -0
  26. package/dist/containers/Tenant/Diagnostics/Consumers/columns/Columns.scss +5 -0
  27. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +66 -0
  28. package/dist/containers/Tenant/Diagnostics/Consumers/columns/index.ts +1 -0
  29. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +4 -3
  30. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +4 -3
  31. package/dist/containers/Tenant/Diagnostics/Consumers/utils/constants.ts +23 -0
  32. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +7 -3
  33. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -9
  34. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +9 -1
  35. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +6 -8
  36. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.scss +33 -0
  37. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.tsx +76 -0
  38. package/dist/containers/Tenant/Diagnostics/Partitions/Headers/index.ts +1 -0
  39. package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.scss +45 -0
  40. package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +254 -0
  41. package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +79 -0
  42. package/dist/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss +13 -0
  43. package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +246 -0
  44. package/dist/containers/Tenant/Diagnostics/Partitions/columns/index.ts +1 -0
  45. package/dist/containers/Tenant/Diagnostics/Partitions/i18n/en.json +13 -0
  46. package/dist/containers/Tenant/Diagnostics/{OverloadedShards → Partitions}/i18n/index.ts +1 -1
  47. package/dist/containers/Tenant/Diagnostics/Partitions/i18n/ru.json +13 -0
  48. package/dist/containers/Tenant/Diagnostics/Partitions/index.ts +1 -0
  49. package/dist/containers/Tenant/Diagnostics/Partitions/utils/constants.ts +74 -0
  50. package/dist/containers/Tenant/Diagnostics/Partitions/utils/types.ts +6 -0
  51. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss +8 -0
  52. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +56 -0
  53. package/dist/containers/Tenant/Diagnostics/TopShards/Filters/index.ts +1 -0
  54. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.scss → TopShards/TopShards.scss} +2 -10
  55. package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.tsx → TopShards/TopShards.tsx} +61 -31
  56. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +6 -0
  57. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/index.ts +11 -0
  58. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +6 -0
  59. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
  60. package/dist/containers/Tenant/utils/schema.ts +1 -16
  61. package/dist/services/api.d.ts +4 -0
  62. package/dist/services/api.js +22 -6
  63. package/dist/store/reducers/authentication.js +0 -15
  64. package/dist/store/reducers/consumer.ts +160 -0
  65. package/dist/store/reducers/index.ts +2 -0
  66. package/dist/store/reducers/settings.js +2 -0
  67. package/dist/store/reducers/shardsWorkload.ts +28 -2
  68. package/dist/store/reducers/topic.ts +82 -2
  69. package/dist/store/state-url-mapping.js +3 -0
  70. package/dist/types/store/consumer.ts +55 -0
  71. package/dist/types/store/shardsWorkload.ts +6 -0
  72. package/dist/types/store/topic.ts +23 -6
  73. package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +24 -0
  74. package/dist/utils/bytesParsers/formatBytesCustom.ts +57 -0
  75. package/dist/utils/bytesParsers/i18n/en.json +7 -0
  76. package/dist/utils/bytesParsers/i18n/index.ts +11 -0
  77. package/dist/utils/bytesParsers/i18n/ru.json +7 -0
  78. package/dist/utils/bytesParsers/index.ts +2 -0
  79. package/dist/utils/constants.ts +3 -0
  80. package/dist/utils/index.js +6 -0
  81. package/dist/utils/storage.ts +2 -2
  82. package/dist/utils/timeParsers/index.ts +2 -1
  83. package/dist/utils/timeParsers/parsers.ts +18 -0
  84. package/dist/utils/timeParsers/{protobuf.ts → protobufParsers.ts} +0 -0
  85. package/dist/utils/typecheckers.ts +5 -0
  86. package/dist/utils/utils.js +3 -3
  87. package/package.json +2 -2
  88. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +0 -4
  89. package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +0 -4
  90. 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: IResponseError;
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.statusText || defaultMessage}</div>;
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
- parseProtobufTimestampToMs,
8
- parseProtobufDurationToMs,
4
+ parseLag,
5
+ parseTimestampToIdleTime,
9
6
  formatDurationToShortTimeFormat,
10
7
  } from '../../../utils/timeParsers';
8
+ import {convertBytesObjectToSpeed} from '../../../utils/bytesParsers';
11
9
 
12
- import {VerticalBars} from '../../VerticalBars/VerticalBars';
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
- if (!value) {
30
- return formatDurationToShortTimeFormat(0);
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 && <VerticalBars values={Object.values(prepareBytesWritten(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,6 @@
1
+ {
2
+ "averageSpeed": "Average speed",
3
+ "perMinute": "per minute",
4
+ "perHour": "per hour",
5
+ "perDay": "per day"
6
+ }
@@ -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,6 @@
1
+ {
2
+ "averageSpeed": "Средняя скорость",
3
+ "perMinute": "за минуту",
4
+ "perHour": "за час",
5
+ "perDay": "за день"
6
+ }
@@ -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, isFullDonorData} from '../../../utils/storage';
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 = Array.isArray(el.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
- <VDisk
265
- data={donor}
266
- // donor and acceptor are always in the same group
267
- poolName={row[TableColumnsIds.PoolName]}
268
- nodes={nodes}
269
- key={stringifyVdiskId(donor.VDiskId)}
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)}>