ydb-embedded-ui 4.14.0 → 4.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/containers/App/App.js +1 -1
  3. package/dist/containers/AsideNavigation/AsideNavigation.tsx +1 -1
  4. package/dist/containers/Authentication/Authentication.tsx +1 -1
  5. package/dist/containers/Storage/Storage.tsx +64 -32
  6. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +56 -73
  7. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +33 -43
  8. package/dist/containers/Storage/utils/index.ts +3 -3
  9. package/dist/containers/Tenant/i18n/en.json +3 -0
  10. package/dist/containers/Tenant/i18n/ru.json +3 -0
  11. package/dist/containers/Tenant/utils/queryTemplates.ts +113 -0
  12. package/dist/containers/Tenant/utils/schema.ts +1 -1
  13. package/dist/containers/Tenant/utils/schemaActions.ts +27 -44
  14. package/dist/containers/UserSettings/i18n/en.json +1 -1
  15. package/dist/containers/UserSettings/i18n/ru.json +1 -1
  16. package/dist/{reportWebVitals.js → reportWebVitals.ts} +3 -1
  17. package/dist/services/api.ts +6 -4
  18. package/dist/store/reducers/{authentication.js → authentication/authentication.ts} +14 -7
  19. package/dist/store/reducers/authentication/types.ts +15 -0
  20. package/dist/store/reducers/describe.ts +1 -16
  21. package/dist/store/reducers/index.ts +1 -1
  22. package/dist/store/reducers/storage/selectors.ts +50 -150
  23. package/dist/store/reducers/storage/storage.ts +73 -25
  24. package/dist/store/reducers/storage/types.ts +49 -17
  25. package/dist/store/reducers/storage/utils.ts +207 -0
  26. package/dist/store/utils.ts +1 -1
  27. package/dist/types/api/error.ts +4 -0
  28. package/dist/types/api/storage.ts +32 -4
  29. package/dist/types/window.d.ts +1 -0
  30. package/dist/utils/hooks/index.ts +1 -0
  31. package/dist/utils/hooks/useStorageRequestParams.ts +28 -0
  32. package/dist/utils/hooks/useTableSort.ts +1 -1
  33. package/dist/utils/storage.ts +31 -3
  34. package/package.json +1 -5
  35. package/dist/HOCS/WithSearch/WithSearch.js +0 -26
  36. package/dist/HOCS/index.js +0 -1
  37. package/dist/components/Hotkey/Hotkey.js +0 -102
  38. package/dist/components/Pagination/Pagination.js +0 -63
  39. package/dist/components/Pagination/Pagination.scss +0 -28
  40. package/dist/types/store/storage.ts +0 -12
  41. /package/dist/{index.js → index.tsx} +0 -0
  42. /package/dist/utils/{monaco.js → monaco.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.15.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.15.0...v4.15.1) (2023-08-21)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **SchemaTree:** update create table template ([#512](https://github.com/ydb-platform/ydb-embedded-ui/issues/512)) ([712b3f3](https://github.com/ydb-platform/ydb-embedded-ui/commit/712b3f3612b09fdc5c850ffc3a984cd86827e5b9))
9
+
10
+ ## [4.15.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.14.0...v4.15.0) (2023-08-17)
11
+
12
+
13
+ ### Features
14
+
15
+ * **SchemaTree:** add actions for topic ([#507](https://github.com/ydb-platform/ydb-embedded-ui/issues/507)) ([6700136](https://github.com/ydb-platform/ydb-embedded-ui/commit/670013629cb68425e670969323a2ef466ef7c018))
16
+ * **Storage:** sort on backend ([#510](https://github.com/ydb-platform/ydb-embedded-ui/issues/510)) ([034a89a](https://github.com/ydb-platform/ydb-embedded-ui/commit/034a89a9844021c5ea3a73c8f6456e35128078c0))
17
+ * **Storage:** v2 api and backend filters ([#506](https://github.com/ydb-platform/ydb-embedded-ui/issues/506)) ([ce4bf6d](https://github.com/ydb-platform/ydb-embedded-ui/commit/ce4bf6d0ef154b87a7b3a44d56281230b2b5b554))
18
+
3
19
  ## [4.14.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.13.0...v4.14.0) (2023-08-11)
4
20
 
5
21
 
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
5
5
  import ContentWrapper, {Content} from './Content';
6
6
  import AsideNavigation from '../AsideNavigation/AsideNavigation';
7
7
 
8
- import {getUser} from '../../store/reducers/authentication';
8
+ import {getUser} from '../../store/reducers/authentication/authentication';
9
9
  import {registerLanguages} from '../../utils/monaco';
10
10
 
11
11
  import './App.scss';
@@ -18,7 +18,7 @@ import userChecked from '../../assets/icons/user-check.svg';
18
18
  import settingsIcon from '../../assets/icons/settings.svg';
19
19
  import supportIcon from '../../assets/icons/support.svg';
20
20
 
21
- import {logout} from '../../store/reducers/authentication';
21
+ import {logout} from '../../store/reducers/authentication/authentication';
22
22
  import {getParsedSettingValue, setSettingValue} from '../../store/reducers/settings/settings';
23
23
  import {TENANT_PAGE, TENANT_PAGES_IDS} from '../../store/reducers/tenant/constants';
24
24
  import routes, {TENANT, createHref, parseQuery} from '../../routes';
@@ -5,7 +5,7 @@ import cn from 'bem-cn-lite';
5
5
 
6
6
  import {Button, TextInput, Icon, Link as ExternalLink} from '@gravity-ui/uikit';
7
7
 
8
- import {authenticate} from '../../store/reducers/authentication';
8
+ import {authenticate} from '../../store/reducers/authentication/authentication';
9
9
  import {useTypedSelector} from '../../utils/hooks';
10
10
 
11
11
  import ydbLogoIcon from '../../assets/icons/ydb.svg';
@@ -2,8 +2,6 @@ import {useCallback, useEffect} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
- import DataTable, {Settings} from '@gravity-ui/react-data-table';
6
-
7
5
  import {Search} from '../../components/Search';
8
6
  import {UptimeFilter} from '../../components/UptimeFIlter';
9
7
  import {AccessDenied} from '../../components/Errors/403';
@@ -11,9 +9,13 @@ import {EntitiesCount} from '../../components/EntitiesCount';
11
9
  import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
12
10
  import {ResponseError} from '../../components/Errors/ResponseError';
13
11
 
14
- import type {StorageType, VisibleEntities} from '../../store/reducers/storage/types';
12
+ import type {
13
+ StorageSortParams,
14
+ StorageType,
15
+ VisibleEntities,
16
+ } from '../../store/reducers/storage/types';
17
+ import type {NodesSortParams} from '../../store/reducers/nodes/types';
15
18
  import {
16
- getStorageInfo,
17
19
  setInitialState,
18
20
  setVisibleEntities,
19
21
  setStorageTextFilter,
@@ -21,17 +23,28 @@ import {
21
23
  setStorageType,
22
24
  setNodesUptimeFilter,
23
25
  setDataWasNotLoaded,
26
+ getStorageNodesInfo,
27
+ getStorageGroupsInfo,
28
+ setNodesSortParams,
29
+ setGroupsSortParams,
24
30
  } from '../../store/reducers/storage/storage';
25
31
  import {
26
32
  selectFilteredGroups,
27
33
  selectFilteredNodes,
28
- selectStorageNodesCount,
29
- selectStorageGroupsCount,
34
+ selectEntitiesCount,
30
35
  selectUsageFilterOptions,
36
+ selectNodesSortParams,
37
+ selectGroupsSortParams,
31
38
  } from '../../store/reducers/storage/selectors';
32
39
  import {VISIBLE_ENTITIES, STORAGE_TYPES} from '../../store/reducers/storage/constants';
33
40
  import {getNodesList, selectNodesMap} from '../../store/reducers/nodesList';
34
- import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
41
+ import {
42
+ useAutofetcher,
43
+ useNodesRequestParams,
44
+ useStorageRequestParams,
45
+ useTableSort,
46
+ useTypedSelector,
47
+ } from '../../utils/hooks';
35
48
  import {AdditionalNodesInfo, NodesUptimeFilterValues} from '../../utils/nodes';
36
49
  import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
37
50
 
@@ -45,11 +58,6 @@ import './Storage.scss';
45
58
 
46
59
  const b = cn('global-storage');
47
60
 
48
- const tableSettings: Settings = {
49
- ...DEFAULT_TABLE_SETTINGS,
50
- defaultOrder: DataTable.DESCENDING,
51
- };
52
-
53
61
  interface StorageProps {
54
62
  additionalNodesInfo?: AdditionalNodesInfo;
55
63
  tenant?: string;
@@ -72,10 +80,11 @@ export const Storage = ({additionalNodesInfo, tenant, nodeId}: StorageProps) =>
72
80
  } = useTypedSelector((state) => state.storage);
73
81
  const storageNodes = useTypedSelector(selectFilteredNodes);
74
82
  const storageGroups = useTypedSelector(selectFilteredGroups);
75
- const nodesCount = useTypedSelector(selectStorageNodesCount);
76
- const groupsCount = useTypedSelector(selectStorageGroupsCount);
83
+ const entitiesCount = useTypedSelector(selectEntitiesCount);
77
84
  const nodesMap = useTypedSelector(selectNodesMap);
78
85
  const usageFilterOptions = useTypedSelector(selectUsageFilterOptions);
86
+ const nodesSortParams = useTypedSelector(selectNodesSortParams);
87
+ const groupsSortParams = useTypedSelector(selectGroupsSortParams);
79
88
 
80
89
  useEffect(() => {
81
90
  dispatch(getNodesList());
@@ -86,27 +95,47 @@ export const Storage = ({additionalNodesInfo, tenant, nodeId}: StorageProps) =>
86
95
  };
87
96
  }, [dispatch]);
88
97
 
98
+ const nodesRequestParams = useNodesRequestParams({
99
+ filter,
100
+ nodesUptimeFilter,
101
+ ...nodesSortParams,
102
+ });
103
+ const storageRequestParams = useStorageRequestParams({
104
+ filter,
105
+ ...groupsSortParams,
106
+ });
107
+
108
+ const [nodesSort, handleNodesSort] = useTableSort(nodesSortParams, (params) =>
109
+ dispatch(setNodesSortParams(params as NodesSortParams)),
110
+ );
111
+ const [groupsSort, handleGroupsSort] = useTableSort(groupsSortParams, (params) =>
112
+ dispatch(setGroupsSortParams(params as StorageSortParams)),
113
+ );
114
+
89
115
  const fetchData = useCallback(
90
116
  (isBackground: boolean) => {
91
117
  if (!isBackground) {
92
118
  dispatch(setDataWasNotLoaded());
93
119
  }
94
120
 
95
- dispatch(
96
- getStorageInfo(
97
- {
98
- tenant,
99
- nodeId,
100
- visibleEntities,
101
- type: storageType,
102
- },
103
- {
104
- concurrentId: 'getStorageInfo',
105
- },
106
- ),
107
- );
121
+ const nodesParams = nodesRequestParams || {};
122
+ const storageParams = storageRequestParams || {};
123
+
124
+ if (storageType === STORAGE_TYPES.nodes) {
125
+ dispatch(getStorageNodesInfo({tenant, visibleEntities, ...nodesParams}));
126
+ } else {
127
+ dispatch(getStorageGroupsInfo({tenant, visibleEntities, nodeId, ...storageParams}));
128
+ }
108
129
  },
109
- [dispatch, tenant, nodeId, visibleEntities, storageType],
130
+ [
131
+ dispatch,
132
+ tenant,
133
+ nodeId,
134
+ visibleEntities,
135
+ storageType,
136
+ storageRequestParams,
137
+ nodesRequestParams,
138
+ ],
110
139
  );
111
140
 
112
141
  const autorefreshEnabled = tenant ? autorefresh : true;
@@ -145,9 +174,11 @@ export const Storage = ({additionalNodesInfo, tenant, nodeId}: StorageProps) =>
145
174
  <StorageGroups
146
175
  visibleEntities={visibleEntities}
147
176
  data={storageGroups}
148
- tableSettings={tableSettings}
177
+ tableSettings={DEFAULT_TABLE_SETTINGS}
149
178
  nodes={nodesMap}
150
179
  onShowAll={() => handleGroupVisibilityChange(VISIBLE_ENTITIES.all)}
180
+ sort={groupsSort}
181
+ handleSort={handleGroupsSort}
151
182
  />
152
183
  )}
153
184
  {storageType === STORAGE_TYPES.nodes && (
@@ -155,9 +186,11 @@ export const Storage = ({additionalNodesInfo, tenant, nodeId}: StorageProps) =>
155
186
  visibleEntities={visibleEntities}
156
187
  nodesUptimeFilter={nodesUptimeFilter}
157
188
  data={storageNodes}
158
- tableSettings={tableSettings}
189
+ tableSettings={DEFAULT_TABLE_SETTINGS}
159
190
  onShowAll={handleShowAllNodes}
160
191
  additionalNodesInfo={additionalNodesInfo}
192
+ sort={nodesSort}
193
+ handleSort={handleNodesSort}
161
194
  />
162
195
  )}
163
196
  </>
@@ -166,7 +199,6 @@ export const Storage = ({additionalNodesInfo, tenant, nodeId}: StorageProps) =>
166
199
 
167
200
  const renderEntitiesCount = () => {
168
201
  const entityName = storageType === STORAGE_TYPES.groups ? 'Groups' : 'Nodes';
169
- const count = storageType === STORAGE_TYPES.groups ? groupsCount : nodesCount;
170
202
  const current =
171
203
  storageType === STORAGE_TYPES.groups ? storageGroups.length : storageNodes.length;
172
204
 
@@ -174,7 +206,7 @@ export const Storage = ({additionalNodesInfo, tenant, nodeId}: StorageProps) =>
174
206
  <EntitiesCount
175
207
  label={entityName}
176
208
  loading={loading && !wasLoaded}
177
- total={count.total}
209
+ total={entitiesCount.total}
178
210
  current={current}
179
211
  />
180
212
  );
@@ -3,13 +3,15 @@ import cn from 'bem-cn-lite';
3
3
  import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
4
4
  import {Icon, Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
5
5
 
6
+ import type {ValueOf} from '../../../types/common';
6
7
  import type {NodesMap} from '../../../types/store/nodesList';
7
8
  import type {PreparedStorageGroup, VisibleEntities} from '../../../store/reducers/storage/types';
9
+ import type {HandleSort} from '../../../utils/hooks/useTableSort';
8
10
 
9
11
  import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
10
12
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
11
13
  import {stringifyVdiskId} from '../../../utils';
12
- import {getUsage, isFullVDiskData} from '../../../utils/storage';
14
+ import {getUsage, isFullVDiskData, isSortableStorageProperty} from '../../../utils/storage';
13
15
 
14
16
  import shieldIcon from '../../../assets/icons/shield.svg';
15
17
  import {Stack} from '../../../components/Stack/Stack';
@@ -22,23 +24,22 @@ import {getDegradedSeverity, getUsageSeverityForStorageGroup} from '../utils';
22
24
  import i18n from './i18n';
23
25
  import './StorageGroups.scss';
24
26
 
25
- enum TableColumnsIds {
26
- PoolName = 'PoolName',
27
- Type = 'Type',
28
- ErasureSpecies = 'ErasureSpecies',
29
- GroupID = 'GroupID',
30
- Used = 'Used',
31
- Limit = 'Limit',
32
- UsedPercents = 'UsedPercents',
33
- UsedSpaceFlag = 'UsedSpaceFlag',
34
- Read = 'Read',
35
- Write = 'Write',
36
- VDisks = 'VDisks',
37
- Missing = 'Missing',
38
- }
27
+ const TableColumnsIds = {
28
+ PoolName: 'PoolName',
29
+ Kind: 'Kind',
30
+ Erasure: 'Erasure',
31
+ GroupId: 'GroupId',
32
+ Used: 'Used',
33
+ Limit: 'Limit',
34
+ Usage: 'Usage',
35
+ UsedSpaceFlag: 'UsedSpaceFlag',
36
+ Read: 'Read',
37
+ Write: 'Write',
38
+ VDisks: 'VDisks',
39
+ Degraded: 'Degraded',
40
+ } as const;
39
41
 
40
- type TableColumnsIdsKeys = keyof typeof TableColumnsIds;
41
- type TableColumnsIdsValues = typeof TableColumnsIds[TableColumnsIdsKeys];
42
+ type TableColumnId = ValueOf<typeof TableColumnsIds>;
42
43
 
43
44
  interface StorageGroupsProps {
44
45
  data: PreparedStorageGroup[];
@@ -46,59 +47,37 @@ interface StorageGroupsProps {
46
47
  tableSettings: Settings;
47
48
  visibleEntities: VisibleEntities;
48
49
  onShowAll?: VoidFunction;
50
+ sort?: SortOrder;
51
+ handleSort?: HandleSort;
49
52
  }
50
53
 
51
- const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
54
+ const tableColumnsNames: Record<TableColumnId, string> = {
52
55
  PoolName: 'Pool Name',
53
- Type: 'Type',
54
- ErasureSpecies: 'Erasure',
55
- GroupID: 'Group ID',
56
+ Kind: 'Type',
57
+ Erasure: 'Erasure',
58
+ GroupId: 'Group ID',
56
59
  Used: 'Used',
57
60
  Limit: 'Limit',
58
61
  UsedSpaceFlag: 'Space',
59
- UsedPercents: 'Usage',
62
+ Usage: 'Usage',
60
63
  Read: 'Read',
61
64
  Write: 'Write',
62
65
  VDisks: 'VDisks',
63
- Missing: 'Degraded',
66
+ Degraded: 'Degraded',
64
67
  };
65
68
 
66
69
  const b = cn('global-storage-groups');
67
70
 
68
- function setSortOrder(visibleEntities: VisibleEntities): SortOrder | undefined {
69
- switch (visibleEntities) {
70
- case VISIBLE_ENTITIES.all: {
71
- return {
72
- columnId: TableColumnsIds.PoolName,
73
- order: DataTable.ASCENDING,
74
- };
75
- }
76
- case VISIBLE_ENTITIES.missing: {
77
- return {
78
- columnId: TableColumnsIds.Missing,
79
- order: DataTable.DESCENDING,
80
- };
81
- }
82
- case VISIBLE_ENTITIES.space: {
83
- return {
84
- columnId: TableColumnsIds.UsedSpaceFlag,
85
- order: DataTable.DESCENDING,
86
- };
87
- }
88
- default: {
89
- return undefined;
90
- }
91
- }
92
- }
93
-
94
71
  export function StorageGroups({
95
72
  data,
96
73
  tableSettings,
97
74
  visibleEntities,
98
75
  nodes,
99
76
  onShowAll,
77
+ sort,
78
+ handleSort,
100
79
  }: StorageGroupsProps) {
101
- const allColumns: Column<PreparedStorageGroup>[] = [
80
+ const rawColumns: Column<PreparedStorageGroup>[] = [
102
81
  {
103
82
  name: TableColumnsIds.PoolName,
104
83
  header: tableColumnsNames[TableColumnsIds.PoolName],
@@ -124,12 +103,12 @@ export function StorageGroups({
124
103
  align: DataTable.LEFT,
125
104
  },
126
105
  {
127
- name: TableColumnsIds.Type,
128
- header: tableColumnsNames[TableColumnsIds.Type],
106
+ name: TableColumnsIds.Kind,
107
+ header: tableColumnsNames[TableColumnsIds.Kind],
129
108
  // prettier-ignore
130
109
  render: ({row}) => (
131
110
  <>
132
- <Label>{row.Type || '—'}</Label>
111
+ <Label>{row.Kind || '—'}</Label>
133
112
  {' '}
134
113
  {row.Encryption && (
135
114
  <Popover
@@ -146,18 +125,18 @@ export function StorageGroups({
146
125
  ),
147
126
  },
148
127
  {
149
- name: TableColumnsIds.ErasureSpecies,
150
- header: tableColumnsNames[TableColumnsIds.ErasureSpecies],
128
+ name: TableColumnsIds.Erasure,
129
+ header: tableColumnsNames[TableColumnsIds.Erasure],
151
130
  render: ({row}) => (row.ErasureSpecies ? row.ErasureSpecies : '-'),
152
131
  align: DataTable.LEFT,
153
132
  },
154
133
  {
155
- name: TableColumnsIds.Missing,
156
- header: tableColumnsNames[TableColumnsIds.Missing],
134
+ name: TableColumnsIds.Degraded,
135
+ header: tableColumnsNames[TableColumnsIds.Degraded],
157
136
  width: 100,
158
137
  render: ({row}) =>
159
- row.Missing ? (
160
- <Label theme={getDegradedSeverity(row)}>Degraded: {row.Missing}</Label>
138
+ row.Degraded ? (
139
+ <Label theme={getDegradedSeverity(row)}>Degraded: {row.Degraded}</Label>
161
140
  ) : (
162
141
  '-'
163
142
  ),
@@ -165,19 +144,18 @@ export function StorageGroups({
165
144
  defaultOrder: DataTable.DESCENDING,
166
145
  },
167
146
  {
168
- name: TableColumnsIds.UsedPercents,
169
- header: tableColumnsNames[TableColumnsIds.UsedPercents],
147
+ name: TableColumnsIds.Usage,
148
+ header: tableColumnsNames[TableColumnsIds.Usage],
170
149
  width: 100,
171
150
  render: ({row}) => {
172
- const usage = getUsage(row, 5);
173
151
  // without a limit the usage can be evaluated as 0,
174
152
  // but the absence of a value is more clear
175
153
  return row.Limit ? (
176
154
  <Label
177
- theme={getUsageSeverityForStorageGroup(usage)}
178
- className={b('usage-label', {overload: usage >= 90})}
155
+ theme={getUsageSeverityForStorageGroup(row.Usage)}
156
+ className={b('usage-label', {overload: row.Usage >= 90})}
179
157
  >
180
- {usage}%
158
+ {row.Usage}%
181
159
  </Label>
182
160
  ) : (
183
161
  '-'
@@ -188,12 +166,13 @@ export function StorageGroups({
188
166
  align: DataTable.LEFT,
189
167
  },
190
168
  {
191
- name: TableColumnsIds.GroupID,
192
- header: tableColumnsNames[TableColumnsIds.GroupID],
169
+ name: TableColumnsIds.GroupId,
170
+ header: tableColumnsNames[TableColumnsIds.GroupId],
193
171
  width: 130,
194
172
  render: ({row}) => {
195
173
  return <span className={b('group-id')}>{row.GroupID}</span>;
196
174
  },
175
+ sortAccessor: (row) => Number(row.GroupID),
197
176
  align: DataTable.RIGHT,
198
177
  },
199
178
  {
@@ -297,18 +276,21 @@ export function StorageGroups({
297
276
  },
298
277
  ];
299
278
 
300
- let columns = allColumns;
279
+ let columns = rawColumns.map((column) => ({
280
+ ...column,
281
+ sortable: isSortableStorageProperty(column.name),
282
+ }));
301
283
 
302
284
  if (visibleEntities === VISIBLE_ENTITIES.all) {
303
- columns = allColumns.filter((col) => {
285
+ columns = columns.filter((col) => {
304
286
  return (
305
- col.name !== TableColumnsIds.Missing && col.name !== TableColumnsIds.UsedSpaceFlag
287
+ col.name !== TableColumnsIds.Degraded && col.name !== TableColumnsIds.UsedSpaceFlag
306
288
  );
307
289
  });
308
290
  }
309
291
 
310
292
  if (visibleEntities === VISIBLE_ENTITIES.space) {
311
- columns = allColumns.filter((col) => col.name !== TableColumnsIds.Missing);
293
+ columns = columns.filter((col) => col.name !== TableColumnsIds.Degraded);
312
294
 
313
295
  if (!data.length) {
314
296
  return (
@@ -322,7 +304,7 @@ export function StorageGroups({
322
304
  }
323
305
 
324
306
  if (visibleEntities === VISIBLE_ENTITIES.missing) {
325
- columns = allColumns.filter((col) => col.name !== TableColumnsIds.UsedSpaceFlag);
307
+ columns = columns.filter((col) => col.name !== TableColumnsIds.UsedSpaceFlag);
326
308
 
327
309
  if (!data.length) {
328
310
  return (
@@ -342,8 +324,9 @@ export function StorageGroups({
342
324
  data={data}
343
325
  columns={columns}
344
326
  settings={tableSettings}
345
- initialSortOrder={setSortOrder(visibleEntities)}
346
327
  emptyDataMessage={i18n('empty.default')}
328
+ sortOrder={sort}
329
+ onSort={handleSort}
347
330
  />
348
331
  ) : null;
349
332
  }
@@ -2,11 +2,14 @@ import cn from 'bem-cn-lite';
2
2
 
3
3
  import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
4
4
 
5
+ import type {ValueOf} from '../../../types/common';
5
6
  import type {PreparedStorageNode, VisibleEntities} from '../../../store/reducers/storage/types';
7
+ import type {HandleSort} from '../../../utils/hooks/useTableSort';
6
8
 
7
9
  import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
8
10
  import {
9
11
  AdditionalNodesInfo,
12
+ isSortableNodesProperty,
10
13
  isUnavailableNode,
11
14
  NodesUptimeFilterValues,
12
15
  } from '../../../utils/nodes';
@@ -19,18 +22,17 @@ import {PDisk} from '../PDisk';
19
22
  import i18n from './i18n';
20
23
  import './StorageNodes.scss';
21
24
 
22
- enum TableColumnsIds {
23
- NodeId = 'NodeId',
24
- FQDN = 'FQDN',
25
- DataCenter = 'DataCenter',
26
- Rack = 'Rack',
27
- Uptime = 'Uptime',
28
- PDisks = 'PDisks',
29
- Missing = 'Missing',
30
- }
25
+ const TableColumnsIds = {
26
+ NodeId: 'NodeId',
27
+ Host: 'Host',
28
+ DC: 'DC',
29
+ Rack: 'Rack',
30
+ Uptime: 'Uptime',
31
+ PDisks: 'PDisks',
32
+ Missing: 'Missing',
33
+ } as const;
31
34
 
32
- type TableColumnsIdsKeys = keyof typeof TableColumnsIds;
33
- type TableColumnsIdsValues = typeof TableColumnsIds[TableColumnsIdsKeys];
35
+ type TableColumnId = ValueOf<typeof TableColumnsIds>;
34
36
 
35
37
  interface StorageNodesProps {
36
38
  data: PreparedStorageNode[];
@@ -39,12 +41,14 @@ interface StorageNodesProps {
39
41
  nodesUptimeFilter: keyof typeof NodesUptimeFilterValues;
40
42
  onShowAll?: VoidFunction;
41
43
  additionalNodesInfo?: AdditionalNodesInfo;
44
+ sort?: SortOrder;
45
+ handleSort?: HandleSort;
42
46
  }
43
47
 
44
- const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
48
+ const tableColumnsNames: Record<TableColumnId, string> = {
45
49
  NodeId: 'Node ID',
46
- FQDN: 'FQDN',
47
- DataCenter: 'DC',
50
+ Host: 'Host',
51
+ DC: 'DC',
48
52
  Rack: 'Rack',
49
53
  Uptime: 'Uptime',
50
54
  PDisks: 'PDisks',
@@ -53,26 +57,6 @@ const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
53
57
 
54
58
  const b = cn('global-storage-nodes');
55
59
 
56
- function setSortOrder(visibleEntities: VisibleEntities): SortOrder | undefined {
57
- switch (visibleEntities) {
58
- case VISIBLE_ENTITIES.all: {
59
- return {
60
- columnId: TableColumnsIds.NodeId,
61
- order: DataTable.ASCENDING,
62
- };
63
- }
64
- case VISIBLE_ENTITIES.missing: {
65
- return {
66
- columnId: TableColumnsIds.Missing,
67
- order: DataTable.DESCENDING,
68
- };
69
- }
70
- default: {
71
- return undefined;
72
- }
73
- }
74
- }
75
-
76
60
  export function StorageNodes({
77
61
  data,
78
62
  tableSettings,
@@ -80,10 +64,12 @@ export function StorageNodes({
80
64
  onShowAll,
81
65
  nodesUptimeFilter,
82
66
  additionalNodesInfo,
67
+ sort,
68
+ handleSort,
83
69
  }: StorageNodesProps) {
84
70
  const getNodeRef = additionalNodesInfo?.getNodeRef;
85
71
 
86
- const allColumns: Column<PreparedStorageNode>[] = [
72
+ const rawColumns: Column<PreparedStorageNode>[] = [
87
73
  {
88
74
  name: TableColumnsIds.NodeId,
89
75
  header: tableColumnsNames[TableColumnsIds.NodeId],
@@ -91,8 +77,8 @@ export function StorageNodes({
91
77
  align: DataTable.RIGHT,
92
78
  },
93
79
  {
94
- name: TableColumnsIds.FQDN,
95
- header: tableColumnsNames[TableColumnsIds.FQDN],
80
+ name: TableColumnsIds.Host,
81
+ header: tableColumnsNames[TableColumnsIds.Host],
96
82
  width: 350,
97
83
  render: ({row}) => {
98
84
  return <NodeHostWrapper node={row} getNodeRef={getNodeRef} />;
@@ -100,8 +86,8 @@ export function StorageNodes({
100
86
  align: DataTable.LEFT,
101
87
  },
102
88
  {
103
- name: TableColumnsIds.DataCenter,
104
- header: tableColumnsNames[TableColumnsIds.DataCenter],
89
+ name: TableColumnsIds.DC,
90
+ header: tableColumnsNames[TableColumnsIds.DC],
105
91
  render: ({row}) => row.DataCenter || '—',
106
92
  align: DataTable.LEFT,
107
93
  },
@@ -144,10 +130,13 @@ export function StorageNodes({
144
130
  },
145
131
  ];
146
132
 
147
- let columns = allColumns;
133
+ let columns = rawColumns.map((column) => ({
134
+ ...column,
135
+ sortable: isSortableNodesProperty(column.name),
136
+ }));
148
137
 
149
- if (visibleEntities === VISIBLE_ENTITIES.space) {
150
- columns = allColumns.filter((col) => col.name !== TableColumnsIds.Missing);
138
+ if (visibleEntities !== VISIBLE_ENTITIES.missing) {
139
+ columns = columns.filter((col) => col.name !== TableColumnsIds.Missing);
151
140
  }
152
141
 
153
142
  if (!data.length) {
@@ -187,9 +176,10 @@ export function StorageNodes({
187
176
  ...tableSettings,
188
177
  dynamicRenderType: 'variable',
189
178
  }}
190
- initialSortOrder={setSortOrder(visibleEntities)}
191
179
  emptyDataMessage={i18n('empty.default')}
192
180
  rowClassName={(row) => b('node', {unavailable: isUnavailableNode(row)})}
181
+ sortOrder={sort}
182
+ onSort={handleSort}
193
183
  />
194
184
  ) : null;
195
185
  }
@@ -1,4 +1,4 @@
1
- import type {IStoragePoolGroup} from '../../../types/store/storage';
1
+ import type {PreparedStorageGroup} from '../../../store/reducers/storage/types';
2
2
  import {EFlag} from '../../../types/api/enums';
3
3
 
4
4
  export * from './constants';
@@ -35,12 +35,12 @@ const degradationEvaluators = {
35
35
  const canEvaluateErasureSpecies = (value?: string): value is keyof typeof degradationEvaluators =>
36
36
  value !== undefined && value in degradationEvaluators;
37
37
 
38
- export const getDegradedSeverity = (group: IStoragePoolGroup) => {
38
+ export const getDegradedSeverity = (group: PreparedStorageGroup) => {
39
39
  const evaluate = canEvaluateErasureSpecies(group.ErasureSpecies)
40
40
  ? degradationEvaluators[group.ErasureSpecies]
41
41
  : defaultDegradationEvaluator;
42
42
 
43
- return evaluate(group.Missing);
43
+ return evaluate(group.Degraded);
44
44
  };
45
45
 
46
46
  export const getUsageSeverityForStorageGroup = generateEvaluator(80, 85, [
@@ -14,8 +14,11 @@
14
14
  "actions.openPreview": "Open preview",
15
15
  "actions.createTable": "Create table...",
16
16
  "actions.createExternalTable": "Create external table...",
17
+ "actions.createTopic": "Create topic...",
17
18
  "actions.dropTable": "Drop table...",
19
+ "actions.dropTopic": "Drop topic...",
18
20
  "actions.alterTable": "Alter table...",
21
+ "actions.alterTopic": "Alter topic...",
19
22
  "actions.selectQuery": "Select query...",
20
23
  "actions.upsertQuery": "Upsert query..."
21
24
  }