feat(test): add custom back navigation handling
This commit is contained in:
14
src/hooks/useExamNavGuard.ts
Normal file
14
src/hooks/useExamNavGuard.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { useBlocker } from "react-router-dom";
|
||||||
|
import { useSatExam } from "../stores/useSatExam";
|
||||||
|
|
||||||
|
export const useExamNavigationGuard = () => {
|
||||||
|
const phase = useSatExam((s) => s.phase);
|
||||||
|
|
||||||
|
const blocker = useBlocker(
|
||||||
|
({ currentLocation, nextLocation }) =>
|
||||||
|
(phase === "MODULE" || phase === "BREAK") &&
|
||||||
|
currentLocation.pathname !== nextLocation.pathname,
|
||||||
|
);
|
||||||
|
|
||||||
|
return blocker;
|
||||||
|
};
|
||||||
@ -37,8 +37,19 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "../../../components/ui/dialog";
|
} from "../../../components/ui/dialog";
|
||||||
|
import { useExamNavigationGuard } from "../../../hooks/useExamNavGuard";
|
||||||
|
|
||||||
export const Test = () => {
|
export const Test = () => {
|
||||||
|
const blocker = useExamNavigationGuard();
|
||||||
|
|
||||||
|
const [showExitDialog, setShowExitDialog] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (blocker.state === "blocked") {
|
||||||
|
setShowExitDialog(true);
|
||||||
|
}
|
||||||
|
}, [blocker.state]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { user } = useAuthStore();
|
const { user } = useAuthStore();
|
||||||
const token = useAuthToken();
|
const token = useAuthToken();
|
||||||
@ -445,7 +456,7 @@ export const Test = () => {
|
|||||||
<div className="flex justify-end gap-3 mt-4">
|
<div className="flex justify-end gap-3 mt-4">
|
||||||
<DialogClose asChild>
|
<DialogClose asChild>
|
||||||
<button className="px-4 py-2 border rounded-lg cursor-pointer font-satoshi">
|
<button className="px-4 py-2 border rounded-lg cursor-pointer font-satoshi">
|
||||||
Cancel
|
Stay
|
||||||
</button>
|
</button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
<button
|
<button
|
||||||
@ -515,6 +526,41 @@ export const Test = () => {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={showExitDialog} onOpenChange={setShowExitDialog}>
|
||||||
|
<DialogContent showCloseButton={false}>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="font-satoshi-medium text-start">
|
||||||
|
Want to quit the test?
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription className="font-satoshi tracking-wide">
|
||||||
|
Your progress will be saved and you can resume later.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3 mt-4">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowExitDialog(false);
|
||||||
|
blocker.reset(); // ⬅️ stay on page
|
||||||
|
}}
|
||||||
|
className="px-4 py-2 border rounded-lg cursor-pointer font-satoshi"
|
||||||
|
>
|
||||||
|
Stay
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
finishExam();
|
||||||
|
blocker.proceed(); // ⬅️ allow navigation
|
||||||
|
}}
|
||||||
|
className="px-4 py-2 bg-red-600 text-white rounded-lg cursor-pointer font-satoshi"
|
||||||
|
>
|
||||||
|
Quit Test
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
|
|||||||
Reference in New Issue
Block a user