ydb-embedded-ui 4.9.0 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +7 -4
  3. package/dist/components/EntityStatus/EntityStatus.js +3 -1
  4. package/dist/components/FormattedBytes/FormattedBytes.tsx +10 -0
  5. package/dist/components/FormattedBytes/utils.tsx +13 -0
  6. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +73 -0
  7. package/dist/components/InfoViewer/formatters/table.ts +6 -5
  8. package/dist/components/ProblemFilter/ProblemFilter.tsx +2 -2
  9. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +4 -4
  10. package/dist/components/TruncatedQuery/{TruncatedQuery.js → TruncatedQuery.tsx} +10 -8
  11. package/dist/containers/AsideNavigation/AsideNavigation.tsx +6 -6
  12. package/dist/containers/Cluster/Cluster.tsx +10 -6
  13. package/dist/containers/Node/Node.tsx +3 -3
  14. package/dist/containers/Nodes/Nodes.tsx +2 -2
  15. package/dist/containers/Storage/PDisk/PDisk.tsx +2 -7
  16. package/dist/containers/Storage/Storage.tsx +240 -0
  17. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +45 -40
  18. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +12 -16
  19. package/dist/containers/Storage/UsageFilter/UsageFilter.scss +1 -0
  20. package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +17 -17
  21. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  22. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -4
  23. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +0 -15
  24. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +10 -3
  25. package/dist/containers/Tenant/{Preview → Query/Preview}/Preview.scss +1 -1
  26. package/dist/containers/Tenant/Query/Preview/Preview.tsx +121 -0
  27. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +1 -1
  28. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +6 -8
  29. package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +1 -1
  30. package/dist/containers/Tenant/Query/i18n/en.json +8 -1
  31. package/dist/containers/Tenant/Query/i18n/ru.json +8 -1
  32. package/dist/containers/Tenants/Tenants.tsx +269 -0
  33. package/dist/services/api.ts +8 -3
  34. package/dist/store/reducers/nodes/nodes.ts +4 -4
  35. package/dist/store/reducers/partitions/types.ts +3 -3
  36. package/dist/store/reducers/settings/settings.ts +4 -2
  37. package/dist/store/reducers/settings/types.ts +3 -1
  38. package/dist/store/reducers/storage/selectors.ts +279 -0
  39. package/dist/store/reducers/storage/storage.ts +191 -0
  40. package/dist/store/reducers/storage/types.ts +80 -0
  41. package/dist/store/reducers/tenants/selectors.ts +46 -0
  42. package/dist/store/reducers/tenants/tenants.ts +21 -14
  43. package/dist/store/reducers/tenants/types.ts +20 -5
  44. package/dist/store/reducers/tenants/utils.ts +68 -0
  45. package/dist/types/additionalProps.ts +8 -0
  46. package/dist/types/api/storage.ts +1 -1
  47. package/dist/types/store/topic.ts +3 -3
  48. package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +38 -0
  49. package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +2 -2
  50. package/dist/utils/bytesParsers/formatBytes.ts +132 -0
  51. package/dist/utils/bytesParsers/i18n/en.json +1 -0
  52. package/dist/utils/bytesParsers/i18n/ru.json +1 -0
  53. package/dist/utils/bytesParsers/index.ts +1 -1
  54. package/dist/utils/index.js +5 -10
  55. package/dist/utils/numeral.ts +8 -0
  56. package/package.json +1 -1
  57. package/dist/components/FullNodeViewer/FullNodeViewer.js +0 -89
  58. package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
  59. package/dist/containers/Node/NodeOverview/NodeOverview.tsx +0 -21
  60. package/dist/containers/Storage/Storage.js +0 -350
  61. package/dist/containers/Tenant/Preview/Preview.js +0 -168
  62. package/dist/containers/Tenants/Tenants.js +0 -363
  63. package/dist/store/reducers/storage/storage.js +0 -404
  64. 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 {TVDiskStateInfo} from '../../../types/api/vdisk';
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: any;
47
- nodes: NodesMap;
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<any>[] = [
101
+ const allColumns: Column<PreparedStorageGroup>[] = [
104
102
  {
105
103
  name: TableColumnsIds.PoolName,
106
104
  header: tableColumnsNames[TableColumnsIds.PoolName],
107
105
  width: 250,
108
- render: ({value}) => {
109
- const splitted = (value as string)?.split('/');
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={value as string}
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: ({value, row}) => (
130
+ render: ({row}) => (
133
131
  <>
134
- <Label>{(value as string) || '—'}</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: ({value, row}) =>
161
- value ? <Label theme={getDegradedSeverity(row)}>Degraded: {value}</Label> : '-',
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: ({value}) => {
193
- return <span className={b('group-id')}>{value as number}</span>;
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: ({value}) => {
202
- return bytesToGB(value, true);
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: ({value}) => {
211
- return bytesToGB(value);
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: ({value}) => {
220
- const val = value as number;
221
+ render: ({row}) => {
222
+ const value = row.UsedSpaceFlag;
223
+
221
224
  let color = 'Red';
222
- if (val < 100) {
225
+
226
+ if (value < 100) {
223
227
  color = 'Green';
224
- } else if (val < 10000) {
228
+ } else if (value < 10000) {
225
229
  color = 'Yellow';
226
- } else if (val < 1000000) {
227
- value = 'Orange';
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: ({value}) => {
239
- return value ? bytesToSpeed(value) : '-';
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: ({value}) => {
248
- return value ? bytesToSpeed(value) : '-';
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: ({value}) => (
260
+ render: ({row}) => (
257
261
  <div className={b('vdisks-wrapper')}>
258
- {_.map(value as TVDiskStateInfo[], (el) => {
259
- const donors = el.Donors;
262
+ {row.VDisks?.map((vDisk) => {
263
+ const donors = vDisk.Donors;
260
264
 
261
265
  return donors && donors.length > 0 ? (
262
- <Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
263
- <VDisk data={el} nodes={nodes} />
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(el.VDiskId)}>
281
- <VDisk data={el} nodes={nodes} />
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 as string}
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
- uptime = 'uptime',
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: any;
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
- uptime: 'Uptime',
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<any>[] = [
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.uptime,
118
- header: tableColumnsNames[TableColumnsIds.uptime],
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: ({value, row}) => (
132
+ render: ({row}) => (
135
133
  <div className={b('pdisks-wrapper')}>
136
- {_.map(value as any, (el) => (
137
- <div className={b('pdisks-item')} key={el.PDiskId}>
138
- <PDisk data={el} nodeId={row.NodeId} />
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;
@@ -13,6 +13,7 @@
13
13
 
14
14
  &-meta {
15
15
  position: relative;
16
+ z-index: 0;
16
17
 
17
18
  padding: 0 5px;
18
19
 
@@ -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 "../../../components/EntityStatus/EntityStatus";
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(() => groups.map(({threshold, count}) => ({
54
- value: String(threshold),
55
- text: `${threshold}%`,
56
- data: {count}
57
- })), [groups]);
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(value))}
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 className={b('option-bar')} style={{width: `${data.count / maxWidth * 100}%`}} />
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 { setDiagnosticsTab} from '../../../store/reducers/tenant/tenant';
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} database={true} />;
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 status={tenant.State} />
31
- <span className={b('tenant-name-trim')}>
32
- <span className={b('tenant-name')}>{Name}</span>
33
- </span>
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 {