145 lines
No EOL
3.9 KiB
JavaScript
145 lines
No EOL
3.9 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* Visualization module for replication analysis dashboard.
|
|
* Responsible for rendering charts and updating tables dynamically.
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} RunComparison
|
|
* @property {string} run_id
|
|
* @property {Object} metrics
|
|
* @property {number[]} metrics.bandwidth
|
|
* @property {number[]} metrics.retry_tailp99
|
|
* @property {string[]} segments
|
|
*/
|
|
|
|
/**
|
|
* Render bandwidth chart based on provided run data.
|
|
* @param {RunComparison} data
|
|
*/
|
|
export function renderBandwidthChart(data) {
|
|
const container = document.getElementById('bandwidth-chart');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '';
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.setAttribute('aria-label', 'Bandwidth Chart');
|
|
canvas.setAttribute('role', 'img');
|
|
container.appendChild(canvas);
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
const bandwidth = data.metrics?.bandwidth ?? [];
|
|
if (!bandwidth.length) {
|
|
ctx.font = '16px sans-serif';
|
|
ctx.fillText('No bandwidth data available', 10, 30);
|
|
return;
|
|
}
|
|
|
|
const spacing = canvas.width / bandwidth.length || 5;
|
|
const maxValue = Math.max(...bandwidth);
|
|
const scale = (canvas.height - 20) / maxValue;
|
|
|
|
ctx.strokeStyle = '#3366cc';
|
|
ctx.beginPath();
|
|
bandwidth.forEach((val, i) => {
|
|
const x = i * spacing + 10;
|
|
const y = canvas.height - val * scale - 10;
|
|
if (i === 0) ctx.moveTo(x, y);
|
|
else ctx.lineTo(x, y);
|
|
});
|
|
ctx.stroke();
|
|
}
|
|
|
|
/**
|
|
* Render retry_tailp99 chart as a trend visualization.
|
|
* @param {RunComparison} data
|
|
*/
|
|
export function renderRetryTailChart(data) {
|
|
const container = document.getElementById('retry-tailp99-chart');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '';
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.setAttribute('aria-label', 'Retry Tailp99 Trend');
|
|
canvas.setAttribute('role', 'img');
|
|
container.appendChild(canvas);
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
const retries = data.metrics?.retry_tailp99 ?? [];
|
|
if (!retries.length) {
|
|
ctx.font = '16px sans-serif';
|
|
ctx.fillText('No retry_tailp99 data available', 10, 30);
|
|
return;
|
|
}
|
|
|
|
const spacing = canvas.width / retries.length || 5;
|
|
const maxValue = Math.max(...retries);
|
|
const scale = (canvas.height - 20) / maxValue;
|
|
|
|
ctx.strokeStyle = '#cc3333';
|
|
ctx.beginPath();
|
|
retries.forEach((val, i) => {
|
|
const x = i * spacing + 10;
|
|
const y = canvas.height - val * scale - 10;
|
|
if (i === 0) ctx.moveTo(x, y);
|
|
else ctx.lineTo(x, y);
|
|
});
|
|
ctx.stroke();
|
|
}
|
|
|
|
/**
|
|
* Update results table with latest run metrics.
|
|
* @param {RunComparison} data
|
|
*/
|
|
export function updateResultsTable(data) {
|
|
const table = document.getElementById('results-table');
|
|
if (!table) return;
|
|
|
|
table.innerHTML = '';
|
|
|
|
const headerRow = document.createElement('tr');
|
|
['Segment', 'Bandwidth', 'Retry Tail p99'].forEach(h => {
|
|
const th = document.createElement('th');
|
|
th.textContent = h;
|
|
headerRow.appendChild(th);
|
|
});
|
|
table.appendChild(headerRow);
|
|
|
|
const {segments = [], metrics: {bandwidth = [], retry_tailp99 = []} = {}} = data;
|
|
const len = Math.max(segments.length, bandwidth.length, retry_tailp99.length);
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
const row = document.createElement('tr');
|
|
const seg = document.createElement('td');
|
|
seg.textContent = segments[i] ?? '-';
|
|
|
|
const bw = document.createElement('td');
|
|
bw.textContent = bandwidth[i]?.toFixed(2) ?? '-';
|
|
|
|
const rt = document.createElement('td');
|
|
rt.textContent = retry_tailp99[i]?.toFixed(2) ?? '-';
|
|
|
|
row.appendChild(seg);
|
|
row.appendChild(bw);
|
|
row.appendChild(rt);
|
|
table.appendChild(row);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure responsiveness on resize.
|
|
*/
|
|
window.addEventListener('resize', () => {
|
|
const dataAttr = document.body.dataset.runComparison;
|
|
if (!dataAttr) return;
|
|
try {
|
|
const data = JSON.parse(dataAttr);
|
|
renderBandwidthChart(data);
|
|
renderRetryTailChart(data);
|
|
} catch (err) {
|
|
console.error('Failed to re-render charts on resize:', err);
|
|
}
|
|
}); |