ydb-embedded-ui 4.9.0 → 4.10.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 +12 -0
- package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +7 -4
- package/dist/components/EntityStatus/EntityStatus.js +3 -1
- package/dist/components/FormattedBytes/FormattedBytes.tsx +10 -0
- package/dist/components/FormattedBytes/utils.tsx +13 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.tsx +73 -0
- package/dist/components/InfoViewer/formatters/table.ts +6 -5
- package/dist/components/ProblemFilter/ProblemFilter.tsx +2 -2
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +4 -4
- package/dist/components/TruncatedQuery/{TruncatedQuery.js → TruncatedQuery.tsx} +10 -8
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +6 -6
- package/dist/containers/Cluster/Cluster.tsx +10 -6
- package/dist/containers/Node/Node.tsx +3 -3
- package/dist/containers/Nodes/Nodes.tsx +2 -2
- package/dist/containers/Storage/PDisk/PDisk.tsx +2 -7
- package/dist/containers/Storage/Storage.tsx +240 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +45 -40
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +12 -16
- package/dist/containers/Storage/UsageFilter/UsageFilter.scss +1 -0
- package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +17 -17
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -4
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +0 -15
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +10 -3
- package/dist/containers/Tenant/{Preview → Query/Preview}/Preview.scss +1 -1
- package/dist/containers/Tenant/Query/Preview/Preview.tsx +121 -0
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +1 -1
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +6 -8
- package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +1 -1
- package/dist/containers/Tenant/Query/i18n/en.json +8 -1
- package/dist/containers/Tenant/Query/i18n/ru.json +8 -1
- package/dist/containers/Tenants/Tenants.tsx +269 -0
- package/dist/services/api.ts +8 -3
- package/dist/store/reducers/nodes/nodes.ts +4 -4
- package/dist/store/reducers/partitions/types.ts +3 -3
- package/dist/store/reducers/settings/settings.ts +4 -2
- package/dist/store/reducers/settings/types.ts +3 -1
- package/dist/store/reducers/storage/selectors.ts +279 -0
- package/dist/store/reducers/storage/storage.ts +191 -0
- package/dist/store/reducers/storage/types.ts +80 -0
- package/dist/store/reducers/tenants/selectors.ts +46 -0
- package/dist/store/reducers/tenants/tenants.ts +21 -14
- package/dist/store/reducers/tenants/types.ts +20 -5
- package/dist/store/reducers/tenants/utils.ts +68 -0
- package/dist/types/additionalProps.ts +8 -0
- package/dist/types/api/storage.ts +1 -1
- package/dist/types/store/topic.ts +3 -3
- package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +38 -0
- package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +2 -2
- package/dist/utils/bytesParsers/formatBytes.ts +132 -0
- package/dist/utils/bytesParsers/i18n/en.json +1 -0
- package/dist/utils/bytesParsers/i18n/ru.json +1 -0
- package/dist/utils/bytesParsers/index.ts +1 -1
- package/dist/utils/index.js +5 -10
- package/dist/utils/numeral.ts +8 -0
- package/package.json +1 -1
- package/dist/components/FullNodeViewer/FullNodeViewer.js +0 -89
- package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
- package/dist/containers/Node/NodeOverview/NodeOverview.tsx +0 -21
- package/dist/containers/Storage/Storage.js +0 -350
- package/dist/containers/Tenant/Preview/Preview.js +0 -168
- package/dist/containers/Tenants/Tenants.js +0 -363
- package/dist/store/reducers/storage/storage.js +0 -404
- package/dist/utils/bytesParsers/formatBytesCustom.ts +0 -57
@@ -0,0 +1,240 @@
|
|
1
|
+
import {useCallback, useEffect} from 'react';
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
|
+
import cn from 'bem-cn-lite';
|
4
|
+
|
5
|
+
import DataTable, {Settings} from '@gravity-ui/react-data-table';
|
6
|
+
|
7
|
+
import {Search} from '../../components/Search';
|
8
|
+
import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
|
9
|
+
import {UptimeFilter} from '../../components/UptimeFIlter';
|
10
|
+
import {AccessDenied} from '../../components/Errors/403';
|
11
|
+
import {EntitiesCount} from '../../components/EntitiesCount';
|
12
|
+
|
13
|
+
import type {StorageType, VisibleEntities} from '../../store/reducers/storage/types';
|
14
|
+
import {
|
15
|
+
getStorageInfo,
|
16
|
+
setInitialState,
|
17
|
+
setVisibleEntities,
|
18
|
+
setStorageTextFilter,
|
19
|
+
setUsageFilter,
|
20
|
+
setStorageType,
|
21
|
+
setNodesUptimeFilter,
|
22
|
+
setDataWasNotLoaded,
|
23
|
+
} from '../../store/reducers/storage/storage';
|
24
|
+
import {
|
25
|
+
selectFilteredGroups,
|
26
|
+
selectFilteredNodes,
|
27
|
+
selectStorageNodesCount,
|
28
|
+
selectStorageGroupsCount,
|
29
|
+
selectUsageFilterOptions,
|
30
|
+
} from '../../store/reducers/storage/selectors';
|
31
|
+
import {VISIBLE_ENTITIES, STORAGE_TYPES} from '../../store/reducers/storage/constants';
|
32
|
+
import {getNodesList, selectNodesMap} from '../../store/reducers/nodesList';
|
33
|
+
import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
|
34
|
+
import {AdditionalNodesInfo, NodesUptimeFilterValues} from '../../utils/nodes';
|
35
|
+
import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
|
36
|
+
|
37
|
+
import {StorageGroups} from './StorageGroups/StorageGroups';
|
38
|
+
import {StorageNodes} from './StorageNodes/StorageNodes';
|
39
|
+
import {StorageTypeFilter} from './StorageTypeFilter/StorageTypeFilter';
|
40
|
+
import {StorageVisibleEntityFilter} from './StorageVisibleEntityFilter/StorageVisibleEntityFilter';
|
41
|
+
import {UsageFilter} from './UsageFilter';
|
42
|
+
|
43
|
+
import './Storage.scss';
|
44
|
+
|
45
|
+
const b = cn('global-storage');
|
46
|
+
|
47
|
+
const tableSettings: Settings = {
|
48
|
+
...DEFAULT_TABLE_SETTINGS,
|
49
|
+
defaultOrder: DataTable.DESCENDING,
|
50
|
+
};
|
51
|
+
|
52
|
+
interface StorageProps {
|
53
|
+
additionalNodesInfo?: AdditionalNodesInfo;
|
54
|
+
tenant?: string;
|
55
|
+
nodeId?: string;
|
56
|
+
}
|
57
|
+
|
58
|
+
export const Storage = ({additionalNodesInfo, tenant, nodeId}: StorageProps) => {
|
59
|
+
const dispatch = useDispatch();
|
60
|
+
|
61
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
62
|
+
const {
|
63
|
+
loading,
|
64
|
+
wasLoaded,
|
65
|
+
error,
|
66
|
+
type: storageType,
|
67
|
+
visible: visibleEntities,
|
68
|
+
filter,
|
69
|
+
usageFilter,
|
70
|
+
nodesUptimeFilter,
|
71
|
+
} = useTypedSelector((state) => state.storage);
|
72
|
+
const storageNodes = useTypedSelector(selectFilteredNodes);
|
73
|
+
const storageGroups = useTypedSelector(selectFilteredGroups);
|
74
|
+
const nodesCount = useTypedSelector(selectStorageNodesCount);
|
75
|
+
const groupsCount = useTypedSelector(selectStorageGroupsCount);
|
76
|
+
const nodesMap = useTypedSelector(selectNodesMap);
|
77
|
+
const usageFilterOptions = useTypedSelector(selectUsageFilterOptions);
|
78
|
+
|
79
|
+
useEffect(() => {
|
80
|
+
dispatch(getNodesList());
|
81
|
+
|
82
|
+
return () => {
|
83
|
+
// Clean data on component unmount
|
84
|
+
dispatch(setInitialState());
|
85
|
+
};
|
86
|
+
}, [dispatch]);
|
87
|
+
|
88
|
+
const fetchData = useCallback(
|
89
|
+
(isBackground: boolean) => {
|
90
|
+
if (!isBackground) {
|
91
|
+
dispatch(setDataWasNotLoaded());
|
92
|
+
}
|
93
|
+
|
94
|
+
dispatch(
|
95
|
+
getStorageInfo(
|
96
|
+
{
|
97
|
+
tenant,
|
98
|
+
nodeId,
|
99
|
+
visibleEntities,
|
100
|
+
type: storageType,
|
101
|
+
},
|
102
|
+
{
|
103
|
+
concurrentId: 'getStorageInfo',
|
104
|
+
},
|
105
|
+
),
|
106
|
+
);
|
107
|
+
},
|
108
|
+
[dispatch, tenant, nodeId, visibleEntities, storageType],
|
109
|
+
);
|
110
|
+
|
111
|
+
const autorefreshEnabled = tenant ? autorefresh : true;
|
112
|
+
|
113
|
+
useAutofetcher(fetchData, [fetchData], autorefreshEnabled);
|
114
|
+
|
115
|
+
const handleUsageFilterChange = (value: string[]) => {
|
116
|
+
dispatch(setUsageFilter(value));
|
117
|
+
};
|
118
|
+
|
119
|
+
const handleTextFilterChange = (value: string) => {
|
120
|
+
dispatch(setStorageTextFilter(value));
|
121
|
+
};
|
122
|
+
|
123
|
+
const handleGroupVisibilityChange = (value: string) => {
|
124
|
+
dispatch(setVisibleEntities(value as VisibleEntities));
|
125
|
+
};
|
126
|
+
|
127
|
+
const handleStorageTypeChange = (value: string) => {
|
128
|
+
dispatch(setStorageType(value as StorageType));
|
129
|
+
};
|
130
|
+
|
131
|
+
const handleUptimeFilterChange = (value: string) => {
|
132
|
+
dispatch(setNodesUptimeFilter(value as NodesUptimeFilterValues));
|
133
|
+
};
|
134
|
+
|
135
|
+
const handleShowAllNodes = () => {
|
136
|
+
handleGroupVisibilityChange(VISIBLE_ENTITIES.all);
|
137
|
+
handleUptimeFilterChange(NodesUptimeFilterValues.All);
|
138
|
+
};
|
139
|
+
|
140
|
+
const renderLoader = () => {
|
141
|
+
return <TableSkeleton className={b('loader')} />;
|
142
|
+
};
|
143
|
+
|
144
|
+
const renderDataTable = () => {
|
145
|
+
return (
|
146
|
+
<div className={b('table-wrapper')}>
|
147
|
+
{storageType === STORAGE_TYPES.groups && (
|
148
|
+
<StorageGroups
|
149
|
+
visibleEntities={visibleEntities}
|
150
|
+
data={storageGroups}
|
151
|
+
tableSettings={tableSettings}
|
152
|
+
nodes={nodesMap}
|
153
|
+
onShowAll={() => handleGroupVisibilityChange(VISIBLE_ENTITIES.all)}
|
154
|
+
/>
|
155
|
+
)}
|
156
|
+
{storageType === STORAGE_TYPES.nodes && (
|
157
|
+
<StorageNodes
|
158
|
+
visibleEntities={visibleEntities}
|
159
|
+
nodesUptimeFilter={nodesUptimeFilter}
|
160
|
+
data={storageNodes}
|
161
|
+
tableSettings={tableSettings}
|
162
|
+
onShowAll={handleShowAllNodes}
|
163
|
+
additionalNodesInfo={additionalNodesInfo}
|
164
|
+
/>
|
165
|
+
)}
|
166
|
+
</div>
|
167
|
+
);
|
168
|
+
};
|
169
|
+
|
170
|
+
const renderEntitiesCount = () => {
|
171
|
+
const entityName = storageType === STORAGE_TYPES.groups ? 'Groups' : 'Nodes';
|
172
|
+
const count = storageType === STORAGE_TYPES.groups ? groupsCount : nodesCount;
|
173
|
+
const current =
|
174
|
+
storageType === STORAGE_TYPES.groups ? storageGroups.length : storageNodes.length;
|
175
|
+
|
176
|
+
return (
|
177
|
+
<EntitiesCount
|
178
|
+
label={entityName}
|
179
|
+
loading={loading && !wasLoaded}
|
180
|
+
total={count.total}
|
181
|
+
current={current}
|
182
|
+
/>
|
183
|
+
);
|
184
|
+
};
|
185
|
+
|
186
|
+
const renderControls = () => {
|
187
|
+
return (
|
188
|
+
<div className={b('controls')}>
|
189
|
+
<div className={b('search')}>
|
190
|
+
<Search
|
191
|
+
placeholder={
|
192
|
+
storageType === STORAGE_TYPES.groups
|
193
|
+
? 'Group ID, Pool name'
|
194
|
+
: 'Node ID, FQDN'
|
195
|
+
}
|
196
|
+
onChange={handleTextFilterChange}
|
197
|
+
value={filter}
|
198
|
+
/>
|
199
|
+
</div>
|
200
|
+
|
201
|
+
<StorageTypeFilter value={storageType} onChange={handleStorageTypeChange} />
|
202
|
+
<StorageVisibleEntityFilter
|
203
|
+
value={visibleEntities}
|
204
|
+
onChange={handleGroupVisibilityChange}
|
205
|
+
/>
|
206
|
+
|
207
|
+
{storageType === STORAGE_TYPES.nodes && (
|
208
|
+
<UptimeFilter value={nodesUptimeFilter} onChange={handleUptimeFilterChange} />
|
209
|
+
)}
|
210
|
+
|
211
|
+
{storageType === STORAGE_TYPES.groups && (
|
212
|
+
<UsageFilter
|
213
|
+
value={usageFilter}
|
214
|
+
onChange={handleUsageFilterChange}
|
215
|
+
groups={usageFilterOptions}
|
216
|
+
disabled={usageFilterOptions.length === 0}
|
217
|
+
/>
|
218
|
+
)}
|
219
|
+
{renderEntitiesCount()}
|
220
|
+
</div>
|
221
|
+
);
|
222
|
+
};
|
223
|
+
|
224
|
+
const showLoader = loading && !wasLoaded;
|
225
|
+
|
226
|
+
if (error) {
|
227
|
+
if (error.status === 403) {
|
228
|
+
return <AccessDenied />;
|
229
|
+
}
|
230
|
+
|
231
|
+
return <div className={b()}>{error.statusText}</div>;
|
232
|
+
}
|
233
|
+
|
234
|
+
return (
|
235
|
+
<div className={b()}>
|
236
|
+
{renderControls()}
|
237
|
+
{showLoader ? renderLoader() : renderDataTable()}
|
238
|
+
</div>
|
239
|
+
);
|
240
|
+
};
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import _ from 'lodash';
|
2
1
|
import cn from 'bem-cn-lite';
|
3
2
|
|
4
3
|
import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
|
5
4
|
import {Icon, Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
|
6
5
|
|
7
6
|
import type {NodesMap} from '../../../types/store/nodesList';
|
8
|
-
import type {
|
9
|
-
import type {VisibleEntities} from '../../../store/reducers/storage/types';
|
7
|
+
import type {PreparedStorageGroup, VisibleEntities} from '../../../store/reducers/storage/types';
|
10
8
|
|
11
9
|
import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
|
12
10
|
import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
|
@@ -43,8 +41,8 @@ type TableColumnsIdsKeys = keyof typeof TableColumnsIds;
|
|
43
41
|
type TableColumnsIdsValues = typeof TableColumnsIds[TableColumnsIdsKeys];
|
44
42
|
|
45
43
|
interface StorageGroupsProps {
|
46
|
-
data:
|
47
|
-
nodes
|
44
|
+
data: PreparedStorageGroup[];
|
45
|
+
nodes?: NodesMap;
|
48
46
|
tableSettings: Settings;
|
49
47
|
visibleEntities: VisibleEntities;
|
50
48
|
onShowAll?: VoidFunction;
|
@@ -93,25 +91,25 @@ function setSortOrder(visibleEntities: VisibleEntities): SortOrder | undefined {
|
|
93
91
|
}
|
94
92
|
}
|
95
93
|
|
96
|
-
function StorageGroups({
|
94
|
+
export function StorageGroups({
|
97
95
|
data,
|
98
96
|
tableSettings,
|
99
97
|
visibleEntities,
|
100
98
|
nodes,
|
101
99
|
onShowAll,
|
102
100
|
}: StorageGroupsProps) {
|
103
|
-
const allColumns: Column<
|
101
|
+
const allColumns: Column<PreparedStorageGroup>[] = [
|
104
102
|
{
|
105
103
|
name: TableColumnsIds.PoolName,
|
106
104
|
header: tableColumnsNames[TableColumnsIds.PoolName],
|
107
105
|
width: 250,
|
108
|
-
render: ({
|
109
|
-
const splitted =
|
106
|
+
render: ({row}) => {
|
107
|
+
const splitted = row.PoolName?.split('/');
|
110
108
|
return (
|
111
109
|
<div className={b('pool-name-wrapper')}>
|
112
110
|
{splitted && (
|
113
111
|
<Popover
|
114
|
-
content={
|
112
|
+
content={row.PoolName}
|
115
113
|
placement={['right']}
|
116
114
|
behavior={PopoverBehavior.Immediate}
|
117
115
|
>
|
@@ -129,9 +127,9 @@ function StorageGroups({
|
|
129
127
|
name: TableColumnsIds.Type,
|
130
128
|
header: tableColumnsNames[TableColumnsIds.Type],
|
131
129
|
// prettier-ignore
|
132
|
-
render: ({
|
130
|
+
render: ({row}) => (
|
133
131
|
<>
|
134
|
-
<Label>{
|
132
|
+
<Label>{row.Type || '—'}</Label>
|
135
133
|
{' '}
|
136
134
|
{row.Encryption && (
|
137
135
|
<Popover
|
@@ -157,8 +155,12 @@ function StorageGroups({
|
|
157
155
|
name: TableColumnsIds.Missing,
|
158
156
|
header: tableColumnsNames[TableColumnsIds.Missing],
|
159
157
|
width: 100,
|
160
|
-
render: ({
|
161
|
-
|
158
|
+
render: ({row}) =>
|
159
|
+
row.Missing ? (
|
160
|
+
<Label theme={getDegradedSeverity(row)}>Degraded: {row.Missing}</Label>
|
161
|
+
) : (
|
162
|
+
'-'
|
163
|
+
),
|
162
164
|
align: DataTable.LEFT,
|
163
165
|
defaultOrder: DataTable.DESCENDING,
|
164
166
|
},
|
@@ -189,8 +191,8 @@ function StorageGroups({
|
|
189
191
|
name: TableColumnsIds.GroupID,
|
190
192
|
header: tableColumnsNames[TableColumnsIds.GroupID],
|
191
193
|
width: 130,
|
192
|
-
render: ({
|
193
|
-
return <span className={b('group-id')}>{
|
194
|
+
render: ({row}) => {
|
195
|
+
return <span className={b('group-id')}>{row.GroupID}</span>;
|
194
196
|
},
|
195
197
|
align: DataTable.RIGHT,
|
196
198
|
},
|
@@ -198,8 +200,8 @@ function StorageGroups({
|
|
198
200
|
name: TableColumnsIds.Used,
|
199
201
|
header: tableColumnsNames[TableColumnsIds.Used],
|
200
202
|
width: 100,
|
201
|
-
render: ({
|
202
|
-
return bytesToGB(
|
203
|
+
render: ({row}) => {
|
204
|
+
return bytesToGB(row.Used, true);
|
203
205
|
},
|
204
206
|
align: DataTable.RIGHT,
|
205
207
|
},
|
@@ -207,8 +209,8 @@ function StorageGroups({
|
|
207
209
|
name: TableColumnsIds.Limit,
|
208
210
|
header: tableColumnsNames[TableColumnsIds.Limit],
|
209
211
|
width: 100,
|
210
|
-
render: ({
|
211
|
-
return bytesToGB(
|
212
|
+
render: ({row}) => {
|
213
|
+
return bytesToGB(row.Limit);
|
212
214
|
},
|
213
215
|
align: DataTable.RIGHT,
|
214
216
|
},
|
@@ -216,15 +218,17 @@ function StorageGroups({
|
|
216
218
|
name: TableColumnsIds.UsedSpaceFlag,
|
217
219
|
header: tableColumnsNames[TableColumnsIds.UsedSpaceFlag],
|
218
220
|
width: 110,
|
219
|
-
render: ({
|
220
|
-
const
|
221
|
+
render: ({row}) => {
|
222
|
+
const value = row.UsedSpaceFlag;
|
223
|
+
|
221
224
|
let color = 'Red';
|
222
|
-
|
225
|
+
|
226
|
+
if (value < 100) {
|
223
227
|
color = 'Green';
|
224
|
-
} else if (
|
228
|
+
} else if (value < 10000) {
|
225
229
|
color = 'Yellow';
|
226
|
-
} else if (
|
227
|
-
|
230
|
+
} else if (value < 1000000) {
|
231
|
+
color = 'Orange';
|
228
232
|
}
|
229
233
|
return <EntityStatus status={color} />;
|
230
234
|
},
|
@@ -235,8 +239,8 @@ function StorageGroups({
|
|
235
239
|
name: TableColumnsIds.Read,
|
236
240
|
header: tableColumnsNames[TableColumnsIds.Read],
|
237
241
|
width: 100,
|
238
|
-
render: ({
|
239
|
-
return
|
242
|
+
render: ({row}) => {
|
243
|
+
return row.Read ? bytesToSpeed(row.Read) : '-';
|
240
244
|
},
|
241
245
|
align: DataTable.RIGHT,
|
242
246
|
},
|
@@ -244,8 +248,8 @@ function StorageGroups({
|
|
244
248
|
name: TableColumnsIds.Write,
|
245
249
|
header: tableColumnsNames[TableColumnsIds.Write],
|
246
250
|
width: 100,
|
247
|
-
render: ({
|
248
|
-
return
|
251
|
+
render: ({row}) => {
|
252
|
+
return row.Write ? bytesToSpeed(row.Write) : '-';
|
249
253
|
},
|
250
254
|
align: DataTable.RIGHT,
|
251
255
|
},
|
@@ -253,14 +257,17 @@ function StorageGroups({
|
|
253
257
|
name: TableColumnsIds.VDisks,
|
254
258
|
className: b('vdisks-column'),
|
255
259
|
header: tableColumnsNames[TableColumnsIds.VDisks],
|
256
|
-
render: ({
|
260
|
+
render: ({row}) => (
|
257
261
|
<div className={b('vdisks-wrapper')}>
|
258
|
-
{
|
259
|
-
const donors =
|
262
|
+
{row.VDisks?.map((vDisk) => {
|
263
|
+
const donors = vDisk.Donors;
|
260
264
|
|
261
265
|
return donors && donors.length > 0 ? (
|
262
|
-
<Stack
|
263
|
-
|
266
|
+
<Stack
|
267
|
+
className={b('vdisks-item')}
|
268
|
+
key={stringifyVdiskId(vDisk.VDiskId)}
|
269
|
+
>
|
270
|
+
<VDisk data={vDisk} nodes={nodes} />
|
264
271
|
{donors.map((donor) => {
|
265
272
|
const isFullData = isFullVDiskData(donor);
|
266
273
|
|
@@ -277,8 +284,8 @@ function StorageGroups({
|
|
277
284
|
})}
|
278
285
|
</Stack>
|
279
286
|
) : (
|
280
|
-
<div className={b('vdisks-item')} key={stringifyVdiskId(
|
281
|
-
<VDisk data={
|
287
|
+
<div className={b('vdisks-item')} key={stringifyVdiskId(vDisk.VDiskId)}>
|
288
|
+
<VDisk data={vDisk} nodes={nodes} />
|
282
289
|
</div>
|
283
290
|
);
|
284
291
|
})}
|
@@ -330,7 +337,7 @@ function StorageGroups({
|
|
330
337
|
|
331
338
|
return data ? (
|
332
339
|
<DataTable
|
333
|
-
key={visibleEntities
|
340
|
+
key={visibleEntities}
|
334
341
|
theme="yandex-cloud"
|
335
342
|
data={data}
|
336
343
|
columns={columns}
|
@@ -340,5 +347,3 @@ function StorageGroups({
|
|
340
347
|
/>
|
341
348
|
) : null;
|
342
349
|
}
|
343
|
-
|
344
|
-
export default StorageGroups;
|
@@ -1,9 +1,8 @@
|
|
1
|
-
import _ from 'lodash';
|
2
1
|
import cn from 'bem-cn-lite';
|
3
2
|
|
4
3
|
import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
|
5
4
|
|
6
|
-
import type {VisibleEntities} from '../../../store/reducers/storage/types';
|
5
|
+
import type {PreparedStorageNode, VisibleEntities} from '../../../store/reducers/storage/types';
|
7
6
|
|
8
7
|
import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
|
9
8
|
import {
|
@@ -25,7 +24,7 @@ enum TableColumnsIds {
|
|
25
24
|
FQDN = 'FQDN',
|
26
25
|
DataCenter = 'DataCenter',
|
27
26
|
Rack = 'Rack',
|
28
|
-
|
27
|
+
Uptime = 'Uptime',
|
29
28
|
PDisks = 'PDisks',
|
30
29
|
Missing = 'Missing',
|
31
30
|
}
|
@@ -34,8 +33,7 @@ type TableColumnsIdsKeys = keyof typeof TableColumnsIds;
|
|
34
33
|
type TableColumnsIdsValues = typeof TableColumnsIds[TableColumnsIdsKeys];
|
35
34
|
|
36
35
|
interface StorageNodesProps {
|
37
|
-
data:
|
38
|
-
nodes: any;
|
36
|
+
data: PreparedStorageNode[];
|
39
37
|
tableSettings: Settings;
|
40
38
|
visibleEntities: VisibleEntities;
|
41
39
|
nodesUptimeFilter: keyof typeof NodesUptimeFilterValues;
|
@@ -48,7 +46,7 @@ const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
|
|
48
46
|
FQDN: 'FQDN',
|
49
47
|
DataCenter: 'DC',
|
50
48
|
Rack: 'Rack',
|
51
|
-
|
49
|
+
Uptime: 'Uptime',
|
52
50
|
PDisks: 'PDisks',
|
53
51
|
Missing: 'Missing',
|
54
52
|
};
|
@@ -75,7 +73,7 @@ function setSortOrder(visibleEntities: VisibleEntities): SortOrder | undefined {
|
|
75
73
|
}
|
76
74
|
}
|
77
75
|
|
78
|
-
function StorageNodes({
|
76
|
+
export function StorageNodes({
|
79
77
|
data,
|
80
78
|
tableSettings,
|
81
79
|
visibleEntities,
|
@@ -85,7 +83,7 @@ function StorageNodes({
|
|
85
83
|
}: StorageNodesProps) {
|
86
84
|
const getNodeRef = additionalNodesInfo?.getNodeRef;
|
87
85
|
|
88
|
-
const allColumns: Column<
|
86
|
+
const allColumns: Column<PreparedStorageNode>[] = [
|
89
87
|
{
|
90
88
|
name: TableColumnsIds.NodeId,
|
91
89
|
header: tableColumnsNames[TableColumnsIds.NodeId],
|
@@ -114,8 +112,8 @@ function StorageNodes({
|
|
114
112
|
align: DataTable.LEFT,
|
115
113
|
},
|
116
114
|
{
|
117
|
-
name: TableColumnsIds.
|
118
|
-
header: tableColumnsNames[TableColumnsIds.
|
115
|
+
name: TableColumnsIds.Uptime,
|
116
|
+
header: tableColumnsNames[TableColumnsIds.Uptime],
|
119
117
|
width: 130,
|
120
118
|
sortAccessor: ({StartTime}) => (StartTime ? -StartTime : 0),
|
121
119
|
align: DataTable.RIGHT,
|
@@ -131,11 +129,11 @@ function StorageNodes({
|
|
131
129
|
name: TableColumnsIds.PDisks,
|
132
130
|
className: b('pdisks-column'),
|
133
131
|
header: tableColumnsNames[TableColumnsIds.PDisks],
|
134
|
-
render: ({
|
132
|
+
render: ({row}) => (
|
135
133
|
<div className={b('pdisks-wrapper')}>
|
136
|
-
{
|
137
|
-
<div className={b('pdisks-item')} key={
|
138
|
-
<PDisk data={
|
134
|
+
{row.PDisks?.map((pDisk) => (
|
135
|
+
<div className={b('pdisks-item')} key={pDisk.PDiskId}>
|
136
|
+
<PDisk data={pDisk} nodeId={row.NodeId} />
|
139
137
|
</div>
|
140
138
|
))}
|
141
139
|
</div>
|
@@ -195,5 +193,3 @@ function StorageNodes({
|
|
195
193
|
/>
|
196
194
|
) : null;
|
197
195
|
}
|
198
|
-
|
199
|
-
export default StorageNodes;
|
@@ -3,7 +3,7 @@ import cn from 'bem-cn-lite';
|
|
3
3
|
|
4
4
|
import {Select, SelectOption} from '@gravity-ui/uikit';
|
5
5
|
|
6
|
-
import EntityStatus from
|
6
|
+
import EntityStatus from '../../../components/EntityStatus/EntityStatus';
|
7
7
|
|
8
8
|
import {getUsageSeverityForEntityStatus} from '../utils';
|
9
9
|
|
@@ -27,14 +27,7 @@ interface UsageFilterProps {
|
|
27
27
|
const b = cn('usage-filter');
|
28
28
|
|
29
29
|
export const UsageFilter = (props: UsageFilterProps) => {
|
30
|
-
const {
|
31
|
-
className,
|
32
|
-
value = [],
|
33
|
-
groups = [],
|
34
|
-
onChange,
|
35
|
-
debounce = 200,
|
36
|
-
disabled,
|
37
|
-
} = props;
|
30
|
+
const {className, value = [], groups = [], onChange, debounce = 200, disabled} = props;
|
38
31
|
|
39
32
|
const [filterValue, setFilterValue] = useState(value);
|
40
33
|
const timer = useRef<number>();
|
@@ -50,11 +43,15 @@ export const UsageFilter = (props: UsageFilterProps) => {
|
|
50
43
|
});
|
51
44
|
}, [value]);
|
52
45
|
|
53
|
-
const options = useMemo(
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
46
|
+
const options = useMemo(
|
47
|
+
() =>
|
48
|
+
groups.map(({threshold, count}) => ({
|
49
|
+
value: String(threshold),
|
50
|
+
text: `${threshold}%`,
|
51
|
+
data: {count},
|
52
|
+
})),
|
53
|
+
[groups],
|
54
|
+
);
|
58
55
|
|
59
56
|
const handleUpdate = (newValue: string[]) => {
|
60
57
|
setFilterValue(newValue);
|
@@ -67,17 +64,20 @@ export const UsageFilter = (props: UsageFilterProps) => {
|
|
67
64
|
|
68
65
|
const maxWidth = Math.max(...groups.map(({count}) => count));
|
69
66
|
|
70
|
-
const renderOption = ({value, data, text}: SelectOption) => (
|
67
|
+
const renderOption = ({value: optionValue, data, text}: SelectOption) => (
|
71
68
|
<div className={b('option')}>
|
72
69
|
<EntityStatus
|
73
70
|
className={b('option-title')}
|
74
|
-
status={getUsageSeverityForEntityStatus(Number(
|
71
|
+
status={getUsageSeverityForEntityStatus(Number(optionValue))}
|
75
72
|
name={text}
|
76
73
|
size="xs"
|
77
74
|
/>
|
78
75
|
<div className={b('option-meta')}>
|
79
76
|
{i18n('groups_count', {count: data.count})}
|
80
|
-
<div
|
77
|
+
<div
|
78
|
+
className={b('option-bar')}
|
79
|
+
style={{width: `${(data.count / maxWidth) * 100}%`}}
|
80
|
+
/>
|
81
81
|
</div>
|
82
82
|
</div>
|
83
83
|
);
|
@@ -13,14 +13,14 @@ import {useTypedSelector} from '../../../utils/hooks';
|
|
13
13
|
import routes, {createHref} from '../../../routes';
|
14
14
|
import type {TenantDiagnosticsTab} from '../../../store/reducers/tenant/types';
|
15
15
|
import {enableAutorefresh, disableAutorefresh} from '../../../store/reducers/schema/schema';
|
16
|
-
import {
|
16
|
+
import {setDiagnosticsTab} from '../../../store/reducers/tenant/tenant';
|
17
17
|
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants';
|
18
18
|
|
19
19
|
import {Loader} from '../../../components/Loader';
|
20
20
|
|
21
21
|
import {Heatmap} from '../../Heatmap';
|
22
22
|
import {Nodes} from '../../Nodes';
|
23
|
-
import Storage from '../../Storage/Storage';
|
23
|
+
import {Storage} from '../../Storage/Storage';
|
24
24
|
import {Tablets} from '../../Tablets';
|
25
25
|
|
26
26
|
import Describe from './Describe/Describe';
|
@@ -130,7 +130,7 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
130
130
|
return <Tablets path={currentSchemaPath} />;
|
131
131
|
}
|
132
132
|
case TENANT_DIAGNOSTICS_TABS_IDS.storage: {
|
133
|
-
return <Storage tenant={tenantNameString}
|
133
|
+
return <Storage tenant={tenantNameString} />;
|
134
134
|
}
|
135
135
|
case TENANT_DIAGNOSTICS_TABS_IDS.network: {
|
136
136
|
return <Network path={tenantNameString} />;
|
@@ -27,10 +27,13 @@ const renderName = (tenant) => {
|
|
27
27
|
const {Name} = tenant;
|
28
28
|
return (
|
29
29
|
<div className={b('tenant-name-wrapper')}>
|
30
|
-
<EntityStatus
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
<EntityStatus
|
31
|
+
status={tenant.State}
|
32
|
+
name={Name}
|
33
|
+
withLeftTrim
|
34
|
+
hasClipboardButton
|
35
|
+
clipboardButtonAlwaysVisible
|
36
|
+
/>
|
34
37
|
</div>
|
35
38
|
);
|
36
39
|
}
|
@@ -10,21 +10,6 @@
|
|
10
10
|
display: flex;
|
11
11
|
overflow: hidden;
|
12
12
|
align-items: center;
|
13
|
-
|
14
|
-
& .yc-link {
|
15
|
-
display: flex;
|
16
|
-
}
|
17
|
-
}
|
18
|
-
&__tenant-name-trim {
|
19
|
-
overflow: hidden;
|
20
|
-
|
21
|
-
white-space: nowrap;
|
22
|
-
text-overflow: ellipsis;
|
23
|
-
direction: rtl;
|
24
|
-
}
|
25
|
-
|
26
|
-
&__tenant-name {
|
27
|
-
unicode-bidi: plaintext;
|
28
13
|
}
|
29
14
|
|
30
15
|
&__top {
|