mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-15 09:35:53 +08:00
feat: add the export panel to the traces explorer (#2983)
* feat: add the export panel to the traces explorer * feat: onExport dashboard widget is updated * chore: made common hook useDashboard --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
9b8f7a091c
commit
1eabacbaf4
@ -12,4 +12,6 @@ export enum QueryParams {
|
|||||||
aggregationOption = 'aggregationOption',
|
aggregationOption = 'aggregationOption',
|
||||||
entity = 'entity',
|
entity = 'entity',
|
||||||
resourceAttributes = 'resourceAttribute',
|
resourceAttributes = 'resourceAttribute',
|
||||||
|
graphType = 'graphType',
|
||||||
|
widgetId = 'widgetId',
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { Button, Typography } from 'antd';
|
import { Button, Typography } from 'antd';
|
||||||
import createDashboard from 'api/dashboard/create';
|
import createDashboard from 'api/dashboard/create';
|
||||||
import getAll from 'api/dashboard/getAll';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useMutation, useQuery } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
|
|
||||||
import { ExportPanelProps } from '.';
|
import { ExportPanelProps } from '.';
|
||||||
import {
|
import {
|
||||||
@ -18,7 +17,7 @@ import {
|
|||||||
} from './styles';
|
} from './styles';
|
||||||
import { getSelectOptions } from './utils';
|
import { getSelectOptions } from './utils';
|
||||||
|
|
||||||
function ExportPanel({ onExport }: ExportPanelProps): JSX.Element {
|
function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
const { t } = useTranslation(['dashboard']);
|
const { t } = useTranslation(['dashboard']);
|
||||||
|
|
||||||
@ -26,16 +25,18 @@ function ExportPanel({ onExport }: ExportPanelProps): JSX.Element {
|
|||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, isLoading, refetch } = useQuery({
|
const {
|
||||||
queryFn: getAll,
|
data,
|
||||||
queryKey: REACT_QUERY_KEY.GET_ALL_DASHBOARDS,
|
isLoading: isAllDashboardsLoading,
|
||||||
});
|
refetch,
|
||||||
|
} = useGetAllDashboard();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutate: createNewDashboard,
|
mutate: createNewDashboard,
|
||||||
isLoading: createDashboardLoading,
|
isLoading: createDashboardLoading,
|
||||||
} = useMutation(createDashboard, {
|
} = useMutation(createDashboard, {
|
||||||
onSuccess: () => {
|
onSuccess: (data) => {
|
||||||
|
onExport(data?.payload || null);
|
||||||
refetch();
|
refetch();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@ -73,6 +74,14 @@ function ExportPanel({ onExport }: ExportPanelProps): JSX.Element {
|
|||||||
});
|
});
|
||||||
}, [t, createNewDashboard]);
|
}, [t, createNewDashboard]);
|
||||||
|
|
||||||
|
const isDashboardLoading = isAllDashboardsLoading || createDashboardLoading;
|
||||||
|
|
||||||
|
const isDisabled =
|
||||||
|
isAllDashboardsLoading ||
|
||||||
|
!options?.length ||
|
||||||
|
!selectedDashboardId ||
|
||||||
|
isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper direction="vertical">
|
<Wrapper direction="vertical">
|
||||||
<Title>Export Panel</Title>
|
<Title>Export Panel</Title>
|
||||||
@ -81,14 +90,15 @@ function ExportPanel({ onExport }: ExportPanelProps): JSX.Element {
|
|||||||
<DashboardSelect
|
<DashboardSelect
|
||||||
placeholder="Select Dashboard"
|
placeholder="Select Dashboard"
|
||||||
options={options}
|
options={options}
|
||||||
loading={isLoading || createDashboardLoading}
|
loading={isDashboardLoading}
|
||||||
disabled={isLoading || createDashboardLoading}
|
disabled={isDashboardLoading}
|
||||||
value={selectedDashboardId}
|
value={selectedDashboardId}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
disabled={isLoading || !options?.length || !selectedDashboardId}
|
loading={isLoading}
|
||||||
|
disabled={isDisabled}
|
||||||
onClick={handleExportClick}
|
onClick={handleExportClick}
|
||||||
>
|
>
|
||||||
Export
|
Export
|
||||||
|
@ -5,7 +5,7 @@ import { Dashboard } from 'types/api/dashboard/getAll';
|
|||||||
import { MENU_KEY, MENU_LABEL } from './config';
|
import { MENU_KEY, MENU_LABEL } from './config';
|
||||||
import ExportPanelContainer from './ExportPanel';
|
import ExportPanelContainer from './ExportPanel';
|
||||||
|
|
||||||
function ExportPanel({ onExport }: ExportPanelProps): JSX.Element {
|
function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
||||||
const [isExport, setIsExport] = useState<boolean>(false);
|
const [isExport, setIsExport] = useState<boolean>(false);
|
||||||
|
|
||||||
const onModalToggle = useCallback((value: boolean) => {
|
const onModalToggle = useCallback((value: boolean) => {
|
||||||
@ -48,22 +48,28 @@ function ExportPanel({ onExport }: ExportPanelProps): JSX.Element {
|
|||||||
<Button>Actions</Button>
|
<Button>Actions</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Modal
|
<Modal
|
||||||
|
footer={null}
|
||||||
onOk={onCancel(false)}
|
onOk={onCancel(false)}
|
||||||
onCancel={onCancel(false)}
|
onCancel={onCancel(false)}
|
||||||
open={isExport}
|
open={isExport}
|
||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<ExportPanelContainer onExport={onExport} />
|
<ExportPanelContainer isLoading={isLoading} onExport={onExport} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExportPanel.defaultProps = {
|
||||||
|
isLoading: false,
|
||||||
|
};
|
||||||
|
|
||||||
interface OnClickProps {
|
interface OnClickProps {
|
||||||
key: string;
|
key: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExportPanelProps {
|
export interface ExportPanelProps {
|
||||||
|
isLoading?: boolean;
|
||||||
onExport: (dashboard: Dashboard | null) => void;
|
onExport: (dashboard: Dashboard | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
frontend/src/hooks/dashboard/useGetAllDashboard.tsx
Normal file
16
frontend/src/hooks/dashboard/useGetAllDashboard.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import getAll from 'api/dashboard/getAll';
|
||||||
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
|
import { useQuery, UseQueryResult } from 'react-query';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
export const useGetAllDashboard = (): DashboardProps =>
|
||||||
|
useQuery({
|
||||||
|
queryFn: getAll,
|
||||||
|
queryKey: REACT_QUERY_KEY.GET_ALL_DASHBOARDS,
|
||||||
|
});
|
||||||
|
|
||||||
|
type DashboardProps = UseQueryResult<
|
||||||
|
SuccessResponse<PayloadProps> | ErrorResponse,
|
||||||
|
unknown
|
||||||
|
>;
|
14
frontend/src/hooks/dashboard/useUpdateDashboard.tsx
Normal file
14
frontend/src/hooks/dashboard/useUpdateDashboard.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import update from 'api/dashboard/update';
|
||||||
|
import { useMutation, UseMutationResult } from 'react-query';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
|
import { Props } from 'types/api/dashboard/update';
|
||||||
|
|
||||||
|
export const useUpdateDashboard = (): UseUpdateDashboard => useMutation(update);
|
||||||
|
|
||||||
|
type UseUpdateDashboard = UseMutationResult<
|
||||||
|
SuccessResponse<Dashboard> | ErrorResponse,
|
||||||
|
unknown,
|
||||||
|
Props,
|
||||||
|
unknown
|
||||||
|
>;
|
37
frontend/src/hooks/dashboard/utils.ts
Normal file
37
frontend/src/hooks/dashboard/utils.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
|
export const addEmptyWidgetInDashboardJSONWithQuery = (
|
||||||
|
dashboard: Dashboard,
|
||||||
|
query: Query,
|
||||||
|
): Dashboard => ({
|
||||||
|
...dashboard,
|
||||||
|
data: {
|
||||||
|
...dashboard.data,
|
||||||
|
layout: [
|
||||||
|
{
|
||||||
|
i: 'empty',
|
||||||
|
w: 6,
|
||||||
|
x: 0,
|
||||||
|
h: 2,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
...(dashboard?.data?.layout || []),
|
||||||
|
],
|
||||||
|
widgets: [
|
||||||
|
...(dashboard?.data?.widgets || []),
|
||||||
|
{
|
||||||
|
id: 'empty',
|
||||||
|
query,
|
||||||
|
description: '',
|
||||||
|
isStacked: false,
|
||||||
|
nullZeroValues: '',
|
||||||
|
opacity: '',
|
||||||
|
title: '',
|
||||||
|
timePreferance: 'GLOBAL_TIME',
|
||||||
|
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
@ -1,28 +1,95 @@
|
|||||||
import { Tabs } from 'antd';
|
import { Tabs } from 'antd';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { QueryParams } from 'constants/query';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { PANEL_TYPES_QUERY } from 'constants/queryBuilderQueryNames';
|
import {
|
||||||
|
COMPOSITE_QUERY,
|
||||||
|
PANEL_TYPES_QUERY,
|
||||||
|
} from 'constants/queryBuilderQueryNames';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import ExportPanel from 'container/ExportPanel';
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import QuerySection from 'container/TracesExplorer/QuerySection';
|
import QuerySection from 'container/TracesExplorer/QuerySection';
|
||||||
|
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||||
|
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import history from 'lib/history';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { generatePath } from 'react-router-dom';
|
||||||
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { Container } from './styles';
|
import { ActionsWrapper, Container } from './styles';
|
||||||
import { getTabsItems } from './utils';
|
import { getTabsItems } from './utils';
|
||||||
|
|
||||||
function TracesExplorer(): JSX.Element {
|
function TracesExplorer(): JSX.Element {
|
||||||
|
const { notifications } = useNotifications();
|
||||||
const {
|
const {
|
||||||
|
currentQuery,
|
||||||
|
stagedQuery,
|
||||||
|
panelType,
|
||||||
updateAllQueriesOperators,
|
updateAllQueriesOperators,
|
||||||
redirectWithQueryBuilderData,
|
redirectWithQueryBuilderData,
|
||||||
currentQuery,
|
|
||||||
panelType,
|
|
||||||
} = useQueryBuilder();
|
} = useQueryBuilder();
|
||||||
|
|
||||||
const tabsItems = getTabsItems();
|
const tabsItems = getTabsItems();
|
||||||
|
|
||||||
const currentTab = panelType || PANEL_TYPES.TIME_SERIES;
|
const currentTab = panelType || PANEL_TYPES.TIME_SERIES;
|
||||||
|
|
||||||
|
const defaultQuery = useMemo(
|
||||||
|
() =>
|
||||||
|
updateAllQueriesOperators(
|
||||||
|
initialQueriesMap.traces,
|
||||||
|
PANEL_TYPES.TIME_SERIES,
|
||||||
|
DataSource.TRACES,
|
||||||
|
),
|
||||||
|
[updateAllQueriesOperators],
|
||||||
|
);
|
||||||
|
|
||||||
|
const exportDefaultQuery = useMemo(
|
||||||
|
() =>
|
||||||
|
updateAllQueriesOperators(
|
||||||
|
stagedQuery || initialQueriesMap.traces,
|
||||||
|
PANEL_TYPES.TIME_SERIES,
|
||||||
|
DataSource.TRACES,
|
||||||
|
),
|
||||||
|
[stagedQuery, updateAllQueriesOperators],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { mutate: updateDashboard, isLoading } = useUpdateDashboard();
|
||||||
|
|
||||||
|
const handleExport = useCallback(
|
||||||
|
(dashboard: Dashboard | null): void => {
|
||||||
|
if (!dashboard) return;
|
||||||
|
|
||||||
|
const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery(
|
||||||
|
dashboard,
|
||||||
|
exportDefaultQuery,
|
||||||
|
);
|
||||||
|
|
||||||
|
updateDashboard(updatedDashboard, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
const dashboardEditView = `${generatePath(ROUTES.DASHBOARD, {
|
||||||
|
dashboardId: data?.payload?.uuid,
|
||||||
|
})}/new?${QueryParams.graphType}=graph&${
|
||||||
|
QueryParams.widgetId
|
||||||
|
}=empty&${COMPOSITE_QUERY}=${JSON.stringify(exportDefaultQuery)}`;
|
||||||
|
|
||||||
|
history.push(dashboardEditView);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
notifications.error({
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[exportDefaultQuery, notifications, updateDashboard],
|
||||||
|
);
|
||||||
|
|
||||||
const handleTabChange = useCallback(
|
const handleTabChange = useCallback(
|
||||||
(newPanelType: string): void => {
|
(newPanelType: string): void => {
|
||||||
if (panelType === newPanelType) return;
|
if (panelType === newPanelType) return;
|
||||||
@ -43,23 +110,17 @@ function TracesExplorer(): JSX.Element {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultValue = useMemo(
|
useShareBuilderUrl(defaultQuery);
|
||||||
() =>
|
|
||||||
updateAllQueriesOperators(
|
|
||||||
initialQueriesMap.traces,
|
|
||||||
PANEL_TYPES.TIME_SERIES,
|
|
||||||
DataSource.TRACES,
|
|
||||||
),
|
|
||||||
[updateAllQueriesOperators],
|
|
||||||
);
|
|
||||||
|
|
||||||
useShareBuilderUrl(defaultValue);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<QuerySection />
|
<QuerySection />
|
||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
|
<ActionsWrapper>
|
||||||
|
<ExportPanel isLoading={isLoading} onExport={handleExport} />
|
||||||
|
</ActionsWrapper>
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultActiveKey={currentTab}
|
defaultActiveKey={currentTab}
|
||||||
activeKey={currentTab}
|
activeKey={currentTab}
|
||||||
|
@ -3,3 +3,8 @@ import styled from 'styled-components';
|
|||||||
export const Container = styled.div`
|
export const Container = styled.div`
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ActionsWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
`;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user