fix(auth/users): corregir edicion de contraseñas de usuarios, cambio de contraseña y evitar pantalla en blanco

This commit is contained in:
2026-09-23 15:38:48 -03:00
parent b540015b7e
commit 6ecaef9b9d
8 changed files with 428 additions and 21 deletions
+25 -3
View File
@@ -38,11 +38,19 @@
</div>
<div class="card-body p-4">
<form method="POST">
{% if error %}
<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>{{ error }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
<form method="POST" action="{% if user and user.id %}/admin/users/edit/{{ user.id }}{% else %}/admin/users/new{% endif %}">
{% if user %}
<input type="hidden" name="id" value="{{ user.id }}">
{% endif %}
<!-- SECCIÓN 1: IDENTIDAD Y DOCUMENTACIÓN -->
<div class="mb-4">
<h6 class="text-uppercase text-primary fw-bold small border-bottom pb-2 mb-3">
@@ -164,12 +172,15 @@
<div class="input-group">
<span class="input-group-text bg-body-tertiary"><i class="bi bi-key"></i></span>
<input type="password" class="form-control" id="password" name="password"
{% if not user %}required{% endif %}
{% if not user %}required minlength="6"{% else %}minlength="6"{% endif %}
placeholder="{% if user %}Dejar en blanco para mantener{% else %}Mínimo 6 caracteres{% endif %}">
<button class="btn btn-outline-secondary" type="button" id="togglePasswordBtn" title="Mostrar u ocultar contraseña">
<i class="bi bi-eye" id="togglePasswordIcon"></i>
</button>
</div>
{% if user %}
<small class="text-muted d-block mt-1">
Solo complete este campo si desea restablecer la contraseña del usuario.
Solo complete este campo si desea restablecer la contraseña del usuario (mínimo 6 caracteres).
</small>
{% endif %}
</div>
@@ -250,6 +261,17 @@ document.addEventListener('DOMContentLoaded', function() {
docTypeSelect.addEventListener('change', updateDocHint);
updateDocHint();
}
var togglePasswordBtn = document.getElementById('togglePasswordBtn');
var passwordInput = document.getElementById('password');
var togglePasswordIcon = document.getElementById('togglePasswordIcon');
if (togglePasswordBtn && passwordInput && togglePasswordIcon) {
togglePasswordBtn.addEventListener('click', function() {
var isPass = passwordInput.getAttribute('type') === 'password';
passwordInput.setAttribute('type', isPass ? 'text' : 'password');
togglePasswordIcon.className = isPass ? 'bi bi-eye-slash' : 'bi bi-eye';
});
}
});
</script>
{% endblock %}
+94 -1
View File
@@ -27,6 +27,11 @@
<i class="bi bi-check-circle-fill me-2"></i>Los datos del usuario han sido guardados exitosamente.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% elif msg == 'password_updated' %}
<div class="alert alert-success alert-dismissible fade show shadow-sm mb-4" role="alert">
<i class="bi bi-shield-check me-2"></i>La contraseña del usuario ha sido restablecida exitosamente.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% elif error %}
<div class="alert alert-danger alert-dismissible fade show shadow-sm mb-4" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>{{ error }}
@@ -182,6 +187,13 @@
<a href="/admin/users/edit/{{ u.id }}" class="btn btn-outline-primary" title="Editar Ficha de Usuario">
<i class="bi bi-pencil"></i>
</a>
<button type="button" class="btn btn-outline-warning btn-reset-password"
data-user-id="{{ u.id }}"
data-user-name="{{ u.name }}"
data-user-email="{{ u.email }}"
title="Restablecer Contraseña">
<i class="bi bi-key"></i>
</button>
{% if u.id != user.id %}
<a href="/auth/impersonate/{{ u.id }}" class="btn btn-outline-info" title="Impersonar usuario (Iniciar sesión como {{ u.name }})">
<i class="bi bi-incognito"></i>
@@ -274,6 +286,54 @@
</div>
</div>
</div>
<!-- Modal Restablecimiento Directo de Contraseña -->
<div class="modal fade" id="resetPasswordModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg" style="border-radius: 14px;">
<div class="modal-header border-0 pb-0">
<div class="d-flex align-items-center">
<div class="badge bg-warning-subtle text-warning p-2 rounded-circle me-2">
<i class="bi bi-key-fill fs-5"></i>
</div>
<h5 class="modal-title fw-bold mb-0">Restablecer Contraseña</h5>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="resetPasswordForm" method="POST" action="/admin/users/password">
<input type="hidden" name="id" id="resetPasswordUserId">
<div class="modal-body text-start py-3">
<p class="mb-2 text-muted small">
Ingrese la nueva contraseña de acceso institucional para el siguiente usuario:
</p>
<div class="p-2 rounded bg-body-tertiary mb-3">
<div class="fw-bold text-primary" id="resetPasswordUserName"></div>
<div class="small text-muted font-monospace" id="resetPasswordUserEmail"></div>
</div>
<div class="mb-3">
<label for="modalNewPassword" class="form-label small fw-semibold">Nueva Contraseña <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-text bg-body-tertiary"><i class="bi bi-lock"></i></span>
<input type="password" class="form-control" id="modalNewPassword" name="password" required minlength="6" placeholder="Mínimo 6 caracteres">
<button class="btn btn-outline-secondary" type="button" id="toggleModalPasswordBtn" title="Mostrar/Ocultar clave">
<i class="bi bi-eye" id="toggleModalPasswordIcon"></i>
</button>
</div>
<div class="form-text small">
La contraseña debe contener al menos 6 caracteres.
</div>
</div>
</div>
<div class="modal-footer border-0 pt-0">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-warning px-3 fw-semibold">
<i class="bi bi-check-circle me-1"></i>Guardar Contraseña
</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
@@ -295,7 +355,6 @@ document.addEventListener('DOMContentLoaded', function() {
if (emailEl) emailEl.textContent = email;
if (formEl) {
formEl.action = actionUrl;
// Add hidden input with id
var hiddenId = formEl.querySelector('input[name="id"]');
if (!hiddenId) {
hiddenId = document.createElement('input');
@@ -308,6 +367,40 @@ document.addEventListener('DOMContentLoaded', function() {
if (deleteModal) deleteModal.show();
});
});
// Reset Password Modal
var resetModalEl = document.getElementById('resetPasswordModal');
var resetModal = resetModalEl ? new bootstrap.Modal(resetModalEl) : null;
var resetUserIdInput = document.getElementById('resetPasswordUserId');
var resetUserName = document.getElementById('resetPasswordUserName');
var resetUserEmail = document.getElementById('resetPasswordUserEmail');
var resetPasswordForm = document.getElementById('resetPasswordForm');
var modalNewPassword = document.getElementById('modalNewPassword');
document.querySelectorAll('.btn-reset-password').forEach(function(btn) {
btn.addEventListener('click', function() {
var userId = this.getAttribute('data-user-id');
var name = this.getAttribute('data-user-name');
var email = this.getAttribute('data-user-email');
if (resetUserIdInput) resetUserIdInput.value = userId;
if (resetUserName) resetUserName.textContent = name;
if (resetUserEmail) resetUserEmail.textContent = email;
if (resetPasswordForm) resetPasswordForm.action = '/admin/users/' + userId + '/password';
if (modalNewPassword) modalNewPassword.value = '';
if (resetModal) resetModal.show();
});
});
var toggleModalPasswordBtn = document.getElementById('toggleModalPasswordBtn');
var toggleModalPasswordIcon = document.getElementById('toggleModalPasswordIcon');
if (toggleModalPasswordBtn && modalNewPassword && toggleModalPasswordIcon) {
toggleModalPasswordBtn.addEventListener('click', function() {
var isPass = modalNewPassword.getAttribute('type') === 'password';
modalNewPassword.setAttribute('type', isPass ? 'text' : 'password');
toggleModalPasswordIcon.className = isPass ? 'bi bi-eye-slash' : 'bi bi-eye';
});
}
});
</script>
{% endblock %}