|
|
|
|
@@ -3,7 +3,9 @@
|
|
|
|
|
|
|
|
|
|
let healthData = null;
|
|
|
|
|
let suggestionsCache = [];
|
|
|
|
|
let healthFilters = { status: "", check_type: "", org: "all" };
|
|
|
|
|
// Default-Filter zeigt nur Probleme (errors + warnings); OK ist meistens Rauschen.
|
|
|
|
|
// "issues" ist ein virtueller Status-Wert, den nur das Frontend versteht (siehe applyHealthFilter).
|
|
|
|
|
let healthFilters = { status: "issues", check_type: "", org: "all" };
|
|
|
|
|
let healthHistoryCache = [];
|
|
|
|
|
|
|
|
|
|
// 60-Sekunden-Cache, damit Tab-Wechsel nicht jedes Mal die volle Antwort neu lädt.
|
|
|
|
|
@@ -91,7 +93,9 @@ function loadAllHealth() {
|
|
|
|
|
|
|
|
|
|
function applyHealthFilter(checks) {
|
|
|
|
|
return checks.filter(c => {
|
|
|
|
|
if (healthFilters.status && c.status !== healthFilters.status) return false;
|
|
|
|
|
// "issues" = Sammelfilter für errors + warnings (Default)
|
|
|
|
|
if (healthFilters.status === "issues" && c.status === "ok") return false;
|
|
|
|
|
if (healthFilters.status && healthFilters.status !== "issues" && c.status !== healthFilters.status) return false;
|
|
|
|
|
if (healthFilters.check_type && c.check_type !== healthFilters.check_type) return false;
|
|
|
|
|
if (healthFilters.org === "global" && c.tenant_id !== null) return false;
|
|
|
|
|
if (healthFilters.org !== "all" && healthFilters.org !== "global"
|
|
|
|
|
@@ -208,6 +212,21 @@ function renderHealthDashboard() {
|
|
|
|
|
|
|
|
|
|
const checkTypes = Array.from(new Set(allChecks.map(c => c.check_type)));
|
|
|
|
|
|
|
|
|
|
// Counter-Aufgliederung aus Backend-Breakdown (pro check_type x status).
|
|
|
|
|
// Beispiel: { reachability: {ok: 281, error: 3, warning: 1}, feed_validity: {...}, stale: {...}, duplicate: {...} }
|
|
|
|
|
const breakdown = healthData.breakdown || {};
|
|
|
|
|
function breakdownLine(statusKey, cssClass) {
|
|
|
|
|
const entries = Object.entries(breakdown)
|
|
|
|
|
.map(([ct, byStatus]) => [ct, byStatus[statusKey] || 0])
|
|
|
|
|
.filter(([_, n]) => n > 0)
|
|
|
|
|
.sort((a, b) => b[1] - a[1]);
|
|
|
|
|
if (entries.length === 0) return "";
|
|
|
|
|
const total = entries.reduce((s, [, n]) => s + n, 0);
|
|
|
|
|
const detail = entries.map(([ct, n]) => `${n} ${CHECK_TYPE_LABELS[ct] || ct}`).join(", ");
|
|
|
|
|
const label = statusKey === "error" ? "Fehler" : (statusKey === "warning" ? "Warnungen" : "OK");
|
|
|
|
|
return `<span class="${cssClass}" title="${esc(detail)}">${total} ${label}</span> <span class="text-secondary" style="font-size:11px;">(${esc(detail)})</span>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
healthHtml = `
|
|
|
|
|
<div class="card">
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
@@ -215,14 +234,15 @@ function renderHealthDashboard() {
|
|
|
|
|
<span class="text-secondary" style="font-size:13px;">
|
|
|
|
|
Letzter Check: ${healthData.last_check ? formatDateTime(healthData.last_check) : "Noch nie"}
|
|
|
|
|
|
|
|
|
|
|
<span class="text-danger">${healthData.errors} Fehler</span>
|
|
|
|
|
<span class="text-warning">${healthData.warnings} Warnungen</span>
|
|
|
|
|
${breakdownLine("error", "text-danger") || `<span class="text-danger">0 Fehler</span>`}
|
|
|
|
|
${breakdownLine("warning", "text-warning") || `<span class="text-warning">0 Warnungen</span>`}
|
|
|
|
|
<span class="text-success">${okCount} OK</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="action-bar" style="border-bottom:1px solid var(--border, rgba(255,255,255,0.08));">
|
|
|
|
|
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;">
|
|
|
|
|
<select class="filter-select" onchange="setHealthFilter('status', this.value)">
|
|
|
|
|
<option value="issues" ${healthFilters.status === "issues" ? "selected" : ""}>Nur Probleme (Default)</option>
|
|
|
|
|
<option value="" ${healthFilters.status === "" ? "selected" : ""}>Alle Status</option>
|
|
|
|
|
<option value="error" ${healthFilters.status === "error" ? "selected" : ""}>Nur Fehler</option>
|
|
|
|
|
<option value="warning" ${healthFilters.status === "warning" ? "selected" : ""}>Nur Warnungen</option>
|
|
|
|
|
@@ -269,6 +289,16 @@ function renderHealthDashboard() {
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>`;
|
|
|
|
|
} else if (hasMore) {
|
|
|
|
|
// 0 Treffer in der Page, aber es gibt noch ungeladene Items.
|
|
|
|
|
// Hinweis, dass der Filter erst über die volle Liste sicher ist.
|
|
|
|
|
healthHtml += `
|
|
|
|
|
<div class="card-body text-muted">
|
|
|
|
|
Keine Treffer in den geladenen ${allChecks.length} von ${totalAll} Items mit dem aktuellen Filter.
|
|
|
|
|
<a href="#" onclick="event.preventDefault(); loadAllHealth()" style="text-decoration:underline;">
|
|
|
|
|
Alle ${totalAll} Health-Checks laden
|
|
|
|
|
</a> und Filter erneut anwenden.
|
|
|
|
|
</div>`;
|
|
|
|
|
} else {
|
|
|
|
|
healthHtml += '<div class="card-body text-muted">Keine Ergebnisse mit diesen Filtern.</div>';
|
|
|
|
|
}
|
|
|
|
|
|