Files
Autoparts-DB/dashboard/pos.min.js
consultoria-as 21959f1b37 FASE 7d: Lazy Loading + Minificación + Auto-serve minified
Cambios implementados:

1. Lazy loading de imágenes:
   - catalog.js: loading="lazy" decoding="async" en part cards y detail panel
   - inventory.js: lazy loading en imagen de detalle de item

2. Minificación de assets:
   - scripts/minify-assets.sh: minifica JS (terser) y CSS para POS y Dashboard
   - 25 archivos .min.js + 5 .min.css generados en pos/static/
   - 14 archivos .min.js + 8 .min.css generados en dashboard/

3. Nginx auto-serve minified:
   - try_files $1.min.js antes de servir .js original
   - try_files $1.min.css antes de servir .css original
   - Transparente para los templates HTML (cero cambios en HTML)

4. Cache warming script:
   - scripts/warm_vehicle_cache.py: pobla Redis con vehicle info por batches
   - Mitiga DISTINCT ON + 4 JOINs sobre 2B filas
   - Corre en background, procesa ~1.5M parts

Tests: 73/73 pasando
2026-04-27 08:34:24 +00:00

1 line
8.9 KiB
JavaScript

!function(){"use strict";var e=null,t=[];function n(e,t){var n=document.createElement("div");n.className="toast "+(t||"success"),n.textContent=e,document.body.appendChild(n),setTimeout((function(){n.remove()}),3e3)}function a(e,t){return t=t||{},fetch(""+e,t).then((function(e){return e.ok?e.json():e.json().then((function(e){throw new Error(e.error||"Error")}))}))}function r(e){if(!e)return"";var t=document.createElement("div");return t.textContent=e,t.innerHTML}function i(e){return"$"+(parseFloat(e)||0).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g,",")}var c=null,o=document.getElementById("customer-search"),s=document.getElementById("customer-dropdown");function d(t){a("/api/pos/customers/"+t).then((function(t){e=t,document.getElementById("customer-select").style.display="none",document.getElementById("customer-info").style.display="flex",document.getElementById("sel-customer-name").textContent=t.name,document.getElementById("sel-customer-rfc").textContent=t.rfc||"Sin RFC";var n=document.getElementById("sel-customer-balance");n.textContent="Saldo: "+i(t.balance),n.className="cb-balance "+(t.balance>0?"positive":"zero"),s.style.display="none",_()}))}o.addEventListener("input",(function(){clearTimeout(c);var e=this.value.trim();e.length<2?s.style.display="none":c=setTimeout((function(){a("/api/pos/customers?search="+encodeURIComponent(e)+"&per_page=10").then((function(e){var t=e.data||[];0===t.length?s.innerHTML='<div style="padding:0.8rem;color:var(--text-secondary)">No se encontraron clientes</div>':(s.innerHTML=t.map((function(e){return'<div class="customer-dropdown-item" data-id="'+e.id_customer+'"><div><span class="cdi-name">'+r(e.name)+"</span>"+(e.rfc?' <span class="cdi-rfc">'+r(e.rfc)+"</span>":"")+'</div><span style="font-size:0.8rem;color:'+(e.balance>0?"var(--danger)":"var(--success)")+'">'+i(e.balance)+"</span></div>"})).join(""),s.querySelectorAll(".customer-dropdown-item").forEach((function(e){e.addEventListener("click",(function(){d(parseInt(e.getAttribute("data-id")))}))}))),s.style.display="block"}))}),300)})),o.addEventListener("blur",(function(){setTimeout((function(){s.style.display="none"}),200)})),document.getElementById("btn-change-customer").addEventListener("click",(function(){e=null,document.getElementById("customer-info").style.display="none",document.getElementById("customer-select").style.display="block",o.value="",o.focus(),_()})),document.getElementById("btn-new-customer").addEventListener("click",(function(){document.getElementById("modal-new-customer").style.display="flex",document.getElementById("nc-name").focus()})),document.getElementById("nc-cancel").addEventListener("click",(function(){document.getElementById("modal-new-customer").style.display="none"})),document.getElementById("nc-save").addEventListener("click",(function(){var e=document.getElementById("nc-name").value.trim();e?a("/api/pos/customers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,rfc:document.getElementById("nc-rfc").value.trim()||null,business_name:document.getElementById("nc-business").value.trim()||null,phone:document.getElementById("nc-phone").value.trim()||null,email:document.getElementById("nc-email").value.trim()||null,address:document.getElementById("nc-address").value.trim()||null,credit_limit:parseFloat(document.getElementById("nc-credit").value)||0,payment_terms:parseInt(document.getElementById("nc-terms").value)||30})}).then((function(t){n("Cliente creado: "+e),document.getElementById("modal-new-customer").style.display="none",d(t.id),["nc-name","nc-rfc","nc-business","nc-phone","nc-email","nc-address"].forEach((function(e){document.getElementById(e).value=""})),document.getElementById("nc-credit").value="0",document.getElementById("nc-terms").value="30"})).catch((function(e){n(e.message,"error")})):n("Ingresa el nombre del cliente","error")}));var u=null,l=document.getElementById("part-search"),m=document.getElementById("part-results"),p=[],y=-1;function f(){m.querySelectorAll(".part-result-item").forEach((function(e,t){t===y?(e.classList.add("part-result-active"),e.scrollIntoView({block:"nearest"})):e.classList.remove("part-result-active")}))}function v(e){var n;e>=0&&e<p.length&&(n=p[e],t.push({part_id:"oem"===n.part_type?n.id_part:null,aftermarket_id:"aftermarket"===n.part_type?n.id_part:null,description:(n.oem_part_number||"")+" - "+(n.name_part||""),part_type:n.part_type,quantity:1,unit_cost:n.cost_usd||0,margin_pct:30,unit_price:1.3*(n.cost_usd||0)}),g(),l.focus(),l.value="",m.style.display="none",p=[],y=-1,l.focus())}function g(){var e=document.getElementById("cart-body");if(0===t.length)return e.innerHTML='<tr><td colspan="8" class="cart-empty">Busca y agrega partes al carrito</td></tr>',void E();e.innerHTML=t.map((function(e,t){var n=e.quantity*e.unit_price;return'<tr><td class="cart-desc">'+r(e.description)+'</td><td><span class="pri-type '+e.part_type+'">'+e.part_type+'</span></td><td><input class="cart-qty" type="number" min="1" value="'+e.quantity+'" data-idx="'+t+'" data-field="quantity"></td><td><input class="cart-cost" type="number" step="0.01" value="'+e.unit_cost.toFixed(2)+'" data-idx="'+t+'" data-field="unit_cost"></td><td><input class="cart-margin" type="number" step="1" value="'+e.margin_pct.toFixed(0)+'" data-idx="'+t+'" data-field="margin_pct">%</td><td>'+i(e.unit_price)+"</td><td>"+i(n)+'</td><td><button class="cart-remove" data-idx="'+t+'">&times;</button></td></tr>'})).join(""),e.querySelectorAll("input").forEach((function(e){e.addEventListener("change",(function(){var n=parseInt(e.getAttribute("data-idx")),a=e.getAttribute("data-field"),r=parseFloat(e.value)||0;t[n][a]=r,"unit_cost"!==a&&"margin_pct"!==a||(t[n].unit_price=t[n].unit_cost*(1+t[n].margin_pct/100)),g()}))})),e.querySelectorAll(".cart-remove").forEach((function(e){e.addEventListener("click",(function(){t.splice(parseInt(e.getAttribute("data-idx")),1),g()}))})),E()}function E(){var e=t.reduce((function(e,t){return e+t.quantity}),0),n=t.reduce((function(e,t){return e+t.quantity*t.unit_price}),0),a=.16*n,r=n+a;document.getElementById("sum-items").textContent=e,document.getElementById("sum-subtotal").textContent=i(n),document.getElementById("sum-tax").textContent=i(a),document.getElementById("sum-total").textContent=i(r),_()}function _(){document.getElementById("btn-facturar").disabled=!(e&&t.length>0)}l.addEventListener("input",(function(){var e=l.value.trim();if(e.length<1)return m.style.display="none",void(p=[]);clearTimeout(u),u=setTimeout((function(){a("/api/pos/search-parts?q="+encodeURIComponent(e)).then((function(e){p=e,y=-1,function(){if(0===p.length&&l.value.trim().length>0)return m.innerHTML='<div style="padding:0.8rem;color:var(--text-secondary)">No se encontraron partes para "'+r(l.value)+'"</div>',void(m.style.display="block");if(0===p.length)return void(m.style.display="none");m.innerHTML=p.map((function(e,t){return'<div class="part-result-item'+(t===y?" part-result-active":"")+'" data-idx="'+t+'"><div><span class="pri-number">'+r(e.oem_part_number)+'</span><span class="pri-name">'+r(e.name_part)+'</span></div><div style="display:flex;align-items:center;gap:0.4rem"><span class="pri-type '+e.part_type+'">'+e.part_type+"</span>"+(e.cost_usd?'<span style="font-size:0.8rem;color:var(--text-secondary)">'+i(e.cost_usd)+"</span>":"")+'<span style="font-size:0.75rem;color:var(--text-secondary)">'+r(e.group_name||"")+"</span></div></div>"})).join(""),m.querySelectorAll(".part-result-item").forEach((function(e){e.addEventListener("mousedown",(function(t){t.preventDefault(),v(parseInt(e.getAttribute("data-idx")))})),e.addEventListener("mouseenter",(function(){y=parseInt(e.getAttribute("data-idx")),f()}))})),m.style.display="block"}()}))}),150)})),l.addEventListener("keydown",(function(e){"none"!==m.style.display&&0!==p.length&&("ArrowDown"===e.key?(e.preventDefault(),y=Math.min(y+1,p.length-1),f()):"ArrowUp"===e.key?(e.preventDefault(),y=Math.max(y-1,0),f()):"Enter"===e.key?(e.preventDefault(),y>=0?v(y):1===p.length&&v(0)):"Escape"===e.key&&(m.style.display="none",y=-1))})),l.addEventListener("focus",(function(){p.length>0&&(m.style.display="block")})),l.addEventListener("blur",(function(){setTimeout((function(){m.style.display="none"}),200)})),document.getElementById("btn-facturar").addEventListener("click",(function(){if(e&&0!==t.length){var r=this;r.disabled=!0,r.textContent="Generando...";var c=t.map((function(e){return{part_id:e.part_id,aftermarket_id:e.aftermarket_id,description:e.description,quantity:e.quantity,unit_cost:e.unit_cost,margin_pct:e.margin_pct,unit_price:Math.round(100*e.unit_price)/100}}));a("/api/pos/invoices",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customer_id:e.id_customer,items:c,notes:document.getElementById("invoice-notes").value.trim()})}).then((function(a){n("Factura "+a.folio+" creada por "+i(a.total)),t=[],g(),document.getElementById("invoice-notes").value="",d(e.id_customer),r.textContent="Facturar"})).catch((function(e){n(e.message,"error"),r.disabled=!1,r.textContent="Facturar"}))}})),g()}();