index3.html
Código:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema de Gestión de Envíos</title>
<link rel="stylesheet" href="styles.css">
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> -->
<!-- <script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script> -->
<script>
// Inicialización global para jsPDF
//window.jsPDF = window.jspdf.jsPDF; window.jsPDF = window.jspdf.jsPDF; // Uncaught TypeError: Cannot read properties of undefined (reading 'jsPDF')at index3.html:14:37//
</script>
</head>
<body>
<div class="container">
<h1>Sistema de Gestión de Envíos</h1>
<form id="dataForm">
<div class="form-row">
<div class="form-group">
<label for="id">ID:</label>
<input type="text" id="id" readonly>
</div>
<div class="form-group">
<label for="codigoEnvio">Código Envío:</label>
<div class="envio-input-group">
<input type="text" id="codigoEnvioPrefijo" placeholder="Prefijo (ej: NA)" maxlength="4" value="NA">
<input type="text" id="numeroEnvio" readonly>
<button type="button" id="setNumeroBtn" class="small-btn">Cambiar</button>
</div>
</div>
</div>
<div class="form-group">
<label for="baseCodeInput">Establecer código base (ej: NA1000):</label>
<div style="display: flex; gap: 10px;">
<input type="text" id="baseCodeInput" placeholder="Prefijo+Número (ej: NA1000)">
<button type="button" id="setBaseCodeBtn" class="small-btn">Establecer</button>
</div>
</div>
<div class="form-group">
<label for="destinatario">Destinatario:</label>
<input type="text" id="destinatario" list="destinatarioList" autocomplete="off" required>
<datalist id="destinatarioList"></datalist>
</div>
<div class="form-row">
<div class="form-group">
<label for="direccion">Dirección:</label>
<input type="text" id="direccion">
</div>
<div class="form-group">
<label for="poblacion">Población:</label>
<input type="text" id="poblacion">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="codigoPostal">Código Postal:</label>
<input type="text" id="codigoPostal">
</div>
<div class="form-group">
<label for="provincia">Provincia:</label>
<input type="text" id="provincia">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="expediente">Expediente:</label>
<input type="text" id="expediente">
</div>
<div class="form-group">
<label for="tramitador">Tramitador:</label>
<input type="text" id="tramitador">
</div>
</div>
<div class="form-group">
<label for="asunto">Asunto:</label>
<textarea id="asunto" rows="3"></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn-primary">Guardar</button>
<button type="button" id="clearBtn" class="btn-secondary">Limpiar</button>
</div>
</form>
<div class="filters">
<h2>Filtros de Búsqueda</h2>
<div class="filter-row">
<div class="filter-group">
<label for="order">Orden:</label>
<select id="order">
<option value="desc">Más recientes primero</option>
<option value="asc">Más antiguos primero</option>
</select>
</div>
<div class="filter-group">
<label for="dateFilter">Desde fecha:</label>
<input type="date" id="dateFilter">
</div>
<div class="filter-group">
<label for="searchInput">Buscar:</label>
<input type="text" id="searchInput" placeholder="Destinatario, expediente...">
</div>
</div>
<div class="export-actions">
<button id="selectAllBtn" class="btn-secondary">Seleccionar todo</button>
<button id="deselectAllBtn" class="btn-secondary">Deseleccionar todo</button>
<button id="bulkEditBtn" class="btn-primary">Modificar Selección</button>
<button id="printPreviewBtn" class="btn-primary">Vista Previa Impresión</button>
<button id="printExecuteBtn" class="btn-primary">Imprimir Selección</button>
<button id="printAcuseBtn" class="btn-primary">Imprimir Acuse</button>
<button id="importCsvBtn" class="btn-primary">Importar CSV</button>
<button id="importJsonBtn" class="btn-primary">Importar JSON</button>
<input type="file" id="fileInput" accept=".csv,.json" style="display: none;">
<button id="exportJsonBtn" class="btn-primary">Exportar JSON</button>
<button id="exportCsvBtn" class="btn-primary">Exportar CSV</button>
</div>
</div>
<div class="results">
<h2>Últimos Registros <span id="recordCount"></span></h2>
<div class="table-container" style="max-height: 500px; overflow-y: auto;">
<table id="resultsTable">
<thead>
<tr>
<th width="30px"><input type="checkbox" id="selectAllCheckbox"></th>
<th>ID</th>
<th>Código Envío</th>
<th>Destinatario</th>
<th>Población</th>
<th>Provincia</th>
<th>Expediente</th>
<th>Asunto</th>
<th class="no-print">Fecha</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div id="printContainer" class="print-container hidden"></div>
<div class="data-viewer">
<h2>Datos Almacenados (LocalStorage)</h2>
<textarea id="rawDataView" readonly></textarea>
<div class="export-actions">
<button id="refreshDataBtn" class="btn-secondary">Actualizar Datos</button>
<button id="clearStorageBtn" class="btn-danger">Limpiar Storage</button>
</div>
</div>
</div>
<!-- Modal para Importación -->
<div id="importModal" class="modal hidden">
<div class="modal-content">
<h3>Opciones de Importación</h3>
<p>Se han encontrado <span id="importCount">0</span> registros para importar.</p>
<div class="import-options">
<div class="import-option">
<input type="radio" id="overwriteOption" name="importOption" value="overwrite" checked>
<label for="overwriteOption">
<span class="option-title">Sobrescribir todo</span>
<span class="option-description">Reemplaza todos los registros existentes</span>
</label>
</div>
<div class="import-option">
<input type="radio" id="appendOption" name="importOption" value="append">
<label for="appendOption">
<span class="option-title">Añadir registros</span>
<span class="option-description">Mantiene los existentes y añade nuevos</span>
</label>
</div>
</div>
<div class="modal-actions">
<button id="confirmImportBtn" class="btn-primary">Confirmar Importación</button>
<button id="cancelImportBtn" class="btn-secondary">Cancelar</button>
</div>
</div>
</div>
<!-- Modal para cambiar número de envío -->
<div id="numeroModal" class="modal hidden">
<div class="modal-content">
<h3>Cambiar número de envío</h3>
<div class="form-group">
<label for="nuevoNumero">Nuevo número:</label>
<input type="number" id="nuevoNumero" min="1">
</div>
<div class="modal-actions">
<button id="confirmNumeroBtn" class="btn-primary">Confirmar</button>
<button id="cancelNumeroBtn" class="btn-secondary">Cancelar</button>
</div>
</div>
</div>
<!-- Modal para edición masiva -->
<div id="bulkEditModal" class="modal hidden">
<div class="modal-content">
<h3>Modificar Códigos de Envío Seleccionados</h3>
<div class="form-group">
<label for="bulkPrefixInput">Prefijo:</label>
<input type="text" id="bulkPrefixInput" maxlength="4" placeholder="Ej: NA">
</div>
<div class="form-group">
<label for="bulkStartNumberInput">Número inicial:</label>
<input type="number" id="bulkStartNumberInput" min="1" placeholder="Ej: 1000">
</div>
<div class="form-group">
<label>
<input type="checkbox" id="bulkIncrementCheckbox"> Usar incremento automático
</label>
</div>
<div id="incrementControls" style="display: none;">
<div class="form-group">
<label for="bulkSuffixInput">Sufijo (opcional):</label>
<input type="text" id="bulkSuffixInput" placeholder="Ej: -A">
</div>
</div>
<div class="modal-actions">
<button id="confirmBulkEditBtn" class="btn-primary">Aplicar Cambios</button>
<button id="cancelBulkEditBtn" class="btn-secondary">Cancelar</button>
</div>
</div>
</div>
<button id="themeToggle" class="theme-toggle">🌓</button>
<script src="jquery-3.6.0.min.js"></script>
<script src="jspdf.umd.min.js"></script>
<script src="JsBarcode.all.min.js"></script>
// Inicialización global para jsPDF
<script>
window.jsPDF = window.jspdf.jsPDF; window.jsPDF = window.jspdf.jsPDF; // Uncaught TypeError: Cannot read properties of undefined (reading 'jsPDF')at index3.html:14:37//
</script>
<script src="script.js"></script>
</body>
</html>
// Elementos del DOM
const elements = {
dataForm: document.getElementById('dataForm'),
idInput: document.getElementById('id'),
codigoEnvioPrefijoInput: document.getElementById('codigoEnvioPrefijo'),
numeroEnvioInput: document.getElementById('numeroEnvio'),
setNumeroBtn: document.getElementById('setNumeroBtn'),
baseCodeInput: document.getElementById('baseCodeInput'),
setBaseCodeBtn: document.getElementById('setBaseCodeBtn'),
destinatarioInput: document.getElementById('destinatario'),
destinatarioList: document.getElementById('destinatarioList'),
direccionInput: document.getElementById('direccion'),
poblacionInput: document.getElementById('poblacion'),
codigoPostalInput: document.getElementById('codigoPostal'),
provinciaInput: document.getElementById('provincia'),
expedienteInput: document.getElementById('expediente'),
tramitadorInput: document.getElementById('tramitador'),
asuntoInput: document.getElementById('asunto'),
clearBtn: document.getElementById('clearBtn'),
orderSelect: document.getElementById('order'),
dateFilterInput: document.getElementById('dateFilter'),
searchInput: document.getElementById('searchInput'),
resultsTable: document.getElementById('resultsTable'),
tableBody: document.querySelector('#resultsTable tbody'),
recordCountSpan: document.getElementById('recordCount'),
selectAllCheckbox: document.getElementById('selectAllCheckbox'),
selectAllBtn: document.getElementById('selectAllBtn'),
deselectAllBtn: document.getElementById('deselectAllBtn'),
bulkEditBtn: document.getElementById('bulkEditBtn'),
printPreviewBtn: document.getElementById('printPreviewBtn'),
printExecuteBtn: document.getElementById('printExecuteBtn'),
printAcuseBtn: document.getElementById('printAcuseBtn'),
exportJsonBtn: document.getElementById('exportJsonBtn'),
exportCsvBtn: document.getElementById('exportCsvBtn'),
refreshDataBtn: document.getElementById('refreshDataBtn'),
clearStorageBtn: document.getElementById('clearStorageBtn'),
rawDataView: document.getElementById('rawDataView'),
numeroModal: document.getElementById('numeroModal'),
nuevoNumeroInput: document.getElementById('nuevoNumero'),
confirmNumeroBtn: document.getElementById('confirmNumeroBtn'),
cancelNumeroBtn: document.getElementById('cancelNumeroBtn'),
bulkEditModal: document.getElementById('bulkEditModal'),
bulkPrefixInput: document.getElementById('bulkPrefixInput'),
bulkStartNumberInput: document.getElementById('bulkStartNumberInput'),
bulkIncrementCheckbox: document.getElementById('bulkIncrementCheckbox'),
bulkSuffixInput: document.getElementById('bulkSuffixInput'),
confirmBulkEditBtn: document.getElementById('confirmBulkEditBtn'),
cancelBulkEditBtn: document.getElementById('cancelBulkEditBtn'),
themeToggle: document.getElementById('themeToggle'),
printContainer: document.getElementById('printContainer'),
importModal: document.getElementById('importModal'),
importCount: document.getElementById('importCount'),
overwriteOption: document.getElementById('overwriteOption'),
appendOption: document.getElementById('appendOption'),
confirmImportBtn: document.getElementById('confirmImportBtn'),
cancelImportBtn: document.getElementById('cancelImportBtn'),
importCsvBtn: document.getElementById('importCsvBtn'),
importJsonBtn: document.getElementById('importJsonBtn'),
fileInput: document.getElementById('fileInput'),
};
// Variables de estado
const state = {
lastId: 0,
lastEnvioNumber: 0,
envioPrefijo: 'CD',
baseCode: null,
destinatariosIndex: {},
selectedRecords: [],
currentBulkEditStart: 0,
currentTheme: 'light',
pendingImportData: []
};
// Inicialización
function initApp() {
loadThemePreference();
initData();
setupEventListeners();
generateNextId();
generateNextNumeroEnvio();
loadRecords();
updateDataViewer();
elements.destinatarioInput.focus();
}
function loadThemePreference() {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
state.currentTheme = 'dark';
} else {
document.documentElement.removeAttribute('data-theme');
localStorage.setItem('theme', 'light');
state.currentTheme = 'light';
}
}
function toggleTheme() {
if (state.currentTheme === 'light') {
document.documentElement.setAttribute('data-theme', 'dark');
state.currentTheme = 'dark';
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.removeAttribute('data-theme');
state.currentTheme = 'light';
localStorage.setItem('theme', 'light');
}
}
function initData() {
if (!localStorage.getItem('personasData')) {
localStorage.setItem('personasData', JSON.stringify([]));
} else {
try {
const savedData = JSON.parse(localStorage.getItem('personasData'));
if (savedData && Array.isArray(savedData)) {
buildDestinatariosIndex(savedData);
if (savedData.length > 0) {
const ids = savedData.map(item => parseInt(item.id));
state.lastId = Math.max(...ids);
const numbers = savedData.map(item => {
const match = parseCodigoEnvio(item.numeroEnvio);
return match ? match.number : 0;
});
state.lastEnvioNumber = Math.max(...numbers);
const lastMatch = parseCodigoEnvio(savedData[savedData.length-1].numeroEnvio);
if (lastMatch) {
state.envioPrefijo = lastMatch.prefix;
elements.codigoEnvioPrefijoInput.value = state.envioPrefijo;
}
}
}
} catch (e) {
console.error("Error cargando datos:", e);
localStorage.setItem('personasData', JSON.stringify([]));
}
}
if (localStorage.getItem('lastEnvioNumber')) {
const storedNumber = parseInt(localStorage.getItem('lastEnvioNumber'));
if (!isNaN(storedNumber)) {
state.lastEnvioNumber = Math.max(state.lastEnvioNumber, storedNumber);
}
}
if (localStorage.getItem('envioPrefijo')) {
state.envioPrefijo = localStorage.getItem('envioPrefijo');
elements.codigoEnvioPrefijoInput.value = state.envioPrefijo;
}
if (localStorage.getItem('baseCode')) {
state.baseCode = localStorage.getItem('baseCode');
elements.baseCodeInput.value = state.baseCode;
}
}
function buildDestinatariosIndex(data) {
state.destinatariosIndex = {};
data.forEach(item => {
if (item.destinatario) {
const key = item.destinatario.toLowerCase();
if (!state.destinatariosIndex[key]) {
state.destinatariosIndex[key] = {
destinatario: item.destinatario,
direccion: item.direccion || '',
poblacion: item.poblacion || '',
codigoPostal: item.codigoPostal || '',
provincia: item.provincia || ''
};
}
}
});
updateDestinatarioList();
}
function updateDestinatarioList() {
elements.destinatarioList.innerHTML = '';
Object.values(state.destinatariosIndex).forEach(destinatario => {
const option = document.createElement('option');
option.value = destinatario.destinatario;
elements.destinatarioList.appendChild(option);
});
}
function updateDataViewer() {
const data = getData();
elements.rawDataView.value = JSON.stringify(data, null, 2);
}
function generateNextId() {
state.lastId++;
elements.idInput.value = state.lastId;
}
function parseCodigoEnvio(codigo) {
if (!codigo) return null;
const match = codigo.match(/^([A-Za-z]{2,4})(\d+)(.*)$/);
if (match) {
return {
prefix: match[1],
number: parseInt(match[2]),
suffix: match[3] || ''
};
}
return null;
}
function generateNextNumeroEnvio() {
const allData = getData();
let maxId = state.lastId;
if (allData.length > 0) {
const ids = allData.map(item => parseInt(item.id));
maxId = Math.max(...ids);
}
const nextNumber = maxId + 1;
const prefix = elements.codigoEnvioPrefijoInput.value || state.envioPrefijo;
elements.numeroEnvioInput.value = `${prefix}${String(state.lastEnvioNumber + 1).padStart(11, '0')}`;
state.lastEnvioNumber = nextNumber;
localStorage.setItem('lastEnvioNumber', nextNumber);
}
function setupEventListeners() {
// Formulario principal
elements.dataForm.addEventListener('submit', handleFormSubmit);
elements.clearBtn.addEventListener('click', clearForm);
// Código de envío
elements.codigoEnvioPrefijoInput.addEventListener('change', updateCodigoEnvio);
elements.setNumeroBtn.addEventListener('click', showNumeroModal);
elements.setBaseCodeBtn.addEventListener('click', setBaseCode);
// Autocompletado de destinatario
elements.destinatarioInput.addEventListener('input', handleDestinatarioInput);
// Filtros y búsqueda
elements.orderSelect.addEventListener('change', loadRecords);
elements.dateFilterInput.addEventListener('change', loadRecords);
elements.searchInput.addEventListener('input', debounce(loadRecords, 300));
// Selección de registros
elements.selectAllBtn.addEventListener('click', selectAllRecords);
elements.deselectAllBtn.addEventListener('click', deselectAllRecords);
elements.selectAllCheckbox.addEventListener('change', toggleSelectAll);
// Acciones masivas
elements.bulkEditBtn.addEventListener('click', showBulkEditModal);
// Impresión
elements.printPreviewBtn.addEventListener('click', showPrintPreview);
elements.printExecuteBtn.addEventListener('click', executePrint);
elements.printAcuseBtn.addEventListener('click', imprimirAcuseIndividual);
// Importación/Exportación
elements.importCsvBtn.addEventListener('click', () => {
elements.fileInput.accept = '.csv';
elements.fileInput.click();
});
elements.importJsonBtn.addEventListener('click', () => {
elements.fileInput.accept = '.json';
elements.fileInput.click();
});
elements.fileInput.addEventListener('change', handleFileImport);
elements.exportJsonBtn.addEventListener('click', exportToJSON);
elements.exportCsvBtn.addEventListener('click', exportToCSV);
// Datos y almacenamiento
elements.refreshDataBtn.addEventListener('click', updateDataViewer);
elements.clearStorageBtn.addEventListener('click', clearStorage);
// Modal de número
elements.confirmNumeroBtn.addEventListener('click', confirmNumeroChange);
elements.cancelNumeroBtn.addEventListener('click', hideNumeroModal);
// Modal de edición masiva
elements.confirmBulkEditBtn.addEventListener('click', applyBulkEdit);
elements.cancelBulkEditBtn.addEventListener('click', hideBulkEditModal);
elements.bulkIncrementCheckbox.addEventListener('change', toggleIncrementVisibility);
// Modal de importación
elements.confirmImportBtn.addEventListener('click', confirmImport);
elements.cancelImportBtn.addEventListener('click', cancelImport);
// Tema
elements.themeToggle.addEventListener('click', toggleTheme);
// Navegación con Enter en campos de formulario
const inputFields = [
elements.destinatarioInput,
elements.direccionInput,
elements.poblacionInput,
elements.codigoPostalInput,
elements.provinciaInput,
elements.expedienteInput,
elements.tramitadorInput,
elements.asuntoInput
];
inputFields.forEach(input => {
if (input) {
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
const currentIndex = inputFields.indexOf(input);
if (currentIndex < inputFields.length - 1) {
inputFields[currentIndex + 1].focus();
} else {
elements.asuntoInput.blur();
}
}
});
}
});
// Configurar el submit solo con el botón
elements.dataForm.addEventListener('submit', function(e) {
e.preventDefault();
handleFormSubmit(e);
});
}
function handleFormSubmit(e) {
e.preventDefault();
const newRecord = {
id: elements.idInput.value,
numeroEnvio: elements.numeroEnvioInput.value,
envioPrefijo: elements.codigoEnvioPrefijoInput.value,
destinatario: elements.destinatarioInput.value,
direccion: elements.direccionInput.value,
poblacion: elements.poblacionInput.value,
codigoPostal: elements.codigoPostalInput.value,
provincia: elements.provinciaInput.value,
expediente: elements.expedienteInput.value,
tramitador: elements.tramitadorInput.value,
asunto: elements.asuntoInput.value,
fecha: new Date().toISOString()
};
const currentData = JSON.parse(localStorage.getItem('personasData'));
currentData.push(newRecord);
localStorage.setItem('personasData', JSON.stringify(currentData));
const key = newRecord.destinatario.toLowerCase();
if (!state.destinatariosIndex[key]) {
state.destinatariosIndex[key] = {
destinatario: newRecord.destinatario,
direccion: newRecord.direccion,
poblacion: newRecord.poblacion,
codigoPostal: newRecord.codigoPostal,
provincia: newRecord.provincia
};
updateDestinatarioList();
}
generateNextId();
generateNextNumeroEnvio();
loadRecords();
updateDataViewer();
clearForm(false);
}
function clearForm(resetId = true) {
if (resetId) {
elements.idInput.value = '';
}
elements.destinatarioInput.value = '';
elements.direccionInput.value = '';
elements.poblacionInput.value = '';
elements.codigoPostalInput.value = '';
elements.provinciaInput.value = '';
elements.expedienteInput.value = '';
elements.tramitadorInput.value = '';
elements.asuntoInput.value = '';
elements.destinatarioInput.focus();
}
function updateCodigoEnvio() {
const prefix = elements.codigoEnvioPrefijoInput.value || state.envioPrefijo;
elements.numeroEnvioInput.value = `${prefix}${String(state.lastEnvioNumber + 1).padStart(11, '0')}`;
state.envioPrefijo = prefix;
localStorage.setItem('envioPrefijo', prefix);
}
function showNumeroModal() {
elements.nuevoNumeroInput.value = state.lastEnvioNumber + 1;
elements.numeroModal.classList.remove('hidden');
elements.nuevoNumeroInput.focus();
}
function hideNumeroModal() {
elements.numeroModal.classList.add('hidden');
}
function confirmNumeroChange() {
const nuevoNumero = parseInt(elements.nuevoNumeroInput.value);
if (!isNaN(nuevoNumero) && nuevoNumero >= 1) {
state.lastEnvioNumber = nuevoNumero - 1;
localStorage.setItem('lastEnvioNumber', state.lastEnvioNumber);
generateNextNumeroEnvio();
hideNumeroModal();
} else {
alert('Por favor ingrese un número válido mayor o igual a 1');
}
}
function setBaseCode() {
const baseCode = elements.baseCodeInput.value.trim();
if (!baseCode) return;
const match = parseCodigoEnvio(baseCode);
if (match) {
state.envioPrefijo = match.prefix;
state.lastEnvioNumber = match.number - 1;
elements.codigoEnvioPrefijoInput.value = state.envioPrefijo;
localStorage.setItem('envioPrefijo', state.envioPrefijo);
localStorage.setItem('lastEnvioNumber', state.lastEnvioNumber);
state.baseCode = baseCode;
localStorage.setItem('baseCode', baseCode);
generateNextNumeroEnvio();
alert(`Código base establecido a ${baseCode}. El próximo código será ${elements.numeroEnvioInput.value}`);
} else {
alert('Formato de código inválido. Use formato: PREFIJO+NÚMERO (ej: NA1000)');
}
}
function handleDestinatarioInput() {
const input = elements.destinatarioInput.value.toLowerCase();
const match = state.destinatariosIndex[input];
if (match) {
elements.direccionInput.value = match.direccion;
elements.poblacionInput.value = match.poblacion;
elements.codigoPostalInput.value = match.codigoPostal;
elements.provinciaInput.value = match.provincia;
}
}
function loadRecords() {
const data = getFilteredData();
elements.tableBody.innerHTML = '';
data.forEach(record => {
const row = document.createElement('tr');
row.dataset.id = record.id;
const isSelected = state.selectedRecords.some(r => r.id === record.id);
row.innerHTML = `
<td><input type="checkbox" class="record-checkbox" ${isSelected ? 'checked' : ''}></td>
<td>${record.id}</td>
<td>${record.numeroEnvio}</td>
<td>${record.destinatario}</td>
<td>${record.poblacion}</td>
<td>${record.provincia}</td>
<td>${record.expediente}</td>
<td>${record.asunto}</td>
<td class="no-print">${new Date(record.fecha).toLocaleDateString()}</td>
`;
row.querySelector('.record-checkbox').addEventListener('change', function() {
const recordId = row.dataset.id;
if (this.checked) {
if (!state.selectedRecords.some(r => r.id === recordId)) {
state.selectedRecords.push({
id: recordId,
numeroEnvio: record.numeroEnvio
});
}
} else {
state.selectedRecords = state.selectedRecords.filter(r => r.id !== recordId);
}
});
elements.tableBody.appendChild(row);
});
elements.recordCountSpan.textContent = `(${data.length})`;
}
function getFilteredData() {
const data = JSON.parse(localStorage.getItem('personasData')) || [];
const order = elements.orderSelect.value;
const dateFilter = elements.dateFilterInput.value;
const searchTerm = elements.searchInput.value.toLowerCase();
let filtered = [...data];
if (dateFilter) {
const filterDate = new Date(dateFilter);
filtered = filtered.filter(item => {
const itemDate = new Date(item.fecha);
return itemDate >= filterDate;
});
}
if (searchTerm) {
filtered = filtered.filter(item =>
item.destinatario.toLowerCase().includes(searchTerm) ||
item.expediente.toLowerCase().includes(searchTerm) ||
item.numeroEnvio.toLowerCase().includes(searchTerm)
);
}
filtered.sort((a, b) => {
const dateA = new Date(a.fecha);
const dateB = new Date(b.fecha);
return order === 'asc' ? dateA - dateB : dateB - dateA;
});
return filtered;
}
function getData() {
return JSON.parse(localStorage.getItem('personasData')) || [];
}
function selectAllRecords() {
document.querySelectorAll('.record-checkbox').forEach(checkbox => {
checkbox.checked = true;
const row = checkbox.closest('tr');
const recordId = row.dataset.id;
if (!state.selectedRecords.some(r => r.id === recordId)) {
state.selectedRecords.push({
id: recordId,
numeroEnvio: row.cells[2].textContent
});
}
});
elements.selectAllCheckbox.checked = true;
}
function deselectAllRecords() {
document.querySelectorAll('.record-checkbox').forEach(checkbox => {
checkbox.checked = false;
});
state.selectedRecords = [];
elements.selectAllCheckbox.checked = false;
}
function toggleSelectAll() {
const isChecked = elements.selectAllCheckbox.checked;
if (isChecked) {
selectAllRecords();
} else {
deselectAllRecords();
}
}
function getSelectedRecords() {
return state.selectedRecords;
}
function showBulkEditModal() {
const selectedRows = getSelectedRecords();
if (selectedRows.length === 0) {
alert('Selecciona al menos un registro para modificar');
return;
}
state.selectedRecords = selectedRows;
state.currentBulkEditStart = 0;
const firstCode = parseCodigoEnvio(selectedRows[0].numeroEnvio);
if (firstCode) {
elements.bulkPrefixInput.value = firstCode.prefix;
elements.bulkStartNumberInput.value = firstCode.number;
}
elements.bulkEditModal.classList.remove('hidden');
elements.bulkPrefixInput.focus();
}
function hideBulkEditModal() {
elements.bulkEditModal.classList.add('hidden');
}
function toggleIncrementVisibility() {
document.getElementById('incrementControls').style.display =
&