mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-01 09:41:22 +00:00
Prevent navigate from dirty notes
This commit is contained in:
@@ -110,10 +110,12 @@ function NoteInfoHover({ note }: { note: any }) {
|
|||||||
|
|
||||||
export default function NotesEditor({
|
export default function NotesEditor({
|
||||||
modelType,
|
modelType,
|
||||||
modelId
|
modelId,
|
||||||
|
setDirtyCallback
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
modelType: ModelType;
|
modelType: ModelType;
|
||||||
modelId: number;
|
modelId: number;
|
||||||
|
setDirtyCallback?: (dirty: boolean) => void;
|
||||||
}>) {
|
}>) {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
@@ -254,6 +256,11 @@ export default function NotesEditor({
|
|||||||
selector: ({ editor: e }) => e?.isActive('table') ?? false
|
selector: ({ editor: e }) => e?.isActive('table') ?? false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Propagate dirty state up to the panel system for navigation guards
|
||||||
|
useEffect(() => {
|
||||||
|
setDirtyCallback?.(isDirty);
|
||||||
|
}, [isDirty, setDirtyCallback]);
|
||||||
|
|
||||||
// Sync editor editable state when permissions change.
|
// Sync editor editable state when permissions change.
|
||||||
// Pass false for emitUpdate to avoid triggering onUpdate (which sets isDirty).
|
// Pass false for emitUpdate to avoid triggering onUpdate (which sets isDirty).
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
|
Title,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
UnstyledButton
|
UnstyledButton
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
@@ -47,6 +48,7 @@ import type {
|
|||||||
} from '@lib/types/Panel';
|
} from '@lib/types/Panel';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useDocumentVisibility, useWindowEvent } from '@mantine/hooks';
|
import { useDocumentVisibility, useWindowEvent } from '@mantine/hooks';
|
||||||
|
import { modals } from '@mantine/modals';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { generateUrl } from '../../functions/urls';
|
import { generateUrl } from '../../functions/urls';
|
||||||
@@ -284,21 +286,15 @@ function BasePanelGroup({
|
|||||||
[allPanels]
|
[allPanels]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Callback when the active panel changes
|
const [isDirty, setIsDirty] = useState(false);
|
||||||
const handlePanelChange = useCallback(
|
useWindowEvent('beforeunload', (event) => {
|
||||||
|
if (isDirty) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const performPanelChange = useCallback(
|
||||||
(targetPanel: string, event?: any) => {
|
(targetPanel: string, event?: any) => {
|
||||||
cancelEvent(event);
|
|
||||||
|
|
||||||
// check if we are currently on a dirty panel, if so prompt the user to confirm navigation
|
|
||||||
if (isDirty) {
|
|
||||||
const confirm = globalThis.confirm(
|
|
||||||
t`You have unsaved changes, are you sure you want to navigate away from this panel?`
|
|
||||||
);
|
|
||||||
if (!confirm) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event && eventModified(event)) {
|
if (event && eventModified(event)) {
|
||||||
const url = `${location.pathname}/../${targetPanel}`;
|
const url = `${location.pathname}/../${targetPanel}`;
|
||||||
navigateToLink(url, navigate, event);
|
navigateToLink(url, navigate, event);
|
||||||
@@ -308,15 +304,39 @@ function BasePanelGroup({
|
|||||||
|
|
||||||
localState.setLastUsedPanel(pageKey)(targetPanel);
|
localState.setLastUsedPanel(pageKey)(targetPanel);
|
||||||
|
|
||||||
// Optionally call external callback hook
|
|
||||||
if (targetPanel && onPanelChange) {
|
if (targetPanel && onPanelChange) {
|
||||||
onPanelChange(targetPanel);
|
onPanelChange(targetPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// change dirty state
|
|
||||||
setIsDirty(false);
|
setIsDirty(false);
|
||||||
},
|
},
|
||||||
[activePanels, navigate, location, onPanelChange]
|
[navigate, location, pageKey, onPanelChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Callback when the active panel changes
|
||||||
|
const handlePanelChange = useCallback(
|
||||||
|
(targetPanel: string, event?: any) => {
|
||||||
|
cancelEvent(event);
|
||||||
|
|
||||||
|
if (isDirty) {
|
||||||
|
modals.openConfirmModal({
|
||||||
|
title: <Title order={4}>{t`Unsaved Changes`}</Title>,
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<Divider />
|
||||||
|
<Text>{t`You have unsaved changes. Are you sure you want to leave this panel?`}</Text>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
labels: { confirm: t`Leave`, cancel: t`Stay` },
|
||||||
|
confirmProps: { color: 'red' },
|
||||||
|
onConfirm: () => performPanelChange(targetPanel, event)
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
performPanelChange(targetPanel, event);
|
||||||
|
},
|
||||||
|
[isDirty, performPanelChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
// if the selected panel state changes update the current panel
|
// if the selected panel state changes update the current panel
|
||||||
@@ -335,13 +355,6 @@ function BasePanelGroup({
|
|||||||
}
|
}
|
||||||
}, [activePanels, panel]);
|
}, [activePanels, panel]);
|
||||||
|
|
||||||
const [isDirty, setIsDirty] = useState(false);
|
|
||||||
useWindowEvent('beforeunload', (event) => {
|
|
||||||
if (isDirty) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Boundary label={`PanelGroup-${pageKey}`}>
|
<Boundary label={`PanelGroup-${pageKey}`}>
|
||||||
<Paper p='sm' radius='xs' shadow='xs' aria-label={`${pageKey}`}>
|
<Paper p='sm' radius='xs' shadow='xs' aria-label={`${pageKey}`}>
|
||||||
|
|||||||
Reference in New Issue
Block a user