ydb-embedded-ui 1.13.2 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/assets/icons/flask.svg +3 -0
  3. package/dist/components/InfoViewer/formatters/common.ts +15 -0
  4. package/dist/components/InfoViewer/formatters/index.ts +2 -0
  5. package/dist/components/InfoViewer/formatters/schema.ts +43 -0
  6. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +44 -0
  7. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +34 -0
  8. package/dist/components/{IndexInfoViewer/IndexInfoViewer.tsx → InfoViewer/schemaInfo/TableIndexInfo.tsx} +7 -18
  9. package/dist/components/InfoViewer/schemaInfo/index.ts +3 -0
  10. package/dist/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +44 -0
  11. package/dist/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +35 -0
  12. package/dist/components/InfoViewer/schemaOverview/index.ts +2 -0
  13. package/dist/components/QueryResultTable/Cell/Cell.tsx +33 -0
  14. package/dist/components/QueryResultTable/Cell/index.ts +1 -0
  15. package/dist/components/QueryResultTable/QueryResultTable.scss +11 -0
  16. package/dist/components/QueryResultTable/QueryResultTable.tsx +115 -0
  17. package/dist/components/QueryResultTable/i18n/en.json +3 -0
  18. package/dist/components/QueryResultTable/i18n/index.ts +11 -0
  19. package/dist/components/QueryResultTable/i18n/ru.json +3 -0
  20. package/dist/components/QueryResultTable/index.ts +1 -0
  21. package/dist/containers/App/App.scss +1 -0
  22. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +39 -14
  23. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +18 -7
  24. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +4 -3
  25. package/dist/containers/Storage/Vdisk/__tests__/colors.tsx +7 -7
  26. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -2
  27. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +8 -3
  29. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +1 -1
  30. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +1 -1
  31. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +36 -10
  32. package/dist/containers/Tenant/Preview/Preview.js +15 -57
  33. package/dist/containers/Tenant/Preview/Preview.scss +4 -8
  34. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +12 -41
  35. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -4
  36. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.scss +1 -2
  37. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
  38. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -1
  39. package/dist/containers/Tenant/utils/schema.ts +3 -0
  40. package/dist/containers/Tenant/utils/schemaActions.ts +1 -2
  41. package/dist/containers/Tenants/Tenants.js +12 -2
  42. package/dist/containers/UserSettings/UserSettings.tsx +26 -3
  43. package/dist/services/api.d.ts +19 -2
  44. package/dist/services/api.js +2 -2
  45. package/dist/setupTests.js +4 -0
  46. package/dist/store/reducers/executeQuery.js +4 -9
  47. package/dist/store/reducers/{preview.js → preview.ts} +22 -18
  48. package/dist/store/reducers/settings.js +3 -1
  49. package/dist/store/utils.ts +88 -0
  50. package/dist/types/api/query.ts +147 -0
  51. package/dist/types/api/schema.ts +235 -2
  52. package/dist/types/index.ts +33 -0
  53. package/dist/types/store/query.ts +9 -0
  54. package/dist/utils/{constants.js → constants.ts} +11 -6
  55. package/dist/utils/index.js +0 -24
  56. package/dist/utils/query.test.ts +189 -0
  57. package/dist/utils/query.ts +156 -0
  58. package/dist/utils/tests/providers.tsx +29 -0
  59. package/package.json +2 -2
  60. package/dist/store/utils.js +0 -51
@@ -1,7 +1,12 @@
1
1
  import React from 'react';
2
- import InternalLink from '../../../components/InternalLink/InternalLink';
2
+ import {useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
+ import {INVERTED_DISKS_KEY} from '../../../utils/constants';
6
+ import {getSettingValue} from '../../../store/reducers/settings';
7
+
8
+ import InternalLink from '../../../components/InternalLink/InternalLink';
9
+
5
10
  import './DiskStateProgressBar.scss';
6
11
 
7
12
  const b = cn('storage-disk-progress-bar');
@@ -26,11 +31,16 @@ function DiskStateProgressBar({
26
31
  severity,
27
32
  href,
28
33
  }: DiskStateProgressBarProps) {
34
+ const inverted = useSelector((state) => getSettingValue(state, INVERTED_DISKS_KEY));
35
+
29
36
  const renderAllocatedPercent = () => {
30
37
  return (
31
38
  diskAllocatedPercent >= 0 && (
32
39
  <React.Fragment>
33
- <div className={b('filled')} style={{width: `${diskAllocatedPercent}%`}} />
40
+ <div
41
+ className={b('filled')}
42
+ style={{width: `${inverted ? 100 - diskAllocatedPercent : diskAllocatedPercent}%`}}
43
+ />
34
44
  <div className={b('filled-title')}>
35
45
  {`${Math.round(diskAllocatedPercent)}%`}
36
46
  </div>
@@ -39,13 +49,14 @@ function DiskStateProgressBar({
39
49
  );
40
50
  };
41
51
 
52
+ const mods: Record<string, boolean | undefined> = {inverted};
53
+ if (severity !== undefined && severity in diskProgressColors) {
54
+ mods[diskProgressColors[severity].toLocaleLowerCase()] = true;
55
+ }
56
+
42
57
  return (
43
58
  <div
44
- className={
45
- severity !== undefined
46
- ? b({[diskProgressColors[severity].toLowerCase()]: true})
47
- : undefined
48
- }
59
+ className={b(mods)}
49
60
  role="meter"
50
61
  aria-label="Disk allocated space"
51
62
  aria-valuemin={0}
@@ -1,13 +1,14 @@
1
- import {render} from '@testing-library/react'
2
1
  import {MemoryRouter} from 'react-router-dom';
3
2
 
3
+ import {renderWithStore} from '../../../../utils/tests/providers';
4
+
4
5
  import {TPDiskState} from '../../../../types/api/storage'
5
6
 
6
7
  import PDisk from '../Pdisk'
7
8
 
8
9
  describe('PDisk state', () => {
9
10
  it('Should determine severity based on State', () => {
10
- const {getAllByRole} = render(
11
+ const {getAllByRole} = renderWithStore(
11
12
  <MemoryRouter>
12
13
  <PDisk
13
14
  NodeId={1}
@@ -26,7 +27,7 @@ describe('PDisk state', () => {
26
27
  });
27
28
 
28
29
  it('Should display as unavailabe when no State is provided', () => {
29
- const {getByRole} = render(
30
+ const {getByRole} = renderWithStore(
30
31
  <MemoryRouter>
31
32
  <PDisk NodeId={1} />
32
33
  </MemoryRouter>
@@ -1,10 +1,10 @@
1
- import {render} from '@testing-library/react'
1
+ import {renderWithStore} from '../../../../utils/tests/providers';
2
2
 
3
3
  import VDisk from '../Vdisk'
4
4
 
5
5
  describe('VDisk state', () => {
6
6
  it('Should determine severity based on the highest value among VDiskState, DiskSpace and FrontQueues', () => {
7
- const {getAllByRole} = render(
7
+ const {getAllByRole} = renderWithStore(
8
8
  <>
9
9
  <VDisk
10
10
  VDiskId={{Domain: 1}}
@@ -35,7 +35,7 @@ describe('VDisk state', () => {
35
35
  });
36
36
 
37
37
  it('Should not pick the highest severity based on FrontQueues value', () => {
38
- const {getAllByRole} = render(
38
+ const {getAllByRole} = renderWithStore(
39
39
  <>
40
40
  <VDisk
41
41
  VDiskId={{Domain: 1}}
@@ -59,7 +59,7 @@ describe('VDisk state', () => {
59
59
  });
60
60
 
61
61
  it('Should display as unavailable when no VDiskState is provided', () => {
62
- const {getAllByRole} = render(
62
+ const {getAllByRole} = renderWithStore(
63
63
  <>
64
64
  <VDisk VDiskId={{Domain: 1}} />
65
65
  <VDisk VDiskId={{Domain: 2}} VDiskState="OK" />
@@ -86,7 +86,7 @@ describe('VDisk state', () => {
86
86
  });
87
87
 
88
88
  it('Should display replicating VDisks in OK state with a distinct color', () => {
89
- const {getAllByRole} = render(
89
+ const {getAllByRole} = renderWithStore(
90
90
  <>
91
91
  <VDisk
92
92
  VDiskId={{Domain: 1}}
@@ -108,7 +108,7 @@ describe('VDisk state', () => {
108
108
  });
109
109
 
110
110
  it('Should display replicating VDisks in a not-OK state with a regular color', () => {
111
- const {getAllByRole} = render(
111
+ const {getAllByRole} = renderWithStore(
112
112
  <>
113
113
  <VDisk
114
114
  VDiskId={{Domain: 1}}
@@ -130,7 +130,7 @@ describe('VDisk state', () => {
130
130
  });
131
131
 
132
132
  it('Should always display donor VDisks with a regular color', () => {
133
- const {getAllByRole} = render(
133
+ const {getAllByRole} = renderWithStore(
134
134
  <>
135
135
  <VDisk
136
136
  VDiskId={{Domain: 1}}
@@ -20,7 +20,7 @@ type Page = {
20
20
 
21
21
  const overview = {
22
22
  id: GeneralPagesIds.overview,
23
- title: 'Overview',
23
+ title: 'Info',
24
24
  };
25
25
 
26
26
  const topQueries = {
@@ -81,6 +81,8 @@ export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, descri
81
81
 
82
82
  export const DIR_PAGES = [overview, topShards, describe];
83
83
 
84
+ export const TOPIC_PAGES = [overview, describe];
85
+
84
86
  // verbose mapping to guarantee correct tabs for new path types
85
87
  // TS will error when a new type is added but not mapped here
86
88
  const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
@@ -95,7 +97,9 @@ const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
95
97
 
96
98
  [EPathType.EPathTypeDir]: DIR_PAGES,
97
99
  [EPathType.EPathTypeTableIndex]: DIR_PAGES,
98
- [EPathType.EPathTypeCdcStream]: DIR_PAGES,
100
+
101
+ [EPathType.EPathTypeCdcStream]: TOPIC_PAGES,
102
+ [EPathType.EPathTypePersQueueGroup]: TOPIC_PAGES,
99
103
  };
100
104
 
101
105
  export const getPagesByType = (type?: EPathType) =>
@@ -8,7 +8,7 @@ import Icon from '../../../../components/Icon/Icon';
8
8
 
9
9
  import {AutoFetcher} from '../../../../utils/autofetcher';
10
10
  import {getHotKeys, setHotKeysOptions} from '../../../../store/reducers/hotKeys';
11
- import {prepareQueryError} from '../../../../utils';
11
+ import {prepareQueryError} from '../../../../utils/query';
12
12
 
13
13
  import {isColumnEntityType, isTableType} from '../../utils/schema';
14
14
 
@@ -6,7 +6,11 @@ import {Loader} from '@yandex-cloud/uikit';
6
6
 
7
7
  //@ts-ignore
8
8
  import SchemaInfoViewer from '../../Schema/SchemaInfoViewer/SchemaInfoViewer';
9
- import {IndexInfoViewer} from '../../../../components/IndexInfoViewer/IndexInfoViewer';
9
+ import {
10
+ CDCStreamInfo,
11
+ TableIndexInfo,
12
+ PersQueueGroupInfo,
13
+ } from '../../../../components/InfoViewer/schemaInfo';
10
14
 
11
15
  import {EPathType} from '../../../../types/api/schema';
12
16
  import {isColumnEntityType, isTableType} from '../../utils/schema';
@@ -121,11 +125,12 @@ function Overview(props: OverviewProps) {
121
125
  [EPathType.EPathTypeDir]: undefined,
122
126
  [EPathType.EPathTypeTable]: undefined,
123
127
  [EPathType.EPathTypeSubDomain]: undefined,
124
- [EPathType.EPathTypeTableIndex]: () => <IndexInfoViewer data={schemaData} />,
128
+ [EPathType.EPathTypeTableIndex]: () => <TableIndexInfo data={schemaData} />,
125
129
  [EPathType.EPathTypeExtSubDomain]: undefined,
126
130
  [EPathType.EPathTypeColumnStore]: undefined,
127
131
  [EPathType.EPathTypeColumnTable]: undefined,
128
- [EPathType.EPathTypeCdcStream]: undefined,
132
+ [EPathType.EPathTypeCdcStream]: () => <CDCStreamInfo data={schemaData} />,
133
+ [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupInfo data={schemaData} />,
129
134
  };
130
135
 
131
136
  return (props.type && pathTypeToComponent[props.type]?.()) || (
@@ -13,7 +13,7 @@ import {isColumnEntityType} from '../../utils/schema';
13
13
 
14
14
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
15
15
  import {TenantGeneralTabsIds} from '../../TenantPages';
16
- import {prepareQueryError} from '../../../../utils';
16
+ import {prepareQueryError} from '../../../../utils/query';
17
17
 
18
18
  import './TopQueries.scss';
19
19
 
@@ -13,7 +13,7 @@ import {AutoFetcher} from '../../../../utils/autofetcher';
13
13
  import HistoryContext from '../../../../contexts/HistoryContext';
14
14
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
15
15
  import {isColumnEntityType} from '../../utils/schema';
16
- import {prepareQueryError} from '../../../../utils';
16
+ import {prepareQueryError} from '../../../../utils/query';
17
17
  import {i18n} from '../../../../utils/i18n';
18
18
 
19
19
  import './TopShards.scss';
@@ -1,4 +1,4 @@
1
- import React, {useEffect, useReducer} from 'react';
1
+ import React, {ReactNode, useEffect, useReducer} from 'react';
2
2
  import {useDispatch, useSelector} from 'react-redux';
3
3
  import {Link} from 'react-router-dom';
4
4
  import cn from 'bem-cn-lite';
@@ -14,9 +14,13 @@ import Acl from '../Acl/Acl';
14
14
  import SchemaViewer from '../Schema/SchemaViewer/SchemaViewer';
15
15
  import CopyToClipboard from '../../../components/CopyToClipboard/CopyToClipboard';
16
16
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
17
+ import {
18
+ CDCStreamOverview,
19
+ PersQueueGroupOverview,
20
+ } from '../../../components/InfoViewer/schemaOverview';
17
21
  import Icon from '../../../components/Icon/Icon';
18
22
 
19
- import type {EPathSubType, EPathType} from '../../../types/api/schema';
23
+ import {EPathSubType, EPathType, TDirEntry} from '../../../types/api/schema';
20
24
  import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
21
25
 
22
26
  import {
@@ -100,8 +104,8 @@ function ObjectSummary(props: ObjectSummaryProps) {
100
104
  });
101
105
 
102
106
  const {name: tenantName, info: infoTab} = queryParams;
103
- const pathData = _.get(data[tenantName as string], 'PathDescription.Self');
104
- const currentSchemaData = _.get(data[currentSchemaPath], 'PathDescription.Self');
107
+ const pathData: TDirEntry | undefined = _.get(data[tenantName as string], 'PathDescription.Self');
108
+ const currentSchemaData: TDirEntry | undefined = _.get(data[currentSchemaPath], 'PathDescription.Self');
105
109
 
106
110
  const tableSchema =
107
111
  currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
@@ -151,14 +155,36 @@ function ObjectSummary(props: ObjectSummaryProps) {
151
155
  };
152
156
 
153
157
  const renderObjectOverview = () => {
154
- const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
155
- let createTime = '';
156
- if (startTimeInMilliseconds) {
157
- createTime = new Date(startTimeInMilliseconds).toUTCString();
158
+ // verbose mapping to guarantee a correct render for new path types
159
+ // TS will error when a new type is added but not mapped here
160
+ const pathTypeToComponent: Record<EPathType, (() => ReactNode) | undefined> = {
161
+ [EPathType.EPathTypeInvalid]: undefined,
162
+ [EPathType.EPathTypeDir]: undefined,
163
+ [EPathType.EPathTypeTable]: undefined,
164
+ [EPathType.EPathTypeSubDomain]: undefined,
165
+ [EPathType.EPathTypeTableIndex]: undefined,
166
+ [EPathType.EPathTypeExtSubDomain]: undefined,
167
+ [EPathType.EPathTypeColumnStore]: undefined,
168
+ [EPathType.EPathTypeColumnTable]: undefined,
169
+ [EPathType.EPathTypeCdcStream]: () => <CDCStreamOverview data={data[currentSchemaPath]} />,
170
+ [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupOverview data={data[currentSchemaPath]} />,
171
+ };
172
+
173
+ let component = currentSchemaData?.PathType && pathTypeToComponent[currentSchemaData.PathType]?.();
174
+
175
+ if (!component) {
176
+ const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
177
+ let createTime = '';
178
+ if (startTimeInMilliseconds) {
179
+ createTime = new Date(startTimeInMilliseconds).toUTCString();
180
+ }
181
+
182
+ component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;
158
183
  }
184
+
159
185
  return (
160
186
  <div className={b('overview-wrapper')}>
161
- <InfoViewer info={[{label: 'Create time', value: createTime}]} />
187
+ {component}
162
188
  </div>
163
189
  );
164
190
  };
@@ -203,7 +229,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
203
229
  rootPath={tenantName as string}
204
230
  // for the root pathData.Name contains the same string as tenantName,
205
231
  // but without the leading slash
206
- rootName={pathData?.Name || tenantName}
232
+ rootName={pathData.Name || String(tenantName)}
207
233
  rootType={pathData.PathType}
208
234
  currentPath={currentSchemaPath}
209
235
  />
@@ -3,30 +3,22 @@ import PropTypes from 'prop-types';
3
3
  import {connect} from 'react-redux';
4
4
  import cn from 'bem-cn-lite';
5
5
 
6
- import DataTable from '@yandex-cloud/react-data-table';
7
6
  import {Loader, Button} from '@yandex-cloud/uikit';
8
7
 
9
8
  import Icon from '../../../components/Icon/Icon';
10
9
  import Fullscreen from '../../../components/Fullscreen/Fullscreen';
10
+ import {QueryResultTable} from '../../../components/QueryResultTable';
11
11
 
12
12
  import {sendQuery, setQueryOptions} from '../../../store/reducers/preview';
13
- import {showTooltip, hideTooltip} from '../../../store/reducers/tooltip';
14
- import {prepareQueryError, prepareQueryResponse} from '../../../utils/index';
15
- import {isNumeric} from '../../../utils/utils';
13
+ import {prepareQueryError} from '../../../utils/query';
16
14
 
17
15
  import {isTableType} from '../utils/schema';
18
16
  import {AutoFetcher} from '../../../utils/autofetcher';
19
17
  import EnableFullscreenButton from '../../../components/EnableFullscreenButton/EnableFullscreenButton';
20
- import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
21
18
  import {setShowPreview} from '../../../store/reducers/schema';
22
19
 
23
20
  import './Preview.scss';
24
21
 
25
- const TABLE_SETTINGS = {
26
- ...DEFAULT_TABLE_SETTINGS,
27
- stripedRows: true,
28
- };
29
-
30
22
  const b = cn('kv-preview');
31
23
 
32
24
  class Preview extends React.Component {
@@ -38,8 +30,6 @@ class Preview extends React.Component {
38
30
  data: PropTypes.array,
39
31
  loading: PropTypes.bool,
40
32
  type: PropTypes.string,
41
- showTooltip: PropTypes.func,
42
- hideTooltip: PropTypes.func,
43
33
  partCount: PropTypes.string,
44
34
  };
45
35
 
@@ -55,11 +45,10 @@ class Preview extends React.Component {
55
45
  }
56
46
 
57
47
  componentDidUpdate(prevProps) {
58
- const {table, hideTooltip, autorefresh, setQueryOptions} = this.props;
48
+ const {table, autorefresh, setQueryOptions} = this.props;
59
49
 
60
50
  if (prevProps.table !== table) {
61
51
  this.sendQueryForPreview();
62
- hideTooltip();
63
52
  setQueryOptions({
64
53
  wasLoaded: false,
65
54
  data: undefined,
@@ -121,41 +110,8 @@ class Preview extends React.Component {
121
110
  );
122
111
  };
123
112
 
124
- renderTable = () => {
125
- const {data, showTooltip} = this.props;
126
-
127
- let columns = [];
128
- if (data && data.length > 0) {
129
- columns = Object.keys(data[0]).map((key) => ({
130
- name: key,
131
- align: isNumeric(data[0][key]) ? DataTable.RIGHT : DataTable.LEFT,
132
- sortAccessor: (row) => isNumeric(row[key]) ? Number(row[key]) : row[key],
133
- render: ({value}) => {
134
- return (
135
- <span
136
- className={b('cell')}
137
- onClick={(e) => showTooltip(e.target, value, 'cell')}
138
- >
139
- {value}
140
- </span>
141
- );
142
- },
143
- }));
144
- }
145
-
146
- const preparedData = prepareQueryResponse(data);
147
-
148
- return <DataTable columns={columns} data={preparedData} settings={TABLE_SETTINGS} />;
149
- };
150
-
151
113
  render() {
152
- const {error, loading, data = [], type, wasLoaded, isFullscreen} = this.props;
153
-
154
- let message;
155
-
156
- if (!isTableType(type)) {
157
- message = <div className={b('message-container')}>Not available</div>;
158
- }
114
+ const {error, loading, data, type, wasLoaded, isFullscreen} = this.props;
159
115
 
160
116
  if (loading && !wasLoaded) {
161
117
  return (
@@ -165,15 +121,19 @@ class Preview extends React.Component {
165
121
  );
166
122
  }
167
123
 
168
- if (error) {
169
- message = <div className={b('message-container')}>{prepareQueryError(error)}</div>;
170
- }
124
+ let message;
171
125
 
172
- if (!loading && data.length === 0) {
173
- message = <div className={b('message-container')}>Table is empty</div>;
126
+ if (!isTableType(type)) {
127
+ message = <div className={b('message-container')}>Not available</div>;
128
+ } else if (error) {
129
+ message = <div className={b('message-container')}>{prepareQueryError(error)}</div>;
174
130
  }
175
131
 
176
- const content = message ?? <div className={b('result')}>{this.renderTable()}</div>;
132
+ const content = message ?? (
133
+ <div className={b('result')}>
134
+ <QueryResultTable data={data.result} columns={data.columns} />
135
+ </div>
136
+ );
177
137
 
178
138
  return (
179
139
  <div className={b()}>
@@ -185,7 +145,7 @@ class Preview extends React.Component {
185
145
  }
186
146
 
187
147
  const mapStateToProps = (state) => {
188
- const {data = [], loading, error, wasLoaded} = state.preview;
148
+ const {data = {}, loading, error, wasLoaded} = state.preview;
189
149
  const {autorefresh, currentSchemaPath} = state.schema;
190
150
 
191
151
  return {
@@ -201,8 +161,6 @@ const mapStateToProps = (state) => {
201
161
 
202
162
  const mapDispatchToProps = {
203
163
  sendQuery,
204
- showTooltip,
205
- hideTooltip,
206
164
  setQueryOptions,
207
165
  setShowPreview,
208
166
  };
@@ -16,8 +16,8 @@
16
16
  justify-content: space-between;
17
17
  align-items: center;
18
18
 
19
- height: 40px;
20
- padding: 0 19px;
19
+ height: 53px;
20
+ padding: 0 20px;
21
21
 
22
22
  border-bottom: 1px solid var(--yc-color-line-generic);
23
23
  background-color: var(--yc-color-base-background);
@@ -38,7 +38,7 @@
38
38
  gap: 5px;
39
39
  }
40
40
  &__message-container {
41
- padding: 15px 19px;
41
+ padding: 15px 20px;
42
42
  }
43
43
 
44
44
  &__loader-container {
@@ -49,14 +49,10 @@
49
49
  height: 100%;
50
50
  }
51
51
 
52
- &__cell {
53
- @include cell-container;
54
- }
55
-
56
52
  &__result {
57
53
  overflow: auto;
58
54
 
59
55
  height: calc(100% - 40px);
60
- padding: 0 19px;
56
+ padding: 0 10px;
61
57
  }
62
58
  }
@@ -4,9 +4,10 @@ import {connect} from 'react-redux';
4
4
  import cn from 'bem-cn-lite';
5
5
  import _ from 'lodash';
6
6
  import MonacoEditor from 'react-monaco-editor';
7
- import DataTable from '@yandex-cloud/react-data-table';
8
7
  import {Button, DropdownMenu} from '@yandex-cloud/uikit';
8
+
9
9
  import SplitPane from '../../../components/SplitPane';
10
+ import {QueryResultTable} from '../../../components/QueryResultTable';
10
11
 
11
12
  import SaveQuery from './SaveQuery/SaveQuery';
12
13
  import SavedQueries from './SavedQueries/SavedQueries';
@@ -26,16 +27,13 @@ import {
26
27
  setMonacoHotKey,
27
28
  } from '../../../store/reducers/executeQuery';
28
29
  import {getExplainQuery, getExplainQueryAst} from '../../../store/reducers/explainQuery';
29
- import {showTooltip} from '../../../store/reducers/tooltip';
30
30
  import {getSettingValue, setSettingValue} from '../../../store/reducers/settings';
31
31
  import {
32
32
  DEFAULT_IS_QUERY_RESULT_COLLAPSED,
33
33
  DEFAULT_SIZE_RESULT_PANE_KEY,
34
- DEFAULT_TABLE_SETTINGS,
35
34
  SAVED_QUERIES_KEY,
36
35
  QUERY_INITIAL_RUN_ACTION_KEY,
37
36
  } from '../../../utils/constants';
38
- import {prepareQueryResponse} from '../../../utils/index';
39
37
 
40
38
  import {parseJson} from '../../../utils/utils';
41
39
 
@@ -55,11 +53,9 @@ export const RUN_ACTIONS = [
55
53
  ];
56
54
 
57
55
  const TABLE_SETTINGS = {
58
- ...DEFAULT_TABLE_SETTINGS,
59
56
  sortable: false,
60
57
  dynamicItemSizeGetter: () => 40,
61
58
  dynamicRenderType: 'variable',
62
- stripedRows: true,
63
59
  };
64
60
 
65
61
  const EDITOR_OPTIONS = {
@@ -84,7 +80,6 @@ const propTypes = {
84
80
  response: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
85
81
  executeQuery: PropTypes.object,
86
82
  explainQuery: PropTypes.object,
87
- showTooltip: PropTypes.func,
88
83
  setMonacoHotKey: PropTypes.func,
89
84
  theme: PropTypes.string,
90
85
  type: PropTypes.string,
@@ -306,40 +301,17 @@ function QueryEditor(props) {
306
301
  const renderExecuteQuery = () => {
307
302
  const {
308
303
  executeQuery: {data, error, stats},
309
- showTooltip,
310
304
  } = props;
311
305
 
312
306
  let content;
313
307
  if (data) {
314
- let columns = [];
315
- if (data.length > 0) {
316
- columns = Object.keys(data[0]).map((key) => ({
317
- name: key,
318
- render: ({value}) => {
319
- return (
320
- <span
321
- className={b('cell')}
322
- onClick={(e) => showTooltip(e.target, value, 'cell')}
323
- >
324
- {value}
325
- </span>
326
- );
327
- },
328
- }));
329
- const preparedData = prepareQueryResponse(data);
330
-
331
- content = columns.length ? (
332
- <DataTable
333
- columns={columns}
334
- data={preparedData}
335
- settings={TABLE_SETTINGS}
336
- theme="yandex-cloud"
337
- rowKey={(_, index) => index}
338
- />
339
- ) : (
340
- <div>{data}</div>
341
- );
342
- }
308
+ content = (
309
+ <QueryResultTable
310
+ data={data.result}
311
+ columns={data.columns}
312
+ settings={TABLE_SETTINGS}
313
+ />
314
+ );
343
315
  }
344
316
  const textResults = getPreparedResult();
345
317
  const disabled = !textResults.length || resultType !== RESULT_TYPES.EXECUTE;
@@ -469,12 +441,12 @@ function QueryEditor(props) {
469
441
  const columnDivider = '\t';
470
442
  const rowDivider = '\n';
471
443
 
472
- if (!data?.length) {
444
+ if (!data?.result?.length) {
473
445
  return '';
474
446
  }
475
447
 
476
- const columnHeaders = Object.keys(data[0]);
477
- const rows = [columnHeaders].concat(data);
448
+ const columnHeaders = Object.keys(data.result[0]);
449
+ const rows = [columnHeaders].concat(data.result);
478
450
 
479
451
  return rows
480
452
  .map((item) => {
@@ -675,7 +647,6 @@ const mapDispatchToProps = {
675
647
  saveQueryToHistory,
676
648
  goToPreviousQuery,
677
649
  goToNextQuery,
678
- showTooltip,
679
650
  getExplainQuery,
680
651
  getExplainQueryAst,
681
652
  setSettingValue,
@@ -94,10 +94,6 @@
94
94
  color: var(--yc-color-text-secondary);
95
95
  }
96
96
 
97
- &__cell {
98
- @include cell-container;
99
- }
100
-
101
97
  &__select-query-action {
102
98
  margin-left: 2px;
103
99
  }
@@ -7,8 +7,7 @@
7
7
  flex-grow: 1;
8
8
  flex-direction: column;
9
9
 
10
- margin-top: 20px;
11
- padding: 0px 8px;
10
+ padding: 0px 10px;
12
11
  }
13
12
  &__controls {
14
13
  position: sticky;
@@ -7,8 +7,8 @@
7
7
  flex-grow: 1;
8
8
  flex-direction: column;
9
9
 
10
- margin-top: 20px;
11
- padding: 0px 8px;
10
+ padding: 0px 10px;
11
+
12
12
  @include query-data-table();
13
13
  & .data-table__table-wrapper {
14
14
  padding-bottom: 0;
@@ -14,7 +14,7 @@ import {getActions} from '../../utils/schemaActions';
14
14
  interface SchemaTreeProps {
15
15
  rootPath: string;
16
16
  rootName: string;
17
- rootType: EPathType;
17
+ rootType?: EPathType;
18
18
  currentPath: string;
19
19
  }
20
20
 
@@ -29,6 +29,7 @@ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined>
29
29
  [EPathType.EPathTypeColumnTable]: 'column_table',
30
30
 
31
31
  [EPathType.EPathTypeCdcStream]: 'topic',
32
+ [EPathType.EPathTypePersQueueGroup]: 'topic',
32
33
  };
33
34
 
34
35
  export const mapPathTypeToNavigationTreeType = (
@@ -51,6 +52,7 @@ const pathTypeToIsTable: Record<EPathType, boolean> = {
51
52
  [EPathType.EPathTypeExtSubDomain]: false,
52
53
  [EPathType.EPathTypeColumnStore]: false,
53
54
  [EPathType.EPathTypeCdcStream]: false,
55
+ [EPathType.EPathTypePersQueueGroup]: false,
54
56
  };
55
57
 
56
58
  export const isTableType = (pathType?: EPathType) =>
@@ -82,6 +84,7 @@ const pathTypeToIsColumn: Record<EPathType, boolean> = {
82
84
  [EPathType.EPathTypeTableIndex]: false,
83
85
  [EPathType.EPathTypeExtSubDomain]: false,
84
86
  [EPathType.EPathTypeCdcStream]: false,
87
+ [EPathType.EPathTypePersQueueGroup]: false,
85
88
  };
86
89
 
87
90
  export const isColumnEntityType = (type?: EPathType) =>