Added guard to prevent accidental loss when navigating to a different website or closing the browser tab.
This commit is contained in:
parent
75f0a9c72e
commit
046869510a
@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback, useRef } from "react";
|
import { useState, useCallback, useRef, useEffect } from "react";
|
||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { GeneralIdRef } from "../../utils/GeneralIdRef";
|
import { GeneralIdRef } from "../../utils/GeneralIdRef";
|
||||||
import {
|
import {
|
||||||
@ -88,11 +88,17 @@ interface UseFormReturn {
|
|||||||
handleSsoProviderPickerChange: (name: string, value: GeneralIdRef) => void;
|
handleSsoProviderPickerChange: (name: string, value: GeneralIdRef) => void;
|
||||||
handleToggleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
handleToggleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
setState: (updates: Partial<FormState>) => void;
|
setState: (updates: Partial<FormState>) => void;
|
||||||
|
hasUnsavedChanges: () => boolean;
|
||||||
|
markAsSaved: () => void;
|
||||||
|
setupNavigationGuard: (onBlock?: (location: Location) => void) => () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useForm = (initialState: FormState): UseFormReturn => {
|
export const useForm = (initialState: FormState): UseFormReturn => {
|
||||||
const [state, setStateInternal] = useState<FormState>(initialState);
|
const [state, setStateInternal] = useState<FormState>(initialState);
|
||||||
const schemaRef = useRef<joiSchema>({});
|
const schemaRef = useRef<joiSchema>({});
|
||||||
|
const initialDataRef = useRef<FormData>(
|
||||||
|
JSON.parse(JSON.stringify(initialState.data)),
|
||||||
|
);
|
||||||
|
|
||||||
const setState = useCallback((updates: Partial<FormState>) => {
|
const setState = useCallback((updates: Partial<FormState>) => {
|
||||||
setStateInternal((prev) => ({ ...prev, ...updates }));
|
setStateInternal((prev) => ({ ...prev, ...updates }));
|
||||||
@ -564,6 +570,42 @@ export const useForm = (initialState: FormState): UseFormReturn => {
|
|||||||
[state.data, validate, setState],
|
[state.data, validate, setState],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Unsaved changes detection
|
||||||
|
const hasUnsavedChanges = useCallback((): boolean => {
|
||||||
|
return (
|
||||||
|
JSON.stringify(state.data) !== JSON.stringify(initialDataRef.current)
|
||||||
|
);
|
||||||
|
}, [state.data]);
|
||||||
|
|
||||||
|
const markAsSaved = useCallback((): void => {
|
||||||
|
initialDataRef.current = JSON.parse(JSON.stringify(state.data));
|
||||||
|
}, [state.data]);
|
||||||
|
|
||||||
|
const setupNavigationGuard = useCallback(
|
||||||
|
(onBlock?: (location: Location) => void): (() => void) => {
|
||||||
|
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||||
|
if (hasUnsavedChanges()) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.returnValue = "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||||
|
|
||||||
|
// Return cleanup function
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[hasUnsavedChanges],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup navigation guard on mount/unmount
|
||||||
|
useEffect(() => {
|
||||||
|
const cleanup = setupNavigationGuard();
|
||||||
|
return cleanup;
|
||||||
|
}, [setupNavigationGuard]);
|
||||||
|
|
||||||
const api: any = {
|
const api: any = {
|
||||||
state,
|
state,
|
||||||
schema: schemaRef.current,
|
schema: schemaRef.current,
|
||||||
@ -588,6 +630,9 @@ export const useForm = (initialState: FormState): UseFormReturn => {
|
|||||||
handleToggleChange,
|
handleToggleChange,
|
||||||
handleTasksChange,
|
handleTasksChange,
|
||||||
setState,
|
setState,
|
||||||
|
hasUnsavedChanges,
|
||||||
|
markAsSaved,
|
||||||
|
setupNavigationGuard,
|
||||||
};
|
};
|
||||||
Object.defineProperty(api, "schema", {
|
Object.defineProperty(api, "schema", {
|
||||||
get: () => schemaRef.current,
|
get: () => schemaRef.current,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user