diff --git a/client/src/views/record/base.js b/client/src/views/record/base.js index 2ca4c35bad..2b69eb9487 100644 --- a/client/src/views/record/base.js +++ b/client/src/views/record/base.js @@ -70,6 +70,7 @@ function (Dep, ViewRecordHelper, DynamicLogic, _) { /** * Dynamic logic. * + * @protected * @type {Object} */ dynamicLogicDefs: {}, @@ -462,9 +463,10 @@ function (Dep, ViewRecordHelper, DynamicLogic, _) { /** * Get field views. * + * @param {boolean} [withHidden] With hidden. * @return {Object.} */ - getFieldViews: function () { + getFieldViews: function (withHidden) { var fields = {}; this.fieldList.forEach(item => { @@ -1408,6 +1410,7 @@ function (Dep, ViewRecordHelper, DynamicLogic, _) { /** * Create a field view. * + * @protected * @param {string} name A field name. * @param {string|null} [view] A view name/path. * @param {Object} [params] Field params. diff --git a/client/src/views/record/detail-bottom.js b/client/src/views/record/detail-bottom.js index 674f0ab393..6a98447975 100644 --- a/client/src/views/record/detail-bottom.js +++ b/client/src/views/record/detail-bottom.js @@ -26,9 +26,17 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/detail-bottom', 'views/record/panels-container', function (Dep) { +define('views/record/detail-bottom', ['views/record/panels-container'], function (Dep) { - return Dep.extend({ + /** + * A detail-bottom record view. + * + * @class + * @name Class + * @extends module:views/record/panels-container.Class + * @memberOf module:views/record/detail-bottom + */ + return Dep.extend(/** @lends module:views/record/detail-bottom.Class# */{ template: 'record/bottom', @@ -42,6 +50,9 @@ define('views/record/detail-bottom', 'views/record/panels-container', function ( portalLayoutDisabled: false, + /** + * @inheritDoc + */ setupPanels: function () { var scope = this.scope; @@ -62,6 +73,9 @@ define('views/record/detail-bottom', 'views/record/panels-container', function ( } }, + /** + * Set up a stream panel. + */ setupStreamPanel: function () { var streamAllowed = this.getAcl().checkModel(this.model, 'stream', true); @@ -119,7 +133,7 @@ define('views/record/detail-bottom', 'views/record/panels-container', function ( this.wait(true); Promise.all([ - new Promise((resolve) => { + new Promise(resolve => { this.getHelper().layoutManager.get( this.scope, 'bottomPanels' + Espo.Utils.upperCaseFirst(this.type), @@ -133,7 +147,7 @@ define('views/record/detail-bottom', 'views/record/panels-container', function ( ]).then(() => { var panelNameList = []; - this.panelList = this.panelList.filter((p) => { + this.panelList = this.panelList.filter(p => { panelNameList.push(p.name); if (p.aclScope) { @@ -187,19 +201,23 @@ define('views/record/detail-bottom', 'views/record/panels-container', function ( }); this.alterPanels(); - this.setupPanelsFinal(); - this.setupPanelViews(); this.wait(false); }); }, + /** + * Set read-only. + */ setReadOnly: function () { this.readOnly = true; }, + /** + * @private + */ addRelationshipPanel: function (name, item) { var scope = this.scope; var scopesDefs = this.getMetadata().get('scopes') || {}; @@ -220,7 +238,7 @@ define('views/record/detail-bottom', 'views/record/panels-container', function ( if (typeof p.order === 'undefined') p.order = 5; - var name = p.name; + name = p.name; var links = (this.model.defs || {}).links || {}; if (!(name in links)) { @@ -261,6 +279,5 @@ define('views/record/detail-bottom', 'views/record/panels-container', function ( this.panelList.push(p); }, - }); }); diff --git a/client/src/views/record/detail-middle.js b/client/src/views/record/detail-middle.js index 4358b0b7b2..358ec280cc 100644 --- a/client/src/views/record/detail-middle.js +++ b/client/src/views/record/detail-middle.js @@ -26,9 +26,17 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/detail-middle', 'view', function (Dep) { +define('views/record/detail-middle', ['view'], function (Dep) { - return Dep.extend({ + /** + * A detail-middle record view. + * + * @class + * @name Class + * @extends module:view.Class + * @memberOf module:views/record/detail-middle + */ + return Dep.extend(/** @lends module:views/record/detail-middle.Class# */{ init: function () { this.recordHelper = this.options.recordHelper; @@ -42,6 +50,11 @@ define('views/record/detail-middle', 'view', function (Dep) { }; }, + /** + * Show a panel. + * + * @param {string} name + */ showPanel: function (name) { if (this.recordHelper.getPanelStateParam(name, 'hiddenLocked')) { return; @@ -52,6 +65,10 @@ define('views/record/detail-middle', 'view', function (Dep) { this.recordHelper.setPanelStateParam(name, 'hidden', false); }, + /** + * @private + * @param {string} name + */ showPanelInternal: function (name) { if (this.isRendered()) { this.$el.find('.panel[data-name="'+name+'"]').removeClass('hidden'); @@ -76,18 +93,32 @@ define('views/record/detail-middle', 'view', function (Dep) { } }, + /** + * Hide a panel. + * + * @param {string} name + */ hidePanel: function (name) { this.hidePanelInternal(name); this.recordHelper.setPanelStateParam(name, 'hidden', true); }, + /** + * @private + * @param {string} name + */ hidePanelInternal: function (name) { if (this.isRendered()) { this.$el.find('.panel[data-name="'+name+'"]').addClass('hidden'); } }, + /** + * Hide a field. + * + * @param {string} name + */ hideField: function (name) { this.recordHelper.setFieldStateParam(name, 'hidden', true); @@ -126,6 +157,11 @@ define('views/record/detail-middle', 'view', function (Dep) { } }, + /** + * Show a field. + * + * @param {string} name + */ showField: function (name) { if (this.recordHelper.getFieldStateParam(name, 'hiddenLocked')) { return; @@ -170,10 +206,18 @@ define('views/record/detail-middle', 'view', function (Dep) { } }, + /** + * @deprecated Use `getFieldViews`. + */ getFields: function () { return this.getFieldViews(); }, + /** + * Get field views. + * + * @return {{string: module:views/fields/base.Class}} + */ getFieldViews: function () { var fieldViews = {}; @@ -186,11 +230,21 @@ define('views/record/detail-middle', 'view', function (Dep) { return fieldViews; }, + /** + * Get a field view. + * + * @param {string} name A field name. + * @return {module:views/fields/base.Class} + */ getFieldView: function (name) { return (this.getFieldViews() || {})[name]; }, - // @todo remove + /** + * For backward compatibility. + * + * @todo Remove. + */ getView: function (name) { var view = Dep.prototype.getView.call(this, name); @@ -200,6 +254,5 @@ define('views/record/detail-middle', 'view', function (Dep) { return view; }, - }); }); diff --git a/client/src/views/record/detail-side.js b/client/src/views/record/detail-side.js index 5818eba76c..606f2bb4bb 100644 --- a/client/src/views/record/detail-side.js +++ b/client/src/views/record/detail-side.js @@ -26,9 +26,17 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/detail-side', 'views/record/panels-container', function (Dep) { +define('views/record/detail-side', ['views/record/panels-container'], function (Dep) { - return Dep.extend({ + /** + * A detail-side record view. + * + * @class + * @name Class + * @extends module:views/record/panels-container.Class + * @memberOf module:views/record/detail-side + */ + return Dep.extend(/** @lends module:views/record/detail-side.Class# */{ template: 'record/side', @@ -40,8 +48,19 @@ define('views/record/detail-side', 'views/record/panels-container', function (De defaultPanel: true, + /** + * A panel list. + * + * @protected + * @type {module:views/record/panels-container~panel[]} + */ panelList: [], + /** + * A default panel. + * + * @type {module:views/record/panels-container~panel} + */ defaultPanelDefs: { name: 'default', label: false, @@ -54,8 +73,8 @@ define('views/record/detail-side', 'views/record/panels-container', function (De }, { name: 'teams' - } - ] + }, + ], } }, @@ -74,8 +93,10 @@ define('views/record/detail-side', 'views/record/panels-container', function (De this.recordViewObject = this.options.recordViewObject; }, - setupPanels: function () { - }, + /** + * @inheritDoc + */ + setupPanels: function () {}, setup: function () { this.type = this.mode; @@ -171,6 +192,11 @@ define('views/record/detail-side', 'views/record/panels-container', function (De ); }, + /** + * Set up a default panel. + * + * @protected + */ setupDefaultPanel: function () { var met = false; @@ -251,6 +277,5 @@ define('views/record/detail-side', 'views/record/panels-container', function (De this.panelList.unshift(defaultPanelDefs); }, - }); }); diff --git a/client/src/views/record/detail-small.js b/client/src/views/record/detail-small.js index 05500602d7..eb5ff551a9 100644 --- a/client/src/views/record/detail-small.js +++ b/client/src/views/record/detail-small.js @@ -25,13 +25,11 @@ * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -Espo.define('views/record/detail-small', 'views/record/detail', function (Dep) { + +define('views/record/detail-small', ['views/record/detail'], function (Dep) { return Dep.extend({ - bottomView: null - + bottomView: null, }); - }); - diff --git a/client/src/views/record/detail.js b/client/src/views/record/detail.js index 6a8249c4cb..87a781cf67 100644 --- a/client/src/views/record/detail.js +++ b/client/src/views/record/detail.js @@ -29,100 +29,299 @@ define('views/record/detail', ['views/record/base', 'view-record-helper'], function (Dep, ViewRecordHelper) { - return Dep.extend({ + /** + * A detail record view. + * + * @class + * @name Class + * @extends module:views/record/base.Class + * @memberOf module:views/record/detail + */ + return Dep.extend(/** @lends module:views/record/detail.Class# */{ + /** + * @inheritDoc + */ template: 'record/detail', + /** + * @inheritDoc + */ type: 'detail', + /** + * Not used. + * + * @deprecated + * @protected + */ name: 'detail', + /** + * A layout name. Can be overridden by an option parameter. + * + * @protected + * @type {string} + */ layoutName: 'detail', - fieldsMode: 'detail', - - mode: 'detail', - - gridLayout: null, - + /** + * A layout. If null, then will be loaded from the backend (using the `layoutName` value). + * Can be overridden by an option parameter. + * + * @protected + * @type {Object[]|null} + */ detailLayout: null, + /** + * A fields mode. + * + * @protected + * @type {'detail'|'edit'|'list'} + */ + fieldsMode: 'detail', + + /** + * A current mode. Only for reading. + * + * @protected + * @type {'detail'|'edit'} + */ + mode: 'detail', + + /** + * @private + */ + gridLayout: null, + + /** + * Disable buttons. Can be overridden by an option parameter. + * + * @protected + * @type {boolean} + */ buttonsDisabled: false, - scope: null, - + /** + * Is record new. Only for reading. + * + * @protected + */ isNew: false, + /** + * A button. Handled by an `action{Name}` method. + * + * @typedef module:views/record/detail~button + * + * @property {string} name A name. + * @property {string} [label] A label. + * @property {string} [html] An HTML. + * @property {'default'|'danger'|'success'|'warning'} [style] A style. + * @property {boolean} [hidden] Hidden. + */ + + /** + * A dropdown item. Handled by an `action{Name}` method. + * + * @typedef module:views/record/detail~dropdownItem + * + * @property {string} name A name. + * @property {string} [label] A label. + * @property {string} [html] An HTML. + * @property {boolean} [hidden] Hidden. + */ + + /** + * A button list. + * + * @protected + * @type {module:views/record/detail~button[]} + */ buttonList: [ { name: 'edit', label: 'Edit', - } + }, ], + /** + * A dropdown item list. + * + * @protected + * @type {module:views/record/detail~dropdownItem[]} + */ dropdownItemList: [ { name: 'delete', - label: 'Remove' - } + label: 'Remove', + }, ], + /** + * A button list for edit mode. + * + * @protected + * @type {module:views/record/detail~button[]} + */ buttonEditList: [ { name: 'save', label: 'Save', style: 'primary', - edit: true + edit: true, }, { name: 'cancelEdit', label: 'Cancel', - edit: true - } + edit: true, + }, ], + /** + * A dropdown item list for edit mode. + * + * @protected + * @type {module:views/record/detail~dropdownItem[]} + */ dropdownEditItemList: [], + /** + * An DOM element ID. Only for reading. + * + * @private + * @type {string} + */ id: null, + /** + * A return-URL. Can be overridden by an option parameter. + * + * @protected + * @type {string|null} + */ returnUrl: null, + /** + * A return dispatch params. Can be overridden by an option parameter. + * + * @protected + * @type {Object|null} + */ returnDispatchParams: null, + /** + * A middle view name. + * + * @protected + */ middleView: 'views/record/detail-middle', + /** + * A side view name. + * + * @protected + */ sideView: 'views/record/detail-side', + /** + * A bottom view name. + * + * @protected + */ bottomView: 'views/record/detail-bottom', + /** + * Disable a side view. Can be overridden by an option parameter. + * + * @protected + */ sideDisabled: false, + /** + * Disable a bottom view. Can be overridden by an option parameter. + * + * @protected + */ bottomDisabled: false, + /** + * Disable edit mode. Can be overridden by an option parameter. + * + * @protected + */ editModeDisabled: false, + /** + * Disable navigate (prev, next) buttons. Can be overridden by an option parameter. + * + * @protected + */ navigateButtonsDisabled: false, + /** + * Read-only. Can be overridden by an option parameter. + */ readOnly: false, + /** + * Middle view expanded to full width (no side view). + * Can be overridden by an option parameter. + * + * @protected + */ isWide: false, - dependencyDefs: {}, - + /** + * Enable a duplicate action. + * + * @protected + */ duplicateAction: true, + /** + * Enable a self-assign action. + * + * @protected + */ selfAssignAction: false, - inlineEditDisabled: false, - + /** + * Enable a print-pdf action. + * + * @protected + */ printPdfAction: true, - portalLayoutDisabled: false, - + /** + * Enable a convert-currency action. + * + * @protected + */ convertCurrencyAction: true, + /** + * Enable a save-and-continue-editing action. + * + * @protected + */ saveAndContinueEditingAction: true, + /** + * Disable the inline-edit. Can be overridden by an option parameter. + * + * @protected + */ + inlineEditDisabled: false, + + /** + * Disable a portal layout usage. Can be overridden by an option parameter. + * + * @protected + */ + portalLayoutDisabled: false, + /** * A panel soft-locked type. * @@ -141,19 +340,42 @@ function (Dep, ViewRecordHelper) { 'dynamicLogic', ], + /** + * Dynamic logic. Can be overridden by an option parameter. + * + * @protected + * @type {Object} + */ + dynamicLogicDefs: {}, + + /** + * Disable confirm leave-out processing. + * + * @protected + */ confirmLeaveDisabled: false, + /** + * @protected + */ setupHandlerType: 'record/detail', + /** + * @inheritDoc + */ events: { 'click .button-container .action': function (e) { Espo.Utils.handleAction(this, e); }, + /** @this module:views/record/detail.Class */ 'click [data-action="showMoreDetailPanels"]': function () { this.showMoreDetailPanels(); }, }, + /** + * An `edit` action. + */ actionEdit: function () { if (!this.editModeDisabled) { this.setEditMode(); @@ -180,6 +402,11 @@ function (Dep, ViewRecordHelper) { this.delete(); }, + /** + * A `save` action. + * + * @param {{options?: module:views/record/base~saveOptions}} [data] Data. + */ actionSave: function (data) { data = data || {}; @@ -205,6 +432,9 @@ function (Dep, ViewRecordHelper) { $(window).scrollTop(0); }, + /** + * A `save-and-continue-editing` action. + */ actionSaveAndContinueEditing: function (data) { data = data || {}; @@ -212,6 +442,9 @@ function (Dep, ViewRecordHelper) { .catch(() => {}); }, + /** + * A `self-assign` action. + */ actionSelfAssign: function () { var attributes = { assignedUserId: this.getUser().id, @@ -235,14 +468,17 @@ function (Dep, ViewRecordHelper) { }); }, + /** + * A `convert-currency` action. + */ actionConvertCurrency: function () { this.createView('modalConvertCurrency', 'views/modals/convert-currency', { entityType: this.entityType, model: this.model, - }, (view) => { + }, view => { view.render(); - this.listenToOnce(view, 'after:update', (attributes) => { + this.listenToOnce(view, 'after:update', attributes => { var isChanged = false; for (var a in attributes) { @@ -268,17 +504,29 @@ function (Dep, ViewRecordHelper) { }); }, + /** + * Compose attribute values for a self-assignment. + * + * @protected + * @return {Object.|null} + */ getSelfAssignAttributes: function () { + return null; }, + /** + * Set up action items. + * + * @protected + */ setupActionItems: function () { if (this.model.isNew()) { this.isNew = true; - this.removeButton('delete'); + this.removeActionItem('delete'); } else if (this.getMetadata().get(['clientDefs', this.scope, 'removeDisabled'])) { - this.removeButton('delete'); + this.removeActionItem('delete'); } if (this.duplicateAction) { @@ -453,14 +701,26 @@ function (Dep, ViewRecordHelper) { } }, + /** + * Disable action items. + */ disableActionItems: function () { this.disableButtons(); }, + /** + * Enable action items. + */ enableActionItems: function () { this.enableButtons(); }, + /** + * Hide a button or dropdown action item. + * + * @protected + * @param {string} name A name. + */ hideActionItem: function (name) { for (var i in this.buttonList) { if (this.buttonList[i].name === name) { @@ -514,6 +774,12 @@ function (Dep, ViewRecordHelper) { } }, + /** + * Show a button or dropdown action item. + * + * @protected + * @param {string} name A name. + */ showActionItem: function (name) { for (var i in this.buttonList) { if (this.buttonList[i].name === name) { @@ -1110,7 +1376,6 @@ function (Dep, ViewRecordHelper) { this.scope = this.options.scope || this.entityType; this.layoutName = this.options.layoutName || this.layoutName; - this.detailLayout = this.options.detailLayout || this.detailLayout; this.type = this.options.type || this.type; @@ -1171,6 +1436,9 @@ function (Dep, ViewRecordHelper) { throw new Error('Model has not been injected into record view.'); } + /** + * @inheritDoc + */ this.recordHelper = new ViewRecordHelper(this.defaultFieldStates, this.defaultFieldStates); this._initInlineEditSave(); @@ -1751,7 +2019,7 @@ function (Dep, ViewRecordHelper) { this.notify('Saved', 'success'); } - this.enableButtons(); + this.enableActionItems(); this.setIsNotChanged(); @@ -1767,11 +2035,11 @@ function (Dep, ViewRecordHelper) { }, beforeBeforeSave: function () { - this.disableButtons(); + this.disableActionItems(); }, afterSaveError: function () { - this.enableButtons(); + this.enableActionItems(); }, afterNotModified: function () { @@ -1779,14 +2047,14 @@ function (Dep, ViewRecordHelper) { Espo.Ui.warning(msg, 'warning'); - this.enableButtons(); + this.enableActionItems(); this.setIsNotChanged(); }, afterNotValid: function () { this.notify('Not valid', 'error'); - this.enableButtons(); + this.enableActionItems(); }, errorHandlerDuplicate: function (duplicates) { @@ -2089,6 +2357,9 @@ function (Dep, ViewRecordHelper) { this.buttonEditList[method](o); }, + /** + * @deprecated Use `enableActionItems`. + */ enableButtons: function () { this.$el.find(".button-container .actions-btn-group .action") .removeAttr('disabled') @@ -2099,6 +2370,9 @@ function (Dep, ViewRecordHelper) { .removeClass('disabled'); }, + /** + * @deprecated Use `disableActionItems`. + */ disableButtons: function () { this.$el.find(".button-container .actions-btn-group .action") .attr('disabled', 'disabled') @@ -2109,6 +2383,20 @@ function (Dep, ViewRecordHelper) { .addClass('disabled'); }, + /** + * Remove a button or dropdown item. + * + * @param {string} name A name. + */ + removeActionItem: function (name) { + this.removeButton(name); + }, + + /** + * @deprecated Use `removeActionItem`. + * + * @param {string} name A name. + */ removeButton: function (name) { for (var i in this.buttonList) { if (this.buttonList[i].name === name) { @@ -2131,6 +2419,13 @@ function (Dep, ViewRecordHelper) { } }, + /** + * Convert a detail layout to an internal layout. + * + * @protected + * @param {Object[]} simplifiedLayout A detail layout. + * @return {Object[]} + */ convertDetailLayout: function (simplifiedLayout) { var layout = []; @@ -2353,6 +2648,10 @@ function (Dep, ViewRecordHelper) { return layout; }, + /** + * @private + * @param {function(Object[]): void}callback + */ getGridLayout: function (callback) { if (this.gridLayout !== null) { callback(this.gridLayout); @@ -2372,9 +2671,9 @@ function (Dep, ViewRecordHelper) { return; } - this._helper.layoutManager.get(this.model.name, this.layoutName, (simpleLayout) => { + this.getHelper().layoutManager.get(this.model.name, this.layoutName, (simpleLayout) => { if (typeof this.modifyDetailLayout === 'function') { - var simpleLayout = Espo.Utils.cloneDeep(simpleLayout); + simpleLayout = Espo.Utils.cloneDeep(simpleLayout); this.modifyDetailLayout(simpleLayout); } @@ -2388,6 +2687,11 @@ function (Dep, ViewRecordHelper) { }); }, + /** + * Create a side view. + * + * @protected + */ createSideView: function () { var el = this.options.el || '#' + (this.id); @@ -2403,6 +2707,11 @@ function (Dep, ViewRecordHelper) { }); }, + /** + * Create a middle view. + * + * @protected + */ createMiddleView: function (callback) { var el = this.options.el || '#' + (this.id); @@ -2425,6 +2734,11 @@ function (Dep, ViewRecordHelper) { }); }, + /** + * Create a bottom view. + * + * @protected + */ createBottomView: function () { var el = this.options.el || '#' + (this.id); @@ -2441,6 +2755,12 @@ function (Dep, ViewRecordHelper) { }); }, + /** + * Create views. + * + * @protected + * @param {function(module:views/record/detail-middle): void} callback + */ build: function (callback) { if (!this.sideDisabled && this.sideView) { this.createSideView(); @@ -2455,6 +2775,11 @@ function (Dep, ViewRecordHelper) { } }, + /** + * Called after create. + * + * @return {boolean} True if redirecting is processed. + */ exitAfterCreate: function () { if (!this.returnAfterCreate && this.model.id) { var url = '#' + this.scope + '/view/' + this.model.id; @@ -2469,6 +2794,8 @@ function (Dep, ViewRecordHelper) { return true; } + + return false; }, /** @@ -2490,6 +2817,7 @@ function (Dep, ViewRecordHelper) { } var url; + var options; if (this.returnUrl) { url = this.returnUrl; @@ -2512,7 +2840,7 @@ function (Dep, ViewRecordHelper) { if (!this.returnDispatchParams) { this.getRouter().navigate(url, {trigger: false}); - var options = { + options = { id: this.model.id, model: this.model, }; @@ -2532,7 +2860,7 @@ function (Dep, ViewRecordHelper) { if (this.returnDispatchParams) { var controller = this.returnDispatchParams.controller; var action = this.returnDispatchParams.action; - var options = this.returnDispatchParams.options || {}; + options = this.returnDispatchParams.options || {}; this.getRouter().navigate(url, {trigger: false}); this.getRouter().dispatch(controller, action, options); @@ -2547,7 +2875,6 @@ function (Dep, ViewRecordHelper) { var topic = 'recordUpdate.' + this.entityType + '.' + this.model.id; this.recordUpdateWebSocketTopic = topic; - this.isSubscribedToWebSocked = true; this.getHelper().webSocketManager.subscribe(topic, (t, data) => { @@ -2576,9 +2903,11 @@ function (Dep, ViewRecordHelper) { this.updatedAttributes = Espo.Utils.cloneDeep(m.attributes); } }); - } else { - this.model.fetch({highlight: true}); + + return; } + + this.model.fetch({highlight: true}); }, blockUpdateWebSocket: function (toUnblock) { @@ -2595,10 +2924,13 @@ function (Dep, ViewRecordHelper) { this.updateWebSocketIsBlocked = false; }, + /** + * Show more detail panels. + */ showMoreDetailPanels: function () { this.hidePanel('showMoreDelimiter'); - this.underShowMoreDetailPanelList.forEach((item) => { + this.underShowMoreDetailPanelList.forEach(item => { this.showPanel(item); }); }, diff --git a/client/src/views/record/edit-for-modal.js b/client/src/views/record/edit-for-modal.js index eb85921fe6..b4a133c6d5 100644 --- a/client/src/views/record/edit-for-modal.js +++ b/client/src/views/record/edit-for-modal.js @@ -28,7 +28,15 @@ define('views/record/edit-for-modal', ['views/record/edit'], function (Dep) { - return Dep.extend({ + /** + * An edit-record view to used for custom forms. + * + * @class + * @name Class + * @extends module:views/record/edit.Class + * @memberOf module:views/record/edit-for-modal + */ + return Dep.extend(/** @lends module:views/record/edit-for-modal.Class# */{ bottomView: null, diff --git a/client/src/views/record/edit.js b/client/src/views/record/edit.js index 7d0d648a9e..f35a5aabea 100644 --- a/client/src/views/record/edit.js +++ b/client/src/views/record/edit.js @@ -28,18 +28,44 @@ define('views/record/edit', ['views/record/detail'], function (Dep) { - return Dep.extend({ + /** + * An edit-record view. Used for create and edit. + * + * @class + * @name Class + * @extends module:views/record/detail.Class + * @memberOf module:views/record/edit + */ + return Dep.extend(/** @lends module:views/record/edit.Class# */{ + /** + * @inheritDoc + */ template: 'record/edit', + /** + * @inheritDoc + */ type: 'edit', + /** + * @inheritDoc + */ name: 'edit', + /** + * @inheritDoc + */ fieldsMode: 'edit', + /** + * @inheritDoc + */ mode: 'edit', + /** + * @inheritDoc + */ buttonList: [ { name: 'save', @@ -52,20 +78,44 @@ define('views/record/edit', ['views/record/detail'], function (Dep) { } ], + /** + * @inheritDoc + */ dropdownItemList: [], + /** + * @inheritDoc + */ sideView: 'views/record/edit-side', + /** + * @inheritDoc + */ bottomView: 'views/record/edit-bottom', + /** + * @inheritDoc + */ duplicateAction: false, + /** + * @inheritDoc + */ saveAndContinueEditingAction: true, + /** + * @inheritDoc + */ saveAndNewAction: true, + /** + * @inheritDoc + */ setupHandlerType: 'record/edit', + /** + * @inheritDoc + */ actionSave: function (data) { var isNew = this.isNew; @@ -78,10 +128,16 @@ define('views/record/edit', ['views/record/detail'], function (Dep) { .catch(function () {}); }, + /** + * A `cancel` action. + */ actionCancel: function () { this.cancel(); }, + /** + * Cancel. + */ cancel: function () { if (this.isChanged) { this.resetModelChanges(); @@ -91,6 +147,9 @@ define('views/record/edit', ['views/record/detail'], function (Dep) { this.exit('cancel'); }, + /** + * @inheritDoc + */ setupBeforeFinal: function () { if (this.model.isNew()) { this.populateDefaults(); @@ -99,6 +158,9 @@ define('views/record/edit', ['views/record/detail'], function (Dep) { Dep.prototype.setupBeforeFinal.call(this); }, + /** + * @inheritDoc + */ setupActionItems: function () { Dep.prototype.setupActionItems.call(this); @@ -124,6 +186,9 @@ define('views/record/edit', ['views/record/detail'], function (Dep) { } }, + /** + * A `save-and-create-new` action. + */ actionSaveAndNew: function (data) { data = data || {}; @@ -145,6 +210,5 @@ define('views/record/edit', ['views/record/detail'], function (Dep) { proceedCallback(); } }, - }); }); diff --git a/client/src/views/record/kanban.js b/client/src/views/record/kanban.js index 8c5e2ca8c3..f2e79b3a1f 100644 --- a/client/src/views/record/kanban.js +++ b/client/src/views/record/kanban.js @@ -28,7 +28,15 @@ define('views/record/kanban', ['views/record/list'], function (Dep) { - return Dep.extend({ + /** + * A kanban record view. + * + * @class + * @name Class + * @extends module:views/record/list.Class + * @memberOf module:views/record/kanban + */ + return Dep.extend(/** @lends module:views/record/kanban.Class# */{ template: 'record/kanban', @@ -1132,6 +1140,5 @@ define('views/record/kanban', ['views/record/list'], function (Dep) { this.sortWasCentered = true; } }, - }); }); diff --git a/client/src/views/record/list.js b/client/src/views/record/list.js index d64322be0b..fbd876420e 100644 --- a/client/src/views/record/list.js +++ b/client/src/views/record/list.js @@ -39,6 +39,17 @@ function (Dep, MassActionHelper, ExportHelper) { */ return Dep.extend(/** @lends module:views/record/list.Class# */{ + /** + * A row action. + * + * @typedef {Object} module:views/record/list~rowAction + * + * @property {string} action An action. + * @property {string} [label] A label. + * @property {string} [link] A link. + * @property {Object.} [data] Data attributes. + */ + /** * @inheritDoc */ @@ -186,7 +197,7 @@ function (Dep, MassActionHelper, ExportHelper) { massActionsDisabled: false, /** - * Can be overridden by an option parameter. + * Disable a portal layout usage. Can be overridden by an option parameter. * * @protected */ @@ -304,7 +315,7 @@ function (Dep, MassActionHelper, ExportHelper) { /** * A list layout. Can be overridden by an option parameter. - * If empty, then will be loaded from the backend (using the `layoutName` value). + * If null, then will be loaded from the backend (using the `layoutName` value). * * @protected * @type {Object[]|null} diff --git a/client/src/views/record/panel-actions.js b/client/src/views/record/panel-actions.js index ddf4ac1bd3..2e983c78df 100644 --- a/client/src/views/record/panel-actions.js +++ b/client/src/views/record/panel-actions.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/panel-actions', 'view', function (Dep) { +define('views/record/panel-actions', ['view'], function (Dep) { return Dep.extend({ @@ -38,7 +38,7 @@ define('views/record/panel-actions', 'view', function (Dep) { buttonList: this.getButtonList(), actionList: this.getActionList(), entityType: this.options.entityType, - scope: this.options.scope + scope: this.options.scope, }; }, @@ -49,21 +49,30 @@ define('views/record/panel-actions', 'view', function (Dep) { getButtonList: function () { var list = []; - this.buttonList.forEach(function (item) { - if (item.hidden) return; + + this.buttonList.forEach(item => { + if (item.hidden) { + return; + } + list.push(item); - }, this); + }); + return list; }, getActionList: function () { var list = []; - this.actionList.forEach(function (item) { - if (item.hidden) return; + + this.actionList.forEach(item => { + if (item.hidden) { + return; + } + list.push(item); - }, this); + }); + return list; }, - }); }); diff --git a/client/src/views/record/panels-container.js b/client/src/views/record/panels-container.js index 66048799a5..965de18ef6 100644 --- a/client/src/views/record/panels-container.js +++ b/client/src/views/record/panels-container.js @@ -26,12 +26,76 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/panels-container', 'view', function (Dep) { +define('views/record/panels-container', ['view'], function (Dep) { - return Dep.extend({ + /** + * A detail record view. + * + * @class + * @name Class + * @extends module:view.Class + * @memberOf module:views/record/panels-container + */ + return Dep.extend(/** @lends module:views/record/panels-container.Class# */{ + /** + * @private + */ panelSoftLockedTypeList: ['default', 'acl', 'delimiter', 'dynamicLogic'], + /** + * A panel. + * + * @typedef {Object} module:views/record/panels-container~panel + * + * @property {string} name A name. + * @property {boolean} [hidden] Hidden. + * @property {string} [label] A label. + * @property {'default'|'success'|'danger'|'warning'} [style] A style. + * @property {string} [titleHtml] A title HTML. + * @property {boolean} [notRefreshable] Not refreshable. + * @property {boolean} [isForm] If for a form. + * @property {module:views/record/panels-container~button[]} [buttonList] Buttons. + * @property {module:views/record/panels-container~action[]} [actionList] Dropdown actions. + * @property {string} [view] A view name. + * @property {Object.} [Options] A view options. + * @property {boolean} [sticked] To stick to an upper panel. + */ + + /** + * A button. + * + * @typedef {Object} module:views/record/panels-container~button + * + * @property {string} action An action. + * @property {boolean} [hidden] Hidden. + * @property {string} [label] A label. Translatable. + * @property {string} [html] A HTML. + * @property {string} [title] A title (on hover). Translatable. + * @property {Object.} [data] Data attributes. + */ + + /** + * An action. + * + * @typedef {Object} module:views/record/panels-container~action + * + * @property {string} [action] An action. + * @property {string} [link] A link URL. + * @property {boolean} [hidden] Hidden. + * @property {string} [label] A label. Translatable. + * @property {string} [html] A HTML. + * @property {Object.} [data] Data attributes. + */ + + /** + * A panel list. + * + * @protected + * @type {module:views/record/panels-container~panel[]} + */ + panelList: null, + data: function () { return { panelList: this.panelList, @@ -64,15 +128,21 @@ define('views/record/panels-container', 'view', function (Dep) { 'click .panels-show-more-delimiter [data-action="showMorePanels"]': 'actionShowMorePanels', }, + /** + * Set read-only. + */ setReadOnly: function () { this.readOnly = true; }, + /** + * Set not read-only. + */ setNotReadOnly: function (onlyNotSetAsReadOnly) { this.readOnly = false; if (onlyNotSetAsReadOnly) { - this.panelList.forEach((item) => { + this.panelList.forEach(item => { this.applyAccessToActions(item.buttonList); this.applyAccessToActions(item.actionList); @@ -87,12 +157,16 @@ define('views/record/panels-container', 'view', function (Dep) { } }, + /** + * @private + * @param {Object[]} actionList + */ applyAccessToActions: function (actionList) { if (!actionList) { return; } - actionList.forEach((item) => { + actionList.forEach(item => { if (!Espo.Utils.checkActionAvailability(this.getHelper(), item)) { item.hidden = true; @@ -114,8 +188,13 @@ define('views/record/panels-container', 'view', function (Dep) { }); }, + /** + * Set up panel views. + * + * @protected + */ setupPanelViews: function () { - this.panelList.forEach((p) => { + this.panelList.forEach(p => { var name = p.name; var options = { @@ -169,12 +248,17 @@ define('views/record/panels-container', 'view', function (Dep) { }); }, + /** + * Set up panels. + * + * @protected + */ setupPanels: function () {}, getFieldViews: function (withHidden) { var fields = {}; - this.panelList.forEach((p) => { + this.panelList.forEach(p => { var panelView = this.getView(p.name); if ((!panelView.disabled || withHidden) && 'getFieldViews' in panelView) { @@ -210,7 +294,8 @@ define('views/record/panels-container', 'view', function (Dep) { if (softLockedType) { this.recordHelper - .setPanelStateParam(name, 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked', false); + .setPanelStateParam(name, 'hidden' + Espo.Utils.upperCaseFirst(softLockedType) + 'Locked', + false); } for (var i = 0; i < this.panelSoftLockedTypeList.length; i++) { @@ -473,6 +558,5 @@ define('views/record/panels-container', 'view', function (Dep) { callback.call(this); }); }, - }); }); diff --git a/client/src/views/record/panels/bottom.js b/client/src/views/record/panels/bottom.js index 08f006199f..2dc7dbf872 100644 --- a/client/src/views/record/panels/bottom.js +++ b/client/src/views/record/panels/bottom.js @@ -26,22 +26,55 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/panels/bottom', 'view', function (Dep) { +define('views/record/panels/bottom', ['view'], function (Dep) { - return Dep.extend({ + /** + * A bottom panel. + * + * @class + * @name Class + * @extends module:view.Class + * @memberOf module:views/record/panels/bottom + */ + return Dep.extend(/** module:views/record/panels/bottom.Class# */{ template: 'record/panels/side', + /** + * A field list. + * + * @protected + * @type {module:views/record/panels/side~field[]} + */ + fieldList: null, + + /** + * @protected + * @type {module:views/record/panels-container~action[]} + */ actionList: null, + /** + * @protected + * @type {module:views/record/panels-container~button[]} + */ buttonList: null, defs: null, + /** + * A mode. + * + * @protected + * @type {'list'|'detail'|'edit'} + */ mode: 'detail', - fieldList: null, - + /** + * Disable. + * + * @protected + */ disabled: false, events: { @@ -133,17 +166,32 @@ define('views/record/panels/bottom', 'view', function (Dep) { this.createFields(); }, - setupFields: function () { - }, + /** + * Set up fields. + * + * @protected + */ + setupFields: function () {}, + /** + * @return {module:views/record/panels-container~button[]} + */ getButtonList: function () { return this.buttonList || []; }, + /** + * @return {module:views/record/panels-container~action[]} + */ getActionList: function () { return this.actionList || []; }, + /** + * Get field views. + * + * @return {Object.} + */ getFieldViews: function () { var fields = {}; @@ -156,12 +204,20 @@ define('views/record/panels/bottom', 'view', function (Dep) { return fields; }, + /** + * @deprecated Use `getFieldViews`. + */ getFields: function () { return this.getFieldViews(); }, + /** + * Get a field list. + * + * @return {module:views/record/panels/side~field[]} + */ getFieldList: function () { - return this.fieldList.map((item) => { + return this.fieldList.map(item => { if (typeof item !== 'object') { return { name: item @@ -172,6 +228,9 @@ define('views/record/panels/bottom', 'view', function (Dep) { }); }, + /** + * @private + */ createFields: function () { this.getFieldList().forEach((item) => { var view = null; @@ -197,6 +256,17 @@ define('views/record/panels/bottom', 'view', function (Dep) { }); }, + /** + * Create a field view. + * + * @protected + * @param {string} field A field name. + * @param {string|null} [viewName] A view name/path. + * @param {Object} [params] Field params. + * @param {'detail'|'edit'|'list'|null} [mode='edit'] A mode. + * @param {boolean} [readOnly] Read-only. + * @param {Object} [options] View options. + */ createField: function (field, viewName, params, mode, readOnly, options) { var type = this.model.getFieldType(field) || 'base'; @@ -274,6 +344,5 @@ define('views/record/panels/bottom', 'view', function (Dep) { this.createView(viewKey, viewName, o); }, - }); }); diff --git a/client/src/views/record/panels/default-side.js b/client/src/views/record/panels/default-side.js index 4def9e0db4..4ca88b2019 100644 --- a/client/src/views/record/panels/default-side.js +++ b/client/src/views/record/panels/default-side.js @@ -26,17 +26,31 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/panels/default-side', 'views/record/panels/side', function (Dep) { +define('views/record/panels/default-side', ['views/record/panels/side'], function (Dep) { - return Dep.extend({ + /** + * A default side panel. + * + * @class + * @name Class + * @extends module:views/record/panels/side.Class + * @memberOf module:views/record/panels/default-side + */ + return Dep.extend(/** @lends module:views/record/panels/default-side.Class */{ data: function () { var data = Dep.prototype.data.call(this); - if (this.complexCreatedDisabled && this.complexModifiedDisabled || (!this.hasComplexCreated && !this.hasComplexModified)) { + + if ( + this.complexCreatedDisabled && + this.complexModifiedDisabled || (!this.hasComplexCreated && !this.hasComplexModified) + ) { data.complexDateFieldsDisabled = true; } + data.hasComplexCreated = this.hasComplexCreated; data.hasComplexModified = this.hasComplexModified; + return data; }, @@ -44,13 +58,11 @@ define('views/record/panels/default-side', 'views/record/panels/side', function this.fieldList = Espo.Utils.cloneDeep(this.fieldList); this.hasComplexCreated = - !!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'createdAt']) - && + !!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'createdAt']) && !!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'createdBy']); this.hasComplexModified = - !!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'modifiedAt']) - && + !!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'modifiedAt']) && !!this.getMetadata().get(['entityDefs', this.model.name, 'fields', 'modifiedBy']); Dep.prototype.setup.call(this); @@ -98,16 +110,23 @@ define('views/record/panels/default-side', 'views/record/panels/side', function } if (!this.complexCreatedDisabled && this.hasComplexCreated) { - this.listenTo(this.model, 'change:createdById', function () { - if (!this.model.get('createdById')) return; + this.listenTo(this.model, 'change:createdById', () => { + if (!this.model.get('createdById')) { + return; + } + this.recordViewObject.showField('complexCreated'); - }, this); + }); } + if (!this.complexModifiedDisabled && this.hasComplexModified) { - this.listenTo(this.model, 'change:modifiedById', function () { - if (!this.model.get('modifiedById')) return; + this.listenTo(this.model, 'change:modifiedById', () => { + if (!this.model.get('modifiedById')) { + return; + } + this.recordViewObject.showField('complexModified'); - }, this); + }); } if (this.getMetadata().get(['scopes', this.model.name ,'stream']) && !this.getUser().isPortal()) { @@ -118,7 +137,9 @@ define('views/record/panels/default-side', 'views/record/panels/side', function view: 'views/fields/followers', readOnly: true, }); + this.controlFollowersField(); + this.listenTo(this.model, 'change:followersIds', this.controlFollowersField, this); } }, @@ -130,6 +151,5 @@ define('views/record/panels/default-side', 'views/record/panels/side', function this.recordViewObject.hideField('followers'); } }, - }); }); diff --git a/client/src/views/record/panels/relationship.js b/client/src/views/record/panels/relationship.js index 1b4f7246b8..ea7e808e35 100644 --- a/client/src/views/record/panels/relationship.js +++ b/client/src/views/record/panels/relationship.js @@ -26,25 +26,63 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/panels/relationship', - ['views/record/panels/bottom', 'search-manager'], function (Dep, SearchManager) { +define('views/record/panels/relationship', ['views/record/panels/bottom', 'search-manager'], +function (Dep, SearchManager) { - return Dep.extend({ + /** + * A relationship panel. + * + * @class + * @name Class + * @extends module:views/record/panels/bottom.Class + * @memberOf module:views/record/panels/relationship + */ + return Dep.extend(/** @lends module:views/record/panels/relationship.Class# */{ + /** + * @inheritDoc + */ template: 'record/panels/relationship', + /** + * A row-actions view. + * + * @protected + */ rowActionsView: 'views/record/row-actions/relationship', + /** + * An API URL. + * + * @protected + */ url: null, + /** + * A scope. + */ scope: null, + /** + * Read-only. + */ readOnly: false, + /** + * Fetch a collection on a model 'after:relate' event. + * + * @protected + */ fetchOnModelAfterRelate: false, + /** + * @protected + */ noCreateScopeList: ['User', 'Team', 'Role', 'Portal'], + /** + * @private + */ recordsPerPage: null, init: function () { @@ -247,8 +285,18 @@ define('views/record/panels/relationship', this.setupLast(); }, + /** + * Set up lastly. + * + * @protected + */ setupLast: function () {}, + /** + * Set up title. + * + * @protected + */ setupTitle: function () { this.title = this.title || this.translate(this.link, 'links', this.model.name); @@ -270,6 +318,11 @@ define('views/record/panels/relationship', } }, + /** + * Set up sorting. + * + * @protected + */ setupSorting: function () { var orderBy = this.defs.orderBy || this.defs.sortBy || this.orderBy; var order = this.defs.orderDirection || this.orderDirection || this.order; @@ -291,10 +344,25 @@ define('views/record/panels/relationship', this.defaultOrder = order; }, + /** + * Set up a list layout. + * + * @protected + */ setupListLayout: function () {}, + /** + * Set up actions. + * + * @protected + */ setupActions: function () {}, + /** + * Set up filter actions. + * + * @protected + */ setupFilterActions: function () { if (this.filterList && this.filterList.length) { this.actionList.push(false); @@ -323,16 +391,28 @@ define('views/record/panels/relationship', } }, + /** + * Translate a filter. + * + * @param {string} name A name. + * @return {string} + */ translateFilter: function (name) { return this.translate(name, 'presetFilters', this.scope); }, + /** + * @private + */ getStoredFilter: function () { var key = 'panelFilter' + this.model.name + '-' + (this.panelName || this.name); return this.getStorage().get('state', key) || null; }, + /** + * @private + */ storeFilter: function (filter) { var key = 'panelFilter' + this.model.name + '-' + (this.panelName || this.name); @@ -343,6 +423,11 @@ define('views/record/panels/relationship', } }, + /** + * Set a filter. + * + * @param {string} filter A filter. + */ setFilter: function (filter) { this.filter = filter; this.collection.data.primaryFilter = null; @@ -352,6 +437,11 @@ define('views/record/panels/relationship', } }, + /** + * A `select-filter` action. + * + * @protected + */ actionSelectFilter: function (data) { var filter = data.name; var filterInternal = filter; @@ -400,10 +490,20 @@ define('views/record/panels/relationship', } }, + /** + * A `refresh` action. + * + * @protected + */ actionRefresh: function () { this.collection.fetch(); }, + /** + * A `view-related-list` action. + * + * @protected + */ actionViewRelatedList: function (data) { var viewName = this.getMetadata().get( @@ -471,14 +571,33 @@ define('views/record/panels/relationship', }); }, + /** + * Is create available. + * + * @protected + * @param {string} scope A scope (entity type). + * @return {boolean}; + */ isCreateAvailable: function (scope) { - return this.defs.create; + return !!this.defs.create; }, + /** + * Is select available. + * + * @protected + * @param {string} scope A scope (entity type). + * @return {boolean}; + */ isSelectAvailable: function (scope) { - return this.defs.select; + return !!this.defs.select; }, + /** + * A `view-related` action. + * + * @protected + */ actionViewRelated: function (data) { var id = data.id; var scope = this.collection.get(id).name; @@ -505,6 +624,11 @@ define('views/record/panels/relationship', }); }, + /** + * An `edit-related` action. + * + * @protected + */ actionEditRelated: function (data) { var id = data.id; var scope = this.collection.get(id).name; @@ -530,6 +654,11 @@ define('views/record/panels/relationship', }); }, + /** + * An `unlink-related` action. + * + * @protected + */ actionUnlinkRelated: function (data) { var id = data.id; @@ -553,6 +682,11 @@ define('views/record/panels/relationship', }); }, + /** + * A `remove-related` action. + * + * @protected + */ actionRemoveRelated: function (data) { var id = data.id; @@ -577,6 +711,11 @@ define('views/record/panels/relationship', }); }, + /** + * An `unlink-all-related` action. + * + * @protected + */ actionUnlinkAllRelated: function (data) { this.confirm(this.translate('unlinkAllConfirmation', 'messages'), () => { Espo.Ui.notify(this.translate('pleaseWait', 'messages')); diff --git a/client/src/views/record/panels/side.js b/client/src/views/record/panels/side.js index 20fb2c632f..781004ffa8 100644 --- a/client/src/views/record/panels/side.js +++ b/client/src/views/record/panels/side.js @@ -26,18 +26,86 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/panels/side', 'view', function (Dep) { +define('views/record/panels/side', ['view'], function (Dep) { - return Dep.extend({ + /** + * A side panel. + * + * @class + * @name Class + * @extends module:view.Class + * @memberOf module:views/record/panels/side + */ + return Dep.extend(/** module:views/record/panels/side.Class# */{ template: 'record/panels/side', + /** + * A field defs. + * + * @typedef module:views/record/panels/side~field + * + * @property {string} name + * @property {string} [labelText] A translated label text. + * @property {string} [view] A view name. + * @property {boolean} [isAdditional] + * @property {boolean} [readOnly] + * @property {Object.} [options] Options. + */ + + /** + * A field list. + * + * @protected + * @type {module:views/record/panels/side~field[]} + */ fieldList: null, + /** + * A mode. + * + * @protected + * @type {'list'|'detail'|'edit'} + */ + mode: 'detail', + + /** + * @protected + * @type {module:views/record/panels-container~action[]} + */ + actionList: null, + + /** + * @protected + * @type {module:views/record/panels-container~button[]} + */ + buttonList: null, + + /** + * Read-only. + * + * @protected + */ + readOnly: false, + + /** + * Disable inline edit. + * + * @protected + */ + inlineEditDisabled: false, + + /** + * Disable. + * + * @protected + */ + disabled: false, + data: function () { return { fieldList: this.getFieldList(), - hiddenFields: this.recordHelper.getHiddenFields() + hiddenFields: this.recordHelper.getHiddenFields(), }; }, @@ -56,18 +124,6 @@ define('views/record/panels/side', 'view', function (Dep) { } }, - mode: 'detail', - - actionList: null, - - buttonList: null, - - readOnly: false, - - inlineEditDisabled: false, - - disabled: false, - init: function () { this.panelName = this.options.panelName; this.defs = this.options.defs || {}; @@ -94,7 +150,7 @@ define('views/record/panels/side', 'view', function (Dep) { setup: function () { this.setupFields(); - this.fieldList = this.fieldList.map((d) => { + this.fieldList = this.fieldList.map(d => { var item = d; if (typeof item !== 'object') { @@ -138,9 +194,24 @@ define('views/record/panels/side', 'view', function (Dep) { } }, - setupFields: function () { - }, + /** + * Set up fields. + * + * @protected + */ + setupFields: function () {}, + /** + * Create a field view. + * + * @protected + * @param {string} field A field name. + * @param {string|null} [viewName] A view name/path. + * @param {Object} [params] Field params. + * @param {'detail'|'edit'|'list'|null} [mode='edit'] A mode. + * @param {boolean} [readOnly] Read-only. + * @param {Object} [options] View options. + */ createField: function (field, viewName, params, mode, readOnly, options) { var type = this.model.getFieldType(field) || 'base'; @@ -221,8 +292,11 @@ define('views/record/panels/side', 'view', function (Dep) { this.createView(viewKey, viewName, o); }, + /** + * @private + */ createFields: function () { - this.getFieldList().forEach((item) => { + this.getFieldList().forEach(item => { var view = null; var field; var readOnly = null; @@ -249,14 +323,22 @@ define('views/record/panels/side', 'view', function (Dep) { }); }, + /** + * @deprecated Use `getFieldViews`. + */ getFields: function () { return this.getFieldViews(); }, + /** + * Get field views. + * + * @return {Object.} + */ getFieldViews: function () { var fields = {}; - this.getFieldList().forEach((item) => { + this.getFieldList().forEach(item => { if (this.hasView(item.viewKey)) { fields[item.name] = this.getView(item.viewKey); } @@ -265,11 +347,16 @@ define('views/record/panels/side', 'view', function (Dep) { return fields; }, + /** + * Get a field list. + * + * @return {module:views/record/panels/side~field[]} + */ getFieldList: function () { - return this.fieldList.map((item) => { + return this.fieldList.map(item => { if (typeof item !== 'object') { return { - name: item + name: item, }; } @@ -277,14 +364,23 @@ define('views/record/panels/side', 'view', function (Dep) { }); }, + /** + * @return {module:views/record/panels-container~action[]} + */ getActionList: function () { return this.actionList || []; }, + /** + * @return {module:views/record/panels-container~button[]} + */ getButtonList: function () { return this.buttonList || []; }, + /** + * A `refresh` action. + */ actionRefresh: function () { this.model.fetch(); }, diff --git a/client/src/views/record/row-actions/default.js b/client/src/views/record/row-actions/default.js index 313fde2177..bb6010f1c7 100644 --- a/client/src/views/record/row-actions/default.js +++ b/client/src/views/record/row-actions/default.js @@ -26,9 +26,17 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/row-actions/default', 'view', function (Dep) { +define('views/record/row-actions/default', ['view'], function (Dep) { - return Dep.extend({ + /** + * A detail-side record view. + * + * @class + * @name Class + * @extends module:view.Class + * @memberOf views/record/row-actions/default + */ + return Dep.extend(/** @lends views/record/row-actions/default.Class# */{ template: 'record/row-actions/default', @@ -60,6 +68,11 @@ define('views/record/row-actions/default', 'view', function (Dep) { }); }, + /** + * Get an action list. + * + * @return {module:views/record/list~rowAction[]} + */ getActionList: function () { var list = [{ action: 'quickView', @@ -101,6 +114,5 @@ define('views/record/row-actions/default', 'view', function (Dep) { scope: this.model.name, }; }, - }); }); diff --git a/client/src/views/record/row-actions/empty.js b/client/src/views/record/row-actions/empty.js index 86406817a4..0bb2e37786 100644 --- a/client/src/views/record/row-actions/empty.js +++ b/client/src/views/record/row-actions/empty.js @@ -26,13 +26,12 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/row-actions/empty', 'views/record/row-actions/default', function (Dep) { +define('views/record/row-actions/empty', ['views/record/row-actions/default'], function (Dep) { return Dep.extend({ getActionList: function () { return []; }, - }); }); diff --git a/client/src/views/record/search.js b/client/src/views/record/search.js index bc86d22528..c0b8ad1962 100644 --- a/client/src/views/record/search.js +++ b/client/src/views/record/search.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/record/search', 'view', function (Dep) { +define('views/record/search', ['view'], function (Dep) { return Dep.extend({