Files
Autoparts-DB/pos/static/js/fleet.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
14 KiB
JavaScript

var Fleet=function(){"use strict";var e="/pos/api/fleet",t=localStorage.getItem("pos_token"),n=[],a=1,o=null;function l(){return{"Content-Type":"application/json",Authorization:"Bearer "+t}}function c(e){return null==e?"0":parseFloat(e).toLocaleString("es-MX")}function i(e){return null==e?"$0.00":"$"+parseFloat(e).toLocaleString("es-MX",{minimumFractionDigits:2,maximumFractionDigits:2})}function d(e){return e?new Date(e).toLocaleDateString("es-MX",{day:"2-digit",month:"short",year:"numeric"}):"—"}function r(e){if(!e)return"";var t=document.createElement("div");return t.textContent=e,t.innerHTML}function u(){fetch(e+"/stats",{headers:l()}).then((function(e){return e.json()})).then((function(e){document.getElementById("statTotal").textContent=c(e.total_vehicles),document.getElementById("statOverdue").textContent=c(e.overdue_count),document.getElementById("statUpcoming").textContent=c(e.upcoming_this_month),document.getElementById("statCost").textContent=i(e.total_cost_month)})).catch((function(){}))}function s(){var t=(document.getElementById("searchInput")||{}).value||"",o=e+"/vehicles?page="+a+"&per_page=50";t&&(o+="&q="+encodeURIComponent(t)),fetch(o,{headers:l()}).then((function(e){return e.json()})).then((function(e){var t;(function(e){var t=document.getElementById("vehicleGrid");if(!e.length)return void(t.innerHTML='<div class="empty-state"><div class="empty-state__icon">&#x1F69A;</div><div class="empty-state__text">No hay vehiculos registrados</div><button class="btn btn--primary" onclick="Fleet.openVehicleModal()">+ Agregar Vehiculo</button></div>');var n="";e.forEach((function(e){var t=(e.make||"")+" "+(e.model||"");e.year&&(t+=" "+e.year),n+='<div class="vehicle-card" onclick="Fleet.viewVehicle('+e.id+')"><div class="vehicle-card__header"><span class="vehicle-card__plate">'+r(e.plate||"SIN PLACA")+'</span><span class="badge '+(e.is_active?"badge--active":"badge--inactive")+'">'+(e.is_active?"Activo":"Inactivo")+'</span></div><div class="vehicle-card__info"><div><strong>'+r(t.trim())+"</strong></div>"+(e.owner_name?"<div>"+r(e.owner_name)+"</div>":"")+(e.color?"<div>Color: "+r(e.color)+"</div>":"")+'</div><div class="vehicle-card__footer"><span>'+c(e.current_mileage)+" km</span><span>"+r(e.fuel_type||"")+"</span></div></div>"})),t.innerHTML=n})(n=e.data||[]),function(e){var t=document.getElementById("vehiclePagination");if(!e||e.total_pages<=1)return void(t.innerHTML="");t.innerHTML='<button class="pagination__btn" '+(e.page<=1?"disabled":"")+' onclick="Fleet.goPage('+(e.page-1)+')">&laquo; Anterior</button><span class="pagination__info">Pagina '+e.page+" de "+e.total_pages+" ("+e.total+' vehiculos)</span><button class="pagination__btn" '+(e.page>=e.total_pages?"disabled":"")+' onclick="Fleet.goPage('+(e.page+1)+')">Siguiente &raquo;</button>'}(e.pagination),t='<option value="">-- Seleccionar vehiculo --</option>',n.forEach((function(e){var n=(e.plate||"S/P")+" - "+(e.make||"")+" "+(e.model||"");t+='<option value="'+e.id+'">'+r(n.trim())+"</option>"})),["schedVehicleSelect","logVehicleSelect"].forEach((function(e){var n=document.getElementById(e);n&&(n.innerHTML=t)}))})).catch((function(){document.getElementById("vehicleGrid").innerHTML='<div class="empty-state"><div class="empty-state__text">Error al cargar vehiculos</div></div>'}))}function m(e){var t=document.getElementById("vehicleModal"),n=document.getElementById("vehicleModalTitle");e&&e.id?(n.textContent="Editar Vehiculo",document.getElementById("vehEditId").value=e.id,document.getElementById("vehPlate").value=e.plate||"",document.getElementById("vehVin").value=e.vin||"",document.getElementById("vehMake").value=e.make||"",document.getElementById("vehModel").value=e.model||"",document.getElementById("vehYear").value=e.year||"",document.getElementById("vehMileage").value=e.current_mileage||0,document.getElementById("vehFuel").value=e.fuel_type||"gasolina",document.getElementById("vehColor").value=e.color||"",document.getElementById("vehOwner").value=e.owner_name||"",document.getElementById("vehNotes").value=e.notes||""):(n.textContent="Nuevo Vehiculo",document.getElementById("vehEditId").value="",["vehPlate","vehVin","vehMake","vehModel","vehYear","vehColor","vehOwner","vehNotes"].forEach((function(e){document.getElementById(e).value=""})),document.getElementById("vehMileage").value="0",document.getElementById("vehFuel").value="gasolina"),t.classList.add("is-open")}function v(){document.getElementById("vehicleModal").classList.remove("is-open")}function h(){fetch(e+"/vehicles?per_page=200",{headers:l()}).then((function(e){return e.json()})).then((function(t){var n=(t.data||[]).map((function(t){return fetch(e+"/vehicles/"+t.id+"/schedules",{headers:l()}).then((function(e){return e.json()})).then((function(e){return{vehicle:t,schedules:e.data||[]}}))}));return Promise.all(n)})).then((function(e){!function(e){var t=document.getElementById("maintBody"),n=[];if(e.forEach((function(e){var t=e.vehicle;e.schedules.forEach((function(e){var a=new Date,o=!1;e.next_due_at&&new Date(e.next_due_at)<a&&(o=!0),e.next_due_km&&e.next_due_km<=t.current_mileage&&(o=!0);var l="";e.interval_km&&(l+=c(e.interval_km)+" km"),e.interval_km&&e.interval_months&&(l+=" / "),e.interval_months&&(l+=e.interval_months+" meses");var i="";e.next_due_at&&(i+=d(e.next_due_at)),e.next_due_at&&e.next_due_km&&(i+=" / "),e.next_due_km&&(i+=c(e.next_due_km)+" km"),n.push("<tr><td><strong>"+r(t.plate||"S/P")+'</strong><br><span style="font-size:var(--text-caption);color:var(--color-text-muted);">'+r((t.make||"")+" "+(t.model||""))+"</span></td><td>"+r(e.maintenance_type)+'</td><td class="mono">'+(l||"—")+"</td><td>"+d(e.last_done_at)+(e.last_done_km?'<br><span class="mono" style="font-size:var(--text-caption);">'+c(e.last_done_km)+" km</span>":"")+"</td><td>"+(i||"—")+'</td><td><span class="badge '+(o?"badge--overdue":"badge--active")+'">'+(o?"Vencido":"Al dia")+'</span></td><td><button class="btn btn--sm btn--ghost" onclick="Fleet.openLogModalFor('+t.id+","+e.id+",'"+r(e.maintenance_type)+"')\">Registrar</button></td></tr>")}))})),!n.length)return void(t.innerHTML='<tr><td colspan="7" style="text-align:center;padding:var(--space-6);color:var(--color-text-muted);">No hay programas de mantenimiento.<br><button class="btn btn--primary btn--sm" style="margin-top:var(--space-3);" onclick="Fleet.openScheduleModal()">+ Crear Programa</button></td></tr>');t.innerHTML=n.join("")}(e)})).catch((function(){document.getElementById("maintBody").innerHTML='<tr><td colspan="7" style="text-align:center;color:var(--color-text-muted);">Error al cargar</td></tr>'}))}function g(){fetch(e+"/vehicles?per_page=200",{headers:l()}).then((function(e){return e.json()})).then((function(t){var n=(t.data||[]).map((function(t){return fetch(e+"/vehicles/"+t.id,{headers:l()}).then((function(e){return e.json()})).then((function(e){return{vehicle:t,logs:e.recent_logs||[]}}))}));return Promise.all(n)})).then((function(e){!function(e){var t=document.getElementById("historyBody"),n=[];if(e.forEach((function(e){e.logs.forEach((function(t){t._plate=e.vehicle.plate||"S/P",t._make=(e.vehicle.make||"")+" "+(e.vehicle.model||""),n.push(t)}))})),n.sort((function(e,t){return new Date(t.created_at)-new Date(e.created_at)})),!n.length)return void(t.innerHTML='<tr><td colspan="7" style="text-align:center;padding:var(--space-6);color:var(--color-text-muted);">No hay registros de mantenimiento</td></tr>');var a="";n.forEach((function(e){a+="<tr><td>"+d(e.created_at)+"</td><td><strong>"+r(e._plate)+'</strong><br><span style="font-size:var(--text-caption);color:var(--color-text-muted);">'+r(e._make)+"</span></td><td>"+r(e.maintenance_type)+'</td><td class="mono">'+c(e.mileage_at)+'</td><td class="mono">'+i(e.cost)+"</td><td>"+r(e.employee_name||"—")+'</td><td style="max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+r(e.notes||"—")+"</td></tr>"})),t.innerHTML=a}(e)})).catch((function(){document.getElementById("historyBody").innerHTML='<tr><td colspan="7" style="text-align:center;color:var(--color-text-muted);">Error al cargar</td></tr>'}))}function p(){fetch(e+"/alerts",{headers:l()}).then((function(e){return e.json()})).then((function(e){!function(e){var t=document.getElementById("alertsList");if(!e.length)return void(t.innerHTML='<div class="empty-state"><div class="empty-state__icon" style="color:#3FB950;">&#10003;</div><div class="empty-state__text">No hay mantenimientos vencidos</div></div>');var n="";e.forEach((function(e){var t="";e.next_due_at&&(t+="Vencio: "+d(e.next_due_at)),e.next_due_km&&(t+=(t?" | ":"")+"Limite: "+c(e.next_due_km)+" km (actual: "+c(e.current_mileage)+" km)"),n+='<div class="alert-card"><div class="alert-card__info"><div class="alert-card__vehicle">'+r(e.plate||"S/P")+" — "+r((e.make||"")+" "+(e.model||""))+(e.year?" "+e.year:"")+'</div><div class="alert-card__detail"><span class="badge badge--overdue">'+r(e.maintenance_type)+"</span> &nbsp; "+t+'</div></div><button class="btn btn--sm btn--primary" onclick="Fleet.openLogModalFor('+e.vehicle_id+","+e.schedule_id+",'"+r(e.maintenance_type)+"')\">Registrar Mant.</button></div>"})),t.innerHTML=n}(e.data||[])})).catch((function(){document.getElementById("alertsList").innerHTML='<div class="empty-state"><div class="empty-state__text">Error al cargar alertas</div></div>'}))}function y(){document.getElementById("scheduleModal").classList.remove("is-open")}function f(){var e=document.getElementById("logModal");document.getElementById("logScheduleSelect").innerHTML='<option value="">-- Sin programa --</option>',document.getElementById("logType").value="",document.getElementById("logMileage").value="",document.getElementById("logCost").value="",document.getElementById("logParts").value="",document.getElementById("logNotes").value="",e.classList.add("is-open")}function _(){document.getElementById("logModal").classList.remove("is-open")}return document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".tab-btn").forEach((function(e){e.addEventListener("click",(function(){var e=this.getAttribute("data-tab");document.querySelectorAll(".tab-btn").forEach((function(e){e.classList.remove("is-active")})),document.querySelectorAll(".tab-panel").forEach((function(e){e.classList.remove("is-active")})),this.classList.add("is-active"),document.getElementById("tab-"+e).classList.add("is-active"),"maintenance"===e&&h(),"history"===e&&g(),"alerts"===e&&p()}))}));var e=document.getElementById("searchInput");e&&e.addEventListener("input",(function(){clearTimeout(o),o=setTimeout((function(){a=1,s()}),300)})),document.getElementById("btnNewVehicle").addEventListener("click",(function(){m()})),u(),s()})),{openVehicleModal:m,closeVehicleModal:v,saveVehicle:function(){var t=document.getElementById("vehEditId").value,n={plate:document.getElementById("vehPlate").value.trim(),vin:document.getElementById("vehVin").value.trim(),make:document.getElementById("vehMake").value.trim(),model:document.getElementById("vehModel").value.trim(),year:parseInt(document.getElementById("vehYear").value)||null,current_mileage:parseInt(document.getElementById("vehMileage").value)||0,fuel_type:document.getElementById("vehFuel").value,color:document.getElementById("vehColor").value.trim(),owner_name:document.getElementById("vehOwner").value.trim(),notes:document.getElementById("vehNotes").value.trim()};if(n.plate||n.vin){var a=e+"/vehicles",o="POST";t&&(a+="/"+t,o="PUT"),fetch(a,{method:o,headers:l(),body:JSON.stringify(n)}).then((function(e){return e.json()})).then((function(e){e.error?alert(e.error):(v(),s(),u())})).catch((function(e){alert("Error: "+e.message)}))}else alert("Se requiere placa o VIN")},viewVehicle:function(t){fetch(e+"/vehicles/"+t,{headers:l()}).then((function(e){return e.json()})).then((function(e){m(e)}))},goPage:function(e){a=e,s()},openScheduleModal:function(e){var t=document.getElementById("scheduleModal");document.getElementById("schedType").value="",document.getElementById("schedIntervalKm").value="",document.getElementById("schedIntervalMonths").value="",document.getElementById("schedNextDate").value="",document.getElementById("schedNextKm").value="",document.getElementById("schedNotes").value="",e&&(document.getElementById("schedVehicleSelect").value=e),t.classList.add("is-open")},closeScheduleModal:y,saveSchedule:function(){var t=document.getElementById("schedVehicleSelect").value;if(t){var n=document.getElementById("schedType").value.trim();if(n){var a={maintenance_type:n,interval_km:parseInt(document.getElementById("schedIntervalKm").value)||null,interval_months:parseInt(document.getElementById("schedIntervalMonths").value)||null,next_due_at:document.getElementById("schedNextDate").value||null,next_due_km:parseInt(document.getElementById("schedNextKm").value)||null,notes:document.getElementById("schedNotes").value.trim()};fetch(e+"/vehicles/"+t+"/schedules",{method:"POST",headers:l(),body:JSON.stringify(a)}).then((function(e){return e.json()})).then((function(e){e.error?alert(e.error):(y(),h(),u())})).catch((function(e){alert("Error: "+e.message)}))}else alert("Ingrese el tipo de mantenimiento")}else alert("Seleccione un vehiculo")},openLogModal:f,openLogModalFor:function(t,a,o){f(),document.getElementById("logVehicleSelect").value=t,o&&(document.getElementById("logType").value=o),fetch(e+"/vehicles/"+t+"/schedules",{headers:l()}).then((function(e){return e.json()})).then((function(e){var t='<option value="">-- Sin programa --</option>';(e.data||[]).forEach((function(e){var n=e.id==a?" selected":"";t+='<option value="'+e.id+'"'+n+">"+r(e.maintenance_type)+"</option>"})),document.getElementById("logScheduleSelect").innerHTML=t}));var c=n.find((function(e){return e.id==t}));c&&(document.getElementById("logMileage").value=c.current_mileage||"")},closeLogModal:_,saveLog:function(){var t=document.getElementById("logVehicleSelect").value;if(t){var n=document.getElementById("logType").value.trim();if(n){var a={schedule_id:parseInt(document.getElementById("logScheduleSelect").value)||null,maintenance_type:n,mileage_at:parseInt(document.getElementById("logMileage").value)||null,cost:parseFloat(document.getElementById("logCost").value)||0,parts_used:document.getElementById("logParts").value.trim(),notes:document.getElementById("logNotes").value.trim()};fetch(e+"/vehicles/"+t+"/log",{method:"POST",headers:l(),body:JSON.stringify(a)}).then((function(e){return e.json()})).then((function(e){if(e.error)alert(e.error);else{_(),u();var t=document.querySelector(".tab-btn.is-active");if(t){var n=t.getAttribute("data-tab");"maintenance"===n&&h(),"history"===n&&g(),"alerts"===n&&p(),"vehicles"===n&&s()}}})).catch((function(e){alert("Error: "+e.message)}))}else alert("Ingrese el tipo de mantenimiento")}else alert("Seleccione un vehiculo")}}}();