ydb-embedded-ui 4.5.1 → 4.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/FullNodeViewer/FullNodeViewer.js +1 -1
  3. package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +1 -1
  4. package/dist/components/PoolUsage/PoolUsage.scss +1 -1
  5. package/dist/components/PoolUsage/PoolUsage.tsx +50 -0
  6. package/dist/containers/App/Content.js +3 -2
  7. package/dist/containers/AsideNavigation/AsideNavigation.tsx +4 -50
  8. package/dist/containers/Cluster/Cluster.scss +7 -48
  9. package/dist/containers/Cluster/Cluster.tsx +136 -20
  10. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +34 -17
  11. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +57 -91
  12. package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.scss +48 -0
  13. package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx +34 -0
  14. package/dist/containers/Cluster/utils.ts +34 -0
  15. package/dist/containers/Header/Header.scss +0 -24
  16. package/dist/containers/Header/Header.tsx +14 -44
  17. package/dist/containers/Node/Node.tsx +23 -21
  18. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +19 -17
  19. package/dist/containers/Nodes/Nodes.tsx +0 -16
  20. package/dist/containers/Nodes/getNodesColumns.tsx +1 -1
  21. package/dist/containers/Storage/Storage.js +1 -11
  22. package/dist/containers/Tablet/Tablet.tsx +28 -0
  23. package/dist/containers/TabletsFilters/TabletsFilters.js +16 -1
  24. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +1 -1
  25. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +3 -0
  26. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  27. package/dist/containers/Tenant/Diagnostics/Network/Network.js +2 -2
  28. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +4 -6
  29. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +56 -53
  30. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +1 -1
  31. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
  32. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  33. package/dist/containers/Tenant/Preview/Preview.js +1 -1
  34. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +26 -22
  35. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +10 -3
  36. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +8 -1
  37. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +1 -6
  38. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -1
  39. package/dist/containers/Tenant/Tenant.tsx +8 -13
  40. package/dist/containers/Tenant/utils/schemaActions.ts +1 -1
  41. package/dist/containers/Tenants/Tenants.js +18 -28
  42. package/dist/containers/Tenants/Tenants.scss +2 -4
  43. package/dist/containers/UserSettings/i18n/en.json +2 -2
  44. package/dist/containers/UserSettings/i18n/ru.json +2 -2
  45. package/dist/containers/UserSettings/settings.ts +4 -4
  46. package/dist/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss +1 -0
  47. package/dist/containers/Versions/NodesTable/NodesTable.tsx +2 -3
  48. package/dist/containers/Versions/Versions.scss +0 -4
  49. package/dist/containers/Versions/Versions.tsx +74 -66
  50. package/dist/routes.ts +0 -7
  51. package/dist/services/api.ts +8 -4
  52. package/dist/store/reducers/clusterNodes/clusterNodes.tsx +4 -0
  53. package/dist/store/reducers/index.ts +6 -4
  54. package/dist/store/reducers/{network.js → network/network.ts} +11 -8
  55. package/dist/store/reducers/network/types.ts +16 -0
  56. package/dist/store/reducers/node/node.ts +102 -0
  57. package/dist/store/reducers/node/selectors.ts +59 -0
  58. package/dist/store/reducers/node/types.ts +44 -0
  59. package/dist/store/reducers/overview/overview.ts +109 -0
  60. package/dist/store/reducers/overview/types.ts +24 -0
  61. package/dist/store/reducers/{schema.ts → schema/schema.ts} +24 -50
  62. package/dist/{types/store/schema.ts → store/reducers/schema/types.ts} +16 -15
  63. package/dist/store/reducers/{schemaAcl.js → schemaAcl/schemaAcl.ts} +20 -9
  64. package/dist/store/reducers/schemaAcl/types.ts +15 -0
  65. package/dist/store/reducers/settings/settings.ts +5 -3
  66. package/dist/types/api/acl.ts +1 -1
  67. package/dist/types/api/query.ts +78 -44
  68. package/dist/types/store/explainQuery.ts +2 -2
  69. package/dist/types/store/query.ts +4 -2
  70. package/dist/utils/constants.ts +3 -1
  71. package/dist/utils/nodes.ts +1 -1
  72. package/dist/utils/query.ts +3 -3
  73. package/package.json +1 -1
  74. package/dist/components/PoolUsage/PoolUsage.js +0 -54
  75. 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
- import qs from 'qs';
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
- AdditionalClusterProps,
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 {useAutofetcher, useTypedSelector} from '../../../utils/hooks';
29
- import {
30
- parseVersionsToVersionToColorMap,
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
- clusterTitle?: string;
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
- clusterTitle,
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
- useEffect(() => {
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
- if (error || nodesError) {
191
- return <ResponseError error={error || nodesError} />;
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
- <div className={b('title')}>
211
- <EntityStatus
212
- size="m"
213
- status={cluster.Overall}
214
- name={clusterTitle ?? cluster.Name ?? 'Unknown cluster'}
215
- />
216
- </div>
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 React, {useEffect} from 'react';
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 = cn('header');
14
+ const b = block('header');
18
15
 
19
- function ClusterName({name}: {name: string}) {
20
- return (
21
- <div className={b('cluster-info')}>
22
- <div className={b('cluster-info-title')}>cluster</div>
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
- interface HeaderProps {
29
- clusterName: string;
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
- <div className={b('cluster-name-wrapper')}>
75
- <ExternalLinkWithIcon title={'Internal Viewer'} url={link} />
76
- {clusterNameFinal && (
77
- <React.Fragment>
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, {CLUSTER_PAGES, createHref} from '../../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
- dispatch(setHeader([headerNodes]));
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, useSelector} from 'react-redux';
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, selectNodeStructure} from '../../../store/reducers/node';
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(props: NodeStructureProps) {
38
+ function NodeStructure({nodeId, className, additionalNodesInfo}: NodeStructureProps) {
36
39
  const dispatch = useDispatch();
37
40
 
38
- const nodeStructure: any = useSelector(selectNodeStructure);
41
+ const nodeStructure = useTypedSelector(selectNodeStructure);
39
42
 
40
- const loadingStructure = useSelector((state: any) => state.node.loadingStructure);
41
- const wasLoadedStructure = useSelector((state: any) => state.node.wasLoadedStructure);
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 props.additionalNodesInfo?.getNodeRef
46
- ? props.additionalNodesInfo.getNodeRef(nodeData)
47
+ return additionalNodesInfo?.getNodeRef
48
+ ? additionalNodesInfo.getNodeRef(nodeData)
47
49
  : undefined;
48
- }, [nodeData, props.additionalNodesInfo]);
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(props.nodeId));
76
+ dispatch(getNodeStructure(nodeId));
75
77
  autofetcher.start();
76
- autofetcher.fetch(() => dispatch(getNodeStructure(props.nodeId)));
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
- }, [props.nodeId, dispatch]);
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: any) => el.id === vdiskIdFromUrl);
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={props.className}>{renderContent()}</div>
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) {