Skip to content

Commit 1198790

Browse files
committed
fix: review issues
1 parent 017fb56 commit 1198790

File tree

4 files changed

+94
-131
lines changed

4 files changed

+94
-131
lines changed

src/containers/Tenant/Query/QueryResult/components/QueryInfoDropdown/QueryInfoDropdown.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {Ellipsis} from '@gravity-ui/icons';
22
import type {ButtonProps} from '@gravity-ui/uikit';
3-
import {Button, DropdownMenu} from '@gravity-ui/uikit';
3+
import {ActionTooltip, Button, DropdownMenu} from '@gravity-ui/uikit';
44

5+
import i18n from '../../i18n';
6+
7+
import type {QueryResultsInfo} from './useQueryInfoMenuItems';
58
import {useQueryInfoMenuItems} from './useQueryInfoMenuItems';
6-
import type {QueryResultsInfo} from './utils';
79

810
import './QueryInfoDropdown.scss';
911

@@ -29,11 +31,13 @@ export function QueryInfoDropdown({
2931

3032
const renderSwitcher = (props: ButtonProps) => {
3133
return (
32-
<Button view="flat-secondary" loading={isLoading} disabled={isLoading} {...props}>
33-
<Button.Icon>
34-
<Ellipsis />
35-
</Button.Icon>
36-
</Button>
34+
<ActionTooltip title={i18n('tooltip_actions')}>
35+
<Button view="flat-secondary" loading={isLoading} disabled={isLoading} {...props}>
36+
<Button.Icon>
37+
<Ellipsis />
38+
</Button.Icon>
39+
</Button>
40+
</ActionTooltip>
3741
);
3842
};
3943

src/containers/Tenant/Query/QueryResult/components/QueryInfoDropdown/useQueryInfoMenuItems.tsx

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ import type {DropdownMenuItem} from '@gravity-ui/uikit';
55
import {Text} from '@gravity-ui/uikit';
66

77
import {planToSvgApi} from '../../../../../../store/reducers/planToSvg';
8+
import type {QueryPlan, ScriptPlan, TKqpStatsQuery} from '../../../../../../types/api/query';
89
import {cn} from '../../../../../../utils/cn';
10+
import createToast from '../../../../../../utils/createToast';
11+
import {prepareCommonErrorMessage} from '../../../../../../utils/errors';
12+
import {parseQueryError} from '../../../../../../utils/query';
913
import i18n from '../../i18n';
1014

11-
import {
12-
createHandleDiagnosticsDownload,
13-
createHandleDownload,
14-
createHandleOpenInNewTab,
15-
} from './utils';
16-
import type {QueryResultsInfo} from './utils';
15+
import {downloadFile} from './utils';
1716
const b = cn('query-info-dropdown');
1817

1918
export interface MenuItemContentProps {
@@ -32,6 +31,18 @@ export function MenuItemContent({title, description}: MenuItemContentProps) {
3231
);
3332
}
3433

34+
export interface QueryResultsInfo {
35+
ast?: string;
36+
stats?: TKqpStatsQuery;
37+
queryText?: string;
38+
plan?: QueryPlan | ScriptPlan;
39+
}
40+
41+
export interface DiagnosticsData extends QueryResultsInfo {
42+
database: string;
43+
error?: ReturnType<typeof parseQueryError>;
44+
}
45+
3546
export interface UseQueryInfoMenuItemsProps {
3647
queryResultsInfo: QueryResultsInfo;
3748
database: string;
@@ -61,12 +72,44 @@ export function useQueryInfoMenuItems({
6172

6273
const plan = queryResultsInfo.plan;
6374
if (plan && hasPlanToSvg) {
64-
const handlersParams = {
65-
plan,
66-
database,
67-
blobUrl,
68-
getPlanToSvg,
69-
setBlobUrl,
75+
const handleGetSvg = () => {
76+
if (blobUrl) {
77+
return Promise.resolve(blobUrl);
78+
}
79+
80+
return getPlanToSvg({plan, database})
81+
.unwrap()
82+
.then((result) => {
83+
const blob = new Blob([result], {type: 'image/svg+xml'});
84+
const url = URL.createObjectURL(blob);
85+
setBlobUrl(url);
86+
return url;
87+
})
88+
.catch((err) => {
89+
const errorMessage = prepareCommonErrorMessage(err);
90+
createToast({
91+
title: i18n('text_error-plan-svg', {error: errorMessage}),
92+
name: 'plan-svg-error',
93+
type: 'error',
94+
});
95+
return null;
96+
});
97+
};
98+
99+
const handleOpenInNewTab = () => {
100+
handleGetSvg().then((url) => {
101+
if (url) {
102+
window.open(url, '_blank');
103+
}
104+
});
105+
};
106+
107+
const handleDownload = () => {
108+
handleGetSvg().then((url) => {
109+
if (url) {
110+
downloadFile(url, 'query-plan.svg');
111+
}
112+
});
70113
};
71114

72115
menuItems.push([
@@ -78,7 +121,7 @@ export function useQueryInfoMenuItems({
78121
/>
79122
),
80123
icon: <ArrowUpRightFromSquare className={b('icon')} />,
81-
action: createHandleOpenInNewTab(handlersParams),
124+
action: handleOpenInNewTab,
82125
className: b('menu-item'),
83126
},
84127
{
@@ -89,13 +132,29 @@ export function useQueryInfoMenuItems({
89132
/>
90133
),
91134
icon: <ArrowDownToLine className={b('icon')} />,
92-
action: createHandleDownload(handlersParams),
135+
action: handleDownload,
93136
className: b('menu-item'),
94137
},
95138
]);
96139
}
97140

98141
if (queryResultsInfo) {
142+
const handleDiagnosticsDownload = () => {
143+
const parsedError = error ? parseQueryError(error) : undefined;
144+
const diagnosticsData: DiagnosticsData = {
145+
...queryResultsInfo,
146+
database,
147+
...(parsedError && {error: parsedError}),
148+
};
149+
150+
const blob = new Blob([JSON.stringify(diagnosticsData, null, 2)], {
151+
type: 'application/json',
152+
});
153+
const url = URL.createObjectURL(blob);
154+
downloadFile(url, `query-diagnostics-${new Date().getTime()}.json`);
155+
URL.revokeObjectURL(url);
156+
};
157+
99158
menuItems.push([
100159
{
101160
text: (
@@ -105,14 +164,14 @@ export function useQueryInfoMenuItems({
105164
/>
106165
),
107166
icon: <ArrowDownToLine className={b('icon')} />,
108-
action: createHandleDiagnosticsDownload({queryResultsInfo, database, error}),
167+
action: handleDiagnosticsDownload,
109168
className: b('menu-item'),
110169
},
111170
]);
112171
}
113172

114173
return menuItems;
115-
}, [queryResultsInfo, hasPlanToSvg, database, blobUrl, getPlanToSvg, error]);
174+
}, [queryResultsInfo, hasPlanToSvg, blobUrl, getPlanToSvg, database, error]);
116175

117176
return {
118177
isLoading,
Lines changed: 7 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,8 @@
1-
import type {planToSvgApi} from '../../../../../../store/reducers/planToSvg';
2-
import type {QueryPlan, ScriptPlan, TKqpStatsQuery} from '../../../../../../types/api/query';
3-
import createToast from '../../../../../../utils/createToast';
4-
import {prepareCommonErrorMessage} from '../../../../../../utils/errors';
5-
import {parseQueryError} from '../../../../../../utils/query';
6-
import i18n from '../../i18n';
7-
8-
export interface QueryResultsInfo {
9-
ast?: string;
10-
stats?: TKqpStatsQuery;
11-
queryText?: string;
12-
plan?: QueryPlan | ScriptPlan;
1+
export function downloadFile(url: string, filename: string) {
2+
const link = document.createElement('a');
3+
link.href = url;
4+
link.download = filename;
5+
document.body.appendChild(link);
6+
link.click();
7+
document.body.removeChild(link);
138
}
14-
15-
export interface DiagnosticsData extends QueryResultsInfo {
16-
database: string;
17-
error?: ReturnType<typeof parseQueryError>;
18-
}
19-
20-
interface GetSvgParams {
21-
plan: QueryPlan | ScriptPlan;
22-
database: string;
23-
blobUrl: string | null;
24-
getPlanToSvg: ReturnType<typeof planToSvgApi.useLazyPlanToSvgQueryQuery>[0];
25-
setBlobUrl: (url: string) => void;
26-
}
27-
28-
export const handleGetSvg = async ({
29-
plan,
30-
database,
31-
blobUrl,
32-
getPlanToSvg,
33-
setBlobUrl,
34-
}: GetSvgParams): Promise<string | null> => {
35-
if (blobUrl) {
36-
return Promise.resolve(blobUrl);
37-
}
38-
39-
try {
40-
const {data: result} = await getPlanToSvg({plan, database});
41-
if (!result) {
42-
throw new Error('No result from planToSvg');
43-
}
44-
const blob = new Blob([result], {type: 'image/svg+xml'});
45-
const url = URL.createObjectURL(blob);
46-
setBlobUrl(url);
47-
return url;
48-
} catch (err) {
49-
const errorMessage = prepareCommonErrorMessage(err);
50-
createToast({
51-
title: i18n('text_error-plan-svg', {error: errorMessage}),
52-
name: 'plan-svg-error',
53-
type: 'error',
54-
});
55-
return null;
56-
}
57-
};
58-
59-
interface HandlersParams extends GetSvgParams {}
60-
61-
export const createHandleOpenInNewTab = (params: HandlersParams) => () => {
62-
handleGetSvg(params).then((url) => {
63-
if (url) {
64-
window.open(url, '_blank');
65-
}
66-
});
67-
};
68-
69-
export const createHandleDownload = (params: HandlersParams) => () => {
70-
handleGetSvg(params).then((url) => {
71-
const link = document.createElement('a');
72-
if (url) {
73-
link.href = url;
74-
link.download = 'query-plan.svg';
75-
document.body.appendChild(link);
76-
link.click();
77-
document.body.removeChild(link);
78-
}
79-
});
80-
};
81-
82-
interface DiagnosticsDownloadParams {
83-
queryResultsInfo: QueryResultsInfo;
84-
database: string;
85-
error?: unknown;
86-
}
87-
88-
export const createHandleDiagnosticsDownload =
89-
({queryResultsInfo, database, error}: DiagnosticsDownloadParams) =>
90-
() => {
91-
const parsedError = error ? parseQueryError(error) : undefined;
92-
const diagnosticsData: DiagnosticsData = {
93-
...queryResultsInfo,
94-
database,
95-
...(parsedError && {error: parsedError}),
96-
};
97-
98-
const blob = new Blob([JSON.stringify(diagnosticsData, null, 2)], {
99-
type: 'application/json',
100-
});
101-
const url = URL.createObjectURL(blob);
102-
const link = document.createElement('a');
103-
link.href = url;
104-
link.download = `query-diagnostics-${new Date().getTime()}.json`;
105-
document.body.appendChild(link);
106-
link.click();
107-
document.body.removeChild(link);
108-
URL.revokeObjectURL(url);
109-
};

src/containers/Tenant/Query/QueryResult/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"trace": "Trace",
1212
"title.truncated": "Truncated",
1313
"title.result": "Result",
14+
"tooltip_actions": "Actions",
1415
"text_open-execution-plan": "Open Execution Plan",
1516
"text_open-execution-plan_description": "New tab",
1617
"text_download": "Download Execution Plan",

0 commit comments

Comments
 (0)