fix: Desktop
- Customize Shortcuts dialog - Show / Hide cards dialog - MultiSelect UI Pills
This commit is contained in:
parent
23d4dfcedf
commit
65484367bd
10 changed files with 293 additions and 384 deletions
|
|
@ -98,7 +98,6 @@ def load_conf_settings(bootinfo):
|
|||
def load_desktop_icons(bootinfo):
|
||||
from frappe.config import get_modules_from_all_apps_for_user
|
||||
bootinfo.allowed_modules = get_modules_from_all_apps_for_user()
|
||||
bootinfo.home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings','')
|
||||
|
||||
def get_allowed_pages():
|
||||
return get_user_pages_or_reports('Page')
|
||||
|
|
|
|||
|
|
@ -302,30 +302,30 @@ def get_links(app, module):
|
|||
link_names.append(item.get("label"))
|
||||
return link_names
|
||||
|
||||
@frappe.whitelist()
|
||||
def hide_modules_from_desktop(modules):
|
||||
modules = frappe.parse_json(modules)
|
||||
home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings')
|
||||
home_settings = frappe.parse_json(home_settings or '{}')
|
||||
|
||||
home_settings['hidden_modules'] = modules
|
||||
frappe.db.set_value('User', frappe.session.user, 'home_settings', json.dumps(home_settings))
|
||||
|
||||
return home_settings
|
||||
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_desk_section_settings(desk_section, new_settings):
|
||||
def update_links_for_module(module_name, links):
|
||||
home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings')
|
||||
if home_settings:
|
||||
home_settings = json.loads(home_settings)
|
||||
else:
|
||||
return {}
|
||||
home_settings = frappe.parse_json(home_settings or '{}')
|
||||
|
||||
new_settings = json.loads(new_settings)
|
||||
home_settings.setdefault('links', {})
|
||||
home_settings['links'].setdefault(module_name, None)
|
||||
home_settings['links'][module_name] = links
|
||||
frappe.db.set_value('User', frappe.session.user, 'home_settings', json.dumps(home_settings))
|
||||
|
||||
for module, data in new_settings.items():
|
||||
if data.get("links"):
|
||||
data["links"] = get_module_link_items_from_list(data["app"], module, data.get("links"))
|
||||
data.pop("app", None)
|
||||
|
||||
home_settings[desk_section] = new_settings
|
||||
settings_json_str = json.dumps(home_settings)
|
||||
# # This didn't work
|
||||
# frappe.db.set_value("User", frappe.session.user, 'home_settings', json.dumps(home_settings))
|
||||
frappe.db.sql("""update tabUser set home_settings = %s""", (settings_json_str), debug=True)
|
||||
frappe.db.commit()
|
||||
|
||||
return new_settings
|
||||
return home_settings
|
||||
|
||||
|
||||
def get_module_link_items_from_list(app, module, list_of_link_names):
|
||||
|
|
|
|||
|
|
@ -238,3 +238,4 @@ frappe.patches.v11_0.set_default_letter_head_source
|
|||
frappe.patches.v12_0.setup_comments_from_communications
|
||||
frappe.patches.v12_0.init_desk_settings #11-03-2019
|
||||
frappe.patches.v12_0.replace_null_values_in_tables
|
||||
frappe.patches.v12_0.reset_home_settings
|
||||
8
frappe/patches/v12_0/reset_home_settings.py
Normal file
8
frappe/patches/v12_0/reset_home_settings.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.db.sql('''
|
||||
UPDATE `tabUser`
|
||||
SET `home_settings` = ''
|
||||
WHERE `user_type` = 'System User'
|
||||
''')
|
||||
|
|
@ -1,6 +1,93 @@
|
|||
import Awesomplete from 'awesomplete';
|
||||
|
||||
frappe.ui.form.ControlMultiSelect = frappe.ui.form.ControlAutocomplete.extend({
|
||||
make_input() {
|
||||
this._super();
|
||||
this.$input_area = $(this.input_area);
|
||||
this.$input_area.addClass('form-control table-multiselect');
|
||||
this.$input.removeClass('form-control');
|
||||
|
||||
this.$input.on("awesomplete-selectcomplete", () => {
|
||||
this.$input.val('').focus();
|
||||
});
|
||||
|
||||
// used as an internal model to store values
|
||||
this.rows = [];
|
||||
|
||||
this.$input_area.on('click', '.btn-remove', (e) => {
|
||||
const $target = $(e.currentTarget);
|
||||
const $value = $target.closest('.tb-selected-value');
|
||||
|
||||
const value = decodeURIComponent($value.data().value);
|
||||
this.rows = this.rows.filter(val => val !== value);
|
||||
|
||||
this.parse_validate_and_set_in_model('');
|
||||
});
|
||||
|
||||
this.$input.on('keydown', e => {
|
||||
// if backspace key pressed on empty input, delete last value
|
||||
if (e.keyCode == frappe.ui.keyCode.BACKSPACE && e.target.value === '') {
|
||||
this.rows = this.rows.slice(0, this.rows.length - 1);
|
||||
this.parse_validate_and_set_in_model('');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
parse(value) {
|
||||
if (value) {
|
||||
this.rows.push(value);
|
||||
}
|
||||
|
||||
return this.rows;
|
||||
},
|
||||
|
||||
validate(value) {
|
||||
const rows = (value || []).slice();
|
||||
|
||||
if (rows.length === 0) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
const all_rows_except_last = rows.slice(0, rows.length - 1);
|
||||
const last_value = rows[rows.length - 1];
|
||||
|
||||
// falsy value
|
||||
if (!last_value) {
|
||||
return all_rows_except_last;
|
||||
}
|
||||
|
||||
// duplicate value
|
||||
if (all_rows_except_last.includes(last_value)) {
|
||||
return all_rows_except_last;
|
||||
}
|
||||
|
||||
return rows;
|
||||
},
|
||||
|
||||
set_formatted_input(value) {
|
||||
this.rows = value || [];
|
||||
this.set_pill_html(this.rows);
|
||||
},
|
||||
|
||||
set_pill_html(values) {
|
||||
const html = values
|
||||
.map(value => this.get_pill_html(value))
|
||||
.join('');
|
||||
|
||||
this.$input_area.find('.tb-selected-value').remove();
|
||||
this.$input_area.prepend(html);
|
||||
},
|
||||
|
||||
get_pill_html(value) {
|
||||
const encoded_value = encodeURIComponent(value);
|
||||
return `<div class="btn-group tb-selected-value" data-value="${encoded_value}">
|
||||
<button class="btn btn-default btn-xs btn-link-to-form">${__(value)}</button>
|
||||
<button class="btn btn-default btn-xs btn-remove">
|
||||
<i class="fa fa-remove text-muted"></i>
|
||||
</button>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
get_awesomplete_settings() {
|
||||
const settings = this._super();
|
||||
|
||||
|
|
@ -23,59 +110,37 @@ frappe.ui.form.ControlMultiSelect = frappe.ui.form.ControlAutocomplete.extend({
|
|||
}
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
replace: function(text) {
|
||||
const before = this.input.value.match(/^.+,\s*|/)[0];
|
||||
this.input.value = before + text + ", ";
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get_value() {
|
||||
let data = this._super();
|
||||
// find value of label from option list and return actual value string
|
||||
if (this.df.options && this.df.options.length && this.df.options[0].label) {
|
||||
data = data.split(',').map(op => op.trim());
|
||||
data = data.map(val => {
|
||||
let option = this.df.options.find(op => op.label === val);
|
||||
return option ? option.value : null;
|
||||
}).filter(n => n != null).join(', ');
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
set_formatted_input(value) {
|
||||
if (!value) return;
|
||||
// find label of value from option list and set from it as input
|
||||
if (this.df.options && this.df.options.length && this.df.options[0].label) {
|
||||
value = value.split(',').map(d => d.trim()).map(val => {
|
||||
let option = this.df.options.find(op => op.value === val);
|
||||
return option ? option.label : val;
|
||||
}).filter(n => n != null).join(', ');
|
||||
}
|
||||
this._super(value);
|
||||
return this.rows;
|
||||
},
|
||||
|
||||
get_values() {
|
||||
const value = this.get_value() || '';
|
||||
const values = value.split(/\s*,\s*/).filter(d => d);
|
||||
|
||||
return values;
|
||||
return this.rows;
|
||||
},
|
||||
|
||||
get_data() {
|
||||
let data;
|
||||
if(this.df.get_data) {
|
||||
data = this.df.get_data();
|
||||
this.set_data(data);
|
||||
if (data && data.then) {
|
||||
data.then((r) => {
|
||||
this.set_data(r);
|
||||
});
|
||||
data = this.get_value();
|
||||
} else {
|
||||
this.set_data(data);
|
||||
}
|
||||
} else {
|
||||
data = this._super();
|
||||
}
|
||||
const values = this.get_values() || [];
|
||||
|
||||
// return values which are not already selected
|
||||
if(data) data.filter(d => !values.includes(d));
|
||||
if (data) data.filter(d => !values.includes(d));
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,11 +3,6 @@
|
|||
v-if="!hidden"
|
||||
class="border module-box"
|
||||
:class="{ 'hovered-box': hovered }"
|
||||
:draggable="true"
|
||||
@dragstart="on_dragstart"
|
||||
@dragend="on_dragend"
|
||||
@dragenter="on_enter"
|
||||
@drop="on_drop"
|
||||
>
|
||||
<div class="flush-top">
|
||||
<div class="module-box-content">
|
||||
|
|
@ -20,23 +15,12 @@
|
|||
</div>
|
||||
</h4>
|
||||
</a>
|
||||
<dropdown v-if="links && links.length" :items="links">
|
||||
<dropdown v-if="dropdown_links && dropdown_links.length" :items="dropdown_links">
|
||||
<span class="pull-right">
|
||||
<i class="octicon octicon-chevron-down"></i>
|
||||
<i class="octicon octicon-chevron-down text-muted"></i>
|
||||
</span>
|
||||
</dropdown>
|
||||
<!-- <span class="drag-handle octicon octicon-three-bars text-extra-muted"></span> -->
|
||||
</div>
|
||||
<!-- <p v-if="links && links.length" class="small text-muted">
|
||||
<a
|
||||
v-for="shortcut in links"
|
||||
:key="(shortcut.name || shortcut.label) + shortcut.type"
|
||||
:href="shortcut.route"
|
||||
class="btn btn-default btn-xs shortcut-tag"
|
||||
title="toggle Tag"
|
||||
>{{ shortcut.label }}</a
|
||||
>
|
||||
</p>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -50,6 +34,7 @@ export default {
|
|||
"index",
|
||||
"name",
|
||||
"label",
|
||||
"category",
|
||||
"type",
|
||||
"module_name",
|
||||
"link",
|
||||
|
|
@ -75,32 +60,21 @@ export default {
|
|||
} else {
|
||||
return "octicon octicon-file-text";
|
||||
}
|
||||
}
|
||||
},
|
||||
dropdown_links() {
|
||||
return this.links
|
||||
.filter(link => !link.hidden)
|
||||
.concat([
|
||||
{ label: __('Customize'), action: () => this.$emit('customize'), class: 'border-top' }
|
||||
]);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
on_dragstart() {
|
||||
this.$emit("box-dragstart", this.index);
|
||||
return 0;
|
||||
},
|
||||
on_dragend() {
|
||||
this.$emit("box-dragend", this.index);
|
||||
return 0;
|
||||
},
|
||||
on_enter() {
|
||||
this.$emit("box-enter", this.index);
|
||||
// this.hovered = 1;
|
||||
},
|
||||
on_drop() {
|
||||
this.$emit("box-drop", this.index);
|
||||
},
|
||||
on_exit() {
|
||||
// this.hovered = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "frappe/public/less/variables";
|
||||
|
||||
.module-box {
|
||||
border-radius: 4px;
|
||||
padding: 5px 15px;
|
||||
|
|
@ -109,16 +83,21 @@ export default {
|
|||
}
|
||||
|
||||
.module-box:hover {
|
||||
box-shadow: 0 3px 4px 0 rgba(18, 18, 19, 0.08);
|
||||
border-color: @text-muted;
|
||||
}
|
||||
|
||||
.hovered-box {
|
||||
background-color: #fafbfc;
|
||||
background-color: @light-bg;
|
||||
}
|
||||
|
||||
.octicon-chevron-down {
|
||||
font-size: 24px;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
padding: 4px 6px 2px 6px;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background: @btn-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.octicon-chevron-down:hover {
|
||||
|
|
|
|||
|
|
@ -2,22 +2,15 @@
|
|||
<div>
|
||||
<div class="section-header level text-muted">
|
||||
<div class="module-category h6 uppercase">{{ category }}</div>
|
||||
|
||||
<div>
|
||||
<a class="small text-muted" @click="show_customize_dialog">{{ __("Customize") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modules-container">
|
||||
<desk-module-box
|
||||
v-for="(module, index) in modules.filter(m => !m.hidden)"
|
||||
v-for="(module, index) in modules"
|
||||
:key="module.name"
|
||||
:index="index"
|
||||
v-bind="module"
|
||||
@box-dragstart="box_dragstart($event)"
|
||||
@box-dragend="box_dragend($event)"
|
||||
@box-enter="box_enter($event)"
|
||||
@box-drop="box_drop($event)"
|
||||
@customize="show_module_card_customize_dialog(module)"
|
||||
></desk-module-box>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -25,228 +18,49 @@
|
|||
|
||||
<script>
|
||||
import DeskModuleBox from "./DeskModuleBox.vue";
|
||||
import { generate_route } from "./utils.js";
|
||||
|
||||
export default {
|
||||
props: ['category', 'all_modules', 'customization_settings'],
|
||||
props: ['category', 'modules'],
|
||||
components: {
|
||||
DeskModuleBox
|
||||
},
|
||||
data() {
|
||||
let default_modules = this.all_modules;
|
||||
let modules = this.get_customized_modules(default_modules, this.customization_settings);
|
||||
|
||||
return {
|
||||
default_modules: default_modules,
|
||||
modules: modules,
|
||||
new_settings: {},
|
||||
dragged_index: -1,
|
||||
hovered_index: -1,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show_customize_dialog() {
|
||||
if(!this.dialog) {
|
||||
const fields = this.make_fields();
|
||||
this.make_and_show_dialog(fields);
|
||||
} else {
|
||||
this.dialog.show();
|
||||
}
|
||||
},
|
||||
make_fields() {
|
||||
let fields = [];
|
||||
this.modules.forEach(module => {
|
||||
fields.push(this.get_module_select_field(module));
|
||||
|
||||
if(module.links) {
|
||||
fields.push(this.get_links_multiselect_field(module));
|
||||
}
|
||||
});
|
||||
return fields;
|
||||
},
|
||||
make_and_show_dialog(fields) {
|
||||
this.dialog = new frappe.ui.Dialog({
|
||||
title: __("Customize " + this.category),
|
||||
fields: fields,
|
||||
primary_action_label: __("Update"),
|
||||
primary_action: (values) => {
|
||||
this.update_settings(values);
|
||||
}
|
||||
});
|
||||
|
||||
this.dialog.modal_body.find('.clearfix').css({'display': 'none'});
|
||||
this.dialog.modal_body.find('.frappe-control*[data-fieldtype="MultiSelect"]').css({'margin-bottom': '30px'});
|
||||
|
||||
this.dialog.show();
|
||||
},
|
||||
|
||||
update_settings(values) {
|
||||
// Figure out the diff from the default settings known from modules
|
||||
let new_settings = {};
|
||||
const checkbox_fields = Object.keys(values).filter(f => !f.includes('links'));
|
||||
|
||||
checkbox_fields.forEach(module_name => {
|
||||
const default_module = this.default_modules.filter(f => f.module_name === module_name)[0];
|
||||
|
||||
// Check if hidden changed
|
||||
const default_hidden = default_module.hidden ? 1 : 0;
|
||||
const new_hidden = !values[module_name] ? 1 : 0;
|
||||
const hidden_changed = new_hidden != default_hidden;
|
||||
|
||||
// Check if links changed
|
||||
let links_changed = 0;
|
||||
let new_links = [];
|
||||
|
||||
if(!new_hidden) {
|
||||
const default_links = default_module.links.map(l => (l.name || l.label));
|
||||
const new_links_str = values[module_name + '_links'] || '';
|
||||
new_links = new_links_str ? new_links_str.split(",") : [];
|
||||
links_changed = !this.are_arrays_equal(new_links, default_links);
|
||||
}
|
||||
|
||||
// Make new settings
|
||||
let new_module_settings;
|
||||
|
||||
if(hidden_changed || links_changed) {
|
||||
new_module_settings = {};
|
||||
if(hidden_changed) {
|
||||
new_module_settings.hidden = new_hidden;
|
||||
}
|
||||
if(links_changed) {
|
||||
new_module_settings.links = new_links;
|
||||
}
|
||||
}
|
||||
|
||||
if(new_module_settings) {
|
||||
new_module_settings.app = this.default_modules.filter(m => m.module_name === module_name)[0].app;
|
||||
new_settings[module_name] = new_module_settings;
|
||||
}
|
||||
});
|
||||
|
||||
if(Object.keys(new_settings)) {
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.moduleview.update_desk_section_settings',
|
||||
freeze: true,
|
||||
args: {
|
||||
desk_section: this.category,
|
||||
new_settings: new_settings
|
||||
},
|
||||
callback: (r) => {
|
||||
let new_settings_with_link_objects = r.message;
|
||||
let home_settings = JSON.parse(frappe.boot.home_settings);
|
||||
home_settings[this.category] = new_settings_with_link_objects;
|
||||
frappe.boot.home_settings = JSON.stringify(home_settings);
|
||||
|
||||
this.modules = this.get_customized_modules(this.default_modules, new_settings_with_link_objects);
|
||||
this.dialog.hide();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.dialog.hide();
|
||||
};
|
||||
},
|
||||
|
||||
get_customized_modules(default_modules, customization_settings={}) {
|
||||
return default_modules.map(module => {
|
||||
let customized_module = JSON.parse(JSON.stringify(module));
|
||||
|
||||
const module_settings = customization_settings[module.module_name];
|
||||
if(module_settings) {
|
||||
if(module_settings.links) {
|
||||
customized_module.links = module_settings.links;
|
||||
}
|
||||
customized_module.hidden = module_settings ? module_settings.hidden : 0;
|
||||
}
|
||||
|
||||
if(customized_module.links) {
|
||||
customized_module.links.forEach(link => {
|
||||
link.route = generate_route(link);
|
||||
});
|
||||
}
|
||||
|
||||
return customized_module;
|
||||
});
|
||||
},
|
||||
|
||||
get_module_select_field(module) {
|
||||
return {
|
||||
label: __(module.module_name),
|
||||
fieldname: module.module_name,
|
||||
fieldtype: "Check",
|
||||
default: module.hidden ? 0 : 1
|
||||
}
|
||||
},
|
||||
|
||||
get_links_multiselect_field(module) {
|
||||
return {
|
||||
label: __(""),
|
||||
fieldname: module.module_name + "_links",
|
||||
fieldtype: "MultiSelect",
|
||||
get_data: function() {
|
||||
let data = [];
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.moduleview.get_links',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
app: module.app,
|
||||
module: module.module_name,
|
||||
show_module_card_customize_dialog(module) {
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __('Customize Shortcuts'),
|
||||
fields: [
|
||||
{
|
||||
label: __('Shortcuts'),
|
||||
fieldname: 'links',
|
||||
fieldtype: 'MultiSelect',
|
||||
get_data() {
|
||||
return frappe.call('frappe.desk.moduleview.get_links', {
|
||||
app: module.app,
|
||||
module: module.module_name,
|
||||
}).then(r => r.message);
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.message;
|
||||
}
|
||||
default: module.links.filter(l => !l.hidden).map(l => l.name)
|
||||
}
|
||||
],
|
||||
primary_action_label: __('Save'),
|
||||
primary_action: ({ links }) => {
|
||||
frappe.call('frappe.desk.moduleview.update_links_for_module', {
|
||||
module_name: module.module_name,
|
||||
links
|
||||
}).then(r => {
|
||||
this.$emit('update_home_settings', r.message);
|
||||
});
|
||||
return data;
|
||||
},
|
||||
default: module.links.map(l => (l.name || l.label)),
|
||||
depends_on: module.module_name
|
||||
};
|
||||
},
|
||||
|
||||
are_arrays_equal(arr1, arr2) {
|
||||
if(arr1.length !== arr2.length) return false;
|
||||
let areEqual = true;
|
||||
arr1.map((d, i) => {
|
||||
if(arr2[i] !== d) areEqual = false;
|
||||
d.hide();
|
||||
}
|
||||
});
|
||||
return areEqual;
|
||||
},
|
||||
|
||||
box_dragstart(index) {
|
||||
this.dragged_index = index;
|
||||
},
|
||||
|
||||
box_dragend(index) {
|
||||
this.dragged_index = -1;
|
||||
this.hovered_index = -1;
|
||||
},
|
||||
|
||||
box_enter(index) {
|
||||
this.hovered_index = index;
|
||||
},
|
||||
|
||||
box_drop(index) {
|
||||
let d = this.dragged_index;
|
||||
let h = this.hovered_index;
|
||||
if (d < h) {
|
||||
this.modules.splice(h, 0, this.modules[d]);
|
||||
this.modules.splice(d, 1);
|
||||
}
|
||||
d.show();
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.section-header {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px solid #d0d8dd;
|
||||
}
|
||||
|
||||
.modules-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
|
|
@ -256,4 +70,3 @@ export default {
|
|||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,30 @@
|
|||
<template>
|
||||
<div class="modules-page-container">
|
||||
<div class="toolbar-underlay"></div>
|
||||
<div v-for="category in module_categories"
|
||||
:key="category">
|
||||
|
||||
<div class="modules-page-container" v-if="home_settings_fetched">
|
||||
<div
|
||||
class="modules-section"
|
||||
v-for="(category, i) in module_categories" :key="category"
|
||||
>
|
||||
<a
|
||||
v-if="i === 0"
|
||||
class="btn-show-hide text-muted text-medium"
|
||||
@click="show_hide_cards_dialog"
|
||||
>
|
||||
{{ __('Show / Hide Cards') }}
|
||||
</a>
|
||||
<desk-section
|
||||
v-if="modules.filter(m => m.category === category).length"
|
||||
v-if="get_modules_for_category(category)"
|
||||
:category="category"
|
||||
:all_modules="modules.filter(m => m.category === category)"
|
||||
:customization_settings="home_settings ? home_settings[category] : {}"
|
||||
:modules="get_modules_for_category(category)"
|
||||
@update_home_settings="hs => update_modules_with_home_settings(hs)"
|
||||
>
|
||||
</desk-section>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DeskSection from './DeskSection.vue';
|
||||
import { generate_route } from './utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -26,77 +32,98 @@ export default {
|
|||
},
|
||||
data() {
|
||||
let modules_list = frappe.boot.allowed_modules
|
||||
.filter(d => (d.type==='module' || d.category==='Places') && !d.blocked);
|
||||
|
||||
modules_list.forEach(module => {
|
||||
module.count = this.get_module_count(module.module_name);
|
||||
});
|
||||
|
||||
const home_settings = frappe.boot.home_settings || '{}';
|
||||
.filter(d => (d.type==='module' || d.category==='Places') && !d.blocked)
|
||||
.map(d => {
|
||||
d.links = (d.links || []).map(link => {
|
||||
link.route = generate_route(link);
|
||||
return link;
|
||||
});
|
||||
return d;
|
||||
});
|
||||
|
||||
return {
|
||||
route_str: frappe.get_route()[1],
|
||||
module_label: '',
|
||||
module_categories: ["Modules", "Domains", "Places", "Administration"],
|
||||
module_categories: ['Modules', 'Domains', 'Places', 'Administration'],
|
||||
modules: modules_list,
|
||||
|
||||
// // Desk customizations. Format of user settings:
|
||||
|
||||
// home_settings = { // <--- Settings
|
||||
// "Domains": { // <--- Category (Desk Section)
|
||||
// "Manufacturing": { // <--- Module
|
||||
// "index": 3,
|
||||
// "links": [],
|
||||
// "hidden": 1,
|
||||
// },
|
||||
|
||||
// },
|
||||
// }
|
||||
|
||||
home_settings: JSON.parse(home_settings)
|
||||
home_settings_fetched: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetch_home_settings();
|
||||
},
|
||||
methods: {
|
||||
get_settings() {
|
||||
fetch_home_settings() {
|
||||
return frappe.db.get_value('User', user, 'home_settings')
|
||||
.then(resp => {
|
||||
this.all_settings = JSON.parse(resp.message['home_settings']);
|
||||
this.settings = this.all_settings[this.category];
|
||||
.then(r => {
|
||||
let home_settings = JSON.parse(r.message.home_settings || '{}');
|
||||
this.update_modules_with_home_settings(home_settings);
|
||||
this.home_settings_fetched = true;
|
||||
});
|
||||
},
|
||||
get_module_count(module_name) {
|
||||
var module_doctypes = frappe.boot.notification_info.module_doctypes[module_name];
|
||||
var sum = 0;
|
||||
update_modules_with_home_settings(home_settings) {
|
||||
this.modules = this.modules.map(m => {
|
||||
let hidden_modules = home_settings.hidden_modules || [];
|
||||
m.hidden = hidden_modules.includes(m.module_name);
|
||||
|
||||
if(module_doctypes && frappe.boot.notification_info.open_count_doctype) {
|
||||
// sum all doctypes for a module
|
||||
for (var j=0, k=module_doctypes.length; j < k; j++) {
|
||||
var doctype = module_doctypes[j];
|
||||
let count = (frappe.boot.notification_info.open_count_doctype[doctype] || 0);
|
||||
count = typeof count == "string" ? parseInt(count) : count;
|
||||
sum += count;
|
||||
let links = home_settings.links && home_settings.links[m.module_name];
|
||||
|
||||
if (links) {
|
||||
links = JSON.parse(links);
|
||||
|
||||
let default_links = m.links.map(link => link.name);
|
||||
m.links = m.links.map(link => {
|
||||
link.hidden = !links.includes(link.name);
|
||||
return link;
|
||||
});
|
||||
let new_links = links
|
||||
.filter(link => !default_links.includes(link))
|
||||
.filter(Boolean)
|
||||
.map(link => {
|
||||
let new_link = { name: link, label: link, type: 'doctype' };
|
||||
new_link.route = generate_route(new_link);
|
||||
return new_link;
|
||||
});
|
||||
m.links = m.links.concat(new_links);
|
||||
}
|
||||
}
|
||||
|
||||
if(frappe.boot.notification_info.open_count_doctype
|
||||
&& frappe.boot.notification_info.open_count_doctype[module_name]!=null) {
|
||||
// notification count explicitly for doctype
|
||||
let count = frappe.boot.notification_info.open_count_doctype[module_name] || 0;
|
||||
count = typeof count == "string" ? parseInt(count) : count;
|
||||
sum += count;
|
||||
}
|
||||
return m;
|
||||
});
|
||||
},
|
||||
get_modules_for_category(category) {
|
||||
return this.modules.filter(m => m.category === category && !m.hidden);
|
||||
},
|
||||
show_hide_cards_dialog() {
|
||||
let fields = this.module_categories.map(category => {
|
||||
let modules = this.modules.filter(m => m.category === category);
|
||||
let options = modules.map(
|
||||
m => ({ label: m.label, value: m.module_name, checked: !m.hidden })
|
||||
);
|
||||
return {
|
||||
label: category,
|
||||
fieldname: category,
|
||||
fieldtype: 'MultiCheck',
|
||||
options,
|
||||
columns: 2
|
||||
}
|
||||
});
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __('Show / Hide Cards'),
|
||||
fields,
|
||||
primary_action_label: __('Save'),
|
||||
primary_action: (values) => {
|
||||
let all_modules = this.modules.map(m => m.module_name);
|
||||
let modules_to_show = Object.keys(values).map(k => values[k]).flatMap(m => m);
|
||||
let modules_to_hide = all_modules.filter(m => !modules_to_show.includes(m));
|
||||
d.hide();
|
||||
|
||||
if(frappe.boot.notification_info.open_count_module
|
||||
&& frappe.boot.notification_info.open_count_module[module_name]!=null) {
|
||||
// notification count explicitly for module
|
||||
let count = frappe.boot.notification_info.open_count_module[module_name] || 0;
|
||||
count = typeof count == "string" ? parseInt(count) : count;
|
||||
sum += count;
|
||||
}
|
||||
frappe.call('frappe.desk.moduleview.hide_modules_from_desktop', {
|
||||
modules: modules_to_hide
|
||||
})
|
||||
.then(r => r.message)
|
||||
.then(hs => this.update_modules_with_home_settings(hs));
|
||||
}
|
||||
});
|
||||
|
||||
sum = sum > 99 ? "99+" : sum;
|
||||
|
||||
return sum;
|
||||
d.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,12 +131,23 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.modules-page-container {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.toolbar-underlay{
|
||||
margin: 70px;
|
||||
.modules-section {
|
||||
position: relative;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.btn-show-hide {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 36px;
|
||||
}
|
||||
|
||||
.toolbar-underlay {
|
||||
margin: 70px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<Popover :align="align">
|
||||
<slot></slot>
|
||||
<ul slot="popover-content" class="list-reset">
|
||||
<li v-for="item of dropdownItems" :key="item.label">
|
||||
<a class="list-item" :href="item.route">{{ item.label }}</a>
|
||||
<ul slot="popover-content" class="list-reset border">
|
||||
<li v-for="item of dropdownItems" :key="item.label" :class="item.class || null">
|
||||
<a v-if="item.route" class="list-item" :href="item.route">{{ item.label }}</a>
|
||||
<div v-else class="list-item" @click="item.action">{{ item.label }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</Popover>
|
||||
|
|
|
|||
|
|
@ -66,12 +66,17 @@
|
|||
}
|
||||
|
||||
.table-multiselect.form-control input {
|
||||
display: inline-block;
|
||||
outline: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font-size: @text-medium;
|
||||
}
|
||||
|
||||
.table-multiselect .awesomplete {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.tb-selected-value {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue