Add metrics_visualization/js/ui.js

This commit is contained in:
Mika 2026-02-26 12:52:56 +00:00
parent 8896e957bc
commit 1bbe7a7086

View file

@ -0,0 +1,124 @@
'use strict';
/**
* @module ui
* Verantwortlich für Rendering und Interaktion der UI-Komponenten.
*/
/**
* Rendert oder aktualisiert das Diagramm basierend auf Metrikdaten.
* @param {Array<Object>} metricsData - Metrikdaten aus der API.
* @param {HTMLElement} chartContainer - Zielcontainer (z. B. Canvas- oder SVG-Element).
*/
export function renderChart(metricsData, chartContainer) {
if (!chartContainer) return;
chartContainer.innerHTML = '';
if (!Array.isArray(metricsData) || metricsData.length === 0) {
const msg = document.createElement('p');
msg.textContent = 'Keine Metrikdaten verfügbar.';
chartContainer.appendChild(msg);
return;
}
// Beispiel: einfache Balkenvisualisierung auf Basis von warn_rate
const maxWarnRate = Math.max(...metricsData.map(m => m.warn_rate ?? 0));
const chart = document.createElement('div');
chart.classList.add('metrics-visualization__bars');
metricsData.forEach(run => {
const bar = document.createElement('div');
bar.classList.add('metrics-visualization__bar');
const height = maxWarnRate > 0 ? (run.warn_rate / maxWarnRate) * 100 : 0;
bar.style.setProperty('--bar-height', `${height}%`);
bar.setAttribute('title', `Run ${run.run_id}: warn_rate ${run.warn_rate}`);
const label = document.createElement('span');
label.classList.add('metrics-visualization__bar-label');
label.textContent = run.run_id;
bar.appendChild(label);
chart.appendChild(bar);
});
chartContainer.appendChild(chart);
}
/**
* Füllt die Tabelle mit Metrikdaten und Run-Vergleichen.
* @param {Array<Object>} metricsData - Metrikdaten aus der API.
* @param {HTMLTableSectionElement} tableBody - tbody-Element zur Befüllung.
*/
export function renderTable(metricsData, tableBody) {
if (!tableBody) return;
tableBody.innerHTML = '';
if (!Array.isArray(metricsData) || metricsData.length === 0) {
const row = document.createElement('tr');
const cell = document.createElement('td');
cell.colSpan = 6;
cell.textContent = 'Keine Daten vorhanden';
row.appendChild(cell);
tableBody.appendChild(row);
return;
}
metricsData.forEach(run => {
const row = document.createElement('tr');
const idCell = document.createElement('td');
idCell.textContent = run.run_id;
const setupCell = document.createElement('td');
setupCell.textContent = run.setup_fingerprint;
const warnCell = document.createElement('td');
warnCell.textContent = run.warn_rate?.toFixed(3) ?? '-';
const unknownCell = document.createElement('td');
unknownCell.textContent = run.unknown_rate?.toFixed(3) ?? '-';
const deltaCell = document.createElement('td');
deltaCell.textContent = run.delta_t_stats ?? '-';
const timeCell = document.createElement('td');
const date = new Date(run.timestamp);
timeCell.textContent = isNaN(date.getTime()) ? '-' : date.toLocaleString();
row.append(idCell, setupCell, warnCell, unknownCell, deltaCell, timeCell);
tableBody.appendChild(row);
});
}
/**
* Filtert Metrikdaten anhand der vom Nutzer gewählten Kriterien.
* @param {Array<Object>} allMetrics - Alle verfügbaren Metrikdaten.
* @param {Object} filters - Vom Nutzer gewählte Filterkriterien.
* @param {('all'|'pinned'|'unpinned')} [filters.type='all'] - Filtern nach pinned/unpinned.
* @param {string|null} [filters.setupFingerprint=null] - Optionaler Setup-Fingerprint zum Vergleich.
* @returns {Array<Object>} Gefilterte Metrikdaten.
*/
export function applyFilters(allMetrics, filters = {}) {
const { type = 'all', setupFingerprint = null } = filters;
let result = Array.isArray(allMetrics) ? [...allMetrics] : [];
if (setupFingerprint) {
result = result.filter(d => d.setup_fingerprint === setupFingerprint);
}
if (type === 'pinned') {
result = result.filter(d => d.pinned === true);
} else if (type === 'unpinned') {
result = result.filter(d => d.pinned === false);
}
return result;
}
/**
* Initialisiert UI-Events für Filterung.
* @param {Function} onFilterChange - Callback, z.B. fetchMetrics(Filter).
*/
export function initFilterListeners(onFilterChange) {
const filterForm = document.querySelector('#filter-panel');
if (!filterForm) return;
filterForm.addEventListener('change', () => {
const type = filterForm.querySelector('[name="runFilter"]:checked')?.value ?? 'all';
const setupFingerprint = filterForm.querySelector('[name="setupFingerprint"]')?.value || null;
onFilterChange({ type, setupFingerprint });
});
}
// Bar-Darstellung Style-Vorgabe per CSS Variable (wird über CSS Datei gehandhabt).