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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
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
+
16
+ ## [4.5.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.1...v4.5.2) (2023-06-06)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * **Versions:** enable table dynamic render ([#416](https://github.com/ydb-platform/ydb-embedded-ui/issues/416)) ([3c877ea](https://github.com/ydb-platform/ydb-embedded-ui/commit/3c877ea88a0c4036213b38099676f473cf3ac2d6))
22
+
3
23
  ## [4.5.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.0...v4.5.1) (2023-06-02)
4
24
 
5
25
 
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
4
4
 
5
5
  import InfoViewer from '../InfoViewer/InfoViewer';
6
6
  import ProgressViewer from '../ProgressViewer/ProgressViewer';
7
- import PoolUsage from '../PoolUsage/PoolUsage';
7
+ import {PoolUsage} from '../PoolUsage/PoolUsage';
8
8
 
9
9
  import {LOAD_AVERAGE_TIME_INTERVALS} from '../../utils/constants';
10
10
  import {calcUptime} from '../../utils';
@@ -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) => {
@@ -1,4 +1,4 @@
1
- .pool-usage {
1
+ .ydb-pool-usage {
2
2
  font-size: var(--yc-text-body-2-font-size);
3
3
  line-height: var(--yc-text-body-2-line-height);
4
4
  &__info {
@@ -0,0 +1,50 @@
1
+ import block from 'bem-cn-lite';
2
+
3
+ import type {TPoolStats} from '../../types/api/nodes';
4
+
5
+ import './PoolUsage.scss';
6
+
7
+ const b = block('ydb-pool-usage');
8
+
9
+ const getLineType = (fillWidth: number) => {
10
+ let fillColor = 'green';
11
+ if (fillWidth > 60 && fillWidth <= 80) {
12
+ fillColor = 'yellow';
13
+ } else if (fillWidth > 80) {
14
+ fillColor = 'red';
15
+ }
16
+
17
+ return fillColor;
18
+ };
19
+
20
+ interface PoolUsageProps {
21
+ data?: TPoolStats;
22
+ }
23
+
24
+ export const PoolUsage = ({data: pool = {}}: PoolUsageProps) => {
25
+ const {Threads, Name = 'Unknown', Usage = 0} = pool;
26
+ const dataExist = Usage && Threads;
27
+
28
+ const value = Math.floor(Usage * 100);
29
+ const fillWidth = value > 100 ? 100 : value;
30
+
31
+ return (
32
+ <div className={b()}>
33
+ <div className={b('info')}>
34
+ <div className={b('pool-name')}>{Name}</div>
35
+ {dataExist && (
36
+ <div className={b('value')}>
37
+ <div className={b('percents')}>{value < 1 ? '<1' : value}%</div>
38
+ <div className={b('threads')}>(×{Threads})</div>
39
+ </div>
40
+ )}
41
+ </div>
42
+ <div className={b('visual')}>
43
+ <div
44
+ className={b('usage-line', {type: getLineType(fillWidth)})}
45
+ style={{width: `${fillWidth}%`}}
46
+ />
47
+ </div>
48
+ </div>
49
+ );
50
+ };
@@ -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