diff --git a/cypress/integration/workspace.js b/cypress/integration/workspace.js index 5283fadcbd..97db14d683 100644 --- a/cypress/integration/workspace.js +++ b/cypress/integration/workspace.js @@ -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(); diff --git a/cypress/integration/workspace_blocks.js b/cypress/integration/workspace_blocks.js index 7c89c70c01..70159150e2 100644 --- a/cypress/integration/workspace_blocks.js +++ b/cypress/integration/workspace_blocks.js @@ -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(); diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index d96de2a415..53e61bf76f 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -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 diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index e27f820385..f3422cd6eb 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -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", diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 946dc87451..831819e959 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -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) diff --git a/frappe/public/js/frappe/ui/sidebar.js b/frappe/public/js/frappe/ui/sidebar.js index 1214304088..70393c9b41 100644 --- a/frappe/public/js/frappe/ui/sidebar.js +++ b/frappe/public/js/frappe/ui/sidebar.js @@ -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 $(`
diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 66d120f993..eabf37dada 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -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; diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index eea7563bb6..d7e4f6c8b5 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -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: {