diff --git a/jey_layout/api.py b/jey_layout/api.py index 45591dc..b0b5a4c 100644 --- a/jey_layout/api.py +++ b/jey_layout/api.py @@ -245,9 +245,7 @@ def save_config(config): config = json.loads(config) cfg = _validate_config(config) _write(desk_layout.working_path(), cfg) - # Drop the per-request cache so a follow-up preview request reflects the save. - if hasattr(frappe.local, desk_layout._CACHE_ATTR): - delattr(frappe.local, desk_layout._CACHE_ATTR) + _bust_caches() # so a follow-up request reflects the save (hide/block + structural) return {"ok": True} @@ -277,6 +275,50 @@ def reset_to_default(): _guard() cfg = desk_layout._read_file(desk_layout.snapshot_path()) _write(desk_layout.working_path(), _validate_config(cfg or {"workspaces": {}})) - if hasattr(frappe.local, desk_layout._CACHE_ATTR): - delattr(frappe.local, desk_layout._CACHE_ATTR) + _bust_caches() return {"ok": True} + + +def _bust_caches(): + for attr in (desk_layout._CACHE_ATTR, "_jey_layout_raw"): + if hasattr(frappe.local, attr): + delattr(frappe.local, attr) + + +@frappe.whitelist() +def blocked_doctypes_list(): + """Current global block list — used by the DocType list-view extension.""" + _guard() + cfg = _read_working() or {} + return [d for d in (cfg.get("blocked_doctypes") or []) if isinstance(d, str)] + + +@frappe.whitelist() +def block_doctypes(names): + return _toggle_blocked(names, True) + + +@frappe.whitelist() +def unblock_doctypes(names): + return _toggle_blocked(names, False) + + +def _toggle_blocked(names, block: bool): + """Add/remove doctypes from the global block list (read-modify-write).""" + _guard() + if isinstance(names, str): + names = frappe.parse_json(names) + names = [n for n in (names or []) if isinstance(n, str) and n] + cfg = _read_working() or {"version": 1, "workspaces": {}} + cur = [n for n in (cfg.get("blocked_doctypes") or []) if isinstance(n, str)] + if block: + for n in names: + if n not in cur: + cur.append(n) + else: + drop = set(names) + cur = [n for n in cur if n not in drop] + cfg["blocked_doctypes"] = cur + _write(desk_layout.working_path(), _validate_config(cfg)) + _bust_caches() + return {"blocked_doctypes": cur} diff --git a/jey_layout/public/js/jey_layout.js b/jey_layout/public/js/jey_layout.js index d63ab24..6b8d8d9 100644 --- a/jey_layout/public/js/jey_layout.js +++ b/jey_layout/public/js/jey_layout.js @@ -35,6 +35,7 @@ let hb_timer = null; let decorating = false; let saved_timer = null; + let blocked_set = new Set(); // current global blocked_doctypes (for the DocType list) function is_admin() { return frappe.user && frappe.user.has_role && frappe.user.has_role("System Manager"); @@ -62,6 +63,8 @@ mount_fab(); update_fab(); update_banner(); + install_doctype_list_actions(); + ensure_doctype_list_extras(); // in case we loaded straight onto the DocType list if (editing) { $("body").addClass("jey-edit"); @@ -70,6 +73,8 @@ } frappe.router.on("change", () => { + update_banner(); // hide/show per page (don't cover toolbars off the desk) + ensure_doctype_list_extras(); // re-add buttons when (re)entering the DocType list if (editing) decorate(); }); } @@ -92,62 +97,130 @@ `).appendTo("body"); $fab.find(".jey-fab-btn").on("click", toggle_edit); - $fab.find('[data-act="block"]').on("click", open_block_doctypes_dialog); + $fab.find('[data-act="block"]').on("click", open_block_doctypes); $fab.find('[data-act="export"]').on("click", do_export); $fab.find('[data-act="reset"]').on("click", do_reset); } - // Global block list — block any doctype, including ones not present on /desk. - function open_block_doctypes_dialog() { - let list = (config.blocked_doctypes || []).slice(); - const d = new frappe.ui.Dialog({ - title: __("Block DocTypes (anywhere, incl. not on the desk)"), - fields: [ - { fieldname: "add", label: __("Add a DocType to block"), fieldtype: "Link", options: "DocType" }, - { fieldname: "list_html", fieldtype: "HTML" }, - ], - primary_action_label: __("Save"), - primary_action: async () => { - if (list.length) config.blocked_doctypes = list; - else delete config.blocked_doctypes; - await save_now(); - d.hide(); - frappe.show_alert( - { message: __("Saved — “✓ Done editing” to apply the block"), indicator: "green" }, - 5 - ); + // Block DocTypes via the REAL /app/doctype list (native search/filters/checkboxes). + function open_block_doctypes() { + frappe.set_route("List", "DocType"); + frappe.show_alert( + { + message: __("Tick DocTypes, then “Jey Layout → Block selected”. Leave edit mode to apply."), + indicator: "blue", }, + 7 + ); + } + + // ---- DocType list-view extension (installed once; extends, never overwrites core) ---- + async function refresh_blocked_set() { + const list = await xcall("blocked_doctypes_list"); + blocked_set = new Set(list || []); + } + + async function jey_block_selected(listview, block) { + const names = listview.get_checked_items(true) || []; + if (!names.length) { + frappe.msgprint(__("Select one or more DocTypes (row checkboxes) first.")); + return; + } + await xcall(block ? "block_doctypes" : "unblock_doctypes", { names: JSON.stringify(names) }); + await refresh_blocked_set(); + frappe.show_alert({ + message: block + ? __("Blocked {0} DocType(s) — leave edit mode to apply", [names.length]) + : __("Unblocked {0} DocType(s)", [names.length]), + indicator: block ? "red" : "blue", }); - const render = () => { - const $w = d.fields_dict.list_html.$wrapper; - $w.empty(); - if (!list.length) { - $w.html(`