run_17_gates_default/artifact.visualization/js/app.js

128 lines
No EOL
4 KiB
JavaScript

/**
* @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<Object>} 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<void>}
*/
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 = '<p role="alert">Fehler beim Laden der Daten.</p>';
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 = `
<section class="metrics">
<h2>Performance-Kennzahlen</h2>
<ul>
<li><strong>P95:</strong> ${p95 ?? '-'} ms</li>
<li><strong>P99:</strong> ${p99 ?? '-'} ms</li>
</ul>
</section>
<section class="rates">
<h2>Raten</h2>
<ul>
<li><strong>Unknown Rate:</strong> ${(unknown_rate * 100).toFixed(2)}%</li>
<li><strong>Heilungsrate:</strong> ${(healing_rate * 100).toFixed(2)}%</li>
</ul>
</section>
<section class="details">
<h2>Statistikdetails</h2>
<div role="table" class="stats-table">
<div role="row" class="stats-header">
<div role="columnheader">Δt&lt;0</div>
<div role="columnheader">Warnrate</div>
<div role="columnheader">Status</div>
</div>
${Array.isArray(stats) && stats.length > 0
? stats.map(s => `
<div role="row" class="stats-row">
<div role="cell">${s.delta_negative}</div>
<div role="cell">${(s.warn_rate * 100).toFixed(2)}%</div>
<div role="cell">${s.status ?? 'n/a'}</div>
</div>`).join('')
: '<div role="row"><div role="cell" colspan="3">Keine Daten</div></div>'}
</div>
</section>
`;
};
/**
* Initialisiert die Anwendung: Datenabruf und Event-Bindings.
* @returns {Promise<void>}
*/
const initApp = async () => {
await refreshData();
bindUIEvents();
};
window.addEventListener('load', initApp);