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
3.8 KiB
JavaScript
1 line
3.8 KiB
JavaScript
!function(){"use strict";const e=document.getElementById("loginPanel"),t=document.getElementById("registerPanel"),o=document.getElementById("loginForm"),n=document.getElementById("registerForm"),r=document.getElementById("alert"),a={ADMIN:"/demo",OWNER:"/demo",BODEGA:"/bodega",TALLER:"/demo"};function s(e,t){r.textContent=e,r.className="login-alert show "+t}function c(){r.className="login-alert",r.textContent=""}function l(e,t){e.classList.toggle("loading",t)}!function(){const e=localStorage.getItem("access_token"),t=localStorage.getItem("user_role");if(e&&t){const e=a[t]||"/index.html";window.location.replace(e)}}(),window.showPanel=function(o){c(),"register"===o?(e.classList.remove("active"),t.classList.add("active")):(t.classList.remove("active"),e.classList.add("active"))},o.addEventListener("submit",(async function(e){e.preventDefault(),c();const t=document.getElementById("loginEmail").value.trim(),n=document.getElementById("loginPassword").value,r=o.querySelector(".btn-submit");if(t&&n){l(r,!0);try{const e=await fetch("/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t,password:n})}),o=await e.json();if(!e.ok)return void s(o.error||o.message||"Credenciales incorrectas.","error");localStorage.setItem("access_token",o.access_token),localStorage.setItem("refresh_token",o.refresh_token||""),localStorage.setItem("user_role",o.role||o.user?.role||""),localStorage.setItem("user_name",o.name||o.user?.name||"");const r=(o.role||o.user?.role||"").toUpperCase(),c=a[r]||"/index.html";window.location.replace(c)}catch(e){s("Error de conexion. Intenta de nuevo.","error")}finally{l(r,!1)}}else s("Completa todos los campos.","error")})),n.addEventListener("submit",(async function(e){e.preventDefault(),c();const t=document.getElementById("regName").value.trim(),o=document.getElementById("regEmail").value.trim(),r=document.getElementById("regPassword").value,a=document.getElementById("regConfirm").value,i=document.getElementById("regBusiness").value.trim(),m=document.getElementById("regPhone").value.trim(),d=document.getElementById("regRole").value,u=n.querySelector(".btn-submit");if(t&&o&&r&&a&&i&&m)if(r.length<8)s("La contrasena debe tener al menos 8 caracteres.","error");else if(r===a){l(u,!0);try{const e=await fetch("/api/auth/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:t,email:o,password:r,role:d,business_name:i,phone:m})}),a=await e.json();if(!e.ok)return void s(a.error||a.message||"Error al crear la cuenta.","error");s("Cuenta creada. Pendiente de aprobacion por administrador.","success"),n.reset()}catch(e){s("Error de conexion. Intenta de nuevo.","error")}finally{l(u,!1)}}else s("Las contrasenas no coinciden.","error");else s("Completa todos los campos.","error")})),window.authFetch=async function(e,t={}){const o=localStorage.getItem("access_token");if(!o)return void window.location.replace("/login.html");const n=Object.assign({},t.headers||{},{Authorization:"Bearer "+o});let r=await fetch(e,Object.assign({},t,{headers:n}));if(401===r.status){const o=await async function(){const e=localStorage.getItem("refresh_token");if(!e)return!1;try{const t=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e})});if(!t.ok)return!1;const o=await t.json();return localStorage.setItem("access_token",o.access_token),o.refresh_token&&localStorage.setItem("refresh_token",o.refresh_token),!0}catch(e){return!1}}();if(!o)return void logout();n.Authorization="Bearer "+localStorage.getItem("access_token"),r=await fetch(e,Object.assign({},t,{headers:n}))}return r},window.logout=function(){localStorage.removeItem("access_token"),localStorage.removeItem("refresh_token"),localStorage.removeItem("user_role"),localStorage.removeItem("user_name"),window.location.replace("/login.html")}}(); |