Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking β€œSign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: redesign query #1974

Merged
merged 54 commits into from
Feb 28, 2025
Merged
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
3bfd8bc
eat: Editor controls buttons
astandrik Feb 18, 2025
5679ec3
fix
astandrik Feb 19, 2025
5de1a3a
feat: buttons
astandrik Feb 19, 2025
c6d7186
fix: loading statuses
astandrik Feb 20, 2025
d62660c
fix: remove backend time
astandrik Feb 20, 2025
8921243
fix: tabs
astandrik Feb 21, 2025
8f64bdf
fix: error state
astandrik Feb 21, 2025
13c3f89
fix: nanofixes
astandrik Feb 21, 2025
335588c
fix: issue with key
astandrik Feb 21, 2025
48c1f10
fix: error paddings
astandrik Feb 21, 2025
14218e5
fix: stopped state
astandrik Feb 21, 2025
2eb86a5
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 21, 2025
4f82207
fix: test
astandrik Feb 21, 2025
c2070d6
Revert "fix: issue with key"
astandrik Feb 21, 2025
9915098
Revert "Revert "fix: issue with key""
astandrik Feb 21, 2025
3b67518
Revert "Revert "Revert "fix: issue with key"""
astandrik Feb 21, 2025
51a8b67
fix: queryId
astandrik Feb 21, 2025
36e6c62
fix: tests fix
astandrik Feb 24, 2025
a21f370
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 24, 2025
9a8dec8
fix: fix tests
astandrik Feb 24, 2025
4c26d05
fix: add long running test
astandrik Feb 24, 2025
9f4b14a
fix: tests
astandrik Feb 24, 2025
0a080c6
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 24, 2025
b3d7f29
fix: remove indicator tests
astandrik Feb 24, 2025
d24374a
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 25, 2025
abd097d
fix: design fixes
astandrik Feb 25, 2025
0fc0dd4
fix: fontsize
astandrik Feb 25, 2025
b4785d5
fix: stop view
astandrik Feb 25, 2025
5b56781
fix: design fixes
astandrik Feb 25, 2025
9b3c146
fix: nanofix
astandrik Feb 25, 2025
633909d
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 26, 2025
107d06b
fix: review fixes
astandrik Feb 26, 2025
100f05a
fix: error handling
astandrik Feb 26, 2025
2bf5ffa
fix: tests
astandrik Feb 26, 2025
13a7375
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 27, 2025
1419447
fix: better error handling
astandrik Feb 27, 2025
40fc79b
fix: get rid of scroll
astandrik Feb 27, 2025
43358b3
fix: review fixes
astandrik Feb 27, 2025
23c3a54
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 27, 2025
2ffb97a
fix: stopped data
astandrik Feb 27, 2025
8fde82c
fix: stopped error illustration
astandrik Feb 27, 2025
e2d225a
fix: stop button animation
astandrik Feb 27, 2025
59cc269
feat: add test
astandrik Feb 27, 2025
99d00ef
fix: test
astandrik Feb 27, 2025
3e658be
fix: investigate test
astandrik Feb 28, 2025
2920be0
fix: try update playwright
astandrik Feb 28, 2025
7985a17
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 28, 2025
841a487
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 28, 2025
f45de0c
fix: try use CI variable
astandrik Feb 28, 2025
c225fa3
Revert "fix: try use CI variable"
astandrik Feb 28, 2025
ac86354
fix: remove only
astandrik Feb 28, 2025
5619802
Merge branch 'main' into astandrik.redesign-query-1949
astandrik Feb 28, 2025
ad3712a
fix: skip in safari
astandrik Feb 28, 2025
d21e055
fix: add comment
astandrik Feb 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: review fixes
astandrik committed Feb 26, 2025
commit 107d06b7f5bb4122ec060c1b6936967848692b38
45 changes: 40 additions & 5 deletions src/components/QueryExecutionStatus/QueryExecutionStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import React from 'react';

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

import {isQueryCancelledError} from '../../containers/Tenant/Query/utils/isQueryCancelledError';
import {setQueryDuration} from '../../store/reducers/query/query';
import {cn} from '../../utils/cn';
import {HOUR_IN_SECONDS, SECOND_IN_MS} from '../../utils/constants';
import {useTypedDispatch} from '../../utils/hooks';
import {isAxiosError} from '../../utils/response';

import {useElapsedTime} from './useElapsedTime';

import './QueryExecutionStatus.scss';

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

export const QueryExecutionStatus = ({className, error, loading}: QueryExecutionStatusProps) => {
export const QueryExecutionStatus = ({
className,
error,
loading,
queryDuration,
}: QueryExecutionStatusProps) => {
let icon: React.ReactNode;
let label: string;
let theme: LabelProps['theme'];
let textColor: TextProps['color'];
const dispatch = useTypedDispatch();

const elapsedTime = useElapsedTime(loading);
const isCancelled = isQueryCancelledError(error);

React.useEffect(() => {
let timerId: ReturnType<typeof setInterval> | undefined;
let startTime = Date.now();

if (loading) {
setQueryDuration(0);
startTime = Date.now();
timerId = setInterval(() => {
dispatch(setQueryDuration(Date.now() - startTime));
}, SECOND_IN_MS);
} else {
clearInterval(timerId);
}
return () => {
clearInterval(timerId);
};
}, [dispatch, loading]);

const formattedQueryDuration = React.useMemo(() => {
if (!queryDuration) {
return duration(0).format('mm:ss');
}
return queryDuration > HOUR_IN_SECONDS * SECOND_IN_MS
? duration(queryDuration).format('hh:mm:ss')
: duration(queryDuration).format('mm:ss');
}, [queryDuration]);

if (loading) {
theme = 'info';
textColor = 'info-heavy';
@@ -63,7 +98,7 @@ export const QueryExecutionStatus = ({className, error, loading}: QueryExecution
size="m"
className={b(null, className)}
icon={icon}
value={elapsedTime}
value={formattedQueryDuration}
>
<Text color={textColor}>{label}</Text>
</Label>
31 changes: 0 additions & 31 deletions src/components/QueryExecutionStatus/useElapsedTime.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -33,6 +33,41 @@ interface QueryEditorControlsProps {
const STOP_APPEAR_TIMEOUT = 400;
const STOP_AUTO_HIDE_TIMEOUT = 5000;

interface ActionButtonProps {
type: 'run' | 'explain';
isHighlighted: boolean;
isLoading: boolean;
isStoppable: boolean;
controlsDisabled: boolean;
onActionClick: () => void;
renderStopButton: () => React.ReactNode;
}

const ActionButton = ({
type,
isHighlighted,
isLoading,
isStoppable,
controlsDisabled,
onActionClick,
renderStopButton,
}: ActionButtonProps) => {
if (isStoppable && isLoading && isHighlighted) {
return renderStopButton();
}

const ButtonComponent = type === 'run' ? EditorButton.Run : EditorButton.Explain;

return (
<ButtonComponent
onClick={onActionClick}
disabled={controlsDisabled}
loading={isLoading}
view={isHighlighted ? 'action' : undefined}
/>
);
};

export const QueryEditorControls = ({
disabled,
isLoading,
@@ -49,17 +84,16 @@ export const QueryEditorControls = ({
const input = useTypedSelector(selectUserInput);
const [sendCancelQuery, cancelQueryResponse] = cancelQueryApi.useCancelQueryMutation();
const stopButtonAppearRef = React.useRef<number>(0);
const [isStopButtonVisibilityTimeoutPassed, setIsStopButtonVisibilityTimeoutPassed] =
React.useState(false);
const [isStoppable, setIsStoppable] = React.useState(false);

React.useEffect(() => {
if (isLoading) {
stopButtonAppearRef.current = window.setTimeout(() => {
setIsStopButtonVisibilityTimeoutPassed(true);
setIsStoppable(true);
}, STOP_APPEAR_TIMEOUT);
} else {
window.clearTimeout(stopButtonAppearRef.current);
setIsStopButtonVisibilityTimeoutPassed(false);
setIsStoppable(false);
cancelQueryResponse.reset();
}

@@ -113,28 +147,24 @@ export const QueryEditorControls = ({
return (
<div className={b()}>
<div className={b('left')}>
{isStopButtonVisibilityTimeoutPassed && isLoading && isRunHighlighted ? (
renderStopButton()
) : (
<EditorButton.Run
onClick={onRunButtonClick}
disabled={controlsDisabled}
loading={isLoading}
view={isRunHighlighted ? 'action' : undefined}
/>
)}

{isStopButtonVisibilityTimeoutPassed && isLoading && isExplainHighlighted ? (
renderStopButton()
) : (
<EditorButton.Explain
onClick={onExplainButtonClick}
disabled={controlsDisabled}
loading={isLoading}
view={isExplainHighlighted ? 'action' : undefined}
/>
)}

<ActionButton
type="run"
isHighlighted={isRunHighlighted}
isLoading={isLoading}
isStoppable={isStoppable}
controlsDisabled={controlsDisabled}
onActionClick={onRunButtonClick}
renderStopButton={renderStopButton}
/>
<ActionButton
type="explain"
isHighlighted={isExplainHighlighted}
isLoading={isLoading}
isStoppable={isStoppable}
controlsDisabled={controlsDisabled}
onActionClick={onExplainButtonClick}
renderStopButton={renderStopButton}
/>
<EditorButton.Settings onClick={onSettingsButtonClick} isLoading={isLoading} />
</div>
<div className={b('right')}>
24 changes: 13 additions & 11 deletions src/containers/Tenant/Query/QueryResult/QueryResultViewer.tsx
Original file line number Diff line number Diff line change
@@ -108,7 +108,7 @@ export function QueryResultViewer({
});
const [useShowPlanToSvg] = useSetting<boolean>(USE_SHOW_PLAN_SVG_KEY);

const {error, isLoading, data = {}} = result;
const {error, isLoading, data = {}, queryDuration} = result;
const {preparedPlan, simplifiedPlan, stats, resultSets, ast} = data;

React.useEffect(() => {
@@ -293,16 +293,18 @@ export function QueryResultViewer({
const renderLeftControls = () => {
return (
<div className={b('controls-left')}>
<React.Fragment>
{radioButtonOptions.length && activeSection ? (
<RadioButton
options={radioButtonOptions}
value={activeSection}
onUpdate={onSelectSection}
/>
) : null}
</React.Fragment>
<QueryExecutionStatus error={error} loading={isLoading} />
{radioButtonOptions.length && activeSection ? (
<RadioButton
options={radioButtonOptions}
value={activeSection}
onUpdate={onSelectSection}
/>
) : null}
<QueryExecutionStatus
error={error}
loading={isLoading}
queryDuration={queryDuration}
/>
{data?.traceId && isExecute ? <TraceButton traceId={data.traceId} /> : null}
</div>
);
Original file line number Diff line number Diff line change
@@ -17,12 +17,6 @@
padding-left: var(--g-spacing-4);
}

&__tab-title {
display: flex;
align-items: center;
gap: var(--g-spacing-2);
}

&__result-wrapper {
display: flex;
flex-direction: column;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {Settings} from '@gravity-ui/react-data-table';
import type {TabsItemProps} from '@gravity-ui/uikit';
import {Tabs, Text} from '@gravity-ui/uikit';
import {Flex, Tabs, Text} from '@gravity-ui/uikit';

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

return (
<div>
<Tabs
className={b('tabs')}
size="l"
items={tabsItems}
activeTab={String(selectedResultSet)}
onSelectTab={(tabId) => setSelectedResultSet(Number(tabId))}
/>
</div>
<Tabs
className={b('tabs')}
size="l"
items={tabsItems}
activeTab={String(selectedResultSet)}
onSelectTab={(tabId) => setSelectedResultSet(Number(tabId))}
/>
);
};

6 changes: 6 additions & 0 deletions src/store/reducers/query/query.ts
Original file line number Diff line number Diff line change
@@ -53,6 +53,11 @@ const slice = createSlice({
setQueryResult: (state, action: PayloadAction<QueryResult | undefined>) => {
state.result = action.payload;
},
setQueryDuration: (state, action: PayloadAction<number>) => {
if (state.result) {
state.result.queryDuration = action.payload;
}
},
saveQueryToHistory: (
state,
action: PayloadAction<{queryText: string; queryId: string}>,
@@ -149,6 +154,7 @@ export default slice.reducer;
export const {
changeUserInput,
setQueryResult,
setQueryDuration,
saveQueryToHistory,
updateQueryInHistory,
goToPreviousQuery,
1 change: 1 addition & 0 deletions src/store/reducers/query/types.ts
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@ export interface QueryResult {
error?: unknown;
queryId: string;
isLoading: boolean;
queryDuration?: number;
}

export interface QueryState {