diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts index 52be70442f..ef0185c399 100644 --- a/frontend/src/constants/queryBuilder.ts +++ b/frontend/src/constants/queryBuilder.ts @@ -232,8 +232,8 @@ export const PANEL_TYPES: Record = { VALUE: 'value', TABLE: 'table', LIST: 'list', - EMPTY_WIDGET: 'EMPTY_WIDGET', TRACE: 'trace', + EMPTY_WIDGET: 'EMPTY_WIDGET', }; export type IQueryBuilderState = 'search'; diff --git a/frontend/src/container/Controls/index.tsx b/frontend/src/container/Controls/index.tsx index e552bc9f28..8971214a05 100644 --- a/frontend/src/container/Controls/index.tsx +++ b/frontend/src/container/Controls/index.tsx @@ -1,13 +1,14 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons'; import { Button, Select } from 'antd'; -import { Pagination } from 'hooks/queryPagination'; +import { DEFAULT_PER_PAGE_OPTIONS, Pagination } from 'hooks/queryPagination'; import { memo, useMemo } from 'react'; -import { defaultSelectStyle, ITEMS_PER_PAGE_OPTIONS } from './config'; +import { defaultSelectStyle } from './config'; import { Container } from './styles'; function Controls({ offset = 0, + perPageOptions = DEFAULT_PER_PAGE_OPTIONS, isLoading, totalCount, countPerPage, @@ -51,7 +52,7 @@ function Controls({ value={countPerPage} onChange={handleCountItemsPerPageChange} > - {ITEMS_PER_PAGE_OPTIONS.map((count) => ( + {perPageOptions.map((count) => ( - - - - {renderAdditionalFilters()} - - - + {!isTracePanelType && ( + + + + {renderAdditionalFilters()} + + + + )} ReactNode; + modifyColumns?: (columns: ColumnsType) => ColumnsType; }; diff --git a/frontend/src/container/QueryTable/QueryTable.tsx b/frontend/src/container/QueryTable/QueryTable.tsx index 8265a8494d..d08abbd592 100644 --- a/frontend/src/container/QueryTable/QueryTable.tsx +++ b/frontend/src/container/QueryTable/QueryTable.tsx @@ -13,6 +13,7 @@ export function QueryTable({ queryTableData, query, renderActionCell, + modifyColumns, ...props }: QueryTableProps): JSX.Element { const { columns, dataSource } = useMemo( @@ -39,9 +40,13 @@ export function QueryTable({ return currentColumns; }, [columns]); + const tableColumns = modifyColumns + ? modifyColumns(modifiedColumns) + : modifiedColumns; + return ( {}; - const handleNavigatePrevious = (): void => {}; - const handleNavigateNext = (): void => {}; +function TraceExplorerControls({ + isLoading, + totalCount, + perPageOptions, + config, +}: TraceExplorerControlsProps): JSX.Element | null { + const { + pagination, + handleCountItemsPerPageChange, + handleNavigateNext, + handleNavigatePrevious, + } = useQueryPagination(totalCount, perPageOptions); return ( + {config && } + ); } +TraceExplorerControls.defaultProps = { + config: null, +}; + +type TraceExplorerControlsProps = Pick< + ControlsProps, + 'isLoading' | 'totalCount' | 'perPageOptions' +> & { + config?: OptionsMenuConfig | null; +}; + export default memo(TraceExplorerControls); diff --git a/frontend/src/container/TracesExplorer/QuerySection/index.tsx b/frontend/src/container/TracesExplorer/QuerySection/index.tsx index f4dafe16ab..dbb922c139 100644 --- a/frontend/src/container/TracesExplorer/QuerySection/index.tsx +++ b/frontend/src/container/TracesExplorer/QuerySection/index.tsx @@ -3,6 +3,7 @@ import { PANEL_TYPES } from 'constants/queryBuilder'; import { QueryBuilder } from 'container/QueryBuilder'; import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; +import { memo } from 'react'; import { DataSource } from 'types/common/queryBuilder'; import { ButtonWrapper, Container } from './styles'; @@ -10,7 +11,7 @@ import { ButtonWrapper, Container } from './styles'; function QuerySection(): JSX.Element { const { handleRunQuery } = useQueryBuilder(); - const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.TIME_SERIES); + const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST); return ( @@ -32,4 +33,4 @@ function QuerySection(): JSX.Element { ); } -export default QuerySection; +export default memo(QuerySection); diff --git a/frontend/src/container/TracesExplorer/TracesView/configs.tsx b/frontend/src/container/TracesExplorer/TracesView/configs.tsx new file mode 100644 index 0000000000..3bbf528eee --- /dev/null +++ b/frontend/src/container/TracesExplorer/TracesView/configs.tsx @@ -0,0 +1,49 @@ +import { Typography } from 'antd'; +import { ColumnsType } from 'antd/es/table'; +import ROUTES from 'constants/routes'; +import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util'; +import { DEFAULT_PER_PAGE_OPTIONS } from 'hooks/queryPagination'; +import { generatePath } from 'react-router-dom'; +import { ListItem } from 'types/api/widgets/getQuery'; + +export const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS]; + +export const columns: ColumnsType = [ + { + title: 'Root Service Name', + dataIndex: 'subQuery.serviceName', + key: 'serviceName', + }, + { + title: 'Root Operation Name', + dataIndex: 'subQuery.name', + key: 'name', + }, + { + title: 'Root Duration (in ms)', + dataIndex: 'subQuery.durationNano', + key: 'durationNano', + render: (duration: number): JSX.Element => ( + {getMs(String(duration))}ms + ), + }, + { + title: 'No of Spans', + dataIndex: 'span_count', + key: 'span_count', + }, + { + title: 'TraceID', + dataIndex: 'traceID', + key: 'traceID', + render: (traceID: string): JSX.Element => ( + + {traceID} + + ), + }, +]; diff --git a/frontend/src/container/TracesExplorer/TracesView/index.tsx b/frontend/src/container/TracesExplorer/TracesView/index.tsx new file mode 100644 index 0000000000..e4ef1e5ec5 --- /dev/null +++ b/frontend/src/container/TracesExplorer/TracesView/index.tsx @@ -0,0 +1,87 @@ +import Typography from 'antd/es/typography/Typography'; +import { ResizeTable } from 'components/ResizeTable'; +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 { Pagination, URL_PAGINATION } from 'hooks/queryPagination'; +import useUrlQueryData from 'hooks/useUrlQueryData'; +import { useMemo } from 'react'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { GlobalReducer } from 'types/reducer/globalTime'; + +import TraceExplorerControls from '../Controls'; +import { columns, PER_PAGE_OPTIONS } from './configs'; +import { ActionsContainer, Container } from './styles'; + +function TracesView(): JSX.Element { + const { stagedQuery, panelType } = useQueryBuilder(); + + const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector< + AppState, + GlobalReducer + >((state) => state.globalTime); + + const { queryData: paginationQueryData } = useUrlQueryData( + URL_PAGINATION, + ); + + const { data, isLoading } = useGetQueryRange( + { + query: stagedQuery || initialQueriesMap.traces, + graphType: panelType || PANEL_TYPES.TRACE, + selectedTime: 'GLOBAL_TIME', + globalSelectedInterval: globalSelectedTime, + params: { + dataSource: 'traces', + }, + tableParams: { + pagination: paginationQueryData, + }, + }, + { + queryKey: [ + REACT_QUERY_KEY.GET_QUERY_RANGE, + globalSelectedTime, + maxTime, + minTime, + stagedQuery, + panelType, + paginationQueryData, + ], + enabled: !!stagedQuery && panelType === PANEL_TYPES.TRACE, + }, + ); + + const responseData = data?.payload?.data?.newResult?.data?.result[0]?.list; + const tableData = useMemo( + () => responseData?.map((listItem) => listItem.data), + [responseData], + ); + + return ( + + + + Showing up to X of the slowest traces form the selected time range + + + + + + ); +} + +export default TracesView; diff --git a/frontend/src/container/TracesExplorer/TracesView/styles.ts b/frontend/src/container/TracesExplorer/TracesView/styles.ts new file mode 100644 index 0000000000..f9c9a7c8ea --- /dev/null +++ b/frontend/src/container/TracesExplorer/TracesView/styles.ts @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + gap: 15px; +`; + +export const ActionsContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; diff --git a/frontend/src/hooks/queryBuilder/useQueryOperations.ts b/frontend/src/hooks/queryBuilder/useQueryOperations.ts index 00cbdebcff..540614d838 100644 --- a/frontend/src/hooks/queryBuilder/useQueryOperations.ts +++ b/frontend/src/hooks/queryBuilder/useQueryOperations.ts @@ -122,6 +122,10 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => { [query.dataSource], ); + const isTracePanelType = useMemo(() => panelType === PANEL_TYPES.TRACE, [ + panelType, + ]); + useEffect(() => { if (initialDataSource && dataSource !== initialDataSource) return; @@ -142,6 +146,7 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => { }, [dataSource, aggregateOperator, getNewListOfAdditionalFilters]); return { + isTracePanelType, isMetricsDataSource, operators, listOfAdditionalFilters, diff --git a/frontend/src/hooks/queryPagination/config.ts b/frontend/src/hooks/queryPagination/config.ts index c67493a070..72dc032051 100644 --- a/frontend/src/hooks/queryPagination/config.ts +++ b/frontend/src/hooks/queryPagination/config.ts @@ -1,8 +1,3 @@ -import { Pagination } from './types'; - export const URL_PAGINATION = 'pagination'; -export const defaultPaginationConfig: Pagination = { - offset: 0, - limit: 25, -}; +export const DEFAULT_PER_PAGE_OPTIONS: number[] = [25, 50, 100, 200]; diff --git a/frontend/src/hooks/queryPagination/types.ts b/frontend/src/hooks/queryPagination/types.ts index f8a40b14a2..8bea38eef9 100644 --- a/frontend/src/hooks/queryPagination/types.ts +++ b/frontend/src/hooks/queryPagination/types.ts @@ -1,4 +1,4 @@ export interface Pagination { offset: number; - limit: 25 | 50 | 100 | 200; + limit: number; } diff --git a/frontend/src/hooks/queryPagination/useQueryPagination.ts b/frontend/src/hooks/queryPagination/useQueryPagination.ts index 8864e35d10..29cee3ecb8 100644 --- a/frontend/src/hooks/queryPagination/useQueryPagination.ts +++ b/frontend/src/hooks/queryPagination/useQueryPagination.ts @@ -1,12 +1,23 @@ import { ControlsProps } from 'container/Controls'; import useUrlQueryData from 'hooks/useUrlQueryData'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; -import { defaultPaginationConfig, URL_PAGINATION } from './config'; +import { DEFAULT_PER_PAGE_OPTIONS, URL_PAGINATION } from './config'; import { Pagination } from './types'; -import { checkIsValidPaginationData } from './utils'; +import { + checkIsValidPaginationData, + getDefaultPaginationConfig, +} from './utils'; + +const useQueryPagination = ( + totalCount: number, + perPageOptions: number[] = DEFAULT_PER_PAGE_OPTIONS, +): UseQueryPagination => { + const defaultPaginationConfig = useMemo( + () => getDefaultPaginationConfig(perPageOptions), + [perPageOptions], + ); -const useQueryPagination = (totalCount: number): UseQueryPagination => { const { query: paginationQuery, queryData: paginationQueryData, @@ -45,12 +56,19 @@ const useQueryPagination = (totalCount: number): UseQueryPagination => { useEffect(() => { const isValidPaginationData = checkIsValidPaginationData( paginationQueryData || defaultPaginationConfig, + perPageOptions, ); if (paginationQuery && isValidPaginationData) return; redirectWithCurrentPagination(defaultPaginationConfig); - }, [paginationQuery, paginationQueryData, redirectWithCurrentPagination]); + }, [ + defaultPaginationConfig, + perPageOptions, + paginationQuery, + paginationQueryData, + redirectWithCurrentPagination, + ]); return { pagination: paginationQueryData || defaultPaginationConfig, diff --git a/frontend/src/hooks/queryPagination/utils.ts b/frontend/src/hooks/queryPagination/utils.ts index 694559aa1a..217a19afe1 100644 --- a/frontend/src/hooks/queryPagination/utils.ts +++ b/frontend/src/hooks/queryPagination/utils.ts @@ -1,12 +1,20 @@ +import { DEFAULT_PER_PAGE_OPTIONS } from './config'; import { Pagination } from './types'; -export const checkIsValidPaginationData = ({ - limit, - offset, -}: Pagination): boolean => +export const checkIsValidPaginationData = ( + { limit, offset }: Pagination, + perPageOptions: number[], +): boolean => Boolean( - limit && - (limit === 25 || limit === 50 || limit === 100 || limit === 200) && - offset && - offset > 0, + Number.isInteger(limit) && + limit > 0 && + offset >= 0 && + perPageOptions.find((option) => option === limit), ); + +export const getDefaultPaginationConfig = ( + perPageOptions = DEFAULT_PER_PAGE_OPTIONS, +): Pagination => ({ + offset: 0, + limit: perPageOptions[0], +}); diff --git a/frontend/src/hooks/useUrlQueryData.ts b/frontend/src/hooks/useUrlQueryData.ts index a4304c8620..1a0e556afd 100644 --- a/frontend/src/hooks/useUrlQueryData.ts +++ b/frontend/src/hooks/useUrlQueryData.ts @@ -10,7 +10,7 @@ const useUrlQueryData = ( const location = useLocation(); const urlQuery = useUrlQuery(); - const query = urlQuery.get(queryKey); + const query = useMemo(() => urlQuery.get(queryKey), [queryKey, urlQuery]); const queryData: T = useMemo(() => (query ? JSON.parse(query) : defaultData), [ query, diff --git a/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts b/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts index 21b3bc7498..dd6869847b 100644 --- a/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts +++ b/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts @@ -15,12 +15,16 @@ export const getOperatorsBySourceAndPanelType = ({ }: GetQueryOperatorsParams): SelectOption[] => { let operatorsByDataSource = mapOfOperators[dataSource]; - if (panelType === PANEL_TYPES.LIST) { + if (panelType === PANEL_TYPES.LIST || panelType === PANEL_TYPES.TRACE) { operatorsByDataSource = operatorsByDataSource.filter( (operator) => operator.value === StringOperators.NOOP, ); } - if (dataSource !== DataSource.METRICS && panelType !== PANEL_TYPES.LIST) { + if ( + dataSource !== DataSource.METRICS && + panelType !== PANEL_TYPES.LIST && + panelType !== PANEL_TYPES.TRACE + ) { operatorsByDataSource = operatorsByDataSource.filter( (operator) => operator.value !== StringOperators.NOOP, ); diff --git a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts index c0016fa0de..f89c8b025e 100644 --- a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts +++ b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts @@ -1,3 +1,4 @@ +import { GetQueryResultsProps } from 'store/actions/dashboard/getQueryResults'; import { MapData, MapQueryDataToApiResult, @@ -6,6 +7,7 @@ import { export const mapQueryDataToApi = ( data: Data[], nameField: Key, + tableParams?: GetQueryResultsProps['tableParams'], ): MapQueryDataToApiResult> => { const newLegendMap: Record = {}; @@ -14,6 +16,10 @@ export const mapQueryDataToApi = ( ...acc, [query[nameField] as string]: { ...query, + ...tableParams?.pagination, + ...(tableParams?.selectColumns + ? { selectColumns: tableParams?.selectColumns } + : null), }, }; diff --git a/frontend/src/lib/query/createTableColumnsFromQuery.ts b/frontend/src/lib/query/createTableColumnsFromQuery.ts index bbb03e284d..7aaa10480d 100644 --- a/frontend/src/lib/query/createTableColumnsFromQuery.ts +++ b/frontend/src/lib/query/createTableColumnsFromQuery.ts @@ -5,7 +5,7 @@ import { QueryTableProps } from 'container/QueryTable/QueryTable.intefaces'; import { toCapitalize } from 'lib/toCapitalize'; import { ReactNode } from 'react'; import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData'; -import { QueryDataV3, SeriesItem } from 'types/api/widgets/getQuery'; +import { ListItem, QueryDataV3, SeriesItem } from 'types/api/widgets/getQuery'; import { v4 as uuid } from 'uuid'; type CreateTableDataFromQueryParams = Pick< @@ -47,6 +47,10 @@ type GetDynamicColumns = ( query: Query, ) => DynamicColumns; +type ListItemData = ListItem['data']; +type ListItemKey = keyof ListItemData; +type SeriesItemLabels = SeriesItem['labels']; + const isFormula = (queryName: string): boolean => FORMULA_REGEXP.test(queryName); @@ -72,57 +76,77 @@ const prepareColumnTitle = (title: string): string => { return toCapitalize(title); }; +const createLabels = ( + labels: T, + label: keyof T, + dynamicColumns: DynamicColumns, +): void => { + if (isColumnExist(label as string, dynamicColumns)) return; + if (isFormula(label as string)) return; + + const labelValue = labels[label]; + + const isNumber = !Number.isNaN(parseFloat(String(labelValue))); + + const fieldObj: DynamicColumn = { + key: label as string, + data: [], + type: 'field', + sortable: isNumber, + }; + + dynamicColumns.push(fieldObj); +}; + const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => { const dynamicColumns: DynamicColumns = []; queryTableData.forEach((currentQuery) => { - if (!currentQuery.series) return; - - if (!isColumnExist('timestamp', dynamicColumns)) { - dynamicColumns.push({ - key: 'timestamp', - data: [], - type: 'field', - sortable: true, + if (currentQuery.list) { + currentQuery.list.forEach((listItem) => { + Object.keys(listItem.data).forEach((label) => { + createLabels( + listItem.data, + label as ListItemKey, + dynamicColumns, + ); + }); }); } - currentQuery.series.forEach((seria) => { - Object.keys(seria.labels).forEach((label) => { - if (isColumnExist(label, dynamicColumns)) return; - if (isFormula(label)) return; - - const labelValue = seria.labels[label]; - - const isNumber = !Number.isNaN(parseFloat(labelValue)); - - const fieldObj: DynamicColumn = { - key: label, + if (currentQuery.series) { + if (!isColumnExist('timestamp', dynamicColumns)) { + dynamicColumns.push({ + key: 'timestamp', data: [], type: 'field', - sortable: isNumber, - }; + sortable: true, + }); + } - dynamicColumns.push(fieldObj); + currentQuery.series.forEach((seria) => { + Object.keys(seria.labels).forEach((label) => { + createLabels(seria.labels, label, dynamicColumns); + }); }); - }); - if (!isFormula(currentQuery.queryName)) { - const builderQuery = query.builder.queryData.find( - (q) => q.queryName === currentQuery.queryName, - ); + if (!isFormula(currentQuery.queryName)) { + const builderQuery = query.builder.queryData.find( + (q) => q.queryName === currentQuery.queryName, + ); - const operator = builderQuery ? builderQuery.aggregateOperator : ''; + const operator = builderQuery ? builderQuery.aggregateOperator : ''; - if (isColumnExist(operator, dynamicColumns)) return; + if (isColumnExist(operator, dynamicColumns)) return; - const operatorColumn: DynamicColumn = { - key: operator, - data: [], - type: 'operator', - sortable: true, - }; - dynamicColumns.push(operatorColumn); + const operatorColumn: DynamicColumn = { + key: operator, + data: [], + type: 'operator', + sortable: true, + }; + dynamicColumns.push(operatorColumn); + } } }); @@ -194,22 +218,47 @@ const fillDataFromSeria = ( }); }; +const fillDataFromList = ( + listItem: ListItem, + columns: DynamicColumns, +): void => { + columns.forEach((column) => { + if (isFormula(column.key as string)) return; + + Object.keys(listItem.data).forEach((label) => { + if (column.key === label) { + if (listItem.data[label as ListItemKey]) { + column.data.push(listItem.data[label as ListItemKey] as string | number); + } else { + column.data.push('N/A'); + } + } + }); + }); +}; + 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) => { - if (!currentQuery.series) return; - const currentOperator = getQueryOperator( query.builder.queryData, currentQuery.queryName, ); - currentQuery.series.forEach((seria) => { - fillDataFromSeria(seria, resultColumns, currentOperator); - }); + if (currentQuery.series) { + currentQuery.series.forEach((seria) => { + fillDataFromSeria(seria, resultColumns, currentOperator); + }); + } + + if (currentQuery.list) { + currentQuery.list.forEach((listItem) => { + fillDataFromList(listItem, resultColumns); + }); + } }); const rowsLength = resultColumns.length > 0 ? resultColumns[0].data.length : 0; diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index 22f7a4f153..d6073fc466 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -16,7 +16,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { generatePath } from 'react-router-dom'; import { Dashboard } from 'types/api/dashboard/getAll'; import { DataSource } from 'types/common/queryBuilder'; @@ -34,19 +34,38 @@ function TracesExplorer(): JSX.Element { redirectWithQueryBuilderData, } = useQueryBuilder(); - const tabsItems = getTabsItems(); - const currentTab = panelType || PANEL_TYPES.TIME_SERIES; + const currentTab = panelType || PANEL_TYPES.LIST; + + const isMultipleQueries = useMemo( + () => + currentQuery.builder.queryData.length > 1 || + currentQuery.builder.queryFormulas.length > 0, + [currentQuery], + ); const defaultQuery = useMemo( () => updateAllQueriesOperators( initialQueriesMap.traces, - PANEL_TYPES.TIME_SERIES, + PANEL_TYPES.LIST, DataSource.TRACES, ), [updateAllQueriesOperators], ); + const isGroupByExist = useMemo(() => { + const groupByCount: number = currentQuery.builder.queryData.reduce( + (acc, query) => acc + query.groupBy.length, + 0, + ); + + return groupByCount > 0; + }, [currentQuery]); + + const tabsItems = getTabsItems({ + isListViewDisabled: isMultipleQueries || isGroupByExist, + }); + const exportDefaultQuery = useMemo( () => updateAllQueriesOperators( @@ -114,6 +133,17 @@ function TracesExplorer(): JSX.Element { useShareBuilderUrl(defaultQuery); + useEffect(() => { + const shouldChangeView = isMultipleQueries || isGroupByExist; + + if ( + (currentTab === PANEL_TYPES.LIST || currentTab === PANEL_TYPES.TRACE) && + shouldChangeView + ) { + handleTabChange(PANEL_TYPES.TIME_SERIES); + } + }, [currentTab, isMultipleQueries, isGroupByExist, handleTabChange]); + return ( <> diff --git a/frontend/src/pages/TracesExplorer/utils.tsx b/frontend/src/pages/TracesExplorer/utils.tsx index 7c38207281..116a125c83 100644 --- a/frontend/src/pages/TracesExplorer/utils.tsx +++ b/frontend/src/pages/TracesExplorer/utils.tsx @@ -1,17 +1,25 @@ import { TabsProps } from 'antd'; import { PANEL_TYPES } from 'constants/queryBuilder'; import TimeSeriesView from 'container/TimeSeriesView'; +import TracesView from 'container/TracesExplorer/TracesView'; import { DataSource } from 'types/common/queryBuilder'; -export const getTabsItems = (): TabsProps['items'] => [ +interface GetTabsItemsProps { + isListViewDisabled: boolean; +} + +export const getTabsItems = ({ + isListViewDisabled, +}: GetTabsItemsProps): TabsProps['items'] => [ + { + label: 'Traces', + key: PANEL_TYPES.TRACE, + children: , + disabled: isListViewDisabled, + }, { label: 'Time Series', key: PANEL_TYPES.TIME_SERIES, children: , }, - { - label: 'Traces', - key: PANEL_TYPES.TRACE, - children:
Traces tab
, - }, ]; diff --git a/frontend/src/store/actions/dashboard/getQueryResults.ts b/frontend/src/store/actions/dashboard/getQueryResults.ts index 137730770b..1487b3f7c8 100644 --- a/frontend/src/store/actions/dashboard/getQueryResults.ts +++ b/frontend/src/store/actions/dashboard/getQueryResults.ts @@ -16,12 +16,14 @@ import { SuccessResponse } from 'types/api'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; +import { Pagination } from 'hooks/queryPagination'; export async function GetMetricQueryRange({ query, globalSelectedInterval, graphType, selectedTime, + tableParams, variables = {}, params = {}, }: GetQueryResultsProps): Promise> { @@ -38,8 +40,9 @@ export async function GetMetricQueryRange({ switch (query.queryType) { case EQueryType.QUERY_BUILDER: { const { queryData: data, queryFormulas } = query.builder; - const currentQueryData = mapQueryDataToApi(data, 'queryName'); + const currentQueryData = mapQueryDataToApi(data, 'queryName', tableParams); const currentFormulas = mapQueryDataToApi(queryFormulas, 'queryName'); + const builderQueries = { ...currentQueryData.data, ...currentFormulas.data, @@ -140,4 +143,8 @@ export interface GetQueryResultsProps { globalSelectedInterval: Time; variables?: Record; params?: Record; + tableParams?: { + pagination?: Pagination; + selectColumns?: any; + }; } diff --git a/frontend/src/types/api/widgets/getQuery.ts b/frontend/src/types/api/widgets/getQuery.ts index f97ffb59e2..0b36af1541 100644 --- a/frontend/src/types/api/widgets/getQuery.ts +++ b/frontend/src/types/api/widgets/getQuery.ts @@ -5,7 +5,7 @@ export interface PayloadProps { result: QueryData[]; } -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 409f66cc5c..d5872c8bbe 100644 --- a/frontend/src/types/common/operations.types.ts +++ b/frontend/src/types/common/operations.types.ts @@ -18,6 +18,7 @@ export type HandleChangeQueryData = < export type UseQueryOperations = ( params: UseQueryOperationsParams, ) => { + isTracePanelType: boolean; isMetricsDataSource: boolean; operators: SelectOption[]; listOfAdditionalFilters: string[]; diff --git a/frontend/src/types/common/queryBuilder.ts b/frontend/src/types/common/queryBuilder.ts index 774ec5a65c..4b6801eccf 100644 --- a/frontend/src/types/common/queryBuilder.ts +++ b/frontend/src/types/common/queryBuilder.ts @@ -143,8 +143,8 @@ export type PanelTypeKeys = | 'VALUE' | 'TABLE' | 'LIST' - | 'EMPTY_WIDGET' - | 'TRACE'; + | 'TRACE' + | 'EMPTY_WIDGET'; export type ReduceOperators = 'last' | 'sum' | 'avg' | 'max' | 'min';