/** * UI Rendering Module for Visualization UI * Handles rendering of Alerts table and Outlier summary panel. * * @module ui */ /** * Renders the Max-only alert data into a table structure within #alert-dashboard. * @param {Array} alerts - Array of alert objects from /alert-data endpoint. */ export function renderAlertDashboard(alerts = []) { const container = document.getElementById('alert-dashboard'); if (!container) return; container.innerHTML = ''; const status = document.createElement('div'); status.className = 'visualization_ui__status'; if (!Array.isArray(alerts) || alerts.length === 0) { status.textContent = 'Keine Alert-Daten vorhanden.'; container.appendChild(status); return; } const table = document.createElement('table'); table.className = 'visualization_ui__table'; const thead = document.createElement('thead'); thead.innerHTML = ` corr_id stratum expires_at_dist_hours retry_total_overhead_ms `; table.appendChild(thead); const tbody = document.createElement('tbody'); for (const alert of alerts) { const row = document.createElement('tr'); row.innerHTML = ` ${escapeHTML(alert.corr_id ?? '')} ${escapeHTML(alert.stratum ?? '')} ${escapeHTML(alert.expires_at_dist_hours ?? '')} ${escapeHTML(alert.retry_total_overhead_ms ?? '')} `; tbody.appendChild(row); } table.appendChild(tbody); container.appendChild(table); } /** * Displays outlier analysis summary metrics inside #outlier-summary panel. * @param {Object} report - Report object fetched from /outlier-report endpoint. */ export function renderOutlierSummary(report = {}) { const container = document.getElementById('outlier-summary'); if (!container) return; container.innerHTML = ''; const summary = document.createElement('div'); summary.className = 'visualization_ui__outlier-summary'; if (!Object.keys(report).length) { summary.textContent = 'Noch keine Outlier-Daten verfügbar.'; container.appendChild(summary); return; } const list = document.createElement('dl'); for (const [key, value] of Object.entries(report)) { const term = document.createElement('dt'); term.textContent = key; const desc = document.createElement('dd'); desc.textContent = typeof value === 'number' ? value.toFixed(3) : String(value); list.appendChild(term); list.appendChild(desc); } summary.appendChild(list); container.appendChild(summary); } /** * Escapes potential HTML in strings to prevent XSS injection. * @param {string} str * @returns {string} */ function escapeHTML(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }