ydb-embedded-ui 2.2.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +37 -0
- package/dist/assets/icons/shield.svg +3 -0
- package/dist/components/Errors/403/AccessDenied.tsx +19 -0
- package/dist/components/Errors/403/index.ts +1 -0
- package/dist/components/Errors/i18n/en.json +4 -0
- package/dist/components/Errors/i18n/index.ts +11 -0
- package/dist/components/Errors/i18n/ru.json +4 -0
- package/dist/components/NodesViewer/NodesViewer.js +1 -1
- package/dist/components/QueryResultTable/QueryResultTable.tsx +16 -21
- package/dist/{containers/Storage/StorageFilter/StorageFilter.tsx → components/Search/Search.tsx} +22 -22
- package/dist/components/Search/index.ts +1 -0
- package/dist/containers/App/App.scss +5 -1
- package/dist/containers/Nodes/Nodes.js +6 -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 +39 -50
- 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/TabletsFilters/TabletsFilters.js +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +6 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +82 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +6 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +6 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +7 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +29 -11
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -8
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +55 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +5 -5
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +16 -6
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/{IssueViewer.scss → IssueTree.scss} +3 -54
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTree.tsx +87 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +50 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +25 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +13 -16
- package/dist/containers/Tenant/Diagnostics/Healthcheck/{IssuePreview/IssuePreview.tsx → Preview/PreviewItem/PreviewItem.tsx} +6 -8
- package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +34 -19
- package/dist/containers/Tenant/Preview/Preview.scss +6 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +1 -9
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
- 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 +14 -16
- package/dist/containers/Tenants/Tenants.js +1 -1
- package/dist/store/reducers/describe.ts +71 -0
- package/dist/store/reducers/healthcheckInfo.ts +123 -0
- package/dist/store/reducers/olapStats.js +13 -0
- package/dist/store/reducers/schema.js +43 -1
- package/dist/store/reducers/storage.js +27 -17
- package/dist/store/reducers/tenant.js +3 -1
- package/dist/store/utils.ts +21 -13
- package/dist/styles/mixins.scss +1 -1
- package/dist/types/api/consumers.ts +3 -0
- package/dist/types/api/healthcheck.ts +1 -1
- package/dist/types/api/storage.ts +35 -10
- package/dist/types/store/healthcheck.ts +5 -1
- package/dist/types/store/storage.ts +1 -0
- package/dist/utils/hooks/useAutofetcher.ts +9 -3
- package/package.json +1 -1
- package/dist/containers/Storage/StorageFilter/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/IssuesList.tsx +0 -62
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/index.ts +0 -1
- package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssuesViewer.js +0 -151
- package/dist/store/reducers/describe.js +0 -45
- package/dist/store/reducers/healthcheckInfo.js +0 -45
@@ -4,8 +4,7 @@ import cn from 'bem-cn-lite';
|
|
4
4
|
import {useLocation} from 'react-router';
|
5
5
|
import qs from 'qs';
|
6
6
|
|
7
|
-
import
|
8
|
-
import {Illustration} from '../../components/Illustration';
|
7
|
+
import {AccessDenied} from '../../components/Errors/403';
|
9
8
|
|
10
9
|
import {setHeader} from '../../store/reducers/header';
|
11
10
|
import ObjectGeneralTabs from './ObjectGeneralTabs/ObjectGeneralTabs';
|
@@ -16,7 +15,7 @@ import SplitPane from '../../components/SplitPane';
|
|
16
15
|
//@ts-ignore
|
17
16
|
import {DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY} from '../../utils/constants';
|
18
17
|
//@ts-ignore
|
19
|
-
import {disableAutorefresh, getSchema} from '../../store/reducers/schema';
|
18
|
+
import {disableAutorefresh, getSchema, resetLoadingState} from '../../store/reducers/schema';
|
20
19
|
//@ts-ignore
|
21
20
|
import {getSchemaAcl} from '../../store/reducers/schemaAcl';
|
22
21
|
import {
|
@@ -57,6 +56,13 @@ function Tenant(props: TenantProps) {
|
|
57
56
|
(state: any) => state.schema,
|
58
57
|
);
|
59
58
|
|
59
|
+
const {PathType: preloadedPathType, PathSubType: preloadedPathSubType} = useSelector(
|
60
|
+
(state: any) => state.schema.data[currentSchemaPath]?.PathDescription?.Self || {},
|
61
|
+
);
|
62
|
+
|
63
|
+
const {PathType: currentPathType, PathSubType: currentPathSubType} =
|
64
|
+
(currentItem as TEvDescribeSchemeResult).PathDescription?.Self || {};
|
65
|
+
|
60
66
|
const {data: {status: tenantStatus = 200} = {}} = useSelector((state: any) => state.tenant);
|
61
67
|
const {error: {status: schemaStatus = 200} = {}} = useSelector((state: any) => state.schema);
|
62
68
|
|
@@ -73,6 +79,7 @@ function Tenant(props: TenantProps) {
|
|
73
79
|
|
74
80
|
useEffect(() => {
|
75
81
|
const schemaPath = currentSchemaPath || tenantName;
|
82
|
+
dispatch(resetLoadingState());
|
76
83
|
dispatch(getSchema({path: tenantName}));
|
77
84
|
dispatch(getSchema({path: schemaPath}));
|
78
85
|
dispatch(getSchemaAcl({path: schemaPath}));
|
@@ -105,11 +112,6 @@ function Tenant(props: TenantProps) {
|
|
105
112
|
};
|
106
113
|
}, [tenantName, dispatch]);
|
107
114
|
|
108
|
-
const {
|
109
|
-
PathType: currentPathType,
|
110
|
-
PathSubType: currentPathSubType,
|
111
|
-
} = (currentItem as TEvDescribeSchemeResult).PathDescription?.Self || {};
|
112
|
-
|
113
115
|
const onCollapseSummaryHandler = () => {
|
114
116
|
dispatchSummaryVisibilityAction(PaneVisibilityActionTypes.triggerCollapse);
|
115
117
|
};
|
@@ -126,11 +128,7 @@ function Tenant(props: TenantProps) {
|
|
126
128
|
return (
|
127
129
|
<div className={b()}>
|
128
130
|
{showBlockingError ? (
|
129
|
-
<
|
130
|
-
image={<Illustration name="403" />}
|
131
|
-
title="Access denied"
|
132
|
-
description="You don’t have the necessary roles to view this page."
|
133
|
-
/>
|
131
|
+
<AccessDenied />
|
134
132
|
) : (
|
135
133
|
<>
|
136
134
|
<ObjectGeneralTabs />
|
@@ -144,15 +142,15 @@ function Tenant(props: TenantProps) {
|
|
144
142
|
onSplitStartDragAdditional={onSplitStartDragAdditional}
|
145
143
|
>
|
146
144
|
<ObjectSummary
|
147
|
-
type={currentPathType}
|
148
|
-
subType={currentPathSubType}
|
145
|
+
type={preloadedPathType || currentPathType}
|
146
|
+
subType={preloadedPathSubType || currentPathSubType}
|
149
147
|
onCollapseSummary={onCollapseSummaryHandler}
|
150
148
|
onExpandSummary={onExpandSummaryHandler}
|
151
149
|
isCollapsed={summaryVisibilityState.collapsed}
|
152
150
|
additionalTenantInfo={props.additionalTenantInfo}
|
153
151
|
/>
|
154
152
|
<ObjectGeneral
|
155
|
-
type={currentPathType}
|
153
|
+
type={preloadedPathType || currentPathType}
|
156
154
|
additionalTenantInfo={props.additionalTenantInfo}
|
157
155
|
additionalNodesInfo={props.additionalNodesInfo}
|
158
156
|
/>
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import {createSelector} from 'reselect';
|
2
|
+
|
3
|
+
import '../../services/api';
|
4
|
+
import {TEvDescribeSchemeResult} from '../../types/api/schema';
|
5
|
+
import {IConsumer} from '../../types/api/consumers';
|
6
|
+
import {createRequestActionTypes, createApiRequest, ApiRequestAction} from '../utils';
|
7
|
+
|
8
|
+
const FETCH_DESCRIBE = createRequestActionTypes('describe', 'FETCH_DESCRIBE');
|
9
|
+
|
10
|
+
const describe = (
|
11
|
+
state = {loading: false, wasLoaded: false, data: {}},
|
12
|
+
action: ApiRequestAction<typeof FETCH_DESCRIBE, TEvDescribeSchemeResult, unknown>,
|
13
|
+
) => {
|
14
|
+
switch (action.type) {
|
15
|
+
case FETCH_DESCRIBE.REQUEST: {
|
16
|
+
return {
|
17
|
+
...state,
|
18
|
+
loading: true,
|
19
|
+
};
|
20
|
+
}
|
21
|
+
case FETCH_DESCRIBE.SUCCESS: {
|
22
|
+
let newData;
|
23
|
+
|
24
|
+
if (action.data.Path) {
|
25
|
+
newData = JSON.parse(JSON.stringify(state.data));
|
26
|
+
newData[action.data.Path] = action.data;
|
27
|
+
} else {
|
28
|
+
newData = state.data;
|
29
|
+
}
|
30
|
+
|
31
|
+
return {
|
32
|
+
...state,
|
33
|
+
data: newData,
|
34
|
+
currentDescribe: action.data,
|
35
|
+
loading: false,
|
36
|
+
wasLoaded: true,
|
37
|
+
error: undefined,
|
38
|
+
};
|
39
|
+
}
|
40
|
+
case FETCH_DESCRIBE.FAILURE: {
|
41
|
+
return {
|
42
|
+
...state,
|
43
|
+
error: action.error,
|
44
|
+
loading: false,
|
45
|
+
};
|
46
|
+
}
|
47
|
+
default:
|
48
|
+
return state;
|
49
|
+
}
|
50
|
+
};
|
51
|
+
|
52
|
+
// Consumers selectors
|
53
|
+
const selectConsumersNames = (state: any, path: string): string[] | undefined =>
|
54
|
+
state.describe.data[path]?.PathDescription?.PersQueueGroup?.PQTabletConfig?.ReadRules;
|
55
|
+
|
56
|
+
export const selectConsumers = createSelector(selectConsumersNames, (names = []): IConsumer[] => {
|
57
|
+
const consumers = names.map((name) => {
|
58
|
+
return {name};
|
59
|
+
});
|
60
|
+
|
61
|
+
return consumers;
|
62
|
+
});
|
63
|
+
|
64
|
+
export function getDescribe({path}: {path: string}) {
|
65
|
+
return createApiRequest({
|
66
|
+
request: window.api.getDescribe({path}),
|
67
|
+
actions: FETCH_DESCRIBE,
|
68
|
+
});
|
69
|
+
}
|
70
|
+
|
71
|
+
export default describe;
|
@@ -0,0 +1,123 @@
|
|
1
|
+
import _flow from 'lodash/fp/flow';
|
2
|
+
import _sortBy from 'lodash/fp/sortBy';
|
3
|
+
import _uniqBy from 'lodash/fp/uniqBy';
|
4
|
+
import _omit from 'lodash/omit';
|
5
|
+
import {createSelector} from 'reselect';
|
6
|
+
|
7
|
+
import {IIssuesTree} from '../../types/store/healthcheck';
|
8
|
+
import {HealthCheckAPIResponse, IssueLog, StatusFlag} from '../../types/api/healthcheck';
|
9
|
+
|
10
|
+
import '../../services/api';
|
11
|
+
import {createRequestActionTypes, createApiRequest, ApiRequestAction} from '../utils';
|
12
|
+
|
13
|
+
const FETCH_HEALTHCHECK = createRequestActionTypes('cluster', 'FETCH_HEALTHCHECK');
|
14
|
+
|
15
|
+
const initialState = {loading: false, wasLoaded: false};
|
16
|
+
|
17
|
+
const healthcheckInfo = function (
|
18
|
+
state = initialState,
|
19
|
+
action: ApiRequestAction<typeof FETCH_HEALTHCHECK, HealthCheckAPIResponse, unknown>,
|
20
|
+
) {
|
21
|
+
switch (action.type) {
|
22
|
+
case FETCH_HEALTHCHECK.REQUEST: {
|
23
|
+
return {
|
24
|
+
...state,
|
25
|
+
loading: true,
|
26
|
+
};
|
27
|
+
}
|
28
|
+
case FETCH_HEALTHCHECK.SUCCESS: {
|
29
|
+
const {data} = action;
|
30
|
+
|
31
|
+
return {
|
32
|
+
...state,
|
33
|
+
data,
|
34
|
+
wasLoaded: true,
|
35
|
+
loading: false,
|
36
|
+
error: undefined,
|
37
|
+
};
|
38
|
+
}
|
39
|
+
case FETCH_HEALTHCHECK.FAILURE: {
|
40
|
+
return {
|
41
|
+
...state,
|
42
|
+
error: action.error,
|
43
|
+
loading: false,
|
44
|
+
};
|
45
|
+
}
|
46
|
+
default:
|
47
|
+
return state;
|
48
|
+
}
|
49
|
+
};
|
50
|
+
|
51
|
+
const mapStatusToPriority: Partial<Record<StatusFlag, number>> = {
|
52
|
+
RED: 0,
|
53
|
+
ORANGE: 1,
|
54
|
+
YELLOW: 2,
|
55
|
+
BLUE: 3,
|
56
|
+
GREEN: 4,
|
57
|
+
};
|
58
|
+
|
59
|
+
const getReasonsForIssue = ({issue, data}: {issue: IssueLog; data: IssueLog[]}) => {
|
60
|
+
return data.filter((item) => issue.reason && issue.reason.indexOf(item.id) !== -1);
|
61
|
+
};
|
62
|
+
|
63
|
+
const getRoots = (data: IssueLog[]) => {
|
64
|
+
let roots = data.filter((item) => {
|
65
|
+
return !data.find((issue) => issue.reason && issue.reason.indexOf(item.id) !== -1);
|
66
|
+
});
|
67
|
+
|
68
|
+
roots = _flow([
|
69
|
+
_uniqBy((item: IssueLog) => item.id),
|
70
|
+
_sortBy(({status}: {status: StatusFlag}) => mapStatusToPriority[status]),
|
71
|
+
])(roots);
|
72
|
+
|
73
|
+
return roots;
|
74
|
+
};
|
75
|
+
|
76
|
+
const getInvertedConsequencesTree = ({
|
77
|
+
data,
|
78
|
+
roots,
|
79
|
+
}: {
|
80
|
+
data: IssueLog[];
|
81
|
+
roots?: IssueLog[];
|
82
|
+
}): IIssuesTree[] => {
|
83
|
+
return roots
|
84
|
+
? roots.map((issue) => {
|
85
|
+
const reasonsItems = getInvertedConsequencesTree({
|
86
|
+
roots: getReasonsForIssue({issue, data}),
|
87
|
+
data,
|
88
|
+
});
|
89
|
+
|
90
|
+
return {
|
91
|
+
...issue,
|
92
|
+
reasonsItems,
|
93
|
+
};
|
94
|
+
})
|
95
|
+
: [];
|
96
|
+
};
|
97
|
+
|
98
|
+
const getIssuesLog = (state: any): IssueLog[] | undefined => state.healthcheckInfo.data?.issue_log;
|
99
|
+
|
100
|
+
export const selectIssuesTreesRoots = createSelector(getIssuesLog, (issues = []) =>
|
101
|
+
getRoots(issues),
|
102
|
+
);
|
103
|
+
|
104
|
+
export const selectIssuesTrees = createSelector(
|
105
|
+
[getIssuesLog, selectIssuesTreesRoots],
|
106
|
+
(data = [], roots = []) => {
|
107
|
+
return getInvertedConsequencesTree({data, roots});
|
108
|
+
},
|
109
|
+
);
|
110
|
+
|
111
|
+
export const selectIssuesTreeById = createSelector(
|
112
|
+
[selectIssuesTrees, (_: any, id: string | undefined) => id],
|
113
|
+
(issuesTrees = [], id) => issuesTrees.find((issuesTree: IIssuesTree) => issuesTree.id === id),
|
114
|
+
);
|
115
|
+
|
116
|
+
export function getHealthcheckInfo(database: string) {
|
117
|
+
return createApiRequest({
|
118
|
+
request: window.api.getHealthcheckInfo(database),
|
119
|
+
actions: FETCH_HEALTHCHECK,
|
120
|
+
});
|
121
|
+
}
|
122
|
+
|
123
|
+
export default healthcheckInfo;
|
@@ -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,
|
@@ -16,7 +18,7 @@ export const initialState = {
|
|
16
18
|
showPreview: false,
|
17
19
|
};
|
18
20
|
|
19
|
-
const schema =
|
21
|
+
const schema = (state = initialState, action) => {
|
20
22
|
switch (action.type) {
|
21
23
|
case FETCH_SCHEMA.REQUEST: {
|
22
24
|
return {
|
@@ -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',
|
@@ -38,7 +39,7 @@ const initialState = {
|
|
38
39
|
type: StorageTypes.groups,
|
39
40
|
};
|
40
41
|
|
41
|
-
const storage =
|
42
|
+
const storage = (state = initialState, action) => {
|
42
43
|
switch (action.type) {
|
43
44
|
case FETCH_STORAGE.REQUEST: {
|
44
45
|
return {
|
@@ -56,10 +57,15 @@ const storage = function z(state = initialState, action) {
|
|
56
57
|
};
|
57
58
|
}
|
58
59
|
case FETCH_STORAGE.FAILURE: {
|
60
|
+
if (action.error?.isCancelled) {
|
61
|
+
return state;
|
62
|
+
}
|
63
|
+
|
59
64
|
return {
|
60
65
|
...state,
|
61
66
|
error: action.error,
|
62
67
|
loading: false,
|
68
|
+
wasLoaded: true,
|
63
69
|
};
|
64
70
|
}
|
65
71
|
case SET_INITIAL: {
|
@@ -229,6 +235,12 @@ export const getFlatListStorageGroups = createSelector([getStoragePools], (stora
|
|
229
235
|
},
|
230
236
|
0,
|
231
237
|
);
|
238
|
+
const mediaType = group.VDisks?.reduce((type, vdisk) => {
|
239
|
+
const currentType = getPDiskType(vdisk.PDisk || {});
|
240
|
+
return currentType && (currentType === type || type === '')
|
241
|
+
? currentType
|
242
|
+
: '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/store/utils.ts
CHANGED
@@ -7,7 +7,10 @@ import {SET_UNAUTHENTICATED} from './reducers/authentication';
|
|
7
7
|
|
8
8
|
export const nop = (result: any) => result;
|
9
9
|
|
10
|
-
export function createRequestActionTypes<Prefix extends string, Type extends string>(
|
10
|
+
export function createRequestActionTypes<Prefix extends string, Type extends string>(
|
11
|
+
prefix: Prefix,
|
12
|
+
type: Type,
|
13
|
+
) {
|
11
14
|
return {
|
12
15
|
REQUEST: `${prefix}/${type}_REQUEST`,
|
13
16
|
SUCCESS: `${prefix}/${type}_SUCCESS`,
|
@@ -15,19 +18,24 @@ export function createRequestActionTypes<Prefix extends string, Type extends str
|
|
15
18
|
} as const;
|
16
19
|
}
|
17
20
|
|
18
|
-
const isAxiosResponse = (response: any): response is AxiosResponse =>
|
21
|
+
const isAxiosResponse = (response: any): response is AxiosResponse =>
|
22
|
+
response && 'status' in response;
|
19
23
|
|
20
24
|
type CreateApiRequestParams<Actions, Response, HandledResponse> = {
|
21
25
|
actions: Actions;
|
22
26
|
request: Promise<Response>;
|
23
|
-
dataHandler
|
27
|
+
dataHandler?: (data: Response, getState?: () => any) => HandledResponse;
|
24
28
|
};
|
25
29
|
|
26
30
|
export function createApiRequest<
|
27
31
|
Actions extends ReturnType<typeof createRequestActionTypes>,
|
28
32
|
Response,
|
29
33
|
HandledResponse,
|
30
|
-
>({
|
34
|
+
>({
|
35
|
+
actions,
|
36
|
+
request,
|
37
|
+
dataHandler = nop,
|
38
|
+
}: CreateApiRequestParams<Actions, Response, HandledResponse>) {
|
31
39
|
const doRequest = async function (dispatch: Dispatch, getState: () => any) {
|
32
40
|
dispatch({
|
33
41
|
type: actions.REQUEST,
|
@@ -73,16 +81,16 @@ export function createApiRequest<
|
|
73
81
|
export type ApiRequestAction<
|
74
82
|
Actions extends ReturnType<typeof createRequestActionTypes>,
|
75
83
|
SuccessResponse = unknown,
|
76
|
-
ErrorResponse = unknown
|
84
|
+
ErrorResponse = unknown,
|
77
85
|
> =
|
78
86
|
| {
|
79
|
-
|
80
|
-
|
87
|
+
type: Actions['REQUEST'];
|
88
|
+
}
|
81
89
|
| {
|
82
|
-
|
83
|
-
|
84
|
-
|
90
|
+
type: Actions['SUCCESS'];
|
91
|
+
data: SuccessResponse;
|
92
|
+
}
|
85
93
|
| {
|
86
|
-
|
87
|
-
|
88
|
-
|
94
|
+
type: Actions['FAILURE'];
|
95
|
+
error: ErrorResponse;
|
96
|
+
};
|
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;
|