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

View File

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

View File

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

View File

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

View File

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

View File

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