diff --git a/frontend/src/components/Logs/RawLogView/index.tsx b/frontend/src/components/Logs/RawLogView/index.tsx
index 711b4420ad00..76d12c1a2287 100644
--- a/frontend/src/components/Logs/RawLogView/index.tsx
+++ b/frontend/src/components/Logs/RawLogView/index.tsx
@@ -30,7 +30,10 @@ function RawLogView(props: RawLogViewProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const text = useMemo(
- () => `${dayjs(data.timestamp / 1e6).format()} | ${data.body}`,
+ () =>
+ typeof data.timestamp === 'string'
+ ? `${dayjs(data.timestamp).format()} | ${data.body}`
+ : `${dayjs(data.timestamp / 1e6).format()} | ${data.body}`,
[data.timestamp, data.body],
);
diff --git a/frontend/src/components/Logs/TableView/index.tsx b/frontend/src/components/Logs/TableView/index.tsx
index 5ec91d87d76c..014ced58a5ee 100644
--- a/frontend/src/components/Logs/TableView/index.tsx
+++ b/frontend/src/components/Logs/TableView/index.tsx
@@ -1,121 +1,18 @@
-import { ExpandAltOutlined } from '@ant-design/icons';
-import Convert from 'ansi-to-html';
-import { Table, Typography } from 'antd';
-import { ColumnsType, ColumnType } from 'antd/es/table';
-import dayjs from 'dayjs';
-import dompurify from 'dompurify';
-// utils
-import { FlatLogData } from 'lib/logs/flatLogData';
-import { useMemo } from 'react';
-import { IField } from 'types/api/logs/fields';
-// interfaces
-import { ILog } from 'types/api/logs/log';
+import { Table } from 'antd';
-// styles
-import { ExpandIconWrapper } from '../RawLogView/styles';
// config
-import { defaultCellStyle, defaultTableStyle, tableScroll } from './config';
-import { TableBodyContent } from './styles';
-
-type ColumnTypeRender
= ReturnType<
- NonNullable['render']>
->;
-
-type LogsTableViewProps = {
- logs: ILog[];
- fields: IField[];
- linesPerRow: number;
- onClickExpand: (log: ILog) => void;
-};
-
-const convert = new Convert();
+import { tableScroll } from './config';
+import { LogsTableViewProps } from './types';
+import { useTableView } from './useTableView';
function LogsTableView(props: LogsTableViewProps): JSX.Element {
- const { logs, fields, linesPerRow, onClickExpand } = props;
-
- const flattenLogData = useMemo(() => logs.map((log) => FlatLogData(log)), [
- logs,
- ]);
-
- const columns: ColumnsType> = useMemo(() => {
- const fieldColumns: ColumnsType> = fields
- .filter((e) => e.name !== 'id')
- .map(({ name }) => ({
- title: name,
- dataIndex: name,
- key: name,
- render: (field): ColumnTypeRender> => ({
- props: {
- style: defaultCellStyle,
- },
- children: (
-
- {field}
-
- ),
- }),
- }));
-
- return [
- {
- title: '',
- dataIndex: 'id',
- key: 'expand',
- // https://github.com/ant-design/ant-design/discussions/36886
- render: (_, item): ColumnTypeRender> => ({
- props: {
- style: defaultCellStyle,
- },
- children: (
- {
- onClickExpand((item as unknown) as ILog);
- }}
- >
-
-
- ),
- }),
- },
- {
- title: 'timestamp',
- dataIndex: 'timestamp',
- key: 'timestamp',
- // https://github.com/ant-design/ant-design/discussions/36886
- render: (field): ColumnTypeRender> => {
- const date = dayjs(field / 1e6).format();
- return {
- children: {date},
- };
- },
- },
- ...fieldColumns,
- {
- title: 'body',
- dataIndex: 'body',
- key: 'body',
- render: (field): ColumnTypeRender> => ({
- props: {
- style: defaultTableStyle,
- },
- children: (
-
- ),
- }),
- },
- ];
- }, [fields, linesPerRow, onClickExpand]);
+ const { dataSource, columns } = useTableView(props);
return (
= ReturnType<
+ NonNullable['render']>
+>;
+
+export type LogsTableViewProps = {
+ logs: ILog[];
+ fields: IField[];
+ linesPerRow: number;
+ onClickExpand: (log: ILog) => void;
+};
diff --git a/frontend/src/components/Logs/TableView/useTableView.tsx b/frontend/src/components/Logs/TableView/useTableView.tsx
new file mode 100644
index 000000000000..fb66235ceff7
--- /dev/null
+++ b/frontend/src/components/Logs/TableView/useTableView.tsx
@@ -0,0 +1,108 @@
+import { ExpandAltOutlined } from '@ant-design/icons';
+import Convert from 'ansi-to-html';
+import { Typography } from 'antd';
+import { ColumnsType } from 'antd/es/table';
+import dayjs from 'dayjs';
+import dompurify from 'dompurify';
+import { FlatLogData } from 'lib/logs/flatLogData';
+import { useMemo } from 'react';
+import { ILog } from 'types/api/logs/log';
+
+import { ExpandIconWrapper } from '../RawLogView/styles';
+import { defaultCellStyle, defaultTableStyle } from './config';
+import { TableBodyContent } from './styles';
+import { ColumnTypeRender, LogsTableViewProps } from './types';
+
+export type UseTableViewResult = {
+ columns: ColumnsType>;
+ dataSource: Record[];
+};
+
+const convert = new Convert();
+
+export const useTableView = (props: LogsTableViewProps): UseTableViewResult => {
+ const { logs, fields, linesPerRow, onClickExpand } = props;
+
+ const flattenLogData = useMemo(() => logs.map((log) => FlatLogData(log)), [
+ logs,
+ ]);
+
+ const columns: ColumnsType> = useMemo(() => {
+ const fieldColumns: ColumnsType> = fields
+ .filter((e) => e.name !== 'id')
+ .map(({ name }) => ({
+ title: name,
+ dataIndex: name,
+ key: name,
+ render: (field): ColumnTypeRender> => ({
+ props: {
+ style: defaultCellStyle,
+ },
+ children: (
+
+ {field}
+
+ ),
+ }),
+ }));
+
+ return [
+ {
+ title: '',
+ dataIndex: 'id',
+ key: 'expand',
+ // https://github.com/ant-design/ant-design/discussions/36886
+ render: (_, item): ColumnTypeRender> => ({
+ props: {
+ style: defaultCellStyle,
+ },
+ children: (
+ {
+ onClickExpand((item as unknown) as ILog);
+ }}
+ >
+
+
+ ),
+ }),
+ },
+ {
+ title: 'timestamp',
+ dataIndex: 'timestamp',
+ key: 'timestamp',
+ // https://github.com/ant-design/ant-design/discussions/36886
+ render: (field): ColumnTypeRender> => {
+ const date =
+ typeof field === 'string'
+ ? dayjs(field).format()
+ : dayjs(field / 1e6).format();
+ return {
+ children: {date},
+ };
+ },
+ },
+ ...fieldColumns,
+ {
+ title: 'body',
+ dataIndex: 'body',
+ key: 'body',
+ render: (field): ColumnTypeRender> => ({
+ props: {
+ style: defaultTableStyle,
+ },
+ children: (
+
+ ),
+ }),
+ },
+ ];
+ }, [fields, linesPerRow, onClickExpand]);
+
+ return { columns, dataSource: flattenLogData };
+};
diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts
index ef0185c39968..8db0f7d2973b 100644
--- a/frontend/src/constants/queryBuilder.ts
+++ b/frontend/src/constants/queryBuilder.ts
@@ -14,6 +14,7 @@ import {
IPromQLQuery,
Query,
QueryState,
+ TagFilter,
} from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard';
import {
@@ -113,6 +114,11 @@ export const initialAutocompleteData: BaseAutocompleteData = {
type: null,
};
+export const initialFilters: TagFilter = {
+ items: [],
+ op: 'AND',
+};
+
const initialQueryBuilderFormValues: IBuilderQuery = {
dataSource: DataSource.METRICS,
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
diff --git a/frontend/src/constants/queryBuilderQueryNames.ts b/frontend/src/constants/queryBuilderQueryNames.ts
index 5a9e9dbfe9a2..b3ee34cf8941 100644
--- a/frontend/src/constants/queryBuilderQueryNames.ts
+++ b/frontend/src/constants/queryBuilderQueryNames.ts
@@ -1,2 +1,16 @@
-export const COMPOSITE_QUERY = 'compositeQuery';
-export const PANEL_TYPES_QUERY = 'panelTypes';
+type QueryParamNames =
+ | 'compositeQuery'
+ | 'panelTypes'
+ | 'pageSize'
+ | 'viewMode'
+ | 'selectedFields'
+ | 'linesPerRow';
+
+export const queryParamNamesMap: Record = {
+ compositeQuery: 'compositeQuery',
+ panelTypes: 'panelTypes',
+ pageSize: 'pageSize',
+ viewMode: 'viewMode',
+ selectedFields: 'selectedFields',
+ linesPerRow: 'linesPerRow',
+};
diff --git a/frontend/src/container/Controls/config.ts b/frontend/src/container/Controls/config.ts
index cc0378c54623..51d85207f89c 100644
--- a/frontend/src/container/Controls/config.ts
+++ b/frontend/src/container/Controls/config.ts
@@ -2,6 +2,8 @@ import { CSSProperties } from 'react';
export const ITEMS_PER_PAGE_OPTIONS = [25, 50, 100, 200];
+export const DEFAULT_PER_PAGE_VALUE = 100;
+
export const defaultSelectStyle: CSSProperties = {
minWidth: '6rem',
};
diff --git a/frontend/src/container/ExplorerControlPanel/ExplorerControlPanel.interfaces.ts b/frontend/src/container/ExplorerControlPanel/ExplorerControlPanel.interfaces.ts
new file mode 100644
index 000000000000..15470012590f
--- /dev/null
+++ b/frontend/src/container/ExplorerControlPanel/ExplorerControlPanel.interfaces.ts
@@ -0,0 +1,7 @@
+import { OptionsMenuConfig } from 'container/OptionsMenu/types';
+
+export type ExplorerControlPanelProps = {
+ isShowPageSize: boolean;
+ isLoading: boolean;
+ optionsMenuConfig?: OptionsMenuConfig;
+};
diff --git a/frontend/src/container/ExplorerControlPanel/index.tsx b/frontend/src/container/ExplorerControlPanel/index.tsx
new file mode 100644
index 000000000000..4d6acb857172
--- /dev/null
+++ b/frontend/src/container/ExplorerControlPanel/index.tsx
@@ -0,0 +1,29 @@
+import { Col, Row } from 'antd';
+import OptionsMenu from 'container/OptionsMenu';
+import PageSizeSelect from 'container/PageSizeSelect';
+
+import { ExplorerControlPanelProps } from './ExplorerControlPanel.interfaces';
+import { ContainerStyled } from './styles';
+
+function ExplorerControlPanel({
+ isLoading,
+ isShowPageSize,
+ optionsMenuConfig,
+}: ExplorerControlPanelProps): JSX.Element {
+ return (
+
+
+ {optionsMenuConfig && (
+
+
+
+ )}
+
+
+
+
+
+ );
+}
+
+export default ExplorerControlPanel;
diff --git a/frontend/src/container/ExplorerControlPanel/styles.ts b/frontend/src/container/ExplorerControlPanel/styles.ts
new file mode 100644
index 000000000000..0c9a799fef1f
--- /dev/null
+++ b/frontend/src/container/ExplorerControlPanel/styles.ts
@@ -0,0 +1,5 @@
+import styled from 'styled-components';
+
+export const ContainerStyled = styled.div`
+ margin-bottom: 0.3rem;
+`;
diff --git a/frontend/src/container/ExportPanel/index.tsx b/frontend/src/container/ExportPanel/index.tsx
index 087b17fe72dc..35ac124faead 100644
--- a/frontend/src/container/ExportPanel/index.tsx
+++ b/frontend/src/container/ExportPanel/index.tsx
@@ -1,5 +1,5 @@
import { Button, Dropdown, MenuProps, Modal } from 'antd';
-import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import { useCallback, useMemo, useState } from 'react';
@@ -22,9 +22,9 @@ function ExportPanel({
const onCreateAlertsHandler = useCallback(() => {
history.push(
- `${ROUTES.ALERTS_NEW}?${COMPOSITE_QUERY}=${encodeURIComponent(
- JSON.stringify(query),
- )}`,
+ `${ROUTES.ALERTS_NEW}?${
+ queryParamNamesMap.compositeQuery
+ }=${encodeURIComponent(JSON.stringify(query))}`,
);
}, [query]);
diff --git a/frontend/src/container/GridGraphLayout/WidgetHeader/index.tsx b/frontend/src/container/GridGraphLayout/WidgetHeader/index.tsx
index a68d48ef8d18..0e950418b701 100644
--- a/frontend/src/container/GridGraphLayout/WidgetHeader/index.tsx
+++ b/frontend/src/container/GridGraphLayout/WidgetHeader/index.tsx
@@ -9,7 +9,7 @@ import {
import { Dropdown, MenuProps, Tooltip, Typography } from 'antd';
import { MenuItemType } from 'antd/es/menu/hooks/useItems';
import Spinner from 'components/Spinner';
-import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import useComponentPermission from 'hooks/useComponentPermission';
import history from 'lib/history';
import { useCallback, useMemo, useState } from 'react';
@@ -64,7 +64,9 @@ function WidgetHeader({
history.push(
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${
widget.panelTypes
- }&${COMPOSITE_QUERY}=${encodeURIComponent(JSON.stringify(widget.query))}`,
+ }&${queryParamNamesMap.compositeQuery}=${encodeURIComponent(
+ JSON.stringify(widget.query),
+ )}`,
);
}, [widget.id, widget.panelTypes, widget.query]);
diff --git a/frontend/src/container/ListAlertRules/ListAlert.tsx b/frontend/src/container/ListAlertRules/ListAlert.tsx
index 76843be3066a..b1bfc7f72506 100644
--- a/frontend/src/container/ListAlertRules/ListAlert.tsx
+++ b/frontend/src/container/ListAlertRules/ListAlert.tsx
@@ -5,7 +5,7 @@ import { ColumnsType } from 'antd/lib/table';
import saveAlertApi from 'api/alerts/save';
import { ResizeTable } from 'components/ResizeTable';
import TextToolTip from 'components/TextToolTip';
-import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import ROUTES from 'constants/routes';
import useComponentPermission from 'hooks/useComponentPermission';
import useInterval from 'hooks/useInterval';
@@ -75,11 +75,9 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
const compositeQuery = mapQueryDataFromApi(record.condition.compositeQuery);
history.push(
- `${
- ROUTES.EDIT_ALERTS
- }?ruleId=${record.id.toString()}&${COMPOSITE_QUERY}=${encodeURIComponent(
- JSON.stringify(compositeQuery),
- )}`,
+ `${ROUTES.EDIT_ALERTS}?ruleId=${record.id.toString()}&${
+ queryParamNamesMap.compositeQuery
+ }=${encodeURIComponent(JSON.stringify(compositeQuery))}`,
);
})
.catch(handleError);
diff --git a/frontend/src/container/LogControls/index.tsx b/frontend/src/container/LogControls/index.tsx
index d0dfe4f27b5b..7c3de3fef3ad 100644
--- a/frontend/src/container/LogControls/index.tsx
+++ b/frontend/src/container/LogControls/index.tsx
@@ -83,12 +83,17 @@ function LogControls(): JSX.Element | null {
const flattenLogData = useMemo(
() =>
- logs.map((log) =>
- FlatLogData({
+ logs.map((log) => {
+ const timestamp =
+ typeof log.timestamp === 'string'
+ ? dayjs(log.timestamp).format()
+ : dayjs(log.timestamp / 1e6).format();
+
+ return FlatLogData({
...log,
- timestamp: (dayjs(log.timestamp / 1e6).format() as unknown) as number,
- }),
- ),
+ timestamp,
+ });
+ }),
[logs],
);
diff --git a/frontend/src/container/LogDetailedView/index.tsx b/frontend/src/container/LogDetailedView/index.tsx
index 47f098244870..3860d657f0fe 100644
--- a/frontend/src/container/LogDetailedView/index.tsx
+++ b/frontend/src/container/LogDetailedView/index.tsx
@@ -1,4 +1,4 @@
-import { Drawer, Tabs } from 'antd';
+import LogDetail from 'components/LogDetail';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { AppState } from 'store/reducers';
@@ -6,9 +6,6 @@ import AppActions from 'types/actions';
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
import { ILogsReducer } from 'types/reducer/logs';
-import JSONView from './JsonView';
-import TableView from './TableView';
-
function LogDetailedView(): JSX.Element {
const { detailedLog } = useSelector(
(state) => state.logs,
@@ -23,33 +20,7 @@ function LogDetailedView(): JSX.Element {
});
};
- const items = [
- {
- label: 'Table',
- key: '1',
- children: detailedLog && ,
- },
- {
- label: 'JSON',
- key: '2',
- children: detailedLog && ,
- },
- ];
-
- return (
-
-
-
- );
+ return ;
}
export default LogDetailedView;
diff --git a/frontend/src/container/LogExplorerDetailedView/LogExplorerDetailedView.interfaces.ts b/frontend/src/container/LogExplorerDetailedView/LogExplorerDetailedView.interfaces.ts
new file mode 100644
index 000000000000..ca9620657fbd
--- /dev/null
+++ b/frontend/src/container/LogExplorerDetailedView/LogExplorerDetailedView.interfaces.ts
@@ -0,0 +1,6 @@
+import { ILog } from 'types/api/logs/log';
+
+export type LogExplorerDetailedViewProps = {
+ log: ILog | null;
+ onClose: () => void;
+};
diff --git a/frontend/src/container/LogExplorerDetailedView/index.tsx b/frontend/src/container/LogExplorerDetailedView/index.tsx
new file mode 100644
index 000000000000..1a573f2ec8b2
--- /dev/null
+++ b/frontend/src/container/LogExplorerDetailedView/index.tsx
@@ -0,0 +1,16 @@
+import LogDetail from 'components/LogDetail';
+
+import { LogExplorerDetailedViewProps } from './LogExplorerDetailedView.interfaces';
+
+function LogExplorerDetailedView({
+ log,
+ onClose,
+}: LogExplorerDetailedViewProps): JSX.Element {
+ const onDrawerClose = (): void => {
+ onClose();
+ };
+
+ return ;
+}
+
+export default LogExplorerDetailedView;
diff --git a/frontend/src/container/LogsExplorerChart/LogsExplorerChart.interfaces.ts b/frontend/src/container/LogsExplorerChart/LogsExplorerChart.interfaces.ts
new file mode 100644
index 000000000000..6df4bacee30c
--- /dev/null
+++ b/frontend/src/container/LogsExplorerChart/LogsExplorerChart.interfaces.ts
@@ -0,0 +1,6 @@
+import { QueryData } from 'types/api/widgets/getQuery';
+
+export type LogsExplorerChartProps = {
+ data: QueryData[];
+ isLoading: boolean;
+};
diff --git a/frontend/src/container/LogsExplorerChart/index.tsx b/frontend/src/container/LogsExplorerChart/index.tsx
index 02cc7606d763..2e85d9c5ded6 100644
--- a/frontend/src/container/LogsExplorerChart/index.tsx
+++ b/frontend/src/container/LogsExplorerChart/index.tsx
@@ -1,48 +1,43 @@
import Graph from 'components/Graph';
import Spinner from 'components/Spinner';
-import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
-import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
-import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
-import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
-import { getExplorerChartData } from 'lib/explorer/getExplorerChartData';
+import getChartData, { GetChartDataProps } from 'lib/getChartData';
+import { colors } from 'lib/getRandomColor';
import { memo, useMemo } from 'react';
-import { useSelector } from 'react-redux';
-import { AppState } from 'store/reducers';
-import { GlobalReducer } from 'types/reducer/globalTime';
+import { LogsExplorerChartProps } from './LogsExplorerChart.interfaces';
import { CardStyled } from './LogsExplorerChart.styled';
-function LogsExplorerChart(): JSX.Element {
- const { stagedQuery, panelType, isEnabledQuery } = useQueryBuilder();
+function LogsExplorerChart({
+ data,
+ isLoading,
+}: LogsExplorerChartProps): JSX.Element {
+ const handleCreateDatasets: Required['createDataset'] = (
+ element,
+ index,
+ allLabels,
+ ) => ({
+ label: allLabels[index],
+ data: element,
+ backgroundColor: colors[index % colors.length] || 'red',
+ borderColor: colors[index % colors.length] || 'red',
+ });
- const { selectedTime } = useSelector(
- (state) => state.globalTime,
+ const graphData = useMemo(
+ () =>
+ getChartData({
+ queryData: [
+ {
+ queryData: data,
+ },
+ ],
+ createDataset: handleCreateDatasets,
+ }),
+ [data],
);
- const { data, isFetching } = useGetQueryRange(
- {
- query: stagedQuery || initialQueriesMap.metrics,
- graphType: panelType || PANEL_TYPES.LIST,
- globalSelectedInterval: selectedTime,
- selectedTime: 'GLOBAL_TIME',
- },
- {
- queryKey: [REACT_QUERY_KEY.GET_QUERY_RANGE, selectedTime, stagedQuery],
- enabled: isEnabledQuery,
- },
- );
-
- const graphData = useMemo(() => {
- if (data?.payload.data && data.payload.data.result.length > 0) {
- return getExplorerChartData([data.payload.data.result[0]]);
- }
-
- return getExplorerChartData([]);
- }, [data]);
-
return (
- {isFetching ? (
+ {isLoading ? (
) : (
(
+ {children}
+);
+
+// eslint-disable-next-line react/function-component-definition
+const CustomTableRow: TableComponents['TableRow'] = ({
+ children,
+ context,
+ ...props
+ // eslint-disable-next-line react/jsx-props-no-spreading
+}) => {children};
+
+function InfinityTable({
+ tableViewProps,
+ infitiyTableProps,
+}: InfinityTableProps): JSX.Element | null {
+ const { onEndReached } = infitiyTableProps;
+ const { dataSource, columns } = useTableView(tableViewProps);
+
+ const itemContent = useCallback(
+ (index: number, log: Record): JSX.Element => (
+ <>
+ {columns.map((column) => {
+ if (!column.render) return Empty | ;
+
+ const element: ColumnTypeRender> = column.render(
+ log[column.key as keyof Record],
+ log,
+ index,
+ );
+
+ const elementWithChildren = element as Exclude<
+ ColumnTypeRender>,
+ ReactNode
+ >;
+
+ const children = elementWithChildren.children as ReactElement;
+ const props = elementWithChildren.props as Record;
+
+ return (
+
+ {cloneElement(children, props)}
+
+ );
+ })}
+ >
+ ),
+ [columns],
+ );
+
+ const tableHeader = useCallback(
+ () => (
+
+ {columns.map((column) => (
+
+ {column.title as string}
+
+ ))}
+
+ ),
+ [columns],
+ );
+
+ return (
+
+ );
+}
+
+export default InfinityTable;
diff --git a/frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts b/frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts
new file mode 100644
index 000000000000..a45d2d6b02e9
--- /dev/null
+++ b/frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts
@@ -0,0 +1,40 @@
+import { themeColors } from 'constants/theme';
+import styled from 'styled-components';
+
+export const TableStyled = styled.table`
+ width: 100%;
+ border-top: 1px solid rgba(253, 253, 253, 0.12);
+ border-radius: 2px 2px 0 0;
+ border-collapse: separate;
+ border-spacing: 0;
+ border-inline-start: 1px solid rgba(253, 253, 253, 0.12);
+ border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
+`;
+
+export const TableCellStyled = styled.td`
+ padding: 0.5rem;
+ border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
+ border-top: 1px solid rgba(253, 253, 253, 0.12);
+ background-color: ${themeColors.lightBlack};
+`;
+
+export const TableRowStyled = styled.tr`
+ &:hover {
+ ${TableCellStyled} {
+ background-color: #1d1d1d;
+ }
+ }
+`;
+
+export const TableHeaderCellStyled = styled.th`
+ padding: 0.5rem;
+ border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
+ background-color: #1d1d1d;
+ &:first-child {
+ border-start-start-radius: 2px;
+ }
+ &:last-child {
+ border-start-end-radius: 2px;
+ border-inline-end: none;
+ }
+`;
diff --git a/frontend/src/container/LogsExplorerList/InfinityTableView/types.ts b/frontend/src/container/LogsExplorerList/InfinityTableView/types.ts
new file mode 100644
index 000000000000..abd32a8c2306
--- /dev/null
+++ b/frontend/src/container/LogsExplorerList/InfinityTableView/types.ts
@@ -0,0 +1,8 @@
+import { LogsTableViewProps } from 'components/Logs/TableView/types';
+
+export type InfinityTableProps = {
+ tableViewProps: LogsTableViewProps;
+ infitiyTableProps: {
+ onEndReached: (index: number) => void;
+ };
+};
diff --git a/frontend/src/container/LogsExplorerList/LogsExplorerList.interfaces.ts b/frontend/src/container/LogsExplorerList/LogsExplorerList.interfaces.ts
index 4d0a534ad20a..b238031ac049 100644
--- a/frontend/src/container/LogsExplorerList/LogsExplorerList.interfaces.ts
+++ b/frontend/src/container/LogsExplorerList/LogsExplorerList.interfaces.ts
@@ -1,3 +1,11 @@
-import { QueryDataV3 } from 'types/api/widgets/getQuery';
+import { ILog } from 'types/api/logs/log';
+import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
-export type LogsExplorerListProps = { data: QueryDataV3[]; isLoading: boolean };
+export type LogsExplorerListProps = {
+ isLoading: boolean;
+ currentStagedQueryData: IBuilderQuery | null;
+ logs: ILog[];
+ onEndReached: (index: number) => void;
+ onExpand: (log: ILog) => void;
+ onOpenDetailedView: (log: ILog) => void;
+};
diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx
index cacc611f9005..de537036f8f9 100644
--- a/frontend/src/container/LogsExplorerList/index.tsx
+++ b/frontend/src/container/LogsExplorerList/index.tsx
@@ -2,38 +2,43 @@ import { Card, Typography } from 'antd';
// components
import ListLogView from 'components/Logs/ListLogView';
import RawLogView from 'components/Logs/RawLogView';
-import LogsTableView from 'components/Logs/TableView';
import Spinner from 'components/Spinner';
-import { LogViewMode } from 'container/LogsTable';
-import { Container, Heading } from 'container/LogsTable/styles';
+import ExplorerControlPanel from 'container/ExplorerControlPanel';
+import { Heading } from 'container/LogsTable/styles';
+import { useOptionsMenu } from 'container/OptionsMenu';
import { contentStyle } from 'container/Trace/Search/config';
+import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import useFontFaceObserver from 'hooks/useFontObserver';
-import { memo, useCallback, useMemo, useState } from 'react';
+import { memo, useCallback, useMemo } from 'react';
import { Virtuoso } from 'react-virtuoso';
// interfaces
import { ILog } from 'types/api/logs/log';
+import { DataSource, StringOperators } from 'types/common/queryBuilder';
+import InfinityTableView from './InfinityTableView';
import { LogsExplorerListProps } from './LogsExplorerList.interfaces';
+import { InfinityWrapperStyled } from './styles';
+import { convertKeysToColumnFields } from './utils';
+
+function Footer(): JSX.Element {
+ return ;
+}
function LogsExplorerList({
- data,
isLoading,
+ currentStagedQueryData,
+ logs,
+ onOpenDetailedView,
+ onEndReached,
+ onExpand,
}: LogsExplorerListProps): JSX.Element {
- const [viewMode] = useState('raw');
- const [linesPerRow] = useState(20);
+ const { initialDataSource } = useQueryBuilder();
- const logs: ILog[] = useMemo(() => {
- if (data.length > 0 && data[0].list) {
- const logs: ILog[] = data[0].list.map((item) => ({
- timestamp: +item.timestamp,
- ...item.data,
- }));
-
- return logs;
- }
-
- return [];
- }, [data]);
+ const { options, config } = useOptionsMenu({
+ dataSource: initialDataSource || DataSource.METRICS,
+ aggregateOperator:
+ currentStagedQueryData?.aggregateOperator || StringOperators.NOOP,
+ });
useFontFaceObserver(
[
@@ -42,75 +47,107 @@ function LogsExplorerList({
weight: '300',
},
],
- viewMode === 'raw',
+ options.format === 'raw',
{
timeout: 5000,
},
);
- // TODO: implement here linesPerRow, mode like in useSelectedLogView
+
+ const selectedFields = useMemo(
+ () => convertKeysToColumnFields(options.selectColumns),
+ [options],
+ );
const getItemContent = useCallback(
- (index: number): JSX.Element => {
- const log = logs[index];
-
- if (viewMode === 'raw') {
+ (_: number, log: ILog): JSX.Element => {
+ if (options.format === 'raw') {
return (
{}}
+ linesPerRow={options.maxLines}
+ onClickExpand={onExpand}
/>
);
}
- return ;
+ return (
+
+ );
},
- [logs, linesPerRow, viewMode],
+ [
+ options.format,
+ options.maxLines,
+ selectedFields,
+ onOpenDetailedView,
+ onExpand,
+ ],
);
const renderContent = useMemo(() => {
- if (viewMode === 'table') {
+ const components = isLoading
+ ? {
+ Footer,
+ }
+ : {};
+
+ if (options.format === 'table') {
return (
- {}}
+
);
}
return (
-
+
);
- }, [getItemContent, linesPerRow, logs, viewMode]);
-
- if (isLoading) {
- return ;
- }
+ }, [
+ isLoading,
+ logs,
+ options.format,
+ options.maxLines,
+ onEndReached,
+ getItemContent,
+ selectedFields,
+ onExpand,
+ ]);
return (
-
- {viewMode !== 'table' && (
+ <>
+
+ {options.format !== 'table' && (
Event
)}
-
{logs.length === 0 && No logs lines found}
-
- {renderContent}
-
+ {renderContent}
+ >
);
}
diff --git a/frontend/src/container/LogsExplorerList/styles.ts b/frontend/src/container/LogsExplorerList/styles.ts
new file mode 100644
index 000000000000..1c3dde6466c4
--- /dev/null
+++ b/frontend/src/container/LogsExplorerList/styles.ts
@@ -0,0 +1,6 @@
+import styled from 'styled-components';
+
+export const InfinityWrapperStyled = styled.div`
+ min-height: 40rem;
+ display: flex;
+`;
diff --git a/frontend/src/container/LogsExplorerList/utils.ts b/frontend/src/container/LogsExplorerList/utils.ts
new file mode 100644
index 000000000000..6058ac4b4f5e
--- /dev/null
+++ b/frontend/src/container/LogsExplorerList/utils.ts
@@ -0,0 +1,11 @@
+import { IField } from 'types/api/logs/fields';
+import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
+
+export const convertKeysToColumnFields = (
+ keys: BaseAutocompleteData[],
+): IField[] =>
+ keys.map((item) => ({
+ dataType: item.dataType as string,
+ name: item.key,
+ type: item.type as string,
+ }));
diff --git a/frontend/src/container/LogsExplorerTable/index.tsx b/frontend/src/container/LogsExplorerTable/index.tsx
index 40aefcf5e5b1..bbcb2aa99b55 100644
--- a/frontend/src/container/LogsExplorerTable/index.tsx
+++ b/frontend/src/container/LogsExplorerTable/index.tsx
@@ -6,8 +6,8 @@ import { memo } from 'react';
import { LogsExplorerTableProps } from './LogsExplorerTable.interfaces';
function LogsExplorerTable({
- isLoading,
data,
+ isLoading,
}: LogsExplorerTableProps): JSX.Element {
const { stagedQuery } = useQueryBuilder();
diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx
index 6229a3fe22e3..a9a8d7089487 100644
--- a/frontend/src/container/LogsExplorerViews/index.tsx
+++ b/frontend/src/container/LogsExplorerViews/index.tsx
@@ -1,50 +1,62 @@
import { TabsProps } from 'antd';
-import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
-import { PANEL_TYPES_QUERY } from 'constants/queryBuilderQueryNames';
-import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import { PANEL_TYPES } from 'constants/queryBuilder';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
+import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
+import LogExplorerDetailedView from 'container/LogExplorerDetailedView';
+import LogsExplorerChart from 'container/LogsExplorerChart';
import LogsExplorerList from 'container/LogsExplorerList';
import LogsExplorerTable from 'container/LogsExplorerTable';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
-import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
+import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
-import { memo, useCallback, useEffect, useMemo } from 'react';
-import { useSelector } from 'react-redux';
-import { AppState } from 'store/reducers';
-import { DataSource } from 'types/common/queryBuilder';
-import { GlobalReducer } from 'types/reducer/globalTime';
+import useUrlQueryData from 'hooks/useUrlQueryData';
+import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
+import { memo, useCallback, useEffect, useMemo, useState } from 'react';
+import { ILog } from 'types/api/logs/log';
+import {
+ IBuilderQuery,
+ OrderByPayload,
+ Query,
+} from 'types/api/queryBuilder/queryBuilderData';
+import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { TabsStyled } from './LogsExplorerViews.styled';
function LogsExplorerViews(): JSX.Element {
+ const { queryData: pageSize } = useUrlQueryData(
+ queryParamNamesMap.pageSize,
+ DEFAULT_PER_PAGE_VALUE,
+ );
+
+ // Context
const {
currentQuery,
stagedQuery,
panelType,
- isEnabledQuery,
updateAllQueriesOperators,
redirectWithQueryBuilderData,
} = useQueryBuilder();
- const { selectedTime } = useSelector(
- (state) => state.globalTime,
- );
+ // State
+ const [activeLog, setActiveLog] = useState(null);
+ const [page, setPage] = useState(1);
+ const [logs, setLogs] = useState([]);
+ const [requestData, setRequestData] = useState(null);
- const { data, isFetching, isError } = useGetQueryRange(
- {
- query: stagedQuery || initialQueriesMap.metrics,
- graphType: panelType || PANEL_TYPES.LIST,
- globalSelectedInterval: selectedTime,
- selectedTime: 'GLOBAL_TIME',
- params: {
- dataSource: DataSource.LOGS,
- },
- },
- {
- queryKey: [REACT_QUERY_KEY.GET_QUERY_RANGE, selectedTime, stagedQuery],
- enabled: isEnabledQuery,
- },
- );
+ const currentStagedQueryData = useMemo(() => {
+ if (!stagedQuery || stagedQuery.builder.queryData.length !== 1) return null;
+
+ return stagedQuery.builder.queryData[0];
+ }, [stagedQuery]);
+
+ const orderByTimestamp: OrderByPayload | null = useMemo(() => {
+ const timestampOrderBy = currentStagedQueryData?.orderBy.find(
+ (item) => item.columnName === 'timestamp',
+ );
+
+ return timestampOrderBy || null;
+ }, [currentStagedQueryData]);
const isMultipleQueries = useMemo(
() =>
@@ -62,35 +74,57 @@ function LogsExplorerViews(): JSX.Element {
return groupByCount > 0;
}, [currentQuery]);
- const currentData = useMemo(
- () => data?.payload.data.newResult.data.result || [],
- [data],
+ const isLimit: boolean = useMemo(() => {
+ if (!currentStagedQueryData) return false;
+ if (!currentStagedQueryData.limit) return false;
+
+ return logs.length >= currentStagedQueryData.limit;
+ }, [logs.length, currentStagedQueryData]);
+
+ const listChartQuery = useMemo(() => {
+ if (!stagedQuery || !currentStagedQueryData) return null;
+
+ const modifiedQueryData: IBuilderQuery = {
+ ...currentStagedQueryData,
+ aggregateOperator: StringOperators.COUNT,
+ };
+
+ const modifiedQuery: Query = {
+ ...stagedQuery,
+ builder: {
+ ...stagedQuery.builder,
+ queryData: stagedQuery.builder.queryData.map((item) => ({
+ ...item,
+ ...modifiedQueryData,
+ })),
+ },
+ };
+
+ return modifiedQuery;
+ }, [stagedQuery, currentStagedQueryData]);
+
+ const listChartData = useGetExplorerQueryRange(
+ listChartQuery,
+ PANEL_TYPES.TIME_SERIES,
);
- const tabsItems: TabsProps['items'] = useMemo(
- () => [
- {
- label: 'List View',
- key: PANEL_TYPES.LIST,
- disabled: isMultipleQueries || isGroupByExist,
- children: ,
- },
- {
- label: 'TimeSeries',
- key: PANEL_TYPES.TIME_SERIES,
- children: (
-
- ),
- },
- {
- label: 'Table',
- key: PANEL_TYPES.TABLE,
- children: ,
- },
- ],
- [isMultipleQueries, isGroupByExist, currentData, isFetching, data, isError],
+ const { data, isFetching, isError } = useGetExplorerQueryRange(
+ requestData,
+ panelType,
+ {
+ keepPreviousData: true,
+ enabled: !isLimit,
+ },
);
+ const handleSetActiveLog = useCallback((nextActiveLog: ILog) => {
+ setActiveLog(nextActiveLog);
+ }, []);
+
+ const handleClearActiveLog = useCallback(() => {
+ setActiveLog(null);
+ }, []);
+
const handleChangeView = useCallback(
(newPanelType: string) => {
if (newPanelType === panelType) return;
@@ -101,7 +135,9 @@ function LogsExplorerViews(): JSX.Element {
DataSource.LOGS,
);
- redirectWithQueryBuilderData(query, { [PANEL_TYPES_QUERY]: newPanelType });
+ redirectWithQueryBuilderData(query, {
+ [queryParamNamesMap.panelTypes]: newPanelType,
+ });
},
[
currentQuery,
@@ -111,6 +147,75 @@ function LogsExplorerViews(): JSX.Element {
],
);
+ const getRequestData = useCallback(
+ (
+ query: Query | null,
+ params: { page: number; log: ILog | null; pageSize: number },
+ ): Query | null => {
+ if (!query) return null;
+
+ const paginateData = getPaginationQueryData({
+ currentStagedQueryData,
+ listItemId: params.log ? params.log.id : null,
+ orderByTimestamp,
+ page: params.page,
+ pageSize: params.pageSize,
+ });
+
+ const data: Query = {
+ ...query,
+ builder: {
+ ...query.builder,
+ queryData: query.builder.queryData.map((item) => ({
+ ...item,
+ ...paginateData,
+ pageSize: params.pageSize,
+ })),
+ },
+ };
+
+ return data;
+ },
+ [currentStagedQueryData, orderByTimestamp],
+ );
+
+ const handleEndReached = useCallback(
+ (index: number) => {
+ if (isLimit) return;
+
+ const lastLog = logs[index];
+
+ const limit = currentStagedQueryData?.limit;
+
+ const nextLogsLenth = logs.length + pageSize;
+
+ const nextPageSize =
+ limit && nextLogsLenth >= limit ? limit - logs.length : pageSize;
+
+ if (!stagedQuery) return;
+
+ const newRequestData = getRequestData(stagedQuery, {
+ page: page + 1,
+ log: orderByTimestamp ? lastLog : null,
+ pageSize: nextPageSize,
+ });
+
+ setPage((prevPage) => prevPage + 1);
+
+ setRequestData(newRequestData);
+ },
+ [
+ isLimit,
+ logs,
+ currentStagedQueryData?.limit,
+ pageSize,
+ stagedQuery,
+ getRequestData,
+ page,
+ orderByTimestamp,
+ ],
+ );
+
useEffect(() => {
const shouldChangeView = isMultipleQueries || isGroupByExist;
@@ -119,15 +224,115 @@ function LogsExplorerViews(): JSX.Element {
}
}, [panelType, isMultipleQueries, isGroupByExist, handleChangeView]);
+ useEffect(() => {
+ 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,
+ }));
+ setLogs((prevLogs) => [...prevLogs, ...currentLogs]);
+ }
+ }, [data]);
+
+ useEffect(() => {
+ if (requestData?.id !== stagedQuery?.id) {
+ const newRequestData = getRequestData(stagedQuery, {
+ page: 1,
+ log: null,
+ pageSize,
+ });
+ setLogs([]);
+ setPage(1);
+ setRequestData(newRequestData);
+ }
+ }, [stagedQuery, requestData, getRequestData, pageSize]);
+
+ const tabsItems: TabsProps['items'] = useMemo(
+ () => [
+ {
+ label: 'List View',
+ key: PANEL_TYPES.LIST,
+ disabled: isMultipleQueries || isGroupByExist,
+ children: (
+
+ ),
+ },
+ {
+ label: 'TimeSeries',
+ key: PANEL_TYPES.TIME_SERIES,
+ children: (
+
+ ),
+ },
+ {
+ label: 'Table',
+ key: PANEL_TYPES.TABLE,
+ children: (
+
+ ),
+ },
+ ],
+ [
+ isMultipleQueries,
+ isGroupByExist,
+ isFetching,
+ currentStagedQueryData,
+ logs,
+ handleSetActiveLog,
+ handleEndReached,
+ data,
+ isError,
+ ],
+ );
+
+ const chartData = useMemo(() => {
+ if (!stagedQuery) return [];
+
+ if (panelType === PANEL_TYPES.LIST) {
+ if (
+ listChartData &&
+ listChartData.data &&
+ listChartData.data.payload.data.result.length > 0
+ ) {
+ return listChartData.data.payload.data.result;
+ }
+ return [];
+ }
+
+ if (!data || data.payload.data.result.length === 0) return [];
+
+ const isGroupByExist = stagedQuery.builder.queryData.some(
+ (queryData) => queryData.groupBy.length > 0,
+ );
+
+ return isGroupByExist
+ ? data.payload.data.result
+ : [data.payload.data.result[0]];
+ }, [stagedQuery, data, panelType, listChartData]);
+
return (
-
+ <>
+
-
+
+ >
);
}
diff --git a/frontend/src/container/LogsTable/index.tsx b/frontend/src/container/LogsTable/index.tsx
index 464258877ec4..b514aa3ee848 100644
--- a/frontend/src/container/LogsTable/index.tsx
+++ b/frontend/src/container/LogsTable/index.tsx
@@ -7,10 +7,11 @@ import Spinner from 'components/Spinner';
import { contentStyle } from 'container/Trace/Search/config';
import useFontFaceObserver from 'hooks/useFontObserver';
import { memo, useCallback, useMemo } from 'react';
-import { useSelector } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import { Virtuoso } from 'react-virtuoso';
-// interfaces
import { AppState } from 'store/reducers';
+// interfaces
+import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
import { ILog } from 'types/api/logs/log';
import { ILogsReducer } from 'types/reducer/logs';
@@ -28,6 +29,8 @@ type LogsTableProps = {
function LogsTable(props: LogsTableProps): JSX.Element {
const { viewMode, onClickExpand, linesPerRow } = props;
+ const dispatch = useDispatch();
+
useFontFaceObserver(
[
{
@@ -58,6 +61,16 @@ function LogsTable(props: LogsTableProps): JSX.Element {
liveTail,
]);
+ const handleOpenDetailedView = useCallback(
+ (logData: ILog) => {
+ dispatch({
+ type: SET_DETAILED_LOG_DATA,
+ payload: logData,
+ });
+ },
+ [dispatch],
+ );
+
const getItemContent = useCallback(
(index: number): JSX.Element => {
const log = logs[index];
@@ -73,9 +86,23 @@ function LogsTable(props: LogsTableProps): JSX.Element {
);
}
- return ;
+ return (
+
+ );
},
- [logs, linesPerRow, viewMode, onClickExpand],
+ [
+ logs,
+ viewMode,
+ selected,
+ handleOpenDetailedView,
+ linesPerRow,
+ onClickExpand,
+ ],
);
const renderContent = useMemo(() => {
diff --git a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx
index f490f1f23e00..8ede8520b78e 100644
--- a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx
+++ b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { initialQueriesMap } from 'constants/queryBuilder';
-import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications';
import history from 'lib/history';
@@ -47,7 +47,7 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
history.push(
`${history.location.pathname}/new?graphType=${name}&widgetId=${
emptyLayout.i
- }&${COMPOSITE_QUERY}=${encodeURIComponent(
+ }&${queryParamNamesMap.compositeQuery}=${encodeURIComponent(
JSON.stringify(initialQueriesMap.metrics),
)}`,
);
diff --git a/frontend/src/container/OptionsMenu/FormatField/index.tsx b/frontend/src/container/OptionsMenu/FormatField/index.tsx
index c4d88bacbea6..3b82631a5d08 100644
--- a/frontend/src/container/OptionsMenu/FormatField/index.tsx
+++ b/frontend/src/container/OptionsMenu/FormatField/index.tsx
@@ -18,9 +18,9 @@ function FormatField({ config }: FormatFieldProps): JSX.Element | null {
value={config.value}
onChange={config.onChange}
>
- {t('options_menu.row')}
- {t('options_menu.default')}
- {t('options_menu.column')}
+ {t('options_menu.row')}
+ {t('options_menu.default')}
+ {t('options_menu.column')}
);
diff --git a/frontend/src/container/OptionsMenu/constants.ts b/frontend/src/container/OptionsMenu/constants.ts
index 9b9d41709d4f..b1e54636865a 100644
--- a/frontend/src/container/OptionsMenu/constants.ts
+++ b/frontend/src/container/OptionsMenu/constants.ts
@@ -4,6 +4,6 @@ export const URL_OPTIONS = 'options';
export const defaultOptionsQuery: OptionsQuery = {
selectColumns: [],
- maxLines: 0,
- format: 'default',
+ maxLines: 2,
+ format: 'list',
};
diff --git a/frontend/src/container/OptionsMenu/types.ts b/frontend/src/container/OptionsMenu/types.ts
index f557e1dbe339..e7fdf89f9758 100644
--- a/frontend/src/container/OptionsMenu/types.ts
+++ b/frontend/src/container/OptionsMenu/types.ts
@@ -1,10 +1,11 @@
import { InputNumberProps, RadioProps, SelectProps } from 'antd';
+import { LogViewMode } from 'container/LogsTable';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
export interface OptionsQuery {
selectColumns: BaseAutocompleteData[];
maxLines: number;
- format: 'default' | 'row' | 'column';
+ format: LogViewMode;
}
export interface InitialOptions
diff --git a/frontend/src/container/OptionsMenu/useOptionsMenu.ts b/frontend/src/container/OptionsMenu/useOptionsMenu.ts
index f39557f9f738..578fd84e2686 100644
--- a/frontend/src/container/OptionsMenu/useOptionsMenu.ts
+++ b/frontend/src/container/OptionsMenu/useOptionsMenu.ts
@@ -35,16 +35,17 @@ const useOptionsMenu = ({
query: optionsQuery,
queryData: optionsQueryData,
redirectWithQuery: redirectWithOptionsData,
- } = useUrlQueryData(URL_OPTIONS);
+ } = useUrlQueryData(URL_OPTIONS, defaultOptionsQuery);
const { data, isFetched, isLoading } = useQuery(
- [QueryBuilderKeys.GET_ATTRIBUTE_KEY],
+ [QueryBuilderKeys.GET_ATTRIBUTE_KEY, dataSource, aggregateOperator],
async () =>
getAggregateKeys({
searchText: '',
dataSource,
aggregateOperator,
aggregateAttribute: '',
+ tagType: null,
}),
);
@@ -86,11 +87,16 @@ const useOptionsMenu = ({
}, [] as BaseAutocompleteData[]);
redirectWithOptionsData({
- ...defaultOptionsQuery,
+ ...optionsQueryData,
selectColumns: newSelectedColumns,
});
},
- [attributeKeys, selectedColumnKeys, redirectWithOptionsData],
+ [
+ selectedColumnKeys,
+ redirectWithOptionsData,
+ optionsQueryData,
+ attributeKeys,
+ ],
);
const handleRemoveSelectedColumn = useCallback(
@@ -116,21 +122,21 @@ const useOptionsMenu = ({
const handleFormatChange = useCallback(
(event: RadioChangeEvent) => {
redirectWithOptionsData({
- ...defaultOptionsQuery,
+ ...optionsQueryData,
format: event.target.value,
});
},
- [redirectWithOptionsData],
+ [optionsQueryData, redirectWithOptionsData],
);
const handleMaxLinesChange = useCallback(
(value: string | number | null) => {
redirectWithOptionsData({
- ...defaultOptionsQuery,
+ ...optionsQueryData,
maxLines: value as number,
});
},
- [redirectWithOptionsData],
+ [optionsQueryData, redirectWithOptionsData],
);
const optionsMenuConfig: Required = useMemo(
diff --git a/frontend/src/container/PageSizeSelect/PageSizeSelect.interfaces.ts b/frontend/src/container/PageSizeSelect/PageSizeSelect.interfaces.ts
new file mode 100644
index 000000000000..dc881ebacc12
--- /dev/null
+++ b/frontend/src/container/PageSizeSelect/PageSizeSelect.interfaces.ts
@@ -0,0 +1,4 @@
+export type PageSizeSelectProps = {
+ isLoading: boolean;
+ isShow: boolean;
+};
diff --git a/frontend/src/container/PageSizeSelect/index.tsx b/frontend/src/container/PageSizeSelect/index.tsx
new file mode 100644
index 000000000000..835549ddaa9a
--- /dev/null
+++ b/frontend/src/container/PageSizeSelect/index.tsx
@@ -0,0 +1,51 @@
+import { Col, Row, Select } from 'antd';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
+import {
+ defaultSelectStyle,
+ ITEMS_PER_PAGE_OPTIONS,
+} from 'container/Controls/config';
+import useUrlQueryData from 'hooks/useUrlQueryData';
+import { useCallback } from 'react';
+
+import { PageSizeSelectProps } from './PageSizeSelect.interfaces';
+
+function PageSizeSelect({
+ isLoading,
+ isShow,
+}: PageSizeSelectProps): JSX.Element | null {
+ const { redirectWithQuery, queryData: pageSize } = useUrlQueryData(
+ queryParamNamesMap.pageSize,
+ ITEMS_PER_PAGE_OPTIONS[0],
+ );
+
+ const handleChangePageSize = useCallback(
+ (value: number) => {
+ redirectWithQuery(value);
+ },
+ [redirectWithQuery],
+ );
+
+ if (!isShow) return null;
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default PageSizeSelect;
diff --git a/frontend/src/container/QueryBuilder/QueryBuilder.interfaces.ts b/frontend/src/container/QueryBuilder/QueryBuilder.interfaces.ts
index ab61c94f22ff..6135e0b67059 100644
--- a/frontend/src/container/QueryBuilder/QueryBuilder.interfaces.ts
+++ b/frontend/src/container/QueryBuilder/QueryBuilder.interfaces.ts
@@ -1,5 +1,6 @@
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
import { ReactNode } from 'react';
+import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
export type QueryBuilderConfig =
@@ -13,4 +14,5 @@ export type QueryBuilderProps = {
config?: QueryBuilderConfig;
panelType: ITEMS;
actions?: ReactNode;
+ inactiveFilters?: Partial>;
};
diff --git a/frontend/src/container/QueryBuilder/QueryBuilder.tsx b/frontend/src/container/QueryBuilder/QueryBuilder.tsx
index 464c4599c107..4aefedb0227a 100644
--- a/frontend/src/container/QueryBuilder/QueryBuilder.tsx
+++ b/frontend/src/container/QueryBuilder/QueryBuilder.tsx
@@ -17,6 +17,7 @@ export const QueryBuilder = memo(function QueryBuilder({
config,
panelType: newPanelType,
actions,
+ inactiveFilters = {},
}: QueryBuilderProps): JSX.Element {
const {
currentQuery,
@@ -74,6 +75,7 @@ export const QueryBuilder = memo(function QueryBuilder({
isAvailableToDisable={isAvailableToDisableQuery}
queryVariant={config?.queryVariant || 'dropdown'}
query={query}
+ inactiveFilters={inactiveFilters}
/>
))}
diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts b/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts
index 414e61678b46..8cee2d9dad1a 100644
--- a/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts
+++ b/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts
@@ -1,3 +1,4 @@
+import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
export type QueryProps = {
@@ -5,4 +6,4 @@ export type QueryProps = {
isAvailableToDisable: boolean;
query: IBuilderQuery;
queryVariant: 'static' | 'dropdown';
-};
+} & Pick;
diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.tsx b/frontend/src/container/QueryBuilder/components/Query/Query.tsx
index d0c5f126a5db..dd42404c6d47 100644
--- a/frontend/src/container/QueryBuilder/components/Query/Query.tsx
+++ b/frontend/src/container/QueryBuilder/components/Query/Query.tsx
@@ -35,6 +35,7 @@ export const Query = memo(function Query({
isAvailableToDisable,
queryVariant,
query,
+ inactiveFilters,
}: QueryProps): JSX.Element {
const { panelType } = useQueryBuilder();
const {
@@ -47,7 +48,7 @@ export const Query = memo(function Query({
handleChangeQueryData,
handleChangeOperator,
handleDeleteQuery,
- } = useQueryOperations({ index, query });
+ } = useQueryOperations({ index, query, inactiveFilters });
const handleChangeAggregateEvery = useCallback(
(value: IBuilderQuery['stepInterval']) => {
@@ -109,6 +110,24 @@ export const Query = memo(function Query({
[handleChangeQueryData],
);
+ const renderAggregateEveryFilter = useCallback(
+ (): JSX.Element | null =>
+ !inactiveFilters?.stepInterval ? (
+
+
+
+
+
+
+
+
+ ) : null,
+ [inactiveFilters?.stepInterval, query, handleChangeAggregateEvery],
+ );
+
const renderAdditionalFilters = useCallback((): ReactNode => {
switch (panelType) {
case PANEL_TYPES.TIME_SERIES: {
@@ -149,19 +168,7 @@ export const Query = memo(function Query({
)}
-
-
-
-
-
-
-
-
-
-
+ {renderAggregateEveryFilter()}
>
);
}
@@ -179,19 +186,7 @@ export const Query = memo(function Query({
-
-
-
-
-
-
-
-
-
-
+ {renderAggregateEveryFilter()}
>
);
}
@@ -230,21 +225,7 @@ export const Query = memo(function Query({
- {panelType !== PANEL_TYPES.LIST && (
-
-
-
-
-
-
-
-
-
-
- )}
+ {renderAggregateEveryFilter()}
>
);
}
@@ -253,10 +234,10 @@ export const Query = memo(function Query({
panelType,
query,
isMetricsDataSource,
- handleChangeAggregateEvery,
handleChangeHavingFilter,
handleChangeLimit,
handleChangeOrderByKeys,
+ renderAggregateEveryFilter,
]);
return (
diff --git a/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx b/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx
index f85c72dc349d..3994c6c73b65 100644
--- a/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx
+++ b/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx
@@ -27,7 +27,7 @@ export function OrderByFilter({
}: OrderByFilterProps): JSX.Element {
const [searchText, setSearchText] = useState('');
const [selectedValue, setSelectedValue] = useState(
- transformToOrderByStringValues(query.orderBy) || [],
+ transformToOrderByStringValues(query.orderBy),
);
const { data, isFetching } = useQuery(
diff --git a/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts b/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts
index 540674dec240..1d415ec2d512 100644
--- a/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts
+++ b/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts
@@ -8,11 +8,25 @@ export const orderByValueDelimiter = '|';
export const transformToOrderByStringValues = (
orderBy: OrderByPayload[],
-): IOption[] =>
- orderBy.map((item) => ({
- label: `${item.columnName} ${item.order}`,
- value: `${item.columnName}${orderByValueDelimiter}${item.order}`,
- }));
+): IOption[] => {
+ const prepareSelectedValue: IOption[] = orderBy.reduce(
+ (acc, item) => {
+ if (item.columnName === '#SIGNOZ_VALUE') return acc;
+
+ const option: IOption = {
+ label: `${item.columnName} ${item.order}`,
+ value: `${item.columnName}${orderByValueDelimiter}${item.order}`,
+ };
+
+ acc.push(option);
+
+ return acc;
+ },
+ [],
+ );
+
+ return prepareSelectedValue;
+};
export function mapLabelValuePairs(
arr: BaseAutocompleteData[],
diff --git a/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts b/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts
index 894167815b84..d9060539cc6b 100644
--- a/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts
+++ b/frontend/src/hooks/queryBuilder/useGetCompositeQueryParam.ts
@@ -1,4 +1,4 @@
-import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import useUrlQuery from 'hooks/useUrlQuery';
import { useMemo } from 'react';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
@@ -7,7 +7,7 @@ export const useGetCompositeQueryParam = (): Query | null => {
const urlQuery = useUrlQuery();
return useMemo(() => {
- const compositeQuery = urlQuery.get(COMPOSITE_QUERY);
+ const compositeQuery = urlQuery.get(queryParamNamesMap.compositeQuery);
let parsedCompositeQuery: Query | null = null;
try {
diff --git a/frontend/src/hooks/queryBuilder/useGetExplorerQueryRange.ts b/frontend/src/hooks/queryBuilder/useGetExplorerQueryRange.ts
new file mode 100644
index 000000000000..15ea31855622
--- /dev/null
+++ b/frontend/src/hooks/queryBuilder/useGetExplorerQueryRange.ts
@@ -0,0 +1,58 @@
+import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
+import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
+import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
+import { useMemo } from 'react';
+import { UseQueryOptions, UseQueryResult } from 'react-query';
+import { useSelector } from 'react-redux';
+import { AppState } from 'store/reducers';
+import { SuccessResponse } from 'types/api';
+import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
+import { Query } from 'types/api/queryBuilder/queryBuilderData';
+import { GlobalReducer } from 'types/reducer/globalTime';
+
+import { useGetQueryRange } from './useGetQueryRange';
+import { useQueryBuilder } from './useQueryBuilder';
+
+export const useGetExplorerQueryRange = (
+ requestData: Query | null,
+ panelType: GRAPH_TYPES | null,
+ options?: UseQueryOptions, Error>,
+): UseQueryResult, Error> => {
+ const { isEnabledQuery } = useQueryBuilder();
+ const { selectedTime: globalSelectedInterval } = useSelector<
+ AppState,
+ GlobalReducer
+ >((state) => state.globalTime);
+
+ const key = useMemo(
+ () =>
+ typeof options?.queryKey === 'string'
+ ? options?.queryKey
+ : REACT_QUERY_KEY.GET_QUERY_RANGE,
+ [options?.queryKey],
+ );
+
+ const isEnabled = useMemo(() => {
+ if (!options) return isEnabledQuery;
+ if (typeof options.enabled === 'boolean') {
+ return isEnabledQuery && options.enabled;
+ }
+
+ return isEnabledQuery;
+ }, [options, isEnabledQuery]);
+
+ return useGetQueryRange(
+ {
+ graphType: panelType || PANEL_TYPES.LIST,
+ selectedTime: 'GLOBAL_TIME',
+ globalSelectedInterval,
+ query: requestData || initialQueriesMap.metrics,
+ },
+ {
+ ...options,
+ retry: false,
+ queryKey: [key, globalSelectedInterval, requestData],
+ enabled: isEnabled,
+ },
+ );
+};
diff --git a/frontend/src/hooks/queryBuilder/useGetPanelTypesQueryParam.ts b/frontend/src/hooks/queryBuilder/useGetPanelTypesQueryParam.ts
index 06cc11829a1b..560790e574e4 100644
--- a/frontend/src/hooks/queryBuilder/useGetPanelTypesQueryParam.ts
+++ b/frontend/src/hooks/queryBuilder/useGetPanelTypesQueryParam.ts
@@ -1,4 +1,4 @@
-import { PANEL_TYPES_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import useUrlQuery from 'hooks/useUrlQuery';
import { useMemo } from 'react';
@@ -9,7 +9,7 @@ export const useGetPanelTypesQueryParam = (
const urlQuery = useUrlQuery();
return useMemo(() => {
- const panelTypeQuery = urlQuery.get(PANEL_TYPES_QUERY);
+ const panelTypeQuery = urlQuery.get(queryParamNamesMap.panelTypes);
return panelTypeQuery ? JSON.parse(panelTypeQuery) : defaultPanelType;
}, [urlQuery, defaultPanelType]);
diff --git a/frontend/src/hooks/queryBuilder/useQueryOperations.ts b/frontend/src/hooks/queryBuilder/useQueryOperations.ts
index 02af0118d02c..d2cc94781972 100644
--- a/frontend/src/hooks/queryBuilder/useQueryOperations.ts
+++ b/frontend/src/hooks/queryBuilder/useQueryOperations.ts
@@ -17,7 +17,11 @@ import {
import { DataSource } from 'types/common/queryBuilder';
import { SelectOption } from 'types/common/select';
-export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
+export const useQueryOperations: UseQueryOperations = ({
+ query,
+ index,
+ inactiveFilters,
+}) => {
const {
handleSetQueryData,
removeQueryBuilderEntityByIndex,
@@ -58,15 +62,23 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
const getNewListOfAdditionalFilters = useCallback(
(dataSource: DataSource): string[] => {
- const listOfFilters = mapOfFilters[dataSource].map((item) => item.text);
+ const result: string[] = mapOfFilters[dataSource].reduce(
+ (acc, item) => {
+ if (inactiveFilters && inactiveFilters[item.field]) {
+ return acc;
+ }
- if (panelType === PANEL_TYPES.LIST) {
- return listOfFilters.filter((filter) => filter !== 'Aggregation interval');
- }
+ acc.push(item.text);
- return listOfFilters;
+ return acc;
+ },
+ [],
+ );
+
+ return result;
},
- [panelType],
+
+ [inactiveFilters],
);
const handleChangeAggregatorAttribute = useCallback(
diff --git a/frontend/src/lib/explorer/getExplorerChartData.ts b/frontend/src/lib/explorer/getExplorerChartData.ts
deleted file mode 100644
index 152b72f9ac6d..000000000000
--- a/frontend/src/lib/explorer/getExplorerChartData.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { ChartData } from 'chart.js';
-import getLabelName from 'lib/getLabelName';
-import { QueryData } from 'types/api/widgets/getQuery';
-
-import { colors } from '../getRandomColor';
-
-export const getExplorerChartData = (
- queryData: QueryData[],
-): ChartData<'bar'> => {
- const uniqueTimeLabels = new Set();
-
- const sortedData = [...queryData].sort((a, b) => {
- if (a.queryName < b.queryName) return -1;
- if (a.queryName > b.queryName) return 1;
- return 0;
- });
-
- const modifiedData: { label: string }[] = sortedData.map((result) => {
- const { metric, queryName, legend } = result;
- result.values.forEach((value) => {
- uniqueTimeLabels.add(value[0] * 1000);
- });
-
- return {
- label: getLabelName(metric, queryName || '', legend || ''),
- };
- });
-
- const labels = Array.from(uniqueTimeLabels)
- .sort((a, b) => a - b)
- .map((value) => new Date(value));
-
- const allLabels = modifiedData.map((e) => e.label);
-
- const data: ChartData<'bar'> = {
- labels,
- datasets: queryData.map((result, index) => ({
- label: allLabels[index],
- data: result.values.map((item) => parseFloat(item[1])),
- backgroundColor: colors[index % colors.length] || 'red',
- borderColor: colors[index % colors.length] || 'red',
- })),
- };
-
- return data;
-};
diff --git a/frontend/src/lib/getChartData.ts b/frontend/src/lib/getChartData.ts
index 8d32d969c229..7c57d2d721ba 100644
--- a/frontend/src/lib/getChartData.ts
+++ b/frontend/src/lib/getChartData.ts
@@ -1,11 +1,14 @@
-import { ChartData } from 'chart.js';
+import { ChartData, ChartDataset } from 'chart.js';
import getLabelName from 'lib/getLabelName';
import { QueryData } from 'types/api/widgets/getQuery';
import convertIntoEpoc from './covertIntoEpoc';
import { colors } from './getRandomColor';
-const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
+const getChartData = ({
+ queryData,
+ createDataset,
+}: GetChartDataProps): ChartData => {
const uniqueTimeLabels = new Set();
queryData.forEach((data) => {
data.queryData.forEach((query) => {
@@ -60,28 +63,39 @@ const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
.reduce((a, b) => [...a, ...b], []);
return {
- datasets: alldata.map((e, index) => ({
- data: e,
- label: allLabels[index],
- borderWidth: 1.5,
- spanGaps: true,
- animations: false,
- borderColor: colors[index % colors.length] || 'red',
- showLine: true,
- pointRadius: 0,
- })),
+ datasets: alldata.map((e, index) => {
+ const datasetBaseConfig = {
+ label: allLabels[index],
+ borderColor: colors[index % colors.length] || 'red',
+ data: e,
+ borderWidth: 1.5,
+ spanGaps: true,
+ animations: false,
+ showLine: true,
+ pointRadius: 0,
+ };
+
+ return createDataset
+ ? createDataset(e, index, allLabels)
+ : datasetBaseConfig;
+ }),
labels: response
.map((e) => e.map((e) => e.first))
.reduce((a, b) => [...a, ...b], [])[0],
};
};
-interface GetChartDataProps {
+export interface GetChartDataProps {
queryData: {
query?: string;
legend?: string;
queryData: QueryData[];
}[];
+ createDataset?: (
+ element: (number | null)[],
+ index: number,
+ allLabels: string[],
+ ) => ChartDataset;
}
export default getChartData;
diff --git a/frontend/src/lib/newQueryBuilder/getPaginationQueryData.ts b/frontend/src/lib/newQueryBuilder/getPaginationQueryData.ts
new file mode 100644
index 000000000000..d2a36392c8e5
--- /dev/null
+++ b/frontend/src/lib/newQueryBuilder/getPaginationQueryData.ts
@@ -0,0 +1,76 @@
+import { initialFilters } from 'constants/queryBuilder';
+import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
+import {
+ IBuilderQuery,
+ OrderByPayload,
+ TagFilter,
+} from 'types/api/queryBuilder/queryBuilderData';
+import { v4 as uuid } from 'uuid';
+
+type SetupPaginationQueryDataParams = {
+ currentStagedQueryData: IBuilderQuery | null;
+ listItemId: string | null;
+ orderByTimestamp: OrderByPayload | null;
+ page: number;
+ pageSize: number;
+};
+
+type SetupPaginationQueryData = (
+ params: SetupPaginationQueryDataParams,
+) => Pick;
+
+export const getPaginationQueryData: SetupPaginationQueryData = ({
+ currentStagedQueryData,
+ listItemId,
+ orderByTimestamp,
+ page,
+ pageSize,
+}) => {
+ if (!currentStagedQueryData) {
+ return { limit: null, filters: initialFilters };
+ }
+
+ const filters = currentStagedQueryData.filters || initialFilters;
+ const offset = (page - 1) * pageSize;
+
+ const queryProps =
+ (orderByTimestamp && currentStagedQueryData.orderBy.length > 1) ||
+ !orderByTimestamp
+ ? {
+ offset,
+ }
+ : {};
+
+ const updatedFilters: TagFilter = {
+ ...filters,
+ items: filters.items.filter((item) => item.key?.key !== 'id'),
+ };
+
+ const tagFilters: TagFilter = {
+ ...filters,
+ items:
+ listItemId && orderByTimestamp
+ ? [
+ {
+ id: uuid(),
+ key: {
+ key: 'id',
+ type: null,
+ dataType: 'string',
+ isColumn: true,
+ },
+ op: orderByTimestamp.order === FILTERS.ASC ? '>' : '<',
+ value: listItemId,
+ },
+ ...updatedFilters.items,
+ ]
+ : updatedFilters.items,
+ };
+
+ const chunkOfQueryData: Partial = {
+ filters: orderByTimestamp ? tagFilters : updatedFilters,
+ ...queryProps,
+ };
+
+ return { ...currentStagedQueryData, ...chunkOfQueryData };
+};
diff --git a/frontend/src/lib/query/createTableColumnsFromQuery.ts b/frontend/src/lib/query/createTableColumnsFromQuery.ts
index 653a859ba858..0637917efda0 100644
--- a/frontend/src/lib/query/createTableColumnsFromQuery.ts
+++ b/frontend/src/lib/query/createTableColumnsFromQuery.ts
@@ -39,6 +39,7 @@ type CreateTableDataFromQuery = (
type FillColumnData = (
queryTableData: QueryDataV3[],
dynamicColumns: DynamicColumns,
+ query: Query,
) => { filledDynamicColumns: DynamicColumns; rowsLength: number };
type GetDynamicColumns = (
@@ -177,7 +178,8 @@ const fillEmptyRowCells = (
const fillDataFromSeria = (
seria: SeriesItem,
columns: DynamicColumns,
- currentQueryName: string,
+ queryName: string,
+ operator: string,
): void => {
const labelEntries = Object.entries(seria.labels);
@@ -193,7 +195,13 @@ const fillDataFromSeria = (
return;
}
- if (currentQueryName === column.key) {
+ if (isFormula(queryName) && queryName === column.key) {
+ column.data.push(parseFloat(value.value).toFixed(2));
+ unusedColumnsKeys.delete(column.key);
+ return;
+ }
+
+ if (!isFormula(queryName) && operator === column.key) {
column.data.push(parseFloat(value.value).toFixed(2));
unusedColumnsKeys.delete(column.key);
return;
@@ -230,20 +238,25 @@ const fillDataFromList = (
});
};
-const fillColumnsData: FillColumnData = (queryTableData, cols) => {
+const fillColumnsData: FillColumnData = (queryTableData, cols, query) => {
const fields = cols.filter((item) => item.type === 'field');
const operators = cols.filter((item) => item.type === 'operator');
const resultColumns = [...fields, ...operators];
queryTableData.forEach((currentQuery) => {
- // const currentOperator = getQueryOperator(
- // query.builder.queryData,
- // currentQuery.queryName,
- // );
-
if (currentQuery.series) {
currentQuery.series.forEach((seria) => {
- fillDataFromSeria(seria, resultColumns, currentQuery.queryName);
+ const currentOperator = getQueryOperator(
+ query.builder.queryData,
+ currentQuery.queryName,
+ );
+
+ fillDataFromSeria(
+ seria,
+ resultColumns,
+ currentQuery.queryName,
+ currentOperator,
+ );
});
}
@@ -313,6 +326,7 @@ export const createTableColumnsFromQuery: CreateTableDataFromQuery = ({
const { filledDynamicColumns, rowsLength } = fillColumnsData(
queryTableData,
dynamicColumns,
+ query,
);
const dataSource = generateData(filledDynamicColumns, rowsLength);
diff --git a/frontend/src/pages/LogsExplorer/index.tsx b/frontend/src/pages/LogsExplorer/index.tsx
index 519538365e63..98278db07636 100644
--- a/frontend/src/pages/LogsExplorer/index.tsx
+++ b/frontend/src/pages/LogsExplorer/index.tsx
@@ -1,8 +1,8 @@
import { Button, Col, Row } from 'antd';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
-import LogsExplorerChart from 'container/LogsExplorerChart';
import LogsExplorerViews from 'container/LogsExplorerViews';
import { QueryBuilder } from 'container/QueryBuilder';
+import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
@@ -11,23 +11,33 @@ import { DataSource } from 'types/common/queryBuilder';
// ** Styles
import { ButtonWrapperStyled, WrapperStyled } from './styles';
+import { prepareQueryWithDefaultTimestamp } from './utils';
function LogsExplorer(): JSX.Element {
const { handleRunQuery, updateAllQueriesOperators } = useQueryBuilder();
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
- const defaultValue = useMemo(
- () =>
- updateAllQueriesOperators(
- initialQueriesMap.logs,
- PANEL_TYPES.LIST,
- DataSource.LOGS,
- ),
- [updateAllQueriesOperators],
- );
+ const defaultValue = useMemo(() => {
+ const updatedQuery = updateAllQueriesOperators(
+ initialQueriesMap.logs,
+ PANEL_TYPES.LIST,
+ DataSource.LOGS,
+ );
+ return prepareQueryWithDefaultTimestamp(updatedQuery);
+ }, [updateAllQueriesOperators]);
useShareBuilderUrl(defaultValue);
+ const inactiveLogsFilters: QueryBuilderProps['inactiveFilters'] = useMemo(() => {
+ if (panelTypes === PANEL_TYPES.TABLE) {
+ const result: QueryBuilderProps['inactiveFilters'] = { stepInterval: true };
+
+ return result;
+ }
+
+ return {};
+ }, [panelTypes]);
+
return (
@@ -35,6 +45,7 @@ function LogsExplorer(): JSX.Element {
diff --git a/frontend/src/pages/LogsExplorer/utils.ts b/frontend/src/pages/LogsExplorer/utils.ts
new file mode 100644
index 000000000000..13e8a29a4f7f
--- /dev/null
+++ b/frontend/src/pages/LogsExplorer/utils.ts
@@ -0,0 +1,12 @@
+import { Query } from 'types/api/queryBuilder/queryBuilderData';
+
+export const prepareQueryWithDefaultTimestamp = (query: Query): Query => ({
+ ...query,
+ builder: {
+ ...query.builder,
+ queryData: query.builder.queryData.map((item) => ({
+ ...item,
+ orderBy: [{ columnName: 'timestamp', order: 'desc' }],
+ })),
+ },
+});
diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx
index 179a9a93e0f4..3bcd8f72442f 100644
--- a/frontend/src/pages/TracesExplorer/index.tsx
+++ b/frontend/src/pages/TracesExplorer/index.tsx
@@ -2,10 +2,7 @@ import { Tabs } from 'antd';
import axios from 'axios';
import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
-import {
- COMPOSITE_QUERY,
- PANEL_TYPES_QUERY,
-} from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import ROUTES from 'constants/routes';
import ExportPanel from 'container/ExportPanel';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
@@ -102,11 +99,9 @@ function TracesExplorer(): JSX.Element {
onSuccess: (data) => {
const dashboardEditView = `${generatePath(ROUTES.DASHBOARD, {
dashboardId: data?.payload?.uuid,
- })}/new?${QueryParams.graphType}=graph&${
- QueryParams.widgetId
- }=empty&${COMPOSITE_QUERY}=${encodeURIComponent(
- JSON.stringify(exportDefaultQuery),
- )}`;
+ })}/new?${QueryParams.graphType}=graph&${QueryParams.widgetId}=empty&${
+ queryParamNamesMap.compositeQuery
+ }=${encodeURIComponent(JSON.stringify(exportDefaultQuery))}`;
history.push(dashboardEditView);
},
@@ -132,7 +127,9 @@ function TracesExplorer(): JSX.Element {
DataSource.TRACES,
);
- redirectWithQueryBuilderData(query, { [PANEL_TYPES_QUERY]: newPanelType });
+ redirectWithQueryBuilderData(query, {
+ [queryParamNamesMap.panelTypes]: newPanelType,
+ });
},
[
currentQuery,
diff --git a/frontend/src/providers/QueryBuilder.tsx b/frontend/src/providers/QueryBuilder.tsx
index 7cb43a1a7e87..e5578f18f812 100644
--- a/frontend/src/providers/QueryBuilder.tsx
+++ b/frontend/src/providers/QueryBuilder.tsx
@@ -13,7 +13,7 @@ import {
MAX_QUERIES,
PANEL_TYPES,
} from 'constants/queryBuilder';
-import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
@@ -461,7 +461,7 @@ export function QueryBuilderProvider({
};
urlQuery.set(
- COMPOSITE_QUERY,
+ queryParamNamesMap.compositeQuery,
encodeURIComponent(JSON.stringify(currentGeneratedQuery)),
);
diff --git a/frontend/src/store/actions/dashboard/saveDashboard.ts b/frontend/src/store/actions/dashboard/saveDashboard.ts
index 14e62fedac09..827a11fc6418 100644
--- a/frontend/src/store/actions/dashboard/saveDashboard.ts
+++ b/frontend/src/store/actions/dashboard/saveDashboard.ts
@@ -1,7 +1,7 @@
import { notification } from 'antd';
import updateDashboardApi from 'api/dashboard/update';
import { AxiosError } from 'axios';
-import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
+import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
import ROUTES from 'constants/routes';
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
@@ -88,7 +88,7 @@ export const SaveDashboard = ({
};
const allLayout = getAllLayout();
const params = new URLSearchParams(window.location.search);
- const compositeQuery = params.get(COMPOSITE_QUERY);
+ const compositeQuery = params.get(queryParamNamesMap.compositeQuery);
const { maxTime, minTime } = store.getState().globalTime;
const query = compositeQuery
? updateStepInterval(
diff --git a/frontend/src/types/api/logs/log.ts b/frontend/src/types/api/logs/log.ts
index eb862daa9c9c..a7c00885ffdb 100644
--- a/frontend/src/types/api/logs/log.ts
+++ b/frontend/src/types/api/logs/log.ts
@@ -1,6 +1,6 @@
export interface ILog {
date: string;
- timestamp: number;
+ timestamp: number | string;
id: string;
traceId: string;
spanId: string;
diff --git a/frontend/src/types/api/queryBuilder/queryBuilderData.ts b/frontend/src/types/api/queryBuilder/queryBuilderData.ts
index ee9cfb6aef61..b16aac3b3cca 100644
--- a/frontend/src/types/api/queryBuilder/queryBuilderData.ts
+++ b/frontend/src/types/api/queryBuilder/queryBuilderData.ts
@@ -59,6 +59,8 @@ export type IBuilderQuery = {
orderBy: OrderByPayload[];
reduceTo: ReduceOperators;
legend: string;
+ pageSize?: number;
+ offset?: number;
};
export interface IClickHouseQuery {
diff --git a/frontend/src/types/api/widgets/getQuery.ts b/frontend/src/types/api/widgets/getQuery.ts
index 0b36af154156..f60eebc0bb8d 100644
--- a/frontend/src/types/api/widgets/getQuery.ts
+++ b/frontend/src/types/api/widgets/getQuery.ts
@@ -5,7 +5,10 @@ export interface PayloadProps {
result: QueryData[];
}
-export type ListItem = { timestamp: string; data: Omit };
+export type ListItem = {
+ timestamp: string;
+ data: Omit;
+};
export interface QueryData {
metric: {
diff --git a/frontend/src/types/common/operations.types.ts b/frontend/src/types/common/operations.types.ts
index d5872c8bbeb9..2664d95ae720 100644
--- a/frontend/src/types/common/operations.types.ts
+++ b/frontend/src/types/common/operations.types.ts
@@ -1,11 +1,13 @@
import { QueryProps } from 'container/QueryBuilder/components/Query/Query.interfaces';
+import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { SelectOption } from './select';
-type UseQueryOperationsParams = Pick;
+type UseQueryOperationsParams = Pick &
+ Pick;
export type HandleChangeQueryData = <
Key extends keyof IBuilderQuery,