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
@@ -0,0 +1,12 @@
1
+ {
2
+ "controls.query-mode-selector_type": "Type:",
3
+ "tabs.newQuery": "New query",
4
+ "tabs.history": "History",
5
+ "tabs.saved": "Saved",
6
+ "history.empty": "History is empty",
7
+ "saved.empty": "There are no saved queries",
8
+ "delete-dialog.header": "Delete query",
9
+ "delete-dialog.question": "Are you sure you want to delete query",
10
+ "delete-dialog.delete": "Delete",
11
+ "delete-dialog.cancel": "Cancel"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "controls.query-mode-selector_type": "Тип:",
3
+ "tabs.newQuery": "Новый запрос",
4
+ "tabs.history": "История",
5
+ "tabs.saved": "Сохраненные",
6
+ "history.empty": "История пуста",
7
+ "saved.empty": "Нет сохраненных запросов",
8
+ "delete-dialog.header": "Удалить запрос",
9
+ "delete-dialog.question": "Вы уверены что хотите удалить запрос",
10
+ "delete-dialog.delete": "Удалить",
11
+ "delete-dialog.cancel": "Отменить"
12
+ }
@@ -0,0 +1,30 @@
1
+ import type {KeyValueRow} from '../../../../types/api/query';
2
+ import type {IQueryResult} from '../../../../types/store/query';
3
+
4
+ export const getPreparedResult = (data: IQueryResult) => {
5
+ const columnDivider = '\t';
6
+ const rowDivider = '\n';
7
+
8
+ if (!data?.result?.length) {
9
+ return '';
10
+ }
11
+
12
+ const columnHeaders = Object.keys(data.result[0]);
13
+ const rows = Array<string[] | KeyValueRow[]>(columnHeaders).concat(data.result);
14
+
15
+ return rows
16
+ .map((item) => {
17
+ const row = [];
18
+
19
+ for (const field in item) {
20
+ if (typeof item[field] === 'object' || Array.isArray(item[field])) {
21
+ row.push(JSON.stringify(item[field]));
22
+ } else {
23
+ row.push(item[field]);
24
+ }
25
+ }
26
+
27
+ return row.join(columnDivider);
28
+ })
29
+ .join(rowDivider);
30
+ };
@@ -3,7 +3,7 @@ import {useDispatch} from 'react-redux';
3
3
 
4
4
  import {NavigationTree} from 'ydb-ui-components';
5
5
 
6
- import {setCurrentSchemaPath, preloadSchemas} from '../../../../store/reducers/schema';
6
+ import {setCurrentSchemaPath, preloadSchemas} from '../../../../store/reducers/schema/schema';
7
7
  import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
8
8
 
9
9
  import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
@@ -8,11 +8,9 @@ import type {TEvDescribeSchemeResult} from '../../types/api/schema';
8
8
 
9
9
  import {DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, DEFAULT_SIZE_TENANT_KEY} from '../../utils/constants';
10
10
  import {useTypedSelector} from '../../utils/hooks';
11
- import routes, {CLUSTER_PAGES, createHref} from '../../routes';
12
- import {setHeader} from '../../store/reducers/header';
13
- import {disableAutorefresh, getSchema, resetLoadingState} from '../../store/reducers/schema';
11
+ import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
12
+ import {disableAutorefresh, getSchema} from '../../store/reducers/schema/schema';
14
13
  import {getSchemaAcl} from '../../store/reducers/schemaAcl/schemaAcl';
15
- import {getTenantInfo, clearTenant} from '../../store/reducers/tenant/tenant';
16
14
 
17
15
  import SplitPane from '../../components/SplitPane';
18
16
  import {AccessDenied} from '../../components/Errors/403';
@@ -62,7 +60,6 @@ function Tenant(props: TenantProps) {
62
60
  const {PathType: currentPathType, PathSubType: currentPathSubType} =
63
61
  (currentItem as TEvDescribeSchemeResult).PathDescription?.Self || {};
64
62
 
65
- const {error: {status: tenantStatus = 200} = {}} = useTypedSelector((state) => state.tenant);
66
63
  const {error: {status: schemaStatus = 200} = {}} = useTypedSelector((state) => state.schema);
67
64
 
68
65
  const dispatch = useDispatch();
@@ -82,7 +79,6 @@ function Tenant(props: TenantProps) {
82
79
  }, [tenantName, dispatch]);
83
80
 
84
81
  useEffect(() => {
85
- dispatch(resetLoadingState());
86
82
  dispatch(getSchema({path: currentSchemaPath}));
87
83
  dispatch(getSchemaAcl({path: currentSchemaPath}));
88
84
  }, [currentSchemaPath, dispatch]);
@@ -93,25 +89,8 @@ function Tenant(props: TenantProps) {
93
89
 
94
90
  useEffect(() => {
95
91
  if (tenantName) {
96
- dispatch(getTenantInfo({path: tenantName}));
97
- dispatch(
98
- setHeader([
99
- {
100
- text: CLUSTER_PAGES.tenants.title,
101
- link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.tenants.id}),
102
- },
103
- {
104
- text: tenantName.startsWith('/') ? tenantName.slice(1) : tenantName,
105
- link: createHref(routes.tenant, undefined, {
106
- name: tenantName,
107
- }),
108
- },
109
- ]),
110
- );
92
+ dispatch(setHeaderBreadcrumbs('tenant', {tenantName}));
111
93
  }
112
- return () => {
113
- dispatch(clearTenant());
114
- };
115
94
  }, [tenantName, dispatch]);
116
95
 
117
96
  const onCollapseSummaryHandler = () => {
@@ -125,7 +104,7 @@ function Tenant(props: TenantProps) {
125
104
  dispatchSummaryVisibilityAction(PaneVisibilityActionTypes.clear);
126
105
  };
127
106
 
128
- const showBlockingError = tenantStatus === 403 || schemaStatus === 403;
107
+ const showBlockingError = schemaStatus === 403;
129
108
 
130
109
  return (
131
110
  <div className={b()}>
@@ -1,5 +1,6 @@
1
- import {Icon} from '../../components/Icon';
2
1
  import {TENANT_GENERAL_TABS_IDS} from '../../store/reducers/tenant/constants';
2
+ import routes, {createHref} from '../../routes';
3
+ import {Icon} from '../../components/Icon';
3
4
 
4
5
  export enum TenantInfoTabsIds {
5
6
  overview = 'overview',
@@ -10,7 +11,8 @@ export enum TenantInfoTabsIds {
10
11
  export enum TenantTabsGroups {
11
12
  info = 'info',
12
13
  general = 'general',
13
- generalTab = 'generalTab',
14
+ queryTab = 'queryTab',
15
+ diagnosticsTab = 'diagnosticsTab',
14
16
  }
15
17
 
16
18
  export const TENANT_GENERAL_TABS = [
@@ -42,3 +44,7 @@ export const TENANT_SCHEMA_TAB = [
42
44
  title: 'Schema',
43
45
  },
44
46
  ];
47
+
48
+ export const getTenantPath = (query = {}) => {
49
+ return createHref(routes.tenant, undefined, query);
50
+ };
@@ -0,0 +1,10 @@
1
+ import type {Settings} from '@gravity-ui/react-data-table';
2
+
3
+ import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
4
+
5
+ export const MAX_QUERY_HEIGHT = 6;
6
+
7
+ export const QUERY_TABLE_SETTINGS: Settings = {
8
+ ...DEFAULT_TABLE_SETTINGS,
9
+ dynamicRenderType: 'variable',
10
+ };
@@ -2,9 +2,12 @@ import {Dispatch} from 'react';
2
2
  import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';
3
3
 
4
4
  import {changeUserInput} from '../../../store/reducers/executeQuery';
5
- import {setShowPreview} from '../../../store/reducers/schema';
6
- import {setTopLevelTab} from '../../../store/reducers/tenant/tenant';
7
- import {TENANT_GENERAL_TABS_IDS} from '../../../store/reducers/tenant/constants';
5
+ import {setShowPreview} from '../../../store/reducers/schema/schema';
6
+ import {setQueryTab, setTopLevelTab} from '../../../store/reducers/tenant/tenant';
7
+ import {
8
+ TENANT_QUERY_TABS_ID,
9
+ TENANT_GENERAL_TABS_IDS,
10
+ } from '../../../store/reducers/tenant/constants';
8
11
  import createToast from '../../../utils/createToast';
9
12
 
10
13
  const createTableTemplate = (path: string) => {
@@ -38,6 +41,7 @@ const bindActions = (
38
41
  const inputQuery = (tmpl: (path: string) => string) => () => {
39
42
  dispatch(changeUserInput({input: tmpl(path)}));
40
43
  dispatch(setTopLevelTab(TENANT_GENERAL_TABS_IDS.query));
44
+ dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
41
45
  setActivePath(path);
42
46
  };
43
47
 
@@ -67,6 +71,7 @@ const bindActions = (
67
71
  openPreview: () => {
68
72
  dispatch(setShowPreview(true));
69
73
  dispatch(setTopLevelTab(TENANT_GENERAL_TABS_IDS.query));
74
+ dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
70
75
  setActivePath(path);
71
76
  },
72
77
  };
@@ -15,7 +15,7 @@ import {ProblemFilter} from '../../components/ProblemFilter';
15
15
  import {Illustration} from '../../components/Illustration';
16
16
  import {AutoFetcher} from '../../utils/autofetcher';
17
17
 
18
- import routes, {CLUSTER_PAGES, createHref} from '../../routes';
18
+ import routes, {createHref} from '../../routes';
19
19
  import {formatCPU, formatBytesToGigabyte, formatNumber} from '../../utils';
20
20
  import {withSearch} from '../../HOCS';
21
21
  import {DEFAULT_TABLE_SETTINGS, TENANT_INITIAL_TAB_KEY} from '../../utils/constants';
@@ -25,7 +25,6 @@ import {
25
25
  getSettingValue,
26
26
  ProblemFilterValues,
27
27
  } from '../../store/reducers/settings/settings';
28
- import {setHeader} from '../../store/reducers/header';
29
28
 
30
29
  import {clusterName} from '../../store';
31
30
  import {TenantTabsGroups, TENANT_GENERAL_TABS, TENANT_INFO_TABS} from '../Tenant/TenantPages';
@@ -51,7 +50,6 @@ class Tenants extends React.Component {
51
50
  tenants: PropTypes.array,
52
51
  searchQuery: PropTypes.string,
53
52
  handleSearchQuery: PropTypes.func,
54
- setHeader: PropTypes.func,
55
53
  filter: PropTypes.string,
56
54
  changeFilter: PropTypes.func,
57
55
  cluster: PropTypes.object,
@@ -77,12 +75,6 @@ class Tenants extends React.Component {
77
75
  this.autofetcher = new AutoFetcher();
78
76
  this.props.getTenantsInfo(clusterName);
79
77
  this.autofetcher.fetch(() => this.props.getTenantsInfo(clusterName));
80
- this.props.setHeader([
81
- {
82
- text: CLUSTER_PAGES.tenants.title,
83
- link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.tenants.id}),
84
- },
85
- ]);
86
78
  }
87
79
 
88
80
  componentWillUnmount() {
@@ -94,14 +86,15 @@ class Tenants extends React.Component {
94
86
 
95
87
  return (
96
88
  <div className={b('controls')}>
97
- <TextInput
98
- className={b('search')}
99
- placeholder="Database name"
100
- value={searchQuery}
101
- onUpdate={handleSearchQuery}
102
- hasClear
103
- autoFocus
104
- />
89
+ <div className={b('search')}>
90
+ <TextInput
91
+ placeholder="Database name"
92
+ value={searchQuery}
93
+ onUpdate={handleSearchQuery}
94
+ hasClear
95
+ autoFocus
96
+ />
97
+ </div>
105
98
  <ProblemFilter value={filter} onChange={changeFilter} />
106
99
  </div>
107
100
  );
@@ -133,15 +126,19 @@ class Tenants extends React.Component {
133
126
  const initialTenantGeneralTab = savedTenantInitialTab || TENANT_GENERAL_TABS[0].id;
134
127
  const initialTenantInfoTab = TENANT_INFO_TABS[0].id;
135
128
 
129
+ const getTenantBackend = (tenant) => {
130
+ const backend = tenant.MonitoringEndpoint ?? tenant.backend;
131
+ return additionalTenantsInfo.tenantBackend
132
+ ? additionalTenantsInfo.tenantBackend(backend)
133
+ : undefined;
134
+ };
135
+
136
136
  const columns = [
137
137
  {
138
138
  name: 'Name',
139
139
  header: 'Database',
140
140
  render: ({value, row}) => {
141
- const backend = row.MonitoringEndpoint ?? row.backend;
142
- const tenantBackend = additionalTenantsInfo.tenantBackend
143
- ? additionalTenantsInfo.tenantBackend(backend)
144
- : undefined;
141
+ const backend = getTenantBackend(row);
145
142
  const isExternalLink = Boolean(backend);
146
143
  return (
147
144
  <div className={b('name-wrapper')}>
@@ -154,7 +151,7 @@ class Tenants extends React.Component {
154
151
  hasClipboardButton
155
152
  path={createHref(routes.tenant, undefined, {
156
153
  name: value,
157
- backend: tenantBackend,
154
+ backend,
158
155
  [TenantTabsGroups.info]: initialTenantInfoTab,
159
156
  [TenantTabsGroups.general]: initialTenantGeneralTab,
160
157
  })}
@@ -294,12 +291,20 @@ class Tenants extends React.Component {
294
291
  header: 'Tablets States',
295
292
  sortable: false,
296
293
  width: 430,
297
- render: ({value, row}) =>
298
- value ? (
299
- <TabletsStatistic path={row.Name} tablets={value} nodeIds={row.NodeIds} />
294
+ render: ({value, row}) => {
295
+ const backend = getTenantBackend(row);
296
+
297
+ return value ? (
298
+ <TabletsStatistic
299
+ path={row.Name}
300
+ tablets={value}
301
+ nodeIds={row.NodeIds}
302
+ backend={backend}
303
+ />
300
304
  ) : (
301
305
  '—'
302
- ),
306
+ );
307
+ },
303
308
  },
304
309
  ];
305
310
 
@@ -309,16 +314,14 @@ class Tenants extends React.Component {
309
314
 
310
315
  return (
311
316
  <div className={b('table-wrapper')}>
312
- <div className={b('table-content')}>
313
- <DataTable
314
- theme="yandex-cloud"
315
- data={filteredTenants}
316
- columns={columns}
317
- settings={DEFAULT_TABLE_SETTINGS}
318
- dynamicRender={true}
319
- emptyDataMessage="No such tenants"
320
- />
321
- </div>
317
+ <DataTable
318
+ theme="yandex-cloud"
319
+ data={filteredTenants}
320
+ columns={columns}
321
+ settings={DEFAULT_TABLE_SETTINGS}
322
+ dynamicRender={true}
323
+ emptyDataMessage="No such tenants"
324
+ />
322
325
  </div>
323
326
  );
324
327
  };
@@ -363,7 +366,6 @@ const mapStateToProps = (state) => {
363
366
  const mapDispatchToProps = {
364
367
  getTenantsInfo,
365
368
  changeFilter,
366
- setHeader,
367
369
  };
368
370
 
369
371
  export default withSearch(connect(mapStateToProps, mapDispatchToProps)(Tenants));
@@ -1,7 +1,7 @@
1
1
  @import '../../styles/mixins.scss';
2
2
 
3
3
  .tenants {
4
- overflow: auto;
4
+ height: 100%;
5
5
  @include flex-container();
6
6
 
7
7
  &__format-label {
@@ -34,10 +34,8 @@
34
34
 
35
35
  &__table-wrapper {
36
36
  overflow: auto;
37
- @include flex-container();
38
- }
37
+ flex-grow: 1;
39
38
 
40
- &__table-content {
41
39
  @include freeze-nth-column(1);
42
40
 
43
41
  @include table-styles;
@@ -15,6 +15,6 @@
15
15
  "settings.useNodesEndpoint.title": "Break the Nodes tab in Diagnostics",
16
16
  "settings.useNodesEndpoint.popover": "Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on versions before 23-1",
17
17
 
18
- "settings.enableQueryModesForExplain.title": "Enable query modes for explain",
19
- "settings.enableQueryModesForExplain.popover": "Enable script | scan query mode selector for both run and explain. May not work on versions before 23-2"
18
+ "settings.enableAdditionalQueryModes.title": "Enable additional query modes",
19
+ "settings.enableAdditionalQueryModes.popover": "Adds 'data' and 'query' modes for run. Enables query modes for explain. May not work on some versions"
20
20
  }
@@ -15,6 +15,6 @@
15
15
  "settings.useNodesEndpoint.title": "Сломать вкладку Nodes в диагностике",
16
16
  "settings.useNodesEndpoint.popover": "Использовать эндпоинт /viewer/json/nodes для вкладки Nodes в диагностике. Может возвращать некорректные данные на версиях до 23-1",
17
17
 
18
- "settings.enableQueryModesForExplain.title": "Включить режимы выполнения запроса для explain",
19
- "settings.enableQueryModesForExplain.popover": "Включить общий переключатель script | scan для run и explain. Может работать некорректно на версиях до 23-2"
18
+ "settings.enableAdditionalQueryModes.title": "Включить дополнительные режимы выполнения запросов",
19
+ "settings.enableAdditionalQueryModes.popover": "Добавляет режимы 'data' и 'query' для run. Включает возможность выбрать режим выполнения запроса для explain. Может работать некорректно на некоторых версиях"
20
20
  }
@@ -4,7 +4,7 @@ import favoriteFilledIcon from '../../assets/icons/star.svg';
4
4
  import flaskIcon from '../../assets/icons/flask.svg';
5
5
 
6
6
  import {
7
- ENABLE_QUERY_MODES_FOR_EXPLAIN,
7
+ ENABLE_ADDITIONAL_QUERY_MODES,
8
8
  INVERTED_DISKS_KEY,
9
9
  THEME_KEY,
10
10
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
@@ -59,9 +59,9 @@ export const useNodesEndpointSetting: SettingProps = {
59
59
  helpPopoverContent: i18n('settings.useNodesEndpoint.popover'),
60
60
  };
61
61
  export const enableQueryModesForExplainSetting: SettingProps = {
62
- settingKey: ENABLE_QUERY_MODES_FOR_EXPLAIN,
63
- title: i18n('settings.enableQueryModesForExplain.title'),
64
- helpPopoverContent: i18n('settings.enableQueryModesForExplain.popover'),
62
+ settingKey: ENABLE_ADDITIONAL_QUERY_MODES,
63
+ title: i18n('settings.enableAdditionalQueryModes.title'),
64
+ helpPopoverContent: i18n('settings.enableAdditionalQueryModes.popover'),
65
65
  };
66
66
 
67
67
  export const generalSection: SettingsSection = {
@@ -3,10 +3,6 @@
3
3
  .ydb-versions {
4
4
  $_: &;
5
5
 
6
- &__content {
7
- padding: 20px;
8
- }
9
-
10
6
  &__controls {
11
7
  display: flex;
12
8
  align-items: center;
@@ -1,10 +1,14 @@
1
1
  import {useState} from 'react';
2
+ import {useDispatch} from 'react-redux';
2
3
  import block from 'bem-cn-lite';
3
4
 
4
5
  import {Checkbox, RadioButton} from '@gravity-ui/uikit';
5
6
 
6
- import type {PreparedClusterNode} from '../../store/reducers/clusterNodes/types';
7
7
  import type {VersionToColorMap} from '../../types/versions';
8
+ import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
9
+ import {getClusterNodes} from '../../store/reducers/clusterNodes/clusterNodes';
10
+ import {Loader} from '../../components/Loader';
11
+
8
12
  import {getGroupedStorageNodes, getGroupedTenantNodes, getOtherNodes} from './groupNodes';
9
13
  import {GroupedNodesTree} from './GroupedNodesTree/GroupedNodesTree';
10
14
  import {GroupByValue} from './types';
@@ -14,11 +18,16 @@ import './Versions.scss';
14
18
  const b = block('ydb-versions');
15
19
 
16
20
  interface VersionsProps {
17
- nodes?: PreparedClusterNode[];
18
21
  versionToColor?: VersionToColorMap;
19
22
  }
20
23
 
21
- export const Versions = ({nodes = [], versionToColor}: VersionsProps) => {
24
+ export const Versions = ({versionToColor}: VersionsProps) => {
25
+ const dispatch = useDispatch();
26
+
27
+ const {nodes = [], loading, wasLoaded} = useTypedSelector((state) => state.clusterNodes);
28
+
29
+ useAutofetcher(() => dispatch(getClusterNodes()), [dispatch], true);
30
+
22
31
  const [groupByValue, setGroupByValue] = useState<GroupByValue>(GroupByValue.VERSION);
23
32
  const [expanded, setExpanded] = useState(false);
24
33
 
@@ -55,67 +64,66 @@ export const Versions = ({nodes = [], versionToColor}: VersionsProps) => {
55
64
  </div>
56
65
  );
57
66
  };
58
- const renderGroupedNodes = () => {
59
- const tenantNodes = getGroupedTenantNodes(nodes, versionToColor, groupByValue);
60
- const storageNodes = getGroupedStorageNodes(nodes, versionToColor);
61
- const otherNodes = getOtherNodes(nodes, versionToColor);
62
- const storageNodesContent = storageNodes?.length ? (
63
- <>
64
- <h3>Storage nodes</h3>
65
- {storageNodes.map(({title, nodes: itemNodes, items, versionColor}, index) => (
66
- <GroupedNodesTree
67
- key={`storage-nodes-${index}`}
68
- title={title}
69
- nodes={itemNodes}
70
- items={items}
71
- versionColor={versionColor}
72
- />
73
- ))}
74
- </>
75
- ) : null;
76
- const tenantNodesContent = tenantNodes?.length ? (
77
- <>
78
- <h3>Database nodes</h3>
79
- {renderControls()}
80
- {tenantNodes.map(
81
- ({title, nodes: itemNodes, items, versionColor, versionsValues}, index) => (
82
- <GroupedNodesTree
83
- key={`tenant-nodes-${index}`}
84
- title={title}
85
- nodes={itemNodes}
86
- items={items}
87
- expanded={expanded}
88
- versionColor={versionColor}
89
- versionsValues={versionsValues}
90
- />
91
- ),
92
- )}
93
- </>
94
- ) : null;
95
- const otherNodesContent = otherNodes?.length ? (
96
- <>
97
- <h3>Other nodes</h3>
98
- {otherNodes.map(
99
- ({title, nodes: itemNodes, items, versionColor, versionsValues}, index) => (
100
- <GroupedNodesTree
101
- key={`other-nodes-${index}`}
102
- title={title}
103
- nodes={itemNodes}
104
- items={items}
105
- versionColor={versionColor}
106
- versionsValues={versionsValues}
107
- />
108
- ),
109
- )}
110
- </>
111
- ) : null;
112
- return (
113
- <div className={b('versions')}>
114
- {storageNodesContent}
115
- {tenantNodesContent}
116
- {otherNodesContent}
117
- </div>
118
- );
119
- };
120
- return <div className={b('content')}>{renderGroupedNodes()}</div>;
67
+
68
+ if (loading && !wasLoaded) {
69
+ return <Loader />;
70
+ }
71
+
72
+ const tenantNodes = getGroupedTenantNodes(nodes, versionToColor, groupByValue);
73
+ const storageNodes = getGroupedStorageNodes(nodes, versionToColor);
74
+ const otherNodes = getOtherNodes(nodes, versionToColor);
75
+ const storageNodesContent = storageNodes?.length ? (
76
+ <>
77
+ <h3>Storage nodes</h3>
78
+ {storageNodes.map(({title, nodes: itemNodes, items, versionColor}) => (
79
+ <GroupedNodesTree
80
+ key={`storage-nodes-${title}`}
81
+ title={title}
82
+ nodes={itemNodes}
83
+ items={items}
84
+ versionColor={versionColor}
85
+ />
86
+ ))}
87
+ </>
88
+ ) : null;
89
+ const tenantNodesContent = tenantNodes?.length ? (
90
+ <>
91
+ <h3>Database nodes</h3>
92
+ {renderControls()}
93
+ {tenantNodes.map(({title, nodes: itemNodes, items, versionColor, versionsValues}) => (
94
+ <GroupedNodesTree
95
+ key={`tenant-nodes-${title}`}
96
+ title={title}
97
+ nodes={itemNodes}
98
+ items={items}
99
+ expanded={expanded}
100
+ versionColor={versionColor}
101
+ versionsValues={versionsValues}
102
+ />
103
+ ))}
104
+ </>
105
+ ) : null;
106
+ const otherNodesContent = otherNodes?.length ? (
107
+ <>
108
+ <h3>Other nodes</h3>
109
+ {otherNodes.map(({title, nodes: itemNodes, items, versionColor, versionsValues}) => (
110
+ <GroupedNodesTree
111
+ key={`other-nodes-${title}`}
112
+ title={title}
113
+ nodes={itemNodes}
114
+ items={items}
115
+ versionColor={versionColor}
116
+ versionsValues={versionsValues}
117
+ />
118
+ ))}
119
+ </>
120
+ ) : null;
121
+
122
+ return (
123
+ <div className={b('versions')}>
124
+ {storageNodesContent}
125
+ {tenantNodesContent}
126
+ {otherNodesContent}
127
+ </div>
128
+ );
121
129
  };
package/dist/routes.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type {Location} from 'history';
1
2
  import qs from 'qs';
2
3
  import {compile} from 'path-to-regexp';
3
4
  import isEmpty from 'lodash/isEmpty';
@@ -13,17 +14,18 @@ const routes = {
13
14
  auth: '/auth',
14
15
  };
15
16
 
16
- export const CLUSTER_PAGES = {
17
- tenants: {id: 'tenants', name: 'Databases', title: 'Database list'},
18
- nodes: {id: 'nodes', name: 'Nodes', title: 'Nodes'},
19
- storage: {id: 'storage', name: 'Storage', title: 'Storage'},
20
- cluster: {id: 'cluster', name: 'Cluster', title: 'Cluster'},
17
+ export const parseQuery = (location: Location) => {
18
+ return qs.parse(location.search, {
19
+ ignoreQueryPrefix: true,
20
+ });
21
21
  };
22
22
 
23
+ export type Query = Record<string | number, string | number | string[] | number[] | undefined>;
24
+
23
25
  export function createHref(
24
26
  route: string,
25
27
  params?: Record<string, string | number>,
26
- query: Record<string | number, string | number | string[] | number[] | undefined> = {},
28
+ query: Query = {},
27
29
  ) {
28
30
  let extendedQuery = query;
29
31