ydb-embedded-ui 4.21.1 → 4.23.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +27 -0
- package/dist/components/ProgressViewer/ProgressViewer.tsx +1 -1
- package/dist/components/VirtualTable/TableChunk.tsx +2 -1
- package/dist/components/VirtualTable/VirtualTable.tsx +1 -1
- package/dist/containers/Cluster/Cluster.tsx +2 -0
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +14 -5
- package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +104 -24
- package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx +1 -1
- package/dist/containers/Cluster/i18n/en.json +16 -0
- package/dist/containers/Cluster/i18n/index.ts +11 -0
- package/dist/containers/Cluster/i18n/ru.json +16 -0
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +4 -1
- package/dist/containers/Nodes/getNodesColumns.tsx +57 -13
- package/dist/containers/Tenant/Diagnostics/Network/Network.js +5 -10
- package/dist/containers/Tenant/Diagnostics/Network/utils.ts +6 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx +18 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByLoad.tsx +18 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx +17 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx +20 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx +18 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverviewTableLayout.tsx +2 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +19 -2
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +8 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/getSectionTitle.tsx +28 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +17 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +17 -1
- package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss +13 -5
- package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +72 -18
- package/dist/containers/Tenant/Query/ExplainResult/ExplainResult.js +2 -1
- package/dist/containers/Tenant/Query/ExplainResult/utils.ts +6 -0
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +11 -24
- package/dist/containers/Tenant/Query/utils/getPreparedResult.ts +4 -5
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +19 -11
- package/dist/containers/UserSettings/i18n/en.json +3 -0
- package/dist/containers/UserSettings/i18n/ru.json +3 -0
- package/dist/containers/UserSettings/settings.ts +7 -0
- package/dist/store/reducers/cluster/__test__/parseGroupsStatsQueryResponse.test.ts +121 -0
- package/dist/store/reducers/cluster/cluster.ts +46 -2
- package/dist/store/reducers/cluster/types.ts +29 -4
- package/dist/store/reducers/cluster/utils.ts +88 -0
- package/dist/store/reducers/executeQuery.ts +4 -3
- package/dist/store/reducers/nodes/types.ts +11 -1
- package/dist/store/reducers/nodes/utils.ts +6 -0
- package/dist/store/reducers/settings/settings.ts +2 -0
- package/dist/types/api/cluster.ts +3 -0
- package/dist/types/api/netInfo.ts +1 -1
- package/dist/types/api/nodes.ts +24 -0
- package/dist/types/api/query.ts +23 -8
- package/dist/types/store/query.ts +6 -0
- package/dist/utils/constants.ts +3 -0
- package/dist/utils/developerUI/__test__/developerUI.test.ts +50 -0
- package/dist/utils/developerUI/developerUI.ts +42 -0
- package/dist/utils/diagnostics.ts +1 -0
- package/dist/utils/hooks/index.ts +1 -0
- package/dist/utils/hooks/useSearchQuery.ts +9 -0
- package/dist/utils/query.ts +60 -12
- package/package.json +1 -1
- package/dist/utils/developerUI.ts +0 -32
- package/dist/utils/index.js +0 -9
- /package/dist/{components/VirtualTable/utils.ts → utils/index.ts} +0 -0
@@ -1,16 +1,20 @@
|
|
1
1
|
import {useDispatch} from 'react-redux';
|
2
2
|
import {useCallback} from 'react';
|
3
3
|
|
4
|
-
import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
|
4
|
+
import {useAutofetcher, useSearchQuery, useTypedSelector} from '../../../../../utils/hooks';
|
5
5
|
import {
|
6
6
|
getTopNodesByLoad,
|
7
7
|
selectTopNodesByLoad,
|
8
8
|
setDataWasNotLoaded,
|
9
9
|
} from '../../../../../store/reducers/tenantOverview/topNodesByLoad/topNodesByLoad';
|
10
|
+
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
|
10
11
|
import type {AdditionalNodesProps} from '../../../../../types/additionalProps';
|
11
12
|
import {getTopNodesByLoadColumns} from '../../../../Nodes/getNodesColumns';
|
12
|
-
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
13
13
|
|
14
|
+
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
|
15
|
+
|
16
|
+
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
17
|
+
import {getSectionTitle} from '../getSectionTitle';
|
14
18
|
import i18n from '../i18n';
|
15
19
|
|
16
20
|
interface TopNodesByLoadProps {
|
@@ -21,6 +25,8 @@ interface TopNodesByLoadProps {
|
|
21
25
|
export function TopNodesByLoad({path, additionalNodesProps}: TopNodesByLoadProps) {
|
22
26
|
const dispatch = useDispatch();
|
23
27
|
|
28
|
+
const query = useSearchQuery();
|
29
|
+
|
24
30
|
const {wasLoaded, loading, error} = useTypedSelector((state) => state.topNodesByLoad);
|
25
31
|
const {autorefresh} = useTypedSelector((state) => state.schema);
|
26
32
|
const topNodes = useTypedSelector(selectTopNodesByLoad);
|
@@ -39,11 +45,20 @@ export function TopNodesByLoad({path, additionalNodesProps}: TopNodesByLoadProps
|
|
39
45
|
|
40
46
|
useAutofetcher(fetchNodes, [fetchNodes], autorefresh);
|
41
47
|
|
48
|
+
const title = getSectionTitle({
|
49
|
+
entity: i18n('nodes'),
|
50
|
+
postfix: i18n('by-load'),
|
51
|
+
link: getTenantPath({
|
52
|
+
...query,
|
53
|
+
[TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS.nodes,
|
54
|
+
}),
|
55
|
+
});
|
56
|
+
|
42
57
|
return (
|
43
58
|
<TenantOverviewTableLayout
|
44
59
|
data={topNodes || []}
|
45
60
|
columns={columns}
|
46
|
-
title=
|
61
|
+
title={title}
|
47
62
|
loading={loading}
|
48
63
|
wasLoaded={wasLoaded}
|
49
64
|
error={error}
|
@@ -3,6 +3,7 @@ import {useHistory, useLocation} from 'react-router';
|
|
3
3
|
import {useCallback} from 'react';
|
4
4
|
|
5
5
|
import {
|
6
|
+
TENANT_DIAGNOSTICS_TABS_IDS,
|
6
7
|
TENANT_PAGE,
|
7
8
|
TENANT_PAGES_IDS,
|
8
9
|
TENANT_QUERY_TABS_ID,
|
@@ -14,9 +15,13 @@ import {
|
|
14
15
|
import {changeUserInput} from '../../../../../store/reducers/executeQuery';
|
15
16
|
import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
|
16
17
|
import {parseQuery} from '../../../../../routes';
|
18
|
+
|
17
19
|
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
|
18
20
|
import {getTenantOverviewTopQueriesColumns} from '../../TopQueries/getTopQueriesColumns';
|
21
|
+
|
19
22
|
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
23
|
+
import {getSectionTitle} from '../getSectionTitle';
|
24
|
+
import i18n from '../i18n';
|
20
25
|
|
21
26
|
interface TopQueriesProps {
|
22
27
|
path: string;
|
@@ -27,6 +32,8 @@ export function TopQueries({path}: TopQueriesProps) {
|
|
27
32
|
const location = useLocation();
|
28
33
|
const history = useHistory();
|
29
34
|
|
35
|
+
const query = parseQuery(location);
|
36
|
+
|
30
37
|
const {autorefresh} = useTypedSelector((state) => state.schema);
|
31
38
|
|
32
39
|
const {
|
@@ -68,12 +75,21 @@ export function TopQueries({path}: TopQueriesProps) {
|
|
68
75
|
[dispatch, history, location],
|
69
76
|
);
|
70
77
|
|
78
|
+
const title = getSectionTitle({
|
79
|
+
entity: i18n('queries'),
|
80
|
+
postfix: i18n('by-cpu-time'),
|
81
|
+
link: getTenantPath({
|
82
|
+
...query,
|
83
|
+
[TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS.topQueries,
|
84
|
+
}),
|
85
|
+
});
|
86
|
+
|
71
87
|
return (
|
72
88
|
<TenantOverviewTableLayout
|
73
89
|
data={data || []}
|
74
90
|
columns={columns}
|
75
91
|
onRowClick={handleRowClick}
|
76
|
-
title=
|
92
|
+
title={title}
|
77
93
|
loading={loading}
|
78
94
|
wasLoaded={wasLoaded}
|
79
95
|
error={error}
|
@@ -2,12 +2,20 @@ import {useDispatch} from 'react-redux';
|
|
2
2
|
import {useLocation} from 'react-router';
|
3
3
|
|
4
4
|
import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
|
5
|
+
import {parseQuery} from '../../../../../routes';
|
6
|
+
|
5
7
|
import {
|
6
8
|
sendTenantOverviewTopShardsQuery,
|
7
9
|
setDataWasNotLoaded,
|
8
10
|
} from '../../../../../store/reducers/tenantOverview/topShards/tenantOverviewTopShards';
|
11
|
+
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
|
9
12
|
import {getTopShardsColumns} from '../../TopShards/getTopShardsColumns';
|
13
|
+
|
14
|
+
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
|
15
|
+
|
10
16
|
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
17
|
+
import {getSectionTitle} from '../getSectionTitle';
|
18
|
+
import i18n from '../i18n';
|
11
19
|
|
12
20
|
interface TopShardsProps {
|
13
21
|
path: string;
|
@@ -17,6 +25,8 @@ export const TopShards = ({path}: TopShardsProps) => {
|
|
17
25
|
const dispatch = useDispatch();
|
18
26
|
const location = useLocation();
|
19
27
|
|
28
|
+
const query = parseQuery(location);
|
29
|
+
|
20
30
|
const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
|
21
31
|
|
22
32
|
const {
|
@@ -39,11 +49,20 @@ export const TopShards = ({path}: TopShardsProps) => {
|
|
39
49
|
|
40
50
|
const columns = getTopShardsColumns(path, location);
|
41
51
|
|
52
|
+
const title = getSectionTitle({
|
53
|
+
entity: i18n('shards'),
|
54
|
+
postfix: i18n('by-cpu-usage'),
|
55
|
+
link: getTenantPath({
|
56
|
+
...query,
|
57
|
+
[TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS.topShards,
|
58
|
+
}),
|
59
|
+
});
|
60
|
+
|
42
61
|
return (
|
43
62
|
<TenantOverviewTableLayout
|
44
63
|
data={data || []}
|
45
64
|
columns={columns}
|
46
|
-
title=
|
65
|
+
title={title}
|
47
66
|
loading={loading}
|
48
67
|
wasLoaded={wasLoaded}
|
49
68
|
error={error}
|
@@ -1,16 +1,20 @@
|
|
1
1
|
import {useDispatch} from 'react-redux';
|
2
2
|
import {useCallback} from 'react';
|
3
3
|
|
4
|
-
import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
|
4
|
+
import {useAutofetcher, useTypedSelector, useSearchQuery} from '../../../../../utils/hooks';
|
5
5
|
import {
|
6
6
|
getTopNodesByMemory,
|
7
7
|
selectTopNodesByMemory,
|
8
8
|
setDataWasNotLoaded,
|
9
9
|
} from '../../../../../store/reducers/tenantOverview/topNodesByMemory/topNodesByMemory';
|
10
|
+
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
|
10
11
|
import type {AdditionalNodesProps} from '../../../../../types/additionalProps';
|
11
12
|
import {getTopNodesByMemoryColumns} from '../../../../Nodes/getNodesColumns';
|
12
|
-
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
13
13
|
|
14
|
+
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
|
15
|
+
|
16
|
+
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
17
|
+
import {getSectionTitle} from '../getSectionTitle';
|
14
18
|
import i18n from '../i18n';
|
15
19
|
|
16
20
|
interface TopNodesByMemoryProps {
|
@@ -21,6 +25,8 @@ interface TopNodesByMemoryProps {
|
|
21
25
|
export function TopNodesByMemory({path, additionalNodesProps}: TopNodesByMemoryProps) {
|
22
26
|
const dispatch = useDispatch();
|
23
27
|
|
28
|
+
const query = useSearchQuery();
|
29
|
+
|
24
30
|
const {wasLoaded, loading, error} = useTypedSelector((state) => state.topNodesByMemory);
|
25
31
|
const {autorefresh} = useTypedSelector((state) => state.schema);
|
26
32
|
const topNodes = useTypedSelector(selectTopNodesByMemory);
|
@@ -41,11 +47,20 @@ export function TopNodesByMemory({path, additionalNodesProps}: TopNodesByMemoryP
|
|
41
47
|
|
42
48
|
useAutofetcher(fetchNodes, [fetchNodes], autorefresh);
|
43
49
|
|
50
|
+
const title = getSectionTitle({
|
51
|
+
entity: i18n('nodes'),
|
52
|
+
postfix: i18n('by-memory'),
|
53
|
+
link: getTenantPath({
|
54
|
+
...query,
|
55
|
+
[TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS.nodes,
|
56
|
+
}),
|
57
|
+
});
|
58
|
+
|
44
59
|
return (
|
45
60
|
<TenantOverviewTableLayout
|
46
61
|
data={topNodes || []}
|
47
62
|
columns={columns}
|
48
|
-
title=
|
63
|
+
title={title}
|
49
64
|
loading={loading}
|
50
65
|
wasLoaded={wasLoaded}
|
51
66
|
error={error}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import type {ReactNode} from 'react';
|
1
2
|
import cn from 'bem-cn-lite';
|
2
3
|
|
3
4
|
import DataTable from '@gravity-ui/react-data-table';
|
@@ -14,7 +15,7 @@ import {ResponseError} from '../../../../components/Errors/ResponseError';
|
|
14
15
|
const b = cn('tenant-overview');
|
15
16
|
|
16
17
|
interface TenantOverviewTableLayoutProps<T> extends Omit<DataTableProps<T>, 'theme'> {
|
17
|
-
title:
|
18
|
+
title: ReactNode;
|
18
19
|
loading?: boolean;
|
19
20
|
wasLoaded?: boolean;
|
20
21
|
error?: IResponseError;
|
@@ -1,14 +1,20 @@
|
|
1
1
|
import {useCallback} from 'react';
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
|
4
|
-
import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
|
4
|
+
import {useAutofetcher, useSearchQuery, useTypedSelector} from '../../../../../utils/hooks';
|
5
5
|
import {
|
6
6
|
setDataWasNotLoaded,
|
7
7
|
getTopStorageGroups,
|
8
8
|
selectTopStorageGroups,
|
9
9
|
} from '../../../../../store/reducers/tenantOverview/topStorageGroups/topStorageGroups';
|
10
|
+
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
|
10
11
|
import {getStorageTopGroupsColumns} from '../../../../Storage/StorageGroups/getStorageGroupsColumns';
|
12
|
+
|
13
|
+
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
|
14
|
+
|
11
15
|
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
16
|
+
import {getSectionTitle} from '../getSectionTitle';
|
17
|
+
import i18n from '../i18n';
|
12
18
|
|
13
19
|
interface TopGroupsProps {
|
14
20
|
tenant?: string;
|
@@ -17,6 +23,8 @@ interface TopGroupsProps {
|
|
17
23
|
export function TopGroups({tenant}: TopGroupsProps) {
|
18
24
|
const dispatch = useDispatch();
|
19
25
|
|
26
|
+
const query = useSearchQuery();
|
27
|
+
|
20
28
|
const {autorefresh} = useTypedSelector((state) => state.schema);
|
21
29
|
const {loading, wasLoaded, error} = useTypedSelector((state) => state.topStorageGroups);
|
22
30
|
const topGroups = useTypedSelector(selectTopStorageGroups);
|
@@ -36,11 +44,20 @@ export function TopGroups({tenant}: TopGroupsProps) {
|
|
36
44
|
|
37
45
|
useAutofetcher(fetchData, [fetchData], autorefresh);
|
38
46
|
|
47
|
+
const title = getSectionTitle({
|
48
|
+
entity: i18n('groups'),
|
49
|
+
postfix: i18n('by-usage'),
|
50
|
+
link: getTenantPath({
|
51
|
+
...query,
|
52
|
+
[TenantTabsGroups.diagnosticsTab]: TENANT_DIAGNOSTICS_TABS_IDS.storage,
|
53
|
+
}),
|
54
|
+
});
|
55
|
+
|
39
56
|
return (
|
40
57
|
<TenantOverviewTableLayout
|
41
58
|
data={topGroups || []}
|
42
59
|
columns={columns}
|
43
|
-
title=
|
60
|
+
title={title}
|
44
61
|
loading={loading}
|
45
62
|
wasLoaded={wasLoaded}
|
46
63
|
error={error}
|
@@ -12,7 +12,10 @@ import type {KeyValueRow} from '../../../../../types/api/query';
|
|
12
12
|
import {formatBytes, getSizeWithSignificantDigits} from '../../../../../utils/bytesParsers';
|
13
13
|
import {LinkToSchemaObject} from '../../../../../components/LinkToSchemaObject/LinkToSchemaObject';
|
14
14
|
import {CellWithPopover} from '../../../../../components/CellWithPopover/CellWithPopover';
|
15
|
+
|
15
16
|
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
|
17
|
+
import {getSectionTitle} from '../getSectionTitle';
|
18
|
+
import i18n from '../i18n';
|
16
19
|
|
17
20
|
import '../TenantOverview.scss';
|
18
21
|
|
@@ -72,12 +75,16 @@ export function TopTables({path}: TopTablesProps) {
|
|
72
75
|
) : null,
|
73
76
|
},
|
74
77
|
];
|
78
|
+
const title = getSectionTitle({
|
79
|
+
entity: i18n('tables'),
|
80
|
+
postfix: i18n('by-size'),
|
81
|
+
});
|
75
82
|
|
76
83
|
return (
|
77
84
|
<TenantOverviewTableLayout
|
78
85
|
data={data || []}
|
79
86
|
columns={columns}
|
80
|
-
title=
|
87
|
+
title={title}
|
81
88
|
loading={loading}
|
82
89
|
wasLoaded={wasLoaded}
|
83
90
|
error={error}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import {InternalLink} from '../../../../components/InternalLink/InternalLink';
|
2
|
+
|
3
|
+
import i18n from './i18n';
|
4
|
+
|
5
|
+
interface GetSectionTitleParams {
|
6
|
+
entity: string;
|
7
|
+
postfix: string;
|
8
|
+
prefix?: string;
|
9
|
+
link?: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
// Titles are formed by the principle "Top entities by parameter"
|
13
|
+
export const getSectionTitle = ({
|
14
|
+
prefix = i18n('top'),
|
15
|
+
entity,
|
16
|
+
postfix,
|
17
|
+
link,
|
18
|
+
}: GetSectionTitleParams) => {
|
19
|
+
if (link) {
|
20
|
+
return (
|
21
|
+
<>
|
22
|
+
{prefix} <InternalLink to={link}>{entity}</InternalLink> {postfix}
|
23
|
+
</>
|
24
|
+
);
|
25
|
+
}
|
26
|
+
|
27
|
+
return `${prefix} ${entity} ${postfix}`;
|
28
|
+
};
|
@@ -7,5 +7,21 @@
|
|
7
7
|
"title.pools": "Pools",
|
8
8
|
"title.metrics": "Metrics",
|
9
9
|
|
10
|
-
"top-groups.empty-data": "No such groups"
|
10
|
+
"top-groups.empty-data": "No such groups",
|
11
|
+
|
12
|
+
"top": "Top",
|
13
|
+
|
14
|
+
"nodes": "nodes",
|
15
|
+
"shards": "shards",
|
16
|
+
"groups": "groups",
|
17
|
+
"queries": "queries",
|
18
|
+
"tables": "tables",
|
19
|
+
|
20
|
+
"by-pools-usage": "by pools usage",
|
21
|
+
"by-cpu-time": "by cpu time",
|
22
|
+
"by-cpu-usage": "by cpu usage",
|
23
|
+
"by-load": "by load",
|
24
|
+
"by-memory": "by memory",
|
25
|
+
"by-usage": "by usage",
|
26
|
+
"by-size": "by size"
|
11
27
|
}
|
@@ -7,5 +7,21 @@
|
|
7
7
|
"title.pools": "Пулы",
|
8
8
|
"title.metrics": "Метрики",
|
9
9
|
|
10
|
-
"top-groups.empty-data": "Нет групп"
|
10
|
+
"top-groups.empty-data": "Нет групп",
|
11
|
+
|
12
|
+
"top": "Топ",
|
13
|
+
|
14
|
+
"nodes": "узлов",
|
15
|
+
"shards": "шардов",
|
16
|
+
"groups": "групп",
|
17
|
+
"queries": "запросов",
|
18
|
+
"tables": "таблиц",
|
19
|
+
|
20
|
+
"by-pools-usage": "по использованию пулов",
|
21
|
+
"by-cpu-time": "по времени cpu",
|
22
|
+
"by-cpu-usage": "по использованию cpu",
|
23
|
+
"by-load": "по нагрузке",
|
24
|
+
"by-memory": "по памяти",
|
25
|
+
"by-usage": "по потреблению",
|
26
|
+
"by-size": "по размеру"
|
11
27
|
}
|
@@ -13,11 +13,19 @@
|
|
13
13
|
& .data-table__table-wrapper {
|
14
14
|
padding-bottom: 0;
|
15
15
|
}
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
}
|
17
|
+
|
18
|
+
&__result-fullscreen-wrapper {
|
19
|
+
display: flex;
|
20
|
+
flex-direction: column;
|
21
|
+
|
22
|
+
width: 100%;
|
23
|
+
margin-top: 10px;
|
24
|
+
padding: 0 10px 10px;
|
25
|
+
}
|
26
|
+
|
27
|
+
&__result-tabs {
|
28
|
+
padding-left: 10px;
|
21
29
|
}
|
22
30
|
|
23
31
|
&__error {
|
@@ -1,26 +1,30 @@
|
|
1
|
-
import React, {
|
1
|
+
import React, {useEffect, useState} from 'react';
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
4
|
import JSONTree from 'react-json-inspector';
|
5
5
|
|
6
|
-
import {RadioButton} from '@gravity-ui/uikit';
|
6
|
+
import {RadioButton, Tabs} from '@gravity-ui/uikit';
|
7
7
|
|
8
8
|
import CopyToClipboard from '../../../../components/CopyToClipboard/CopyToClipboard';
|
9
9
|
import Divider from '../../../../components/Divider/Divider';
|
10
10
|
import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
|
11
11
|
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
|
12
12
|
import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
|
13
|
+
import {QueryResultTable} from '../../../../components/QueryResultTable/QueryResultTable';
|
13
14
|
|
14
15
|
import type {ValueOf} from '../../../../types/common';
|
15
16
|
import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query';
|
17
|
+
import type {ColumnType, KeyValueRow} from '../../../../types/api/query';
|
16
18
|
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
17
19
|
import {prepareQueryError} from '../../../../utils/query';
|
18
20
|
import {useTypedSelector} from '../../../../utils/hooks';
|
21
|
+
import {getArray} from '../../../../utils';
|
19
22
|
|
20
23
|
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
|
21
24
|
|
22
25
|
import {ResultIssues} from '../Issues/Issues';
|
23
26
|
import {QueryDuration} from '../QueryDuration/QueryDuration';
|
27
|
+
import {getPreparedResult} from '../utils/getPreparedResult';
|
24
28
|
|
25
29
|
import './ExecuteResult.scss';
|
26
30
|
|
@@ -39,30 +43,35 @@ const resultOptions = [
|
|
39
43
|
];
|
40
44
|
|
41
45
|
interface ExecuteResultProps {
|
42
|
-
|
43
|
-
result: ReactNode;
|
46
|
+
data: IQueryResult | undefined;
|
44
47
|
stats: IQueryResult['stats'] | undefined;
|
45
48
|
error: string | QueryErrorResponse | undefined;
|
46
|
-
copyDisabled?: boolean;
|
47
49
|
isResultsCollapsed?: boolean;
|
48
50
|
onCollapseResults: VoidFunction;
|
49
51
|
onExpandResults: VoidFunction;
|
50
52
|
}
|
51
53
|
|
52
54
|
export function ExecuteResult({
|
53
|
-
|
54
|
-
result,
|
55
|
+
data,
|
55
56
|
stats,
|
56
57
|
error,
|
57
|
-
copyDisabled,
|
58
58
|
isResultsCollapsed,
|
59
59
|
onCollapseResults,
|
60
60
|
onExpandResults,
|
61
61
|
}: ExecuteResultProps) {
|
62
|
+
const [selectedResultSet, setSelectedResultSet] = useState(0);
|
62
63
|
const [activeSection, setActiveSection] = useState<SectionID>(resultOptionsIds.result);
|
64
|
+
|
63
65
|
const isFullscreen = useTypedSelector((state) => state.fullscreen);
|
64
66
|
const dispatch = useDispatch();
|
65
67
|
|
68
|
+
const resultsSetsCount = data?.resultSets?.length;
|
69
|
+
const isMulti = resultsSetsCount && resultsSetsCount > 0;
|
70
|
+
const currentResult = isMulti ? data?.resultSets?.[selectedResultSet].result : data?.result;
|
71
|
+
const currentColumns = isMulti ? data?.resultSets?.[selectedResultSet].columns : data?.columns;
|
72
|
+
const textResults = getPreparedResult(currentResult);
|
73
|
+
const copyDisabled = !textResults.length;
|
74
|
+
|
66
75
|
useEffect(() => {
|
67
76
|
return () => {
|
68
77
|
dispatch(disableFullscreen());
|
@@ -73,6 +82,37 @@ export function ExecuteResult({
|
|
73
82
|
setActiveSection(value as SectionID);
|
74
83
|
};
|
75
84
|
|
85
|
+
const renderResultTable = (
|
86
|
+
result: KeyValueRow[] | undefined,
|
87
|
+
columns: ColumnType[] | undefined,
|
88
|
+
) => {
|
89
|
+
return <QueryResultTable data={result} columns={columns} settings={{sortable: false}} />;
|
90
|
+
};
|
91
|
+
|
92
|
+
const renderContent = () => {
|
93
|
+
return (
|
94
|
+
<>
|
95
|
+
{isMulti && resultsSetsCount > 1 && (
|
96
|
+
<div>
|
97
|
+
<Tabs
|
98
|
+
className={b('result-tabs')}
|
99
|
+
size="l"
|
100
|
+
items={getArray(resultsSetsCount).map((item) => ({
|
101
|
+
id: String(item),
|
102
|
+
title: `Result #${item + 1}`,
|
103
|
+
}))}
|
104
|
+
activeTab={String(selectedResultSet)}
|
105
|
+
onSelectTab={(tabId) => setSelectedResultSet(Number(tabId))}
|
106
|
+
/>
|
107
|
+
</div>
|
108
|
+
)}
|
109
|
+
<div className={b('result')}>
|
110
|
+
{renderResultTable(currentResult, currentColumns)}
|
111
|
+
</div>
|
112
|
+
</>
|
113
|
+
);
|
114
|
+
};
|
115
|
+
|
76
116
|
const renderClipboardButton = () => {
|
77
117
|
return (
|
78
118
|
<CopyToClipboard
|
@@ -108,12 +148,14 @@ export function ExecuteResult({
|
|
108
148
|
};
|
109
149
|
|
110
150
|
const renderResult = () => {
|
151
|
+
const content = renderContent();
|
152
|
+
|
111
153
|
return (
|
112
154
|
<React.Fragment>
|
113
|
-
{
|
155
|
+
{content}
|
114
156
|
{isFullscreen && (
|
115
157
|
<Fullscreen>
|
116
|
-
<div className={b('result'
|
158
|
+
<div className={b('result-fullscreen-wrapper')}>{content}</div>
|
117
159
|
</Fullscreen>
|
118
160
|
)}
|
119
161
|
</React.Fragment>
|
@@ -126,13 +168,15 @@ export function ExecuteResult({
|
|
126
168
|
}
|
127
169
|
|
128
170
|
if (typeof error === 'object' && error.data?.issues && Array.isArray(error.data.issues)) {
|
171
|
+
const content = <ResultIssues data={error.data} />;
|
172
|
+
|
129
173
|
return (
|
130
174
|
<React.Fragment>
|
131
|
-
|
175
|
+
{content}
|
132
176
|
{isFullscreen && (
|
133
177
|
<Fullscreen>
|
134
|
-
<div className={b('result',
|
135
|
-
|
178
|
+
<div className={b('result-fullscreen-wrapper', b('result'))}>
|
179
|
+
{content}
|
136
180
|
</div>
|
137
181
|
</Fullscreen>
|
138
182
|
)}
|
@@ -145,6 +189,19 @@ export function ExecuteResult({
|
|
145
189
|
return <div className={b('error')}>{parsedError}</div>;
|
146
190
|
};
|
147
191
|
|
192
|
+
const renderResultSection = () => {
|
193
|
+
if (activeSection === resultOptionsIds.result && !error) {
|
194
|
+
return renderResult();
|
195
|
+
}
|
196
|
+
|
197
|
+
return (
|
198
|
+
<div className={b('result')}>
|
199
|
+
{activeSection === resultOptionsIds.stats && !error && renderStats()}
|
200
|
+
{renderIssues()}
|
201
|
+
</div>
|
202
|
+
);
|
203
|
+
};
|
204
|
+
|
148
205
|
return (
|
149
206
|
<React.Fragment>
|
150
207
|
<div className={b('controls')}>
|
@@ -174,11 +231,8 @@ export function ExecuteResult({
|
|
174
231
|
/>
|
175
232
|
</div>
|
176
233
|
</div>
|
177
|
-
|
178
|
-
|
179
|
-
{activeSection === resultOptionsIds.stats && !error && renderStats()}
|
180
|
-
{renderIssues()}
|
181
|
-
</div>
|
234
|
+
|
235
|
+
{renderResultSection()}
|
182
236
|
</React.Fragment>
|
183
237
|
);
|
184
238
|
}
|
@@ -16,11 +16,12 @@ import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus'
|
|
16
16
|
import {explainVersions} from '../../../../store/reducers/explainQuery';
|
17
17
|
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
18
18
|
|
19
|
-
import {renderExplainNode} from '../../../../utils';
|
20
19
|
import {LANGUAGE_S_EXPRESSION_ID} from '../../../../utils/monaco';
|
21
20
|
|
22
21
|
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
|
23
22
|
|
23
|
+
import {renderExplainNode} from './utils';
|
24
|
+
|
24
25
|
import './ExplainResult.scss';
|
25
26
|
|
26
27
|
const b = cn('ydb-query-explain-result');
|