diff --git a/client/src/views/settings/fields/dashboard-layout.js b/client/src/views/settings/fields/dashboard-layout.js index 86ddc04d8a..3414e7c813 100644 --- a/client/src/views/settings/fields/dashboard-layout.js +++ b/client/src/views/settings/fields/dashboard-layout.js @@ -26,498 +26,554 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/settings/fields/dashboard-layout', ['views/fields/base', 'lib!gridstack'], function (Dep, GridStack) { +import BaseFieldView from 'views/fields/base'; +import GridStack from 'gridstack'; - return Dep.extend({ +export default class SettingsDashboardLayoutFieldView extends BaseFieldView { - detailTemplate: 'settings/fields/dashboard-layout/detail', - editTemplate: 'settings/fields/dashboard-layout/edit', + detailTemplate = 'settings/fields/dashboard-layout/detail' + editTemplate = 'settings/fields/dashboard-layout/edit' - validationElementSelector: 'button[data-action="addDashlet"]', + validationElementSelector = 'button[data-action="addDashlet"]' - WIDTH_MULTIPLIER: 3, + WIDTH_MULTIPLIER = 3 - events: { - 'click button[data-action="selectTab"]': function (e) { - var tab = parseInt($(e.currentTarget).data('tab')); - this.selectTab(tab); - }, - 'click [data-action="removeDashlet"]': function (e) { - var id = $(e.currentTarget).data('id'); - this.removeDashlet(id); - }, - 'click [data-action="editDashlet"]': function (e) { - var id = $(e.currentTarget).data('id'); - var name = $(e.currentTarget).data('name'); + data() { + return { + dashboardLayout: this.dashboardLayout, + currentTab: this.currentTab, + isEmpty: this.isEmpty(), + }; + } - this.editDashlet(id, name); - }, - 'click button[data-action="editTabs"]': function () { - this.editTabs(); - }, - 'click button[data-action="addDashlet"]': function () { - this.createView('addDashlet', 'views/modals/add-dashlet', { - parentType: this.model.entityType, - }, view => { - view.render(); + /** + * @protected + * @return {boolean} + */ + hasLocked() { + return this.model.entityType === 'Preferences'; + } - this.listenToOnce(view, 'add', (name) => { - this.addDashlet(name); - }); - }); - }, - }, + setup() { + this.addActionHandler('selectTab', (e, target) => { + const tab = parseInt(target.dataset.tab); - data: function () { - return { - dashboardLayout: this.dashboardLayout, - currentTab: this.currentTab, - isEmpty: this.isEmpty(), - }; - }, + this.selectTab(tab); + }); - hasLocked: function () { - return this.model.entityType === 'Preferences'; - }, + this.addActionHandler('removeDashlet', (e, target) => { + const id = target.dataset.id; - setup: function () { - this.dashboardLayout = Espo.Utils.cloneDeep(this.model.get(this.name) || []); - this.dashletsOptions = Espo.Utils.cloneDeep(this.model.get('dashletsOptions') || {}); + this.removeDashlet(id); + }); + + this.addActionHandler('editDashlet', (e, target) => { + const id = target.dataset.id; + const name = target.dataset.name; + + this.editDashlet(id, name); + }); + + this.addActionHandler('editTabs', () => this.editTabs()); + + this.addActionHandler('addDashlet', () => { + this.createView('addDashlet', 'views/modals/add-dashlet', { + parentType: this.model.entityType, + }, view => { + view.render(); + + this.listenToOnce(view, 'add', name => this.addDashlet(name)); + }); + }); + + this.dashboardLayout = Espo.Utils.cloneDeep(this.model.get(this.name) || []); + this.dashletsOptions = Espo.Utils.cloneDeep(this.model.get('dashletsOptions') || {}); + + if (this.hasLocked()) { + this.dashboardLocked = this.model.get('dashboardLocked') || false; + } + + this.listenTo(this.model, 'change', () => { + if (this.model.hasChanged(this.name)) { + this.dashboardLayout = Espo.Utils.cloneDeep(this.model.get(this.name) || []); + } + + if (this.model.hasChanged('dashletsOptions')) { + this.dashletsOptions = Espo.Utils.cloneDeep(this.model.get('dashletsOptions') || {}); + } + + if (this.model.hasChanged(this.name)) { + if (this.dashboardLayout.length) { + if (this.isDetailMode()) { + this.selectTab(0); + } + } + } if (this.hasLocked()) { this.dashboardLocked = this.model.get('dashboardLocked') || false; } + }); - this.listenTo(this.model, 'change', () => { - if (this.model.hasChanged(this.name)) { - this.dashboardLayout = Espo.Utils.cloneDeep(this.model.get(this.name) || []); - } + this.currentTab = -1; + this.currentTabLayout = null; - if (this.model.hasChanged('dashletsOptions')) { - this.dashletsOptions = Espo.Utils.cloneDeep(this.model.get('dashletsOptions') || {}); - } + if (this.dashboardLayout.length) { + this.selectTab(0); + } + } - if (this.model.hasChanged(this.name)) { - if (this.dashboardLayout.length) { - if (this.isDetailMode()) { - this.selectTab(0); - } - } - } + /** + * @protected + * @param {number} tab + */ + selectTab(tab) { + this.currentTab = tab; + this.setupCurrentTabLayout(); - if (this.hasLocked()) { - this.dashboardLocked = this.model.get('dashboardLocked') || false; - } - }); + if (this.isRendered()) { + this.reRender() + .then(() => { + this.$el + .find(`[data-action="selectTab"][data-tab="${tab}"]`) + .focus(); + }) + } + } - this.currentTab = -1; + /** + * @protected + */ + setupCurrentTabLayout() { + if (!~this.currentTab) { this.currentTabLayout = null; + } - if (this.dashboardLayout.length) { - this.selectTab(0); + let tabLayout = this.dashboardLayout[this.currentTab].layout || []; + + tabLayout = GridStack.Utils.sort(tabLayout); + + this.currentTabLayout = tabLayout; + } + + /** + * @protected + * @param {string} id + * @param {string} name + */ + addDashletHtml(id, name) { + const $item = this.prepareGridstackItem(id, name); + + this.grid.addWidget( + $item.get(0), + { + x: 0, + y: 0, + w: 2 * this.WIDTH_MULTIPLIER, + h: 2, } - }, + ); + } - selectTab: function (tab) { - this.currentTab = tab; - this.setupCurrentTabLayout(); + /** + * @private + * @return {string} + */ + generateId() { + return (Math.floor(Math.random() * 10000001)).toString(); + } - if (this.isRendered()) { - this.reRender() - .then(() => { - this.$el - .find(`[data-action="selectTab"][data-tab="${tab}"]`) - .focus(); - }) - } - }, + /** + * @protected + * @param {string} name + */ + addDashlet(name) { + const id = 'd' + (Math.floor(Math.random() * 1000001)).toString(); - setupCurrentTabLayout: function () { - if (!~this.currentTab) { - this.currentTabLayout = null; - } - - var tabLayout = this.dashboardLayout[this.currentTab].layout || []; - - tabLayout = GridStack.Utils.sort(tabLayout); - - this.currentTabLayout = tabLayout; - }, - - addDashletHtml: function (id, name) { - var $item = this.prepareGridstackItem(id, name); - - this.grid.addWidget( - $item.get(0), - { - x: 0, - y: 0, - w: 2 * this.WIDTH_MULTIPLIER, - h: 2, - } - ); - }, - - generateId: function () { - return (Math.floor(Math.random() * 10000001)).toString(); - }, - - addDashlet: function (name) { - var id = 'd' + (Math.floor(Math.random() * 1000001)).toString(); - - if (!~this.currentTab) { - this.dashboardLayout.push({ - name: 'My Espo', - layout: [], - id: this.generateId(), - }); - - this.currentTab = 0; - this.setupCurrentTabLayout(); - - this.once('after:render', () => { - setTimeout(() => { - this.addDashletHtml(id, name); - this.fetchLayout(); - }, 50); - }); - - this.reRender(); - } - else { - this.addDashletHtml(id, name); - this.fetchLayout(); - } - }, - - removeDashlet: function (id) { - let $item = this.$gridstack.find('.grid-stack-item[data-id="'+id+'"]'); - - this.grid.removeWidget($item.get(0), true); - - var layout = this.dashboardLayout[this.currentTab].layout; - - layout.forEach((o, i) => { - if (o.id === id) { - layout.splice(i, 1); - } + if (!~this.currentTab) { + this.dashboardLayout.push({ + name: 'My Espo', + layout: [], + id: this.generateId(), }); - delete this.dashletsOptions[id]; - + this.currentTab = 0; this.setupCurrentTabLayout(); - }, - editTabs: function () { - let options = { - dashboardLayout: this.dashboardLayout, - tabListIsNotRequired: true, - }; + this.once('after:render', () => { + setTimeout(() => { + this.addDashletHtml(id, name); + this.fetchLayout(); + }, 50); + }); - if (this.hasLocked()) { - options.dashboardLocked = this.dashboardLocked; + this.reRender(); + } else { + this.addDashletHtml(id, name); + this.fetchLayout(); + } + } + + /** + * @protected + * @param {string} id + */ + removeDashlet(id) { + const $item = this.$gridstack.find('.grid-stack-item[data-id="' + id + '"]'); + + this.grid.removeWidget($item.get(0), true); + + const layout = this.dashboardLayout[this.currentTab].layout; + + layout.forEach((o, i) => { + if (o.id === id) { + layout.splice(i, 1); } + }); - this.createView('editTabs', 'views/modals/edit-dashboard', options, view => { - view.render(); + delete this.dashletsOptions[id]; - this.listenToOnce(view, 'after:save', data => { - view.close(); + this.setupCurrentTabLayout(); + } - let dashboardLayout = []; + /** + * @protected + */ + editTabs() { + const options = { + dashboardLayout: this.dashboardLayout, + tabListIsNotRequired: true, + }; - data.dashboardTabList.forEach(name => { - var layout = []; - var id = this.generateId(); + if (this.hasLocked()) { + options.dashboardLocked = this.dashboardLocked; + } - this.dashboardLayout.forEach(d => { - if (d.name === name) { - layout = d.layout; - id = d.id; - } - }); + this.createView('editTabs', 'views/modals/edit-dashboard', options, view => { + view.render(); - if (name in data.renameMap) { - name = data.renameMap[name]; + this.listenToOnce(view, 'after:save', data => { + view.close(); + + const dashboardLayout = []; + + data.dashboardTabList.forEach(name => { + let layout = []; + let id = this.generateId(); + + this.dashboardLayout.forEach(d => { + if (d.name === name) { + layout = d.layout; + id = d.id; } - - dashboardLayout.push({ - name: name, - layout: layout, - id: id, - }); }); - this.dashboardLayout = dashboardLayout; - - if (this.hasLocked()) { - this.dashboardLocked = data.dashboardLocked; + if (name in data.renameMap) { + name = data.renameMap[name]; } - this.selectTab(0); - - this.deleteNotExistingDashletsOptions(); + dashboardLayout.push({ + name: name, + layout: layout, + id: id, + }); }); - }); - }, - deleteNotExistingDashletsOptions: function () { - var idListMet = []; + this.dashboardLayout = dashboardLayout; - (this.dashboardLayout || []).forEach((itemTab) => { - (itemTab.layout || []).forEach((item) => { - idListMet.push(item.id); - }); - }); - - Object.keys(this.dashletsOptions).forEach((id) => { - if (!~idListMet.indexOf(id)) { - delete this.dashletsOptions[id]; - } - }); - }, - - editDashlet: function (id, name) { - var options = this.dashletsOptions[id] || {}; - options = Espo.Utils.cloneDeep(options); - - var defaultOptions = this.getMetadata().get(['dashlets', name , 'options', 'defaults']) || {}; - - Object.keys(defaultOptions).forEach((item) => { - if (item in options) { - return; + if (this.hasLocked()) { + this.dashboardLocked = data.dashboardLocked; } - options[item] = Espo.Utils.cloneDeep(defaultOptions[item]); - }); + this.selectTab(0); - if (!('title' in options)) { - options.title = this.translate(name, 'dashlets'); + this.deleteNotExistingDashletsOptions(); + }); + }); + } + + /** + * @private + */ + deleteNotExistingDashletsOptions() { + const idListMet = []; + + (this.dashboardLayout || []).forEach((itemTab) => { + (itemTab.layout || []).forEach((item) => { + idListMet.push(item.id); + }); + }); + + Object.keys(this.dashletsOptions).forEach((id) => { + if (!~idListMet.indexOf(id)) { + delete this.dashletsOptions[id]; } + }); + } - var optionsView = this.getMetadata().get(['dashlets', name, 'options', 'view']) || - 'views/dashlets/options/base'; + /** + * @protected + * @param {string} id + * @param {string} name + */ + editDashlet(id, name) { + let options = this.dashletsOptions[id] || {}; + options = Espo.Utils.cloneDeep(options); - this.createView('options', optionsView, { - name: name, - optionsData: options, - fields: this.getMetadata().get(['dashlets', name, 'options', 'fields']) || {}, - userId: this.model.entityType === 'Preferences' ? this.model.id : null, - }, view => { - view.render(); + const defaultOptions = this.getMetadata().get(['dashlets', name, 'options', 'defaults']) || {}; - this.listenToOnce(view, 'save', (attributes) => { - this.dashletsOptions[id] = attributes; - - view.close(); - - if ('title' in attributes) { - var title = attributes.title; - - if (!title) { - title = this.translate(name, 'dashlets'); - } - - this.$el.find('[data-id="'+id+'"] .panel-title').text(title); - } - }); - }); - }, - - fetchLayout: function () { - if (!~this.currentTab) { + Object.keys(defaultOptions).forEach((item) => { + if (item in options) { return; } - this.dashboardLayout[this.currentTab].layout = _.map(this.$gridstack.find('.grid-stack-item'), el => { - var $el = $(el); + options[item] = Espo.Utils.cloneDeep(defaultOptions[item]); + }); - let x = $el.attr('gs-x'); - let y = $el.attr('gs-y'); - let h = $el.attr('gs-h'); - let w = $el.attr('gs-w'); + if (!('title' in options)) { + options.title = this.translate(name, 'dashlets'); + } - return { - id: $el.data('id'), - name: $el.data('name'), - x: x / this.WIDTH_MULTIPLIER, - y: y, - width: w / this.WIDTH_MULTIPLIER, - height: h, - }; + const optionsView = this.getMetadata().get(['dashlets', name, 'options', 'view']) || + 'views/dashlets/options/base'; + + this.createView('options', optionsView, { + name: name, + optionsData: options, + fields: this.getMetadata().get(['dashlets', name, 'options', 'fields']) || {}, + userId: this.model.entityType === 'Preferences' ? this.model.id : null, + }, view => { + view.render(); + + this.listenToOnce(view, 'save', (attributes) => { + this.dashletsOptions[id] = attributes; + + view.close(); + + if ('title' in attributes) { + let title = attributes.title; + + if (!title) { + title = this.translate(name, 'dashlets'); + } + + this.$el.find('[data-id="'+id+'"] .panel-title').text(title); + } + }); + }); + } + + /** + * @protected + */ + fetchLayout() { + if (!~this.currentTab) { + return; + } + + this.dashboardLayout[this.currentTab].layout = _.map(this.$gridstack.find('.grid-stack-item'), el => { + const $el = $(el); + + const x = $el.attr('gs-x'); + const y = $el.attr('gs-y'); + const h = $el.attr('gs-h'); + const w = $el.attr('gs-w'); + + return { + id: $el.data('id'), + name: $el.data('name'), + x: x / this.WIDTH_MULTIPLIER, + y: y, + width: w / this.WIDTH_MULTIPLIER, + height: h, + }; + }); + + this.setupCurrentTabLayout(); + } + + afterRender() { + if (this.currentTabLayout) { + const $gridstack = this.$gridstack = this.$el.find('> .grid-stack'); + + const grid = this.grid = GridStack.init({ + minWidth: 4, + cellHeight: 60, + margin: 10, + column: 12, + resizable: { + handles: 'se', + helper: false + }, + disableOneColumnMode: true, + animate: false, + staticGrid: this.mode !== 'edit', + disableResize: this.mode !== 'edit', + disableDrag: this.mode !== 'edit', }); - this.setupCurrentTabLayout(); - }, + grid.removeAll(); - afterRender: function () { - if (this.currentTabLayout) { - var $gridstack = this.$gridstack = this.$el.find('> .grid-stack'); + this.currentTabLayout.forEach((o) => { + const $item = this.prepareGridstackItem(o.id, o.name); - var grid = this.grid = GridStack.init({ - minWidth: 4, - cellHeight: 60, - margin: 10, - column: 12, - resizable: { - handles: 'se', - helper: false - }, - disableOneColumnMode: true, - animate: false, - staticGrid: this.mode !== 'edit', - disableResize: this.mode !== 'edit', - disableDrag: this.mode !== 'edit', - }); + this.grid.addWidget( + $item.get(0), + { + x: o.x * this.WIDTH_MULTIPLIER, + y: o.y, + w: o.width * this.WIDTH_MULTIPLIER, + h: o.height, + } + ); + }); - grid.removeAll(); + $gridstack.find(' .grid-stack-item').css('position', 'absolute'); - this.currentTabLayout.forEach((o) => { - var $item = this.prepareGridstackItem(o.id, o.name); + $gridstack.on('change', () => { + this.fetchLayout(); + this.trigger('change'); + }); + } + } - this.grid.addWidget( - $item.get(0), - { - x: o.x * this.WIDTH_MULTIPLIER, - y: o.y, - w: o.width * this.WIDTH_MULTIPLIER, - h: o.height, - } - ); - }); + /** + * @private + * @param {string} id + * @param {string} name + * @return {jQuery|JQuery} + */ + prepareGridstackItem(id, name) { + const $item = $('
').addClass('grid-stack-item'); + let actionsHtml = ''; - $gridstack.find(' .grid-stack-item').css('position', 'absolute'); - - $gridstack.on('change', (e, itemList) => { - this.fetchLayout(); - this.trigger('change'); - }); - } - }, - - prepareGridstackItem: function (id, name) { - let $item = $('
').addClass('grid-stack-item'); - let actionsHtml = ''; - - if (this.isEditMode()) { - actionsHtml += - $('
') - .addClass('btn-group pull-right') - .append( - $('