diff --git a/visualization_ui/js/ui.js b/visualization_ui/js/ui.js new file mode 100644 index 0000000..d2f817d --- /dev/null +++ b/visualization_ui/js/ui.js @@ -0,0 +1,100 @@ +/** + * 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, '''); +}