3
0

Fix module all records url to reflect edit state

This commit is contained in:
Jože Fortun
2024-05-13 01:41:08 +02:00
parent e766b4be2c
commit 60376b71c2
14 changed files with 178 additions and 168 deletions
@@ -57,7 +57,7 @@
<slot name="end-actions" />
<c-input-confirm
v-if="isCreated && !(isDeleted || hideDelete || settings.hideDelete)"
v-if="(processingDelete || isCreated) && !(isDeleted || hideDelete || settings.hideDelete)"
:disabled="!record || !canDeleteRecord || processing"
:processing="processingDelete"
:text="labels.delete || $t('label.delete')"
@@ -68,7 +68,7 @@
/>
<c-input-confirm
v-if="isDeleted && !(hideDelete || settings.hideDelete)"
v-else-if="(processingUndelete || isDeleted) && !(hideDelete || settings.hideDelete)"
:disabled="!record || !canUndeleteRecord || processing"
:processing="processingUndelete"
:text="$t('label.restore')"
@@ -27,6 +27,10 @@ export default {
v = this.record ? this.record.values[this.field.name] : undefined
if (this.field.isMulti) {
if (!Array.isArray(v)) {
v = []
}
return v.map(v => findLabel(v, this.field.options.options) || v)
} else {
return findLabel(v, this.field.options.options) || v
@@ -193,7 +193,9 @@ export default {
...resolutions,
this.evaluateExpressions(),
]).finally(() => {
this.evaluating = false
setTimeout(() => {
this.evaluating = false
}, 300)
})
if (this.options.referenceModuleID) {
@@ -170,7 +170,9 @@ export default {
...resolutions,
this.evaluateExpressions(),
]).finally(() => {
this.evaluating = false
setTimeout(() => {
this.evaluating = false
}, 300)
})
},
},
@@ -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
+24 -21
View File
@@ -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<void>}
*/
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) {
@@ -1,20 +0,0 @@
<script>
import ViewRecord from './View'
export default {
i18nOptions: {
namespaces: 'module',
},
name: 'CreateRecord',
extends: ViewRecord,
data () {
return {
inEditing: true,
inCreating: true,
}
},
}
</script>
@@ -1,20 +0,0 @@
<script>
import ViewRecord from './View'
export default {
i18nOptions: {
namespaces: 'module',
},
name: 'EditRecord',
extends: ViewRecord,
data () {
return {
inEditing: true,
inCreating: false,
}
},
}
</script>
@@ -29,7 +29,7 @@
</portal>
<record-list-base
v-if="block && page"
v-if="block && page && module"
:block="block"
:page="page"
:module="module"
@@ -56,10 +56,23 @@ export default {
RecordListBase,
},
props: {
namespace: {
type: Object,
required: false,
default: undefined,
},
moduleID: {
type: String,
required: false,
default: '',
},
},
data () {
return {
block: undefined,
namespace: this.$attrs.namespace,
}
},
@@ -75,8 +88,8 @@ export default {
},
module () {
if (this.$route.params.moduleID) {
return this.getModuleByID(this.$route.params.moduleID)
if (this.moduleID) {
return this.getModuleByID(this.moduleID)
} else {
return undefined
}
@@ -102,13 +115,13 @@ export default {
},
watch: {
module: {
handler (module) {
if (module) {
const { meta = { ui: {} }, moduleID } = module || {}
moduleID: {
handler () {
if (this.module) {
const { meta = { ui: {} }, moduleID } = this.module || {}
let fields = ((meta.ui || {}).admin || {}).fields || []
fields = fields.length ? fields : module.fields
fields = fields.length ? fields : [...this.module.fields.slice(0, 10), ...this.module.systemFields()]
this.block.options.moduleID = moduleID
this.block.options.fields = fields
@@ -121,7 +134,7 @@ export default {
const { meta = { ui: {} }, moduleID } = this.module || {}
let fields = ((meta.ui || {}).admin || {}).fields || []
fields = fields.length ? fields : this.module.fields
fields = fields.length ? fields : [...this.module.fields.slice(0, 10), ...this.module.systemFields()]
// Init block
const block = new compose.PageBlockRecordList({
@@ -185,14 +198,11 @@ export default {
this.module.meta.ui.admin = { ...(this.module.meta.ui.admin || {}), fields }
}
this.updateModule(this.module).then(() => {
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 = {}
},
},
}
@@ -63,7 +63,7 @@
<component
:is="getRecordComponent"
:errors="errors"
v-bind="{ ...bindParams, module, block, record }"
v-bind="{ namespace, page, module, block, record }"
class="p-2"
/>
</b-col>
@@ -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
},
},
}
@@ -30,7 +30,6 @@
:blocks="blocks"
:mode="inEditing ? 'editor' : 'base'"
class="h-100"
@reload="loadRecord()"
/>
<portal
@@ -139,7 +138,7 @@ export default {
],
beforeRouteLeave (to, from, next) {
this.checkUnsavedChanges(next, to)
next(this.checkUnsavedChanges())
},
beforeRouteUpdate (to, from, next) {
@@ -152,7 +151,7 @@ export default {
return
}
this.checkUnsavedChanges(next, to)
next(this.checkUnsavedChanges())
},
props: {
@@ -242,12 +241,6 @@ export default {
return this.showRecordModal ? 'record-modal-footer' : 'toolbar'
},
newRouteParams () {
// Remove recordID and values from route params
const { recordID, values, ...params } = this.$route.params
return params
},
getUiEventResourceType () {
return 'record-page'
},
@@ -295,7 +288,7 @@ export default {
const { name, handle } = this.module
const titlePrefix = this.isNew ? 'create' : this.edit ? 'edit' : 'view'
const titlePrefix = this.isNew ? 'create' : this.inEditing ? 'edit' : 'view'
return this.$t(`page:public.record.${titlePrefix}.title`, { name: name || handle, interpolation: { escapeValue: false } })
},
@@ -365,7 +358,7 @@ export default {
this.$root.$on('refetch-record-blocks', this.refetchRecordBlocks)
if (this.showRecordModal) {
this.$root.$on('bv::modal::hide', this.checkUnsavedChangesOnModal)
this.$root.$on('bv::modal::hide', this.checkUnsavedChanges)
}
},
@@ -401,8 +394,7 @@ export default {
return response()
.then(record => {
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
+4 -4
View File
@@ -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'),
+1 -1
View File
@@ -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)))
@@ -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: