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.
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
@@ -7,6 +7,7 @@ import routes, {createHref} from '../../../routes';
7
7
  import {EFlag} from '../../../types/api/enums';
8
8
  import {EVDiskState, TVDiskStateInfo} from '../../../types/api/vdisk';
9
9
  import {stringifyVdiskId} from '../../../utils';
10
+ import {isFullVDiksData} from '../../../utils/storage';
10
11
 
11
12
  import {STRUCTURE} from '../../Node/NodePages';
12
13
 
@@ -14,6 +15,7 @@ import {DiskStateProgressBar, EDiskStateSeverity} from '../DiskStateProgressBar'
14
15
  import type {NodesHosts} from '../PDiskPopup';
15
16
  import {VDiskPopup} from '../VDiskPopup';
16
17
 
18
+ import type {IUnavailableDonor} from '../utils/types';
17
19
  import {NOT_AVAILABLE_SEVERITY} from '../utils';
18
20
 
19
21
  import './VDisk.scss';
@@ -46,20 +48,29 @@ const getColorSeverity = (color?: EFlag) => {
46
48
  };
47
49
 
48
50
  interface VDiskProps {
49
- data?: TVDiskStateInfo;
51
+ data?: TVDiskStateInfo | IUnavailableDonor;
50
52
  poolName?: string;
51
53
  nodes?: NodesHosts;
52
54
  compact?: boolean;
53
55
  }
54
56
 
55
57
  export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
56
- const [severity, setSeverity] = useState(getStateSeverity(data.VDiskState));
58
+ const isFullData = isFullVDiksData(data);
59
+
60
+ const [severity, setSeverity] = useState(
61
+ getStateSeverity(isFullData ? data.VDiskState : undefined),
62
+ );
57
63
  const [isPopupVisible, setIsPopupVisible] = useState(false);
58
64
 
59
65
  const anchor = useRef(null);
60
66
 
61
67
  // determine disk status severity
62
68
  useEffect(() => {
69
+ if (!isFullData) {
70
+ setSeverity(NOT_AVAILABLE_SEVERITY);
71
+ return;
72
+ }
73
+
63
74
  const {DiskSpace, VDiskState, FrontQueues, Replicated, DonorMode} = data;
64
75
 
65
76
  // if the disk is not available, this determines its status severity regardless of other features
@@ -84,7 +95,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
84
95
  }
85
96
 
86
97
  setSeverity(newSeverity);
87
- }, [data]);
98
+ }, [data, isFullData]);
88
99
 
89
100
  const showPopup = () => {
90
101
  setIsPopupVisible(true);
@@ -95,6 +106,10 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
95
106
  };
96
107
 
97
108
  const vdiskAllocatedPercent = useMemo(() => {
109
+ if (!isFullData) {
110
+ return undefined;
111
+ }
112
+
98
113
  const {AvailableSize, AllocatedSize, PDisk} = data;
99
114
  const available = AvailableSize ? AvailableSize : PDisk?.AvailableSize;
100
115
 
@@ -105,7 +120,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
105
120
  return isNaN(Number(AllocatedSize))
106
121
  ? undefined
107
122
  : (Number(AllocatedSize) * 100) / (Number(available) + Number(AllocatedSize));
108
- }, [data]);
123
+ }, [data, isFullData]);
109
124
 
110
125
  return (
111
126
  <React.Fragment>
@@ -117,7 +132,7 @@ export const VDisk = ({data = {}, poolName, nodes, compact}: VDiskProps) => {
117
132
  open={isPopupVisible}
118
133
  />
119
134
  <div className={b()} ref={anchor} onMouseEnter={showPopup} onMouseLeave={hidePopup}>
120
- {data.NodeId ? (
135
+ {data.NodeId && isFullData ? (
121
136
  <InternalLink
122
137
  to={createHref(
123
138
  routes.node,
@@ -6,9 +6,12 @@ import {Label, Popup, PopupProps} from '@gravity-ui/uikit';
6
6
  import {InfoViewer, InfoViewerItem} from '../../../components/InfoViewer';
7
7
 
8
8
  import {EFlag} from '../../../types/api/enums';
9
- import {TVDiskStateInfo} from '../../../types/api/vdisk';
9
+ import type {TVDiskStateInfo} from '../../../types/api/vdisk';
10
10
  import {stringifyVdiskId} from '../../../utils';
11
11
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
12
+ import {isFullVDiksData} from '../../../utils/storage';
13
+
14
+ import type {IUnavailableDonor} from '../utils/types';
12
15
 
13
16
  import {NodesHosts, preparePDiskData} from '../PDiskPopup';
14
17
 
@@ -16,6 +19,24 @@ import './VDiskPopup.scss';
16
19
 
17
20
  const b = cn('vdisk-storage-popup');
18
21
 
22
+ const prepareUnavailableVDiskData = (data: IUnavailableDonor, poolName?: string) => {
23
+ const {NodeId, PDiskId, VSlotId} = data;
24
+
25
+ const vdiskData: InfoViewerItem[] = [{label: 'State', value: 'not available'}];
26
+
27
+ if (poolName) {
28
+ vdiskData.push({label: 'StoragePool', value: poolName});
29
+ }
30
+
31
+ vdiskData.push(
32
+ {label: 'NodeId', value: NodeId ?? '–'},
33
+ {label: 'PDiskId', value: PDiskId ?? '–'},
34
+ {label: 'VSlotId', value: VSlotId ?? '–'},
35
+ );
36
+
37
+ return vdiskData;
38
+ };
39
+
19
40
  const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
20
41
  const {
21
42
  VDiskId,
@@ -105,16 +126,24 @@ const prepareVDiskData = (data: TVDiskStateInfo, poolName?: string) => {
105
126
  };
106
127
 
107
128
  interface VDiskPopupProps extends PopupProps {
108
- data: TVDiskStateInfo;
129
+ data: TVDiskStateInfo | IUnavailableDonor;
109
130
  poolName?: string;
110
131
  nodes?: NodesHosts;
111
132
  }
112
133
 
113
134
  export const VDiskPopup = ({data, poolName, nodes, ...props}: VDiskPopupProps) => {
114
- const vdiskInfo = useMemo(() => prepareVDiskData(data, poolName), [data, poolName]);
135
+ const isFullData = isFullVDiksData(data);
136
+
137
+ const vdiskInfo = useMemo(
138
+ () =>
139
+ isFullData
140
+ ? prepareVDiskData(data, poolName)
141
+ : prepareUnavailableVDiskData(data, poolName),
142
+ [data, poolName, isFullData],
143
+ );
115
144
  const pdiskInfo = useMemo(
116
- () => data.PDisk && preparePDiskData(data.PDisk, nodes),
117
- [data.PDisk, nodes],
145
+ () => isFullData && data.PDisk && preparePDiskData(data.PDisk, nodes),
146
+ [data, nodes, isFullData],
118
147
  );
119
148
 
120
149
  return (
@@ -0,0 +1,5 @@
1
+ import {TVSlotId} from '../../../types/api/vdisk';
2
+
3
+ export interface IUnavailableDonor extends TVSlotId {
4
+ DonorMode?: boolean;
5
+ }
@@ -1,6 +1,35 @@
1
- .ydb-consumers {
1
+ @import '../../../../styles/mixins.scss';
2
+
3
+ .ydb-diagnostics-consumers {
4
+ overflow: auto;
5
+ flex-grow: 1;
6
+
7
+ height: 100%;
8
+
9
+ @include flex-container();
10
+
11
+ &__controls {
12
+ @include controls();
13
+ }
14
+
2
15
  &__search {
3
- width: 200px;
4
- margin-bottom: 20px;
16
+ @include search();
17
+ }
18
+
19
+ &__table-settings .yc-icon {
20
+ width: 20px;
21
+ }
22
+
23
+ &__table-wrapper {
24
+ overflow: auto;
25
+ @include flex-container();
26
+ }
27
+
28
+ &__table-content {
29
+ overflow: auto;
30
+
31
+ height: 100%;
32
+
33
+ @include freeze-nth-column(1);
5
34
  }
6
35
  }
@@ -1,31 +1,36 @@
1
- import {useCallback, useEffect, useState} from 'react';
1
+ import {useCallback, useMemo, useState} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import block from 'bem-cn-lite';
4
4
  import {escapeRegExp} from 'lodash/fp';
5
5
 
6
- import DataTable, {Column} from '@gravity-ui/react-data-table';
6
+ import DataTable from '@gravity-ui/react-data-table';
7
7
 
8
8
  import type {EPathType} from '../../../../types/api/schema';
9
+
9
10
  import {Loader} from '../../../../components/Loader';
10
- import {prepareQueryError} from '../../../../utils/query';
11
- import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
12
- import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
13
11
  import {Search} from '../../../../components/Search';
12
+ import {ResponseError} from '../../../../components/Errors/ResponseError';
13
+
14
+ import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
15
+ import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
16
+
14
17
  import {
15
- getDescribe,
16
- selectConsumers,
17
- setCurrentDescribePath,
18
+ selectPreparedConsumersData,
19
+ selectPreparedTopicStats,
20
+ getTopic,
18
21
  setDataWasNotLoaded,
19
- } from '../../../../store/reducers/describe';
20
- import {selectSchemaMergedChildrenPaths} from '../../../../store/reducers/schema';
22
+ } from '../../../../store/reducers/topic';
21
23
 
22
24
  import {isCdcStreamEntityType} from '../../utils/schema';
23
25
 
26
+ import {ConsumersTopicStats} from './TopicStats';
27
+ import {columns} from './columns';
28
+
24
29
  import i18n from './i18n';
25
30
 
26
31
  import './Consumers.scss';
27
32
 
28
- const b = block('ydb-consumers');
33
+ const b = block('ydb-diagnostics-consumers');
29
34
 
30
35
  interface ConsumersProps {
31
36
  path: string;
@@ -33,93 +38,81 @@ interface ConsumersProps {
33
38
  }
34
39
 
35
40
  export const Consumers = ({path, type}: ConsumersProps) => {
41
+ const isCdcStream = isCdcStreamEntityType(type);
42
+
36
43
  const dispatch = useDispatch();
37
44
 
38
- const isCdcStream = isCdcStreamEntityType(type);
45
+ const [searchValue, setSearchValue] = useState('');
39
46
 
40
- const mergedChildrenPaths = useTypedSelector((state) =>
41
- selectSchemaMergedChildrenPaths(state, path, type),
42
- );
47
+ const {autorefresh} = useTypedSelector((state) => state.schema);
48
+ const {loading, wasLoaded, error} = useTypedSelector((state) => state.topic);
43
49
 
44
- const dataPath = isCdcStream ? mergedChildrenPaths?.[0] : path;
50
+ const consumers = useTypedSelector((state) => selectPreparedConsumersData(state));
51
+ const topic = useTypedSelector((state) => selectPreparedTopicStats(state));
45
52
 
46
53
  const fetchData = useCallback(
47
- (isBackground: boolean) => {
54
+ (isBackground) => {
48
55
  if (!isBackground) {
49
- dispatch(setDataWasNotLoaded());
56
+ dispatch(setDataWasNotLoaded);
50
57
  }
51
58
 
52
- if (dataPath) {
53
- dispatch(setCurrentDescribePath(dataPath));
54
- dispatch(getDescribe({path: dataPath}));
55
- }
59
+ dispatch(getTopic(path));
56
60
  },
57
-
58
- [dispatch, dataPath],
61
+ [dispatch, path],
59
62
  );
60
63
 
61
- const {autorefresh} = useTypedSelector((state) => state.schema);
62
-
63
64
  useAutofetcher(fetchData, [fetchData], autorefresh);
64
65
 
65
- const {loading, wasLoaded, error} = useTypedSelector((state) => state.describe);
66
- const consumers = useTypedSelector((state) => selectConsumers(state, dataPath));
66
+ const dataToRender = useMemo(() => {
67
+ if (!consumers) {
68
+ return [];
69
+ }
67
70
 
68
- const [consumersToRender, setConsumersToRender] = useState(consumers);
71
+ const searchRe = new RegExp(escapeRegExp(searchValue), 'i');
69
72
 
70
- useEffect(() => {
71
- setConsumersToRender(consumers);
72
- }, [consumers]);
73
+ return consumers.filter((consumer) => {
74
+ return searchRe.test(String(consumer.name));
75
+ });
76
+ }, [consumers, searchValue]);
73
77
 
74
- const filterConsumersByName = (search: string) => {
75
- const filteredConsumers = search
76
- ? consumers.filter((consumer) => {
77
- const re = new RegExp(escapeRegExp(search), 'i');
78
- return re.test(consumer.name);
79
- })
80
- : consumers;
81
-
82
- setConsumersToRender(filteredConsumers);
83
- };
84
-
85
- const handleSearch = (value: string) => {
86
- filterConsumersByName(value);
78
+ const handleSearchChange = (value: string) => {
79
+ setSearchValue(value);
87
80
  };
88
81
 
89
- const columns: Column<any>[] = [
90
- {
91
- name: 'name',
92
- header: i18n('table.columns.consumerName'),
93
- width: 200,
94
- },
95
- ];
96
-
97
82
  if (loading && !wasLoaded) {
98
83
  return <Loader size="m" />;
99
84
  }
100
85
 
101
- if (!loading && error) {
102
- return <div className={b('message', 'error')}>{prepareQueryError(error)}</div>;
86
+ if (error) {
87
+ return <ResponseError error={error} />;
103
88
  }
104
89
 
105
- if (consumers.length === 0) {
106
- return <div className={b('message')}>{i18n('noConsumersMessage')}</div>;
90
+ if (!consumers || !consumers.length) {
91
+ return <div>{i18n(`noConsumersMessage.${isCdcStream ? 'stream' : 'topic'}`)}</div>;
107
92
  }
108
93
 
109
94
  return (
110
95
  <div className={b()}>
111
- <Search
112
- onChange={handleSearch}
113
- placeholder={i18n('search.placeholder')}
114
- className={b('search')}
115
- />
116
- <DataTable
117
- theme="yandex-cloud"
118
- settings={DEFAULT_TABLE_SETTINGS}
119
- columns={columns}
120
- data={consumersToRender}
121
- emptyDataMessage={i18n('table.emptyDataMessage')}
122
- />
96
+ <div className={b('controls')}>
97
+ <Search
98
+ onChange={handleSearchChange}
99
+ placeholder={i18n('controls.search')}
100
+ className={b('search')}
101
+ value={searchValue}
102
+ />
103
+ {topic && <ConsumersTopicStats data={topic} />}
104
+ </div>
105
+ <div className={b('table-wrapper')}>
106
+ <div className={b('table-content')}>
107
+ <DataTable
108
+ theme="yandex-cloud"
109
+ data={dataToRender}
110
+ columns={columns}
111
+ settings={DEFAULT_TABLE_SETTINGS}
112
+ emptyDataMessage={i18n('table.emptyDataMessage')}
113
+ />
114
+ </div>
115
+ </div>
123
116
  </div>
124
117
  );
125
118
  };
@@ -0,0 +1,13 @@
1
+ .ydb-diagnostics-consumers-columns-header {
2
+ &__lags {
3
+ white-space: nowrap;
4
+ }
5
+
6
+ &__lags-popover-content {
7
+ max-width: 300px;
8
+
9
+ div:nth-child(1) {
10
+ margin-bottom: 10px;
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,27 @@
1
+ import block from 'bem-cn-lite';
2
+
3
+ import {ReadLagImage} from '../../../../../components/LagImages';
4
+ import {LabelWithPopover} from '../../../../../components/LabelWithPopover';
5
+
6
+ import {CONSUMERS_COLUMNS_IDS, CONSUMERS_COLUMNS_TITILES} from '../utils/constants';
7
+
8
+ import i18n from '../i18n';
9
+
10
+ import './Headers.scss';
11
+
12
+ const b = block('ydb-diagnostics-consumers-columns-header');
13
+
14
+ export const ReadLagsHeader = () => (
15
+ <LabelWithPopover
16
+ className={b('lags')}
17
+ headerText={CONSUMERS_COLUMNS_TITILES[CONSUMERS_COLUMNS_IDS.READ_LAGS]}
18
+ popoverContent={
19
+ <div className={b('lags-popover-content')}>
20
+ <div>{i18n('lagsPopover.readLags')}</div>
21
+ <div>
22
+ <ReadLagImage />
23
+ </div>
24
+ </div>
25
+ }
26
+ />
27
+ );
@@ -0,0 +1 @@
1
+ export * from './Headers';
@@ -0,0 +1,32 @@
1
+ .ydb-diagnostics-consumers-topic-stats {
2
+ font-size: var(--yc-text-body-2-font-size);
3
+ line-height: var(--yc-text-body-2-line-height);
4
+
5
+ &__wrapper {
6
+ display: flex;
7
+ flex-direction: row;
8
+
9
+ padding-left: 16px;
10
+
11
+ border-left: 1px solid var(--yc-color-line-generic);
12
+ }
13
+
14
+ &__item {
15
+ display: flex;
16
+ flex-direction: column;
17
+
18
+ margin-right: 20px;
19
+ }
20
+ &__label {
21
+ margin-bottom: 4px;
22
+
23
+ color: var(--yc-color-text-secondary);
24
+ }
25
+ &__value {
26
+ display: flex;
27
+ justify-content: flex-end;
28
+ align-items: center;
29
+
30
+ height: 30px;
31
+ }
32
+ }
@@ -0,0 +1,43 @@
1
+ import block from 'bem-cn-lite';
2
+
3
+ import type {IPreparedTopicStats} from '../../../../../types/store/topic';
4
+ import {formatMsToUptime} from '../../../../../utils';
5
+ import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
6
+
7
+ import './ConsumersTopicStats.scss';
8
+
9
+ const b = block('ydb-diagnostics-consumers-topic-stats');
10
+
11
+ interface ConsumersTopicStatsProps {
12
+ data?: IPreparedTopicStats;
13
+ }
14
+
15
+ export const ConsumersTopicStats = ({data}: ConsumersTopicStatsProps) => {
16
+ const {writeSpeed, partitionsWriteLag, partitionsIdleTime} = data || {};
17
+
18
+ const values = [
19
+ {
20
+ label: 'Write speed',
21
+ value: <SpeedMultiMeter data={writeSpeed} />,
22
+ },
23
+ {
24
+ label: 'Write lag',
25
+ value: formatMsToUptime(partitionsWriteLag || 0),
26
+ },
27
+ {
28
+ label: 'Write idle time',
29
+ value: formatMsToUptime(partitionsIdleTime || 0),
30
+ },
31
+ ];
32
+
33
+ return (
34
+ <div className={b('wrapper')}>
35
+ {values.map((value, index) => (
36
+ <div key={index} className={b('item')}>
37
+ <div className={b('label')}>{value.label}</div>
38
+ <div className={b('value')}>{value.value}</div>
39
+ </div>
40
+ ))}
41
+ </div>
42
+ );
43
+ };
@@ -0,0 +1 @@
1
+ export * from './ConsumersTopicStats';
@@ -0,0 +1,5 @@
1
+ .ydb-diagnostics-consumers-columns {
2
+ &__lags-header {
3
+ text-align: center;
4
+ }
5
+ }
@@ -0,0 +1,66 @@
1
+ import DataTable, {Column} from '@gravity-ui/react-data-table';
2
+ import block from 'bem-cn-lite';
3
+
4
+ import type {IPreparedConsumerData} from '../../../../../types/store/topic';
5
+ import {SpeedMultiMeter} from '../../../../../components/SpeedMultiMeter';
6
+ import {formatMsToUptime} from '../../../../../utils';
7
+
8
+ import {
9
+ CONSUMERS_COLUMNS_IDS,
10
+ CONSUMERS_COLUMNS_TITILES,
11
+ CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS,
12
+ CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES,
13
+ } from '../utils/constants';
14
+
15
+ import {ReadLagsHeader} from '../Headers';
16
+
17
+ import './Columns.scss';
18
+
19
+ const b = block('ydb-diagnostics-consumers-columns');
20
+
21
+ export const columns: Column<IPreparedConsumerData>[] = [
22
+ {
23
+ name: CONSUMERS_COLUMNS_IDS.CONSUMER,
24
+ header: CONSUMERS_COLUMNS_TITILES[CONSUMERS_COLUMNS_IDS.CONSUMER],
25
+ align: DataTable.LEFT,
26
+ render: ({row}) => row.name || '-',
27
+ },
28
+ {
29
+ name: CONSUMERS_COLUMNS_IDS.READ_SPEED,
30
+ header: CONSUMERS_COLUMNS_TITILES[CONSUMERS_COLUMNS_IDS.READ_SPEED],
31
+ align: DataTable.RIGHT,
32
+ sortAccessor: (row) => row.readSpeed.perMinute,
33
+ render: ({row}) => <SpeedMultiMeter data={row.readSpeed} />,
34
+ },
35
+ {
36
+ name: CONSUMERS_COLUMNS_IDS.READ_LAGS,
37
+ header: <ReadLagsHeader />,
38
+ className: b('lags-header'),
39
+ sub: [
40
+ {
41
+ name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.WRITE_LAG,
42
+ header: CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES[
43
+ CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.WRITE_LAG
44
+ ],
45
+ align: DataTable.RIGHT,
46
+ render: ({row}) => formatMsToUptime(row.writeLag),
47
+ },
48
+ {
49
+ name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG,
50
+ header: CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES[
51
+ CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG
52
+ ],
53
+ align: DataTable.RIGHT,
54
+ render: ({row}) => formatMsToUptime(row.readLag),
55
+ },
56
+ {
57
+ name: CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME,
58
+ header: CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES[
59
+ CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME
60
+ ],
61
+ align: DataTable.RIGHT,
62
+ render: ({row}) => formatMsToUptime(row.readIdleTime),
63
+ },
64
+ ],
65
+ },
66
+ ];
@@ -0,0 +1 @@
1
+ export * from './columns';
@@ -1,6 +1,7 @@
1
1
  {
2
- "search.placeholder": "Consumer name",
2
+ "noConsumersMessage.topic": "This topic has no consumers",
3
+ "noConsumersMessage.stream": "This changefeed has no consumers",
4
+ "lagsPopover.readLags": "Read lags statistics, maximum among all consumer partitions (time format dd hh:mm:ss)",
3
5
  "table.emptyDataMessage": "No consumers match the current search",
4
- "table.columns.consumerName": "Consumer",
5
- "noConsumersMessage": "This topic has no consumers"
6
+ "controls.search": "Consumer"
6
7
  }
@@ -1,6 +1,7 @@
1
1
  {
2
- "search.placeholder": "Название читателя",
2
+ "noConsumersMessage.topic": "У этого топика нет читателей",
3
+ "noConsumersMessage.stream": "У этого стрима нет читателей",
4
+ "lagsPopover.readLags": "Статистика лагов чтения, максимальной значение среди всех партиций читателя (формат времени дд чч:мм:сс)",
3
5
  "table.emptyDataMessage": "По заданному поиску нет читателей",
4
- "table.columns.consumerName": "Читатель",
5
- "noConsumersMessage": "У этого топика нет читателей"
6
+ "controls.search": "Consumer"
6
7
  }
@@ -0,0 +1,23 @@
1
+ export const CONSUMERS_COLUMNS_IDS = {
2
+ CONSUMER: 'consumer',
3
+ READ_SPEED: 'readSpeed',
4
+ READ_LAGS: 'readLags',
5
+ } as const;
6
+
7
+ export const CONSUMERS_COLUMNS_TITILES = {
8
+ [CONSUMERS_COLUMNS_IDS.CONSUMER]: 'Consumer',
9
+ [CONSUMERS_COLUMNS_IDS.READ_SPEED]: 'Read speed',
10
+ [CONSUMERS_COLUMNS_IDS.READ_LAGS]: 'Read lags, duration',
11
+ } as const;
12
+
13
+ export const CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS = {
14
+ WRITE_LAG: 'writeLag',
15
+ READ_LAG: 'readLag',
16
+ READ_IDLE_TIME: 'readIdleTime',
17
+ } as const;
18
+
19
+ export const CONSUMERS_READ_LAGS_SUB_COLUMNS_TITLES = {
20
+ [CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.WRITE_LAG]: 'write lag',
21
+ [CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_LAG]: 'read lag',
22
+ [CONSUMERS_READ_LAGS_SUB_COLUMNS_IDS.READ_IDLE_TIME]: 'read idle time',
23
+ } as const;