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
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