mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-12 03:01:28 +08:00

* feat: qb-suggestions base setup * chore: make the dropdown a little similar to the designs * chore: move out example queries from og and add to renderer * chore: added the handlers for example queries * chore: hide the example queries as soon as the user starts typing * feat: handle changes for cancel query * chore: remove stupid concept of option group * chore: show only first 3 items and add option to show all filters * chore: minor css changes and remove transitions * feat: integrate suggestions api and control re-renders * feat: added keyboard shortcuts for the dropdown * fix: design cleanups and touchups * fix: build issues and tests * chore: extra safety check for base64 and fix tests * fix: qs doesn't handle padding in base64 strings, added client logic * chore: some code comments * chore: some code comments * chore: increase the height of the bar when key is set * chore: address minor designs * chore: update the keyboard shortcut to cmd+/ * feat: correct the option render for logs for tooltip * chore: search bar to not loose focus on btn click * fix: update the spacing and icon for search bar * chore: address review comments
756 lines
19 KiB
TypeScript
756 lines
19 KiB
TypeScript
/* eslint-disable sonarjs/cognitive-complexity */
|
|
import './LogsExplorerViews.styles.scss';
|
|
|
|
import { Button } from 'antd';
|
|
import logEvent from 'api/common/logEvent';
|
|
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
|
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
|
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
|
import { QueryParams } from 'constants/query';
|
|
import {
|
|
initialFilters,
|
|
initialQueriesMap,
|
|
initialQueryBuilderFormValues,
|
|
PANEL_TYPES,
|
|
} from 'constants/queryBuilder';
|
|
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
|
|
import Download from 'container/DownloadV2/DownloadV2';
|
|
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
|
|
import GoToTop from 'container/GoToTop';
|
|
import LogsExplorerChart from 'container/LogsExplorerChart';
|
|
import LogsExplorerList from 'container/LogsExplorerList';
|
|
import LogsExplorerTable from 'container/LogsExplorerTable';
|
|
import { useOptionsMenu } from 'container/OptionsMenu';
|
|
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
|
|
import dayjs from 'dayjs';
|
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
|
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
|
import { LogTimeRange } from 'hooks/logs/types';
|
|
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
|
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
|
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|
import useAxiosError from 'hooks/useAxiosError';
|
|
import useClickOutside from 'hooks/useClickOutside';
|
|
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
|
|
import { useNotifications } from 'hooks/useNotifications';
|
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
|
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
|
import {
|
|
cloneDeep,
|
|
defaultTo,
|
|
isEmpty,
|
|
isUndefined,
|
|
omit,
|
|
set,
|
|
} from 'lodash-es';
|
|
import { Sliders } from 'lucide-react';
|
|
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
|
|
import {
|
|
memo,
|
|
MutableRefObject,
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useRef,
|
|
useState,
|
|
} from 'react';
|
|
import { useSelector } from 'react-redux';
|
|
import { useHistory } from 'react-router-dom';
|
|
import { AppState } from 'store/reducers';
|
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
|
import { ILog } from 'types/api/logs/log';
|
|
import {
|
|
IBuilderQuery,
|
|
OrderByPayload,
|
|
Query,
|
|
TagFilter,
|
|
} from 'types/api/queryBuilder/queryBuilderData';
|
|
import {
|
|
DataSource,
|
|
LogsAggregatorOperator,
|
|
StringOperators,
|
|
} from 'types/common/queryBuilder';
|
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink';
|
|
import { v4 } from 'uuid';
|
|
|
|
function LogsExplorerViews({
|
|
selectedView,
|
|
showFrequencyChart,
|
|
setIsLoadingQueries,
|
|
listQueryKeyRef,
|
|
chartQueryKeyRef,
|
|
}: {
|
|
selectedView: SELECTED_VIEWS;
|
|
showFrequencyChart: boolean;
|
|
setIsLoadingQueries: React.Dispatch<React.SetStateAction<boolean>>;
|
|
listQueryKeyRef: MutableRefObject<any>;
|
|
chartQueryKeyRef: MutableRefObject<any>;
|
|
}): JSX.Element {
|
|
const { notifications } = useNotifications();
|
|
const history = useHistory();
|
|
|
|
// this is to respect the panel type present in the URL rather than defaulting it to list always.
|
|
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
|
|
|
|
const { activeLogId, timeRange, onTimeRangeChange } = useCopyLogLink();
|
|
|
|
const { queryData: pageSize } = useUrlQueryData(
|
|
QueryParams.pageSize,
|
|
DEFAULT_PER_PAGE_VALUE,
|
|
);
|
|
|
|
const { minTime } = useSelector<AppState, GlobalReducer>(
|
|
(state) => state.globalTime,
|
|
);
|
|
|
|
const currentMinTimeRef = useRef<number>(minTime);
|
|
|
|
// Context
|
|
const {
|
|
initialDataSource,
|
|
currentQuery,
|
|
stagedQuery,
|
|
panelType,
|
|
updateAllQueriesOperators,
|
|
handleSetConfig,
|
|
} = useQueryBuilder();
|
|
|
|
const [selectedPanelType, setSelectedPanelType] = useState<PANEL_TYPES>(
|
|
panelType || PANEL_TYPES.LIST,
|
|
);
|
|
|
|
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
|
|
|
// State
|
|
const [page, setPage] = useState<number>(1);
|
|
const [logs, setLogs] = useState<ILog[]>([]);
|
|
const [requestData, setRequestData] = useState<Query | null>(null);
|
|
const [showFormatMenuItems, setShowFormatMenuItems] = useState(false);
|
|
|
|
const handleAxisError = useAxiosError();
|
|
|
|
const listQuery = useMemo(() => {
|
|
if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null;
|
|
|
|
return stagedQuery.builder.queryData.find((item) => !item.disabled) || null;
|
|
}, [stagedQuery]);
|
|
|
|
const { options, config } = useOptionsMenu({
|
|
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
|
|
dataSource: initialDataSource || DataSource.LOGS,
|
|
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
|
|
});
|
|
|
|
const orderByTimestamp: OrderByPayload | null = useMemo(() => {
|
|
const timestampOrderBy = listQuery?.orderBy.find(
|
|
(item) => item.columnName === 'timestamp',
|
|
);
|
|
|
|
return timestampOrderBy || null;
|
|
}, [listQuery]);
|
|
|
|
const isMultipleQueries = useMemo(
|
|
() =>
|
|
currentQuery.builder.queryData.length > 1 ||
|
|
currentQuery.builder.queryFormulas.length > 0,
|
|
[currentQuery],
|
|
);
|
|
|
|
const isGroupByExist = useMemo(() => {
|
|
const groupByCount: number = currentQuery.builder.queryData.reduce<number>(
|
|
(acc, query) => acc + query.groupBy.length,
|
|
0,
|
|
);
|
|
|
|
return groupByCount > 0;
|
|
}, [currentQuery]);
|
|
|
|
const isLimit: boolean = useMemo(() => {
|
|
if (!listQuery) return false;
|
|
if (!listQuery.limit) return false;
|
|
|
|
return logs.length >= listQuery.limit;
|
|
}, [logs.length, listQuery]);
|
|
|
|
const listChartQuery = useMemo(() => {
|
|
if (!stagedQuery || !listQuery) return null;
|
|
|
|
const modifiedQueryData: IBuilderQuery = {
|
|
...listQuery,
|
|
aggregateOperator: LogsAggregatorOperator.COUNT,
|
|
};
|
|
|
|
const modifiedQuery: Query = {
|
|
...stagedQuery,
|
|
builder: {
|
|
...stagedQuery.builder,
|
|
queryData: stagedQuery.builder.queryData.map((item) => ({
|
|
...item,
|
|
...modifiedQueryData,
|
|
})),
|
|
},
|
|
};
|
|
|
|
return modifiedQuery;
|
|
}, [stagedQuery, listQuery]);
|
|
|
|
const exportDefaultQuery = useMemo(
|
|
() =>
|
|
updateAllQueriesOperators(
|
|
currentQuery || initialQueriesMap.logs,
|
|
selectedPanelType,
|
|
DataSource.LOGS,
|
|
),
|
|
[currentQuery, selectedPanelType, updateAllQueriesOperators],
|
|
);
|
|
|
|
const handleModeChange = (panelType: PANEL_TYPES): void => {
|
|
if (selectedView === SELECTED_VIEWS.SEARCH) {
|
|
handleSetConfig(panelType, DataSource.LOGS);
|
|
}
|
|
|
|
setShowFormatMenuItems(false);
|
|
handleExplorerTabChange(panelType);
|
|
};
|
|
|
|
const {
|
|
data: listChartData,
|
|
isFetching: isFetchingListChartData,
|
|
isLoading: isLoadingListChartData,
|
|
} = useGetExplorerQueryRange(
|
|
listChartQuery,
|
|
PANEL_TYPES.TIME_SERIES,
|
|
DEFAULT_ENTITY_VERSION,
|
|
{
|
|
enabled: !!listChartQuery && panelType === PANEL_TYPES.LIST,
|
|
},
|
|
{},
|
|
undefined,
|
|
chartQueryKeyRef,
|
|
);
|
|
|
|
const { data, isLoading, isFetching, isError } = useGetExplorerQueryRange(
|
|
requestData,
|
|
panelType,
|
|
DEFAULT_ENTITY_VERSION,
|
|
{
|
|
keepPreviousData: true,
|
|
enabled: !isLimit && !!requestData,
|
|
},
|
|
{
|
|
...(timeRange &&
|
|
activeLogId &&
|
|
!logs.length && {
|
|
start: timeRange.start,
|
|
end: timeRange.end,
|
|
}),
|
|
},
|
|
undefined,
|
|
listQueryKeyRef,
|
|
);
|
|
|
|
const getRequestData = useCallback(
|
|
(
|
|
query: Query | null,
|
|
params: {
|
|
page: number;
|
|
log: ILog | null;
|
|
pageSize: number;
|
|
filters: TagFilter;
|
|
},
|
|
): Query | null => {
|
|
if (!query) return null;
|
|
|
|
const paginateData = getPaginationQueryData({
|
|
filters: params.filters,
|
|
listItemId: params.log ? params.log.id : null,
|
|
orderByTimestamp,
|
|
page: params.page,
|
|
pageSize: params.pageSize,
|
|
});
|
|
|
|
const queryData: IBuilderQuery[] =
|
|
query.builder.queryData.length > 1
|
|
? query.builder.queryData
|
|
: [
|
|
{
|
|
...(listQuery || initialQueryBuilderFormValues),
|
|
...paginateData,
|
|
},
|
|
];
|
|
|
|
const data: Query = {
|
|
...query,
|
|
builder: {
|
|
...query.builder,
|
|
queryData,
|
|
},
|
|
};
|
|
|
|
return data;
|
|
},
|
|
[orderByTimestamp, listQuery],
|
|
);
|
|
|
|
const handleEndReached = useCallback(
|
|
(index: number) => {
|
|
if (!listQuery) return;
|
|
|
|
if (isLimit) return;
|
|
if (logs.length < pageSize) return;
|
|
|
|
const { limit, filters } = listQuery;
|
|
|
|
const lastLog = logs[index];
|
|
|
|
const nextLogsLength = logs.length + pageSize;
|
|
|
|
const nextPageSize =
|
|
limit && nextLogsLength >= limit ? limit - logs.length : pageSize;
|
|
|
|
if (!stagedQuery) return;
|
|
|
|
const newRequestData = getRequestData(stagedQuery, {
|
|
filters,
|
|
page: page + 1,
|
|
log: orderByTimestamp ? lastLog : null,
|
|
pageSize: nextPageSize,
|
|
});
|
|
|
|
setPage((prevPage) => prevPage + 1);
|
|
|
|
setRequestData(newRequestData);
|
|
},
|
|
[
|
|
isLimit,
|
|
logs,
|
|
listQuery,
|
|
pageSize,
|
|
stagedQuery,
|
|
getRequestData,
|
|
page,
|
|
orderByTimestamp,
|
|
],
|
|
);
|
|
|
|
const logEventCalledRef = useRef(false);
|
|
useEffect(() => {
|
|
if (!logEventCalledRef.current && !isUndefined(data?.payload)) {
|
|
const currentData = data?.payload?.data?.newResult?.data?.result || [];
|
|
logEvent('Logs Explorer: Page visited', {
|
|
panelType,
|
|
isEmpty: !currentData?.[0]?.list,
|
|
});
|
|
logEventCalledRef.current = true;
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [data?.payload]);
|
|
|
|
const {
|
|
mutate: updateDashboard,
|
|
isLoading: isUpdateDashboardLoading,
|
|
} = useUpdateDashboard();
|
|
|
|
const getUpdatedQueryForExport = useCallback((): Query => {
|
|
const updatedQuery = cloneDeep(currentQuery);
|
|
|
|
set(updatedQuery, 'builder.queryData[0].pageSize', 10);
|
|
|
|
return updatedQuery;
|
|
}, [currentQuery]);
|
|
|
|
const handleExport = useCallback(
|
|
(dashboard: Dashboard | null, isNewDashboard?: boolean): void => {
|
|
if (!dashboard || !panelType) return;
|
|
|
|
const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType)
|
|
? panelType
|
|
: PANEL_TYPES.TIME_SERIES;
|
|
|
|
const widgetId = v4();
|
|
|
|
const query =
|
|
panelType === PANEL_TYPES.LIST
|
|
? getUpdatedQueryForExport()
|
|
: exportDefaultQuery;
|
|
|
|
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
|
|
dashboard,
|
|
query,
|
|
widgetId,
|
|
panelTypeParam,
|
|
options.selectColumns,
|
|
);
|
|
|
|
logEvent('Logs Explorer: Add to dashboard successful', {
|
|
panelType,
|
|
isNewDashboard,
|
|
dashboardName: dashboard?.data?.title,
|
|
});
|
|
|
|
updateDashboard(updatedDashboard, {
|
|
onSuccess: (data) => {
|
|
if (data.error) {
|
|
const message =
|
|
data.error === 'feature usage exceeded' ? (
|
|
<span>
|
|
Panel limit exceeded for {DataSource.LOGS} in community edition. Please
|
|
checkout our paid plans{' '}
|
|
<a
|
|
href="https://signoz.io/pricing/?utm_source=product&utm_medium=dashboard-limit"
|
|
rel="noreferrer noopener"
|
|
target="_blank"
|
|
>
|
|
here
|
|
</a>
|
|
</span>
|
|
) : (
|
|
data.error
|
|
);
|
|
notifications.error({
|
|
message,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
const dashboardEditView = generateExportToDashboardLink({
|
|
query,
|
|
panelType: panelTypeParam,
|
|
dashboardId: data.payload?.uuid || '',
|
|
widgetId,
|
|
});
|
|
|
|
history.push(dashboardEditView);
|
|
},
|
|
onError: handleAxisError,
|
|
});
|
|
},
|
|
[
|
|
getUpdatedQueryForExport,
|
|
exportDefaultQuery,
|
|
options.selectColumns,
|
|
history,
|
|
notifications,
|
|
panelType,
|
|
updateDashboard,
|
|
handleAxisError,
|
|
],
|
|
);
|
|
|
|
useEffect(() => {
|
|
const shouldChangeView =
|
|
(isMultipleQueries || isGroupByExist) &&
|
|
selectedView !== SELECTED_VIEWS.SEARCH;
|
|
|
|
if (selectedPanelType === PANEL_TYPES.LIST && shouldChangeView) {
|
|
handleExplorerTabChange(PANEL_TYPES.TIME_SERIES);
|
|
setSelectedPanelType(PANEL_TYPES.TIME_SERIES);
|
|
}
|
|
|
|
if (panelType) {
|
|
setSelectedPanelType(panelType);
|
|
}
|
|
}, [
|
|
isMultipleQueries,
|
|
isGroupByExist,
|
|
selectedPanelType,
|
|
selectedView,
|
|
handleExplorerTabChange,
|
|
panelType,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
selectedView &&
|
|
selectedView === SELECTED_VIEWS.SEARCH &&
|
|
handleSetConfig
|
|
) {
|
|
handleSetConfig(defaultTo(panelTypes, PANEL_TYPES.LIST), DataSource.LOGS);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [handleSetConfig, panelTypes]);
|
|
|
|
useEffect(() => {
|
|
const currentParams = data?.params as Omit<LogTimeRange, 'pageSize'>;
|
|
const currentData = data?.payload?.data?.newResult?.data?.result || [];
|
|
if (currentData.length > 0 && currentData[0].list) {
|
|
const currentLogs: ILog[] = currentData[0].list.map((item) => ({
|
|
...item.data,
|
|
timestamp: item.timestamp,
|
|
}));
|
|
const newLogs = [...logs, ...currentLogs];
|
|
|
|
setLogs(newLogs);
|
|
onTimeRangeChange({
|
|
start: currentParams?.start,
|
|
end: timeRange?.end || currentParams?.end,
|
|
pageSize: newLogs.length,
|
|
});
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [data]);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
requestData?.id !== stagedQuery?.id ||
|
|
currentMinTimeRef.current !== minTime
|
|
) {
|
|
const newRequestData = getRequestData(stagedQuery, {
|
|
filters: listQuery?.filters || initialFilters,
|
|
page: 1,
|
|
log: null,
|
|
pageSize:
|
|
timeRange?.pageSize && activeLogId ? timeRange?.pageSize : pageSize,
|
|
});
|
|
|
|
setLogs([]);
|
|
setPage(1);
|
|
setRequestData(newRequestData);
|
|
currentMinTimeRef.current = minTime;
|
|
}
|
|
}, [
|
|
stagedQuery,
|
|
requestData,
|
|
getRequestData,
|
|
listQuery,
|
|
pageSize,
|
|
minTime,
|
|
timeRange,
|
|
activeLogId,
|
|
onTimeRangeChange,
|
|
panelType,
|
|
selectedView,
|
|
]);
|
|
|
|
const chartData = useMemo(() => {
|
|
if (!stagedQuery) return [];
|
|
|
|
if (panelType === PANEL_TYPES.LIST) {
|
|
if (listChartData && listChartData.payload.data.result.length > 0) {
|
|
return listChartData.payload.data.result;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
if (!data || data.payload.data.result.length === 0) return [];
|
|
|
|
const isGroupByExist = stagedQuery.builder.queryData.some(
|
|
(queryData) => queryData.groupBy.length > 0,
|
|
);
|
|
|
|
const firstPayloadQuery = data.payload.data.result.find(
|
|
(item) => item.queryName === listQuery?.queryName,
|
|
);
|
|
|
|
const firstPayloadQueryArray = firstPayloadQuery ? [firstPayloadQuery] : [];
|
|
|
|
return isGroupByExist ? data.payload.data.result : firstPayloadQueryArray;
|
|
}, [stagedQuery, panelType, data, listChartData, listQuery]);
|
|
|
|
const formatItems = [
|
|
{
|
|
key: 'raw',
|
|
label: 'Raw',
|
|
data: {
|
|
title: 'max lines per row',
|
|
},
|
|
},
|
|
{
|
|
key: 'list',
|
|
label: 'Default',
|
|
},
|
|
{
|
|
key: 'table',
|
|
label: 'Column',
|
|
data: {
|
|
title: 'columns',
|
|
},
|
|
},
|
|
];
|
|
|
|
const handleToggleShowFormatOptions = (): void =>
|
|
setShowFormatMenuItems(!showFormatMenuItems);
|
|
|
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
|
|
useClickOutside({
|
|
ref: menuRef,
|
|
onClickOutside: () => {
|
|
if (showFormatMenuItems) {
|
|
setShowFormatMenuItems(false);
|
|
}
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (
|
|
isLoading ||
|
|
isFetching ||
|
|
isLoadingListChartData ||
|
|
isFetchingListChartData
|
|
) {
|
|
setIsLoadingQueries(true);
|
|
} else {
|
|
setIsLoadingQueries(false);
|
|
}
|
|
}, [
|
|
isLoading,
|
|
isFetching,
|
|
isFetchingListChartData,
|
|
isLoadingListChartData,
|
|
setIsLoadingQueries,
|
|
]);
|
|
|
|
const flattenLogData = useMemo(
|
|
() =>
|
|
logs.map((log) => {
|
|
const timestamp =
|
|
typeof log.timestamp === 'string'
|
|
? dayjs(log.timestamp).format('YYYY-MM-DD HH:mm:ss.SSS')
|
|
: dayjs(log.timestamp / 1e6).format('YYYY-MM-DD HH:mm:ss.SSS');
|
|
|
|
return FlatLogData({
|
|
timestamp,
|
|
body: log.body,
|
|
...omit(log, 'timestamp', 'body'),
|
|
});
|
|
}),
|
|
[logs],
|
|
);
|
|
|
|
return (
|
|
<div className="logs-explorer-views-container">
|
|
{showFrequencyChart && (
|
|
<LogsExplorerChart
|
|
className="logs-histogram"
|
|
isLoading={isFetchingListChartData || isLoadingListChartData}
|
|
data={chartData}
|
|
/>
|
|
)}
|
|
|
|
<div className="logs-explorer-views-types">
|
|
<div className="views-tabs-container">
|
|
<Button.Group className="views-tabs">
|
|
<Button
|
|
value={PANEL_TYPES.LIST}
|
|
className={
|
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
|
selectedPanelType === PANEL_TYPES.LIST ? 'selected_view tab' : 'tab'
|
|
}
|
|
disabled={
|
|
(isMultipleQueries || isGroupByExist) && selectedView !== 'search'
|
|
}
|
|
onClick={(): void => handleModeChange(PANEL_TYPES.LIST)}
|
|
data-testid="logs-list-view"
|
|
>
|
|
List view
|
|
</Button>
|
|
<Button
|
|
value={PANEL_TYPES.TIME_SERIES}
|
|
className={
|
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
|
selectedPanelType === PANEL_TYPES.TIME_SERIES
|
|
? 'selected_view tab'
|
|
: 'tab'
|
|
}
|
|
onClick={(): void => handleModeChange(PANEL_TYPES.TIME_SERIES)}
|
|
data-testid="time-series-view"
|
|
>
|
|
Time series
|
|
</Button>
|
|
<Button
|
|
value={PANEL_TYPES.TABLE}
|
|
className={
|
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
|
selectedPanelType === PANEL_TYPES.TABLE ? 'selected_view tab' : 'tab'
|
|
}
|
|
onClick={(): void => handleModeChange(PANEL_TYPES.TABLE)}
|
|
data-testid="table-view"
|
|
>
|
|
Table
|
|
</Button>
|
|
</Button.Group>
|
|
<div className="logs-actions-container">
|
|
{selectedPanelType === PANEL_TYPES.LIST && (
|
|
<div className="tab-options">
|
|
<Download
|
|
data={flattenLogData}
|
|
isLoading={isFetching}
|
|
fileName="log_data"
|
|
/>
|
|
<div className="format-options-container" ref={menuRef}>
|
|
<Button
|
|
className="periscope-btn"
|
|
onClick={handleToggleShowFormatOptions}
|
|
icon={<Sliders size={14} />}
|
|
data-testid="periscope-btn"
|
|
/>
|
|
|
|
{showFormatMenuItems && (
|
|
<LogsFormatOptionsMenu
|
|
title="FORMAT"
|
|
items={formatItems}
|
|
selectedOptionFormat={options.format}
|
|
config={config}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="logs-explorer-views-type-content">
|
|
{selectedPanelType === PANEL_TYPES.LIST && (
|
|
<LogsExplorerList
|
|
isLoading={isLoading}
|
|
isFetching={isFetching}
|
|
currentStagedQueryData={listQuery}
|
|
logs={logs}
|
|
onEndReached={handleEndReached}
|
|
isError={isError}
|
|
isFilterApplied={!isEmpty(listQuery?.filters.items)}
|
|
/>
|
|
)}
|
|
|
|
{selectedPanelType === PANEL_TYPES.TIME_SERIES && (
|
|
<TimeSeriesView
|
|
isLoading={isLoading || isFetching}
|
|
data={data}
|
|
isError={isError}
|
|
isFilterApplied={!isEmpty(listQuery?.filters.items)}
|
|
dataSource={DataSource.LOGS}
|
|
/>
|
|
)}
|
|
|
|
{selectedPanelType === PANEL_TYPES.TABLE && (
|
|
<LogsExplorerTable
|
|
data={data?.payload?.data?.newResult?.data?.result || []}
|
|
isLoading={isLoading || isFetching}
|
|
isError={isError}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<GoToTop />
|
|
|
|
<ExplorerOptionWrapper
|
|
disabled={!stagedQuery}
|
|
query={exportDefaultQuery}
|
|
isLoading={isUpdateDashboardLoading}
|
|
onExport={handleExport}
|
|
sourcepage={DataSource.LOGS}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default memo(LogsExplorerViews);
|