mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 03:49:00 +08:00
fix: exporer log details action buttons (#3126)
* fix: exporer log details action buttons * chore: magic strings is removed --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
60c0836d3e
commit
d26022efb1
@ -1,7 +1,9 @@
|
||||
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
|
||||
import { ActionItemProps } from 'container/LogDetailedView/ActionItem';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
|
||||
export type LogDetailProps = {
|
||||
log: ILog | null;
|
||||
onClose: () => void;
|
||||
} & Pick<AddToQueryHOCProps, 'onAddToQuery'>;
|
||||
} & Pick<AddToQueryHOCProps, 'onAddToQuery'> &
|
||||
Pick<ActionItemProps, 'onClickActionItem'>;
|
||||
|
@ -8,6 +8,7 @@ function LogDetail({
|
||||
log,
|
||||
onClose,
|
||||
onAddToQuery,
|
||||
onClickActionItem,
|
||||
}: LogDetailProps): JSX.Element {
|
||||
const onDrawerClose = (): void => {
|
||||
onClose();
|
||||
@ -17,7 +18,13 @@ function LogDetail({
|
||||
{
|
||||
label: 'Table',
|
||||
key: '1',
|
||||
children: log && <TableView logData={log} onAddToQuery={onAddToQuery} />,
|
||||
children: log && (
|
||||
<TableView
|
||||
logData={log}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onClickActionItem={onClickActionItem}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: 'JSON',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Popover } from 'antd';
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import { memo, ReactNode, useCallback, useMemo } from 'react';
|
||||
|
||||
import { ButtonContainer } from './styles';
|
||||
@ -10,7 +11,7 @@ function AddToQueryHOC({
|
||||
children,
|
||||
}: AddToQueryHOCProps): JSX.Element {
|
||||
const handleQueryAdd = useCallback(() => {
|
||||
onAddToQuery(fieldKey, fieldValue);
|
||||
onAddToQuery(fieldKey, fieldValue, OPERATORS.IN);
|
||||
}, [fieldKey, fieldValue, onAddToQuery]);
|
||||
|
||||
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
||||
@ -29,7 +30,7 @@ function AddToQueryHOC({
|
||||
export interface AddToQueryHOCProps {
|
||||
fieldKey: string;
|
||||
fieldValue: string;
|
||||
onAddToQuery: (fieldKey: string, fieldValue: string) => void;
|
||||
onAddToQuery: (fieldKey: string, fieldValue: string, operator: string) => void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
|
@ -1,148 +1,43 @@
|
||||
import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
|
||||
import { Button, Col, Popover } from 'antd';
|
||||
import getStep from 'lib/getStep';
|
||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||
import { getIdConditions } from 'pages/Logs/utils';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { getLogs } from 'store/actions/logs/getLogs';
|
||||
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import { removeJSONStringifyQuotes } from 'lib/removeJSONStringifyQuotes';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
|
||||
const removeJSONStringifyQuotes = (s: string): string => {
|
||||
if (!s || !s.length) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (s[0] === '"' && s[s.length - 1] === '"') {
|
||||
return s.slice(1, s.length - 1);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
interface ActionItemProps {
|
||||
fieldKey: string;
|
||||
fieldValue: string;
|
||||
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
|
||||
getLogsAggregate: (
|
||||
props: Parameters<typeof getLogsAggregate>[0],
|
||||
) => ReturnType<typeof getLogsAggregate>;
|
||||
}
|
||||
function ActionItem({
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
getLogs,
|
||||
getLogsAggregate,
|
||||
}: ActionItemProps): JSX.Element | unknown {
|
||||
const {
|
||||
searchFilter: { queryString },
|
||||
logLinesPerPage,
|
||||
idStart,
|
||||
liveTail,
|
||||
idEnd,
|
||||
order,
|
||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
onClickActionItem,
|
||||
}: ActionItemProps): JSX.Element {
|
||||
const handleClick = useCallback(
|
||||
(operator: string) => {
|
||||
const validatedFieldValue = removeJSONStringifyQuotes(fieldValue);
|
||||
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
onClickActionItem(fieldKey, validatedFieldValue, operator);
|
||||
},
|
||||
[onClickActionItem, fieldKey, fieldValue],
|
||||
);
|
||||
|
||||
const handleQueryAdd = (newQueryString: string): void => {
|
||||
let updatedQueryString = queryString || '';
|
||||
const onClickHandler = useCallback(
|
||||
(operator: string) => (): void => {
|
||||
handleClick(operator);
|
||||
},
|
||||
[handleClick],
|
||||
);
|
||||
|
||||
if (updatedQueryString.length === 0) {
|
||||
updatedQueryString += `${newQueryString}`;
|
||||
} else {
|
||||
updatedQueryString += ` AND ${newQueryString}`;
|
||||
}
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_STRING,
|
||||
payload: {
|
||||
searchQueryString: updatedQueryString,
|
||||
},
|
||||
});
|
||||
|
||||
if (liveTail === 'STOPPED') {
|
||||
getLogs({
|
||||
q: updatedQueryString,
|
||||
limit: logLinesPerPage,
|
||||
orderBy: 'timestamp',
|
||||
order,
|
||||
timestampStart: minTime,
|
||||
timestampEnd: maxTime,
|
||||
...getIdConditions(idStart, idEnd, order),
|
||||
});
|
||||
getLogsAggregate({
|
||||
timestampStart: minTime,
|
||||
timestampEnd: maxTime,
|
||||
step: getStep({
|
||||
start: minTime,
|
||||
end: maxTime,
|
||||
inputFormat: 'ns',
|
||||
}),
|
||||
q: updatedQueryString,
|
||||
});
|
||||
} else if (liveTail === 'PLAYING') {
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
payload: 'PAUSED',
|
||||
});
|
||||
setTimeout(
|
||||
() =>
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
payload: liveTail,
|
||||
}),
|
||||
0,
|
||||
);
|
||||
}
|
||||
};
|
||||
const validatedFieldValue = removeJSONStringifyQuotes(fieldValue);
|
||||
const PopOverMenuContent = useMemo(
|
||||
() => (
|
||||
<Col>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={(): void =>
|
||||
handleQueryAdd(
|
||||
generateFilterQuery({
|
||||
fieldKey,
|
||||
fieldValue: validatedFieldValue,
|
||||
type: 'IN',
|
||||
}),
|
||||
)
|
||||
}
|
||||
>
|
||||
<Button type="text" size="small" onClick={onClickHandler(OPERATORS.IN)}>
|
||||
<PlusCircleOutlined /> Filter for value
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={(): void =>
|
||||
handleQueryAdd(
|
||||
generateFilterQuery({
|
||||
fieldKey,
|
||||
fieldValue: validatedFieldValue,
|
||||
type: 'NIN',
|
||||
}),
|
||||
)
|
||||
}
|
||||
>
|
||||
<Button type="text" size="small" onClick={onClickHandler(OPERATORS.NIN)}>
|
||||
<MinusCircleOutlined /> Filter out value
|
||||
</Button>
|
||||
</Col>
|
||||
),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[fieldKey, validatedFieldValue],
|
||||
[onClickHandler],
|
||||
);
|
||||
return (
|
||||
<Popover placement="bottomLeft" content={PopOverMenuContent} trigger="click">
|
||||
@ -152,19 +47,15 @@ function ActionItem({
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
interface DispatchProps {
|
||||
getLogs: (props: Parameters<typeof getLogs>[0]) => (dispatch: never) => void;
|
||||
getLogsAggregate: (
|
||||
props: Parameters<typeof getLogsAggregate>[0],
|
||||
) => (dispatch: never) => void;
|
||||
|
||||
export interface ActionItemProps {
|
||||
fieldKey: string;
|
||||
fieldValue: string;
|
||||
onClickActionItem: (
|
||||
fieldKey: string,
|
||||
fieldValue: string,
|
||||
operator: string,
|
||||
) => void;
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (
|
||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
||||
): DispatchProps => ({
|
||||
getLogs: bindActionCreators(getLogs, dispatch),
|
||||
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export default connect(null, mapDispatchToProps)(memo(ActionItem as any));
|
||||
export default memo(ActionItem);
|
||||
|
@ -20,7 +20,7 @@ import AppActions from 'types/actions';
|
||||
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
|
||||
import ActionItem from './ActionItem';
|
||||
import ActionItem, { ActionItemProps } from './ActionItem';
|
||||
import { flattenObject, recursiveParseJSON } from './utils';
|
||||
|
||||
// Fields which should be restricted from adding it to query
|
||||
@ -30,9 +30,15 @@ interface TableViewProps {
|
||||
logData: ILog;
|
||||
}
|
||||
|
||||
type Props = TableViewProps & Pick<AddToQueryHOCProps, 'onAddToQuery'>;
|
||||
type Props = TableViewProps &
|
||||
Pick<AddToQueryHOCProps, 'onAddToQuery'> &
|
||||
Pick<ActionItemProps, 'onClickActionItem'>;
|
||||
|
||||
function TableView({ logData, onAddToQuery }: Props): JSX.Element | null {
|
||||
function TableView({
|
||||
logData,
|
||||
onAddToQuery,
|
||||
onClickActionItem,
|
||||
}: Props): JSX.Element | null {
|
||||
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
|
||||
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
@ -89,7 +95,13 @@ function TableView({ logData, onAddToQuery }: Props): JSX.Element | null {
|
||||
render: (fieldData: Record<string, string>): JSX.Element | null => {
|
||||
const fieldKey = fieldData.field.split('.').slice(-1);
|
||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
||||
return <ActionItem fieldKey={fieldKey} fieldValue={fieldData.value} />;
|
||||
return (
|
||||
<ActionItem
|
||||
fieldKey={fieldKey[0]}
|
||||
fieldValue={fieldData.value}
|
||||
onClickActionItem={onClickActionItem}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
@ -1,21 +1,49 @@
|
||||
import LogDetail from 'components/LogDetail';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||
import { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getGeneratedFilterQueryString } from 'lib/getGeneratedFilterQueryString';
|
||||
import getStep from 'lib/getStep';
|
||||
import { getIdConditions } from 'pages/Logs/utils';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Dispatch } from 'redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { getLogs } from 'store/actions/logs/getLogs';
|
||||
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
||||
import {
|
||||
SET_DETAILED_LOG_DATA,
|
||||
SET_SEARCH_QUERY_STRING,
|
||||
TOGGLE_LIVE_TAIL,
|
||||
} from 'types/actions/logs';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
function LogDetailedView(): JSX.Element {
|
||||
type LogDetailedViewProps = {
|
||||
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
|
||||
getLogsAggregate: (
|
||||
props: Parameters<typeof getLogsAggregate>[0],
|
||||
) => ReturnType<typeof getLogsAggregate>;
|
||||
};
|
||||
|
||||
function LogDetailedView({
|
||||
getLogs,
|
||||
getLogsAggregate,
|
||||
}: LogDetailedViewProps): JSX.Element {
|
||||
const history = useHistory();
|
||||
const {
|
||||
detailedLog,
|
||||
searchFilter: { queryString },
|
||||
logLinesPerPage,
|
||||
idStart,
|
||||
liveTail,
|
||||
idEnd,
|
||||
order,
|
||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
|
||||
@ -26,32 +54,109 @@ function LogDetailedView(): JSX.Element {
|
||||
});
|
||||
};
|
||||
|
||||
const handleQueryAdd = useCallback(
|
||||
(fieldKey: string, fieldValue: string) => {
|
||||
const generatedQuery = generateFilterQuery({
|
||||
const handleAddToQuery = useCallback(
|
||||
(fieldKey: string, fieldValue: string, operator: string) => {
|
||||
const updatedQueryString = getGeneratedFilterQueryString(
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
type: 'IN',
|
||||
});
|
||||
operator,
|
||||
queryString,
|
||||
);
|
||||
|
||||
let updatedQueryString = queryString || '';
|
||||
if (updatedQueryString.length === 0) {
|
||||
updatedQueryString += `${generatedQuery}`;
|
||||
} else {
|
||||
updatedQueryString += ` AND ${generatedQuery}`;
|
||||
}
|
||||
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||
},
|
||||
[history, queryString],
|
||||
);
|
||||
|
||||
const handleClickActionItem = useCallback(
|
||||
(fieldKey: string, fieldValue: string, operator: string): void => {
|
||||
const updatedQueryString = getGeneratedFilterQueryString(
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
operator,
|
||||
queryString,
|
||||
);
|
||||
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_STRING,
|
||||
payload: {
|
||||
searchQueryString: updatedQueryString,
|
||||
},
|
||||
});
|
||||
|
||||
if (liveTail === 'STOPPED') {
|
||||
getLogs({
|
||||
q: updatedQueryString,
|
||||
limit: logLinesPerPage,
|
||||
orderBy: 'timestamp',
|
||||
order,
|
||||
timestampStart: minTime,
|
||||
timestampEnd: maxTime,
|
||||
...getIdConditions(idStart, idEnd, order),
|
||||
});
|
||||
getLogsAggregate({
|
||||
timestampStart: minTime,
|
||||
timestampEnd: maxTime,
|
||||
step: getStep({
|
||||
start: minTime,
|
||||
end: maxTime,
|
||||
inputFormat: 'ns',
|
||||
}),
|
||||
q: updatedQueryString,
|
||||
});
|
||||
} else if (liveTail === 'PLAYING') {
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
payload: 'PAUSED',
|
||||
});
|
||||
setTimeout(
|
||||
() =>
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
payload: liveTail,
|
||||
}),
|
||||
0,
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
dispatch,
|
||||
getLogs,
|
||||
getLogsAggregate,
|
||||
idEnd,
|
||||
idStart,
|
||||
liveTail,
|
||||
logLinesPerPage,
|
||||
maxTime,
|
||||
minTime,
|
||||
order,
|
||||
queryString,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<LogDetail
|
||||
log={detailedLog}
|
||||
onClose={onDrawerClose}
|
||||
onAddToQuery={handleQueryAdd}
|
||||
onAddToQuery={handleAddToQuery}
|
||||
onClickActionItem={handleClickActionItem}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default LogDetailedView;
|
||||
interface DispatchProps {
|
||||
getLogs: (props: Parameters<typeof getLogs>[0]) => (dispatch: never) => void;
|
||||
getLogsAggregate: (
|
||||
props: Parameters<typeof getLogsAggregate>[0],
|
||||
) => (dispatch: never) => void;
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (
|
||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
||||
): DispatchProps => ({
|
||||
getLogs: bindActionCreators(getLogs, dispatch),
|
||||
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export default connect(null, mapDispatchToProps)(memo(LogDetailedView as any));
|
||||
|
@ -5,6 +5,7 @@ import TabLabel from 'components/TabLabel';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import {
|
||||
initialQueriesMap,
|
||||
OPERATORS,
|
||||
PANEL_TYPES,
|
||||
QueryBuilderKeys,
|
||||
} from 'constants/queryBuilder';
|
||||
@ -226,8 +227,8 @@ function LogsExplorerViews(): JSX.Element {
|
||||
[currentStagedQueryData, orderByTimestamp],
|
||||
);
|
||||
|
||||
const handleAddQuery = useCallback(
|
||||
(fieldKey: string, fieldValue: string): void => {
|
||||
const handleAddToQuery = useCallback(
|
||||
(fieldKey: string, fieldValue: string, operator: string): void => {
|
||||
const keysAutocomplete: BaseAutocompleteData[] =
|
||||
queryClient.getQueryData<SuccessResponse<IQueryAutocompleteResponse>>(
|
||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS],
|
||||
@ -239,6 +240,9 @@ function LogsExplorerViews(): JSX.Element {
|
||||
fieldKey,
|
||||
);
|
||||
|
||||
const currentOperator =
|
||||
Object.keys(OPERATORS).find((op) => op === operator) || '';
|
||||
|
||||
const nextQuery: Query = {
|
||||
...currentQuery,
|
||||
builder: {
|
||||
@ -254,7 +258,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
{
|
||||
id: uuid(),
|
||||
key: existAutocompleteKey,
|
||||
op: '=',
|
||||
op: currentOperator,
|
||||
value: fieldValue,
|
||||
},
|
||||
],
|
||||
@ -422,7 +426,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
onOpenDetailedView={handleSetActiveLog}
|
||||
onEndReached={handleEndReached}
|
||||
onExpand={handleSetActiveLog}
|
||||
onAddToQuery={handleAddQuery}
|
||||
onAddToQuery={handleAddToQuery}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@ -453,7 +457,7 @@ function LogsExplorerViews(): JSX.Element {
|
||||
logs,
|
||||
handleSetActiveLog,
|
||||
handleEndReached,
|
||||
handleAddQuery,
|
||||
handleAddToQuery,
|
||||
data,
|
||||
isError,
|
||||
],
|
||||
@ -506,7 +510,8 @@ function LogsExplorerViews(): JSX.Element {
|
||||
<LogDetail
|
||||
log={activeLog}
|
||||
onClose={handleClearActiveLog}
|
||||
onAddToQuery={handleAddQuery}
|
||||
onAddToQuery={handleAddToQuery}
|
||||
onClickActionItem={handleAddToQuery}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -7,7 +7,7 @@ import Spinner from 'components/Spinner';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { contentStyle } from 'container/Trace/Search/config';
|
||||
import useFontFaceObserver from 'hooks/useFontObserver';
|
||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||
import { getGeneratedFilterQueryString } from 'lib/getGeneratedFilterQueryString';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
@ -77,20 +77,15 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
const handleQueryAdd = useCallback(
|
||||
(fieldKey: string, fieldValue: string) => {
|
||||
const generatedQuery = generateFilterQuery({
|
||||
const handleAddToQuery = useCallback(
|
||||
(fieldKey: string, fieldValue: string, operator: string) => {
|
||||
const updatedQueryString = getGeneratedFilterQueryString(
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
type: 'IN',
|
||||
});
|
||||
operator,
|
||||
queryString,
|
||||
);
|
||||
|
||||
let updatedQueryString = queryString || '';
|
||||
if (updatedQueryString.length === 0) {
|
||||
updatedQueryString += `${generatedQuery}`;
|
||||
} else {
|
||||
updatedQueryString += ` AND ${generatedQuery}`;
|
||||
}
|
||||
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||
},
|
||||
[history, queryString],
|
||||
@ -117,7 +112,7 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
logData={log}
|
||||
selectedFields={selected}
|
||||
onOpenDetailedView={handleOpenDetailedView}
|
||||
onAddToQuery={handleQueryAdd}
|
||||
onAddToQuery={handleAddToQuery}
|
||||
/>
|
||||
);
|
||||
},
|
||||
@ -128,7 +123,7 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
linesPerRow,
|
||||
onClickExpand,
|
||||
handleOpenDetailedView,
|
||||
handleQueryAdd,
|
||||
handleAddToQuery,
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -34,8 +34,12 @@ export const useTag = (
|
||||
() =>
|
||||
(query?.filters?.items || []).map((ele) => {
|
||||
if (isInNInOperator(getOperatorFromValue(ele.op))) {
|
||||
const csvString = Papa.unparse([ele.value]);
|
||||
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${csvString}`;
|
||||
try {
|
||||
const csvString = Papa.unparse([ele.value]);
|
||||
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${csvString}`;
|
||||
} catch {
|
||||
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${ele.value}`;
|
||||
}
|
||||
}
|
||||
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${ele.value}`;
|
||||
}),
|
||||
|
24
frontend/src/lib/getGeneratedFilterQueryString.ts
Normal file
24
frontend/src/lib/getGeneratedFilterQueryString.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { generateFilterQuery } from './logs/generateFilterQuery';
|
||||
|
||||
export const getGeneratedFilterQueryString = (
|
||||
fieldKey: string,
|
||||
fieldValue: string,
|
||||
operator: string,
|
||||
queryString: string,
|
||||
): string => {
|
||||
let updatedQueryString = queryString || '';
|
||||
|
||||
const generatedString = generateFilterQuery({
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
type: operator,
|
||||
});
|
||||
|
||||
if (updatedQueryString.length === 0) {
|
||||
updatedQueryString += `${generatedString}`;
|
||||
} else {
|
||||
updatedQueryString += ` AND ${generatedString}`;
|
||||
}
|
||||
|
||||
return updatedQueryString;
|
||||
};
|
10
frontend/src/lib/removeJSONStringifyQuotes.ts
Normal file
10
frontend/src/lib/removeJSONStringifyQuotes.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export const removeJSONStringifyQuotes = (s: string): string => {
|
||||
if (!s || !s.length) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (s[0] === '"' && s[s.length - 1] === '"') {
|
||||
return s.slice(1, s.length - 1);
|
||||
}
|
||||
return s;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user