ydb-embedded-ui 1.0.4 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +38 -0
- package/dist/assets/icons/question.svg +1 -0
- package/dist/components/AsideNavigation/AsideHeader.tsx +2 -1
- package/dist/components/ClusterInfo/ClusterInfo.tsx +8 -4
- package/dist/components/FullNodeViewer/FullNodeViewer.scss +4 -9
- package/dist/components/InfoViewer/InfoViewer.scss +3 -2
- package/dist/components/InternalLink/InternalLink.js +8 -0
- package/dist/components/Loader/Loader.scss +5 -0
- package/dist/components/Loader/Loader.tsx +16 -0
- package/dist/components/PDiskViewer/PDiskViewer.js +3 -4
- package/dist/containers/App/App.scss +4 -0
- package/dist/containers/App/Content.js +0 -2
- package/dist/containers/AppIcons/AppIcons.js +4 -0
- package/dist/containers/Authentication/Authentication.tsx +2 -2
- package/dist/containers/Cluster/Cluster.tsx +1 -1
- package/dist/containers/Header/Header.tsx +6 -1
- package/dist/containers/Heatmap/Heatmap.js +0 -1
- package/dist/containers/Node/Node.scss +12 -1
- package/dist/containers/Node/Node.tsx +174 -0
- package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
- package/dist/containers/Node/NodeOverview/NodeOverview.tsx +23 -0
- package/dist/containers/Node/NodePages.js +16 -0
- package/dist/containers/Node/NodeStructure/NodeStructure.scss +151 -0
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +155 -0
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +299 -0
- package/dist/containers/Node/NodeStructure/Vdisk.tsx +153 -0
- package/dist/containers/Pdisk/Pdisk.js +2 -5
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +10 -3
- package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +20 -15
- package/dist/containers/Storage/Pdisk/Pdisk.scss +1 -0
- package/dist/containers/Storage/Pdisk/Pdisk.tsx +7 -5
- package/dist/containers/Storage/Storage.js +12 -9
- package/dist/containers/Storage/StorageGroups/StorageGroups.scss +2 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +2 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +3 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +2 -2
- package/dist/containers/Storage/Vdisk/Vdisk.js +7 -6
- package/dist/containers/Storage/Vdisk/Vdisk.scss +1 -0
- package/dist/containers/Tablet/Tablet.js +2 -7
- package/dist/containers/Tablets/Tablets.js +4 -12
- package/dist/containers/Tenant/Acl/Acl.js +0 -3
- package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +1 -3
- package/dist/containers/Tenant/Diagnostics/Network/Network.js +3 -6
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +2 -2
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +24 -24
- package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +4 -0
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +4 -1
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +1 -0
- package/dist/containers/Vdisk/Vdisk.js +2 -4
- package/dist/containers/VdiskPdiskNode/VdiskPdiskNode.js +4 -6
- package/dist/services/api.js +0 -1
- package/dist/store/reducers/executeQuery.js +1 -1
- package/dist/store/reducers/header.ts +1 -1
- package/dist/store/reducers/node.js +98 -3
- package/dist/store/reducers/nodes.js +0 -3
- package/dist/store/reducers/storage.js +8 -2
- package/dist/store/reducers/tablets.js +0 -3
- package/dist/utils/constants.js +0 -6
- package/dist/utils/getNodesColumns.js +2 -9
- package/dist/utils/utils.js +10 -1
- package/package.json +43 -29
- package/dist/containers/Node/Node.js +0 -184
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,43 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### [1.1.2](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.1.1...v1.1.2) (2022-04-19)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* **ObjectSummary:** should correctly parse table creation time ([c9887dd](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/c9887dd162720667dcbe3b4834b3b0ba5a9f3f6e))
|
9
|
+
|
10
|
+
### [1.1.1](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.1.0...v1.1.1) (2022-04-19)
|
11
|
+
|
12
|
+
|
13
|
+
### Bug Fixes
|
14
|
+
|
15
|
+
* add typecheck + fix type errors ([e6d9086](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/e6d9086c46702a611f848c992377d18826ca2e23))
|
16
|
+
* **Node:** scroll to selected vdisk should not apply to undefined container ([7236a43](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/7236a43655b935777abb5b8df228ae011ceb6bed))
|
17
|
+
|
18
|
+
## [1.1.0](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.0.4...v1.1.0) (2022-04-15)
|
19
|
+
|
20
|
+
|
21
|
+
### Features
|
22
|
+
|
23
|
+
* local precommit check ([d5da9b3](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/d5da9b3fb89eeeb5461e7e14fe33964a8ed9078d))
|
24
|
+
* new Node Structure view ([5cf5dd3](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/5cf5dd39fa59625be4bb89f16796f16ecb9d9d78))
|
25
|
+
|
26
|
+
|
27
|
+
### Bug Fixes
|
28
|
+
|
29
|
+
* **Authentication:** should be able to send authentication data with empty password [YDB-1610] ([5d4d881](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/5d4d8810bb2ddabb9db1316a99194f5a1bd986b6))
|
30
|
+
* **Cluster:** should show additional info ([cb21ce3](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/cb21ce317c55d05c7a7c166bc09dc1fe14e41692))
|
31
|
+
* code-review ([a706903](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/a706903e6a30ee62aff5829c37ba8c197335e106))
|
32
|
+
* different interface fixes ([0bd3a32](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/0bd3a32bf1502cc6d0f7419aa9d00653afe5d7bf))
|
33
|
+
* improve usability ([20f1acc](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/20f1acc255876968ea366a860d33a12eecc5e74f))
|
34
|
+
* **Nodes:** default path to node should be Overview ([ac4add6](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/ac4add6c1403ac2b9614f252fabf23b9e97ef2c2))
|
35
|
+
* query run type select should be styled as action button [YDB-1567] ([d06cd6a](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/d06cd6ac72ccb8c7eef205fddb1153e6383baeea))
|
36
|
+
* **QueryEditor:** should resolve row key by index [YDB-1604] ([4acd2a3](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/4acd2a30d03f2e45368587839549f4e5981f93dd))
|
37
|
+
* refactoring ([0c5aca5](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/0c5aca5b96bc5d5e9c3f121aa1ffe394f3fbd28f))
|
38
|
+
* **Storage:** wording fixed [YDB-1552] ([431f77f](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/431f77f090073037404639c686246d2f115d98f4))
|
39
|
+
* styles ([2725055](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/2725055b0f25e711c73e2888da41cfaf2657b110))
|
40
|
+
|
3
41
|
### [1.0.4](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.0.3...v1.0.4) (2022-03-24)
|
4
42
|
|
5
43
|
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 400c-18 0-32-14-32-32s13.1-32 32-32c17.1 0 32 14 32 32S273.1 400 256 400zM325.1 258L280 286V288c0 13-11 24-24 24S232 301 232 288V272c0-8 4-16 12-21l57-34C308 213 312 206 312 198C312 186 301.1 176 289.1 176h-51.1C225.1 176 216 186 216 198c0 13-11 24-24 24s-24-11-24-24C168 159 199 128 237.1 128h51.1C329 128 360 159 360 198C360 222 347 245 325.1 258z"/></svg>
|
@@ -20,7 +20,7 @@ import {
|
|
20
20
|
FooterItemIconView,
|
21
21
|
} from './constants';
|
22
22
|
import i18n from './i18n';
|
23
|
-
|
23
|
+
|
24
24
|
import {getLocalData, setLocalData} from './helpers';
|
25
25
|
import {SetSlotsContext, SlotsProvider} from './AsideHeaderFooterSlot/SlotsContext';
|
26
26
|
import {SlotName} from './AsideHeaderFooterSlot/AsideHeaderFooterSlot';
|
@@ -28,6 +28,7 @@ import {SlotName} from './AsideHeaderFooterSlot/AsideHeaderFooterSlot';
|
|
28
28
|
import controlMenuButton from '../../assets/icons/control-menu-button.svg';
|
29
29
|
|
30
30
|
import './AsideHeader.scss';
|
31
|
+
import {Lang} from '../../utils/i18n';
|
31
32
|
|
32
33
|
const b = block('nv-aside-header');
|
33
34
|
|
@@ -31,7 +31,7 @@ import {Link, Loader} from '@yandex-cloud/uikit';
|
|
31
31
|
//@ts-ignore
|
32
32
|
import Icon from '../Icon/Icon';
|
33
33
|
import {setHeader} from '../../store/reducers/header';
|
34
|
-
import routes, {
|
34
|
+
import routes, {CLUSTER_PAGES, createHref} from '../../routes';
|
35
35
|
|
36
36
|
const b = cn('cluster-info');
|
37
37
|
|
@@ -62,7 +62,7 @@ interface ClusterInfoProps {
|
|
62
62
|
setHeader: any;
|
63
63
|
getClusterInfo: (clusterName: string) => void;
|
64
64
|
clusterTitle?: string;
|
65
|
-
|
65
|
+
additionalClusterInfo?: IClusterInfoItem[];
|
66
66
|
loading: boolean;
|
67
67
|
singleClusterMode: boolean;
|
68
68
|
wasLoaded: boolean;
|
@@ -128,7 +128,11 @@ class ClusterInfo extends React.Component<ClusterInfoProps> {
|
|
128
128
|
private autofetcher: any;
|
129
129
|
|
130
130
|
private getInfo() {
|
131
|
-
const {
|
131
|
+
const {
|
132
|
+
cluster = {} as ICluster,
|
133
|
+
additionalClusterInfo = [],
|
134
|
+
singleClusterMode,
|
135
|
+
} = this.props;
|
132
136
|
const {StorageTotal, StorageUsed} = cluster;
|
133
137
|
|
134
138
|
let link = backend + '/internal';
|
@@ -173,7 +177,7 @@ class ClusterInfo extends React.Component<ClusterInfoProps> {
|
|
173
177
|
label: 'Versions',
|
174
178
|
value: <div>{cluster.Versions?.join(', ')}</div>,
|
175
179
|
},
|
176
|
-
...
|
180
|
+
...additionalClusterInfo,
|
177
181
|
{
|
178
182
|
label: 'Internal viewer',
|
179
183
|
value: (
|
@@ -3,13 +3,12 @@
|
|
3
3
|
.full-node-viewer {
|
4
4
|
font-size: var(--yc-text-body2-font-size);
|
5
5
|
line-height: var(--yc-text-body2-line-height);
|
6
|
-
@include container-fluid();
|
7
6
|
|
8
7
|
&__title {
|
9
8
|
margin: 0 20px 0 0;
|
10
9
|
|
11
10
|
font-size: var(--yc-text-body2-font-size);
|
12
|
-
font-weight:
|
11
|
+
font-weight: 600;
|
13
12
|
line-height: var(--yc-text-body2-line-height);
|
14
13
|
text-transform: uppercase;
|
15
14
|
}
|
@@ -27,14 +26,12 @@
|
|
27
26
|
|
28
27
|
&__common-info {
|
29
28
|
display: flex;
|
29
|
+
flex-direction: column;
|
30
30
|
justify-content: flex-start;
|
31
31
|
align-items: stretch;
|
32
32
|
}
|
33
33
|
|
34
34
|
&__section {
|
35
|
-
margin-right: 20px;
|
36
|
-
padding: 15px;
|
37
|
-
|
38
35
|
border-radius: 10px;
|
39
36
|
|
40
37
|
&_pools {
|
@@ -42,8 +39,6 @@
|
|
42
39
|
grid-gap: 7px 20px;
|
43
40
|
|
44
41
|
grid-template-columns: 110px 110px;
|
45
|
-
|
46
|
-
padding: 15px 15px 15px 0;
|
47
42
|
}
|
48
43
|
}
|
49
44
|
|
@@ -72,10 +67,10 @@
|
|
72
67
|
}
|
73
68
|
|
74
69
|
&__section-title {
|
75
|
-
margin: 15px 0
|
70
|
+
margin: 15px 0 10px;
|
76
71
|
|
77
72
|
font-size: var(--yc-text-body2-font-size);
|
78
|
-
font-weight:
|
73
|
+
font-weight: 600;
|
79
74
|
line-height: var(--yc-text-body2-line-height);
|
80
75
|
}
|
81
76
|
|
@@ -2,10 +2,10 @@
|
|
2
2
|
font-size: var(--yc-text-body2-font-size);
|
3
3
|
line-height: var(--yc-text-body2-line-height);
|
4
4
|
&__title {
|
5
|
-
margin:
|
5
|
+
margin: 15px 0 10px;
|
6
6
|
|
7
7
|
font-size: var(--yc-text-body2-font-size);
|
8
|
-
font-weight:
|
8
|
+
font-weight: 600;
|
9
9
|
line-height: var(--yc-text-body2-line-height);
|
10
10
|
}
|
11
11
|
|
@@ -30,6 +30,7 @@
|
|
30
30
|
flex: 1 1 auto;
|
31
31
|
align-items: baseline;
|
32
32
|
|
33
|
+
min-width: 200px;
|
33
34
|
max-width: 200px;
|
34
35
|
|
35
36
|
white-space: nowrap;
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import PropTypes from 'prop-types';
|
1
2
|
import cn from 'bem-cn-lite';
|
2
3
|
|
3
4
|
import {Link} from 'react-router-dom';
|
@@ -13,3 +14,10 @@ export default function InternalLink({to, children, onClick, className}) {
|
|
13
14
|
children
|
14
15
|
);
|
15
16
|
}
|
17
|
+
|
18
|
+
InternalLink.propTypes = {
|
19
|
+
to: PropTypes.string.isRequired,
|
20
|
+
children: PropTypes.node,
|
21
|
+
onClick: PropTypes.func,
|
22
|
+
className: PropTypes.string,
|
23
|
+
};
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
import {Loader as KitLoader, LoaderSize} from '@yandex-cloud/uikit';
|
3
|
+
|
4
|
+
import './Loader.scss';
|
5
|
+
|
6
|
+
const b = cn('kv-loader');
|
7
|
+
|
8
|
+
function Loader({size = 'l'}: {size?: LoaderSize}) {
|
9
|
+
return (
|
10
|
+
<div className={b()}>
|
11
|
+
<KitLoader size={size} />
|
12
|
+
</div>
|
13
|
+
);
|
14
|
+
}
|
15
|
+
|
16
|
+
export default Loader;
|
@@ -8,6 +8,8 @@ import EntityStatus from '../EntityStatus/EntityStatus';
|
|
8
8
|
import {formatStorageValues} from '../../utils';
|
9
9
|
import routes, {createHref} from '../../routes';
|
10
10
|
|
11
|
+
import {getDefaultNodePath} from '../../containers/Node/NodePages';
|
12
|
+
|
11
13
|
import './PDiskViewer.scss';
|
12
14
|
|
13
15
|
const b = cn('pdisk-viewer');
|
@@ -48,10 +50,7 @@ class PDiskViewer extends React.Component {
|
|
48
50
|
<EntityStatus
|
49
51
|
status={'green'}
|
50
52
|
label="NodeID"
|
51
|
-
path={
|
52
|
-
id: disk.NodeId,
|
53
|
-
activeTab: 'storage',
|
54
|
-
})}
|
53
|
+
path={getDefaultNodePath(disk.NodeId)}
|
55
54
|
name={disk.NodeId}
|
56
55
|
/>
|
57
56
|
</div>
|
@@ -12,7 +12,6 @@ import Tenant from '../Tenant/Tenant';
|
|
12
12
|
import Node from '../Node/Node';
|
13
13
|
import Pdisk from '../Pdisk/Pdisk';
|
14
14
|
import Group from '../Group/Group';
|
15
|
-
import VdiskPdiskNode from '../VdiskPdiskNode/VdiskPdiskNode';
|
16
15
|
import Pool from '../Pool/Pool';
|
17
16
|
import Tablet from '../Tablet/Tablet';
|
18
17
|
import TabletsFilters from '../TabletsFilters/TabletsFilters';
|
@@ -44,7 +43,6 @@ export function Content(props) {
|
|
44
43
|
<Route path={routes.cluster} component={Cluster} />
|
45
44
|
<Route path={routes.tenant} component={Tenant} />
|
46
45
|
<Route path={routes.pdisk} component={Pdisk} />
|
47
|
-
<Route path={routes.vdisk} component={VdiskPdiskNode} />
|
48
46
|
<Route path={routes.node} component={Node} />
|
49
47
|
<Route path={routes.group} component={Group} />
|
50
48
|
<Route path={routes.pool} component={Pool} />
|
@@ -2,6 +2,10 @@ const AppIcons = () => (
|
|
2
2
|
/* eslint-disable */
|
3
3
|
<svg width="0" height="0">
|
4
4
|
<defs>
|
5
|
+
<path
|
6
|
+
id="icon.information"
|
7
|
+
d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z"
|
8
|
+
/>
|
5
9
|
<path
|
6
10
|
id="icon.tablePreview"
|
7
11
|
d="M13.2812 2.4375H2.71875C2.0332 2.4375 1.5 2.99609 1.5 3.65625V12.5938C1.5 13.2793 2.0332 13.8125 2.71875 13.8125H13.2812C13.9414 13.8125 14.5 13.2793 14.5 12.5938V3.65625C14.5 2.99609 13.9414 2.4375 13.2812 2.4375ZM7.1875 12.1875H3.125V9.75H7.1875V12.1875ZM7.1875 8.125H3.125V5.6875H7.1875V8.125ZM12.875 12.1875H8.8125V9.75H12.875V12.1875ZM12.875 8.125H8.8125V5.6875H12.875V8.125Z"
|
@@ -43,7 +43,7 @@ function Authentication({authenticate, error}: any) {
|
|
43
43
|
authenticate(login, pass);
|
44
44
|
};
|
45
45
|
|
46
|
-
const onEnterClick = (e: KeyboardEvent<HTMLInputElement>) => {
|
46
|
+
const onEnterClick = (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
47
47
|
if (e.keyCode === 13) {
|
48
48
|
onLoginClick();
|
49
49
|
}
|
@@ -99,7 +99,7 @@ function Authentication({authenticate, error}: any) {
|
|
99
99
|
onClick={onLoginClick}
|
100
100
|
width="max"
|
101
101
|
size="l"
|
102
|
-
disabled={Boolean(!login ||
|
102
|
+
disabled={Boolean(!login || loginError || passwordError)}
|
103
103
|
className={b('button-sign-in')}
|
104
104
|
>
|
105
105
|
Sign in
|
@@ -60,7 +60,12 @@ function Header() {
|
|
60
60
|
}
|
61
61
|
|
62
62
|
const breadcrumbItems = header.reduce((acc, el) => {
|
63
|
-
|
63
|
+
const action = () => {
|
64
|
+
if (el.link) {
|
65
|
+
history.push(el.link);
|
66
|
+
}
|
67
|
+
};
|
68
|
+
acc.push({text: el.text, action});
|
64
69
|
return acc;
|
65
70
|
}, [] as BreadcrumbsItem[]);
|
66
71
|
|
@@ -35,7 +35,6 @@ class Heatmap extends React.Component {
|
|
35
35
|
hideTooltip: PropTypes.func,
|
36
36
|
getTabletsInfo: PropTypes.func,
|
37
37
|
nodeId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
38
|
-
timeoutForRequest: PropTypes.number,
|
39
38
|
path: PropTypes.string,
|
40
39
|
clearWasLoadingFlag: PropTypes.func,
|
41
40
|
metrics: PropTypes.array,
|
@@ -5,6 +5,8 @@
|
|
5
5
|
@include flex-container();
|
6
6
|
|
7
7
|
&__content {
|
8
|
+
position: relative;
|
9
|
+
|
8
10
|
overflow: auto;
|
9
11
|
@include flex-container();
|
10
12
|
}
|
@@ -17,7 +19,8 @@
|
|
17
19
|
}
|
18
20
|
|
19
21
|
&__tabs {
|
20
|
-
padding: 0
|
22
|
+
padding: 16px 20px 0;
|
23
|
+
@include body2-typography();
|
21
24
|
}
|
22
25
|
|
23
26
|
&__tab {
|
@@ -33,4 +36,12 @@
|
|
33
36
|
text-transform: uppercase;
|
34
37
|
}
|
35
38
|
}
|
39
|
+
|
40
|
+
&__overview-wrapper {
|
41
|
+
padding: 0 20px 20px;
|
42
|
+
}
|
43
|
+
|
44
|
+
&__node-page-wrapper {
|
45
|
+
padding: 20px;
|
46
|
+
}
|
36
47
|
}
|
@@ -0,0 +1,174 @@
|
|
1
|
+
import * as React from 'react';
|
2
|
+
import {useRouteMatch} from 'react-router';
|
3
|
+
import cn from 'bem-cn-lite';
|
4
|
+
import {useDispatch, useSelector} from 'react-redux';
|
5
|
+
import _ from 'lodash';
|
6
|
+
|
7
|
+
import {Tabs} from '@yandex-cloud/uikit';
|
8
|
+
import {Link} from 'react-router-dom';
|
9
|
+
|
10
|
+
import {TABLETS, STORAGE, NODE_PAGES, OVERVIEW, STRUCTURE} from './NodePages';
|
11
|
+
import Tablets from '../Tablets/Tablets';
|
12
|
+
import Storage from '../Storage/Storage';
|
13
|
+
import NodeOverview from './NodeOverview/NodeOverview';
|
14
|
+
import NodeStructure from './NodeStructure/NodeStructure';
|
15
|
+
import Loader from '../../components/Loader/Loader';
|
16
|
+
|
17
|
+
import {getNodeInfo, resetNode} from '../../store/reducers/node';
|
18
|
+
import routes, {CLUSTER_PAGES, createHref} from '../../routes';
|
19
|
+
import {setHeader} from '../../store/reducers/header';
|
20
|
+
import {AutoFetcher} from '../../utils/autofetcher';
|
21
|
+
|
22
|
+
import './Node.scss';
|
23
|
+
|
24
|
+
const b = cn('node');
|
25
|
+
|
26
|
+
export const STORAGE_ROLE = 'Storage';
|
27
|
+
|
28
|
+
const headerNodes = {
|
29
|
+
text: CLUSTER_PAGES.nodes.title,
|
30
|
+
link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.nodes.id}),
|
31
|
+
};
|
32
|
+
|
33
|
+
const autofetcher = new AutoFetcher();
|
34
|
+
|
35
|
+
interface NodeProps {
|
36
|
+
additionalNodesInfo?: any;
|
37
|
+
className?: string;
|
38
|
+
}
|
39
|
+
|
40
|
+
function Node(props: NodeProps) {
|
41
|
+
const dispatch = useDispatch();
|
42
|
+
|
43
|
+
const wasLoaded = useSelector((state: any) => state.node.wasLoaded);
|
44
|
+
const loading = useSelector((state: any) => state.node.loading);
|
45
|
+
const error = useSelector((state: any) => state.node.error);
|
46
|
+
|
47
|
+
const node = useSelector((state: any) => state.node?.data?.SystemStateInfo?.[0]);
|
48
|
+
|
49
|
+
const nodeHost = node?.Host;
|
50
|
+
|
51
|
+
const match =
|
52
|
+
useRouteMatch<{id: string; activeTab: string}>(routes.node) ?? Object.create(null);
|
53
|
+
|
54
|
+
const {id: nodeId, activeTab} = match.params;
|
55
|
+
|
56
|
+
const {activeTabVerified, nodeTabs} = React.useMemo(() => {
|
57
|
+
const hasStorage = _.find(node?.Roles as any[], (el) => el === STORAGE_ROLE);
|
58
|
+
let activeTabVerified = activeTab;
|
59
|
+
if (!hasStorage && activeTab === STORAGE) {
|
60
|
+
activeTabVerified = OVERVIEW;
|
61
|
+
}
|
62
|
+
const nodePages = hasStorage ? NODE_PAGES : NODE_PAGES.filter((el) => el.id !== STORAGE);
|
63
|
+
|
64
|
+
const nodeTabs = nodePages.map((page) => {
|
65
|
+
return {
|
66
|
+
...page,
|
67
|
+
title: page.name,
|
68
|
+
};
|
69
|
+
});
|
70
|
+
|
71
|
+
return {activeTabVerified, nodeTabs};
|
72
|
+
}, [activeTab, node]);
|
73
|
+
|
74
|
+
React.useEffect(() => {
|
75
|
+
const fetchData = () => dispatch(getNodeInfo(nodeId));
|
76
|
+
fetchData();
|
77
|
+
autofetcher.start();
|
78
|
+
autofetcher.fetch(() => fetchData());
|
79
|
+
dispatch(setHeader([headerNodes]));
|
80
|
+
return () => {
|
81
|
+
autofetcher.stop();
|
82
|
+
dispatch(resetNode());
|
83
|
+
};
|
84
|
+
}, [nodeId]);
|
85
|
+
|
86
|
+
React.useEffect(() => {
|
87
|
+
dispatch(
|
88
|
+
setHeader([
|
89
|
+
headerNodes,
|
90
|
+
{
|
91
|
+
text: nodeHost,
|
92
|
+
},
|
93
|
+
]),
|
94
|
+
);
|
95
|
+
}, [nodeHost]);
|
96
|
+
|
97
|
+
const renderTabs = () => {
|
98
|
+
return (
|
99
|
+
<div className={b('tabs')}>
|
100
|
+
<Tabs
|
101
|
+
items={nodeTabs}
|
102
|
+
activeTab={activeTabVerified}
|
103
|
+
wrapTo={({id}, node) => (
|
104
|
+
<Link
|
105
|
+
to={createHref(routes.node, {id: nodeId, activeTab: id})}
|
106
|
+
key={id}
|
107
|
+
className={b('tab')}
|
108
|
+
>
|
109
|
+
{node}
|
110
|
+
</Link>
|
111
|
+
)}
|
112
|
+
allowNotSelected={true}
|
113
|
+
/>
|
114
|
+
</div>
|
115
|
+
);
|
116
|
+
};
|
117
|
+
const renderTabContent = () => {
|
118
|
+
const {additionalNodesInfo} = props;
|
119
|
+
|
120
|
+
switch (activeTab) {
|
121
|
+
case STORAGE: {
|
122
|
+
return (
|
123
|
+
<div className={b('storage')}>
|
124
|
+
<Storage nodeId={nodeId} />
|
125
|
+
</div>
|
126
|
+
);
|
127
|
+
}
|
128
|
+
case TABLETS: {
|
129
|
+
return <Tablets nodeId={nodeId} className={b('node-page-wrapper')} />;
|
130
|
+
}
|
131
|
+
|
132
|
+
case OVERVIEW: {
|
133
|
+
return (
|
134
|
+
<NodeOverview
|
135
|
+
additionalNodesInfo={additionalNodesInfo}
|
136
|
+
node={node}
|
137
|
+
className={b('overview-wrapper')}
|
138
|
+
/>
|
139
|
+
);
|
140
|
+
}
|
141
|
+
|
142
|
+
case STRUCTURE: {
|
143
|
+
return (
|
144
|
+
<NodeStructure
|
145
|
+
className={b('node-page-wrapper')}
|
146
|
+
nodeId={nodeId}
|
147
|
+
additionalNodesInfo={additionalNodesInfo}
|
148
|
+
/>
|
149
|
+
);
|
150
|
+
}
|
151
|
+
default:
|
152
|
+
return false;
|
153
|
+
}
|
154
|
+
};
|
155
|
+
|
156
|
+
if (loading && !wasLoaded) {
|
157
|
+
return <Loader />;
|
158
|
+
} else if (error) {
|
159
|
+
return <div>{error.statusText}</div>;
|
160
|
+
} else {
|
161
|
+
if (node) {
|
162
|
+
return (
|
163
|
+
<div className={b(null, props.className)}>
|
164
|
+
{renderTabs()}
|
165
|
+
|
166
|
+
<div className={b('content')}>{renderTabContent()}</div>
|
167
|
+
</div>
|
168
|
+
);
|
169
|
+
}
|
170
|
+
return <div className="error">no node data</div>;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
export default Node;
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
//@ts-ignore
|
3
|
+
import FullNodeViewer from '../../../components/FullNodeViewer/FullNodeViewer';
|
4
|
+
import {backend} from '../../../store';
|
5
|
+
|
6
|
+
interface NodeOverviewProps {
|
7
|
+
node: any;
|
8
|
+
additionalNodesInfo: any;
|
9
|
+
className?: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
function NodeOverview({node, additionalNodesInfo, className}: NodeOverviewProps) {
|
13
|
+
return (
|
14
|
+
<FullNodeViewer
|
15
|
+
node={node}
|
16
|
+
backend={backend}
|
17
|
+
additionalNodesInfo={additionalNodesInfo}
|
18
|
+
className={className}
|
19
|
+
/>
|
20
|
+
);
|
21
|
+
}
|
22
|
+
|
23
|
+
export default NodeOverview;
|
@@ -1,13 +1,29 @@
|
|
1
|
+
import routes, {createHref} from '../../routes';
|
2
|
+
|
1
3
|
export const STORAGE = 'storage';
|
2
4
|
export const TABLETS = 'tablets';
|
5
|
+
export const OVERVIEW = 'overview';
|
6
|
+
export const STRUCTURE = 'structure';
|
3
7
|
|
4
8
|
export const NODE_PAGES = [
|
9
|
+
{
|
10
|
+
id: OVERVIEW,
|
11
|
+
name: 'Overview',
|
12
|
+
},
|
5
13
|
{
|
6
14
|
id: STORAGE,
|
7
15
|
name: 'Storage',
|
8
16
|
},
|
17
|
+
{id: STRUCTURE, name: 'Structure'},
|
9
18
|
{
|
10
19
|
id: TABLETS,
|
11
20
|
name: 'Tablets',
|
12
21
|
},
|
13
22
|
];
|
23
|
+
|
24
|
+
export function getDefaultNodePath(nodeId) {
|
25
|
+
return createHref(routes.node, {
|
26
|
+
id: nodeId,
|
27
|
+
activeTab: OVERVIEW,
|
28
|
+
});
|
29
|
+
}
|