From ca40c99ac2bdf8b88ea13332a66c6c8dd527ee5e Mon Sep 17 00:00:00 2001 From: yuri Date: Fri, 9 Jan 2015 11:37:03 +0200 Subject: [PATCH] improve relationship panels --- frontend/client/src/search-manager.js | 79 ++++---- frontend/client/src/views/list.js | 38 ++-- .../client/src/views/record/detail-bottom.js | 40 ++-- .../src/views/record/panels/relationship.js | 39 ++-- .../views/record/row-actions/relationship.js | 4 +- frontend/client/src/views/record/search.js | 184 +++++++++--------- 6 files changed, 194 insertions(+), 190 deletions(-) diff --git a/frontend/client/src/search-manager.js b/frontend/client/src/search-manager.js index ee042c7fd6..2017f012d7 100644 --- a/frontend/client/src/search-manager.js +++ b/frontend/client/src/search-manager.js @@ -17,7 +17,7 @@ * * You should have received a copy of the GNU General Public License * along with EspoCRM. If not, see http://www.gnu.org/licenses/. - ************************************************************************/ + ************************************************************************/ (function (Espo, _) { Espo.SearchManager = function (collection, type, storage, dateTime, defaultData) { @@ -26,20 +26,20 @@ this.storage = storage; this.type = type || 'list'; this.dateTime = dateTime; - + this.data = this.defaultData = defaultData || { - textFilter: '', + textFilter: '', bool: {}, advanced: {}, }; - + this.sanitizeData(); }; - + _.extend(Espo.SearchManager.prototype, { - + data: null, - + sanitizeData: function () { if (!('advanced' in this.data)) { this.data.advanced = {}; @@ -49,19 +49,19 @@ } if (!('textFilter' in this.data)) { this.data.textFilter = ''; - } + } }, - - getWhere: function () { - var where = []; - + + getWhere: function () { + var where = []; + if (this.data.textFilter && this.data.textFilter != '') { where.push({ type: 'textFilter', value: this.data.textFilter }); } - + if (this.data.bool) { var o = { type: 'boolFilters', @@ -74,26 +74,25 @@ } if (o.value.length) { where.push(o); - } + } } - + if (this.data.advanced) { for (var name in this.data.advanced) { var field = name; var defs = this.data.advanced[name]; - + if (!defs) { continue; } - - + if ('where' in defs) { where.push(defs.where); - } else { + } else { if ('field' in defs) { field = defs.field; - } - var type = defs.type; + } + var type = defs.type; if (defs.dateTime) { where.push(this.getDateTimeWhere(type, field, defs.value)); } else { @@ -103,42 +102,42 @@ field: field, value: value, }); - } + } } } } return where; - }, - + }, + loadStored: function () { this.data = this.storage.get(this.type + 'Search', this.scope) || _.clone(this.defaultData); - this.sanitizeData(); + this.sanitizeData(); return this; }, - + get: function () { return this.data; }, - + setAdvanced: function (advanced) { this.data.advanced = advanced; }, - + set: function (data) { this.data = data; if (this.storage) { this.storage.set(this.type + 'Search', this.scope, data); } }, - + reset: function () { this.data = _.clone(this.defaultData); if (this.storage) { this.storage.clear(this.type + 'Search', this.scope); } }, - + getDateTimeWhere: function (type, field, value) { var where = { field: field @@ -146,13 +145,13 @@ if (!value && ~['on', 'before', 'after'].indexOf(type)) { return null; } - + switch (type) { case 'today': where.type = 'between'; var start = this.dateTime.getNowMoment().startOf('day').utc(); - - var from = start.format(this.dateTime.internalDateTimeFormat); + + var from = start.format(this.dateTime.internalDateTimeFormat); var to = start.add('days', 1).format(this.dateTime.internalDateTimeFormat); where.value = [from, to]; break; @@ -167,22 +166,22 @@ case 'on': where.type = 'between'; var start = moment(value, this.dateTime.internalDateFormat, this.timeZone).utc(); - + var from = start.format(this.dateTime.internalDateTimeFormat); var to = start.add('days', 1).format(this.dateTime.internalDateTimeFormat); - + where.value = [from, to]; break; case 'before': - where.type = 'before'; + where.type = 'before'; where.value = moment(value, this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat); break; case 'after': - where.type = 'after'; + where.type = 'after'; where.value = moment(value, this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat); break; case 'between': - where.type = 'between'; + where.type = 'between'; if (value[0] && value[1]) { var from = moment(value[0], this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat); var to = moment(value[1], this.dateTime.internalDateFormat, this.timeZone).utc().format(this.dateTime.internalDateTimeFormat); @@ -190,9 +189,9 @@ } break; default: - where.type = type; + where.type = type; } - + return where; }, }); diff --git a/frontend/client/src/views/list.js b/frontend/client/src/views/list.js index 1be2f95b58..bf70bec5aa 100644 --- a/frontend/client/src/views/list.js +++ b/frontend/client/src/views/list.js @@ -17,9 +17,9 @@ * * You should have received a copy of the GNU General Public License * along with EspoCRM. If not, see http://www.gnu.org/licenses/. - ************************************************************************/ + ************************************************************************/ -Espo.define('Views.List', 'Views.Main', function (Dep) { +Espo.define('Views.List', ['Views.Main', 'SearchManager'], function (Dep, SearchManager) { return Dep.extend({ @@ -37,11 +37,11 @@ Espo.define('Views.List', 'Views.Main', function (Dep) { view: 'Header' } }, - + searchPanel: true, - + searchManager: true, - + createButton: true, setup: function () { @@ -51,20 +51,20 @@ Espo.define('Views.List', 'Views.Main', function (Dep) { if (this.getMetadata().get('clientDefs.' + this.scope + '.disableSearchPanel')) { this.searchPanel = false; } - + if (this.searchPanel) { this.createView('search', 'Record.Search', { collection: this.collection, el: '#main > .search-container', searchManager: this.searchManager, - }, function (view) { + }, function (view) { this.listenTo(view, 'reset', function () { this.collection.sortBy = this.defaultSortBy; this.collection.asc = this.defaultAsc; this.getStorage().clear('listSorting', this.collection.name) }, this); }.bind(this)); - } + } if (this.createButton) { this.menu.buttons.unshift({ @@ -73,21 +73,21 @@ Espo.define('Views.List', 'Views.Main', function (Dep) { style: 'primary', acl: 'edit' }); - } + } }, - + getSearchDefaultData: function () { return null; }, - + setupSearchManager: function () { var collection = this.collection; - - var searchManager = new Espo.SearchManager(collection, 'list', this.getStorage(), this.getDateTime(), this.getSearchDefaultData()); + + var searchManager = new SearchManager(collection, 'list', this.getStorage(), this.getDateTime(), this.getSearchDefaultData()); searchManager.loadStored(); collection.where = searchManager.getWhere(); collection.maxSize = this.getConfig().get('recordsPerPage') || collection.maxSize; - + this.searchManager = searchManager; }, @@ -97,7 +97,7 @@ Espo.define('Views.List', 'Views.Main', function (Dep) { var collection = this.collection; var sortingParams = this.getStorage().get('listSorting', collection.name) || {}; - + this.defaultSortBy = collection.sortBy; this.defaultAsc = collection.asc; @@ -111,9 +111,9 @@ Espo.define('Views.List', 'Views.Main', function (Dep) { afterRender: function () { this.notify('Loading...'); - + var listViewName = this.getMetadata().get('clientDefs.' + this.scope + '.recordViews.list') || 'Record.List'; - + this.listenToOnce(this.collection, 'sync', function () { this.createView('list', listViewName, { collection: this.collection, @@ -121,13 +121,13 @@ Espo.define('Views.List', 'Views.Main', function (Dep) { }, function (view) { view.render(); view.notify(false); - + if (this.searchPanel) { this.listenTo(this.getView('list'), 'sort', function (obj) { this.getStorage().set('listSorting', this.collection.name, obj); }, this); } - + }.bind(this)); }, this); this.collection.fetch(); diff --git a/frontend/client/src/views/record/detail-bottom.js b/frontend/client/src/views/record/detail-bottom.js index 1b221de8bf..89f8ee6e9a 100644 --- a/frontend/client/src/views/record/detail-bottom.js +++ b/frontend/client/src/views/record/detail-bottom.js @@ -17,14 +17,14 @@ * * You should have received a copy of the GNU General Public License * along with EspoCRM. If not, see http://www.gnu.org/licenses/. - ************************************************************************/ + ************************************************************************/ Espo.define('Views.Record.DetailBottom', 'View', function (Dep) { return Dep.extend({ template: 'record.bottom', - + mode: 'detail', data: function () { @@ -53,13 +53,13 @@ Espo.define('Views.Record.DetailBottom', 'View', function (Dep) { }, setup: function () { - this.panels = []; + this.panels = []; var scope = this.model.name; - + this.wait(true); - + var panels = _.clone(this.getMetadata().get('clientDefs.' + scope + '.bottomPanels.' + this.mode) || []); - + if (this.mode == 'detail' && this.getMetadata().get('scopes.' + scope + '.stream')) { panels.unshift({ "name":"stream", @@ -67,7 +67,7 @@ Espo.define('Views.Record.DetailBottom', 'View', function (Dep) { "view":"Stream.Panel" }); } - + panels.forEach(function (p) { var name = p.name; this.panels.push(p); @@ -83,29 +83,30 @@ Espo.define('Views.Record.DetailBottom', 'View', function (Dep) { p.buttons = this.filterActions(view.getButtons()); } if (p.label) { - p.title = this.translate(p.label, 'labels', scope); + p.title = this.translate(p.label, 'labels', scope); } else { p.title = view.title; } }.bind(this)); - }.bind(this)); - + }.bind(this)); + this._helper.layoutManager.get(this.model.name, 'relationships', function (layout) { - + var panelList = layout; panelList.forEach(function (name) { var p = {name: name}; var name = p.name; - - var foreignScope = this.model.defs.links[name].entity; + + var foreignScope = this.model.defs.links[name].entity; if (!this.getAcl().check(foreignScope, 'read')) { return; } - + this.panels.push(p); - - var viewName = 'Record.Panels.Relationship'; - var defs = this.getMetadata().get('clientDefs.' + scope + '.relationshipPanels.' + name) || {}; + + var defs = this.getMetadata().get('clientDefs.' + scope + '.relationshipPanels.' + name) || {}; + + var viewName = defs.view || 'Record.Panels.Relationship'; var total = 8; @@ -123,13 +124,12 @@ Espo.define('Views.Record.DetailBottom', 'View', function (Dep) { p.buttons = this.filterActions(view.getButtons()); } p.title = view.title; - }.bind(this)); + }.bind(this)); }.bind(this)); - + this.wait(false); }.bind(this)); }, - filterActions: function (actions) { diff --git a/frontend/client/src/views/record/panels/relationship.js b/frontend/client/src/views/record/panels/relationship.js index 9418dd1dbc..28565c3bd0 100644 --- a/frontend/client/src/views/record/panels/relationship.js +++ b/frontend/client/src/views/record/panels/relationship.js @@ -17,18 +17,18 @@ * * You should have received a copy of the GNU General Public License * along with EspoCRM. If not, see http://www.gnu.org/licenses/. - ************************************************************************/ + ************************************************************************/ -Espo.define('Views.Record.Panels.Relationship', 'Views.Record.Panels.Bottom', function (Dep) { +Espo.define('Views.Record.Panels.Relationship', ['Views.Record.Panels.Bottom', 'SearchManager'], function (Dep, SearchManager) { return Dep.extend({ template: 'record.panels.relationship', - + rowActionsView: 'Record.RowActions.Relationship', setup: function () { - this.link = this.panelName; + this.link = this.panelName; if (!(this.link in this.model.defs.links)) { throw new Error('Link \'' + this.link + '\' is not defined in model \'' + this.model.name + '\''); } @@ -50,7 +50,7 @@ Espo.define('Views.Record.Panels.Relationship', 'Views.Record.Panels.Bottom', fu if (this.getAcl().check(this.scope, 'edit')) { this.buttons.create = this.defs.create; } - + this.actions = _.clone(this.defs.actions || []); if (this.defs.select) { @@ -62,7 +62,7 @@ Espo.define('Views.Record.Panels.Relationship', 'Views.Record.Panels.Bottom', fu } }); } - + var type = 'listSmall'; var listLayout = null; var layout = this.defs.layout || null; @@ -77,10 +77,17 @@ Espo.define('Views.Record.Panels.Relationship', 'Views.Record.Panels.Bottom', fu var sortBy = this.defs.sortBy || null; var asc = this.defs.asc || null; + this.wait(true); - this.getCollectionFactory().create(this.scope, function (collection) { + this.getCollectionFactory().create(this.scope, function (collection) { collection.maxSize = this.getConfig().get('recordsPerPageSmall') || 5; - + + if (this.defs.filters) { + var searchManager = new SearchManager(collection, 'listRelationship', false, this.getDateTime()); + searchManager.setAdvanced(this.defs.filters); + collection.where = searchManager.getWhere(); + } + collection.url = collection.urlRoot = url; if (sortBy) { collection.sortBy = sortBy; @@ -88,9 +95,9 @@ Espo.define('Views.Record.Panels.Relationship', 'Views.Record.Panels.Bottom', fu if (asc) { collection.asc = asc; } - this.collection = collection; - - this.once('after:render', function () { + this.collection = collection; + + this.once('after:render', function () { collection.once('sync', function () { this.createView('list', 'Record.List', { collection: collection, @@ -105,17 +112,15 @@ Espo.define('Views.Record.Panels.Relationship', 'Views.Record.Panels.Bottom', fu }, this); collection.fetch(); }, this); - - this.wait(false); - }.bind(this)); + }, this); }, - + getActions: function () { return this.actions || []; }, - + getButtons: function () { if (this.buttons && this.buttons.create) { return [{ @@ -168,7 +173,7 @@ Espo.define('Views.Record.Panels.Relationship', 'Views.Record.Panels.Bottom', fu }); } }, - + actionRemoveRelated: function (id) { var self = this; if (confirm(this.translate('removeRecordConfirmation', 'messages'))) { diff --git a/frontend/client/src/views/record/row-actions/relationship.js b/frontend/client/src/views/record/row-actions/relationship.js index d36c758322..01703d51a3 100644 --- a/frontend/client/src/views/record/row-actions/relationship.js +++ b/frontend/client/src/views/record/row-actions/relationship.js @@ -22,7 +22,7 @@ Espo.define('Views.Record.RowActions.Relationship', 'Views.Record.RowActions.Default', function (Dep) { return Dep.extend({ - + getActions: function () { if (this.options.acl.edit) { return [ @@ -50,7 +50,7 @@ Espo.define('Views.Record.RowActions.Relationship', 'Views.Record.RowActions.Def ]; } }, - + }); }); diff --git a/frontend/client/src/views/record/search.js b/frontend/client/src/views/record/search.js index e8f044b98e..1cdc946ec4 100644 --- a/frontend/client/src/views/record/search.js +++ b/frontend/client/src/views/record/search.js @@ -36,7 +36,7 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { advanced: null, bool: null, - + disableSavePreset: false, data: function () { @@ -56,7 +56,7 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { setup: function () { this.scope = this.collection.name; this.searchManager = this.options.searchManager; - + if ('disableSavePreset' in this.options) { this.disableSavePreset = this.options.disableSavePreset; } @@ -64,15 +64,15 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { this.addReadyCondition(function () { return this.fields != null && this.moreFields != null; }.bind(this)); - + this.boolFilters = this.getMetadata().get('clientDefs.' + this.scope + '.boolFilters') || []; this._helper.layoutManager.get(this.scope, 'filters', function (list) { this.moreFields = list; this.tryReady(); }.bind(this)); - - this.presetFilters = _.clone(this.getMetadata().get('clientDefs.' + this.scope + '.presetFilters') || []); + + this.presetFilters = _.clone(this.getMetadata().get('clientDefs.' + this.scope + '.presetFilters') || []); ((this.getPreferences().get('presetFilters') || {})[this.scope] || []).forEach(function (item) { this.presetFilters.push(item); }, this); @@ -80,21 +80,21 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { this.loadSearchData(); this.model = new this.collection.model(); - this.model.clear(); + this.model.clear(); this.createFilters(); }, - - createFilters: function (callback) { + + createFilters: function (callback) { var i = 0; var count = Object.keys(this.advanced || {}).length; - - if (count == 0) { + + if (count == 0) { if (typeof callback === 'function') { callback(); } } - + for (var field in this.advanced) { this.createFilter(field, this.advanced[field], function () { i++; @@ -116,65 +116,65 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { 'click button[data-action="search"]': function (e) { this.search(); }, - 'click a[data-action="addFilter"]': function (e) { + 'click a[data-action="addFilter"]': function (e) { var $target = $(e.currentTarget); var name = $target.data('name'); - this.advanced[name] = {}; - + this.advanced[name] = {}; + $target.closest('li').addClass('hide'); - + this.presetName = null; - - this.createFilter(name, {}, function () { + + this.createFilter(name, {}, function () { this.fetch(); this.updateSearch(); }.bind(this)); this.updateAddFilterButton(); - + this.managePresetFilters(); }, - 'click .advanced-filters a.remove-filter': function (e) { + 'click .advanced-filters a.remove-filter': function (e) { var $target = $(e.currentTarget); var name = $target.data('name'); - + this.$el.find('ul.filter-list li[data-name="' + name + '"]').removeClass('hide'); var container = this.getView('filter-' + name).$el.closest('div.filter'); - this.clearView('filter-' + name); + this.clearView('filter-' + name); container.remove(); delete this.advanced[name]; - + this.presetName = null; - - this.updateAddFilterButton(); - + + this.updateAddFilterButton(); + this.fetch(); this.updateSearch(); - + this.managePresetFilters(); }, 'click button[data-action="reset"]': function (e) { this.resetFilters(); }, 'click button[data-action="refresh"]': function (e) { - this.notify('Loading...'); + this.notify('Loading...'); this.listenToOnce(this.collection, 'sync', function () { this.notify(false); }.bind(this)); - + this.collection.reset(); this.collection.fetch(); - + }, 'click a[data-action="selectPreset"]': function (e) { this.presetName = $(e.currentTarget).data('name') || null; - + this.removeFilters(); - - this.advanced = this.getPresetData(); + + this.advanced = this.getPresetData(); this.updateSearch(); - + this.managePresetFilters(); - + this.createFilters(function () { this.render(); }.bind(this)); @@ -185,17 +185,17 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { }, 'click .advanced-filters-bar a[data-action="savePreset"]': function (e) { this.createView('savePreset', 'Modals.SaveFilters', {}, function (view) { - view.render(); + view.render(); this.listenToOnce(view, 'save', function (name) { this.savePreset(name); view.close(); - + this.removeFilters(); this.createFilters(function () { this.render(); - }.bind(this)); + }.bind(this)); - }, this); + }, this); }.bind(this)); }, 'click .advanced-filters-bar a[data-action="removePreset"]': function (e) { @@ -209,19 +209,19 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { this.search(); } }, - + removeFilters: function () { - this.$advancedFiltersPanel.empty(); - for (var name in this.advanced) { + this.$advancedFiltersPanel.empty(); + for (var name in this.advanced) { this.clearView('filter-' + name); } }, - + resetFilters: function () { this.trigger('reset'); this.removeFilters(); - + this.presetName = null; this.searchManager.reset(); @@ -230,74 +230,74 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { this.render(); this.updateCollection(); }, - + savePreset: function (name) { var id = 'f' + (Math.floor(Math.random() * 1000001)).toString(); - + this.fetch(); this.updateSearch(); - + var presetFilters = this.getPreferences().get('presetFilters') || {}; if (!(this.scope in presetFilters)) { presetFilters[this.scope] = []; } - + var data = { id: id, name: id, label: name, data: this.advanced }; - + presetFilters[this.scope].push(data); - + this.presetFilters.push(data); - + this.getPreferences().set('presetFilters', presetFilters); this.getPreferences().save({patch: true}); this.getPreferences().trigger('update'); - - this.presetName = id; + + this.presetName = id; }, - - removePreset: function (id) { + + removePreset: function (id) { var presetFilters = this.getPreferences().get('presetFilters') || {}; if (!(this.scope in presetFilters)) { presetFilters[this.scope] = []; } - + var list; list = presetFilters[this.scope]; list.forEach(function (item, i) { if (item.id == id) { list.splice(i, 1); - return; + return; } }, this); - + list = this.presetFilters; list.forEach(function (item, i) { if (item.id == id) { list.splice(i, 1); - return; + return; } - }, this); + }, this); + - this.getPreferences().set('presetFilters', presetFilters); this.getPreferences().save({patch: true}); - this.getPreferences().trigger('update'); - + this.getPreferences().trigger('update'); + this.presetName = null; this.advanced = {}; - + this.removeFilters(); - + this.render(); this.updateSearch(); this.updateCollection(); }, - + updateAddFilterButton: function () { var $ul = this.$el.find('ul.filter-list'); if ($ul.children().not('.hide').size() == 0) { @@ -309,22 +309,22 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { afterRender: function () { this.updateAddFilterButton(); - + this.$advancedFiltersBar = this.$el.find('.advanced-filters-bar'); this.$advancedFiltersPanel = this.$el.find('.advanced-filters'); - + this.managePresetFilters(); }, - - managePresetFilters: function () { + + managePresetFilters: function () { var name = this.presetName || null; var data = this.getPresetData(); - + this.$el.find('ul.filter-menu a.preset span').remove(); - + if (name) { this.$advancedFiltersPanel.addClass('hidden'); - + var label = null; var style = null; var id = null; @@ -335,33 +335,33 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { id = item.id; return; } - }, this); - label = label || this.translate(this.presetName, 'presetFilters', this.scope); - + }, this); + label = label || this.translate(this.presetName, 'presetFilters', this.scope); + var barContentHtml = '' + label + ''; if (id) { barContentHtml += ' '; } - - this.$advancedFiltersBar.removeClass('hidden').html(barContentHtml); + + this.$advancedFiltersBar.removeClass('hidden').html(barContentHtml); } else { - this.$advancedFiltersPanel.removeClass('hidden'); - - if (Object.keys(this.advanced).length !== 0) { - if (!this.disableSavePreset) { + this.$advancedFiltersPanel.removeClass('hidden'); + + if (Object.keys(this.advanced).length !== 0) { + if (!this.disableSavePreset) { var barHtml = '' + this.translate('Save Filters') + ''; - this.$advancedFiltersBar.removeClass('hidden').html(barHtml); + this.$advancedFiltersBar.removeClass('hidden').html(barHtml); } else { this.$advancedFiltersBar.addClass('hidden').empty(); - } + } return; } else { - this.$advancedFiltersBar.addClass('hidden').empty(); - } + this.$advancedFiltersBar.addClass('hidden').empty(); + } } name = name || ''; - + this.$el.find('ul.filter-menu a.preset[data-name="'+name+'"]').prepend(''); }, @@ -389,7 +389,7 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { this.collection.fetch(); }, - + getPresetData: function () { var data = {}; this.presetFilters.forEach(function (item) { @@ -404,11 +404,11 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { loadSearchData: function () { var searchData = this.searchManager.get(); this.textFilter = searchData.textFilter; - + if ('presetName' in searchData) { - this.presetName = searchData.presetName; - } - + this.presetName = searchData.presetName; + } + if (this.presetName) { this.advanced = this.getPresetData(); } else { @@ -447,7 +447,7 @@ Espo.define('Views.Record.Search', 'View', function (Dep) { this.textFilter = this.$el.find('input[name="textFilter"]').val(); this.bool = {}; - + this.boolFilters.forEach(function (name) { this.bool[name] = this.$el.find('input[name="' + name + '"]').prop('checked'); }, this);