Add expression evaluation to page layouts

This commit is contained in:
Jože Fortun
2023-04-07 12:39:27 +02:00
parent 5aaf90f40d
commit a8aa759408
4 changed files with 112 additions and 23 deletions
+2
View File
@@ -3,7 +3,9 @@ import Vue from 'vue'
import toast from './toast'
import resourceTranslations from './resource-translations'
import vueSelectPosition from './vue-select-position'
import uiHelpers from './uiHelpers'
Vue.mixin(toast)
Vue.mixin(resourceTranslations)
Vue.mixin(vueSelectPosition)
Vue.mixin(uiHelpers)
@@ -0,0 +1,24 @@
export default {
methods: {
getBreakpoint () {
let breakpoint
const width = window.innerWidth
if (width < 576) {
breakpoint = 'xs'
} else if (width >= 576 && width < 768) {
breakpoint = 'sm'
} else if (width >= 768 && width < 992) {
breakpoint = 'md'
} else if (width >= 992 && width < 1200) {
breakpoint = 'lg'
} else if (width >= 1200 && width < 1400) {
breakpoint = 'xl'
} else {
breakpoint = 'xxl'
}
return breakpoint
},
},
}
@@ -133,8 +133,7 @@ export default {
layouts: [],
layout: undefined,
layoutButtons: {},
layoutButtons: new Set(),
blocks: [],
}
},
@@ -166,7 +165,7 @@ export default {
recordToolbarLabels () {
// Use an intermediate object so we can reflect all changes in one go;
const aux = {}
const { config = {} } = this.layout
const { config = {} } = this.layout || {}
const { buttons = {} } = config
Object.entries(buttons).forEach(([key, { label = '' }]) => {
@@ -176,7 +175,7 @@ export default {
},
layoutActions () {
const { config = {} } = this.layout
const { config = {} } = this.layout || {}
const { actions = [] } = config
return actions
@@ -208,6 +207,9 @@ export default {
immediate: true,
handler () {
this.layouts = this.getPageLayouts(this.page.pageID)
this.layout = undefined
this.blocks = []
this.determineLayout()
},
},
@@ -317,12 +319,43 @@ export default {
}
},
determineLayout (pageLayoutID) {
this.layout = this.layouts.find(l => {
const { roles = [] } = l.config.visibility
evaluateLayoutExpressions () {
const expressions = {}
const variables = {
user: this.$auth.user,
record: this.record || {},
screen: {
width: window.innerWidth,
height: window.innerHeight,
userAgent: navigator.userAgent,
breakpoint: this.getBreakpoint(), // This is from a global mixin uiHelpers
},
layout: this.layout || {},
}
this.layouts.forEach(({ pageLayoutID, config }) => {
if (!config.visibility.expression) return
expressions[pageLayoutID] = config.visibility.expression
})
return this.$SystemAPI.expressionEvaluate({ variables, expressions }).catch(() => {
Object.keys(expressions).forEach(key => (expressions[key] = false))
return expressions
})
},
async determineLayout (pageLayoutID) {
const expressions = await this.evaluateLayoutExpressions()
// Check layouts for expressions/roles and find the first one that fits
this.layout = this.layouts.find(l => {
if (pageLayoutID && l.pageLayoutID !== pageLayoutID) return
const { expression, roles = [] } = l.config.visibility
if (expression && !expressions[l.pageLayoutID]) return false
if (!roles.length) return true
return this.$auth.user.roles.some(roleID => roles.includes(roleID))
@@ -50,7 +50,7 @@
class="flex-grow-1 overflow-auto d-flex px-2 w-100"
>
<router-view
v-if="recordID || isRecordCreatePage"
v-if="isRecordPage"
:namespace="namespace"
:module="module"
:page="page"
@@ -117,7 +117,6 @@ export default {
return {
layouts: [],
layout: undefined,
blocks: [],
}
},
@@ -128,8 +127,8 @@ export default {
getPageLayouts: 'pageLayout/getByPageID',
}),
isRecordCreatePage () {
return this.$route.name === 'page.record.create'
isRecordPage () {
return this.recordID || this.$route.name === 'page.record.create'
},
module () {
@@ -150,9 +149,9 @@ export default {
},
pageTitle () {
if (this.page.pageID !== NoID) {
if (this.page.pageID !== NoID && this.layout) {
const { title = '', handle = '' } = this.page
const { meta = {} } = this.layout || {}
const { meta = {} } = this.layout
return meta.title || title || handle || this.$t('navigation:noPageTitle')
}
@@ -169,16 +168,16 @@ export default {
},
watch: {
'page.title': {
immediate: true,
handler (title) {
},
},
'page.pageID': {
immediate: true,
handler () {
this.determineLayout()
this.layouts = []
this.layout = undefined
this.blocks = []
if (!this.isRecordPage) {
this.determineLayout()
}
// If the page changed we need to clear the record pagination since its not relevant anymore
if (this.recordPaginationUsable) {
@@ -208,10 +207,41 @@ export default {
clearRecordIDs: 'ui/clearRecordIDs',
}),
determineLayout () {
evaluateLayoutExpressions () {
const expressions = {}
const variables = {
screen: {
width: window.innerWidth,
height: window.innerHeight,
userAgent: navigator.userAgent,
breakpoint: this.getBreakpoint(), // This is from a global mixin uiHelpers
},
user: this.$auth.user,
layout: this.layout || {},
}
this.layouts.forEach(({ pageLayoutID, config }) => {
if (!config.visibility.expression) return
expressions[pageLayoutID] = config.visibility.expression
})
return this.$SystemAPI.expressionEvaluate({ variables, expressions }).catch(() => {
Object.keys(expressions).forEach(key => (expressions[key] = false))
return expressions
})
},
async determineLayout () {
this.layouts = this.getPageLayouts(this.page.pageID)
this.layout = this.layouts.find(l => {
const { roles = [] } = l.config.visibility
const expressions = await this.evaluateLayoutExpressions()
// Check layouts for expressions/roles and find the first one that fits
this.layout = this.layouts.find(({ pageLayoutID, config = {} }) => {
const { expression, roles = [] } = config.visibility
if (expression && !expressions[pageLayoutID]) return false
if (!roles.length) return true