From 8519b508bcf7d30fca1ef902c5fa9740392eace4 Mon Sep 17 00:00:00 2001 From: Mika Date: Mon, 9 Mar 2026 13:44:27 +0000 Subject: [PATCH] Add artifact.visualization/js/app.js --- artifact.visualization/js/app.js | 128 +++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 artifact.visualization/js/app.js diff --git a/artifact.visualization/js/app.js b/artifact.visualization/js/app.js new file mode 100644 index 0000000..b587a02 --- /dev/null +++ b/artifact.visualization/js/app.js @@ -0,0 +1,128 @@ +/** + * @file js/app.js + * @description Entry logic: Initialisiert Anwendung, lädt Daten und orchestriert UI-Aktualisierungen. + */ + +/** + * Lädt Retry-Statistiken anhand optionaler Filterparameter. + * @param {Object} [filters={}] - Optionale Filter (z.B. {stratum, policy_hash}). + * @returns {Promise} Statistikdaten aus der API. + */ +const fetchRetryStats = async (filters = {}) => { + const params = new URLSearchParams(filters); + const url = params.toString() ? `/api/retry-stats?${params}` : '/api/retry-stats'; + try { + const response = await fetch(url); + if (!response.ok) throw new Error(`API Error: ${response.status}`); + return await response.json(); + } catch (err) { + console.error('Fehler beim Abrufen der Retry-Statistiken:', err); + return null; + } +}; + +/** + * Bindet Event-Listener an UI-Elemente für interaktive Filterung. + * @returns {void} + */ +const bindUIEvents = () => { + const stratumSelect = document.getElementById('filter-stratum'); + const policySelect = document.getElementById('filter-policy'); + + if (stratumSelect) { + stratumSelect.addEventListener('change', () => { + refreshData({ stratum: stratumSelect.value, policy_hash: policySelect?.value }); + }); + } + + if (policySelect) { + policySelect.addEventListener('change', () => { + refreshData({ stratum: stratumSelect?.value, policy_hash: policySelect.value }); + }); + } + + const refreshButton = document.getElementById('btn-refresh'); + if (refreshButton) { + refreshButton.addEventListener('click', () => { + refreshData({ stratum: stratumSelect?.value, policy_hash: policySelect?.value }); + }); + } +}; + +/** + * Aktualisiert Dashboard-Daten durch Abrufen und Rendern neuer Statistiken. + * @param {Object} [filters={}] - Aktuelle Filterparameter. + * @returns {Promise} + */ +const refreshData = async (filters = {}) => { + const container = document.getElementById('dashboard'); + if (!container) return; + + container.setAttribute('aria-busy', 'true'); + + const data = await fetchRetryStats(filters); + if (!data) { + container.innerHTML = '

Fehler beim Laden der Daten.

'; + container.removeAttribute('aria-busy'); + return; + } + + renderDashboard(data); + container.removeAttribute('aria-busy'); +}; + +/** + * Rendert die geladenen Daten ins Dashboard. + * @param {Object} data - Datenobjekt aus API. + */ +const renderDashboard = (data) => { + const { p95, p99, unknown_rate, healing_rate, stats } = data; + const statsContainer = document.getElementById('dashboard'); + if (!statsContainer) return; + + statsContainer.innerHTML = ` +
+

Performance-Kennzahlen

+
    +
  • P95: ${p95 ?? '-'} ms
  • +
  • P99: ${p99 ?? '-'} ms
  • +
+
+
+

Raten

+
    +
  • Unknown Rate: ${(unknown_rate * 100).toFixed(2)}%
  • +
  • Heilungsrate: ${(healing_rate * 100).toFixed(2)}%
  • +
+
+
+

Statistikdetails

+
+
+
Δt<0
+
Warnrate
+
Status
+
+ ${Array.isArray(stats) && stats.length > 0 + ? stats.map(s => ` +
+
${s.delta_negative}
+
${(s.warn_rate * 100).toFixed(2)}%
+
${s.status ?? 'n/a'}
+
`).join('') + : '
Keine Daten
'} +
+
+ `; +}; + +/** + * Initialisiert die Anwendung: Datenabruf und Event-Bindings. + * @returns {Promise} + */ +const initApp = async () => { + await refreshData(); + bindUIEvents(); +}; + +window.addEventListener('load', initApp); \ No newline at end of file