mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 08:15:54 +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 Spinner from 'components/Spinner';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import { UpdateDashboard } from 'container/GridGraphLayout/utils';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import usePreviousValue from 'hooks/usePreviousValue';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import getChartData from 'lib/getChartData';
|
||||
import history from 'lib/history';
|
||||
import isEmpty from 'lodash-es/isEmpty';
|
||||
import {
|
||||
Dispatch,
|
||||
@ -33,6 +35,7 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import DashboardReducer from 'types/reducer/dashboards';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { LayoutProps } from '..';
|
||||
import EmptyWidget from '../EmptyWidget';
|
||||
@ -157,6 +160,46 @@ function GridCardGraph({
|
||||
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 => (
|
||||
<>
|
||||
<Modal
|
||||
@ -210,6 +253,7 @@ function GridCardGraph({
|
||||
widget={widget}
|
||||
onView={handleOnView}
|
||||
onDelete={handleOnDelete}
|
||||
onClone={onCloneHandler}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
/>
|
||||
@ -241,6 +285,7 @@ function GridCardGraph({
|
||||
widget={widget}
|
||||
onView={handleOnView}
|
||||
onDelete={handleOnDelete}
|
||||
onClone={onCloneHandler}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
/>
|
||||
@ -286,6 +331,7 @@ function GridCardGraph({
|
||||
widget={widget}
|
||||
onView={handleOnView}
|
||||
onDelete={handleOnDelete}
|
||||
onClone={onCloneHandler}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
/>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
CopyOutlined,
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
EditFilled,
|
||||
@ -38,6 +39,7 @@ interface IWidgetHeaderProps {
|
||||
widget: Widgets;
|
||||
onView: VoidFunction;
|
||||
onDelete: VoidFunction;
|
||||
onClone: VoidFunction;
|
||||
parentHover: boolean;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
||||
@ -49,6 +51,7 @@ function WidgetHeader({
|
||||
widget,
|
||||
onView,
|
||||
onDelete,
|
||||
onClone,
|
||||
parentHover,
|
||||
queryResponse,
|
||||
errorMessage,
|
||||
@ -81,8 +84,12 @@ function WidgetHeader({
|
||||
key: 'delete',
|
||||
method: onDelete,
|
||||
},
|
||||
clone: {
|
||||
key: 'clone',
|
||||
method: onClone,
|
||||
},
|
||||
}),
|
||||
[onDelete, onEditHandler, onView],
|
||||
[onDelete, onEditHandler, onView, onClone],
|
||||
);
|
||||
|
||||
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
|
||||
@ -116,6 +123,12 @@ function WidgetHeader({
|
||||
disabled: !editWidget,
|
||||
label: 'Edit',
|
||||
},
|
||||
{
|
||||
key: keyMethodMapping.clone.key,
|
||||
icon: <CopyOutlined />,
|
||||
disabled: false,
|
||||
label: 'Clone',
|
||||
},
|
||||
{
|
||||
key: keyMethodMapping.delete.key,
|
||||
icon: <DeleteOutlined />,
|
||||
@ -130,6 +143,7 @@ function WidgetHeader({
|
||||
keyMethodMapping.delete.key,
|
||||
keyMethodMapping.edit.key,
|
||||
keyMethodMapping.view.key,
|
||||
keyMethodMapping.clone.key,
|
||||
queryResponse.isLoading,
|
||||
],
|
||||
);
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
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';
|
||||
|
||||
export const UpdateDashboard = async (
|
||||
@ -19,9 +19,11 @@ export const UpdateDashboard = async (
|
||||
layout,
|
||||
selectedDashboard,
|
||||
isRedirected,
|
||||
widgetData,
|
||||
}: UpdateDashboardProps,
|
||||
notify: NotificationInstance,
|
||||
): Promise<Dashboard | undefined> => {
|
||||
const copyTitle = `${widgetData?.title} - Copy`;
|
||||
const updatedSelectedDashboard: Dashboard = {
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
@ -33,13 +35,13 @@ export const UpdateDashboard = async (
|
||||
widgets: [
|
||||
...(data.widgets || []),
|
||||
{
|
||||
description: '',
|
||||
description: widgetData?.description || '',
|
||||
id: generateWidgetId,
|
||||
isStacked: false,
|
||||
nullZeroValues: '',
|
||||
nullZeroValues: widgetData?.nullZeroValues || '',
|
||||
opacity: '',
|
||||
panelTypes: graphType,
|
||||
query: {
|
||||
query: widgetData?.query || {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [initialQueryPromQLData],
|
||||
clickhouse_sql: [initialClickHouseData],
|
||||
@ -49,13 +51,15 @@ export const UpdateDashboard = async (
|
||||
},
|
||||
},
|
||||
queryData: {
|
||||
data: { queryData: [] },
|
||||
data: {
|
||||
queryData: widgetData?.queryData.data.queryData || [],
|
||||
},
|
||||
error: false,
|
||||
errorMessage: '',
|
||||
loading: false,
|
||||
},
|
||||
timePreferance: 'GLOBAL_TIME',
|
||||
title: '',
|
||||
timePreferance: widgetData?.timePreferance || 'GLOBAL_TIME',
|
||||
title: widgetData ? copyTitle : '',
|
||||
},
|
||||
],
|
||||
layout,
|
||||
@ -91,4 +95,5 @@ interface UpdateDashboardProps {
|
||||
layout: Layout[];
|
||||
selectedDashboard: Dashboard;
|
||||
isRedirected: boolean;
|
||||
widgetData?: Widgets;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user