Fix/redact bug (#6048)

This commit is contained in:
Reece Browne
2026-04-02 17:39:45 +01:00
committed by GitHub
parent de9625942b
commit 0adcbeedf1
10 changed files with 180 additions and 142 deletions
@@ -1,3 +1,4 @@
import { useRef, useEffect } from "react";
import { Modal, Text, Button, Group, Stack } from "@mantine/core";
import { useNavigationGuard } from "@app/contexts/NavigationContext";
import { useTranslation } from "react-i18next";
@@ -6,51 +7,69 @@ import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
import { Z_INDEX_TOAST } from "@app/styles/zIndex";
interface NavigationWarningModalProps {
onApplyAndContinue?: () => Promise<void>;
onExportAndContinue?: () => Promise<void>;
/** Called when discarding - allows saving applied changes while discarding pending ones */
onDiscardAndContinue?: () => Promise<void>;
}
const NavigationWarningModal = ({ onApplyAndContinue, onExportAndContinue, onDiscardAndContinue }: NavigationWarningModalProps) => {
const NavigationWarningModal = () => {
const { t } = useTranslation();
const { showNavigationWarning, hasUnsavedChanges, pendingNavigation, cancelNavigation, confirmNavigation, setHasUnsavedChanges } =
useNavigationGuard();
const {
showNavigationWarning,
hasUnsavedChanges,
pendingNavigation,
cancelNavigation,
setHasUnsavedChanges,
navigationWarningHandlersRef,
} = useNavigationGuard();
// Store pendingNavigation in a ref so async handlers always have the latest,
// not a stale closure captured before an await.
const pendingNavigationRef = useRef(pendingNavigation);
useEffect(() => {
pendingNavigationRef.current = pendingNavigation;
}, [pendingNavigation]);
const handleKeepWorking = () => {
cancelNavigation();
};
const handleDiscardChanges = async () => {
// If a discard handler is provided, call it to save any already-applied changes, then discard the unsaved changes
if (onDiscardAndContinue) {
await onDiscardAndContinue();
}
const finishAndNavigate = () => {
const nav = pendingNavigationRef.current;
setHasUnsavedChanges(false);
confirmNavigation();
cancelNavigation();
if (nav) {
nav();
}
};
const handleDiscardChanges = async () => {
const handlers = navigationWarningHandlersRef.current;
if (handlers?.onDiscardAndContinue) {
await handlers.onDiscardAndContinue();
}
finishAndNavigate();
};
const handleApplyAndContinue = async () => {
if (onApplyAndContinue) {
await onApplyAndContinue();
const handlers = navigationWarningHandlersRef.current;
if (handlers?.onApplyAndContinue) {
await handlers.onApplyAndContinue();
}
setHasUnsavedChanges(false);
confirmNavigation();
finishAndNavigate();
};
const handleExportAndContinue = async () => {
if (onExportAndContinue) {
await onExportAndContinue();
const handlers = navigationWarningHandlersRef.current;
if (handlers?.onExportAndContinue) {
await handlers.onExportAndContinue();
}
setHasUnsavedChanges(false);
confirmNavigation();
finishAndNavigate();
};
// Read handler availability at render time for button visibility
const handlers = navigationWarningHandlersRef.current;
const hasApply = !!handlers?.onApplyAndContinue;
const hasExport = !!handlers?.onExportAndContinue;
const BUTTON_WIDTH = "12rem";
// Only show modal if there are unsaved changes AND there's an actual pending navigation
// This prevents the modal from showing due to spurious state updates
if (!hasUnsavedChanges || !pendingNavigation) {
return null;
}
@@ -87,12 +106,12 @@ const NavigationWarningModal = ({ onApplyAndContinue, onExportAndContinue, onDis
<Button variant="filled" color="var(--mantine-color-red-9)" onClick={handleDiscardChanges} w={BUTTON_WIDTH} leftSection={<DeleteOutlineIcon fontSize="small" />}>
{t("discardChanges", "Discard Changes")}
</Button>
{onApplyAndContinue && (
{hasApply && (
<Button variant="filled" onClick={handleApplyAndContinue} w={BUTTON_WIDTH} leftSection={<CheckCircleOutlineIcon fontSize="small" />}>
{t("applyAndContinue", "Apply & Leave")}
</Button>
)}
{onExportAndContinue && (
{hasExport && (
<Button variant="filled" onClick={handleExportAndContinue} w={BUTTON_WIDTH} leftSection={<CheckCircleOutlineIcon fontSize="small" />}>
{t("exportAndContinue", "Export & Leave")}
</Button>
@@ -108,12 +127,12 @@ const NavigationWarningModal = ({ onApplyAndContinue, onExportAndContinue, onDis
<Button variant="filled" color="var(--mantine-color-red-9)" onClick={handleDiscardChanges} w={BUTTON_WIDTH} leftSection={<DeleteOutlineIcon fontSize="small" />}>
{t("discardChanges", "Discard Changes")}
</Button>
{onApplyAndContinue && (
{hasApply && (
<Button variant="filled" onClick={handleApplyAndContinue} w={BUTTON_WIDTH} leftSection={<CheckCircleOutlineIcon fontSize="small" />}>
{t("applyAndContinue", "Apply & Leave")}
</Button>
)}
{onExportAndContinue && (
{hasExport && (
<Button variant="filled" onClick={handleExportAndContinue} w={BUTTON_WIDTH} leftSection={<CheckCircleOutlineIcon fontSize="small" />}>
{t("exportAndContinue", "Export & Leave")}
</Button>