Skip to content

Commit 107d06b

Browse files
committed
fix: review fixes
1 parent 633909d commit 107d06b

File tree

8 files changed

+126
-91
lines changed

8 files changed

+126
-91
lines changed

src/components/QueryExecutionStatus/QueryExecutionStatus.tsx

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import React from 'react';
22

3+
import {duration} from '@gravity-ui/date-utils';
34
import {CircleCheckFill, CircleQuestionFill, CircleStop, CircleXmark} from '@gravity-ui/icons';
45
import type {LabelProps, TextProps} from '@gravity-ui/uikit';
56
import {Icon, Label, Spin, Text} from '@gravity-ui/uikit';
67

78
import {isQueryCancelledError} from '../../containers/Tenant/Query/utils/isQueryCancelledError';
9+
import {setQueryDuration} from '../../store/reducers/query/query';
810
import {cn} from '../../utils/cn';
11+
import {HOUR_IN_SECONDS, SECOND_IN_MS} from '../../utils/constants';
12+
import {useTypedDispatch} from '../../utils/hooks';
913
import {isAxiosError} from '../../utils/response';
1014

11-
import {useElapsedTime} from './useElapsedTime';
12-
1315
import './QueryExecutionStatus.scss';
1416

1517
const b = cn('kv-query-execution-status');
@@ -18,17 +20,50 @@ interface QueryExecutionStatusProps {
1820
className?: string;
1921
error?: unknown;
2022
loading?: boolean;
23+
queryDuration?: number;
2124
}
2225

23-
export const QueryExecutionStatus = ({className, error, loading}: QueryExecutionStatusProps) => {
26+
export const QueryExecutionStatus = ({
27+
className,
28+
error,
29+
loading,
30+
queryDuration,
31+
}: QueryExecutionStatusProps) => {
2432
let icon: React.ReactNode;
2533
let label: string;
2634
let theme: LabelProps['theme'];
2735
let textColor: TextProps['color'];
36+
const dispatch = useTypedDispatch();
2837

29-
const elapsedTime = useElapsedTime(loading);
3038
const isCancelled = isQueryCancelledError(error);
3139

40+
React.useEffect(() => {
41+
let timerId: ReturnType<typeof setInterval> | undefined;
42+
let startTime = Date.now();
43+
44+
if (loading) {
45+
setQueryDuration(0);
46+
startTime = Date.now();
47+
timerId = setInterval(() => {
48+
dispatch(setQueryDuration(Date.now() - startTime));
49+
}, SECOND_IN_MS);
50+
} else {
51+
clearInterval(timerId);
52+
}
53+
return () => {
54+
clearInterval(timerId);
55+
};
56+
}, [dispatch, loading]);
57+
58+
const formattedQueryDuration = React.useMemo(() => {
59+
if (!queryDuration) {
60+
return duration(0).format('mm:ss');
61+
}
62+
return queryDuration > HOUR_IN_SECONDS * SECOND_IN_MS
63+
? duration(queryDuration).format('hh:mm:ss')
64+
: duration(queryDuration).format('mm:ss');
65+
}, [queryDuration]);
66+
3267
if (loading) {
3368
theme = 'info';
3469
textColor = 'info-heavy';
@@ -63,7 +98,7 @@ export const QueryExecutionStatus = ({className, error, loading}: QueryExecution
6398
size="m"
6499
className={b(null, className)}
65100
icon={icon}
66-
value={elapsedTime}
101+
value={formattedQueryDuration}
67102
>
68103
<Text color={textColor}>{label}</Text>
69104
</Label>

src/components/QueryExecutionStatus/useElapsedTime.tsx

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,41 @@ interface QueryEditorControlsProps {
3333
const STOP_APPEAR_TIMEOUT = 400;
3434
const STOP_AUTO_HIDE_TIMEOUT = 5000;
3535

36+
interface ActionButtonProps {
37+
type: 'run' | 'explain';
38+
isHighlighted: boolean;
39+
isLoading: boolean;
40+
isStoppable: boolean;
41+
controlsDisabled: boolean;
42+
onActionClick: () => void;
43+
renderStopButton: () => React.ReactNode;
44+
}
45+
46+
const ActionButton = ({
47+
type,
48+
isHighlighted,
49+
isLoading,
50+
isStoppable,
51+
controlsDisabled,
52+
onActionClick,
53+
renderStopButton,
54+
}: ActionButtonProps) => {
55+
if (isStoppable && isLoading && isHighlighted) {
56+
return renderStopButton();
57+
}
58+
59+
const ButtonComponent = type === 'run' ? EditorButton.Run : EditorButton.Explain;
60+
61+
return (
62+
<ButtonComponent
63+
onClick={onActionClick}
64+
disabled={controlsDisabled}
65+
loading={isLoading}
66+
view={isHighlighted ? 'action' : undefined}
67+
/>
68+
);
69+
};
70+
3671
export const QueryEditorControls = ({
3772
disabled,
3873
isLoading,
@@ -49,17 +84,16 @@ export const QueryEditorControls = ({
4984
const input = useTypedSelector(selectUserInput);
5085
const [sendCancelQuery, cancelQueryResponse] = cancelQueryApi.useCancelQueryMutation();
5186
const stopButtonAppearRef = React.useRef<number>(0);
52-
const [isStopButtonVisibilityTimeoutPassed, setIsStopButtonVisibilityTimeoutPassed] =
53-
React.useState(false);
87+
const [isStoppable, setIsStoppable] = React.useState(false);
5488

5589
React.useEffect(() => {
5690
if (isLoading) {
5791
stopButtonAppearRef.current = window.setTimeout(() => {
58-
setIsStopButtonVisibilityTimeoutPassed(true);
92+
setIsStoppable(true);
5993
}, STOP_APPEAR_TIMEOUT);
6094
} else {
6195
window.clearTimeout(stopButtonAppearRef.current);
62-
setIsStopButtonVisibilityTimeoutPassed(false);
96+
setIsStoppable(false);
6397
cancelQueryResponse.reset();
6498
}
6599

@@ -113,28 +147,24 @@ export const QueryEditorControls = ({
113147
return (
114148
<div className={b()}>
115149
<div className={b('left')}>
116-
{isStopButtonVisibilityTimeoutPassed && isLoading && isRunHighlighted ? (
117-
renderStopButton()
118-
) : (
119-
<EditorButton.Run
120-
onClick={onRunButtonClick}
121-
disabled={controlsDisabled}
122-
loading={isLoading}
123-
view={isRunHighlighted ? 'action' : undefined}
124-
/>
125-
)}
126-
127-
{isStopButtonVisibilityTimeoutPassed && isLoading && isExplainHighlighted ? (
128-
renderStopButton()
129-
) : (
130-
<EditorButton.Explain
131-
onClick={onExplainButtonClick}
132-
disabled={controlsDisabled}
133-
loading={isLoading}
134-
view={isExplainHighlighted ? 'action' : undefined}
135-
/>
136-
)}
137-
150+
<ActionButton
151+
type="run"
152+
isHighlighted={isRunHighlighted}
153+
isLoading={isLoading}
154+
isStoppable={isStoppable}
155+
controlsDisabled={controlsDisabled}
156+
onActionClick={onRunButtonClick}
157+
renderStopButton={renderStopButton}
158+
/>
159+
<ActionButton
160+
type="explain"
161+
isHighlighted={isExplainHighlighted}
162+
isLoading={isLoading}
163+
isStoppable={isStoppable}
164+
controlsDisabled={controlsDisabled}
165+
onActionClick={onExplainButtonClick}
166+
renderStopButton={renderStopButton}
167+
/>
138168
<EditorButton.Settings onClick={onSettingsButtonClick} isLoading={isLoading} />
139169
</div>
140170
<div className={b('right')}>

src/containers/Tenant/Query/QueryResult/QueryResultViewer.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export function QueryResultViewer({
108108
});
109109
const [useShowPlanToSvg] = useSetting<boolean>(USE_SHOW_PLAN_SVG_KEY);
110110

111-
const {error, isLoading, data = {}} = result;
111+
const {error, isLoading, data = {}, queryDuration} = result;
112112
const {preparedPlan, simplifiedPlan, stats, resultSets, ast} = data;
113113

114114
React.useEffect(() => {
@@ -293,16 +293,18 @@ export function QueryResultViewer({
293293
const renderLeftControls = () => {
294294
return (
295295
<div className={b('controls-left')}>
296-
<React.Fragment>
297-
{radioButtonOptions.length && activeSection ? (
298-
<RadioButton
299-
options={radioButtonOptions}
300-
value={activeSection}
301-
onUpdate={onSelectSection}
302-
/>
303-
) : null}
304-
</React.Fragment>
305-
<QueryExecutionStatus error={error} loading={isLoading} />
296+
{radioButtonOptions.length && activeSection ? (
297+
<RadioButton
298+
options={radioButtonOptions}
299+
value={activeSection}
300+
onUpdate={onSelectSection}
301+
/>
302+
) : null}
303+
<QueryExecutionStatus
304+
error={error}
305+
loading={isLoading}
306+
queryDuration={queryDuration}
307+
/>
306308
{data?.traceId && isExecute ? <TraceButton traceId={data.traceId} /> : null}
307309
</div>
308310
);

src/containers/Tenant/Query/QueryResult/components/ResultSetsViewer/ResultSetsViewer.scss

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@
1717
padding-left: var(--g-spacing-4);
1818
}
1919

20-
&__tab-title {
21-
display: flex;
22-
align-items: center;
23-
gap: var(--g-spacing-2);
24-
}
25-
2620
&__result-wrapper {
2721
display: flex;
2822
flex-direction: column;

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {Settings} from '@gravity-ui/react-data-table';
22
import type {TabsItemProps} from '@gravity-ui/uikit';
3-
import {Tabs, Text} from '@gravity-ui/uikit';
3+
import {Flex, Tabs, Text} from '@gravity-ui/uikit';
44

55
import {QueryResultTable} from '../../../../../../components/QueryResultTable';
66
import type {ParsedResultSet} from '../../../../../../types/store/query';
@@ -31,26 +31,24 @@ export function ResultSetsViewer(props: ResultSetsViewerProps) {
3131
return {
3232
id: String(index),
3333
title: (
34-
<div className={b('tab-title')}>
34+
<Flex gap={2} alignItems="center">
3535
<Text>
3636
{`Result #${index + 1}${resultSets?.[index]?.truncated ? '(T)' : ''}`}
3737
</Text>
3838
<Text color="secondary">{resultSet.result?.length || 0}</Text>
39-
</div>
39+
</Flex>
4040
),
4141
};
4242
}) || [];
4343

4444
return (
45-
<div>
46-
<Tabs
47-
className={b('tabs')}
48-
size="l"
49-
items={tabsItems}
50-
activeTab={String(selectedResultSet)}
51-
onSelectTab={(tabId) => setSelectedResultSet(Number(tabId))}
52-
/>
53-
</div>
45+
<Tabs
46+
className={b('tabs')}
47+
size="l"
48+
items={tabsItems}
49+
activeTab={String(selectedResultSet)}
50+
onSelectTab={(tabId) => setSelectedResultSet(Number(tabId))}
51+
/>
5452
);
5553
};
5654

src/store/reducers/query/query.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ const slice = createSlice({
5353
setQueryResult: (state, action: PayloadAction<QueryResult | undefined>) => {
5454
state.result = action.payload;
5555
},
56+
setQueryDuration: (state, action: PayloadAction<number>) => {
57+
if (state.result) {
58+
state.result.queryDuration = action.payload;
59+
}
60+
},
5661
saveQueryToHistory: (
5762
state,
5863
action: PayloadAction<{queryText: string; queryId: string}>,
@@ -149,6 +154,7 @@ export default slice.reducer;
149154
export const {
150155
changeUserInput,
151156
setQueryResult,
157+
setQueryDuration,
152158
saveQueryToHistory,
153159
updateQueryInHistory,
154160
goToPreviousQuery,

src/store/reducers/query/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface QueryResult {
5454
error?: unknown;
5555
queryId: string;
5656
isLoading: boolean;
57+
queryDuration?: number;
5758
}
5859

5960
export interface QueryState {

0 commit comments

Comments
 (0)