From 65484367bd0ef4c8e5e3080c1453db45fa333fec Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Fri, 29 Mar 2019 13:16:11 +0530 Subject: [PATCH] fix: Desktop - Customize Shortcuts dialog - Show / Hide cards dialog - MultiSelect UI Pills --- frappe/boot.py | 1 - frappe/desk/moduleview.py | 38 +-- frappe/patches.txt | 1 + frappe/patches/v12_0/reset_home_settings.py | 8 + .../js/frappe/form/controls/multiselect.js | 131 +++++++--- .../frappe/views/components/DeskModuleBox.vue | 65 ++--- .../frappe/views/components/DeskSection.vue | 245 +++--------------- .../js/frappe/views/components/Desktop.vue | 176 ++++++++----- .../js/frappe/views/components/Dropdown.vue | 7 +- frappe/public/less/controls.less | 5 + 10 files changed, 293 insertions(+), 384 deletions(-) create mode 100644 frappe/patches/v12_0/reset_home_settings.py diff --git a/frappe/boot.py b/frappe/boot.py index 05117acb7a..043c1b0361 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -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') diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index 5978bc7ef0..a9a460b124 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -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): diff --git a/frappe/patches.txt b/frappe/patches.txt index 43d0699647..d3b3ca02f1 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -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 \ No newline at end of file diff --git a/frappe/patches/v12_0/reset_home_settings.py b/frappe/patches/v12_0/reset_home_settings.py new file mode 100644 index 0000000000..db16c31f15 --- /dev/null +++ b/frappe/patches/v12_0/reset_home_settings.py @@ -0,0 +1,8 @@ +import frappe + +def execute(): + frappe.db.sql(''' + UPDATE `tabUser` + SET `home_settings` = '' + WHERE `user_type` = 'System User' + ''') diff --git a/frappe/public/js/frappe/form/controls/multiselect.js b/frappe/public/js/frappe/form/controls/multiselect.js index 64ca4fc83d..93bcd2ca15 100644 --- a/frappe/public/js/frappe/form/controls/multiselect.js +++ b/frappe/public/js/frappe/form/controls/multiselect.js @@ -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 `
+ + +
`; + }, + 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; } }); diff --git a/frappe/public/js/frappe/views/components/DeskModuleBox.vue b/frappe/public/js/frappe/views/components/DeskModuleBox.vue index 245b47d147..3589b3ea12 100644 --- a/frappe/public/js/frappe/views/components/DeskModuleBox.vue +++ b/frappe/public/js/frappe/views/components/DeskModuleBox.vue @@ -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" >
@@ -20,23 +15,12 @@
- + - + -
- @@ -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; - } - } }; - diff --git a/frappe/public/js/frappe/views/components/Desktop.vue b/frappe/public/js/frappe/views/components/Desktop.vue index 74d3cc6622..240f3511a8 100644 --- a/frappe/public/js/frappe/views/components/Desktop.vue +++ b/frappe/public/js/frappe/views/components/Desktop.vue @@ -1,24 +1,30 @@