From 79791dc8d28457be015d78b5a5a426a0215e335d Mon Sep 17 00:00:00 2001 From: Kelani Tolulope Date: Wed, 2 Aug 2023 14:57:50 +0100 Subject: [PATCH] Add changes detection to record editing --- client/web/compose/src/mixins/record.js | 8 +++ .../compose/src/views/Admin/Modules/Edit.vue | 18 +++++-- .../src/views/Admin/Modules/Records/View.vue | 26 +++++++++ .../web/compose/src/views/Namespace/Edit.vue | 8 +-- .../src/views/Public/Pages/Records/View.vue | 53 +++++++++++++++++++ locale/en/corteza-webapp-compose/general.yaml | 4 +- 6 files changed, 108 insertions(+), 9 deletions(-) diff --git a/client/web/compose/src/mixins/record.js b/client/web/compose/src/mixins/record.js index 72fe233b8..5a27081b3 100644 --- a/client/web/compose/src/mixins/record.js +++ b/client/web/compose/src/mixins/record.js @@ -13,6 +13,7 @@ export default { processingUndelete: false, processingSubmit: false, record: undefined, + initialRecordState: undefined, errors: new validator.Validated(), } }, @@ -163,6 +164,8 @@ export default { } else { this.inCreating = false this.inEditing = false + // reset the record initial state in cases where the record edit page is redirected to the record view page + this.initialRecordState = this.record.clone() if (this.showRecordModal) { this.$emit('handle-record-redirect', { recordID: record.recordID, recordPageID: this.page.pageID }) @@ -172,6 +175,9 @@ export default { if (!isNew) { this.record = record + // reset the record initial state in cases where the record edit page is opened on a modal + this.initialRecordState = this.record.clone() + this.determineLayout().then(() => { this.$root.$emit(`refetch-non-record-blocks:${this.page.pageID}`) }) @@ -230,6 +236,7 @@ export default { this.inCreating = false this.inEditing = false this.record = record + this.initialRecordState = this.record.clone() this.$router.push({ name: route, params: { ...this.$route.params, recordID: record.recordID } }) } @@ -326,6 +333,7 @@ export default { this.onModalHide() this.fields = [] this.record = new compose.Record(this.module, {}) + this.initialRecordState = this.record.clone() this.$emit('save') }) .catch(this.toastErrorHandler(this.$t('notification:record.deleteBulkRecordUpdateFailed'))) diff --git a/client/web/compose/src/views/Admin/Modules/Edit.vue b/client/web/compose/src/views/Admin/Modules/Edit.vue index 8c4cd66c1..1832deae7 100644 --- a/client/web/compose/src/views/Admin/Modules/Edit.vue +++ b/client/web/compose/src/views/Admin/Modules/Edit.vue @@ -3,7 +3,6 @@ {{ title }} - import axios from 'axios' +import { isEqual } from 'lodash' import { mapGetters } from 'vuex' import RecordToolbar from 'corteza-webapp-compose/src/components/Common/RecordToolbar' import record from 'corteza-webapp-compose/src/mixins/record' @@ -156,6 +157,10 @@ export default { getNextAndPrevRecord: 'ui/getNextAndPrevRecord', }), + isNew () { + return this.record.recordID === NoID + }, + title () { const { name, handle } = this.module const titlePrefix = this.inCreating ? 'create' : this.inEditing ? 'edit' : 'view' @@ -232,6 +237,7 @@ export default { created () { this.createBlocks() this.record = new compose.Record(this.module, { values: this.values }) + this.initialRecordState = this.record.clone() }, beforeDestroy () { @@ -239,6 +245,14 @@ export default { this.setDefaultValues() }, + beforeRouteLeave (to, from, next) { + this.checkUnsavedChanges(next, to) + }, + + beforeRouteUpdate (to, from, next) { + this.checkUnsavedChanges(next, to) + }, + methods: { createBlocks () { this.fields.forEach(f => { @@ -267,6 +281,7 @@ export default { response() .then(record => { this.record = new compose.Record(module, record) + this.initialRecordState = this.record.clone() }) .catch((e) => { if (!axios.isCancel(e)) { @@ -322,6 +337,17 @@ export default { cancel() }) }, + + checkUnsavedChanges (next, to) { + if (this.isNew) { + return true + } else { + const recordValues = JSON.parse(JSON.stringify(this.record.values)) + const initialRecordState = JSON.parse(JSON.stringify(this.initialRecordState.values)) + + next(!isEqual(recordValues, initialRecordState) ? window.confirm(this.$t('general:editor.unsavedChanges')) : true) + } + }, }, } diff --git a/client/web/compose/src/views/Namespace/Edit.vue b/client/web/compose/src/views/Namespace/Edit.vue index 11ec69048..74c839568 100644 --- a/client/web/compose/src/views/Namespace/Edit.vue +++ b/client/web/compose/src/views/Namespace/Edit.vue @@ -782,15 +782,17 @@ export default { }, checkUnsavedNamespace (next) { - if (!this.namespace.deletedAt) { + if (this.isNew) { + next(true) + } else if (!this.namespace.deletedAt) { const namespaceState = !isEqual(this.namespace.clone(), this.initialNamespaceState.clone()) const isApplicationState = !(this.isApplication === this.isApplicationInitialState) const namespaceAssetsState = !isEqual(this.namespaceAssets, this.namespaceAssetsInitialState) return next((namespaceState || isApplicationState || namespaceAssetsState) ? window.confirm(this.$t('manage.unsavedChanges')) : true) + } else { + next(true) } - - next() }, setDefaultValues () { diff --git a/client/web/compose/src/views/Public/Pages/Records/View.vue b/client/web/compose/src/views/Public/Pages/Records/View.vue index c1bf21c0f..d0e9ef27e 100644 --- a/client/web/compose/src/views/Public/Pages/Records/View.vue +++ b/client/web/compose/src/views/Public/Pages/Records/View.vue @@ -110,6 +110,7 @@