ydb-embedded-ui 4.27.1 → 4.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/assets/illustrations/dark/error.svg +32 -0
  3. package/dist/assets/illustrations/light/error.svg +32 -0
  4. package/dist/components/EmptyState/EmptyState.scss +5 -2
  5. package/dist/components/EmptyState/EmptyState.tsx +11 -3
  6. package/dist/components/ErrorBoundary/ErrorBoundary.scss +40 -0
  7. package/dist/components/ErrorBoundary/ErrorBoundary.tsx +62 -0
  8. package/dist/components/ErrorBoundary/i18n/en.json +7 -0
  9. package/dist/components/ErrorBoundary/i18n/index.ts +11 -0
  10. package/dist/components/ErrorBoundary/i18n/ru.json +7 -0
  11. package/dist/components/Errors/403/AccessDenied.tsx +4 -3
  12. package/dist/components/Illustration/Illustration.tsx +3 -1
  13. package/dist/components/ProblemFilter/ProblemFilter.tsx +1 -1
  14. package/dist/components/UptimeFIlter/UptimeFilter.tsx +1 -1
  15. package/dist/components/VirtualTable/VirtualTable.scss +1 -1
  16. package/dist/containers/App/App.js +5 -2
  17. package/dist/containers/Cluster/Cluster.tsx +11 -12
  18. package/dist/containers/Nodes/Nodes.tsx +4 -4
  19. package/dist/containers/Nodes/NodesWrapper.tsx +21 -0
  20. package/dist/containers/Nodes/VirtualNodes.tsx +4 -14
  21. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +1 -0
  22. package/dist/containers/Storage/EmptyFilter/EmptyFilter.tsx +1 -0
  23. package/dist/containers/Storage/PDisk/PDisk.tsx +5 -7
  24. package/dist/containers/Storage/Storage.tsx +30 -67
  25. package/dist/containers/Storage/StorageControls/StorageControls.tsx +104 -0
  26. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +18 -62
  27. package/dist/containers/Storage/StorageGroups/StorageGroupsEmptyDataMessage.tsx +30 -0
  28. package/dist/containers/Storage/StorageGroups/VirtualStorageGroups.tsx +94 -0
  29. package/dist/containers/Storage/StorageGroups/getGroups.ts +21 -0
  30. package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +73 -50
  31. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +23 -138
  32. package/dist/containers/Storage/StorageNodes/StorageNodesEmptyDataMessage.tsx +44 -0
  33. package/dist/containers/Storage/StorageNodes/VirtualStorageNodes.tsx +105 -0
  34. package/dist/containers/Storage/StorageNodes/getNodes.ts +26 -0
  35. package/dist/containers/Storage/StorageNodes/getStorageNodesColumns.tsx +125 -0
  36. package/dist/containers/Storage/StorageNodes/shared.ts +9 -0
  37. package/dist/containers/Storage/StorageTypeFilter/StorageTypeFilter.tsx +1 -1
  38. package/dist/containers/Storage/StorageVisibleEntitiesFilter/StorageVisibleEntitiesFilter.tsx +1 -1
  39. package/dist/containers/Storage/StorageWrapper.tsx +23 -0
  40. package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +3 -4
  41. package/dist/containers/Storage/VirtualStorage.tsx +112 -0
  42. package/dist/containers/Storage/i18n/en.json +7 -0
  43. package/dist/containers/Storage/i18n/index.ts +11 -0
  44. package/dist/containers/Storage/i18n/ru.json +7 -0
  45. package/dist/containers/Storage/shared.ts +3 -0
  46. package/dist/containers/Tenants/Tenants.tsx +2 -2
  47. package/dist/containers/UserSettings/i18n/en.json +2 -2
  48. package/dist/containers/UserSettings/i18n/ru.json +2 -2
  49. package/dist/containers/UserSettings/settings.ts +4 -4
  50. package/dist/index.tsx +8 -5
  51. package/dist/store/reducers/storage/selectors.ts +0 -20
  52. package/dist/utils/registerError.ts +18 -0
  53. package/package.json +7 -6
@@ -1,11 +1,7 @@
1
1
  import {useCallback, useEffect} from 'react';
2
2
  import {useDispatch} from 'react-redux';
3
- import cn from 'bem-cn-lite';
4
3
 
5
- import {Search} from '../../components/Search';
6
- import {UptimeFilter} from '../../components/UptimeFIlter';
7
4
  import {AccessDenied} from '../../components/Errors/403';
8
- import {EntitiesCount} from '../../components/EntitiesCount';
9
5
  import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
10
6
  import {ResponseError} from '../../components/Errors/ResponseError';
11
7
 
@@ -51,14 +47,11 @@ import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
51
47
 
52
48
  import {StorageGroups} from './StorageGroups/StorageGroups';
53
49
  import {StorageNodes} from './StorageNodes/StorageNodes';
54
- import {StorageTypeFilter} from './StorageTypeFilter/StorageTypeFilter';
55
- import {StorageVisibleEntitiesFilter} from './StorageVisibleEntitiesFilter/StorageVisibleEntitiesFilter';
56
- import {UsageFilter} from './UsageFilter';
50
+ import {StorageControls} from './StorageControls/StorageControls';
51
+ import {b} from './shared';
57
52
 
58
53
  import './Storage.scss';
59
54
 
60
- const b = cn('global-storage');
61
-
62
55
  interface StorageProps {
63
56
  additionalNodesProps?: AdditionalNodesProps;
64
57
  tenant?: string;
@@ -155,16 +148,16 @@ export const Storage = ({additionalNodesProps, tenant, nodeId}: StorageProps) =>
155
148
  dispatch(setStorageTextFilter(value));
156
149
  };
157
150
 
158
- const handleGroupVisibilityChange = (value: string) => {
159
- dispatch(setVisibleEntities(value as VisibleEntities));
151
+ const handleGroupVisibilityChange = (value: VisibleEntities) => {
152
+ dispatch(setVisibleEntities(value));
160
153
  };
161
154
 
162
- const handleStorageTypeChange = (value: string) => {
163
- dispatch(setStorageType(value as StorageType));
155
+ const handleStorageTypeChange = (value: StorageType) => {
156
+ dispatch(setStorageType(value));
164
157
  };
165
158
 
166
- const handleUptimeFilterChange = (value: string) => {
167
- dispatch(setNodesUptimeFilter(value as NodesUptimeFilterValues));
159
+ const handleUptimeFilterChange = (value: NodesUptimeFilterValues) => {
160
+ dispatch(setNodesUptimeFilter(value));
168
161
  };
169
162
 
170
163
  const handleShowAllNodes = () => {
@@ -202,65 +195,35 @@ export const Storage = ({additionalNodesProps, tenant, nodeId}: StorageProps) =>
202
195
  );
203
196
  };
204
197
 
205
- const renderEntitiesCount = () => {
206
- const entityName = storageType === STORAGE_TYPES.groups ? 'Groups' : 'Nodes';
207
- const current =
208
- storageType === STORAGE_TYPES.groups ? storageGroups.length : storageNodes.length;
209
-
210
- return (
211
- <EntitiesCount
212
- label={entityName}
213
- loading={loading && !wasLoaded}
214
- total={entitiesCount.total}
215
- current={current}
216
- />
217
- );
218
- };
219
-
220
198
  const renderControls = () => {
221
199
  return (
222
- <>
223
- <div className={b('search')}>
224
- <Search
225
- placeholder={
226
- storageType === STORAGE_TYPES.groups
227
- ? 'Group ID, Pool name'
228
- : 'Node ID, FQDN'
229
- }
230
- onChange={handleTextFilterChange}
231
- value={filter}
232
- />
233
- </div>
234
-
235
- {!isNodePage && (
236
- <StorageTypeFilter value={storageType} onChange={handleStorageTypeChange} />
237
- )}
238
-
239
- <StorageVisibleEntitiesFilter
240
- value={visibleEntities}
241
- onChange={handleGroupVisibilityChange}
242
- />
243
-
244
- {storageType === STORAGE_TYPES.nodes && (
245
- <UptimeFilter value={nodesUptimeFilter} onChange={handleUptimeFilterChange} />
246
- )}
247
-
248
- {storageType === STORAGE_TYPES.groups && (
249
- <UsageFilter
250
- value={usageFilter}
251
- onChange={handleUsageFilterChange}
252
- groups={usageFilterOptions}
253
- disabled={usageFilterOptions.length === 0}
254
- />
255
- )}
256
- {renderEntitiesCount()}
257
- </>
200
+ <StorageControls
201
+ searchValue={filter}
202
+ handleSearchValueChange={handleTextFilterChange}
203
+ withTypeSelector={!isNodePage}
204
+ storageType={storageType}
205
+ handleStorageTypeChange={handleStorageTypeChange}
206
+ visibleEntities={visibleEntities}
207
+ handleVisibleEntitiesChange={handleGroupVisibilityChange}
208
+ nodesUptimeFilter={nodesUptimeFilter}
209
+ handleNodesUptimeFilterChange={handleUptimeFilterChange}
210
+ groupsUsageFilter={usageFilter}
211
+ groupsUsageFilterOptions={usageFilterOptions}
212
+ handleGroupsUsageFilterChange={handleUsageFilterChange}
213
+ entitiesCountCurrent={
214
+ storageType === STORAGE_TYPES.groups
215
+ ? storageGroups.length
216
+ : storageNodes.length
217
+ }
218
+ entitiesCountTotal={entitiesCount.total}
219
+ entitiesLoading={loading && !wasLoaded}
220
+ />
258
221
  );
259
222
  };
260
223
 
261
224
  if (error) {
262
225
  if (error.status === 403) {
263
- return <AccessDenied />;
226
+ return <AccessDenied position="left" />;
264
227
  }
265
228
 
266
229
  return <ResponseError error={error} />;
@@ -0,0 +1,104 @@
1
+ import {EntitiesCount} from '../../../components/EntitiesCount/EntitiesCount';
2
+ import {Search} from '../../../components/Search/Search';
3
+ import {UptimeFilter} from '../../../components/UptimeFIlter';
4
+
5
+ import type {StorageType, VisibleEntities} from '../../../store/reducers/storage/types';
6
+ import {STORAGE_TYPES} from '../../../store/reducers/storage/constants';
7
+ import {NodesUptimeFilterValues} from '../../../utils/nodes';
8
+
9
+ import {UsageFilter, type UsageFilterItem} from '../UsageFilter/UsageFilter';
10
+ import {StorageTypeFilter} from '../StorageTypeFilter/StorageTypeFilter';
11
+ import {StorageVisibleEntitiesFilter} from '../StorageVisibleEntitiesFilter/StorageVisibleEntitiesFilter';
12
+ import i18n from '../i18n';
13
+ import {b} from '../shared';
14
+
15
+ interface StorageControlsProps {
16
+ searchValue?: string;
17
+ handleSearchValueChange: (value: string) => void;
18
+
19
+ withTypeSelector?: boolean;
20
+ storageType: StorageType;
21
+ handleStorageTypeChange: (value: StorageType) => void;
22
+
23
+ visibleEntities: VisibleEntities;
24
+ handleVisibleEntitiesChange: (value: VisibleEntities) => void;
25
+
26
+ nodesUptimeFilter: NodesUptimeFilterValues;
27
+ handleNodesUptimeFilterChange: (value: NodesUptimeFilterValues) => void;
28
+
29
+ withGroupsUsageFilter?: boolean;
30
+ groupsUsageFilter?: string[];
31
+ groupsUsageFilterOptions?: UsageFilterItem[];
32
+ handleGroupsUsageFilterChange?: (value: string[]) => void;
33
+
34
+ entitiesCountCurrent: number;
35
+ entitiesCountTotal?: number;
36
+ entitiesLoading: boolean;
37
+ }
38
+
39
+ export const StorageControls = ({
40
+ searchValue,
41
+ handleSearchValueChange,
42
+
43
+ withTypeSelector,
44
+ storageType,
45
+ handleStorageTypeChange,
46
+
47
+ visibleEntities,
48
+ handleVisibleEntitiesChange,
49
+
50
+ nodesUptimeFilter,
51
+ handleNodesUptimeFilterChange,
52
+
53
+ withGroupsUsageFilter,
54
+ groupsUsageFilter,
55
+ groupsUsageFilterOptions,
56
+ handleGroupsUsageFilterChange,
57
+
58
+ entitiesCountCurrent,
59
+ entitiesCountTotal,
60
+ entitiesLoading,
61
+ }: StorageControlsProps) => {
62
+ const isNodes = storageType === STORAGE_TYPES.nodes;
63
+ const entityName = isNodes ? i18n('nodes') : i18n('groups');
64
+
65
+ return (
66
+ <>
67
+ <Search
68
+ value={searchValue}
69
+ onChange={handleSearchValueChange}
70
+ placeholder={
71
+ isNodes
72
+ ? i18n('controls_nodes-search-placeholder')
73
+ : i18n('controls_groups-search-placeholder')
74
+ }
75
+ className={b('search')}
76
+ />
77
+ {withTypeSelector && (
78
+ <StorageTypeFilter value={storageType} onChange={handleStorageTypeChange} />
79
+ )}
80
+ <StorageVisibleEntitiesFilter
81
+ value={visibleEntities}
82
+ onChange={handleVisibleEntitiesChange}
83
+ />
84
+
85
+ {isNodes && (
86
+ <UptimeFilter value={nodesUptimeFilter} onChange={handleNodesUptimeFilterChange} />
87
+ )}
88
+ {!isNodes && withGroupsUsageFilter && (
89
+ <UsageFilter
90
+ value={groupsUsageFilter}
91
+ onChange={handleGroupsUsageFilterChange}
92
+ groups={groupsUsageFilterOptions}
93
+ />
94
+ )}
95
+
96
+ <EntitiesCount
97
+ label={entityName}
98
+ loading={entitiesLoading}
99
+ total={entitiesCountTotal}
100
+ current={entitiesCountCurrent}
101
+ />
102
+ </>
103
+ );
104
+ };
@@ -1,31 +1,17 @@
1
- import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
1
+ import {useMemo} from 'react';
2
+
3
+ import DataTable, {Settings, SortOrder} from '@gravity-ui/react-data-table';
2
4
 
3
5
  import type {NodesMap} from '../../../types/store/nodesList';
4
6
  import type {PreparedStorageGroup, VisibleEntities} from '../../../store/reducers/storage/types';
5
7
  import type {HandleSort} from '../../../utils/hooks/useTableSort';
6
8
  import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
7
- import {isSortableStorageProperty} from '../../../utils/storage';
8
- import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
9
- import {getStorageGroupsColumns} from './getStorageGroupsColumns';
9
+ import {getPreparedStorageGroupsColumns} from './getStorageGroupsColumns';
10
+ import {StorageGroupsEmptyDataMessage} from './StorageGroupsEmptyDataMessage';
10
11
 
11
12
  import i18n from './i18n';
12
13
  import './StorageGroups.scss';
13
14
 
14
- const TableColumnsIds = {
15
- PoolName: 'PoolName',
16
- Kind: 'Kind',
17
- Erasure: 'Erasure',
18
- GroupId: 'GroupId',
19
- Used: 'Used',
20
- Limit: 'Limit',
21
- Usage: 'Usage',
22
- UsedSpaceFlag: 'UsedSpaceFlag',
23
- Read: 'Read',
24
- Write: 'Write',
25
- VDisks: 'VDisks',
26
- Degraded: 'Degraded',
27
- } as const;
28
-
29
15
  interface StorageGroupsProps {
30
16
  data: PreparedStorageGroup[];
31
17
  nodes?: NodesMap;
@@ -45,50 +31,20 @@ export function StorageGroups({
45
31
  sort,
46
32
  handleSort,
47
33
  }: StorageGroupsProps) {
48
- const rawColumns: Column<PreparedStorageGroup>[] = getStorageGroupsColumns(nodes);
49
-
50
- let columns = rawColumns.map((column) => ({
51
- ...column,
52
- sortable: isSortableStorageProperty(column.name),
53
- }));
54
-
55
- if (visibleEntities === VISIBLE_ENTITIES.all) {
56
- columns = columns.filter((col) => {
57
- return (
58
- col.name !== TableColumnsIds.Degraded && col.name !== TableColumnsIds.UsedSpaceFlag
59
- );
60
- });
61
- }
62
-
63
- if (visibleEntities === VISIBLE_ENTITIES.space) {
64
- columns = columns.filter((col) => col.name !== TableColumnsIds.Degraded);
65
-
66
- if (!data.length) {
67
- return (
68
- <EmptyFilter
69
- title={i18n('empty.out_of_space')}
70
- showAll={i18n('show_all')}
71
- onShowAll={onShowAll}
72
- />
73
- );
74
- }
75
- }
76
-
77
- if (visibleEntities === VISIBLE_ENTITIES.missing) {
78
- columns = columns.filter((col) => col.name !== TableColumnsIds.UsedSpaceFlag);
79
-
80
- if (!data.length) {
81
- return (
82
- <EmptyFilter
83
- title={i18n('empty.degraded')}
84
- showAll={i18n('show_all')}
85
- onShowAll={onShowAll}
86
- />
87
- );
88
- }
34
+ const columns = useMemo(() => {
35
+ return getPreparedStorageGroupsColumns(nodes, visibleEntities);
36
+ }, [nodes, visibleEntities]);
37
+
38
+ if (!data.length && visibleEntities !== VISIBLE_ENTITIES.all) {
39
+ return (
40
+ <StorageGroupsEmptyDataMessage
41
+ onShowAll={onShowAll}
42
+ visibleEntities={visibleEntities}
43
+ />
44
+ );
89
45
  }
90
46
 
91
- return data ? (
47
+ return (
92
48
  <DataTable
93
49
  key={visibleEntities}
94
50
  theme="yandex-cloud"
@@ -99,5 +55,5 @@ export function StorageGroups({
99
55
  sortOrder={sort}
100
56
  onSort={handleSort}
101
57
  />
102
- ) : null;
58
+ );
103
59
  }
@@ -0,0 +1,30 @@
1
+ import type {VisibleEntities} from '../../../store/reducers/storage/types';
2
+ import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
3
+ import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
4
+ import i18n from './i18n';
5
+
6
+ interface StorageNodesEmptyDataMessageProps {
7
+ visibleEntities?: VisibleEntities;
8
+ onShowAll?: VoidFunction;
9
+ }
10
+
11
+ export const StorageGroupsEmptyDataMessage = ({
12
+ visibleEntities,
13
+ onShowAll,
14
+ }: StorageNodesEmptyDataMessageProps) => {
15
+ let message;
16
+
17
+ if (visibleEntities === VISIBLE_ENTITIES.space) {
18
+ message = i18n('empty.out_of_space');
19
+ }
20
+
21
+ if (visibleEntities === VISIBLE_ENTITIES.missing) {
22
+ message = i18n('empty.degraded');
23
+ }
24
+
25
+ if (message) {
26
+ return <EmptyFilter title={message} showAll={i18n('show_all')} onShowAll={onShowAll} />;
27
+ }
28
+
29
+ return null;
30
+ };
@@ -0,0 +1,94 @@
1
+ import {useCallback, useMemo} from 'react';
2
+
3
+ import type {NodesMap} from '../../../types/store/nodesList';
4
+ import type {StorageSortValue} from '../../../utils/storage';
5
+ import {
6
+ VirtualTable,
7
+ type FetchData,
8
+ type RenderControls,
9
+ type RenderErrorMessage,
10
+ } from '../../../components/VirtualTable';
11
+ import type {PreparedStorageGroup, VisibleEntities} from '../../../store/reducers/storage/types';
12
+ import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
13
+
14
+ import {StorageGroupsEmptyDataMessage} from './StorageGroupsEmptyDataMessage';
15
+ import {getPreparedStorageGroupsColumns} from './getStorageGroupsColumns';
16
+ import {getStorageGroups} from './getGroups';
17
+ import i18n from './i18n';
18
+
19
+ interface VirtualStorageGroupsProps {
20
+ searchValue: string;
21
+ visibleEntities: VisibleEntities;
22
+ tenant?: string;
23
+ nodeId?: string;
24
+ nodesMap?: NodesMap;
25
+
26
+ onShowAll: VoidFunction;
27
+
28
+ parentContainer?: Element | null;
29
+ renderControls: RenderControls;
30
+ renderErrorMessage: RenderErrorMessage;
31
+ }
32
+
33
+ export const VirtualStorageGroups = ({
34
+ searchValue,
35
+ visibleEntities,
36
+ tenant,
37
+ nodeId,
38
+ nodesMap,
39
+ onShowAll,
40
+ parentContainer,
41
+ renderControls,
42
+ renderErrorMessage,
43
+ }: VirtualStorageGroupsProps) => {
44
+ const filters = useMemo(() => {
45
+ return [searchValue, visibleEntities, tenant, nodeId];
46
+ }, [searchValue, visibleEntities, tenant, nodeId]);
47
+
48
+ const fetchData = useCallback<FetchData<PreparedStorageGroup>>(
49
+ async (limit, offset, {sortOrder, columnId} = {}) => {
50
+ return await getStorageGroups({
51
+ limit,
52
+ offset,
53
+ filter: searchValue,
54
+ visibleEntities,
55
+ tenant,
56
+ nodeId,
57
+
58
+ sortOrder,
59
+ sortValue: columnId as StorageSortValue,
60
+ });
61
+ },
62
+ [nodeId, searchValue, tenant, visibleEntities],
63
+ );
64
+
65
+ const columns = useMemo(() => {
66
+ return getPreparedStorageGroupsColumns(nodesMap, visibleEntities);
67
+ }, [nodesMap, visibleEntities]);
68
+
69
+ const renderEmptyDataMessage = () => {
70
+ if (visibleEntities !== VISIBLE_ENTITIES.all) {
71
+ return (
72
+ <StorageGroupsEmptyDataMessage
73
+ onShowAll={onShowAll}
74
+ visibleEntities={visibleEntities}
75
+ />
76
+ );
77
+ }
78
+
79
+ return i18n('empty.default');
80
+ };
81
+
82
+ return (
83
+ <VirtualTable
84
+ parentContainer={parentContainer}
85
+ columns={columns}
86
+ fetchData={fetchData}
87
+ limit={50}
88
+ renderControls={renderControls}
89
+ renderErrorMessage={renderErrorMessage}
90
+ renderEmptyDataMessage={renderEmptyDataMessage}
91
+ dependencyArray={filters}
92
+ />
93
+ );
94
+ };
@@ -0,0 +1,21 @@
1
+ import type {StorageApiRequestParams} from '../../../store/reducers/storage/types';
2
+ import {prepareStorageGroupsResponse} from '../../../store/reducers/storage/utils';
3
+ import {EVersion} from '../../../types/api/storage';
4
+
5
+ const getConcurrentId = (limit?: number, offset?: number) => {
6
+ return `getStorageGroups|offset${offset}|limit${limit}`;
7
+ };
8
+
9
+ export const getStorageGroups = async ({limit, offset, ...params}: StorageApiRequestParams) => {
10
+ const response = await window.api.getStorageInfo(
11
+ {version: EVersion.v2, limit, offset, ...params},
12
+ {concurrentId: getConcurrentId(limit, offset)},
13
+ );
14
+ const preparedResponse = prepareStorageGroupsResponse(response);
15
+
16
+ return {
17
+ data: preparedResponse.groups || [],
18
+ found: preparedResponse.found || 0,
19
+ total: preparedResponse.total || 0,
20
+ };
21
+ };