custom navbar items

This commit is contained in:
Yuri Kuznetsov
2023-11-07 17:35:41 +02:00
parent 2f6ab60b04
commit 392e6bca48
7 changed files with 248 additions and 78 deletions
+19
View File
@@ -854,6 +854,25 @@
</SchemaInfo>
</value>
</entry>
<entry key="metadata/app/clientNavbar">
<value>
<SchemaInfo>
<option name="generatedName" value="New Schema" />
<option name="name" value="metadata/app/clientNavbar" />
<option name="relativePathToSchema" value="schema/metadata/app/clientNavbar.json" />
<option name="schemaVersion" value="JSON Schema version 7" />
<option name="patterns">
<list>
<Item>
<option name="pattern" value="true" />
<option name="path" value="*/Resources/metadata/app/clientNavbar.json" />
<option name="mappingKind" value="Pattern" />
</Item>
</list>
</option>
</SchemaInfo>
</value>
</entry>
<entry key="metadata/app/orm">
<value>
<SchemaInfo>
+6
View File
@@ -352,6 +352,12 @@
],
"url": "./schema/metadata/app/metadata.json"
},
{
"fileMatch": [
"*/Resources/metadata/app/clientNavbar.json"
],
"url": "./schema/metadata/app/clientNavbar.json"
},
{
"fileMatch": [
"*/Resources/metadata/app/orm.json"
@@ -0,0 +1,11 @@
{
"itemDefs": {
"quickCreate": {
"view": "views/site/navbar/quick-create",
"class": "dropdown hidden-xs quick-create-container"
}
},
"itemList": [
"quickCreate"
]
}
+2 -21
View File
@@ -174,28 +174,9 @@
<li class="nav navbar-nav navbar-form global-search-container">
{{{globalSearch}}}
</li>
{{#if enableQuickCreate}}
<li class="dropdown hidden-xs quick-create-container">
<a
id="nav-quick-create-dropdown"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
tabindex="0"
title="{{translate 'Create'}}"
><i class="fas fa-plus icon"></i></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="nav-quick-create-dropdown">
<li class="dropdown-header">{{translate 'Create'}}</li>
{{#each quickCreateList}}
<li><a
href="#{{./this}}/create"
data-name="{{./this}}"
data-action="quick-create"
>{{translate this category='scopeNames'}}</a></li>
{{#each itemDataList}}
<li class="{{class}}" data-item="{{name}}">{{{var key ../this}}}</li>
{{/each}}
</ul>
</li>
{{/if}}
<li class="dropdown notifications-badge-container">
{{{notificationsBadge}}}
</li>
+59 -49
View File
@@ -56,14 +56,6 @@ class NavbarSiteView extends View {
this.xsCollapse();
},
/** @this NavbarSiteView */
'click a[data-action="quick-create"]': function (e) {
e.preventDefault();
const scope = $(e.currentTarget).data('name');
this.quickCreate(scope);
},
/** @this NavbarSiteView */
'click a.minimizer': function () {
this.switchMinimizer();
},
@@ -100,10 +92,9 @@ class NavbarSiteView extends View {
tabDefsList2: this.tabDefsList.filter(item => item.isInMore),
title: this.options.title,
menuDataList: this.getMenuDataList(),
quickCreateList: this.quickCreateList,
enableQuickCreate: this.quickCreateList.length > 0,
userId: this.getUser().id,
logoSrc: this.getLogoSrc(),
itemDataList: this.getItemDataList(),
};
}
@@ -360,10 +351,6 @@ class NavbarSiteView extends View {
return tabList;
}
getQuickCreateList() {
return this.getConfig().get('quickCreateList') || [];
}
setup() {
this.getRouter().on('routed', (e) => {
if (e.controller) {
@@ -375,30 +362,29 @@ class NavbarSiteView extends View {
this.selectTab(false);
});
/** @type {string[]} */
this.itemList = this.getMetadata().get(['app', 'clientNavbar', 'itemList']) || [];
this.createView('notificationsBadge', 'views/notification/badge', {
selector: '.notifications-badge-container',
});
const setup = () => {
this.setupQuickCreateList();
this.setupTabDefsList();
return Promise
.all(this.itemList.map(item => this.createItemView(item)));
};
const update = () => {
setup().then(() => this.reRender());
};
this.setupGlobalSearch();
setup();
this.listenTo(this.getHelper().settings, 'sync', () => {
setup();
this.reRender();
});
this.listenTo(this.getHelper().language, 'sync', () => {
setup();
this.reRender();
});
this.listenTo(this.getHelper().settings, 'sync', () => update());
this.listenTo(this.getHelper().language, 'sync', () => update());
this.once('remove', () => {
$(window).off('resize.navbar');
@@ -409,26 +395,63 @@ class NavbarSiteView extends View {
});
}
setupQuickCreateList() {
const scopes = this.getMetadata().get('scopes') || {};
getItemDataList() {
const defsMap = {};
this.quickCreateList = this.getQuickCreateList().filter(scope =>{
if (!scopes[scope]) {
this.itemList.forEach(name => {
defsMap[name] = this.getItemDefs(name);
});
return this.itemList
.filter(name => {
const defs = defsMap[name];
if (!defs) {
return false;
}
if ((scopes[scope] || {}).disabled) {
return;
}
const view = this.getView(name + 'Item');
if ((scopes[scope] || {}).acl) {
return this.getAcl().check(scope, 'create');
if ('isAvailable' in view) {
return view.isAvailable();
}
return true;
})
.map(name => {
return {
key: name + 'Item',
name: name,
class: defsMap[name].class || '',
};
});
}
/**
*
* @param {string} name
* @return {{view: string, class: string}}
*/
getItemDefs(name) {
return this.getMetadata().get(['app', 'clientNavbar', 'itemDefs', name]);
}
/**
* @param {string} name
* @return {Promise}
*/
createItemView(name) {
const defs = this.getItemDefs(name)
if (!defs || !defs.view) {
return Promise.resolve();
}
const key = name + 'Item';
return this.createView(key, defs.view, {selector: `[data-item="${name}"]`});
}
filterTabItem(scope) {
if (~['Home', '_delimiter_', '_delimiter-ext_'].indexOf(scope)) {
return true;
@@ -1200,19 +1223,6 @@ class NavbarSiteView extends View {
return list;
}
quickCreate(scope) {
Espo.Ui.notify(' ... ');
const type = this.getMetadata().get(['clientDefs', scope, 'quickCreateModalType']) || 'edit';
const viewName = this.getMetadata().get(['clientDefs', scope, 'modalViews', type]) || 'views/modals/edit';
this.createView('quickCreate', viewName , {scope: scope}, (view) => {
view.once('after:render', () => Espo.Ui.notify(false));
view.render();
});
}
// noinspection JSUnusedGlobalSymbols
actionLogout() {
this.getRouter().logout();
@@ -0,0 +1,105 @@
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2023 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* 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.
************************************************************************/
import View from 'view';
class QuickCreateNavbarView extends View {
templateContent = `
<a
id="nav-quick-create-dropdown"
class="dropdown-toggle"
data-toggle="dropdown"
role="button"
tabindex="0"
title="{{translate 'Create'}}"
><i class="fas fa-plus icon"></i></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="nav-quick-create-dropdown">
<li class="dropdown-header">{{translate 'Create'}}</li>
{{#each list}}
<li><a
href="#{{./this}}/create"
data-name="{{./this}}"
data-action="quickCreate"
>{{translate this category='scopeNames'}}</a></li>
{{/each}}
</ul>
`
data() {
return {
list: this.list,
};
}
setup() {
this.addActionHandler('quickCreate', (e, element) => {
e.preventDefault();
this.processCreate(element.dataset.name);
});
const scopes = this.getMetadata().get('scopes') || {};
/** @type {string[]} */
const list = this.getConfig().get('quickCreateList') || [];
this.list = list.filter(scope => {
if (!scopes[scope]) {
return false;
}
if ((scopes[scope] || {}).disabled) {
return;
}
if ((scopes[scope] || {}).acl) {
return this.getAcl().check(scope, 'create');
}
return true;
});
}
isAvailable() {
return this.list.length > 0;
}
processCreate(scope) {
Espo.Ui.notify(' ... ');
const type = this.getMetadata().get(['clientDefs', scope, 'quickCreateModalType']) || 'edit';
const viewName = this.getMetadata().get(['clientDefs', scope, 'modalViews', type]) || 'views/modals/edit';
this.createView('dialog', viewName , {scope: scope})
.then(view => view.render())
.then(() => Espo.Ui.notify(false));
}
}
export default QuickCreateNavbarView;
+38
View File
@@ -0,0 +1,38 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://www.espocrm.com/schema/metadata/app/client.json",
"title": "app/navbar",
"description": "Navbar definitions.",
"type": "object",
"properties": {
"itemList": {
"type": "array",
"description": "Navbar items.",
"items": {
"type": "string",
"anyOf": [
{"type": "string"},
{"const": "__APPEND__"}
]
}
},
"itemDefs": {
"description": "Navbar item definitions",
"type": "object",
"additionalProperties": {
"description": "An item.",
"type": "object",
"properties": {
"view": {
"type": "string",
"description": "A view."
},
"class": {
"type": "string",
"description": "A CSS class."
}
}
}
}
}
}