Add artifact.visualization/js/app.js
This commit is contained in:
parent
b5f8e4de24
commit
8519b508bc
1 changed files with 128 additions and 0 deletions
128
artifact.visualization/js/app.js
Normal file
128
artifact.visualization/js/app.js
Normal file
|
|
@ -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<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<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);
|
||||||
Loading…
Reference in a new issue