Skip to content

Commit 94f4f9f

Browse files
committed
feat: enhance unsaved changes confirmation before route leave
1 parent f6a0b6b commit 94f4f9f

File tree

2 files changed

+52
-20
lines changed

2 files changed

+52
-20
lines changed

adminforth/spa/src/views/CreateView.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const router = useRouter();
103103
const record = ref({});
104104
105105
const coreStore = useCoreStore();
106-
const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
106+
const { clearSaveInterceptors, runSaveInterceptors, alert, confirm } = useAdminforth();
107107
108108
const { t } = useI18n();
109109
@@ -135,9 +135,9 @@ onBeforeUnmount(() => {
135135
window.removeEventListener('beforeunload', () => {});
136136
});
137137
138-
onBeforeRouteLeave((to, from, next) => {
138+
onBeforeRouteLeave(async (to, from, next) => {
139139
if (!checkIfWeCanLeavePage()) {
140-
const answer = confirm('There are unsaved changes. Are you sure you want to leave this page?');
140+
const answer = await confirm({message: t('There are unsaved changes. Are you sure you want to leave this page?'), yes: 'Yes', no: 'No'});
141141
if (!answer) return next(false);
142142
}
143143
next();

adminforth/spa/src/views/EditView.vue

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<BreadcrumbsWithButtons>
1313
<!-- save and cancle -->
14-
<button @click="$router.back()"
14+
<button @click="() => {cancelButtonClicked = true; $router.back()}"
1515
class="flex items-center py-1 px-3 me-2 text-sm font-medium text-lightEditViewButtonText rounded-default focus:outline-none bg-lightEditViewButtonBackground rounded border border-lightEditViewButtonBorder hover:bg-lightEditViewButtonBackgroundHover hover:text-lightEditViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightEditViewButtonFocusRing dark:focus:ring-darkEditViewButtonFocusRing dark:bg-darkEditViewButtonBackground dark:text-darkEditViewButtonText dark:border-darkEditViewButtonBorder dark:hover:text-darkEditViewButtonTextHover dark:hover:bg-darkEditViewButtonBackgroundHover"
1616
>
1717
{{ $t('Cancel') }}
@@ -76,8 +76,8 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
7676
import { useCoreStore } from '@/stores/core';
7777
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
7878
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
79-
import { computed, onMounted, onBeforeMount, ref, type Ref, nextTick, watch } from 'vue';
80-
import { useRoute, useRouter } from 'vue-router';
79+
import { computed, onMounted, onBeforeMount, ref, type Ref, nextTick, watch, onBeforeUnmount } from 'vue';
80+
import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router';
8181
import { showErrorTost } from '@/composables/useFrontendApi';
8282
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
8383
import { useAdminforth } from '@/adminforth';
@@ -87,7 +87,7 @@ import type { AdminForthResourceColumn } from '@/types/Back';
8787
8888
const { t } = useI18n();
8989
const coreStore = useCoreStore();
90-
const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
90+
const { clearSaveInterceptors, runSaveInterceptors, alert, confirm } = useAdminforth();
9191
9292
const isValid = ref(false);
9393
const validating = ref(false);
@@ -101,6 +101,34 @@ const saving = ref(false);
101101
102102
const record: Ref<Record<string, any>> = ref({});
103103
104+
const initialRecord = computed(() => coreStore.record);
105+
const wasSaveSuccessful = ref(false);
106+
const cancelButtonClicked = ref(false);
107+
108+
function checkIfWeCanLeavePage() {
109+
return wasSaveSuccessful.value || cancelButtonClicked.value || JSON.stringify(record.value) === JSON.stringify(initialRecord.value);
110+
}
111+
112+
window.addEventListener('beforeunload', (event) => {
113+
if (!checkIfWeCanLeavePage()) {
114+
event.preventDefault();
115+
event.returnValue = '';
116+
}
117+
});
118+
119+
onBeforeUnmount(() => {
120+
window.removeEventListener('beforeunload', () => {});
121+
});
122+
123+
onBeforeRouteLeave(async (to, from, next) => {
124+
if (!checkIfWeCanLeavePage()) {
125+
const answer = await confirm({message: t('There are unsaved changes. Are you sure you want to leave this page?'), yes: 'Yes', no: 'No'});
126+
if (!answer) return next(false);
127+
}
128+
next();
129+
});
130+
131+
104132
watch(record, (newVal) => {
105133
console.log('Record updated:', newVal);
106134
}, { deep: true });
@@ -198,24 +226,28 @@ async function saveRecord() {
198226
if (columnIsUpdated) {
199227
updates[key] = record.value[key];
200228
}
201-
saving.value = false;
202229
}
203-
204-
const resp = await callAdminForthApi({
205-
method: 'POST',
206-
path: `/update_record`,
207-
body: {
208-
resourceId: route.params.resourceId,
209-
recordId: route.params.primaryKey,
210-
record: updates,
211-
meta: {
212-
...(interceptorConfirmationResult ? { confirmationResult: interceptorConfirmationResult } : {}),
230+
let resp = null;
231+
try {
232+
resp = await callAdminForthApi({
233+
method: 'POST',
234+
path: `/update_record`,
235+
body: {
236+
resourceId: route.params.resourceId,
237+
recordId: route.params.primaryKey,
238+
record: updates,
239+
meta: {
240+
...(interceptorConfirmationResult ? { confirmationResult: interceptorConfirmationResult } : {}),
241+
},
213242
},
214-
},
215-
});
243+
});
244+
} finally {
245+
saving.value = false;
246+
}
216247
if (resp.error && resp.error !== 'Operation aborted by hook') {
217248
showErrorTost(resp.error);
218249
} else {
250+
wasSaveSuccessful.value = true;
219251
alert({
220252
message: t('Record updated successfully'),
221253
variant: 'success',

0 commit comments

Comments
 (0)