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.
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) {