feat: configurable default views (#18409)

Co-authored-by: hrwx <himanshuwarekar@yahoo.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
This commit is contained in:
Shariq Ansari 2022-10-17 16:36:01 +05:30 committed by GitHub
parent cfbab92249
commit b0c1e400ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 673 additions and 92 deletions

View file

@ -24,6 +24,7 @@ context("Folder Navigation", () => {
it("Navigating the nested folders, checking if the URL formed is correct, checking if the added content in the child folder is correct", () => {
//Navigating inside the Attachments folder
cy.wait(500);
cy.get('[title="Attachments"] > span').click();
//To check if the URL formed after visiting the attachments folder is correct
@ -36,6 +37,7 @@ context("Folder Navigation", () => {
cy.click_modal_primary_button("Create");
//Navigating inside the added folder in the Attachments folder
cy.wait(500);
cy.get('[title="Test Folder"] > span').click();
//To check if the URL is correct after visiting the Test Folder
@ -51,7 +53,12 @@ context("Folder Navigation", () => {
cy.click_modal_primary_button("Upload");
//To check if the added file is present in the Test Folder
cy.get("span.level-item > span").should("contain", "Test Folder");
cy.visit("/app/file/view/home/Attachments");
cy.wait(500);
cy.get("span.level-item > a > span").should("contain", "Test Folder");
cy.visit("/app/file/view/home/Attachments/Test%20Folder");
cy.wait(500);
cy.get(".list-row-container").eq(0).should("contain.text", "72402.jpg");
cy.get(".list-row-checkbox").eq(0).click();

View file

@ -0,0 +1,231 @@
context("View", () => {
before(() => {
cy.login();
cy.visit("/app/website");
});
it("Route to ToDo List View", () => {
cy.visit("/app/todo/view/list");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("List");
});
});
it("Route to ToDo Report View", () => {
cy.visit("/app/todo/view/report");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Report");
});
});
it("Route to ToDo Dashboard View", () => {
cy.visit("/app/todo/view/dashboard");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Dashboard");
});
});
it("Route to ToDo Gantt View", () => {
cy.visit("/app/todo/view/gantt");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Gantt");
});
});
it("Route to ToDo Kanban View", () => {
cy.call("frappe.tests.ui_test_helpers.create_kanban").then(() => {
cy.visit("/app/note/view/kanban/_Note _Kanban");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Kanban");
});
});
});
it("Route to ToDo Calendar View", () => {
cy.visit("/app/todo/view/calendar");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Calendar");
});
});
it("Route to Custom Tree View", () => {
cy.call("frappe.tests.ui_test_helpers.setup_tree_doctype").then(() => {
cy.visit("/app/custom-tree/view/tree");
cy.wait(500);
cy.window()
.its("cur_tree")
.then((list) => {
expect(list.view_name).to.equal("Tree");
});
});
});
it("Route to Custom Image View", () => {
cy.call("frappe.tests.ui_test_helpers.setup_image_doctype").then(() => {
cy.visit("app/custom-image/view/image");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Image");
});
});
});
it("Route to Communication Inbox View", () => {
cy.call("frappe.tests.ui_test_helpers.setup_inbox").then(() => {
cy.visit("app/communication/view/inbox");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Inbox");
});
});
});
it("Route to File View", () => {
cy.visit("app/file");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("File");
expect(list.current_folder).to.equal("Home");
});
cy.visit("app/file/view/home/Attachments");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("File");
expect(list.current_folder).to.equal("Home/Attachments");
});
});
it("Re-route to default view", () => {
cy.call("frappe.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => {
cy.visit("app/event");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Report");
});
});
});
it("Route to default view from app/{doctype}", () => {
cy.call("frappe.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => {
cy.visit("/app/event");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Report");
});
});
});
it("Route to default view from app/{doctype}/view", () => {
cy.call("frappe.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => {
cy.visit("/app/event/view");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Report");
});
});
});
it("Force Route to default view from app/{doctype}", () => {
cy.call("frappe.tests.ui_test_helpers.setup_default_view", {
view: "Report",
force_reroute: true,
}).then(() => {
cy.visit("/app/event");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Report");
});
});
});
it("Force Route to default view from app/{doctype}/view", () => {
cy.call("frappe.tests.ui_test_helpers.setup_default_view", {
view: "Report",
force_reroute: true,
}).then(() => {
cy.visit("/app/event/view");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Report");
});
});
});
it("Force Route to default view from app/{doctype}/view", () => {
cy.call("frappe.tests.ui_test_helpers.setup_default_view", {
view: "Report",
force_reroute: true,
}).then(() => {
cy.visit("/app/event/view/list");
cy.wait(500);
cy.window()
.its("cur_list")
.then((list) => {
expect(list.view_name).to.equal("Report");
});
});
});
it("Validate Route History for Default View", () => {
cy.call("frappe.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => {
cy.visit("/app/event");
cy.visit("/app/event/view/list");
cy.location("pathname").should("eq", "/app/event/view/list");
cy.go("back");
cy.location("pathname").should("eq", "/app/event");
});
});
it("Route to Form", () => {
cy.call("frappe.tests.ui_test_helpers.create_note").then(() => {
cy.visit("/app/note/Routing Test");
cy.window()
.its("cur_frm")
.then((frm) => {
expect(frm.doc.title).to.equal("Routing Test");
});
});
});
it("Route to Settings Workspace", () => {
cy.visit("/app/settings");
cy.get(".title-text").should("contain", "Settings");
});
});

View file

@ -2,6 +2,7 @@
"actions": [],
"allow_import": 1,
"creation": "2013-01-29 10:47:14",
"default_view": "Inbox",
"description": "Keeps track of all communications",
"doctype": "DocType",
"document_type": "Setup",
@ -198,7 +199,6 @@
"label": "More Information"
},
{
"bold": 0,
"default": "Now",
"fieldname": "communication_date",
"fieldtype": "Datetime",
@ -395,7 +395,7 @@
"icon": "fa fa-comment",
"idx": 1,
"links": [],
"modified": "2022-03-30 11:24:25.728637",
"modified": "2022-05-09 00:13:45.310564",
"modified_by": "Administrator",
"module": "Core",
"name": "Communication",
@ -454,8 +454,9 @@
"sender_field": "sender",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"subject_field": "subject",
"title_field": "subject",
"track_changes": 1,
"track_seen": 1
}
}

View file

@ -55,6 +55,7 @@ frappe.ui.form.on("DocType", {
if (frm.is_new()) {
frm.events.set_default_permission(frm);
frm.set_value("default_view", "List");
} else {
frm.toggle_enable("engine", 0);
}
@ -66,12 +67,14 @@ frappe.ui.form.on("DocType", {
frm.cscript.autoname(frm);
frm.cscript.set_naming_rule_description(frm);
frm.trigger("setup_default_views");
},
istable: (frm) => {
if (frm.doc.istable && frm.is_new()) {
frm.set_value("autoname", "autoincrement");
frm.set_value("allow_rename", 0);
frm.set_value("default_view", null);
} else if (!frm.doc.istable && !frm.is_new()) {
frm.events.set_default_permission(frm);
}
@ -82,6 +85,18 @@ frappe.ui.form.on("DocType", {
frm.add_child("permissions", { role: "System Manager" });
}
},
is_tree: (frm) => {
frm.trigger("setup_default_views");
},
is_calendar_and_gantt: (frm) => {
frm.trigger("setup_default_views");
},
setup_default_views: (frm) => {
frappe.model.set_default_views_for_doctype(frm.doc.name, frm);
},
});
frappe.ui.form.on("DocField", {
@ -171,6 +186,10 @@ frappe.ui.form.on("DocField", {
fieldtype: function (frm) {
frm.trigger("max_attachments");
},
fields_add: (frm) => {
frm.trigger("setup_default_views");
},
});
extend_cscript(cur_frm.cscript, new frappe.model.DocTypeController({ frm: cur_frm }));

View file

@ -14,6 +14,7 @@
"istable",
"issingle",
"is_tree",
"is_calendar_and_gantt",
"editable_grid",
"quick_entry",
"cb01",
@ -53,6 +54,8 @@
"default_print_format",
"sort_field",
"sort_order",
"default_view",
"force_re_route_to_default_view",
"column_break_29",
"document_type",
"icon",
@ -606,6 +609,24 @@
"fieldname": "make_attachments_public",
"fieldtype": "Check",
"label": "Make Attachments Public by Default"
},
{
"fieldname": "default_view",
"fieldtype": "Select",
"label": "Default View"
},
{
"default": "0",
"fieldname": "force_re_route_to_default_view",
"fieldtype": "Check",
"label": "Force Re-route to Default View"
},
{
"default": "0",
"description": "Enables Calendar and Gantt views.",
"fieldname": "is_calendar_and_gantt",
"fieldtype": "Check",
"label": "Is Calendar and Gantt"
}
],
"icon": "fa fa-bolt",
@ -688,7 +709,7 @@
"link_fieldname": "reference_doctype"
}
],
"modified": "2022-09-02 12:05:59.589751",
"modified": "2022-10-12 14:13:27.315351",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",

View file

@ -2,6 +2,7 @@
"actions": [],
"allow_import": 1,
"creation": "2012-12-12 11:19:22",
"default_view": "File",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
@ -169,10 +170,11 @@
"read_only": 1
}
],
"force_re_route_to_default_view": 1,
"icon": "fa fa-file",
"idx": 1,
"links": [],
"modified": "2022-09-13 15:50:15.508250",
"modified": "2022-09-13 15:50:15.508251",
"modified_by": "Administrator",
"module": "Core",
"name": "File",

View file

@ -72,6 +72,7 @@ frappe.ui.form.on("Customize Form", {
} else {
frm.refresh();
frm.trigger("setup_sortable");
frm.trigger("setup_default_views");
}
}
localStorage["customize_doctype"] = frm.doc.doc_type;
@ -82,8 +83,12 @@ frappe.ui.form.on("Customize Form", {
}
},
is_calendar_and_gantt: function (frm) {
frm.trigger("setup_default_views");
},
setup_sortable: function (frm) {
frm.doc.fields.forEach(function (f, i) {
frm.doc.fields.forEach(function (f) {
if (!f.is_custom_field) {
f._sortable = false;
}
@ -222,6 +227,10 @@ frappe.ui.form.on("Customize Form", {
frm.set_df_property("sort_field", "options", fields);
}
},
setup_default_views(frm) {
frappe.model.set_default_views_for_doctype(frm.doc.doc_type, frm);
},
});
// can't delete standard fields
@ -237,6 +246,7 @@ frappe.ui.form.on("Customize Form Field", {
var f = frappe.model.get_doc(cdt, cdn);
f.is_system_generated = false;
f.is_custom_field = true;
frm.trigger("setup_default_views");
},
});

View file

@ -13,6 +13,7 @@
"search_fields",
"column_break_5",
"istable",
"is_calendar_and_gantt",
"editable_grid",
"quick_entry",
"track_changes",
@ -35,6 +36,8 @@
"show_title_field_in_link",
"translated_doctype",
"default_print_format",
"default_view",
"force_re_route_to_default_view",
"column_break_29",
"show_preview_popup",
"email_settings_section",
@ -337,6 +340,25 @@
"fieldname": "make_attachments_public",
"fieldtype": "Check",
"label": "Make Attachments Public by Default"
},
{
"fieldname": "default_view",
"fieldtype": "Select",
"label": "Default View"
},
{
"default": "0",
"depends_on": "default_view",
"fieldname": "force_re_route_to_default_view",
"fieldtype": "Check",
"label": "Force Re-route to Default View"
},
{
"default": "0",
"description": "Enables Calendar and Gantt views.",
"fieldname": "is_calendar_and_gantt",
"fieldtype": "Check",
"label": "Is Calendar and Gantt"
}
],
"hide_toolbar": 1,
@ -345,7 +367,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-08-24 06:57:47.966331",
"modified": "2022-08-30 11:45:16.772277",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",

View file

@ -586,6 +586,10 @@ doctype_properties = {
"naming_rule": "Data",
"autoname": "Data",
"show_title_field_in_link": "Check",
"translate_link_fields": "Check",
"is_calendar_and_gantt": "Check",
"default_view": "Select",
"force_re_route_to_default_view": "Check",
"translated_doctype": "Check",
}

View file

@ -196,7 +196,7 @@ frappe.views.BaseList = class BaseList {
Map: "map",
};
if (frappe.boot.desk_settings.view_switcher) {
if (frappe.boot.desk_settings.view_switcher && !this.meta.force_re_route_to_default_view) {
/* @preserve
for translation, don't remove
__("List View") __("Report View") __("Dashboard View") __("Gantt View"),

View file

@ -349,7 +349,7 @@ $.extend(frappe.model, {
is_tree: function (doctype) {
if (!doctype) return false;
return frappe.boot.treeviews.indexOf(doctype) != -1;
return locals.DocType[doctype] && locals.DocType[doctype].is_tree;
},
is_fresh(doc) {
@ -754,6 +754,42 @@ $.extend(frappe.model, {
}
return frappe.model.numeric_fieldtypes.includes(fieldtype);
},
set_default_views_for_doctype(doctype, frm) {
frappe.model.with_doctype(doctype, () => {
let meta = frappe.get_meta(doctype);
let default_views = ["List", "Report", "Dashboard", "Kanban"];
if (meta.is_calendar_and_gantt && frappe.views.calendar[doctype]) {
let views = ["Calendar", "Gantt"];
default_views.push(...views);
}
if (meta.is_tree) {
default_views.push("Tree");
}
if (frm.doc.image_field) {
default_views.push("Image");
}
if (doctype === "Communication" && frappe.boot.email_accounts.length) {
default_views.push("Inbox");
}
if (
(frm.doc.fields.find((i) => i.fieldname === "latitude") &&
frm.doc.fields.find((i) => i.fieldname === "longitude")) ||
frm.doc.fields.find(
(i) => i.fieldname === "location" && i.fieldtype == "Geolocation"
)
) {
default_views.push("Map");
}
frm.set_df_property("default_view", "options", default_views);
});
},
});
// legacy

View file

@ -89,6 +89,18 @@ frappe.router = {
"image",
"inbox",
],
list_views_route: {
list: "List",
kanban: "Kanban",
report: "Report",
calendar: "Calendar",
tree: "Tree",
gantt: "Gantt",
dashboard: "Dashboard",
image: "Image",
inbox: "Inbox",
file: "Home",
},
layout_mapped: {},
is_app_route(path) {
@ -115,7 +127,7 @@ frappe.router = {
}
},
route() {
async route() {
// resolve the route from the URL or hash
// translate it so the objects are well defined
// and render the page as required
@ -126,22 +138,22 @@ frappe.router = {
if (this.re_route(sub_path)) return;
this.current_sub_path = sub_path;
this.current_route = this.parse();
this.current_route = await this.parse();
this.set_history(sub_path);
this.render();
this.set_title(sub_path);
this.trigger("change");
},
parse(route) {
async parse(route) {
route = this.get_sub_path_string(route).split("/");
if (!route) return [];
route = $.map(route, this.decode_component);
this.set_route_options_from_url();
return this.convert_to_standard_route(route);
return await this.convert_to_standard_route(route);
},
convert_to_standard_route(route) {
async convert_to_standard_route(route) {
// /app/settings = ["Workspaces", "Settings"]
// /app/private/settings = ["Workspaces", "private", "Settings"]
// /app/user = ["List", "User"]
@ -161,7 +173,7 @@ frappe.router = {
route = ["Workspaces", "private", frappe.workspaces[private_workspace].title];
} else if (this.routes[route[0]]) {
// route
route = this.set_doctype_route(route);
route = await this.set_doctype_route(route);
}
return route;
@ -174,36 +186,85 @@ frappe.router = {
set_doctype_route(route) {
let doctype_route = this.routes[route[0]];
// doctype route
if (route[1]) {
if (route[2] && route[1] === "view") {
route = this.get_standard_route_for_list(route, doctype_route);
} else {
return frappe.model.with_doctype(doctype_route.doctype).then(() => {
// doctype route
let meta = frappe.get_meta(doctype_route.doctype);
if (route[1] && route[1] === "view" && route[2]) {
route = this.get_standard_route_for_list(
route,
doctype_route,
meta.force_re_route_to_default_view && meta.default_view
? meta.default_view
: null
);
} else if (route[1] && route[1] !== "view" && !route[2]) {
let docname = route[1];
if (route.length > 2) {
docname = route.slice(1).join("/");
}
route = ["Form", doctype_route.doctype, docname];
} else if (frappe.model.is_single(doctype_route.doctype)) {
route = ["Form", doctype_route.doctype, doctype_route.doctype];
} else if (meta.default_view) {
route = [
"List",
doctype_route.doctype,
this.list_views_route[meta.default_view.toLowerCase()],
];
} else {
route = ["List", doctype_route.doctype, "List"];
}
} else if (frappe.model.is_single(doctype_route.doctype)) {
route = ["Form", doctype_route.doctype, doctype_route.doctype];
} else {
route = ["List", doctype_route.doctype, "List"];
}
// reset the layout to avoid using incorrect views
this.doctype_layout = doctype_route.doctype_layout;
return route;
// reset the layout to avoid using incorrect views
this.doctype_layout = doctype_route.doctype_layout;
return route;
});
},
get_standard_route_for_list(route, doctype_route) {
get_standard_route_for_list(route, doctype_route, default_view) {
let standard_route;
if (route[2].toLowerCase() === "tree") {
let _route = default_view || route[2] || "";
if (_route.toLowerCase() === "tree") {
standard_route = ["Tree", doctype_route.doctype];
} else {
standard_route = ["List", doctype_route.doctype, frappe.utils.to_title_case(route[2])];
let new_route = this.list_views_route[_route.toLowerCase()];
let re_route = route[2].toLowerCase() !== new_route.toLowerCase();
if (re_route) {
/**
* In case of force_re_route, the url of the route should change,
* if the _route and route[2] are different, it means there is a default_view
* with force_re_route enabled.
*
* To change the url, to the correct view, the route[2] is changed with default_view
*
* Eg: If default_view is set to Report with force_re_route enabled and user routes
* to List,
* route: [todo, view, list]
* default_view: report
*
* replaces the list to report and re-routes to the new route but should be replaced in
* the history since the list route should not exist in history as we are rerouting it to
* report
*/
frappe.route_flags.replace_route = true;
route[2] = _route.toLowerCase();
this.set_route(route);
}
standard_route = [
"List",
doctype_route.doctype,
this.list_views_route[_route.toLowerCase()],
];
// calendar / kanban / dashboard / folder
if (route[3]) standard_route.push(...route.slice(3, route.length));
}
return standard_route;
},
@ -345,6 +406,7 @@ frappe.router = {
} else if (view === "tree") {
new_route = [this.slug(route[1]), "view", "tree"];
}
return new_route;
},

View file

@ -208,13 +208,10 @@ frappe.search.utils = {
},
});
}
if (in_list(frappe.boot.treeviews, item)) {
out.push(option("Tree", ["Tree", item], 0.05));
} else {
out.push(option("List", ["List", item], 0.05));
if (frappe.model.can_get_report(item)) {
out.push(option("Report", ["List", item, "Report"], 0.04));
}
out.push(option("List", ["List", item], 0.05));
if (frappe.model.can_get_report(item)) {
out.push(option("Report", ["List", item, "Report"], 0.04));
}
}
}

View file

@ -1260,20 +1260,12 @@ Object.assign(frappe.utils, {
if (frappe.model.is_single(item.doctype)) {
route = doctype_slug;
} else {
if (!item.doc_view) {
if (frappe.model.is_tree(item.doctype)) {
item.doc_view = "Tree";
} else {
item.doc_view = "List";
}
}
switch (item.doc_view) {
case "List":
if (item.filters) {
frappe.route_options = item.filters;
}
route = doctype_slug;
route = `${doctype_slug}/view/list`;
break;
case "Tree":
route = `${doctype_slug}/view/tree`;
@ -1290,12 +1282,11 @@ Object.assign(frappe.utils, {
case "Calendar":
route = `${doctype_slug}/view/calendar/default`;
break;
case "Kanban":
route = `${doctype_slug}/view/kanban`;
break;
default:
frappe.throw({
message: __("Not a valid view:") + item.doc_view,
title: __("Unknown View"),
});
route = "";
route = doctype_slug;
}
}
} else if (type === "report") {

View file

@ -143,7 +143,7 @@ frappe.breadcrumbs = {
} else {
let route;
const doctype_route = frappe.router.slug(frappe.router.doctype_layout || doctype);
if (frappe.boot.treeviews.indexOf(doctype) !== -1) {
if (doctype_meta.is_tree) {
let view = frappe.model.user_settings[doctype].last_view || "Tree";
route = `${doctype_route}/view/${view}`;
} else {

View file

@ -74,7 +74,7 @@ frappe.views.FileView = class FileView extends frappe.views.ListView {
this.page_title = __("File Manager");
const route = frappe.get_route();
this.current_folder = route.slice(2).join("/");
this.current_folder = route.slice(2).join("/") || "Home";
this.filters = [["File", "folder", "=", this.current_folder, true]];
this.order_by = this.view_user_settings.order_by || "file_name asc";
@ -286,7 +286,7 @@ frappe.views.FileView = class FileView extends frappe.views.ListView {
}
get_breadcrumbs_html() {
const route = frappe.router.parse();
const route = frappe.get_route();
const folders = route.slice(2);
return folders

View file

@ -9,14 +9,9 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
const doctype = route[1];
const user_settings = frappe.get_user_settings(doctype)["Kanban"] || {};
if (!user_settings.last_kanban_board) {
frappe.msgprint({
title: __("Error"),
indicator: "red",
message: __("Missing parameter Kanban Board Name"),
});
frappe.set_route("List", doctype, "List");
return true;
return new frappe.views.KanbanView({ doctype: doctype });
}
route.push(user_settings.last_kanban_board);
frappe.set_route(route);
return true;
@ -28,9 +23,35 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
return "Kanban";
}
show() {
frappe.views.KanbanView.get_kanbans(this.doctype).then((kanbans) => {
if (!kanbans.length) {
return frappe.views.KanbanView.show_kanban_dialog(this.doctype, true);
} else if (kanbans.length && frappe.get_route().length !== 4) {
return frappe.views.KanbanView.show_kanban_dialog(this.doctype, true);
} else {
this.kanbans = kanbans;
return frappe.run_serially([
() => this.show_skeleton(),
() => this.fetch_meta(),
() => this.hide_skeleton(),
() => this.check_permissions(),
() => this.init(),
() => this.before_refresh(),
() => this.refresh(),
]);
}
});
}
setup_defaults() {
return super.setup_defaults().then(() => {
this.board_name = frappe.get_route()[3];
let get_board_name = () => {
return this.kanbans.length && this.kanbans[0].name;
};
this.board_name = frappe.get_route()[3] || get_board_name() || null;
this.page_title = __(this.board_name);
this.card_meta = this.get_card_meta();
this.page_length = 0;
@ -143,21 +164,22 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
render() {
const board_name = this.board_name;
if (this.kanban && board_name === this.kanban.board_name) {
this.kanban.update(this.data);
return;
if (!this.kanban) {
this.kanban = new frappe.views.KanbanBoard({
doctype: this.doctype,
board: this.board,
board_name: board_name,
cards: this.data,
card_meta: this.card_meta,
wrapper: this.$result,
cur_list: this,
user_settings: this.view_user_settings,
});
}
this.kanban = new frappe.views.KanbanBoard({
doctype: this.doctype,
board: this.board,
board_name: board_name,
cards: this.data,
card_meta: this.card_meta,
wrapper: this.$result,
cur_list: this,
user_settings: this.view_user_settings,
});
if (this.kanban && board_name === this.kanban.board_name) {
this.kanban.update(this.data);
}
}
get_card_meta() {

View file

@ -37,6 +37,10 @@ frappe.views.TreeFactory = class TreeFactory extends frappe.views.Factory {
let treeview = frappe.views.trees[route[1]];
treeview && treeview.make_tree();
}
get view_name() {
return "Tree";
}
};
frappe.views.TreeView = class TreeView {
@ -196,6 +200,7 @@ frappe.views.TreeView = class TreeView {
});
cur_tree = this.tree;
cur_tree.view_name = "Tree";
this.post_render();
}

View file

@ -384,18 +384,22 @@ class ShortcutDialog extends WidgetDialog {
onchange: () => {
if (this.dialog.get_value("type") == "DocType") {
let doctype = this.dialog.get_value("link_to");
if (doctype && frappe.boot.single_types.includes(doctype)) {
this.hide_filters();
} else if (doctype) {
this.setup_filter(doctype);
this.show_filters();
}
frappe.model.with_doctype(doctype, () => {
let meta = frappe.get_meta(doctype);
const views = ["List", "Report Builder", "Dashboard", "New"];
if (frappe.boot.treeviews.includes(doctype)) views.push("Tree");
if (frappe.boot.calendars.includes(doctype)) views.push("Calendar");
if (doctype && frappe.boot.single_types.includes(doctype)) {
this.hide_filters();
} else if (doctype) {
this.setup_filter(doctype);
this.show_filters();
}
this.dialog.set_df_property("doc_view", "options", views.join("\n"));
const views = ["List", "Report Builder", "Dashboard", "New"];
if (meta.is_tree === "Tree") views.push("Tree");
if (frappe.boot.calendars.includes(doctype)) views.push("Calendar");
this.dialog.set_df_property("doc_view", "options", views.join("\n"));
});
} else {
this.hide_filters();
}
@ -405,7 +409,7 @@ class ShortcutDialog extends WidgetDialog {
fieldtype: "Select",
fieldname: "doc_view",
label: "DocType View",
options: "List\nReport Builder\nDashboard\nTree\nNew\nCalendar",
options: "List\nReport Builder\nDashboard\nTree\nNew\nCalendar\nKanban",
description: __(
"Which view of the associated DocType should this shortcut take you to?"
),

View file

@ -43,16 +43,32 @@ def create_todo_records():
frappe.db.truncate("ToDo")
frappe.get_doc(
{"doctype": "ToDo", "date": add_to_date(now(), days=7), "description": "this is first todo"}
{
"doctype": "ToDo",
"date": add_to_date(now(), days=7),
"description": "this is first todo",
}
).insert()
frappe.get_doc(
{"doctype": "ToDo", "date": add_to_date(now(), days=-7), "description": "this is second todo"}
{
"doctype": "ToDo",
"date": add_to_date(now(), days=-7),
"description": "this is second todo",
}
).insert()
frappe.get_doc(
{"doctype": "ToDo", "date": add_to_date(now(), months=2), "description": "this is third todo"}
{
"doctype": "ToDo",
"date": add_to_date(now(), months=2),
"description": "this is third todo",
}
).insert()
frappe.get_doc(
{"doctype": "ToDo", "date": add_to_date(now(), months=-2), "description": "this is fourth todo"}
{
"doctype": "ToDo",
"date": add_to_date(now(), months=-2),
"description": "this is fourth todo",
}
).insert()
@ -431,3 +447,134 @@ def create_test_user():
user.append("roles", {"role": role})
user.save()
@frappe.whitelist()
def setup_tree_doctype():
frappe.delete_doc_if_exists("DocType", "Custom Tree")
frappe.get_doc(
{
"doctype": "DocType",
"module": "Core",
"custom": 1,
"fields": [
{"fieldname": "tree", "fieldtype": "Data", "label": "Tree"},
],
"permissions": [{"role": "System Manager", "read": 1}],
"name": "Custom Tree",
"is_tree": True,
"naming_rule": "By fieldname",
"autoname": "field:tree",
}
).insert()
if not frappe.db.exists("Custom Tree", "All Trees"):
frappe.get_doc({"doctype": "Custom Tree", "tree": "All Trees"}).insert()
@frappe.whitelist()
def setup_image_doctype():
frappe.delete_doc_if_exists("DocType", "Custom Image")
frappe.get_doc(
{
"doctype": "DocType",
"module": "Core",
"custom": 1,
"fields": [
{"fieldname": "image", "fieldtype": "Attach Image", "label": "Image"},
],
"permissions": [{"role": "System Manager", "read": 1}],
"name": "Custom Image",
"image_field": "image",
}
).insert()
@frappe.whitelist()
def setup_inbox():
frappe.db.sql("DELETE FROM `tabUser Email`")
user = frappe.get_doc("User", frappe.session.user)
user.append("user_emails", {"email_account": "Email Linking"})
user.save()
@frappe.whitelist()
def setup_default_view(view, force_reroute=None):
frappe.delete_doc_if_exists("Property Setter", "Event-main-default_view")
frappe.delete_doc_if_exists("Property Setter", "Event-main-force_re_route_to_default_view")
frappe.get_doc(
{
"is_system_generated": 0,
"doctype_or_field": "DocType",
"doc_type": "Event",
"property": "default_view",
"property_type": "Select",
"value": view,
"doctype": "Property Setter",
}
).insert()
if force_reroute:
frappe.get_doc(
{
"is_system_generated": 0,
"doctype_or_field": "DocType",
"doc_type": "Event",
"property": "force_re_route_to_default_view",
"property_type": "Check",
"value": "1",
"doctype": "Property Setter",
}
).insert()
@frappe.whitelist()
def create_note():
if not frappe.db.exists("Note", "Routing Test"):
frappe.get_doc({"doctype": "Note", "title": "Routing Test"}).insert()
@frappe.whitelist()
def create_kanban():
if not frappe.db.exists("Custom Field", "Note-kanban"):
frappe.get_doc(
{
"is_system_generated": 0,
"dt": "Note",
"label": "Kanban",
"fieldname": "kanban",
"insert_after": "seen_by",
"fieldtype": "Select",
"options": "Open\nClosed",
"doctype": "Custom Field",
}
).insert()
if not frappe.db.exists("Kanban Board", "_Note _Kanban"):
frappe.get_doc(
{
"doctype": "Kanban Board",
"name": "_Note _Kanban",
"kanban_board_name": "_Note _Kanban",
"reference_doctype": "Note",
"field_name": "kanban",
"private": 1,
"show_labels": 0,
"columns": [
{
"column_name": "Open",
"status": "Active",
"indicator": "Gray",
},
{
"column_name": "Closed",
"status": "Active",
"indicator": "Gray",
},
],
}
).insert()