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:
dnazarenkoo 2023-06-28 15:55:20 +03:00 committed by GitHub
parent 9b8f7a091c
commit 1eabacbaf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 181 additions and 30 deletions

View File

@ -12,4 +12,6 @@ export enum QueryParams {
aggregationOption = 'aggregationOption', aggregationOption = 'aggregationOption',
entity = 'entity', entity = 'entity',
resourceAttributes = 'resourceAttribute', resourceAttributes = 'resourceAttribute',
graphType = 'graphType',
widgetId = 'widgetId',
} }

View File

@ -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

View File

@ -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;
} }

View 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
>;

View 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
>;

View 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,
},
],
},
});

View File

@ -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}

View File

@ -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;
`;