diff --git a/frappe/public/build.json b/frappe/public/build.json index ff6ba0468a..3a6cdcfa2f 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -293,6 +293,7 @@ "public/js/frappe/list/list_view.js", "public/js/frappe/list/list_factory.js", + "public/js/frappe/list/views.js", "public/js/frappe/list/list_sidebar.js", "public/js/frappe/list/list_sidebar.html", "public/js/frappe/list/list_sidebar_stat.html", diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index 5877b09209..cca49c96a0 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -25,6 +25,7 @@ frappe.views.BaseList = class BaseList { this.setup_side_bar, this.setup_main_section, this.setup_view, + this.setup_view_menu, ].map((fn) => fn.bind(this)); this.init_promise = frappe.run_serially(tasks); @@ -164,9 +165,20 @@ frappe.views.BaseList = class BaseList { this.page.set_title(this.page_title); } + setup_view_menu() { + this.views_menu = this.page.add_custom_button_group(__(`View as {0}`, [this.view_name]), 'list'); + this.views_list = new frappe.views.Views({ + doctype: this.doctype, + parent: this.views_menu, + page: this.page, + list_view: this, + sidebar: this.list_sidebar, + }); + } + set_menu_items() { if (this.secondary_action) { - const $secondary_action = this.page.set_secondary_action( + const $secondary_action = this.page.set_secondary_action( this.secondary_action.label, this.secondary_action.action, this.secondary_action.icon @@ -739,13 +751,13 @@ class FilterArea { // utility function to validate view modes frappe.views.view_modes = [ "List", + "Report", + "Dashboard", "Gantt", "Kanban", "Calendar", "Image", "Inbox", - "Report", - "Dashboard", ]; frappe.views.is_valid = (view_mode) => frappe.views.view_modes.includes(view_mode); diff --git a/frappe/public/js/frappe/list/list_sidebar.html b/frappe/public/js/frappe/list/list_sidebar.html index dcbbe7ac5e..8fe4166f23 100644 --- a/frappe/public/js/frappe/list/list_sidebar.html +++ b/frappe/public/js/frappe/list/list_sidebar.html @@ -1,96 +1,74 @@ - - - - - - + + + diff --git a/frappe/public/js/frappe/list/list_sidebar.js b/frappe/public/js/frappe/list/list_sidebar.js index 4a6ac56294..bd1ce1a7cf 100644 --- a/frappe/public/js/frappe/list/list_sidebar.js +++ b/frappe/public/js/frappe/list/list_sidebar.js @@ -1,6 +1,5 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt -import ListFilter from './list_filter'; frappe.provide('frappe.views'); // opts: @@ -23,13 +22,6 @@ frappe.views.ListSidebar = class ListSidebar { .html(sidebar_content) .appendTo(this.page.sidebar.empty()); - this.setup_reports(); - this.setup_list_filter(); - this.setup_views(); - this.setup_kanban_boards(); - this.setup_calendar_view(); - this.setup_email_inbox(); - this.setup_keyboard_shortcuts(); this.setup_list_group_by(); // do not remove @@ -46,193 +38,6 @@ frappe.views.ListSidebar = class ListSidebar { } - setup_views() { - var show_list_link = false; - - if (frappe.views.calendar[this.doctype]) { - this.sidebar.find('.list-link[data-view="Calendar"]').removeClass("hide"); - this.sidebar.find('.list-link[data-view="Gantt"]').removeClass('hide'); - show_list_link = true; - } - //show link for kanban view - this.sidebar.find('.list-link[data-view="Kanban"]').removeClass('hide'); - if (this.doctype === "Communication" && frappe.boot.email_accounts.length) { - this.sidebar.find('.list-link[data-view="Inbox"]').removeClass('hide'); - show_list_link = true; - } - - if (frappe.treeview_settings[this.doctype] || frappe.get_meta(this.doctype).is_tree) { - this.sidebar.find(".tree-link").removeClass("hide"); - } - - this.current_view = 'List'; - var route = frappe.get_route(); - if (route.length > 2 && frappe.views.view_modes.includes(route[2])) { - this.current_view = route[2]; - - if (this.current_view === 'Kanban') { - this.kanban_board = route[3]; - } else if (this.current_view === 'Inbox') { - this.email_account = route[3]; - } - } - - // disable link for current view - this.sidebar.find('.list-link[data-view="' + this.current_view + '"] a') - .attr('disabled', 'disabled').addClass('disabled'); - - //enable link for Kanban view - this.sidebar.find('.list-link[data-view="Kanban"] a, .list-link[data-view="Inbox"] a') - .attr('disabled', null).removeClass('disabled'); - - // show image link if image_view - if (this.list_view.meta.image_field) { - this.sidebar.find('.list-link[data-view="Image"]').removeClass('hide'); - show_list_link = true; - } - - if (show_list_link) { - this.sidebar.find('.list-link[data-view="List"]').removeClass('hide'); - } - } - - setup_reports() { - // add reports linked to this doctype to the dropdown - var me = this; - var added = []; - var dropdown = this.page.sidebar.find('.reports-dropdown'); - var divider = false; - - var add_reports = function(reports) { - $.each(reports, function(name, r) { - if (!r.ref_doctype || r.ref_doctype == me.doctype) { - var report_type = r.report_type === 'Report Builder' ? - `List/${r.ref_doctype}/Report` : 'query-report'; - - var route = r.route || report_type + '/' + (r.title || r.name); - - if (added.indexOf(route) === -1) { - // don't repeat - added.push(route); - - if (!divider) { - me.get_divider().appendTo(dropdown); - divider = true; - } - - $('
  • ' + - __(r.title || r.name) + '
  • ').appendTo(dropdown); - } - } - }); - }; - - // from reference doctype - if (this.list_view.settings.reports) { - add_reports(this.list_view.settings.reports); - } - - // Sort reports alphabetically - var reports = Object.values(frappe.boot.user.all_reports).sort((a,b) => a.title.localeCompare(b.title)) || []; - - // from specially tagged reports - add_reports(reports); - } - - setup_list_filter() { - this.list_filter = new ListFilter({ - wrapper: this.page.sidebar.find('.list-filters'), - doctype: this.doctype, - list_view: this.list_view - }); - } - - setup_kanban_boards() { - const $dropdown = this.page.sidebar.find('.kanban-dropdown'); - frappe.views.KanbanView.setup_dropdown_in_sidebar(this.doctype, $dropdown); - } - - setup_calendar_view() { - const doctype = this.doctype; - - frappe.db.get_list('Calendar View', { - filters: { - reference_doctype: doctype - } - }).then(result => { - if (!(result && Array.isArray(result) && result.length)) return; - const calendar_views = result; - const $link_calendar = this.sidebar.find('.list-link[data-view="Calendar"]'); - - let default_link = ''; - if (frappe.views.calendar[this.doctype]) { - // has standard calendar view - default_link = `
  • - ${ __("Default") }
  • `; - } - const other_links = calendar_views.map( - calendar_view => `
  • - ${ __(calendar_view.name) } -
  • ` - ).join(''); - - const dropdown_html = ` -
    - - -
    - `; - $link_calendar.removeClass('hide'); - $link_calendar.html(dropdown_html); - }); - } - - setup_email_inbox() { - // get active email account for the user and add in dropdown - if (this.doctype != "Communication") - return; - - let $dropdown = this.page.sidebar.find('.email-account-dropdown'); - let divider = false; - - if (has_common(frappe.user_roles, ["System Manager", "Administrator"])) { - $(`
  • ${__("New Email Account")}
  • `) - .appendTo($dropdown); - } - - let accounts = frappe.boot.email_accounts; - accounts.forEach((account) => { - let email_account = (account.email_id == "All Accounts") ? "All Accounts" : account.email_account; - let route = ["List", "Communication", "Inbox", email_account].join('/'); - let display_name = ["All Accounts", "Sent Mail", "Spam", "Trash"].includes(account.email_id) ? __(account.email_id) : account.email_id; - - if (!divider) { - this.get_divider().appendTo($dropdown); - divider = true; - } - $(`
  • ${display_name}
  • `).appendTo($dropdown); - if (account.email_id === "Sent Mail") - divider = false; - }); - - $dropdown.find('.new-email-account').click(function() { - frappe.new_doc("Email Account"); - }); - } - - setup_keyboard_shortcuts() { - this.sidebar.find('.list-link > a, .list-link > .btn-group > a').each((i, el) => { - frappe.ui.keys - .get_shortcut_group(this.page) - .add($(el)); - }); - } - setup_list_group_by() { this.list_group_by = new frappe.views.ListGroupBy({ doctype: this.doctype, @@ -255,7 +60,7 @@ frappe.views.ListSidebar = class ListSidebar { stats: me.stats, doctype: me.doctype, // wait for list filter area to be generated before getting filters, or fallback to default filters - filters: (me.list_view.filter_area ? me.list_filter.get_current_filters() : me.default_filters) || [] + filters: (me.list_view.filter_area ? me.list_view.get_filters_for_args() : me.default_filters) || [] }, callback: function(r) { me.render_stat("_user_tags", (r.message.stats || {})["_user_tags"]); diff --git a/frappe/public/js/frappe/list/list_sidebar_group_by.js b/frappe/public/js/frappe/list/list_sidebar_group_by.js index d9324297a7..10fec477d4 100644 --- a/frappe/public/js/frappe/list/list_sidebar_group_by.js +++ b/frappe/public/js/frappe/list/list_sidebar_group_by.js @@ -1,4 +1,3 @@ - frappe.provide('frappe.views'); frappe.views.ListGroupBy = class ListGroupBy { @@ -8,8 +7,10 @@ frappe.views.ListGroupBy = class ListGroupBy { this.user_settings = frappe.get_user_settings(this.doctype); this.group_by_fields = ['assigned_to', 'owner']; - if(this.user_settings.group_by_fields) { - this.group_by_fields = this.group_by_fields.concat(this.user_settings.group_by_fields); + if (this.user_settings.group_by_fields) { + this.group_by_fields = this.group_by_fields.concat( + this.user_settings.group_by_fields + ); } this.render_group_by_items(); this.make_group_by_fields_modal(); @@ -18,23 +19,31 @@ frappe.views.ListGroupBy = class ListGroupBy { } make_group_by_fields_modal() { - let d = new frappe.ui.Dialog ({ - title: __("Select Filters"), - fields: this.get_group_by_dropdown_fields() + let d = new frappe.ui.Dialog({ + title: __('Select Filters'), + fields: this.get_group_by_dropdown_fields(), }); - d.set_primary_action("Save", ({ group_by_fields }) => { - frappe.model.user_settings.save(this.doctype, 'group_by_fields', group_by_fields || null); - this.group_by_fields = group_by_fields ? ['assigned_to', 'owner', ...group_by_fields] : ['assigned_to', 'owner']; + d.set_primary_action('Save', ({ group_by_fields }) => { + frappe.model.user_settings.save( + this.doctype, + 'group_by_fields', + group_by_fields || null + ); + this.group_by_fields = group_by_fields + ? ['assigned_to', 'owner', ...group_by_fields] + : ['assigned_to', 'owner']; this.render_group_by_items(); d.hide(); }); d.$body.prepend(``); - this.page.sidebar.find(".add-list-group-by a").on("click", () => { + this.page.sidebar.find('.add-list-group-by a').on('click', () => { frappe.utils.setup_search(d.$body, '.unit-checkbox', '.label-area'); d.show(); }); @@ -43,14 +52,11 @@ frappe.views.ListGroupBy = class ListGroupBy { make_wrapper() { this.$wrapper = this.sidebar.sidebar.find('.list-group-by'); let html = ` -
  • - ${__('Filter By')} -
  • - `; @@ -74,19 +80,19 @@ frappe.views.ListGroupBy = class ListGroupBy { } return ``; }; let html = this.group_by_fields.map(get_item_html).join(''); @@ -94,18 +100,27 @@ frappe.views.ListGroupBy = class ListGroupBy { } setup_dropdown() { - this.$wrapper.on('click', '.group-by-field', (e)=> { + this.$wrapper.on('click', '.group-by-field', (e) => { let dropdown = $(e.currentTarget).find('.group-by-dropdown'); - let fieldname = $(e.currentTarget).find('a').attr('data-fieldname'); - let fieldtype = $(e.currentTarget).find('a').attr('data-fieldtype'); - this.get_group_by_count(fieldname).then(field_count_list => { + let fieldname = $(e.currentTarget) + .find('a') + .attr('data-fieldname'); + let fieldtype = $(e.currentTarget) + .find('a') + .attr('data-fieldtype'); + this.get_group_by_count(fieldname).then((field_count_list) => { if (field_count_list.length) { this.render_dropdown_items(field_count_list, fieldtype, dropdown); - frappe.utils.setup_search(dropdown, '.group-by-item', '.group-by-value', 'data-name'); + frappe.utils.setup_search( + dropdown, + '.group-by-item', + '.group-by-value', + 'data-name' + ); } else { dropdown.html( - `
    - ${__("No filters found")} + `
    + ${__('No filters found')}
    ` ); } @@ -115,18 +130,19 @@ frappe.views.ListGroupBy = class ListGroupBy { get_group_by_dropdown_fields() { let group_by_fields = []; - let fields = this.list_view.meta.fields.filter((f)=> ["Select", "Link", "Data", "Int", "Check"].includes(f.fieldtype)); + let fields = this.list_view.meta.fields.filter((f) => + ['Select', 'Link', 'Data', 'Int', 'Check'].includes(f.fieldtype) + ); group_by_fields.push({ label: __(this.doctype), fieldname: 'group_by_fields', fieldtype: 'MultiCheck', columns: 2, - options: fields - .map(df => ({ - label: __(df.label), - value: df.fieldname, - checked: this.group_by_fields.includes(df.fieldname) - })) + options: fields.map((df) => ({ + label: __(df.label), + value: df.fieldname, + checked: this.group_by_fields.includes(df.fieldname), + })), }); return group_by_fields; } @@ -135,25 +151,32 @@ frappe.views.ListGroupBy = class ListGroupBy { let current_filters = this.list_view.get_filters_for_args(); // remove filter of the current field - current_filters = - current_filters.filter((f_arr) => !f_arr.includes(field === 'assigned_to' ? '_assign': field)); + current_filters = current_filters.filter( + (f_arr) => !f_arr.includes(field === 'assigned_to' ? '_assign' : field) + ); - let args = { + let args = { doctype: this.doctype, current_filters: current_filters, field: field, }; - - return frappe.call('frappe.desk.listview.get_group_by_count', args).then((r) => { - let field_counts = r.message || []; - field_counts = field_counts.filter(f => f.count !== 0); - let current_user = field_counts.find(f => f.name === frappe.session.user); - field_counts = field_counts.filter(f => !['Guest', 'Administrator', frappe.session.user].includes(f.name)); - // Set frappe.session.user on top of the list - if (current_user) field_counts.unshift(current_user); - return field_counts; - }); + return frappe + .call('frappe.desk.listview.get_group_by_count', args) + .then((r) => { + let field_counts = r.message || []; + field_counts = field_counts.filter((f) => f.count !== 0); + let current_user = field_counts.find( + (f) => f.name === frappe.session.user + ); + field_counts = field_counts.filter( + (f) => + !['Guest', 'Administrator', frappe.session.user].includes(f.name) + ); + // Set frappe.session.user on top of the list + if (current_user) field_counts.unshift(current_user); + return field_counts; + }); } render_dropdown_items(fields, fieldtype, dropdown) { @@ -162,20 +185,22 @@ frappe.views.ListGroupBy = class ListGroupBy { if (label === frappe.session.user) { label = __('Me'); } else if (fieldtype && fieldtype == 'Check') { - label = label == '0'? __('No'): __('Yes'); + label = label == '0' ? __('No') : __('Yes'); } let value = field.name == null ? '' : encodeURIComponent(field.name); return `
  • - - ${label} - ${field.count} + + ${label} + ${field.count}
  • `; }; let standard_html = ` `; @@ -186,27 +211,34 @@ frappe.views.ListGroupBy = class ListGroupBy { setup_filter_by() { this.$wrapper.on('click', '.group-by-item', (e) => { let $target = $(e.currentTarget); - let fieldname = $target.parents('.group-by-field').find('a').data('fieldname'); - let value = typeof $target.data('value') === 'string' - ? decodeURIComponent($target.data('value').trim()) - : $target.data('value'); - fieldname = fieldname === 'assigned_to' ? '_assign': fieldname; + let fieldname = $target + .parents('.group-by-field') + .find('a') + .data('fieldname'); + let value = + typeof $target.data('value') === 'string' + ? decodeURIComponent($target.data('value').trim()) + : $target.data('value'); + fieldname = fieldname === 'assigned_to' ? '_assign' : fieldname; - return this.list_view.filter_area.remove(fieldname) - .then(() => { - let operator = '='; - if (value === '') { - operator = 'is'; - value = 'not set'; - } - if (fieldname === '_assign') { - operator = 'like'; - value = `%${value}%`; - } + return this.list_view.filter_area.remove(fieldname).then(() => { + let operator = '='; + if (value === '') { + operator = 'is'; + value = 'not set'; + } + if (fieldname === '_assign') { + operator = 'like'; + value = `%${value}%`; + } - return this.list_view.filter_area.add(this.doctype, fieldname, operator, value); - }); + return this.list_view.filter_area.add( + this.doctype, + fieldname, + operator, + value + ); + }); }); } - }; diff --git a/frappe/public/js/frappe/list/list_sidebar_stat.html b/frappe/public/js/frappe/list/list_sidebar_stat.html index ed51f9ae7b..dd5b728af6 100644 --- a/frappe/public/js/frappe/list/list_sidebar_stat.html +++ b/frappe/public/js/frappe/list/list_sidebar_stat.html @@ -7,9 +7,9 @@ var stat_count = stat[i][1]; %}
  • - - {{ stat_count }} + {{ __(stat_label) }} + {{ stat_count }}
  • {% } diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 1840566669..60b6722511 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -751,7 +751,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { doc[this.meta.title_field || ""] !== doc.name ) { html += ` -