Add max_outlier_visualization/js/ui.js
This commit is contained in:
parent
d44c0e0da0
commit
3412f5793c
1 changed files with 168 additions and 0 deletions
168
max_outlier_visualization/js/ui.js
Normal file
168
max_outlier_visualization/js/ui.js
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
"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;
|
||||
Loading…
Reference in a new issue