Skip to content

Commit b63aa47

Browse files
committed
Ask for confirmation when navigation away from unsaved dirty forms
Fixes #1541
1 parent e1293af commit b63aa47

File tree

3 files changed

+25
-0
lines changed

3 files changed

+25
-0
lines changed

Tekst-Web/i18n/ui/deDE.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ common:
7979
loading: Lade...
8080
url: URL
8181
downloadSaved: Datei gespeichert als {filename}.
82+
dirtyFormConfirm: Sie haben ungespeicherte Änderungen. Wollen Sie dieses Formular wirklich schließen?
8283
areYouSureHelpTextHint: |
8384
Bitte vergewissern Sie sich, dass Sie die entsprechende Passage in den
8485
Hilfetexten zu diesem Thema gelesen und verstanden haben.

Tekst-Web/i18n/ui/enUS.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ common:
7979
loading: Loading...
8080
url: URL
8181
downloadSaved: File saved as {filename}.
82+
dirtyFormConfirm: You have unsaved changes. Are you sure you want to close this form?
8283
areYouSureHelpTextHint: |
8384
Please make sure you have read and understood the corresponding passage in the
8485
help texts on this topic. You can find the help texts for any page by clicking the

Tekst-Web/src/composables/modelChanges.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import { $t } from '@/i18n';
2+
import { useDialog } from 'naive-ui';
13
import { computed, ref, type Ref } from 'vue';
4+
import { onBeforeRouteLeave } from 'vue-router';
25

36
export function useModelChanges(model: Ref<Record<string, unknown> | undefined>) {
7+
const dialog = useDialog();
48
const valuesToJSON = (o: Record<string, unknown> | undefined) =>
59
Object.fromEntries(Object.entries(o || {}).map(([k, v]) => [k, JSON.stringify(v)]));
610
const beforeEntriesJson = ref(valuesToJSON(model.value));
@@ -9,6 +13,25 @@ export function useModelChanges(model: Ref<Record<string, unknown> | undefined>)
913
Object.entries(afterEntriesJson.value).some(([k, v]) => v !== beforeEntriesJson.value[k])
1014
);
1115

16+
// register router guard to prevent navigation if there are unsaved changes
17+
onBeforeRouteLeave(
18+
async (_to, _from) =>
19+
!changed.value ||
20+
(await new Promise((resolve) =>
21+
dialog.warning({
22+
title: $t('common.warning'),
23+
content: $t('common.dirtyFormConfirm'),
24+
positiveText: $t('common.yes'),
25+
negativeText: $t('common.no'),
26+
onPositiveClick: () => resolve(true),
27+
onNegativeClick: () => resolve(false),
28+
onClose: () => resolve(false),
29+
onEsc: () => resolve(false),
30+
onMaskClick: () => resolve(false),
31+
})
32+
))
33+
);
34+
1235
function getChanges(forceProps?: string[]) {
1336
if (!changed.value) {
1437
return {};

0 commit comments

Comments
 (0)