diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js
index 73819a1222..cc4ae4f8e3 100644
--- a/cypress/integration/awesome_bar.js
+++ b/cypress/integration/awesome_bar.js
@@ -10,8 +10,10 @@ context("Awesome Bar", () => {
});
beforeEach(() => {
- cy.get(".navbar .navbar-home").click();
- cy.findByPlaceholderText("Search or type a command (Ctrl + G)").as("awesome_bar");
+ let txt = `Search or type a command (${
+ window.navigator.platform === "MacIntel" ? "⌘" : "Ctrl"
+ } + G)`;
+ cy.findByPlaceholderText(txt).as("awesome_bar");
cy.get("@awesome_bar").type("{selectall}");
});
@@ -35,13 +37,13 @@ context("Awesome Bar", () => {
cy.get("@awesome_bar").type("{enter}");
cy.get(".title-text").should("contain", "To Do");
cy.wait(400); // Wait a bit longer before checking the filter.
- cy.get('[data-original-title="ID"] > input').should("have.value", "%test%");
+ cy.get('[data-original-title="ID"]:visible > input').should("have.value", "%test%");
// filter preserved, now finds something else
cy.visit("/app/todo");
cy.get(".title-text").should("contain", "To Do");
cy.wait(200); // Wait a bit longer before checking the filter.
- cy.get('[data-original-title="ID"] > input').as("filter");
+ cy.get('[data-original-title="ID"]:visible > input').as("filter");
cy.get("@filter").should("have.value", "%test%");
cy.get("@awesome_bar").type("anothertest in todo");
cy.wait(200); // Wait a bit longer before hitting enter.
diff --git a/cypress/integration/form.js b/cypress/integration/form.js
index 3ff00c7e49..1ddf365c71 100644
--- a/cypress/integration/form.js
+++ b/cypress/integration/form.js
@@ -71,6 +71,8 @@ context("Form", () => {
let expectBackgroundColor = "rgb(255, 245, 245)";
cy.visit("/app/contact/new");
+ cy.fill_field("company_name", "Test Company");
+
cy.get('.frappe-control[data-fieldname="email_ids"]').as("table");
cy.get("@table").find("button.grid-add-row").click();
cy.get("@table").find("button.grid-add-row").click();
@@ -80,7 +82,6 @@ context("Form", () => {
cy.get("@row1").find("input.input-with-feedback.form-control").as("email_input1");
cy.get("@email_input1").type(website_input, { waitForAnimations: false });
- cy.fill_field("company_name", "Test Company");
cy.get("@row2").click();
cy.get("@row2").find("input.input-with-feedback.form-control").as("email_input2");
diff --git a/cypress/integration/workspace.js b/cypress/integration/workspace.js
index 79800faa01..7c15ee1fab 100644
--- a/cypress/integration/workspace.js
+++ b/cypress/integration/workspace.js
@@ -20,6 +20,8 @@ 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.wait(300);
+
cy.get_open_dialog().find(".modal-header").click();
cy.get_open_dialog().find(".btn-primary").click();
@@ -29,7 +31,7 @@ context("Workspace 2.0", () => {
"item-public",
"0"
);
-
+ cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should(
@@ -52,6 +54,7 @@ context("Workspace 2.0", () => {
cy.fill_field("title", "Test Child Page", "Data");
cy.fill_field("parent", "Test Private Page", "Select");
cy.get_open_dialog().find(".modal-header").click();
+ cy.wait(300);
cy.get_open_dialog().find(".btn-primary").click();
// check if sidebar item is added in pubic section
@@ -60,7 +63,7 @@ context("Workspace 2.0", () => {
"item-public",
"0"
);
-
+ cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Child Page"]').should(
@@ -72,84 +75,12 @@ context("Workspace 2.0", () => {
cy.wait("@new_page");
});
- it("Duplicate Page", () => {
- cy.intercept({
- method: "POST",
- url: "api/method/frappe.desk.doctype.workspace.workspace.duplicate_page",
- }).as("page_duplicated");
-
- cy.get(".codex-editor__redactor .ce-block");
- cy.get(".btn-edit-workspace").click();
-
- cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item");
-
- cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
- cy.get("@sidebar-item").find(".dropdown-btn").first().click();
- cy.get("@sidebar-item")
- .find(".dropdown-list .dropdown-item")
- .contains("Duplicate")
- .first()
- .click({ force: true });
-
- cy.get_open_dialog().fill_field("title", "Duplicate Page", "Data");
- cy.click_modal_primary_button("Duplicate");
-
- cy.wait("@page_duplicated");
- });
-
- it("Drag Sidebar Item", () => {
- cy.intercept({
- method: "POST",
- url: "api/method/frappe.desk.doctype.workspace.workspace.sort_pages",
- }).as("page_sorted");
-
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]').as("sidebar-item");
-
- cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
- cy.get("@sidebar-item").find(".drag-handle").first().move({ deltaX: 0, deltaY: 100 });
-
- cy.get('.sidebar-item-container[item-name="Build"]').as("sidebar-item");
-
- cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
- cy.get("@sidebar-item").find(".drag-handle").first().move({ deltaX: 0, deltaY: 100 });
-
- cy.wait("@page_sorted");
- });
-
- it("Edit Page Detail", () => {
- cy.intercept({
- method: "POST",
- url: "api/method/frappe.desk.doctype.workspace.workspace.update_page",
- }).as("page_updated");
-
- cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item");
-
- cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
- cy.get("@sidebar-item").find(".dropdown-btn").first().click();
- cy.get("@sidebar-item")
- .find(".dropdown-list .dropdown-item")
- .contains("Edit")
- .first()
- .click({ force: true });
-
- cy.get_open_dialog().fill_field("title", " 1", "Data");
- cy.get_open_dialog().find('input[data-fieldname="is_public"]').check();
- cy.click_modal_primary_button("Update");
-
- cy.get(
- '.standard-sidebar-section:first .sidebar-item-container[item-name="Test Private Page"]'
- ).should("not.exist");
- cy.get(
- '.standard-sidebar-section:last .sidebar-item-container[item-name="Test Private Page 1"]'
- ).should("exist");
-
- cy.wait("@page_updated");
- });
-
it("Add New Block", () => {
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]').as("sidebar-item");
+ cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item");
- cy.get("@sidebar-item").find(".standard-sidebar-item").first().click();
+ cy.get("@sidebar-item").find(".standard-sidebar-item").first().click({ force: true });
+
+ cy.get(".btn-edit-workspace").click({ force: true });
cy.get(".ce-block").click().type("{enter}");
cy.get(".block-list-container .block-list-item").contains("Heading").click();
@@ -184,71 +115,7 @@ context("Workspace 2.0", () => {
cy.get(".ce-block:last").should("have.class", "col-xs-11");
cy.get(".ce-block:last .dropdown-item").contains("Expand").click();
cy.get(".ce-block:last").should("have.class", "col-xs-12");
-
+ cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
});
-
- it("Hide/Unhide Workspaces", () => {
- // hide
- cy.intercept({
- method: "POST",
- url: "api/method/frappe.desk.doctype.workspace.workspace.hide_page",
- }).as("hide_page");
-
- cy.get(".codex-editor__redactor .ce-block");
- cy.get(".btn-edit-workspace").click();
-
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
- .find(".sidebar-item-control .setting-btn")
- .click();
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
- .find('.dropdown-item[title="Hide Workspace"]')
- .click({ force: true });
- cy.wait(300);
- cy.get('.standard-actions .btn-secondary[data-label="Discard"]').click();
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("not.be.visible");
-
- cy.wait("@hide_page");
-
- // unhide
- cy.intercept({
- method: "POST",
- url: "api/method/frappe.desk.doctype.workspace.workspace.unhide_page",
- }).as("unhide_page");
-
- cy.get(".codex-editor__redactor .ce-block");
- cy.get(".btn-edit-workspace").click();
-
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
- .find('[title="Unhide Workspace"]')
- .click({ force: true });
- cy.wait(300);
-
- cy.get('.standard-actions .btn-secondary[data-label="Discard"]').click();
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("be.visible");
-
- cy.wait("@unhide_page");
- });
-
- it("Delete Duplicate Page", () => {
- cy.intercept({
- method: "POST",
- url: "api/method/frappe.desk.doctype.workspace.workspace.delete_page",
- }).as("page_deleted");
-
- cy.get(".codex-editor__redactor .ce-block");
- cy.get(".btn-edit-workspace").click();
-
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
- .find(".sidebar-item-control .setting-btn")
- .click();
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]')
- .find('.dropdown-item[title="Delete Workspace"]')
- .click({ force: true });
- cy.wait(300);
- cy.get(".modal-footer > .standard-actions > .btn-modal-primary:visible").first().click();
- cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("not.exist");
-
- cy.wait("@page_deleted");
- });
});
diff --git a/cypress/integration/workspace_blocks.js b/cypress/integration/workspace_blocks.js
index e601efc9b3..959c05b8cb 100644
--- a/cypress/integration/workspace_blocks.js
+++ b/cypress/integration/workspace_blocks.js
@@ -29,7 +29,7 @@ context("Workspace Blocks", () => {
"item-public",
"0"
);
-
+ cy.wait(300);
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Block Page"]').should(
diff --git a/frappe/boot.py b/frappe/boot.py
index 94c34abce3..240924291b 100644
--- a/frappe/boot.py
+++ b/frappe/boot.py
@@ -143,7 +143,7 @@ def load_conf_settings(bootinfo):
def load_desktop_data(bootinfo):
from frappe.desk.desktop import get_workspace_sidebar_items
- bootinfo.allowed_workspaces = get_workspace_sidebar_items().get("pages")
+ bootinfo.sidebar_pages = get_workspace_sidebar_items()
bootinfo.module_wise_workspaces = get_controller("Workspace").get_module_wise_workspaces()
bootinfo.dashboards = frappe.get_all("Dashboard")
diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js
index c7199c925d..9bc28f6cfa 100644
--- a/frappe/core/doctype/doctype/doctype.js
+++ b/frappe/core/doctype/doctype/doctype.js
@@ -77,10 +77,6 @@ frappe.ui.form.on("DocType", {
let msg = __(
"This site is running in developer mode. Any change made here will be updated in code."
);
- msg += "
";
- msg += __("If you just want to customize for your site, use {0} instead.", [
- customize_form_link,
- ]);
frm.dashboard.add_comment(msg, "yellow", true);
}
diff --git a/frappe/core/workspace/build/build.json b/frappe/core/workspace/build/build.json
index 8ecce4c7e8..b3028f080b 100644
--- a/frappe/core/workspace/build/build.json
+++ b/frappe/core/workspace/build/build.json
@@ -7,7 +7,7 @@
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
- "icon": "tool",
+ "icon": "organization",
"idx": 1,
"is_hidden": 0,
"label": "Build",
@@ -301,7 +301,7 @@
"type": "Link"
}
],
- "modified": "2024-08-16 12:31:51.279839",
+ "modified": "2024-08-26 15:14:40.193261",
"modified_by": "Administrator",
"module": "Core",
"name": "Build",
@@ -312,7 +312,7 @@
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
- "sequence_id": 27.0,
+ "sequence_id": 2.0,
"shortcuts": [
{
"color": "Grey",
diff --git a/frappe/desk/doctype/workspace/workspace.js b/frappe/desk/doctype/workspace/workspace.js
index b5f298c477..cd53041fe7 100644
--- a/frappe/desk/doctype/workspace/workspace.js
+++ b/frappe/desk/doctype/workspace/workspace.js
@@ -20,15 +20,11 @@ frappe.ui.form.on("Workspace", {
.attr("target", "_blank");
frm.layout.message.empty();
- let message = __(
- "This document allows you to edit limited fields. For all kinds of workspace customization, use the Edit button located on the workspace page"
- );
+ let message = __("Please click Edit on the Workspace for best results");
if (
- frm.doc.for_user ||
- (frm.doc.public &&
- !frm.has_perm("write") &&
- !frappe.user.has_role("Workspace Manager"))
+ (frm.doc.for_user && frm.doc.for_user !== frappe.session.user) ||
+ (frm.doc.public && !frappe.user.has_role("Workspace Manager"))
) {
frm.trigger("disable_form");
diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json
index 05f33e5ae0..c9c68137fe 100644
--- a/frappe/desk/doctype/workspace/workspace.json
+++ b/frappe/desk/doctype/workspace/workspace.json
@@ -77,8 +77,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Module",
- "options": "Module Def",
- "read_only": 1
+ "options": "Module Def"
},
{
"fieldname": "column_break_3",
@@ -101,8 +100,7 @@
{
"fieldname": "for_user",
"fieldtype": "Data",
- "label": "For User",
- "read_only": 1
+ "label": "For User"
},
{
"default": "0",
@@ -114,8 +112,7 @@
{
"fieldname": "icon",
"fieldtype": "Icon",
- "label": "Icon",
- "read_only": 1
+ "label": "Icon"
},
{
"fieldname": "links",
@@ -137,7 +134,6 @@
"fieldname": "title",
"fieldtype": "Data",
"label": "Title",
- "read_only": 1,
"reqd": 1
},
{
@@ -156,8 +152,7 @@
{
"fieldname": "sequence_id",
"fieldtype": "Float",
- "label": "Sequence Id",
- "read_only": 1
+ "label": "Sequence Id"
},
{
"fieldname": "roles",
@@ -219,7 +214,7 @@
],
"in_create": 1,
"links": [],
- "modified": "2024-05-30 17:30:36.791171",
+ "modified": "2024-08-26 17:16:05.820503",
"modified_by": "Administrator",
"module": "Desk",
"name": "Workspace",
diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py
index 1ea99d33f0..7f8bcea15b 100644
--- a/frappe/desk/doctype/workspace/workspace.py
+++ b/frappe/desk/doctype/workspace/workspace.py
@@ -6,7 +6,7 @@ from json import loads
import frappe
from frappe import _
-from frappe.desk.desktop import save_new_widget
+from frappe.desk.desktop import get_workspace_sidebar_items, save_new_widget
from frappe.desk.utils import validate_route_conflict
from frappe.model.document import Document
from frappe.model.rename_doc import rename_doc
@@ -261,7 +261,7 @@ def new_page(new_page):
doc = frappe.new_doc("Workspace")
doc.title = page.get("title")
- doc.icon = page.get("icon")
+ doc.icon = page.get("icon") or "dashboard"
doc.indicator_color = page.get("indicator_color")
doc.content = page.get("content")
doc.parent_page = page.get("parent_page")
@@ -271,7 +271,7 @@ def new_page(new_page):
doc.sequence_id = last_sequence_id(doc) + 1
doc.save(ignore_permissions=True)
- return doc
+ return get_workspace_sidebar_items()
@frappe.whitelist()
@@ -341,125 +341,6 @@ def update_page(name, title, icon, indicator_color, parent, public):
return {"name": title, "public": public, "label": new_name}
-def hide_unhide_page(page_name: str, is_hidden: bool):
- page = frappe.get_doc("Workspace", page_name)
-
- if page.get("public") and not is_workspace_manager():
- frappe.throw(
- _("Need Workspace Manager role to hide/unhide public workspaces"), frappe.PermissionError
- )
-
- if not page.get("public") and page.get("for_user") != frappe.session.user and not is_workspace_manager():
- frappe.throw(_("Cannot update private workspace of other users"), frappe.PermissionError)
-
- page.is_hidden = int(is_hidden)
- page.save(ignore_permissions=True)
- return True
-
-
-@frappe.whitelist()
-def hide_page(page_name: str):
- return hide_unhide_page(page_name, 1)
-
-
-@frappe.whitelist()
-def unhide_page(page_name: str):
- return hide_unhide_page(page_name, 0)
-
-
-@frappe.whitelist()
-def duplicate_page(page_name, new_page):
- if not loads(new_page):
- return
-
- new_page = loads(new_page)
-
- if new_page.get("is_public") and not is_workspace_manager():
- return
-
- old_doc = frappe.get_doc("Workspace", page_name)
- doc = frappe.copy_doc(old_doc)
- doc.title = new_page.get("title")
- doc.icon = new_page.get("icon")
- doc.indicator_color = new_page.get("indicator_color")
- doc.parent_page = new_page.get("parent") or ""
- doc.public = new_page.get("is_public")
- doc.for_user = ""
- doc.label = doc.title
- doc.module = ""
- if not doc.public:
- doc.for_user = doc.for_user or frappe.session.user
- doc.label = f"{doc.title}-{doc.for_user}"
- doc.name = doc.label
- if old_doc.public == doc.public:
- doc.sequence_id += 0.1
- else:
- doc.sequence_id = last_sequence_id(doc) + 1
- doc.insert(ignore_permissions=True)
-
- return doc
-
-
-@frappe.whitelist()
-def delete_page(page):
- if not loads(page):
- return
-
- page = loads(page)
-
- if page.get("public") and not is_workspace_manager():
- frappe.throw(
- _("Cannot delete public workspace without Workspace Manager role"),
- frappe.PermissionError,
- )
- elif not page.get("public") and not is_workspace_manager():
- workspace_owner = frappe.get_value("Workspace", page.get("name"), "for_user")
- if workspace_owner != frappe.session.user:
- frappe.throw(
- _("Cannot delete private workspace of other users"),
- frappe.PermissionError,
- )
-
- if frappe.db.exists("Workspace", page.get("name")):
- frappe.get_doc("Workspace", page.get("name")).delete(ignore_permissions=True)
-
- return {"name": page.get("name"), "public": page.get("public"), "title": page.get("title")}
-
-
-@frappe.whitelist()
-def sort_pages(sb_public_items, sb_private_items):
- if not loads(sb_public_items) and not loads(sb_private_items):
- return
-
- sb_public_items = loads(sb_public_items)
- sb_private_items = loads(sb_private_items)
-
- workspace_public_pages = get_page_list(["name", "title"], {"public": 1})
- workspace_private_pages = get_page_list(["name", "title"], {"for_user": frappe.session.user})
-
- if sb_private_items:
- return sort_page(workspace_private_pages, sb_private_items)
-
- if sb_public_items and is_workspace_manager():
- return sort_page(workspace_public_pages, sb_public_items)
-
- return False
-
-
-def sort_page(workspace_pages, pages):
- for seq, d in enumerate(pages):
- for page in workspace_pages:
- if page.title == d.get("title"):
- doc = frappe.get_doc("Workspace", page.name)
- doc.sequence_id = seq + 1
- doc.parent_page = d.get("parent_page") or ""
- doc.flags.ignore_links = True
- doc.save(ignore_permissions=True)
- break
-
- return True
-
-
def last_sequence_id(doc):
doc_exists = frappe.db.exists({"doctype": "Workspace", "public": doc.public, "for_user": doc.for_user})
diff --git a/frappe/desk/doctype/workspace_settings/workspace_settings.js b/frappe/desk/doctype/workspace_settings/workspace_settings.js
index d1ba550205..06915000c8 100644
--- a/frappe/desk/doctype/workspace_settings/workspace_settings.js
+++ b/frappe/desk/doctype/workspace_settings/workspace_settings.js
@@ -5,33 +5,35 @@ frappe.ui.form.on("Workspace Settings", {
setup(frm) {
frm.hide_full_form_button = true;
frm.docfields = [];
+ frm.workspace_map = {};
let workspace_visibilty = JSON.parse(frm.doc.workspace_visibility_json || "{}");
// build fields from workspaces
let cnt = 0,
column_added = false;
- for (let w of frappe.boot.allowed_workspaces) {
- if (w.public) {
+ for (let page of frappe.boot.allowed_workspaces) {
+ if (page.public) {
+ frm.workspace_map[page.name] = page;
cnt++;
frm.docfields.push({
fieldtype: "Check",
- fieldname: w.name,
- label: w.title,
- initial_value: workspace_visibilty[w.name] !== 0, // not set is also visible
+ fieldname: page.name,
+ label: page.title + (page.parent_page ? ` (${page.parent_page})` : ""),
+ initial_value: workspace_visibilty[page.name] !== 0, // not set is also visible
});
}
-
- if (cnt >= frappe.boot.allowed_workspaces.length / 2 && !column_added) {
- // add column break to split into 2 columns
- frm.docfields.push({ fieldtype: "Column Break" });
- column_added = true;
- }
}
frappe.temp = frm;
},
validate(frm) {
frm.doc.workspace_visibility_json = JSON.stringify(frm.dialog.get_values());
+ frm.doc.workspace_sequence = JSON.stringify(
+ frm.wrapper
+ .find(".frappe-control")
+ .get()
+ .map((e) => e.fieldobj.df.fieldname)
+ );
frm.doc.workspace_setup_completed = 1;
},
after_save(frm) {
@@ -39,6 +41,50 @@ frappe.ui.form.on("Workspace Settings", {
window.location.reload();
},
refresh(frm) {
- frm.dialog.set_alert(__("Select modules you want to see in the sidebar"));
+ let get_page = (e) => frm.workspace_map[e.fieldobj.df.fieldname];
+
+ frm.dialog.set_alert(__("Select, sort modules you want to see in the sidebar"));
+ if (!frm.workspace_sortable) {
+ frm.wrapper.find(".frappe-control").css({ "margin-bottom": "0.5rem" });
+
+ let forms = frm.wrapper.find("form");
+ frm.workspace_sortable = Sortable.create(forms.get(0), {
+ group: "workspace_settings",
+ animation: 150,
+ onEnd: (o) => {
+ // re-order so that child items are below parent items
+ for (let e of frm.wrapper.find(".frappe-control").get()) {
+ let page = get_page(e);
+ if (page.parent_page) {
+ // insert as the last child of the parent element
+ let parent_element = frm.wrapper
+ .find(`[data-fieldname="${page.parent_page}"`)
+ .closest(".frappe-control");
+ let parent_page = page.parent_page;
+
+ // find the last child
+ while (parent_element) {
+ let next_element = parent_element.next(".frappe-control");
+
+ if (!next_element.length) {
+ // end of list
+ $(e).insertAfter(parent_element);
+ break;
+ } else {
+ let page = get_page(next_element.get(0));
+ if (page.parent_page != parent_page) {
+ // different parent, last child found
+ $(e).insertAfter(parent_element);
+ break;
+ }
+ }
+
+ parent_element = next_element;
+ }
+ }
+ }
+ },
+ });
+ }
},
});
diff --git a/frappe/desk/doctype/workspace_settings/workspace_settings.py b/frappe/desk/doctype/workspace_settings/workspace_settings.py
index 7909a37c1e..df5773e820 100644
--- a/frappe/desk/doctype/workspace_settings/workspace_settings.py
+++ b/frappe/desk/doctype/workspace_settings/workspace_settings.py
@@ -1,7 +1,9 @@
# Copyright (c) 2024, Frappe Technologies and contributors
# For license information, please see license.txt
-# import frappe
+import json
+
+import frappe
from frappe.model.document import Document
@@ -19,3 +21,12 @@ class WorkspaceSettings(Document):
# end: auto-generated types
pass
+
+ def validate(self):
+ cnt = 1
+ for page_name in json.loads(self.workspace_sequence):
+ frappe.db.set_value("Workspace", page_name, "sequence_id", cnt)
+ cnt += 1
+
+ def on_update(self):
+ frappe.clear_cache()
diff --git a/frappe/desk/page/setup_wizard/setup_wizard.js b/frappe/desk/page/setup_wizard/setup_wizard.js
index 49f15f3c81..6231e911ce 100644
--- a/frappe/desk/page/setup_wizard/setup_wizard.js
+++ b/frappe/desk/page/setup_wizard/setup_wizard.js
@@ -397,7 +397,6 @@ frappe.setup.slides_settings = [
fieldtype: "Select",
reqd: 1,
},
- { fieldtype: "Column Break" },
{
fieldname: "currency",
label: __("Currency"),
diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js
index 1be36c978f..b16e409397 100644
--- a/frappe/public/js/frappe/desk.js
+++ b/frappe/public/js/frappe/desk.js
@@ -37,6 +37,7 @@ frappe.Application = class Application {
this.load_bootinfo();
this.load_user_permissions();
this.make_nav_bar();
+ this.make_sidebar();
this.set_favicon();
this.set_fullwidth_if_enabled();
this.add_browser_class();
@@ -46,6 +47,51 @@ frappe.Application = class Application {
frappe.ui.keys.setup();
+ this.setup_theme();
+
+ // page container
+ this.make_page_container();
+ this.setup_tours();
+ this.set_route();
+
+ // trigger app startup
+ $(document).trigger("startup");
+ $(document).trigger("app_ready");
+
+ this.show_notices();
+ this.show_notes();
+
+ if (frappe.ui.startup_setup_dialog && !frappe.boot.setup_complete) {
+ frappe.ui.startup_setup_dialog.pre_show();
+ frappe.ui.startup_setup_dialog.show();
+ }
+
+ // listen to build errors
+ this.setup_build_events();
+
+ if (frappe.sys_defaults.email_user_password) {
+ var email_list = frappe.sys_defaults.email_user_password.split(",");
+ for (var u in email_list) {
+ if (email_list[u] === frappe.user.name) {
+ this.set_password(email_list[u]);
+ }
+ }
+ }
+
+ // REDESIGN-TODO: Fix preview popovers
+ this.link_preview = new frappe.ui.LinkPreview();
+
+ frappe.broadcast.emit("boot", {
+ csrf_token: frappe.csrf_token,
+ user: frappe.session.user,
+ });
+ }
+
+ make_sidebar() {
+ this.sidebar = new frappe.ui.Sidebar({});
+ }
+
+ setup_theme() {
frappe.ui.keys.add_shortcut({
shortcut: "shift+ctrl+g",
description: __("Switch Theme"),
@@ -71,9 +117,9 @@ frappe.Application = class Application {
});
frappe.ui.set_theme();
+ }
- // page container
- this.make_page_container();
+ setup_tours() {
if (
!window.Cypress &&
frappe.boot.onboarding_tours &&
@@ -90,13 +136,9 @@ frappe.Application = class Application {
});
}
}
- this.set_route();
-
- // trigger app startup
- $(document).trigger("startup");
-
- $(document).trigger("app_ready");
+ }
+ show_notices() {
if (frappe.boot.messages) {
frappe.msgprint(frappe.boot.messages);
}
@@ -116,13 +158,6 @@ frappe.Application = class Application {
console.log(`%c${console_security_message}`, "font-size: large");
}
- this.show_notes();
-
- if (frappe.ui.startup_setup_dialog && !frappe.boot.setup_complete) {
- frappe.ui.startup_setup_dialog.pre_show();
- frappe.ui.startup_setup_dialog.show();
- }
-
frappe.realtime.on("version-update", function () {
var dialog = frappe.msgprint({
message: __(
@@ -136,26 +171,6 @@ frappe.Application = class Application {
});
dialog.get_close_btn().toggle(false);
});
-
- // listen to build errors
- this.setup_build_events();
-
- if (frappe.sys_defaults.email_user_password) {
- var email_list = frappe.sys_defaults.email_user_password.split(",");
- for (var u in email_list) {
- if (email_list[u] === frappe.user.name) {
- this.set_password(email_list[u]);
- }
- }
- }
-
- // REDESIGN-TODO: Fix preview popovers
- this.link_preview = new frappe.ui.LinkPreview();
-
- frappe.broadcast.emit("boot", {
- csrf_token: frappe.csrf_token,
- user: frappe.session.user,
- });
}
set_route() {
@@ -275,6 +290,8 @@ frappe.Application = class Application {
setup_workspaces() {
frappe.modules = {};
frappe.workspaces = {};
+ frappe.boot.allowed_workspaces = frappe.boot.sidebar_pages.pages;
+
for (let page of frappe.boot.allowed_workspaces || []) {
frappe.modules[page.module] = page;
frappe.workspaces[frappe.router.slug(page.name)] = page;
diff --git a/frappe/public/js/frappe/form/controls/table_multiselect.js b/frappe/public/js/frappe/form/controls/table_multiselect.js
index c4372e54ac..cfe749a524 100644
--- a/frappe/public/js/frappe/form/controls/table_multiselect.js
+++ b/frappe/public/js/frappe/form/controls/table_multiselect.js
@@ -1,6 +1,7 @@
frappe.ui.form.ControlTableMultiSelect = class ControlTableMultiSelect extends (
frappe.ui.form.ControlLink
) {
+ static horizontal = false;
make_input() {
super.make_input();
diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js
index 038e3c55ef..ce72887537 100644
--- a/frappe/public/js/frappe/form/layout.js
+++ b/frappe/public/js/frappe/form/layout.js
@@ -503,7 +503,7 @@ frappe.ui.form.Layout = class Layout {
let tabs_content = this.tabs_content[0];
if (!tabs_list.length) return;
- $(window).scroll(
+ $(".main-section").scroll(
frappe.utils.throttle(() => {
let current_scroll = document.documentElement.scrollTop;
if (current_scroll > 0 && last_scroll <= current_scroll) {
diff --git a/frappe/public/js/frappe/form/section.js b/frappe/public/js/frappe/form/section.js
index 2767fe4ccf..5d14abf1e3 100644
--- a/frappe/public/js/frappe/form/section.js
+++ b/frappe/public/js/frappe/form/section.js
@@ -27,8 +27,8 @@ export default class Section {
make() {
let make_card = this.card_layout;
- this.wrapper = $(`