From bae6c16a6b19fb8732e31689ad36844aa2d0a533 Mon Sep 17 00:00:00 2001
From: Ali <010109ali@gmail.com>
Date: Thu, 16 Apr 2026 09:49:27 +0000
Subject: [PATCH] bug fixes and qol changes
---
jey_theme/public/css/shared.css | 124 ++++++++++++++++-
jey_theme/public/js/jey_theme.js | 232 +++++++++++++++++++++++++++++++
2 files changed, 351 insertions(+), 5 deletions(-)
diff --git a/jey_theme/public/css/shared.css b/jey_theme/public/css/shared.css
index c33c331..775a75f 100644
--- a/jey_theme/public/css/shared.css
+++ b/jey_theme/public/css/shared.css
@@ -253,7 +253,8 @@ input.list-header-checkbox:checked::after {
Sidebar — Full Width button
========================================================================== */
-.jey-fullwidth-link {
+.jey-fullwidth-link,
+.jey-theme-switch-link {
text-decoration: none;
font-size: var(--text-sm);
display: flex;
@@ -262,18 +263,28 @@ input.list-header-checkbox:checked::after {
margin-bottom: 8px;
}
-.jey-fullwidth-link svg {
+.jey-fullwidth-link svg,
+.jey-theme-switch-link svg {
margin: 0;
flex: 0 0 auto;
}
-.jey-fullwidth-link span {
+.jey-fullwidth-link span,
+.jey-theme-switch-link span {
margin-left: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+/* Hide dropdown items now surfaced as sidebar buttons */
+.dropdown-menu-item[onclick*="toggle_full_width"],
+.dropdown-menu-item[onclick*="ThemeSwitcher"],
+.dropdown-menu-item[data-name="website"],
+.dropdown-menu-item.jey-hidden-item {
+ display: none !important;
+}
+
/* ==========================================================================
Desktop Icons — Shared base (all themes)
@@ -467,7 +478,14 @@ input.list-header-checkbox:checked::after {
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 0.35s ease;
position: relative;
- overflow: hidden;
+}
+
+/* Allow dropdown menus (card actions) to escape the widget box.
+ Without this, .widget-control .dropdown-menu is clipped/hidden behind
+ the widget because Popper renders it as a sibling inside the box. */
+.ce-block .links-widget-box .widget-control .dropdown-menu,
+.ce-block .number-widget-box .widget-control .dropdown-menu {
+ z-index: 1050;
}
/* Stagger delays */
@@ -503,7 +521,7 @@ input.list-header-checkbox:checked::after {
}
to {
opacity: 1;
- transform: translateY(0) scale(1);
+ transform: none;
}
}
@@ -535,6 +553,18 @@ input.list-header-checkbox:checked::after {
0 2px 6px rgba(0, 0, 0, 0.04);
}
+/* Lift widget above its siblings while hovered or while its card-actions
+ dropdown is open, so the dropdown menu (which lives inside the widget's
+ own stacking context due to transform) is not covered by neighbours. */
+.ce-block .links-widget-box:hover,
+.ce-block .number-widget-box:hover,
+.ce-block .links-widget-box:focus-within,
+.ce-block .number-widget-box:focus-within,
+.ce-block .links-widget-box:has(.dropdown-menu.show),
+.ce-block .number-widget-box:has(.dropdown-menu.show) {
+ z-index: 100;
+}
+
/* Link items: colored left accent bar slides in */
.links-widget-box .link-item {
@@ -604,6 +634,90 @@ input.list-header-checkbox:checked::after {
background-position: center;
}
+/* ==========================================================================
+ Notification Bell — Top-right position
+ ========================================================================== */
+
+/* Hide original notification button in sidebar */
+.sidebar-notification {
+ display: none !important;
+}
+
+/* Fixed wrapper */
+.jey-topright-wrapper {
+ position: fixed;
+ top: 16px;
+ right: 24px;
+ z-index: 1039;
+}
+
+/* Bell button */
+.jey-topright-bell {
+ position: relative;
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: var(--card-bg, #fff);
+ border: 1px solid var(--border-color, #e2e2e2);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ box-shadow: 0 1px 4px rgba(0,0,0,0.08);
+ transition: box-shadow 0.2s, background 0.2s;
+ color: var(--text-muted, #74808b);
+ padding: 0;
+}
+
+.jey-topright-bell:hover {
+ background: var(--subtle-fg, #f5f5f5);
+ box-shadow: 0 2px 8px rgba(0,0,0,0.12);
+ color: var(--text-color, #333);
+}
+
+/* Unread count badge */
+.jey-notif-badge {
+ position: absolute;
+ top: -4px;
+ right: -4px;
+ background: var(--red-500, #e53e3e);
+ color: #fff;
+ font-size: 10px;
+ font-weight: 600;
+ min-width: 16px;
+ height: 16px;
+ line-height: 16px;
+ border-radius: 8px;
+ text-align: center;
+ padding: 0 4px;
+ pointer-events: none;
+}
+
+/* Notification dropdown panel */
+.jey-topright-wrapper .dropdown-notifications {
+ display: block;
+ position: absolute;
+ top: 44px;
+ right: 0;
+ width: 360px;
+ border-radius: 12px;
+ box-shadow: 0 8px 24px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.04);
+ background: var(--card-bg, #fff);
+ border: 1px solid var(--border-color, #e2e2e2);
+ max-height: 70vh;
+ overflow-y: auto;
+}
+
+.jey-topright-wrapper .dropdown-notifications.hidden {
+ display: none !important;
+}
+
+@media (max-width: 400px) {
+ .jey-topright-wrapper .dropdown-notifications {
+ width: calc(100vw - 32px);
+ }
+}
+
/* ==========================================================================
Hide "Getting Started" onboarding panels
========================================================================== */
diff --git a/jey_theme/public/js/jey_theme.js b/jey_theme/public/js/jey_theme.js
index f0ef256..b92057d 100644
--- a/jey_theme/public/js/jey_theme.js
+++ b/jey_theme/public/js/jey_theme.js
@@ -677,6 +677,238 @@
$(document).on("page-change", addFullWidthButton);
})();
+/* ==========================================================================
+ Toggle Theme button in sidebar (above Collapse)
+ ========================================================================== */
+
+(function () {
+ "use strict";
+
+ function addThemeSwitchButton() {
+ var collapse = document.querySelector(".collapse-sidebar-link");
+ if (!collapse || document.querySelector(".jey-theme-switch-link")) return;
+
+ var link = document.createElement("a");
+ link.className = "jey-theme-switch-link";
+ link.innerHTML =
+ frappe.utils.icon("palette", "sm", "", "", "text-ink-gray-7 current-color", true) +
+ "" + __("Switch Theme") + "";
+
+ link.addEventListener("click", function () {
+ new frappe.ui.ThemeSwitcher().show();
+ });
+
+ collapse.parentNode.insertBefore(link, collapse);
+ }
+
+ $(document).ready(addThemeSwitchButton);
+ $(document).on("page-change", addThemeSwitchButton);
+})();
+
+/* ==========================================================================
+ Hide "Website" item from sidebar user dropdown
+ ========================================================================== */
+
+(function () {
+ "use strict";
+
+ function tagWebsiteItem() {
+ var label = __("Website");
+ document.querySelectorAll(".dropdown-menu-item:not(.jey-hidden-item)").forEach(function (el) {
+ var title = el.querySelector(".menu-item-title");
+ if (title && title.textContent.trim() === label) {
+ el.classList.add("jey-hidden-item");
+ }
+ });
+ }
+
+ $(document).ready(tagWebsiteItem);
+ var observer = new MutationObserver(tagWebsiteItem);
+ observer.observe(document.body, { childList: true, subtree: true });
+})();
+
+/* ==========================================================================
+ Reload button next to Notifications in sidebar
+ ========================================================================== */
+
+(function () {
+ "use strict";
+
+ function addReloadButton() {
+ var notif = document.querySelector(".sidebar-notification");
+ if (!notif || document.querySelector(".jey-reload-btn")) return;
+
+ var btn = notif.cloneNode(true);
+ btn.classList.remove("sidebar-notification");
+ btn.classList.remove("hidden");
+ btn.classList.add("jey-reload-btn");
+ btn.removeAttribute("id");
+ btn.setAttribute("title", __("Reload"));
+
+ var iconWrap = btn.querySelector(".sidebar-item-icon");
+ if (iconWrap) {
+ iconWrap.setAttribute("item-icon", "rotate-ccw");
+ iconWrap.innerHTML = frappe.utils.icon(
+ "rotate-ccw", "sm", "", "", "text-ink-gray-7 current-color", true
+ );
+ }
+ var label = btn.querySelector(".sidebar-item-label");
+ if (label) label.textContent = __("Reload");
+
+ btn.addEventListener("click", function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ frappe.ui.toolbar.clear_cache();
+ });
+
+ notif.parentNode.insertBefore(btn, notif.nextSibling);
+ }
+
+ $(document).ready(function () {
+ addReloadButton();
+ var tries = 0;
+ var poll = setInterval(function () {
+ addReloadButton();
+ if (++tries > 30 || document.querySelector(".jey-reload-btn")) {
+ clearInterval(poll);
+ }
+ }, 300);
+ });
+ $(document).on("page-change", addReloadButton);
+})();
+
+/* ==========================================================================
+ Notification Bell — Move to top-right corner
+ ========================================================================== */
+
+(function () {
+ "use strict";
+
+ function moveNotifications() {
+ var dropdown = document.querySelector(".standard-items-sections .dropdown-notifications, .body-sidebar .dropdown-notifications:not(.jey-moved)");
+ var wrapper = document.querySelector(".jey-topright-wrapper");
+
+ // A new dropdown appeared after page rebuild — move it into existing wrapper
+ if (dropdown && wrapper) {
+ dropdown.classList.add("jey-moved", "hidden");
+ dropdown.parentNode.removeChild(dropdown);
+ var old = wrapper.querySelector(".dropdown-notifications");
+ if (old) old.remove();
+ wrapper.appendChild(dropdown);
+ return;
+ }
+
+ if (wrapper) return; // already set up
+ if (!dropdown) return; // nothing to move yet
+
+ // --- First-time setup ---
+ wrapper = document.createElement("div");
+ wrapper.className = "jey-topright-wrapper";
+
+ var bell = document.createElement("button");
+ bell.className = "jey-topright-bell btn-reset";
+ bell.setAttribute("title", __("Notifications"));
+ bell.innerHTML =
+ '';
+ wrapper.appendChild(bell);
+
+ dropdown.classList.add("jey-moved", "hidden");
+ dropdown.parentNode.removeChild(dropdown);
+ wrapper.appendChild(dropdown);
+
+ document.body.appendChild(wrapper);
+
+ function updateBadge() {
+ var dd = wrapper.querySelector(".dropdown-notifications");
+ if (!dd) return;
+ var unread = dd.querySelectorAll(".notification-item.unread").length;
+ var badge = bell.querySelector(".jey-notif-badge");
+ if (unread > 0) {
+ if (!badge) {
+ badge = document.createElement("span");
+ badge.className = "jey-notif-badge";
+ bell.appendChild(badge);
+ }
+ badge.textContent = unread > 9 ? "9+" : unread;
+ } else if (badge) {
+ badge.remove();
+ }
+ }
+
+ bell.addEventListener("click", function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ var dd = wrapper.querySelector(".dropdown-notifications");
+ if (dd) dd.classList.toggle("hidden");
+ updateBadge();
+ });
+
+ document.addEventListener("click", function (e) {
+ if (!wrapper.contains(e.target)) {
+ var dd = wrapper.querySelector(".dropdown-notifications");
+ if (dd) dd.classList.add("hidden");
+ }
+ });
+
+ wrapper.addEventListener("click", function (e) {
+ if (e.target.closest(".close-notification-dialogue")) {
+ var dd = wrapper.querySelector(".dropdown-notifications");
+ if (dd) dd.classList.add("hidden");
+ }
+ });
+
+ updateBadge();
+ var obs = new MutationObserver(updateBadge);
+ obs.observe(dropdown, { childList: true, subtree: true, attributes: true, attributeFilter: ["class"] });
+ }
+
+ $(document).ready(function () {
+ moveNotifications();
+ var tries = 0;
+ var poll = setInterval(function () {
+ moveNotifications();
+ if (++tries > 30) clearInterval(poll);
+ }, 300);
+ });
+ $(document).on("page-change", moveNotifications);
+})();
+
+/* ==========================================================================
+ Fix: nested menus left orphaned when parent menu is toggled closed
+ (sidebar-header app-switcher → Workspaces submenu stays on screen)
+ ========================================================================== */
+
+(function () {
+ "use strict";
+
+ function patchMenuHide() {
+ if (!window.frappe || !frappe.ui || !frappe.ui.menu) return false;
+ var proto = frappe.ui.menu.prototype;
+ if (proto.__jeyHidePatched) return true;
+ var origHide = proto.hide;
+ proto.hide = function () {
+ if (this.nested_menus && this.nested_menus.length) {
+ this.nested_menus.forEach(function (m) { m && m.hide && m.hide(); });
+ }
+ this.current_menu = null;
+ return origHide.apply(this, arguments);
+ };
+ proto.__jeyHidePatched = true;
+ return true;
+ }
+
+ if (!patchMenuHide()) {
+ var tries = 0;
+ var poll = setInterval(function () {
+ if (patchMenuHide() || ++tries > 50) clearInterval(poll);
+ }, 200);
+ }
+})();
+
/* ==========================================================================
Override Frappe Theme Switcher — Jey themes
========================================================================== */