mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 19:15:58 +08:00
feat: ability to clone a panel (#2444)
Co-authored-by: gitstart <gitstart@users.noreply.github.com> Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev> Co-authored-by: Palash Gupta <palashgdev@gmail.com> Co-authored-by: Prashant Shahi <prashant@signoz.io> Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
This commit is contained in:
parent
342e94d093
commit
826cbe0803
@ -2,10 +2,12 @@ import { Typography } from 'antd';
|
|||||||
import { ChartData } from 'chart.js';
|
import { ChartData } from 'chart.js';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import GridGraphComponent from 'container/GridGraphComponent';
|
import GridGraphComponent from 'container/GridGraphComponent';
|
||||||
|
import { UpdateDashboard } from 'container/GridGraphLayout/utils';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import usePreviousValue from 'hooks/usePreviousValue';
|
import usePreviousValue from 'hooks/usePreviousValue';
|
||||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||||
import getChartData from 'lib/getChartData';
|
import getChartData from 'lib/getChartData';
|
||||||
|
import history from 'lib/history';
|
||||||
import isEmpty from 'lodash-es/isEmpty';
|
import isEmpty from 'lodash-es/isEmpty';
|
||||||
import {
|
import {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@ -33,6 +35,7 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
|||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import DashboardReducer from 'types/reducer/dashboards';
|
import DashboardReducer from 'types/reducer/dashboards';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { LayoutProps } from '..';
|
import { LayoutProps } from '..';
|
||||||
import EmptyWidget from '../EmptyWidget';
|
import EmptyWidget from '../EmptyWidget';
|
||||||
@ -157,6 +160,46 @@ function GridCardGraph({
|
|||||||
t,
|
t,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const onCloneHandler = async (): Promise<void> => {
|
||||||
|
const uuid = v4();
|
||||||
|
|
||||||
|
const layout = [
|
||||||
|
{
|
||||||
|
i: uuid,
|
||||||
|
w: 6,
|
||||||
|
x: 0,
|
||||||
|
h: 2,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
...(selectedDashboard.data.layout || []),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (widget) {
|
||||||
|
await UpdateDashboard(
|
||||||
|
{
|
||||||
|
data: selectedDashboard.data,
|
||||||
|
generateWidgetId: uuid,
|
||||||
|
graphType: widget.panelTypes,
|
||||||
|
selectedDashboard,
|
||||||
|
layout,
|
||||||
|
widgetData: widget,
|
||||||
|
isRedirected: false,
|
||||||
|
},
|
||||||
|
notifications,
|
||||||
|
).then(() => {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Panel cloned successfully, redirecting to new copy.',
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
history.push(
|
||||||
|
`${history.location.pathname}/new?graphType=${widget.panelTypes}&widgetId=${uuid}`,
|
||||||
|
);
|
||||||
|
}, 1500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getModals = (): JSX.Element => (
|
const getModals = (): JSX.Element => (
|
||||||
<>
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
@ -210,6 +253,7 @@ function GridCardGraph({
|
|||||||
widget={widget}
|
widget={widget}
|
||||||
onView={handleOnView}
|
onView={handleOnView}
|
||||||
onDelete={handleOnDelete}
|
onDelete={handleOnDelete}
|
||||||
|
onClone={onCloneHandler}
|
||||||
queryResponse={queryResponse}
|
queryResponse={queryResponse}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
/>
|
/>
|
||||||
@ -241,6 +285,7 @@ function GridCardGraph({
|
|||||||
widget={widget}
|
widget={widget}
|
||||||
onView={handleOnView}
|
onView={handleOnView}
|
||||||
onDelete={handleOnDelete}
|
onDelete={handleOnDelete}
|
||||||
|
onClone={onCloneHandler}
|
||||||
queryResponse={queryResponse}
|
queryResponse={queryResponse}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
/>
|
/>
|
||||||
@ -286,6 +331,7 @@ function GridCardGraph({
|
|||||||
widget={widget}
|
widget={widget}
|
||||||
onView={handleOnView}
|
onView={handleOnView}
|
||||||
onDelete={handleOnDelete}
|
onDelete={handleOnDelete}
|
||||||
|
onClone={onCloneHandler}
|
||||||
queryResponse={queryResponse}
|
queryResponse={queryResponse}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
CopyOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
EditFilled,
|
EditFilled,
|
||||||
@ -38,6 +39,7 @@ interface IWidgetHeaderProps {
|
|||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
onView: VoidFunction;
|
onView: VoidFunction;
|
||||||
onDelete: VoidFunction;
|
onDelete: VoidFunction;
|
||||||
|
onClone: VoidFunction;
|
||||||
parentHover: boolean;
|
parentHover: boolean;
|
||||||
queryResponse: UseQueryResult<
|
queryResponse: UseQueryResult<
|
||||||
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
||||||
@ -49,6 +51,7 @@ function WidgetHeader({
|
|||||||
widget,
|
widget,
|
||||||
onView,
|
onView,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
onClone,
|
||||||
parentHover,
|
parentHover,
|
||||||
queryResponse,
|
queryResponse,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
@ -81,8 +84,12 @@ function WidgetHeader({
|
|||||||
key: 'delete',
|
key: 'delete',
|
||||||
method: onDelete,
|
method: onDelete,
|
||||||
},
|
},
|
||||||
|
clone: {
|
||||||
|
key: 'clone',
|
||||||
|
method: onClone,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[onDelete, onEditHandler, onView],
|
[onDelete, onEditHandler, onView, onClone],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
|
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
|
||||||
@ -116,6 +123,12 @@ function WidgetHeader({
|
|||||||
disabled: !editWidget,
|
disabled: !editWidget,
|
||||||
label: 'Edit',
|
label: 'Edit',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: keyMethodMapping.clone.key,
|
||||||
|
icon: <CopyOutlined />,
|
||||||
|
disabled: false,
|
||||||
|
label: 'Clone',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: keyMethodMapping.delete.key,
|
key: keyMethodMapping.delete.key,
|
||||||
icon: <DeleteOutlined />,
|
icon: <DeleteOutlined />,
|
||||||
@ -130,6 +143,7 @@ function WidgetHeader({
|
|||||||
keyMethodMapping.delete.key,
|
keyMethodMapping.delete.key,
|
||||||
keyMethodMapping.edit.key,
|
keyMethodMapping.edit.key,
|
||||||
keyMethodMapping.view.key,
|
keyMethodMapping.view.key,
|
||||||
|
keyMethodMapping.clone.key,
|
||||||
queryResponse.isLoading,
|
queryResponse.isLoading,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import { Layout } from 'react-grid-layout';
|
import { Layout } from 'react-grid-layout';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
|
||||||
export const UpdateDashboard = async (
|
export const UpdateDashboard = async (
|
||||||
@ -19,9 +19,11 @@ export const UpdateDashboard = async (
|
|||||||
layout,
|
layout,
|
||||||
selectedDashboard,
|
selectedDashboard,
|
||||||
isRedirected,
|
isRedirected,
|
||||||
|
widgetData,
|
||||||
}: UpdateDashboardProps,
|
}: UpdateDashboardProps,
|
||||||
notify: NotificationInstance,
|
notify: NotificationInstance,
|
||||||
): Promise<Dashboard | undefined> => {
|
): Promise<Dashboard | undefined> => {
|
||||||
|
const copyTitle = `${widgetData?.title} - Copy`;
|
||||||
const updatedSelectedDashboard: Dashboard = {
|
const updatedSelectedDashboard: Dashboard = {
|
||||||
...selectedDashboard,
|
...selectedDashboard,
|
||||||
data: {
|
data: {
|
||||||
@ -33,13 +35,13 @@ export const UpdateDashboard = async (
|
|||||||
widgets: [
|
widgets: [
|
||||||
...(data.widgets || []),
|
...(data.widgets || []),
|
||||||
{
|
{
|
||||||
description: '',
|
description: widgetData?.description || '',
|
||||||
id: generateWidgetId,
|
id: generateWidgetId,
|
||||||
isStacked: false,
|
isStacked: false,
|
||||||
nullZeroValues: '',
|
nullZeroValues: widgetData?.nullZeroValues || '',
|
||||||
opacity: '',
|
opacity: '',
|
||||||
panelTypes: graphType,
|
panelTypes: graphType,
|
||||||
query: {
|
query: widgetData?.query || {
|
||||||
queryType: EQueryType.QUERY_BUILDER,
|
queryType: EQueryType.QUERY_BUILDER,
|
||||||
promql: [initialQueryPromQLData],
|
promql: [initialQueryPromQLData],
|
||||||
clickhouse_sql: [initialClickHouseData],
|
clickhouse_sql: [initialClickHouseData],
|
||||||
@ -49,13 +51,15 @@ export const UpdateDashboard = async (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
queryData: {
|
queryData: {
|
||||||
data: { queryData: [] },
|
data: {
|
||||||
|
queryData: widgetData?.queryData.data.queryData || [],
|
||||||
|
},
|
||||||
error: false,
|
error: false,
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
loading: false,
|
loading: false,
|
||||||
},
|
},
|
||||||
timePreferance: 'GLOBAL_TIME',
|
timePreferance: widgetData?.timePreferance || 'GLOBAL_TIME',
|
||||||
title: '',
|
title: widgetData ? copyTitle : '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
layout,
|
layout,
|
||||||
@ -91,4 +95,5 @@ interface UpdateDashboardProps {
|
|||||||
layout: Layout[];
|
layout: Layout[];
|
||||||
selectedDashboard: Dashboard;
|
selectedDashboard: Dashboard;
|
||||||
isRedirected: boolean;
|
isRedirected: boolean;
|
||||||
|
widgetData?: Widgets;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user