diff --git a/data/datos.db b/data/datos.db
new file mode 100644
index 0000000..113e80e
Binary files /dev/null and b/data/datos.db differ
diff --git a/frontend/css/styles.css b/frontend/css/styles.css
index a4f0e0c..77117d2 100644
--- a/frontend/css/styles.css
+++ b/frontend/css/styles.css
@@ -110,3 +110,291 @@ body {
color: #666;
font-size: 1.1rem;
}
+
+.section-actions {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 1.5rem;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.65rem 1.25rem;
+ font-size: 0.95rem;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.btn-primary {
+ background-color: #1abc9c;
+ color: white;
+}
+
+.btn-primary:hover {
+ background-color: #16a085;
+}
+
+.btn-secondary {
+ background-color: #95a5a6;
+ color: white;
+}
+
+.btn-secondary:hover {
+ background-color: #7f8c8d;
+}
+
+.btn-icon {
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ font-size: 1.1rem;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ transition: background-color 0.2s;
+}
+
+.btn-icon:hover {
+ background-color: #ecf0f1;
+}
+
+.table-container {
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
+ overflow-x: auto;
+}
+
+.data-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.95rem;
+}
+
+.data-table th,
+.data-table td {
+ padding: 1rem;
+ text-align: left;
+ border-bottom: 1px solid #ecf0f1;
+}
+
+.data-table th {
+ background-color: #f8f9fa;
+ color: #2c3e50;
+ font-weight: 600;
+}
+
+.data-table tbody tr:hover {
+ background-color: #f8f9fa;
+}
+
+.data-table .empty {
+ text-align: center;
+ color: #7f8c8d;
+}
+
+.badge {
+ display: inline-block;
+ padding: 0.35rem 0.7rem;
+ border-radius: 12px;
+ font-size: 0.85rem;
+ font-weight: 600;
+}
+
+.badge-active {
+ background-color: #d5f5e3;
+ color: #1e8449;
+}
+
+.badge-inactive {
+ background-color: #fadbd8;
+ color: #c0392b;
+}
+
+.modal {
+ position: fixed;
+ inset: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.modal.hidden {
+ display: none;
+}
+
+.modal-backdrop {
+ position: absolute;
+ inset: 0;
+ background-color: rgba(0, 0, 0, 0.45);
+}
+
+.modal-content {
+ position: relative;
+ background: white;
+ border-radius: 8px;
+ width: 90%;
+ max-width: 700px;
+ max-height: 90vh;
+ overflow-y: auto;
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
+ z-index: 1001;
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1.25rem 1.5rem;
+ border-bottom: 1px solid #ecf0f1;
+}
+
+.modal-header h2 {
+ color: #2c3e50;
+ font-size: 1.25rem;
+}
+
+.btn-close {
+ background: transparent;
+ border: none;
+ font-size: 1.5rem;
+ color: #7f8c8d;
+ cursor: pointer;
+}
+
+.btn-close:hover {
+ color: #2c3e50;
+}
+
+.modal form {
+ padding: 1.5rem;
+}
+
+.form-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem 1.5rem;
+}
+
+.form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+}
+
+.form-group label {
+ font-size: 0.9rem;
+ color: #555;
+ font-weight: 600;
+}
+
+.form-group label.readonly-label {
+ color: #95a5a6;
+ cursor: not-allowed;
+}
+
+.form-group input,
+.form-group select {
+ padding: 0.65rem 0.75rem;
+ border: 1px solid #d5d8dc;
+ border-radius: 6px;
+ font-size: 0.95rem;
+}
+
+.form-group input:focus,
+.form-group select:focus {
+ outline: none;
+ border-color: #1abc9c;
+ box-shadow: 0 0 0 2px rgba(26, 188, 156, 0.15);
+}
+
+.modal-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 0.75rem;
+ margin-top: 1.5rem;
+ padding-top: 1rem;
+ border-top: 1px solid #ecf0f1;
+}
+
+.checkbox-inline {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-top: 0.5rem;
+ font-size: 0.9rem;
+ color: #555;
+ font-weight: normal;
+ cursor: pointer;
+}
+
+.checkbox-inline input[type="checkbox"] {
+ width: auto;
+ cursor: pointer;
+}
+
+input:disabled,
+input[readonly] {
+ background-color: #ecf0f1;
+ cursor: not-allowed;
+ color: #7f8c8d;
+}
+
+.form-group input[readonly] + label,
+.form-group label.readonly-label {
+ color: #95a5a6;
+ cursor: not-allowed;
+}
+
+.config-card {
+ background: #ffffff;
+ border-radius: 10px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ padding: 2rem;
+ width: 100%;
+ max-width: 1200px;
+}
+
+.config-header {
+ margin-bottom: 1.5rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid #ecf0f1;
+}
+
+.config-header h3 {
+ color: #2c3e50;
+ font-size: 1.25rem;
+ margin-bottom: 0.35rem;
+}
+
+.config-header p {
+ color: #7f8c8d;
+ font-size: 0.95rem;
+}
+
+.config-card .form-grid {
+ grid-template-columns: repeat(3, 1fr);
+}
+
+.config-footer {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ margin-top: 1.5rem;
+ padding-top: 1rem;
+ border-top: 1px solid #ecf0f1;
+}
+
+.mensaje {
+ font-size: 0.9rem;
+ color: #1e8449;
+ font-weight: 600;
+}
+
+.pesos-input {
+ text-align: right;
+}
diff --git a/frontend/index.html b/frontend/index.html
index 74f61e6..c49493f 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -8,12 +8,17 @@
+
-
+
+
+
+
-
- Gestión de trabajadores.
+
+ Seguimiento de nóminas y empleados.
+
+
+
+
+
+
+
+
+
+ | Documento |
+ Nombre completo |
+ Puesto |
+ Salario mensual |
+ Estado |
+ Acciones |
+
+
+
+
+ | No hay trabajadores registrados. |
+
+
+
+
+
+
+
+
+
@@ -51,9 +152,72 @@
Gestión de liquidaciones.
+
+
+
+
+
+
+ Historial de cambios y movimientos.
+
diff --git a/frontend/js/app.js b/frontend/js/app.js
index 592eb3f..6a1fb4f 100644
--- a/frontend/js/app.js
+++ b/frontend/js/app.js
@@ -1,13 +1,17 @@
+//// PASAR DE UN BOTON A OTRO BOTON
document.addEventListener('DOMContentLoaded', function () {
const menuButtons = document.querySelectorAll('.menu-btn');
const sections = document.querySelectorAll('.section');
const sectionTitle = document.getElementById('section-title');
const titles = {
+ seguimiento: 'Seguimiento',
trabajadores: 'Trabajadores',
nominas: 'Nóminas',
liquidaciones: 'Liquidaciones',
- licencia: 'Licencia'
+ configuraciones: 'Configuraciones',
+ licencia: 'Licencia',
+ historial: 'Historial'
};
menuButtons.forEach(button => {
@@ -23,4 +27,272 @@ document.addEventListener('DOMContentLoaded', function () {
sectionTitle.textContent = titles[target] || '';
});
});
+
+//// MODULO DE TRABAJADORES
+ const trabajadores = [];
+ let editandoId = null;
+
+ const modalTrabajador = document.getElementById('modal-trabajador');
+ const formTrabajador = document.getElementById('form-trabajador');
+ const btnNuevoTrabajador = document.getElementById('btn-nuevo-trabajador');
+ const btnCerrarModal = document.getElementById('btn-cerrar-modal-trabajador');
+ const btnCancelar = document.getElementById('btn-cancelar-trabajador');
+ const modalTitulo = document.getElementById('modal-titulo-trabajador');
+ const tablaBody = document.querySelector('#tabla-trabajadores tbody');
+ const chkSmv = document.getElementById('chk-smv');
+ const inputSalario = document.getElementById('trabajador-salario');
+
+ let configuracionGlobal = {
+ id: null,
+ nombre_empresa: '',
+ salario_minimo: '',
+ auxilio_transporte: '',
+ horas_semanales: '',
+ horas_mensuales: '',
+ dia_inicio_mes: '',
+ dia_fin_mes: '',
+ valor_hora_ordinaria: '',
+ valor_hora_extra: '',
+ valor_hora_extra_nocturna: ''
+ };
+
+ function formatearPesos(valor) {
+ const partes = Number(valor).toFixed(2).split('.');
+ const entera = partes[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+ const decimal = ',' + partes[1];
+ return entera + decimal;
+ }
+
+ function formatearPesosSinDecimales(valor) {
+ const numero = Number(valor);
+ if (isNaN(numero)) return '';
+ return numero.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+ }
+
+ function limpiarFormatoPesos(valor) {
+ return String(valor || '').replace(/\./g, '').replace(',', '.');
+ }
+
+ function abrirModal(modo = 'nuevo') {
+ modalTrabajador.classList.remove('hidden');
+ modalTitulo.textContent = modo === 'editar' ? 'Editar trabajador' : 'Nuevo trabajador';
+ }
+
+ function cerrarModal() {
+ modalTrabajador.classList.add('hidden');
+ formTrabajador.reset();
+ document.getElementById('trabajador-id-original').value = '';
+ editandoId = null;
+ }
+
+ function renderTabla() {
+ tablaBody.innerHTML = '';
+
+ if (trabajadores.length === 0) {
+ tablaBody.innerHTML = '| No hay trabajadores registrados. |
';
+ return;
+ }
+
+ trabajadores.forEach(t => {
+ const tr = document.createElement('tr');
+ tr.innerHTML = `
+ ${t.id} |
+ ${t.nombre} ${t.apellidos} |
+ ${t.puesto || '-'} |
+ ${t.salario_mensual ? formatearPesos(Number(t.salario_mensual)) : '-'} |
+
+
+ ${t.activo == 1 ? 'Activo' : 'Inactivo'}
+
+ |
+
+
+
+ |
+ `;
+
+ tr.querySelector('.btn-editar').addEventListener('click', () => cargarEdicion(t.id));
+ tr.querySelector('.btn-eliminar').addEventListener('click', () => eliminarTrabajador(t.id));
+
+ tablaBody.appendChild(tr);
+ });
+ }
+
+ function guardarTrabajador(e) {
+ e.preventDefault();
+
+ const datos = {
+ id: document.getElementById('trabajador-id').value.trim(),
+ nombre: document.getElementById('trabajador-nombre').value.trim(),
+ apellidos: document.getElementById('trabajador-apellidos').value.trim(),
+ puesto: document.getElementById('trabajador-puesto').value.trim(),
+ salario_mensual: document.getElementById('trabajador-salario').value,
+ fecha_ingreso: document.getElementById('trabajador-fecha').value,
+ seguridad_social: document.getElementById('trabajador-seguridad').value.trim(),
+ activo: document.getElementById('trabajador-activo').value
+ };
+
+ if (editandoId !== null) {
+ const index = trabajadores.findIndex(t => t.id === editandoId);
+ if (index !== -1) {
+ trabajadores[index] = { ...datos, idOriginal: editandoId };
+ }
+ } else {
+ const existe = trabajadores.some(t => t.id === datos.id);
+ if (existe) {
+ alert('Ya existe un trabajador con ese documento de identidad.');
+ return;
+ }
+ trabajadores.push(datos);
+ }
+
+ cerrarModal();
+ renderTabla();
+ }
+
+ function cargarEdicion(id) {
+ const t = trabajadores.find(item => item.id === id);
+ if (!t) return;
+
+ editandoId = id;
+ document.getElementById('trabajador-id-original').value = id;
+ document.getElementById('trabajador-id').value = t.id;
+ document.getElementById('trabajador-nombre').value = t.nombre;
+ document.getElementById('trabajador-apellidos').value = t.apellidos;
+ document.getElementById('trabajador-puesto').value = t.puesto;
+ document.getElementById('trabajador-salario').value = t.salario_mensual;
+ document.getElementById('trabajador-fecha').value = t.fecha_ingreso;
+ document.getElementById('trabajador-seguridad').value = t.seguridad_social;
+ document.getElementById('trabajador-activo').value = t.activo;
+
+ abrirModal('editar');
+ }
+
+ function eliminarTrabajador(id) {
+ if (!confirm('¿Estás seguro de eliminar este trabajador?')) return;
+ const index = trabajadores.findIndex(t => t.id === id);
+ if (index !== -1) {
+ trabajadores.splice(index, 1);
+ renderTabla();
+ }
+ }
+
+ btnNuevoTrabajador.addEventListener('click', () => abrirModal('nuevo'));
+ btnCerrarModal.addEventListener('click', cerrarModal);
+ btnCancelar.addEventListener('click', cerrarModal);
+ formTrabajador.addEventListener('submit', guardarTrabajador);
+
+ if (chkSmv && inputSalario) {
+ chkSmv.addEventListener('change', function () {
+ if (this.checked) {
+ const smv = configuracionGlobal.salario_minimo;
+ if (!smv) {
+ alert('No hay un salario mínimo configurado. Ve a Configuraciones y guárdalo primero.');
+ this.checked = false;
+ return;
+ }
+ inputSalario.value = smv;
+ inputSalario.disabled = true;
+ } else {
+ inputSalario.disabled = false;
+ }
+ });
+ }
+
+ renderTabla();
+
+ // --- Módulo de Configuraciones ---
+ const formConfiguraciones = document.getElementById('form-configuraciones');
+ const btnGuardarConfiguraciones = document.getElementById('btn-guardar-configuraciones');
+ const cfgMensaje = document.getElementById('cfg-mensaje');
+
+ const camposPesosSinDecimales = [
+ 'cfg-salario-minimo',
+ 'cfg-auxilio-transporte',
+ 'cfg-hora-ordinaria'
+ ];
+
+ const inputHorasSemanales = document.getElementById('cfg-horas-semanales');
+ const inputHorasMensuales = document.getElementById('cfg-horas-mensuales');
+ const inputHoraOrdinaria = document.getElementById('cfg-hora-ordinaria');
+ const inputHoraExtra = document.getElementById('cfg-hora-extra');
+ const inputHoraExtraNocturna = document.getElementById('cfg-hora-extra-nocturna');
+
+ function aplicarFormatoPesosSinDecimales(input) {
+ const limpio = limpiarFormatoPesos(input.value);
+ const numero = Number(limpio);
+ input.value = isNaN(numero) ? '' : formatearPesosSinDecimales(numero);
+ }
+
+ function calcularCamposDependientes() {
+ const horasSemanales = Number(inputHorasSemanales.value);
+ if (!isNaN(horasSemanales) && inputHorasMensuales) {
+ inputHorasMensuales.value = (horasSemanales * 5).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+ }
+
+ const horaOrdinaria = Number(limpiarFormatoPesos(inputHoraOrdinaria.value));
+ if (!isNaN(horaOrdinaria)) {
+ if (inputHoraExtra) {
+ inputHoraExtra.value = formatearPesosSinDecimales(horaOrdinaria * 1.25);
+ }
+ if (inputHoraExtraNocturna) {
+ inputHoraExtraNocturna.value = formatearPesosSinDecimales(horaOrdinaria * 1.75);
+ }
+ }
+ }
+
+ function cargarConfiguracionEnFormulario() {
+ document.getElementById('cfg-nombre-empresa').value = configuracionGlobal.nombre_empresa;
+ document.getElementById('cfg-salario-minimo').value = configuracionGlobal.salario_minimo ? formatearPesosSinDecimales(configuracionGlobal.salario_minimo) : '';
+ document.getElementById('cfg-auxilio-transporte').value = configuracionGlobal.auxilio_transporte ? formatearPesosSinDecimales(configuracionGlobal.auxilio_transporte) : '';
+ document.getElementById('cfg-horas-semanales').value = configuracionGlobal.horas_semanales;
+ document.getElementById('cfg-horas-mensuales').value = configuracionGlobal.horas_mensuales;
+ document.getElementById('cfg-dia-inicio').value = configuracionGlobal.dia_inicio_mes;
+ document.getElementById('cfg-dia-fin').value = configuracionGlobal.dia_fin_mes;
+ document.getElementById('cfg-hora-ordinaria').value = configuracionGlobal.valor_hora_ordinaria ? formatearPesosSinDecimales(configuracionGlobal.valor_hora_ordinaria) : '';
+ calcularCamposDependientes();
+ }
+
+ function guardarConfiguracion(e) {
+ e.preventDefault();
+
+ configuracionGlobal = {
+ id: configuracionGlobal.id || 1,
+ nombre_empresa: document.getElementById('cfg-nombre-empresa').value.trim(),
+ salario_minimo: limpiarFormatoPesos(document.getElementById('cfg-salario-minimo').value),
+ auxilio_transporte: limpiarFormatoPesos(document.getElementById('cfg-auxilio-transporte').value),
+ horas_semanales: document.getElementById('cfg-horas-semanales').value,
+ horas_mensuales: document.getElementById('cfg-horas-mensuales').value,
+ dia_inicio_mes: document.getElementById('cfg-dia-inicio').value,
+ dia_fin_mes: document.getElementById('cfg-dia-fin').value,
+ valor_hora_ordinaria: limpiarFormatoPesos(document.getElementById('cfg-hora-ordinaria').value),
+ valor_hora_extra: limpiarFormatoPesos(document.getElementById('cfg-hora-extra').value),
+ valor_hora_extra_nocturna: limpiarFormatoPesos(document.getElementById('cfg-hora-extra-nocturna').value)
+ };
+
+ cargarConfiguracionEnFormulario();
+
+ if (cfgMensaje) {
+ cfgMensaje.textContent = 'Configuración guardada correctamente.';
+ setTimeout(() => { cfgMensaje.textContent = ''; }, 3000);
+ }
+ }
+
+ if (formConfiguraciones) {
+ camposPesosSinDecimales.forEach(id => {
+ const input = document.getElementById(id);
+ if (input) {
+ input.addEventListener('blur', () => aplicarFormatoPesosSinDecimales(input));
+ }
+ });
+
+ inputHorasSemanales.addEventListener('input', calcularCamposDependientes);
+ inputHoraOrdinaria.addEventListener('blur', function () {
+ aplicarFormatoPesosSinDecimales(this);
+ calcularCamposDependientes();
+ });
+
+ formConfiguraciones.addEventListener('submit', guardarConfiguracion);
+ cargarConfiguracionEnFormulario();
+ }
});