diff --git a/deploy/public/index.html b/deploy/public/index.html index 2ec9e96..80b53a7 100644 --- a/deploy/public/index.html +++ b/deploy/public/index.html @@ -225,6 +225,8 @@ a{color:var(--blue);text-decoration:none;transition:color .2s}a:hover{color:var( .show-more-btn{font-size:.8rem;color:var(--blue);cursor:pointer;padding:.5rem 0;margin-top:.25rem;transition:color .2s;user-select:none} .show-more-btn:hover{color:var(--cyan);text-decoration:underline} +.sort-toggle{font-size:.7rem;color:var(--muted);cursor:pointer;padding:.2rem .5rem;border:1px solid var(--border);border-radius:4px;transition:all .2s;user-select:none} +.sort-toggle:hover{color:var(--blue);border-color:var(--blue)} /* Routing Overview - Propagation Bars */ .routing-stats-row{display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:1.25rem} @@ -532,6 +534,44 @@ function copyToClipboard(text, btn) { }); } +function renderAuditList(containerId, list, sortBy, type) { + var sorted = list.slice(); + if (sortBy === 'asn') sorted.sort(function(a, b) { return a.asn - b.asn; }); + else if (sortBy === 'frequency') sorted.sort(function(a, b) { return (b.frequency || 0) - (a.frequency || 0); }); + else if (sortBy === 'name') sorted.sort(function(a, b) { return (a.name || '').localeCompare(b.name || ''); }); + + var labelId = type === 'missing' ? 'missingSortLabel' : 'extraSortLabel'; + var labelEl = document.getElementById(labelId); + if (labelEl) labelEl.textContent = sortBy === 'asn' ? 'by ASN' : sortBy === 'frequency' ? 'by frequency' : 'by name'; + + var LIMIT = 5; + var h = ''; + sorted.slice(0, LIMIT).forEach(function(item) { + h += '
'; + h += '' + asnLink(item.asn) + ''; + if (item.name && item.name !== 'AS' + item.asn) h += ' ' + escHtml(item.name) + ''; + if (type === 'missing') h += ' seen in ' + (item.frequency_pct || 0) + '% of paths (' + (item.frequency || 0) + ')'; + else h += ' not seen in any path'; + h += '
'; + }); + if (sorted.length > LIMIT) { + var moreId = containerId + 'More'; + h += ''; + var remaining = sorted.length - LIMIT; + h += '
Show ' + remaining + ' more...
'; + } + document.getElementById(containerId).innerHTML = h; +} + async function doLookup() { const raw = $('asnInput').value.trim().replace(/[^0-9]/g, ''); if (!raw) return; @@ -1197,57 +1237,26 @@ function renderAspaDeep(d) { h += '
Completeness
' + (audit.completeness_pct || 0) + '%
'; h += ''; - // Missing from ASPA (collapsible after 5) + // Missing from ASPA (collapsible, sortable) if (audit.missing_from_aspa && audit.missing_from_aspa.length > 0) { - var missingList = audit.missing_from_aspa; - var LIMIT = 5; - h += '
Missing from ASPA Declaration (' + missingList.length + ')
'; - missingList.slice(0, LIMIT).forEach(function(m) { - h += '
'; - h += '' + asnLink(m.asn) + ''; - if (m.name && m.name !== 'AS' + m.asn) h += ' ' + escHtml(m.name) + ''; - h += ' seen in ' + (m.frequency_pct || 0) + '% of paths (' + (m.frequency || 0) + ')'; - h += '
'; - }); - if (missingList.length > LIMIT) { - var moreId = 'missingMore' + Date.now(); - h += ''; - h += '
Show ' + (missingList.length - LIMIT) + ' more...
'; - } + window._auditMissing = audit.missing_from_aspa; + window._auditMissingSort = 'frequency'; + h += '
'; + h += 'Missing from ASPA Declaration (' + audit.missing_from_aspa.length + ')'; + h += 'Sort: by frequency'; + h += '
'; + h += '
'; } - // Extra in ASPA (collapsible after 5) + // Extra in ASPA (collapsible, sortable) if (audit.extra_in_aspa && audit.extra_in_aspa.length > 0) { - var extraList = audit.extra_in_aspa; - h += '
Extra in ASPA (not seen in paths) (' + extraList.length + ')
'; - extraList.slice(0, LIMIT).forEach(function(e) { - h += '
'; - h += '' + asnLink(e.asn) + ''; - if (e.name && e.name !== 'AS' + e.asn) h += ' ' + escHtml(e.name) + ''; - h += ' not seen in any path'; - h += '
'; - }); - if (extraList.length > LIMIT) { - var extraMoreId = 'extraMore' + Date.now(); - h += ''; - h += '
Show ' + (extraList.length - LIMIT) + ' more...
'; - } + window._auditExtra = audit.extra_in_aspa; + window._auditExtraSort = 'asn'; + h += '
'; + h += 'Extra in ASPA (not seen in paths) (' + audit.extra_in_aspa.length + ')'; + h += 'Sort: by ASN'; + h += '
'; + h += '
'; } if ((!audit.missing_from_aspa || audit.missing_from_aspa.length === 0) && (!audit.extra_in_aspa || audit.extra_in_aspa.length === 0)) { @@ -1316,6 +1325,14 @@ function renderAspaDeep(d) { h += '
ASPA verification completed in ' + (d.meta ? d.meta.duration_ms : '?') + 'ms | ' + (d.meta ? d.meta.paths_analyzed : '?') + ' of ' + (d.meta ? d.meta.total_paths_seen : '?') + ' paths analyzed
'; $('aspaDeepContent').innerHTML = h; + + // Initial render of sortable audit lists + if (window._auditMissing && window._auditMissing.length > 0) { + renderAuditList('missingListEl', window._auditMissing, 'frequency', 'missing'); + } + if (window._auditExtra && window._auditExtra.length > 0) { + renderAuditList('extraListEl', window._auditExtra, 'asn', 'extra'); + } }