ydb-embedded-ui 2.4.4 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
  }