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
1 line
11 KiB
JavaScript
1 line
11 KiB
JavaScript
!function(){"use strict";var e="/pos/api/diagrams",t=localStorage.getItem("pos_token");if(t){var a={Authorization:"Bearer "+t,"Content-Type":"application/json"},n={view:"list",diagrams:[],categories:[],currentDiagram:null,hotspots:[],selectedHotspot:null,scale:1,panX:0,panY:0,isPanning:!1,lastPointer:null},i=document.getElementById("diagramList"),o=document.getElementById("diagramViewer"),r=document.getElementById("svgContainer"),s=document.getElementById("svgWrapper"),c=document.getElementById("backBtn"),l=document.getElementById("zoomInBtn"),d=document.getElementById("zoomOutBtn"),u=document.getElementById("zoomResetBtn"),m=document.getElementById("diagramTitle"),p=document.getElementById("hotspotPanel"),g=document.getElementById("hotspotBody"),v=document.getElementById("hotspotClose"),f=document.getElementById("partsList"),y=document.getElementById("loading"),h=document.getElementById("emptyState"),_=document.getElementById("diagramFilter"),b=document.getElementById("profileAvatar"),E=document.getElementById("profileName"),L=document.getElementById("profileRole");window.DiagramsApp={openDiagram:function(t){k(e+"/"+t,(function(t,r){t||!r||r.error||(n.currentDiagram=r,n.hotspots=r.hotspots||[],function(){n.view="viewer",i.style.display="none",o.style.display="flex",A();var t=n.currentDiagram;m&&(m.textContent=t.name_es||t.name);M(),r=t.id_diagram,s.innerHTML='<div class="svg-loading">Cargando diagrama...</div>',fetch(e+"/"+r+"/svg",{headers:a}).then((function(e){return e.text()})).then((function(e){s.innerHTML=e;var t=s.querySelector("svg");t&&(t.style.width="100%",t.style.height="100%",t.setAttribute("preserveAspectRatio","xMidYMid meet"),function(e){var t=e.querySelectorAll("[data-hotspot]");t.forEach((function(e){var t=parseInt(e.getAttribute("data-hotspot"));e.style.cursor="pointer",e.addEventListener("click",(function(e){e.stopPropagation(),C(t)})),e.addEventListener("mouseenter",(function(){x(t,!0)})),e.addEventListener("mouseleave",(function(){x(t,!1)}))}))}(t))})).catch((function(){s.innerHTML='<div class="svg-error">Error al cargar el diagrama</div>'})),function(){if(!f)return;var e='<h4 class="parts-list__title">Partes en diagrama</h4>';n.hotspots.forEach((function(t){var a=P(t.callout_number),n=t.part_name_es||t.part_name||a.name,i=t.part_number||"";e+='<div class="part-item" data-callout="'+t.callout_number+'" onclick="DiagramsApp.selectHotspot('+t.callout_number+')">',e+='<span class="part-item__callout">'+t.callout_number+"</span>",e+='<div class="part-item__info">',e+='<div class="part-item__name">'+Y(n)+"</div>",i&&(e+='<div class="part-item__number">'+Y(i)+"</div>"),e+="</div>",e+="</div>"})),0===n.hotspots.length&&(e+='<div class="parts-list__empty">Sin partes definidas</div>');f.innerHTML=e}();var r}())}))},selectHotspot:C,viewPart:function(e){window.open("/pos/catalog?part="+e,"_blank")},searchPart:function(e){window.open("/pos/catalog?search="+encodeURIComponent(e),"_blank")},addToCart:function(e){alert("Funcion disponible desde el catalogo. Busca la parte para agregarla al carrito.")}},"loading"===document.readyState?document.addEventListener("DOMContentLoaded",B):B()}else window.location.href="/pos/login";function B(){!function(){try{var e=JSON.parse(localStorage.getItem("pos_user")||"{}");if(E&&(E.textContent=e.name||"--"),L&&(L.textContent=e.role||"--"),b){var t=e.name||"--";b.textContent=t.split(" ").map((function(e){return e[0]||""})).join("").substring(0,2).toUpperCase()}}catch(e){}}(),k(e+"/",(function(e,t){if(!e&&t&&t.data){n.diagrams=t.data;var a={};t.data.forEach((function(e){var t=e.category_name||"Other";a[t]||(a[t]={name:t,name_es:e.category_name_es||t,diagrams:[]}),a[t].diagrams.push(e)})),n.categories=Object.values(a),w()}else h.style.display="flex"})),function(){c&&c.addEventListener("click",I);l&&l.addEventListener("click",(function(){D(1.25)}));d&&d.addEventListener("click",(function(){D(.8)}));u&&u.addEventListener("click",M);v&&v.addEventListener("click",A);_&&_.addEventListener("input",w);r&&(r.addEventListener("wheel",(function(e){e.preventDefault(),D(e.deltaY<0?1.1:.9)}),{passive:!1}),r.addEventListener("mousedown",H),r.addEventListener("mousemove",q),r.addEventListener("mouseup",O),r.addEventListener("mouseleave",O),r.addEventListener("touchstart",(function(e){1===e.touches.length&&H({clientX:e.touches[0].clientX,clientY:e.touches[0].clientY,preventDefault:function(){}})})),r.addEventListener("touchmove",(function(e){1===e.touches.length&&(e.preventDefault(),q({clientX:e.touches[0].clientX,clientY:e.touches[0].clientY}))}),{passive:!1}),r.addEventListener("touchend",O));document.addEventListener("keydown",(function(e){"viewer"===n.view&&("Escape"===e.key&&(n.selectedHotspot?A():I()),"+"!==e.key&&"="!==e.key||D(1.15),"-"===e.key&&D(.87),"0"===e.key&&M())}))}()}function k(e,t){y.style.display="flex",h.style.display="none",fetch(e,{headers:a}).then((function(e){return e.json()})).then((function(e){y.style.display="none",t(null,e)})).catch((function(e){y.style.display="none",t(e)}))}function w(){n.view="list",o.style.display="none",i.style.display="block";var e=(_&&_.value||"").toLowerCase(),t="";n.categories.forEach((function(a){var n=a.diagrams.filter((function(t){return!e||(-1!==(t.name_es||t.name||"").toLowerCase().indexOf(e)||-1!==(t.category_name_es||"").toLowerCase().indexOf(e)||-1!==(t.group_name_es||"").toLowerCase().indexOf(e))}));0!==n.length&&(t+='<div class="diagram-category">',t+='<h3 class="category-title">'+Y(a.name_es||a.name)+"</h3>",t+='<div class="diagram-grid">',n.forEach((function(e){t+='<div class="diagram-card" data-id="'+e.id_diagram+'" onclick="DiagramsApp.openDiagram('+e.id_diagram+')">',t+='<div class="diagram-card__preview">',t+='<img src="/pos/static/'+Y(e.image_path)+'" alt="'+Y(e.name_es||e.name)+'" loading="lazy" />',t+="</div>",t+='<div class="diagram-card__info">',t+='<div class="diagram-card__name">'+Y(e.name_es||e.name)+"</div>",t+='<div class="diagram-card__group">'+Y(e.group_name_es||e.group_name||"")+"</div>",t+="</div>",t+="</div>"})),t+="</div></div>")})),h.style.display=t?"none":"flex",i.innerHTML=t}function I(){n.view="list",n.currentDiagram=null,n.selectedHotspot=null,o.style.display="none",i.style.display="block",A()}function x(e,t){f.querySelectorAll(".part-item").forEach((function(a){parseInt(a.dataset.callout)===e&&a.classList.toggle("is-highlighted",t)}))}function C(e){var t=n.hotspots.find((function(t){return t.callout_number===e}));if(t){n.selectedHotspot=t;var a=s.querySelector("svg");a&&a.querySelectorAll("[data-hotspot]").forEach((function(t){parseInt(t.getAttribute("data-hotspot"))===e?(t.style.fill="rgba(245, 166, 35, 0.25)",t.style.stroke="#F5A623",t.style.strokeWidth="3"):(t.style.fill="transparent",t.style.stroke="none")})),f.querySelectorAll(".part-item").forEach((function(t){t.classList.toggle("is-active",parseInt(t.dataset.callout)===e)})),function(e){p.classList.add("is-open");e.part_name_es||e.part_name||e.callout_number;var t=e.part_number||"",a=e.description_es||e.description||"",n=P(e.callout_number),i=e.part_name_es||e.part_name||n.name,o=a||n.desc,r="";r+='<div class="hotspot-detail">',r+='<div class="hotspot-callout">'+e.callout_number+"</div>",r+='<h4 class="hotspot-name">'+Y(i)+"</h4>",t&&(r+='<div class="hotspot-partnumber">No. Parte: '+Y(t)+"</div>");o&&(r+='<p class="hotspot-desc">'+Y(o)+"</p>");r+='<div class="hotspot-actions">',e.part_id?(r+='<button class="btn btn-primary btn-sm" onclick="DiagramsApp.viewPart('+e.part_id+')">Ver detalle</button>',r+='<button class="btn btn-accent btn-sm" onclick="DiagramsApp.addToCart('+e.part_id+')"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 002 1.61h9.72a2 2 0 002-1.61L23 6H6"/></svg> Agregar</button>'):r+='<button class="btn btn-primary btn-sm" onclick="DiagramsApp.searchPart(\''+Y(i)+"')\">Buscar en catalogo</button>";r+="</div>",r+="</div>",g.innerHTML=r}(t)}}function P(e){var t=n.currentDiagram;if(!t)return{name:"Parte "+e,desc:""};var a=(t.name||"").toLowerCase();return-1!==a.indexOf("brak")||-1!==a.indexOf("freno")?{1:{name:"Disco de freno",desc:"Disco ventilado de freno delantero. Se recomienda cambiar en pares."},2:{name:"Caliper de freno",desc:"Caliper con pistones, incluye purga. Verificar compatibilidad con tipo de pastilla."},3:{name:"Pastillas de freno",desc:"Juego de pastillas con indicador de desgaste. Material ceramico o semi-metalico."},4:{name:"Manguera de freno",desc:"Manguera flexible de alta presion. Revisar por grietas cada 40,000 km."},5:{name:"Cilindro maestro",desc:"Cilindro maestro con deposito de liquido de frenos. Incluye empaques."}}[e]||{name:"Parte "+e,desc:""}:-1!==a.indexOf("susp")?{1:{name:"Amortiguador",desc:"Amortiguador delantero de gas. Se recomienda cambiar en pares."},2:{name:"Resorte helicoidal",desc:"Resorte de suspension delantera. Verificar altura libre."},3:{name:"Brazo de control",desc:"Brazo inferior de control con bujes. Incluye herraje de montaje."},4:{name:"Rotula",desc:"Rotula inferior de suspension. Incluye guardapolvo y seguros."},5:{name:"Barra de acoplamiento",desc:"Barra de acoplamiento de direccion con terminales. Requiere alineacion."}}[e]||{name:"Parte "+e,desc:""}:(-1!==a.indexOf("engine")||-1!==a.indexOf("motor"))&&{1:{name:"Filtro de aire",desc:"Filtro de aire del motor. Cambiar cada 15,000-20,000 km."},2:{name:"Bujias",desc:"Juego de bujias. Verificar tipo (platino, iridio) segun especificacion del motor."},3:{name:"Banda serpentina",desc:"Banda de accesorios. Revisar tension y desgaste. Incluye alternador, A/C y direccion."},4:{name:"Junta de culata",desc:"Junta de cabeza de cilindros. Material MLS multicapa. Requiere torque especifico."},5:{name:"Filtro de aceite",desc:"Filtro de aceite del motor. Cambiar en cada servicio de aceite."}}[e]||{name:"Parte "+e,desc:""}}function A(){p.classList.remove("is-open"),n.selectedHotspot=null;var e=s.querySelector("svg");e&&e.querySelectorAll("[data-hotspot]").forEach((function(e){e.style.fill="transparent",e.style.stroke="none"})),f&&f.querySelectorAll(".part-item").forEach((function(e){e.classList.remove("is-active")}))}function D(e){n.scale=Math.max(.3,Math.min(5,n.scale*e)),S()}function M(){n.scale=1,n.panX=0,n.panY=0,S()}function S(){s&&(s.style.transform="translate("+n.panX+"px, "+n.panY+"px) scale("+n.scale+")")}function H(e){e.button&&0!==e.button||(n.isPanning=!0,n.lastPointer={x:e.clientX,y:e.clientY})}function q(e){if(n.isPanning&&n.lastPointer){var t=e.clientX-n.lastPointer.x,a=e.clientY-n.lastPointer.y;n.panX+=t,n.panY+=a,n.lastPointer={x:e.clientX,y:e.clientY},S()}}function O(){n.isPanning=!1,n.lastPointer=null}function Y(e){if(!e)return"";var t=document.createElement("div");return t.appendChild(document.createTextNode(e)),t.innerHTML}}(); |