Merge branch 'develop' into revert-34642-grid-css
This commit is contained in:
commit
4cf5f6312b
206 changed files with 17680 additions and 2242 deletions
|
|
@ -96,6 +96,7 @@
|
|||
"hljs": true,
|
||||
"Awesomplete": true,
|
||||
"Sortable": true,
|
||||
"gemoji": true,
|
||||
"Showdown": true,
|
||||
"Taggle": true,
|
||||
"Gantt": true,
|
||||
|
|
|
|||
|
|
@ -36,5 +36,9 @@ module.exports = defineConfig({
|
|||
testIsolation: false,
|
||||
baseUrl: "http://test_site_ui:8000",
|
||||
specPattern: ["./cypress/integration/*.js", "**/ui_test_*.js"],
|
||||
excludeSpecPattern: [
|
||||
"./cypress/integration/workspace.js",
|
||||
"./cypress/integration/workspace_blocks.js",
|
||||
],
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ context("Awesome Bar", () => {
|
|||
before(() => {
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
cy.visit("/app/todo"); // Make sure ToDo filters are cleared.
|
||||
cy.visit("/desk/todo"); // Make sure ToDo filters are cleared.
|
||||
cy.clear_filters();
|
||||
cy.visit("/app/web-page"); // Make sure Blog Post filters are cleared.
|
||||
cy.visit("/desk/web-page"); // Make sure Blog Post filters are cleared.
|
||||
cy.clear_filters();
|
||||
cy.visit("/app/build"); // Go to some other page.
|
||||
cy.visit("/desk/build"); // Go to some other page.
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
@ -18,7 +18,7 @@ context("Awesome Bar", () => {
|
|||
});
|
||||
|
||||
after(() => {
|
||||
cy.visit("/app/todo"); // Make sure we're not bleeding any filters to the next spec.
|
||||
cy.visit("/desk/todo"); // Make sure we're not bleeding any filters to the next spec.
|
||||
cy.clear_filters();
|
||||
});
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ context("Awesome Bar", () => {
|
|||
cy.get(".awesomplete").findByRole("listbox").should("be.visible");
|
||||
cy.get("@awesome_bar").type("{enter}");
|
||||
cy.get(".title-text").should("contain", "To Do");
|
||||
cy.location("pathname").should("eq", "/app/todo");
|
||||
cy.location("pathname").should("eq", "/desk/todo");
|
||||
});
|
||||
|
||||
it("finds text in doctype list", () => {
|
||||
|
|
@ -40,7 +40,7 @@ context("Awesome Bar", () => {
|
|||
cy.get('[data-original-title="ID"]:visible > input').should("have.value", "%test%");
|
||||
|
||||
// filter preserved, now finds something else
|
||||
cy.visit("/app/todo");
|
||||
cy.visit("/desk/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"]:visible > input').as("filter");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Attach Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/doctype");
|
||||
cy.visit("/desk/doctype");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -166,7 +166,7 @@ context("Attach Control", () => {
|
|||
context("Attach Control with Failed Document Save", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/doctype");
|
||||
cy.visit("/desk/doctype");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Autocomplete", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
cy.wait(4000);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Barcode", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_barcode() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Color", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_color() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ context("Control Currency", () => {
|
|||
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_currency(df_options = {}) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Data Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/doctype");
|
||||
cy.visit("/desk/doctype");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -39,7 +39,7 @@ context("Data Control", () => {
|
|||
});
|
||||
|
||||
it("check custom formatters", () => {
|
||||
cy.visit(`/app/doctype/User`);
|
||||
cy.visit(`/desk/doctype/User`);
|
||||
cy.get(
|
||||
'[data-fieldname="fields"] .grid-row[data-idx="3"] [data-fieldname="fieldtype"] .static-area'
|
||||
).should("have.text", "Section Break");
|
||||
|
|
@ -49,7 +49,10 @@ context("Data Control", () => {
|
|||
cy.new_form("Test Data Control");
|
||||
|
||||
//Checking the URL for the new form of the doctype
|
||||
cy.location("pathname").should("contains", "/app/test-data-control/new-test-data-control");
|
||||
cy.location("pathname").should(
|
||||
"contains",
|
||||
"/desk/test-data-control/new-test-data-control"
|
||||
);
|
||||
cy.get(".title-text").should("have.text", "New Test Data Control");
|
||||
cy.get('.frappe-control[data-fieldname="name1"]')
|
||||
.find("label")
|
||||
|
|
@ -130,7 +133,7 @@ context("Data Control", () => {
|
|||
//Checking if the fields contains the data which has been filled in
|
||||
cy.location("pathname").should(
|
||||
"not.contains",
|
||||
"/app/test-data-control/new-test-data-control"
|
||||
"/desk/test-data-control/new-test-data-control"
|
||||
);
|
||||
cy.get_field("name1").should("have.value", "Komal");
|
||||
cy.get_field("email").should("have.value", "komal@test.com");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Date Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
function get_dialog(date_field_options) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Date Range Control", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
function get_dialog() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Duration", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_duration(hide_days = 0, hide_seconds = 0) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Dynamic Link", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/doctype");
|
||||
cy.visit("/desk/doctype");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -107,7 +107,7 @@ context("Dynamic Link", () => {
|
|||
});
|
||||
|
||||
it("Creating a dynamic link and verifying it", () => {
|
||||
cy.visit("/app/test-dynamic-link");
|
||||
cy.visit("/desk/test-dynamic-link");
|
||||
|
||||
//Clicking on the Document ID field
|
||||
cy.get_field("doc_type").clear();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Float", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_float() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Icon", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_icon() {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
context("Control Link", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
cy.create_records({
|
||||
doctype: "ToDo",
|
||||
description: "this is a test todo for link",
|
||||
|
|
@ -120,7 +120,7 @@ context("Control Link", () => {
|
|||
cy.get("@input").trigger("mouseover");
|
||||
cy.get(".frappe-control[data-fieldname=link] .btn-open")
|
||||
.should("be.visible")
|
||||
.should("have.attr", "href", `/app/todo/${todos[0]}`);
|
||||
.should("have.attr", "href", `/desk/todo/${todos[0]}`);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -176,7 +176,7 @@ context("Control Link", () => {
|
|||
|
||||
it("should update dependant fields (via fetch_from)", () => {
|
||||
cy.get("@todos").then((todos) => {
|
||||
cy.visit(`/app/todo/${todos[0]}`);
|
||||
cy.visit(`/desk/todo/${todos[0]}`);
|
||||
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
|
||||
cy.intercept("/api/method/frappe.client.validate_link*").as("validate_link");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
context("Control Markdown Editor", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
it("should allow inserting images by drag and drop", () => {
|
||||
cy.visit("/app/web-page/new");
|
||||
cy.visit("/desk/web-page/new");
|
||||
cy.fill_field("content_type", "Markdown", "Select");
|
||||
cy.get_field("main_section_md", "Markdown Editor").selectFile(
|
||||
"cypress/fixtures/sample_image.jpg",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import doctype_with_phone from "../fixtures/doctype_with_phone";
|
|||
context("Control Phone", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -68,13 +68,13 @@ context("Control Phone", () => {
|
|||
});
|
||||
|
||||
it("existing document should render phone field with data", () => {
|
||||
cy.visit("/app/doctype");
|
||||
cy.visit("/desk/doctype");
|
||||
cy.insert_doc("DocType", doctype_with_phone, true);
|
||||
cy.clear_cache();
|
||||
|
||||
// Creating custom doctype
|
||||
cy.insert_doc("DocType", doctype_with_phone, true);
|
||||
cy.visit("/app/doctype-with-phone");
|
||||
cy.visit("/desk/doctype-with-phone");
|
||||
cy.click_listview_primary_button("Add Doctype With Phone");
|
||||
|
||||
// create a record
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Rating", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_rating() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Control Select", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
function get_dialog_with_select() {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ describe(
|
|||
() => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit(`/app/note/new`);
|
||||
cy.visit(`/desk/note/new`);
|
||||
// close the sidebar cause default is expanded
|
||||
cy.get(".body-sidebar .collapse-sidebar-link").click();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Customize Form", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/customize-form");
|
||||
cy.visit("/desk/customize-form");
|
||||
});
|
||||
it("Changing to naming rule should update autoname", () => {
|
||||
cy.fill_field("doc_type", "ToDo", "Link").blur();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
describe("Dashboard view", { scrollBehavior: false }, () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
it("should load", () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Dashboard Chart", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
it("Check filter populate for child table doctype", () => {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ context("Dashboard links", () => {
|
|||
});
|
||||
|
||||
it("Adding a new contact, checking for the counter on the dashboard and deleting the created contact", () => {
|
||||
cy.visit("/app/contact");
|
||||
cy.visit("/desk/contact");
|
||||
cy.clear_filters();
|
||||
|
||||
cy.visit(`/app/user/${cy.config("testUser")}`);
|
||||
|
|
@ -48,7 +48,7 @@ context("Dashboard links", () => {
|
|||
cy.get('[data-doctype="Contact"]').contains("Contact").click();
|
||||
|
||||
//Deleting the newly created contact
|
||||
cy.visit("/app/contact");
|
||||
cy.visit("/desk/contact");
|
||||
cy.get(".list-subject > .select-like > .list-row-checkbox").eq(0).click({ force: true });
|
||||
cy.findByRole("button", { name: "Actions" }).click();
|
||||
cy.get('.actions-btn-group [data-label="Delete"]').click();
|
||||
|
|
@ -56,7 +56,7 @@ context("Dashboard links", () => {
|
|||
|
||||
//To check if the counter from the "Contact" doc link is removed
|
||||
cy.wait(700);
|
||||
cy.visit("/app/user");
|
||||
cy.visit("/desk/user");
|
||||
cy.get(".list-row-col > .level-item > .ellipsis").eq(0).click({ force: true });
|
||||
cy.get('[data-doctype="Contact"]').should("contain", "Contact");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const doctype_name = data_field_validation_doctype.name;
|
|||
context("Data Field Input Validation in New Form", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy.insert_doc("DocType", data_field_validation_doctype, true);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const doctype_name = datetime_doctype.name;
|
|||
context("Control Date, Time and DateTime", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy.insert_doc("DocType", datetime_doctype, true);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Depends On", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ context("FileUploader", () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
cy.wait(2000); // workspace can load async and clear active dialog
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const type_value = (value) => {
|
|||
context("Form", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -28,11 +28,11 @@ context("Form", () => {
|
|||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
it("create a new form", () => {
|
||||
cy.visit("/app/todo/new");
|
||||
cy.visit("/desk/todo/new");
|
||||
cy.get_field("description", "Text Editor")
|
||||
.type("this is a test todo", { force: true })
|
||||
.wait(1000);
|
||||
|
|
@ -51,7 +51,7 @@ context("Form", () => {
|
|||
});
|
||||
|
||||
it("navigates between documents with child table list filters applied", () => {
|
||||
cy.visit("/app/contact");
|
||||
cy.visit("/desk/contact");
|
||||
|
||||
cy.clear_filters();
|
||||
cy.get('.standard-filter-section [data-fieldname="name"] input')
|
||||
|
|
@ -60,7 +60,7 @@ context("Form", () => {
|
|||
cy.click_listview_row_item_with_text("Test Form Contact 3");
|
||||
|
||||
// clear filters
|
||||
cy.visit("/app/contact");
|
||||
cy.visit("/desk/contact");
|
||||
cy.clear_filters();
|
||||
});
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ context("Form", () => {
|
|||
let valid_email = "user@email.com";
|
||||
let expectBackgroundColor = "rgb(255, 245, 245)";
|
||||
|
||||
cy.visit("/app/contact/new");
|
||||
cy.visit("/desk/contact/new");
|
||||
cy.fill_field("company_name", "Test Company");
|
||||
|
||||
cy.get('.frappe-control[data-fieldname="email_ids"]').as("table");
|
||||
|
|
@ -105,7 +105,7 @@ context("Form", () => {
|
|||
});
|
||||
|
||||
it("update docfield property using set_df_property in child table", () => {
|
||||
cy.visit("/app/contact/Test Form Contact 1");
|
||||
cy.visit("/desk/contact/Test Form Contact 1");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@ const doctype_name = form_builder_doctype.name;
|
|||
context("Form Builder", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
return cy.insert_doc("DocType", form_builder_doctype, true);
|
||||
});
|
||||
|
||||
it("Open Form Builder for Web Form Doctype/Customize Form", () => {
|
||||
// doctype
|
||||
cy.visit("/app/doctype/Web Form");
|
||||
cy.visit("/desk/doctype/Web Form");
|
||||
cy.findByRole("tab", { name: "Form" }).click();
|
||||
cy.get(".form-builder-container").should("exist");
|
||||
|
||||
// customize form
|
||||
cy.visit("/app/customize-form?doc_type=Web%20Form");
|
||||
cy.visit("/desk/customize-form?doc_type=Web%20Form");
|
||||
cy.findByRole("tab", { name: "Form" }).click();
|
||||
cy.get(".form-builder-container").should("exist");
|
||||
});
|
||||
|
|
@ -264,7 +264,7 @@ context("Form Builder", () => {
|
|||
|
||||
cy.findByRole("button", { name: "Save" }).click({ force: true });
|
||||
|
||||
cy.visit("/app/form-builder-doctype/new");
|
||||
cy.visit("/desk/form-builder-doctype/new");
|
||||
cy.get("[data-fieldname='data3'] .clearfix label").should("have.text", "New Title");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const doctype_name = doctype_with_tab_break.name;
|
|||
context("Form Tab Break", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy.insert_doc("DocType", doctype_with_tab_break, true);
|
||||
});
|
||||
it("Should switch tab and open correct tabs on validation error", () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context.skip("Form Tour", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -11,11 +11,11 @@ context.skip("Form Tour", () => {
|
|||
});
|
||||
|
||||
const open_test_form_tour = () => {
|
||||
cy.visit("/app/form-tour/Test Form Tour");
|
||||
cy.visit("/desk/form-tour/Test Form Tour");
|
||||
cy.findByRole("button", { name: "Show Tour" }).should("be.visible").as("show_tour");
|
||||
cy.get("@show_tour").click();
|
||||
cy.wait(500);
|
||||
cy.url().should("include", "/app/contact");
|
||||
cy.url().should("include", "/desk/contact");
|
||||
};
|
||||
|
||||
it("jump to a form tour", open_test_form_tour);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
context("Grid", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -16,7 +16,7 @@ context("Grid", () => {
|
|||
});
|
||||
});
|
||||
it("update docfield property using update_docfield_property", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
|
|
@ -40,7 +40,7 @@ context("Grid", () => {
|
|||
});
|
||||
});
|
||||
it("update docfield property using toggle_display", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
|
|
@ -64,7 +64,7 @@ context("Grid", () => {
|
|||
});
|
||||
});
|
||||
it("update docfield property using toggle_enable", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
|
|
@ -88,7 +88,7 @@ context("Grid", () => {
|
|||
});
|
||||
});
|
||||
it("update docfield property using toggle_reqd", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Grid Configuration", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website-settings");
|
||||
cy.visit("/desk/website-settings");
|
||||
});
|
||||
it("Set user wise grid settings", () => {
|
||||
cy.findByRole("tab", { name: "Navbar" }).click();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
context("Grid Pagination", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -16,14 +16,14 @@ context("Grid Pagination", () => {
|
|||
});
|
||||
});
|
||||
it("creates pages for child table", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "1");
|
||||
cy.get("@table").find(".total-page-number").should("contain", "20");
|
||||
cy.get("@table").find(".grid-body .grid-row").should("have.length", 50);
|
||||
});
|
||||
it("goes to the next and previous page", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").find(".next-page").click();
|
||||
cy.get("@table").find(".current-page-number").should("have.value", "2");
|
||||
|
|
@ -36,7 +36,7 @@ context("Grid Pagination", () => {
|
|||
cy.get("@table").find(".grid-body .grid-row").first().should("have.attr", "data-idx", "1");
|
||||
});
|
||||
it("adds and deletes rows and changes page", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").findByRole("button", { name: "Add Row" }).click();
|
||||
cy.get("@table").find(".grid-body .row-index").should("contain", 1001);
|
||||
|
|
@ -49,7 +49,7 @@ context("Grid Pagination", () => {
|
|||
cy.get("@table").find(".total-page-number").should("contain", "20");
|
||||
});
|
||||
it("go to specific page, use up and down arrow, type characters, 0 page and more than existing page", () => {
|
||||
cy.visit("/app/contact/Test Contact");
|
||||
cy.visit("/desk/contact/Test Contact");
|
||||
cy.get('.frappe-control[data-fieldname="phone_nos"]').as("table");
|
||||
cy.get("@table").find(".current-page-number").focus().clear().type("17").blur();
|
||||
cy.get("@table").find(".grid-body .row-index").should("contain", 801);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ context("Grid Search", () => {
|
|||
before(() => {
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
cy.insert_doc("DocType", child_table_doctype, true);
|
||||
cy.insert_doc("DocType", child_table_doctype_1, true);
|
||||
cy.insert_doc("DocType", doctype_with_child_table, true);
|
||||
|
|
@ -40,7 +40,7 @@ context("Grid Search", () => {
|
|||
});
|
||||
});
|
||||
|
||||
cy.visit(`/app/doctype-with-child-table/Test Grid Search`);
|
||||
cy.visit(`/desk/doctype-with-child-table/Test Grid Search`);
|
||||
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as("table");
|
||||
cy.get("@table").find(".grid-row-check:last").click();
|
||||
|
|
@ -49,7 +49,7 @@ context("Grid Search", () => {
|
|||
});
|
||||
|
||||
it("test search field for different fieldtypes", () => {
|
||||
cy.visit(`/app/doctype-with-child-table/Test Grid Search`);
|
||||
cy.visit(`/desk/doctype-with-child-table/Test Grid Search`);
|
||||
|
||||
cy.get('.frappe-control[data-fieldname="child_table_1"]').as("table");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
context("Kanban Board", () => {
|
||||
before(() => {
|
||||
cy.login("frappe@example.com");
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
it("Create ToDo Kanban", () => {
|
||||
cy.visit("/app/todo");
|
||||
cy.visit("/desk/todo");
|
||||
|
||||
cy.get(".page-actions .custom-btn-group button").click();
|
||||
cy.get(".page-actions .custom-btn-group ul.dropdown-menu li").contains("Kanban").click();
|
||||
|
|
@ -33,7 +33,7 @@ context("Kanban Board", () => {
|
|||
});
|
||||
|
||||
it("Add and Remove fields", () => {
|
||||
cy.visit("/app/todo/view/kanban/ToDo Kanban");
|
||||
cy.visit("/desk/todo/view/kanban/ToDo Kanban");
|
||||
|
||||
cy.intercept(
|
||||
"POST",
|
||||
|
|
@ -110,7 +110,7 @@ context("Kanban Board", () => {
|
|||
|
||||
cy.switch_to_user(not_system_manager);
|
||||
|
||||
cy.visit("/app/todo/view/kanban/Admin Kanban");
|
||||
cy.visit("/desk/todo/view/kanban/Admin Kanban");
|
||||
|
||||
// Menu button should be hidden (dropdown for 'Save Filters' and 'Delete Kanban Board')
|
||||
cy.get(".no-list-sidebar .menu-btn-group .btn-default[data-original-title='Menu']").should(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("List Paging", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -11,7 +11,7 @@ context("List Paging", () => {
|
|||
});
|
||||
|
||||
it("test load more with count selection buttons", () => {
|
||||
cy.visit("/app/todo/view/report");
|
||||
cy.visit("/desk/todo/view/report");
|
||||
cy.clear_filters();
|
||||
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "20 of");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("List View", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
context("List View Settings", () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
it("Default settings", () => {
|
||||
cy.visit("/app/List/DocType/List");
|
||||
cy.visit("/desk/List/DocType/List");
|
||||
cy.clear_filters();
|
||||
cy.get(".list-count").should("contain", "20 of");
|
||||
cy.get(".list-stats").should("contain", "Tags");
|
||||
});
|
||||
it("disable count and sidebar stats then verify", () => {
|
||||
cy.wait(300);
|
||||
cy.visit("/app/List/DocType/List");
|
||||
cy.visit("/desk/List/DocType/List");
|
||||
cy.clear_filters();
|
||||
cy.wait(300);
|
||||
cy.get(".list-count").should("contain", "20 of");
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ context("Login", () => {
|
|||
cy.get("#login_password").type(Cypress.env("adminPassword"));
|
||||
|
||||
cy.findByRole("button", { name: "Login" }).click();
|
||||
cy.location("pathname").should("match", /^\/app/);
|
||||
cy.location("pathname").should("match", /^\/desk/);
|
||||
cy.window().its("frappe.session.user").should("eq", "Administrator");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("MultiSelectDialog", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
const contact_template = {
|
||||
doctype: "Contact",
|
||||
first_name: "Test",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ context("Navigation", () => {
|
|||
before(() => {
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
it("Navigate to route with hash in document name", () => {
|
||||
cy.insert_doc(
|
||||
|
|
@ -15,14 +15,14 @@ context("Navigation", () => {
|
|||
},
|
||||
true
|
||||
);
|
||||
cy.visit(`/app/client-script/${encodeURIComponent("ABC#123")}`);
|
||||
cy.visit(`/desk/client-script/${encodeURIComponent("ABC#123")}`);
|
||||
cy.title().should("eq", "ABC#123");
|
||||
cy.go("back");
|
||||
cy.title().should("eq", "Website");
|
||||
});
|
||||
|
||||
it("Navigate to previous page after login", () => {
|
||||
cy.visit("/app/todo");
|
||||
cy.visit("/desk/todo");
|
||||
cy.get(".page-head").findByTitle("To Do").should("be.visible");
|
||||
cy.clear_filters();
|
||||
cy.call("logout");
|
||||
|
|
@ -31,6 +31,6 @@ context("Navigation", () => {
|
|||
cy.location("pathname").should("eq", "/login");
|
||||
cy.login();
|
||||
cy.reload().as("reload");
|
||||
cy.location("pathname").should("eq", "/app/todo");
|
||||
cy.location("pathname").should("eq", "/desk/todo");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Number Card", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
it("Check filter populate for child table doctype", () => {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ context.skip("Permissions API", () => {
|
|||
before(() => {
|
||||
cy.visit("/login");
|
||||
cy.remove_role("frappe@example.com", "System Manager");
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
it("Checks permissions via `has_perm` for Kanban Board DocType", () => {
|
||||
cy.visit("/app/kanban-board/view/list");
|
||||
cy.visit("/desk/kanban-board/view/list");
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
|
|
@ -20,7 +20,7 @@ context.skip("Permissions API", () => {
|
|||
});
|
||||
|
||||
it("Checks permissions via `get_perm` for Kanban Board DocType", () => {
|
||||
cy.visit("/app/kanban-board/view/list");
|
||||
cy.visit("/desk/kanban-board/view/list");
|
||||
cy.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Query Report", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
cy.insert_doc(
|
||||
"Report",
|
||||
{
|
||||
|
|
@ -19,7 +19,7 @@ context("Query Report", () => {
|
|||
});
|
||||
|
||||
it("add custom column in report", () => {
|
||||
cy.visit("/app/query-report/Permitted Documents For User");
|
||||
cy.visit("/desk/query-report/Permitted Documents For User");
|
||||
|
||||
cy.get(".page-form.flex", { timeout: 60000 })
|
||||
.should("have.length", 1)
|
||||
|
|
@ -77,12 +77,12 @@ context("Query Report", () => {
|
|||
.findByRole("button", { name: "Submit" })
|
||||
.click({ timeout: 1000, force: true });
|
||||
|
||||
cy.visit("/app/query-report/" + report);
|
||||
cy.visit("/desk/query-report/" + report);
|
||||
cy.get(".datatable").should("exist");
|
||||
};
|
||||
|
||||
it("test multi level query report", () => {
|
||||
cy.visit("/app/query-report/Test ToDo Report");
|
||||
cy.visit("/desk/query-report/Test ToDo Report");
|
||||
cy.get(".datatable").should("exist");
|
||||
|
||||
save_report_and_open("Test ToDo Report 1", " 1");
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const doctype_name = custom_submittable_doctype.name;
|
|||
context("Report View", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
cy.insert_doc("DocType", custom_submittable_doctype, true);
|
||||
cy.clear_cache();
|
||||
cy.insert_doc(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Rounding behaviour", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/");
|
||||
cy.visit("/desk/");
|
||||
});
|
||||
|
||||
it("Commercial Rounding", () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const list_view = "/app/todo";
|
||||
const list_view = "/desk/todo";
|
||||
|
||||
// test round trip with filter types
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ context("Sidebar", () => {
|
|||
before(() => {
|
||||
cy.visit("/");
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -87,7 +87,7 @@ context("Sidebar", () => {
|
|||
description: "Sidebar Attachment ToDo",
|
||||
}).then((todo) => {
|
||||
let todo_name = todo.message.name;
|
||||
cy.visit("/app/todo");
|
||||
cy.visit("/desk/todo");
|
||||
cy.click_sidebar_button("Assigned To");
|
||||
|
||||
//To check if no filter is available in "Assigned To" dropdown
|
||||
|
|
@ -99,7 +99,7 @@ context("Sidebar", () => {
|
|||
cy.get_field("assign_to_me", "Check").click();
|
||||
cy.wait(1000);
|
||||
cy.get(".modal-footer > .standard-actions > .btn-primary").click();
|
||||
cy.visit("/app/todo");
|
||||
cy.visit("/desk/todo");
|
||||
cy.click_sidebar_button("Assigned To");
|
||||
|
||||
//To check if filter is added in "Assigned To" dropdown after assignment
|
||||
|
|
@ -136,7 +136,7 @@ context("Sidebar", () => {
|
|||
cy.get(".assignments > .avatar-group > .avatar > .avatar-frame").click();
|
||||
cy.get(".remove-btn").click({ force: true });
|
||||
cy.hide_dialog();
|
||||
cy.visit("/app/todo");
|
||||
cy.visit("/desk/todo");
|
||||
cy.click_sidebar_button("Assigned To");
|
||||
cy.get(".empty-state").should("contain", "No filters found");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ context("Realtime updates", () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit("/app/todo");
|
||||
cy.visit("/desk/todo");
|
||||
// required because immediately after load socket is still connecting.
|
||||
// Not a huge deal breaker in prod.
|
||||
cy.wait(500);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const doctype_name = data_field_validation_doctype.name;
|
|||
context("URL Data Field Input", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
return cy.insert_doc("DocType", data_field_validation_doctype, true);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Utils", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
function run_util(name, ...args) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
context("View", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
});
|
||||
|
||||
it("Route to ToDo List View", () => {
|
||||
cy.visit("/app/todo/view/list");
|
||||
cy.visit("/desk/todo/view/list");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -15,7 +15,7 @@ context("View", () => {
|
|||
});
|
||||
|
||||
it("Route to ToDo Report View", () => {
|
||||
cy.visit("/app/todo/view/report");
|
||||
cy.visit("/desk/todo/view/report");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -25,7 +25,7 @@ context("View", () => {
|
|||
});
|
||||
|
||||
it("Route to ToDo Dashboard View", () => {
|
||||
cy.visit("/app/todo/view/dashboard");
|
||||
cy.visit("/desk/todo/view/dashboard");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -35,7 +35,7 @@ context("View", () => {
|
|||
});
|
||||
|
||||
it("Route to ToDo Gantt View", () => {
|
||||
cy.visit("/app/todo/view/gantt");
|
||||
cy.visit("/desk/todo/view/gantt");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -46,7 +46,7 @@ context("View", () => {
|
|||
|
||||
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.visit("/desk/note/view/kanban/_Note _Kanban");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -57,7 +57,7 @@ context("View", () => {
|
|||
});
|
||||
|
||||
it("Route to ToDo Calendar View", () => {
|
||||
cy.visit("/app/todo/view/calendar");
|
||||
cy.visit("/desk/todo/view/calendar");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -68,7 +68,7 @@ context("View", () => {
|
|||
|
||||
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.visit("/desk/custom-tree/view/tree");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_tree")
|
||||
|
|
@ -137,7 +137,7 @@ context("View", () => {
|
|||
|
||||
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.visit("/desk/event");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -149,7 +149,7 @@ context("View", () => {
|
|||
|
||||
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.visit("/desk/event/view");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -164,7 +164,7 @@ context("View", () => {
|
|||
view: "Report",
|
||||
force_reroute: true,
|
||||
}).then(() => {
|
||||
cy.visit("/app/event");
|
||||
cy.visit("/desk/event");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -179,7 +179,7 @@ context("View", () => {
|
|||
view: "Report",
|
||||
force_reroute: true,
|
||||
}).then(() => {
|
||||
cy.visit("/app/event/view");
|
||||
cy.visit("/desk/event/view");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -194,7 +194,7 @@ context("View", () => {
|
|||
view: "Report",
|
||||
force_reroute: true,
|
||||
}).then(() => {
|
||||
cy.visit("/app/event/view/list");
|
||||
cy.visit("/desk/event/view/list");
|
||||
cy.wait(500);
|
||||
cy.window()
|
||||
.its("cur_list")
|
||||
|
|
@ -206,17 +206,17 @@ context("View", () => {
|
|||
|
||||
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.visit("/desk/event");
|
||||
cy.visit("/desk/event/view/list");
|
||||
cy.location("pathname").should("eq", "/desk/event/view/list");
|
||||
cy.go("back");
|
||||
cy.location("pathname").should("eq", "/app/event");
|
||||
cy.location("pathname").should("eq", "/desk/event");
|
||||
});
|
||||
});
|
||||
|
||||
it("Route to Form", () => {
|
||||
const test_user = cy.config("testUser");
|
||||
cy.visit(`/app/user/${test_user}`);
|
||||
cy.visit(`/desk/user/${test_user}`);
|
||||
cy.window()
|
||||
.its("cur_frm")
|
||||
.then((frm) => {
|
||||
|
|
@ -225,7 +225,7 @@ context("View", () => {
|
|||
});
|
||||
|
||||
it("Route to Website Workspace", () => {
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
cy.get(".title-text").should("contain", "Website");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Web Form", () => {
|
||||
before(() => {
|
||||
cy.login("Administrator");
|
||||
cy.visit("/app/");
|
||||
cy.visit("/desk/");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -11,7 +11,7 @@ context("Web Form", () => {
|
|||
});
|
||||
|
||||
it("Create Web Form", () => {
|
||||
cy.visit("/app/web-form/new");
|
||||
cy.visit("/desk/web-form/new");
|
||||
|
||||
cy.intercept("POST", "/api/method/frappe.desk.form.save.savedocs").as("save_form");
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ context("Web Form", () => {
|
|||
it("Login Required", () => {
|
||||
cy.call("logout");
|
||||
cy.login("Administrator");
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Settings" }).click();
|
||||
cy.get('input[data-fieldname="login_required"]').check({ force: true });
|
||||
|
|
@ -74,7 +74,7 @@ context("Web Form", () => {
|
|||
|
||||
it("Show List", () => {
|
||||
cy.login("Administrator");
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Settings" }).click();
|
||||
cy.get(".section-head").contains("List Settings").click();
|
||||
|
|
@ -88,7 +88,7 @@ context("Web Form", () => {
|
|||
});
|
||||
|
||||
it("Show Custom List Title", () => {
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Settings" }).click();
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ context("Web Form", () => {
|
|||
cy.get(".web-list-table thead th").contains("Sr.");
|
||||
cy.get(".web-list-table thead th").contains("Title");
|
||||
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Settings" }).click();
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ context("Web Form", () => {
|
|||
});
|
||||
|
||||
it("Custom Breadcrumbs", () => {
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Customization" }).click();
|
||||
cy.fill_field("breadcrumbs", '[{"label": _("Notes"), "route":"note"}]', "Code");
|
||||
|
|
@ -192,7 +192,7 @@ context("Web Form", () => {
|
|||
});
|
||||
|
||||
it("Edit Mode", () => {
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Settings" }).click();
|
||||
cy.get('input[data-fieldname="allow_edit"]').check();
|
||||
|
|
@ -216,7 +216,7 @@ context("Web Form", () => {
|
|||
});
|
||||
|
||||
it("Allow Multiple Response", () => {
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Settings" }).click();
|
||||
cy.get('input[data-fieldname="allow_multiple"]').check();
|
||||
|
|
@ -234,7 +234,7 @@ context("Web Form", () => {
|
|||
});
|
||||
|
||||
it("Allow Delete", () => {
|
||||
cy.visit("/app/web-form/note");
|
||||
cy.visit("/desk/web-form/note");
|
||||
|
||||
cy.findByRole("tab", { name: "Settings" }).click();
|
||||
cy.get('input[data-fieldname="allow_delete"]').check();
|
||||
|
|
|
|||
|
|
@ -2,16 +2,23 @@ context("Workspace 2.0", () => {
|
|||
before(() => {
|
||||
cy.visit("/login");
|
||||
cy.login();
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
.then((frappe) => {
|
||||
return frappe.xcall("frappe.tests.ui_test_helpers.empty_my_workspaces");
|
||||
});
|
||||
});
|
||||
|
||||
it("Navigate to page from sidebar", () => {
|
||||
cy.visit("/app/build");
|
||||
cy.visit("/desk/build");
|
||||
cy.get(".codex-editor__redactor .ce-block");
|
||||
cy.get('.sidebar-item-container[item-title="Website"]').first().click();
|
||||
cy.location("pathname").should("eq", "/app/website");
|
||||
cy.get('.sidebar-item-container[item-name="Page"]').first().click();
|
||||
cy.location("pathname").should("eq", "/desk/page");
|
||||
});
|
||||
|
||||
it("Create Private Page", () => {
|
||||
cy.visit("/desk/website");
|
||||
cy.intercept({
|
||||
method: "POST",
|
||||
url: "api/method/frappe.desk.doctype.workspace.workspace.new_page",
|
||||
|
|
@ -27,61 +34,16 @@ context("Workspace 2.0", () => {
|
|||
cy.get_open_dialog().find(".btn-primary").click();
|
||||
|
||||
// check if sidebar item is added in pubic section
|
||||
cy.get('.sidebar-item-container[item-title="Test Private Page"]').should(
|
||||
"have.attr",
|
||||
"item-public",
|
||||
"0"
|
||||
);
|
||||
cy.get('.sidebar-item-container[item-name="Test Private Page"]');
|
||||
cy.wait(300);
|
||||
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
|
||||
cy.wait(300);
|
||||
cy.get('.sidebar-item-container[item-title="Test Private Page"]').should(
|
||||
"have.attr",
|
||||
"item-public",
|
||||
"0"
|
||||
);
|
||||
|
||||
cy.wait("@new_page");
|
||||
});
|
||||
|
||||
it("Create Child Page", () => {
|
||||
cy.intercept({
|
||||
method: "POST",
|
||||
url: "api/method/frappe.desk.doctype.workspace.workspace.new_page",
|
||||
}).as("new_page");
|
||||
|
||||
cy.get(".codex-editor__redactor .ce-block");
|
||||
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();
|
||||
|
||||
// check if sidebar item is added in pubic section
|
||||
cy.get('.sidebar-item-container[item-title="Test Child Page"]').should(
|
||||
"have.attr",
|
||||
"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-title="Test Child Page"]').should(
|
||||
"have.attr",
|
||||
"item-public",
|
||||
"0"
|
||||
);
|
||||
cy.get('.sidebar-item-container[item-name="Test Private Page"]');
|
||||
|
||||
cy.wait("@new_page");
|
||||
});
|
||||
|
||||
it("Add New Block", () => {
|
||||
cy.get('.sidebar-item-container[item-title="Test Private Page"]').as("sidebar-item");
|
||||
|
||||
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}");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
context("Workspace Blocks", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
return cy
|
||||
.window()
|
||||
.its("frappe")
|
||||
|
|
@ -17,7 +17,7 @@ context("Workspace Blocks", () => {
|
|||
url: "api/method/frappe.desk.doctype.workspace.workspace.new_page",
|
||||
}).as("new_page");
|
||||
|
||||
cy.visit("/app/website");
|
||||
cy.visit("/desk/website");
|
||||
cy.get(".codex-editor__redactor .ce-block");
|
||||
cy.get(".btn-new-workspace").click();
|
||||
cy.fill_field("title", "Test Block Page", "Data");
|
||||
|
|
@ -26,19 +26,11 @@ context("Workspace Blocks", () => {
|
|||
cy.get_open_dialog().find(".btn-primary").click();
|
||||
|
||||
// check if sidebar item is added in private section
|
||||
cy.get('.sidebar-item-container[item-title="Test Block Page"]').should(
|
||||
"have.attr",
|
||||
"item-public",
|
||||
"0"
|
||||
);
|
||||
cy.get('.sidebar-item-container[item-name="Test Block Page"]');
|
||||
cy.wait(300);
|
||||
cy.get('.standard-actions .btn-primary[data-label="Save"]').click();
|
||||
cy.wait(300);
|
||||
cy.get('.sidebar-item-container[item-title="Test Block Page"]').should(
|
||||
"have.attr",
|
||||
"item-public",
|
||||
"0"
|
||||
);
|
||||
cy.get('.sidebar-item-container[item-name="Test Block Page"]');
|
||||
|
||||
cy.wait("@new_page");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ def get_default_path():
|
|||
if len(_apps) == 1:
|
||||
return _apps[0].get("route") or "/apps"
|
||||
elif is_desk_apps(_apps):
|
||||
return "/app"
|
||||
return "/desk"
|
||||
return "/apps"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ class LoginManager:
|
|||
frappe.local.cookie_manager.set_cookie("system_user", "yes", deduplicate=True)
|
||||
if not resume:
|
||||
frappe.local.response["message"] = "Logged In"
|
||||
frappe.local.response["home_page"] = get_default_path() or "/app"
|
||||
frappe.local.response["home_page"] = get_default_path() or "/desk"
|
||||
|
||||
if not resume:
|
||||
frappe.response["full_name"] = self.full_name
|
||||
|
|
|
|||
|
|
@ -1,345 +0,0 @@
|
|||
{
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"sR-UFcO7II\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Import Data\",\"col\":3}},{\"id\":\"IkcVmgWb3z\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"ToDo\",\"col\":3}},{\"id\":\"6wir-jZFRE\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"File\",\"col\":3}},{\"id\":\"45a1jzQkTm\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Assignment Rule\",\"col\":3}},{\"id\":\"EgtURZsoiF\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"0yceBIfhHM\",\"type\":\"card\",\"data\":{\"card_name\":\"Data\",\"col\":4}},{\"id\":\"42WbBA9rpj\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"wE9n7TIrAc\",\"type\":\"card\",\"data\":{\"card_name\":\"Alerts and Notifications\",\"col\":4}},{\"id\":\"7_U7_xCOos\",\"type\":\"card\",\"data\":{\"card_name\":\"Email\",\"col\":4}},{\"id\":\"3imoh2oqsJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"id\":\"SlYKJZj5r3\",\"type\":\"card\",\"data\":{\"card_name\":\"Automation\",\"col\":4}}]",
|
||||
"creation": "2020-03-02 14:53:24.980279",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "tool",
|
||||
"idx": 0,
|
||||
"is_hidden": 0,
|
||||
"label": "Tools",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Automation",
|
||||
"link_count": 3,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Assignment Rule",
|
||||
"link_count": 0,
|
||||
"link_to": "Assignment Rule",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Milestone",
|
||||
"link_count": 0,
|
||||
"link_to": "Milestone",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Auto Repeat",
|
||||
"link_count": 0,
|
||||
"link_to": "Auto Repeat",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Tools",
|
||||
"link_count": 4,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "To Do",
|
||||
"link_count": 0,
|
||||
"link_to": "ToDo",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Calendar",
|
||||
"link_count": 0,
|
||||
"link_to": "Event",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Note",
|
||||
"link_count": 0,
|
||||
"link_to": "Note",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Files",
|
||||
"link_count": 0,
|
||||
"link_to": "File",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Printing",
|
||||
"link_count": 4,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Print Format Builder",
|
||||
"link_count": 0,
|
||||
"link_to": "print-format-builder",
|
||||
"link_type": "Page",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Print Format Builder (New)",
|
||||
"link_count": 0,
|
||||
"link_to": "print-format-builder-beta",
|
||||
"link_type": "Page",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Print Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "Print Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Print Heading",
|
||||
"link_count": 0,
|
||||
"link_to": "Print Heading",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Alerts and Notifications",
|
||||
"link_count": 3,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Notification",
|
||||
"link_count": 0,
|
||||
"link_to": "Notification",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Auto Email Report",
|
||||
"link_count": 0,
|
||||
"link_to": "Auto Email Report",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Notification Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "Notification Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"description": "Manage your data",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Data",
|
||||
"link_count": 5,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Import Data",
|
||||
"link_count": 0,
|
||||
"link_to": "Data Import",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Export Data",
|
||||
"link_count": 0,
|
||||
"link_to": "Data Export",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bulk Update",
|
||||
"link_count": 0,
|
||||
"link_to": "Bulk Update",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Download Backups",
|
||||
"link_count": 0,
|
||||
"link_to": "backups",
|
||||
"link_type": "Page",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Deleted Documents",
|
||||
"link_count": 0,
|
||||
"link_to": "Deleted Document",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email",
|
||||
"link_count": 4,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email Account",
|
||||
"link_count": 0,
|
||||
"link_to": "Email Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email Domain",
|
||||
"link_count": 0,
|
||||
"link_to": "Email Domain",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email Template",
|
||||
"link_count": 0,
|
||||
"link_to": "Email Template",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Email Group",
|
||||
"link_count": 0,
|
||||
"link_to": "Email Group",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2025-06-27 11:39:44.392114",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Automation",
|
||||
"name": "Tools",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 1.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Import Data",
|
||||
"link_to": "Data Import",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "ToDo",
|
||||
"link_to": "ToDo",
|
||||
"stats_filter": "[[\"ToDo\",\"status\",\"=\",\"Open\",false]]",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "File",
|
||||
"link_to": "File",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Assignment Rule",
|
||||
"link_to": "Assignment Rule",
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"title": "Tools"
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ from frappe.core.doctype.installed_applications.installed_applications import (
|
|||
)
|
||||
from frappe.core.doctype.navbar_settings.navbar_settings import get_app_logo, get_navbar_settings
|
||||
from frappe.desk.doctype.changelog_feed.changelog_feed import get_changelog_feed_items
|
||||
from frappe.desk.doctype.desktop_icon.desktop_icon import get_desktop_icons
|
||||
from frappe.desk.doctype.form_tour.form_tour import get_onboarding_ui_tours
|
||||
from frappe.desk.doctype.route_history.route_history import frequently_visited_links
|
||||
from frappe.desk.form.load import get_meta_bundle
|
||||
|
|
@ -148,8 +149,13 @@ def load_conf_settings(bootinfo):
|
|||
def load_desktop_data(bootinfo):
|
||||
from frappe.desk.desktop import get_workspace_sidebar_items
|
||||
|
||||
bootinfo.sidebar_pages = get_workspace_sidebar_items()
|
||||
allowed_pages = [d.name for d in bootinfo.sidebar_pages.get("pages")]
|
||||
bootinfo.desktop_icons = get_desktop_icons()
|
||||
bootinfo.workspaces = get_workspace_sidebar_items()
|
||||
bootinfo.show_app_icons_as_folder = frappe.db.get_single_value(
|
||||
"Desktop Settings", "show_app_icons_as_folder"
|
||||
)
|
||||
bootinfo.workspace_sidebar_item = get_sidebar_items()
|
||||
allowed_pages = [d.name for d in bootinfo.workspaces.get("pages")]
|
||||
bootinfo.module_wise_workspaces = get_controller("Workspace").get_module_wise_workspaces()
|
||||
bootinfo.dashboards = frappe.get_all("Dashboard")
|
||||
bootinfo.app_data = []
|
||||
|
|
@ -196,7 +202,7 @@ def load_desktop_data(bootinfo):
|
|||
frappe.get_hooks("app_home", app_name=app_name)
|
||||
and frappe.get_hooks("app_home", app_name=app_name)[0]
|
||||
)
|
||||
or (workspaces and "/app/" + frappe.utils.slug(workspaces[0]))
|
||||
or (workspaces and "/desk/" + frappe.utils.slug(workspaces[0]))
|
||||
or "",
|
||||
app_logo_url=app_info.get("logo")
|
||||
or frappe.get_hooks("app_logo_url", app_name=app_name)
|
||||
|
|
@ -360,7 +366,7 @@ def add_home_page(bootinfo, docs):
|
|||
bootinfo["home_page"] = page.name
|
||||
except (frappe.DoesNotExistError, frappe.PermissionError):
|
||||
frappe.clear_last_message()
|
||||
bootinfo["home_page"] = "Workspaces"
|
||||
bootinfo["home_page"] = "desktop"
|
||||
|
||||
|
||||
def add_timezone_info(bootinfo):
|
||||
|
|
@ -518,3 +524,68 @@ def get_sentry_dsn():
|
|||
return
|
||||
|
||||
return os.getenv("FRAPPE_SENTRY_DSN")
|
||||
|
||||
|
||||
def get_sidebar_items():
|
||||
sidebars = frappe.get_all(
|
||||
"Workspace Sidebar", fields=["name", "header_icon"], filters={"name": ["not like", "%My Workspaces%"]}
|
||||
)
|
||||
add_user_specific_sidebar(sidebars)
|
||||
sidebar_items = {}
|
||||
|
||||
for s in sidebars:
|
||||
w = frappe.get_doc("Workspace Sidebar", s["name"])
|
||||
sidebar_items[s["name"].lower()] = {
|
||||
"label": s["name"],
|
||||
"items": [],
|
||||
"header_icon": s["header_icon"],
|
||||
"module": w.module,
|
||||
}
|
||||
for si in w.items:
|
||||
workspace_sidebar = {
|
||||
"label": si.label,
|
||||
"link_to": si.link_to,
|
||||
"link_type": si.link_type,
|
||||
"type": si.type,
|
||||
"icon": si.icon,
|
||||
"child": si.child,
|
||||
"collapsible": si.collapsible,
|
||||
"indent": si.indent,
|
||||
"keep_closed": si.keep_closed,
|
||||
"display_depends_on": si.display_depends_on,
|
||||
"url": si.url,
|
||||
"show_arrow": si.show_arrow,
|
||||
"filters": si.filters,
|
||||
"route_options": si.route_options,
|
||||
}
|
||||
if si.link_type == "Report" and si.link_to:
|
||||
report_type, ref_doctype = frappe.db.get_value(
|
||||
"Report", si.link_to, ["report_type", "ref_doctype"]
|
||||
)
|
||||
workspace_sidebar["report"] = {
|
||||
"report_type": report_type,
|
||||
"ref_doctype": ref_doctype,
|
||||
}
|
||||
|
||||
if (
|
||||
"My Workspaces" in s["name"]
|
||||
or si.type == "Section Break"
|
||||
or w.is_item_allowed(si.link_to, si.link_type)
|
||||
):
|
||||
sidebar_items[s["name"].lower()]["items"].append(workspace_sidebar)
|
||||
|
||||
old_name = f"my workspaces-{frappe.session.user}"
|
||||
if old_name in sidebar_items:
|
||||
sidebar_items["my workspaces"] = sidebar_items.pop(old_name)
|
||||
return sidebar_items
|
||||
|
||||
|
||||
def add_user_specific_sidebar(sidebars):
|
||||
try:
|
||||
my_workspace_for_user = frappe.get_doc("Workspace Sidebar", f"My Workspaces-{frappe.session.user}")
|
||||
sidebars.append(
|
||||
{"name": my_workspace_for_user.name, "header_icon": my_workspace_for_user.header_icon}
|
||||
)
|
||||
except frappe.DoesNotExistError:
|
||||
my_workspace = frappe.get_doc("Workspace Sidebar", "My Workspaces")
|
||||
sidebars.append({"name": my_workspace.name, "header_icon": my_workspace.header_icon})
|
||||
|
|
|
|||
|
|
@ -1584,6 +1584,33 @@ def bypass_patch(context: CliCtxObj, patch_name: str, yes: bool):
|
|||
frappe.destroy()
|
||||
|
||||
|
||||
@click.command("create-desktop-icons-and-sidebar")
|
||||
@pass_context
|
||||
def create_icons_and_sidebar(context: CliCtxObj):
|
||||
"""Create desktop icons and workspace sidebars."""
|
||||
from frappe.desk.doctype.desktop_icon.desktop_icon import create_desktop_icons
|
||||
from frappe.desk.doctype.workspace_sidebar.workspace_sidebar import (
|
||||
create_workspace_sidebar_for_workspaces,
|
||||
)
|
||||
|
||||
if not context.sites:
|
||||
raise SiteNotSpecifiedError
|
||||
for site in context.sites:
|
||||
frappe.init(site)
|
||||
frappe.connect()
|
||||
try:
|
||||
print("Creating Desktop Icons")
|
||||
create_desktop_icons()
|
||||
print("Creating Workspace Sidebars")
|
||||
create_workspace_sidebar_for_workspaces()
|
||||
# Saving it in a command need it
|
||||
frappe.db.commit() # nosemgrep
|
||||
except Exception as e:
|
||||
print(f"Error creating icons {site}: {e}")
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
|
||||
commands = [
|
||||
add_system_manager,
|
||||
add_user_for_sites,
|
||||
|
|
@ -1620,4 +1647,5 @@ commands = [
|
|||
trim_database,
|
||||
clear_log_table,
|
||||
bypass_patch,
|
||||
create_icons_and_sidebar,
|
||||
]
|
||||
|
|
|
|||
|
|
@ -437,6 +437,7 @@ def run_parallel_tests(
|
|||
@click.option("--parallel", is_flag=True, help="Run UI Test in parallel mode")
|
||||
@click.option("--with-coverage", is_flag=True, help="Generate coverage report")
|
||||
@click.option("--browser", default="chrome", help="Browser to run tests in")
|
||||
@click.option("--spec", help="Spec file to run")
|
||||
@click.option("--ci-build-id")
|
||||
@pass_context
|
||||
def run_ui_tests(
|
||||
|
|
@ -448,6 +449,7 @@ def run_ui_tests(
|
|||
browser="chrome",
|
||||
ci_build_id=None,
|
||||
cypressargs=None,
|
||||
spec=None,
|
||||
):
|
||||
"Run UI tests"
|
||||
site = get_site(context)
|
||||
|
|
@ -494,6 +496,8 @@ def run_ui_tests(
|
|||
|
||||
# run for headless mode
|
||||
run_or_open = f"run --browser {browser}" if headless else "open"
|
||||
if headless and spec:
|
||||
run_or_open += f" --spec {spec}"
|
||||
formatted_command = f"{site_env} {password_env} {coverage_env} {cypress_path} {run_or_open}"
|
||||
|
||||
if os.environ.get("CYPRESS_RECORD_KEY"):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"based_on": "creation",
|
||||
"chart_name": "Background Job Activity",
|
||||
"chart_type": "Count",
|
||||
"creation": "2025-09-08 11:56:13.469137",
|
||||
"currency": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Scheduled Job Type",
|
||||
"dynamic_filters_json": "[]",
|
||||
"filters_json": "[]",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 0,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2025-10-30 21:36:33.646973",
|
||||
"modified": "2025-10-30 21:37:11.340673",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Background Job Activity",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"parent_document_type": "",
|
||||
"roles": [],
|
||||
"show_values_over_chart": 0,
|
||||
"source": "",
|
||||
"time_interval": "Daily",
|
||||
"timeseries": 1,
|
||||
"timespan": "Last Year",
|
||||
"type": "Line",
|
||||
"use_report_chart": 0,
|
||||
"value_based_on": "",
|
||||
"y_axis": []
|
||||
}
|
||||
|
|
@ -375,7 +375,7 @@ class TestUser(IntegrationTestCase):
|
|||
frappe.set_user("testpassword@example.com")
|
||||
test_user = frappe.get_doc("User", "testpassword@example.com")
|
||||
key = self.reset_password(test_user)
|
||||
self.assertEqual(update_password(new_password, key=key), "/app")
|
||||
self.assertEqual(update_password(new_password, key=key), "/desk")
|
||||
self.assertEqual(
|
||||
update_password(new_password, key="wrong_key"),
|
||||
"The reset password link has either been used before or is invalid",
|
||||
|
|
|
|||
|
|
@ -951,7 +951,7 @@ def update_password(
|
|||
frappe.db.set_value("User", user, "reset_password_key", "")
|
||||
|
||||
if user_doc.user_type == "System User":
|
||||
return get_default_path() or "/app"
|
||||
return get_default_path() or "/desk"
|
||||
else:
|
||||
return redirect_url or get_default_path() or get_home_page()
|
||||
|
||||
|
|
|
|||
41
frappe/core/workspace/system/system.json
Normal file
41
frappe/core/workspace/system/system.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"app": "frappe",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "Background Job Activity",
|
||||
"label": "Background Job Activity"
|
||||
},
|
||||
{
|
||||
"chart_name": "Notifications By Type",
|
||||
"label": "Notification Summary"
|
||||
}
|
||||
],
|
||||
"content": "[{\"id\":\"-bxX6Dwxxy\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Background Job Activity\",\"col\":12}},{\"id\":\"gccD2r7Ut3\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Notification Summary\",\"col\":12}}]",
|
||||
"creation": "2025-09-08 11:33:57.533875",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "monitor-check",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "System",
|
||||
"link_type": "DocType",
|
||||
"links": [],
|
||||
"modified": "2025-10-30 18:22:58.416219",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "System",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 27.0,
|
||||
"shortcuts": [],
|
||||
"title": "System",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
context("Client Script", () => {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit("/app");
|
||||
cy.visit("/desk");
|
||||
});
|
||||
|
||||
it("should run form script in doctype form", () => {
|
||||
|
|
@ -16,7 +16,7 @@ context("Client Script", () => {
|
|||
},
|
||||
true
|
||||
);
|
||||
cy.visit("/app/todo/new", {
|
||||
cy.visit("/desk/todo/new", {
|
||||
onBeforeLoad(win) {
|
||||
cy.spy(win.console, "log").as("consoleLog");
|
||||
},
|
||||
|
|
@ -36,7 +36,7 @@ context("Client Script", () => {
|
|||
},
|
||||
true
|
||||
);
|
||||
cy.visit("/app/todo", {
|
||||
cy.visit("/desk/todo", {
|
||||
onBeforeLoad(win) {
|
||||
cy.spy(win.console, "log").as("consoleLog");
|
||||
},
|
||||
|
|
@ -56,7 +56,7 @@ context("Client Script", () => {
|
|||
},
|
||||
true
|
||||
);
|
||||
cy.visit("/app/todo", {
|
||||
cy.visit("/desk/todo", {
|
||||
onBeforeLoad(win) {
|
||||
cy.spy(win.console, "log").as("consoleLog");
|
||||
},
|
||||
|
|
@ -87,7 +87,7 @@ context("Client Script", () => {
|
|||
},
|
||||
true
|
||||
);
|
||||
cy.visit("/app/todo/new", {
|
||||
cy.visit("/desk/todo/new", {
|
||||
onBeforeLoad(win) {
|
||||
cy.spy(win.console, "log").as("consoleLog");
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"based_on": "creation",
|
||||
"chart_name": "Email Activity",
|
||||
"chart_type": "Count",
|
||||
"creation": "2025-09-08 11:26:02.676908",
|
||||
"currency": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Email Queue",
|
||||
"dynamic_filters_json": "[]",
|
||||
"filters_json": "[]",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 0,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2025-09-08 11:26:32.458436",
|
||||
"modified": "2025-09-08 11:26:42.394911",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Email Activity",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"parent_document_type": "",
|
||||
"roles": [],
|
||||
"show_values_over_chart": 0,
|
||||
"source": "",
|
||||
"time_interval": "Daily",
|
||||
"timeseries": 1,
|
||||
"timespan": "Last Week",
|
||||
"type": "Line",
|
||||
"use_report_chart": 0,
|
||||
"value_based_on": "",
|
||||
"y_axis": []
|
||||
}
|
||||
34
frappe/desk/dashboard_chart/login/login.json
Normal file
34
frappe/desk/dashboard_chart/login/login.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"based_on": "communication_date",
|
||||
"chart_name": "Login",
|
||||
"chart_type": "Count",
|
||||
"creation": "2025-08-28 16:48:49.946848",
|
||||
"currency": "INR",
|
||||
"docstatus": 0,
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Activity Log",
|
||||
"dynamic_filters_json": "[]",
|
||||
"filters_json": "[[\"Activity Log\",\"status\",\"=\",\"Success\",false]]",
|
||||
"group_by_type": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 0,
|
||||
"is_standard": 1,
|
||||
"last_synced_on": "2025-09-11 03:02:03.338607",
|
||||
"modified": "2025-09-11 03:02:10.575994",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Login",
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"parent_document_type": "",
|
||||
"roles": [],
|
||||
"show_values_over_chart": 0,
|
||||
"source": "",
|
||||
"time_interval": "Daily",
|
||||
"timeseries": 1,
|
||||
"timespan": "Last Week",
|
||||
"type": "Line",
|
||||
"use_report_chart": 0,
|
||||
"value_based_on": "",
|
||||
"y_axis": []
|
||||
}
|
||||
|
|
@ -707,3 +707,8 @@ def update_onboarding_step(name, field, value):
|
|||
frappe.db.set_value("Onboarding Step", name, field, value)
|
||||
|
||||
capture(frappe.scrub(name), app="frappe_onboarding", properties={field: value})
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_installed_apps():
|
||||
return frappe.get_installed_apps()
|
||||
|
|
|
|||
|
|
@ -2,5 +2,31 @@
|
|||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Desktop Icon", {
|
||||
refresh: function (frm) {},
|
||||
setup: function (frm) {
|
||||
frm.set_query("parent_icon", function () {
|
||||
return {
|
||||
filters: {
|
||||
icon_type: ["in", ["Folder", "App"]],
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
refresh: function (frm) {
|
||||
if (frm.doc.link_to && frm.doc.link_type) {
|
||||
frm.add_custom_button(
|
||||
__("Workspace Sidebar"),
|
||||
function () {
|
||||
frappe.new_doc("Workspace Sidebar", {}, (doc) => {
|
||||
doc.title = frm.doc.label;
|
||||
doc.header_icon = frm.doc.icon;
|
||||
let sidebar_item = frappe.model.add_child(doc, "items");
|
||||
sidebar_item.label = frm.doc.link_to;
|
||||
sidebar_item.link_to = frm.doc.link_to;
|
||||
sidebar_item.link_type = frm.doc.link_type;
|
||||
});
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,41 +1,34 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:label",
|
||||
"creation": "2016-02-22 03:47:45.387068",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"module_name",
|
||||
"label",
|
||||
"standard",
|
||||
"custom",
|
||||
"icon_type",
|
||||
"link_type",
|
||||
"link_to",
|
||||
"parent_icon",
|
||||
"sidebar",
|
||||
"column_break_3",
|
||||
"app",
|
||||
"description",
|
||||
"category",
|
||||
"hidden",
|
||||
"blocked",
|
||||
"force_show",
|
||||
"section_break_7",
|
||||
"type",
|
||||
"_doctype",
|
||||
"_report",
|
||||
"link",
|
||||
"column_break_10",
|
||||
"color",
|
||||
"icon",
|
||||
"reverse",
|
||||
"idx"
|
||||
"logo_url",
|
||||
"idx",
|
||||
"link",
|
||||
"hidden",
|
||||
"roles_tab",
|
||||
"roles"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "module_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Module Name"
|
||||
},
|
||||
{
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"label": "Label"
|
||||
"label": "Label",
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
|
|
@ -44,32 +37,54 @@
|
|||
"in_list_view": 1,
|
||||
"label": "Standard"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "custom",
|
||||
"fieldtype": "Check",
|
||||
"label": "Custom",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "app",
|
||||
"fieldtype": "Data",
|
||||
"label": "App",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldname": "link",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Description"
|
||||
"label": "Link"
|
||||
},
|
||||
{
|
||||
"fieldname": "category",
|
||||
"fieldname": "icon",
|
||||
"fieldtype": "Icon",
|
||||
"label": "Icon"
|
||||
},
|
||||
{
|
||||
"fieldname": "idx",
|
||||
"fieldtype": "Int",
|
||||
"label": "Idx"
|
||||
},
|
||||
{
|
||||
"fieldname": "logo_url",
|
||||
"fieldtype": "Data",
|
||||
"label": "Category"
|
||||
"label": "Logo URL"
|
||||
},
|
||||
{
|
||||
"fieldname": "icon_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Icon Type",
|
||||
"options": "Folder\nApp\nLink"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.standard == 1",
|
||||
"fieldname": "app",
|
||||
"fieldtype": "Autocomplete",
|
||||
"label": "App",
|
||||
"options": "Installed Applications"
|
||||
},
|
||||
{
|
||||
"fieldname": "link_to",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Link To",
|
||||
"options": "link_type"
|
||||
},
|
||||
{
|
||||
"fieldname": "parent_icon",
|
||||
"fieldtype": "Link",
|
||||
"label": "Parent Icon",
|
||||
"options": "Desktop Icon"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
|
|
@ -78,79 +93,37 @@
|
|||
"label": "Hidden"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "blocked",
|
||||
"fieldtype": "Check",
|
||||
"label": "Blocked"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "force_show",
|
||||
"fieldtype": "Check",
|
||||
"label": "Force Show",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldname": "link_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Type",
|
||||
"options": "module\nlist\nlink\npage\nquery-report"
|
||||
"label": "Link Type",
|
||||
"options": "DocType\nWorkspace\nExternal"
|
||||
},
|
||||
{
|
||||
"fieldname": "_doctype",
|
||||
"fieldname": "roles_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Roles"
|
||||
},
|
||||
{
|
||||
"fieldname": "roles",
|
||||
"fieldtype": "Table",
|
||||
"label": "Roles",
|
||||
"options": "Has Role"
|
||||
},
|
||||
{
|
||||
"fieldname": "sidebar",
|
||||
"fieldtype": "Link",
|
||||
"label": "_doctype",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "_report",
|
||||
"fieldtype": "Link",
|
||||
"label": "_report",
|
||||
"options": "Report"
|
||||
},
|
||||
{
|
||||
"fieldname": "link",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Link"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "color",
|
||||
"fieldtype": "Data",
|
||||
"label": "Color"
|
||||
},
|
||||
{
|
||||
"fieldname": "icon",
|
||||
"fieldtype": "Data",
|
||||
"label": "Icon"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "reverse",
|
||||
"fieldtype": "Check",
|
||||
"label": "Reverse Icon Color"
|
||||
},
|
||||
{
|
||||
"fieldname": "idx",
|
||||
"fieldtype": "Int",
|
||||
"label": "Idx"
|
||||
"label": "Sidebar",
|
||||
"options": "Workspace Sidebar"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:02:17.847139",
|
||||
"modified": "2025-11-15 22:10:10.463829",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Desktop Icon",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
|
@ -167,9 +140,10 @@
|
|||
}
|
||||
],
|
||||
"read_only": 1,
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "module_name",
|
||||
"title_field": "label",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@
|
|||
# License: MIT. See LICENSE
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.user import UserPermissions
|
||||
from frappe.modules.export_file import write_document_file
|
||||
from frappe.modules.import_file import import_file_by_path
|
||||
from frappe.modules.utils import create_directory_on_app_path, get_app_level_directory_path
|
||||
|
||||
|
||||
class DesktopIcon(Document):
|
||||
|
|
@ -17,26 +20,22 @@ class DesktopIcon(Document):
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.core.doctype.has_role.has_role import HasRole
|
||||
from frappe.types import DF
|
||||
|
||||
_doctype: DF.Link | None
|
||||
_report: DF.Link | None
|
||||
app: DF.Data | None
|
||||
blocked: DF.Check
|
||||
category: DF.Data | None
|
||||
color: DF.Data | None
|
||||
custom: DF.Check
|
||||
description: DF.SmallText | None
|
||||
force_show: DF.Check
|
||||
app: DF.Autocomplete | None
|
||||
hidden: DF.Check
|
||||
icon: DF.Data | None
|
||||
icon_type: DF.Literal["Folder", "App", "Link"]
|
||||
idx: DF.Int
|
||||
label: DF.Data | None
|
||||
link: DF.SmallText | None
|
||||
module_name: DF.Data | None
|
||||
reverse: DF.Check
|
||||
link_to: DF.DynamicLink | None
|
||||
link_type: DF.Literal["DocType", "Workspace", "External"]
|
||||
logo_url: DF.Data | None
|
||||
parent_icon: DF.Link | None
|
||||
roles: DF.Table[HasRole]
|
||||
sidebar: DF.Link | None
|
||||
standard: DF.Check
|
||||
type: DF.Literal["module", "list", "link", "page", "query-report"]
|
||||
# end: auto-generated types
|
||||
|
||||
def validate(self):
|
||||
|
|
@ -45,10 +44,54 @@ class DesktopIcon(Document):
|
|||
|
||||
def on_trash(self):
|
||||
clear_desktop_icons_cache()
|
||||
if frappe.conf.developer_mode:
|
||||
if self.standard == 1 and self.app:
|
||||
self.delete_desktop_icon_file()
|
||||
|
||||
def on_update(self):
|
||||
if frappe.conf.developer_mode:
|
||||
if self.standard == 1 and self.app:
|
||||
self.export_desktop_icon()
|
||||
|
||||
def export_desktop_icon(self):
|
||||
folder_path = create_directory_on_app_path("desktop_icon", self.app)
|
||||
file_path = os.path.join(folder_path, f"{frappe.scrub(self.label)}.json")
|
||||
doc_export = self.as_dict(no_nulls=True, no_private_properties=True)
|
||||
# if self.parent_icon:
|
||||
# print(self.parent_icon)
|
||||
# doc_export["parent_icon"] = frappe.db.get_value("Desktop Icon", self.parent_icon, "label")
|
||||
with open(file_path, "w+") as icon_file_doc:
|
||||
icon_file_doc.write(frappe.as_json(doc_export) + "\n")
|
||||
|
||||
def delete_desktop_icon_file(self):
|
||||
folder_path = create_directory_on_app_path("desktop_icon", self.app)
|
||||
file_path = os.path.join(folder_path, f"{frappe.scrub(self.label)}.json")
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
def is_permitted(self):
|
||||
"""Return True if `Has Role` is not set or the user is allowed."""
|
||||
from frappe.utils import has_common
|
||||
|
||||
allowed = [d.role for d in frappe.get_all("Has Role", fields=["role"], filters={"parent": self.name})]
|
||||
|
||||
if not allowed:
|
||||
return True
|
||||
|
||||
roles = frappe.get_roles()
|
||||
|
||||
if has_common(roles, allowed):
|
||||
return True
|
||||
|
||||
def after_insert(self):
|
||||
clear_desktop_icons_cache()
|
||||
|
||||
|
||||
def after_doctype_insert():
|
||||
frappe.db.add_unique("Desktop Icon", ("module_name", "owner", "standard"))
|
||||
pass
|
||||
|
||||
|
||||
# frappe.db.add_unique("Desktop Icon", ("owner", "standard"))
|
||||
|
||||
|
||||
def get_desktop_icons(user=None):
|
||||
|
|
@ -60,23 +103,19 @@ def get_desktop_icons(user=None):
|
|||
|
||||
if not user_icons:
|
||||
fields = [
|
||||
"module_name",
|
||||
"hidden",
|
||||
"label",
|
||||
"link",
|
||||
"type",
|
||||
"link_type",
|
||||
"icon_type",
|
||||
"parent_icon",
|
||||
"icon",
|
||||
"color",
|
||||
"description",
|
||||
"category",
|
||||
"_doctype",
|
||||
"_report",
|
||||
"link_to",
|
||||
"idx",
|
||||
"force_show",
|
||||
"reverse",
|
||||
"custom",
|
||||
"standard",
|
||||
"blocked",
|
||||
"logo_url",
|
||||
"hidden",
|
||||
"name",
|
||||
"sidebar",
|
||||
]
|
||||
|
||||
active_domains = frappe.get_active_domains()
|
||||
|
|
@ -92,6 +131,7 @@ def get_desktop_icons(user=None):
|
|||
standard_icons = frappe.get_all("Desktop Icon", fields=fields, filters={"standard": 1})
|
||||
|
||||
standard_map = {}
|
||||
|
||||
for icon in standard_icons:
|
||||
if icon._doctype in blocked_doctypes:
|
||||
icon.blocked = 1
|
||||
|
|
@ -141,12 +181,25 @@ def get_desktop_icons(user=None):
|
|||
user_icons.sort(key=lambda a: a.idx)
|
||||
|
||||
# translate
|
||||
for d in user_icons:
|
||||
if d.label:
|
||||
d.label = _(d.label, context=d.parent)
|
||||
# for d in user_icons:
|
||||
# if d.label:
|
||||
# d.label = _(d.label, context=d.parent)
|
||||
# includes
|
||||
permitted_icons = []
|
||||
permitted_parent_labels = set()
|
||||
|
||||
for s in user_icons:
|
||||
icon = frappe.get_doc("Desktop Icon", s)
|
||||
if icon.is_permitted():
|
||||
permitted_icons.append(s)
|
||||
|
||||
if not s.parent_icon:
|
||||
permitted_parent_labels.add(s.label)
|
||||
|
||||
user_icons = [
|
||||
s for s in permitted_icons if not s.parent_icon or s.parent_icon in permitted_parent_labels
|
||||
]
|
||||
frappe.cache.hset("desktop_icons", user, user_icons)
|
||||
|
||||
return user_icons
|
||||
|
||||
|
||||
|
|
@ -394,7 +447,19 @@ def make_user_copy(module_name, user):
|
|||
def sync_desktop_icons():
|
||||
"""Sync desktop icons from all apps"""
|
||||
for app in frappe.get_installed_apps():
|
||||
sync_from_app(app)
|
||||
sync_icons(app)
|
||||
# sync_from_app(app)
|
||||
|
||||
|
||||
def sync_icons(app_name):
|
||||
icon_directory = get_app_level_directory_path("desktop_icon", app_name)
|
||||
if os.path.exists(icon_directory):
|
||||
icon_files = [os.path.join(icon_directory, filename) for filename in os.listdir(icon_directory)]
|
||||
for doc_path in icon_files:
|
||||
imported = import_file_by_path(doc_path)
|
||||
if imported:
|
||||
frappe.db.commit(chain=True)
|
||||
# print(icon_directory)
|
||||
|
||||
|
||||
def sync_from_app(app):
|
||||
|
|
@ -477,6 +542,8 @@ def get_module_icons(user=None):
|
|||
|
||||
def get_user_icons(user):
|
||||
"""Get user icons for module setup page"""
|
||||
from frappe.utils.user import UserPermissions
|
||||
|
||||
user_perms = UserPermissions(user)
|
||||
user_perms.build_permissions()
|
||||
|
||||
|
|
@ -494,10 +561,10 @@ def get_user_icons(user):
|
|||
if icon.module_name == ["Help", "Settings"]:
|
||||
pass
|
||||
|
||||
elif icon.type == "page" and icon.link not in allowed_pages:
|
||||
elif icon.link_type == "page" and icon.link not in allowed_pages:
|
||||
add = False
|
||||
|
||||
elif icon.type == "module" and icon.module_name not in user_perms.allow_modules:
|
||||
elif icon.link_type == "module" and icon.module_name not in user_perms.allow_modules:
|
||||
add = False
|
||||
|
||||
if add:
|
||||
|
|
@ -570,3 +637,91 @@ def hide(name, user=None):
|
|||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def create_desktop_icons_from_workspace():
|
||||
workspaces = frappe.get_all(
|
||||
"Workspace",
|
||||
filters={"public": 1, "name": ["!=", "Welcome Workspace"]},
|
||||
fields=["name", "icon", "app", "module"],
|
||||
)
|
||||
|
||||
for w in workspaces:
|
||||
icon = frappe.new_doc("Desktop Icon")
|
||||
icon.link_type = "Workspace"
|
||||
icon.label = w.name
|
||||
icon.icon_type = "Link"
|
||||
icon.standard = 1
|
||||
icon.link_to = w.name
|
||||
icon.icon = w.icon
|
||||
if w.module:
|
||||
app_name = frappe.db.get_value("Module Def", w.module, "app_name")
|
||||
if app_name in frappe.get_installed_apps():
|
||||
app_title = frappe.get_hooks("app_title", app_name=app_name)[0]
|
||||
app_icon = frappe.db.exists("Desktop Icon", {"label": app_title, "icon_type": "App"})
|
||||
if app_icon:
|
||||
icon.parent_icon = app_icon
|
||||
|
||||
# Portal App With Desk Workspace
|
||||
if frappe.db.get_value("Desktop Icon", app_icon, "link") and not frappe.db.get_value(
|
||||
"Desktop Icon", app_icon, "link"
|
||||
).startswith("/app"):
|
||||
icon.hidden = 1
|
||||
icon.parent_icon = None
|
||||
|
||||
# If Desk App has one workspace with the same name
|
||||
if icon.label == app_title and (
|
||||
app_icon and frappe.db.get_value("Desktop Icon", app_icon, "link").startswith("/app")
|
||||
):
|
||||
icon.hidden = 1
|
||||
icon.parent_icon = None
|
||||
|
||||
try:
|
||||
if not frappe.db.exists(
|
||||
"Desktop Icon", [{"label": icon.label, "icon_type": icon.icon_type}]
|
||||
):
|
||||
icon.insert(ignore_if_duplicate=True)
|
||||
except Exception as e:
|
||||
frappe.error_log(title="Creation of Desktop Icon Failed", message=e)
|
||||
|
||||
|
||||
def generate_color():
|
||||
colors = ["orange", "pink", "blue", "green", "dark", "red", "yellow", "purple", "gray"]
|
||||
return random.choice(colors)
|
||||
|
||||
|
||||
def create_desktop_icons_from_installed_apps():
|
||||
apps = frappe.get_installed_apps()
|
||||
index = 0
|
||||
for a in apps:
|
||||
app_title = frappe.get_hooks("app_title", app_name=a)[0]
|
||||
app_details = frappe.get_hooks("add_to_apps_screen", app_name=a)
|
||||
if len(app_details) != 0:
|
||||
icon = frappe.new_doc("Desktop Icon")
|
||||
icon.label = app_title
|
||||
icon.link_type = "External"
|
||||
icon.standard = 1
|
||||
icon.idx = index
|
||||
icon.icon_type = "App"
|
||||
icon.link = app_details[0]["route"]
|
||||
icon.logo_url = app_details[0]["logo"]
|
||||
if not frappe.db.exists("Desktop Icon", [{"label": icon.label, "icon_type": icon.icon_type}]):
|
||||
icon.save()
|
||||
index += 1
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_sequence(desktop_icons):
|
||||
cnt = 1
|
||||
for item in json.loads(desktop_icons):
|
||||
frappe.db.set_value("Workspace", item.get("name"), "sequence_id", cnt)
|
||||
frappe.db.set_value("Workspace", item.get("name"), "parent_page", item.get("parent") or "")
|
||||
cnt += 1
|
||||
|
||||
frappe.clear_cache()
|
||||
frappe.toast(frappe._("Updated"))
|
||||
|
||||
|
||||
def create_desktop_icons():
|
||||
create_desktop_icons_from_installed_apps()
|
||||
create_desktop_icons_from_workspace()
|
||||
|
|
|
|||
20
frappe/desk/doctype/desktop_icon/test_desktop_icon.py
Normal file
20
frappe/desk/doctype/desktop_icon/test_desktop_icon.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
class IntegrationTestDesktopIcon(IntegrationTestCase):
|
||||
"""
|
||||
Integration tests for DesktopIcon.
|
||||
Use this class for testing interactions between multiple components.
|
||||
"""
|
||||
|
||||
pass
|
||||
0
frappe/desk/doctype/desktop_settings/__init__.py
Normal file
0
frappe/desk/doctype/desktop_settings/__init__.py
Normal file
8
frappe/desk/doctype/desktop_settings/desktop_settings.js
Normal file
8
frappe/desk/doctype/desktop_settings/desktop_settings.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2025, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Desktop Settings", {
|
||||
refresh(frm) {
|
||||
frm.add_custom_button(__("Visit Desktop"), () => frappe.set_route("desktop"));
|
||||
},
|
||||
});
|
||||
58
frappe/desk/doctype/desktop_settings/desktop_settings.json
Normal file
58
frappe/desk/doctype/desktop_settings/desktop_settings.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2025-09-07 17:00:48.624209",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"icon_style",
|
||||
"navbar_style",
|
||||
"show_app_icons_as_folder"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "Subtle Reverse",
|
||||
"fieldname": "icon_style",
|
||||
"fieldtype": "Select",
|
||||
"label": "Icon Style",
|
||||
"options": "Monochrome\nSubtle\nSubtle Reverse\nSubtle Reverse w Opacity"
|
||||
},
|
||||
{
|
||||
"fieldname": "navbar_style",
|
||||
"fieldtype": "Select",
|
||||
"label": "Navbar Style",
|
||||
"options": "Awesomebar\nmacOS Launchpad\nBrand Logo\nBrand Logo with Search\nTimeless Launchpad\nApps with Search"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_app_icons_as_folder",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show App Icons As Folder"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2025-11-15 11:38:34.968344",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Desktop Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
29
frappe/desk/doctype/desktop_settings/desktop_settings.py
Normal file
29
frappe/desk/doctype/desktop_settings/desktop_settings.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class DesktopSettings(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
icon_style: DF.Literal["Monochrome", "Subtle", "Subtle Reverse", "Subtle Reverse w Opacity"]
|
||||
navbar_style: DF.Literal[
|
||||
"Awesomebar",
|
||||
"macOS Launchpad",
|
||||
"Brand Logo",
|
||||
"Brand Logo with Search",
|
||||
"Timeless Launchpad",
|
||||
"Apps with Search",
|
||||
]
|
||||
show_app_icons_as_folder: DF.Check
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
class IntegrationTestDesktopSettings(IntegrationTestCase):
|
||||
"""
|
||||
Integration tests for DesktopSettings.
|
||||
Use this class for testing interactions between multiple components.
|
||||
"""
|
||||
|
||||
pass
|
||||
0
frappe/desk/doctype/sidebar_item_group/__init__.py
Normal file
0
frappe/desk/doctype/sidebar_item_group/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2025, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
// frappe.ui.form.on("Sidebar Item Group", {
|
||||
// refresh(frm) {
|
||||
|
||||
// },
|
||||
// });
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:sidebar",
|
||||
"creation": "2025-11-10 12:49:52.421973",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"sidebar",
|
||||
"links",
|
||||
"app"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "sidebar",
|
||||
"fieldtype": "Link",
|
||||
"label": "Sidebar",
|
||||
"options": "Workspace Sidebar",
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "links",
|
||||
"fieldtype": "Table",
|
||||
"label": "Links",
|
||||
"options": "Sidebar Item Group Link"
|
||||
},
|
||||
{
|
||||
"fieldname": "app",
|
||||
"fieldtype": "Autocomplete",
|
||||
"label": "App",
|
||||
"options": "Installed Applications"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-11-13 10:03:42.852599",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Sidebar Item Group",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"rows_threshold_for_grid_search": 20,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
58
frappe/desk/doctype/sidebar_item_group/sidebar_item_group.py
Normal file
58
frappe/desk/doctype/sidebar_item_group/sidebar_item_group.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import os
|
||||
|
||||
import frappe
|
||||
from frappe.boot import get_allowed_pages, get_allowed_reports
|
||||
from frappe.model.document import Document
|
||||
from frappe.modules.utils import create_directory_on_app_path, get_app_level_directory_path
|
||||
|
||||
|
||||
class SidebarItemGroup(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.desk.doctype.sidebar_item_group_link.sidebar_item_group_link import SidebarItemGroupLink
|
||||
from frappe.types import DF
|
||||
|
||||
app: DF.Autocomplete | None
|
||||
links: DF.Table[SidebarItemGroupLink]
|
||||
sidebar: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
def on_update(self):
|
||||
if frappe.conf.developer_mode:
|
||||
if self.app:
|
||||
self.export_sidebar_item_group()
|
||||
|
||||
def export_sidebar_item_group(self):
|
||||
folder_path = create_directory_on_app_path("sidebar_item_group", self.app)
|
||||
file_path = os.path.join(folder_path, f"{frappe.scrub(self.name)}.json")
|
||||
doc_export = self.as_dict(no_nulls=True, no_private_properties=True)
|
||||
with open(file_path, "w+") as doc_file:
|
||||
doc_file.write(frappe.as_json(doc_export) + "\n")
|
||||
|
||||
def on_trash(self):
|
||||
if frappe.conf.developer_mode and self.app:
|
||||
self.delete_file()
|
||||
|
||||
def delete_file(self):
|
||||
folder_path = get_app_level_directory_path("sidebar_item_group", self.app)
|
||||
file_path = os.path.join(folder_path, f"{frappe.scrub(self.name)}.json")
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_reports(module_name=None):
|
||||
reports_info = []
|
||||
if module_name:
|
||||
sidebar_group = frappe.get_doc("Sidebar Item Group", module_name)
|
||||
for report_links in sidebar_group.links:
|
||||
if report_links.report in get_allowed_reports().keys():
|
||||
reports_info.append(get_allowed_reports()[report_links.report])
|
||||
return reports_info
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
class IntegrationTestSidebarItemGroup(IntegrationTestCase):
|
||||
"""
|
||||
Integration tests for SidebarItemGroup.
|
||||
Use this class for testing interactions between multiple components.
|
||||
"""
|
||||
|
||||
pass
|
||||
0
frappe/desk/doctype/sidebar_item_group_link/__init__.py
Normal file
0
frappe/desk/doctype/sidebar_item_group_link/__init__.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2025-11-13 10:02:13.869571",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"report"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "report",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Report",
|
||||
"options": "Report"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-11-13 10:03:51.263771",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Sidebar Item Group Link",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"row_format": "Dynamic",
|
||||
"rows_threshold_for_grid_search": 20,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class SidebarItemGroupLink(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
report: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
|
@ -6,7 +6,9 @@ from json import loads
|
|||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.boot import get_sidebar_items
|
||||
from frappe.desk.desktop import get_workspace_sidebar_items, save_new_widget
|
||||
from frappe.desk.doctype.workspace_sidebar.workspace_sidebar import add_to_my_workspace
|
||||
from frappe.desk.utils import validate_route_conflict
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.rename_doc import rename_doc
|
||||
|
|
@ -125,6 +127,14 @@ class Workspace(Document):
|
|||
def on_trash(self):
|
||||
if self.public and not is_workspace_manager():
|
||||
frappe.throw(_("You need to be Workspace Manager to delete a public workspace."))
|
||||
self.delete_from_my_workspaces()
|
||||
|
||||
def delete_from_my_workspaces(self):
|
||||
if not self.public:
|
||||
my_workspaces = frappe.get_doc("Workspace Sidebar", "My Workspaces")
|
||||
for w in my_workspaces.items:
|
||||
if self.name == w.link_to:
|
||||
frappe.delete_doc("Workspace Sidebar Item", w.name)
|
||||
|
||||
def after_delete(self):
|
||||
if disable_saving_as_public():
|
||||
|
|
@ -294,7 +304,10 @@ def new_page(new_page):
|
|||
doc.sequence_id = last_sequence_id(doc) + 1
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
return get_workspace_sidebar_items()
|
||||
# add to workspace sidebar items
|
||||
if not doc.public:
|
||||
add_to_my_workspace(doc)
|
||||
return {"workspace_pages": get_workspace_sidebar_items(), "sidebar_items": get_sidebar_items()}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@ frappe.ui.form.on("Workspace Settings", {
|
|||
frm.docfields.push({
|
||||
fieldtype: "Check",
|
||||
fieldname: page.name,
|
||||
hidden: !frappe.boot.app_data_map[frappe.current_app].workspaces.includes(
|
||||
page.title
|
||||
),
|
||||
label: page.title + (page.parent_page ? ` (${page.parent_page})` : ""),
|
||||
initial_value: workspace_visibilty[page.name] !== 0, // not set is also visible
|
||||
});
|
||||
|
|
|
|||
0
frappe/desk/doctype/workspace_sidebar/__init__.py
Normal file
0
frappe/desk/doctype/workspace_sidebar/__init__.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
class IntegrationTestWorkspaceSidebar(IntegrationTestCase):
|
||||
"""
|
||||
Integration tests for WorkspaceSidebar.
|
||||
Use this class for testing interactions between multiple components.
|
||||
"""
|
||||
|
||||
pass
|
||||
18
frappe/desk/doctype/workspace_sidebar/workspace_sidebar.js
Normal file
18
frappe/desk/doctype/workspace_sidebar/workspace_sidebar.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2025, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Workspace Sidebar", {
|
||||
refresh(frm) {
|
||||
if (!frm.is_new()) {
|
||||
frm.add_custom_button(__(`View Sidebar`), () => {
|
||||
if (frm.doc.items[0].link_type === "DocType") {
|
||||
frappe.set_route("List", frm.doc.items[0].link_to);
|
||||
return;
|
||||
} else if (frm.doc.items[0].link_type === "Workspace") {
|
||||
frappe.set_route("Workspaces", frm.doc.items[0].link_to);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
80
frappe/desk/doctype/workspace_sidebar/workspace_sidebar.json
Normal file
80
frappe/desk/doctype/workspace_sidebar/workspace_sidebar.json
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:title",
|
||||
"creation": "2025-08-12 12:06:45.016314",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"header_icon",
|
||||
"app",
|
||||
"for_user",
|
||||
"items",
|
||||
"module"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Title",
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Items",
|
||||
"options": "Workspace Sidebar Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "header_icon",
|
||||
"fieldtype": "Icon",
|
||||
"label": "Header Icon"
|
||||
},
|
||||
{
|
||||
"fieldname": "app",
|
||||
"fieldtype": "Autocomplete",
|
||||
"label": "App",
|
||||
"options": "Installed Applications"
|
||||
},
|
||||
{
|
||||
"fieldname": "for_user",
|
||||
"fieldtype": "Link",
|
||||
"label": "For User",
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"label": "Module"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-11-17 01:17:20.583501",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Workspace Sidebar",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
240
frappe/desk/doctype/workspace_sidebar/workspace_sidebar.py
Normal file
240
frappe/desk/doctype/workspace_sidebar/workspace_sidebar.py
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import os
|
||||
from json import JSONDecodeError, dumps, loads
|
||||
|
||||
import click
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.boot import get_allowed_pages, get_allowed_reports
|
||||
from frappe.model.document import Document
|
||||
from frappe.modules.utils import create_directory_on_app_path
|
||||
|
||||
|
||||
class WorkspaceSidebar(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.desk.doctype.workspace_sidebar_item.workspace_sidebar_item import WorkspaceSidebarItem
|
||||
from frappe.types import DF
|
||||
|
||||
app: DF.Autocomplete | None
|
||||
for_user: DF.Link | None
|
||||
items: DF.Table[WorkspaceSidebarItem]
|
||||
module: DF.Text | None
|
||||
title: DF.Data | None
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not frappe.flags.in_migrate:
|
||||
self.user = frappe.get_user()
|
||||
self.can_read = self.get_cached("user_perm_can_read", self.get_can_read_items)
|
||||
|
||||
self.allowed_pages = get_allowed_pages(cache=True)
|
||||
self.allowed_reports = get_allowed_reports(cache=True)
|
||||
self.restricted_doctypes = frappe.cache.get_value("domain_restricted_doctypes")
|
||||
self.restricted_pages = frappe.cache.get_value("domain_restricted_pages")
|
||||
|
||||
def get_can_read_items(self):
|
||||
if not self.user.can_read:
|
||||
self.user.build_permissions()
|
||||
|
||||
def before_save(self):
|
||||
if frappe.conf.developer_mode:
|
||||
if self.app:
|
||||
self.export_sidebar()
|
||||
self.set_module()
|
||||
|
||||
def export_sidebar(self):
|
||||
folder_path = create_directory_on_app_path("workspace_sidebar", self.app)
|
||||
file_path = os.path.join(folder_path, f"{frappe.scrub(self.title)}.json")
|
||||
doc_export = self.as_dict(no_nulls=True, no_private_properties=True)
|
||||
with open(file_path, "w+") as doc_file:
|
||||
doc_file.write(frappe.as_json(doc_export) + "\n")
|
||||
|
||||
def delete_file(self):
|
||||
folder_path = create_directory_on_app_path("workspace_sidebar", self.app)
|
||||
file_path = os.path.join(folder_path, f"{frappe.scrub(self.title)}.json")
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
def on_trash(self):
|
||||
if is_workspace_manager():
|
||||
if frappe.conf.developer_mode and self.app:
|
||||
self.delete_file()
|
||||
else:
|
||||
frappe.throw(_("You need to be Workspace Manager to delete a public workspace."))
|
||||
|
||||
def is_item_allowed(self, name, item_type):
|
||||
if frappe.session.user == "Administrator":
|
||||
return True
|
||||
|
||||
item_type = item_type.lower()
|
||||
|
||||
if item_type == "doctype":
|
||||
return (
|
||||
name in (self.can_read or [])
|
||||
and name in (self.restricted_doctypes or [])
|
||||
and frappe.has_permission(name)
|
||||
)
|
||||
if item_type == "page":
|
||||
return name in self.allowed_pages and name in self.restricted_pages
|
||||
if item_type == "report":
|
||||
return name in self.allowed_reports
|
||||
if item_type == "help":
|
||||
return True
|
||||
if item_type == "dashboard":
|
||||
return True
|
||||
if item_type == "url":
|
||||
return True
|
||||
|
||||
def get_cached(self, cache_key, fallback_fn):
|
||||
value = frappe.cache.get_value(cache_key, user=frappe.session.user)
|
||||
if value is not None:
|
||||
return value
|
||||
|
||||
value = fallback_fn()
|
||||
|
||||
# Expire every six hour
|
||||
frappe.cache.set_value(cache_key, value, frappe.session.user, 21600)
|
||||
return value
|
||||
|
||||
def set_module(self):
|
||||
if not self.module:
|
||||
self.module = self.get_module_from_items()
|
||||
|
||||
def get_module_from_items(self):
|
||||
all_modules_in_sidebars = []
|
||||
|
||||
for item in self.items:
|
||||
if item.type != "Section Break" and item.type != "Sidebar Item Group" and item.link_type != "URL":
|
||||
try:
|
||||
all_modules_in_sidebars.append(frappe.get_doc(item.link_type, item.link_to).module)
|
||||
except frappe.DoesNotExistError as e:
|
||||
frappe.logger().error(e)
|
||||
from collections import Counter
|
||||
|
||||
counts = Counter(all_modules_in_sidebars)
|
||||
if counts and counts.most_common(1)[0]:
|
||||
return counts.most_common(1)[0][0]
|
||||
|
||||
|
||||
def is_workspace_manager():
|
||||
return "Workspace Manager" in frappe.get_roles()
|
||||
|
||||
|
||||
def create_workspace_sidebar_for_workspaces():
|
||||
from frappe.query_builder import DocType
|
||||
|
||||
workspace = DocType("Workspace")
|
||||
|
||||
all_workspaces = (
|
||||
frappe.qb.from_(workspace)
|
||||
.select(workspace.name)
|
||||
.where((workspace.public == 1) & (workspace.name != "Welcome Workspace"))
|
||||
).run(pluck=True)
|
||||
|
||||
existing_sidebars = frappe.get_all("Workspace Sidebar", pluck="title")
|
||||
for workspace in all_workspaces:
|
||||
if workspace not in existing_sidebars:
|
||||
workspace_doc = frappe.get_doc("Workspace", workspace)
|
||||
sidebar = frappe.new_doc("Workspace Sidebar")
|
||||
sidebar.title = workspace
|
||||
sidebar.header_icon = frappe.db.get_value("Workspace", workspace, "icon")
|
||||
click.echo(f"Creating Sidebar Items for {workspace}")
|
||||
shortcuts = workspace_doc.shortcuts
|
||||
|
||||
items = []
|
||||
idx = 1
|
||||
# Adding the workspace itself as home
|
||||
workspace_sidebar_item = frappe.new_doc("Workspace Sidebar Item")
|
||||
workspace_sidebar_item.update(
|
||||
{"label": "Home", "link_to": workspace, "link_type": "Workspace", "type": "Link", "idx": 0}
|
||||
)
|
||||
items.append(workspace_sidebar_item)
|
||||
# Process Shortcuts
|
||||
for s in shortcuts:
|
||||
workspace_sidebar_item = frappe.new_doc("Workspace Sidebar Item")
|
||||
workspace_sidebar_item.update(
|
||||
{"label": s.label, "link_to": s.link_to, "link_type": s.type, "type": "Link", "idx": idx}
|
||||
)
|
||||
items.append(workspace_sidebar_item)
|
||||
idx += 1
|
||||
try:
|
||||
sidebar.items = items
|
||||
sidebar.save()
|
||||
except Exception as e:
|
||||
frappe.log_error(title="Failed To Create Sidebar", message=e)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_sidebar_items(sidebar_title, sidebar_items):
|
||||
sidebar_items = loads(sidebar_items)
|
||||
w = frappe.get_doc("Workspace Sidebar", sidebar_title)
|
||||
items = []
|
||||
current_idx = 1
|
||||
for item in sidebar_items:
|
||||
si = frappe.new_doc("Workspace Sidebar Item")
|
||||
si.update(item)
|
||||
items.append(si)
|
||||
si.idx = current_idx
|
||||
items.append(si)
|
||||
current_idx += 1
|
||||
|
||||
nested_items = item.get("nested_items", [])
|
||||
if nested_items:
|
||||
for nested_item in nested_items:
|
||||
new_nested_item = frappe.new_doc("Workspace Sidebar Item")
|
||||
new_nested_item.update(nested_item)
|
||||
new_nested_item.child = 1
|
||||
new_nested_item.idx = current_idx
|
||||
items.append(new_nested_item)
|
||||
current_idx += 1
|
||||
|
||||
w.items = items
|
||||
w.save()
|
||||
return w
|
||||
|
||||
|
||||
def add_to_my_workspace(workspace):
|
||||
try:
|
||||
if not workspace.for_user:
|
||||
return
|
||||
|
||||
sidebar_name = f"My Workspaces-{workspace.for_user}"
|
||||
existing_sidebar = frappe.db.exists("Workspace Sidebar", sidebar_name)
|
||||
|
||||
if existing_sidebar:
|
||||
private_sidebar = frappe.get_doc("Workspace Sidebar", existing_sidebar)
|
||||
else:
|
||||
# clone sidebar
|
||||
base_sidebar = frappe.get_doc("Workspace Sidebar", "My Workspaces")
|
||||
private_sidebar = frappe.copy_doc(base_sidebar)
|
||||
private_sidebar.title = sidebar_name
|
||||
private_sidebar.for_user = workspace.for_user
|
||||
private_sidebar.owner = workspace.for_user
|
||||
private_sidebar.items = []
|
||||
|
||||
sidebar_item = {
|
||||
"label": workspace.title,
|
||||
"type": "Link",
|
||||
"link_to": f"{workspace.title}-{workspace.for_user}",
|
||||
"link_type": "Workspace",
|
||||
"icon": workspace.icon,
|
||||
}
|
||||
|
||||
private_sidebar.append("items", sidebar_item)
|
||||
|
||||
if existing_sidebar:
|
||||
private_sidebar.save()
|
||||
else:
|
||||
private_sidebar.insert()
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(title="Error in Adding Private Workspaces", message=e)
|
||||
0
frappe/desk/doctype/workspace_sidebar_item/__init__.py
Normal file
0
frappe/desk/doctype/workspace_sidebar_item/__init__.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2025-08-12 12:46:41.926121",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"details_section",
|
||||
"label",
|
||||
"link_type",
|
||||
"icon",
|
||||
"column_break_krzu",
|
||||
"type",
|
||||
"link_to",
|
||||
"child",
|
||||
"url",
|
||||
"display_section",
|
||||
"collapsible_column",
|
||||
"collapsible",
|
||||
"indent",
|
||||
"keep_closed",
|
||||
"show_arrow",
|
||||
"column_break_jexf",
|
||||
"display_depends_on",
|
||||
"section_break_whjq",
|
||||
"filters",
|
||||
"route_options"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "Link",
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"options": "Link\nSection Break\nSpacer\nSidebar Item Group"
|
||||
},
|
||||
{
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Label"
|
||||
},
|
||||
{
|
||||
"default": "DocType",
|
||||
"depends_on": "eval: doc.type == 'Link' || doc.indent == 1",
|
||||
"fieldname": "link_type",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Link Type",
|
||||
"options": "DocType\nPage\nReport\nWorkspace\nDashboard\nURL"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.link_type != \"URL\" && doc.type == 'Link' || doc.indent == 1",
|
||||
"fieldname": "link_to",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Link To",
|
||||
"options": "link_type"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.type == \"Link\"",
|
||||
"fieldname": "icon",
|
||||
"fieldtype": "Icon",
|
||||
"in_list_view": 1,
|
||||
"label": "Icon",
|
||||
"options": "Emojis"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "child",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Child Item"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.type == \"Section Break\"",
|
||||
"fieldname": "indent",
|
||||
"fieldtype": "Check",
|
||||
"label": "Indent"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval: doc.type == \"Section Break\"",
|
||||
"fieldname": "collapsible",
|
||||
"fieldtype": "Check",
|
||||
"label": "Collapsible"
|
||||
},
|
||||
{
|
||||
"fieldname": "details_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Details"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_krzu",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.type == \"Section Break\"",
|
||||
"fieldname": "display_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Display"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.type == \"Section Break\"",
|
||||
"fieldname": "collapsible_column",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Collapsible"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.type == \"Section Break\"",
|
||||
"fieldname": "keep_closed",
|
||||
"fieldtype": "Check",
|
||||
"label": "Keep Closed"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_jexf",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "display_depends_on",
|
||||
"fieldtype": "Code",
|
||||
"label": "Display Depends On (JS)",
|
||||
"options": "JS",
|
||||
"width": "30"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.link_type == \"URL\"",
|
||||
"fieldname": "url",
|
||||
"fieldtype": "Data",
|
||||
"label": "URL"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.indent == 1",
|
||||
"fieldname": "show_arrow",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Arrow"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_whjq",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "filters",
|
||||
"fieldtype": "Code",
|
||||
"label": "Filters",
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "route_options",
|
||||
"fieldtype": "Code",
|
||||
"label": "Route Options",
|
||||
"options": "JSON"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-11-13 10:30:13.048477",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Workspace Sidebar Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Copyright (c) 2025, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class WorkspaceSidebarItem(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
child: DF.Check
|
||||
collapsible: DF.Check
|
||||
display_depends_on: DF.Code | None
|
||||
filters: DF.Code | None
|
||||
indent: DF.Check
|
||||
keep_closed: DF.Check
|
||||
label: DF.Data | None
|
||||
link_to: DF.DynamicLink | None
|
||||
link_type: DF.Literal["DocType", "Page", "Report", "Workspace", "Dashboard", "URL"]
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
route_options: DF.Code | None
|
||||
show_arrow: DF.Check
|
||||
type: DF.Literal["Link", "Section Break", "Spacer", "Sidebar Item Group"]
|
||||
url: DF.Data | None
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"aggregate_function_based_on": "",
|
||||
"color": "#ff0000",
|
||||
"creation": "2025-08-28 16:53:16.406273",
|
||||
"currency": "",
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Activity Log",
|
||||
"dynamic_filters_json": "[]",
|
||||
"filters_json": "[[\"Activity Log\",\"status\",\"=\",\"Failed\",false]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
"is_public": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Failed Login Attempts",
|
||||
"modified": "2025-08-31 19:21:55.040453",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Failed Login Attempts",
|
||||
"owner": "Administrator",
|
||||
"parent_document_type": "",
|
||||
"report_function": "Sum",
|
||||
"show_full_number": 0,
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Weekly",
|
||||
"type": "Document Type"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue