diff --git a/client/web/compose/src/components/Common/RecordToolbar.vue b/client/web/compose/src/components/Common/RecordToolbar.vue
index 126db4dc7..cdb7cc45a 100644
--- a/client/web/compose/src/components/Common/RecordToolbar.vue
+++ b/client/web/compose/src/components/Common/RecordToolbar.vue
@@ -57,7 +57,7 @@
findLabel(v, this.field.options.options) || v)
} else {
return findLabel(v, this.field.options.options) || v
diff --git a/client/web/compose/src/components/PageBlocks/RecordBase.vue b/client/web/compose/src/components/PageBlocks/RecordBase.vue
index 31d13e844..7ab167ba8 100644
--- a/client/web/compose/src/components/PageBlocks/RecordBase.vue
+++ b/client/web/compose/src/components/PageBlocks/RecordBase.vue
@@ -193,7 +193,9 @@ export default {
...resolutions,
this.evaluateExpressions(),
]).finally(() => {
- this.evaluating = false
+ setTimeout(() => {
+ this.evaluating = false
+ }, 300)
})
if (this.options.referenceModuleID) {
diff --git a/client/web/compose/src/components/PageBlocks/RecordEditor.vue b/client/web/compose/src/components/PageBlocks/RecordEditor.vue
index 4ccac8f33..267622e6b 100644
--- a/client/web/compose/src/components/PageBlocks/RecordEditor.vue
+++ b/client/web/compose/src/components/PageBlocks/RecordEditor.vue
@@ -170,7 +170,9 @@ export default {
...resolutions,
this.evaluateExpressions(),
]).finally(() => {
- this.evaluating = false
+ setTimeout(() => {
+ this.evaluating = false
+ }, 300)
})
},
},
diff --git a/client/web/compose/src/components/PageBlocks/RecordListBase.vue b/client/web/compose/src/components/PageBlocks/RecordListBase.vue
index 11b441132..33d64d9da 100644
--- a/client/web/compose/src/components/PageBlocks/RecordListBase.vue
+++ b/client/web/compose/src/components/PageBlocks/RecordListBase.vue
@@ -971,7 +971,7 @@ export default {
} else {
// Record list block does not have any configured fields
// Use first five fields from the module.
- fields = this.recordListModule.fields.slice(0, 5)
+ fields = [...this.recordListModule.fields.slice(0, 5), ...this.recordListModule.systemFields()]
}
const configured = fields.map(mf => ({
@@ -1366,7 +1366,7 @@ export default {
}
const filter = []
- let sort = ''
+ let sort = 'createdAt DESC'
if (presort) {
sort = presort
diff --git a/client/web/compose/src/mixins/record.js b/client/web/compose/src/mixins/record.js
index ca7c4d1c6..14ff1def4 100644
--- a/client/web/compose/src/mixins/record.js
+++ b/client/web/compose/src/mixins/record.js
@@ -176,7 +176,7 @@ export default {
notPageID: this.page.pageID,
})
} else {
- this.$router.push({ name: route, params: { ...this.$route.params, recordID: this.record.recordID } })
+ this.$router.push({ name: route, params: { ...this.$route.params, recordID: this.record.recordID, edit: false } })
}
}
@@ -185,7 +185,6 @@ export default {
.catch(this.toastErrorHandler(this.$t(`notification:record.${isNew ? 'create' : 'update'}Failed`)))
.finally(() => {
this.processingSubmit = false
- this.processing = false
})
}, 500),
@@ -193,7 +192,7 @@ export default {
* Handle form submit for record browser
* @returns {Promise}
*/
- handleFormSubmitSimple: throttle(function (route = 'page.record') {
+ handleFormSubmitSimple: throttle(function (route = 'admin.modules.record.view') {
this.processingSubmit = true
this.processing = true
@@ -229,11 +228,10 @@ export default {
if (this.record.valueErrors.set) {
this.toastWarning(this.$t('notification:record.validationWarnings'))
} else {
- this.edit = false
this.record = record
this.initialRecordState = this.record.clone()
- this.$router.push({ name: route, params: { ...this.$route.params, recordID: record.recordID } })
+ this.$router.push({ name: route, params: { ...this.$route.params, recordID: record.recordID, edit: false } })
}
})
.catch(this.toastErrorHandler(this.$t(
@@ -257,19 +255,20 @@ export default {
return this
.dispatchUiEvent('beforeDelete')
- .then(this.$ComposeAPI.recordDelete(this.record))
- .then(() => {
- this.record.deletedAt = (new Date()).toISOString()
- })
+ .then(() => this.$ComposeAPI.recordDelete(this.record))
.then(this.dispatchUiEvent('afterDelete'))
.then(this.updatePrompts())
- .then(this.loadRecord)
- .then(this.toastSuccess(this.$t('notification:record.deleteSuccess')))
- .catch(this.toastErrorHandler(this.$t('notification:record.deleteFailed')))
- .finally(() => {
- this.processing = false
+ .then(() => {
+ this.record = undefined
+ this.initialRecordState = undefined
+
+ return this.refresh()
+ }).then(() => {
+ this.toastSuccess(this.$t('notification:record.deleteSuccess'))
+ }).finally(() => {
this.processingDelete = false
- })
+ this.processing = false
+ }).catch(this.toastErrorHandler(this.$t('notification:record.deleteFailed')))
}, 500),
handleUndelete: throttle(function () {
@@ -278,16 +277,20 @@ export default {
return this
.dispatchUiEvent('beforeUndelete')
- .then(this.$ComposeAPI.recordUndelete(this.record))
+ .then(() => this.$ComposeAPI.recordUndelete(this.record))
.then(this.dispatchUiEvent('afterUndelete'))
.then(this.updatePrompts())
- .then(this.loadRecord)
- .then(this.toastSuccess(this.$t('notification:record.restoreSuccess')))
- .catch(this.toastErrorHandler(this.$t('notification:record.restoreFailed')))
- .finally(() => {
+ .then(() => {
+ this.record = undefined
+ this.initialRecordState = undefined
+
+ return this.refresh()
+ }).then(() => {
+ this.toastSuccess(this.$t('notification:record.restoreSuccess'))
+ }).finally(() => {
this.processingUndelete = false
this.processing = false
- })
+ }).catch(this.toastErrorHandler(this.$t('notification:record.restoreFailed')))
}, 500),
handleBulkUpdateSelectedRecords: throttle(function (query) {
diff --git a/client/web/compose/src/views/Admin/Modules/Records/Create.vue b/client/web/compose/src/views/Admin/Modules/Records/Create.vue
deleted file mode 100644
index d07543249..000000000
--- a/client/web/compose/src/views/Admin/Modules/Records/Create.vue
+++ /dev/null
@@ -1,20 +0,0 @@
-
diff --git a/client/web/compose/src/views/Admin/Modules/Records/Edit.vue b/client/web/compose/src/views/Admin/Modules/Records/Edit.vue
deleted file mode 100644
index 4ead75430..000000000
--- a/client/web/compose/src/views/Admin/Modules/Records/Edit.vue
+++ /dev/null
@@ -1,20 +0,0 @@
-
diff --git a/client/web/compose/src/views/Admin/Modules/Records/List.vue b/client/web/compose/src/views/Admin/Modules/Records/List.vue
index 7a7325eca..a4c079b2e 100644
--- a/client/web/compose/src/views/Admin/Modules/Records/List.vue
+++ b/client/web/compose/src/views/Admin/Modules/Records/List.vue
@@ -29,7 +29,7 @@
{
- this.toastSuccess(this.$t('notification:module.columns.saved'))
- }).catch(this.toastErrorHandler(this.$t('notification:module.columns.saveFailed')))
+ this.updateModule(this.module)
},
setDefaultValues () {
this.block = undefined
- this.namespace = {}
},
},
}
diff --git a/client/web/compose/src/views/Admin/Modules/Records/View.vue b/client/web/compose/src/views/Admin/Modules/Records/View.vue
index 9d3fe4bba..b65dfe1ad 100644
--- a/client/web/compose/src/views/Admin/Modules/Records/View.vue
+++ b/client/web/compose/src/views/Admin/Modules/Records/View.vue
@@ -63,7 +63,7 @@
@@ -128,14 +128,37 @@ export default {
],
beforeRouteLeave (to, from, next) {
- this.checkUnsavedChanges(next, to)
+ next(this.checkUnsavedChanges())
},
beforeRouteUpdate (to, from, next) {
- this.checkUnsavedChanges(next, to)
+ next(this.checkUnsavedChanges())
},
props: {
+ namespace: {
+ type: Object,
+ required: false,
+ default: undefined,
+ },
+
+ moduleID: {
+ type: String,
+ required: false,
+ default: '',
+ },
+
+ recordID: {
+ type: String,
+ required: false,
+ default: '',
+ },
+
+ edit: {
+ type: Boolean,
+ default: false,
+ },
+
// If component was called (via router) with some pre-seed values
values: {
type: Object,
@@ -146,15 +169,9 @@ export default {
data () {
return {
- inEditing: false,
- inCreating: false,
-
blocks: [],
- bindParams: {
- page: new compose.Page(),
- namespace: this.$attrs.namespace,
- },
+ page: new compose.Page(),
recordNavigation: {
prev: undefined,
@@ -171,19 +188,19 @@ export default {
}),
isNew () {
- return this.record.recordID === NoID
+ return !this.recordID || this.recordID === NoID
},
title () {
const { name, handle } = this.module
- const titlePrefix = this.inCreating ? 'create' : this.inEditing ? 'edit' : 'view'
+ const titlePrefix = this.isNew ? 'create' : this.inEditing ? 'edit' : 'view'
return this.$t(`allRecords.${titlePrefix}.title`, { name: name || handle, interpolation: { escapeValue: false } })
},
module () {
- if (this.$attrs.moduleID) {
- return this.getModuleByID(this.$attrs.moduleID)
+ if (this.moduleID) {
+ return this.getModuleByID(this.moduleID)
} else {
return undefined
}
@@ -239,12 +256,20 @@ export default {
},
watch: {
- '$attrs.recordID': {
+ recordID: {
immediate: true,
handler () {
this.record = undefined
this.initialRecordState = undefined
- this.loadRecord()
+
+ this.refresh()
+ },
+ },
+
+ edit: {
+ immediate: true,
+ handler (edit) {
+ this.inEditing = edit
},
},
@@ -279,21 +304,25 @@ export default {
createBlocks () {
this.fields.forEach(f => {
const options = {
- moduleID: this.$attrs.moduleID,
+ moduleID: this.moduleID,
fields: f,
}
this.blocks.push(new compose.PageBlockRecord({ options }))
})
},
+ refresh () {
+ return this.loadRecord()
+ },
+
loadRecord () {
- const { moduleID = NoID, recordID = NoID } = this.$attrs
+ const { moduleID = NoID, recordID = NoID } = this
if (!moduleID || moduleID === NoID) return
const module = Object.freeze(this.getModuleByID(moduleID).clone())
if (recordID && recordID !== NoID) {
- const { namespaceID } = this.$attrs.namespace
+ const { namespaceID } = this.namespace
const { response, cancel } = this.$ComposeAPI
.recordReadCancellable({ namespaceID, moduleID, recordID })
@@ -312,8 +341,7 @@ export default {
})
} else {
this.record = new compose.Record(module, { values: this.values })
- this.inEditing = true
- this.inCreating = true
+ this.initialRecordState = undefined
}
},
@@ -322,21 +350,19 @@ export default {
},
handleAdd () {
- this.$router.push({ name: 'admin.modules.record.create', params: { moduleID: this.module.moduleID } })
+ this.$router.push({ name: 'admin.modules.record.create', params: { moduleID: this.module.moduleID, edit: true } })
},
handleClone () {
- this.$router.push({ name: 'admin.modules.record.create', params: { moduleID: this.module.moduleID, values: this.record.values } })
+ this.$router.push({ name: 'admin.modules.record.create', params: { moduleID: this.module.moduleID, values: this.record.values, edit: true } })
},
handleEdit () {
- this.inEditing = true
- this.inCreating = false
+ this.$router.push({ name: 'admin.modules.record.edit', params: { moduleID: this.module.moduleID, edit: true } })
},
handleView () {
- this.inEditing = false
- this.inCreating = false
+ this.$router.push({ name: 'admin.modules.record.view', params: { moduleID: this.module.moduleID, edit: false } })
},
handleRedirectToPrevOrNext (recordID) {
@@ -348,7 +374,6 @@ export default {
},
setDefaultValues () {
- this.inEditing = false
this.blocks = []
this.bindParams = {}
this.abortableRequests = []
@@ -360,15 +385,25 @@ export default {
})
},
- checkUnsavedChanges (next, to) {
- if (this.isNew) {
- next(true)
- } else {
- const recordValues = JSON.parse(JSON.stringify(this.record.values))
- const initialRecordState = JSON.parse(JSON.stringify(this.initialRecordState.values))
+ compareRecordValues () {
+ const recordValues = JSON.parse(JSON.stringify(this.record ? this.record.values : {}))
+ const initialRecordState = JSON.parse(JSON.stringify(this.initialRecordState ? this.initialRecordState.values : {}))
- next(!isEqual(recordValues, initialRecordState) ? window.confirm(this.$t('general:editor.unsavedChanges')) : true)
+ return !isEqual(recordValues, initialRecordState)
+ },
+
+ checkUnsavedChanges () {
+ if (!this.edit) return true
+
+ const recordStateChange = this.compareRecordValues() ? window.confirm(this.$t('general:record.unsavedChanges')) : true
+
+ if (!recordStateChange) {
+ this.processing = false
+ } else {
+ this.record = this.initialRecordState ? this.initialRecordState.clone() : undefined
}
+
+ return recordStateChange
},
},
}
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 db0ba8075..43ffd00c2 100644
--- a/client/web/compose/src/views/Public/Pages/Records/View.vue
+++ b/client/web/compose/src/views/Public/Pages/Records/View.vue
@@ -30,7 +30,6 @@
:blocks="blocks"
:mode="inEditing ? 'editor' : 'base'"
class="h-100"
- @reload="loadRecord()"
/>
{
return new Promise(resolve => setTimeout(resolve, 300)).then(() => {
- this.record = new compose.Record(module, record)
- this.initialRecordState = this.record.clone()
+ return new compose.Record(module, record)
})
})
.catch(e => {
@@ -421,8 +413,7 @@ export default {
}
}
- this.record = new compose.Record(module, { values: this.values })
- this.initialRecordState = this.record.clone()
+ return new compose.Record(module, { values: this.values })
}
}
},
@@ -433,7 +424,7 @@ export default {
* came from (and "where" is back).
*/
if (this.showRecordModal) {
- if (this.checkUnsavedChangesOnModal()) {
+ if (this.checkUnsavedChanges()) {
this.popModalPreviousPage().then(({ recordID, recordPageID, edit }) => {
this.$emit('on-modal-back', { recordID, recordPageID, pushModalPreviousPage: false, edit })
})
@@ -454,11 +445,11 @@ export default {
this.processing = true
if (this.showRecordModal) {
- if (this.checkUnsavedChangesOnModal()) {
+ if (this.checkUnsavedChanges()) {
this.$emit('handle-record-redirect', { recordID: NoID, recordPageID: this.page.pageID, edit: true })
}
} else {
- this.$router.push({ name: 'page.record.create', params: this.newRouteParams })
+ this.$router.push({ name: 'page.record.create', params: { pageID: this.page.pageID, edit: true } })
}
},
@@ -466,11 +457,11 @@ export default {
this.processing = true
if (this.showRecordModal) {
- if (this.checkUnsavedChangesOnModal()) {
+ if (this.checkUnsavedChanges()) {
this.$emit('handle-record-redirect', { recordID: NoID, recordPageID: this.page.pageID, values: this.record.values, edit: true })
}
} else {
- this.$router.push({ name: 'page.record.create', params: { pageID: this.page.pageID, values: this.record.values } })
+ this.$router.push({ name: 'page.record.create', params: { pageID: this.page.pageID, values: this.record.values, edit: true } })
}
},
@@ -480,7 +471,7 @@ export default {
if (this.showRecordModal) {
this.$emit('handle-record-redirect', { recordID: this.recordID, recordPageID: this.page.pageID, edit: true })
} else {
- this.$router.push({ name: 'page.record.edit', params: { recordID: this.recordID, pageID: this.page.pageID } })
+ this.$router.push({ name: 'page.record.edit', params: { recordID: this.recordID, pageID: this.page.pageID, edit: true } })
}
},
@@ -488,11 +479,11 @@ export default {
this.processing = true
if (this.showRecordModal) {
- if (this.checkUnsavedChangesOnModal()) {
+ if (this.checkUnsavedChanges()) {
this.$emit('handle-record-redirect', { recordID: this.recordID, recordPageID: this.page.pageID, edit: false })
}
} else {
- this.$router.push({ name: 'page.record', params: { recordID: this.recordID, pageID: this.page.pageID } })
+ this.$router.push({ name: 'page.record', params: { recordID: this.recordID, pageID: this.page.pageID, edit: false } })
}
},
@@ -502,7 +493,7 @@ export default {
this.processing = true
if (this.showRecordModal) {
- if (this.checkUnsavedChangesOnModal()) {
+ if (this.checkUnsavedChanges()) {
this.$emit('handle-record-redirect', { recordID, recordPageID: this.page.pageID })
}
} else {
@@ -549,7 +540,7 @@ export default {
})
},
- async determineLayout (pageLayoutID, variables = {}) {
+ async determineLayout (pageLayoutID, variables = {}, redirectOnFail = true) {
// Clear stored records so they can be refetched with latest values
this.clearRecordSet()
let expressions = {}
@@ -576,7 +567,10 @@ export default {
if (!matchedLayout) {
this.toastWarning(this.$t('notification:page.page-layout.notFound.view'))
- this.$router.go(-1)
+
+ if (redirectOnFail) {
+ this.$router.go(-1)
+ }
return
}
@@ -620,15 +614,21 @@ export default {
}
// Refetch the record and other page blocks that use records
- this.loadRecord()
+ this.loadRecord().then(record => {
+ this.record = record
+ this.initialRecordState = record.clone()
+ })
this.$root.$emit(`refetch-non-record-blocks:${this.page.pageID}`)
},
async refresh (variables = {}) {
this.processing = true
- return this.loadRecord().then(() => {
- return this.determineLayout(undefined, variables)
+ return this.loadRecord().then(record => {
+ return this.determineLayout(undefined, variables).then(() => {
+ this.record = record
+ this.initialRecordState = record.clone()
+ })
}).finally(() => {
this.processing = false
})
@@ -638,7 +638,7 @@ export default {
if (kind === 'toLayout') {
this.processing = true
- this.determineLayout(params.pageLayoutID).finally(() => {
+ this.determineLayout(params.pageLayoutID, {}, false).finally(() => {
this.processing = false
})
} else if (kind === 'toURL') {
@@ -666,7 +666,7 @@ export default {
this.$root.$off('refetch-record-blocks', this.refetchRecordBlocks)
if (this.showRecordModal) {
- this.$root.$off('bv::modal::hide', this.checkUnsavedChangesOnModal)
+ this.$root.$off('bv::modal::hide', this.checkUnsavedChanges)
}
},
@@ -677,26 +677,19 @@ export default {
return !isEqual(recordValues, initialRecordState)
},
- checkUnsavedChanges (next) {
- if (!this.edit) {
- next(true)
- return
- }
-
- next(this.compareRecordValues() ? window.confirm(this.$t('general:module.unsavedChanges')) : true)
- },
-
- checkUnsavedChangesOnModal (bvEvent, modalId) {
+ checkUnsavedChanges (bvEvent, modalId) {
if ((bvEvent && modalId !== 'record-modal') || !this.edit) return true
- const recordStateChange = this.compareRecordValues() ? window.confirm(this.$t('general:module.unsavedChanges')) : true
+ const recordStateChange = this.compareRecordValues() ? window.confirm(this.$t('general:record.unsavedChanges')) : true
- if (bvEvent && !recordStateChange) {
- bvEvent.preventDefault()
- }
+ if (!recordStateChange) {
+ this.processing = false
- if (recordStateChange) {
- this.initialRecordState = this.record.clone()
+ if (bvEvent) {
+ bvEvent.preventDefault()
+ }
+ } else {
+ this.record = this.initialRecordState ? this.initialRecordState.clone() : undefined
}
return recordStateChange
diff --git a/client/web/compose/src/views/routes.js b/client/web/compose/src/views/routes.js
index 7df4ec720..506e44a9e 100644
--- a/client/web/compose/src/views/routes.js
+++ b/client/web/compose/src/views/routes.js
@@ -39,8 +39,8 @@ export default [
...r('page', ':pageID?', 'Public/Pages/View'),
children: [
- r('page.record.edit', 'record/:recordID/edit', 'Public/Pages/Records/View', { edit: true }),
r('page.record', 'record/:recordID', 'Public/Pages/Records/View', { edit: false }),
+ r('page.record.edit', 'record/:recordID/edit', 'Public/Pages/Records/View', { edit: true }),
r('page.record.create', 'record', 'Public/Pages/Records/View', { edit: true }),
],
},
@@ -55,9 +55,9 @@ export default [
r('admin.modules.create', 'modules/new', 'Admin/Modules/Edit'),
r('admin.modules.edit', 'modules/:moduleID/edit', 'Admin/Modules/Edit'),
r('admin.modules.record.list', 'modules/:moduleID/record/list', 'Admin/Modules/Records/List'),
- r('admin.modules.record.view', 'modules/:moduleID/record/:recordID', 'Admin/Modules/Records/View'),
- r('admin.modules.record.create', 'modules/:moduleID/record', 'Admin/Modules/Records/Create'),
- r('admin.modules.record.edit', 'modules/:moduleID/record/:recordID/edit', 'Admin/Modules/Records/Edit'),
+ r('admin.modules.record.view', 'modules/:moduleID/record/:recordID', 'Admin/Modules/Records/View', { edit: false }),
+ r('admin.modules.record.create', 'modules/:moduleID/record', 'Admin/Modules/Records/View', { edit: true }),
+ r('admin.modules.record.edit', 'modules/:moduleID/record/:recordID/edit', 'Admin/Modules/Records/View', { edit: true }),
r('admin.pages', 'pages', 'Admin/Pages/List'),
r('admin.pages.edit', 'pages/:pageID/edit', 'Admin/Pages/Edit'),
diff --git a/lib/js/src/compose/types/module.ts b/lib/js/src/compose/types/module.ts
index d129e99a0..633a6170b 100644
--- a/lib/js/src/compose/types/module.ts
+++ b/lib/js/src/compose/types/module.ts
@@ -87,12 +87,12 @@ interface RecordDeDupRule {
*/
export const systemFields = Object.freeze([
{ isSystem: true, name: 'recordID', label: 'Record ID', kind: 'String' },
- { isSystem: true, name: 'revision', label: 'Revision', kind: 'Number' },
{ isSystem: true, name: 'ownedBy', label: 'Owned by', kind: 'User' },
{ isSystem: true, name: 'createdBy', label: 'Created by', kind: 'User' },
{ isSystem: true, name: 'createdAt', label: 'Created at', kind: 'DateTime' },
{ isSystem: true, name: 'updatedBy', label: 'Updated by', kind: 'User' },
{ isSystem: true, name: 'updatedAt', label: 'Updated at', kind: 'DateTime' },
+ { isSystem: true, name: 'revision', label: 'Revision', kind: 'Number' },
{ isSystem: true, name: 'deletedBy', label: 'Deleted by', kind: 'User' },
{ isSystem: true, name: 'deletedAt', label: 'Deleted at', kind: 'DateTime' },
].map(f => ModuleFieldMaker(f)))
diff --git a/locale/en/corteza-webapp-admin/general.yaml b/locale/en/corteza-webapp-admin/general.yaml
index 9dde0b406..24adff57a 100644
--- a/locale/en/corteza-webapp-admin/general.yaml
+++ b/locale/en/corteza-webapp-admin/general.yaml
@@ -54,6 +54,7 @@ label:
fileTypeNotAllowed: File type not allowed
editor:
unsavedChanges: Unsaved changes will be lost. Do you wish to leave the page?
+record:
unsavedChanges: Unsaved changes will be lost. Do you wish to close the record?
resourceList:
pagination: