ydb-embedded-ui 4.5.2 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/assets/icons/versions.svg +3 -0
  3. package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +7 -2
  4. package/dist/components/Tablet/Tablet.tsx +17 -3
  5. package/dist/components/TabletsStatistic/TabletsStatistic.tsx +23 -16
  6. package/dist/containers/App/Content.js +8 -4
  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 +129 -20
  10. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +34 -17
  11. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +58 -92
  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.tsx +45 -0
  15. package/dist/containers/Header/Header.scss +4 -19
  16. package/dist/containers/Header/Header.tsx +72 -46
  17. package/dist/containers/Header/breadcrumbs.ts +146 -0
  18. package/dist/containers/Node/Node.tsx +25 -29
  19. package/dist/containers/Node/NodePages.ts +10 -6
  20. package/dist/containers/Nodes/Nodes.tsx +0 -16
  21. package/dist/containers/Nodes/getNodesColumns.tsx +1 -1
  22. package/dist/containers/Storage/Storage.js +1 -11
  23. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +11 -3
  24. package/dist/containers/Tablet/Tablet.tsx +40 -4
  25. package/dist/containers/Tablet/TabletInfo/TabletInfo.tsx +2 -2
  26. package/dist/containers/TabletsFilters/TabletsFilters.js +15 -2
  27. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +1 -1
  29. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.scss +7 -0
  30. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -4
  31. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +5 -3
  32. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +1 -1
  33. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +4 -6
  34. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +56 -53
  35. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +2 -1
  36. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +11 -13
  37. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +1 -1
  38. package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +2 -2
  39. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +7 -3
  40. package/dist/containers/Tenant/Preview/Preview.js +1 -1
  41. package/dist/containers/Tenant/{QueryEditor/QueryResult/QueryResult.js → Query/ExecuteResult/ExecuteResult.js} +3 -5
  42. package/dist/containers/Tenant/{QueryEditor/QueryResult/QueryResult.scss → Query/ExecuteResult/ExecuteResult.scss} +1 -1
  43. package/dist/containers/Tenant/{QueryEditor/QueryExplain/QueryExplain.js → Query/ExplainResult/ExplainResult.js} +3 -5
  44. package/dist/containers/Tenant/{QueryEditor/QueryExplain/QueryExplain.scss → Query/ExplainResult/ExplainResult.scss} +1 -1
  45. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss +20 -0
  46. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +60 -0
  47. package/dist/containers/Tenant/Query/Query.scss +16 -0
  48. package/dist/containers/Tenant/Query/Query.tsx +73 -0
  49. package/dist/containers/Tenant/{QueryEditor → Query/QueryEditor}/QueryEditor.js +43 -100
  50. package/dist/containers/Tenant/{QueryEditor → Query/QueryEditor}/QueryEditor.scss +7 -23
  51. package/dist/containers/Tenant/{QueryEditor → Query}/QueryEditorControls/OldQueryEditorControls.tsx +10 -3
  52. package/dist/containers/Tenant/{QueryEditor → Query}/QueryEditorControls/QueryEditorControls.scss +1 -4
  53. package/dist/containers/Tenant/{QueryEditor → Query}/QueryEditorControls/QueryEditorControls.tsx +8 -1
  54. package/dist/containers/Tenant/{QueryEditor → Query}/QueryEditorControls/shared.ts +1 -6
  55. package/dist/containers/Tenant/Query/QueryTabs/QueryTabs.tsx +59 -0
  56. package/dist/containers/Tenant/{QueryEditor → Query}/SaveQuery/SaveQuery.js +5 -5
  57. package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.scss +55 -0
  58. package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +150 -0
  59. package/dist/containers/Tenant/Query/i18n/en.json +12 -0
  60. package/dist/containers/Tenant/Query/i18n/ru.json +12 -0
  61. package/dist/containers/Tenant/Query/utils/getPreparedResult.ts +30 -0
  62. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -1
  63. package/dist/containers/Tenant/Tenant.tsx +4 -25
  64. package/dist/containers/Tenant/TenantPages.tsx +8 -2
  65. package/dist/containers/Tenant/utils/constants.ts +10 -0
  66. package/dist/containers/Tenant/utils/schemaActions.ts +8 -3
  67. package/dist/containers/Tenants/Tenants.js +39 -37
  68. package/dist/containers/Tenants/Tenants.scss +2 -4
  69. package/dist/containers/UserSettings/i18n/en.json +2 -2
  70. package/dist/containers/UserSettings/i18n/ru.json +2 -2
  71. package/dist/containers/UserSettings/settings.ts +4 -4
  72. package/dist/containers/Versions/Versions.scss +0 -4
  73. package/dist/containers/Versions/Versions.tsx +74 -66
  74. package/dist/routes.ts +8 -6
  75. package/dist/services/api.ts +15 -7
  76. package/dist/store/reducers/clusterNodes/clusterNodes.tsx +4 -0
  77. package/dist/store/reducers/executeQuery.ts +1 -1
  78. package/dist/store/reducers/header/header.ts +31 -0
  79. package/dist/store/reducers/header/types.ts +54 -0
  80. package/dist/store/reducers/index.ts +4 -2
  81. package/dist/store/reducers/node/types.ts +2 -0
  82. package/dist/store/reducers/overview/overview.ts +109 -0
  83. package/dist/store/reducers/overview/types.ts +24 -0
  84. package/dist/store/reducers/{schema.ts → schema/schema.ts} +24 -50
  85. package/dist/{types/store/schema.ts → store/reducers/schema/types.ts} +16 -15
  86. package/dist/store/reducers/settings/settings.ts +5 -3
  87. package/dist/store/reducers/tablet.ts +18 -1
  88. package/dist/store/reducers/tenant/constants.ts +6 -0
  89. package/dist/store/reducers/tenant/tenant.ts +21 -2
  90. package/dist/store/reducers/tenant/types.ts +9 -2
  91. package/dist/store/reducers/topic.ts +1 -1
  92. package/dist/store/state-url-mapping.js +4 -1
  93. package/dist/types/api/query.ts +78 -44
  94. package/dist/types/store/explainQuery.ts +2 -2
  95. package/dist/types/store/query.ts +9 -2
  96. package/dist/types/store/tablet.ts +7 -4
  97. package/dist/utils/constants.ts +5 -1
  98. package/dist/utils/nodes.ts +1 -1
  99. package/dist/utils/query.ts +3 -3
  100. package/package.json +2 -1
  101. package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.scss +0 -85
  102. package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.tsx +0 -95
  103. package/dist/containers/Tenant/QueryEditor/SavedQueries/SavedQueries.js +0 -161
  104. package/dist/containers/Tenant/QueryEditor/SavedQueries/SavedQueries.scss +0 -93
  105. package/dist/containers/Tenant/QueryEditor/i18n/en.json +0 -3
  106. package/dist/containers/Tenant/QueryEditor/i18n/ru.json +0 -3
  107. package/dist/store/reducers/header.ts +0 -26
  108. /package/dist/containers/Tenant/{QueryEditor → Query}/Issues/Issues.scss +0 -0
  109. /package/dist/containers/Tenant/{QueryEditor → Query}/Issues/Issues.tsx +0 -0
  110. /package/dist/containers/Tenant/{QueryEditor → Query}/Issues/models.ts +0 -0
  111. /package/dist/containers/Tenant/{QueryEditor → Query}/QueryDuration/QueryDuration.scss +0 -0
  112. /package/dist/containers/Tenant/{QueryEditor → Query}/QueryDuration/QueryDuration.tsx +0 -0
  113. /package/dist/containers/Tenant/{QueryEditor → Query}/SaveQuery/SaveQuery.scss +0 -0
  114. /package/dist/containers/Tenant/{QueryEditor → Query}/i18n/index.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.7.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.6.0...v4.7.0) (2023-06-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * **QueryEditor:** transform history and saved to tabs ([#427](https://github.com/ydb-platform/ydb-embedded-ui/issues/427)) ([6378ca7](https://github.com/ydb-platform/ydb-embedded-ui/commit/6378ca7013239b33e55c1f88fdde7cab3a102df6))
9
+ * update breadcrumbs ([#432](https://github.com/ydb-platform/ydb-embedded-ui/issues/432)) ([e583a03](https://github.com/ydb-platform/ydb-embedded-ui/commit/e583a03fe0d77698f29c924e611133f015c3f7ad))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **Cluster:** add icons to tabs ([#430](https://github.com/ydb-platform/ydb-embedded-ui/issues/430)) ([e9e649f](https://github.com/ydb-platform/ydb-embedded-ui/commit/e9e649f614691e44172c9b93dd3119066c145413))
15
+ * **ClusterInfo:** hide by default ([#435](https://github.com/ydb-platform/ydb-embedded-ui/issues/435)) ([ef2b353](https://github.com/ydb-platform/ydb-embedded-ui/commit/ef2b3535f2c6324a34c4386680f5050655a04eb4))
16
+ * **Cluster:** use counter from uikit for tabs ([#428](https://github.com/ydb-platform/ydb-embedded-ui/issues/428)) ([19ca3bd](https://github.com/ydb-platform/ydb-embedded-ui/commit/19ca3bd14b15bdab1a9621939ddceee6d23b08ac))
17
+ * **DetailedOverview:** prevent tenant info scroll on overflow ([#434](https://github.com/ydb-platform/ydb-embedded-ui/issues/434)) ([8ed6076](https://github.com/ydb-platform/ydb-embedded-ui/commit/8ed60760d54913d05f39d35d00a34c8b1d7d9738))
18
+ * rename Internal Viewer to Developer UI ([#423](https://github.com/ydb-platform/ydb-embedded-ui/issues/423)) ([3eb21f3](https://github.com/ydb-platform/ydb-embedded-ui/commit/3eb21f35a230cc591f02ef9b195f99031f832e8a))
19
+ * **Storage:** update columns ([#437](https://github.com/ydb-platform/ydb-embedded-ui/issues/437)) ([264fbc9](https://github.com/ydb-platform/ydb-embedded-ui/commit/264fbc984cd9ef1467110d3e2f5ed9b29a526c2b))
20
+ * **Tablet:** clear tablet data on unmount ([#425](https://github.com/ydb-platform/ydb-embedded-ui/issues/425)) ([5d308cd](https://github.com/ydb-platform/ydb-embedded-ui/commit/5d308cdded342d7a40cbc6a91431d3f286c39b8a))
21
+ * **TabletsStatistic:** use tenant backend ([#429](https://github.com/ydb-platform/ydb-embedded-ui/issues/429)) ([d290684](https://github.com/ydb-platform/ydb-embedded-ui/commit/d290684ba08aec8b66c0492ba571a5337b5b896c))
22
+
23
+ ## [4.6.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.2...v4.6.0) (2023-06-13)
24
+
25
+
26
+ ### Features
27
+
28
+ * **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))
29
+ * 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))
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * **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))
35
+
3
36
  ## [4.5.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.1...v4.5.2) (2023-06-06)
4
37
 
5
38
 
@@ -0,0 +1,3 @@
1
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M8.625 13.0156C8.625 13.2695 8.47266 13.498 8.24414 13.5742C8.04102 13.6758 7.76172 13.6504 7.58398 13.4727L5.55273 11.6445C5.42578 11.543 5.375 11.3652 5.375 11.1875C5.375 11.0352 5.42578 10.8574 5.55273 10.7559L7.58398 8.92773C7.76172 8.75 8.04102 8.72461 8.24414 8.82617C8.47266 8.90234 8.625 9.13086 8.625 9.35938V10.5781H9.03125C10.0215 10.5781 10.8594 9.76562 10.8594 8.75V4.61133C10.0215 4.35742 9.4375 3.57031 9.4375 2.65625C9.4375 1.53906 10.3262 0.625 11.4688 0.625C12.5859 0.625 13.5 1.53906 13.5 2.65625C13.5 3.57031 12.8906 4.35742 12.0781 4.61133V8.75C12.0781 10.4512 10.707 11.7969 9.03125 11.7969H8.625V13.0156ZM12.2812 2.65625C12.2812 2.22461 11.9004 1.84375 11.4688 1.84375C11.0117 1.84375 10.6562 2.22461 10.6562 2.65625C10.6562 3.11328 11.0117 3.46875 11.4688 3.46875C11.9004 3.46875 12.2812 3.11328 12.2812 2.65625ZM5.375 1.23438C5.375 1.00586 5.50195 0.777344 5.73047 0.701172C5.93359 0.599609 6.21289 0.625 6.39062 0.802734L8.42188 2.63086C8.54883 2.73242 8.625 2.91016 8.625 3.0625C8.625 3.24023 8.54883 3.41797 8.42188 3.51953L6.39062 5.34766C6.21289 5.52539 5.93359 5.55078 5.73047 5.44922C5.50195 5.37305 5.375 5.14453 5.375 4.89062V3.67188H4.96875C3.95312 3.67188 3.14062 4.50977 3.14062 5.5V9.66406C3.95312 9.91797 4.5625 10.7051 4.5625 11.5938C4.5625 12.7363 3.64844 13.625 2.53125 13.625C1.38867 13.625 0.5 12.7363 0.5 11.5938C0.5 10.7051 1.08398 9.91797 1.92188 9.66406V5.5C1.92188 3.82422 3.26758 2.45312 4.96875 2.45312H5.375V1.23438ZM1.71875 11.5938C1.71875 12.0508 2.07422 12.4062 2.53125 12.4062C2.96289 12.4062 3.34375 12.0508 3.34375 11.5938C3.34375 11.1621 2.96289 10.7812 2.53125 10.7812C2.07422 10.7812 1.71875 11.1621 1.71875 11.5938Z"/>
3
+ </svg>
@@ -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) => {
@@ -26,6 +26,11 @@ export const NodeHostWrapper = ({node, getNodeRef}: NodeHostWrapperProps) => {
26
26
 
27
27
  const isNodeAvailable = !isUnavailableNode(node);
28
28
  const nodeRef = isNodeAvailable && getNodeRef ? getNodeRef(node) + 'internal' : undefined;
29
+ const nodePath = isNodeAvailable
30
+ ? getDefaultNodePath(node.NodeId, {
31
+ tenantName: node.TenantName,
32
+ })
33
+ : undefined;
29
34
 
30
35
  return (
31
36
  <div className={b()}>
@@ -39,7 +44,7 @@ export const NodeHostWrapper = ({node, getNodeRef}: NodeHostWrapperProps) => {
39
44
  <EntityStatus
40
45
  name={node.Host}
41
46
  status={node.SystemState}
42
- path={isNodeAvailable ? getDefaultNodePath(node.NodeId) : undefined}
47
+ path={nodePath}
43
48
  hasClipboardButton
44
49
  className={b('host')}
45
50
  />
@@ -14,18 +14,32 @@ const b = cn('tablet');
14
14
 
15
15
  interface TabletProps {
16
16
  tablet?: TTabletStateInfo;
17
+ tenantName?: string;
17
18
  }
18
19
 
19
- export const Tablet = ({tablet = {}}: TabletProps) => {
20
- const {TabletId: id} = tablet;
20
+ export const Tablet = ({tablet = {}, tenantName}: TabletProps) => {
21
+ const {TabletId: id, NodeId, Type, State} = tablet;
21
22
  const status = tablet.Overall?.toLowerCase();
22
23
 
24
+ const tabletPath =
25
+ id &&
26
+ createHref(
27
+ routes.tablet,
28
+ {id},
29
+ {
30
+ nodeId: NodeId,
31
+ type: Type,
32
+ state: State,
33
+ tenantName,
34
+ },
35
+ );
36
+
23
37
  return (
24
38
  <ContentWithPopup
25
39
  className={b('wrapper')}
26
40
  content={<TabletTooltipContent data={tablet} className={b('popup-content')} />}
27
41
  >
28
- <InternalLink to={id && createHref(routes.tablet, {id})}>
42
+ <InternalLink to={tabletPath}>
29
43
  <div className={b({status})}>
30
44
  <div className={b('type')}>{[getTabletLabel(tablet.Type)]}</div>
31
45
  </div>
@@ -12,9 +12,9 @@ import './TabletsStatistic.scss';
12
12
 
13
13
  const b = cn('tablets-statistic');
14
14
 
15
- type ITablets = TFullTabletStateInfo[] | TComputeTabletStateInfo[];
15
+ type Tablets = TFullTabletStateInfo[] | TComputeTabletStateInfo[];
16
16
 
17
- const prepareTablets = (tablets: ITablets) => {
17
+ const prepareTablets = (tablets: Tablets) => {
18
18
  const res = tablets.map((tablet) => {
19
19
  return {
20
20
  label: getTabletLabel(tablet.Type),
@@ -28,25 +28,32 @@ const prepareTablets = (tablets: ITablets) => {
28
28
  };
29
29
 
30
30
  interface TabletsStatisticProps {
31
- tablets: ITablets;
31
+ tablets: Tablets;
32
32
  path: string | undefined;
33
33
  nodeIds: string[] | number[];
34
+ backend?: string;
34
35
  }
35
36
 
36
- export const TabletsStatistic = ({tablets = [], path, nodeIds}: TabletsStatisticProps) => {
37
+ export const TabletsStatistic = ({tablets = [], path, nodeIds, backend}: TabletsStatisticProps) => {
37
38
  const renderTabletInfo = (item: ReturnType<typeof prepareTablets>[number], index: number) => {
38
- return (
39
- <Link
40
- to={createHref(routes.tabletsFilters, undefined, {
41
- nodeIds,
42
- state: item.state,
43
- type: item.type,
44
- path,
45
- })}
46
- key={index}
47
- className={b('tablet', {state: item.state?.toLowerCase()})}
48
- >
49
- {item.label}: {item.count}
39
+ const tabletsPath = createHref(routes.tabletsFilters, undefined, {
40
+ nodeIds,
41
+ state: item.state,
42
+ type: item.type,
43
+ path,
44
+ backend,
45
+ });
46
+
47
+ const label = `${item.label}: ${item.count}`;
48
+ const className = b('tablet', {state: item.state?.toLowerCase()});
49
+
50
+ return backend ? (
51
+ <a href={tabletsPath} key={index} className={className}>
52
+ {label}
53
+ </a>
54
+ ) : (
55
+ <Link to={tabletsPath} key={index} className={className}>
56
+ {label}
50
57
  </Link>
51
58
  );
52
59
  };
@@ -1,11 +1,11 @@
1
1
  import React from 'react';
2
- import {Switch, Route, Redirect, Router} from 'react-router-dom';
2
+ import {Switch, Route, Redirect, Router, useLocation} from 'react-router-dom';
3
3
  import cn from 'bem-cn-lite';
4
4
  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,10 +23,13 @@ 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
 
29
30
  export function Content(props) {
31
+ const location = useLocation();
32
+
30
33
  const {singleClusterMode} = props;
31
34
  const isClustersPage =
32
35
  location.pathname.includes('/clusters') ||
@@ -44,7 +47,7 @@ export function Content(props) {
44
47
  <Route path={routes.tabletsFilters} component={TabletsFilters} />
45
48
  <Redirect
46
49
  to={createHref(routes.cluster, {
47
- activeTab: CLUSTER_PAGES.tenants.id,
50
+ activeTab: clusterTabsIds.tenants,
48
51
  })}
49
52
  />
50
53
  </Switch>
@@ -53,7 +56,7 @@ export function Content(props) {
53
56
  };
54
57
  return (
55
58
  <React.Fragment>
56
- {!isClustersPage && <Header clusterName={props.clusterName} />}
59
+ {!isClustersPage && <Header mainPage={props.mainPage} />}
57
60
  <main className={b('main')}>{renderRoute()}</main>
58
61
  <ReduxTooltip />
59
62
  <AppIcons />
@@ -65,6 +68,7 @@ Content.propTypes = {
65
68
  singleClusterMode: PropTypes.bool,
66
69
  children: PropTypes.node,
67
70
  clusterName: PropTypes.string,
71
+ mainPage: PropTypes.object,
68
72
  };
69
73
 
70
74
  function ContentWrapper(props) {
@@ -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,112 @@
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 {setHeaderBreadcrumbs} from '../../store/reducers/header/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(setHeaderBreadcrumbs('cluster', {}));
84
+ }, [dispatch, Name]);
85
+
86
+ const versionToColor = useMemo(() => {
87
+ if (additionalVersionsProps?.getVersionToColorMap) {
88
+ return additionalVersionsProps?.getVersionToColorMap();
89
+ }
90
+ return parseVersionsToVersionToColorMap(cluster?.Versions);
91
+ }, [additionalVersionsProps, cluster]);
92
+
93
+ const versionsValues = useMemo(() => {
94
+ return parseNodesToVersionsValues(nodes, versionToColor);
95
+ }, [nodes, versionToColor]);
96
+
97
+ const renderTab = () => {
32
98
  switch (activeTab) {
33
- case CLUSTER_PAGES.tenants.id: {
99
+ case clusterTabsIds.tenants: {
34
100
  return <Tenants additionalTenantsInfo={additionalTenantsInfo} />;
35
101
  }
36
- case CLUSTER_PAGES.nodes.id: {
102
+ case clusterTabsIds.nodes: {
37
103
  return <Nodes additionalNodesInfo={additionalNodesInfo} />;
38
104
  }
39
- case CLUSTER_PAGES.storage.id: {
105
+ case clusterTabsIds.storage: {
40
106
  return <Storage additionalNodesInfo={additionalNodesInfo} />;
41
107
  }
42
- case CLUSTER_PAGES.cluster.id: {
43
- return (
44
- <ClusterInfo
45
- additionalClusterProps={additionalClusterProps}
46
- additionalVersionsProps={additionalVersionsProps}
47
- />
48
- );
108
+ case clusterTabsIds.versions: {
109
+ return <Versions versionToColor={versionToColor} />;
49
110
  }
50
111
  default: {
51
112
  return null;
@@ -53,7 +114,55 @@ function Cluster({
53
114
  }
54
115
  };
55
116
 
56
- return <div className={b({tab: activeTab})}>{renderRoutes()}</div>;
117
+ const getTabEntityCount = (tabId: ClusterTab) => {
118
+ switch (tabId) {
119
+ case clusterTabsIds.tenants: {
120
+ return cluster?.Tenants ? Number(cluster.Tenants) : undefined;
121
+ }
122
+ case clusterTabsIds.nodes: {
123
+ return cluster?.NodesTotal ? Number(cluster.NodesTotal) : undefined;
124
+ }
125
+ default: {
126
+ return undefined;
127
+ }
128
+ }
129
+ };
130
+
131
+ return (
132
+ <div className={b()}>
133
+ <ClusterInfo
134
+ cluster={cluster}
135
+ versionsValues={versionsValues}
136
+ loading={infoLoading}
137
+ error={clusterError}
138
+ additionalClusterProps={additionalClusterProps}
139
+ />
140
+
141
+ <div>
142
+ <Tabs
143
+ size="l"
144
+ allowNotSelected={true}
145
+ activeTab={activeTab as string}
146
+ items={clusterTabs.map((item) => {
147
+ return {
148
+ ...item,
149
+ counter: getTabEntityCount(item.id),
150
+ };
151
+ })}
152
+ wrapTo={({id}, node) => {
153
+ const path = getClusterPath(id as ClusterTab, queryParams);
154
+ return (
155
+ <InternalLink to={path} key={id}>
156
+ {node}
157
+ </InternalLink>
158
+ );
159
+ }}
160
+ />
161
+ </div>
162
+
163
+ <div className={b('content')}>{renderTab()}</div>
164
+ </div>
165
+ );
57
166
  }
58
167
 
59
168
  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