ydb-embedded-ui 2.2.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +20 -0
- package/dist/assets/icons/shield.svg +3 -0
- package/dist/components/NodesViewer/NodesViewer.js +1 -1
- package/dist/containers/App/App.scss +5 -1
- package/dist/containers/Nodes/Nodes.js +1 -1
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +7 -5
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +1 -11
- package/dist/containers/Storage/Pdisk/Pdisk.scss +15 -8
- package/dist/containers/Storage/Pdisk/Pdisk.tsx +22 -14
- package/dist/containers/Storage/Storage.js +29 -48
- package/dist/containers/Storage/StorageGroups/StorageGroups.scss +1 -4
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +27 -2
- package/dist/containers/Storage/StorageGroups/i18n/en.json +2 -1
- package/dist/containers/Storage/StorageGroups/i18n/ru.json +2 -1
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +14 -12
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +7 -5
- package/dist/containers/Storage/Vdisk/Vdisk.js +36 -23
- package/dist/containers/Storage/Vdisk/Vdisk.scss +6 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +7 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +25 -11
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +34 -19
- package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -1
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +27 -20
- package/dist/containers/Tenant/Tenant.tsx +12 -9
- package/dist/containers/Tenants/Tenants.js +1 -1
- package/dist/store/reducers/olapStats.js +13 -0
- package/dist/store/reducers/schema.js +42 -0
- package/dist/store/reducers/storage.js +26 -16
- package/dist/store/reducers/tenant.js +3 -1
- package/dist/styles/mixins.scss +1 -1
- package/dist/types/api/storage.ts +35 -10
- package/dist/types/store/storage.ts +1 -0
- package/dist/utils/hooks/useAutofetcher.ts +9 -3
- package/package.json +1 -1
@@ -5,7 +5,7 @@ import {Link} from 'react-router-dom';
|
|
5
5
|
import {useDispatch, useSelector} from 'react-redux';
|
6
6
|
import {useLocation} from 'react-router';
|
7
7
|
|
8
|
-
import {Switch, Tabs} from '@gravity-ui/uikit';
|
8
|
+
import {Loader, Switch, Tabs} from '@gravity-ui/uikit';
|
9
9
|
|
10
10
|
//@ts-ignore
|
11
11
|
import TopQueries from './TopQueries/TopQueries';
|
@@ -53,9 +53,9 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
53
53
|
currentSchema: currentItem = {},
|
54
54
|
autorefresh,
|
55
55
|
} = useSelector((state: any) => state.schema);
|
56
|
-
const {
|
57
|
-
|
58
|
-
|
56
|
+
const {diagnosticsTab = GeneralPagesIds.overview, wasLoaded} = useSelector(
|
57
|
+
(state: any) => state.tenant,
|
58
|
+
);
|
59
59
|
|
60
60
|
const location = useLocation();
|
61
61
|
|
@@ -79,14 +79,17 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
79
79
|
dispatch(setDiagnosticsTab(tab));
|
80
80
|
};
|
81
81
|
const activeTab = useMemo(() => {
|
82
|
-
if (
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
if (wasLoaded) {
|
83
|
+
if (pages.find((el) => el.id === diagnosticsTab)) {
|
84
|
+
return diagnosticsTab;
|
85
|
+
} else {
|
86
|
+
const newPage = pages[0].id;
|
87
|
+
forwardToDiagnosticTab(newPage);
|
88
|
+
return newPage;
|
89
|
+
}
|
88
90
|
}
|
89
|
-
|
91
|
+
return undefined;
|
92
|
+
}, [pages, diagnosticsTab, wasLoaded]);
|
90
93
|
|
91
94
|
const onAutorefreshToggle = (value: boolean) => {
|
92
95
|
if (value) {
|
@@ -185,6 +188,17 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
185
188
|
);
|
186
189
|
};
|
187
190
|
|
191
|
+
// Loader prevents incorrect loading of tabs
|
192
|
+
// After tabs are initially loaded it is no longer needed
|
193
|
+
// Thus there is no also "loading" check as in other parts of the project
|
194
|
+
if (!wasLoaded) {
|
195
|
+
return (
|
196
|
+
<div className={b('loader')}>
|
197
|
+
<Loader size="l" />
|
198
|
+
</div>
|
199
|
+
);
|
200
|
+
}
|
201
|
+
|
188
202
|
return (
|
189
203
|
<div className={b()}>
|
190
204
|
{renderTabs()}
|
@@ -15,9 +15,12 @@ import {
|
|
15
15
|
import {EPathType} from '../../../../types/api/schema';
|
16
16
|
import {isColumnEntityType, isTableType} from '../../utils/schema';
|
17
17
|
//@ts-ignore
|
18
|
-
import {getSchema} from '../../../../store/reducers/schema';
|
18
|
+
import {getSchema, resetLoadingState} from '../../../../store/reducers/schema';
|
19
19
|
//@ts-ignore
|
20
|
-
import {
|
20
|
+
import {
|
21
|
+
getOlapStats,
|
22
|
+
resetLoadingState as resetOlapLoadingState,
|
23
|
+
} from '../../../../store/reducers/olapStats';
|
21
24
|
import {useAutofetcher} from '../../../../utils/hooks';
|
22
25
|
|
23
26
|
import './Overview.scss';
|
@@ -64,24 +67,36 @@ function Overview(props: OverviewProps) {
|
|
64
67
|
|
65
68
|
const {
|
66
69
|
currentSchema: currentItem = {},
|
67
|
-
loading,
|
70
|
+
loading: schemaLoading,
|
68
71
|
wasLoaded,
|
69
72
|
autorefresh,
|
70
73
|
currentSchemaPath,
|
71
74
|
} = useSelector((state: any) => state.schema);
|
72
75
|
|
73
|
-
const {
|
74
|
-
|
75
|
-
|
76
|
+
const {data: {result: olapStats} = {result: undefined}, loading: olapStatsLoading} =
|
77
|
+
useSelector((state: any) => state.olapStats);
|
78
|
+
|
79
|
+
const loading = schemaLoading || olapStatsLoading;
|
76
80
|
|
77
|
-
useAutofetcher(
|
78
|
-
|
79
|
-
|
81
|
+
useAutofetcher(
|
82
|
+
(isBackground) => {
|
83
|
+
if (!isBackground) {
|
84
|
+
dispatch(resetLoadingState());
|
85
|
+
}
|
80
86
|
|
81
|
-
|
82
|
-
dispatch(
|
83
|
-
|
84
|
-
|
87
|
+
const schemaPath = currentSchemaPath || tenantName;
|
88
|
+
dispatch(getSchema({path: schemaPath}));
|
89
|
+
|
90
|
+
if (isTableType(type) && isColumnEntityType(type)) {
|
91
|
+
if (!isBackground) {
|
92
|
+
dispatch(resetOlapLoadingState());
|
93
|
+
}
|
94
|
+
dispatch(getOlapStats({path: schemaPath}));
|
95
|
+
}
|
96
|
+
},
|
97
|
+
[currentSchemaPath, dispatch, tenantName, type],
|
98
|
+
autorefresh,
|
99
|
+
);
|
85
100
|
|
86
101
|
const tableSchema =
|
87
102
|
currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
|
@@ -116,17 +131,17 @@ function Overview(props: OverviewProps) {
|
|
116
131
|
[EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupInfo data={schemaData} />,
|
117
132
|
};
|
118
133
|
|
119
|
-
return (
|
120
|
-
|
134
|
+
return (
|
135
|
+
(type && pathTypeToComponent[type]?.()) || (
|
136
|
+
<SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
|
137
|
+
)
|
121
138
|
);
|
122
|
-
}
|
139
|
+
};
|
123
140
|
|
124
141
|
return loading && !wasLoaded ? (
|
125
142
|
renderLoader()
|
126
143
|
) : (
|
127
|
-
<div className={props.className}>
|
128
|
-
{renderContent()}
|
129
|
-
</div>
|
144
|
+
<div className={props.className}>{renderContent()}</div>
|
130
145
|
);
|
131
146
|
}
|
132
147
|
|
@@ -3,7 +3,7 @@ import {useDispatch} from 'react-redux';
|
|
3
3
|
|
4
4
|
import {NavigationTree} from 'ydb-ui-components';
|
5
5
|
|
6
|
-
import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
|
6
|
+
import {setCurrentSchemaPath, getSchema, preloadSchema} from '../../../../store/reducers/schema';
|
7
7
|
import {getDescribe} from '../../../../store/reducers/describe';
|
8
8
|
import {getSchemaAcl} from '../../../../store/reducers/schemaAcl';
|
9
9
|
import type {EPathType} from '../../../../types/api/schema';
|
@@ -19,28 +19,35 @@ interface SchemaTreeProps {
|
|
19
19
|
}
|
20
20
|
|
21
21
|
export function SchemaTree(props: SchemaTreeProps) {
|
22
|
-
const {
|
23
|
-
rootPath,
|
24
|
-
rootName,
|
25
|
-
rootType,
|
26
|
-
currentPath,
|
27
|
-
} = props;
|
22
|
+
const {rootPath, rootName, rootType, currentPath} = props;
|
28
23
|
|
29
24
|
const dispatch = useDispatch();
|
30
25
|
|
31
|
-
const fetchPath = (path: string) =>
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
26
|
+
const fetchPath = (path: string) =>
|
27
|
+
window.api
|
28
|
+
.getSchema({path}, {concurrentId: `NavigationTree.getSchema|${path}`})
|
29
|
+
.then((data) => {
|
30
|
+
const {PathDescription: {Children = []} = {}} = data;
|
31
|
+
|
32
|
+
dispatch(preloadSchema(path, data));
|
33
|
+
|
34
|
+
return Children.map((childData) => {
|
35
|
+
const {Name = '', PathType, PathSubType} = childData;
|
36
|
+
|
37
|
+
// not full data, but it contains PathType, which ensures seamless switch between nodes
|
38
|
+
dispatch(
|
39
|
+
preloadSchema(`${path}/${Name}`, {PathDescription: {Self: childData}}),
|
40
|
+
);
|
41
|
+
|
42
|
+
return {
|
43
|
+
name: Name,
|
44
|
+
type: mapPathTypeToNavigationTreeType(PathType, PathSubType),
|
45
|
+
// FIXME: should only be explicitly set to true for tables with indexes
|
46
|
+
// at the moment of writing there is no property to determine this, fix later
|
47
|
+
expandable: true,
|
48
|
+
};
|
49
|
+
});
|
50
|
+
});
|
44
51
|
|
45
52
|
const handleActivePathUpdate = (activePath: string) => {
|
46
53
|
dispatch(setCurrentSchemaPath(activePath));
|
@@ -16,7 +16,7 @@ import SplitPane from '../../components/SplitPane';
|
|
16
16
|
//@ts-ignore
|
17
17
|
import {DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY} from '../../utils/constants';
|
18
18
|
//@ts-ignore
|
19
|
-
import {disableAutorefresh, getSchema} from '../../store/reducers/schema';
|
19
|
+
import {disableAutorefresh, getSchema, resetLoadingState} from '../../store/reducers/schema';
|
20
20
|
//@ts-ignore
|
21
21
|
import {getSchemaAcl} from '../../store/reducers/schemaAcl';
|
22
22
|
import {
|
@@ -57,6 +57,13 @@ function Tenant(props: TenantProps) {
|
|
57
57
|
(state: any) => state.schema,
|
58
58
|
);
|
59
59
|
|
60
|
+
const {PathType: preloadedPathType, PathSubType: preloadedPathSubType} = useSelector(
|
61
|
+
(state: any) => state.schema.data[currentSchemaPath]?.PathDescription?.Self || {},
|
62
|
+
);
|
63
|
+
|
64
|
+
const {PathType: currentPathType, PathSubType: currentPathSubType} =
|
65
|
+
(currentItem as TEvDescribeSchemeResult).PathDescription?.Self || {};
|
66
|
+
|
60
67
|
const {data: {status: tenantStatus = 200} = {}} = useSelector((state: any) => state.tenant);
|
61
68
|
const {error: {status: schemaStatus = 200} = {}} = useSelector((state: any) => state.schema);
|
62
69
|
|
@@ -73,6 +80,7 @@ function Tenant(props: TenantProps) {
|
|
73
80
|
|
74
81
|
useEffect(() => {
|
75
82
|
const schemaPath = currentSchemaPath || tenantName;
|
83
|
+
dispatch(resetLoadingState());
|
76
84
|
dispatch(getSchema({path: tenantName}));
|
77
85
|
dispatch(getSchema({path: schemaPath}));
|
78
86
|
dispatch(getSchemaAcl({path: schemaPath}));
|
@@ -105,11 +113,6 @@ function Tenant(props: TenantProps) {
|
|
105
113
|
};
|
106
114
|
}, [tenantName, dispatch]);
|
107
115
|
|
108
|
-
const {
|
109
|
-
PathType: currentPathType,
|
110
|
-
PathSubType: currentPathSubType,
|
111
|
-
} = (currentItem as TEvDescribeSchemeResult).PathDescription?.Self || {};
|
112
|
-
|
113
116
|
const onCollapseSummaryHandler = () => {
|
114
117
|
dispatchSummaryVisibilityAction(PaneVisibilityActionTypes.triggerCollapse);
|
115
118
|
};
|
@@ -144,15 +147,15 @@ function Tenant(props: TenantProps) {
|
|
144
147
|
onSplitStartDragAdditional={onSplitStartDragAdditional}
|
145
148
|
>
|
146
149
|
<ObjectSummary
|
147
|
-
type={currentPathType}
|
148
|
-
subType={currentPathSubType}
|
150
|
+
type={preloadedPathType || currentPathType}
|
151
|
+
subType={preloadedPathSubType || currentPathSubType}
|
149
152
|
onCollapseSummary={onCollapseSummaryHandler}
|
150
153
|
onExpandSummary={onExpandSummaryHandler}
|
151
154
|
isCollapsed={summaryVisibilityState.collapsed}
|
152
155
|
additionalTenantInfo={props.additionalTenantInfo}
|
153
156
|
/>
|
154
157
|
<ObjectGeneral
|
155
|
-
type={currentPathType}
|
158
|
+
type={preloadedPathType || currentPathType}
|
156
159
|
additionalTenantInfo={props.additionalTenantInfo}
|
157
160
|
additionalNodesInfo={props.additionalNodesInfo}
|
158
161
|
/>
|
@@ -6,6 +6,7 @@ import {createRequestActionTypes, createApiRequest} from '../utils';
|
|
6
6
|
|
7
7
|
const FETCH_OLAP_STATS = createRequestActionTypes('query', 'SEND_OLAP_STATS_QUERY');
|
8
8
|
const SET_OLAP_STATS_OPTIONS = createRequestActionTypes('query', 'SET_OLAP_STATS_OPTIONS');
|
9
|
+
const RESET_LOADING_STATE = 'olapStats/RESET_LOADING_STATE';
|
9
10
|
|
10
11
|
const initialState = {
|
11
12
|
loading: false,
|
@@ -48,6 +49,12 @@ const olapStats = (state = initialState, action) => {
|
|
48
49
|
...state,
|
49
50
|
...action.data,
|
50
51
|
};
|
52
|
+
case RESET_LOADING_STATE: {
|
53
|
+
return {
|
54
|
+
...state,
|
55
|
+
wasLoaded: initialState.wasLoaded,
|
56
|
+
};
|
57
|
+
}
|
51
58
|
default:
|
52
59
|
return state;
|
53
60
|
}
|
@@ -73,4 +80,10 @@ export function setOlapStatsOptions(options) {
|
|
73
80
|
};
|
74
81
|
}
|
75
82
|
|
83
|
+
export function resetLoadingState() {
|
84
|
+
return {
|
85
|
+
type: RESET_LOADING_STATE,
|
86
|
+
};
|
87
|
+
}
|
88
|
+
|
76
89
|
export default olapStats;
|
@@ -2,10 +2,12 @@ import {createRequestActionTypes, createApiRequest} from '../utils';
|
|
2
2
|
import '../../services/api';
|
3
3
|
|
4
4
|
const FETCH_SCHEMA = createRequestActionTypes('schema', 'FETCH_SCHEMA');
|
5
|
+
const PRELOAD_SCHEMA = 'schema/PRELOAD_SCHEMA';
|
5
6
|
const SET_SCHEMA = 'schema/SET_SCHEMA';
|
6
7
|
const SET_SHOW_PREVIEW = 'schema/SET_SHOW_PREVIEW';
|
7
8
|
const ENABLE_AUTOREFRESH = 'schema/ENABLE_AUTOREFRESH';
|
8
9
|
const DISABLE_AUTOREFRESH = 'schema/DISABLE_AUTOREFRESH';
|
10
|
+
const RESET_LOADING_STATE = 'schema/RESET_LOADING_STATE';
|
9
11
|
|
10
12
|
export const initialState = {
|
11
13
|
loading: true,
|
@@ -42,16 +44,34 @@ const schema = function z(state = initialState, action) {
|
|
42
44
|
};
|
43
45
|
}
|
44
46
|
case FETCH_SCHEMA.FAILURE: {
|
47
|
+
if (action.error.isCancelled) {
|
48
|
+
return state;
|
49
|
+
}
|
50
|
+
|
45
51
|
return {
|
46
52
|
...state,
|
47
53
|
error: action.error,
|
48
54
|
loading: false,
|
49
55
|
};
|
50
56
|
}
|
57
|
+
case PRELOAD_SCHEMA: {
|
58
|
+
if (state.data[action.path]) {
|
59
|
+
return state;
|
60
|
+
}
|
61
|
+
|
62
|
+
return {
|
63
|
+
...state,
|
64
|
+
data: {
|
65
|
+
...state.data,
|
66
|
+
[action.path]: action.data,
|
67
|
+
},
|
68
|
+
};
|
69
|
+
}
|
51
70
|
case SET_SCHEMA: {
|
52
71
|
return {
|
53
72
|
...state,
|
54
73
|
currentSchemaPath: action.data,
|
74
|
+
wasLoaded: false,
|
55
75
|
};
|
56
76
|
}
|
57
77
|
case ENABLE_AUTOREFRESH: {
|
@@ -72,6 +92,12 @@ const schema = function z(state = initialState, action) {
|
|
72
92
|
showPreview: action.data,
|
73
93
|
};
|
74
94
|
}
|
95
|
+
case RESET_LOADING_STATE: {
|
96
|
+
return {
|
97
|
+
...state,
|
98
|
+
wasLoaded: initialState.wasLoaded,
|
99
|
+
};
|
100
|
+
}
|
75
101
|
default:
|
76
102
|
return state;
|
77
103
|
}
|
@@ -106,4 +132,20 @@ export function setShowPreview(value) {
|
|
106
132
|
data: value,
|
107
133
|
};
|
108
134
|
}
|
135
|
+
|
136
|
+
// only stores the passed data if the path doesn't exist yet
|
137
|
+
export function preloadSchema(path, data) {
|
138
|
+
return {
|
139
|
+
type: PRELOAD_SCHEMA,
|
140
|
+
path,
|
141
|
+
data,
|
142
|
+
};
|
143
|
+
}
|
144
|
+
|
145
|
+
export function resetLoadingState() {
|
146
|
+
return {
|
147
|
+
type: RESET_LOADING_STATE,
|
148
|
+
};
|
149
|
+
}
|
150
|
+
|
109
151
|
export default schema;
|
@@ -4,6 +4,7 @@ import _ from 'lodash';
|
|
4
4
|
import {createSelector} from 'reselect';
|
5
5
|
import {calcUptime} from '../../utils';
|
6
6
|
import {getUsage} from '../../utils/storage';
|
7
|
+
import {getPDiskType} from '../../utils/pdisk';
|
7
8
|
|
8
9
|
export const VisibleEntities = {
|
9
10
|
All: 'All',
|
@@ -56,10 +57,17 @@ const storage = function z(state = initialState, action) {
|
|
56
57
|
};
|
57
58
|
}
|
58
59
|
case FETCH_STORAGE.FAILURE: {
|
60
|
+
if (action.error.isCancelled) {
|
61
|
+
return {
|
62
|
+
...state,
|
63
|
+
};
|
64
|
+
}
|
65
|
+
|
59
66
|
return {
|
60
67
|
...state,
|
61
68
|
error: action.error,
|
62
69
|
loading: false,
|
70
|
+
wasLoaded: true,
|
63
71
|
};
|
64
72
|
}
|
65
73
|
case SET_INITIAL: {
|
@@ -229,6 +237,10 @@ export const getFlatListStorageGroups = createSelector([getStoragePools], (stora
|
|
229
237
|
},
|
230
238
|
0,
|
231
239
|
);
|
240
|
+
const mediaType = group.VDisks?.reduce((type, vdisk) => {
|
241
|
+
const currentType = getPDiskType(vdisk.PDisk || {});
|
242
|
+
return currentType && (currentType === type || type === '') ? currentType : 'Mixed';
|
243
|
+
}, '');
|
232
244
|
return [
|
233
245
|
...acc,
|
234
246
|
{
|
@@ -240,6 +252,7 @@ export const getFlatListStorageGroups = createSelector([getStoragePools], (stora
|
|
240
252
|
Limit: limitSizeBytes,
|
241
253
|
Missing: missing,
|
242
254
|
UsedSpaceFlag,
|
255
|
+
Type: mediaType || null,
|
243
256
|
},
|
244
257
|
];
|
245
258
|
},
|
@@ -334,25 +347,22 @@ export const getFilteredEntities = createSelector(
|
|
334
347
|
},
|
335
348
|
);
|
336
349
|
|
337
|
-
export const getUsageFilterOptions = createSelector(
|
338
|
-
|
339
|
-
(entities) => {
|
340
|
-
const items = {};
|
350
|
+
export const getUsageFilterOptions = createSelector(getVisibleEntitiesList, (entities) => {
|
351
|
+
const items = {};
|
341
352
|
|
342
|
-
|
343
|
-
|
353
|
+
entities.forEach((entity) => {
|
354
|
+
const usage = getUsage(entity, 5);
|
344
355
|
|
345
|
-
|
346
|
-
|
347
|
-
|
356
|
+
if (!Object.hasOwn(items, usage)) {
|
357
|
+
items[usage] = 0;
|
358
|
+
}
|
348
359
|
|
349
|
-
|
350
|
-
|
360
|
+
items[usage] += 1;
|
361
|
+
});
|
351
362
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
);
|
363
|
+
return Object.entries(items)
|
364
|
+
.map(([threshold, count]) => ({threshold, count}))
|
365
|
+
.sort((a, b) => b.threshold - a.threshold);
|
366
|
+
});
|
357
367
|
|
358
368
|
export default storage;
|
@@ -6,7 +6,7 @@ const FETCH_TENANT = createRequestActionTypes('tenant', 'FETCH_TENANT');
|
|
6
6
|
const SET_TOP_LEVEL_TAB = 'tenant/SET_TOP_LEVEL_TAB';
|
7
7
|
const SET_DIAGNOSTICS_TAB = 'tenant/SET_DIAGNOSTICS_TAB';
|
8
8
|
|
9
|
-
const tenantReducer = (state = {loading: false, tenant: {}}, action) => {
|
9
|
+
const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, action) => {
|
10
10
|
switch (action.type) {
|
11
11
|
case FETCH_TENANT.REQUEST: {
|
12
12
|
return {
|
@@ -23,6 +23,7 @@ const tenantReducer = (state = {loading: false, tenant: {}}, action) => {
|
|
23
23
|
tenant,
|
24
24
|
tenantNodes,
|
25
25
|
loading: false,
|
26
|
+
wasLoaded: true,
|
26
27
|
error: undefined,
|
27
28
|
};
|
28
29
|
}
|
@@ -32,6 +33,7 @@ const tenantReducer = (state = {loading: false, tenant: {}}, action) => {
|
|
32
33
|
...state,
|
33
34
|
data: action.error,
|
34
35
|
loading: false,
|
36
|
+
wasLoaded: true,
|
35
37
|
};
|
36
38
|
}
|
37
39
|
|
package/dist/styles/mixins.scss
CHANGED
@@ -150,7 +150,7 @@
|
|
150
150
|
background-color: var(--yc-color-base-background);
|
151
151
|
}
|
152
152
|
.data-table__row:hover .data-table__td:nth-child(#{$nth}) {
|
153
|
-
background-color: var(--
|
153
|
+
background-color: var(--ydb-data-table-color-hover) !important;
|
154
154
|
}
|
155
155
|
}
|
156
156
|
|
@@ -172,28 +172,53 @@ export interface TVDiskStateInfo {
|
|
172
172
|
}
|
173
173
|
|
174
174
|
export interface TBSGroupStateInfo {
|
175
|
-
|
176
|
-
GroupID?: string;
|
175
|
+
GroupID?: number;
|
177
176
|
ErasureSpecies?: string;
|
178
177
|
VDisks?: TVDiskStateInfo[];
|
179
178
|
/** uint64 */
|
180
179
|
ChangeTime?: string;
|
181
|
-
|
182
|
-
|
183
|
-
/** uint32 */
|
184
|
-
GroupGeneration?: string;
|
180
|
+
NodeId?: number;
|
181
|
+
GroupGeneration?: number;
|
185
182
|
Overall?: EFlag;
|
186
183
|
Latency?: EFlag;
|
187
|
-
|
188
|
-
|
189
|
-
|
184
|
+
Count?: number;
|
185
|
+
StoragePoolName?: string;
|
186
|
+
/** uint64 */
|
187
|
+
AllocatedSize?: string;
|
188
|
+
/** uint64 */
|
189
|
+
AvailableSize?: string;
|
190
|
+
/** uint64 */
|
191
|
+
ReadThroughput?: string;
|
192
|
+
/** uint64 */
|
193
|
+
WriteThroughput?: string;
|
194
|
+
Encryption?: boolean;
|
195
|
+
}
|
196
|
+
|
197
|
+
interface THiveStorageGroupStats {
|
198
|
+
GroupID?: number;
|
199
|
+
/** uint64 */
|
200
|
+
AcquiredUnits?: string;
|
201
|
+
AcquiredIOPS?: number;
|
202
|
+
/** uint64 */
|
203
|
+
AcquiredThroughput?: string;
|
204
|
+
/** uint64 */
|
205
|
+
AcquiredSize?: string;
|
206
|
+
MaximumIOPS?: number;
|
207
|
+
/** uint64 */
|
208
|
+
MaximumThroughput?: string;
|
209
|
+
/** uint64 */
|
210
|
+
MaximumSize?: string;
|
211
|
+
/** uint64 */
|
212
|
+
AllocatedSize?: string;
|
213
|
+
/** uint64 */
|
214
|
+
AvailableSize?: string;
|
190
215
|
}
|
191
216
|
|
192
217
|
export interface TStoragePoolInfo {
|
193
218
|
Overall?: EFlag;
|
194
219
|
Name?: string;
|
195
220
|
Kind?: string;
|
196
|
-
Groups?: TBSGroupStateInfo[];
|
221
|
+
Groups?: (TBSGroupStateInfo & THiveStorageGroupStats)[];
|
197
222
|
/** uint64 */
|
198
223
|
AcquiredUnits?: string;
|
199
224
|
AcquiredIOPS?: number;
|
@@ -2,7 +2,11 @@ import {DependencyList, useEffect, useRef} from 'react';
|
|
2
2
|
|
3
3
|
import {AutoFetcher} from '../autofetcher';
|
4
4
|
|
5
|
-
export const useAutofetcher = (
|
5
|
+
export const useAutofetcher = (
|
6
|
+
fetchData: (isBackground: boolean) => void,
|
7
|
+
deps: DependencyList,
|
8
|
+
enabled = true,
|
9
|
+
) => {
|
6
10
|
const ref = useRef<AutoFetcher | null>(null);
|
7
11
|
|
8
12
|
if (ref.current === null) {
|
@@ -12,14 +16,16 @@ export const useAutofetcher = (fetchData: VoidFunction, deps: DependencyList, en
|
|
12
16
|
const autofetcher = ref.current;
|
13
17
|
|
14
18
|
// initial fetch
|
15
|
-
useEffect(
|
19
|
+
useEffect(() => {
|
20
|
+
fetchData(false);
|
21
|
+
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
|
16
22
|
|
17
23
|
useEffect(() => {
|
18
24
|
autofetcher.stop();
|
19
25
|
|
20
26
|
if (enabled) {
|
21
27
|
autofetcher.start();
|
22
|
-
autofetcher.fetch(fetchData);
|
28
|
+
autofetcher.fetch(() => fetchData(true));
|
23
29
|
}
|
24
30
|
|
25
31
|
return () => {
|