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