diff --git a/src/app/editor/[projectId]/FlowchartEditor.tsx b/src/app/editor/[projectId]/FlowchartEditor.tsx index 47f1ccd..3cd6759 100644 --- a/src/app/editor/[projectId]/FlowchartEditor.tsx +++ b/src/app/editor/[projectId]/FlowchartEditor.tsx @@ -1,6 +1,7 @@ 'use client' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useRouter } from 'next/navigation' import ReactFlow, { Background, BackgroundVariant, @@ -416,6 +417,10 @@ function FlowchartEditorInner({ projectId, projectName, initialData }: Flowchart const [importConfirmDialog, setImportConfirmDialog] = useState<{ pendingData: FlowchartData } | null>(null) + const [showNavigationWarning, setShowNavigationWarning] = useState(false) + + // Track the last saved data to determine dirty state + const lastSavedDataRef = useRef(initialData) // Ref for hidden file input const fileInputRef = useRef(null) @@ -473,6 +478,33 @@ function FlowchartEditorInner({ projectId, projectName, initialData }: Flowchart } }, [nodes, edges, projectId, draftState.showPrompt]) + // Calculate dirty state by comparing current data with last saved data + const isDirty = useMemo(() => { + const currentData: FlowchartData = { + nodes: fromReactFlowNodes(nodes), + edges: fromReactFlowEdges(edges), + } + return !flowchartDataEquals(currentData, lastSavedDataRef.current) + }, [nodes, edges]) + + // Browser beforeunload warning when dirty + useEffect(() => { + const handleBeforeUnload = (event: BeforeUnloadEvent) => { + if (isDirty) { + event.preventDefault() + // Modern browsers require returnValue to be set + event.returnValue = '' + return '' + } + } + + window.addEventListener('beforeunload', handleBeforeUnload) + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload) + } + }, [isDirty]) + // Handle restoring draft const handleRestoreDraft = useCallback(() => { if (draftState.savedDraft) { @@ -590,6 +622,9 @@ function FlowchartEditorInner({ projectId, projectName, initialData }: Flowchart // Clear LocalStorage draft after successful save clearDraft(projectId) + // Update last saved data ref to mark as not dirty + lastSavedDataRef.current = flowchartData + setToast({ message: 'Project saved successfully', type: 'success' }) } catch (error) { console.error('Failed to save project:', error) @@ -932,8 +967,63 @@ function FlowchartEditorInner({ projectId, projectName, initialData }: Flowchart setConditionEditor(null) }, []) + // Router for navigation + const router = useRouter() + + // Handle back button click - show warning if dirty + const handleBackClick = useCallback(() => { + if (isDirty) { + setShowNavigationWarning(true) + } else { + router.push('/dashboard') + } + }, [isDirty, router]) + + // Confirm navigation (discard unsaved changes) + const handleConfirmNavigation = useCallback(() => { + setShowNavigationWarning(false) + router.push('/dashboard') + }, [router]) + + // Cancel navigation + const handleCancelNavigation = useCallback(() => { + setShowNavigationWarning(false) + }, []) + return ( -
+
+ {/* Editor header with back button and project name */} +
+
+ +

+ {projectName} +

+ {isDirty && ( + + (unsaved changes) + + )} +
+
+ )} + {/* Navigation warning dialog */} + {showNavigationWarning && ( +
+
+

+ Unsaved Changes +

+

+ You have unsaved changes that will be lost if you leave this page. + Are you sure you want to leave? +

+
+ + +
+
+
+ )} + {/* Hidden file input for import */} -
-
- - - - - -

- {project.name} -

-
-
- -
- -
-
+ ) }