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
This commit is contained in:
2026-04-27 08:34:24 +00:00
parent e21722a3a9
commit 21959f1b37
56 changed files with 5629 additions and 4 deletions

1
dashboard/chat-public.min.js vendored Normal file
View File

@@ -0,0 +1 @@
!function(){"use strict";var e=!1,t=!1,n=[];function a(){var e=document.createElement("button");e.className="chat-fab",e.id="chatFab",e.title="Asistente IA",e.innerHTML="&#x1F4AC;",e.setAttribute("aria-label","Abrir asistente IA");var t=document.createElement("div");t.className="chat-panel",t.id="chatPanel",t.innerHTML='<div class="chat-header"><h3>Asistente — Buscar partes</h3><button class="chat-header-close" id="chatClose" aria-label="Cerrar">&times;</button></div><div class="chat-messages" id="chatMessages"><div class="chat-msg ai">Hola, soy el asistente de Nexus Autoparts. Dime que refaccion buscas y te ayudo a encontrarla en el catalogo.</div><div class="chat-typing" id="chatTyping"><span></span><span></span><span></span></div></div><div class="chat-input-area"><textarea class="chat-input" id="chatInput" placeholder="Ej: Balatas para Tsuru 2015..." rows="1"></textarea><button class="chat-send-btn" id="chatSend" aria-label="Enviar">&#9654;</button></div>',document.body.appendChild(e),document.body.appendChild(t),e.addEventListener("click",s),document.getElementById("chatClose").addEventListener("click",s),document.getElementById("chatSend").addEventListener("click",c),document.getElementById("chatInput").addEventListener("keydown",(function(e){"Enter"!==e.key||e.shiftKey||(e.preventDefault(),c())})),document.getElementById("chatInput").addEventListener("input",(function(){this.style.height="auto",this.style.height=Math.min(this.scrollHeight,80)+"px"}))}function s(){e=!e;var t=document.getElementById("chatPanel"),n=document.getElementById("chatFab");e?(t.classList.add("open"),n.style.display="none",document.getElementById("chatInput").focus()):(t.classList.remove("open"),n.style.display="flex")}function c(){if(!t){var e=document.getElementById("chatInput"),a=e.value.trim();a&&(e.value="",e.style.height="auto",i(a,"user"),n.push({role:"user",content:a}),n.length>20&&n.splice(0,2),t=!0,document.getElementById("chatSend").disabled=!0,d(!0),fetch("/api/chat",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:a,history:n.slice(-10)})}).then((function(e){return e.json()})).then((function(e){if(e.error)i("Error: "+e.error,"ai");else{var t,a,c,d,l=e.response||"Sin respuesta.";i(l,"ai"),n.push({role:"assistant",content:l}),e.search_results&&e.search_results.length>0&&(t=e.search_results,a=document.getElementById("chatMessages"),c=document.getElementById("chatTyping"),(d=document.createElement("div")).className="chat-parts",t.slice(0,8).forEach((function(e){var t=document.createElement("div");t.className="chat-part-card";var n=e.name_es||e.name_part||"",a=e.oem_part_number||e.part_number||"",c=e.brand||"";t.innerHTML='<div class="part-number">'+o(a)+'</div><div class="part-name">'+o(n)+(c?' <span style="color:var(--color-text-muted);">('+o(c)+")</span>":"")+"</div>",t.style.cursor="pointer",t.addEventListener("click",(function(){var e=document.getElementById("searchInput");e&&a&&(e.value=a,"function"==typeof window.doSearch&&window.doSearch(),s())})),d.appendChild(t)})),a.insertBefore(d,c),r())}})).catch((function(e){i("Error de conexion: "+e.message,"ai")})).finally((function(){t=!1,document.getElementById("chatSend").disabled=!1,d(!1)})))}}function i(e,t){var n=document.getElementById("chatMessages"),a=document.getElementById("chatTyping"),s=document.createElement("div");s.className="chat-msg "+t,s.textContent=e,n.insertBefore(s,a),r()}function d(e){var t=document.getElementById("chatTyping");t&&t.classList.toggle("visible",e),e&&r()}function r(){var e=document.getElementById("chatMessages");e&&(e.scrollTop=e.scrollHeight)}function o(e){if(!e)return"";var t=document.createElement("div");return t.textContent=e,t.innerHTML}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):a()}();