ydb-embedded-ui 4.5.2 → 4.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +1 -1
  3. package/dist/containers/App/Content.js +3 -2
  4. package/dist/containers/AsideNavigation/AsideNavigation.tsx +4 -50
  5. package/dist/containers/Cluster/Cluster.scss +7 -48
  6. package/dist/containers/Cluster/Cluster.tsx +136 -20
  7. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +34 -17
  8. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +57 -91
  9. package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.scss +48 -0
  10. package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx +34 -0
  11. package/dist/containers/Cluster/utils.ts +34 -0
  12. package/dist/containers/Header/Header.scss +0 -24
  13. package/dist/containers/Header/Header.tsx +14 -44
  14. package/dist/containers/Node/Node.tsx +22 -20
  15. package/dist/containers/Nodes/Nodes.tsx +0 -16
  16. package/dist/containers/Nodes/getNodesColumns.tsx +1 -1
  17. package/dist/containers/Storage/Storage.js +1 -11
  18. package/dist/containers/Tablet/Tablet.tsx +28 -0
  19. package/dist/containers/TabletsFilters/TabletsFilters.js +16 -1
  20. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +1 -1
  21. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +3 -0
  22. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  23. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +4 -6
  24. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +56 -53
  25. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
  26. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  27. package/dist/containers/Tenant/Preview/Preview.js +1 -1
  28. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +26 -22
  29. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +10 -3
  30. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +8 -1
  31. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +1 -6
  32. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -1
  33. package/dist/containers/Tenant/Tenant.tsx +7 -12
  34. package/dist/containers/Tenant/utils/schemaActions.ts +1 -1
  35. package/dist/containers/Tenants/Tenants.js +18 -28
  36. package/dist/containers/Tenants/Tenants.scss +2 -4
  37. package/dist/containers/UserSettings/i18n/en.json +2 -2
  38. package/dist/containers/UserSettings/i18n/ru.json +2 -2
  39. package/dist/containers/UserSettings/settings.ts +4 -4
  40. package/dist/containers/Versions/Versions.scss +0 -4
  41. package/dist/containers/Versions/Versions.tsx +74 -66
  42. package/dist/routes.ts +0 -7
  43. package/dist/services/api.ts +6 -2
  44. package/dist/store/reducers/clusterNodes/clusterNodes.tsx +4 -0
  45. package/dist/store/reducers/index.ts +3 -1
  46. package/dist/store/reducers/overview/overview.ts +109 -0
  47. package/dist/store/reducers/overview/types.ts +24 -0
  48. package/dist/store/reducers/{schema.ts → schema/schema.ts} +24 -50
  49. package/dist/{types/store/schema.ts → store/reducers/schema/types.ts} +16 -15
  50. package/dist/store/reducers/settings/settings.ts +5 -3
  51. package/dist/types/api/query.ts +78 -44
  52. package/dist/types/store/explainQuery.ts +2 -2
  53. package/dist/types/store/query.ts +4 -2
  54. package/dist/utils/constants.ts +3 -1
  55. package/dist/utils/nodes.ts +1 -1
  56. package/dist/utils/query.ts +3 -3
  57. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.6.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.2...v4.6.0) (2023-06-13)
4
+
5
+
6
+ ### Features
7
+
8
+ * **QueryEditor:** add data and query modes ([#422](https://github.com/ydb-platform/ydb-embedded-ui/issues/422)) ([c142f03](https://github.com/ydb-platform/ydb-embedded-ui/commit/c142f03e9caeab4dcf1d34b3988e949a94213932))
9
+ * rework navigation, update breadcrumbs ([#418](https://github.com/ydb-platform/ydb-embedded-ui/issues/418)) ([2d807d6](https://github.com/ydb-platform/ydb-embedded-ui/commit/2d807d6a52e13edcf2a7e1591672224339d91949))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **Diagnostics:** remove unneded tenantInfo fetch ([#420](https://github.com/ydb-platform/ydb-embedded-ui/issues/420)) ([ccaafe4](https://github.com/ydb-platform/ydb-embedded-ui/commit/ccaafe4ec9346ee1ec2ebd2a62600274f2175bfb))
15
+
3
16
  ## [4.5.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.1...v4.5.2) (2023-06-06)
4
17
 
5
18
 
@@ -16,7 +16,7 @@ const b = block('ydb-node-host-wrapper');
16
16
 
17
17
  interface NodeHostWrapperProps {
18
18
  node: INodesPreparedEntity;
19
- getNodeRef?: (node?: NodeAddress) => string;
19
+ getNodeRef?: (node?: NodeAddress) => string | null;
20
20
  }
21
21
 
22
22
  export const NodeHostWrapper = ({node, getNodeRef}: NodeHostWrapperProps) => {
@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
5
5
 
6
6
  import {ThemeProvider} from '@gravity-ui/uikit';
7
7
 
8
- import routes, {createHref, CLUSTER_PAGES} from '../../routes';
8
+ import routes, {createHref} from '../../routes';
9
9
 
10
10
  import Cluster from '../Cluster/Cluster';
11
11
  import Tenant from '../Tenant/Tenant';
@@ -23,6 +23,7 @@ import './App.scss';
23
23
  import PropTypes from 'prop-types';
24
24
  import HistoryContext from '../../contexts/HistoryContext';
25
25
  import Authentication from '../Authentication/Authentication';
26
+ import {clusterTabsIds} from '../Cluster/utils';
26
27
 
27
28
  const b = cn('app');
28
29
 
@@ -44,7 +45,7 @@ export function Content(props) {
44
45
  <Route path={routes.tabletsFilters} component={TabletsFilters} />
45
46
  <Redirect
46
47
  to={createHref(routes.cluster, {
47
- activeTab: CLUSTER_PAGES.tenants.id,
48
+ activeTab: clusterTabsIds.tenants,
48
49
  })}
49
50
  />
50
51
  </Switch>
@@ -9,11 +9,7 @@ import {AsideHeader, MenuItem as AsideHeaderMenuItem, FooterItem} from '@gravity
9
9
 
10
10
  import signOutIcon from '../../assets/icons/signOut.svg';
11
11
  import signInIcon from '../../assets/icons/signIn.svg';
12
- import databaseIcon from '../../assets/icons/server.svg';
13
- import storageIcon from '../../assets/icons/storage.svg';
14
- import clusterIcon from '../../assets/icons/cluster.svg';
15
12
  import ydbLogoIcon from '../../assets/icons/ydb.svg';
16
- import databasesIcon from '../../assets/icons/databases.svg';
17
13
  import userSecret from '../../assets/icons/user-secret.svg';
18
14
  import userChecked from '../../assets/icons/user-check.svg';
19
15
  import settingsIcon from '../../assets/icons/settings.svg';
@@ -21,7 +17,7 @@ import supportIcon from '../../assets/icons/support.svg';
21
17
 
22
18
  import {UserSettings} from '../UserSettings/UserSettings';
23
19
 
24
- import routes, {createHref, CLUSTER_PAGES} from '../../routes';
20
+ import routes, {createHref} from '../../routes';
25
21
 
26
22
  import {logout} from '../../store/reducers/authentication';
27
23
  import {getParsedSettingValue, setSettingValue} from '../../store/reducers/settings/settings';
@@ -115,46 +111,8 @@ interface AsideNavigationProps {
115
111
  setSettingValue: (name: string, value: string) => void;
116
112
  }
117
113
 
118
- const items: MenuItem[] = [
119
- {
120
- id: CLUSTER_PAGES.tenants.id,
121
- title: 'Databases',
122
- icon: databasesIcon,
123
- iconSize: 20,
124
- location: createHref(routes.cluster, {
125
- activeTab: CLUSTER_PAGES.tenants.id,
126
- }),
127
- locationKeys: ['/tenant'],
128
- },
129
- {
130
- id: CLUSTER_PAGES.nodes.id,
131
- title: 'Nodes',
132
- icon: databaseIcon,
133
- iconSize: 20,
134
- location: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.nodes.id}),
135
- locationKeys: ['/node'],
136
- },
137
- {
138
- id: CLUSTER_PAGES.storage.id,
139
- title: 'Storage',
140
- icon: storageIcon,
141
- iconSize: 20,
142
- location: createHref(routes.cluster, {
143
- activeTab: CLUSTER_PAGES.storage.id,
144
- }),
145
- locationKeys: ['/storage'],
146
- },
147
- {
148
- id: CLUSTER_PAGES.cluster.id,
149
- title: 'Cluster',
150
- icon: clusterIcon,
151
- iconSize: 20,
152
- location: createHref(routes.cluster, {
153
- activeTab: CLUSTER_PAGES.cluster.id,
154
- }),
155
- locationKeys: ['/cluster/cluster'],
156
- },
157
- ];
114
+ // FIXME: add items or delete
115
+ const items: MenuItem[] = [];
158
116
 
159
117
  enum Panel {
160
118
  UserSettings = 'UserSettings',
@@ -172,17 +130,13 @@ function AsideNavigation(props: AsideNavigationProps) {
172
130
 
173
131
  const menuItems: AsideHeaderMenuItem[] = React.useMemo(() => {
174
132
  const {pathname} = location;
175
- const isClusterPage = pathname === '/cluster';
176
133
  const menuItems: AsideHeaderMenuItem[] = items.map((item) => {
177
134
  const locationKeysCoincidence = item.locationKeys?.filter((key) =>
178
135
  pathname.startsWith(key),
179
136
  );
180
- let current =
137
+ const current =
181
138
  (locationKeysCoincidence && locationKeysCoincidence.length > 0) ||
182
139
  item.location.startsWith(pathname);
183
- if (isClusterPage && item.id !== CLUSTER_PAGES.tenants.id) {
184
- current = false;
185
- }
186
140
  return {
187
141
  id: item.id,
188
142
  title: item.title,
@@ -2,57 +2,16 @@
2
2
 
3
3
  .cluster {
4
4
  overflow: auto;
5
+ flex-grow: 1;
5
6
 
6
- padding: 0 20px;
7
- @include flex-container();
8
-
9
- &_tab_cluster {
10
- padding: 0px;
11
- }
12
-
13
- &__tab {
14
- text-decoration: none;
15
-
16
- &:first-letter {
17
- text-transform: uppercase;
18
- }
19
- }
20
-
21
- &__format-label {
22
- margin-right: 15px;
23
- }
24
-
25
- &__title {
26
- text-align: center;
27
- }
7
+ height: 100%;
8
+ padding: 20px 20px 0px;
28
9
 
29
- &__tooltip {
30
- animation: none !important;
31
- }
32
-
33
- &__search {
34
- width: 255px;
35
- margin: 0 15px 0 0;
36
- }
37
-
38
- &__tablets {
39
- padding: 0 !important;
40
-
41
- .tablets-viewer__grid {
42
- grid-template-columns: 125px;
43
- }
44
- }
45
-
46
- &__controls {
47
- display: flex;
48
- justify-content: space-between;
10
+ @include flex-container();
49
11
 
50
- margin: 17px 0;
51
- }
12
+ &__content {
13
+ overflow: auto;
52
14
 
53
- &__table-wrapper {
54
- display: flex;
55
- flex: 1 1 auto;
56
- flex-direction: column;
15
+ height: 100%;
57
16
  }
58
17
  }
@@ -1,51 +1,133 @@
1
- import {useRouteMatch} from 'react-router';
1
+ import {useEffect, useMemo} from 'react';
2
+ import {useLocation, useRouteMatch} from 'react-router';
3
+ import {useDispatch} from 'react-redux';
2
4
  import cn from 'bem-cn-lite';
5
+ import qs from 'qs';
6
+
7
+ import {Tabs} from '@gravity-ui/uikit';
3
8
 
4
9
  import type {AdditionalClusterProps, AdditionalVersionsProps} from '../../types/additionalProps';
5
- import routes, {CLUSTER_PAGES} from '../../routes';
10
+ import type {AdditionalNodesInfo} from '../../utils/nodes';
11
+ import routes from '../../routes';
6
12
 
7
- import {ClusterInfo} from './ClusterInfo/ClusterInfo';
13
+ import {setHeader} from '../../store/reducers/header';
14
+ import {getClusterInfo} from '../../store/reducers/cluster/cluster';
15
+ import {getClusterNodes} from '../../store/reducers/clusterNodes/clusterNodes';
16
+ import {parseNodesToVersionsValues, parseVersionsToVersionToColorMap} from '../../utils/versions';
17
+ import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
18
+
19
+ import {InternalLink} from '../../components/InternalLink';
8
20
  import Tenants from '../Tenants/Tenants';
9
21
  import {Nodes} from '../Nodes/Nodes';
10
22
  import Storage from '../Storage/Storage';
23
+ import {Versions} from '../Versions/Versions';
24
+
25
+ import {ClusterInfo} from './ClusterInfo/ClusterInfo';
26
+ import {ClusterTab, clusterTabs, clusterTabsIds, getClusterPath} from './utils';
11
27
 
12
28
  import './Cluster.scss';
13
29
 
14
30
  const b = cn('cluster');
15
31
 
16
32
  interface ClusterProps {
17
- additionalTenantsInfo?: any;
18
- additionalNodesInfo?: any;
33
+ additionalTenantsInfo?: unknown;
34
+ additionalNodesInfo?: AdditionalNodesInfo;
19
35
  additionalClusterProps?: AdditionalClusterProps;
20
36
  additionalVersionsProps?: AdditionalVersionsProps;
21
37
  }
22
38
 
23
39
  function Cluster({
40
+ additionalClusterProps,
24
41
  additionalTenantsInfo,
25
42
  additionalNodesInfo,
26
- additionalClusterProps,
27
43
  additionalVersionsProps,
28
44
  }: ClusterProps) {
29
- const match = useRouteMatch<{activeTab?: string}>(routes.cluster);
30
- const activeTab = match?.params?.activeTab ?? CLUSTER_PAGES.tenants.id;
31
- const renderRoutes = () => {
45
+ const dispatch = useDispatch();
46
+
47
+ const match = useRouteMatch<{activeTab: string}>(routes.cluster);
48
+ const {activeTab = clusterTabsIds.tenants} = match?.params || {};
49
+
50
+ const location = useLocation();
51
+ const queryParams = qs.parse(location.search, {
52
+ ignoreQueryPrefix: true,
53
+ });
54
+ const {clusterName} = queryParams;
55
+
56
+ const {
57
+ data: cluster = {},
58
+ loading: clusterLoading,
59
+ wasLoaded: clusterWasLoaded,
60
+ error: clusterError,
61
+ } = useTypedSelector((state) => state.cluster);
62
+ const {
63
+ nodes,
64
+ loading: nodesLoading,
65
+ wasLoaded: nodesWasLoaded,
66
+ } = useTypedSelector((state) => state.clusterNodes);
67
+
68
+ const {Name} = cluster;
69
+
70
+ const infoLoading = (clusterLoading && !clusterWasLoaded) || (nodesLoading && !nodesWasLoaded);
71
+
72
+ useEffect(() => {
73
+ dispatch(getClusterNodes());
74
+ }, [dispatch]);
75
+
76
+ useAutofetcher(
77
+ () => dispatch(getClusterInfo(clusterName ? String(clusterName) : undefined)),
78
+ [dispatch, clusterName],
79
+ true,
80
+ );
81
+
82
+ useEffect(() => {
83
+ dispatch(
84
+ setHeader([
85
+ {
86
+ text: Name || 'Cluster',
87
+ link: getClusterPath(),
88
+ },
89
+ ]),
90
+ );
91
+ }, [dispatch, Name]);
92
+
93
+ const versionToColor = useMemo(() => {
94
+ if (additionalVersionsProps?.getVersionToColorMap) {
95
+ return additionalVersionsProps?.getVersionToColorMap();
96
+ }
97
+ return parseVersionsToVersionToColorMap(cluster?.Versions);
98
+ }, [additionalVersionsProps, cluster]);
99
+
100
+ const versionsValues = useMemo(() => {
101
+ return parseNodesToVersionsValues(nodes, versionToColor);
102
+ }, [nodes, versionToColor]);
103
+
104
+ const renderTab = () => {
32
105
  switch (activeTab) {
33
- case CLUSTER_PAGES.tenants.id: {
106
+ case clusterTabsIds.tenants: {
34
107
  return <Tenants additionalTenantsInfo={additionalTenantsInfo} />;
35
108
  }
36
- case CLUSTER_PAGES.nodes.id: {
109
+ case clusterTabsIds.nodes: {
37
110
  return <Nodes additionalNodesInfo={additionalNodesInfo} />;
38
111
  }
39
- case CLUSTER_PAGES.storage.id: {
112
+ case clusterTabsIds.storage: {
40
113
  return <Storage additionalNodesInfo={additionalNodesInfo} />;
41
114
  }
42
- case CLUSTER_PAGES.cluster.id: {
43
- return (
44
- <ClusterInfo
45
- additionalClusterProps={additionalClusterProps}
46
- additionalVersionsProps={additionalVersionsProps}
47
- />
48
- );
115
+ case clusterTabsIds.versions: {
116
+ return <Versions versionToColor={versionToColor} />;
117
+ }
118
+ default: {
119
+ return null;
120
+ }
121
+ }
122
+ };
123
+
124
+ const getTabEntityCount = (tabId: ClusterTab) => {
125
+ switch (tabId) {
126
+ case clusterTabsIds.tenants: {
127
+ return cluster?.Tenants;
128
+ }
129
+ case clusterTabsIds.nodes: {
130
+ return cluster?.NodesTotal;
49
131
  }
50
132
  default: {
51
133
  return null;
@@ -53,7 +135,41 @@ function Cluster({
53
135
  }
54
136
  };
55
137
 
56
- return <div className={b({tab: activeTab})}>{renderRoutes()}</div>;
138
+ return (
139
+ <div className={b()}>
140
+ <ClusterInfo
141
+ cluster={cluster}
142
+ versionsValues={versionsValues}
143
+ loading={infoLoading}
144
+ error={clusterError}
145
+ additionalClusterProps={additionalClusterProps}
146
+ />
147
+
148
+ <div>
149
+ <Tabs
150
+ size="l"
151
+ allowNotSelected={true}
152
+ activeTab={activeTab as string}
153
+ items={clusterTabs.map((item) => {
154
+ return {
155
+ ...item,
156
+ title: `${item.title} ${getTabEntityCount(item.id) || ''}`,
157
+ };
158
+ })}
159
+ wrapTo={({id}, node) => {
160
+ const path = getClusterPath(id as ClusterTab, queryParams);
161
+ return (
162
+ <InternalLink to={path} key={id}>
163
+ {node}
164
+ </InternalLink>
165
+ );
166
+ }}
167
+ />
168
+ </div>
169
+
170
+ <div className={b('content')}>{renderTab()}</div>
171
+ </div>
172
+ );
57
173
  }
58
174
 
59
175
  export default Cluster;
@@ -1,31 +1,48 @@
1
1
  @import '../../../styles/mixins';
2
2
 
3
3
  .cluster-info {
4
- $_: &;
5
- $progress_width: 100%;
4
+ width: 100%;
6
5
 
7
- display: flex;
8
- flex: 1 1 auto;
9
- flex-direction: column;
6
+ &__header {
7
+ display: flex;
10
8
 
11
- width: 100%;
9
+ width: fit-content;
10
+ margin-bottom: 20px;
12
11
 
13
- font-size: var(--yc-text-body-2-font-size);
14
- line-height: var(--yc-text-body-2-line-height);
12
+ cursor: pointer;
15
13
 
16
- &__header {
17
- padding: 20px;
14
+ &__expand-button {
15
+ margin-left: 6px;
18
16
 
19
- border-bottom: 1px solid var(--yc-color-line-generic);
20
- background: var(--yc-color-base-background);
17
+ &_rotate {
18
+ transform: rotate(180deg);
19
+ }
20
+ }
21
21
  }
22
22
 
23
- &__title {
24
- display: flex;
25
- align-items: center;
23
+ &__title .entity-status__name {
24
+ font-size: var(--yc-text-header-1-font-size);
25
+ font-weight: var(--yc-text-header-font-weight);
26
+ line-height: var(--yc-text-header-1-line-height);
27
+ }
28
+
29
+ &__title-skeleton {
30
+ width: 20%;
31
+ min-width: 200px;
32
+ height: var(--yc-text-header-1-line-height);
33
+ }
26
34
 
27
- margin-top: 16px;
35
+ &__error {
36
+ font-size: var(--yc-text-body-2-font-size);
37
+ line-height: var(--yc-text-body-2-line-height);
38
+ }
39
+
40
+ &__info {
28
41
  margin-bottom: 20px;
42
+
43
+ &_hidden {
44
+ display: none;
45
+ }
29
46
  }
30
47
 
31
48
  &__metric-field {
@@ -38,7 +55,7 @@
38
55
  align-items: center;
39
56
 
40
57
  & .tablet {
41
- margin-bottom: 2px;
58
+ margin-top: 2px;
42
59
  }
43
60
  }
44
61
 
@@ -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
  };