diff --git a/metrics_visualization/js/ui.js b/metrics_visualization/js/ui.js new file mode 100644 index 0000000..7aadad6 --- /dev/null +++ b/metrics_visualization/js/ui.js @@ -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} 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} 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} 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} 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). \ No newline at end of file