feat: show confirm dialog when editing case or steps
This commit is contained in:
@@ -216,7 +216,8 @@
|
||||
"delete_file": "Delete file",
|
||||
"click_to_upload": "Click to upload",
|
||||
"or_drag_and_drop": "or drag and drop",
|
||||
"max_file_size": "Max. file size"
|
||||
"max_file_size": "Max. file size",
|
||||
"are_you_sure_leave": "Are you sure you want to leave the page?"
|
||||
},
|
||||
"Runs": {
|
||||
"run_list": "Test Run List",
|
||||
|
||||
@@ -215,7 +215,8 @@
|
||||
"delete_file": "ファイルを削除",
|
||||
"click_to_upload": "クリックしてアップロード",
|
||||
"or_drag_and_drop": "またはドラッグアンドドロップ",
|
||||
"max_file_size": "最大ファイルサイズ"
|
||||
"max_file_size": "最大ファイルサイズ",
|
||||
"are_you_sure_leave": "ページを離れてもよろしいですか?"
|
||||
},
|
||||
"Runs": {
|
||||
"run_list": "テストラン一覧",
|
||||
|
||||
@@ -11,6 +11,7 @@ import { fetchCase, updateCase } from '@/utils/caseControl';
|
||||
import { updateSteps } from './stepControl';
|
||||
import { fetchCreateAttachments, fetchDownloadAttachment, fetchDeleteAttachment } from './attachmentControl';
|
||||
import { TokenContext } from '@/utils/TokenProvider';
|
||||
import { useFormGuard } from '@/utils/formGuard';
|
||||
|
||||
const defaultTestCase = {
|
||||
id: 0,
|
||||
@@ -44,9 +45,12 @@ export default function CaseEditor({ projectId, folderId, caseId, messages, loca
|
||||
const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false);
|
||||
const [isUpdating, setIsUpdating] = useState<boolean>(false);
|
||||
const [plusCount, setPlusCount] = useState<number>(0);
|
||||
const [isDirty, setIsDirty] = useState(false);
|
||||
const router = useRouter();
|
||||
useFormGuard(isDirty, messages.areYouSureLeave);
|
||||
|
||||
const onPlusClick = async (newStepNo: number) => {
|
||||
setIsDirty(true);
|
||||
const newStep: StepType = {
|
||||
id: plusCount,
|
||||
step: '',
|
||||
@@ -86,6 +90,8 @@ export default function CaseEditor({ projectId, folderId, caseId, messages, loca
|
||||
};
|
||||
|
||||
const onDeleteClick = async (stepId: number) => {
|
||||
setIsDirty(true);
|
||||
|
||||
// find deletedStep's stepNo
|
||||
if (testCase.Steps) {
|
||||
const deletedStep = testCase.Steps.find((step) => step.id === stepId);
|
||||
|
||||
@@ -58,6 +58,7 @@ export default function Page({
|
||||
clickToUpload: t('click_to_upload'),
|
||||
orDragAndDrop: t('or_drag_and_drop'),
|
||||
maxFileSize: t('max_file_size'),
|
||||
areYouSureLeave: t('are_you_sure_leave'),
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -131,6 +131,7 @@ export type CaseMessages = {
|
||||
clickToUpload: string;
|
||||
orDragAndDrop: string;
|
||||
maxFileSize: string;
|
||||
areYouSureLeave: string;
|
||||
};
|
||||
|
||||
export { CaseType, StepType, AttachmentType, CaseTypeCountType, CasePriorityCountType, CasesMessages, CaseMessages };
|
||||
|
||||
30
frontend/utils/formGuard.ts
Normal file
30
frontend/utils/formGuard.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useFormGuard = (isDirty: boolean, confirmText: string) => {
|
||||
useEffect(() => {
|
||||
console.log(isDirty);
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (isDirty && event.target instanceof Element && event.target.closest('a:not([target="_blank"]')) {
|
||||
if (!window.confirm(confirmText)) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
||||
if (isDirty) {
|
||||
event.preventDefault();
|
||||
return (event.returnValue = '');
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
window.addEventListener('click', handleClick, true);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
window.removeEventListener('click', handleClick, true);
|
||||
};
|
||||
}, [isDirty]);
|
||||
};
|
||||
Reference in New Issue
Block a user