ydb-embedded-ui 4.5.1 → 4.6.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.
- package/CHANGELOG.md +20 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.js +1 -1
- package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +1 -1
- package/dist/components/PoolUsage/PoolUsage.scss +1 -1
- package/dist/components/PoolUsage/PoolUsage.tsx +50 -0
- package/dist/containers/App/Content.js +3 -2
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +4 -50
- package/dist/containers/Cluster/Cluster.scss +7 -48
- package/dist/containers/Cluster/Cluster.tsx +136 -20
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +34 -17
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +57 -91
- package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.scss +48 -0
- package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx +34 -0
- package/dist/containers/Cluster/utils.ts +34 -0
- package/dist/containers/Header/Header.scss +0 -24
- package/dist/containers/Header/Header.tsx +14 -44
- package/dist/containers/Node/Node.tsx +23 -21
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +19 -17
- package/dist/containers/Nodes/Nodes.tsx +0 -16
- package/dist/containers/Nodes/getNodesColumns.tsx +1 -1
- package/dist/containers/Storage/Storage.js +1 -11
- package/dist/containers/Tablet/Tablet.tsx +28 -0
- package/dist/containers/TabletsFilters/TabletsFilters.js +16 -1
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +1 -1
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +3 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/Network/Network.js +2 -2
- package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +4 -6
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +56 -53
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +1 -1
- package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
- package/dist/containers/Tenant/Preview/Preview.js +1 -1
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +26 -22
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +10 -3
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +8 -1
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +1 -6
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -1
- package/dist/containers/Tenant/Tenant.tsx +8 -13
- package/dist/containers/Tenant/utils/schemaActions.ts +1 -1
- package/dist/containers/Tenants/Tenants.js +18 -28
- package/dist/containers/Tenants/Tenants.scss +2 -4
- package/dist/containers/UserSettings/i18n/en.json +2 -2
- package/dist/containers/UserSettings/i18n/ru.json +2 -2
- package/dist/containers/UserSettings/settings.ts +4 -4
- package/dist/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss +1 -0
- package/dist/containers/Versions/NodesTable/NodesTable.tsx +2 -3
- package/dist/containers/Versions/Versions.scss +0 -4
- package/dist/containers/Versions/Versions.tsx +74 -66
- package/dist/routes.ts +0 -7
- package/dist/services/api.ts +8 -4
- package/dist/store/reducers/clusterNodes/clusterNodes.tsx +4 -0
- package/dist/store/reducers/index.ts +6 -4
- package/dist/store/reducers/{network.js → network/network.ts} +11 -8
- package/dist/store/reducers/network/types.ts +16 -0
- package/dist/store/reducers/node/node.ts +102 -0
- package/dist/store/reducers/node/selectors.ts +59 -0
- package/dist/store/reducers/node/types.ts +44 -0
- package/dist/store/reducers/overview/overview.ts +109 -0
- package/dist/store/reducers/overview/types.ts +24 -0
- package/dist/store/reducers/{schema.ts → schema/schema.ts} +24 -50
- package/dist/{types/store/schema.ts → store/reducers/schema/types.ts} +16 -15
- package/dist/store/reducers/{schemaAcl.js → schemaAcl/schemaAcl.ts} +20 -9
- package/dist/store/reducers/schemaAcl/types.ts +15 -0
- package/dist/store/reducers/settings/settings.ts +5 -3
- package/dist/types/api/acl.ts +1 -1
- package/dist/types/api/query.ts +78 -44
- package/dist/types/store/explainQuery.ts +2 -2
- package/dist/types/store/query.ts +4 -2
- package/dist/utils/constants.ts +3 -1
- package/dist/utils/nodes.ts +1 -1
- package/dist/utils/query.ts +3 -3
- package/package.json +1 -1
- package/dist/components/PoolUsage/PoolUsage.js +0 -54
- package/dist/store/reducers/node.js +0 -141
@@ -1,39 +1,27 @@
|
|
1
|
-
import {useCallback, useEffect, useMemo} from 'react';
|
2
|
-
import {useDispatch} from 'react-redux';
|
3
|
-
import {useLocation} from 'react-router';
|
4
1
|
import block from 'bem-cn-lite';
|
5
|
-
|
2
|
+
|
3
|
+
import {Skeleton} from '@gravity-ui/uikit';
|
6
4
|
|
7
5
|
import EntityStatus from '../../../components/EntityStatus/EntityStatus';
|
8
6
|
import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
|
9
7
|
import InfoViewer, {InfoViewerItem} from '../../../components/InfoViewer/InfoViewer';
|
10
8
|
import {Tags} from '../../../components/Tags';
|
11
9
|
import {Tablet} from '../../../components/Tablet';
|
12
|
-
import {Loader} from '../../../components/Loader';
|
13
10
|
import {ResponseError} from '../../../components/Errors/ResponseError';
|
14
11
|
import {ExternalLinkWithIcon} from '../../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
|
12
|
+
import {IconWrapper as Icon} from '../../../components/Icon/Icon';
|
15
13
|
|
16
|
-
import type {
|
17
|
-
|
18
|
-
AdditionalVersionsProps,
|
19
|
-
ClusterLink,
|
20
|
-
} from '../../../types/additionalProps';
|
14
|
+
import type {IResponseError} from '../../../types/api/error';
|
15
|
+
import type {AdditionalClusterProps, ClusterLink} from '../../../types/additionalProps';
|
21
16
|
import type {VersionValue} from '../../../types/versions';
|
22
17
|
import type {TClusterInfo} from '../../../types/api/cluster';
|
23
|
-
import {getClusterNodes} from '../../../store/reducers/clusterNodes/clusterNodes';
|
24
|
-
import {getClusterInfo} from '../../../store/reducers/cluster/cluster';
|
25
18
|
import {backend, customBackend} from '../../../store';
|
26
|
-
import {setHeader} from '../../../store/reducers/header';
|
27
19
|
import {formatStorageValues} from '../../../utils';
|
28
|
-
import {
|
29
|
-
import {
|
30
|
-
|
31
|
-
parseNodesToVersionsValues,
|
32
|
-
} from '../../../utils/versions';
|
33
|
-
import routes, {CLUSTER_PAGES, createHref} from '../../../routes';
|
34
|
-
|
35
|
-
import {Versions} from '../../Versions/Versions';
|
20
|
+
import {useSetting, useTypedSelector} from '../../../utils/hooks';
|
21
|
+
import {CLUSTER_INFO_HIDDEN_KEY} from '../../../utils/constants';
|
22
|
+
|
36
23
|
import {VersionsBar} from '../VersionsBar/VersionsBar';
|
24
|
+
import {ClusterInfoSkeleton} from '../ClusterInfoSkeleton/ClusterInfoSkeleton';
|
37
25
|
|
38
26
|
import {compareTablets} from './utils';
|
39
27
|
|
@@ -122,74 +110,27 @@ const getInfo = (
|
|
122
110
|
};
|
123
111
|
|
124
112
|
interface ClusterInfoProps {
|
125
|
-
|
113
|
+
cluster?: TClusterInfo;
|
114
|
+
versionsValues?: VersionValue[];
|
115
|
+
loading?: boolean;
|
116
|
+
error?: IResponseError;
|
126
117
|
additionalClusterProps?: AdditionalClusterProps;
|
127
|
-
additionalVersionsProps?: AdditionalVersionsProps;
|
128
118
|
}
|
129
119
|
|
130
120
|
export const ClusterInfo = ({
|
131
|
-
|
121
|
+
cluster = {},
|
122
|
+
versionsValues = [],
|
123
|
+
loading,
|
124
|
+
error,
|
132
125
|
additionalClusterProps = {},
|
133
|
-
additionalVersionsProps = {},
|
134
126
|
}: ClusterInfoProps) => {
|
135
|
-
const dispatch = useDispatch();
|
136
|
-
const location = useLocation();
|
137
|
-
|
138
|
-
const queryParams = qs.parse(location.search, {
|
139
|
-
ignoreQueryPrefix: true,
|
140
|
-
});
|
141
|
-
const {clusterName} = queryParams;
|
142
|
-
|
143
|
-
const {
|
144
|
-
data: cluster = {},
|
145
|
-
loading,
|
146
|
-
wasLoaded,
|
147
|
-
error,
|
148
|
-
} = useTypedSelector((state) => state.cluster);
|
149
|
-
const {
|
150
|
-
nodes,
|
151
|
-
loading: nodesLoading,
|
152
|
-
wasLoaded: nodesWasLoaded,
|
153
|
-
error: nodesError,
|
154
|
-
} = useTypedSelector((state) => state.clusterNodes);
|
155
127
|
const singleClusterMode = useTypedSelector((state) => state.singleClusterMode);
|
156
128
|
|
157
|
-
|
158
|
-
dispatch(
|
159
|
-
setHeader([
|
160
|
-
{
|
161
|
-
text: CLUSTER_PAGES.cluster.title,
|
162
|
-
link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.cluster.id}),
|
163
|
-
},
|
164
|
-
]),
|
165
|
-
);
|
166
|
-
}, [dispatch]);
|
167
|
-
|
168
|
-
const fetchData = useCallback(() => {
|
169
|
-
dispatch(getClusterInfo(clusterName ? String(clusterName) : undefined));
|
170
|
-
dispatch(getClusterNodes());
|
171
|
-
}, [dispatch, clusterName]);
|
172
|
-
|
173
|
-
useAutofetcher(fetchData, [fetchData], true);
|
174
|
-
|
175
|
-
const versionToColor = useMemo(() => {
|
176
|
-
if (additionalVersionsProps?.getVersionToColorMap) {
|
177
|
-
return additionalVersionsProps.getVersionToColorMap();
|
178
|
-
}
|
179
|
-
return parseVersionsToVersionToColorMap(cluster.Versions);
|
180
|
-
}, [additionalVersionsProps, cluster]);
|
181
|
-
|
182
|
-
const versionsValues = useMemo(() => {
|
183
|
-
return parseNodesToVersionsValues(nodes, versionToColor);
|
184
|
-
}, [nodes, versionToColor]);
|
185
|
-
|
186
|
-
if ((loading && !wasLoaded) || (nodesLoading && !nodesWasLoaded)) {
|
187
|
-
return <Loader size="l" />;
|
188
|
-
}
|
129
|
+
const [clusterInfoHidden, setClusterInfoHidden] = useSetting(CLUSTER_INFO_HIDDEN_KEY, false);
|
189
130
|
|
190
|
-
|
191
|
-
|
192
|
-
}
|
131
|
+
const togleClusterInfoVisibility = () => {
|
132
|
+
setClusterInfoHidden(!clusterInfoHidden);
|
133
|
+
};
|
193
134
|
|
194
135
|
let internalLink = backend + '/internal';
|
195
136
|
|
@@ -204,20 +145,45 @@ export const ClusterInfo = ({
|
|
204
145
|
...links,
|
205
146
|
]);
|
206
147
|
|
148
|
+
const getContent = () => {
|
149
|
+
if (loading) {
|
150
|
+
return <ClusterInfoSkeleton />;
|
151
|
+
}
|
152
|
+
|
153
|
+
if (error) {
|
154
|
+
<ResponseError error={error} className={b('error')} />;
|
155
|
+
}
|
156
|
+
|
157
|
+
return <InfoViewer dots={true} info={clusterInfo} />;
|
158
|
+
};
|
159
|
+
|
160
|
+
const getClusterTitle = () => {
|
161
|
+
if (loading) {
|
162
|
+
return <Skeleton className={b('title-skeleton')} />;
|
163
|
+
}
|
164
|
+
|
165
|
+
return (
|
166
|
+
<EntityStatus
|
167
|
+
size="m"
|
168
|
+
status={cluster?.Overall}
|
169
|
+
name={cluster?.Name ?? 'Unknown cluster'}
|
170
|
+
className={b('title')}
|
171
|
+
/>
|
172
|
+
);
|
173
|
+
};
|
174
|
+
|
207
175
|
return (
|
208
176
|
<div className={b()}>
|
209
|
-
<div className={b('header')}>
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
<InfoViewer dots={true} info={clusterInfo} />
|
177
|
+
<div className={b('header')} onClick={togleClusterInfoVisibility}>
|
178
|
+
{getClusterTitle()}
|
179
|
+
<Icon
|
180
|
+
name="chevron-down"
|
181
|
+
width={24}
|
182
|
+
height={24}
|
183
|
+
className={b('header__expand-button', {rotate: clusterInfoHidden})}
|
184
|
+
/>
|
218
185
|
</div>
|
219
|
-
|
220
|
-
<Versions nodes={nodes} versionToColor={versionToColor} />
|
186
|
+
<div className={b('info', {hidden: clusterInfoHidden})}>{getContent()}</div>
|
221
187
|
</div>
|
222
188
|
);
|
223
189
|
};
|
@@ -0,0 +1,48 @@
|
|
1
|
+
.ydb-cluster-info-skeleton {
|
2
|
+
display: flex;
|
3
|
+
flex-direction: column;
|
4
|
+
gap: 16px;
|
5
|
+
|
6
|
+
margin-top: 5px;
|
7
|
+
|
8
|
+
&__row {
|
9
|
+
display: flex;
|
10
|
+
align-items: flex-start;
|
11
|
+
|
12
|
+
min-height: var(--yc-text-body-2-font-size);
|
13
|
+
|
14
|
+
.yc-skeleton {
|
15
|
+
min-height: var(--yc-text-body-2-font-size);
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
&__label {
|
20
|
+
display: flex;
|
21
|
+
flex: 0 1 auto;
|
22
|
+
align-items: baseline;
|
23
|
+
|
24
|
+
width: 200px;
|
25
|
+
|
26
|
+
&__text {
|
27
|
+
width: 100px;
|
28
|
+
}
|
29
|
+
|
30
|
+
&__dots {
|
31
|
+
width: 100px;
|
32
|
+
margin: 0 2px;
|
33
|
+
|
34
|
+
border-bottom: 1px dotted var(--yc-color-text-secondary);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
&__value {
|
39
|
+
min-width: 200px;
|
40
|
+
max-width: 20%;
|
41
|
+
}
|
42
|
+
|
43
|
+
&__versions {
|
44
|
+
min-width: 400px;
|
45
|
+
max-width: 40%;
|
46
|
+
height: 36px;
|
47
|
+
}
|
48
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {Skeleton} from '@gravity-ui/uikit';
|
4
|
+
|
5
|
+
import './ClusterInfoSkeleton.scss';
|
6
|
+
|
7
|
+
const b = block('ydb-cluster-info-skeleton');
|
8
|
+
|
9
|
+
const SkeletonLabel = () => (
|
10
|
+
<div className={b('label')}>
|
11
|
+
<Skeleton className={b('label__text')} />
|
12
|
+
<div className={b('label__dots')} />
|
13
|
+
</div>
|
14
|
+
);
|
15
|
+
|
16
|
+
interface ClusterInfoSkeletonProps {
|
17
|
+
className?: string;
|
18
|
+
rows?: number;
|
19
|
+
}
|
20
|
+
|
21
|
+
export const ClusterInfoSkeleton = ({rows = 6, className}: ClusterInfoSkeletonProps) => (
|
22
|
+
<div className={b(null, className)}>
|
23
|
+
{[...new Array(rows)].map((_, index) => (
|
24
|
+
<div className={b('row')} key={`skeleton-row-${index}`}>
|
25
|
+
<SkeletonLabel />
|
26
|
+
<Skeleton className={b('value')} />
|
27
|
+
</div>
|
28
|
+
))}
|
29
|
+
<div className={b('row')} key="versions">
|
30
|
+
<SkeletonLabel />
|
31
|
+
<Skeleton className={b('versions')} />
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
);
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import type {ValueOf} from '../../types/common';
|
2
|
+
import routes, {createHref} from '../../routes';
|
3
|
+
|
4
|
+
export const clusterTabsIds = {
|
5
|
+
tenants: 'tenants',
|
6
|
+
nodes: 'nodes',
|
7
|
+
storage: 'storage',
|
8
|
+
versions: 'versions',
|
9
|
+
} as const;
|
10
|
+
|
11
|
+
export type ClusterTab = ValueOf<typeof clusterTabsIds>;
|
12
|
+
|
13
|
+
const tenants = {
|
14
|
+
id: clusterTabsIds.tenants,
|
15
|
+
title: 'Databases',
|
16
|
+
};
|
17
|
+
const nodes = {
|
18
|
+
id: clusterTabsIds.nodes,
|
19
|
+
title: 'Nodes',
|
20
|
+
};
|
21
|
+
const storage = {
|
22
|
+
id: clusterTabsIds.storage,
|
23
|
+
title: 'Storage',
|
24
|
+
};
|
25
|
+
const versions = {
|
26
|
+
id: clusterTabsIds.versions,
|
27
|
+
title: 'Versions',
|
28
|
+
};
|
29
|
+
|
30
|
+
export const clusterTabs = [tenants, nodes, storage, versions];
|
31
|
+
|
32
|
+
export const getClusterPath = (activeTab: ClusterTab = clusterTabsIds.tenants, query = {}) => {
|
33
|
+
return createHref(routes.cluster, {activeTab}, query);
|
34
|
+
};
|
@@ -13,28 +13,4 @@
|
|
13
13
|
border-bottom: 1px solid var(--yc-color-line-generic);
|
14
14
|
|
15
15
|
@include body2-typography;
|
16
|
-
|
17
|
-
&__cluster-info-title {
|
18
|
-
font-size: var(--yc-text-body-1-font-size);
|
19
|
-
text-transform: uppercase;
|
20
|
-
|
21
|
-
color: var(--yc-color-text-secondary);
|
22
|
-
}
|
23
|
-
|
24
|
-
&__cluster-info-name {
|
25
|
-
font-size: var(--yc-text-body-2-font-size);
|
26
|
-
font-weight: 500;
|
27
|
-
}
|
28
|
-
|
29
|
-
&__cluster-name-wrapper {
|
30
|
-
display: flex;
|
31
|
-
align-items: center;
|
32
|
-
|
33
|
-
height: 100%;
|
34
|
-
gap: 5px;
|
35
|
-
}
|
36
|
-
|
37
|
-
&__divider {
|
38
|
-
height: 80%;
|
39
|
-
}
|
40
16
|
}
|
@@ -1,56 +1,33 @@
|
|
1
|
-
import
|
2
|
-
import {useDispatch} from 'react-redux';
|
3
|
-
import cn from 'bem-cn-lite';
|
1
|
+
import block from 'bem-cn-lite';
|
4
2
|
import {useHistory} from 'react-router';
|
3
|
+
|
5
4
|
import {Breadcrumbs, BreadcrumbsItem} from '@gravity-ui/uikit';
|
6
5
|
|
7
|
-
import Divider from '../../components/Divider/Divider';
|
8
6
|
import {ExternalLinkWithIcon} from '../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
|
9
7
|
|
10
8
|
import {backend, customBackend} from '../../store';
|
11
|
-
import {getHostInfo} from '../../store/reducers/host';
|
12
9
|
import {HeaderItemType} from '../../store/reducers/header';
|
13
10
|
import {useTypedSelector} from '../../utils/hooks';
|
14
11
|
|
15
12
|
import './Header.scss';
|
16
13
|
|
17
|
-
const b =
|
14
|
+
const b = block('header');
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
<div className={b('cluster-info-name')}>{name}</div>
|
24
|
-
</div>
|
25
|
-
);
|
26
|
-
}
|
16
|
+
const getInternalLink = (singleClusterMode: boolean) => {
|
17
|
+
if (singleClusterMode && !customBackend) {
|
18
|
+
return `/internal`;
|
19
|
+
}
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
}
|
31
|
-
|
32
|
-
function Header({clusterName}: HeaderProps) {
|
33
|
-
const dispatch = useDispatch();
|
21
|
+
return backend + '/internal';
|
22
|
+
};
|
34
23
|
|
24
|
+
function Header() {
|
35
25
|
const {singleClusterMode, header}: {singleClusterMode: boolean; header: HeaderItemType[]} =
|
36
26
|
useTypedSelector((state) => state);
|
37
|
-
const {data: host} = useTypedSelector((state) => state.host);
|
38
27
|
|
39
28
|
const history = useHistory();
|
40
29
|
|
41
|
-
useEffect(() => {
|
42
|
-
dispatch(getHostInfo());
|
43
|
-
}, [dispatch]);
|
44
|
-
|
45
30
|
const renderHeader = () => {
|
46
|
-
const clusterNameFinal = singleClusterMode ? host?.ClusterName : clusterName;
|
47
|
-
|
48
|
-
let link = backend + '/internal';
|
49
|
-
|
50
|
-
if (singleClusterMode && !customBackend) {
|
51
|
-
link = `/internal`;
|
52
|
-
}
|
53
|
-
|
54
31
|
const breadcrumbItems = header.reduce((acc, el) => {
|
55
32
|
const action = () => {
|
56
33
|
if (el.link) {
|
@@ -71,17 +48,10 @@ function Header({clusterName}: HeaderProps) {
|
|
71
48
|
/>
|
72
49
|
</div>
|
73
50
|
|
74
|
-
<
|
75
|
-
|
76
|
-
{
|
77
|
-
|
78
|
-
<div className={b('divider')}>
|
79
|
-
<Divider />
|
80
|
-
</div>
|
81
|
-
<ClusterName name={clusterNameFinal} />
|
82
|
-
</React.Fragment>
|
83
|
-
)}
|
84
|
-
</div>
|
51
|
+
<ExternalLinkWithIcon
|
52
|
+
title={'Internal Viewer'}
|
53
|
+
url={getInternalLink(singleClusterMode)}
|
54
|
+
/>
|
85
55
|
</header>
|
86
56
|
);
|
87
57
|
};
|
@@ -15,10 +15,11 @@ import NodeStructure from './NodeStructure/NodeStructure';
|
|
15
15
|
import {Loader} from '../../components/Loader';
|
16
16
|
import {BasicNodeViewer} from '../../components/BasicNodeViewer';
|
17
17
|
|
18
|
-
import {getNodeInfo, resetNode} from '../../store/reducers/node';
|
19
|
-
import routes, {
|
20
|
-
import {setHeader} from '../../store/reducers/header';
|
18
|
+
import {getNodeInfo, resetNode} from '../../store/reducers/node/node';
|
19
|
+
import routes, {createHref} from '../../routes';
|
20
|
+
import {HeaderItemType, setHeader} from '../../store/reducers/header';
|
21
21
|
import {AutoFetcher} from '../../utils/autofetcher';
|
22
|
+
import {clusterTabsIds, getClusterPath} from '../Cluster/utils';
|
22
23
|
|
23
24
|
import './Node.scss';
|
24
25
|
|
@@ -26,11 +27,6 @@ const b = cn('node');
|
|
26
27
|
|
27
28
|
export const STORAGE_ROLE = 'Storage';
|
28
29
|
|
29
|
-
const headerNodes = {
|
30
|
-
text: CLUSTER_PAGES.nodes.title,
|
31
|
-
link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.nodes.id}),
|
32
|
-
};
|
33
|
-
|
34
30
|
const autofetcher = new AutoFetcher();
|
35
31
|
|
36
32
|
interface NodeProps {
|
@@ -72,28 +68,34 @@ function Node(props: NodeProps) {
|
|
72
68
|
return {activeTabVerified, nodeTabs};
|
73
69
|
}, [activeTab, node]);
|
74
70
|
|
71
|
+
React.useEffect(() => {
|
72
|
+
const headerItems: HeaderItemType[] = [
|
73
|
+
{
|
74
|
+
text: 'Cluster',
|
75
|
+
link: getClusterPath(clusterTabsIds.nodes),
|
76
|
+
},
|
77
|
+
];
|
78
|
+
|
79
|
+
if (nodeHost) {
|
80
|
+
headerItems.push({
|
81
|
+
text: nodeHost,
|
82
|
+
});
|
83
|
+
}
|
84
|
+
|
85
|
+
dispatch(setHeader(headerItems));
|
86
|
+
}, [dispatch, nodeHost]);
|
87
|
+
|
75
88
|
React.useEffect(() => {
|
76
89
|
const fetchData = () => dispatch(getNodeInfo(nodeId));
|
77
90
|
fetchData();
|
78
91
|
autofetcher.start();
|
79
92
|
autofetcher.fetch(() => fetchData());
|
80
|
-
|
93
|
+
|
81
94
|
return () => {
|
82
95
|
autofetcher.stop();
|
83
96
|
dispatch(resetNode());
|
84
97
|
};
|
85
|
-
}, [nodeId]);
|
86
|
-
|
87
|
-
React.useEffect(() => {
|
88
|
-
dispatch(
|
89
|
-
setHeader([
|
90
|
-
headerNodes,
|
91
|
-
{
|
92
|
-
text: nodeHost,
|
93
|
-
},
|
94
|
-
]),
|
95
|
-
);
|
96
|
-
}, [nodeHost]);
|
98
|
+
}, [dispatch, nodeId]);
|
97
99
|
|
98
100
|
const renderTabs = () => {
|
99
101
|
return (
|
@@ -1,16 +1,19 @@
|
|
1
1
|
import {useEffect, useRef, useMemo} from 'react';
|
2
|
-
import {useDispatch
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
3
|
import url from 'url';
|
4
4
|
import _ from 'lodash';
|
5
5
|
|
6
6
|
import cn from 'bem-cn-lite';
|
7
7
|
|
8
|
-
import {PDisk} from './Pdisk';
|
9
8
|
import {Loader} from '../.././../components/Loader';
|
10
9
|
|
11
|
-
import {getNodeStructure
|
10
|
+
import {getNodeStructure} from '../../../store/reducers/node/node';
|
11
|
+
import {selectNodeStructure} from '../../../store/reducers/node/selectors';
|
12
12
|
|
13
13
|
import {AutoFetcher} from '../../../utils/autofetcher';
|
14
|
+
import {useTypedSelector} from '../../../utils/hooks';
|
15
|
+
|
16
|
+
import {PDisk} from './Pdisk';
|
14
17
|
|
15
18
|
import './NodeStructure.scss';
|
16
19
|
|
@@ -32,20 +35,19 @@ interface NodeStructureProps {
|
|
32
35
|
|
33
36
|
const autofetcher = new AutoFetcher();
|
34
37
|
|
35
|
-
function NodeStructure(
|
38
|
+
function NodeStructure({nodeId, className, additionalNodesInfo}: NodeStructureProps) {
|
36
39
|
const dispatch = useDispatch();
|
37
40
|
|
38
|
-
const nodeStructure
|
41
|
+
const nodeStructure = useTypedSelector(selectNodeStructure);
|
39
42
|
|
40
|
-
const loadingStructure =
|
41
|
-
const
|
42
|
-
const nodeData = useSelector((state: any) => state.node?.data?.SystemStateInfo?.[0]);
|
43
|
+
const {loadingStructure, wasLoadedStructure} = useTypedSelector((state) => state.node);
|
44
|
+
const nodeData = useTypedSelector((state) => state.node?.data?.SystemStateInfo?.[0]);
|
43
45
|
|
44
46
|
const nodeHref = useMemo(() => {
|
45
|
-
return
|
46
|
-
?
|
47
|
+
return additionalNodesInfo?.getNodeRef
|
48
|
+
? additionalNodesInfo.getNodeRef(nodeData)
|
47
49
|
: undefined;
|
48
|
-
}, [nodeData,
|
50
|
+
}, [nodeData, additionalNodesInfo]);
|
49
51
|
|
50
52
|
const {pdiskId: pdiskIdFromUrl, vdiskId: vdiskIdFromUrl} = url.parse(
|
51
53
|
window.location.href,
|
@@ -71,16 +73,16 @@ function NodeStructure(props: NodeStructureProps) {
|
|
71
73
|
}, []);
|
72
74
|
|
73
75
|
useEffect(() => {
|
74
|
-
dispatch(getNodeStructure(
|
76
|
+
dispatch(getNodeStructure(nodeId));
|
75
77
|
autofetcher.start();
|
76
|
-
autofetcher.fetch(() => dispatch(getNodeStructure(
|
78
|
+
autofetcher.fetch(() => dispatch(getNodeStructure(nodeId)));
|
77
79
|
|
78
80
|
return () => {
|
79
81
|
scrolled.current = false;
|
80
82
|
isReady.current = false;
|
81
83
|
autofetcher.stop();
|
82
84
|
};
|
83
|
-
}, [
|
85
|
+
}, [nodeId, dispatch]);
|
84
86
|
|
85
87
|
useEffect(() => {
|
86
88
|
if (!_.isEmpty(nodeStructure) && scrollContainer) {
|
@@ -98,9 +100,9 @@ function NodeStructure(props: NodeStructureProps) {
|
|
98
100
|
|
99
101
|
if (vdiskIdFromUrl) {
|
100
102
|
const vDisks = nodeStructure[pdiskIdFromUrl as string]?.vDisks;
|
101
|
-
const vDisk = vDisks?.find((el
|
103
|
+
const vDisk = vDisks?.find((el) => el.id === vdiskIdFromUrl);
|
102
104
|
const dataTable = vDisk ? document.querySelector('.data-table') : undefined;
|
103
|
-
const order = vDisk?.order;
|
105
|
+
const order = vDisk?.order || 0;
|
104
106
|
|
105
107
|
if (dataTable) {
|
106
108
|
scrollToVdisk += (dataTable as HTMLElement).offsetTop + 40 * order;
|
@@ -147,7 +149,7 @@ function NodeStructure(props: NodeStructureProps) {
|
|
147
149
|
|
148
150
|
return (
|
149
151
|
<div className={b()} ref={scrollContainerRef}>
|
150
|
-
<div className={
|
152
|
+
<div className={className}>{renderContent()}</div>
|
151
153
|
</div>
|
152
154
|
);
|
153
155
|
}
|
@@ -15,13 +15,10 @@ import {ProblemFilter} from '../../components/ProblemFilter';
|
|
15
15
|
import {UptimeFilter} from '../../components/UptimeFIlter';
|
16
16
|
import {EntitiesCount} from '../../components/EntitiesCount';
|
17
17
|
|
18
|
-
import routes, {CLUSTER_PAGES, createHref} from '../../routes';
|
19
|
-
|
20
18
|
import {DEFAULT_TABLE_SETTINGS, USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY} from '../../utils/constants';
|
21
19
|
import {useAutofetcher, useSetting, useTypedSelector} from '../../utils/hooks';
|
22
20
|
import {AdditionalNodesInfo, isUnavailableNode, NodesUptimeFilterValues} from '../../utils/nodes';
|
23
21
|
|
24
|
-
import {setHeader} from '../../store/reducers/header';
|
25
22
|
import {
|
26
23
|
getNodes,
|
27
24
|
getFilteredPreparedNodesList,
|
@@ -82,19 +79,6 @@ export const Nodes = ({path, type, className, additionalNodesInfo = {}}: NodesPr
|
|
82
79
|
|
83
80
|
useAutofetcher(fetchNodes, [fetchNodes], isClusterNodes ? true : autorefresh);
|
84
81
|
|
85
|
-
useEffect(() => {
|
86
|
-
if (isClusterNodes) {
|
87
|
-
dispatch(
|
88
|
-
setHeader([
|
89
|
-
{
|
90
|
-
text: CLUSTER_PAGES.nodes.title,
|
91
|
-
link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.nodes.id}),
|
92
|
-
},
|
93
|
-
]),
|
94
|
-
);
|
95
|
-
}
|
96
|
-
}, [dispatch, isClusterNodes]);
|
97
|
-
|
98
82
|
const handleSearchQueryChange = (value: string) => {
|
99
83
|
dispatch(setSearchValue(value));
|
100
84
|
};
|
@@ -13,7 +13,7 @@ import type {INodesPreparedEntity} from '../../types/store/nodes';
|
|
13
13
|
|
14
14
|
interface GetNodesColumnsProps {
|
15
15
|
tabletsPath?: string;
|
16
|
-
getNodeRef?: (node?: NodeAddress) => string;
|
16
|
+
getNodeRef?: (node?: NodeAddress) => string | null;
|
17
17
|
}
|
18
18
|
|
19
19
|
export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps) {
|