modalized edit worker + global css

This commit is contained in:
2026-03-19 23:32:55 -03:00
parent a1bfb25abf
commit e430c732bd
7 changed files with 125 additions and 118 deletions

5
app.py
View File

@@ -290,7 +290,7 @@ def manage_workers():
flash("El RUT ya existe en el sistema.", "danger")
# Fetch workers and JOIN their module name
c.execute('''SELECT w.id, w.rut, w.name, w.phone, m.name
c.execute('''SELECT w.id, w.rut, w.name, w.phone, m.name, w.modulo_id
FROM workers w
LEFT JOIN modulos m ON w.modulo_id = m.id
WHERE w.is_admin = 0''')
@@ -378,7 +378,8 @@ def admin_reset_password(id):
conn.close()
flash(f"Contraseña de {worker[0]} restablecida. La nueva contraseña es: <strong>{new_password}</strong>", "warning")
return redirect(url_for('edit_worker', id=id))
return redirect(url_for('manage_workers'))
@app.route('/admin/estructura', methods=['GET', 'POST'])

12
static/style.css Normal file
View File

@@ -0,0 +1,12 @@
[data-bs-theme="dark"] .form-control[readonly] {
background-color: #1a1d21; /* Un fondo casi negro, más oscuro que el modal */
border-color: #373b3e; /* Un borde sutil */
color: #e3e6e8; /* Texto blanco grisáceo, muy legible */
opacity: 1; /* Evita la opacidad predeterminada de Bootstrap */
cursor: not-allowed; /* Refuerza visualmente que no es editable */
}
[data-bs-theme="dark"] .text-muted-rut {
color: #adb5bd !important; /* Un gris medio para el label "RUT (No editable)" */
}

View File

@@ -7,7 +7,6 @@
<!-- HEAD -->
{% endblock %}
{% block content %}
<h2 class="mb-4">Catálogo de Productos por Zona</h2>
@@ -148,9 +147,7 @@
});
});
}
</script>
<script>
document.querySelectorAll('.money-input').forEach(function(input) {
input.addEventListener('input', function(e) {
let value = this.value.replace(/\D/g, '');

View File

@@ -1,5 +1,5 @@
{% extends "macros/base.html" %}
{% from 'macros/modals.html' import confirm_modal, edit_worker_modal %}
{% block title %}Gestión de Trabajadores{% endblock %}
@@ -18,6 +18,8 @@
{% endif %}
{% endwith %}
{{ edit_worker_modal(modulos) }}
<div class="card mb-4">
<div class="card-header bg-secondary text-white">Agregar Nuevo Trabajador</div>
<div class="card-body">
@@ -70,10 +72,28 @@
<td class="align-middle">{{ worker[3] }}</td>
<td class="align-middle"><span class="badge bg-info text-dark">{{ worker[4] }}</span></td>
<td class="text-end">
<a href="{{ url_for('edit_worker', id=worker[0]) }}" class="btn btn-sm btn-outline-info">Editar</a>
<form action="{{ url_for('delete_worker', id=worker[0]) }}" method="POST" class="d-inline">
<button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('¿Eliminar a este trabajador?');">Eliminar</button>
</form>
<button type="button"
class="btn btn-sm btn-outline-info"
data-bs-toggle="modal"
data-bs-target="#editWorkerModal"
data-id="{{ worker[0] }}"
data-rut="{{ worker[1] }}"
data-name="{{ worker[2] }}"
data-phone="{{ worker[3] }}"
data-modulo="{{ worker[5] }}"> Editar
</button>
<button type="button" class="btn btn-sm btn-outline-danger" data-bs-toggle="modal" data-bs-target="#delWorker{{ worker[0] }}">
Eliminar
</button>
{{ confirm_modal(
id='delWorker' ~ worker[0],
title='Eliminar Trabajador',
message='¿Eliminar a ' ~ worker[2] ~ '?',
action_url=url_for('delete_worker', id=worker[0]),
btn_class='btn-danger'
) }}
</td>
</tr>
{% else %}
@@ -87,6 +107,27 @@
</div>
<script>
const editWorkerModal = document.getElementById('editWorkerModal');
if (editWorkerModal) {
editWorkerModal.addEventListener('show.bs.modal', function (event) {
const button = event.relatedTarget;
const id = button.getAttribute('data-id');
// IMPORTANTE: Definir las rutas correctamente
const editForm = editWorkerModal.querySelector('#editWorkerForm');
const resetForm = editWorkerModal.querySelector('#resetPasswordForm');
editForm.action = "/admin/workers/edit/" + id;
resetForm.action = "/admin/workers/reset_password/" + id;
// Rellenar campos
editWorkerModal.querySelector('#edit_worker_rut').value = button.getAttribute('data-rut');
editWorkerModal.querySelector('#edit_worker_name').value = button.getAttribute('data-name');
editWorkerModal.querySelector('#edit_worker_phone').value = button.getAttribute('data-phone');
editWorkerModal.querySelector('#edit_worker_modulo').value = button.getAttribute('data-modulo');
});
}
document.getElementById('rutInput').addEventListener('input', function(e) {
let value = this.value.replace(/[^0-9kK]/g, '').toUpperCase();
if (value.length > 1) {
@@ -99,18 +140,17 @@
}
});
document.getElementById('phoneInput').addEventListener('input', function(e) {
let value = this.value.replace(/\D/g, '');
function formatPhone(input) {
let value = input.value.replace(/\D/g, '');
if (value.startsWith('56')) value = value.substring(2);
value = value.substring(0, 9);
if (value.length > 5) input.value = value.replace(/(\d{1})(\d{4})(\d+)/, '$1 $2 $3');
else if (value.length > 1) input.value = value.replace(/(\d{1})(\d+)/, '$1 $2');
else input.value = value;
}
if (value.length > 5) {
this.value = value.replace(/(\d{1})(\d{4})(\d+)/, '$1 $2 $3');
} else if (value.length > 1) {
this.value = value.replace(/(\d{1})(\d+)/, '$1 $2');
} else {
this.value = value;
}
document.querySelectorAll('.phone-input, #phoneInput').forEach(inp => {
inp.addEventListener('input', () => formatPhone(inp));
});
</script>
{% endblock %}

View File

@@ -1,97 +0,0 @@
{% extends "macros/base.html" %}
{% block title %}Editar Trabajador{% endblock %}
{% block head %}
<!-- HEAD -->
{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-8">
<h2 class="mb-4">Editar Trabajador</h2>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message|safe }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="card mb-4">
<div class="card-body">
<form method="POST">
<div class="mb-3">
<label class="form-label">RUT</label>
<input type="text" class="form-control text-muted" value="{{ worker[1] }}" readonly disabled>
</div>
<div class="mb-3">
<label class="form-label">Nombre Completo</label>
<input type="text" class="form-control" name="name" value="{{ worker[2] }}" required>
</div>
<div class="mb-4">
<label class="form-label">Teléfono</label>
<input type="text" class="form-control" name="phone" id="phoneInput" value="{{ worker[3] }}" required>
</div>
<div class="mb-4">
<label class="form-label">Módulo Asignado</label>
<select class="form-select" name="modulo_id" required>
<option value="" disabled>Seleccionar Módulo...</option>
{% for mod in modulos %}
<option value="{{ mod[0] }}" {% if mod[0] == worker[4] %}selected{% endif %}>
{{ mod[2] }} - {{ mod[1] }}
</option>
{% endfor %}
</select>
</div>
<div class="d-flex justify-content-between">
<a href="{{ url_for('manage_workers') }}" class="btn btn-secondary">Cancelar</a>
<button type="submit" class="btn btn-primary">Actualizar Datos</button>
</div>
</form>
</div>
</div>
<div class="card border-warning">
<div class="card-header bg-warning text-dark border-warning">
<strong>Opciones de Seguridad</strong>
</div>
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1">Restablecer Contraseña</h6>
<small class="text-muted">Genera una nueva contraseña aleatoria si el trabajador olvidó la suya.</small>
</div>
<form action="{{ url_for('admin_reset_password', id=worker[0]) }}" method="POST">
<button type="submit" class="btn btn-outline-warning" onclick="return confirm('¿Generar nueva contraseña? La anterior dejará de funcionar inmediatamente.');">
Generar Nueva
</button>
</form>
</div>
</div>
</div>
</div>
<script>
document.getElementById('phoneInput').addEventListener('input', function(e) {
let value = this.value.replace(/\D/g, '');
if (value.startsWith('56')) value = value.substring(2);
value = value.substring(0, 9);
if (value.length > 5) {
this.value = value.replace(/(\d{1})(\d{4})(\d+)/, '$1 $2 $3');
} else if (value.length > 1) {
this.value = value.replace(/(\d{1})(\d+)/, '$1 $2');
} else {
this.value = value;
}
});
window.addEventListener('DOMContentLoaded', () => {
document.getElementById('phoneInput').dispatchEvent(new Event('input'));
});
</script>
{% endblock %}

View File

@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema de Rendiciones - {% block title %}{% endblock %}</title>
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.png') }}" type="image/x-icon">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
</head>

View File

@@ -62,3 +62,57 @@
</div>
</div>
{% endmacro %}
{% macro edit_worker_modal(modulos) %}
<div class="modal fade" id="editWorkerModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Editar Trabajador</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="editWorkerForm" method="POST" action="">
<div class="mb-3">
<label class="form-label text-muted-rut">RUT (No editable)</label>
<input type="text" class="form-control" id="edit_worker_rut" readonly>
</div>
<div class="mb-3">
<label class="form-label">Nombre Completo</label>
<input type="text" class="form-control" name="name" id="edit_worker_name" required>
</div>
<div class="mb-3">
<label class="form-label">Teléfono</label>
<input type="text" class="form-control phone-input" name="phone" id="edit_worker_phone" required>
</div>
<div class="mb-3">
<label class="form-label">Módulo Asignado</label>
<select class="form-select" name="modulo_id" id="edit_worker_modulo" required>
{% for mod in modulos %}
<option value="{{ mod[0] }}">{{ mod[2] }} - {{ mod[1] }}</option>
{% endfor %}
</select>
</div>
</form>
<hr>
<div class="d-grid">
<form id="resetPasswordForm" method="POST" action="" onsubmit="return confirm('¿Generar nueva contraseña?');">
<button type="submit" class="btn btn-outline-warning btn-sm w-100">
<i class="bi bi-key me-1"></i> Restablecer Contraseña
</button>
</form>
<small class="text-muted text-center mt-1">Generará una nueva clave aleatoria.</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" form="editWorkerForm" class="btn btn-primary">Guardar Cambios</button>
</div>
</div>
</div>
</div>
{% endmacro %}