From 34ce9530870883e4ec43cdf91e3d0e8b3f2fce8c Mon Sep 17 00:00:00 2001 From: alesan99 Date: Wed, 20 Nov 2024 09:52:59 -0600 Subject: [PATCH 01/11] WIP unload protect --- .../lib/components/FormMeta/Definition.tsx | 34 +++++++++++++++++-- .../lib/components/Forms/ResourceView.tsx | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 8f29b175a42..47c2cc19833 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -14,6 +14,7 @@ import type { ViewDescription } from '../FormParse'; import { Dialog } from '../Molecules/Dialog'; import { ProtectedTool } from '../Permissions/PermissionDenied'; import { userPreferences } from '../Preferences/userPreferences'; +import { UnloadProtectDialog } from '../Router/UnloadProtect'; export function Definition({ table, @@ -39,6 +40,7 @@ export function Definition({ ); } + function FormDefinitionDialog({ table, viewDescription, @@ -48,11 +50,31 @@ function FormDefinitionDialog({ readonly viewDescription: ViewDescription | undefined; readonly onClose: () => void; }): JSX.Element { + const [showUnloadProtect, setShowUnloadProtect] = React.useState(false); + + const [useFieldLabels = true, setUseFieldLabels] = useCachedState( + 'forms', + 'useFieldLabels' + ); + + const initialValue = React.useRef(useFieldLabels); + const isChanged = React.useRef(false); + React.useEffect(() => { + isChanged.current = useFieldLabels !== initialValue.current; + }, [useFieldLabels]); + return ( + {commonText.close()} + } header={resourcesText.formDefinition()} - onClose={handleClose} + onClose={(): void => { + if (isChanged.current) setShowUnloadProtect(true); + else handleClose(); + }} > @@ -63,6 +85,14 @@ function FormDefinitionDialog({ viewSetId={viewDescription.viewSetId} /> )} + {showUnloadProtect && ( + setShowUnloadProtect(false)} + onConfirm={(): void => globalThis.location.reload()} + > + {formsText.unsavedFormUnloadProtect()} + + )} ); } diff --git a/specifyweb/frontend/js_src/lib/components/Forms/ResourceView.tsx b/specifyweb/frontend/js_src/lib/components/Forms/ResourceView.tsx index 87441abe3e2..27b6805d5e0 100644 --- a/specifyweb/frontend/js_src/lib/components/Forms/ResourceView.tsx +++ b/specifyweb/frontend/js_src/lib/components/Forms/ResourceView.tsx @@ -365,7 +365,7 @@ export function ResourceView({ header={customTitle ?? title} headerButtons={ <> - {headerButtons?.(specifyNetworkBadge) ?? ( + {headerButtons?.(headerContent) ?? ( <> From 67679f3759eb3429b0e4ca11d262dbd35fc18c94 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Fri, 22 Nov 2024 10:07:17 -0600 Subject: [PATCH 02/11] WIP test --- .../lib/components/FormMeta/Definition.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 47c2cc19833..7182fedb041 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -15,6 +15,9 @@ import { Dialog } from '../Molecules/Dialog'; import { ProtectedTool } from '../Permissions/PermissionDenied'; import { userPreferences } from '../Preferences/userPreferences'; import { UnloadProtectDialog } from '../Router/UnloadProtect'; +import { useUnloadProtect } from '../../hooks/navigation'; +import { saveFormUnloadProtect } from '../Forms/Save'; +import { SetUnloadProtectsContext } from '../Router/UnloadProtect'; export function Definition({ table, @@ -63,6 +66,15 @@ function FormDefinitionDialog({ isChanged.current = useFieldLabels !== initialValue.current; }, [useFieldLabels]); + const unsetUnloadProtect = useUnloadProtect( + isChanged.current, + saveFormUnloadProtect + ); + const [unloadProtect, setUnloadProtect] = React.useState< + (() => void) | undefined + >(undefined); + const setUnloadProtects = React.useContext(SetUnloadProtectsContext)!; + return ( { if (isChanged.current) setShowUnloadProtect(true); - else handleClose(); + else + handleClose(); }} > @@ -85,7 +98,7 @@ function FormDefinitionDialog({ viewSetId={viewDescription.viewSetId} /> )} - {showUnloadProtect && ( + {typeof unloadProtect === 'function' && ( setShowUnloadProtect(false)} onConfirm={(): void => globalThis.location.reload()} From c5c87b4888c39d39fc38e010ad7d96d6e080d1d4 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 25 Nov 2024 11:09:52 -0600 Subject: [PATCH 03/11] WIP Disable Form Definition Close button when unsaved changes --- .../lib/components/FormMeta/Definition.tsx | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 7182fedb041..56aa7891c76 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -15,9 +15,10 @@ import { Dialog } from '../Molecules/Dialog'; import { ProtectedTool } from '../Permissions/PermissionDenied'; import { userPreferences } from '../Preferences/userPreferences'; import { UnloadProtectDialog } from '../Router/UnloadProtect'; +import { unsetUnloadProtect } from '../../hooks/navigation'; import { useUnloadProtect } from '../../hooks/navigation'; import { saveFormUnloadProtect } from '../Forms/Save'; -import { SetUnloadProtectsContext } from '../Router/UnloadProtect'; +import { UnloadProtectsContext, SetUnloadProtectsContext } from '../Router/UnloadProtect'; export function Definition({ table, @@ -66,28 +67,31 @@ function FormDefinitionDialog({ isChanged.current = useFieldLabels !== initialValue.current; }, [useFieldLabels]); - const unsetUnloadProtect = useUnloadProtect( - isChanged.current, - saveFormUnloadProtect - ); + // const unsetUnloadProtect = useUnloadProtect( + // isChanged.current, + // saveFormUnloadProtect + // ); + // Show a warning dialog if navigating away before saving the record const [unloadProtect, setUnloadProtect] = React.useState< (() => void) | undefined >(undefined); const setUnloadProtects = React.useContext(SetUnloadProtectsContext)!; + const unloadProtects = React.useContext(UnloadProtectsContext)!; return ( - 0} >{commonText.close()} } header={resourcesText.formDefinition()} onClose={(): void => { - if (isChanged.current) setShowUnloadProtect(true); + if (useFieldLabels !== initialValue.current && unloadProtects.length > 0) setUnloadProtect(() => () => {return true}); else handleClose(); }} + onClose={handleClose} > @@ -99,13 +103,37 @@ function FormDefinitionDialog({ /> )} {typeof unloadProtect === 'function' && ( + + {commonText.cancel()} + { + unsetUnloadProtect(setUnloadProtects, saveFormUnloadProtect); + setUnloadProtects([]); + unloadProtect(); + setUnloadProtect(undefined); + globalThis.location.reload(); + }} + > + {commonText.proceed()} + + + } + header={formsText.unsavedFormUnloadProtect()} + onClose={(): void => setUnloadProtect(undefined)} + > + {formsText.unsavedFormUnloadProtect()} + + )} + {/* {typeof unloadProtect === 'function' && ( setShowUnloadProtect(false)} + onCancel={(): void => {}} onConfirm={(): void => globalThis.location.reload()} > {formsText.unsavedFormUnloadProtect()} - )} + )} */} ); } From 9767b31d988e31865b7238bf217d6dd2d77308ff Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 25 Nov 2024 12:14:53 -0600 Subject: [PATCH 04/11] WIP Add unload protect dialog on unsaved changes --- .../lib/components/FormMeta/Definition.tsx | 47 +++++-------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 56aa7891c76..8bbe3a52577 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -78,20 +78,23 @@ function FormDefinitionDialog({ const setUnloadProtects = React.useContext(SetUnloadProtectsContext)!; const unloadProtects = React.useContext(UnloadProtectsContext)!; + const handleDialogClose = (): void => { + if (useFieldLabels !== initialValue.current && unloadProtects.length > 0) { + setShowUnloadProtect(true); + } else { + handleClose(); + } + } + return ( - 0} + {commonText.close()} } header={resourcesText.formDefinition()} - onClose={(): void => { - if (useFieldLabels !== initialValue.current && unloadProtects.length > 0) setUnloadProtect(() => () => {return true}); - else - handleClose(); - }} - onClose={handleClose} + onClose={handleDialogClose} > @@ -102,38 +105,14 @@ function FormDefinitionDialog({ viewSetId={viewDescription.viewSetId} /> )} - {typeof unloadProtect === 'function' && ( - - {commonText.cancel()} - { - unsetUnloadProtect(setUnloadProtects, saveFormUnloadProtect); - setUnloadProtects([]); - unloadProtect(); - setUnloadProtect(undefined); - globalThis.location.reload(); - }} - > - {commonText.proceed()} - - - } - header={formsText.unsavedFormUnloadProtect()} - onClose={(): void => setUnloadProtect(undefined)} - > - {formsText.unsavedFormUnloadProtect()} - - )} - {/* {typeof unloadProtect === 'function' && ( + {showUnloadProtect && ( {}} + onCancel={(): void => {setShowUnloadProtect(false)}} onConfirm={(): void => globalThis.location.reload()} > {formsText.unsavedFormUnloadProtect()} - )} */} + )} ); } From beccf7b27017184bdc76568094efa80caa2eadb5 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 25 Nov 2024 12:52:59 -0600 Subject: [PATCH 05/11] Cleanup --- .../lib/components/FormMeta/Definition.tsx | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 8bbe3a52577..5a914b36013 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -15,10 +15,7 @@ import { Dialog } from '../Molecules/Dialog'; import { ProtectedTool } from '../Permissions/PermissionDenied'; import { userPreferences } from '../Preferences/userPreferences'; import { UnloadProtectDialog } from '../Router/UnloadProtect'; -import { unsetUnloadProtect } from '../../hooks/navigation'; -import { useUnloadProtect } from '../../hooks/navigation'; -import { saveFormUnloadProtect } from '../Forms/Save'; -import { UnloadProtectsContext, SetUnloadProtectsContext } from '../Router/UnloadProtect'; +import { UnloadProtectsContext } from '../Router/UnloadProtect'; export function Definition({ table, @@ -56,26 +53,13 @@ function FormDefinitionDialog({ }): JSX.Element { const [showUnloadProtect, setShowUnloadProtect] = React.useState(false); - const [useFieldLabels = true, setUseFieldLabels] = useCachedState( + const [useFieldLabels = true] = useCachedState( 'forms', 'useFieldLabels' ); const initialValue = React.useRef(useFieldLabels); - const isChanged = React.useRef(false); - React.useEffect(() => { - isChanged.current = useFieldLabels !== initialValue.current; - }, [useFieldLabels]); - // const unsetUnloadProtect = useUnloadProtect( - // isChanged.current, - // saveFormUnloadProtect - // ); - // Show a warning dialog if navigating away before saving the record - const [unloadProtect, setUnloadProtect] = React.useState< - (() => void) | undefined - >(undefined); - const setUnloadProtects = React.useContext(SetUnloadProtectsContext)!; const unloadProtects = React.useContext(UnloadProtectsContext)!; const handleDialogClose = (): void => { From bb318623bff76fa40aaa6160d2f2a3d50747e991 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 25 Nov 2024 18:56:26 +0000 Subject: [PATCH 06/11] Lint code with ESLint and Prettier Triggered by beccf7b27017184bdc76568094efa80caa2eadb5 on branch refs/heads/issue-2514-2 --- .../lib/components/FormMeta/Definition.tsx | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 5a914b36013..591908e4036 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -41,7 +41,6 @@ export function Definition({ ); } - function FormDefinitionDialog({ table, viewDescription, @@ -52,11 +51,8 @@ function FormDefinitionDialog({ readonly onClose: () => void; }): JSX.Element { const [showUnloadProtect, setShowUnloadProtect] = React.useState(false); - - const [useFieldLabels = true] = useCachedState( - 'forms', - 'useFieldLabels' - ); + + const [useFieldLabels = true] = useCachedState('forms', 'useFieldLabels'); const initialValue = React.useRef(useFieldLabels); @@ -68,15 +64,15 @@ function FormDefinitionDialog({ } else { handleClose(); } - } + }; return ( - {commonText.close()} - } + + {commonText.close()} + + } header={resourcesText.formDefinition()} onClose={handleDialogClose} > @@ -91,7 +87,9 @@ function FormDefinitionDialog({ )} {showUnloadProtect && ( {setShowUnloadProtect(false)}} + onCancel={(): void => { + setShowUnloadProtect(false); + }} onConfirm={(): void => globalThis.location.reload()} > {formsText.unsavedFormUnloadProtect()} From 51c2740f56af38e033f747c70d032e1ffbcc02d2 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 25 Nov 2024 13:23:17 -0600 Subject: [PATCH 07/11] Revert close button --- .../js_src/lib/components/FormMeta/Definition.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 591908e4036..502e79d2df0 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -51,13 +51,11 @@ function FormDefinitionDialog({ readonly onClose: () => void; }): JSX.Element { const [showUnloadProtect, setShowUnloadProtect] = React.useState(false); - + const unloadProtects = React.useContext(UnloadProtectsContext)!; + const [useFieldLabels = true] = useCachedState('forms', 'useFieldLabels'); - const initialValue = React.useRef(useFieldLabels); - const unloadProtects = React.useContext(UnloadProtectsContext)!; - const handleDialogClose = (): void => { if (useFieldLabels !== initialValue.current && unloadProtects.length > 0) { setShowUnloadProtect(true); @@ -68,11 +66,7 @@ function FormDefinitionDialog({ return ( - {commonText.close()} - - } + buttons={commonText.close()} header={resourcesText.formDefinition()} onClose={handleDialogClose} > From 7f57e4207c545fe13bf262abdf609aa77dd319d0 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 25 Nov 2024 19:26:54 +0000 Subject: [PATCH 08/11] Lint code with ESLint and Prettier Triggered by 51c2740f56af38e033f747c70d032e1ffbcc02d2 on branch refs/heads/issue-2514-2 --- .../frontend/js_src/lib/components/FormMeta/Definition.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 502e79d2df0..f7be8cb4a58 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -52,7 +52,7 @@ function FormDefinitionDialog({ }): JSX.Element { const [showUnloadProtect, setShowUnloadProtect] = React.useState(false); const unloadProtects = React.useContext(UnloadProtectsContext)!; - + const [useFieldLabels = true] = useCachedState('forms', 'useFieldLabels'); const initialValue = React.useRef(useFieldLabels); From ccb57b07f5ec7c8262ddc812f71bef34260a68fa Mon Sep 17 00:00:00 2001 From: alesan99 Date: Mon, 2 Dec 2024 08:55:40 -0600 Subject: [PATCH 09/11] Simplify unload protect code --- .../lib/components/FormMeta/Definition.tsx | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index f7be8cb4a58..3e13800ee73 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -54,11 +54,15 @@ function FormDefinitionDialog({ const unloadProtects = React.useContext(UnloadProtectsContext)!; const [useFieldLabels = true] = useCachedState('forms', 'useFieldLabels'); - const initialValue = React.useRef(useFieldLabels); + const initialFieldLabelsValue = React.useRef(useFieldLabels); const handleDialogClose = (): void => { - if (useFieldLabels !== initialValue.current && unloadProtects.length > 0) { - setShowUnloadProtect(true); + if (useFieldLabels !== initialFieldLabelsValue.current) { + if (!showUnloadProtect && unloadProtects.length > 0) { + setShowUnloadProtect(true); + return; + } + globalThis.location.reload(); } else { handleClose(); } @@ -84,7 +88,7 @@ function FormDefinitionDialog({ onCancel={(): void => { setShowUnloadProtect(false); }} - onConfirm={(): void => globalThis.location.reload()} + onConfirm={(): void => handleDialogClose()} > {formsText.unsavedFormUnloadProtect()} @@ -121,21 +125,6 @@ function UseLabels(): JSX.Element { 'useFieldLabels' ); - const initialValue = React.useRef(useFieldLabels); - const isChanged = React.useRef(false); - React.useEffect(() => { - isChanged.current = useFieldLabels !== initialValue.current; - }, [useFieldLabels]); - - React.useEffect( - () => () => { - if (isChanged.current) { - globalThis.location.reload(); - } - }, - [] - ); - return ( Date: Mon, 2 Dec 2024 14:59:15 +0000 Subject: [PATCH 10/11] Lint code with ESLint and Prettier Triggered by ccb57b07f5ec7c8262ddc812f71bef34260a68fa on branch refs/heads/issue-2514-2 --- .../frontend/js_src/lib/components/FormMeta/Definition.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 3e13800ee73..372890e365a 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -57,14 +57,14 @@ function FormDefinitionDialog({ const initialFieldLabelsValue = React.useRef(useFieldLabels); const handleDialogClose = (): void => { - if (useFieldLabels !== initialFieldLabelsValue.current) { + if (useFieldLabels === initialFieldLabelsValue.current) { + handleClose(); + } else { if (!showUnloadProtect && unloadProtects.length > 0) { setShowUnloadProtect(true); return; } globalThis.location.reload(); - } else { - handleClose(); } }; From d1964006028bc81d60e36f6d5030cddf483effe5 Mon Sep 17 00:00:00 2001 From: alesan99 Date: Tue, 3 Dec 2024 09:17:31 -0600 Subject: [PATCH 11/11] Update specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx Co-authored-by: Max Patiiuk --- .../frontend/js_src/lib/components/FormMeta/Definition.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx index 372890e365a..a8cc6434812 100644 --- a/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx +++ b/specifyweb/frontend/js_src/lib/components/FormMeta/Definition.tsx @@ -88,7 +88,7 @@ function FormDefinitionDialog({ onCancel={(): void => { setShowUnloadProtect(false); }} - onConfirm={(): void => handleDialogClose()} + onConfirm={handleDialogClose} > {formsText.unsavedFormUnloadProtect()}