fix(sidebar): render fallback sidebar when no workspace matches route
Frappe v16's Sidebar.set_workspace_sidebar() leaves .sidebar-items empty when a List/Form route's doctype is referenced by 2+ workspace sidebars but none is the doctype's module home workspace — it falls through without calling setup(). Symptom: sidebar goes blank on hard reload (e.g. after clear-cache/migrate/build) and "self-heals" only via in-app navigation. Repro: hard-reload /app/price-list (module Stock, linked only from the Selling and Buying sidebars). Patch the prototype so that if the items container is still empty after the core method runs, it falls back to the first sidebar referencing the route's doctype, else the route's module home workspace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1c9c67f5d9
commit
c65eeb741a
|
|
@ -2499,3 +2499,79 @@
|
|||
init();
|
||||
}
|
||||
})();
|
||||
|
||||
/* ==========================================================================
|
||||
Sidebar empty-state fix (works around a Frappe v16 core bug)
|
||||
|
||||
frappe.ui.Sidebar.set_workspace_sidebar() decides which workspace sidebar to
|
||||
render for a List/Form route. When the route's doctype is referenced by MORE
|
||||
THAN ONE workspace sidebar but none of them is the doctype's own module
|
||||
workspace, the core method falls through without calling setup() — leaving
|
||||
.sidebar-items empty (no items, not even the "No Sidebar Items" placeholder,
|
||||
because create_sidebar() never runs). It appears to "self-heal" only because
|
||||
navigating in from a matching workspace sets this.sidebar_title, which an
|
||||
early return in the method then preserves; a hard reload (e.g. after
|
||||
clear-cache / migrate / build) loses that state and the sidebar goes blank.
|
||||
|
||||
Repro: hard-reload on /app/price-list. Price List's module is "Stock", but it
|
||||
is only linked from the "Selling" and "Buying" sidebars, so
|
||||
get_workspace_for_module("Stock") yields "Stock" — which is not in
|
||||
["Selling","Buying"] — so setup() is never called.
|
||||
|
||||
Fix: after the core method runs, if the items container is still empty, fall
|
||||
back to the first sidebar that references the route's doctype, otherwise to
|
||||
the route's module home workspace. The fallback calls setup() directly (never
|
||||
set_workspace_sidebar), so it can't recurse; an empty workspace still renders
|
||||
the "No Sidebar Items" placeholder (one child), so isEmpty stays false.
|
||||
========================================================================== */
|
||||
(function () {
|
||||
function patch() {
|
||||
if (
|
||||
!window.frappe ||
|
||||
!frappe.ui ||
|
||||
!frappe.ui.Sidebar ||
|
||||
!frappe.ui.Sidebar.prototype ||
|
||||
frappe.ui.Sidebar.prototype.__jeySidebarFallbackPatched
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var proto = frappe.ui.Sidebar.prototype;
|
||||
var original = proto.set_workspace_sidebar;
|
||||
proto.__jeySidebarFallbackPatched = true;
|
||||
|
||||
proto.set_workspace_sidebar = function (router) {
|
||||
original.call(this, router);
|
||||
try {
|
||||
var container = this.$items_container;
|
||||
if (container && container.children().length > 0) return;
|
||||
|
||||
// Fallback 1: first sidebar that references the current doctype.
|
||||
if (this.preferred_sidebars && this.preferred_sidebars.length) {
|
||||
this.setup(this.preferred_sidebars[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback 2: the home workspace for the route's module.
|
||||
var module = router && router.meta && router.meta.module;
|
||||
var workspace = module && this.get_workspace_for_module(module);
|
||||
if (workspace && frappe.boot.workspace_sidebar_item[workspace.toLowerCase()]) {
|
||||
this.setup(workspace);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[jey_theme] sidebar fallback failed", e);
|
||||
}
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
// frappe.ui.Sidebar may not be defined yet when this file runs; retry until
|
||||
// the desk bundle has loaded. Patching the prototype affects the already
|
||||
// created frappe.app.sidebar instance too, since lookup happens at call time.
|
||||
if (!patch()) {
|
||||
var tries = 0;
|
||||
var timer = setInterval(function () {
|
||||
if (patch() || ++tries > 100) clearInterval(timer);
|
||||
}, 50);
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
Loading…
Reference in New Issue