168 lines
No EOL
4 KiB
JavaScript
168 lines
No EOL
4 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* UI Logic for Max-Outlier Visualization
|
|
* Handles rendering of table, chart, and filter interactions.
|
|
* Relies on api.js for data fetching.
|
|
*/
|
|
|
|
import { fetchMaxOutlierResults } from './api.js';
|
|
|
|
const ui = (() => {
|
|
const tableContainer = document.getElementById('outlier-table');
|
|
const chartContainer = document.getElementById('outlier-chart');
|
|
const filters = document.querySelectorAll('.filter-input');
|
|
|
|
/**
|
|
* Render results table from API data.
|
|
* @param {Array<Object>} data
|
|
*/
|
|
const renderTable = (data) => {
|
|
if (!tableContainer) return;
|
|
const table = document.createElement('table');
|
|
table.className = 'outlier__table';
|
|
|
|
const header = document.createElement('thead');
|
|
header.innerHTML = `
|
|
<tr>
|
|
<th>Run ID</th>
|
|
<th>Parallelism</th>
|
|
<th>Stratum</th>
|
|
<th>Δt</th>
|
|
<th>p95</th>
|
|
<th>p99</th>
|
|
<th>Retry Status</th>
|
|
</tr>
|
|
`;
|
|
table.appendChild(header);
|
|
|
|
const body = document.createElement('tbody');
|
|
data.forEach((row) => {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${row.run_id ?? '-'}</td>
|
|
<td>${row.parallelism ?? '-'}</td>
|
|
<td>${row.stratum ?? '-'}</td>
|
|
<td>${row.delta_t ?? '-'}</td>
|
|
<td>${row.p95 ?? '-'}</td>
|
|
<td>${row.p99 ?? '-'}</td>
|
|
<td>${row.retry_status ?? '-'}</td>
|
|
`;
|
|
body.appendChild(tr);
|
|
});
|
|
table.appendChild(body);
|
|
|
|
tableContainer.innerHTML = '';
|
|
tableContainer.appendChild(table);
|
|
};
|
|
|
|
/**
|
|
* Render chart for p95/p99 visualization.
|
|
* @param {Array<Object>} data
|
|
*/
|
|
const renderChart = (data) => {
|
|
if (!chartContainer) return;
|
|
const ctx = chartContainer.getContext('2d');
|
|
if (!ctx) return;
|
|
|
|
const labels = data.map((d) => `${d.run_id}`);
|
|
const p95Values = data.map((d) => d.p95);
|
|
const p99Values = data.map((d) => d.p99);
|
|
|
|
if (chartContainer.chartInstance) {
|
|
chartContainer.chartInstance.destroy();
|
|
}
|
|
|
|
// Basic chart setup using Chart.js style interface if loaded
|
|
chartContainer.chartInstance = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels,
|
|
datasets: [
|
|
{
|
|
label: 'p95',
|
|
data: p95Values,
|
|
borderColor: 'var(--primary)',
|
|
backgroundColor: 'rgba(0, 128, 255, 0.2)',
|
|
fill: true,
|
|
tension: 0.2,
|
|
},
|
|
{
|
|
label: 'p99',
|
|
data: p99Values,
|
|
borderColor: 'var(--accent)',
|
|
backgroundColor: 'rgba(255, 64, 64, 0.2)',
|
|
fill: true,
|
|
tension: 0.2,
|
|
},
|
|
],
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
x: {
|
|
title: {
|
|
display: true,
|
|
text: 'Run ID'
|
|
}
|
|
},
|
|
y: {
|
|
title: {
|
|
display: true,
|
|
text: 'Metric Value'
|
|
}
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
position: 'top'
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Handle filter input change, fetch new data and re-render views.
|
|
* @param {Event} e
|
|
*/
|
|
const handleFilterChange = async (e) => {
|
|
e?.preventDefault();
|
|
const params = {};
|
|
filters.forEach((input) => {
|
|
if (input.value.trim()) {
|
|
params[input.name] = input.value.trim();
|
|
}
|
|
});
|
|
|
|
try {
|
|
const results = await fetchMaxOutlierResults(params);
|
|
renderTable(results);
|
|
renderChart(results);
|
|
} catch (err) {
|
|
console.error('Failed to update data:', err);
|
|
}
|
|
};
|
|
|
|
// Binding filter events on page load
|
|
const bindFilterEvents = () => {
|
|
filters.forEach((input) => {
|
|
input.addEventListener('change', handleFilterChange);
|
|
});
|
|
};
|
|
|
|
// Public API
|
|
return {
|
|
renderTable,
|
|
renderChart,
|
|
handleFilterChange,
|
|
bindFilterEvents
|
|
};
|
|
})();
|
|
|
|
export default ui; |