adminforth 2.17.0-next.71 → 2.17.0-next.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@
12
12
 
13
13
  <BreadcrumbsWithButtons>
14
14
  <!-- save and cancle -->
15
- <button @click="$router.back()"
15
+ <button @click="() => {cancelButtonClicked = true; $router.back()}"
16
16
  class="af-cancel-button flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-lightCreateViewButtonText focus:outline-none bg-lightCreateViewButtonBackground rounded border border-lightCreateViewButtonBorder hover:bg-lightCreateViewButtonBackgroundHover hover:text-lightCreateViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightCreateViewButtonFocusRing dark:focus:ring-darkCreateViewButtonFocusRing dark:bg-darkCreateViewButtonBackground dark:text-darkCreateViewButtonText dark:border-darkCreateViewButtonBorder dark:hover:text-darkCreateViewButtonTextHover dark:hover:bg-darkCreateViewButtonBackgroundHover"
17
17
  >
18
18
  {{ $t('Cancel') }}
@@ -81,8 +81,8 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
81
81
  import { useCoreStore } from '@/stores/core';
82
82
  import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, checkShowIf } from '@/utils';
83
83
  import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
84
- import { onMounted, onBeforeMount, ref, watch, nextTick } from 'vue';
85
- import { useRoute, useRouter } from 'vue-router';
84
+ import { onMounted, onBeforeMount, onBeforeUnmount, ref, watch, nextTick } from 'vue';
85
+ import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router';
86
86
  import { computed } from 'vue';
87
87
  import { showErrorTost } from '@/composables/useFrontendApi';
88
88
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
@@ -103,7 +103,7 @@ const router = useRouter();
103
103
  const record = ref({});
104
104
 
105
105
  const coreStore = useCoreStore();
106
- const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
106
+ const { clearSaveInterceptors, runSaveInterceptors, alert, confirm } = useAdminforth();
107
107
 
108
108
  const { t } = useI18n();
109
109
 
@@ -113,11 +113,38 @@ const initialValues = ref({});
113
113
 
114
114
  const readonlyColumns = ref([]);
115
115
 
116
+ const cancelButtonClicked = ref(false);
117
+ const wasSaveSuccessful = ref(false);
116
118
 
117
119
  async function onUpdateRecord(newRecord: any) {
118
120
  record.value = newRecord;
119
121
  }
120
122
 
123
+ function checkIfWeCanLeavePage() {
124
+ return wasSaveSuccessful.value || cancelButtonClicked.value || JSON.stringify(record.value) === JSON.stringify(initialValues.value);
125
+ }
126
+
127
+ function onBeforeUnload(event: BeforeUnloadEvent) {
128
+ if (!checkIfWeCanLeavePage()) {
129
+ event.preventDefault();
130
+ event.returnValue = '';
131
+ }
132
+ }
133
+
134
+ window.addEventListener('beforeunload', onBeforeUnload);
135
+
136
+ onBeforeUnmount(() => {
137
+ window.removeEventListener('beforeunload', onBeforeUnload);
138
+ });
139
+
140
+ onBeforeRouteLeave(async (to, from, next) => {
141
+ if (!checkIfWeCanLeavePage()) {
142
+ const answer = await confirm({message: t('There are unsaved changes. Are you sure you want to leave this page?'), yes: 'Yes', no: 'No'});
143
+ if (!answer) return next(false);
144
+ }
145
+ next();
146
+ });
147
+
121
148
  onBeforeMount(() => {
122
149
  clearSaveInterceptors(route.params.resourceId as string);
123
150
  });
@@ -202,6 +229,7 @@ async function saveRecord() {
202
229
  showErrorTost(response.error);
203
230
  } else {
204
231
  saving.value = false;
232
+ wasSaveSuccessful.value = true;
205
233
  if (route.query.returnTo) {
206
234
  router.push(<string>route.query.returnTo);
207
235
  } else {
@@ -11,7 +11,7 @@
11
11
 
12
12
  <BreadcrumbsWithButtons>
13
13
  <!-- save and cancle -->
14
- <button @click="$router.back()"
14
+ <button @click="() => {cancelButtonClicked = true; $router.back()}"
15
15
  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"
16
16
  >
17
17
  {{ $t('Cancel') }}
@@ -76,8 +76,8 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
76
76
  import { useCoreStore } from '@/stores/core';
77
77
  import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
78
78
  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';
81
81
  import { showErrorTost } from '@/composables/useFrontendApi';
82
82
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
83
83
  import { useAdminforth } from '@/adminforth';
@@ -87,7 +87,7 @@ import type { AdminForthResourceColumn } from '@/types/Back';
87
87
 
88
88
  const { t } = useI18n();
89
89
  const coreStore = useCoreStore();
90
- const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
90
+ const { clearSaveInterceptors, runSaveInterceptors, alert, confirm } = useAdminforth();
91
91
 
92
92
  const isValid = ref(false);
93
93
  const validating = ref(false);
@@ -101,6 +101,36 @@ const saving = ref(false);
101
101
 
102
102
  const record: Ref<Record<string, any>> = ref({});
103
103
 
104
+ const initialRecord = computed(() => coreStore.record);
105
+ const wasSaveSuccessful = ref(false);
106
+ const cancelButtonClicked = ref(false);
107
+
108
+ function onBeforeUnload(event: BeforeUnloadEvent) {
109
+ if (!checkIfWeCanLeavePage()) {
110
+ event.preventDefault();
111
+ event.returnValue = '';
112
+ }
113
+ }
114
+
115
+ function checkIfWeCanLeavePage() {
116
+ return wasSaveSuccessful.value || cancelButtonClicked.value || JSON.stringify(record.value) === JSON.stringify(initialRecord.value);
117
+ }
118
+
119
+ window.addEventListener('beforeunload', onBeforeUnload);
120
+
121
+ onBeforeUnmount(() => {
122
+ window.removeEventListener('beforeunload', onBeforeUnload);
123
+ });
124
+
125
+ onBeforeRouteLeave(async (to, from, next) => {
126
+ if (!checkIfWeCanLeavePage()) {
127
+ const answer = await confirm({message: t('There are unsaved changes. Are you sure you want to leave this page?'), yes: 'Yes', no: 'No'});
128
+ if (!answer) return next(false);
129
+ }
130
+ next();
131
+ });
132
+
133
+
104
134
  watch(record, (newVal) => {
105
135
  console.log('Record updated:', newVal);
106
136
  }, { deep: true });
@@ -198,24 +228,28 @@ async function saveRecord() {
198
228
  if (columnIsUpdated) {
199
229
  updates[key] = record.value[key];
200
230
  }
201
- saving.value = false;
202
231
  }
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 } : {}),
232
+ let resp = null;
233
+ try {
234
+ resp = await callAdminForthApi({
235
+ method: 'POST',
236
+ path: `/update_record`,
237
+ body: {
238
+ resourceId: route.params.resourceId,
239
+ recordId: route.params.primaryKey,
240
+ record: updates,
241
+ meta: {
242
+ ...(interceptorConfirmationResult ? { confirmationResult: interceptorConfirmationResult } : {}),
243
+ },
213
244
  },
214
- },
215
- });
245
+ });
246
+ } finally {
247
+ saving.value = false;
248
+ }
216
249
  if (resp.error && resp.error !== 'Operation aborted by hook') {
217
250
  showErrorTost(resp.error);
218
251
  } else {
252
+ wasSaveSuccessful.value = true;
219
253
  alert({
220
254
  message: t('Record updated successfully'),
221
255
  variant: 'success',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "2.17.0-next.71",
3
+ "version": "2.17.0-next.72",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",