max_only_alert_analysis/visualization_ui/js/ui.js

100 lines
2.8 KiB
JavaScript

/**
* 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<Object>} 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 = `
<tr>
<th>corr_id</th>
<th>stratum</th>
<th>expires_at_dist_hours</th>
<th>retry_total_overhead_ms</th>
</tr>
`;
table.appendChild(thead);
const tbody = document.createElement('tbody');
for (const alert of alerts) {
const row = document.createElement('tr');
row.innerHTML = `
<td>${escapeHTML(alert.corr_id ?? '')}</td>
<td>${escapeHTML(alert.stratum ?? '')}</td>
<td>${escapeHTML(alert.expires_at_dist_hours ?? '')}</td>
<td>${escapeHTML(alert.retry_total_overhead_ms ?? '')}</td>
`;
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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}