From 5aaf90f40d221c7b135bd0c4db951bfe31fc268b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C5=BEe=20Fortun?= Date: Thu, 30 Mar 2023 21:52:05 +0200 Subject: [PATCH] Add page layout record toolbar and actions --- .../src/components/Common/RecordToolbar.vue | 42 +++- .../compose/src/views/Admin/Pages/Builder.vue | 2 +- .../compose/src/views/Admin/Pages/Edit.vue | 199 ++++++++++++++++-- .../src/views/Public/Pages/Records/View.vue | 49 ++++- lib/js/src/compose/types/page-layout.ts | 32 ++- 5 files changed, 289 insertions(+), 35 deletions(-) diff --git a/client/web/compose/src/components/Common/RecordToolbar.vue b/client/web/compose/src/components/Common/RecordToolbar.vue index 43bc6edbb..79a6d6d0d 100644 --- a/client/web/compose/src/components/Common/RecordToolbar.vue +++ b/client/web/compose/src/components/Common/RecordToolbar.vue @@ -13,7 +13,7 @@ class="d-flex align-items-center justify-content-start" > + + @@ -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: { diff --git a/client/web/compose/src/views/Admin/Pages/Builder.vue b/client/web/compose/src/views/Admin/Pages/Builder.vue index 273f175d2..6d6ec5790 100644 --- a/client/web/compose/src/views/Admin/Pages/Builder.vue +++ b/client/web/compose/src/views/Admin/Pages/Builder.vue @@ -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" /> diff --git a/client/web/compose/src/views/Admin/Pages/Edit.vue b/client/web/compose/src/views/Admin/Pages/Edit.vue index 4f98e704f..5cb4beb81 100644 --- a/client/web/compose/src/views/Admin/Pages/Edit.vue +++ b/client/web/compose/src/views/Admin/Pages/Edit.vue @@ -212,6 +212,7 @@ @@ -242,7 +242,6 @@ @@ -274,9 +273,8 @@ class="text-center align-middle" > @@ -306,6 +304,10 @@ @cancel="layoutEditor.layout = undefined" @hide="layoutEditor.layout = undefined" > +
+ Visibility +
+ + + 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, 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 5f98c855c..6ac390134 100644 --- a/client/web/compose/src/views/Public/Pages/Records/View.vue +++ b/client/web/compose/src/views/Public/Pages/Records/View.vue @@ -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" - /> + > + + @@ -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 diff --git a/lib/js/src/compose/types/page-layout.ts b/lib/js/src/compose/types/page-layout.ts index 5f11f46ea..c57c22f15 100644 --- a/lib/js/src/compose/types/page-layout.ts +++ b/lib/js/src/compose/types/page-layout.ts @@ -8,22 +8,25 @@ export type PageLayoutInput = PageLayout | Partial 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 */