Add changes detection to record editing

This commit is contained in:
Kelani Tolulope
2023-10-30 11:15:45 +01:00
parent 59b381c9c1
commit 79791dc8d2
6 changed files with 108 additions and 9 deletions
+8
View File
@@ -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')))
@@ -3,7 +3,6 @@
<portal to="topbar-title">
{{ title }}
</portal>
<portal to="topbar-tools">
<b-button-group
v-if="isEdit"
@@ -567,6 +566,10 @@ export default {
return this.$route.name === 'admin.modules.edit' ? this.$t('edit.edit') : this.$t('edit.create')
},
isNew () {
return this.moduleID === NoID
},
trModule: {
get () {
if (!this.module) {
@@ -708,11 +711,16 @@ export default {
}),
checkUnsavedModule (next) {
if (!this.module.deletedAt) {
return next(!isEqual(this.module.clone(), this.initialModuleState.clone()) ? window.confirm(this.$t('general.unsavedChanges')) : true)
}
if (this.isNew) {
next(true)
} else if (!this.module.deletedAt) {
const moduleState = this.module ? this.module.clone() : {}
const initialModuleState = this.initialModuleState ? this.initialModuleState.clone() : {}
next()
next(!isEqual(moduleState, initialModuleState) ? window.confirm(this.$t('general.unsavedChanges')) : true)
} else {
next(true)
}
},
handleNewField () {
@@ -101,6 +101,7 @@
<script>
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)
}
},
},
}
</script>
@@ -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 () {
@@ -110,6 +110,7 @@
</template>
<script>
import { isEqual } from 'lodash'
import { mapGetters, mapActions } from 'vuex'
import Grid from 'corteza-webapp-compose/src/components/Public/Page/Grid'
import RecordToolbar from 'corteza-webapp-compose/src/components/Common/RecordToolbar'
@@ -295,7 +296,11 @@ export default {
immediate: true,
handler () {
this.record = undefined
this.initialRecordState = undefined
this.refresh()
this.loadRecord().then(() => {
this.determineLayout()
})
},
},
@@ -339,6 +344,10 @@ export default {
mounted () {
this.$root.$on('refetch-record-blocks', this.refetchRecordBlocks)
if (this.showRecordModal) {
this.$root.$on('bv::modal::hide', this.checkUnsavedChangesOnModal)
}
},
beforeDestroy () {
@@ -347,6 +356,16 @@ export default {
this.setDefaultValues()
},
// Destroy event before route leave to ensure it doesn't destroy the newly created one
beforeRouteLeave (to, from, next) {
this.$root.$off('refetch-record-blocks', this.refetchRecordBlocks)
this.checkUnsavedChanges(next, to)
},
beforeRouteUpdate (to, from, next) {
this.checkUnsavedChanges(next, to)
},
methods: {
...mapActions({
popPreviousPages: 'ui/popPreviousPages',
@@ -374,6 +393,7 @@ export default {
.then(record => {
return new Promise(resolve => setTimeout(resolve, 300)).then(() => {
this.record = new compose.Record(module, record)
this.initialRecordState = this.record.clone()
})
})
.catch(e => {
@@ -384,6 +404,7 @@ export default {
})
} else {
this.record = new compose.Record(module, { values: this.values })
this.initialRecordState = this.record.clone()
this.inEditing = true
this.inCreating = true
@@ -431,6 +452,7 @@ export default {
this.inEditing = true
this.inCreating = true
this.record = new compose.Record(this.module, { values: this.values })
this.initialRecordState = this.record.clone()
this.$emit('handle-record-redirect', { recordID: NoID, recordPageID: this.page.pageID })
},
@@ -442,6 +464,8 @@ export default {
this.inEditing = true
this.inCreating = true
this.record = new compose.Record(this.module, { values: this.record.values })
this.initialRecordState = this.record.clone()
this.$emit('handle-record-redirect', { recordID: NoID, recordPageID: this.page.pageID, values: this.record.values })
},
@@ -607,6 +631,35 @@ export default {
destroyEvents () {
this.$root.$off('refetch-record-blocks', this.refetchRecordBlocks)
if (this.showRecordModal) {
this.$root.$off('bv::modal::hide', this.checkUnsavedChangesOnModal)
}
},
compareRecordValues () {
const recordValues = JSON.parse(JSON.stringify(this.record ? this.record.values : {}))
const initialRecordState = JSON.parse(JSON.stringify(this.initialRecordState ? this.initialRecordState.values : {}))
return !isEqual(recordValues, initialRecordState)
},
checkUnsavedChanges (next, to) {
if (this.inCreating) {
next(true)
} else {
next(this.compareRecordValues() ? window.confirm(this.$t('general:editor.unsavedChanges')) : true)
}
},
checkUnsavedChangesOnModal (bvEvent, modalId) {
if (modalId === 'record-modal' && !this.inCreating) {
const recordStateChange = this.compareRecordValues() ? window.confirm(this.$t('general:editor.unsavedChanges')) : true
if (!recordStateChange) {
bvEvent.preventDefault()
}
}
},
},
}
@@ -174,4 +174,6 @@ variants:
info: Info
success: Success
danger: Danger
warning: Warning
warning: Warning
editor:
unsavedChanges: Unsaved changes will be lost. Do you wish to leave the page?