ydb-embedded-ui 2.4.4 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/components/InfoViewer/InfoViewer.scss +3 -3
  3. package/dist/components/InfoViewer/formatters/schema.ts +2 -1
  4. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +23 -22
  5. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +8 -13
  6. package/dist/components/InfoViewer/schemaInfo/TableIndexInfo.tsx +2 -12
  7. package/dist/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +3 -16
  8. package/dist/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +8 -13
  9. package/dist/components/InfoViewer/utils.ts +6 -6
  10. package/dist/components/Loader/Loader.scss +6 -3
  11. package/dist/components/Loader/Loader.tsx +7 -5
  12. package/dist/components/Loader/index.ts +1 -0
  13. package/dist/components/UptimeFIlter/UptimeFilter.tsx +21 -0
  14. package/dist/components/UptimeFIlter/index.ts +1 -0
  15. package/dist/containers/Node/Node.scss +1 -0
  16. package/dist/containers/Node/Node.tsx +3 -8
  17. package/dist/containers/Node/NodeStructure/NodeStructure.scss +0 -6
  18. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +1 -1
  19. package/dist/containers/Nodes/Nodes.js +22 -10
  20. package/dist/{components → containers}/NodesViewer/NodesViewer.js +49 -62
  21. package/dist/{components → containers}/NodesViewer/NodesViewer.scss +0 -0
  22. package/dist/containers/Storage/Pdisk/Pdisk.scss +1 -1
  23. package/dist/containers/Storage/Pdisk/Pdisk.tsx +3 -9
  24. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +1 -1
  25. package/dist/containers/Storage/Storage.js +46 -11
  26. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +39 -32
  27. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +2 -2
  28. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +35 -17
  29. package/dist/containers/Storage/StorageNodes/i18n/en.json +6 -4
  30. package/dist/containers/Storage/StorageNodes/i18n/ru.json +6 -4
  31. package/dist/containers/Storage/UsageFilter/UsageFilter.scss +10 -5
  32. package/dist/containers/Tenant/Acl/Acl.js +1 -7
  33. package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +22 -14
  34. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +52 -10
  35. package/dist/containers/Tenant/Diagnostics/Describe/Describe.scss +0 -8
  36. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +42 -15
  37. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +0 -7
  38. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +19 -15
  39. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +1 -1
  40. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +13 -5
  41. package/dist/containers/Tenant/Diagnostics/Network/Network.js +17 -4
  42. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +50 -16
  43. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +16 -2
  44. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +1 -0
  45. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +21 -13
  46. package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +1 -5
  47. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +4 -4
  48. package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -4
  49. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +23 -28
  50. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +2 -2
  51. package/dist/containers/Tenant/TenantPages.tsx +1 -1
  52. package/dist/containers/Tenant/utils/schema.ts +84 -0
  53. package/dist/containers/Tenant/utils/schemaActions.ts +9 -20
  54. package/dist/services/api.d.ts +17 -11
  55. package/dist/store/reducers/clusterNodes.js +29 -10
  56. package/dist/store/reducers/describe.ts +56 -14
  57. package/dist/store/reducers/healthcheckInfo.ts +23 -8
  58. package/dist/store/reducers/network.js +22 -1
  59. package/dist/store/reducers/nodes.js +37 -3
  60. package/dist/store/reducers/schema.ts +229 -0
  61. package/dist/store/reducers/storage.js +59 -5
  62. package/dist/types/api/enums.ts +10 -0
  63. package/dist/types/api/nodes.ts +96 -0
  64. package/dist/types/api/pdisk.ts +48 -0
  65. package/dist/types/api/schema.ts +148 -9
  66. package/dist/types/api/storage.ts +3 -173
  67. package/dist/types/api/tablet.ts +97 -0
  68. package/dist/types/api/vdisk.ts +120 -0
  69. package/dist/types/store/describe.ts +8 -2
  70. package/dist/types/store/healthcheck.ts +12 -0
  71. package/dist/types/store/schema.ts +52 -0
  72. package/dist/utils/index.js +6 -2
  73. package/dist/utils/nodes.ts +9 -0
  74. package/dist/utils/pdisk.ts +1 -1
  75. package/dist/utils/storage.ts +1 -1
  76. package/package.json +1 -1
  77. package/dist/store/reducers/schema.js +0 -148
@@ -1,28 +1,46 @@
1
1
  import React from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import PropTypes from 'prop-types';
4
- import _ from 'lodash';
5
4
  import {connect} from 'react-redux';
6
5
 
7
6
  import {TextInput, Label} from '@gravity-ui/uikit';
8
7
  import DataTable from '@yandex-cloud/react-data-table';
9
8
 
10
- import ProblemFilter, {problemFilterType} from '../ProblemFilter/ProblemFilter';
9
+ import ProblemFilter, {problemFilterType} from '../../components/ProblemFilter/ProblemFilter';
10
+ import {UptimeFilter} from '../../components/UptimeFIlter';
11
+ import {Illustration} from '../../components/Illustration';
11
12
 
12
13
  import {withSearch} from '../../HOCS';
13
14
  import {calcUptime} from '../../utils';
14
- import {ALL, DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
15
+ import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
15
16
  import {changeFilter} from '../../store/reducers/settings';
17
+ import {filterNodesByStatusAndUptime} from '../../store/reducers/clusterNodes';
18
+ import {setNodesUptimeFilter} from '../../store/reducers/nodes';
16
19
  import {hideTooltip, showTooltip} from '../../store/reducers/tooltip';
17
20
  import {getNodesColumns} from '../../utils/getNodesColumns';
18
21
 
19
- import {Illustration} from '../Illustration';
20
-
21
22
  import './NodesViewer.scss';
22
23
 
23
24
  const b = cn('nodes-viewer');
24
25
 
25
26
  class NodesViewer extends React.PureComponent {
27
+ static selectNodesToShow(nodes, searchQuery) {
28
+ let preparedNodes = nodes;
29
+ if (nodes && Array.isArray(nodes)) {
30
+ preparedNodes = nodes
31
+ .map((node) => {
32
+ node.uptime = calcUptime(node.StartTime);
33
+ return node;
34
+ })
35
+ /* Filter by nodes with the Host field.
36
+ If a node does not have a Host field it is also
37
+ included in the filter and displayed with a dash in the corresponding column
38
+ */
39
+ .filter((node) => (node.Host ? node.Host.includes(searchQuery) : true));
40
+ }
41
+ return preparedNodes;
42
+ }
43
+
26
44
  static propTypes = {
27
45
  nodes: PropTypes.array.isRequired,
28
46
  className: PropTypes.string,
@@ -30,7 +48,9 @@ class NodesViewer extends React.PureComponent {
30
48
  handleSearchQuery: PropTypes.func,
31
49
  showTooltip: PropTypes.func,
32
50
  hideTooltip: PropTypes.func,
33
- filter: problemFilterType,
51
+ problemFilter: problemFilterType,
52
+ nodesUptimeFilter: PropTypes.string,
53
+ setNodesUptimeFilter: PropTypes.func,
34
54
  changeFilter: PropTypes.func,
35
55
  showControls: PropTypes.bool,
36
56
  additionalNodesInfo: PropTypes.object,
@@ -42,60 +62,18 @@ class NodesViewer extends React.PureComponent {
42
62
  showControls: true,
43
63
  };
44
64
 
45
- state = {
46
- filteredNodes: [],
47
- nodesToShow: [],
65
+ handleProblemFilterChange = (value) => {
66
+ this.props.changeFilter(value);
48
67
  };
49
68
 
50
- static getDerivedStateFromProps(props, state) {
51
- const {nodes, filter} = props;
52
- if (!_.isEqual(nodes, state.nodes)) {
53
- return {
54
- nodes,
55
- filteredNodes: NodesViewer.filterNodes(nodes, filter),
56
- };
57
- }
58
- return null;
59
- }
60
-
61
- static filterNodes(nodes, filter) {
62
- if (filter === ALL) {
63
- return nodes;
64
- }
65
-
66
- return _.filter(nodes, (node) => {
67
- return node.Overall && node.Overall !== 'Green';
68
- });
69
- }
70
-
71
- static selectNodesToShow(nodes, searchQuery) {
72
- let preparedNodes = nodes;
73
- if (nodes && Array.isArray(nodes)) {
74
- preparedNodes = nodes
75
- .map((node) => {
76
- node.uptime = calcUptime(node.StartTime);
77
- return node;
78
- })
79
- /* Filter by nodes with the Host field.
80
- If a node does not have a Host field it is also
81
- included in the filter and displayed with a dash in the corresponding column
82
- */
83
- .filter((node) => (node.Host ? node.Host.includes(searchQuery) : true));
84
- }
85
- return preparedNodes;
86
- }
87
-
88
- onChangeProblemFilter = (filter) => {
89
- const {nodes, changeFilter} = this.props;
90
- const filteredNodes = NodesViewer.filterNodes(nodes, filter);
91
-
92
- changeFilter(filter);
93
- this.setState({filteredNodes});
69
+ handleUptimeFilterChange = (value) => {
70
+ this.props.setNodesUptimeFilter(value);
94
71
  };
95
72
 
96
73
  renderControls() {
97
- const {searchQuery, handleSearchQuery, filter} = this.props;
98
- const nodesToShow = NodesViewer.selectNodesToShow(this.state.filteredNodes, searchQuery);
74
+ const {nodes, searchQuery, handleSearchQuery, nodesUptimeFilter, problemFilter} =
75
+ this.props;
76
+ const nodesToShow = NodesViewer.selectNodesToShow(nodes, searchQuery);
99
77
 
100
78
  return (
101
79
  <div className={b('controls')}>
@@ -108,7 +86,8 @@ class NodesViewer extends React.PureComponent {
108
86
  hasClear
109
87
  autoFocus
110
88
  />
111
- <ProblemFilter value={filter} onChange={this.onChangeProblemFilter} />
89
+ <ProblemFilter value={problemFilter} onChange={this.handleProblemFilterChange} />
90
+ <UptimeFilter value={nodesUptimeFilter} onChange={this.handleUptimeFilterChange} />
112
91
  <Label theme="info" size="m">{`Nodes: ${nodesToShow.length}`}</Label>
113
92
  </div>
114
93
  );
@@ -119,13 +98,13 @@ class NodesViewer extends React.PureComponent {
119
98
  className,
120
99
  searchQuery,
121
100
  path,
122
- filter,
101
+ problemFilter,
123
102
  showControls,
124
103
  hideTooltip,
125
104
  showTooltip,
126
105
  additionalNodesInfo = {},
106
+ nodes,
127
107
  } = this.props;
128
- const {filteredNodes = []} = this.state;
129
108
 
130
109
  const columns = getNodesColumns({
131
110
  tabletsPath: path,
@@ -134,7 +113,7 @@ class NodesViewer extends React.PureComponent {
134
113
  getNodeRef: additionalNodesInfo.getNodeRef,
135
114
  });
136
115
 
137
- const nodesToShow = NodesViewer.selectNodesToShow(filteredNodes, searchQuery);
116
+ const nodesToShow = NodesViewer.selectNodesToShow(nodes, searchQuery);
138
117
 
139
118
  return (
140
119
  <div className={`${b()} ${className}`}>
@@ -146,7 +125,7 @@ class NodesViewer extends React.PureComponent {
146
125
  <div className={b('table-content')}>
147
126
  <DataTable
148
127
  theme="yandex-cloud"
149
- key={filter}
128
+ key={problemFilter}
150
129
  data={nodesToShow}
151
130
  columns={columns}
152
131
  settings={DEFAULT_TABLE_SETTINGS}
@@ -160,9 +139,16 @@ class NodesViewer extends React.PureComponent {
160
139
  }
161
140
  }
162
141
 
163
- const mapStateToProps = (state) => {
142
+ const mapStateToProps = (state, ownProps) => {
143
+ const {nodesUptimeFilter} = state.nodes;
144
+ const {problemFilter} = state.settings;
145
+
146
+ const nodes = filterNodesByStatusAndUptime(ownProps.nodes, problemFilter, nodesUptimeFilter);
147
+
164
148
  return {
165
- filter: state.settings.problemFilter,
149
+ problemFilter,
150
+ nodesUptimeFilter,
151
+ nodes,
166
152
  };
167
153
  };
168
154
 
@@ -170,6 +156,7 @@ const mapDispatchToProps = {
170
156
  changeFilter,
171
157
  hideTooltip,
172
158
  showTooltip,
159
+ setNodesUptimeFilter,
173
160
  };
174
161
 
175
162
  const ConnectedNodesViewer = connect(mapStateToProps, mapDispatchToProps)(NodesViewer);
@@ -1,7 +1,7 @@
1
1
  .pdisk-storage {
2
2
  position: relative;
3
3
 
4
- min-width: 120px;
4
+ width: 120px;
5
5
 
6
6
  border-radius: 4px; // to match interactive area with disk shape
7
7
 
@@ -8,7 +8,7 @@ import InternalLink from '../../../components/InternalLink/InternalLink';
8
8
 
9
9
  import routes, {createHref} from '../../../routes';
10
10
  import type {RequiredField} from '../../../types';
11
- import {TPDiskStateInfo, TPDiskState} from '../../../types/api/storage';
11
+ import {TPDiskStateInfo, TPDiskState} from '../../../types/api/pdisk';
12
12
  import {getPDiskId} from '../../../utils';
13
13
  import {getPDiskType} from '../../../utils/pdisk';
14
14
  import {bytesToGB} from '../../../utils/utils';
@@ -96,9 +96,7 @@ function Pdisk(props: PDiskProps) {
96
96
  Realtime &&
97
97
  errorColors.includes(Realtime) &&
98
98
  pdiskData.push({label: 'Realtime', value: Realtime});
99
- Device &&
100
- errorColors.includes(Device) &&
101
- pdiskData.push({label: 'Device', value: Device});
99
+ Device && errorColors.includes(Device) && pdiskData.push({label: 'Device', value: Device});
102
100
  return pdiskData;
103
101
  };
104
102
  /* eslint-enable */
@@ -113,11 +111,7 @@ function Pdisk(props: PDiskProps) {
113
111
  // matches the default offset for popup with arrow out of a sense of beauty
114
112
  offset={[0, 12]}
115
113
  >
116
- <InfoViewer
117
- title="PDisk"
118
- info={preparePdiskData()}
119
- size="s"
120
- />
114
+ <InfoViewer title="PDisk" info={preparePdiskData()} size="s" />
121
115
  </Popup>
122
116
  );
123
117
 
@@ -2,7 +2,7 @@ import {MemoryRouter} from 'react-router-dom';
2
2
 
3
3
  import {renderWithStore} from '../../../../utils/tests/providers';
4
4
 
5
- import {TPDiskState} from '../../../../types/api/storage'
5
+ import {TPDiskState} from '../../../../types/api/pdisk'
6
6
 
7
7
  import PDisk from '../Pdisk'
8
8
 
@@ -8,7 +8,9 @@ import {RadioButton, Label} from '@gravity-ui/uikit';
8
8
  import {Search} from '../../components/Search';
9
9
  import {UsageFilter} from './UsageFilter';
10
10
  import {AutoFetcher} from '../../utils/autofetcher';
11
+ import {NodesUptimeFilterValues} from '../../utils/nodes';
11
12
  import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
13
+ import {UptimeFilter} from '../../components/UptimeFIlter';
12
14
  import {AccessDenied} from '../../components/Errors/403';
13
15
 
14
16
  import {
@@ -22,6 +24,8 @@ import {
22
24
  getNodesObject,
23
25
  StorageTypes,
24
26
  setStorageType,
27
+ setNodesUptimeFilter,
28
+ setDataWasNotLoaded,
25
29
  VisibleEntitiesTitles,
26
30
  getStoragePoolsGroupsCount,
27
31
  getStorageNodesCount,
@@ -66,6 +70,9 @@ class Storage extends React.Component {
66
70
  setHeader: PropTypes.func,
67
71
  tenant: PropTypes.string,
68
72
  nodeId: PropTypes.string,
73
+ nodesUptimeFilter: PropTypes.string,
74
+ setNodesUptimeFilter: PropTypes.func,
75
+ setDataWasNotLoaded: PropTypes.func,
69
76
  };
70
77
 
71
78
  componentDidMount() {
@@ -101,7 +108,8 @@ class Storage extends React.Component {
101
108
  }
102
109
 
103
110
  componentDidUpdate(prevProps) {
104
- const {visibleEntities, storageType, autorefresh, database} = this.props;
111
+ const {visibleEntities, storageType, autorefresh, database, tenant, setDataWasNotLoaded} =
112
+ this.props;
105
113
 
106
114
  const startFetch = () => {
107
115
  this.getStorageInfo({
@@ -139,6 +147,12 @@ class Storage extends React.Component {
139
147
  restartAutorefresh();
140
148
  }
141
149
  }
150
+
151
+ if (tenant !== prevProps.tenant) {
152
+ setDataWasNotLoaded();
153
+ startFetch();
154
+ restartAutorefresh();
155
+ }
142
156
  }
143
157
 
144
158
  componentWillUnmount() {
@@ -166,7 +180,8 @@ class Storage extends React.Component {
166
180
  }
167
181
 
168
182
  renderDataTable() {
169
- const {flatListStorageEntities, visibleEntities, nodes, storageType} = this.props;
183
+ const {flatListStorageEntities, visibleEntities, nodesUptimeFilter, nodes, storageType} =
184
+ this.props;
170
185
 
171
186
  return (
172
187
  <div className={b('table-wrapper')}>
@@ -182,9 +197,10 @@ class Storage extends React.Component {
182
197
  {storageType === StorageTypes.nodes && (
183
198
  <StorageNodes
184
199
  visibleEntities={visibleEntities}
200
+ nodesUptimeFilter={nodesUptimeFilter}
185
201
  data={flatListStorageEntities}
186
202
  tableSettings={tableSettings}
187
- onShowAll={() => this.onGroupVisibilityChange(VisibleEntities.All)}
203
+ onShowAll={this.onShowAllNodes}
188
204
  />
189
205
  )}
190
206
  </div>
@@ -201,6 +217,15 @@ class Storage extends React.Component {
201
217
  setStorageType(value);
202
218
  };
203
219
 
220
+ onUptimeFilterChange = (value) => {
221
+ this.props.setNodesUptimeFilter(value);
222
+ };
223
+
224
+ onShowAllNodes = () => {
225
+ this.onGroupVisibilityChange(VisibleEntities.All);
226
+ this.onUptimeFilterChange(NodesUptimeFilterValues.All);
227
+ };
228
+
204
229
  renderEntitiesCount() {
205
230
  const {storageType, groupsCount, nodesCount, flatListStorageEntities, loading, wasLoaded} =
206
231
  this.props;
@@ -230,6 +255,7 @@ class Storage extends React.Component {
230
255
  visibleEntities,
231
256
  storageType,
232
257
  usageFilter,
258
+ nodesUptimeFilter,
233
259
  setUsageFilter,
234
260
  usageFilterOptions,
235
261
  } = this.props;
@@ -247,6 +273,16 @@ class Storage extends React.Component {
247
273
  value={filter}
248
274
  />
249
275
  </div>
276
+
277
+ <RadioButton value={storageType} onUpdate={this.onStorageTypeChange}>
278
+ <RadioButton.Option value={StorageTypes.groups}>
279
+ {StorageTypes.groups}
280
+ </RadioButton.Option>
281
+ <RadioButton.Option value={StorageTypes.nodes}>
282
+ {StorageTypes.nodes}
283
+ </RadioButton.Option>
284
+ </RadioButton>
285
+
250
286
  <RadioButton value={visibleEntities} onUpdate={this.onGroupVisibilityChange}>
251
287
  <RadioButton.Option value={VisibleEntities.Missing}>
252
288
  {VisibleEntitiesTitles[VisibleEntities.Missing]}
@@ -259,14 +295,9 @@ class Storage extends React.Component {
259
295
  </RadioButton.Option>
260
296
  </RadioButton>
261
297
 
262
- <RadioButton value={storageType} onUpdate={this.onStorageTypeChange}>
263
- <RadioButton.Option value={StorageTypes.groups}>
264
- {StorageTypes.groups}
265
- </RadioButton.Option>
266
- <RadioButton.Option value={StorageTypes.nodes}>
267
- {StorageTypes.nodes}
268
- </RadioButton.Option>
269
- </RadioButton>
298
+ {storageType === StorageTypes.nodes && (
299
+ <UptimeFilter value={nodesUptimeFilter} onChange={this.onUptimeFilterChange} />
300
+ )}
270
301
 
271
302
  {storageType === StorageTypes.groups && (
272
303
  <UsageFilter
@@ -314,6 +345,7 @@ function mapStateToProps(state) {
314
345
  type: storageType,
315
346
  filter,
316
347
  usageFilter,
348
+ nodesUptimeFilter,
317
349
  } = state.storage;
318
350
 
319
351
  return {
@@ -329,6 +361,7 @@ function mapStateToProps(state) {
329
361
  storageType,
330
362
  filter,
331
363
  usageFilter,
364
+ nodesUptimeFilter,
332
365
  usageFilterOptions: getUsageFilterOptions(state),
333
366
  };
334
367
  }
@@ -339,9 +372,11 @@ const mapDispatchToProps = {
339
372
  setStorageFilter,
340
373
  setUsageFilter,
341
374
  setVisibleEntities: setVisibleEntities,
375
+ setNodesUptimeFilter,
342
376
  getNodesList,
343
377
  setStorageType,
344
378
  setHeader,
379
+ setDataWasNotLoaded,
345
380
  };
346
381
 
347
382
  export default connect(mapStateToProps, mapDispatchToProps)(Storage);
@@ -9,7 +9,7 @@ import {Stack} from '../../../components/Stack/Stack';
9
9
  //@ts-ignore
10
10
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
11
11
 
12
- import {TVDiskStateInfo} from '../../../types/api/storage';
12
+ import {TVDiskStateInfo} from '../../../types/api/vdisk';
13
13
  //@ts-ignore
14
14
  import {VisibleEntities} from '../../../store/reducers/storage';
15
15
  //@ts-ignore
@@ -92,7 +92,13 @@ function setSortOrder(visibleEntities: keyof typeof VisibleEntities): SortOrder
92
92
  }
93
93
  }
94
94
 
95
- function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}: StorageGroupsProps) {
95
+ function StorageGroups({
96
+ data,
97
+ tableSettings,
98
+ visibleEntities,
99
+ nodes,
100
+ onShowAll,
101
+ }: StorageGroupsProps) {
96
102
  const allColumns: Column<any>[] = [
97
103
  {
98
104
  name: TableColumnsIds.PoolName,
@@ -123,7 +129,7 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
123
129
  header: tableColumnsNames[TableColumnsIds.Type],
124
130
  render: ({value, row}) => (
125
131
  <>
126
- <Label>{value as string || '—'}</Label>
132
+ <Label>{(value as string) || '—'}</Label>
127
133
  {' '}
128
134
  {row.Encryption && (
129
135
  <Popover
@@ -143,9 +149,8 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
143
149
  name: TableColumnsIds.Missing,
144
150
  header: tableColumnsNames[TableColumnsIds.Missing],
145
151
  width: 100,
146
- render: ({value, row}) => value ? (
147
- <Label theme={getDegradedSeverity(row)}>Degraded: {value}</Label>
148
- ) : '-',
152
+ render: ({value, row}) =>
153
+ value ? <Label theme={getDegradedSeverity(row)}>Degraded: {value}</Label> : '-',
149
154
  align: DataTable.LEFT,
150
155
  defaultOrder: DataTable.DESCENDING,
151
156
  },
@@ -164,10 +169,12 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
164
169
  >
165
170
  {usage}%
166
171
  </Label>
167
- ) : '-';
172
+ ) : (
173
+ '-'
174
+ );
168
175
  },
169
176
  // without a limit exclude usage from sort to display at the bottom
170
- sortAccessor: (row) => row.Limit ? getUsage(row) : null,
177
+ sortAccessor: (row) => (row.Limit ? getUsage(row) : null),
171
178
  align: DataTable.LEFT,
172
179
  },
173
180
  {
@@ -241,35 +248,35 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
241
248
  render: ({value, row}) => (
242
249
  <div className={b('vdisks-wrapper')}>
243
250
  {_.map(value as TVDiskStateInfo[], (el) => {
244
- const donors = Array.isArray(el.Donors) ? el.Donors.filter(isFullDonorData) : [];
251
+ const donors = Array.isArray(el.Donors)
252
+ ? el.Donors.filter(isFullDonorData)
253
+ : [];
245
254
 
246
- return (
247
- donors.length > 0 ? (
248
- <Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
249
- <Vdisk
250
- {...el}
251
- PoolName={row[TableColumnsIds.PoolName]}
252
- nodes={nodes}
253
- />
254
- {donors.map((donor) => (
255
- <Vdisk
256
- {...donor}
257
- // donor and acceptor are always in the same group
258
- PoolName={row[TableColumnsIds.PoolName]}
259
- nodes={nodes}
260
- key={stringifyVdiskId(donor.VDiskId)}
261
- />
262
- ))}
263
- </Stack>
264
- ) : (
265
- <div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
255
+ return donors.length > 0 ? (
256
+ <Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
257
+ <Vdisk
258
+ {...el}
259
+ PoolName={row[TableColumnsIds.PoolName]}
260
+ nodes={nodes}
261
+ />
262
+ {donors.map((donor) => (
266
263
  <Vdisk
267
- {...el}
264
+ {...donor}
265
+ // donor and acceptor are always in the same group
268
266
  PoolName={row[TableColumnsIds.PoolName]}
269
267
  nodes={nodes}
268
+ key={stringifyVdiskId(donor.VDiskId)}
270
269
  />
271
- </div>
272
- )
270
+ ))}
271
+ </Stack>
272
+ ) : (
273
+ <div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
274
+ <Vdisk
275
+ {...el}
276
+ PoolName={row[TableColumnsIds.PoolName]}
277
+ nodes={nodes}
278
+ />
279
+ </div>
273
280
  );
274
281
  })}
275
282
  </div>
@@ -3,9 +3,9 @@
3
3
  display: flex;
4
4
  overflow-x: auto;
5
5
  overflow-y: hidden;
6
- justify-content: center;
6
+ justify-content: left;
7
7
 
8
- min-width: 500px;
8
+ width: max-content;
9
9
  }
10
10
  &__pdisks-item {
11
11
  flex-grow: 1;
@@ -5,6 +5,7 @@ import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-t
5
5
  import {Popover, PopoverBehavior} from '@gravity-ui/uikit';
6
6
 
7
7
  import {VisibleEntities} from '../../../store/reducers/storage';
8
+ import {NodesUptimeFilterValues} from '../../../utils/nodes';
8
9
 
9
10
  import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
10
11
  import Pdisk from '../Pdisk/Pdisk';
@@ -28,6 +29,7 @@ interface StorageNodesProps {
28
29
  nodes: any;
29
30
  tableSettings: Settings;
30
31
  visibleEntities: keyof typeof VisibleEntities;
32
+ nodesUptimeFilter: keyof typeof NodesUptimeFilterValues;
31
33
  onShowAll?: VoidFunction;
32
34
  }
33
35
 
@@ -61,7 +63,13 @@ function setSortOrder(visibleEntities: keyof typeof VisibleEntities): SortOrder
61
63
  }
62
64
  }
63
65
 
64
- function StorageNodes({data, tableSettings, visibleEntities, onShowAll}: StorageNodesProps) {
66
+ function StorageNodes({
67
+ data,
68
+ tableSettings,
69
+ visibleEntities,
70
+ onShowAll,
71
+ nodesUptimeFilter,
72
+ }: StorageNodesProps) {
65
73
  const allColumns: Column<any>[] = [
66
74
  {
67
75
  name: TableColumnsIds.NodeId,
@@ -124,23 +132,33 @@ function StorageNodes({data, tableSettings, visibleEntities, onShowAll}: Storage
124
132
 
125
133
  if (visibleEntities === VisibleEntities.Space) {
126
134
  columns = allColumns.filter((col) => col.name !== TableColumnsIds.Missing);
127
-
128
- return (
129
- <EmptyFilter
130
- title={i18n('empty.out_of_space')}
131
- showAll={i18n('show_all')}
132
- onShowAll={onShowAll}
133
- />
134
- );
135
135
  }
136
- if (visibleEntities === VisibleEntities.Missing) {
137
- return (
138
- <EmptyFilter
139
- title={i18n('empty.degraded')}
140
- showAll={i18n('show_all')}
141
- onShowAll={onShowAll}
142
- />
143
- );
136
+
137
+ if (!data.length) {
138
+ let message;
139
+
140
+ if (visibleEntities === VisibleEntities.Space) {
141
+ message = i18n('empty.out_of_space');
142
+ }
143
+
144
+ if (visibleEntities === VisibleEntities.Missing) {
145
+ message = i18n('empty.degraded');
146
+ }
147
+
148
+ if (nodesUptimeFilter === NodesUptimeFilterValues.SmallUptime) {
149
+ message = i18n('empty.small_uptime');
150
+ }
151
+
152
+ if (
153
+ visibleEntities !== VisibleEntities.All &&
154
+ nodesUptimeFilter !== NodesUptimeFilterValues.All
155
+ ) {
156
+ message = i18n('empty.several_filters');
157
+ }
158
+
159
+ if (message) {
160
+ return <EmptyFilter title={message} showAll={i18n('show_all')} onShowAll={onShowAll} />;
161
+ }
144
162
  }
145
163
 
146
164
  return data ? (
@@ -1,6 +1,8 @@
1
1
  {
2
- "empty.default": "No such nodes",
3
- "empty.out_of_space": "No nodes with out of space errors",
4
- "empty.degraded": "No degraded nodes",
5
- "show_all": "Show all nodes"
2
+ "empty.default": "No such nodes",
3
+ "empty.out_of_space": "No nodes with out of space errors",
4
+ "empty.degraded": "No degraded nodes",
5
+ "empty.small_uptime": "No nodes with uptime < 1h",
6
+ "empty.several_filters": "No nodes match current filters combination",
7
+ "show_all": "Show all nodes"
6
8
  }
@@ -1,6 +1,8 @@
1
1
  {
2
- "empty.default": "Нет узлов",
3
- "empty.out_of_space": "Нет узлов, в которых кончается место",
4
- "empty.degraded": "Нет деградировавших узлов",
5
- "show_all": "Показать все узлы"
2
+ "empty.default": "Нет узлов",
3
+ "empty.out_of_space": "Нет узлов, в которых кончается место",
4
+ "empty.degraded": "Нет деградировавших узлов",
5
+ "empty.small_uptime": "Нет узлов с uptime < 1h",
6
+ "empty.several_filters": "Нет узлов, подходящих под текущие фильтры",
7
+ "show_all": "Показать все узлы"
6
8
  }