hbox-docs/docs/tableros/ruta.js

97 lines
4.9 KiB
JavaScript

/* Ruta a producción: una fila por versión de EinarBoard y sus siete pasos
(esquemático → layout → auditoría → Gerber → piloto → banco → pedido).
Los datos salen de /datos/proyecto.json («tarjetas» y «etapas»); este archivo solo dibuja.
Lo usan la página de inicio y el tablero de Ingeniería.
Uso: Ruta.montar(contenedor, datos, { enlace: t => '/tableros/ingenieria/#' + t.clave });
Cómo se lee: línea continua = hecho; punteada = lo que falta; el anillo de color marca el paso
en el que va cada tarjeta (cian = en curso, coral = detenida). En el teléfono los textos de cada
paso se esconden y queda una línea con el paso actual y el que sigue.
Estilos: tableros/tablero.css (sección «Ruta a producción»). */
(function () {
const TONO = { hecho: 'Lista', ahora: 'En curso', aviso: 'Con pendientes', bloqueado: 'Detenida' };
const ESTADO_PASO = { hecho: 'hecho', ahora: 'en curso', bloqueado: 'detenido', falta: 'falta' };
const BADGE = { 'V5.5': 'badge-v55', 'V5.6': 'badge-v56', 'V5.7': 'badge-v57', 'v6': 'badge-v6' };
const el = (t, a, ...k) => {
const e = document.createElement(t);
if (a) for (const p in a) { if (p === 'text') e.textContent = a[p]; else if (p === 'html') e.innerHTML = a[p]; else e.setAttribute(p, a[p]); }
k.forEach(c => c != null && e.append(c));
return e;
};
// «Layout · Pasada 5 · Michelle, ≈ 1 h»
const describe = (etapa, p) => [etapa, p[1], p[2]].filter(Boolean).join(' · ');
function resumen(t, etapas) {
const pasos = t.pasos || [];
const i = pasos.findIndex(p => p[0] === 'ahora' || p[0] === 'bloqueado');
if (i < 0) {
if (pasos.length && pasos.every(p => p[0] === 'hecho')) return 'Todos los pasos hechos.';
const j = pasos.findIndex(p => p[0] === 'falta');
return j < 0 ? '' : 'Sigue: ' + describe(etapas[j], pasos[j]);
}
let s = (pasos[i][0] === 'bloqueado' ? 'Detenida en ' : 'Ahora: ') + describe(etapas[i], pasos[i]);
const j = pasos.findIndex((p, k) => k > i && p[0] === 'falta');
if (j > 0) s += '. Sigue: ' + [etapas[j], pasos[j][1]].filter(Boolean).join(' · ');
return s + '.';
}
function montar(cont, datos, opts) {
opts = opts || {};
const etapas = datos.etapas || [];
const tarjetas = datos.tarjetas || [];
cont.innerHTML = '';
const caja = el('div', { class: 'ruta', style: '--ruta-pasos:' + etapas.length });
const cab = el('div', { class: 'ruta-fila ruta-cab', 'aria-hidden': 'true' });
const nombres = el('div', { class: 'ruta-pasos-cab' });
etapas.forEach(e => nombres.append(el('span', { text: e })));
cab.append(el('span', { class: 'ruta-cab-t', text: 'Tarjeta' }), nombres);
caja.append(cab);
tarjetas.forEach(t => {
const fila = el('div', { class: 'ruta-fila tono-' + (t.tono || 'falta'), role: 'group', 'aria-label': 'EinarBoard ' + t.version + ': ' + (t.estado || '') });
if (opts.anclas) fila.id = t.clave;
const href = opts.enlace ? opts.enlace(t) : (t.liga || '#');
const info = el('a', { class: 'ruta-tarjeta', href });
if (t.imagen) info.append(el('img', { src: t.imagen, alt: t.alt || ('EinarBoard ' + t.version), width: '420', height: '300', loading: 'lazy', decoding: 'async' }));
const txt = el('span', { class: 'ruta-info' });
txt.append(el('span', { class: 'ruta-ver' },
el('span', { class: BADGE[t.version] || 'badge-v4', text: t.version }),
el('span', { class: 'ruta-estado', text: t.estado || TONO[t.tono] || '' })));
if (t.nuevo) txt.append(el('span', { class: 'ruta-nuevo', text: t.nuevo }));
info.append(txt);
fila.append(info);
const pasos = t.pasos || [];
const lista = el('ol', { class: 'ruta-pasos' });
pasos.forEach((p, i) => {
const est = p[0] || 'falta';
const llega = est === 'hecho' || est === 'ahora' || est === 'bloqueado';
const sale = est === 'hecho' && pasos[i + 1] && pasos[i + 1][0] !== 'falta';
const cls = ['ruta-paso', 'p-' + est];
if (i === 0) cls.push('primero');
if (i === pasos.length - 1) cls.push('ultimo');
if (llega) cls.push('entra');
if (sale || (est === 'hecho' && i === pasos.length - 1)) cls.push('sale');
const li = el('li', { class: cls.join(' '), title: describe(etapas[i], p) + ' (' + ESTADO_PASO[est] + ')' });
li.append(el('span', { class: 'ruta-punto', 'aria-hidden': 'true' }));
li.append(el('span', { class: 'vh', text: (etapas[i] || '') + ', ' + ESTADO_PASO[est] + '. ' }));
li.append(el('span', { class: 'ruta-txt', text: p[1] || '' }));
if (p[2]) li.append(el('span', { class: 'ruta-sub', text: p[2] }));
lista.append(li);
});
fila.append(lista);
fila.append(el('p', { class: 'ruta-resumen', text: resumen(t, etapas) }));
caja.append(fila);
});
cont.append(caja);
return caja;
}
window.Ruta = { montar, resumen };
})();