ux(quellen-health): Default "Nur Probleme", Counter feiner gegliedert, Filter-Hint bei Pagination

Schritt 1 der Quellen-Health-Aufraeumung. Drei UX-Verbesserungen, kein Daten-Eingriff:

1. Default-Filter "Nur Probleme" (errors + warnings, ohne OK).
   - Neuer Status-Filter-Wert "issues" als virtuelles Frontend-Konstrukt.
   - applyHealthFilter behandelt "issues" als status != ok.
   - Default in healthFilters ist jetzt "issues". User sieht beim
     Tab-Klick sofort die kritischen 146 Eintraege statt der 281
     gruenen OK-Zeilen.

2. Counter aufgegliedert nach check_type.
   - Backend (/api/sources/health): zusaetzliches Feld "breakdown"
     mit der GROUP-BY (check_type, status) Aggregation.
   - Frontend rendert pro Status-Zeile die feine Aufschluesselung,
     z.B. "143 Warnungen (112 Aktualität, 27 Feed-Validität, 3 Duplikat,
     1 Erreichbarkeit)".
   - Hilft dem Admin, sofort zu sehen wo das Problem liegt.

3. Filter-Hint bei Pagination + leeren Treffern.
   - Wenn der aktuelle Filter ueber die geladenen 100 Items keinen
     Treffer findet UND has_more=true, zeigt das Frontend einen
     Hinweis-Link "Alle X Health-Checks laden und Filter erneut
     anwenden".
   - Loest das Edge-Problem, dass z.B. Filter "Nur OK" auf den
     Default-100 (errors first) leer schien.

Cache-Buster fuer source-health.js auf 20260509g gebumpt.
Dieser Commit ist enthalten in:
claude-dev
2026-05-09 13:24:44 +00:00
Ursprung 50749323f8
Commit e8bb2495ee
3 geänderte Dateien mit 53 neuen und 11 gelöschten Zeilen

Datei anzeigen

@@ -594,14 +594,25 @@ async def get_health(
"limit": limit, "offset": offset, "has_more": False,
}
# Aggregate über GESAMTEN Bestand (eine GROUP-BY-Query, billig)
# Aggregate über GESAMTEN Bestand. Eine GROUP-BY-Query nach (check_type, status)
# liefert sowohl die Top-Counters als auch das feine Breakdown für die UI.
cursor = await db.execute(
"SELECT status, COUNT(*) AS n FROM source_health_checks GROUP BY status"
"SELECT check_type, status, COUNT(*) AS n FROM source_health_checks GROUP BY check_type, status"
)
counts = {row["status"]: row["n"] for row in await cursor.fetchall()}
error_count = counts.get("error", 0)
warning_count = counts.get("warning", 0)
ok_count = counts.get("ok", 0)
breakdown = {} # {check_type: {status: count}}
error_count = 0
warning_count = 0
ok_count = 0
for row in await cursor.fetchall():
ct = row["check_type"]
st = row["status"]
breakdown.setdefault(ct, {})[st] = row["n"]
if st == "error":
error_count += row["n"]
elif st == "warning":
warning_count += row["n"]
elif st == "ok":
ok_count += row["n"]
total_checks = error_count + warning_count + ok_count
# Paginierte Daten
@@ -641,6 +652,7 @@ async def get_health(
"errors": error_count,
"warnings": warning_count,
"ok": ok_count,
"breakdown": breakdown,
"checks": checks,
"all_orgs": all_orgs,
"limit": limit,