147 lines
7.1 KiB
Markdown
147 lines
7.1 KiB
Markdown
---
|
|
title: "Visitas de campo"
|
|
description: "Los diagnósticos en campo y en banco, por terminal y por unidad, con fotos y logs."
|
|
template: tablero.html
|
|
hide:
|
|
- toc
|
|
---
|
|
|
|
<div class="tb tb-campo">
|
|
<header class="tb-cab">
|
|
<div class="tb-cab__t"><h1>Visitas de campo</h1><span class="meta" id="meta">Cargando…</span></div>
|
|
<div class="tb-cab__acc"><a class="boton sec" href="/formularios/diagnostico.html"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M14 3.5H7a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8.5zM14 3.5v5h5M9 13h6M9 16.5h4"/></svg>Nueva acta de diagnóstico</a><button type="button" class="sec" id="refrescar"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M19.5 12a7.5 7.5 0 1 1-2.2-5.3M19.5 4.5v4h-4"/></svg>Actualizar</button></div>
|
|
</header>
|
|
|
|
<div id="cont"></div>
|
|
</div>
|
|
|
|
<script src="/formularios/flota.js"></script>
|
|
<script>
|
|
(function () {
|
|
const TIPOS = ['diagnostico-campo','diagnostico-banco'];
|
|
const TIPO = 'diagnostico-campo';
|
|
const cont = document.getElementById('cont');
|
|
const el = (t,a,...k)=>{const e=document.createElement(t); if(a) for(const p in a){ if(p==='html')e.innerHTML=a[p]; else if(p==='text')e.textContent=a[p]; else e.setAttribute(p,a[p]); } k.forEach(c=>e.append(c)); return e;};
|
|
const fecha = s => (s||'').replace('T',' ').slice(0,16);
|
|
|
|
function pill(v){
|
|
const t = String(v.accion||'');
|
|
if (/^Instalada y reportando/.test(t)) return ['ok','Operando'];
|
|
if (/^Instalada/.test(t)) return ['falla','Instalada sin resolver'];
|
|
if (/Querétaro/.test(t)) return ['info','En camino a QRO'];
|
|
if (/taller/.test(t)) return ['info','En taller'];
|
|
if (/Reemplazada/.test(t)) return ['ok','Reemplazada'];
|
|
return ['pend', v.resultado || '—'];
|
|
}
|
|
|
|
async function detalle(tipo, serie, archivo, caja){
|
|
caja.textContent = 'Cargando…';
|
|
try{
|
|
const r = await fetch(`/api/acta?tipo=${encodeURIComponent(tipo)}&serie=${encodeURIComponent(serie)}&archivo=${encodeURIComponent(archivo)}`);
|
|
const j = await r.json();
|
|
if(!j.exists) throw new Error('no encontrada');
|
|
const t = el('table',{class:'det'});
|
|
const res = j.acta.respuestas || {};
|
|
Object.keys(res).forEach(k=>{
|
|
const v = res[k]; if(v.val===''||v.val===false||v.val==null) return;
|
|
t.append(el('tr',null, el('td',{class:'k',text:(v.lbl||k).replace(/<[^>]+>/g,'')}),
|
|
el('td',{text: v.val===true ? 'sí' : String(v.val)})));
|
|
});
|
|
caja.innerHTML=''; caja.append(t);
|
|
}catch(e){ caja.textContent = 'No se pudo cargar el detalle.'; }
|
|
}
|
|
|
|
function render(actas){
|
|
cont.innerHTML='';
|
|
const porUnidad = {};
|
|
actas.filter(a=>TIPOS.includes(a.tipo)).forEach(a=>{ (porUnidad[a.serie]=porUnidad[a.serie]||[]).push(a); });
|
|
|
|
const flota = (window.FLOTA||[]).slice();
|
|
Object.keys(porUnidad).forEach(s=>{ if(!flota.some(u=>u.serie===s)) flota.push({serie:s, etiqueta:s, maquina:'(fuera del registro de flota)', terminal:'Sin clasificar'}); });
|
|
|
|
const visitas = actas.filter(a=>TIPOS.includes(a.tipo));
|
|
const conVisita = Object.keys(porUnidad).length;
|
|
const operando = flota.filter(u=>{const v=(porUnidad[u.serie]||[])[0]; return v && pill(v)[0]==='ok';}).length;
|
|
const kp = el('div',{class:'kpis'});
|
|
[[flota.length,'Unidades en el registro'],[visitas.length,'Diagnósticos recibidos'],
|
|
[conVisita,'Unidades ya diagnosticadas'],[operando,'Reportadas como operando']]
|
|
.forEach(([n,l])=>kp.append(el('div',{class:'kpi'}, el('div',{class:'n',text:String(n)}), el('div',{class:'l',text:l}))));
|
|
cont.append(kp);
|
|
|
|
const grupos = {};
|
|
flota.forEach(u=>{ (grupos[u.terminal||'Sin clasificar']=grupos[u.terminal||'Sin clasificar']||[]).push(u); });
|
|
|
|
Object.keys(grupos).sort().forEach(g=>{
|
|
const b = el('section',{class:'bloque'});
|
|
b.append(el('h2',{text:g}));
|
|
b.append(el('p',{class:'resumen',text:grupos[g].length+' unidad(es) registradas en esta terminal.'}));
|
|
grupos[g].forEach(u=>{
|
|
const vs = (porUnidad[u.serie]||[]);
|
|
const box = el('div',{class:'unidad'});
|
|
const cab = el('div',{class:'cab'});
|
|
cab.append(el('div',null,
|
|
el('div',{class:'s'}, el('a',{href:'/tableros/unidad/?serie='+encodeURIComponent(u.serie),
|
|
style:'color:inherit;text-decoration:none;border-bottom:1px dotted currentColor',
|
|
title:'Ver el expediente completo',text:u.serie})),
|
|
el('div',{class:'m',text:(u.maquina||'')+(u.nota?' · '+u.nota:'')})));
|
|
const [cls,txt] = vs.length ? pill(vs[0]) : ['pend','Sin diagnóstico'];
|
|
if(DANOS[u.serie]) cab.append(el('span',{class:'pill falla',text:DANOS[u.serie].marca==='BAJA'?'Baja':'Dañada'}));
|
|
cab.append(el('span',{class:'pill '+cls,text:txt}));
|
|
box.append(cab);
|
|
|
|
if(!vs.length) box.append(el('div',{class:'visita vacio',text:'Todavía no hay ningún diagnóstico registrado para esta unidad.'}));
|
|
vs.forEach(v=>{
|
|
const d = el('div',{class:'visita'});
|
|
d.append(el('div',{class:'f',text:(v.tipo==='diagnostico-banco'?'Banco · ':'Campo · ')+fecha(v.recibido)+' · '+(v.tecnico||'—')+(v.motivo?' · '+v.motivo:'')}));
|
|
if(v.clasificacion) d.append(el('div',{class:'cl',text:v.clasificacion}));
|
|
if(v.accion) d.append(el('div',{text:'→ '+v.accion}));
|
|
if(v.fotos && v.fotos.length){
|
|
const f = el('div',{class:'fotos'});
|
|
v.fotos.forEach(r=>{
|
|
const a = el('a',{href:'/api/adjunto?ruta='+encodeURIComponent(r),target:'_blank'});
|
|
a.append(el('img',{src:'/api/adjunto?ruta='+encodeURIComponent(r),loading:'lazy'}));
|
|
f.append(a);
|
|
});
|
|
d.append(f);
|
|
}
|
|
if(v.archivos && v.archivos.length){
|
|
const g = el('div',{class:'logs'});
|
|
v.archivos.forEach(a=>{
|
|
const x = el('a',{href:'/api/adjunto?ruta='+encodeURIComponent(a.ruta),target:'_blank',
|
|
text:(a.nombre||'captura')});
|
|
g.append(x);
|
|
});
|
|
d.append(g);
|
|
}
|
|
const det = el('details');
|
|
det.append(el('summary',{text:'Ver todas las respuestas'}));
|
|
const caja = el('div'); det.append(caja);
|
|
det.addEventListener('toggle',()=>{ if(det.open && !caja.dataset.listo){ caja.dataset.listo='1'; detalle(v.tipo, v.serie, v.archivo_n, caja); } });
|
|
d.append(det);
|
|
box.append(d);
|
|
});
|
|
b.append(box);
|
|
});
|
|
cont.append(b);
|
|
});
|
|
}
|
|
|
|
let DANOS = {};
|
|
async function cargar(){
|
|
document.getElementById('meta').textContent = 'Cargando…';
|
|
try{
|
|
try{ const d=await (await fetch('/api/danos',{cache:'no-store'})).json(); DANOS={}; (d.series||[]).forEach(x=>DANOS[x.serie]=x); }catch(e){}
|
|
const r = await fetch('/api/actas',{cache:'no-store'});
|
|
const j = await r.json();
|
|
render(j.actas||[]);
|
|
document.getElementById('meta').textContent = 'Actualizado ' + new Date().toLocaleString('es-MX',{dateStyle:'short',timeStyle:'short'});
|
|
}catch(e){
|
|
cont.innerHTML=''; cont.append(el('div',{class:'err',text:'No se pudo leer el servidor de actas: '+e.message}));
|
|
document.getElementById('meta').textContent = 'Error';
|
|
}
|
|
}
|
|
document.getElementById('refrescar').addEventListener('click',cargar);
|
|
cargar();
|
|
})();
|
|
</script>
|