diff --git a/frontend/public/locales/en-GB/dashboard.json b/frontend/public/locales/en-GB/dashboard.json index 35e9b47b45..6179004aff 100644 --- a/frontend/public/locales/en-GB/dashboard.json +++ b/frontend/public/locales/en-GB/dashboard.json @@ -21,5 +21,9 @@ "error_while_updating_variable": "Error while updating variable", "dashboard_has_been_updated": "Dashboard has been updated", "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." } diff --git a/frontend/public/locales/en/dashboard.json b/frontend/public/locales/en/dashboard.json index aa2454c3a3..a74f23d228 100644 --- a/frontend/public/locales/en/dashboard.json +++ b/frontend/public/locales/en/dashboard.json @@ -24,5 +24,9 @@ "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_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." } diff --git a/frontend/src/components/ExplorerCard/utils.ts b/frontend/src/components/ExplorerCard/utils.ts index 7385fbcc7f..3a2eeac95f 100644 --- a/frontend/src/components/ExplorerCard/utils.ts +++ b/frontend/src/components/ExplorerCard/utils.ts @@ -5,6 +5,7 @@ import { QueryParams } from 'constants/query'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi'; import isEqual from 'lodash-es/isEqual'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { DeleteViewHandlerProps, @@ -35,6 +36,45 @@ export const getViewDetailsUsingViewKey: GetViewDetailsUsingViewKey = ( return undefined; }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const omitIdFromQuery = (query: Query | null): any => ({ + ...query, + builder: { + ...query?.builder, + queryData: query?.builder.queryData.map((queryData) => { + const { id, ...rest } = queryData.aggregateAttribute; + const newAggregateAttribute = rest; + const newGroupByAttributes = queryData.groupBy.map((groupByAttribute) => { + const { id, ...rest } = groupByAttribute; + return rest; + }); + const newItems = queryData.filters.items.map((item) => { + const { id, ...newItem } = item; + if (item.key) { + const { id, ...rest } = item.key; + return { + ...newItem, + key: rest, + }; + } + return newItem; + }); + return { + ...queryData, + aggregateAttribute: newAggregateAttribute, + groupBy: newGroupByAttributes, + filters: { + ...queryData.filters, + items: newItems, + }, + limit: queryData.limit ? queryData.limit : 0, + offset: queryData.offset ? queryData.offset : 0, + pageSize: queryData.pageSize ? queryData.pageSize : 0, + }; + }), + }, +}); + export const isQueryUpdatedInView = ({ viewKey, data, @@ -48,43 +88,7 @@ export const isQueryUpdatedInView = ({ const { query, panelType } = currentViewDetails; // Omitting id from aggregateAttribute and groupBy - const updatedCurrentQuery = { - ...stagedQuery, - builder: { - ...stagedQuery?.builder, - queryData: stagedQuery?.builder.queryData.map((queryData) => { - const { id, ...rest } = queryData.aggregateAttribute; - const newAggregateAttribute = rest; - const newGroupByAttributes = queryData.groupBy.map((groupByAttribute) => { - const { id, ...rest } = groupByAttribute; - return rest; - }); - const newItems = queryData.filters.items.map((item) => { - const { id, ...newItem } = item; - if (item.key) { - const { id, ...rest } = item.key; - return { - ...newItem, - key: rest, - }; - } - return newItem; - }); - return { - ...queryData, - aggregateAttribute: newAggregateAttribute, - groupBy: newGroupByAttributes, - filters: { - ...queryData.filters, - items: newItems, - }, - limit: queryData.limit ? queryData.limit : 0, - offset: queryData.offset ? queryData.offset : 0, - pageSize: queryData.pageSize ? queryData.pageSize : 0, - }; - }), - }, - }; + const updatedCurrentQuery = omitIdFromQuery(stagedQuery); return ( panelType !== currentPanelType || diff --git a/frontend/src/container/NewWidget/index.tsx b/frontend/src/container/NewWidget/index.tsx index 18f05ecec2..e4f40ee2ac 100644 --- a/frontend/src/container/NewWidget/index.tsx +++ b/frontend/src/container/NewWidget/index.tsx @@ -1,5 +1,6 @@ -import { LockFilled } from '@ant-design/icons'; -import { Button, Modal, Tooltip, Typography } from 'antd'; +/* eslint-disable sonarjs/cognitive-complexity */ +import { LockFilled, WarningOutlined } from '@ant-design/icons'; +import { Button, Modal, Space, Tooltip, Typography } from 'antd'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { FeatureKeys } from 'constants/features'; import { PANEL_TYPES } from 'constants/queryBuilder'; @@ -18,6 +19,7 @@ import { getSelectedWidgetIndex, } from 'providers/Dashboard/util'; import { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { generatePath, useLocation, useParams } from 'react-router-dom'; import { AppState } from 'store/reducers'; @@ -39,6 +41,7 @@ import { RightContainerWrapper, } from './styles'; import { NewWidgetProps } from './types'; +import { getIsQueryModified } from './utils'; function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { const { @@ -47,7 +50,14 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { setToScrollWidgetId, } = useDashboard(); - const { currentQuery } = useQueryBuilder(); + const { t } = useTranslation(['dashboard']); + + const { currentQuery, stagedQuery } = useQueryBuilder(); + + const isQueryModified = useMemo( + () => getIsQueryModified(currentQuery, stagedQuery), + [currentQuery, stagedQuery], + ); const { featureResponse } = useSelector( (state) => state.app, @@ -92,6 +102,12 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { selectedWidget?.fillSpans || false, ); const [saveModal, setSaveModal] = useState(false); + const [discardModal, setDiscardModal] = useState(false); + + const closeModal = (): void => { + setSaveModal(false); + setDiscardModal(false); + }; const [graphType, setGraphType] = useState(selectedGraph); @@ -206,6 +222,14 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { ]); 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 })); }, [dashboardId]); @@ -321,21 +345,54 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { + + Unsaved Changes + + ) : ( + 'Save Widget' + ) + } focusTriggerAfterClose forceRender destroyOnClose closable - onCancel={(): void => setSaveModal(false)} + onCancel={closeModal} onOk={onClickSaveHandler} centered open={saveModal} width={600} > - - Your graph built with {' '} - query will be saved. Press OK to confirm. - + {!isQueryModified ? ( + + {t('your_graph_build_with')}{' '} + + {t('dashboar_ok_confirm')} + + ) : ( + {t('dashboard_unsave_changes')} + )} + + + + Unsaved Changes + + } + focusTriggerAfterClose + forceRender + destroyOnClose + closable + onCancel={closeModal} + onOk={discardChanges} + centered + open={discardModal} + width={600} + > + {t('dashboard_unsave_changes')} ); diff --git a/frontend/src/container/NewWidget/utils.ts b/frontend/src/container/NewWidget/utils.ts new file mode 100644 index 0000000000..15fcd278d7 --- /dev/null +++ b/frontend/src/container/NewWidget/utils.ts @@ -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); +};