ydb-embedded-ui 3.3.4 → 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 (73) hide show
  1. package/CHANGELOG.md +19 -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 +18 -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 +4 -0
  33. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +8 -2
  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/Partitions/i18n/index.ts +11 -0
  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/utils/schema.ts +1 -16
  52. package/dist/services/api.d.ts +4 -0
  53. package/dist/services/api.js +22 -6
  54. package/dist/store/reducers/consumer.ts +160 -0
  55. package/dist/store/reducers/index.ts +2 -0
  56. package/dist/store/reducers/settings.js +2 -0
  57. package/dist/store/reducers/topic.ts +82 -2
  58. package/dist/types/store/consumer.ts +55 -0
  59. package/dist/types/store/topic.ts +23 -6
  60. package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +24 -0
  61. package/dist/utils/bytesParsers/formatBytesCustom.ts +57 -0
  62. package/dist/utils/bytesParsers/i18n/en.json +7 -0
  63. package/dist/utils/bytesParsers/i18n/index.ts +11 -0
  64. package/dist/utils/bytesParsers/i18n/ru.json +7 -0
  65. package/dist/utils/bytesParsers/index.ts +2 -0
  66. package/dist/utils/constants.ts +3 -0
  67. package/dist/utils/index.js +6 -0
  68. package/dist/utils/storage.ts +2 -2
  69. package/dist/utils/timeParsers/index.ts +2 -1
  70. package/dist/utils/timeParsers/parsers.ts +18 -0
  71. package/dist/utils/timeParsers/{protobuf.ts → protobufParsers.ts} +0 -0
  72. package/dist/utils/utils.js +3 -3
  73. package/package.json +2 -2
@@ -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;
@@ -27,6 +27,7 @@ import {Nodes} from '../../Nodes';
27
27
  //@ts-ignore
28
28
  import {Tablets} from '../../Tablets';
29
29
  import {Consumers} from './Consumers';
30
+ import {PartitionsWrapper} from './Partitions';
30
31
 
31
32
  import routes, {createHref} from '../../../routes';
32
33
  import type {EPathType} from '../../../types/api/schema';
@@ -156,6 +157,9 @@ function Diagnostics(props: DiagnosticsProps) {
156
157
  case GeneralPagesIds.consumers: {
157
158
  return <Consumers path={currentSchemaPath} type={type} />;
158
159
  }
160
+ case GeneralPagesIds.partitions: {
161
+ return <PartitionsWrapper path={currentSchemaPath} type={type} />;
162
+ }
159
163
  default: {
160
164
  return <div>No data...</div>;
161
165
  }