Merge pull request #27908 from frappe/ws_types

feat(Workspace): user can now add doctype, page, report, external url as workspace and get easy access on sidebar
This commit is contained in:
Sumit Bhanushali 2024-09-30 11:09:00 +05:30 committed by GitHub
commit 08aa9158dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 176 additions and 43 deletions

View file

@ -20,6 +20,7 @@ context("Workspace 2.0", () => {
cy.get(".codex-editor__redactor .ce-block");
cy.get(".btn-new-workspace").click();
cy.fill_field("title", "Test Private Page", "Data");
cy.fill_field("type", "Workspace", "Select");
cy.wait(300);
cy.get_open_dialog().find(".modal-header").click();
@ -53,6 +54,7 @@ context("Workspace 2.0", () => {
cy.get(".btn-new-workspace").click();
cy.fill_field("title", "Test Child Page", "Data");
cy.fill_field("parent", "Test Private Page", "Select");
cy.fill_field("type", "Workspace", "Select");
cy.get_open_dialog().find(".modal-header").click();
cy.wait(300);
cy.get_open_dialog().find(".btn-primary").click();

View file

@ -20,6 +20,7 @@ context("Workspace Blocks", () => {
cy.get(".codex-editor__redactor .ce-block");
cy.get(".btn-new-workspace").click();
cy.fill_field("title", "Test Block Page", "Data");
cy.fill_field("type", "Workspace", "Select");
cy.get_open_dialog().find(".modal-header").click();
cy.get_open_dialog().find(".btn-primary").click();

View file

@ -447,6 +447,10 @@ def get_workspace_sidebar_items():
"indicator_color",
"is_hidden",
"app",
"type",
"link_type",
"link_to",
"external_link",
]
all_pages = frappe.get_all(
"Workspace", fields=fields, filters=filters, order_by=order_by, ignore_permissions=True
@ -480,6 +484,14 @@ def get_workspace_sidebar_items():
page["app"] = frappe.db.get_value("Module Def", page["module"], "app_name") or get_module_app(
page["module"]
)
if page["link_type"] == "Report":
report_type, ref_doctype = frappe.db.get_value(
"Report", page["link_to"], ["report_type", "ref_doctype"]
)
page["report"] = {
"report_type": report_type,
"ref_doctype": ref_doctype,
}
except frappe.PermissionError:
pass

View file

@ -15,6 +15,10 @@
"parent_page",
"module",
"app",
"type",
"link_type",
"link_to",
"external_link",
"column_break_3",
"icon",
"indicator_color",
@ -49,6 +53,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "charts",
"depends_on": "eval:doc.type===\"Workspace\"",
"fieldname": "tab_break_2",
"fieldtype": "Tab Break",
"label": "Dashboards"
@ -87,6 +92,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "shortcuts",
"depends_on": "eval:doc.type===\"Workspace\"",
"fieldname": "tab_break_15",
"fieldtype": "Tab Break",
"label": "Shortcuts"
@ -94,6 +100,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "links",
"depends_on": "eval:doc.type===\"Workspace\"",
"fieldname": "tab_break_18",
"fieldtype": "Tab Break",
"label": "Link Cards"
@ -167,6 +174,7 @@
"label": "Roles"
},
{
"depends_on": "eval:doc.type===\"Workspace\"",
"fieldname": "quick_lists_tab",
"fieldtype": "Tab Break",
"label": "Quick Lists"
@ -184,6 +192,7 @@
"label": "Is Hidden"
},
{
"depends_on": "eval:doc.type===\"Workspace\"",
"fieldname": "number_cards_tab",
"fieldtype": "Tab Break",
"label": "Number Cards"
@ -195,6 +204,7 @@
"options": "Workspace Number Card"
},
{
"depends_on": "eval:doc.type===\"Workspace\"",
"fieldname": "custom_blocks_tab",
"fieldtype": "Tab Break",
"label": "Custom Blocks"
@ -216,11 +226,43 @@
"fieldname": "app",
"fieldtype": "Data",
"label": "App"
},
{
"default": "Workspace",
"fieldname": "type",
"fieldtype": "Select",
"label": "Type",
"options": "Workspace\nLink\nURL",
"reqd": 1
},
{
"depends_on": "eval:doc.type==\"Link\"",
"fieldname": "link_type",
"fieldtype": "Select",
"label": "Link Type",
"mandatory_depends_on": "eval:doc.type==\"Link\"",
"options": "DocType\nPage\nReport"
},
{
"depends_on": "eval:doc.type==\"Link\"",
"fieldname": "link_to",
"fieldtype": "Dynamic Link",
"label": "Link To",
"mandatory_depends_on": "eval:doc.type==\"Link\"",
"options": "link_type"
},
{
"depends_on": "eval:doc.type==\"URL\"",
"fieldname": "external_link",
"fieldtype": "Data",
"label": "External Link",
"mandatory_depends_on": "eval:doc.type==\"URL\"",
"options": "URL"
}
],
"in_create": 1,
"links": [],
"modified": "2024-09-04 22:24:24.780174",
"modified": "2024-09-26 15:47:24.710881",
"modified_by": "Administrator",
"module": "Desk",
"name": "Workspace",

View file

@ -34,6 +34,7 @@ class Workspace(Document):
charts: DF.Table[WorkspaceChart]
content: DF.LongText | None
custom_blocks: DF.Table[WorkspaceCustomBlock]
external_link: DF.Data | None
for_user: DF.Data | None
hide_custom: DF.Check
indicator_color: DF.Literal[
@ -52,6 +53,8 @@ class Workspace(Document):
]
is_hidden: DF.Check
label: DF.Data
link_to: DF.DynamicLink | None
link_type: DF.Literal["DocType", "Page", "Report"]
links: DF.Table[WorkspaceLink]
module: DF.Link | None
number_cards: DF.Table[WorkspaceNumberCard]
@ -63,6 +66,7 @@ class Workspace(Document):
sequence_id: DF.Float
shortcuts: DF.Table[WorkspaceShortcut]
title: DF.Data
type: DF.Literal["Workspace", "Link", "URL"]
# end: auto-generated types
def validate(self):
@ -275,6 +279,10 @@ def new_page(new_page):
doc.for_user = page.get("for_user")
doc.public = page.get("public")
doc.app = page.get("app")
doc.type = page.get("type")
doc.link_to = page.get("link_to")
doc.link_type = page.get("link_type")
doc.external_link = page.get("external_link")
doc.sequence_id = last_sequence_id(doc) + 1
doc.save(ignore_permissions=True)

View file

@ -296,6 +296,27 @@ frappe.ui.Sidebar = class Sidebar {
sidebar_item_container(item) {
item.indicator_color =
item.indicator_color || this.indicator_colors[Math.floor(Math.random() * 12)];
let path;
if (item.type === "Link") {
if (item.link_type === "Report") {
path = frappe.utils.generate_route({
type: item.link_type,
name: item.link_to,
is_query_report: item.report.report_type === "Query Report",
report_ref_doctype: item.report.ref_doctype,
});
} else {
path = frappe.utils.generate_route({ type: item.link_type, name: item.link_to });
}
} else if (item.type === "URL") {
path = item.external_link;
} else {
if (item.public) {
path = "/app/" + frappe.router.slug(item.name);
} else {
path = "/app/private/" + frappe.router.slug(item.name.split("-")[0]);
}
}
return $(`
<div
@ -308,11 +329,8 @@ frappe.ui.Sidebar = class Sidebar {
>
<div class="standard-sidebar-item ${item.selected ? "selected" : ""}">
<a
href="/app/${
item.public
? frappe.router.slug(item.name)
: "private/" + frappe.router.slug(item.name.split("-")[0])
}"
href="${path}"
target="${item.type === "URL" ? "_blank" : ""}"
class="item-anchor ${item.is_editable ? "" : "block-click"}" title="${__(item.title)}"
>
<span class="sidebar-item-icon" item-icon=${item.icon || "folder-normal"}>

View file

@ -1307,7 +1307,7 @@ Object.assign(frappe.utils, {
route =
frappe.router.slug(item.report_ref_doctype) + "/view/report/" + item.name;
} else {
route = "/report/" + item.name;
route = "report/" + item.name;
}
} else if (type === "page") {
route = item.name;

View file

@ -388,6 +388,41 @@ frappe.views.Workspace = class Workspace {
fieldname: "title",
reqd: 1,
},
{
label: __("Type"),
fieldtype: "Select",
fieldname: "type",
options: ["Workspace", "Link", "URL"],
reqd: 1,
onchange: function () {
d.set_df_property("link_type", "hidden", this.get_value() != "Link");
d.set_df_property("link_to", "hidden", this.get_value() != "Link");
},
},
{
label: __("Link Type"),
depends_on: `eval:doc.type=='Link'`,
mandatory_depends_on: `eval:doc.type=='Link'`,
fieldtype: "Select",
fieldname: "link_type",
options: ["DocType", "Page", "Report"],
},
{
label: __("Link To"),
depends_on: `eval:doc.type=='Link'`,
mandatory_depends_on: `eval:doc.type=='Link'`,
fieldtype: "Dynamic Link",
fieldname: "link_to",
options: "link_type",
},
{
label: __("External Link"),
depends_on: `eval:doc.type=='URL'`,
mandatory_depends_on: `eval:doc.type=='URL'`,
fieldtype: "Data",
fieldname: "external_link",
options: "URL",
},
{
label: __("Parent"),
fieldtype: "Select",
@ -428,7 +463,9 @@ frappe.views.Workspace = class Workspace {
primary_action: (values) => {
values.title = strip_html(values.title);
d.hide();
this.setup_customization_buttons({ is_editable: true });
if (values.type === "Workspace") {
this.setup_customization_buttons({ is_editable: true });
}
let name = values.title + (values.is_public ? "" : "-" + frappe.session.user);
let blocks = [
@ -451,50 +488,63 @@ frappe.views.Workspace = class Workspace {
is_editable: true,
selected: true,
app: frappe.current_app,
type: values.type,
link_type: values.link_type,
link_to: values.link_to,
external_link: values.external_link,
};
this.editor
.render({
blocks: blocks,
})
.then(async () => {
if (this.editor.configuration.readOnly) {
this.is_read_only = false;
await this.editor.readOnly.toggle();
}
if (new_page.type !== "Workspace") {
this.create_page(new_page);
} else {
this.editor
.render({
blocks: blocks,
})
.then(async () => {
if (this.editor.configuration.readOnly) {
this.is_read_only = false;
await this.editor.readOnly.toggle();
}
frappe.call({
method: "frappe.desk.doctype.workspace.workspace.new_page",
args: {
new_page: new_page,
},
callback: (r) => {
if (r.message) {
let message = __("Workspace {0} created", [
new_page.title.bold(),
]);
if (!window.Cypress) {
frappe.show_alert({
message: message,
indicator: "green",
});
}
frappe.boot.sidebar_pages = r.message;
this.sidebar.setup_pages();
let pre_url = new_page.public ? "" : "private/";
let route = pre_url + frappe.router.slug(new_page.title);
frappe.set_route(route);
}
},
this.create_page(new_page).then(() => {
let pre_url = new_page.public ? "" : "private/";
let route = pre_url + frappe.router.slug(new_page.title);
frappe.set_route(route);
});
});
});
}
},
});
d.show();
}
create_page(new_page) {
return new Promise((resolve) => {
frappe.call({
method: "frappe.desk.doctype.workspace.workspace.new_page",
args: {
new_page: new_page,
},
callback: (r) => {
if (r.message) {
let message = __("Workspace {0} created", [new_page.title.bold()]);
if (!window.Cypress) {
frappe.show_alert({
message: message,
indicator: "green",
});
}
frappe.boot.sidebar_pages = r.message;
this.sidebar.setup_pages();
resolve();
}
},
});
});
}
initialize_editorjs(blocks) {
this.tools = {
header: {