mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 02:39:04 +08:00
feat: [5038] enable list view - add to dashboard (#5268)
* feat: [5038] enable list view - add to dashboard * feat: pass page size for list view export
This commit is contained in:
parent
64e06ab3f9
commit
38b1de5ccc
@ -40,4 +40,5 @@ export const getComponentForPanelType = (
|
|||||||
export const AVAILABLE_EXPORT_PANEL_TYPES = [
|
export const AVAILABLE_EXPORT_PANEL_TYPES = [
|
||||||
PANEL_TYPES.TIME_SERIES,
|
PANEL_TYPES.TIME_SERIES,
|
||||||
PANEL_TYPES.TABLE,
|
PANEL_TYPES.TABLE,
|
||||||
|
PANEL_TYPES.LIST,
|
||||||
];
|
];
|
||||||
|
@ -37,7 +37,7 @@ import { useNotifications } from 'hooks/useNotifications';
|
|||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
||||||
import { defaultTo, isEmpty, omit } from 'lodash-es';
|
import { cloneDeep, defaultTo, isEmpty, omit, set } from 'lodash-es';
|
||||||
import { Sliders } from 'lucide-react';
|
import { Sliders } from 'lucide-react';
|
||||||
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
|
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
|
||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
@ -117,6 +117,12 @@ function LogsExplorerViews({
|
|||||||
return stagedQuery.builder.queryData.find((item) => !item.disabled) || null;
|
return stagedQuery.builder.queryData.find((item) => !item.disabled) || null;
|
||||||
}, [stagedQuery]);
|
}, [stagedQuery]);
|
||||||
|
|
||||||
|
const { options, config } = useOptionsMenu({
|
||||||
|
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
|
||||||
|
dataSource: initialDataSource || DataSource.LOGS,
|
||||||
|
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
|
||||||
|
});
|
||||||
|
|
||||||
const orderByTimestamp: OrderByPayload | null = useMemo(() => {
|
const orderByTimestamp: OrderByPayload | null = useMemo(() => {
|
||||||
const timestampOrderBy = listQuery?.orderBy.find(
|
const timestampOrderBy = listQuery?.orderBy.find(
|
||||||
(item) => item.columnName === 'timestamp',
|
(item) => item.columnName === 'timestamp',
|
||||||
@ -174,10 +180,10 @@ function LogsExplorerViews({
|
|||||||
() =>
|
() =>
|
||||||
updateAllQueriesOperators(
|
updateAllQueriesOperators(
|
||||||
currentQuery || initialQueriesMap.logs,
|
currentQuery || initialQueriesMap.logs,
|
||||||
PANEL_TYPES.TIME_SERIES,
|
selectedPanelType,
|
||||||
DataSource.LOGS,
|
DataSource.LOGS,
|
||||||
),
|
),
|
||||||
[currentQuery, updateAllQueriesOperators],
|
[currentQuery, selectedPanelType, updateAllQueriesOperators],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleModeChange = (panelType: PANEL_TYPES): void => {
|
const handleModeChange = (panelType: PANEL_TYPES): void => {
|
||||||
@ -309,6 +315,14 @@ function LogsExplorerViews({
|
|||||||
isLoading: isUpdateDashboardLoading,
|
isLoading: isUpdateDashboardLoading,
|
||||||
} = useUpdateDashboard();
|
} = useUpdateDashboard();
|
||||||
|
|
||||||
|
const getUpdatedQueryForExport = useCallback((): Query => {
|
||||||
|
const updatedQuery = cloneDeep(currentQuery);
|
||||||
|
|
||||||
|
set(updatedQuery, 'builder.queryData[0].pageSize', 10);
|
||||||
|
|
||||||
|
return updatedQuery;
|
||||||
|
}, [currentQuery]);
|
||||||
|
|
||||||
const handleExport = useCallback(
|
const handleExport = useCallback(
|
||||||
(dashboard: Dashboard | null): void => {
|
(dashboard: Dashboard | null): void => {
|
||||||
if (!dashboard || !panelType) return;
|
if (!dashboard || !panelType) return;
|
||||||
@ -319,11 +333,17 @@ function LogsExplorerViews({
|
|||||||
|
|
||||||
const widgetId = v4();
|
const widgetId = v4();
|
||||||
|
|
||||||
|
const query =
|
||||||
|
panelType === PANEL_TYPES.LIST
|
||||||
|
? getUpdatedQueryForExport()
|
||||||
|
: exportDefaultQuery;
|
||||||
|
|
||||||
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
|
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
|
||||||
dashboard,
|
dashboard,
|
||||||
exportDefaultQuery,
|
query,
|
||||||
widgetId,
|
widgetId,
|
||||||
panelTypeParam,
|
panelTypeParam,
|
||||||
|
options.selectColumns,
|
||||||
);
|
);
|
||||||
|
|
||||||
updateDashboard(updatedDashboard, {
|
updateDashboard(updatedDashboard, {
|
||||||
@ -353,7 +373,7 @@ function LogsExplorerViews({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dashboardEditView = generateExportToDashboardLink({
|
const dashboardEditView = generateExportToDashboardLink({
|
||||||
query: exportDefaultQuery,
|
query,
|
||||||
panelType: panelTypeParam,
|
panelType: panelTypeParam,
|
||||||
dashboardId: data.payload?.uuid || '',
|
dashboardId: data.payload?.uuid || '',
|
||||||
widgetId,
|
widgetId,
|
||||||
@ -365,7 +385,9 @@ function LogsExplorerViews({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
getUpdatedQueryForExport,
|
||||||
exportDefaultQuery,
|
exportDefaultQuery,
|
||||||
|
options.selectColumns,
|
||||||
history,
|
history,
|
||||||
notifications,
|
notifications,
|
||||||
panelType,
|
panelType,
|
||||||
@ -460,12 +482,6 @@ function LogsExplorerViews({
|
|||||||
selectedView,
|
selectedView,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { options, config } = useOptionsMenu({
|
|
||||||
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
|
|
||||||
dataSource: initialDataSource || DataSource.METRICS,
|
|
||||||
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
|
|
||||||
});
|
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
if (!stagedQuery) return [];
|
if (!stagedQuery) return [];
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.resize-table {
|
.resize-table {
|
||||||
height: calc(100% - 40px);
|
height: calc(100% - 70px);
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
@ -189,50 +189,6 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[PANEL_TYPES.HISTOGRAM]: {
|
|
||||||
[DataSource.LOGS]: {
|
|
||||||
builder: {
|
|
||||||
queryData: [
|
|
||||||
'filters',
|
|
||||||
'aggregateOperator',
|
|
||||||
'aggregateAttribute',
|
|
||||||
'groupBy',
|
|
||||||
'limit',
|
|
||||||
'having',
|
|
||||||
'orderBy',
|
|
||||||
'functions',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[DataSource.METRICS]: {
|
|
||||||
builder: {
|
|
||||||
queryData: [
|
|
||||||
'filters',
|
|
||||||
'aggregateOperator',
|
|
||||||
'aggregateAttribute',
|
|
||||||
'groupBy',
|
|
||||||
'limit',
|
|
||||||
'having',
|
|
||||||
'orderBy',
|
|
||||||
'functions',
|
|
||||||
'spaceAggregation',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[DataSource.TRACES]: {
|
|
||||||
builder: {
|
|
||||||
queryData: [
|
|
||||||
'filters',
|
|
||||||
'aggregateOperator',
|
|
||||||
'aggregateAttribute',
|
|
||||||
'groupBy',
|
|
||||||
'limit',
|
|
||||||
'having',
|
|
||||||
'orderBy',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[PANEL_TYPES.TABLE]: {
|
[PANEL_TYPES.TABLE]: {
|
||||||
[DataSource.LOGS]: {
|
[DataSource.LOGS]: {
|
||||||
builder: {
|
builder: {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.resize-table {
|
.resize-table {
|
||||||
height: calc(100% - 40px);
|
height: calc(100% - 70px);
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
@ -1,43 +1,61 @@
|
|||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
|
const baseLogsSelectedColumns = {
|
||||||
|
dataType: 'string',
|
||||||
|
type: '',
|
||||||
|
name: 'timestamp',
|
||||||
|
};
|
||||||
|
|
||||||
export const addEmptyWidgetInDashboardJSONWithQuery = (
|
export const addEmptyWidgetInDashboardJSONWithQuery = (
|
||||||
dashboard: Dashboard,
|
dashboard: Dashboard,
|
||||||
query: Query,
|
query: Query,
|
||||||
widgetId: string,
|
widgetId: string,
|
||||||
panelTypes?: PANEL_TYPES,
|
panelType?: PANEL_TYPES,
|
||||||
): Dashboard => ({
|
selectedColumns?: BaseAutocompleteData[] | null,
|
||||||
...dashboard,
|
): Dashboard => {
|
||||||
data: {
|
const logsSelectedColumns = [
|
||||||
...dashboard.data,
|
baseLogsSelectedColumns,
|
||||||
layout: [
|
...convertKeysToColumnFields(selectedColumns || []),
|
||||||
{
|
];
|
||||||
i: widgetId,
|
|
||||||
w: 6,
|
return {
|
||||||
x: 0,
|
...dashboard,
|
||||||
h: 6,
|
data: {
|
||||||
y: 0,
|
...dashboard.data,
|
||||||
},
|
layout: [
|
||||||
...(dashboard?.data?.layout || []),
|
{
|
||||||
],
|
i: widgetId,
|
||||||
widgets: [
|
w: 6,
|
||||||
...(dashboard?.data?.widgets || []),
|
x: 0,
|
||||||
{
|
h: 6,
|
||||||
id: widgetId,
|
y: 0,
|
||||||
query,
|
},
|
||||||
description: '',
|
...(dashboard?.data?.layout || []),
|
||||||
isStacked: false,
|
],
|
||||||
nullZeroValues: '',
|
widgets: [
|
||||||
opacity: '',
|
...(dashboard?.data?.widgets || []),
|
||||||
title: '',
|
{
|
||||||
timePreferance: 'GLOBAL_TIME',
|
id: widgetId,
|
||||||
panelTypes: panelTypes || PANEL_TYPES.TIME_SERIES,
|
query,
|
||||||
softMax: null,
|
description: '',
|
||||||
softMin: null,
|
isStacked: false,
|
||||||
selectedLogFields: [],
|
nullZeroValues: '',
|
||||||
selectedTracesFields: [],
|
opacity: '',
|
||||||
},
|
title: '',
|
||||||
],
|
timePreferance: 'GLOBAL_TIME',
|
||||||
},
|
panelTypes: panelType || PANEL_TYPES.TIME_SERIES,
|
||||||
});
|
softMax: null,
|
||||||
|
softMin: null,
|
||||||
|
selectedLogFields:
|
||||||
|
panelType === PANEL_TYPES.LIST ? logsSelectedColumns : [],
|
||||||
|
selectedTracesFields:
|
||||||
|
panelType === PANEL_TYPES.LIST ? selectedColumns || [] : [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -5,12 +5,15 @@ import * as Sentry from '@sentry/react';
|
|||||||
import { Button, Card, Tabs, Tooltip } from 'antd';
|
import { Button, Card, Tabs, Tooltip } from 'antd';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
|
import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
|
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
|
||||||
import ExportPanel from 'container/ExportPanel';
|
import ExportPanel from 'container/ExportPanel';
|
||||||
|
import { useOptionsMenu } from 'container/OptionsMenu';
|
||||||
import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions';
|
import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions';
|
||||||
import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2';
|
import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2';
|
||||||
|
import { defaultSelectedColumns } from 'container/TracesExplorer/ListView/configs';
|
||||||
import QuerySection from 'container/TracesExplorer/QuerySection';
|
import QuerySection from 'container/TracesExplorer/QuerySection';
|
||||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
||||||
@ -20,9 +23,11 @@ import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
|||||||
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
|
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
|
import { cloneDeep, set } from 'lodash-es';
|
||||||
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink';
|
import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
@ -42,6 +47,15 @@ function TracesExplorer(): JSX.Element {
|
|||||||
stagedQuery,
|
stagedQuery,
|
||||||
} = useQueryBuilder();
|
} = useQueryBuilder();
|
||||||
|
|
||||||
|
const { options } = useOptionsMenu({
|
||||||
|
storageKey: LOCALSTORAGE.TRACES_LIST_OPTIONS,
|
||||||
|
dataSource: DataSource.TRACES,
|
||||||
|
aggregateOperator: 'noop',
|
||||||
|
initialOptions: {
|
||||||
|
selectColumns: defaultSelectedColumns,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const currentPanelType = useGetPanelTypesQueryParam();
|
const currentPanelType = useGetPanelTypesQueryParam();
|
||||||
|
|
||||||
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
||||||
@ -101,6 +115,18 @@ function TracesExplorer(): JSX.Element {
|
|||||||
|
|
||||||
const { mutate: updateDashboard, isLoading } = useUpdateDashboard();
|
const { mutate: updateDashboard, isLoading } = useUpdateDashboard();
|
||||||
|
|
||||||
|
const getUpdatedQueryForExport = (): Query => {
|
||||||
|
const updatedQuery = cloneDeep(currentQuery);
|
||||||
|
|
||||||
|
set(
|
||||||
|
updatedQuery,
|
||||||
|
'builder.queryData[0].selectColumns',
|
||||||
|
options.selectColumns,
|
||||||
|
);
|
||||||
|
|
||||||
|
return updatedQuery;
|
||||||
|
};
|
||||||
|
|
||||||
const handleExport = useCallback(
|
const handleExport = useCallback(
|
||||||
(dashboard: Dashboard | null): void => {
|
(dashboard: Dashboard | null): void => {
|
||||||
if (!dashboard || !panelType) return;
|
if (!dashboard || !panelType) return;
|
||||||
@ -111,11 +137,17 @@ function TracesExplorer(): JSX.Element {
|
|||||||
|
|
||||||
const widgetId = v4();
|
const widgetId = v4();
|
||||||
|
|
||||||
|
const query =
|
||||||
|
panelType === PANEL_TYPES.LIST
|
||||||
|
? getUpdatedQueryForExport()
|
||||||
|
: exportDefaultQuery;
|
||||||
|
|
||||||
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
|
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
|
||||||
dashboard,
|
dashboard,
|
||||||
exportDefaultQuery,
|
query,
|
||||||
widgetId,
|
widgetId,
|
||||||
panelTypeParam,
|
panelTypeParam,
|
||||||
|
options.selectColumns,
|
||||||
);
|
);
|
||||||
|
|
||||||
updateDashboard(updatedDashboard, {
|
updateDashboard(updatedDashboard, {
|
||||||
@ -144,7 +176,7 @@ function TracesExplorer(): JSX.Element {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dashboardEditView = generateExportToDashboardLink({
|
const dashboardEditView = generateExportToDashboardLink({
|
||||||
query: exportDefaultQuery,
|
query,
|
||||||
panelType: panelTypeParam,
|
panelType: panelTypeParam,
|
||||||
dashboardId: data.payload?.uuid || '',
|
dashboardId: data.payload?.uuid || '',
|
||||||
widgetId,
|
widgetId,
|
||||||
@ -161,6 +193,7 @@ function TracesExplorer(): JSX.Element {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[exportDefaultQuery, notifications, panelType, updateDashboard],
|
[exportDefaultQuery, notifications, panelType, updateDashboard],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ export type IBuilderQuery = {
|
|||||||
legend: string;
|
legend: string;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
|
selectColumns?: BaseAutocompleteData[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IClickHouseQuery {
|
export interface IClickHouseQuery {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user