1// maint_settings_btn_lock.js 2// 3// (FIX 2026-08 <chá»ng spam click â Auto PM Settings>) Tắt (khoá) NGAY LẬP 4// TỨC 3 nút hà nh Äá»ng trong tab "Auto PM" (Settings > Bảo trì) khi vừa bấm, 5// trưá»c khi server ká»p phản há»i â tránh user bấm liên tục trong lúc callback 6// server Äang chạy (VD auto-PM quét/áp dụng nhiá»u thiết bá» mất và i giây). 7// 8// â ï¸ ROOT CAUSE cá»§a bug "<2> nút không ÄÆ°á»£c báºt lại sau khi lá»i" (2026-08): 9// Bản cÅ© set thẳng `el.disabled = true` bằng JS thuần. React (bên trong 10// Dash) vẫn nghÄ© prop "disabled" cá»§a nút là False (vì nó không biết JS bên 11// ngoà i vừa Äá»i DOM tháºt). Khi callback server chạy xong và trả vá» 12// Output("disabled", False) â CÃNG giá trá» False mà React Äang lưu trong bá» 13// nhá» â React so sánh thấy "không Äá»i" nên KHÃNG Äá»ng và o DOM tháºt nữa => 14// nút bá» kẹt disabled=true vÄ©nh viá» n trên DOM dù Dash nghÄ© nó Äã là False. 15// Äây là lá»i kinh Äiá»n khi mutate trá»±c tiếp 1 DOM property mà React Äang 16// quản lý (React/Dash không há» biết Äá» Äá»ng bá» lại). 17// 18// FIX: không Äụng và o thuá»c tÃnh "disabled" tháºt nữa. Khoá bằng 1 class CSS 19// (pointer-events: none + giảm opacity) â hoà n toà n tách biá»t khá»i state 20// React Äang theo dõi, nên không bao giá» bá» desync. Má» khoá bằng cách theo 21// dõi (MutationObserver) khá»i kết quả (Alert) tương ứng cá»§a má»i nút â server 22// LUÃN ghi ná»i dung má»i và o Äúng khá»i Äó á» Má»I nhánh xá» lý (thà nh công/lá»i/ 23// không Äá»§ quyá»n...), nên Äây là tÃn hiá»u Äáng tin cáºy nhất báo "callback Äã 24// chạy xong", không phụ thuá»c và o thuá»c tÃnh "disabled". 25// 26// Cá» tình KHÃNG dùng Dash clientside_callback cho viá»c khoá/má» khoá vì các 27// nút nà y Äã có callback SERVER ghi và o Äúng Output("disabled",...) Äó (cho 28// mục ÄÃch khác â VD readonly theo quyá»n) â thêm 1 clientside callback nữa 29// cùng Output sẽ gây lá»i "Duplicate callback outputs" (Äã gặp thá»±c tế). 30(function () { 31 // id nút -> id khá»i kết quả (Alert) mà server luôn cáºp nháºt sau khi xá» lý xong 32 var LOCK_MAP = { 33 "maint-auto-run-now-btn": "maint-auto-run-result", 34 "maint-rebalance-scan-btn": "maint-rebalance-preview", 35 "maint-rebalance-apply-btn": "maint-rebalance-result", 36 }; 37 var LOCK_IDS = Object.keys(LOCK_MAP); 38 var LOCK_CLASS = "asm-btn-locked"; 39 // Khoá cứng tá»i Äa (an toà n nếu vì lý do nà o Äó không nháºn ÄÆ°á»£c tÃn hiá»u 40 // mutation â VD lá»i mạng khiến request không bao giá» hoà n tất) Äá» nút 41 // không bá» kẹt vÄ©nh viá» n. 42 var SAFETY_UNLOCK_MS = 30000; 43 44 function unlock(el) { 45 el.classList.remove(LOCK_CLASS); 46 if (el._asmLockTimeout) { 47 clearTimeout(el._asmLockTimeout); 48 el._asmLockTimeout = null; 49 } 50 } 51 52 document.addEventListener( 53 "click", 54 function (e) { 55 for (var i = 0; i < LOCK_IDS.length; i++) { 56 var id = LOCK_IDS[i]; 57 var el = e.target.closest ? e.target.closest("#" + id) : null; 58 if (!el) continue; 59 if (el.disabled || el.classList.contains(LOCK_CLASS)) { 60 // Äã bá» disable tháºt (VD readonly/không Äá»§ quyá»n) hoặc Äang khoá 61 // chá» server â chặn không cho Dash nháºn thêm click nà y. 62 e.stopPropagation(); 63 e.preventDefault(); 64 return; 65 } 66 el.classList.add(LOCK_CLASS); 67 el._asmLockTimeout = setTimeout(function (elRef) { 68 return function () { unlock(elRef); }; 69 }(el), SAFETY_UNLOCK_MS); 70 } 71 }, 72 true // capture phase â chạy trưá»c khi Dash xá» lý sá»± kiá»n click 73 ); 74 75 function watchResult(btnId, resultId) { 76 var resultEl = document.getElementById(resultId); 77 if (!resultEl || resultEl._asmLockObserver) return; 78 var obs = new MutationObserver(function () { 79 var btnEl = document.getElementById(btnId); 80 if (btnEl) unlock(btnEl); 81 }); 82 obs.observe(resultEl, { childList: true, subtree: true, characterData: true }); 83 resultEl._asmLockObserver = obs; 84 } 85 86 function attachAll() { 87 for (var id in LOCK_MAP) { 88 watchResult(id, LOCK_MAP[id]); 89 } 90 } 91 92 // Modal Auto PM có thá» má»/Äóng nhiá»u lần trong 1 phiên â Dash re-mount lại 93 // các node nà y má»i lần má» lại (MutationObserver cÅ© gắn trên node cÅ© sẽ 94 // không còn hiá»u lá»±c), nên cần tá»± gắn lại observer má»i khi khá»i kết quả 95 // xuất hiá»n lại trong DOM. 96 var bodyObserver = new MutationObserver(attachAll); 97 bodyObserver.observe(document.body, { childList: true, subtree: true }); 98 document.addEventListener("DOMContentLoaded", attachAll); 99 attachAll(); 100})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.