/* 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 }; })();