2026-04-02 17:39:45 +01:00
|
|
|
import { useRef, useEffect } from "react";
|
2025-10-03 17:02:05 +01:00
|
|
|
import { Modal, Text, Button, Group, Stack } from "@mantine/core";
|
2025-10-28 10:29:36 +00:00
|
|
|
import { useNavigationGuard } from "@app/contexts/NavigationContext";
|
2025-10-03 17:02:05 +01:00
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
|
|
|
|
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
|
|
|
|
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
|
2026-01-05 15:12:14 +00:00
|
|
|
import { Z_INDEX_TOAST } from "@app/styles/zIndex";
|
2025-07-16 17:53:50 +01:00
|
|
|
|
2026-04-02 17:39:45 +01:00
|
|
|
const NavigationWarningModal = () => {
|
2025-09-26 16:29:58 +01:00
|
|
|
const { t } = useTranslation();
|
2026-04-02 17:39:45 +01:00
|
|
|
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]);
|
2025-07-16 17:53:50 +01:00
|
|
|
|
|
|
|
|
const handleKeepWorking = () => {
|
|
|
|
|
cancelNavigation();
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-02 17:39:45 +01:00
|
|
|
const finishAndNavigate = () => {
|
|
|
|
|
const nav = pendingNavigationRef.current;
|
|
|
|
|
setHasUnsavedChanges(false);
|
|
|
|
|
cancelNavigation();
|
|
|
|
|
if (nav) {
|
|
|
|
|
nav();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-05 15:12:14 +00:00
|
|
|
const handleDiscardChanges = async () => {
|
2026-04-02 17:39:45 +01:00
|
|
|
const handlers = navigationWarningHandlersRef.current;
|
|
|
|
|
if (handlers?.onDiscardAndContinue) {
|
|
|
|
|
await handlers.onDiscardAndContinue();
|
2026-01-05 15:12:14 +00:00
|
|
|
}
|
2026-04-02 17:39:45 +01:00
|
|
|
finishAndNavigate();
|
2025-07-16 17:53:50 +01:00
|
|
|
};
|
|
|
|
|
|
2025-10-02 20:14:35 +01:00
|
|
|
const handleApplyAndContinue = async () => {
|
2026-04-02 17:39:45 +01:00
|
|
|
const handlers = navigationWarningHandlersRef.current;
|
|
|
|
|
if (handlers?.onApplyAndContinue) {
|
|
|
|
|
await handlers.onApplyAndContinue();
|
2025-10-02 20:14:35 +01:00
|
|
|
}
|
2026-04-02 17:39:45 +01:00
|
|
|
finishAndNavigate();
|
2025-10-02 20:14:35 +01:00
|
|
|
};
|
2025-07-16 17:53:50 +01:00
|
|
|
|
2026-01-05 15:12:14 +00:00
|
|
|
const handleExportAndContinue = async () => {
|
2026-04-02 17:39:45 +01:00
|
|
|
const handlers = navigationWarningHandlersRef.current;
|
|
|
|
|
if (handlers?.onExportAndContinue) {
|
|
|
|
|
await handlers.onExportAndContinue();
|
2025-07-16 17:53:50 +01:00
|
|
|
}
|
2026-04-02 17:39:45 +01:00
|
|
|
finishAndNavigate();
|
2025-07-16 17:53:50 +01:00
|
|
|
};
|
2026-01-05 15:12:14 +00:00
|
|
|
|
2026-04-02 17:39:45 +01:00
|
|
|
// Read handler availability at render time for button visibility
|
|
|
|
|
const handlers = navigationWarningHandlersRef.current;
|
|
|
|
|
const hasApply = !!handlers?.onApplyAndContinue;
|
|
|
|
|
const hasExport = !!handlers?.onExportAndContinue;
|
|
|
|
|
|
2026-01-05 15:12:14 +00:00
|
|
|
const BUTTON_WIDTH = "12rem";
|
2025-07-16 17:53:50 +01:00
|
|
|
|
2025-12-03 20:37:23 +00:00
|
|
|
// Only show modal if there are unsaved changes AND there's an actual pending navigation
|
|
|
|
|
if (!hasUnsavedChanges || !pendingNavigation) {
|
2025-07-16 17:53:50 +01:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Modal
|
|
|
|
|
opened={showNavigationWarning}
|
|
|
|
|
onClose={handleKeepWorking}
|
2025-09-26 16:29:58 +01:00
|
|
|
title={t("unsavedChangesTitle", "Unsaved Changes")}
|
2025-07-16 17:53:50 +01:00
|
|
|
centered
|
2025-10-03 17:02:05 +01:00
|
|
|
size="auto"
|
|
|
|
|
closeOnClickOutside={true}
|
|
|
|
|
closeOnEscape={true}
|
2026-01-05 15:12:14 +00:00
|
|
|
zIndex={Z_INDEX_TOAST}
|
2025-07-16 17:53:50 +01:00
|
|
|
>
|
2025-10-03 17:02:05 +01:00
|
|
|
<Stack>
|
2026-04-10 17:41:19 +01:00
|
|
|
<Stack ta="center" p="md">
|
|
|
|
|
<Text size="md" fw="300">
|
|
|
|
|
{t("unsavedChanges", "You have unsaved changes to your PDF.")}
|
|
|
|
|
</Text>
|
|
|
|
|
<Text size="lg" fw="500">
|
|
|
|
|
{t("areYouSure", "Are you sure you want to leave?")}
|
|
|
|
|
</Text>
|
2025-10-03 17:02:05 +01:00
|
|
|
</Stack>
|
2025-09-26 16:29:58 +01:00
|
|
|
|
2025-10-03 17:02:05 +01:00
|
|
|
{/* Desktop layout: 2 groups side by side */}
|
|
|
|
|
<Group justify="space-between" gap="xl" visibleFrom="md">
|
|
|
|
|
<Group gap="sm">
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="light"
|
|
|
|
|
color="var(--mantine-color-gray-8)"
|
|
|
|
|
onClick={handleKeepWorking}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<ArrowBackIcon fontSize="small" />}
|
|
|
|
|
>
|
2025-09-26 16:29:58 +01:00
|
|
|
{t("keepWorking", "Keep Working")}
|
2025-07-16 17:53:50 +01:00
|
|
|
</Button>
|
2025-10-02 20:14:35 +01:00
|
|
|
</Group>
|
2025-10-03 17:02:05 +01:00
|
|
|
<Group gap="sm">
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="filled"
|
|
|
|
|
color="var(--mantine-color-red-9)"
|
|
|
|
|
onClick={handleDiscardChanges}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<DeleteOutlineIcon fontSize="small" />}
|
|
|
|
|
>
|
2025-10-03 17:02:05 +01:00
|
|
|
{t("discardChanges", "Discard Changes")}
|
|
|
|
|
</Button>
|
2026-04-02 17:39:45 +01:00
|
|
|
{hasApply && (
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="filled"
|
|
|
|
|
onClick={handleApplyAndContinue}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<CheckCircleOutlineIcon fontSize="small" />}
|
|
|
|
|
>
|
2025-10-03 17:02:05 +01:00
|
|
|
{t("applyAndContinue", "Apply & Leave")}
|
2025-09-26 16:29:58 +01:00
|
|
|
</Button>
|
|
|
|
|
)}
|
2026-04-02 17:39:45 +01:00
|
|
|
{hasExport && (
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="filled"
|
|
|
|
|
onClick={handleExportAndContinue}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<CheckCircleOutlineIcon fontSize="small" />}
|
|
|
|
|
>
|
2026-01-05 15:12:14 +00:00
|
|
|
{t("exportAndContinue", "Export & Leave")}
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
2025-09-26 16:29:58 +01:00
|
|
|
</Group>
|
2025-07-16 17:53:50 +01:00
|
|
|
</Group>
|
2025-10-03 17:02:05 +01:00
|
|
|
|
|
|
|
|
{/* Mobile layout: centered stack of 4 buttons */}
|
|
|
|
|
<Stack align="center" gap="sm" hiddenFrom="md">
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="light"
|
|
|
|
|
color="var(--mantine-color-gray-8)"
|
|
|
|
|
onClick={handleKeepWorking}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<ArrowBackIcon fontSize="small" />}
|
|
|
|
|
>
|
2025-10-03 17:02:05 +01:00
|
|
|
{t("keepWorking", "Keep Working")}
|
|
|
|
|
</Button>
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="filled"
|
|
|
|
|
color="var(--mantine-color-red-9)"
|
|
|
|
|
onClick={handleDiscardChanges}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<DeleteOutlineIcon fontSize="small" />}
|
|
|
|
|
>
|
2025-10-03 17:02:05 +01:00
|
|
|
{t("discardChanges", "Discard Changes")}
|
|
|
|
|
</Button>
|
2026-04-02 17:39:45 +01:00
|
|
|
{hasApply && (
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="filled"
|
|
|
|
|
onClick={handleApplyAndContinue}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<CheckCircleOutlineIcon fontSize="small" />}
|
|
|
|
|
>
|
2025-10-03 17:02:05 +01:00
|
|
|
{t("applyAndContinue", "Apply & Leave")}
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
2026-04-02 17:39:45 +01:00
|
|
|
{hasExport && (
|
2026-04-10 17:41:19 +01:00
|
|
|
<Button
|
|
|
|
|
variant="filled"
|
|
|
|
|
onClick={handleExportAndContinue}
|
|
|
|
|
w={BUTTON_WIDTH}
|
|
|
|
|
leftSection={<CheckCircleOutlineIcon fontSize="small" />}
|
|
|
|
|
>
|
2026-01-05 15:12:14 +00:00
|
|
|
{t("exportAndContinue", "Export & Leave")}
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
2025-10-03 17:02:05 +01:00
|
|
|
</Stack>
|
2025-07-16 17:53:50 +01:00
|
|
|
</Stack>
|
|
|
|
|
</Modal>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-26 16:29:58 +01:00
|
|
|
export default NavigationWarningModal;
|