Excel y sesiones.json a las 00
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user