/** * Admin Panel - User Management Logic (Premium Edition) */ let allMgmtUsers = []; let filteredMgmtUsers = []; let deleteTargetId = null; let activeExpandedId = null; async function loadUserManagement() { const grid = document.getElementById('users-mgmt-grid'); if (!grid) return; grid.innerHTML = '
Fetching user directory...
'; try { const res = await fetch('/api/admin/users'); allMgmtUsers = await res.json(); updateUserMgmtStats(allMgmtUsers); applyUserMgmtFilters(); } catch (err) { grid.innerHTML = '
Error loading user management data.
'; } } function updateUserMgmtStats(users) { const totalEl = document.getElementById('total-users-val'); const activeEl = document.getElementById('active-users-val'); const disabledEl = document.getElementById('disabled-users-val'); if (totalEl) totalEl.innerText = users.length; if (activeEl) activeEl.innerText = users.filter(u => u.is_active).length; if (disabledEl) disabledEl.innerText = users.filter(u => !u.is_active).length; } function applyUserMgmtFilters() { const searchInput = document.getElementById('user-mgmt-search'); const searchTerm = searchInput ? searchInput.value.trim().toLowerCase() : ''; // We'll also use a global 'currentMgmtFilter' set by the tabs const activeTab = document.querySelector('.u-tab-m.active'); const filterRole = activeTab ? activeTab.getAttribute('onclick').match(/'([^']+)'/)[1] : 'all'; filteredMgmtUsers = allMgmtUsers.filter(u => { const searchable = `${u.id} ${u.name} ${u.phone} ${u.email}`.toLowerCase(); const matchesSearch = searchTerm === '' || searchable.includes(searchTerm); const matchesRole = filterRole === 'all' || u.role === filterRole; return matchesSearch && matchesRole; }); renderUserMgmtGrid(filteredMgmtUsers); } function filterUserMgmt(role, btn) { document.querySelectorAll('.u-tab-m').forEach(b => b.classList.remove('active')); btn.classList.add('active'); applyUserMgmtFilters(); } function renderUserMgmtGrid(users) { const grid = document.getElementById('users-mgmt-grid'); if (!grid) return; if (users.length === 0) { grid.innerHTML = '
No matching users found.
'; return; } grid.innerHTML = users.map(u => { const initial = (u.name || 'U').charAt(0).toUpperCase(); const status = u.is_active ? 'active' : 'disabled'; const isExpanded = activeExpandedId === u.id; // Role-Based Styling let crystalClass = 'crystal-user'; let roleLabel = 'General User'; let roleIcon = 'fa-user-shield'; if (u.role === 'admin') { crystalClass = 'crystal-admin'; roleLabel = 'System Admin'; roleIcon = 'fa-lock'; } else if (u.role === 'admin-user') { crystalClass = 'crystal-agent'; roleLabel = 'Agent / Moderator'; roleIcon = 'fa-user-tie'; } return `
${initial}
${escapeHtml(u.name)}
${escapeHtml(u.phone)}
${status}
${u.id.slice(0, 10)}... ${escapeHtml(u.joined_at.split(' ')[0])}
${u.role !== 'admin' ? ` ` : ` `}
`; }).join(''); } function toggleUserCardExpansion(userId) { console.log('toggleUserCardExpansion called for:', userId); const card = document.getElementById(`user-m-card-${userId}`); const metaDiv = document.getElementById(`user-meta-${userId}`); const expansionDiv = document.getElementById(`user-expansion-${userId}`); const manageBtn = document.getElementById(`user-manage-btn-${userId}`); const actionsDiv = document.getElementById(`user-actions-${userId}`); console.log('Elements found:', { card: !!card, metaDiv: !!metaDiv, expansionDiv: !!expansionDiv, manageBtn: !!manageBtn, actionsDiv: !!actionsDiv }); if (!card || !metaDiv || !expansionDiv) { console.error('Required elements not found!'); return; } const isExpanded = card.classList.contains('expanded'); console.log('Is expanded:', isExpanded); if (!isExpanded) { // Expand this card console.log('Expanding card...'); card.classList.add('expanded'); metaDiv.style.display = 'none'; expansionDiv.style.display = 'flex'; // Find user data const user = allMgmtUsers.find(u => u.id === userId) || filteredMgmtUsers.find(u => u.id === userId); console.log('User data:', user); if (!user) return; // Build expansion content const isAdmin = user.role === 'admin'; expansionDiv.innerHTML = `
Security Override
${!isAdmin ? ` ` : ` `}
`; // Update button to Cancel if (manageBtn) manageBtn.innerHTML = ' Cancel'; // Hide Enable/Disable button in expanded mode const enableDisableBtn = actionsDiv ? actionsDiv.querySelector('[onclick^="toggleUserMgmtStatus"]') : null; if (enableDisableBtn) enableDisableBtn.style.display = 'none'; activeExpandedId = userId; } else { // Collapse this card console.log('Collapsing card...'); card.classList.remove('expanded'); metaDiv.style.display = 'flex'; expansionDiv.style.display = 'none'; expansionDiv.innerHTML = ''; // Update button back to Manage Access if (manageBtn) manageBtn.innerHTML = ' Manage Access'; // Show Enable/Disable button again const enableDisableBtn = actionsDiv ? actionsDiv.querySelector('[onclick^="toggleUserMgmtStatus"]') : null; if (enableDisableBtn) enableDisableBtn.style.display = 'flex'; activeExpandedId = null; } } async function toggleUserMgmtStatus(userId) { try { const res = await fetch(`/api/admin/users/${userId}/toggle-status`, { method: 'POST' }); const data = await res.json(); if (res.ok) { // Collapse any expanded card and refresh list activeExpandedId = null; loadUserManagement(); if (typeof showToast === 'function') showToast('success', 'Status Updated', 'User access level has been modified.'); } else { if (typeof showToast === 'function') showToast('error', 'Action Denied', data.error || 'Failed to update status.'); } } catch (e) { if (typeof showToast === 'function') showToast('error', 'Network Error', 'Failed to connect to server.'); } } async function updateUserMgmtPassword(userId) { const passInput = document.getElementById(`new-pass-${userId}`); const newPass = passInput ? passInput.value.trim() : ''; if (!newPass) { alert('Please enter a password.'); return; } try { const res = await fetch(`/api/admin/users/${userId}/change-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ new_password: newPass }) }); if (res.ok) { passInput.value = ''; if (typeof showToast === 'function') showToast('success', 'Security Update', 'User password has been manually overridden.'); } } catch (e) {} } /* ───────────────────────────────────────────────────── CHANGE PASSWORD MODAL LOGIC ───────────────────────────────────────────────────── */ let changePassTargetId = null; function openChangePasswordModal(userId) { changePassTargetId = userId; const modal = document.getElementById('user-change-pass-modal'); const newPassEl = document.getElementById('change-pass-new'); const errorEl = document.getElementById('change-pass-error'); // Get the password from the input field const passInput = document.getElementById(`new-pass-${userId}`); const newPassword = passInput ? passInput.value : ''; if (!newPassword) { if (typeof showToast === 'function') showToast('error', 'Error', 'Please enter a new password first.'); return; } if (newPassEl) newPassEl.value = newPassword; if (errorEl) errorEl.innerText = ''; if (modal) modal.style.display = 'flex'; // Set up the confirm button const finalBtn = document.getElementById('final-change-pass-btn'); if (finalBtn) { finalBtn.onclick = confirmChangePassword; } } function closeChangePasswordModal() { const modal = document.getElementById('user-change-pass-modal'); if (modal) modal.style.display = 'none'; changePassTargetId = null; } async function confirmChangePassword() { if (!changePassTargetId) return; const newPassEl = document.getElementById('change-pass-new'); const errorEl = document.getElementById('change-pass-error'); const newPassword = newPassEl ? newPassEl.value : ''; if (errorEl) errorEl.innerText = 'Updating password...'; try { const res = await fetch(`/api/admin/users/${changePassTargetId}/change-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ new_password: newPassword }) }); const data = await res.json(); if (res.ok) { closeChangePasswordModal(); // Clear the password input const passInput = document.getElementById(`new-pass-${changePassTargetId}`); if (passInput) passInput.value = ''; if (typeof showToast === 'function') showToast('success', 'Success', 'User password has been changed.'); } else { if (errorEl) errorEl.innerText = data.error || 'Failed to change password.'; } } catch (e) { if (errorEl) errorEl.innerText = 'Network error occurred.'; } } /* ───────────────────────────────────────────────────── SECURE DELETION LOGIC ───────────────────────────────────────────────────── */ function openDeleteUserModal(id, name) { deleteTargetId = id; const modal = document.getElementById('user-delete-modal'); const nameEl = document.getElementById('delete-target-name'); const errorEl = document.getElementById('delete-error'); const passInput = document.getElementById('admin-auth-pass'); if (nameEl) nameEl.innerText = name; if (errorEl) errorEl.innerText = ''; if (passInput) passInput.value = ''; if (modal) modal.style.display = 'flex'; // Set up the final button const finalBtn = document.getElementById('final-delete-btn'); if (finalBtn) { finalBtn.onclick = confirmDeleteUser; } } function closeDeleteModal() { const modal = document.getElementById('user-delete-modal'); if (modal) modal.style.display = 'none'; deleteTargetId = null; } async function confirmDeleteUser() { if (!deleteTargetId) return; const passInput = document.getElementById('admin-auth-pass'); const adminPass = passInput ? passInput.value : ''; const errorEl = document.getElementById('delete-error'); if (!adminPass) { if (errorEl) errorEl.innerText = 'Authorization required.'; return; } if (errorEl) errorEl.innerText = 'Deleting account...'; try { const res = await fetch(`/api/admin/users/${deleteTargetId}/delete`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ admin_password: adminPass }) }); const data = await res.json(); if (res.ok) { // Show success toast first if (typeof showToast === 'function') showToast('success', 'Success', 'User deleted successfully.'); // Wait 1 second before closing modal and refreshing setTimeout(() => { closeDeleteModal(); loadUserManagement(); }, 1000); } else { if (errorEl) errorEl.innerText = data.error || 'Authorization failed.'; if (typeof showToast === 'function') showToast('error', 'Error', data.error || 'Failed to delete user.'); } } catch (e) { if (errorEl) errorEl.innerText = 'Network error occurred.'; } } // Helper: Escape HTML function escapeHtml(value) { return String(value) .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); }