mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-28 16:02:03 +08:00
Merge branch 'develop' into chore/hotrod-locust
This commit is contained in:
commit
d7f5e5d6ac
@ -1,3 +1,7 @@
|
|||||||
|
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
export type LogDetailProps = { log: ILog | null; onClose: () => void };
|
export type LogDetailProps = {
|
||||||
|
log: ILog | null;
|
||||||
|
onClose: () => void;
|
||||||
|
} & Pick<AddToQueryHOCProps, 'onAddToQuery'>;
|
||||||
|
@ -4,7 +4,11 @@ import TableView from 'container/LogDetailedView/TableView';
|
|||||||
|
|
||||||
import { LogDetailProps } from './LogDetail.interfaces';
|
import { LogDetailProps } from './LogDetail.interfaces';
|
||||||
|
|
||||||
function LogDetail({ log, onClose }: LogDetailProps): JSX.Element {
|
function LogDetail({
|
||||||
|
log,
|
||||||
|
onClose,
|
||||||
|
onAddToQuery,
|
||||||
|
}: LogDetailProps): JSX.Element {
|
||||||
const onDrawerClose = (): void => {
|
const onDrawerClose = (): void => {
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
@ -13,7 +17,7 @@ function LogDetail({ log, onClose }: LogDetailProps): JSX.Element {
|
|||||||
{
|
{
|
||||||
label: 'Table',
|
label: 'Table',
|
||||||
key: '1',
|
key: '1',
|
||||||
children: log && <TableView logData={log} />,
|
children: log && <TableView logData={log} onAddToQuery={onAddToQuery} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'JSON',
|
label: 'JSON',
|
||||||
|
@ -1,39 +1,17 @@
|
|||||||
import { Popover } from 'antd';
|
import { Popover } from 'antd';
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import history from 'lib/history';
|
|
||||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
|
||||||
import { memo, ReactNode, useCallback, useMemo } from 'react';
|
import { memo, ReactNode, useCallback, useMemo } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
|
||||||
|
|
||||||
import { ButtonContainer } from './styles';
|
import { ButtonContainer } from './styles';
|
||||||
|
|
||||||
function AddToQueryHOC({
|
function AddToQueryHOC({
|
||||||
fieldKey,
|
fieldKey,
|
||||||
fieldValue,
|
fieldValue,
|
||||||
|
onAddToQuery,
|
||||||
children,
|
children,
|
||||||
}: AddToQueryHOCProps): JSX.Element {
|
}: AddToQueryHOCProps): JSX.Element {
|
||||||
const {
|
|
||||||
searchFilter: { queryString },
|
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
|
||||||
|
|
||||||
const generatedQuery = useMemo(
|
|
||||||
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
|
|
||||||
[fieldKey, fieldValue],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleQueryAdd = useCallback(() => {
|
const handleQueryAdd = useCallback(() => {
|
||||||
let updatedQueryString = queryString || '';
|
onAddToQuery(fieldKey, fieldValue);
|
||||||
|
}, [fieldKey, fieldValue, onAddToQuery]);
|
||||||
if (updatedQueryString.length === 0) {
|
|
||||||
updatedQueryString += `${generatedQuery}`;
|
|
||||||
} else {
|
|
||||||
updatedQueryString += ` AND ${generatedQuery}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
|
||||||
}, [generatedQuery, queryString]);
|
|
||||||
|
|
||||||
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
||||||
fieldKey,
|
fieldKey,
|
||||||
@ -48,9 +26,10 @@ function AddToQueryHOC({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddToQueryHOCProps {
|
export interface AddToQueryHOCProps {
|
||||||
fieldKey: string;
|
fieldKey: string;
|
||||||
fieldValue: string;
|
fieldValue: string;
|
||||||
|
onAddToQuery: (fieldKey: string, fieldValue: string) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,16 +2,22 @@ import { blue, grey, orange } from '@ant-design/colors';
|
|||||||
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
|
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
|
||||||
import Convert from 'ansi-to-html';
|
import Convert from 'ansi-to-html';
|
||||||
import { Button, Divider, Row, Typography } from 'antd';
|
import { Button, Divider, Row, Typography } from 'antd';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import dompurify from 'dompurify';
|
import dompurify from 'dompurify';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
// utils
|
// utils
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
// interfaces
|
// interfaces
|
||||||
import { IField } from 'types/api/logs/fields';
|
import { IField } from 'types/api/logs/fields';
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import AddToQueryHOC from '../AddToQueryHOC';
|
import AddToQueryHOC from '../AddToQueryHOC';
|
||||||
@ -57,9 +63,37 @@ function LogSelectedField({
|
|||||||
fieldKey = '',
|
fieldKey = '',
|
||||||
fieldValue = '',
|
fieldValue = '',
|
||||||
}: LogFieldProps): JSX.Element {
|
}: LogFieldProps): JSX.Element {
|
||||||
|
const history = useHistory();
|
||||||
|
const {
|
||||||
|
searchFilter: { queryString },
|
||||||
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
|
|
||||||
|
const handleQueryAdd = useCallback(
|
||||||
|
(fieldKey: string, fieldValue: string) => {
|
||||||
|
const generatedQuery = generateFilterQuery({
|
||||||
|
fieldKey,
|
||||||
|
fieldValue,
|
||||||
|
type: 'IN',
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedQueryString = queryString || '';
|
||||||
|
if (updatedQueryString.length === 0) {
|
||||||
|
updatedQueryString += `${generatedQuery}`;
|
||||||
|
} else {
|
||||||
|
updatedQueryString += ` AND ${generatedQuery}`;
|
||||||
|
}
|
||||||
|
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||||
|
},
|
||||||
|
[history, queryString],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectedLog>
|
<SelectedLog>
|
||||||
<AddToQueryHOC fieldKey={fieldKey} fieldValue={fieldValue}>
|
<AddToQueryHOC
|
||||||
|
fieldKey={fieldKey}
|
||||||
|
fieldValue={fieldValue}
|
||||||
|
onAddToQuery={handleQueryAdd}
|
||||||
|
>
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
<span style={{ color: blue[4] }}>{fieldKey}</span>
|
<span style={{ color: blue[4] }}>{fieldKey}</span>
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
@ -66,7 +66,6 @@ export const alphabet: string[] = alpha.map((str) => String.fromCharCode(str));
|
|||||||
export enum QueryBuilderKeys {
|
export enum QueryBuilderKeys {
|
||||||
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
||||||
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
|
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
|
||||||
GET_ATTRIBUTE_KEY = 'GET_ATTRIBUTE_KEY',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mapOfOperators = {
|
export const mapOfOperators = {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { Button, Typography } from 'antd';
|
import { Button, Typography } from 'antd';
|
||||||
import createDashboard from 'api/dashboard/create';
|
import createDashboard from 'api/dashboard/create';
|
||||||
import axios from 'axios';
|
|
||||||
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import useAxiosError from 'hooks/useAxiosError';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
@ -15,10 +14,9 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
Wrapper,
|
Wrapper,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import { getSelectOptions } from './utils';
|
import { filterOptions, getSelectOptions } from './utils';
|
||||||
|
|
||||||
function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
||||||
const { notifications } = useNotifications();
|
|
||||||
const { t } = useTranslation(['dashboard']);
|
const { t } = useTranslation(['dashboard']);
|
||||||
|
|
||||||
const [selectedDashboardId, setSelectedDashboardId] = useState<string | null>(
|
const [selectedDashboardId, setSelectedDashboardId] = useState<string | null>(
|
||||||
@ -31,21 +29,19 @@ function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
|||||||
refetch,
|
refetch,
|
||||||
} = useGetAllDashboard();
|
} = useGetAllDashboard();
|
||||||
|
|
||||||
|
const handleError = useAxiosError();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutate: createNewDashboard,
|
mutate: createNewDashboard,
|
||||||
isLoading: createDashboardLoading,
|
isLoading: createDashboardLoading,
|
||||||
} = useMutation(createDashboard, {
|
} = useMutation(createDashboard, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
onExport(data?.payload || null);
|
if (data.payload) {
|
||||||
|
onExport(data?.payload);
|
||||||
|
}
|
||||||
refetch();
|
refetch();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: handleError,
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
notifications.error({
|
|
||||||
message: error.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const options = useMemo(() => getSelectOptions(data?.payload || []), [data]);
|
const options = useMemo(() => getSelectOptions(data?.payload || []), [data]);
|
||||||
@ -90,10 +86,12 @@ function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
|||||||
<DashboardSelect
|
<DashboardSelect
|
||||||
placeholder="Select Dashboard"
|
placeholder="Select Dashboard"
|
||||||
options={options}
|
options={options}
|
||||||
|
showSearch
|
||||||
loading={isDashboardLoading}
|
loading={isDashboardLoading}
|
||||||
disabled={isDashboardLoading}
|
disabled={isDashboardLoading}
|
||||||
value={selectedDashboardId}
|
value={selectedDashboardId}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
|
filterOption={filterOptions}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
export const MENU_KEY = {
|
|
||||||
EXPORT: 'export',
|
|
||||||
CREATE_ALERTS: 'create-alerts',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MENU_LABEL = {
|
|
||||||
EXPORT: 'Export Panel',
|
|
||||||
CREATE_ALERTS: 'Create Alerts',
|
|
||||||
};
|
|
@ -1,12 +1,12 @@
|
|||||||
import { Button, Dropdown, MenuProps, Modal } from 'antd';
|
import { AlertOutlined, AreaChartOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Modal, Space } from 'antd';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
import { MENU_KEY, MENU_LABEL } from './config';
|
|
||||||
import ExportPanelContainer from './ExportPanel';
|
import ExportPanelContainer from './ExportPanel';
|
||||||
|
|
||||||
function ExportPanel({
|
function ExportPanel({
|
||||||
@ -28,51 +28,37 @@ function ExportPanel({
|
|||||||
);
|
);
|
||||||
}, [query]);
|
}, [query]);
|
||||||
|
|
||||||
const onMenuClickHandler: MenuProps['onClick'] = useCallback(
|
|
||||||
(e: OnClickProps) => {
|
|
||||||
if (e.key === MENU_KEY.EXPORT) {
|
|
||||||
onModalToggle(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.key === MENU_KEY.CREATE_ALERTS) {
|
|
||||||
onCreateAlertsHandler();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[onModalToggle, onCreateAlertsHandler],
|
|
||||||
);
|
|
||||||
|
|
||||||
const menu: MenuProps = useMemo(
|
|
||||||
() => ({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
key: MENU_KEY.EXPORT,
|
|
||||||
label: MENU_LABEL.EXPORT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: MENU_KEY.CREATE_ALERTS,
|
|
||||||
label: MENU_LABEL.CREATE_ALERTS,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
onClick: onMenuClickHandler,
|
|
||||||
}),
|
|
||||||
[onMenuClickHandler],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onCancel = (value: boolean) => (): void => {
|
const onCancel = (value: boolean) => (): void => {
|
||||||
onModalToggle(value);
|
onModalToggle(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onAddToDashboard = (): void => {
|
||||||
|
setIsExport(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dropdown trigger={['click']} menu={menu}>
|
<Space size={24}>
|
||||||
<Button>Actions</Button>
|
<Button
|
||||||
</Dropdown>
|
icon={<AreaChartOutlined />}
|
||||||
|
onClick={onAddToDashboard}
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
Add to Dashboard
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button onClick={onCreateAlertsHandler} icon={<AlertOutlined />}>
|
||||||
|
Setup Alerts
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
footer={null}
|
footer={null}
|
||||||
onOk={onCancel(false)}
|
onOk={onCancel(false)}
|
||||||
onCancel={onCancel(false)}
|
onCancel={onCancel(false)}
|
||||||
open={isExport}
|
open={isExport}
|
||||||
centered
|
centered
|
||||||
|
destroyOnClose
|
||||||
>
|
>
|
||||||
<ExportPanelContainer
|
<ExportPanelContainer
|
||||||
query={query}
|
query={query}
|
||||||
@ -84,10 +70,6 @@ function ExportPanel({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnClickProps {
|
|
||||||
key: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExportPanelProps {
|
export interface ExportPanelProps {
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
onExport: (dashboard: Dashboard | null) => void;
|
onExport: (dashboard: Dashboard | null) => void;
|
||||||
|
@ -8,3 +8,11 @@ export const getSelectOptions = (
|
|||||||
label: data.title,
|
label: data.title,
|
||||||
value: uuid,
|
value: uuid,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const filterOptions: SelectProps['filterOption'] = (
|
||||||
|
input,
|
||||||
|
options,
|
||||||
|
): boolean =>
|
||||||
|
(options?.label?.toString() ?? '')
|
||||||
|
?.toLowerCase()
|
||||||
|
.includes(input.toLowerCase());
|
||||||
|
@ -3,7 +3,9 @@ import { LinkOutlined } from '@ant-design/icons';
|
|||||||
import { Input, Space, Tooltip } from 'antd';
|
import { Input, Space, Tooltip } from 'antd';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
import Editor from 'components/Editor';
|
import Editor from 'components/Editor';
|
||||||
import AddToQueryHOC from 'components/Logs/AddToQueryHOC';
|
import AddToQueryHOC, {
|
||||||
|
AddToQueryHOCProps,
|
||||||
|
} from 'components/Logs/AddToQueryHOC';
|
||||||
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
|
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -27,7 +29,10 @@ const RESTRICTED_FIELDS = ['timestamp'];
|
|||||||
interface TableViewProps {
|
interface TableViewProps {
|
||||||
logData: ILog;
|
logData: ILog;
|
||||||
}
|
}
|
||||||
function TableView({ logData }: TableViewProps): JSX.Element | null {
|
|
||||||
|
type Props = TableViewProps & Pick<AddToQueryHOCProps, 'onAddToQuery'>;
|
||||||
|
|
||||||
|
function TableView({ logData, onAddToQuery }: Props): JSX.Element | null {
|
||||||
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
|
const [fieldSearchInput, setFieldSearchInput] = useState<string>('');
|
||||||
|
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
@ -128,7 +133,11 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
|
|||||||
|
|
||||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
||||||
return (
|
return (
|
||||||
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
|
<AddToQueryHOC
|
||||||
|
fieldKey={fieldKey[0]}
|
||||||
|
fieldValue={flattenLogData[field]}
|
||||||
|
onAddToQuery={onAddToQuery}
|
||||||
|
>
|
||||||
{renderedField}
|
{renderedField}
|
||||||
</AddToQueryHOC>
|
</AddToQueryHOC>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import LogDetail from 'components/LogDetail';
|
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 { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
@ -7,9 +11,11 @@ import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
|
|||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
function LogDetailedView(): JSX.Element {
|
function LogDetailedView(): JSX.Element {
|
||||||
const { detailedLog } = useSelector<AppState, ILogsReducer>(
|
const history = useHistory();
|
||||||
(state) => state.logs,
|
const {
|
||||||
);
|
detailedLog,
|
||||||
|
searchFilter: { queryString },
|
||||||
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
|
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
@ -20,7 +26,32 @@ function LogDetailedView(): JSX.Element {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return <LogDetail log={detailedLog} onClose={onDrawerClose} />;
|
const handleQueryAdd = useCallback(
|
||||||
|
(fieldKey: string, fieldValue: string) => {
|
||||||
|
const generatedQuery = generateFilterQuery({
|
||||||
|
fieldKey,
|
||||||
|
fieldValue,
|
||||||
|
type: 'IN',
|
||||||
|
});
|
||||||
|
|
||||||
|
let updatedQueryString = queryString || '';
|
||||||
|
if (updatedQueryString.length === 0) {
|
||||||
|
updatedQueryString += `${generatedQuery}`;
|
||||||
|
} else {
|
||||||
|
updatedQueryString += ` AND ${generatedQuery}`;
|
||||||
|
}
|
||||||
|
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||||
|
},
|
||||||
|
[history, queryString],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LogDetail
|
||||||
|
log={detailedLog}
|
||||||
|
onClose={onDrawerClose}
|
||||||
|
onAddToQuery={handleQueryAdd}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LogDetailedView;
|
export default LogDetailedView;
|
||||||
|
@ -1,4 +1,16 @@
|
|||||||
import LogDetail from 'components/LogDetail';
|
import LogDetail from 'components/LogDetail';
|
||||||
|
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useQueryClient } from 'react-query';
|
||||||
|
import { SuccessResponse } from 'types/api';
|
||||||
|
import {
|
||||||
|
BaseAutocompleteData,
|
||||||
|
IQueryAutocompleteResponse,
|
||||||
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import { LogExplorerDetailedViewProps } from './LogExplorerDetailedView.interfaces';
|
import { LogExplorerDetailedViewProps } from './LogExplorerDetailedView.interfaces';
|
||||||
|
|
||||||
@ -6,11 +18,52 @@ function LogExplorerDetailedView({
|
|||||||
log,
|
log,
|
||||||
onClose,
|
onClose,
|
||||||
}: LogExplorerDetailedViewProps): JSX.Element {
|
}: LogExplorerDetailedViewProps): JSX.Element {
|
||||||
const onDrawerClose = (): void => {
|
const queryClient = useQueryClient();
|
||||||
onClose();
|
const { redirectWithQueryBuilderData, currentQuery } = useQueryBuilder();
|
||||||
};
|
|
||||||
|
|
||||||
return <LogDetail log={log} onClose={onDrawerClose} />;
|
const handleAddQuery = useCallback(
|
||||||
|
(fieldKey: string, fieldValue: string): void => {
|
||||||
|
const keysAutocomplete: BaseAutocompleteData[] =
|
||||||
|
queryClient.getQueryData<SuccessResponse<IQueryAutocompleteResponse>>(
|
||||||
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS],
|
||||||
|
{ exact: false },
|
||||||
|
)?.payload.attributeKeys || [];
|
||||||
|
|
||||||
|
const existAutocompleteKey = chooseAutocompleteFromCustomValue(
|
||||||
|
keysAutocomplete,
|
||||||
|
[fieldKey],
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const nextQuery: Query = {
|
||||||
|
...currentQuery,
|
||||||
|
builder: {
|
||||||
|
...currentQuery.builder,
|
||||||
|
queryData: currentQuery.builder.queryData.map((item) => ({
|
||||||
|
...item,
|
||||||
|
filters: {
|
||||||
|
...item.filters,
|
||||||
|
items: [
|
||||||
|
...item.filters.items.filter(
|
||||||
|
(item) => item.key?.id !== existAutocompleteKey.id,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
id: uuid(),
|
||||||
|
key: existAutocompleteKey,
|
||||||
|
op: '=',
|
||||||
|
value: fieldValue,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
redirectWithQueryBuilderData(nextQuery);
|
||||||
|
},
|
||||||
|
[currentQuery, queryClient, redirectWithQueryBuilderData],
|
||||||
|
);
|
||||||
|
|
||||||
|
return <LogDetail log={log} onClose={onClose} onAddToQuery={handleAddQuery} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LogExplorerDetailedView;
|
export default LogExplorerDetailedView;
|
||||||
|
@ -6,8 +6,8 @@ import {
|
|||||||
getTagToken,
|
getTagToken,
|
||||||
isInNInOperator,
|
isInNInOperator,
|
||||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
|
import useDebounceValue from 'hooks/useDebounce';
|
||||||
import { isEqual, uniqWith } from 'lodash-es';
|
import { isEqual, uniqWith } from 'lodash-es';
|
||||||
import debounce from 'lodash-es/debounce';
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useDebounce } from 'react-use';
|
import { useDebounce } from 'react-use';
|
||||||
@ -39,25 +39,23 @@ export const useFetchKeysAndValues = (
|
|||||||
const [sourceKeys, setSourceKeys] = useState<BaseAutocompleteData[]>([]);
|
const [sourceKeys, setSourceKeys] = useState<BaseAutocompleteData[]>([]);
|
||||||
const [results, setResults] = useState<string[]>([]);
|
const [results, setResults] = useState<string[]>([]);
|
||||||
|
|
||||||
const searchParams = useMemo(
|
const memoizedSearchParams = useMemo(
|
||||||
() =>
|
() => [
|
||||||
debounce(
|
|
||||||
() => [
|
|
||||||
searchKey,
|
|
||||||
query.dataSource,
|
|
||||||
query.aggregateOperator,
|
|
||||||
query.aggregateAttribute.key,
|
|
||||||
],
|
|
||||||
300,
|
|
||||||
),
|
|
||||||
[
|
|
||||||
query.aggregateAttribute.key,
|
|
||||||
query.aggregateOperator,
|
|
||||||
query.dataSource,
|
|
||||||
searchKey,
|
searchKey,
|
||||||
|
query.dataSource,
|
||||||
|
query.aggregateOperator,
|
||||||
|
query.aggregateAttribute.key,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
searchKey,
|
||||||
|
query.dataSource,
|
||||||
|
query.aggregateOperator,
|
||||||
|
query.aggregateAttribute.key,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const searchParams = useDebounceValue(memoizedSearchParams, 300);
|
||||||
|
|
||||||
const isQueryEnabled = useMemo(
|
const isQueryEnabled = useMemo(
|
||||||
() =>
|
() =>
|
||||||
query.dataSource === DataSource.METRICS
|
query.dataSource === DataSource.METRICS
|
||||||
@ -73,7 +71,7 @@ export const useFetchKeysAndValues = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { data, isFetching, status } = useQuery(
|
const { data, isFetching, status } = useQuery(
|
||||||
[QueryBuilderKeys.GET_ATTRIBUTE_KEY, searchParams()],
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchParams],
|
||||||
async () =>
|
async () =>
|
||||||
getAggregateKeys({
|
getAggregateKeys({
|
||||||
searchText: searchKey,
|
searchText: searchKey,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user