|
1 | 1 | import React from 'react'; |
2 | 2 |
|
3 | | -import type {Column} from '@gravity-ui/react-data-table'; |
4 | | -import {Select, TableColumnSetup} from '@gravity-ui/uikit'; |
| 3 | +import type {RadioButtonOption} from '@gravity-ui/uikit'; |
| 4 | +import {RadioButton} from '@gravity-ui/uikit'; |
| 5 | +import {useHistory, useLocation} from 'react-router-dom'; |
| 6 | +import {StringParam, useQueryParam} from 'use-query-params'; |
| 7 | +import {z} from 'zod'; |
5 | 8 |
|
6 | 9 | import type {DateRangeValues} from '../../../../components/DateRange'; |
7 | | -import {DateRange} from '../../../../components/DateRange'; |
8 | | -import {ResponseError} from '../../../../components/Errors/ResponseError'; |
9 | | -import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable'; |
10 | | -import {Search} from '../../../../components/Search'; |
11 | | -import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout'; |
12 | | -import {topQueriesApi} from '../../../../store/reducers/executeTopQueries/executeTopQueries'; |
| 10 | +import {parseQuery} from '../../../../routes'; |
| 11 | +import {setTopQueriesFilters} from '../../../../store/reducers/executeTopQueries/executeTopQueries'; |
13 | 12 | import type {TimeFrame} from '../../../../store/reducers/executeTopQueries/types'; |
14 | | -import type {KeyValueRow} from '../../../../types/api/query'; |
15 | | -import {cn} from '../../../../utils/cn'; |
16 | | -import {useAutoRefreshInterval, useTypedSelector} from '../../../../utils/hooks'; |
17 | | -import {useSelectedColumns} from '../../../../utils/hooks/useSelectedColumns'; |
18 | | -import {parseQueryErrorToString} from '../../../../utils/query'; |
19 | | - |
20 | | -import {getTopQueriesColumns} from './columns/columns'; |
| 13 | +import {changeUserInput, setIsDirty} from '../../../../store/reducers/query/query'; |
21 | 14 | import { |
22 | | - DEFAULT_TOP_QUERIES_COLUMNS, |
23 | | - QUERIES_COLUMNS_TITLES, |
24 | | - REQUIRED_TOP_QUERIES_COLUMNS, |
25 | | - TOP_QUERIES_COLUMNS_WIDTH_LS_KEY, |
26 | | - TOP_QUERIES_SELECTED_COLUMNS_LS_KEY, |
27 | | -} from './columns/constants'; |
28 | | -import {DEFAULT_TIME_FILTER_VALUE, TIME_FRAME_OPTIONS} from './constants'; |
| 15 | + TENANT_PAGE, |
| 16 | + TENANT_PAGES_IDS, |
| 17 | + TENANT_QUERY_TABS_ID, |
| 18 | +} from '../../../../store/reducers/tenant/constants'; |
| 19 | +import {useTypedDispatch} from '../../../../utils/hooks'; |
| 20 | +import {useChangeInputWithConfirmation} from '../../../../utils/hooks/withConfirmation/useChangeInputWithConfirmation'; |
| 21 | +import {TenantTabsGroups, getTenantPath} from '../../TenantPages'; |
| 22 | + |
| 23 | +import {RunningQueriesData} from './RunningQueriesData'; |
| 24 | +import {TopQueriesData} from './TopQueriesData'; |
| 25 | +import {TimeFrameIds} from './constants'; |
29 | 26 | import i18n from './i18n'; |
30 | | -import {TOP_QUERIES_TABLE_SETTINGS, useTopQueriesSort} from './utils'; |
31 | 27 |
|
32 | | -const b = cn('kv-top-queries'); |
| 28 | +import './TopQueries.scss'; |
| 29 | + |
| 30 | +const QueryModeIds = { |
| 31 | + top: 'top', |
| 32 | + running: 'running', |
| 33 | +} as const; |
33 | 34 |
|
34 | | -interface TopQueriesDataProps { |
| 35 | +const QUERY_MODE_OPTIONS: RadioButtonOption[] = [ |
| 36 | + { |
| 37 | + value: QueryModeIds.top, |
| 38 | + get content() { |
| 39 | + return i18n('mode_top'); |
| 40 | + }, |
| 41 | + }, |
| 42 | + { |
| 43 | + value: QueryModeIds.running, |
| 44 | + get content() { |
| 45 | + return i18n('mode_running'); |
| 46 | + }, |
| 47 | + }, |
| 48 | +]; |
| 49 | + |
| 50 | +const queryModeSchema = z.nativeEnum(QueryModeIds).catch(QueryModeIds.top); |
| 51 | +const timeFrameSchema = z.nativeEnum(TimeFrameIds).catch(TimeFrameIds.hour); |
| 52 | + |
| 53 | +interface TopQueriesProps { |
35 | 54 | tenantName: string; |
36 | | - timeFrame: TimeFrame; |
37 | | - renderQueryModeControl: () => React.ReactNode; |
38 | | - onRowClick: (query: string) => void; |
39 | | - handleTimeFrameChange: (value: string[]) => void; |
40 | | - handleDateRangeChange: (value: DateRangeValues) => void; |
41 | | - handleTextSearchUpdate: (text: string) => void; |
42 | 55 | } |
43 | 56 |
|
44 | | -export const TopQueriesData = ({ |
45 | | - tenantName, |
46 | | - timeFrame, |
47 | | - renderQueryModeControl, |
48 | | - onRowClick, |
49 | | - handleTimeFrameChange, |
50 | | - handleDateRangeChange, |
51 | | - handleTextSearchUpdate, |
52 | | -}: TopQueriesDataProps) => { |
53 | | - const [autoRefreshInterval] = useAutoRefreshInterval(); |
54 | | - const filters = useTypedSelector((state) => state.executeTopQueries); |
55 | | - |
56 | | - // Get columns for top queries |
57 | | - const columns: Column<KeyValueRow>[] = React.useMemo(() => { |
58 | | - return getTopQueriesColumns(); |
59 | | - }, []); |
60 | | - |
61 | | - // Use selected columns hook |
62 | | - const {columnsToShow, columnsToSelect, setColumns} = useSelectedColumns( |
63 | | - columns, |
64 | | - TOP_QUERIES_SELECTED_COLUMNS_LS_KEY, |
65 | | - QUERIES_COLUMNS_TITLES, |
66 | | - DEFAULT_TOP_QUERIES_COLUMNS, |
67 | | - REQUIRED_TOP_QUERIES_COLUMNS, |
68 | | - ); |
| 57 | +export const TopQueries = ({tenantName}: TopQueriesProps) => { |
| 58 | + const dispatch = useTypedDispatch(); |
| 59 | + const location = useLocation(); |
| 60 | + const history = useHistory(); |
| 61 | + const [_queryMode = QueryModeIds.top, setQueryMode] = useQueryParam('queryMode', StringParam); |
| 62 | + const [_timeFrame = TimeFrameIds.hour, setTimeFrame] = useQueryParam('timeFrame', StringParam); |
| 63 | + |
| 64 | + const queryMode = queryModeSchema.parse(_queryMode); |
| 65 | + const timeFrame = timeFrameSchema.parse(_timeFrame); |
69 | 66 |
|
70 | | - const {tableSort, handleTableSort, backendSort} = useTopQueriesSort(); |
| 67 | + const isTopQueries = queryMode === QueryModeIds.top; |
71 | 68 |
|
72 | | - const {currentData, data, isFetching, isLoading, error} = topQueriesApi.useGetTopQueriesQuery( |
73 | | - { |
74 | | - database: tenantName, |
75 | | - filters, |
76 | | - sortOrder: backendSort, |
77 | | - timeFrame, |
| 69 | + const applyRowClick = React.useCallback( |
| 70 | + (input: string) => { |
| 71 | + dispatch(changeUserInput({input})); |
| 72 | + dispatch(setIsDirty(false)); |
| 73 | + |
| 74 | + const queryParams = parseQuery(location); |
| 75 | + |
| 76 | + const queryPath = getTenantPath({ |
| 77 | + ...queryParams, |
| 78 | + [TENANT_PAGE]: TENANT_PAGES_IDS.query, |
| 79 | + [TenantTabsGroups.queryTab]: TENANT_QUERY_TABS_ID.newQuery, |
| 80 | + }); |
| 81 | + |
| 82 | + history.push(queryPath); |
78 | 83 | }, |
79 | | - {pollingInterval: autoRefreshInterval}, |
| 84 | + [dispatch, history, location], |
80 | 85 | ); |
81 | 86 |
|
82 | | - const handleRowClick = (row: KeyValueRow) => { |
83 | | - return onRowClick(row.QueryText as string); |
| 87 | + const onRowClick = useChangeInputWithConfirmation(applyRowClick); |
| 88 | + |
| 89 | + const handleTextSearchUpdate = (text: string) => { |
| 90 | + dispatch(setTopQueriesFilters({text})); |
84 | 91 | }; |
85 | 92 |
|
86 | | - return ( |
87 | | - <TableWithControlsLayout> |
88 | | - <TableWithControlsLayout.Controls> |
89 | | - {renderQueryModeControl()} |
90 | | - <Select |
91 | | - options={TIME_FRAME_OPTIONS} |
92 | | - value={[timeFrame]} |
93 | | - onUpdate={handleTimeFrameChange} |
94 | | - /> |
95 | | - <DateRange |
96 | | - from={filters.from} |
97 | | - to={filters.to} |
98 | | - onChange={handleDateRangeChange} |
99 | | - defaultValue={DEFAULT_TIME_FILTER_VALUE} |
100 | | - /> |
101 | | - <Search |
102 | | - value={filters.text} |
103 | | - onChange={handleTextSearchUpdate} |
104 | | - placeholder={i18n('filter.text.placeholder')} |
105 | | - className={b('search')} |
106 | | - /> |
107 | | - <TableColumnSetup |
108 | | - popupWidth={200} |
109 | | - items={columnsToSelect} |
110 | | - showStatus |
111 | | - onUpdate={setColumns} |
112 | | - sortable={false} |
113 | | - /> |
114 | | - </TableWithControlsLayout.Controls> |
115 | | - |
116 | | - {error ? <ResponseError error={parseQueryErrorToString(error)} /> : null} |
117 | | - <TableWithControlsLayout.Table loading={isLoading}> |
118 | | - <ResizeableDataTable |
119 | | - emptyDataMessage={i18n('no-data')} |
120 | | - columnsWidthLSKey={TOP_QUERIES_COLUMNS_WIDTH_LS_KEY} |
121 | | - columns={columnsToShow} |
122 | | - data={data?.resultSets?.[0].result || []} |
123 | | - loading={isFetching && currentData === undefined} |
124 | | - settings={TOP_QUERIES_TABLE_SETTINGS} |
125 | | - onRowClick={handleRowClick} |
126 | | - rowClassName={() => b('row')} |
127 | | - sortOrder={tableSort} |
128 | | - onSort={handleTableSort} |
129 | | - /> |
130 | | - </TableWithControlsLayout.Table> |
131 | | - </TableWithControlsLayout> |
| 93 | + const handleTimeFrameChange = (value: string[]) => { |
| 94 | + setTimeFrame(value[0] as TimeFrame, 'replaceIn'); |
| 95 | + }; |
| 96 | + |
| 97 | + const handleDateRangeChange = (value: DateRangeValues) => { |
| 98 | + dispatch(setTopQueriesFilters(value)); |
| 99 | + }; |
| 100 | + |
| 101 | + const renderQueryModeControl = React.useCallback(() => { |
| 102 | + return ( |
| 103 | + <RadioButton options={QUERY_MODE_OPTIONS} value={queryMode} onUpdate={setQueryMode} /> |
| 104 | + ); |
| 105 | + }, [queryMode, setQueryMode]); |
| 106 | + |
| 107 | + return isTopQueries ? ( |
| 108 | + <TopQueriesData |
| 109 | + tenantName={tenantName} |
| 110 | + timeFrame={timeFrame} |
| 111 | + renderQueryModeControl={renderQueryModeControl} |
| 112 | + onRowClick={onRowClick} |
| 113 | + handleTimeFrameChange={handleTimeFrameChange} |
| 114 | + handleDateRangeChange={handleDateRangeChange} |
| 115 | + handleTextSearchUpdate={handleTextSearchUpdate} |
| 116 | + /> |
| 117 | + ) : ( |
| 118 | + <RunningQueriesData |
| 119 | + tenantName={tenantName} |
| 120 | + renderQueryModeControl={renderQueryModeControl} |
| 121 | + onRowClick={onRowClick} |
| 122 | + handleTextSearchUpdate={handleTextSearchUpdate} |
| 123 | + /> |
132 | 124 | ); |
133 | 125 | }; |
0 commit comments