Add page layout record toolbar and actions
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
class="d-flex align-items-center justify-content-start"
|
||||
>
|
||||
<b-button
|
||||
v-if="!settings.hideBack"
|
||||
v-if="!(hideBack || settings.hideBack)"
|
||||
data-test-id="button-back"
|
||||
variant="link"
|
||||
class="text-dark back"
|
||||
@@ -64,7 +64,7 @@
|
||||
v-if="module"
|
||||
>
|
||||
<c-input-confirm
|
||||
v-if="(isCreated && !settings.hideDelete && !isDeleted)"
|
||||
v-if="isCreated && !(isDeleted || hideDelete || settings.hideDelete)"
|
||||
:disabled="!canDeleteRecord"
|
||||
size="lg"
|
||||
size-confirm="lg"
|
||||
@@ -105,7 +105,7 @@
|
||||
</c-input-confirm>
|
||||
|
||||
<b-button
|
||||
v-if="!inEditing && module.canCreateRecord && !hideClone && isCreated && !settings.hideClone"
|
||||
v-if="!inEditing && isCreated && module.canCreateRecord && !(hideClone || settings.hideClone)"
|
||||
data-test-id="button-clone"
|
||||
variant="light"
|
||||
size="lg"
|
||||
@@ -117,7 +117,7 @@
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
v-if="!inEditing && !settings.hideEdit && isCreated"
|
||||
v-if="!inEditing && isCreated && !(hideEdit || settings.hideEdit)"
|
||||
data-test-id="button-edit"
|
||||
:disabled="!record.canUpdateRecord || processing"
|
||||
variant="light"
|
||||
@@ -129,7 +129,7 @@
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
v-if="module.canCreateRecord && !hideAdd && !inEditing && !settings.hideNew"
|
||||
v-if="!inEditing && module.canCreateRecord && !(hideNew || settings.hideNew)"
|
||||
data-test-id="button-add-new"
|
||||
variant="primary"
|
||||
size="lg"
|
||||
@@ -141,7 +141,7 @@
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
v-if="inEditing && !settings.hideSubmit"
|
||||
v-if="inEditing && !(hideSubmit || settings.hideSubmit)"
|
||||
data-test-id="button-submit"
|
||||
:disabled="!canSaveRecord || processing"
|
||||
class="d-flex align-items-center justify-content-center ml-2"
|
||||
@@ -162,6 +162,8 @@
|
||||
{{ labels.submit || $t('label.save') }}
|
||||
</span>
|
||||
</b-button>
|
||||
|
||||
<slot name="actions" />
|
||||
</template>
|
||||
</b-col>
|
||||
</b-row>
|
||||
@@ -220,14 +222,34 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
|
||||
hideClone: {
|
||||
hideBack: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
default: () => true,
|
||||
},
|
||||
|
||||
hideAdd: {
|
||||
hideDelete: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
default: () => true,
|
||||
},
|
||||
|
||||
hideNew: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
|
||||
hideClone: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
|
||||
hideEdit: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
|
||||
hideSubmit: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
|
||||
showRecordModal: {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
ref="layoutSelect"
|
||||
size="sm"
|
||||
:value="layout.pageLayoutID"
|
||||
:options="layouts"
|
||||
value-field="pageLayoutID"
|
||||
text-field="label"
|
||||
:options="layouts"
|
||||
style="width: 300px;"
|
||||
@change="switchLayout"
|
||||
/>
|
||||
|
||||
@@ -212,6 +212,7 @@
|
||||
<draggable
|
||||
v-model="layouts"
|
||||
handle=".handle"
|
||||
group="layouts"
|
||||
tag="b-tbody"
|
||||
>
|
||||
<tr
|
||||
@@ -231,7 +232,6 @@
|
||||
<b-form-input
|
||||
v-model="layout.meta.title"
|
||||
:state="layoutTitleState(layout.meta.title)"
|
||||
@input="layout.meta.updated = true"
|
||||
/>
|
||||
</b-td>
|
||||
|
||||
@@ -242,7 +242,6 @@
|
||||
<b-form-input
|
||||
v-model="layout.handle"
|
||||
:state="layoutHandleState(layout.handle)"
|
||||
@input="layout.meta.updated = true"
|
||||
/>
|
||||
|
||||
<b-input-group-append>
|
||||
@@ -274,9 +273,8 @@
|
||||
class="text-center align-middle"
|
||||
>
|
||||
<c-input-confirm
|
||||
:title="$t('tabs.tooltip.delete')"
|
||||
class="ml-2"
|
||||
@confirmed="deleteLayout(index)"
|
||||
@confirmed="removeLayout(index)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -306,6 +304,10 @@
|
||||
@cancel="layoutEditor.layout = undefined"
|
||||
@hide="layoutEditor.layout = undefined"
|
||||
>
|
||||
<h5 class="mb-3">
|
||||
Visibility
|
||||
</h5>
|
||||
|
||||
<b-form-group
|
||||
label="Condition"
|
||||
label-class="text-primary"
|
||||
@@ -340,6 +342,157 @@
|
||||
class="bg-white"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<template v-if="isRecordPage">
|
||||
<hr>
|
||||
|
||||
<h5 class="mb-3">
|
||||
Record toolbar
|
||||
</h5>
|
||||
|
||||
<b-form-group
|
||||
label="Buttons"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<b-form-checkbox
|
||||
v-model="layoutEditor.layout.config.buttons.back.enabled"
|
||||
>
|
||||
Show back button
|
||||
</b-form-checkbox>
|
||||
|
||||
<b-form-checkbox
|
||||
v-model="layoutEditor.layout.config.buttons.delete.enabled"
|
||||
>
|
||||
Show delete button
|
||||
</b-form-checkbox>
|
||||
|
||||
<b-form-checkbox
|
||||
v-model="layoutEditor.layout.config.buttons.clone.enabled"
|
||||
>
|
||||
Show clone button
|
||||
</b-form-checkbox>
|
||||
|
||||
<b-form-checkbox
|
||||
v-model="layoutEditor.layout.config.buttons.new.enabled"
|
||||
>
|
||||
Show new button
|
||||
</b-form-checkbox>
|
||||
|
||||
<b-form-checkbox
|
||||
v-model="layoutEditor.layout.config.buttons.edit.enabled"
|
||||
>
|
||||
Show edit button
|
||||
</b-form-checkbox>
|
||||
|
||||
<b-form-checkbox
|
||||
v-model="layoutEditor.layout.config.buttons.submit.enabled"
|
||||
>
|
||||
Show submit button
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
label="Actions"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<b-table-simple
|
||||
responsive="lg"
|
||||
borderless
|
||||
small
|
||||
>
|
||||
<b-thead>
|
||||
<tr>
|
||||
<th />
|
||||
|
||||
<th
|
||||
class="text-primary"
|
||||
style="width: 25%; min-width: 190px;"
|
||||
>
|
||||
Label
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="text-primary"
|
||||
style="width: 33%; min-width: 240px;"
|
||||
>
|
||||
Type
|
||||
</th>
|
||||
|
||||
<th
|
||||
class="text-primary"
|
||||
style="width: 33%; min-width: 240px;"
|
||||
>
|
||||
Options
|
||||
</th>
|
||||
|
||||
<th style="min-width: 80px;" />
|
||||
</tr>
|
||||
</b-thead>
|
||||
|
||||
<draggable
|
||||
v-model="layoutEditor.layout.config.actions"
|
||||
handle=".handle"
|
||||
group="actions"
|
||||
tag="b-tbody"
|
||||
>
|
||||
<tr
|
||||
v-for="(action, index) in layoutEditor.layout.config.actions"
|
||||
:key="index"
|
||||
>
|
||||
<b-td class="handle text-center align-middle pr-2">
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'bars']"
|
||||
class="grab m-0 text-light p-0"
|
||||
/>
|
||||
</b-td>
|
||||
|
||||
<b-td
|
||||
class="align-middle"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="action.meta.label"
|
||||
/>
|
||||
</b-td>
|
||||
|
||||
<b-td
|
||||
class="align-middle"
|
||||
>
|
||||
<b-form-select
|
||||
v-model="action.kind"
|
||||
:options="actionKindOptions"
|
||||
/>
|
||||
</b-td>
|
||||
|
||||
<b-td
|
||||
class="align-middle"
|
||||
>
|
||||
<b-form-select
|
||||
v-model="action.params.pageLayoutID"
|
||||
:options="actionLayoutOptions"
|
||||
value-field="pageLayoutID"
|
||||
text-field="label"
|
||||
/>
|
||||
</b-td>
|
||||
|
||||
<td
|
||||
class="text-center align-middle"
|
||||
>
|
||||
<c-input-confirm
|
||||
class="ml-2"
|
||||
@confirmed="removeLayoutAction(index)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</draggable>
|
||||
</b-table-simple>
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="addLayoutAction"
|
||||
>
|
||||
Add action
|
||||
</b-button>
|
||||
</b-form-group>
|
||||
</template>
|
||||
</b-modal>
|
||||
|
||||
<b-modal
|
||||
@@ -536,7 +689,7 @@ export default {
|
||||
layout: undefined,
|
||||
},
|
||||
|
||||
deletedLayouts: new Set(),
|
||||
removedLayouts: new Set(),
|
||||
|
||||
roles: {
|
||||
processing: false,
|
||||
@@ -627,6 +780,20 @@ export default {
|
||||
this.$set(this.layoutEditor.layout.config.visibility, 'roles', roles)
|
||||
},
|
||||
},
|
||||
|
||||
actionKindOptions () {
|
||||
return [
|
||||
{ value: 'toLayout', text: 'Go to layout' },
|
||||
]
|
||||
},
|
||||
|
||||
actionLayoutOptions () {
|
||||
return [
|
||||
{ pageLayoutID: '', label: 'No route selected' },
|
||||
...this.layouts.filter(({ pageLayoutID }) => pageLayoutID !== NoID)
|
||||
.map(({ pageLayoutID, handle, meta }) => ({ pageLayoutID, label: meta.title || handle || pageLayoutID })),
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -636,7 +803,7 @@ export default {
|
||||
this.page = undefined
|
||||
this.layouts = []
|
||||
|
||||
this.deletedLayouts = new Set()
|
||||
this.removedLayouts = new Set()
|
||||
|
||||
if (pageID) {
|
||||
this.processing = true
|
||||
@@ -674,7 +841,7 @@ export default {
|
||||
async fetchLayouts () {
|
||||
const { namespaceID } = this.namespace
|
||||
return this.findLayoutsByPageID({ namespaceID, pageID: this.pageID }).then(layouts => {
|
||||
this.layouts = layouts.map(layout => new compose.PageLayout(layout))
|
||||
this.layouts = layouts
|
||||
})
|
||||
},
|
||||
|
||||
@@ -699,10 +866,10 @@ export default {
|
||||
this.layoutEditor.layout = undefined
|
||||
},
|
||||
|
||||
deleteLayout (index) {
|
||||
removeLayout (index) {
|
||||
const { pageLayoutID } = this.layouts[index] || {}
|
||||
if (pageLayoutID !== NoID) {
|
||||
this.deletedLayouts.add(this.layouts[index])
|
||||
this.removedLayouts.add(this.layouts[index])
|
||||
}
|
||||
|
||||
this.layouts.splice(index, 1)
|
||||
@@ -710,12 +877,12 @@ export default {
|
||||
|
||||
configureLayout (index) {
|
||||
this.layoutEditor.index = index
|
||||
this.layoutEditor.layout = { ...this.layouts[index] }
|
||||
this.layoutEditor.layout = new compose.PageLayout(this.layouts[index])
|
||||
},
|
||||
|
||||
async handleSaveLayouts () {
|
||||
// Delete first so old deleted handles don't interfere with new identical ones
|
||||
return Promise.all([...this.deletedLayouts].map(this.deletePageLayout)).then(() => {
|
||||
return Promise.all([...this.removedLayouts].map(this.deletePageLayout)).then(() => {
|
||||
return Promise.all(this.layouts.map(layout => {
|
||||
if (layout.pageLayoutID === NoID) {
|
||||
return this.createPageLayout(layout)
|
||||
@@ -754,7 +921,7 @@ export default {
|
||||
}).then(this.handlePageLayoutReorder)
|
||||
.then(() => {
|
||||
this.fetchLayouts()
|
||||
this.deletedLayouts = new Set()
|
||||
this.removedLayouts = new Set()
|
||||
|
||||
this.toastSuccess(this.$t('notification:page.saved'))
|
||||
if (closeOnSuccess) {
|
||||
@@ -791,6 +958,14 @@ export default {
|
||||
.catch(this.toastErrorHandler(this.$t('notification:page.iconFetchFailed')))
|
||||
},
|
||||
|
||||
addLayoutAction () {
|
||||
this.layoutEditor.layout.addAction()
|
||||
},
|
||||
|
||||
removeLayoutAction (index) {
|
||||
this.layoutEditor.layout.config.actions.splice(index, 1)
|
||||
},
|
||||
|
||||
async saveIcon () {
|
||||
return this.$ComposeAPI.pageUpdateIcon({
|
||||
namespaceID: this.namespace.namespaceID,
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
:in-editing="inEditing"
|
||||
:show-record-modal="showRecordModal"
|
||||
:record-navigation="recordNavigation"
|
||||
:hide-back="!layoutButtons.has('back')"
|
||||
:hide-delete="!layoutButtons.has('delete')"
|
||||
:hide-new="!layoutButtons.has('new')"
|
||||
:hide-clone="!layoutButtons.has('clone')"
|
||||
:hide-edit="!layoutButtons.has('edit')"
|
||||
:hide-submit="!layoutButtons.has('submit')"
|
||||
@add="handleAdd()"
|
||||
@clone="handleClone()"
|
||||
@edit="handleEdit()"
|
||||
@@ -48,7 +54,20 @@
|
||||
@back="handleBack()"
|
||||
@submit="handleFormSubmit('page.record')"
|
||||
@update-navigation="handleRedirectToPrevOrNext"
|
||||
/>
|
||||
>
|
||||
<template #actions>
|
||||
<b-button
|
||||
v-for="(action, index) in layoutActions"
|
||||
:key="index"
|
||||
variant="primary"
|
||||
size="lg"
|
||||
class="ml-2"
|
||||
@click.prevent="determineLayout(action.params.pageLayoutID)"
|
||||
>
|
||||
{{ action.Meta.label }}
|
||||
</b-button>
|
||||
</template>
|
||||
</record-toolbar>
|
||||
</portal>
|
||||
</div>
|
||||
</template>
|
||||
@@ -114,6 +133,7 @@ export default {
|
||||
|
||||
layouts: [],
|
||||
layout: undefined,
|
||||
layoutButtons: {},
|
||||
|
||||
blocks: [],
|
||||
}
|
||||
@@ -146,13 +166,22 @@ export default {
|
||||
recordToolbarLabels () {
|
||||
// Use an intermediate object so we can reflect all changes in one go;
|
||||
const aux = {}
|
||||
const { buttons = {} } = this.page.config || {}
|
||||
const { config = {} } = this.layout
|
||||
const { buttons = {} } = config
|
||||
|
||||
Object.entries(buttons).forEach(([key, { label = '' }]) => {
|
||||
aux[key] = label
|
||||
})
|
||||
return aux
|
||||
},
|
||||
|
||||
layoutActions () {
|
||||
const { config = {} } = this.layout
|
||||
const { actions = [] } = config
|
||||
|
||||
return actions
|
||||
},
|
||||
|
||||
title () {
|
||||
const { name, handle } = this.module
|
||||
const titlePrefix = this.inCreating ? 'create' : this.inEditing ? 'edit' : 'view'
|
||||
@@ -178,6 +207,7 @@ export default {
|
||||
'page.pageID': {
|
||||
immediate: true,
|
||||
handler () {
|
||||
this.layouts = this.getPageLayouts(this.page.pageID)
|
||||
this.determineLayout()
|
||||
},
|
||||
},
|
||||
@@ -287,16 +317,27 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
determineLayout () {
|
||||
this.layouts = this.getPageLayouts(this.page.pageID)
|
||||
determineLayout (pageLayoutID) {
|
||||
this.layout = this.layouts.find(l => {
|
||||
const { roles = [] } = l.config.visibility
|
||||
|
||||
if (pageLayoutID && l.pageLayoutID !== pageLayoutID) return
|
||||
|
||||
if (!roles.length) return true
|
||||
|
||||
return this.$auth.user.roles.some(roleID => roles.includes(roleID))
|
||||
})
|
||||
|
||||
const { config = {} } = this.layout
|
||||
const { buttons = [] } = config
|
||||
|
||||
this.layoutButtons = Object.entries(buttons).reduce((acc, [key, value]) => {
|
||||
if (value.enabled) {
|
||||
acc.add(key)
|
||||
}
|
||||
return acc
|
||||
}, new Set())
|
||||
|
||||
this.blocks = (this.layout || {}).blocks.map(({ blockID, xywh }) => {
|
||||
const block = this.page.blocks.find(b => b.blockID === blockID)
|
||||
block.xywh = xywh
|
||||
|
||||
@@ -8,22 +8,25 @@ export type PageLayoutInput = PageLayout | Partial<PageLayout>
|
||||
interface PageLayoutConfig {
|
||||
visibility: Visibility;
|
||||
buttons: {
|
||||
submit: Button;
|
||||
back: Button;
|
||||
delete: Button;
|
||||
new: Button;
|
||||
edit: Button;
|
||||
clone: Button;
|
||||
back: Button;
|
||||
edit: Button;
|
||||
submit: Button;
|
||||
};
|
||||
actions: Action[];
|
||||
}
|
||||
|
||||
interface Action {
|
||||
actionID: string;
|
||||
kind: string;
|
||||
placement: string;
|
||||
params: unknown;
|
||||
meta: unknown;
|
||||
meta: ActionMeta;
|
||||
}
|
||||
|
||||
interface ActionMeta {
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface Visibility {
|
||||
@@ -52,12 +55,12 @@ export class PageLayout {
|
||||
roles: [],
|
||||
},
|
||||
buttons: {
|
||||
submit: { enabled: true },
|
||||
back: { enabled: true },
|
||||
delete: { enabled: true },
|
||||
clone: { enabled: true },
|
||||
new: { enabled: true },
|
||||
edit: { enabled: true },
|
||||
clone: { enabled: true },
|
||||
back: { enabled: true },
|
||||
submit: { enabled: true },
|
||||
},
|
||||
actions: [],
|
||||
}
|
||||
@@ -100,6 +103,19 @@ export class PageLayout {
|
||||
return new PageLayout(JSON.parse(JSON.stringify(this)))
|
||||
}
|
||||
|
||||
addAction () {
|
||||
this.config.actions.push({
|
||||
kind: 'toLayout',
|
||||
placement: '',
|
||||
params: {
|
||||
pageLayoutID: '',
|
||||
},
|
||||
meta: {
|
||||
label: '',
|
||||
},
|
||||
} as Action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns resource ID
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user