feat(#37): Add prompt when leaving with unsaved changes
This commit is contained in:
@@ -97,6 +97,44 @@ const isReadOnly = computed(() => {
|
|||||||
return applicationForm.value?.createdBy?.keycloakId !== user.value?.keycloakId
|
return applicationForm.value?.createdBy?.keycloakId !== user.value?.keycloakId
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Unsaved changes tracking
|
||||||
|
const originalFormJson = ref<string>('')
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
originalFormJson.value = JSON.stringify(applicationForm.value)
|
||||||
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasUnsavedChanges = computed(() => {
|
||||||
|
if (!originalFormJson.value) return false
|
||||||
|
return JSON.stringify(applicationForm.value) !== originalFormJson.value
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleBeforeUnload(e: BeforeUnloadEvent) {
|
||||||
|
if (hasUnsavedChanges.value) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
|
// Allow navigation between sections of the same form
|
||||||
|
if (to.params.id === from.params.id) {
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasUnsavedChanges.value) {
|
||||||
|
const answer = window.confirm($t('applicationForms.unsavedWarning'))
|
||||||
|
next(answer)
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const validationMap = ref<Map<FormElementId, ComplianceStatus> | undefined>()
|
const validationMap = ref<Map<FormElementId, ComplianceStatus> | undefined>()
|
||||||
const validationStatus = ref<ComplianceStatus>(ComplianceStatus.NonCritical)
|
const validationStatus = ref<ComplianceStatus>(ComplianceStatus.NonCritical)
|
||||||
|
|
||||||
@@ -126,6 +164,7 @@ async function onSave() {
|
|||||||
const updated = await updateForm(applicationForm.value.id, applicationForm.value)
|
const updated = await updateForm(applicationForm.value.id, applicationForm.value)
|
||||||
if (updated) {
|
if (updated) {
|
||||||
updateApplicationForm(updated)
|
updateApplicationForm(updated)
|
||||||
|
originalFormJson.value = JSON.stringify(updated)
|
||||||
toast.add({ title: $t('common.success'), description: $t('applicationForms.saved'), color: 'success' })
|
toast.add({ title: $t('common.success'), description: $t('applicationForms.saved'), color: 'success' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,38 @@ const applicationFormTemplate = computed(
|
|||||||
const validationMap = ref<Map<FormElementId, ComplianceStatus> | undefined>()
|
const validationMap = ref<Map<FormElementId, ComplianceStatus> | undefined>()
|
||||||
const validationStatus = ref<ComplianceStatus>(ComplianceStatus.NonCritical)
|
const validationStatus = ref<ComplianceStatus>(ComplianceStatus.NonCritical)
|
||||||
|
|
||||||
|
// Unsaved changes tracking
|
||||||
|
const originalFormJson = ref<string>('')
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
originalFormJson.value = JSON.stringify(applicationFormTemplate.value)
|
||||||
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasUnsavedChanges = computed(() => {
|
||||||
|
if (!originalFormJson.value) return false
|
||||||
|
return JSON.stringify(applicationFormTemplate.value) !== originalFormJson.value
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleBeforeUnload(e: BeforeUnloadEvent) {
|
||||||
|
if (hasUnsavedChanges.value) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeRouteLeave((_to, _from, next) => {
|
||||||
|
if (hasUnsavedChanges.value) {
|
||||||
|
const answer = window.confirm($t('applicationForms.unsavedWarning'))
|
||||||
|
next(answer)
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const allFormElements = computed(() => {
|
const allFormElements = computed(() => {
|
||||||
return (
|
return (
|
||||||
applicationFormTemplate.value?.formElementSections?.flatMap((section: FormElementSectionDto) =>
|
applicationFormTemplate.value?.formElementSections?.flatMap((section: FormElementSectionDto) =>
|
||||||
@@ -105,8 +137,11 @@ watch(
|
|||||||
|
|
||||||
async function onSave() {
|
async function onSave() {
|
||||||
const applicationForm = await prepareAndCreateApplicationForm()
|
const applicationForm = await prepareAndCreateApplicationForm()
|
||||||
if (applicationForm) {
|
if (applicationForm?.id) {
|
||||||
|
// Reset to prevent unsaved changes warning when navigating
|
||||||
|
originalFormJson.value = JSON.stringify(applicationFormTemplate.value)
|
||||||
toast.add({ title: $t('common.success'), description: $t('applicationForms.saved'), color: 'success' })
|
toast.add({ title: $t('common.success'), description: $t('applicationForms.saved'), color: 'success' })
|
||||||
|
await navigateTo(`/application-forms/${applicationForm.id}/0`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +149,8 @@ async function onSubmit() {
|
|||||||
const applicationForm = await prepareAndCreateApplicationForm()
|
const applicationForm = await prepareAndCreateApplicationForm()
|
||||||
if (applicationForm?.id) {
|
if (applicationForm?.id) {
|
||||||
await submitApplicationForm(applicationForm.id)
|
await submitApplicationForm(applicationForm.id)
|
||||||
|
// Reset to prevent unsaved changes warning when navigating
|
||||||
|
originalFormJson.value = JSON.stringify(applicationFormTemplate.value)
|
||||||
await navigateTo('/')
|
await navigateTo('/')
|
||||||
toast.add({ title: $t('common.success'), description: $t('applicationForms.submitted'), color: 'success' })
|
toast.add({ title: $t('common.success'), description: $t('applicationForms.submitted'), color: 'success' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,8 @@
|
|||||||
"form": "Formular",
|
"form": "Formular",
|
||||||
"versions": "Versionen",
|
"versions": "Versionen",
|
||||||
"preview": "Vorschau"
|
"preview": "Vorschau"
|
||||||
}
|
},
|
||||||
|
"unsavedWarning": "Sie haben ungespeicherte Änderungen. Möchten Sie die Seite wirklich verlassen?"
|
||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
"title": "Vorlagen",
|
"title": "Vorlagen",
|
||||||
|
|||||||
@@ -51,7 +51,8 @@
|
|||||||
"form": "Form",
|
"form": "Form",
|
||||||
"versions": "Versions",
|
"versions": "Versions",
|
||||||
"preview": "Preview"
|
"preview": "Preview"
|
||||||
}
|
},
|
||||||
|
"unsavedWarning": "You have unsaved changes. Do you really want to leave this page?"
|
||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
"title": "Templates",
|
"title": "Templates",
|
||||||
|
|||||||
Reference in New Issue
Block a user