diff --git a/src/components/appBar/AppBar.tsx b/src/components/appBar/AppBar.tsx index 03ec5652..7ceefbcf 100644 --- a/src/components/appBar/AppBar.tsx +++ b/src/components/appBar/AppBar.tsx @@ -53,6 +53,7 @@ const AppBar = ({ title, showBackButton = false, topPanelHeight }: Props) => { const { appBarTitle, systemsList } = useAppBar(); const location = useLocation(); const isGlobalSystemSwitchDisabled = shouldSystemSwitchBeDisabled(location.pathname); + const { isModified, setShowUnsavedChangesDialog } = useAppBar(); const [anchorEl, setAnchorEl] = React.useState(null); const [selectedSystem, setSelectedSystem] = useSelectedSystemSummaries(); @@ -84,7 +85,11 @@ const AppBar = ({ title, showBackButton = false, topPanelHeight }: Props) => { }; const goBack = () => { - history(-1); + if (isModified) { + setShowUnsavedChangesDialog(true); + } else { + history(-1); + } }; const menuId = "user-account-menu"; diff --git a/src/components/editor/faultTree/canvas/EditorCanvas.tsx b/src/components/editor/faultTree/canvas/EditorCanvas.tsx index e9e4650a..ff4a44d3 100644 --- a/src/components/editor/faultTree/canvas/EditorCanvas.tsx +++ b/src/components/editor/faultTree/canvas/EditorCanvas.tsx @@ -26,6 +26,7 @@ import { useFaultTrees } from "@hooks/useFaultTrees"; import { calculateCutSets } from "@services/faultTreeService"; import { SnackbarType, useSnackbar } from "@hooks/useSnackbar"; import { useNavigate } from "react-router-dom"; +import { useAppBar } from "@contexts/AppBarContext"; enum MOVE_NODE { DRAGGING = 0, @@ -92,6 +93,7 @@ const EditorCanvas = ({ const [updatedMinOperationalHours, setUpdatedMinOperationalHours] = useState(initialMinOperationalHours); const [inputColor, setInputColor] = useState(""); const [showSnackbar] = useSnackbar(); + const { isModified, setShowUnsavedChangesDialog } = useAppBar(); let dragStartPosition = null; @@ -300,6 +302,10 @@ const EditorCanvas = ({ }; const handleSetNewDefaultOperationalHours = () => { + if (isModified) { + setShowUnsavedChangesDialog(true); + return; + } const newOperationalDataFilter = Object.assign({}, faultTree.operationalDataFilter); newOperationalDataFilter.minOperationalHours = updatedMinOperationalHours || faultTree.operationalDataFilter.minOperationalHours; @@ -347,7 +353,7 @@ const EditorCanvas = ({ {!showTable && ( void) => { + return React.useCallback( + (action: () => void) => { + if (isModified) { + setShowUnsavedChangesDialog(true); + } else { + action(); + } + }, + [isModified, setShowUnsavedChangesDialog], + ); +}; + +const SidebarMenuHeader: React.FC = ({ onConvertToTable, onExportDiagram, onRestoreLayout, onCutSetAnalysis, rendering, -}: DiagramOptionsProps) => { +}) => { const table = useCurrentFaultTreeTable(); + const { isModified, setShowUnsavedChangesDialog } = useAppBar(); + + const actionCall = useActionCall(isModified, setShowUnsavedChangesDialog); return ( actionCall(onExportDiagram)} + onConvertToTable={() => actionCall(onConvertToTable)} + onRestoreLayout={() => actionCall(onRestoreLayout)} + onCutSetAnalysis={() => actionCall(onCutSetAnalysis)} tableConversionAllowed={!table} rendering={rendering} /> - ); diff --git a/src/components/editor/faultTree/menu/faultEvent/FaultEventMenu.tsx b/src/components/editor/faultTree/menu/faultEvent/FaultEventMenu.tsx index f4fd8d95..bad48570 100644 --- a/src/components/editor/faultTree/menu/faultEvent/FaultEventMenu.tsx +++ b/src/components/editor/faultTree/menu/faultEvent/FaultEventMenu.tsx @@ -27,9 +27,10 @@ import { ReusableFaultEventsProvider } from "@hooks/useReusableFaultEvents"; import { useSelectedSystemSummaries } from "@hooks/useSelectedSystemSummaries"; import { useForm } from "react-hook-form"; import UnsavedChangesDialog from "./UnsavedChangesDialog"; +import { useAppBar } from "@contexts/AppBarContext"; interface Props { - newShapeToolData?: FaultEvent; + selectedShapeToolData?: FaultEvent; onEventUpdated: (faultEvent: FaultEvent) => void; refreshTree: () => void; rootIri?: string; @@ -58,26 +59,25 @@ const getFailureRateIris = (supertypes) => { ); }; -const FaultEventMenu = ({ newShapeToolData, onEventUpdated, refreshTree, rootIri }: Props) => { +const FaultEventMenu = ({ selectedShapeToolData, onEventUpdated, refreshTree, rootIri }: Props) => { const { t } = useTranslation(); const formMethods = useForm(); const { formState, getValues } = formMethods; const { isDirty } = formState; const { classes } = useStyles(); + const { isModified, setIsModified, showUnsavedChangesDialog, setShowUnsavedChangesDialog } = useAppBar(); const theme = useTheme(); const [failureModeDialogOpen, setFailureModeDialogOpen] = useState(false); const [resetMenu, setResetMenu] = useState(false); - const [showSaveAndRejectButton, setShowSaveAndRejectButton] = useState(false); - const [showUnsavedChangesDialog, setShowUnsavedChangesDialog] = useState(false); const [shapeToolData, setShapeToolData] = useState(undefined); useEffect(() => { - if (showSaveAndRejectButton) { + if (isModified) { setShowUnsavedChangesDialog(true); } else { - setShapeToolData(newShapeToolData); + setShapeToolData(selectedShapeToolData); } - }, [newShapeToolData]); + }, [selectedShapeToolData]); const [failureModeOverviewDialogOpen, setFailureModeOverviewDialogOpen] = useState(false); const [failureModeOverview, setFailureModeOverview] = useState(null); @@ -104,7 +104,7 @@ const FaultEventMenu = ({ newShapeToolData, onEventUpdated, refreshTree, rootIri const [snsPredictedIri, setSnsPredictedIri] = useState(undefined); const handleOnSave = async () => { - setShowSaveAndRejectButton(false); + setIsModified(false); const values = getValues(); const { description, gateType } = values; const updateEvent = async (data) => await onEventUpdated({ ...shapeToolData, ...data, gateType, description }); @@ -136,7 +136,7 @@ const FaultEventMenu = ({ newShapeToolData, onEventUpdated, refreshTree, rootIri const handleOnDiscard = () => { formMethods.reset(); setResetMenu(!resetMenu); - setShowSaveAndRejectButton(false); + setIsModified(false); }; const handleManuallyDefinedFailureRateChange = (event, type: NodeTypeWithManualFailureRate) => { @@ -149,17 +149,17 @@ const FaultEventMenu = ({ newShapeToolData, onEventUpdated, refreshTree, rootIri if (type === NodeTypeWithManualFailureRate.External) { setExternalManuallyDefinedFailureRate(inputValue); } - setShowSaveAndRejectButton(true); + setIsModified(true); } }; const handleSnsBasicSelectedFailureRateChange = (event: React.ChangeEvent) => { if (event.target.value === preselectedRadioButton) { setSelectedRadioButton(event.target.value as RadioButtonType); - setShowSaveAndRejectButton(false); + setIsModified(false); } else { setSelectedRadioButton(event.target.value as RadioButtonType); - setShowSaveAndRejectButton(true); + setIsModified(true); } }; @@ -171,7 +171,7 @@ const FaultEventMenu = ({ newShapeToolData, onEventUpdated, refreshTree, rootIri const handleUnsavedChanges = (action) => { action(); setShowUnsavedChangesDialog(false); - setShapeToolData(newShapeToolData); + setShapeToolData(selectedShapeToolData); }; useEffect(() => { @@ -280,7 +280,7 @@ const FaultEventMenu = ({ newShapeToolData, onEventUpdated, refreshTree, rootIri }, [shapeToolData, resetMenu]); useEffect(() => { - setShowSaveAndRejectButton(isDirty); + setIsModified(isDirty); }, [isDirty]); const basedFailureRate = shapeToolData?.supertypes?.hasFailureRate?.estimate?.value; @@ -460,7 +460,7 @@ const FaultEventMenu = ({ newShapeToolData, onEventUpdated, refreshTree, rootIri )} - {showSaveAndRejectButton && ( + {isModified && ( diff --git a/src/contexts/AppBarContext.tsx b/src/contexts/AppBarContext.tsx index 87580662..27e14d7a 100644 --- a/src/contexts/AppBarContext.tsx +++ b/src/contexts/AppBarContext.tsx @@ -14,6 +14,10 @@ interface AppBarTitleContextType { systemsList: System[]; setSystemsList: React.Dispatch>; addSystemToList: (system: System) => void; + isModified: boolean; + setIsModified: React.Dispatch>; + showUnsavedChangesDialog: boolean; + setShowUnsavedChangesDialog: React.Dispatch>; } interface AppBarTitleProviderProps { @@ -33,6 +37,10 @@ const AppBarMainContext = createContext({ systemsList: [], setSystemsList: () => {}, addSystemToList: (system: System) => {}, + isModified: false, + setIsModified: () => {}, + showUnsavedChangesDialog: false, + setShowUnsavedChangesDialog: () => {}, }); export const useAppBar = () => useContext(AppBarMainContext); @@ -40,6 +48,8 @@ export const useAppBar = () => useContext(AppBarMainContext); export const AppBarProvider = ({ children }: AppBarTitleProviderProps) => { const [appBarTitle, setAppBarTitle] = useState(""); const [systemsList, setSystemsList] = useState([]); + const [isModified, setIsModified] = useState(false); + const [showUnsavedChangesDialog, setShowUnsavedChangesDialog] = useState(false); const location = useLocation(); const { t } = useTranslation(); const [showSnackbar] = useSnackbar(); @@ -83,7 +93,19 @@ export const AppBarProvider = ({ children }: AppBarTitleProviderProps) => { }; return ( - + {children} );