fix: save layout bug is resolved (#840)

* fix: save layout bug is resolved

* chore: onClick is also added in the component slider

* chore: dashboard Id is added

* chore: non dashboard widget is filtered out

* chore: panel layout stack issue is resolved
This commit is contained in:
palash-signoz 2022-03-21 21:04:32 +05:30 committed by GitHub
parent 86bdb9a5ad
commit c00f0f159b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 211 additions and 57 deletions

View File

@ -48,7 +48,7 @@ const GridCardGraph = ({
(async (): Promise<void> => {
try {
const getMaxMinTime = GetMaxMinTime({
graphType: widget.panelTypes,
graphType: widget?.panelTypes,
maxTime,
minTime,
});
@ -125,7 +125,7 @@ const GridCardGraph = ({
[],
);
const getModals = () => {
const getModals = (): JSX.Element => {
return (
<>
<Modal

View File

@ -1,12 +1,12 @@
/* eslint-disable react/display-name */
import { SaveFilled } from '@ant-design/icons';
import { notification } from 'antd';
import updateDashboardApi from 'api/dashboard/update';
import Spinner from 'components/Spinner';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
import { Layout } from 'react-grid-layout';
import { useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import { AppState } from 'store/reducers';
import DashboardReducer from 'types/reducer/dashboards';
import { v4 } from 'uuid';
@ -20,11 +20,9 @@ import {
CardContainer,
ReactGridLayout,
} from './styles';
import { updateDashboard } from './utils';
const GridGraph = (): JSX.Element => {
const { push } = useHistory();
const { pathname } = useLocation();
const { dashboards, loading } = useSelector<AppState, DashboardReducer>(
(state) => state.dashboards,
);
@ -50,6 +48,7 @@ const GridGraph = (): JSX.Element => {
return [];
}
// when the layout is not present
if (data.layout === undefined) {
return widgets.map((e, index) => {
return {
@ -69,17 +68,24 @@ const GridGraph = (): JSX.Element => {
};
});
} else {
return data.layout.map((e, index) => ({
...e,
y: 0,
Component: (): JSX.Element => (
<Graph
name={e.i + index}
isDeleted={isDeleted}
widget={widgets[index]}
/>
),
}));
return data.layout
.filter((_, index) => widgets[index])
.map((e, index) => ({
...e,
Component: (): JSX.Element => {
if (widgets[index]) {
return (
<Graph
name={e.i + index}
isDeleted={isDeleted}
widget={widgets[index]}
yAxisUnit={widgets[index].yAxisUnit}
/>
);
}
return <></>;
},
}));
}
}, [widgets, data.layout]);
@ -89,21 +95,34 @@ const GridGraph = (): JSX.Element => {
(isMounted.current === true || isDeleted.current === true)
) {
const preLayouts = getPreLayouts();
setLayout(() => [
...preLayouts,
{
i: (preLayouts.length + 1).toString(),
x: (preLayouts.length % 2) * 6,
y: Infinity,
w: 6,
h: 2,
Component: AddWidgetWrapper,
maxW: 6,
isDraggable: false,
isResizable: false,
isBounded: true,
},
]);
setLayout(() => {
const getX = (): number => {
if (preLayouts && preLayouts?.length > 0) {
const last = preLayouts[preLayouts?.length - 1];
return (last.w + last.x) % 12;
}
return 0;
};
console.log({ x: getX() });
return [
...preLayouts,
{
i: (preLayouts.length + 1).toString(),
x: getX(),
y: Infinity,
w: 6,
h: 2,
Component: AddWidgetWrapper,
maxW: 6,
isDraggable: false,
isResizable: false,
isBounded: true,
},
];
});
}
return (): void => {
@ -112,18 +131,39 @@ const GridGraph = (): JSX.Element => {
}, [widgets, layouts.length, AddWidgetWrapper, loading, getPreLayouts]);
const onDropHandler = useCallback(
(allLayouts: Layout[], currectLayout: Layout, event: DragEvent) => {
async (allLayouts: Layout[], currentLayout: Layout, event: DragEvent) => {
event.preventDefault();
if (event.dataTransfer) {
const graphType = event.dataTransfer.getData('text') as GRAPH_TYPES;
const generateWidgetId = v4();
push(`${pathname}/new?graphType=${graphType}&widgetId=${generateWidgetId}`);
try {
const graphType = event.dataTransfer.getData('text') as GRAPH_TYPES;
const generateWidgetId = v4();
await updateDashboard({
data,
generateWidgetId,
graphType,
selectedDashboard: selectedDashboard,
layout: allLayouts
.map((e, index) => ({
...e,
i: index.toString(),
// when a new element drops
w: e.i === '__dropping-elem__' ? 6 : e.w,
h: e.i === '__dropping-elem__' ? 2 : e.h,
}))
.filter((e) => e.maxW === undefined),
});
} catch (error) {
notification.error({
message: error.toString() || 'Something went wrong',
});
}
}
},
[pathname, push],
[data, selectedDashboard],
);
const onLayoutSaveHanlder = async (): Promise<void> => {
const onLayoutSaveHandler = async (): Promise<void> => {
setSaveLayoutState((state) => ({
...state,
error: false,
@ -175,7 +215,7 @@ const GridGraph = (): JSX.Element => {
<ButtonContainer>
<Button
loading={saveLayoutState.loading}
onClick={onLayoutSaveHanlder}
onClick={onLayoutSaveHandler}
icon={<SaveFilled />}
danger={saveLayoutState.error}
>
@ -198,7 +238,7 @@ const GridGraph = (): JSX.Element => {
{layouts.map(({ Component, ...rest }, index) => {
const widget = (widgets || [])[index] || {};
const type = widget.panelTypes;
const type = widget?.panelTypes || 'TIME_SERIES';
const isQueryType = type === 'VALUE';

View File

@ -0,0 +1,66 @@
import { notification } from 'antd';
import updateDashboardApi from 'api/dashboard/update';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import history from 'lib/history';
import { Layout } from 'react-grid-layout';
import { Dashboard } from 'types/api/dashboard/getAll';
export const updateDashboard = async ({
data,
graphType,
generateWidgetId,
layout,
selectedDashboard,
}: UpdateDashboardProps): Promise<void> => {
const response = await updateDashboardApi({
title: data.title,
uuid: selectedDashboard.uuid,
description: data.description,
name: data.name,
tags: data.tags,
widgets: [
...(data.widgets || []),
{
description: '',
id: generateWidgetId,
isStacked: false,
nullZeroValues: '',
opacity: '',
panelTypes: graphType,
query: [
{
query: '',
legend: '',
},
],
queryData: {
data: [],
error: false,
errorMessage: '',
loading: false,
},
timePreferance: 'GLOBAL_TIME',
title: '',
},
],
layout: layout,
});
if (response.statusCode === 200) {
history.push(
`${history.location.pathname}/new?graphType=${graphType}&widgetId=${generateWidgetId}`,
);
} else {
notification.error({
message: response.error || 'Something went wrong',
});
}
};
interface UpdateDashboardProps {
data: Dashboard['data'];
graphType: GRAPH_TYPES;
generateWidgetId: string;
layout: Layout[];
selectedDashboard: Dashboard;
}

View File

@ -1,37 +1,75 @@
import { notification } from 'antd';
import { updateDashboard } from 'container/GridGraphLayout/utils';
import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
import DashboardReducer from 'types/reducer/dashboards';
import { v4 } from 'uuid';
import menuItems, { ITEMS } from './menuItems';
import { Card, Container, Text } from './styles';
const DashboardGraphSlider = (): JSX.Element => {
const { dashboards } = useSelector<AppState, DashboardReducer>(
(state) => state.dashboards,
);
const [selectedDashboard] = dashboards;
const { data } = selectedDashboard;
const onDragStartHandler: React.DragEventHandler<HTMLDivElement> = useCallback(
(event: React.DragEvent<HTMLDivElement>) => {
event.dataTransfer.setData('text/plain', event.currentTarget.id);
},
[],
);
const { push } = useHistory();
const { pathname } = useLocation();
const onClickHandler = useCallback(
(name: ITEMS) => {
const generateWidgetId = v4();
push(`${pathname}/new?graphType=${name}&widgetId=${generateWidgetId}`);
async (name: ITEMS) => {
try {
const generateWidgetId = v4();
const getX = (): number => {
if (data.layout && data.layout?.length > 0) {
const lastIndexX = data.layout[data.layout?.length - 1];
return (lastIndexX.w + lastIndexX.x) % 12;
}
return 0;
};
await updateDashboard({
data,
generateWidgetId,
graphType: name,
layout: [
...(data.layout || []),
{
h: 2,
i: ((data.layout || [])?.length + 1).toString(),
w: 6,
x: getX(),
y: 0,
},
],
selectedDashboard,
});
} catch (error) {
notification.error({
message: 'Something went wrong',
});
}
},
[push, pathname],
[data, selectedDashboard],
);
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const fillColor: React.CSSProperties['color'] = isDarkMode ? 'white' : 'black';
return (
<Container>
{menuItems.map(({ name, Icon, display }) => (
<Card
onClick={(): void => onClickHandler(name)}
onClick={(): Promise<void> => onClickHandler(name)}
id={name}
onDragStart={onDragStartHandler}
key={name}

View File

@ -13,7 +13,7 @@ const LeftContainer = ({
}: LeftContainerProps): JSX.Element => {
return (
<>
<WidgetGraph selectedGraph={selectedGraph} yAxisUnit={yAxisUnit}/>
<WidgetGraph selectedGraph={selectedGraph} yAxisUnit={yAxisUnit} />
<QueryContainer>
<QuerySection selectedTime={selectedTime} />

View File

@ -23,7 +23,7 @@ import {
} from 'store/actions/dashboard/updateQuery';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { GlobalTime } from 'types/actions/globalTime';
import { Widgets } from 'types/api/dashboard/getAll';
import DashboardReducer from 'types/reducer/dashboards';
import { GlobalReducer } from 'types/reducer/globalTime';
@ -48,7 +48,7 @@ const NewWidget = ({
const { dashboards } = useSelector<AppState, DashboardReducer>(
(state) => state.dashboards,
);
const { maxTime, minTime, selectedTime: globalSelectedInterval } = useSelector<
const { selectedTime: globalSelectedInterval } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
@ -77,7 +77,9 @@ const NewWidget = ({
const [description, setDescription] = useState<string>(
selectedWidget?.description || '',
);
const [yAxisUnit, setYAxisUnit] = useState<string>(selectedWidget?.yAxisUnit || 'none');
const [yAxisUnit, setYAxisUnit] = useState<string>(
selectedWidget?.yAxisUnit || 'none',
);
const [stacked, setStacked] = useState<boolean>(
selectedWidget?.isStacked || false,
@ -125,9 +127,10 @@ const NewWidget = ({
saveSettingOfPanel,
selectedDashboard,
dashboardId,
yAxisUnit,
]);
const onClickApplyHandler = () => {
const onClickApplyHandler = (): void => {
selectedWidget?.query.forEach((element, index) => {
updateQuery({
widgetId: selectedWidget?.id || '',
@ -146,7 +149,7 @@ const NewWidget = ({
timePreferance: selectedTime.enum,
title,
widgetId: selectedWidget?.id || '',
yAxisUnit
yAxisUnit,
});
};
@ -167,11 +170,10 @@ const NewWidget = ({
}, [
selectedWidget?.query,
selectedTime.enum,
maxTime,
minTime,
selectedWidget?.id,
selectedGraph,
getQueryResults,
globalSelectedInterval,
]);
useEffect(() => {
@ -188,7 +190,11 @@ const NewWidget = ({
<PanelContainer>
<LeftContainerWrapper flex={5}>
<LeftContainer selectedTime={selectedTime} selectedGraph={selectedGraph} yAxisUnit={yAxisUnit}/>
<LeftContainer
selectedTime={selectedTime}
selectedGraph={selectedGraph}
yAxisUnit={yAxisUnit}
/>
</LeftContainerWrapper>
<RightContainerWrapper flex={1}>
@ -219,6 +225,7 @@ const NewWidget = ({
export interface NewWidgetProps {
selectedGraph: GRAPH_TYPES;
yAxisUnit: Widgets['yAxisUnit'];
}
interface DispatchProps {

View File

@ -55,6 +55,7 @@ export const SaveDashboard = ({
];
const response = await updateDashboardApi({
...selectedDashboard.data,
uuid,
// this is the data for the dashboard
title: selectedDashboard.data.title,
@ -122,4 +123,5 @@ export interface SaveDashboardProps {
nullZeroValues: Widgets['nullZeroValues'];
widgetId: Widgets['id'];
dashboardId: string;
yAxisUnit: Widgets['yAxisUnit'];
}

View File

@ -44,6 +44,7 @@ export interface Widgets {
}[];
};
stepSize?: number;
yAxisUnit?: string;
}
export interface Query {