ydb-embedded-ui 4.12.0 → 4.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/components/InfoViewer/formatters/common.ts +4 -2
  3. package/dist/components/InfoViewer/i18n/en.json +4 -0
  4. package/dist/components/InfoViewer/i18n/index.ts +11 -0
  5. package/dist/components/InfoViewer/i18n/ru.json +4 -0
  6. package/dist/components/Tablet/Tablet.scss +1 -16
  7. package/dist/components/Tablet/Tablet.tsx +5 -5
  8. package/dist/components/TabletIcon/TabletIcon.scss +17 -0
  9. package/dist/components/TabletIcon/TabletIcon.tsx +18 -0
  10. package/dist/containers/Header/Header.scss +2 -0
  11. package/dist/containers/Header/Header.tsx +2 -7
  12. package/dist/containers/Header/{breadcrumbs.ts → breadcrumbs.tsx} +19 -8
  13. package/dist/containers/Nodes/Nodes.tsx +53 -16
  14. package/dist/containers/Nodes/getNodesColumns.tsx +31 -13
  15. package/dist/containers/Tablet/Tablet.tsx +9 -3
  16. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +5 -2
  17. package/dist/containers/Tenant/Info/ExternalDataSource/ExternalDataSource.scss +5 -0
  18. package/dist/containers/Tenant/Info/ExternalDataSource/ExternalDataSource.tsx +81 -0
  19. package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.scss +5 -0
  20. package/dist/containers/Tenant/Info/ExternalTable/ExternalTable.tsx +103 -0
  21. package/dist/containers/Tenant/Info/i18n/en.json +8 -0
  22. package/dist/containers/Tenant/Info/i18n/index.ts +11 -0
  23. package/dist/containers/Tenant/Info/i18n/ru.json +8 -0
  24. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +4 -4
  25. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +10 -3
  26. package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.scss +8 -0
  27. package/dist/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +13 -1
  28. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +4 -6
  29. package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +3 -1
  30. package/dist/containers/Tenant/Query/i18n/en.json +6 -4
  31. package/dist/containers/Tenant/Query/i18n/ru.json +6 -4
  32. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +12 -2
  33. package/dist/containers/Tenant/i18n/en.json +12 -2
  34. package/dist/containers/Tenant/i18n/ru.json +11 -1
  35. package/dist/containers/Tenant/utils/schemaActions.ts +76 -28
  36. package/dist/containers/Tenant/utils/schemaControls.tsx +69 -0
  37. package/dist/containers/UserSettings/i18n/en.json +3 -0
  38. package/dist/containers/UserSettings/i18n/ru.json +3 -0
  39. package/dist/containers/UserSettings/settings.ts +12 -1
  40. package/dist/index.js +7 -3
  41. package/dist/services/api.ts +24 -12
  42. package/dist/store/reducers/header/types.ts +2 -0
  43. package/dist/store/reducers/nodes/nodes.ts +23 -6
  44. package/dist/store/reducers/nodes/selectors.ts +2 -2
  45. package/dist/store/reducers/nodes/types.ts +15 -5
  46. package/dist/store/reducers/settings/settings.ts +5 -0
  47. package/dist/styles/constants.scss +3 -0
  48. package/dist/types/api/compute.ts +0 -12
  49. package/dist/types/api/nodes.ts +0 -12
  50. package/dist/utils/constants.ts +3 -0
  51. package/dist/utils/filters.ts +23 -0
  52. package/dist/utils/hooks/i18n/en.json +3 -0
  53. package/dist/utils/hooks/i18n/index.ts +11 -0
  54. package/dist/utils/hooks/i18n/ru.json +3 -0
  55. package/dist/utils/hooks/index.ts +4 -0
  56. package/dist/utils/hooks/useNodesRequestParams.ts +46 -0
  57. package/dist/utils/hooks/useQueryModes.ts +34 -0
  58. package/dist/utils/hooks/useTableSort.ts +37 -0
  59. package/dist/utils/nodes.ts +25 -0
  60. package/dist/utils/query.ts +5 -1
  61. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.14.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.13.0...v4.14.0) (2023-08-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Nodes:** filter and sort on backend ([#503](https://github.com/ydb-platform/ydb-embedded-ui/issues/503)) ([2e8ab8e](https://github.com/ydb-platform/ydb-embedded-ui/commit/2e8ab8e9965db61ec281f7340b89dd3967b639df))
9
+ * **Query:** add explanation to query duration ([#501](https://github.com/ydb-platform/ydb-embedded-ui/issues/501)) ([a5f5140](https://github.com/ydb-platform/ydb-embedded-ui/commit/a5f5140a23864147d8495e3c6b94709e5e710a9b))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **Header:** add icons for nodes and tablets ([#500](https://github.com/ydb-platform/ydb-embedded-ui/issues/500)) ([862660c](https://github.com/ydb-platform/ydb-embedded-ui/commit/862660c1928c2c2b626e4417cd043f0bd5a65df9))
15
+ * **Query:** fix query method selector help text ([#504](https://github.com/ydb-platform/ydb-embedded-ui/issues/504)) ([65cdf9e](https://github.com/ydb-platform/ydb-embedded-ui/commit/65cdf9ee93277c193cc1ad036b2cb38d2ae15b71))
16
+ * **Query:** transfer API calls to a new line ([#499](https://github.com/ydb-platform/ydb-embedded-ui/issues/499)) ([de3d540](https://github.com/ydb-platform/ydb-embedded-ui/commit/de3d5404310f32ba05598bb99a1afb1b65ab45a1))
17
+ * **SchemaTree:** transfer Show Preview to SchemaTree ([#505](https://github.com/ydb-platform/ydb-embedded-ui/issues/505)) ([46220c4](https://github.com/ydb-platform/ydb-embedded-ui/commit/46220c4b2cd111acf12712b4693744c52aaf7231))
18
+
19
+ ## [4.13.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.12.0...v4.13.0) (2023-08-04)
20
+
21
+
22
+ ### Features
23
+
24
+ * info and summary tabs for external objects ([#493](https://github.com/ydb-platform/ydb-embedded-ui/issues/493)) ([88d9041](https://github.com/ydb-platform/ydb-embedded-ui/commit/88d9041f080f13046aeaf55765609dbc13b87285))
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **SchemaTree:** add actions to external objects ([#497](https://github.com/ydb-platform/ydb-embedded-ui/issues/497)) ([5029579](https://github.com/ydb-platform/ydb-embedded-ui/commit/5029579796dd5fb985005f39e9ef8daf142366d0))
30
+ * **SchemaTree:** set required query mode for tree actions ([#491](https://github.com/ydb-platform/ydb-embedded-ui/issues/491)) ([ccd1eda](https://github.com/ydb-platform/ydb-embedded-ui/commit/ccd1edac0d84357cd605c9d131c99890449d8bd8))
31
+
3
32
  ## [4.12.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.11.1...v4.12.0) (2023-08-02)
4
33
 
5
34
 
@@ -3,13 +3,15 @@ import {formatDateTime} from '../../../utils';
3
3
 
4
4
  import {createInfoFormatter} from '../utils';
5
5
 
6
+ import i18n from '../i18n';
7
+
6
8
  export const formatCommonItem = createInfoFormatter<TDirEntry>({
7
9
  values: {
8
10
  PathType: (value) => value?.substring('EPathType'.length),
9
11
  CreateStep: formatDateTime,
10
12
  },
11
13
  labels: {
12
- PathType: 'Type',
13
- CreateStep: 'Created',
14
+ PathType: i18n('common.type'),
15
+ CreateStep: i18n('common.created'),
14
16
  },
15
17
  });
@@ -0,0 +1,4 @@
1
+ {
2
+ "common.created": "Created",
3
+ "common.type": "Type"
4
+ }
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-components-info-viewer';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,4 @@
1
+ {
2
+ "common.created": "Создано",
3
+ "common.type": "Тип"
4
+ }
@@ -1,17 +1,8 @@
1
1
  .tablet {
2
- display: flex;
3
- justify-content: center;
4
-
5
- width: 23px;
6
- height: 18px;
7
-
8
- font-size: 10px;
9
2
  cursor: pointer;
10
- text-transform: uppercase;
11
3
 
12
4
  color: var(--yc-color-text-complementary);
13
- border: 1px solid var(--yc-color-base-generic-medium-hover);
14
- border-radius: 4px;
5
+ border-color: var(--yc-color-base-generic-medium-hover);
15
6
 
16
7
  &__wrapper {
17
8
  margin-right: 2px;
@@ -25,12 +16,6 @@
25
16
  padding: 10px;
26
17
  }
27
18
 
28
- &__type {
29
- line-height: 17px;
30
-
31
- color: var(--yc-color-text-complementary);
32
- }
33
-
34
19
  &_status_gray {
35
20
  background-color: var(--yc-color-text-complementary);
36
21
  }
@@ -5,6 +5,7 @@ import {getTabletLabel} from '../../utils/constants';
5
5
  import routes, {createHref} from '../../routes';
6
6
 
7
7
  import {ContentWithPopup} from '../ContentWithPopup/ContentWithPopup';
8
+ import {TabletIcon} from '../TabletIcon/TabletIcon';
8
9
  import {InternalLink} from '../InternalLink';
9
10
  import {TabletTooltipContent} from '../TooltipsContent';
10
11
 
@@ -18,10 +19,11 @@ interface TabletProps {
18
19
  }
19
20
 
20
21
  export const Tablet = ({tablet = {}, tenantName}: TabletProps) => {
21
- const {TabletId: id, NodeId} = tablet;
22
+ const {TabletId: id, NodeId, Type} = tablet;
22
23
  const status = tablet.Overall?.toLowerCase();
23
24
 
24
- const tabletPath = id && createHref(routes.tablet, {id}, {nodeId: NodeId, tenantName});
25
+ const tabletPath =
26
+ id && createHref(routes.tablet, {id}, {nodeId: NodeId, tenantName, type: Type});
25
27
 
26
28
  return (
27
29
  <ContentWithPopup
@@ -29,9 +31,7 @@ export const Tablet = ({tablet = {}, tenantName}: TabletProps) => {
29
31
  content={<TabletTooltipContent data={tablet} className={b('popup-content')} />}
30
32
  >
31
33
  <InternalLink to={tabletPath}>
32
- <div className={b({status})}>
33
- <div className={b('type')}>{[getTabletLabel(tablet.Type)]}</div>
34
- </div>
34
+ <TabletIcon className={b({status})} text={getTabletLabel(tablet.Type)} />
35
35
  </InternalLink>
36
36
  </ContentWithPopup>
37
37
  );
@@ -0,0 +1,17 @@
1
+ .tablet-icon {
2
+ display: flex;
3
+ justify-content: center;
4
+
5
+ width: 23px;
6
+ height: 16px;
7
+
8
+ font-size: 10px;
9
+ text-transform: uppercase;
10
+
11
+ border: 1px solid;
12
+ border-radius: 4px;
13
+
14
+ &__type {
15
+ line-height: 14px;
16
+ }
17
+ }
@@ -0,0 +1,18 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import './TabletIcon.scss';
4
+
5
+ interface TabletIconProps {
6
+ text?: string;
7
+ className?: string;
8
+ }
9
+
10
+ const b = cn('tablet-icon');
11
+
12
+ export const TabletIcon = ({text, className}: TabletIconProps) => {
13
+ return (
14
+ <div className={b(null, className)}>
15
+ <div className={b('type')}>{text || 'T'}</div>
16
+ </div>
17
+ );
18
+ };
@@ -15,6 +15,8 @@
15
15
  align-items: center;
16
16
 
17
17
  &__icon {
18
+ display: flex;
19
+
18
20
  margin-right: 3px;
19
21
  }
20
22
  }
@@ -3,7 +3,7 @@ import {useHistory, useLocation} from 'react-router';
3
3
  import {useDispatch} from 'react-redux';
4
4
  import block from 'bem-cn-lite';
5
5
 
6
- import {Breadcrumbs, Icon} from '@gravity-ui/uikit';
6
+ import {Breadcrumbs} from '@gravity-ui/uikit';
7
7
 
8
8
  import {ExternalLinkWithIcon} from '../../components/ExternalLinkWithIcon/ExternalLinkWithIcon';
9
9
 
@@ -91,12 +91,7 @@ function Header({mainPage}: HeaderProps) {
91
91
  }
92
92
  return (
93
93
  <span className={b('breadcrumb')}>
94
- <Icon
95
- width={16}
96
- height={16}
97
- data={icon}
98
- className={b('breadcrumb__icon')}
99
- />
94
+ <div className={b('breadcrumb__icon')}>{icon}</div>
100
95
  {text}
101
96
  </span>
102
97
  );
@@ -1,5 +1,9 @@
1
- import nodesRightIcon from '@gravity-ui/icons/svgs/nodes-right.svg';
2
- import databaseIcon from '@gravity-ui/icons/svgs/database.svg';
1
+ import {
2
+ NodesRight as ClusterIcon,
3
+ Database as DatabaseIcon,
4
+ Cpu as ComputeNodeIcon,
5
+ HardDrive as StorageNodeIcon,
6
+ } from '@gravity-ui/icons';
3
7
 
4
8
  import type {
5
9
  BreadcrumbsOptions,
@@ -15,8 +19,9 @@ import {
15
19
  TENANT_PAGE,
16
20
  TENANT_PAGES_IDS,
17
21
  } from '../../store/reducers/tenant/constants';
22
+ import {TabletIcon} from '../../components/TabletIcon/TabletIcon';
18
23
  import routes, {createHref} from '../../routes';
19
- import {CLUSTER_DEFAULT_TITLE} from '../../utils/constants';
24
+ import {CLUSTER_DEFAULT_TITLE, getTabletLabel} from '../../utils/constants';
20
25
 
21
26
  import {getClusterPath} from '../Cluster/utils';
22
27
  import {TenantTabsGroups, getTenantPath} from '../Tenant/TenantPages';
@@ -29,7 +34,7 @@ const prepareTenantName = (tenantName: string) => {
29
34
  export interface RawBreadcrumbItem {
30
35
  text: string;
31
36
  link?: string;
32
- icon?: SVGIconData;
37
+ icon?: JSX.Element;
33
38
  }
34
39
 
35
40
  const getClusterBreadcrumbs = (
@@ -42,7 +47,7 @@ const getClusterBreadcrumbs = (
42
47
  {
43
48
  text: clusterName || CLUSTER_DEFAULT_TITLE,
44
49
  link: getClusterPath(clusterTab, query),
45
- icon: nodesRightIcon,
50
+ icon: <ClusterIcon />,
46
51
  },
47
52
  ];
48
53
  };
@@ -56,7 +61,7 @@ const getTenantBreadcrumbs = (
56
61
  const text = tenantName ? prepareTenantName(tenantName) : 'Tenant';
57
62
  const link = tenantName ? getTenantPath({...query, name: tenantName}) : undefined;
58
63
 
59
- return [...getClusterBreadcrumbs(options, query), {text, link, icon: databaseIcon}];
64
+ return [...getClusterBreadcrumbs(options, query), {text, link, icon: <DatabaseIcon />}];
60
65
  };
61
66
 
62
67
  const getNodeBreadcrumbs = (options: NodeBreadcrumbsOptions, query = {}): RawBreadcrumbItem[] => {
@@ -81,8 +86,13 @@ const getNodeBreadcrumbs = (options: NodeBreadcrumbsOptions, query = {}): RawBre
81
86
 
82
87
  const text = nodeId ? `Node ${nodeId}` : 'Node';
83
88
  const link = nodeId ? getDefaultNodePath(nodeId, query) : undefined;
89
+ const icon = isStorageNode ? <StorageNodeIcon /> : <ComputeNodeIcon />;
84
90
 
85
- breadcrumbs.push({text, link});
91
+ breadcrumbs.push({
92
+ text,
93
+ link,
94
+ icon,
95
+ });
86
96
 
87
97
  return breadcrumbs;
88
98
  };
@@ -123,12 +133,13 @@ const getTabletBreadcrubms = (
123
133
  options: TabletBreadcrumbsOptions,
124
134
  query = {},
125
135
  ): RawBreadcrumbItem[] => {
126
- const {tabletId} = options;
136
+ const {tabletId, tabletType} = options;
127
137
 
128
138
  const breadcrumbs = getTabletsBreadcrubms(options, query);
129
139
 
130
140
  breadcrumbs.push({
131
141
  text: tabletId || 'Tablet',
142
+ icon: <TabletIcon text={getTabletLabel(tabletType)} />,
132
143
  });
133
144
 
134
145
  return breadcrumbs;
@@ -3,9 +3,11 @@ import cn from 'bem-cn-lite';
3
3
  import {useDispatch} from 'react-redux';
4
4
 
5
5
  import DataTable from '@gravity-ui/react-data-table';
6
+ import {ASCENDING} from '@gravity-ui/react-data-table/build/esm/lib/constants';
6
7
 
7
8
  import type {EPathType} from '../../types/api/schema';
8
9
  import type {ProblemFilterValue} from '../../store/reducers/settings/types';
10
+ import type {NodesSortParams} from '../../store/reducers/nodes/types';
9
11
 
10
12
  import {AccessDenied} from '../../components/Errors/403';
11
13
  import {Illustration} from '../../components/Illustration';
@@ -17,7 +19,13 @@ import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/
17
19
  import {ResponseError} from '../../components/Errors/ResponseError';
18
20
 
19
21
  import {DEFAULT_TABLE_SETTINGS, USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY} from '../../utils/constants';
20
- import {useAutofetcher, useSetting, useTypedSelector} from '../../utils/hooks';
22
+ import {
23
+ useAutofetcher,
24
+ useSetting,
25
+ useTypedSelector,
26
+ useNodesRequestParams,
27
+ useTableSort,
28
+ } from '../../utils/hooks';
21
29
  import {AdditionalNodesInfo, isUnavailableNode, NodesUptimeFilterValues} from '../../utils/nodes';
22
30
 
23
31
  import {
@@ -26,6 +34,8 @@ import {
26
34
  setSearchValue,
27
35
  resetNodesState,
28
36
  getComputeNodes,
37
+ setDataWasNotLoaded,
38
+ setSort,
29
39
  } from '../../store/reducers/nodes/nodes';
30
40
  import {selectFilteredNodes} from '../../store/reducers/nodes/selectors';
31
41
  import {changeFilter, ProblemFilterValues} from '../../store/reducers/settings/settings';
@@ -58,8 +68,16 @@ export const Nodes = ({path, type, additionalNodesInfo = {}}: NodesProps) => {
58
68
  dispatch(resetNodesState());
59
69
  }, [dispatch, path]);
60
70
 
61
- const {wasLoaded, loading, error, nodesUptimeFilter, searchValue, totalNodes} =
62
- useTypedSelector((state) => state.nodes);
71
+ const {
72
+ wasLoaded,
73
+ loading,
74
+ error,
75
+ nodesUptimeFilter,
76
+ searchValue,
77
+ sortOrder = ASCENDING,
78
+ sortValue = 'NodeId',
79
+ totalNodes,
80
+ } = useTypedSelector((state) => state.nodes);
63
81
  const problemFilter = useTypedSelector((state) => state.settings.problemFilter);
64
82
  const {autorefresh} = useTypedSelector((state) => state.schema);
65
83
 
@@ -67,18 +85,39 @@ export const Nodes = ({path, type, additionalNodesInfo = {}}: NodesProps) => {
67
85
 
68
86
  const [useNodesEndpoint] = useSetting(USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY);
69
87
 
70
- const fetchNodes = useCallback(() => {
71
- // For not DB entities we always use /compute endpoint instead of /nodes
72
- // since /nodes can return data only for tenants
73
- if (path && (!useNodesEndpoint || !isDatabaseEntityType(type))) {
74
- dispatch(getComputeNodes({path}));
75
- } else {
76
- dispatch(getNodes({tenant: path}));
77
- }
78
- }, [dispatch, path, type, useNodesEndpoint]);
88
+ const requestParams = useNodesRequestParams({
89
+ filter: searchValue,
90
+ problemFilter,
91
+ nodesUptimeFilter,
92
+ sortOrder,
93
+ sortValue,
94
+ });
95
+
96
+ const fetchNodes = useCallback(
97
+ (isBackground) => {
98
+ if (!isBackground) {
99
+ dispatch(setDataWasNotLoaded());
100
+ }
101
+
102
+ const params = requestParams || {};
103
+
104
+ // For not DB entities we always use /compute endpoint instead of /nodes
105
+ // since /nodes can return data only for tenants
106
+ if (path && (!useNodesEndpoint || !isDatabaseEntityType(type))) {
107
+ dispatch(getComputeNodes({path, ...params}));
108
+ } else {
109
+ dispatch(getNodes({tenant: path, ...params}));
110
+ }
111
+ },
112
+ [dispatch, path, type, useNodesEndpoint, requestParams],
113
+ );
79
114
 
80
115
  useAutofetcher(fetchNodes, [fetchNodes], isClusterNodes ? true : autorefresh);
81
116
 
117
+ const [sort, handleSort] = useTableSort({sortValue, sortOrder}, (sortParams) =>
118
+ dispatch(setSort(sortParams as NodesSortParams)),
119
+ );
120
+
82
121
  const handleSearchQueryChange = (value: string) => {
83
122
  dispatch(setSearchValue(value));
84
123
  };
@@ -132,10 +171,8 @@ export const Nodes = ({path, type, additionalNodesInfo = {}}: NodesProps) => {
132
171
  data={nodes || []}
133
172
  columns={columns}
134
173
  settings={DEFAULT_TABLE_SETTINGS}
135
- initialSortOrder={{
136
- columnId: 'NodeId',
137
- order: DataTable.ASCENDING,
138
- }}
174
+ sortOrder={sort}
175
+ onSort={handleSort}
139
176
  emptyDataMessage={i18n('empty.default')}
140
177
  rowClassName={(row) => b('node', {unavailable: isUnavailableNode(row)})}
141
178
  />
@@ -6,26 +6,42 @@ import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
6
6
  import {TabletsStatistic} from '../../components/TabletsStatistic';
7
7
  import {NodeHostWrapper} from '../../components/NodeHostWrapper/NodeHostWrapper';
8
8
 
9
- import type {NodeAddress} from '../../utils/nodes';
9
+ import {isSortableNodesProperty, type NodeAddress} from '../../utils/nodes';
10
10
  import {formatBytesToGigabyte} from '../../utils/index';
11
11
 
12
12
  import type {NodesPreparedEntity} from '../../store/reducers/nodes/types';
13
13
 
14
+ const NODES_COLUMNS_IDS = {
15
+ NodeId: 'NodeId',
16
+ Host: 'Host',
17
+ DC: 'DC',
18
+ Rack: 'Rack',
19
+ Version: 'Version',
20
+ Uptime: 'Uptime',
21
+ Memory: 'Memory',
22
+ CPU: 'CPU',
23
+ LoadAverage: 'LoadAverage',
24
+ Tablets: 'Tablets',
25
+ };
26
+
14
27
  interface GetNodesColumnsProps {
15
28
  tabletsPath?: string;
16
29
  getNodeRef?: (node?: NodeAddress) => string | null;
17
30
  }
18
31
 
19
- export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps) {
32
+ export function getNodesColumns({
33
+ tabletsPath,
34
+ getNodeRef,
35
+ }: GetNodesColumnsProps): Column<NodesPreparedEntity>[] {
20
36
  const columns: Column<NodesPreparedEntity>[] = [
21
37
  {
22
- name: 'NodeId',
38
+ name: NODES_COLUMNS_IDS.NodeId,
23
39
  header: '#',
24
40
  width: '80px',
25
41
  align: DataTable.RIGHT,
26
42
  },
27
43
  {
28
- name: 'Host',
44
+ name: NODES_COLUMNS_IDS.Host,
29
45
  render: ({row}) => {
30
46
  return <NodeHostWrapper node={row} getNodeRef={getNodeRef} />;
31
47
  },
@@ -33,21 +49,21 @@ export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps)
33
49
  align: DataTable.LEFT,
34
50
  },
35
51
  {
36
- name: 'DataCenter',
52
+ name: NODES_COLUMNS_IDS.DC,
37
53
  header: 'DC',
38
54
  align: DataTable.LEFT,
39
55
  render: ({row}) => (row.DataCenter ? row.DataCenter : '—'),
40
56
  width: '60px',
41
57
  },
42
58
  {
43
- name: 'Rack',
59
+ name: NODES_COLUMNS_IDS.Rack,
44
60
  header: 'Rack',
45
61
  align: DataTable.LEFT,
46
62
  render: ({row}) => (row.Rack ? row.Rack : '—'),
47
63
  width: '80px',
48
64
  },
49
65
  {
50
- name: 'Version',
66
+ name: NODES_COLUMNS_IDS.Version,
51
67
  width: '200px',
52
68
  align: DataTable.LEFT,
53
69
  render: ({row}) => {
@@ -55,14 +71,14 @@ export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps)
55
71
  },
56
72
  },
57
73
  {
58
- name: 'Uptime',
74
+ name: NODES_COLUMNS_IDS.Uptime,
59
75
  header: 'Uptime',
60
76
  sortAccessor: ({StartTime}) => StartTime && -StartTime,
61
77
  align: DataTable.RIGHT,
62
78
  width: '110px',
63
79
  },
64
80
  {
65
- name: 'MemoryUsed',
81
+ name: NODES_COLUMNS_IDS.Memory,
66
82
  header: 'Memory',
67
83
  sortAccessor: ({MemoryUsed = 0}) => Number(MemoryUsed),
68
84
  defaultOrder: DataTable.DESCENDING,
@@ -77,7 +93,7 @@ export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps)
77
93
  width: '120px',
78
94
  },
79
95
  {
80
- name: 'PoolStats',
96
+ name: NODES_COLUMNS_IDS.CPU,
81
97
  header: 'CPU',
82
98
  sortAccessor: ({PoolStats = []}) =>
83
99
  PoolStats.reduce((acc, item) => {
@@ -93,7 +109,7 @@ export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps)
93
109
  width: '120px',
94
110
  },
95
111
  {
96
- name: 'LoadAverage',
112
+ name: NODES_COLUMNS_IDS.LoadAverage,
97
113
  header: 'Load average',
98
114
  sortAccessor: ({LoadAverage = []}) =>
99
115
  LoadAverage.slice(0, 1).reduce((acc, item) => acc + item, 0),
@@ -112,7 +128,7 @@ export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps)
112
128
  width: '140px',
113
129
  },
114
130
  {
115
- name: 'Tablets',
131
+ name: NODES_COLUMNS_IDS.Tablets,
116
132
  width: '430px',
117
133
  render: ({row}) => {
118
134
  return row.Tablets ? (
@@ -129,5 +145,7 @@ export function getNodesColumns({tabletsPath, getNodeRef}: GetNodesColumnsProps)
129
145
  },
130
146
  ];
131
147
 
132
- return columns;
148
+ return columns.map((column) => {
149
+ return {...column, sortable: isSortableNodesProperty(column.name)};
150
+ });
133
151
  }
@@ -13,6 +13,7 @@ import {DEVELOPER_UI_TITLE} from '../../utils/constants';
13
13
  import '../../services/api';
14
14
  import {parseQuery} from '../../routes';
15
15
 
16
+ import type {EType} from '../../types/api/tablet';
16
17
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
17
18
  import {ResponseError} from '../../components/Errors/ResponseError';
18
19
  import {Tag} from '../../components/Tag';
@@ -48,10 +49,14 @@ export const Tablet = () => {
48
49
  error,
49
50
  } = useTypedSelector((state) => state.tablet);
50
51
 
51
- const {nodeId: queryNodeId, tenantName: queryTenantName} = parseQuery(location);
52
-
52
+ const {
53
+ nodeId: queryNodeId,
54
+ tenantName: queryTenantName,
55
+ type: queryTabletType,
56
+ } = parseQuery(location);
53
57
  const nodeId = tablet.NodeId?.toString() || queryNodeId?.toString();
54
58
  const tenantName = tenantPath || queryTenantName?.toString();
59
+ const type = tablet.Type || (queryTabletType?.toString() as EType | undefined);
55
60
 
56
61
  // NOTE: should be reviewed when migrating to React 18
57
62
  useEffect(() => {
@@ -79,9 +84,10 @@ export const Tablet = () => {
79
84
  nodeIds: nodeId ? [nodeId] : [],
80
85
  tenantName,
81
86
  tabletId: id,
87
+ tabletType: type,
82
88
  }),
83
89
  );
84
- }, [dispatch, tenantName, id, nodeId]);
90
+ }, [dispatch, tenantName, id, nodeId, type]);
85
91
 
86
92
  const renderExternalLinks = (link: {name: string; path: string}, index: number) => {
87
93
  return (
@@ -27,6 +27,9 @@ import {
27
27
  isPathTypeWithTopic,
28
28
  } from '../../utils/schema';
29
29
 
30
+ import {ExternalTableInfo} from '../../Info/ExternalTable/ExternalTable';
31
+ import {ExternalDataSourceInfo} from '../../Info/ExternalDataSource/ExternalDataSource';
32
+
30
33
  import {TopicInfo} from './TopicInfo';
31
34
  import {ChangefeedInfo} from './ChangefeedInfo';
32
35
  import {TableInfo} from './TableInfo';
@@ -124,8 +127,8 @@ function Overview({type, tenantName}: OverviewProps) {
124
127
  <ChangefeedInfo data={data} topic={additionalData?.[0]} />
125
128
  ),
126
129
  [EPathType.EPathTypePersQueueGroup]: () => <TopicInfo data={data} />,
127
- [EPathType.EPathTypeExternalTable]: undefined,
128
- [EPathType.EPathTypeExternalDataSource]: undefined,
130
+ [EPathType.EPathTypeExternalTable]: () => <ExternalTableInfo data={data} />,
131
+ [EPathType.EPathTypeExternalDataSource]: () => <ExternalDataSourceInfo data={data} />,
129
132
  };
130
133
 
131
134
  return (
@@ -0,0 +1,5 @@
1
+ .ydb-external-data-source-info {
2
+ &__location {
3
+ max-width: var(--tenant-object-info-max-value-width);
4
+ }
5
+ }