RetinAI / web /users.html
ViannyCruz's picture
Upload 5 files
40256dd verified
Raw
History Blame
22.4 kB
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Gestión de Usuarios">
<meta name="author" content="Sistema Médico">
<link rel="icon" href="favicon.ico">
<title>Usuarios</title>
<link rel="stylesheet" href="css/simplebar.css">
<link href="https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,100;0,200;0,300;0,400;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/feather.css">
<link rel="stylesheet" href="css/app-light.css" id="lightTheme">
<link rel="stylesheet" href="css/app-dark.css" id="darkTheme" disabled>
</head>
<body class="vertical light">
<div class="wrapper">
<!-- Top Navigation -->
<nav class="topnav navbar navbar-light">
<button type="button" class="navbar-toggler text-muted mt-2 p-0 mr-3 collapseSidebar">
<i class="fe fe-menu navbar-toggler-icon"></i>
</button>
<ul class="nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-muted pr-0" href="#" id="navbarDropdownMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="avatar avatar-sm mt-2">
<img src="./assets/images/persona.png" alt="..." class="avatar-img rounded-circle">
</span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="#" onclick="confirmLogout()">Cerrar Sesión</a>
</div>
</li>
</ul>
</nav>
<!-- Sidebar -->
<aside class="sidebar-left border-right bg-white shadow" id="leftSidebar" data-simplebar>
<a href="#" class="btn collapseSidebar toggle-btn d-lg-none text-muted ml-2 mt-3" data-toggle="toggle">
<i class="fe fe-x"><span class="sr-only"></span></i>
</a>
<nav class="vertnav navbar navbar-light">
<!-- nav bar -->
<div class="w-100 mb-4 d-flex">
<a class="navbar-brand mx-auto mt-2 flex-fill text-center">
<img src="assets/images/LOGO.png" alt="Logo">
</a>
</div>
<ul class="navbar-nav flex-fill w-100 mb-2">
<li class="nav-item">
<a class="nav-link" href="./index.html">
<i class="fe fe-home fe-16"></i>
<span class="ml-3 item-text">Dashboard</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./diagnosis.html">
<i class="fe fe-eye fe-16"></i>
<span class="ml-3 item-text">Diagnóstico</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./patients.html">
<i class="fe fe-users fe-16"></i>
<span class="ml-3 item-text">Pacientes</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./historial-consultas.html">
<i class="fe fe-file-text fe-16"></i>
<span class="ml-3 item-text">Consultas</span>
</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="./users.html">
<i class="fe fe-user fe-16"></i>
<span class="ml-3 item-text">Usuarios</span>
</a>
</li>
</ul>
</nav>
</aside>
<!-- Main Content -->
<main role="main" class="main-content">
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-12">
<!-- Header -->
<div class="row align-items-center mb-2">
<div class="col">
<h2 class="h5 page-title">Gestión de Usuarios</h2>
</div>
<div class="col-auto">
<button class="btn btn-primary" onclick="loadUsers()">
<i class="fe fe-refresh-cw fe-16 mr-2"></i>Actualizar
</button>
<button class="btn btn-success ml-2" onclick="showAddUserModal()">
<i class="fe fe-plus fe-16 mr-2"></i>Nuevo Usuario
</button>
</div>
</div>
<!-- Users Table -->
<div class="card shadow">
<div class="card-header">
<h6 class="card-title mb-0">
<i class="fe fe-users mr-2"></i>Lista de Usuarios
</h6>
</div>
<div class="card-body">
<div id="loadingSpinner" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Cargando...</span>
</div>
<p class="text-muted mt-2">Cargando usuarios...</p>
</div>
<div id="usersTableContainer" style="display: none;">
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="thead-light">
<tr>
<th>ID</th>
<th>Nombre de Usuario</th>
<th>Rol</th>
<th>Fecha de Creación</th>
<th class="text-center">Acciones</th>
</tr>
</thead>
<tbody id="usersTableBody">
<!-- Datos se cargarán aquí dinámicamente -->
</tbody>
</table>
</div>
</div>
<div id="emptyState" class="text-center py-5" style="display: none;">
<i class="fe fe-users fe-48 text-muted mb-3"></i>
<h6 class="text-muted">No se encontraron usuarios</h6>
<p class="text-muted">No hay usuarios registrados en el sistema.</p>
<button class="btn btn-primary" onclick="showAddUserModal()">
<i class="fe fe-plus mr-1"></i>Crear Primer Usuario
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Modal para agregar/editar usuario -->
<div class="modal fade" id="userModal" tabindex="-1" role="dialog" aria-labelledby="userModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="userModalLabel">
<i class="fe fe-user mr-2"></i>Nuevo Usuario
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="userForm">
<input type="hidden" id="userId">
<div class="form-group">
<label for="username">Nombre de Usuario</label>
<input type="text" class="form-control" id="username" required>
</div>
<div class="form-group">
<label for="password">Contraseña</label>
<input type="password" class="form-control" id="password" required>
</div>
<div class="form-group">
<label for="confirmPassword">Confirmar Contraseña</label>
<input type="password" class="form-control" id="confirmPassword" required>
</div>
<div class="form-group">
<label for="role">Rol</label>
<select class="form-control" id="role" required>
<option value="Doctor">Doctor</option>
<option value="Admin">Administrador</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancelar</button>
<button type="button" class="btn btn-primary" id="saveUserBtn" onclick="saveUser()">Guardar</button>
</div>
</div>
</div>
</div>
<script src="js/jquery.min.js"></script>
<script src="js/popper.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/simplebar.min.js"></script>
<script src="js/config.js"></script>
<script src="api.js"></script>
<script>
$(document).ready(function() {
checkAuthentication();
loadUsers();
$('#logoutBtn').on('click', function(e) {
e.preventDefault();
if (confirm('¿Estás seguro que deseas cerrar sesión?')) {
api.logout().then(() => { window.location.href = 'auth-login.html'; });
}
});
});
function checkAuthentication() {
api.getSession().then(r => { if (!r.success) window.location.href = 'auth-login.html'; }).catch(() => { window.location.href = 'auth-login.html'; });
}
function loadUsers() {
$('#loadingSpinner').show();
$('#usersTableContainer').hide();
$('#emptyState').hide();
api.getUsers().then(function(response) {
$('#loadingSpinner').hide();
if (response.success) {
if (response.users.length > 0) {
renderUsersTable(response.users);
$('#usersTableContainer').show();
} else {
$('#emptyState').show();
}
} else {
console.error('Error cargando usuarios:', response.message);
showAlert('error', 'Error cargando usuarios: ' + response.message);
$('#emptyState').show();
}
});
}
function renderUsersTable(users) {
const tbody = $('#usersTableBody');
tbody.empty();
users.forEach(user => {
const row = `
<tr>
<td>${user.userID}</td>
<td>${user.username}</td>
<td>
<span class="badge ${user.role === 'Admin' ? 'badge-primary' : 'badge-secondary'}">
${user.role}
</span>
</td>
<td>${user.creationDate}</td>
<td class="text-center">
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-primary"
onclick="editUser(${user.userID})"
title="Editar">
<i class="fe fe-edit fe-12"></i>
</button>
<button class="btn btn-sm btn-outline-danger"
onclick="confirmDeleteUser(${user.userID})"
title="Eliminar">
<i class="fe fe-trash-2 fe-12"></i>
</button>
</div>
</td>
</tr>
`;
tbody.append(row);
});
}
function showAddUserModal() {
$('#userForm')[0].reset();
$('#userId').val('');
$('#userModalLabel').html('<i class="fe fe-user-plus mr-2"></i>Nuevo Usuario');
$('#userModal').modal('show');
}
function editUser(userId) {
api.getUsers().then(function(response) {
if (response.success) {
const user = response.users.find(u => u.userID == userId);
if (user) {
$('#userId').val(user.userID);
$('#username').val(user.username);
$('#role').val(user.role);
$('#password').val('');
$('#confirmPassword').val('');
$('#userModalLabel').html('<i class="fe fe-edit mr-2"></i>Editar Usuario');
$('#userModal').modal('show');
}
}
});
}
function saveUser() {
const userId = $('#userId').val();
const username = $('#username').val().trim();
const password = $('#password').val();
const confirmPassword = $('#confirmPassword').val();
const role = $('#role').val();
if (!username) {
showAlert('error', 'El nombre de usuario es requerido');
return;
}
if (!userId && (!password || !confirmPassword)) {
showAlert('error', 'La contraseña es requerida para nuevos usuarios');
return;
}
if (password !== confirmPassword) {
showAlert('error', 'Las contraseñas no coinciden');
return;
}
if (userId) {
const userData = {
userId: userId,
username: username,
role: role
};
if (password) {
userData.password = password;
}
api.updateUser(userId, userData).then(function(response) {
if (response.success) {
showAlert('success', 'Usuario actualizado exitosamente');
$('#userModal').modal('hide');
loadUsers();
} else {
showAlert('error', response.message || 'Error actualizando usuario');
}
});
} else {
api.createUser(username, password, role).then(function(response) {
if (response.success) {
showAlert('success', 'Usuario creado exitosamente');
$('#userModal').modal('hide');
loadUsers();
} else {
showAlert('error', response.message || 'Error creando usuario');
}
});
}
}
function confirmDeleteUser(userId) {
if (confirm('¿Estás seguro que deseas eliminar este usuario? Esta acción no se puede deshacer.')) {
deleteUser(userId);
}
}
function deleteUser(userId) {
api.deleteUser(userId).then(function(response) {
if (response.success) {
showAlert('success', 'Usuario eliminado exitosamente');
loadUsers();
} else {
showAlert('error', response.message || 'Error eliminando usuario');
}
});
}
function showAlert(type, message) {
const alertClass = {
'success': 'alert-success',
'error': 'alert-danger',
'warning': 'alert-warning',
'info': 'alert-info'
}[type] || 'alert-info';
const alertHtml = `
<div class="alert ${alertClass} alert-dismissible fade show" role="alert">
${message}
<button type="button" class="close" data-dismiss="alert">
<span aria-hidden="true">&times;</span>
</button>
</div>
`;
$('.container-fluid').prepend(alertHtml);
setTimeout(() => {
$('.alert').first().alert('close');
}, 5000);
}
</script>
<script>
function confirmLogout() {
executeLogout();
}
function executeLogout() {
api.logout().then(() => { window.location.href = 'auth-login.html'; });
}
</script>
<script>
function checkAdminAccess() {
api.getSession().then(r => { if (!r.success) { window.location.href = 'auth-login.html'; } else if (r.user.role !== 'Admin') { alert('Acceso denegado: Solo administradores'); window.location.href = 'index.html'; } }).catch(() => { window.location.href = 'auth-login.html'; });
}
function renderUsersTableSecure(users) {
const tbody = $('#usersTableBody');
tbody.empty();
api.getSession().then(function(currentUserResponse) { if (!currentUserResponse.success) return; const currentUserId = currentUserResponse.user.userID;
users.forEach(user => {
const canDelete = user.userID !== currentUserId;
const deleteButton = canDelete ?
`<button class="btn btn-sm btn-outline-danger"
onclick="confirmDeleteUser(${user.userID})"
title="Eliminar">
<i class="fe fe-trash-2 fe-12"></i>
</button>` :
`<button class="btn btn-sm btn-outline-secondary"
disabled
title="No puedes eliminar tu propia cuenta">
<i class="fe fe-user-x fe-12"></i>
</button>`;
const row = `
<tr>
<td>${user.userID}</td>
<td>
${user.username}
${user.userID === currentUserId ? '<span class="badge badge-info ml-2">Tú</span>' : ''}
</td>
<td>
<span class="badge ${user.role === 'Admin' ? 'badge-primary' : 'badge-secondary'}">
${user.role}
</span>
</td>
<td>${user.creationDate}</td>
<td class="text-center">
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-primary"
onclick="editUser(${user.userID})"
title="Editar">
<i class="fe fe-edit fe-12"></i>
</button>
${deleteButton}
</div>
</td>
</tr>
`;
tbody.append(row);
});
});
}
function confirmDeleteUserSecure(userId) {
api.getSession().then(function(response) { if (!response.success) { showAlert('error', 'Error de autenticación'); return; } if (response.user.userID === userId) {
showAlert('error', 'No puedes eliminar tu propia cuenta');
return;
}
const confirmation = confirm(
'ELIMINAR USUARIO\n\n' +
'¿Estás seguro que deseas eliminar este usuario?\n\n' +
'• Esta acción NO se puede deshacer\n' +
'• Se perderán todos los datos asociados\n' +
'• El usuario no podrá acceder al sistema\n\n' +
'Escribe "CONFIRMAR" para continuar:'
);
if (confirmation) {
const secondConfirmation = prompt(
'Para confirmar la eliminación, escribe: CONFIRMAR'
);
if (secondConfirmation === 'CONFIRMAR') {
deleteUser(userId);
} else {
showAlert('info', 'Eliminación cancelada - Confirmación incorrecta');
}
}
});
}
$(document).ready(function() {
checkAdminAccess();
setTimeout(() => {
checkAuthentication();
loadUsers();
$('#logoutBtn').on('click', function(e) {
e.preventDefault();
if (confirm('¿Estás seguro que deseas cerrar sesión?')) {
api.logout().then(() => { window.location.href = 'auth-login.html'; });
}
});
}, 500); // Pequeño delay para asegurar que la verificación de admin se complete
});
window.renderUsersTable = renderUsersTableSecure;
window.confirmDeleteUser = confirmDeleteUserSecure;
</script>
<script>
(function() {
function centerWindow() {
try {
const screenWidth = window.screen.availWidth;
const screenHeight = window.screen.availHeight;
const x = Math.floor((screenWidth - 1200) / 2);
const y = Math.floor((screenHeight - 800) / 2);
if (window.moveTo) {
window.moveTo(Math.max(0, x), Math.max(0, y));
}
} catch (e) {
console.warn('No se pudo centrar ventana:', e);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', centerWindow);
} else {
centerWindow();
}
window.addEventListener('load', centerWindow);
window.addEventListener('focus', centerWindow);
})();
</script>
</body>
</html>