From e5d074687a31258a18a9f73f9e9547899d17b0ba Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Wed, 18 Mar 2026 22:25:09 +0000 Subject: [PATCH] feat: add users management tab to admin panel New Sistema > Usuarios section with user listing, role badges (ADMIN=blue, OWNER=purple, TALLER=green, BODEGA=orange), activate/deactivate toggle, and pending users badge count. Co-Authored-By: Claude Opus 4.6 (1M context) --- dashboard/admin.html | 38 +++++++++++++++ dashboard/admin.js | 113 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/dashboard/admin.html b/dashboard/admin.html index 61f98eb..cfb4769 100644 --- a/dashboard/admin.html +++ b/dashboard/admin.html @@ -668,6 +668,15 @@ Exportar CSV + + @@ -1207,6 +1216,35 @@ + + +
+ + +
+
+ + + + + + + + + + + + + + + +
NombreEmailNegocioRolActivoÚltimo LoginAcciones
+
+
+
+ diff --git a/dashboard/admin.js b/dashboard/admin.js index db18cec..62fe78d 100644 --- a/dashboard/admin.js +++ b/dashboard/admin.js @@ -118,6 +118,9 @@ function showSection(sectionId) { case 'diagrams': // Just show section, user uses search break; + case 'users': + loadUsers(); + break; } } @@ -1226,18 +1229,18 @@ function renderPagination(containerId, pagination, pageKey, loadFunction) { let html = ''; // Previous button - html += ``; + html += ``; // Page numbers const startPage = Math.max(1, page - 2); const endPage = Math.min(total_pages, page + 2); for (let i = startPage; i <= endPage; i++) { - html += ``; + html += ``; } // Next button - html += ``; + html += ``; container.innerHTML = html; } @@ -1967,3 +1970,107 @@ async function deleteHotspot(hotspotId) { showAlert(e.message, 'error'); } } + +// ============================================================================ +// User Management +// ============================================================================ + +const roleBadgeColors = { + ADMIN: '#3b82f6', + OWNER: '#8b5cf6', + TALLER: '#22c55e', + BODEGA: '#f59e0b' +}; + +function formatDate(dateStr) { + if (!dateStr) return 'Nunca'; + var d = new Date(dateStr); + if (isNaN(d.getTime())) return dateStr; + return d.toLocaleDateString('es-MX', { year: 'numeric', month: 'short', day: 'numeric' }) + + ' ' + d.toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit' }); +} + +function getRoleBadge(role) { + var color = roleBadgeColors[role] || '#6b7280'; + return '' + (role || 'N/A') + ''; +} + +function getActiveBadge(isActive) { + if (isActive) { + return 'Activo'; + } + return 'Inactivo'; +} + +async function loadUsers() { + var token = localStorage.getItem('access_token'); + var tbody = document.getElementById('usersTable'); + tbody.innerHTML = '
'; + + try { + var res = await fetch('/api/admin/users', { + headers: { 'Authorization': 'Bearer ' + token } + }); + if (!res.ok) throw new Error('Error al cargar usuarios (' + res.status + ')'); + var data = await res.json(); + var users = Array.isArray(data) ? data : (data.data || []); + + // Update pending badge + var pending = users.filter(function(u) { return !u.is_active; }).length; + var badge = document.getElementById('pendingUsersBadge'); + if (badge) { + if (pending > 0) { + badge.textContent = pending; + badge.style.display = 'inline-block'; + } else { + badge.style.display = 'none'; + } + } + + if (users.length === 0) { + tbody.innerHTML = 'No hay usuarios registrados'; + return; + } + + tbody.innerHTML = users.map(function(u) { + var toggleLabel = u.is_active ? 'Desactivar' : 'Activar'; + var toggleClass = u.is_active ? 'btn-secondary' : 'btn-primary'; + return '' + + '' + (u.name || u.nombre || '-') + '' + + '' + (u.email || '-') + '' + + '' + (u.business_name || u.negocio || '-') + '' + + '' + getRoleBadge(u.role || u.rol) + '' + + '' + getActiveBadge(u.is_active) + '' + + '' + formatDate(u.last_login || u.ultimo_login) + '' + + '' + + ''; + }).join(''); + } catch (e) { + tbody.innerHTML = '' + e.message + ''; + } +} + +async function toggleUserActive(userId, currentActive) { + var token = localStorage.getItem('access_token'); + var action = currentActive ? 'desactivar' : 'activar'; + if (!confirm('¿Seguro que deseas ' + action + ' este usuario?')) return; + + try { + var res = await fetch('/api/admin/users/' + userId + '/activate', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + token + }, + body: JSON.stringify({ is_active: !currentActive }) + }); + if (!res.ok) { + var err = await res.json(); + throw new Error(err.error || 'Error al actualizar usuario'); + } + showAlert('Usuario ' + (currentActive ? 'desactivado' : 'activado') + ' correctamente'); + loadUsers(); + } catch (e) { + showAlert(e.message, 'error'); + } +}