Add page layout reordering and roles check
This commit is contained in:
@@ -194,7 +194,7 @@ export default {
|
||||
const reorder = () => {
|
||||
const pageIDs = afterParent.children.map(p => p.pageID)
|
||||
if (pageIDs.length) {
|
||||
this.$ComposeAPI.pageReorder({ namespaceID, selfID: afterID, pageIDs: pageIDs }).then(() => {
|
||||
this.$ComposeAPI.pageReorder({ namespaceID, selfID: afterID, pageIDs }).then(() => {
|
||||
return this.$store.dispatch('page/load', { namespaceID, clear: true, force: true })
|
||||
}).then(() => {
|
||||
this.toastSuccess(this.$t('reordered'))
|
||||
|
||||
@@ -9,57 +9,57 @@
|
||||
>
|
||||
<b-spinner />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
class="w-100 h-100"
|
||||
@mouseover="disableMap"
|
||||
@mouseleave="enableMap"
|
||||
>
|
||||
<l-map
|
||||
v-if="map"
|
||||
ref="map"
|
||||
:zoom="map.zoom"
|
||||
:center="map.center"
|
||||
:min-zoom="map.zoomMin"
|
||||
:max-zoom="map.zoomMax"
|
||||
:bounds="map.bounds"
|
||||
:max-bounds="map.bounds"
|
||||
class="w-100 h-100"
|
||||
@locationfound="onLocationFound"
|
||||
>
|
||||
<l-tile-layer
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
:attribution="map.attribution"
|
||||
/>
|
||||
<l-polygon
|
||||
v-for="(geometry, i) in geometries"
|
||||
:key="`polygon-${i}`"
|
||||
:lat-lngs="geometry.map(value => value.geometry)"
|
||||
:color="colors[i]"
|
||||
/>
|
||||
|
||||
<l-marker
|
||||
v-for="(marker, i) in localValue"
|
||||
:key="`marker-${i}`"
|
||||
:lat-lng="marker.value"
|
||||
:icon="getIcon(marker)"
|
||||
/>
|
||||
<l-control class="leaflet-bar">
|
||||
<a
|
||||
:title="$t('geometry.tooltip.goToCurrentLocation')"
|
||||
role="button"
|
||||
class="d-flex justify-content-center align-items-center"
|
||||
@click="goToCurrentLocation"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'location-arrow']"
|
||||
class="text-primary"
|
||||
/>
|
||||
</a>
|
||||
</l-control>
|
||||
</l-map>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
v-else
|
||||
class="w-100 h-100"
|
||||
@mouseover="disableMap"
|
||||
@mouseleave="enableMap"
|
||||
>
|
||||
<l-map
|
||||
v-if="map"
|
||||
ref="map"
|
||||
:zoom="map.zoom"
|
||||
:center="map.center"
|
||||
:min-zoom="map.zoomMin"
|
||||
:max-zoom="map.zoomMax"
|
||||
:bounds="map.bounds"
|
||||
:max-bounds="map.bounds"
|
||||
class="w-100 h-100"
|
||||
@locationfound="onLocationFound"
|
||||
>
|
||||
<l-tile-layer
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
:attribution="map.attribution"
|
||||
/>
|
||||
<l-polygon
|
||||
v-for="(geometry, i) in geometries"
|
||||
:key="`polygon-${i}`"
|
||||
:lat-lngs="geometry.map(value => value.geometry)"
|
||||
:color="colors[i]"
|
||||
/>
|
||||
|
||||
<l-marker
|
||||
v-for="(marker, i) in localValue"
|
||||
:key="`marker-${i}`"
|
||||
:lat-lng="marker.value"
|
||||
:icon="getIcon(marker)"
|
||||
/>
|
||||
<l-control class="leaflet-bar">
|
||||
<a
|
||||
:title="$t('geometry.tooltip.goToCurrentLocation')"
|
||||
role="button"
|
||||
class="d-flex justify-content-center align-items-center"
|
||||
@click="goToCurrentLocation"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'location-arrow']"
|
||||
class="text-primary"
|
||||
/>
|
||||
</a>
|
||||
</l-control>
|
||||
</l-map>
|
||||
</div>
|
||||
</wrap>
|
||||
</template>
|
||||
|
||||
@@ -125,14 +125,18 @@ export default {
|
||||
this.loadEvents()
|
||||
},
|
||||
},
|
||||
|
||||
options: {
|
||||
deep: true,
|
||||
handler () {
|
||||
this.loadEvents()
|
||||
},
|
||||
},
|
||||
boundingRect () {
|
||||
this.loadEvents()
|
||||
|
||||
boundingRect: {
|
||||
handler () {
|
||||
this.loadEvents()
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -211,6 +215,10 @@ export default {
|
||||
})
|
||||
})).finally(() => {
|
||||
this.processing = false
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.map.mapObject.invalidateSize()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<grid
|
||||
v-if="layout"
|
||||
:key="layout.layoutID"
|
||||
:blocks="blocks"
|
||||
:editable="false"
|
||||
>
|
||||
@@ -11,7 +9,7 @@
|
||||
<page-block
|
||||
v-bind="{ ...$attrs }"
|
||||
:page="page"
|
||||
:blocks="page.blocks"
|
||||
:blocks="blocks"
|
||||
:block="block"
|
||||
:bounding-rect="boundingRect"
|
||||
:block-index="index"
|
||||
@@ -22,7 +20,6 @@
|
||||
</grid>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Grid from '../../Common/Grid'
|
||||
import PageBlock from '../../PageBlocks'
|
||||
import { compose } from '@cortezaproject/corteza-js'
|
||||
@@ -40,43 +37,10 @@ export default {
|
||||
type: compose.Page,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
layouts: [],
|
||||
layout: undefined,
|
||||
|
||||
blocks: [],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getPageLayouts: 'pageLayout/getByPageID',
|
||||
}),
|
||||
},
|
||||
|
||||
watch: {
|
||||
'page.pageID': {
|
||||
immediate: true,
|
||||
handler (pageID) {
|
||||
this.layouts = this.getPageLayouts(pageID)
|
||||
const { layoutID } = this.$route.query
|
||||
|
||||
if (layoutID) {
|
||||
this.layout = this.layouts.find(({ pageLayoutID }) => pageLayoutID === layoutID)
|
||||
} else {
|
||||
this.layout = this.layouts[0]
|
||||
this.$router.replace({ ...this.$route, query: { ...this.$route.query, layoutID: this.layout.pageLayoutID } })
|
||||
}
|
||||
|
||||
this.blocks = (this.layout || {}).blocks.map(({ blockID, xywh }) => {
|
||||
const block = this.page.blocks.find(b => b.blockID === blockID)
|
||||
block.xywh = xywh
|
||||
return block
|
||||
})
|
||||
},
|
||||
blocks: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function (ComposeAPI) {
|
||||
},
|
||||
|
||||
getByPageID (state) {
|
||||
return (ID) => state.set.filter(({ pageID }) => ID === pageID)
|
||||
return (ID) => state.set.filter(({ pageID }) => ID === pageID).sort((a, b) => a.weight - b.weight)
|
||||
},
|
||||
|
||||
set (state) {
|
||||
@@ -55,7 +55,7 @@ export default function (ComposeAPI) {
|
||||
|
||||
commit(types.loading)
|
||||
commit(types.pending)
|
||||
return ComposeAPI.pageLayoutListNamespace({ namespaceID }).then(({ set, filter }) => {
|
||||
return ComposeAPI.pageLayoutListNamespace({ namespaceID, sort: 'weight ASC' }).then(({ set, filter }) => {
|
||||
if (set && set.length > 0) {
|
||||
commit(types.updateSet, set.map(pl => new compose.PageLayout(pl)))
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export default function (ComposeAPI) {
|
||||
}
|
||||
|
||||
commit(types.pending)
|
||||
return ComposeAPI.pageLayoutList({ namespaceID, pageID }).then(({ set }) => {
|
||||
return ComposeAPI.pageLayoutList({ namespaceID, pageID, sort: 'weight ASC' }).then(({ set }) => {
|
||||
commit(types.updateSet, set.map(pl => new compose.PageLayout(pl)))
|
||||
return set
|
||||
}).finally(() => {
|
||||
|
||||
@@ -329,6 +329,7 @@
|
||||
v-model="currentLayoutRoles"
|
||||
:options="roles.options"
|
||||
:loading="roles.processing"
|
||||
placeholder="Pick roles that the layout will be shown to"
|
||||
:get-option-label="role => role.name"
|
||||
:reduce="role => role.roleID"
|
||||
:selectable="role => !currentLayoutRoles.includes(role.roleID)"
|
||||
@@ -440,6 +441,7 @@
|
||||
:hide-delete="hideDelete"
|
||||
:hide-save="!page.canUpdatePage"
|
||||
:disable-save="disableSave"
|
||||
:processing="processing"
|
||||
@clone="handleClone()"
|
||||
@delete="handleDeletePage"
|
||||
@save="handleSave()"
|
||||
@@ -516,6 +518,8 @@ export default {
|
||||
|
||||
data () {
|
||||
return {
|
||||
processing: false,
|
||||
|
||||
page: new compose.Page(),
|
||||
|
||||
showIconModal: false,
|
||||
@@ -523,7 +527,6 @@ export default {
|
||||
selectedAttachmentID: '',
|
||||
linkUrl: '',
|
||||
|
||||
processing: false,
|
||||
layouts: [],
|
||||
|
||||
layoutEditor: {
|
||||
@@ -634,13 +637,16 @@ export default {
|
||||
this.deletedLayouts = new Set()
|
||||
|
||||
if (pageID) {
|
||||
this.processing = true
|
||||
|
||||
const { namespaceID } = this.namespace
|
||||
this.findPageByID({ namespaceID, pageID }).then((page) => {
|
||||
this.page = page.clone()
|
||||
return this.fetchAttachments()
|
||||
}).catch(this.toastErrorHandler(this.$t('notification:page.loadFailed')))
|
||||
|
||||
this.fetchLayouts().catch(this.toastErrorHandler(this.$t('notification:page.loadFailed')))
|
||||
}).then(this.fetchLayouts)
|
||||
.finally(() => {
|
||||
this.processing = false
|
||||
}).catch(this.toastErrorHandler(this.$t('notification:page.loadFailed')))
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -718,27 +724,43 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
async handlePageLayoutReorder () {
|
||||
const { namespaceID } = this.namespace
|
||||
const pageIDs = this.layouts.map(({ pageLayoutID }) => pageLayoutID)
|
||||
|
||||
return this.$ComposeAPI.pageLayoutReorder({ namespaceID, pageID: this.pageID, pageIDs }).then(() => {
|
||||
return this.$store.dispatch('pageLayout/load', { namespaceID, clear: true, force: true })
|
||||
})
|
||||
},
|
||||
|
||||
handleSave ({ closeOnSuccess = false } = {}) {
|
||||
this.processing = true
|
||||
|
||||
/**
|
||||
* Pass a special tag alongside payload that
|
||||
* instructs store layer to add content-language header to the API request
|
||||
*/
|
||||
const resourceTranslationLanguage = this.currentLanguage
|
||||
const { namespaceID } = this.namespace
|
||||
|
||||
return this.saveIcon().then(icon => {
|
||||
this.page.config.navItem.icon = icon
|
||||
return this.updatePage({ namespaceID, ...this.page, resourceTranslationLanguage }).then((page) => {
|
||||
this.page = page.clone()
|
||||
return this.handleSaveLayouts().then(this.fetchLayouts)
|
||||
})
|
||||
}).then(() => {
|
||||
this.deletedLayouts = new Set()
|
||||
return this.updatePage({ namespaceID, ...this.page, resourceTranslationLanguage })
|
||||
}).then(page => {
|
||||
this.page = page.clone()
|
||||
return this.handleSaveLayouts()
|
||||
}).then(this.handlePageLayoutReorder)
|
||||
.then(() => {
|
||||
this.fetchLayouts()
|
||||
this.deletedLayouts = new Set()
|
||||
|
||||
this.toastSuccess(this.$t('notification:page.saved'))
|
||||
if (closeOnSuccess) {
|
||||
this.$router.push({ name: 'admin.pages' })
|
||||
}
|
||||
}).catch(this.toastErrorHandler(this.$t('notification:page.saveFailed')))
|
||||
this.toastSuccess(this.$t('notification:page.saved'))
|
||||
if (closeOnSuccess) {
|
||||
this.$router.push({ name: 'admin.pages' })
|
||||
}
|
||||
}).finally(() => {
|
||||
this.processing = false
|
||||
}).catch(this.toastErrorHandler(this.$t('notification:page.saveFailed')))
|
||||
},
|
||||
|
||||
handleDeletePage (strategy = 'abort') {
|
||||
@@ -752,8 +774,6 @@ export default {
|
||||
},
|
||||
|
||||
async fetchAttachments () {
|
||||
this.processing = true
|
||||
|
||||
return this.$ComposeAPI.iconList({ sort: 'id DESC' })
|
||||
.then(({ set: attachments = [] }) => {
|
||||
const baseURL = this.$ComposeAPI.baseURL
|
||||
@@ -767,9 +787,6 @@ export default {
|
||||
}
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:page.iconFetchFailed')))
|
||||
.finally(() => {
|
||||
this.processing = false
|
||||
})
|
||||
},
|
||||
|
||||
async saveIcon () {
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
:namespace="namespace"
|
||||
:module="module"
|
||||
:page="page"
|
||||
:blocks="blocks"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -112,9 +113,19 @@ export default {
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
layouts: [],
|
||||
layout: undefined,
|
||||
|
||||
blocks: [],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
recordPaginationUsable: 'ui/recordPaginationUsable',
|
||||
getPageLayouts: 'pageLayout/getByPageID',
|
||||
}),
|
||||
|
||||
isRecordCreatePage () {
|
||||
@@ -141,7 +152,8 @@ export default {
|
||||
pageTitle () {
|
||||
if (this.page.pageID !== NoID) {
|
||||
const { title = '', handle = '' } = this.page
|
||||
return title || handle || this.$t('navigation:noPageTitle')
|
||||
const { meta = {} } = this.layout || {}
|
||||
return meta.title || title || handle || this.$t('navigation:noPageTitle')
|
||||
}
|
||||
|
||||
return ''
|
||||
@@ -160,19 +172,37 @@ export default {
|
||||
'page.title': {
|
||||
immediate: true,
|
||||
handler (title) {
|
||||
document.title = [title, this.namespace.name, this.$t('general:label.app-name.public')].filter(v => v).join(' | ')
|
||||
},
|
||||
},
|
||||
|
||||
'page.pageID': {
|
||||
immediate: true,
|
||||
handler () {
|
||||
handler (pageID) {
|
||||
// If the page changed we need to clear the record pagination since its not relevant anymore
|
||||
if (this.recordPaginationUsable) {
|
||||
this.setRecordPaginationUsable(false)
|
||||
} else {
|
||||
this.clearRecordIDs()
|
||||
}
|
||||
|
||||
this.layouts = this.getPageLayouts(pageID)
|
||||
this.layout = this.layouts.find(l => {
|
||||
const { roles = [] } = l.config.visibility
|
||||
|
||||
if (!roles.length) return true
|
||||
|
||||
return this.$auth.user.roles.some(roleID => roles.includes(roleID))
|
||||
})
|
||||
|
||||
const { meta = {} } = this.layout || {}
|
||||
const title = meta.title || this.page.title
|
||||
document.title = [title, this.namespace.name, this.$t('general:label.app-name.public')].filter(v => v).join(' | ')
|
||||
|
||||
this.blocks = (this.layout || {}).blocks.map(({ blockID, xywh }) => {
|
||||
const block = this.page.blocks.find(b => b.blockID === blockID)
|
||||
block.xywh = xywh
|
||||
return block
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user