📦 Captura de Marbetes

Iniciar sesión

'); w.document.close();w.focus();setTimeout(()=>w.print(),500); } async function addUser(){ if(currentUser?.role!=='admin')return; const u=document.getElementById('newUser').value.trim(),p=document.getElementById('newPass').value; if(!u||!p){alert('Captura usuario y contraseña.');return} try{await api('/users',{method:'POST',body:JSON.stringify({user:u,password:p,name:u})});document.getElementById('newUser').value='';document.getElementById('newPass').value='';await renderUsers();alert('Usuario creado correctamente. Ya puede iniciar sesión desde otro dispositivo.');} catch(e){alert(e.message);} } async function renderUsers(){ const usersDiv=document.getElementById('users'); try{ const r=await api('/users'); users={}; r.users.forEach(x=>users[x.user]=x); usersDiv.innerHTML=r.users.map(x=>`
${escapeHtml(x.user)} · ${x.role==='admin'?'Administrador':'Usuario'}${x.role==='admin'?'':'
'}
`).join(''); }catch(e){usersDiv.innerHTML=`
No se pudieron cargar los usuarios: ${escapeHtml(e.message)}
`;} } async function deleteUser(encodedUser){ if(currentUser?.role!=='admin')return; const u=decodeURIComponent(encodedUser); if(!confirm(`¿Seguro que deseas eliminar al usuario "${u}"?\n\nEsta acción no se puede deshacer.`))return; try{await api('/users',{method:'DELETE',body:JSON.stringify({user:u})});await renderUsers();}catch(e){alert(e.message);} } async function loadCatalogFile(){ if(currentUser?.role!=='admin')return; const f=document.getElementById('catalogFile').files[0]; if(!f){alert('Selecciona un archivo Excel.');return;} const btn=document.querySelector('#admin button.primary'); btn.disabled=true; btn.textContent='Procesando catálogo...'; try{ const {data,sheetName,sourceRows}=await importExcelCatalog(f); const groups={}; for(const row of data){ const b=String(row.upc).replace(/\D/g,'').slice(0,2).padStart(2,'0').slice(0,2); (groups[b] ||= []).push(row); } const buckets=Object.keys(groups).sort(); const CHUNK_SIZE=2000; const importId='imp_'+Date.now()+'_'+Math.random().toString(36).slice(2,8); const parts=[]; let uploaded=0; const totalParts=buckets.reduce((n,b)=>n+Math.ceil(groups[b].length/CHUNK_SIZE),0); for(const b of buckets){ const rows=groups[b]; let part=0; for(let i=0;iCatálogo central actualizado correctamente.
${data.length.toLocaleString()} registros válidos · Hoja: ${escapeHtml(sheetName)} · Filas leídas: ${sourceRows.toLocaleString()}
Se conservaron los UPC repetidos. Este catálogo ya está disponible para los teléfonos de los usuarios.`); alert(`Catálogo central actualizado: ${data.length.toLocaleString()} registros.`); }catch(e){setCatalogStatus(`
No se pudo actualizar el catálogo.
${escapeHtml(e.message||String(e))}
`);} finally{btn.disabled=false;btn.textContent='Actualizar catálogo';} } function escapeHtml(s){return String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))} init();