From 17c4c26ea8e71163b611368c1ae0b295e1d5995b Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Fri, 14 Feb 2025 17:18:20 +0300 Subject: [PATCH] fix query log --- client/src/actions/queryLogs.ts | 22 +- client/src/components/Logs/Filters/Form.tsx | 32 +- .../components/Logs/Filters/SearchField.tsx | 6 + client/src/components/Logs/Filters/index.tsx | 6 +- client/src/components/Logs/InfiniteTable.tsx | 4 +- client/src/components/Logs/index.tsx | 36 ++- client/src/helpers/form.tsx | 299 ------------------ client/src/helpers/helpers.tsx | 2 - client/src/install/Setup/Settings.tsx | 5 +- 9 files changed, 57 insertions(+), 355 deletions(-) diff --git a/client/src/actions/queryLogs.ts b/client/src/actions/queryLogs.ts index e4d0ab31..70dc568a 100644 --- a/client/src/actions/queryLogs.ts +++ b/client/src/actions/queryLogs.ts @@ -3,8 +3,9 @@ import { createAction } from 'redux-actions'; import apiClient from '../api/Api'; import { normalizeLogs } from '../helpers/helpers'; -import { DEFAULT_LOGS_FILTER, FORM_NAME, QUERY_LOGS_PAGE_LIMIT } from '../helpers/constants'; +import { DEFAULT_LOGS_FILTER, QUERY_LOGS_PAGE_LIMIT } from '../helpers/constants'; import { addErrorToast, addSuccessToast } from './toasts'; +import { SearchFormValues } from '../components/Logs'; const getLogsWithParams = async (config: any) => { const { older_than, filter, ...values } = config; @@ -27,12 +28,10 @@ export const getAdditionalLogsRequest = createAction('GET_ADDITIONAL_LOGS_REQUES export const getAdditionalLogsFailure = createAction('GET_ADDITIONAL_LOGS_FAILURE'); export const getAdditionalLogsSuccess = createAction('GET_ADDITIONAL_LOGS_SUCCESS'); -const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, getState: any, total?: any) => { +const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, currentQuery?: string, total?: any) => { const { logs, oldest } = data; const totalData = total || { logs }; - const queryForm = getState().form[FORM_NAME.LOGS_FILTER]; - const currentQuery = queryForm && queryForm.values.search; const previousQuery = filter?.search; const isQueryTheSame = typeof previousQuery === 'string' && typeof currentQuery === 'string' && previousQuery === currentQuery; @@ -51,7 +50,7 @@ const shortPollQueryLogs = async (data: any, filter: any, dispatch: any, getStat filter, }); if (additionalLogs.oldest.length > 0) { - return await shortPollQueryLogs(additionalLogs, filter, dispatch, getState, { + return await shortPollQueryLogs(additionalLogs, filter, dispatch, currentQuery, { logs: [...totalData.logs, ...additionalLogs.logs], oldest: additionalLogs.oldest, }); @@ -91,17 +90,18 @@ export const updateLogs = () => async (dispatch: any, getState: any) => { } }; -export const getLogs = () => async (dispatch: any, getState: any) => { +export const getLogs = (currentQuery?: string) => async (dispatch: any, getState: any) => { dispatch(getLogsRequest()); try { const { isFiltered, filter, oldest } = getState().queryLogs; + const data = await getLogsWithParams({ older_than: oldest, filter, }); if (isFiltered) { - const additionalData = await shortPollQueryLogs(data, filter, dispatch, getState); + const additionalData = await shortPollQueryLogs(data, filter, dispatch, currentQuery); const updatedData = additionalData.logs ? { ...data, ...additionalData } : data; dispatch(getLogsSuccess(updatedData)); } else { @@ -122,13 +122,13 @@ export const setLogsFilterRequest = createAction('SET_LOGS_FILTER_REQUEST'); * @param {string} filter.response_status 'QUERY' field of RESPONSE_FILTER object * @returns function */ -export const setLogsFilter = (filter: any) => setLogsFilterRequest(filter); +export const setLogsFilter = (filter: SearchFormValues) => setLogsFilterRequest(filter); export const setFilteredLogsRequest = createAction('SET_FILTERED_LOGS_REQUEST'); export const setFilteredLogsFailure = createAction('SET_FILTERED_LOGS_FAILURE'); export const setFilteredLogsSuccess = createAction('SET_FILTERED_LOGS_SUCCESS'); -export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState: any) => { +export const setFilteredLogs = (filter?: SearchFormValues) => async (dispatch: any) => { dispatch(setFilteredLogsRequest()); try { const data = await getLogsWithParams({ @@ -136,7 +136,9 @@ export const setFilteredLogs = (filter?: any) => async (dispatch: any, getState: filter, }); - const additionalData = await shortPollQueryLogs(data, filter, dispatch, getState); + const currentQuery = filter?.search; + + const additionalData = await shortPollQueryLogs(data, filter, dispatch, currentQuery); const updatedData = additionalData.logs ? { ...data, ...additionalData } : data; dispatch( diff --git a/client/src/components/Logs/Filters/Form.tsx b/client/src/components/Logs/Filters/Form.tsx index 46b92128..3714f308 100644 --- a/client/src/components/Logs/Filters/Form.tsx +++ b/client/src/components/Logs/Filters/Form.tsx @@ -5,7 +5,7 @@ import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; import classNames from 'classnames'; -import { useForm } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form'; import { DEBOUNCE_FILTER_TIMEOUT, DEFAULT_LOGS_FILTER, @@ -15,33 +15,22 @@ import { import { setLogsFilter } from '../../../actions/queryLogs'; import useDebounce from '../../../helpers/useDebounce'; -import { createOnBlurHandler, getLogsUrlParams } from '../../../helpers/helpers'; +import { getLogsUrlParams } from '../../../helpers/helpers'; import { SearchField } from './SearchField'; - -export type FormValues = { - search: string; - response_status: string; -}; +import { SearchFormValues } from '..'; type Props = { - initialValues: FormValues; className?: string; setIsLoading: (value: boolean) => void; }; -export const Form = ({ initialValues, className, setIsLoading }: Props) => { +export const Form = ({ className, setIsLoading }: Props) => { const { t } = useTranslation(); const dispatch = useDispatch(); const history = useHistory(); - const { register, watch, setValue } = useForm({ - mode: 'onBlur', - defaultValues: { - search: initialValues.search || DEFAULT_LOGS_FILTER.search, - response_status: initialValues.response_status || DEFAULT_LOGS_FILTER.response_status, - }, - }); + const { register, watch, setValue } = useFormContext(); const searchValue = watch('search'); const responseStatusValue = watch('response_status'); @@ -77,16 +66,6 @@ export const Form = ({ initialValues, className, setIsLoading }: Props) => { } }; - const handleBlur = (e: React.FocusEvent) => - createOnBlurHandler( - e, - { - value: e.target.value, - onChange: (v: string) => setValue('search', v), - }, - (data: string) => data.trim(), - ); - return (
{ setValue('search', val)} - onBlur={handleBlur} onKeyDown={onEnterPress} onClear={onInputClear} placeholder={t('domain_or_client')} diff --git a/client/src/components/Logs/Filters/SearchField.tsx b/client/src/components/Logs/Filters/SearchField.tsx index 1113bba0..0ee39f26 100644 --- a/client/src/components/Logs/Filters/SearchField.tsx +++ b/client/src/components/Logs/Filters/SearchField.tsx @@ -19,6 +19,11 @@ export const SearchField = ({ handleChange(e.target.value); }; + const handleBlur = (e: React.FocusEvent) => { + e.target.value = e.target.value.trim(); + handleChange(e.target.value) + } + return ( <>
@@ -30,6 +35,7 @@ export const SearchField = ({ className={className} value={value} onChange={handleInputChange} + onBlur={handleBlur} {...rest} /> {typeof value === 'string' && value.length > 0 && ( diff --git a/client/src/components/Logs/Filters/index.tsx b/client/src/components/Logs/Filters/index.tsx index d2c5c37a..b4a332dc 100644 --- a/client/src/components/Logs/Filters/index.tsx +++ b/client/src/components/Logs/Filters/index.tsx @@ -2,17 +2,16 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; -import { Form, FormValues } from './Form'; +import { Form } from './Form'; import { refreshFilteredLogs } from '../../../actions/queryLogs'; import { addSuccessToast } from '../../../actions/toasts'; interface FiltersProps { - initialValues: FormValues; processingGetLogs: boolean; setIsLoading: (...args: unknown[]) => unknown; } -const Filters = ({ initialValues, setIsLoading }: FiltersProps) => { +const Filters = ({ setIsLoading }: FiltersProps) => { const { t } = useTranslation(); const dispatch = useDispatch(); @@ -40,7 +39,6 @@ const Filters = ({ initialValues, setIsLoading }: FiltersProps) => {
); diff --git a/client/src/components/Logs/InfiniteTable.tsx b/client/src/components/Logs/InfiniteTable.tsx index b6b9ca17..e1483640 100644 --- a/client/src/components/Logs/InfiniteTable.tsx +++ b/client/src/components/Logs/InfiniteTable.tsx @@ -18,6 +18,7 @@ interface InfiniteTableProps { isLoading: boolean; items: unknown[]; isSmallScreen: boolean; + currentQuery: string; setDetailedDataCurrent: Dispatch>; setButtonType: (...args: unknown[]) => unknown; setModalOpened: (...args: unknown[]) => unknown; @@ -27,6 +28,7 @@ const InfiniteTable = ({ isLoading, items, isSmallScreen, + currentQuery, setDetailedDataCurrent, setButtonType, setModalOpened, @@ -43,7 +45,7 @@ const InfiniteTable = ({ const listener = useCallback(() => { if (!loadingRef.current && loader.current && isScrolledIntoView(loader.current)) { - dispatch(getLogs()); + dispatch(getLogs(currentQuery)); } }, []); diff --git a/client/src/components/Logs/index.tsx b/client/src/components/Logs/index.tsx index 6bf2abfa..8f9013bb 100644 --- a/client/src/components/Logs/index.tsx +++ b/client/src/components/Logs/index.tsx @@ -7,7 +7,8 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import queryString from 'query-string'; import classNames from 'classnames'; -import { BLOCK_ACTIONS, MEDIUM_SCREEN_SIZE } from '../../helpers/constants'; +import { FormProvider, useForm } from 'react-hook-form'; +import { BLOCK_ACTIONS, DEFAULT_LOGS_FILTER, MEDIUM_SCREEN_SIZE } from '../../helpers/constants'; import Loading from '../ui/Loading'; @@ -29,6 +30,11 @@ import { BUTTON_PREFIX } from './Cells/helpers'; import AnonymizerNotification from './AnonymizerNotification'; import { RootState } from '../../initialState'; +export type SearchFormValues = { + search: string; + response_status: string; +}; + const processContent = (data: any, _buttonType: string) => Object.entries(data).map(([key, value]) => { if (!value) { @@ -76,7 +82,6 @@ const Logs = () => { const { enabled, processingGetConfig, - // processingAdditionalLogs, processingGetLogs, anonymize_client_ip: anonymizeClientIp, } = useSelector((state: RootState) => state.queryLogs, shallowEqual); @@ -88,6 +93,17 @@ const Logs = () => { const search = search_url_param || filter?.search || ''; const response_status = response_status_url_param || filter?.response_status || ''; + const formMethods = useForm({ + mode: 'onBlur', + defaultValues: { + search: search || DEFAULT_LOGS_FILTER.search, + response_status: response_status || DEFAULT_LOGS_FILTER.response_status, + }, + }); + + const { watch } = formMethods; + const currentQuery = watch('search'); + const [isSmallScreen, setIsSmallScreen] = useState(window.innerWidth <= MEDIUM_SCREEN_SIZE); const [detailedDataCurrent, setDetailedDataCurrent] = useState({}); const [buttonType, setButtonType] = useState(BLOCK_ACTIONS.BLOCK); @@ -174,15 +190,12 @@ const Logs = () => { const renderPage = () => ( <> - + + + { setDetailedDataCurrent={setDetailedDataCurrent} setButtonType={setButtonType} setModalOpened={setModalOpened} + currentQuery={currentQuery} /> unknown; - min?: number; - max?: number; - step?: number; - onScroll?: (...args: unknown[]) => unknown; - meta: { - touched?: boolean; - error?: string; - }; -} - -export const renderField = (props: renderFieldProps, elementType: any) => { - const { - input, - id, - className, - placeholder, - type, - disabled, - normalizeOnBlur, - onScroll, - autoComplete, - meta: { touched, error }, - min, - max, - step, - } = props; - - const onBlur = (event: any) => createOnBlurHandler(event, input, normalizeOnBlur); - - const element = React.createElement(elementType, { - ...input, - id, - className, - placeholder, - autoComplete, - disabled, - type, - min, - max, - step, - onBlur, - onScroll, - }); - - return ( - <> - {element} - {!disabled && touched && error && ( - - {error} - - )} - - ); -}; - -export const renderTextareaField = (props: any) => renderField(props, 'textarea'); - -export const renderInputField = (props: any) => renderField(props, 'input'); - -interface renderGroupFieldProps { - input: object; - id?: string; - className?: string; - placeholder?: string; - type?: string; - disabled?: boolean; - autoComplete?: string; - isActionAvailable?: boolean; - removeField?: (...args: unknown[]) => unknown; - meta: { - touched?: boolean; - error?: string; - }; - normalizeOnBlur?: (...args: unknown[]) => unknown; -} - -export const renderGroupField = ({ - input, - id, - className, - placeholder, - type, - disabled, - autoComplete, - isActionAvailable, - removeField, - meta: { touched, error }, - normalizeOnBlur, -}: renderGroupFieldProps) => { - const onBlur = (event: any) => createOnBlurHandler(event, input, normalizeOnBlur); - - return ( - <> -
- - {isActionAvailable && ( - - - - )} -
- {!disabled && touched && error && ( - - {error} - - )} - - ); -}; - -interface renderRadioFieldProps { - input: object; - placeholder?: string; - subtitle?: string; - disabled?: boolean; - meta: { - touched?: boolean; - error?: string; - }; -} - -export const renderRadioField = ({ - input, - placeholder, - subtitle, - disabled, - meta: { touched, error }, -}: renderRadioFieldProps) => ( - - - {!disabled && touched && error && ( - - {error} - - )} - -); - -interface CheckboxFieldProps { - input: object; - placeholder?: string; - subtitle?: React.ReactNode; - disabled?: boolean; - onClick?: (...args: unknown[]) => unknown; - modifier?: string; - checked?: boolean; - meta: { - touched?: boolean; - error?: string; - }; -} - -export const CheckboxField = ({ - input, - placeholder, - subtitle, - disabled, - onClick, - modifier = 'checkbox--form', - meta: { touched, error }, -}: CheckboxFieldProps) => ( - <> - - {!disabled && touched && error && ( -
- {error} -
- )} - -); - -interface renderSelectFieldProps { - input: object; - disabled?: boolean; - label?: string; - children: unknown[] | React.ReactElement; - meta: { - touched?: boolean; - error?: string; - }; -} - -export const renderSelectField = ({ input, meta: { touched, error }, children, label }: renderSelectFieldProps) => { - const showWarning = touched && error; - - return ( - <> - {label && ( - - )} - - - {showWarning && ( - - {error} - - )} - - ); -}; - -interface renderServiceFieldProps { - input: object; - placeholder?: string; - disabled?: boolean; - modifier?: string; - icon?: string; - meta: { - touched?: boolean; - error?: string; - }; -} - -export const renderServiceField = ({ - input, - placeholder, - disabled, - modifier, - icon, - meta: { touched, error }, -}: renderServiceFieldProps) => ( - <> -