Excel y sesiones.json a las 00

This commit is contained in:
2026-06-05 22:56:35 -05:00
parent aa5e85ae94
commit 984f2f103d
7 changed files with 550 additions and 107 deletions
+150
View File
@@ -46,6 +46,7 @@ document.addEventListener('DOMContentLoaded', async () => {
setupCategoriasModals();
setupProductosUpload();
setupProductosSearch();
setupExcelUpload();
await checkSession();
});
@@ -1327,6 +1328,155 @@ function setupProductosSearch() {
});
}
// ════════════════════════════════
// EXCEL/CSV — asignar nombre + precio por referencia
// ════════════════════════════════
function setupExcelUpload() {
const input = document.getElementById('excelArchivo');
const hint = document.getElementById('excelArchivoHint');
const form = document.getElementById('formExcelUpload');
const btn = document.getElementById('btnProcesarExcel');
if (!input || !form) return;
input.addEventListener('change', () => {
const f = input.files && input.files[0];
if (f) {
const sizeKB = (f.size / 1024).toFixed(1);
hint.textContent = `📄 ${f.name} · ${sizeKB} KB`;
} else {
hint.textContent = 'Ningún archivo seleccionado';
}
btn.disabled = !f;
});
form.addEventListener('submit', submitExcelUpload);
}
async function submitExcelUpload(e) {
e.preventDefault();
const input = document.getElementById('excelArchivo');
const btn = document.getElementById('btnProcesarExcel');
const errEl = document.getElementById('excelError');
const summ = document.getElementById('excelSummary');
const f = input.files && input.files[0];
errEl.textContent = '';
errEl.classList.remove('show');
summ.style.display = 'none';
summ.innerHTML = '';
if (!f) {
errEl.textContent = 'Selecciona un archivo';
errEl.classList.add('show');
return;
}
// Lock UI
btn.disabled = true;
btn.querySelector('.btn-text').textContent = 'Procesando…';
btn.querySelector('.btn-spinner').style.display = 'inline-block';
const fd = new FormData();
fd.append('archivo', f, f.name);
try {
const res = await fetch('/api/productos/excel', { method: 'POST', body: fd, credentials: 'include' });
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Error al procesar el archivo');
renderExcelSummary(data.resumen);
// Refrescar lista de productos disponibles (para que se vean los nuevos nombres)
if (currentView === 'productos' && typeof cargarProductosDisponibles === 'function') {
cargarProductosDisponibles();
}
} catch (err) {
errEl.textContent = err.message;
errEl.classList.add('show');
} finally {
btn.disabled = false;
btn.querySelector('.btn-text').textContent = 'Procesar archivo';
btn.querySelector('.btn-spinner').style.display = 'none';
}
}
function renderExcelSummary(r) {
const cont = document.getElementById('excelSummary');
cont.innerHTML = '';
if (r.actualizados > 0) {
const row = document.createElement('div');
row.className = 'upload-summary-row is-success';
const items = (r.actualizadosList || []).slice(0, 10).map(a =>
`<li><code>${escapeHtml(a.ref)}</code>${a.fila ? ` <span style="opacity:0.7">(fila ${a.fila})</span>` : ''}</li>`
).join('');
const more = r.actualizadosList && r.actualizadosList.length > 10
? `<li style="opacity:0.7">…y ${r.actualizadosList.length - 10} más</li>` : '';
row.innerHTML = `
<span class="icon">✅</span>
<div class="text">
<strong>${r.actualizados} producto(s) actualizado(s)</strong>
<ul>${items}${more}</ul>
</div>
`;
cont.appendChild(row);
}
if (r.noEncontrados > 0) {
const row = document.createElement('div');
row.className = 'upload-summary-row is-warning';
const items = (r.noEncontradosList || []).slice(0, 10).map(ref =>
`<li><code>${escapeHtml(ref)}</code> <span style="opacity:0.7">(no existe en el catálogo)</span></li>`
).join('');
const more = r.noEncontradosList && r.noEncontradosList.length > 10
? `<li style="opacity:0.7">…y ${r.noEncontradosList.length - 10} más</li>` : '';
row.innerHTML = `
<span class="icon">⚠️</span>
<div class="text">
<strong>${r.noEncontrados} referencia(s) no encontrada(s) — no se crearon productos nuevos</strong>
<ul>${items}${more}</ul>
</div>
`;
cont.appendChild(row);
}
if (r.errores > 0) {
const row = document.createElement('div');
row.className = 'upload-summary-row is-error';
const items = (r.erroresList || []).slice(0, 10).map(e =>
`<li><code>${escapeHtml(e.ref || ('Fila ' + e.fila))}</code> — ${escapeHtml(e.motivo)}</li>`
).join('');
const more = r.erroresList && r.erroresList.length > 10
? `<li style="opacity:0.7">…y ${r.erroresList.length - 10} más</li>` : '';
row.innerHTML = `
<span class="icon">❌</span>
<div class="text">
<strong>${r.errores} error(es) al procesar</strong>
<ul>${items}${more}</ul>
</div>
`;
cont.appendChild(row);
}
if (r.filasVacias > 0) {
const row = document.createElement('div');
row.className = 'upload-summary-row is-warning';
row.innerHTML = `
<span class="icon">️</span>
<div class="text">
<strong>${r.filasVacias} fila(s) vacía(s) ignorada(s)</strong>
</div>
`;
cont.appendChild(row);
}
if (cont.children.length === 0) {
cont.innerHTML = `<div class="upload-summary-row is-warning"><span class="icon">️</span><div class="text"><strong>Nada se actualizó</strong></div></div>`;
}
cont.style.display = 'flex';
}
function abrirAgotarProducto(ref) {
if (!confirm(`¿Marcar el producto "${ref}" como agotado?\n\nSu imagen se moverá a la carpeta "agotados" y dejará de mostrarse en el catálogo público. Podrás reactivarlo después desde la pestaña "Agotados".`)) return;
agotarProducto(ref);