(payload = {})
| 7457 | let html = `<div class="mb-2 px-3 py-2 rounded border ${cls} text-sm">${label}</div>`; |
| 7458 | html += `<p class="text-xs text-gray-500 mb-2">Interface: ${escapeHtml(d.interface || '—')} · ${d.seconds}s · ${d.packet_count} BPDUs, ${d.bridge_count} bridge(s), ${d.instance_count} instance(s) @ ${d.rate}/s · ${d.tcn_count} TCN${d.learned ? ' · <span class="text-gray-400">baseline learned now</span>' : ''}</p>`; |
| 7459 | if ((d.instances || []).length) { |
| 7460 | html += '<p class="text-xs uppercase text-gray-400 mt-2 mb-1">Root bridge per instance</p>' + |
| 7461 | '<table class="min-w-full text-xs text-gray-300 whitespace-nowrap"><thead>' + |
| 7462 | '<tr class="text-left text-gray-500"><th class="px-2 py-1">Proto</th><th class="px-2 py-1">VLAN/inst</th><th class="px-2 py-1">Advertised root</th><th class="px-2 py-1">By</th><th class="px-2 py-1">Status</th></tr>' + |
| 7463 | '</thead><tbody>' + |
| 7464 | d.instances.map(i => { |
| 7465 | const bad = (i.status === 'hijacked' || i.status === 'new'); |
| 7466 | const st = bad ? `<span class="text-red-300">${escapeHtml(i.status)}</span>` : `<span class="text-gray-500">${escapeHtml(i.status)}</span>`; |
| 7467 | return `<tr class="border-t border-slate-800"> |
| 7468 | <td class="px-2 py-1 text-gray-400">${escapeHtml(i.proto)}</td> |
| 7469 | <td class="px-2 py-1 font-mono">${i.vlan}</td> |
| 7470 | <td class="px-2 py-1 font-mono ${bad ? 'text-red-300' : ''}">${i.root_prio == null ? '—' : i.root_prio}.${escapeHtml(i.root_mac || '?')}</td> |
| 7471 | <td class="px-2 py-1 font-mono text-gray-400">${escapeHtml(i.advertised_by || '—')}</td> |
| 7472 | <td class="px-2 py-1">${st}</td> |
| 7473 | </tr>`; |
| 7474 | }).join('') + |
| 7475 | '</tbody></table>'; |
| 7476 | } |
| 7477 | if ((d.bridges || []).length) { |
| 7478 | html += '<p class="text-xs uppercase text-gray-400 mt-2 mb-1">Bridges seen (' + d.bridges.length + ')</p>' + |
| 7479 | '<table class="min-w-full text-xs text-gray-300 whitespace-nowrap"><thead>' + |
| 7480 | '<tr class="text-left text-gray-500"><th class="px-2 py-1">Bridge MAC</th><th class="px-2 py-1">Proto</th><th class="px-2 py-1">Role(s)</th><th class="px-2 py-1">Status</th></tr>' + |
| 7481 | '</thead><tbody>' + |
| 7482 | d.bridges.map(b => { |
| 7483 | const badge = b.baseline ? '<span class="text-green-400">✓ known</span>' : '<span class="text-amber-300">? new</span>'; |
| 7484 | return `<tr class="border-t border-slate-800"> |
| 7485 | <td class="px-2 py-1 font-mono ${b.baseline ? '' : 'text-amber-300'}">${escapeHtml(b.mac)}</td> |
| 7486 | <td class="px-2 py-1 text-gray-400">${escapeHtml(b.proto)}</td> |
| 7487 | <td class="px-2 py-1 text-gray-400">${escapeHtml((b.roles || []).join(', ') || '—')}</td> |
no test coverage detected