Skip to content

Commit 03c3a7d

Browse files
committed
fix: simplier approach
1 parent d22b97d commit 03c3a7d

11 files changed

+185
-243
lines changed

src/containers/Tenant/Diagnostics/TopQueries/QueryDetails.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,41 @@ interface QueryDetailsProps {
1919
infoItems: InfoViewerItem[];
2020
onClose: () => void;
2121
onOpenInEditor: () => void;
22+
onCopyLink?: () => void;
2223
}
2324

2425
export const QueryDetails = ({
2526
queryText,
2627
infoItems,
2728
onClose,
2829
onOpenInEditor,
30+
onCopyLink,
2931
}: QueryDetailsProps) => {
32+
// Function to copy current URL to clipboard
33+
const copyLinkToClipboard = (e: React.MouseEvent) => {
34+
e.stopPropagation();
35+
36+
// If onCopyLink is provided, call it to generate and copy a shareable URL
37+
// The actual copy to clipboard is handled in the parent component
38+
if (onCopyLink) {
39+
onCopyLink();
40+
}
41+
};
42+
3043
return (
3144
<div className={b()}>
3245
<div className={b('header')}>
3346
<div className={b('title')}>Query</div>
3447
<div className={b('actions')}>
35-
<Button view="flat-secondary" onClick={onClose}>
36-
<Icon data={Link} size={16} />
37-
</Button>
48+
{onCopyLink && (
49+
<Button
50+
view="flat-secondary"
51+
onClick={copyLinkToClipboard}
52+
title={i18n('query-details.copy-link')}
53+
>
54+
<Icon data={Link} size={16} />
55+
</Button>
56+
)}
3857
<EnableFullscreenButton />
3958
<Button view="flat-secondary" onClick={onClose}>
4059
<Icon data={Xmark} size={16} />

src/containers/Tenant/Diagnostics/TopQueries/QueryDetailsDrawerContent.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@ const b = cn('kv-top-queries');
2626
interface QueryDetailsDrawerContentProps {
2727
row: KeyValueRow | null;
2828
onClose: () => void;
29+
onCopyLink?: () => void;
2930
}
3031

31-
export const QueryDetailsDrawerContent = ({row, onClose}: QueryDetailsDrawerContentProps) => {
32+
export const QueryDetailsDrawerContent = ({
33+
row,
34+
onClose,
35+
onCopyLink,
36+
}: QueryDetailsDrawerContentProps) => {
3237
const dispatch = useTypedDispatch();
3338
const location = useLocation();
3439
const history = useHistory();
@@ -58,6 +63,7 @@ export const QueryDetailsDrawerContent = ({row, onClose}: QueryDetailsDrawerCont
5863
infoItems={createQueryInfoItems(row)}
5964
onClose={onClose}
6065
onOpenInEditor={handleOpenInEditor}
66+
onCopyLink={onCopyLink}
6167
/>
6268
);
6369
}

src/containers/Tenant/Diagnostics/TopQueries/RunningQueriesData.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {
2424
RUNNING_QUERIES_COLUMNS_WIDTH_LS_KEY,
2525
RUNNING_QUERIES_SELECTED_COLUMNS_LS_KEY,
2626
} from './columns/constants';
27-
import {useRunningQueriesRowSelection} from './hooks/useRunningQueriesRowSelection';
2827
import i18n from './i18n';
2928
import {TOP_QUERIES_TABLE_SETTINGS, useRunningQueriesSort} from './utils';
3029

@@ -43,6 +42,9 @@ export const RunningQueriesData = ({
4342
}: RunningQueriesDataProps) => {
4443
const [autoRefreshInterval] = useAutoRefreshInterval();
4544
const filters = useTypedSelector((state) => state.executeTopQueries);
45+
// Internal state for selected row
46+
// null is reserved for not found state
47+
const [selectedRow, setSelectedRow] = React.useState<KeyValueRow | null | undefined>(undefined);
4648

4749
// Get columns for running queries
4850
const columns: Column<KeyValueRow>[] = React.useMemo(() => {
@@ -71,22 +73,35 @@ export const RunningQueriesData = ({
7173
);
7274

7375
const rows = data?.resultSets?.[0]?.result;
74-
const {handleRowSelect, selectedRow, hasSearchParams} = useRunningQueriesRowSelection(rows);
76+
77+
const isDrawerVisible = selectedRow !== undefined;
7578

7679
const handleCloseDetails = React.useCallback(() => {
77-
handleRowSelect(null);
78-
}, [handleRowSelect]);
80+
setSelectedRow(null);
81+
}, [setSelectedRow]);
7982

8083
const renderDrawerContent = React.useCallback(() => {
81-
if (!hasSearchParams) {
84+
if (!isDrawerVisible) {
8285
return null;
8386
}
8487
return <QueryDetailsDrawerContent row={selectedRow} onClose={handleCloseDetails} />;
85-
}, [hasSearchParams, selectedRow, handleCloseDetails]);
88+
}, [isDrawerVisible, selectedRow, handleCloseDetails]);
89+
90+
const onRowClick = React.useCallback(
91+
(
92+
row: KeyValueRow | null,
93+
_index?: number,
94+
event?: React.MouseEvent<HTMLTableRowElement>,
95+
) => {
96+
event?.stopPropagation();
97+
setSelectedRow(row);
98+
},
99+
[setSelectedRow],
100+
);
86101

87102
return (
88103
<DrawerWrapper
89-
isDrawerVisible={hasSearchParams && !isFetching}
104+
isDrawerVisible={isDrawerVisible && !isFetching}
90105
onCloseDrawer={handleCloseDetails}
91106
renderDrawerContent={renderDrawerContent}
92107
drawerId="running-query-details"
@@ -122,7 +137,7 @@ export const RunningQueriesData = ({
122137
data={rows || []}
123138
loading={isFetching && currentData === undefined}
124139
settings={TOP_QUERIES_TABLE_SETTINGS}
125-
onRowClick={handleRowSelect}
140+
onRowClick={onRowClick}
126141
rowClassName={(row) => b('row', {active: row === selectedRow})}
127142
sortOrder={tableSort}
128143
onSort={handleTableSort}

src/containers/Tenant/Diagnostics/TopQueries/TopQueriesData.tsx

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22

33
import type {Column} from '@gravity-ui/react-data-table';
44
import {Select, TableColumnSetup} from '@gravity-ui/uikit';
5+
import {isEqual} from 'lodash';
56

67
import type {DateRangeValues} from '../../../../components/DateRange';
78
import {DateRange} from '../../../../components/DateRange';
@@ -28,9 +29,10 @@ import {
2829
TOP_QUERIES_SELECTED_COLUMNS_LS_KEY,
2930
} from './columns/constants';
3031
import {DEFAULT_TIME_FILTER_VALUE, TIME_FRAME_OPTIONS} from './constants';
31-
import {useTopQueriesRowSelection} from './hooks/useTopQueriesRowSelection';
32+
import {useSetSelectedTopQueryRowFromParams} from './hooks/useSetSelectedTopQueryRowFromParams';
3233
import i18n from './i18n';
3334
import {TOP_QUERIES_TABLE_SETTINGS, useTopQueriesSort} from './utils';
35+
import {generateShareableUrl} from './utils/generateShareableUrl';
3436

3537
const b = cn('kv-top-queries');
3638

@@ -53,6 +55,9 @@ export const TopQueriesData = ({
5355
}: TopQueriesDataProps) => {
5456
const [autoRefreshInterval] = useAutoRefreshInterval();
5557
const filters = useTypedSelector((state) => state.executeTopQueries);
58+
// Internal state for selected row
59+
// null is reserved for not found state
60+
const [selectedRow, setSelectedRow] = React.useState<KeyValueRow | null | undefined>(undefined);
5661

5762
// Get columns for top queries
5863
const columns: Column<KeyValueRow>[] = React.useMemo(() => {
@@ -69,7 +74,7 @@ export const TopQueriesData = ({
6974
);
7075

7176
const {tableSort, handleTableSort, backendSort} = useTopQueriesSort();
72-
const {currentData, data, isFetching, isLoading, error} = topQueriesApi.useGetTopQueriesQuery(
77+
const {currentData, isFetching, isLoading, error} = topQueriesApi.useGetTopQueriesQuery(
7378
{
7479
database: tenantName,
7580
filters,
@@ -79,23 +84,50 @@ export const TopQueriesData = ({
7984
{pollingInterval: autoRefreshInterval},
8085
);
8186

82-
const rows = data?.resultSets?.[0]?.result;
83-
const {handleRowSelect, selectedRow, hasSearchParams} = useTopQueriesRowSelection(rows);
87+
const rows = currentData?.resultSets?.[0]?.result;
88+
useSetSelectedTopQueryRowFromParams(setSelectedRow, rows);
8489

8590
const handleCloseDetails = React.useCallback(() => {
86-
handleRowSelect(null);
87-
}, [handleRowSelect]);
91+
setSelectedRow(undefined);
92+
}, [setSelectedRow]);
93+
94+
const isDrawerVisible = selectedRow !== undefined;
95+
96+
const onCopyLink = React.useCallback(() => {
97+
if (selectedRow) {
98+
const shareableUrl = generateShareableUrl(selectedRow);
99+
navigator.clipboard.writeText(shareableUrl);
100+
}
101+
}, [selectedRow]);
88102

89103
const renderDrawerContent = React.useCallback(() => {
90-
if (!hasSearchParams) {
104+
if (!isDrawerVisible) {
91105
return null;
92106
}
93-
return <QueryDetailsDrawerContent row={selectedRow} onClose={handleCloseDetails} />;
94-
}, [hasSearchParams, selectedRow, handleCloseDetails]);
107+
return (
108+
<QueryDetailsDrawerContent
109+
row={selectedRow}
110+
onClose={handleCloseDetails}
111+
onCopyLink={onCopyLink}
112+
/>
113+
);
114+
}, [isDrawerVisible, selectedRow, handleCloseDetails, onCopyLink]);
115+
116+
const onRowClick = React.useCallback(
117+
(
118+
row: KeyValueRow | null,
119+
_index?: number,
120+
event?: React.MouseEvent<HTMLTableRowElement>,
121+
) => {
122+
event?.stopPropagation();
123+
setSelectedRow(row);
124+
},
125+
[setSelectedRow],
126+
);
95127

96128
return (
97129
<DrawerWrapper
98-
isDrawerVisible={Boolean(hasSearchParams && !isFetching)}
130+
isDrawerVisible={isDrawerVisible && !isFetching}
99131
onCloseDrawer={handleCloseDetails}
100132
renderDrawerContent={renderDrawerContent}
101133
drawerId="query-details"
@@ -142,8 +174,8 @@ export const TopQueriesData = ({
142174
data={rows || []}
143175
loading={isFetching && currentData === undefined}
144176
settings={TOP_QUERIES_TABLE_SETTINGS}
145-
onRowClick={handleRowSelect}
146-
rowClassName={(row) => b('row', {active: row === selectedRow})}
177+
onRowClick={onRowClick}
178+
rowClassName={(row) => b('row', {active: isEqual(row, selectedRow)})}
147179
sortOrder={tableSort}
148180
onSort={handleTableSort}
149181
/>

src/containers/Tenant/Diagnostics/TopQueries/hooks/useRunningQueriesRowSelection.ts

Lines changed: 0 additions & 94 deletions
This file was deleted.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
3+
import {StringParam, useQueryParams} from 'use-query-params';
4+
5+
import type {KeyValueRow} from '../../../../../types/api/query';
6+
import {getTopQueryRowQueryParams} from '../utils/getTopQueryRowQueryParams';
7+
8+
export interface SearchParamsQueryParams {
9+
rank?: string;
10+
intervalEnd?: string;
11+
endTime?: string;
12+
queryHash?: string;
13+
}
14+
15+
export function useSetSelectedTopQueryRowFromParams(
16+
setSelectedRow: (row: KeyValueRow | null) => void,
17+
rows?: KeyValueRow[] | null,
18+
) {
19+
const [queryParams, setQueryParams] = useQueryParams({
20+
selectedRow: StringParam,
21+
});
22+
23+
// Handle initialization from URL params
24+
React.useEffect(() => {
25+
if (rows && queryParams.selectedRow) {
26+
const searchParamsQuery: SearchParamsQueryParams = JSON.parse(queryParams.selectedRow);
27+
const matchedRow = rows.find((row) => {
28+
const params = getTopQueryRowQueryParams(row);
29+
return (
30+
params.rank === searchParamsQuery.rank &&
31+
params.intervalEnd === searchParamsQuery.intervalEnd &&
32+
params.endTime === searchParamsQuery.endTime &&
33+
searchParamsQuery.queryHash === params.queryHash
34+
);
35+
});
36+
37+
if (matchedRow) {
38+
setSelectedRow(matchedRow);
39+
} else {
40+
// If we had a selectedRow in URL but couldn't find a matching row,
41+
// explicitly set selectedRow to null to indicate empty state
42+
setSelectedRow(null);
43+
}
44+
45+
// Clear URL params after using them
46+
setQueryParams({selectedRow: undefined});
47+
}
48+
}, [queryParams.selectedRow, setQueryParams, rows, setSelectedRow]);
49+
50+
return null;
51+
}

0 commit comments

Comments
 (0)