ydb-embedded-ui 4.27.1 → 4.29.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 (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
+ };