(function () { // ✅ 스크립트가 실수로 2번 로드돼도 바인딩 1번만 if (window.__ADMIN_UI_JS_BOUND__) return; window.__ADMIN_UI_JS_BOUND__ = true; // ----------------------- // click handlers // ----------------------- document.addEventListener('click', (e) => { const t = e.target; // password toggle if (t && t.matches('[data-toggle="password"]')) { const pw = document.getElementById('password'); if (!pw) return; const isPw = pw.type === 'password'; pw.type = isPw ? 'text' : 'password'; t.textContent = isPw ? '숨기기' : '보기'; return; } }); // ----------------------- // ✅ data-confirm (ONLY ONCE) // - capture 단계에서 먼저 실행되어 // 취소 시 다른 submit 리스너(버튼 disable 등) 실행 안 됨 // ----------------------- document.addEventListener('submit', (e) => { const form = e.target; if (!(form instanceof HTMLFormElement)) return; const raw = form.getAttribute('data-confirm'); if (!raw) return; // 이미 confirm 통과한 submit이면 다시 confirm 금지 if (form.dataset.confirmed === '1') return; // "\n" 문자, " " 엔티티 모두 줄바꿈 처리 const msg = String(raw) .replace(/\\n/g, '\n') .replace(/ /g, '\n'); if (!window.confirm(msg)) { e.preventDefault(); e.stopImmediatePropagation(); return; } form.dataset.confirmed = '1'; // confirm 통과 -> 그대로 진행 (다른 submit 리스너 정상 실행) }, true); // ----------------------- // login submit UI (disable + text) // ----------------------- // document.addEventListener('submit', (e) => { // const form = e.target; // if (!form || !form.matches('[data-form="login"]')) return; // // const btn = form.querySelector('[data-submit]'); // if (btn) { // btn.disabled = true; // btn.dataset.original = btn.textContent; // btn.textContent = '처리 중...'; // } // }); // ----------------------- // showMsg (toast) // ----------------------- if (typeof window.showMsg !== 'function') { function ensureWrap() { let wrap = document.getElementById('a-toast-wrap'); if (!wrap) { wrap = document.createElement('div'); wrap.id = 'a-toast-wrap'; wrap.className = 'a-toast-wrap'; document.body.appendChild(wrap); } return wrap; } window.showMsg = function (message, opt = {}) { const type = opt.type || 'info'; // success | info | warn | danger const title = opt.title || ''; const wrap = ensureWrap(); const toast = document.createElement('div'); toast.className = `a-toast a-toast--${type}`; const t = document.createElement('div'); t.className = 'a-toast__title'; t.textContent = title || (type === 'success' ? '완료' : type === 'danger' ? '오류' : '안내'); const m = document.createElement('div'); m.className = 'a-toast__msg'; m.textContent = String(message || ''); toast.appendChild(t); toast.appendChild(m); wrap.appendChild(toast); const ttl = Number(opt.ttl || 3000); window.setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateY(-4px)'; toast.style.transition = 'all .15s ease'; window.setTimeout(() => toast.remove(), 180); }, ttl); toast.addEventListener('click', () => toast.remove()); return Promise.resolve(); }; } // flash 자동 표시 document.addEventListener('DOMContentLoaded', async () => { const list = window.__adminFlash; if (!Array.isArray(list) || list.length === 0) return; for (const f of list) { await window.showMsg(f.message, { type: f.type, title: f.title, ttl: 3200 }); } window.__adminFlash = []; }); })();