From 4c137771e09f1b9c350db20b5413d935482bbb1f Mon Sep 17 00:00:00 2001 From: Mika Date: Mon, 9 Mar 2026 13:44:28 +0000 Subject: [PATCH] Add artifact.visualization/js/ui.js --- artifact.visualization/js/ui.js | 103 ++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 artifact.visualization/js/ui.js diff --git a/artifact.visualization/js/ui.js b/artifact.visualization/js/ui.js new file mode 100644 index 0000000..0f45248 --- /dev/null +++ b/artifact.visualization/js/ui.js @@ -0,0 +1,103 @@ +'use strict'; + +/** + * @module ui + * Funktionen zur Aktualisierung der visuellen Komponenten (Karten und Diagramme) + */ + +/** + * Aktualisiert die Kennzahlenkarten im DOM basierend auf API-Daten. + * @param {Object} retry_analysis_report - JSON Report aus der API /api/retry-stats + */ +export function renderSummaryCards(retry_analysis_report) { + const container = document.getElementById('stats-summary'); + if (!container) return; + + container.innerHTML = ''; + + const metrics = [ + { label: '\u0394t<0', value: retry_analysis_report?.delta_t_neg_rate ?? 'N/A', unit: '%' }, + { label: 'Warn Rate', value: retry_analysis_report?.warn_rate ?? 'N/A', unit: '%' }, + { label: 'Unknown Rate', value: retry_analysis_report?.unknown_rate ?? 'N/A', unit: '%' }, + { label: 'Heilungsrate', value: retry_analysis_report?.healing_rate ?? 'N/A', unit: '%' }, + { label: 'p95', value: retry_analysis_report?.p95 ?? 'N/A', unit: 'ms' }, + { label: 'p99', value: retry_analysis_report?.p99 ?? 'N/A', unit: 'ms' } + ]; + + const frag = document.createDocumentFragment(); + + metrics.forEach(metric => { + const card = document.createElement('div'); + card.className = 'summary-card'; + + const title = document.createElement('h3'); + title.textContent = metric.label; + + const value = document.createElement('p'); + value.className = 'summary-card__value'; + value.textContent = `${metric.value} ${metric.unit}`; + + card.appendChild(title); + card.appendChild(value); + frag.appendChild(card); + }); + + container.appendChild(frag); +} + +/** + * Erzeugt oder aktualisiert ein Diagramm in #retry-chart basierend auf API-Daten. + * @param {Object} retry_analysis_report - JSON Report aus der API /api/retry-stats + */ +export function renderRetryChart(retry_analysis_report) { + const chartContainer = document.getElementById('retry-chart'); + if (!chartContainer) return; + + chartContainer.innerHTML = ''; + + const canvas = document.createElement('canvas'); + canvas.id = 'retryChartCanvas'; + canvas.setAttribute('aria-label', 'Retry Statistik Diagramm'); + chartContainer.appendChild(canvas); + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Minimalistische Chart-Implementation ohne externe Libraries + const { stats = [] } = retry_analysis_report || {}; + if (stats.length === 0) { + ctx.fillStyle = '#666'; + ctx.font = '16px sans-serif'; + ctx.fillText('Keine Daten verfügbar', 10, 30); + return; + } + + const width = canvas.width = chartContainer.clientWidth || 400; + const height = canvas.height = 200; + const margin = 20; + const colorP95 = '#2c7'; + const colorP99 = '#f93'; + + const maxValue = Math.max(...stats.map(s => Math.max(s.p95 ?? 0, s.p99 ?? 0))); + + const barWidth = Math.max(5, (width - margin * 2) / stats.length); + + stats.forEach((s, index) => { + const x = margin + index * barWidth; + const p95Height = ((s.p95 ?? 0) / maxValue) * (height - margin * 2); + const p99Height = ((s.p99 ?? 0) / maxValue) * (height - margin * 2); + + // p95 bar + ctx.fillStyle = colorP95; + ctx.fillRect(x, height - p95Height - margin, barWidth * 0.4, p95Height); + // p99 bar + ctx.fillStyle = colorP99; + ctx.fillRect(x + barWidth * 0.5, height - p99Height - margin, barWidth * 0.4, p99Height); + }); + + // Axis Labels + ctx.fillStyle = '#000'; + ctx.font = '12px sans-serif'; + ctx.fillText('p95', 10, 12); + ctx.fillText('p99', 50, 12); +}