ydb-embedded-ui 4.27.1 → 4.28.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 (42) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/components/EmptyState/EmptyState.scss +5 -2
  3. package/dist/components/EmptyState/EmptyState.tsx +11 -3
  4. package/dist/components/Errors/403/AccessDenied.tsx +4 -3
  5. package/dist/components/ProblemFilter/ProblemFilter.tsx +1 -1
  6. package/dist/components/UptimeFIlter/UptimeFilter.tsx +1 -1
  7. package/dist/components/VirtualTable/VirtualTable.scss +1 -1
  8. package/dist/containers/Cluster/Cluster.tsx +11 -12
  9. package/dist/containers/Nodes/Nodes.tsx +4 -4
  10. package/dist/containers/Nodes/NodesWrapper.tsx +21 -0
  11. package/dist/containers/Nodes/VirtualNodes.tsx +4 -14
  12. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +1 -0
  13. package/dist/containers/Storage/EmptyFilter/EmptyFilter.tsx +1 -0
  14. package/dist/containers/Storage/PDisk/PDisk.tsx +5 -7
  15. package/dist/containers/Storage/Storage.tsx +30 -67
  16. package/dist/containers/Storage/StorageControls/StorageControls.tsx +104 -0
  17. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +18 -62
  18. package/dist/containers/Storage/StorageGroups/StorageGroupsEmptyDataMessage.tsx +30 -0
  19. package/dist/containers/Storage/StorageGroups/VirtualStorageGroups.tsx +94 -0
  20. package/dist/containers/Storage/StorageGroups/getGroups.ts +21 -0
  21. package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +73 -50
  22. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +23 -138
  23. package/dist/containers/Storage/StorageNodes/StorageNodesEmptyDataMessage.tsx +44 -0
  24. package/dist/containers/Storage/StorageNodes/VirtualStorageNodes.tsx +105 -0
  25. package/dist/containers/Storage/StorageNodes/getNodes.ts +26 -0
  26. package/dist/containers/Storage/StorageNodes/getStorageNodesColumns.tsx +125 -0
  27. package/dist/containers/Storage/StorageNodes/shared.ts +9 -0
  28. package/dist/containers/Storage/StorageTypeFilter/StorageTypeFilter.tsx +1 -1
  29. package/dist/containers/Storage/StorageVisibleEntitiesFilter/StorageVisibleEntitiesFilter.tsx +1 -1
  30. package/dist/containers/Storage/StorageWrapper.tsx +23 -0
  31. package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +3 -4
  32. package/dist/containers/Storage/VirtualStorage.tsx +112 -0
  33. package/dist/containers/Storage/i18n/en.json +7 -0
  34. package/dist/containers/Storage/i18n/index.ts +11 -0
  35. package/dist/containers/Storage/i18n/ru.json +7 -0
  36. package/dist/containers/Storage/shared.ts +3 -0
  37. package/dist/containers/Tenants/Tenants.tsx +2 -2
  38. package/dist/containers/UserSettings/i18n/en.json +2 -2
  39. package/dist/containers/UserSettings/i18n/ru.json +2 -2
  40. package/dist/containers/UserSettings/settings.ts +4 -4
  41. package/dist/store/reducers/storage/selectors.ts +0 -20
  42. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.28.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.27.1...v4.28.0) (2024-01-10)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Storage:** use VirtualTable ([#628](https://github.com/ydb-platform/ydb-embedded-ui/issues/628)) ([67fd9b0](https://github.com/ydb-platform/ydb-embedded-ui/commit/67fd9b03653dd28be650094c987451b09fcce858))
9
+
3
10
  ## [4.27.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.27.0...v4.27.1) (2024-01-10)
4
11
 
5
12
 
@@ -19,10 +19,13 @@
19
19
  }
20
20
 
21
21
  &_size_m {
22
- position: relative;
23
-
24
22
  width: 800px;
25
23
  height: 240px;
24
+ }
25
+
26
+ &_position_center {
27
+ position: relative;
28
+
26
29
  margin: 0 auto;
27
30
  }
28
31
  }
@@ -13,18 +13,26 @@ const sizes = {
13
13
  l: 350,
14
14
  };
15
15
 
16
- interface EmptyStateProps {
16
+ export interface EmptyStateProps {
17
17
  title: string;
18
18
  image?: ReactNode;
19
19
  description?: ReactNode;
20
20
  actions?: ReactNode[];
21
21
  size?: keyof typeof sizes;
22
+ position?: 'left' | 'center';
22
23
  }
23
24
 
24
- export const EmptyState = ({image, title, description, actions, size = 'm'}: EmptyStateProps) => {
25
+ export const EmptyState = ({
26
+ image,
27
+ title,
28
+ description,
29
+ actions,
30
+ size = 'm',
31
+ position = 'center',
32
+ }: EmptyStateProps) => {
25
33
  return (
26
34
  <div className={block({size})}>
27
- <div className={block('wrapper', {size})}>
35
+ <div className={block('wrapper', {size, position})}>
28
36
  <div className={block('image')}>
29
37
  {image ? (
30
38
  image
@@ -1,19 +1,20 @@
1
- import {EmptyState} from '../../EmptyState';
1
+ import {EmptyState, type EmptyStateProps} from '../../EmptyState';
2
2
  import {Illustration} from '../../Illustration';
3
3
 
4
4
  import i18n from '../i18n';
5
5
 
6
- interface AccessDeniedProps {
6
+ interface AccessDeniedProps extends Omit<EmptyStateProps, 'image' | 'title' | 'description'> {
7
7
  title?: string;
8
8
  description?: string;
9
9
  }
10
10
 
11
- export const AccessDenied = ({title, description}: AccessDeniedProps) => {
11
+ export const AccessDenied = ({title, description, ...restProps}: AccessDeniedProps) => {
12
12
  return (
13
13
  <EmptyState
14
14
  image={<Illustration name="403" />}
15
15
  title={title || i18n('403.title')}
16
16
  description={description || i18n('403.description')}
17
+ {...restProps}
17
18
  />
18
19
  );
19
20
  };
@@ -5,7 +5,7 @@ import {ProblemFilterValues} from '../../store/reducers/settings/settings';
5
5
 
6
6
  interface ProblemFilterProps {
7
7
  value: ProblemFilterValue;
8
- onChange: (value: string) => void;
8
+ onChange: (value: ProblemFilterValue) => void;
9
9
  className?: string;
10
10
  }
11
11
 
@@ -4,7 +4,7 @@ import {NodesUptimeFilterValues, NodesUptimeFilterTitles} from '../../utils/node
4
4
 
5
5
  interface UptimeFilterProps {
6
6
  value: NodesUptimeFilterValues;
7
- onChange: (value: string) => void;
7
+ onChange: (value: NodesUptimeFilterValues) => void;
8
8
  className?: string;
9
9
  }
10
10
 
@@ -15,7 +15,7 @@
15
15
  @include body-2-typography();
16
16
 
17
17
  &__table {
18
- width: 100%;
18
+ width: max-content;
19
19
  max-width: 100%;
20
20
 
21
21
  table-layout: fixed;
@@ -18,14 +18,12 @@ import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
18
18
  import {getClusterInfo} from '../../store/reducers/cluster/cluster';
19
19
  import {getClusterNodes} from '../../store/reducers/clusterNodes/clusterNodes';
20
20
  import {parseNodesToVersionsValues, parseVersionsToVersionToColorMap} from '../../utils/versions';
21
- import {useAutofetcher, useSetting, useTypedSelector} from '../../utils/hooks';
22
- import {USE_BACKEND_PARAMS_FOR_TABLES_KEY} from '../../utils/constants';
21
+ import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
23
22
 
24
23
  import {InternalLink} from '../../components/InternalLink';
25
24
  import {Tenants} from '../Tenants/Tenants';
26
- import {Nodes} from '../Nodes/Nodes';
27
- import {VirtualNodes} from '../Nodes/VirtualNodes';
28
- import {Storage} from '../Storage/Storage';
25
+ import {StorageWrapper} from '../Storage/StorageWrapper';
26
+ import {NodesWrapper} from '../Nodes/NodesWrapper';
29
27
  import {Versions} from '../Versions/Versions';
30
28
 
31
29
  import {ClusterInfo} from './ClusterInfo/ClusterInfo';
@@ -55,8 +53,6 @@ function Cluster({
55
53
  const match = useRouteMatch<{activeTab: string}>(routes.cluster);
56
54
  const {activeTab = clusterTabsIds.tenants} = match?.params || {};
57
55
 
58
- const [useVirtualNodes] = useSetting<boolean>(USE_BACKEND_PARAMS_FOR_TABLES_KEY);
59
-
60
56
  const location = useLocation();
61
57
  const queryParams = qs.parse(location.search, {
62
58
  ignoreQueryPrefix: true,
@@ -111,17 +107,20 @@ function Cluster({
111
107
  return <Tenants additionalTenantsProps={additionalTenantsProps} />;
112
108
  }
113
109
  case clusterTabsIds.nodes: {
114
- return useVirtualNodes ? (
115
- <VirtualNodes
110
+ return (
111
+ <NodesWrapper
116
112
  parentContainer={container.current}
117
113
  additionalNodesProps={additionalNodesProps}
118
114
  />
119
- ) : (
120
- <Nodes additionalNodesProps={additionalNodesProps} />
121
115
  );
122
116
  }
123
117
  case clusterTabsIds.storage: {
124
- return <Storage additionalNodesProps={additionalNodesProps} />;
118
+ return (
119
+ <StorageWrapper
120
+ parentContainer={container.current}
121
+ additionalNodesProps={additionalNodesProps}
122
+ />
123
+ );
125
124
  }
126
125
  case clusterTabsIds.versions: {
127
126
  return <Versions versionToColor={versionToColor} />;
@@ -106,12 +106,12 @@ export const Nodes = ({path, additionalNodesProps = {}}: NodesProps) => {
106
106
  dispatch(setSearchValue(value));
107
107
  };
108
108
 
109
- const handleProblemFilterChange = (value: string) => {
110
- dispatch(changeFilter(value as ProblemFilterValue));
109
+ const handleProblemFilterChange = (value: ProblemFilterValue) => {
110
+ dispatch(changeFilter(value));
111
111
  };
112
112
 
113
- const handleUptimeFilterChange = (value: string) => {
114
- dispatch(setNodesUptimeFilter(value as NodesUptimeFilterValues));
113
+ const handleUptimeFilterChange = (value: NodesUptimeFilterValues) => {
114
+ dispatch(setNodesUptimeFilter(value));
115
115
  };
116
116
 
117
117
  const renderControls = () => {
@@ -0,0 +1,21 @@
1
+ import type {AdditionalNodesProps} from '../../types/additionalProps';
2
+ import {USE_BACKEND_PARAMS_FOR_TABLES_KEY} from '../../utils/constants';
3
+ import {useSetting} from '../../utils/hooks';
4
+
5
+ import {VirtualNodes} from './VirtualNodes';
6
+ import {Nodes} from './Nodes';
7
+
8
+ interface NodesWrapperProps {
9
+ parentContainer?: Element | null;
10
+ additionalNodesProps?: AdditionalNodesProps;
11
+ }
12
+
13
+ export const NodesWrapper = ({parentContainer, ...props}: NodesWrapperProps) => {
14
+ const [useVirtualTable] = useSetting<boolean>(USE_BACKEND_PARAMS_FOR_TABLES_KEY);
15
+
16
+ if (useVirtualTable) {
17
+ return <VirtualNodes parentContainer={parentContainer} {...props} />;
18
+ }
19
+
20
+ return <Nodes {...props} />;
21
+ };
@@ -72,27 +72,17 @@ export const VirtualNodes = ({parentContainer, additionalNodesProps}: NodesProps
72
72
  return b('node', {unavailable: isUnavailableNode(row)});
73
73
  };
74
74
 
75
- const handleSearchQueryChange = (value: string) => {
76
- setSearchValue(value);
77
- };
78
- const handleProblemFilterChange = (value: string) => {
79
- setProblemFilter(value as ProblemFilterValue);
80
- };
81
- const handleUptimeFilterChange = (value: string) => {
82
- setUptimeFilter(value as NodesUptimeFilterValues);
83
- };
84
-
85
75
  const renderControls: RenderControls = ({totalEntities, foundEntities, inited}) => {
86
76
  return (
87
77
  <>
88
78
  <Search
89
- onChange={handleSearchQueryChange}
79
+ onChange={setSearchValue}
90
80
  placeholder="Host name"
91
81
  className={b('search')}
92
82
  value={searchValue}
93
83
  />
94
- <ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
95
- <UptimeFilter value={uptimeFilter} onChange={handleUptimeFilterChange} />
84
+ <ProblemFilter value={problemFilter} onChange={setProblemFilter} />
85
+ <UptimeFilter value={uptimeFilter} onChange={setUptimeFilter} />
96
86
  <EntitiesCount
97
87
  total={totalEntities}
98
88
  current={foundEntities}
@@ -116,7 +106,7 @@ export const VirtualNodes = ({parentContainer, additionalNodesProps}: NodesProps
116
106
 
117
107
  const renderErrorMessage: RenderErrorMessage = (error) => {
118
108
  if (error && error.status === 403) {
119
- return <AccessDenied />;
109
+ return <AccessDenied position="left" />;
120
110
  }
121
111
 
122
112
  return <ResponseError error={error} />;
@@ -6,6 +6,7 @@
6
6
  $inner-border-radius: $outer-border-radius - $border-width;
7
7
 
8
8
  position: relative;
9
+ z-index: 0;
9
10
 
10
11
  display: block;
11
12
 
@@ -20,6 +20,7 @@ export const EmptyFilter = ({
20
20
  }: EmptyFilterProps) => (
21
21
  <EmptyState
22
22
  image={<Illustration name="thumbsUp" />}
23
+ position="left"
23
24
  title={title}
24
25
  description={message}
25
26
  actions={
@@ -1,14 +1,13 @@
1
1
  import React, {useEffect, useState, useRef, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
+ import type {TVDiskStateInfo} from '../../../types/api/vdisk';
4
5
  import {InternalLink} from '../../../components/InternalLink';
5
6
  import {Stack} from '../../../components/Stack/Stack';
6
7
 
7
8
  import routes, {createHref} from '../../../routes';
8
- import {selectVDisksForPDisk} from '../../../store/reducers/storage/selectors';
9
9
  import {TPDiskStateInfo, TPDiskState} from '../../../types/api/pdisk';
10
10
  import {stringifyVdiskId} from '../../../utils/dataFormatters/dataFormatters';
11
- import {useTypedSelector} from '../../../utils/hooks';
12
11
  import {getPDiskType} from '../../../utils/pdisk';
13
12
  import {isFullVDiskData} from '../../../utils/storage';
14
13
 
@@ -44,6 +43,7 @@ const stateSeverity = {
44
43
  interface PDiskProps {
45
44
  nodeId: number;
46
45
  data?: TPDiskStateInfo;
46
+ vDisks?: TVDiskStateInfo[];
47
47
  }
48
48
 
49
49
  const isSeverityKey = (key?: TPDiskState): key is keyof typeof stateSeverity =>
@@ -53,12 +53,10 @@ const getStateSeverity = (pDiskState?: TPDiskState) => {
53
53
  return isSeverityKey(pDiskState) ? stateSeverity[pDiskState] : NOT_AVAILABLE_SEVERITY;
54
54
  };
55
55
 
56
- export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
56
+ export const PDisk = ({nodeId, data: rawData = {}, vDisks}: PDiskProps) => {
57
57
  // NodeId in data is required for the popup
58
58
  const data = useMemo(() => ({...rawData, NodeId: nodeId}), [rawData, nodeId]);
59
59
 
60
- const vdisks = useTypedSelector((state) => selectVDisksForPDisk(state, nodeId, data.PDiskId));
61
-
62
60
  const [severity, setSeverity] = useState(getStateSeverity(data.State));
63
61
  const [isPopupVisible, setIsPopupVisible] = useState(false);
64
62
 
@@ -100,13 +98,13 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
100
98
  };
101
99
 
102
100
  const renderVDisks = () => {
103
- if (!vdisks?.length) {
101
+ if (!vDisks?.length) {
104
102
  return null;
105
103
  }
106
104
 
107
105
  return (
108
106
  <div className={b('vdisks')}>
109
- {vdisks.map((vdisk) => {
107
+ {vDisks.map((vdisk) => {
110
108
  const donors = vdisk.Donors;
111
109
 
112
110
  return (
@@ -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
+ };