refactor: pop for unsaved changes (#4188)

This commit is contained in:
Rajat Dabade 2023-12-14 11:43:02 +05:30 committed by GitHub
parent ec500831ef
commit 9c1ea0cde9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 48 deletions

View File

@ -21,5 +21,9 @@
"error_while_updating_variable": "Error while updating variable", "error_while_updating_variable": "Error while updating variable",
"dashboard_has_been_updated": "Dashboard has been updated", "dashboard_has_been_updated": "Dashboard has been updated",
"do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?", "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?",
"delete_dashboard_success": "{{name}} dashboard deleted successfully" "delete_dashboard_success": "{{name}} dashboard deleted successfully",
"dashboard_unsave_changes": "There are unsaved changes in the Query builder, please stage and run the query or the changes will be lost. Press OK to discard.",
"dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.",
"your_graph_build_with": "Your graph built with",
"dashboar_ok_confirm": "query will be saved. Press OK to confirm."
} }

View File

@ -24,5 +24,9 @@
"do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?", "do_you_want_to_refresh_the_dashboard": "Do you want to refresh the dashboard?",
"locked_dashboard_delete_tooltip_admin_author": "Dashboard is locked. Please unlock the dashboard to enable delete.", "locked_dashboard_delete_tooltip_admin_author": "Dashboard is locked. Please unlock the dashboard to enable delete.",
"locked_dashboard_delete_tooltip_editor": "Dashboard is locked. Please contact admin to delete the dashboard.", "locked_dashboard_delete_tooltip_editor": "Dashboard is locked. Please contact admin to delete the dashboard.",
"delete_dashboard_success": "{{name}} dashboard deleted successfully" "delete_dashboard_success": "{{name}} dashboard deleted successfully",
"dashboard_unsave_changes": "There are unsaved changes in the Query builder, please stage and run the query or the changes will be lost. Press OK to discard.",
"dashboard_save_changes": "Your graph built with {{queryTag}} query will be saved. Press OK to confirm.",
"your_graph_build_with": "Your graph built with",
"dashboar_ok_confirm": "query will be saved. Press OK to confirm."
} }

View File

@ -5,6 +5,7 @@ import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi'; import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi';
import isEqual from 'lodash-es/isEqual'; import isEqual from 'lodash-es/isEqual';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { import {
DeleteViewHandlerProps, DeleteViewHandlerProps,
@ -35,24 +36,12 @@ export const getViewDetailsUsingViewKey: GetViewDetailsUsingViewKey = (
return undefined; return undefined;
}; };
export const isQueryUpdatedInView = ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any
viewKey, export const omitIdFromQuery = (query: Query | null): any => ({
data, ...query,
stagedQuery,
currentPanelType,
}: IsQueryUpdatedInViewProps): boolean => {
const currentViewDetails = getViewDetailsUsingViewKey(viewKey, data);
if (!currentViewDetails) {
return false;
}
const { query, panelType } = currentViewDetails;
// Omitting id from aggregateAttribute and groupBy
const updatedCurrentQuery = {
...stagedQuery,
builder: { builder: {
...stagedQuery?.builder, ...query?.builder,
queryData: stagedQuery?.builder.queryData.map((queryData) => { queryData: query?.builder.queryData.map((queryData) => {
const { id, ...rest } = queryData.aggregateAttribute; const { id, ...rest } = queryData.aggregateAttribute;
const newAggregateAttribute = rest; const newAggregateAttribute = rest;
const newGroupByAttributes = queryData.groupBy.map((groupByAttribute) => { const newGroupByAttributes = queryData.groupBy.map((groupByAttribute) => {
@ -84,7 +73,22 @@ export const isQueryUpdatedInView = ({
}; };
}), }),
}, },
}; });
export const isQueryUpdatedInView = ({
viewKey,
data,
stagedQuery,
currentPanelType,
}: IsQueryUpdatedInViewProps): boolean => {
const currentViewDetails = getViewDetailsUsingViewKey(viewKey, data);
if (!currentViewDetails) {
return false;
}
const { query, panelType } = currentViewDetails;
// Omitting id from aggregateAttribute and groupBy
const updatedCurrentQuery = omitIdFromQuery(stagedQuery);
return ( return (
panelType !== currentPanelType || panelType !== currentPanelType ||

View File

@ -1,5 +1,6 @@
import { LockFilled } from '@ant-design/icons'; /* eslint-disable sonarjs/cognitive-complexity */
import { Button, Modal, Tooltip, Typography } from 'antd'; import { LockFilled, WarningOutlined } from '@ant-design/icons';
import { Button, Modal, Space, Tooltip, Typography } from 'antd';
import { SOMETHING_WENT_WRONG } from 'constants/api'; import { SOMETHING_WENT_WRONG } from 'constants/api';
import { FeatureKeys } from 'constants/features'; import { FeatureKeys } from 'constants/features';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
@ -18,6 +19,7 @@ import {
getSelectedWidgetIndex, getSelectedWidgetIndex,
} from 'providers/Dashboard/util'; } from 'providers/Dashboard/util';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { generatePath, useLocation, useParams } from 'react-router-dom'; import { generatePath, useLocation, useParams } from 'react-router-dom';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
@ -39,6 +41,7 @@ import {
RightContainerWrapper, RightContainerWrapper,
} from './styles'; } from './styles';
import { NewWidgetProps } from './types'; import { NewWidgetProps } from './types';
import { getIsQueryModified } from './utils';
function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
const { const {
@ -47,7 +50,14 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
setToScrollWidgetId, setToScrollWidgetId,
} = useDashboard(); } = useDashboard();
const { currentQuery } = useQueryBuilder(); const { t } = useTranslation(['dashboard']);
const { currentQuery, stagedQuery } = useQueryBuilder();
const isQueryModified = useMemo(
() => getIsQueryModified(currentQuery, stagedQuery),
[currentQuery, stagedQuery],
);
const { featureResponse } = useSelector<AppState, AppReducer>( const { featureResponse } = useSelector<AppState, AppReducer>(
(state) => state.app, (state) => state.app,
@ -92,6 +102,12 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
selectedWidget?.fillSpans || false, selectedWidget?.fillSpans || false,
); );
const [saveModal, setSaveModal] = useState(false); const [saveModal, setSaveModal] = useState(false);
const [discardModal, setDiscardModal] = useState(false);
const closeModal = (): void => {
setSaveModal(false);
setDiscardModal(false);
};
const [graphType, setGraphType] = useState(selectedGraph); const [graphType, setGraphType] = useState(selectedGraph);
@ -206,6 +222,14 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
]); ]);
const onClickDiscardHandler = useCallback(() => { const onClickDiscardHandler = useCallback(() => {
if (isQueryModified) {
setDiscardModal(true);
return;
}
history.push(generatePath(ROUTES.DASHBOARD, { dashboardId }));
}, [dashboardId, isQueryModified]);
const discardChanges = useCallback(() => {
history.push(generatePath(ROUTES.DASHBOARD, { dashboardId })); history.push(generatePath(ROUTES.DASHBOARD, { dashboardId }));
}, [dashboardId]); }, [dashboardId]);
@ -321,21 +345,54 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
</RightContainerWrapper> </RightContainerWrapper>
</PanelContainer> </PanelContainer>
<Modal <Modal
title="Save Changes" title={
isQueryModified ? (
<Space>
<WarningOutlined style={{ fontSize: '16px', color: '#fdd600' }} />
Unsaved Changes
</Space>
) : (
'Save Widget'
)
}
focusTriggerAfterClose focusTriggerAfterClose
forceRender forceRender
destroyOnClose destroyOnClose
closable closable
onCancel={(): void => setSaveModal(false)} onCancel={closeModal}
onOk={onClickSaveHandler} onOk={onClickSaveHandler}
centered centered
open={saveModal} open={saveModal}
width={600} width={600}
> >
{!isQueryModified ? (
<Typography> <Typography>
Your graph built with <QueryTypeTag queryType={currentQuery.queryType} />{' '} {t('your_graph_build_with')}{' '}
query will be saved. Press OK to confirm. <QueryTypeTag queryType={currentQuery.queryType} />
{t('dashboar_ok_confirm')}
</Typography> </Typography>
) : (
<Typography>{t('dashboard_unsave_changes')} </Typography>
)}
</Modal>
<Modal
title={
<Space>
<WarningOutlined style={{ fontSize: '16px', color: '#fdd600' }} />
Unsaved Changes
</Space>
}
focusTriggerAfterClose
forceRender
destroyOnClose
closable
onCancel={closeModal}
onOk={discardChanges}
centered
open={discardModal}
width={600}
>
<Typography>{t('dashboard_unsave_changes')}</Typography>
</Modal> </Modal>
</Container> </Container>
); );

View File

@ -0,0 +1,15 @@
import { omitIdFromQuery } from 'components/ExplorerCard/utils';
import { isEqual } from 'lodash-es';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
export const getIsQueryModified = (
currentQuery: Query,
stagedQuery: Query | null,
): boolean => {
if (!stagedQuery) {
return false;
}
const omitIdFromStageQuery = omitIdFromQuery(stagedQuery);
const omitIdFromCurrentQuery = omitIdFromQuery(currentQuery);
return !isEqual(omitIdFromStageQuery, omitIdFromCurrentQuery);
};