ydb-embedded-ui 2.4.4 → 2.5.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 (49) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/InfoViewer/formatters/schema.ts +2 -1
  3. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +3 -16
  4. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +8 -13
  5. package/dist/components/InfoViewer/schemaInfo/TableIndexInfo.tsx +2 -12
  6. package/dist/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +3 -16
  7. package/dist/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +8 -13
  8. package/dist/components/InfoViewer/utils.ts +6 -6
  9. package/dist/components/Loader/Loader.scss +6 -3
  10. package/dist/components/Loader/Loader.tsx +7 -5
  11. package/dist/components/Loader/index.ts +1 -0
  12. package/dist/components/UptimeFIlter/UptimeFilter.tsx +21 -0
  13. package/dist/components/UptimeFIlter/index.ts +1 -0
  14. package/dist/containers/Node/Node.scss +1 -0
  15. package/dist/containers/Node/Node.tsx +3 -8
  16. package/dist/containers/Node/NodeStructure/NodeStructure.scss +0 -6
  17. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +1 -1
  18. package/dist/containers/Nodes/Nodes.js +22 -10
  19. package/dist/{components → containers}/NodesViewer/NodesViewer.js +49 -62
  20. package/dist/{components → containers}/NodesViewer/NodesViewer.scss +0 -0
  21. package/dist/containers/Storage/Pdisk/Pdisk.scss +1 -1
  22. package/dist/containers/Storage/Storage.js +35 -10
  23. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +2 -2
  24. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +35 -17
  25. package/dist/containers/Storage/StorageNodes/i18n/en.json +6 -4
  26. package/dist/containers/Storage/StorageNodes/i18n/ru.json +6 -4
  27. package/dist/containers/Storage/UsageFilter/UsageFilter.scss +10 -5
  28. package/dist/containers/Tenant/Acl/Acl.js +1 -7
  29. package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +1 -1
  30. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +34 -8
  31. package/dist/containers/Tenant/Diagnostics/Describe/Describe.scss +0 -8
  32. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +2 -6
  33. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +0 -7
  34. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +5 -7
  35. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +21 -13
  36. package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +1 -5
  37. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +4 -4
  38. package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -4
  39. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +23 -28
  40. package/dist/containers/Tenant/TenantPages.tsx +1 -1
  41. package/dist/containers/Tenant/utils/schemaActions.ts +9 -20
  42. package/dist/store/reducers/clusterNodes.js +29 -10
  43. package/dist/store/reducers/nodes.js +24 -3
  44. package/dist/store/reducers/{schema.js → schema.ts} +22 -14
  45. package/dist/store/reducers/storage.js +46 -5
  46. package/dist/types/store/schema.ts +46 -0
  47. package/dist/utils/index.js +6 -2
  48. package/dist/utils/nodes.ts +9 -0
  49. package/package.json +1 -1
@@ -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,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,7 @@ import {
22
24
  getNodesObject,
23
25
  StorageTypes,
24
26
  setStorageType,
27
+ setNodesUptimeFilter,
25
28
  VisibleEntitiesTitles,
26
29
  getStoragePoolsGroupsCount,
27
30
  getStorageNodesCount,
@@ -66,6 +69,8 @@ class Storage extends React.Component {
66
69
  setHeader: PropTypes.func,
67
70
  tenant: PropTypes.string,
68
71
  nodeId: PropTypes.string,
72
+ nodesUptimeFilter: PropTypes.string,
73
+ setNodesUptimeFilter: PropTypes.func,
69
74
  };
70
75
 
71
76
  componentDidMount() {
@@ -166,7 +171,8 @@ class Storage extends React.Component {
166
171
  }
167
172
 
168
173
  renderDataTable() {
169
- const {flatListStorageEntities, visibleEntities, nodes, storageType} = this.props;
174
+ const {flatListStorageEntities, visibleEntities, nodesUptimeFilter, nodes, storageType} =
175
+ this.props;
170
176
 
171
177
  return (
172
178
  <div className={b('table-wrapper')}>
@@ -182,9 +188,10 @@ class Storage extends React.Component {
182
188
  {storageType === StorageTypes.nodes && (
183
189
  <StorageNodes
184
190
  visibleEntities={visibleEntities}
191
+ nodesUptimeFilter={nodesUptimeFilter}
185
192
  data={flatListStorageEntities}
186
193
  tableSettings={tableSettings}
187
- onShowAll={() => this.onGroupVisibilityChange(VisibleEntities.All)}
194
+ onShowAll={this.onShowAllNodes}
188
195
  />
189
196
  )}
190
197
  </div>
@@ -201,6 +208,15 @@ class Storage extends React.Component {
201
208
  setStorageType(value);
202
209
  };
203
210
 
211
+ onUptimeFilterChange = (value) => {
212
+ this.props.setNodesUptimeFilter(value);
213
+ };
214
+
215
+ onShowAllNodes = () => {
216
+ this.onGroupVisibilityChange(VisibleEntities.All);
217
+ this.onUptimeFilterChange(NodesUptimeFilterValues.All);
218
+ };
219
+
204
220
  renderEntitiesCount() {
205
221
  const {storageType, groupsCount, nodesCount, flatListStorageEntities, loading, wasLoaded} =
206
222
  this.props;
@@ -230,6 +246,7 @@ class Storage extends React.Component {
230
246
  visibleEntities,
231
247
  storageType,
232
248
  usageFilter,
249
+ nodesUptimeFilter,
233
250
  setUsageFilter,
234
251
  usageFilterOptions,
235
252
  } = this.props;
@@ -247,6 +264,16 @@ class Storage extends React.Component {
247
264
  value={filter}
248
265
  />
249
266
  </div>
267
+
268
+ <RadioButton value={storageType} onUpdate={this.onStorageTypeChange}>
269
+ <RadioButton.Option value={StorageTypes.groups}>
270
+ {StorageTypes.groups}
271
+ </RadioButton.Option>
272
+ <RadioButton.Option value={StorageTypes.nodes}>
273
+ {StorageTypes.nodes}
274
+ </RadioButton.Option>
275
+ </RadioButton>
276
+
250
277
  <RadioButton value={visibleEntities} onUpdate={this.onGroupVisibilityChange}>
251
278
  <RadioButton.Option value={VisibleEntities.Missing}>
252
279
  {VisibleEntitiesTitles[VisibleEntities.Missing]}
@@ -259,14 +286,9 @@ class Storage extends React.Component {
259
286
  </RadioButton.Option>
260
287
  </RadioButton>
261
288
 
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>
289
+ {storageType === StorageTypes.nodes && (
290
+ <UptimeFilter value={nodesUptimeFilter} onChange={this.onUptimeFilterChange} />
291
+ )}
270
292
 
271
293
  {storageType === StorageTypes.groups && (
272
294
  <UsageFilter
@@ -314,6 +336,7 @@ function mapStateToProps(state) {
314
336
  type: storageType,
315
337
  filter,
316
338
  usageFilter,
339
+ nodesUptimeFilter,
317
340
  } = state.storage;
318
341
 
319
342
  return {
@@ -329,6 +352,7 @@ function mapStateToProps(state) {
329
352
  storageType,
330
353
  filter,
331
354
  usageFilter,
355
+ nodesUptimeFilter,
332
356
  usageFilterOptions: getUsageFilterOptions(state),
333
357
  };
334
358
  }
@@ -339,6 +363,7 @@ const mapDispatchToProps = {
339
363
  setStorageFilter,
340
364
  setUsageFilter,
341
365
  setVisibleEntities: setVisibleEntities,
366
+ setNodesUptimeFilter,
342
367
  getNodesList,
343
368
  setStorageType,
344
369
  setHeader,
@@ -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
  }
@@ -1,4 +1,6 @@
1
1
  .usage-filter {
2
+ min-width: 100px;
3
+
2
4
  &__option {
3
5
  flex-grow: 1;
4
6
 
@@ -10,22 +12,25 @@
10
12
  }
11
13
 
12
14
  &-meta {
13
- padding: 0 5px;
14
15
  position: relative;
15
- border-radius: 3px;
16
+
17
+ padding: 0 5px;
18
+
16
19
  font-size: var(--yc-text-caption-2-font-size);
17
20
  line-height: var(--yc-text-caption-2-line-height);
21
+
22
+ border-radius: 3px;
18
23
  }
19
24
 
20
25
  &-bar {
21
26
  position: absolute;
22
- left: 0;
27
+ z-index: -1;
23
28
  top: 0;
24
29
  bottom: 0;
25
- z-index: -1;
30
+ left: 0;
26
31
 
27
- background-color: var(--yc-color-infographics-info-medium);
28
32
  border-radius: 3px;
33
+ background-color: var(--yc-color-infographics-info-medium);
29
34
  }
30
35
  }
31
36
  }
@@ -83,13 +83,7 @@ class Acl extends React.Component {
83
83
  return null;
84
84
  }
85
85
 
86
- return (
87
- <DataTable
88
- columns={this.COLUMNS}
89
- data={acl}
90
- settings={TABLE_SETTINGS}
91
- />
92
- );
86
+ return <DataTable columns={this.COLUMNS} data={acl} settings={TABLE_SETTINGS} />;
93
87
  };
94
88
 
95
89
  renderOwner = () => {
@@ -7,7 +7,7 @@ import qs from 'qs';
7
7
 
8
8
  import {Loader} from '@gravity-ui/uikit';
9
9
 
10
- import NodesViewer from '../../../../components/NodesViewer/NodesViewer';
10
+ import NodesViewer from '../../../NodesViewer/NodesViewer';
11
11
 
12
12
  import {backend} from '../../../../store';
13
13
  import {hideTooltip, showTooltip} from '../../../../store/reducers/tooltip';
@@ -1,13 +1,20 @@
1
- import {useEffect, useState} from 'react';
1
+ import {useCallback, useEffect, useState} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import block from 'bem-cn-lite';
4
4
 
5
5
  import DataTable, {Column} from '@yandex-cloud/react-data-table';
6
6
 
7
+ import { Loader } from '../../../../components/Loader';
8
+ import {prepareQueryError} from '../../../../utils/query';
7
9
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
8
10
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
9
11
  import {Search} from '../../../../components/Search';
10
- import {getDescribe, selectConsumers} from '../../../../store/reducers/describe';
12
+ import {
13
+ getDescribe,
14
+ selectConsumers,
15
+ setCurrentDescribePath,
16
+ setDataWasNotLoaded,
17
+ } from '../../../../store/reducers/describe';
11
18
 
12
19
  import i18n from './i18n';
13
20
 
@@ -20,14 +27,25 @@ interface ConsumersProps {
20
27
  }
21
28
 
22
29
  export const Consumers = ({path}: ConsumersProps) => {
23
- const dispath = useDispatch();
30
+ const dispatch = useDispatch();
31
+
32
+ const fetchData = useCallback(
33
+ (isBackground: boolean) => {
34
+ if (!isBackground) {
35
+ dispatch(setDataWasNotLoaded());
36
+ }
37
+ dispatch(setCurrentDescribePath(path));
38
+ dispatch(getDescribe({path}));
39
+ },
24
40
 
25
- const fetchData = () => {
26
- dispath(getDescribe({path}));
27
- };
41
+ [path, dispatch],
42
+ );
43
+
44
+ const {autorefresh} = useTypedSelector((state) => state.schema);
28
45
 
29
- useAutofetcher(fetchData, [path]);
46
+ useAutofetcher(fetchData, [fetchData], autorefresh);
30
47
 
48
+ const {loading, wasLoaded, error} = useTypedSelector((state) => state.describe);
31
49
  const consumers = useTypedSelector((state) => selectConsumers(state, path));
32
50
 
33
51
  const [consumersToRender, setConsumersToRender] = useState(consumers);
@@ -59,8 +77,16 @@ export const Consumers = ({path}: ConsumersProps) => {
59
77
  },
60
78
  ];
61
79
 
80
+ if (loading && !wasLoaded) {
81
+ return <Loader size="m" />;
82
+ }
83
+
84
+ if (!loading && error) {
85
+ return <div className={b('message', 'error')}>{prepareQueryError(error)}</div>;
86
+ }
87
+
62
88
  if (consumers.length === 0) {
63
- return <div>{i18n('noConsumersMessage')}</div>;
89
+ return <div className={b('message')}>{i18n('noConsumersMessage')}</div>;
64
90
  }
65
91
 
66
92
  return (
@@ -13,14 +13,6 @@
13
13
  padding: 10px 20px 20px 0;
14
14
  }
15
15
 
16
- &__loader-container {
17
- display: flex;
18
- justify-content: center;
19
- align-items: center;
20
-
21
- height: 100%;
22
- }
23
-
24
16
  &__tree {
25
17
  @include json-tree-styles();
26
18
  }
@@ -5,7 +5,7 @@ import cn from 'bem-cn-lite';
5
5
  import JSONTree from 'react-json-inspector';
6
6
  import 'react-json-inspector/json-inspector.css';
7
7
 
8
- import {Loader} from '@gravity-ui/uikit';
8
+ import {Loader} from '../../../../components/Loader';
9
9
 
10
10
  import {prepareQueryError} from '../../../../utils/query';
11
11
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
@@ -51,11 +51,7 @@ const Describe = ({tenant}: IDescribeProps) => {
51
51
  useAutofetcher(fetchData, [fetchData], autorefresh);
52
52
 
53
53
  if (loading && !wasLoaded) {
54
- return (
55
- <div className={b('loader-container')}>
56
- <Loader size="m" />
57
- </div>
58
- );
54
+ return <Loader size="m" />;
59
55
  }
60
56
 
61
57
  if (error) {
@@ -5,13 +5,6 @@
5
5
 
6
6
  height: 100%;
7
7
 
8
- &__loader {
9
- display: flex;
10
- flex-grow: 1;
11
- justify-content: center;
12
- align-items: center;
13
- }
14
-
15
8
  &__header-wrapper {
16
9
  padding: 13px 20px 16px;
17
10