Merge pull request #35223 from iamejaaz/remove-list-sidebar
feat: remove list sidebar
This commit is contained in:
commit
329243f646
18 changed files with 545 additions and 669 deletions
|
|
@ -41,6 +41,7 @@ module.exports = defineConfig({
|
|||
excludeSpecPattern: [
|
||||
"./cypress/integration/workspace.js",
|
||||
"./cypress/integration/workspace_blocks.js",
|
||||
"./cypress/integration/customize_form.js",
|
||||
],
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ context("List View Settings", () => {
|
|||
});
|
||||
it("Default settings", () => {
|
||||
cy.get(".list-count").should("contain", "20 of");
|
||||
cy.get(".list-stats").should("contain", "Tags");
|
||||
});
|
||||
it("disable count and sidebar stats then verify", () => {
|
||||
cy.get(".list-count").should("contain", "20 of");
|
||||
|
|
|
|||
|
|
@ -82,63 +82,63 @@ context("Sidebar", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('Test for checking "Assigned To" counter value, adding filter and adding & removing an assignment', () => {
|
||||
cy.call("frappe.tests.ui_test_helpers.create_todo", {
|
||||
description: "Sidebar Attachment ToDo",
|
||||
}).then((todo) => {
|
||||
let todo_name = todo.message.name;
|
||||
cy.visit("/desk/todo");
|
||||
cy.click_sidebar_button("Assigned To");
|
||||
// it('Test for checking "Assigned To" counter value, adding filter and adding & removing an assignment', () => {
|
||||
// cy.call("frappe.tests.ui_test_helpers.create_todo", {
|
||||
// description: "Sidebar Attachment ToDo",
|
||||
// }).then((todo) => {
|
||||
// let todo_name = todo.message.name;
|
||||
// cy.visit("/desk/todo");
|
||||
// cy.click_sidebar_button("Assigned To");
|
||||
|
||||
//To check if no filter is available in "Assigned To" dropdown
|
||||
cy.get(".empty-state").should("contain", "No filters found");
|
||||
// //To check if no filter is available in "Assigned To" dropdown
|
||||
// cy.get(".empty-state").should("contain", "No filters found");
|
||||
|
||||
//Assigning a doctype to a user
|
||||
cy.visit(`/app/todo/${todo_name}`);
|
||||
cy.get(".add-assignment-btn").click();
|
||||
cy.get_field("assign_to_me", "Check").click();
|
||||
cy.wait(1000);
|
||||
cy.get(".modal-footer > .standard-actions > .btn-primary").click();
|
||||
cy.visit("/desk/todo");
|
||||
cy.click_sidebar_button("Assigned To");
|
||||
// //Assigning a doctype to a user
|
||||
// cy.visit(`/app/todo/${todo_name}`);
|
||||
// cy.get(".add-assignment-btn").click();
|
||||
// cy.get_field("assign_to_me", "Check").click();
|
||||
// cy.wait(1000);
|
||||
// cy.get(".modal-footer > .standard-actions > .btn-primary").click();
|
||||
// cy.visit("/desk/todo");
|
||||
// cy.click_sidebar_button("Assigned To");
|
||||
|
||||
//To check if filter is added in "Assigned To" dropdown after assignment
|
||||
cy.get(
|
||||
".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item"
|
||||
).should("contain", "1");
|
||||
// //To check if filter is added in "Assigned To" dropdown after assignment
|
||||
// cy.get(
|
||||
// ".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item"
|
||||
// ).should("contain", "1");
|
||||
|
||||
//To check if there is no filter added to the listview
|
||||
cy.get(".filter-button").should("contain", "Filter");
|
||||
// //To check if there is no filter added to the listview
|
||||
// cy.get(".filter-button").should("contain", "Filter");
|
||||
|
||||
//To add a filter to display data into the listview
|
||||
cy.get(
|
||||
".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item"
|
||||
).click();
|
||||
// //To add a filter to display data into the listview
|
||||
// cy.get(
|
||||
// ".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item"
|
||||
// ).click();
|
||||
|
||||
//To check if filter is applied
|
||||
cy.click_filter_button().get(".filter-label").should("contain", "1");
|
||||
cy.get(".fieldname-select-area > .awesomplete > .form-control").should(
|
||||
"have.value",
|
||||
"Assigned To"
|
||||
);
|
||||
cy.get(".condition").should("have.value", "like");
|
||||
cy.get(".filter-field > .form-group > .input-with-feedback").should(
|
||||
"have.value",
|
||||
`%${cy.config("testUser")}%`
|
||||
);
|
||||
cy.click_filter_button();
|
||||
// //To check if filter is applied
|
||||
// cy.click_filter_button().get(".filter-label").should("contain", "1");
|
||||
// cy.get(".fieldname-select-area > .awesomplete > .form-control").should(
|
||||
// "have.value",
|
||||
// "Assigned To"
|
||||
// );
|
||||
// cy.get(".condition").should("have.value", "like");
|
||||
// cy.get(".filter-field > .form-group > .input-with-feedback").should(
|
||||
// "have.value",
|
||||
// `%${cy.config("testUser")}%`
|
||||
// );
|
||||
// cy.click_filter_button();
|
||||
|
||||
//To remove the applied filter
|
||||
cy.clear_filters();
|
||||
// //To remove the applied filter
|
||||
// cy.clear_filters();
|
||||
|
||||
//To remove the assignment
|
||||
cy.visit(`/app/todo/${todo_name}`);
|
||||
cy.get(".assignments > .avatar-group > .avatar > .avatar-frame").click();
|
||||
cy.get(".remove-btn").click({ force: true });
|
||||
cy.hide_dialog();
|
||||
cy.visit("/desk/todo");
|
||||
cy.click_sidebar_button("Assigned To");
|
||||
cy.get(".empty-state").should("contain", "No filters found");
|
||||
});
|
||||
});
|
||||
// //To remove the assignment
|
||||
// cy.visit(`/app/todo/${todo_name}`);
|
||||
// cy.get(".assignments > .avatar-group > .avatar > .avatar-frame").click();
|
||||
// cy.get(".remove-btn").click({ force: true });
|
||||
// cy.hide_dialog();
|
||||
// cy.visit("/desk/todo");
|
||||
// cy.click_sidebar_button("Assigned To");
|
||||
// cy.get(".empty-state").should("contain", "No filters found");
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"disable_auto_refresh",
|
||||
"disable_sidebar_stats",
|
||||
"disable_automatic_recency_filters",
|
||||
"show_tags",
|
||||
"column_break_oany",
|
||||
"disable_comment_count",
|
||||
"disable_scrolling",
|
||||
|
|
@ -81,11 +82,17 @@
|
|||
{
|
||||
"fieldname": "section_break_evqq",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_tags",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Tags"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"links": [],
|
||||
"modified": "2025-08-25 15:54:18.886680",
|
||||
"modified": "2025-12-12 14:26:20.920434",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "List View Settings",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class ListViewSettings(Document):
|
|||
disable_scrolling: DF.Check
|
||||
disable_sidebar_stats: DF.Check
|
||||
fields: DF.Code | None
|
||||
show_tags: DF.Check
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export default class GridRowForm {
|
|||
<button class="btn btn-secondary btn-sm pull-right grid-insert-row-below hidden-xs">
|
||||
${__("Insert Below")}</button>
|
||||
<button class="btn btn-danger btn-sm pull-right grid-delete-row">
|
||||
${frappe.utils.icon("delete")}
|
||||
${frappe.utils.icon("trash-2")} ${__("Delete")}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import ListFilter from "./list_filter";
|
||||
frappe.provide("frappe.views");
|
||||
|
||||
frappe.views.BaseList = class BaseList {
|
||||
|
|
@ -14,6 +15,7 @@ frappe.views.BaseList = class BaseList {
|
|||
() => this.init(),
|
||||
() => this.before_refresh(),
|
||||
() => this.refresh(),
|
||||
() => this.setup_list_filter_by(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +28,6 @@ frappe.views.BaseList = class BaseList {
|
|||
this.setup_fields,
|
||||
// make view
|
||||
this.setup_page,
|
||||
this.setup_side_bar,
|
||||
this.setup_main_section,
|
||||
this.setup_view,
|
||||
this.setup_view_menu,
|
||||
|
|
@ -220,7 +221,6 @@ frappe.views.BaseList = class BaseList {
|
|||
parent: this.views_menu,
|
||||
page: this.page,
|
||||
list_view: this,
|
||||
sidebar: this.list_sidebar,
|
||||
icon_map: icon_map,
|
||||
label_map: label_map,
|
||||
});
|
||||
|
|
@ -275,24 +275,6 @@ frappe.views.BaseList = class BaseList {
|
|||
frappe.breadcrumbs.add(this.meta.module, this.doctype);
|
||||
}
|
||||
|
||||
setup_side_bar() {
|
||||
if (this.page.disable_sidebar_toggle) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.list_sidebar = new frappe.views.ListSidebar({
|
||||
doctype: this.doctype,
|
||||
stats: this.stats,
|
||||
parent: this.$page.find(".layout-side-section"),
|
||||
page: this.page,
|
||||
list_view: this,
|
||||
});
|
||||
}
|
||||
|
||||
toggle_side_bar(show) {
|
||||
frappe.app.sidebar.toggle_sidebar();
|
||||
}
|
||||
|
||||
show_or_hide_sidebar() {
|
||||
let show_sidebar = JSON.parse(localStorage.show_sidebar || "true");
|
||||
$(document.body).toggleClass("no-list-sidebar", !show_sidebar);
|
||||
|
|
@ -636,6 +618,10 @@ frappe.views.BaseList = class BaseList {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
setup_list_filter_by() {
|
||||
new ListFilter(this);
|
||||
}
|
||||
};
|
||||
|
||||
class FilterArea {
|
||||
|
|
@ -698,6 +684,12 @@ class FilterArea {
|
|||
setup() {
|
||||
if (!this.list_view.hide_page_form) this.make_standard_filters();
|
||||
this.make_filter_list();
|
||||
this.user_setting_fields =
|
||||
frappe.get_user_settings(this.list_view.doctype)?.group_by_fields || [];
|
||||
|
||||
if (["assigned_to", "owner", "tags"].some((v) => this.user_setting_fields.includes(v))) {
|
||||
this.render_non_standard_fields_filter();
|
||||
}
|
||||
}
|
||||
|
||||
get() {
|
||||
|
|
@ -810,6 +802,283 @@ class FilterArea {
|
|||
}, {});
|
||||
}
|
||||
|
||||
render_non_standard_fields_filter() {
|
||||
let get_item_html = (fieldname) => {
|
||||
let label, fieldtype;
|
||||
if (fieldname === "assigned_to") {
|
||||
label = __("Assigned To");
|
||||
} else if (fieldname === "owner") {
|
||||
label = __("Created By");
|
||||
} else if (fieldname === "tags") {
|
||||
label = __("Tags");
|
||||
}
|
||||
|
||||
return `<div class="group-by-field list-link form-group frappe-control input-max-width">
|
||||
<a class="btn btn-default btn-sm flex justify-between list-sidebar-button w-100" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false"
|
||||
data-label="${label}" data-fieldname="${fieldname}" data-fieldtype="${fieldtype}"
|
||||
href="#" onclick="return false;">
|
||||
<span class="ellipsis">${__(label)}</span>
|
||||
<span>${frappe.utils.icon("select", "xs")}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu group-by-dropdown" role="menu">
|
||||
</ul>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
let filtes_to_add = [];
|
||||
|
||||
if (this.user_setting_fields.includes("owner")) {
|
||||
filtes_to_add.push("owner");
|
||||
}
|
||||
|
||||
if (this.user_setting_fields.includes("assigned_to")) {
|
||||
filtes_to_add.push("assigned_to");
|
||||
}
|
||||
|
||||
if (this.user_setting_fields.includes("tags")) {
|
||||
filtes_to_add.push("tags");
|
||||
}
|
||||
|
||||
let html = filtes_to_add.map(get_item_html).join("");
|
||||
this.list_view.page.page_form.find(".standard-filter-section").append(html);
|
||||
this.setup_non_standard_items_dropdown();
|
||||
this.setup_filter_by();
|
||||
}
|
||||
|
||||
setup_non_standard_items_dropdown() {
|
||||
let standard_filter_container = this.list_view.page.page_form.find(
|
||||
".standard-filter-section"
|
||||
);
|
||||
standard_filter_container.find(".group-by-field").on("show.bs.dropdown", (e) => {
|
||||
let $dropdown = $(e.currentTarget).find(".group-by-dropdown");
|
||||
this.set_dropdown_loading_state($dropdown);
|
||||
let fieldname = $(e.currentTarget).find("a").attr("data-fieldname");
|
||||
let fieldtype = $(e.currentTarget).find("a").attr("data-fieldtype");
|
||||
|
||||
if (fieldname == "tags") {
|
||||
$dropdown.addClass("list-stats-dropdown");
|
||||
this.get_stats($dropdown);
|
||||
return;
|
||||
}
|
||||
this.get_group_by_count(fieldname).then((field_count_list) => {
|
||||
if (field_count_list.length) {
|
||||
if (fieldname == "assigned_to") {
|
||||
fieldname = "_assign";
|
||||
}
|
||||
if (fieldname == "tags") {
|
||||
fieldname = "_user_tags";
|
||||
}
|
||||
let applied_filter = this.list_view.get_filter_value(fieldname);
|
||||
this.render_dropdown_items(
|
||||
field_count_list,
|
||||
fieldtype,
|
||||
$dropdown,
|
||||
applied_filter
|
||||
);
|
||||
this.setup_search($dropdown);
|
||||
} else {
|
||||
this.set_empty_state($dropdown);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setup_filter_by() {
|
||||
let standard_filter_container = this.list_view.page.page_form.find(
|
||||
".standard-filter-section"
|
||||
);
|
||||
standard_filter_container.on("click", ".group-by-item", (e) => {
|
||||
let $target = $(e.currentTarget);
|
||||
|
||||
let is_selected = $target.hasClass("selected");
|
||||
|
||||
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");
|
||||
|
||||
if (fieldname == "assigned_to") {
|
||||
fieldname = "_assign";
|
||||
}
|
||||
if (fieldname == "tags") {
|
||||
fieldname = "_user_tags";
|
||||
}
|
||||
|
||||
return this.list_view.filter_area.remove(fieldname).then(() => {
|
||||
if (is_selected) return;
|
||||
return this.apply_filter(fieldname, value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render_dropdown_items(fields, fieldtype, $dropdown, applied_filter) {
|
||||
let standard_html = `
|
||||
<div class="dropdown-search mb-1">
|
||||
<input type="text"
|
||||
placeholder="${__("Search")}"
|
||||
data-element="search"
|
||||
class="dropdown-search-input form-control input-xs"
|
||||
>
|
||||
</div>
|
||||
`;
|
||||
let applied_filter_html = "";
|
||||
let dropdown_items_html = "";
|
||||
|
||||
fields.map((field) => {
|
||||
if (field.name === applied_filter) {
|
||||
applied_filter_html = this.get_dropdown_html(field, fieldtype, true);
|
||||
} else {
|
||||
dropdown_items_html += this.get_dropdown_html(field, fieldtype);
|
||||
}
|
||||
});
|
||||
|
||||
let dropdown_html = standard_html + applied_filter_html + dropdown_items_html;
|
||||
$dropdown.toggleClass("has-selected", Boolean(applied_filter_html));
|
||||
$dropdown.html(dropdown_html);
|
||||
}
|
||||
|
||||
get_dropdown_html(field, fieldtype, applied = false) {
|
||||
let label;
|
||||
if (field.name == null) {
|
||||
label = __("Not Set");
|
||||
} else if (field.name === frappe.session.user) {
|
||||
label = __("Me");
|
||||
} else if (fieldtype && fieldtype == "Check") {
|
||||
label = field.name == "0" ? __("No") : __("Yes");
|
||||
} else if (fieldtype && fieldtype == "Link" && field.title) {
|
||||
label = __(field.title);
|
||||
} else {
|
||||
label = __(field.name);
|
||||
}
|
||||
let value = field.name == null ? "" : encodeURIComponent(field.name);
|
||||
let applied_html = applied
|
||||
? `<span class="applied"> ${frappe.utils.icon("tick", "xs")} </span>`
|
||||
: "";
|
||||
return `<div class="group-by-item ${applied ? "selected" : ""}" data-value="${value}">
|
||||
<a class="dropdown-item flex justify-between" href="#" onclick="return false;">
|
||||
<span class="group-by-value ellipsis" data-name="${field.name}">
|
||||
${applied_html}
|
||||
${label}
|
||||
</span>
|
||||
<span class="group-by-count">${field.count}</span>
|
||||
</a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
get_stats($dropdown) {
|
||||
let me = this;
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.desk.reportview.get_sidebar_stats",
|
||||
type: "GET",
|
||||
args: {
|
||||
stats: ["_user_tags"],
|
||||
doctype: me.list_view.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_view.get_filters_for_args()
|
||||
: me.default_filters) || [],
|
||||
},
|
||||
callback: function (r) {
|
||||
let stats = (r.message.stats || {})["_user_tags"] || [];
|
||||
me.render_stat(stats, $dropdown);
|
||||
frappe.utils.setup_search($dropdown, ".stat-link", ".stat-label");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render_stat(stats, $dropdown) {
|
||||
let args = {
|
||||
stats: stats,
|
||||
label: __("Tags"),
|
||||
applied_filter: this.list_view.get_filter_value("_user_tags"),
|
||||
};
|
||||
|
||||
let tag_list = $(frappe.render_template("list_sidebar_stat", args)).on(
|
||||
"click",
|
||||
".stat-link",
|
||||
(e) => {
|
||||
let fieldname = $(e.currentTarget).attr("data-field");
|
||||
let label = $(e.currentTarget).attr("data-label");
|
||||
let condition = "like";
|
||||
let existing = this.list_view.filter_area.filter_list.get_filter(fieldname);
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
}
|
||||
if (label == "No Tags") {
|
||||
label = "not set";
|
||||
condition = "is";
|
||||
}
|
||||
this.list_view.filter_area.add(this.doctype, fieldname, condition, label);
|
||||
}
|
||||
);
|
||||
|
||||
$dropdown.html(tag_list);
|
||||
}
|
||||
|
||||
get_group_by_count(field) {
|
||||
let current_filters = this.list_view.get_filters_for_args();
|
||||
|
||||
current_filters = current_filters.filter(
|
||||
(f_arr) => !f_arr.includes(field === "assigned_to" ? "_assign" : field)
|
||||
);
|
||||
|
||||
let args = {
|
||||
doctype: this.list_view.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;
|
||||
});
|
||||
}
|
||||
|
||||
apply_filter(fieldname, value) {
|
||||
let operator = "=";
|
||||
if (value === "") {
|
||||
operator = "is";
|
||||
value = "not set";
|
||||
}
|
||||
if (fieldname === "_assign") {
|
||||
operator = "like";
|
||||
value = `%${value}%`;
|
||||
}
|
||||
|
||||
return this.list_view.filter_area.add(this.list_view.doctype, fieldname, operator, value);
|
||||
}
|
||||
|
||||
set_dropdown_loading_state($dropdown) {
|
||||
$dropdown.html(`<li>
|
||||
<div class="empty-state group-by-loading">
|
||||
${__("Loading...")}
|
||||
</div>
|
||||
</li>`);
|
||||
}
|
||||
|
||||
setup_search($dropdown) {
|
||||
frappe.utils.setup_search($dropdown, ".group-by-item", ".group-by-value", "data-name");
|
||||
}
|
||||
|
||||
set_empty_state($dropdown) {
|
||||
$dropdown.html(
|
||||
`<div class="empty-state group-by-empty">
|
||||
${__("No filters found")}
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
remove_filters(filters) {
|
||||
filters.map((f) => {
|
||||
this.remove(f[1]);
|
||||
|
|
@ -985,7 +1254,7 @@ class FilterArea {
|
|||
const $inputGroup = $input.parent();
|
||||
|
||||
const $dropdown = $(`
|
||||
<div class="input-group-btn">
|
||||
<div class="input-group-btn mr-0">
|
||||
<button type="button"
|
||||
class="btn btn-default match-type-dropdown-btn"
|
||||
data-toggle="dropdown"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ frappe.views.ListFactory = class ListFactory extends frappe.views.Factory {
|
|||
|
||||
frappe.provide("frappe.views.list_view." + doctype);
|
||||
|
||||
const hide_sidebar = view_class.no_sidebar || !frappe.boot.desk_settings.list_sidebar;
|
||||
const hide_sidebar = true;
|
||||
|
||||
frappe.views.list_view[me.page_name] = new view_class({
|
||||
doctype: doctype,
|
||||
|
|
|
|||
|
|
@ -1,169 +1,156 @@
|
|||
frappe.provide("frappe.ui");
|
||||
|
||||
export default class ListFilter {
|
||||
constructor({ wrapper, doctype }) {
|
||||
constructor(list_view) {
|
||||
this.list_view = list_view;
|
||||
|
||||
Object.assign(this, arguments[0]);
|
||||
this.can_add_global = frappe.user.has_role(["System Manager", "Administrator"]);
|
||||
this.filters = [];
|
||||
this.make();
|
||||
this.bind();
|
||||
this.refresh();
|
||||
this.refresh_list_filter();
|
||||
}
|
||||
|
||||
make() {
|
||||
// init dom
|
||||
this.wrapper.html(`
|
||||
<div class="input-area"></div>
|
||||
<div class="sidebar-action">
|
||||
<a class="saved-filters-preview">${__("Show Saved")}</a>
|
||||
</div>
|
||||
<div class="saved-filters"></div>
|
||||
`);
|
||||
|
||||
this.$input_area = this.wrapper.find(".input-area");
|
||||
this.$list_filters = this.wrapper.find(".list-filters");
|
||||
this.$saved_filters = this.wrapper.find(".saved-filters").hide();
|
||||
this.$saved_filters_preview = this.wrapper.find(".saved-filters-preview");
|
||||
this.saved_filters_hidden = true;
|
||||
this.toggle_saved_filters(true);
|
||||
|
||||
this.filter_input = frappe.ui.form.make_control({
|
||||
df: {
|
||||
fieldtype: "Data",
|
||||
placeholder: __("Filter Name"),
|
||||
input_class: "input-xs",
|
||||
},
|
||||
parent: this.$input_area,
|
||||
render_input: 1,
|
||||
});
|
||||
|
||||
this.is_global_input = frappe.ui.form.make_control({
|
||||
df: {
|
||||
fieldtype: "Check",
|
||||
label: __("Is Global"),
|
||||
},
|
||||
parent: this.$input_area,
|
||||
render_input: 1,
|
||||
});
|
||||
}
|
||||
|
||||
bind() {
|
||||
this.bind_save_filter();
|
||||
this.bind_toggle_saved_filters();
|
||||
this.bind_click_filter();
|
||||
this.bind_remove_filter();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
refresh_list_filter() {
|
||||
this.get_list_filters().then(() => {
|
||||
if (this.filters.length) {
|
||||
// expand collapsible sections
|
||||
this.wrapper.hasClass("hide") && this.section_title.trigger("click");
|
||||
this.$saved_filters_preview.show();
|
||||
} else {
|
||||
// hide collapsible sections
|
||||
!this.wrapper.hasClass("hide") && this.section_title.trigger("click");
|
||||
this.$saved_filters_preview.hide();
|
||||
}
|
||||
|
||||
const html = this.filters.map((filter) => this.filter_template(filter));
|
||||
this.wrapper.find(".filter-pill").remove();
|
||||
this.$saved_filters.append(html);
|
||||
this.render_saved_filters();
|
||||
});
|
||||
this.is_global_input.toggle(false);
|
||||
this.filter_input.set_description("");
|
||||
}
|
||||
|
||||
filter_template(filter) {
|
||||
return `<div class="list-link filter-pill list-sidebar-button btn btn-default" data-name="${
|
||||
filter.name
|
||||
}">
|
||||
<a class="ellipsis filter-name">${filter.filter_name}</a>
|
||||
<a class="remove">${frappe.utils.icon("close")}</a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
bind_toggle_saved_filters() {
|
||||
this.wrapper.find(".saved-filters-preview").click(() => {
|
||||
this.toggle_saved_filters(this.saved_filters_hidden);
|
||||
});
|
||||
}
|
||||
|
||||
toggle_saved_filters(show) {
|
||||
this.$saved_filters.toggle(show);
|
||||
const label = show ? __("Hide Saved") : __("Show Saved");
|
||||
this.wrapper.find(".saved-filters-preview").text(label);
|
||||
this.saved_filters_hidden = !this.saved_filters_hidden;
|
||||
}
|
||||
|
||||
bind_click_filter() {
|
||||
this.wrapper.on("click", ".filter-pill .filter-name", (e) => {
|
||||
let $filter = $(e.currentTarget).parent(".filter-pill");
|
||||
this.set_applied_filter($filter);
|
||||
const name = $filter.attr("data-name");
|
||||
this.list_view.filter_area.clear().then(() => {
|
||||
this.list_view.filter_area.add(this.get_filters_values(name));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bind_remove_filter() {
|
||||
this.wrapper.on("click", ".filter-pill .remove", (e) => {
|
||||
const $li = $(e.currentTarget).closest(".filter-pill");
|
||||
const filter_label = $li.text().trim();
|
||||
|
||||
frappe.confirm(
|
||||
__("Are you sure you want to remove the {0} filter?", [filter_label.bold()]),
|
||||
() => {
|
||||
const name = $li.attr("data-name");
|
||||
const applied_filters = this.get_filters_values(name);
|
||||
$li.remove();
|
||||
this.remove_filter(name).then(() => this.refresh());
|
||||
this.list_view.filter_area.remove_filters(applied_filters);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
bind_save_filter() {
|
||||
this.filter_input.$input.keydown(
|
||||
frappe.utils.debounce((e) => {
|
||||
const value = this.filter_input.get_value();
|
||||
const has_value = Boolean(value);
|
||||
|
||||
if (e.which === frappe.ui.keyCode["ENTER"]) {
|
||||
if (!has_value || this.filter_name_exists(value)) return;
|
||||
|
||||
this.filter_input.set_value("");
|
||||
this.save_filter(value).then(() => this.refresh());
|
||||
this.toggle_saved_filters(true);
|
||||
} else {
|
||||
let help_text = __("Press Enter to save");
|
||||
|
||||
if (this.filter_name_exists(value)) {
|
||||
help_text = __("Duplicate Filter Name");
|
||||
}
|
||||
|
||||
this.filter_input.set_description(has_value ? help_text : "");
|
||||
|
||||
if (this.can_add_global) {
|
||||
this.is_global_input.toggle(has_value);
|
||||
}
|
||||
}
|
||||
}, 300)
|
||||
this.saved_filters_btn = this.list_view.page.add_inner_button(
|
||||
__("Filters"),
|
||||
[],
|
||||
__("Saved Filters")
|
||||
);
|
||||
}
|
||||
|
||||
save_filter(filter_name) {
|
||||
render_saved_filters() {
|
||||
const $menu = this.saved_filters_btn.parent();
|
||||
$menu.empty();
|
||||
|
||||
this.filters.forEach((filter) => {
|
||||
const $item = this.filter_template(filter);
|
||||
|
||||
// Apply filter
|
||||
$item.find(".filter-label").on("click", () => {
|
||||
this.apply_saved_filter(filter.name);
|
||||
});
|
||||
|
||||
// Remove filter
|
||||
$item.find(".remove-filter").on("click", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.bind_remove_filter(filter);
|
||||
});
|
||||
|
||||
$menu.append($item);
|
||||
});
|
||||
|
||||
this.append_create_new_item($menu);
|
||||
}
|
||||
|
||||
apply_saved_filter(filter_name) {
|
||||
this.list_view.filter_area.clear().then(() => {
|
||||
this.list_view.filter_area.add(this.get_filters_values(filter_name));
|
||||
});
|
||||
}
|
||||
|
||||
bind_remove_filter(filter) {
|
||||
frappe.confirm(
|
||||
__("Are you sure you want to remove the {0} filter?", [filter.filter_name.bold()]),
|
||||
() => {
|
||||
const name = filter.name;
|
||||
const applied_filters = this.get_filters_values(name);
|
||||
this.remove_filter(name).then(() => this.refresh_list_filter());
|
||||
this.list_view.filter_area.remove_filters(applied_filters);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
append_create_new_item($menu) {
|
||||
const new_filter = {
|
||||
name: "create_new",
|
||||
filter_name: "Create New",
|
||||
};
|
||||
|
||||
const $create_item = this.filter_template(new_filter, true);
|
||||
$create_item.find(".filter-label").on("click", (e) => {
|
||||
this.show_create_filter_dialog();
|
||||
});
|
||||
$menu.append($create_item);
|
||||
}
|
||||
|
||||
show_create_filter_dialog() {
|
||||
const fields = [
|
||||
{
|
||||
fieldname: "filter_name",
|
||||
label: __("Filter Name"),
|
||||
fieldtype: "Data",
|
||||
reqd: 1,
|
||||
description: __("Press Enter to save"),
|
||||
},
|
||||
];
|
||||
|
||||
// Conditionally add "Is Global" checkbox
|
||||
if (this.can_add_global) {
|
||||
fields.push({
|
||||
fieldname: "is_global",
|
||||
label: __("Is Global"),
|
||||
fieldtype: "Check",
|
||||
default: 0,
|
||||
});
|
||||
}
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __("Create Saved Filter"),
|
||||
fields: fields,
|
||||
primary_action_label: __("Create"),
|
||||
primary_action: (values) => {
|
||||
this.bind_save_filter(dialog, values.filter_name, values?.is_global);
|
||||
},
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
bind_save_filter(dialog, filter_name, is_global) {
|
||||
const value = filter_name;
|
||||
const has_value = Boolean(value);
|
||||
if (!has_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.filter_name_exists(value)) {
|
||||
$(dialog.fields_dict.filter_name.wrapper).addClass("has-error");
|
||||
dialog.fields_dict.filter_name.set_description(__("Duplicate Filter Name"));
|
||||
return;
|
||||
}
|
||||
this.save_filter(value, is_global).then(() => {
|
||||
this.refresh_list_filter();
|
||||
dialog.hide();
|
||||
});
|
||||
}
|
||||
|
||||
save_filter(filter_name, is_global) {
|
||||
return frappe.db.insert({
|
||||
doctype: "List Filter",
|
||||
reference_doctype: this.list_view.doctype,
|
||||
filter_name,
|
||||
for_user: this.is_global_input.get_value() ? "" : frappe.session.user,
|
||||
for_user: is_global ? "" : frappe.session.user,
|
||||
filters: JSON.stringify(this.get_current_filters()),
|
||||
});
|
||||
}
|
||||
|
||||
filter_template(filter, add_new = false) {
|
||||
return $(`
|
||||
<li class="saved-filter-item" data-name="${filter.name}">
|
||||
<a class="dropdown-item d-flex justify-content-between align-items-center">
|
||||
<span class="filter-label">
|
||||
${frappe.utils.escape_html(__(filter.filter_name))}
|
||||
</span>
|
||||
<span class="remove-filter ${add_new ? "d-none" : ""} ">
|
||||
${frappe.utils.icon("x", "sm")}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
`);
|
||||
}
|
||||
|
||||
remove_filter(name) {
|
||||
if (!name) return;
|
||||
return frappe.db.delete_doc("List Filter", name);
|
||||
|
|
@ -198,11 +185,4 @@ export default class ListFilter {
|
|||
this.filters = filters || [];
|
||||
});
|
||||
}
|
||||
|
||||
set_applied_filter($filter) {
|
||||
this.$saved_filters
|
||||
.find(".btn-primary-light")
|
||||
.toggleClass("btn-primary-light btn-default");
|
||||
$filter.toggleClass("btn-default btn-primary-light");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
<div class="sidebar-section user-actions hide">
|
||||
</div>
|
||||
<div class="sidebar-section views-section hide">
|
||||
<div class="sidebar-label">
|
||||
</div>
|
||||
<div class="current-view">
|
||||
<div class="list-link">
|
||||
<a class="btn btn-default btn-sm list-sidebar-button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
href="#"
|
||||
>
|
||||
<span class="selected-view ellipsis">
|
||||
</span>
|
||||
<span>
|
||||
<svg class="icon icon-xs">
|
||||
<use href="#icon-select"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu views-dropdown" role="menu">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sidebar-action">
|
||||
<a class="view-action"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section filter-section">
|
||||
<div class="sidebar-label">
|
||||
<svg class="es-icon es-line icon-xs" aria-hidden="true">
|
||||
<use class="" href="#es-line-down"></use>
|
||||
</svg>
|
||||
<span>{{ __("Filter By") }}</span>
|
||||
</div>
|
||||
|
||||
<div class="list-group-by">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="sidebar-section tags-section">
|
||||
<div class="sidebar-label">
|
||||
<svg class="es-icon es-line icon-xs" aria-hidden="true">
|
||||
<use class="" href="#es-line-right-chevron"></use>
|
||||
</svg>
|
||||
<span>{{ __("Tags") }}</span>
|
||||
</div>
|
||||
|
||||
<div class="list-tags hide">
|
||||
<div class="list-stats list-link">
|
||||
<a
|
||||
class="btn btn-default btn-sm list-sidebar-button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
href="#"
|
||||
>
|
||||
<span>{{ __("Tags") }}</span>
|
||||
<span>
|
||||
<svg class="icon icon-xs">
|
||||
<use href="#icon-select"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu list-stats-dropdown" role="menu">
|
||||
<div class="dropdown-search">
|
||||
<input type="text" placeholder={{__("Search") }} data-element="search" class="form-control input-xs">
|
||||
</div>
|
||||
<div class="stat-result">
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sidebar-action show-tags">
|
||||
<a class="list-tag-preview">{{ __("Show Tags") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section save-filter-section">
|
||||
<div class="sidebar-label">
|
||||
<svg class="es-icon es-line icon-xs" aria-hidden="true">
|
||||
<use class="" href="#es-line-right-chevron"></use>
|
||||
</svg>
|
||||
<span>{{ __("Saved Filters") }}</span>
|
||||
</div>
|
||||
<div class="list-filters list-link hide"></div>
|
||||
</div>
|
||||
|
|
@ -1,277 +0,0 @@
|
|||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
import ListFilter from "./list_filter";
|
||||
frappe.provide("frappe.views");
|
||||
|
||||
// opts:
|
||||
// stats = list of fields
|
||||
// doctype
|
||||
// parent
|
||||
|
||||
frappe.views.ListSidebar = class ListSidebar {
|
||||
constructor(opts) {
|
||||
$.extend(this, opts);
|
||||
this.make();
|
||||
}
|
||||
|
||||
make() {
|
||||
var sidebar_content = frappe.render_template("list_sidebar", { doctype: this.doctype });
|
||||
|
||||
this.sidebar = $('<div class="list-sidebar overlay-sidebar hidden-xs hidden-sm"></div>')
|
||||
.html(sidebar_content)
|
||||
.appendTo(this.page.sidebar.empty());
|
||||
|
||||
this.setup_list_filter();
|
||||
this.setup_list_group_by();
|
||||
this.setup_collapsible();
|
||||
|
||||
// do not remove
|
||||
// used to trigger custom scripts
|
||||
$(document).trigger("list_sidebar_setup");
|
||||
|
||||
if (
|
||||
this.list_view.list_view_settings &&
|
||||
this.list_view.list_view_settings.disable_sidebar_stats
|
||||
) {
|
||||
this.sidebar.find(".list-tags").remove();
|
||||
} else {
|
||||
this.sidebar.find(".list-stats").on("show.bs.dropdown", (e) => {
|
||||
this.reload_stats();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
this.list_view.settings.get_coords_method ||
|
||||
(this.list_view.meta.fields.find((i) => i.fieldname === "latitude") &&
|
||||
this.list_view.meta.fields.find((i) => i.fieldname === "longitude")) ||
|
||||
this.list_view.meta.fields.find(
|
||||
(i) => i.fieldname === "location" && i.fieldtype == "Geolocation"
|
||||
)
|
||||
) {
|
||||
this.sidebar.find('.list-link[data-view="Map"]').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;
|
||||
}
|
||||
|
||||
$(
|
||||
'<li><a href="#' + route + '">' + __(r.title || r.name) + "</a></li>"
|
||||
).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,
|
||||
section_title: this.page.sidebar.find(".save-filter-section .sidebar-label"),
|
||||
});
|
||||
}
|
||||
|
||||
setup_collapsible() {
|
||||
// tags and save filter sections should be collapsible
|
||||
let sections = [
|
||||
["tags-section", "list-tags"],
|
||||
["save-filter-section", "list-filters"],
|
||||
["filter-section", "list-group-by"],
|
||||
];
|
||||
|
||||
for (let s of sections) {
|
||||
this.page.sidebar.find(`.${s[0]} .sidebar-label`).on("click", () => {
|
||||
let list_tags = this.page.sidebar.find("." + s[1]);
|
||||
let icon = "#es-line-down";
|
||||
list_tags.toggleClass("hide");
|
||||
if (list_tags.hasClass("hide")) {
|
||||
icon = "#es-line-right-chevron";
|
||||
}
|
||||
this.page.sidebar.find(`.${s[0]} .es-line use`).attr("href", icon);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setup_kanban_boards() {
|
||||
const $dropdown = this.page.sidebar.find(".kanban-dropdown");
|
||||
frappe.views.KanbanView.setup_dropdown_in_sidebar(this.doctype, $dropdown);
|
||||
}
|
||||
|
||||
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,
|
||||
sidebar: this,
|
||||
list_view: this.list_view,
|
||||
page: this.page,
|
||||
});
|
||||
}
|
||||
|
||||
get_stats() {
|
||||
var me = this;
|
||||
|
||||
let dropdown_options = me.sidebar.find(".list-stats-dropdown .stat-result");
|
||||
this.set_loading_state(dropdown_options);
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.desk.reportview.get_sidebar_stats",
|
||||
type: "GET",
|
||||
args: {
|
||||
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_view.get_filters_for_args()
|
||||
: me.default_filters) || [],
|
||||
},
|
||||
callback: function (r) {
|
||||
let stats = (r.message.stats || {})["_user_tags"] || [];
|
||||
me.render_stat(stats);
|
||||
let stats_dropdown = me.sidebar.find(".list-stats-dropdown");
|
||||
frappe.utils.setup_search(stats_dropdown, ".stat-link", ".stat-label");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
set_loading_state(dropdown) {
|
||||
dropdown.html(`<div>
|
||||
<div class="empty-state">
|
||||
${__("Loading...")}
|
||||
</div>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
render_stat(stats) {
|
||||
let args = {
|
||||
stats: stats,
|
||||
label: __("Tags"),
|
||||
};
|
||||
|
||||
let tag_list = $(frappe.render_template("list_sidebar_stat", args)).on(
|
||||
"click",
|
||||
".stat-link",
|
||||
(e) => {
|
||||
let fieldname = $(e.currentTarget).attr("data-field");
|
||||
let label = $(e.currentTarget).attr("data-label");
|
||||
let condition = "like";
|
||||
let existing = this.list_view.filter_area.filter_list.get_filter(fieldname);
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
}
|
||||
if (label == "No Tags") {
|
||||
label = "not set";
|
||||
condition = "is";
|
||||
}
|
||||
this.list_view.filter_area.add(this.doctype, fieldname, condition, label);
|
||||
}
|
||||
);
|
||||
|
||||
this.sidebar.find(".list-stats-dropdown .stat-result").html(tag_list);
|
||||
}
|
||||
|
||||
reload_stats() {
|
||||
this.sidebar.find(".stat-link").remove();
|
||||
this.sidebar.find(".stat-no-records").remove();
|
||||
this.get_stats();
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@ frappe.provide("frappe.views");
|
|||
|
||||
frappe.views.ListGroupBy = class ListGroupBy {
|
||||
constructor(opts) {
|
||||
// TODO: move assigned to and owner logic in this file, currently this file is not use
|
||||
$.extend(this, opts);
|
||||
this.make_wrapper();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
|
||||
{% if (stats.length) { %}
|
||||
<div class="dropdown-search mb-1">
|
||||
<input type="text"
|
||||
placeholder="${__("Search")}"
|
||||
data-element="search"
|
||||
class="dropdown-search-input form-control input-xs"
|
||||
>
|
||||
</div>
|
||||
{% } %}
|
||||
{% if (!stats.length) { %}
|
||||
<li class="stat-no-records text-muted">{{ __("No records tagged.") }}</li>
|
||||
{% } else {
|
||||
|
|
@ -7,8 +15,14 @@
|
|||
var stat_count = stats[i][1];
|
||||
%}
|
||||
<li>
|
||||
<a class="stat-link dropdown-item" data-label="{{ stat_label %}" data-field="_user_tags" href="#" onclick="return false;">
|
||||
<span class="stat-label">{{ __(stat_label) }}</span>
|
||||
<a class="stat-link dropdown-item flex justify-between group-by-item" data-label="{{ stat_label }}" data-value="{{ stat_label }}" data-field="_user_tags" href="#" onclick="return false;">
|
||||
|
||||
<span class="stat-label">
|
||||
{% if (applied_filter == stat_label) { %}
|
||||
<span class="applied"> {{ frappe.utils.icon("tick", "xs") }} </span>
|
||||
{% } %}
|
||||
{{ __(stat_label) }}
|
||||
</span>
|
||||
<span>{{ stat_count }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -329,6 +329,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
refresh_columns(meta, list_view_settings) {
|
||||
this.meta = meta;
|
||||
this.tags_shown = list_view_settings?.show_tags;
|
||||
this.list_view_settings = list_view_settings;
|
||||
|
||||
this.setup_columns();
|
||||
|
|
@ -727,7 +728,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
let classes = [
|
||||
"list-row-col ellipsis",
|
||||
col.type == "Subject" ? "list-subject level" : "hidden-xs",
|
||||
col.type == "Tag" ? "tag-col hide" : "",
|
||||
col.type == "Tag" ? `tag-col ${!this.tags_shown ? "hide" : ""} ` : "",
|
||||
frappe.model.is_numeric_field(col.df) ? "text-right" : "",
|
||||
col.df?.fieldname,
|
||||
].join(" ");
|
||||
|
|
@ -1338,7 +1339,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
this.setup_sort_by();
|
||||
this.setup_list_click();
|
||||
this.setup_drag_click();
|
||||
this.setup_tag_event();
|
||||
this.setup_tag_visibility();
|
||||
this.setup_new_doc_event();
|
||||
this.setup_check_events();
|
||||
this.setup_like();
|
||||
|
|
@ -1679,13 +1680,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
});
|
||||
}
|
||||
|
||||
setup_tag_event() {
|
||||
this.tags_shown = false;
|
||||
this.list_sidebar &&
|
||||
this.list_sidebar.parent.on("click", ".list-tag-preview", () => {
|
||||
this.tags_shown = !this.tags_shown;
|
||||
this.toggle_tags();
|
||||
});
|
||||
setup_tag_visibility() {
|
||||
this.tags_shown = this.list_view_settings?.show_tags;
|
||||
}
|
||||
|
||||
setup_realtime_updates() {
|
||||
|
|
@ -1853,12 +1849,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
this.toggle_actions_menu_button(this.$checks.length > 0);
|
||||
}
|
||||
|
||||
toggle_tags() {
|
||||
this.$result.find(".tag-col").toggleClass("hide");
|
||||
const preview_label = this.tags_shown ? __("Hide Tags") : __("Show Tags");
|
||||
this.list_sidebar.parent.find(".list-tag-preview").text(preview_label);
|
||||
}
|
||||
|
||||
get_checked_items(only_docnames) {
|
||||
const docnames = Array.from(this.$checks || []).map((check) =>
|
||||
cstr(unescape($(check).data().name))
|
||||
|
|
@ -1973,14 +1963,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
label: __("Toggle Sidebar", null, "Button in list view menu"),
|
||||
action: () => this.toggle_side_bar(),
|
||||
condition: () => !this.page.disable_sidebar_toggle,
|
||||
standard: true,
|
||||
shortcut: "Ctrl+G",
|
||||
});
|
||||
|
||||
if (frappe.user.has_role("System Manager") && frappe.boot.developer_mode) {
|
||||
// edit doctype
|
||||
items.push({
|
||||
|
|
@ -2044,14 +2026,28 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
get_group_by_dropdown_fields() {
|
||||
let group_by_fields = [];
|
||||
let default_fields = ["assigned_to", "owner"];
|
||||
default_fields = default_fields.concat(
|
||||
frappe.get_user_settings(this.doctype)?.group_by_fields || []
|
||||
);
|
||||
let default_fields = frappe.get_user_settings(this.doctype)?.group_by_fields || [];
|
||||
|
||||
let fields = this.meta.fields.filter((f) =>
|
||||
["Select", "Link", "Data", "Int", "Check"].includes(f.fieldtype)
|
||||
);
|
||||
|
||||
let default_fields_dict = [
|
||||
{
|
||||
label: "Assigned To",
|
||||
fieldname: "assigned_to",
|
||||
},
|
||||
{
|
||||
label: "Created By",
|
||||
fieldname: "owner",
|
||||
},
|
||||
{
|
||||
label: "Tags",
|
||||
fieldname: "tags",
|
||||
},
|
||||
];
|
||||
fields = fields.concat(default_fields_dict);
|
||||
|
||||
group_by_fields.push({
|
||||
label: __(this.doctype),
|
||||
fieldname: "group_by_fields",
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@ frappe.views.ListViewSelect = class ListViewSelect {
|
|||
if (frappe.get_route().length > 3) {
|
||||
default_action = {
|
||||
label: __("Report Builder"),
|
||||
action: () => this.set_route("report"),
|
||||
action: () => frappe.set_route("report"),
|
||||
};
|
||||
}
|
||||
this.setup_dropdown_in_sidebar("Report", reports, default_action);
|
||||
this.setup_dropdown_in_navbar("Report", reports, default_action);
|
||||
},
|
||||
},
|
||||
Dashboard: {
|
||||
|
|
@ -79,7 +79,7 @@ frappe.views.ListViewSelect = class ListViewSelect {
|
|||
action: () => this.set_route("calendar", "default"),
|
||||
current_view_handler: () => {
|
||||
this.get_calendars().then((calendars) => {
|
||||
this.setup_dropdown_in_sidebar("Calendar", calendars);
|
||||
this.setup_dropdown_in_navbar("Calendar", calendars);
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
@ -99,7 +99,7 @@ frappe.views.ListViewSelect = class ListViewSelect {
|
|||
action: () => frappe.new_doc("Email Account"),
|
||||
};
|
||||
}
|
||||
this.setup_dropdown_in_sidebar("Inbox", accounts, default_action);
|
||||
this.setup_dropdown_in_navbar("Inbox", accounts, default_action);
|
||||
},
|
||||
},
|
||||
Image: {
|
||||
|
|
@ -144,40 +144,22 @@ frappe.views.ListViewSelect = class ListViewSelect {
|
|||
});
|
||||
}
|
||||
|
||||
setup_dropdown_in_sidebar(view, items, default_action) {
|
||||
if (!this.sidebar) return;
|
||||
const views_wrapper = this.sidebar.sidebar.find(".views-section");
|
||||
views_wrapper.find(".sidebar-label").html(__(view));
|
||||
const $dropdown = views_wrapper.find(".views-dropdown");
|
||||
|
||||
setup_dropdown_in_navbar(view, items, default_action) {
|
||||
let placeholder = __("Select {0}", [__(view)]);
|
||||
let html = ``;
|
||||
|
||||
if (!items || !items.length) {
|
||||
html = `<div class="empty-state">
|
||||
${__("No {0} Found", [__(view)])}
|
||||
</div>`;
|
||||
} else {
|
||||
const page_name = this.get_page_name();
|
||||
if (items && items.length) {
|
||||
items.map((item) => {
|
||||
if (item.name.toLowerCase() == page_name.toLowerCase()) {
|
||||
placeholder = item.name;
|
||||
} else {
|
||||
html += `<li><a class="dropdown-item" href="${item.route}">${item.name}</a></li>`;
|
||||
}
|
||||
this.page.add_inner_button(
|
||||
item.name,
|
||||
() => location.replace(item.route),
|
||||
placeholder
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
views_wrapper.find(".selected-view").html(placeholder);
|
||||
|
||||
if (default_action) {
|
||||
views_wrapper.find(".sidebar-action a").html(default_action.label);
|
||||
views_wrapper.find(".sidebar-action a").click(() => default_action.action());
|
||||
if (default_action && Object.keys(default_action).length) {
|
||||
this.page.add_inner_button(default_action.label, default_action.action, placeholder);
|
||||
}
|
||||
|
||||
$dropdown.html(html);
|
||||
|
||||
views_wrapper.removeClass("hide");
|
||||
}
|
||||
|
||||
setup_kanban_switcher(kanbans) {
|
||||
|
|
|
|||
|
|
@ -77,12 +77,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
super.setup_page();
|
||||
}
|
||||
|
||||
toggle_side_bar() {
|
||||
super.toggle_side_bar();
|
||||
// refresh datatable when sidebar is toggled to accomodate extra space
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
setup_result_area() {
|
||||
super.setup_result_area();
|
||||
this.setup_charts_area();
|
||||
|
|
@ -1572,11 +1566,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
label: __("Toggle Chart"),
|
||||
action: () => this.toggle_charts(),
|
||||
},
|
||||
{
|
||||
label: __("Toggle Sidebar"),
|
||||
action: () => this.toggle_side_bar(),
|
||||
shortcut: "Ctrl+K",
|
||||
},
|
||||
{
|
||||
label: __("Pick Columns"),
|
||||
action: () => {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ import "./frappe/list/list_view.js";
|
|||
import "./frappe/list/list_factory.js";
|
||||
|
||||
import "./frappe/list/list_view_select.js";
|
||||
import "./frappe/list/list_sidebar.js";
|
||||
import "./frappe/list/list_sidebar.html";
|
||||
import "./frappe/list/list_sidebar_stat.html";
|
||||
import "./frappe/list/list_sidebar_group_by.js";
|
||||
import "./frappe/list/list_view_permission_restrictions.html";
|
||||
|
|
|
|||
|
|
@ -481,6 +481,12 @@ input.list-header-checkbox {
|
|||
.form-group {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.group-by-field {
|
||||
.group-by-dropdown {
|
||||
max-width: 220px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue