`;
modalRoot.appendChild(modal);
enhanceFloatWraps(modal);
modal.addEventListener('click', async (ev) => {
let btn = ev.target.closest('button'); if (!btn) return;
if (btn.id === 'u-cancel') { closeModal(); }
else if (btn.id === 'u-save') {
btn.disabled = true; setBtnLabel(btn, 'Creating...');
let payload = {
email: modal.querySelector('#u-email').value.trim(),
name: modal.querySelector('#u-name').value.trim(),
role: modal.querySelector('#u-role').value,
password: modal.querySelector('#u-password').value
};
if (isSuper && modal.querySelector('#u-partner')) {
payload.partner_id = modal.querySelector('#u-partner').value;
}
if (!payload.email || !payload.password) {
let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = 'Email and password required';
btn.disabled = false; setBtnLabel(btn, 'Create'); return;
}
if (isSuper && !payload.partner_id) {
let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = 'Partner is required';
btn.disabled = false; setBtnLabel(btn, 'Create'); return;
}
try {
let r = await api('admin/api/users.php?action=create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (r.ok) { toast('Created', 'success'); loadUsers(); closeModal(); }
else { let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = r.error || JSON.stringify(r); }
} catch (e) { let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = e.message; }
btn.disabled = false; setBtnLabel(btn, 'Create');
}
});
} else {
// edit
modal.innerHTML = `
Loading…
`;
modalRoot.appendChild(modal);
try {
let r = await api('admin/api/users.php?action=get&id=' + encodeURIComponent(id));
let u = r.data;
modal.querySelector('.modal').innerHTML = `
Edit User: ${escapeHtml(u.name || u.email)}
${isSuper ? `
` : ''}
`;
enhanceFloatWraps(modal);
modal.querySelectorAll('.modal-actions button').forEach(b => b.setAttribute('type', 'button'));
modal.querySelector('#u-role').value = u.role;
modal.addEventListener('click', async (ev) => {
let btn = ev.target.closest('button'); if (!btn) return;
if (btn.id === 'u-cancel') { closeModal(); }
else if (btn.id === 'u-delete') {
if (confirm('Delete user?')) {
btn.disabled = true; setBtnLabel(btn, 'Deleting...');
try { await api('admin/api/users.php?action=delete&id=' + id); toast('Deleted', 'success'); closeModal(); loadUsers(); }
catch (e) { let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = e.message; btn.disabled = false; setBtnLabel(btn, 'Delete'); }
}
}
else if (btn.id === 'u-save') {
btn.disabled = true; setBtnLabel(btn, 'Saving...');
let payload = {
user_id: id,
email: modal.querySelector('#u-email').value.trim(),
name: modal.querySelector('#u-name').value.trim(),
role: modal.querySelector('#u-role').value
};
if (isSuper && modal.querySelector('#u-partner')) {
payload.partner_id = modal.querySelector('#u-partner').value;
}
if (isSuper && !payload.partner_id) {
let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = 'Partner is required';
btn.disabled = false; setBtnLabel(btn, 'Save'); return;
}
let pw = modal.querySelector('#u-password').value; if (pw) payload.password = pw;
try {
let r = await api('admin/api/users.php?action=update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (r.ok) { toast('Saved', 'success'); loadUsers(); closeModal(); }
else { let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = r.error || JSON.stringify(r); }
} catch (e) { let a = modal.querySelector('#u-alert'); a.style.display = 'block'; a.textContent = e.message; }
btn.disabled = false; setBtnLabel(btn, 'Save');
}
});
} catch (e) { toast('Error: ' + e.message, 'error'); }
}
}
window.openUserModal = openUserModal;
async function loadPartners(q = '') {
showSection('partners');
const per = el('#partner-per-page') ? el('#partner-per-page').value : 25;
try {
let url = 'admin/api/partners.php?action=list&per_page=' + per + '&sort_by=partner_id&sort_dir=asc' + (q ? ('&q=' + encodeURIComponent(q)) : (el('#partner-search')?.value ? '&q=' + encodeURIComponent(el('#partner-search').value) : ''));
let r = await api(url);
let tbody = el('#partners-table tbody'); tbody.innerHTML = '';
r.data.forEach(row => {
let tr = document.createElement('tr'); tr.classList.add('row-click'); tr.dataset.id = row.partner_id;
tr.innerHTML = `
${row.partner_id}
${escapeHtml(row.name)}
${escapeHtml(row.partner_type || '')}
${escapeHtml(row.oms_partner_id || '')}
`;
tr.addEventListener('click', () => showPartnerModal(row.partner_id));
tbody.appendChild(tr);
});
} catch (e) { toast('Error loading partners: ' + e.message, 'error'); }
}
function escapeHtml(s) { if (s == null) return ''; return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); }
function closeModal() { console.log('closeModal called'); debugBanner('Modal closed'); let r = document.getElementById('modal-root'); r.innerHTML = ''; r.style.display = 'none'; }
function enhanceFloatWraps(root = document) {
root = root || document;
let wraps = Array.from((root || document).querySelectorAll('.float-wrap'));
wraps.forEach(w => {
let input = w.querySelector('input,textarea,select'); if (!input) return;
const update = () => {
if (input.tagName === 'SELECT') {
if (input.value && input.value !== '') w.classList.add('has-value'); else w.classList.remove('has-value');
} else {
if (input.value && input.value.trim() !== '') w.classList.add('has-value'); else w.classList.remove('has-value');
}
};
update();
input.addEventListener('input', update); input.addEventListener('change', update);
let lbl = w.querySelector('label'); if (lbl) lbl.addEventListener('click', () => input.focus());
});
}
// helper to set button label inside .btn-inner if present
function setBtnLabel(btn, label) { try { let bi = btn && btn.querySelector && btn.querySelector('.btn-inner'); if (bi) bi.textContent = label; else if (btn) btn.textContent = label; } catch (e) { } }
// small toast helper
function toast(msg, type = 'info') { const t = document.createElement('div'); t.style.position = 'fixed'; t.style.right = '20px'; t.style.bottom = '20px'; t.style.background = 'linear-gradient(90deg,var(--accent),var(--accent-2))'; t.style.color = '#07101a'; t.style.padding = '12px 16px'; t.style.borderRadius = '10px'; t.style.boxShadow = '0 8px 40px rgba(0,0,0,0.5)'; t.style.zIndex = 3000; t.textContent = msg; document.body.appendChild(t); setTimeout(() => { t.style.transition = 'all .4s'; t.style.opacity = 0; t.style.transform = 'translateY(20px)'; setTimeout(() => t.remove(), 420); }, 1800); }
// small dev badge to confirm JS bundle is loaded (non-intrusive)
function showDevBadge() { try { let r = document.querySelector('.top-right'); if (!r) return; let b = document.createElement('div'); b.id = 'dev-badge'; b.textContent = 'JS loaded • ' + (new Date()).toLocaleTimeString(); b.style.fontSize = '12px'; b.style.padding = '6px 8px'; b.style.borderRadius = '8px'; b.style.background = 'rgba(255,255,255,0.03)'; b.style.border = '1px solid rgba(255,255,255,0.02)'; b.style.color = 'var(--muted)'; b.style.fontWeight = '700'; b.style.backdropFilter = 'blur(4px)'; b.style.boxShadow = '0 10px 30px rgba(0,0,0,0.4)'; r.appendChild(b); setTimeout(() => { if (b) b.style.opacity = 0.9; }, 120); } catch (e) { console.warn('badge failed', e); } }
function formatShortDate(ds) {
if (!ds) return '—';
try {
const d = new Date(ds);
if (isNaN(d.getTime())) return ds;
return (d.getMonth() + 1) + '/' + d.getDate() + '/' + String(d.getFullYear()).slice(-2);
} catch (e) { return ds; }
}
// start
document.addEventListener('DOMContentLoaded', init);
// ===========================================================================
// DEBUG CHEAT CODE FUNCTIONALITY
// DELETE THIS ENTIRE CHUNK TO REMOVE THE HIDDEN DROPDOWN
// ===========================================================================
let cheatCount = 0;
function initDebugCheat() {
document.addEventListener('keydown', (e) => {
// Check for '$' (Shift + 4)
if (e.key === '$') {
cheatCount++;
if (cheatCount === 7) {
showCheatDropdown();
}
}
});
}
async function showCheatDropdown() {
let wrap = document.querySelector('.debug-cheat-wrap');
if (wrap) return;
wrap = document.createElement('div');
wrap.className = 'debug-cheat-wrap';
wrap.innerHTML = `