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.
- package/CHANGELOG.md +19 -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 +18 -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 +4 -0
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +8 -2
- 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/Partitions/i18n/index.ts +11 -0
- 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/utils/schema.ts +1 -16
- package/dist/services/api.d.ts +4 -0
- package/dist/services/api.js +22 -6
- 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/topic.ts +82 -2
- package/dist/types/store/consumer.ts +55 -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/utils.js +3 -3
- package/package.json +2 -2
@@ -12,6 +12,7 @@ export enum GeneralPagesIds {
|
|
12
12
|
'hotKeys' = 'hotKeys',
|
13
13
|
'graph' = 'graph',
|
14
14
|
'consumers' = 'consumers',
|
15
|
+
'partitions' = 'partitions',
|
15
16
|
}
|
16
17
|
|
17
18
|
type Page = {
|
@@ -72,6 +73,11 @@ const consumers = {
|
|
72
73
|
title: 'Consumers',
|
73
74
|
};
|
74
75
|
|
76
|
+
const partitions = {
|
77
|
+
id: GeneralPagesIds.partitions,
|
78
|
+
title: 'Partitions',
|
79
|
+
};
|
80
|
+
|
75
81
|
export const DATABASE_PAGES = [
|
76
82
|
overview,
|
77
83
|
topQueries,
|
@@ -87,8 +93,8 @@ export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, descri
|
|
87
93
|
|
88
94
|
export const DIR_PAGES = [overview, topShards, describe];
|
89
95
|
|
90
|
-
export const CDC_STREAM_PAGES = [overview, consumers, describe];
|
91
|
-
export const TOPIC_PAGES = [overview, consumers, describe];
|
96
|
+
export const CDC_STREAM_PAGES = [overview, consumers, partitions, describe];
|
97
|
+
export const TOPIC_PAGES = [overview, consumers, partitions, describe];
|
92
98
|
|
93
99
|
// verbose mapping to guarantee correct tabs for new path types
|
94
100
|
// TS will error when a new type is added but not mapped here
|
@@ -6,12 +6,10 @@ import type {DescribeTopicResult} from '../../../../../types/api/topic';
|
|
6
6
|
import {Loader} from '../../../../../components/Loader';
|
7
7
|
import {InfoViewerItem, formatObject, InfoViewer} from '../../../../../components/InfoViewer';
|
8
8
|
|
9
|
-
import {
|
10
|
-
prepareBytesWritten,
|
11
|
-
formatTopicStats,
|
12
|
-
} from '../../../../../components/InfoViewer/formatters';
|
9
|
+
import {formatTopicStats} from '../../../../../components/InfoViewer/formatters';
|
13
10
|
|
14
11
|
import {useTypedSelector} from '../../../../../utils/hooks';
|
12
|
+
import {convertBytesObjectToSpeed} from '../../../../../utils/bytesParsers';
|
15
13
|
import {formatBps} from '../../../../../utils';
|
16
14
|
|
17
15
|
import i18n from './i18n';
|
@@ -29,20 +27,20 @@ const prepareTopicInfo = (data: DescribeTopicResult): Array<InfoViewerItem> => {
|
|
29
27
|
};
|
30
28
|
|
31
29
|
const prepareBytesWrittenInfo = (data: DescribeTopicResult): Array<InfoViewerItem> => {
|
32
|
-
const preparedBytes =
|
30
|
+
const preparedBytes = convertBytesObjectToSpeed(data?.topic_stats?.bytes_written);
|
33
31
|
|
34
32
|
return [
|
35
33
|
{
|
36
34
|
label: 'per minute',
|
37
|
-
value: formatBps(preparedBytes.
|
35
|
+
value: formatBps(preparedBytes.perMinute),
|
38
36
|
},
|
39
37
|
{
|
40
38
|
label: 'per hour',
|
41
|
-
value: formatBps(preparedBytes.
|
39
|
+
value: formatBps(preparedBytes.perHour),
|
42
40
|
},
|
43
41
|
{
|
44
42
|
label: 'per day',
|
45
|
-
value: formatBps(preparedBytes.
|
43
|
+
value: formatBps(preparedBytes.perDay),
|
46
44
|
},
|
47
45
|
];
|
48
46
|
};
|
@@ -0,0 +1,33 @@
|
|
1
|
+
.ydb-diagnostics-partitions-columns-header {
|
2
|
+
&__multiline {
|
3
|
+
white-space: normal;
|
4
|
+
}
|
5
|
+
|
6
|
+
&__read-session {
|
7
|
+
width: 80px;
|
8
|
+
|
9
|
+
white-space: normal;
|
10
|
+
}
|
11
|
+
|
12
|
+
&__lags {
|
13
|
+
white-space: nowrap;
|
14
|
+
}
|
15
|
+
|
16
|
+
&__messages {
|
17
|
+
width: 90px;
|
18
|
+
|
19
|
+
white-space: normal;
|
20
|
+
}
|
21
|
+
|
22
|
+
&__lags-popover-content {
|
23
|
+
max-width: 300px;
|
24
|
+
|
25
|
+
div:nth-child(1) {
|
26
|
+
margin-bottom: 10px;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
&__messages-popover-content {
|
31
|
+
max-width: 200px;
|
32
|
+
}
|
33
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {LabelWithPopover} from '../../../../../components/LabelWithPopover';
|
4
|
+
import {WriteLagImage, ReadLagImage} from '../../../../../components/LagImages';
|
5
|
+
|
6
|
+
import {PARTITIONS_COLUMNS_IDS, PARTITIONS_COLUMNS_TITILES} from '../utils/constants';
|
7
|
+
|
8
|
+
import i18n from '../i18n';
|
9
|
+
|
10
|
+
import './Headers.scss';
|
11
|
+
|
12
|
+
const b = block('ydb-diagnostics-partitions-columns-header');
|
13
|
+
|
14
|
+
interface MultilineHeaderProps {
|
15
|
+
title: string;
|
16
|
+
}
|
17
|
+
|
18
|
+
export const MultilineHeader = ({title}: MultilineHeaderProps) => (
|
19
|
+
<div className={b('multiline')}>{title}</div>
|
20
|
+
);
|
21
|
+
|
22
|
+
export const ReadSessionHeader = () => (
|
23
|
+
<div className={b('read-session')}>
|
24
|
+
{PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.READ_SESSION_ID]}
|
25
|
+
</div>
|
26
|
+
);
|
27
|
+
|
28
|
+
export const WriteLagsHeader = () => (
|
29
|
+
<LabelWithPopover
|
30
|
+
className={b('lags')}
|
31
|
+
headerText={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.READ_LAGS]}
|
32
|
+
popoverContent={
|
33
|
+
<div className={b('lags-popover-content')}>
|
34
|
+
<div>{i18n('lagsPopover.writeLags')}</div>
|
35
|
+
<div>
|
36
|
+
<WriteLagImage />
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
}
|
40
|
+
/>
|
41
|
+
);
|
42
|
+
|
43
|
+
export const ReadLagsHeader = () => (
|
44
|
+
<LabelWithPopover
|
45
|
+
className={b('lags')}
|
46
|
+
headerText={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.WRITE_LAGS]}
|
47
|
+
popoverContent={
|
48
|
+
<div className={b('lags-popover-content')}>
|
49
|
+
<div>{i18n('lagsPopover.readLags')}</div>
|
50
|
+
<div>
|
51
|
+
<ReadLagImage />
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
}
|
55
|
+
/>
|
56
|
+
);
|
57
|
+
|
58
|
+
export const UnreadMessagesHeader = () => (
|
59
|
+
<LabelWithPopover
|
60
|
+
className={b('messages')}
|
61
|
+
headerText={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.UNREAD_MESSAGES]}
|
62
|
+
popoverContent={
|
63
|
+
<div className={b('messages-popover-content')}>{i18n('headers.unread')}</div>
|
64
|
+
}
|
65
|
+
/>
|
66
|
+
);
|
67
|
+
|
68
|
+
export const UncommitedMessagesHeader = () => (
|
69
|
+
<LabelWithPopover
|
70
|
+
className={b('messages')}
|
71
|
+
headerText={PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.UNCOMMITED_MESSAGES]}
|
72
|
+
popoverContent={
|
73
|
+
<div className={b('messages-popover-content')}>{i18n('headers.uncommited')}</div>
|
74
|
+
}
|
75
|
+
/>
|
76
|
+
);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './Headers';
|
@@ -0,0 +1,45 @@
|
|
1
|
+
@import '../../../../styles/mixins.scss';
|
2
|
+
|
3
|
+
.ydb-diagnostics-partitions {
|
4
|
+
overflow: auto;
|
5
|
+
flex-grow: 1;
|
6
|
+
|
7
|
+
height: 100%;
|
8
|
+
|
9
|
+
@include flex-container();
|
10
|
+
|
11
|
+
&__controls {
|
12
|
+
@include controls();
|
13
|
+
}
|
14
|
+
|
15
|
+
&__consumer-select {
|
16
|
+
width: 220px;
|
17
|
+
}
|
18
|
+
|
19
|
+
&__search {
|
20
|
+
@include search();
|
21
|
+
&_partition {
|
22
|
+
width: 100px;
|
23
|
+
}
|
24
|
+
&_general {
|
25
|
+
width: 280px;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
&__table-settings .yc-icon {
|
30
|
+
width: 20px;
|
31
|
+
}
|
32
|
+
|
33
|
+
&__table-wrapper {
|
34
|
+
overflow: auto;
|
35
|
+
@include flex-container();
|
36
|
+
}
|
37
|
+
|
38
|
+
&__table-content {
|
39
|
+
overflow: auto;
|
40
|
+
|
41
|
+
height: 100%;
|
42
|
+
|
43
|
+
@include freeze-nth-column(1);
|
44
|
+
}
|
45
|
+
}
|
@@ -0,0 +1,254 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
import {useCallback, useEffect, useMemo, useState} from 'react';
|
3
|
+
import {useDispatch} from 'react-redux';
|
4
|
+
import {escapeRegExp} from 'lodash/fp';
|
5
|
+
|
6
|
+
import DataTable from '@gravity-ui/react-data-table';
|
7
|
+
import {Select, TableColumnSetup} from '@gravity-ui/uikit';
|
8
|
+
import {TableColumnSetupItem} from '@gravity-ui/uikit/build/esm/components/Table/hoc/withTableSettings/withTableSettings';
|
9
|
+
|
10
|
+
import type {EPathType} from '../../../../types/api/schema';
|
11
|
+
|
12
|
+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
13
|
+
import {DEFAULT_TABLE_SETTINGS, PARTITIONS_SELECTED_COLUMNS_KEY} from '../../../../utils/constants';
|
14
|
+
|
15
|
+
import {getSettingValue, setSettingValue} from '../../../../store/reducers/settings';
|
16
|
+
import {
|
17
|
+
getConsumer,
|
18
|
+
selectPreparedPartitionsData,
|
19
|
+
setDataWasNotLoaded,
|
20
|
+
} from '../../../../store/reducers/consumer';
|
21
|
+
|
22
|
+
import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton';
|
23
|
+
import {Search} from '../../../../components/Search';
|
24
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
25
|
+
|
26
|
+
import {isCdcStreamEntityType} from '../../utils/schema';
|
27
|
+
|
28
|
+
import type {IPreparedPartitionDataWithHosts} from './utils/types';
|
29
|
+
import {
|
30
|
+
PARTITIONS_COLUMNS_IDS,
|
31
|
+
PARTITIONS_COLUMNS_TITILES,
|
32
|
+
PARTITIONS_DEFAULT_SELECTED_COLUMNS,
|
33
|
+
} from './utils/constants';
|
34
|
+
|
35
|
+
import {columns as partitionsColumns} from './columns';
|
36
|
+
|
37
|
+
import i18n from './i18n';
|
38
|
+
|
39
|
+
import './Partitions.scss';
|
40
|
+
|
41
|
+
export const b = block('ydb-diagnostics-partitions');
|
42
|
+
|
43
|
+
interface PartitionsProps {
|
44
|
+
path?: string;
|
45
|
+
type?: EPathType;
|
46
|
+
nodes?: Record<number, string>;
|
47
|
+
consumers?: string[];
|
48
|
+
}
|
49
|
+
|
50
|
+
export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
|
51
|
+
const isCdcStream = isCdcStreamEntityType(type);
|
52
|
+
|
53
|
+
const dispatch = useDispatch();
|
54
|
+
|
55
|
+
const [selectedConsumer, setSelectedConsumer] = useState<string[]>();
|
56
|
+
const [generalSearchValue, setGeneralSearchValue] = useState('');
|
57
|
+
const [partitionIdSearchValue, setPartitionIdSearchValue] = useState('');
|
58
|
+
|
59
|
+
const [componentCurrentPath, setComponentCurrentPath] = useState(path);
|
60
|
+
|
61
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
62
|
+
const {loading, wasLoaded, error} = useTypedSelector((state) => state.consumer);
|
63
|
+
|
64
|
+
const partitions = useTypedSelector((state) => selectPreparedPartitionsData(state));
|
65
|
+
|
66
|
+
const savedSelectedColumns: string = useTypedSelector((state) =>
|
67
|
+
getSettingValue(state, PARTITIONS_SELECTED_COLUMNS_KEY),
|
68
|
+
);
|
69
|
+
|
70
|
+
useEffect(() => {
|
71
|
+
// Manual path control to ensure it updates with other values so no request with wrong params will be sent
|
72
|
+
setComponentCurrentPath(path);
|
73
|
+
}, [path]);
|
74
|
+
|
75
|
+
const fetchConsumerData = useCallback(
|
76
|
+
(isBackground: boolean) => {
|
77
|
+
if (!isBackground) {
|
78
|
+
dispatch(setDataWasNotLoaded());
|
79
|
+
}
|
80
|
+
|
81
|
+
if (selectedConsumer && selectedConsumer.length) {
|
82
|
+
dispatch(getConsumer(componentCurrentPath, selectedConsumer[0]));
|
83
|
+
}
|
84
|
+
},
|
85
|
+
[dispatch, selectedConsumer, componentCurrentPath],
|
86
|
+
);
|
87
|
+
|
88
|
+
useAutofetcher(fetchConsumerData, [fetchConsumerData], autorefresh);
|
89
|
+
|
90
|
+
const consumersToSelect = useMemo(
|
91
|
+
() =>
|
92
|
+
consumers
|
93
|
+
? consumers.map((consumer) => ({
|
94
|
+
value: consumer,
|
95
|
+
content: consumer,
|
96
|
+
}))
|
97
|
+
: undefined,
|
98
|
+
[consumers],
|
99
|
+
);
|
100
|
+
|
101
|
+
useEffect(() => {
|
102
|
+
if (consumersToSelect && consumersToSelect.length) {
|
103
|
+
setSelectedConsumer([consumersToSelect[0].value]);
|
104
|
+
} else {
|
105
|
+
setSelectedConsumer(undefined);
|
106
|
+
}
|
107
|
+
}, [consumersToSelect]);
|
108
|
+
|
109
|
+
const selectedColumns: string[] = useMemo(
|
110
|
+
() =>
|
111
|
+
savedSelectedColumns
|
112
|
+
? JSON.parse(savedSelectedColumns)
|
113
|
+
: PARTITIONS_DEFAULT_SELECTED_COLUMNS,
|
114
|
+
[savedSelectedColumns],
|
115
|
+
);
|
116
|
+
|
117
|
+
const columnsToSelect = useMemo(() => {
|
118
|
+
return Object.values(PARTITIONS_COLUMNS_IDS).map((id) => {
|
119
|
+
return {
|
120
|
+
title: PARTITIONS_COLUMNS_TITILES[id],
|
121
|
+
selected: Boolean(selectedColumns?.includes(id)),
|
122
|
+
id: id,
|
123
|
+
required: id === PARTITIONS_COLUMNS_IDS.PARTITION_ID,
|
124
|
+
};
|
125
|
+
});
|
126
|
+
}, [selectedColumns]);
|
127
|
+
|
128
|
+
const columnsToShow = useMemo(() => {
|
129
|
+
return partitionsColumns.filter((column) => selectedColumns?.includes(column.name));
|
130
|
+
}, [selectedColumns]);
|
131
|
+
|
132
|
+
const partitionsWithHosts: IPreparedPartitionDataWithHosts[] | undefined = useMemo(() => {
|
133
|
+
return partitions?.map((partition) => {
|
134
|
+
const partitionHost =
|
135
|
+
partition.partitionNodeId && nodes ? nodes[partition.partitionNodeId] : undefined;
|
136
|
+
|
137
|
+
const connectionHost =
|
138
|
+
partition.connectionNodeId && nodes ? nodes[partition.connectionNodeId] : undefined;
|
139
|
+
|
140
|
+
return {
|
141
|
+
...partition,
|
142
|
+
partitionHost,
|
143
|
+
connectionHost,
|
144
|
+
};
|
145
|
+
});
|
146
|
+
}, [partitions, nodes]);
|
147
|
+
|
148
|
+
const dataToRender = useMemo(() => {
|
149
|
+
if (!partitionsWithHosts) {
|
150
|
+
return [];
|
151
|
+
}
|
152
|
+
|
153
|
+
const partitionIdRe = new RegExp(escapeRegExp(partitionIdSearchValue), 'i');
|
154
|
+
const generalRe = new RegExp(escapeRegExp(generalSearchValue), 'i');
|
155
|
+
|
156
|
+
return partitionsWithHosts.filter((partition) => {
|
157
|
+
const {
|
158
|
+
partitionId,
|
159
|
+
readerName = '',
|
160
|
+
readSessionId = '',
|
161
|
+
partitionNodeId,
|
162
|
+
connectionNodeId,
|
163
|
+
partitionHost = '',
|
164
|
+
connectionHost = '',
|
165
|
+
} = partition;
|
166
|
+
|
167
|
+
const isPartitionIdMatch = partitionIdRe.test(partitionId);
|
168
|
+
const isOtherValuesMatch =
|
169
|
+
generalRe.test(readerName) ||
|
170
|
+
generalRe.test(readSessionId) ||
|
171
|
+
generalRe.test(String(partitionNodeId)) ||
|
172
|
+
generalRe.test(String(connectionNodeId)) ||
|
173
|
+
generalRe.test(partitionHost) ||
|
174
|
+
generalRe.test(connectionHost);
|
175
|
+
|
176
|
+
return isPartitionIdMatch && isOtherValuesMatch;
|
177
|
+
});
|
178
|
+
}, [partitionIdSearchValue, generalSearchValue, partitionsWithHosts]);
|
179
|
+
|
180
|
+
const hadleTableColumnsSetupChange = (value: TableColumnSetupItem[]) => {
|
181
|
+
const columns = value.filter((el) => el.selected).map((el) => el.id);
|
182
|
+
dispatch(setSettingValue(PARTITIONS_SELECTED_COLUMNS_KEY, JSON.stringify(columns)));
|
183
|
+
};
|
184
|
+
|
185
|
+
const handleConsumerSelectChange = (value: string[]) => {
|
186
|
+
setSelectedConsumer(value);
|
187
|
+
};
|
188
|
+
|
189
|
+
const handlePartitionIdSearchChange = (value: string) => {
|
190
|
+
setPartitionIdSearchValue(value);
|
191
|
+
};
|
192
|
+
|
193
|
+
const handleGeneralSearchChange = (value: string) => {
|
194
|
+
setGeneralSearchValue(value);
|
195
|
+
};
|
196
|
+
|
197
|
+
if (error) {
|
198
|
+
return <ResponseError error={error} />;
|
199
|
+
}
|
200
|
+
|
201
|
+
if (!consumersToSelect || !consumersToSelect.length) {
|
202
|
+
return <div>{i18n(`noConsumersMessage.${isCdcStream ? 'stream' : 'topic'}`)}</div>;
|
203
|
+
}
|
204
|
+
|
205
|
+
return (
|
206
|
+
<div className={b()}>
|
207
|
+
<div className={b('controls')}>
|
208
|
+
<Select
|
209
|
+
className={b('consumer-select')}
|
210
|
+
placeholder={i18n('controls.consumerSelector.placeholder')}
|
211
|
+
label={i18n('controls.consumerSelector')}
|
212
|
+
options={consumersToSelect}
|
213
|
+
value={selectedConsumer}
|
214
|
+
onUpdate={handleConsumerSelectChange}
|
215
|
+
/>
|
216
|
+
<Search
|
217
|
+
onChange={handlePartitionIdSearchChange}
|
218
|
+
placeholder={i18n('controls.partitionSearch')}
|
219
|
+
className={b('search', {partition: true})}
|
220
|
+
value={partitionIdSearchValue}
|
221
|
+
/>
|
222
|
+
<Search
|
223
|
+
onChange={handleGeneralSearchChange}
|
224
|
+
placeholder={i18n('controls.generalSearch')}
|
225
|
+
className={b('search', {general: true})}
|
226
|
+
value={generalSearchValue}
|
227
|
+
/>
|
228
|
+
<TableColumnSetup
|
229
|
+
key="TableColumnSetup"
|
230
|
+
popupWidth="242px"
|
231
|
+
items={columnsToSelect}
|
232
|
+
showStatus
|
233
|
+
onUpdate={hadleTableColumnsSetupChange}
|
234
|
+
className={b('table-settings')}
|
235
|
+
/>
|
236
|
+
</div>
|
237
|
+
<div className={b('table-wrapper')}>
|
238
|
+
<div className={b('table-content')}>
|
239
|
+
{loading && !wasLoaded ? (
|
240
|
+
<TableSkeleton className={b('loader')} />
|
241
|
+
) : (
|
242
|
+
<DataTable
|
243
|
+
theme="yandex-cloud"
|
244
|
+
data={dataToRender}
|
245
|
+
columns={columnsToShow}
|
246
|
+
settings={DEFAULT_TABLE_SETTINGS}
|
247
|
+
emptyDataMessage={i18n('table.emptyDataMessage')}
|
248
|
+
/>
|
249
|
+
)}
|
250
|
+
</div>
|
251
|
+
</div>
|
252
|
+
</div>
|
253
|
+
);
|
254
|
+
};
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import {useEffect, useMemo} from 'react';
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
|
+
|
4
|
+
import type {EPathType} from '../../../../types/api/schema';
|
5
|
+
|
6
|
+
import {useTypedSelector} from '../../../../utils/hooks';
|
7
|
+
|
8
|
+
import {
|
9
|
+
getTopic,
|
10
|
+
setDataWasNotLoaded as setTopicDataWasNotLoaded,
|
11
|
+
} from '../../../../store/reducers/topic';
|
12
|
+
import {
|
13
|
+
getNodes,
|
14
|
+
setDataWasNotLoaded as setNodesDataWasNotLoaded,
|
15
|
+
} from '../../../../store/reducers/nodes';
|
16
|
+
|
17
|
+
import {Loader} from '../../../../components/Loader';
|
18
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
19
|
+
|
20
|
+
import {Partitions} from './Partitions';
|
21
|
+
|
22
|
+
interface PartitionsWrapperProps {
|
23
|
+
path?: string;
|
24
|
+
type?: EPathType;
|
25
|
+
}
|
26
|
+
|
27
|
+
export const PartitionsWrapper = ({path, type}: PartitionsWrapperProps) => {
|
28
|
+
const dispatch = useDispatch();
|
29
|
+
|
30
|
+
const {
|
31
|
+
loading: topicLoading,
|
32
|
+
wasLoaded: topicWasLoaded,
|
33
|
+
error: topicError,
|
34
|
+
data: topicData,
|
35
|
+
} = useTypedSelector((state) => state.topic);
|
36
|
+
|
37
|
+
const {
|
38
|
+
loading: nodesLoading,
|
39
|
+
wasLoaded: nodesWasLoaded,
|
40
|
+
error: nodesError,
|
41
|
+
data: nodesData,
|
42
|
+
} = useTypedSelector((state) => state.nodes);
|
43
|
+
|
44
|
+
const consumers = useMemo(
|
45
|
+
() =>
|
46
|
+
topicData?.consumers
|
47
|
+
?.map((consumer) => consumer?.name)
|
48
|
+
.filter((consumer): consumer is string => consumer !== undefined),
|
49
|
+
[topicData],
|
50
|
+
);
|
51
|
+
|
52
|
+
const nodes = useMemo(() => {
|
53
|
+
const preparedNodesObject: Record<number, string> = {};
|
54
|
+
nodesData?.forEach((node) => {
|
55
|
+
if (node.NodeId && node.Host) {
|
56
|
+
preparedNodesObject[node.NodeId] = node.Host;
|
57
|
+
}
|
58
|
+
});
|
59
|
+
return preparedNodesObject;
|
60
|
+
}, [nodesData]);
|
61
|
+
|
62
|
+
useEffect(() => {
|
63
|
+
dispatch(setTopicDataWasNotLoaded());
|
64
|
+
dispatch(setNodesDataWasNotLoaded());
|
65
|
+
|
66
|
+
dispatch(getTopic(path));
|
67
|
+
dispatch(getNodes({}));
|
68
|
+
}, [dispatch, path]);
|
69
|
+
|
70
|
+
if ((topicLoading && !topicWasLoaded) || (nodesLoading && !nodesWasLoaded)) {
|
71
|
+
return <Loader />;
|
72
|
+
}
|
73
|
+
|
74
|
+
if (topicError || nodesError) {
|
75
|
+
return <ResponseError error={topicError || nodesError} />;
|
76
|
+
}
|
77
|
+
|
78
|
+
return <Partitions path={path} type={type} consumers={consumers} nodes={nodes} />;
|
79
|
+
};
|