UI-Fixes (Button-Wrap, Suchfeld-Icon, Log-Farben, Nav-Persistenz), manueller Check-Trigger, echter Live-Refresh
- section-head: Button ("+ Neue Zugangsdaten" etc.) rutscht bei langer
Beschreibung nicht mehr in die nächste Zeile — bleibt immer rechts oben,
der Beschreibungstext wickelt stattdessen innerhalb seiner eigenen Spalte.
- Suchfeld-Icon überlappte den Platzhaltertext: eine spätere, generische
Input-Regel mit gleicher Spezifität hat die padding-left-Regel des
Lupe-Icons überschrieben (reine Reihenfolge-im-Stylesheet-Sache) — Selektor
spezifischer gemacht.
- Live-Log: Neustart-bezogene Zeilen (manueller Neustart, automatischer
PoE-Restart bei Ausfall) werden jetzt orange markiert statt farblos.
- Aufgeklappte Nav-Gruppen bleiben über Seitenwechsel hinweg erhalten
(localStorage), zusätzlich zur automatischen Aufklappung der Gruppe der
aktuell aktiven Seite.
- Datei-Auswahl (Import) im Dark Mode gestylt (Text + Button), inkl.
color-scheme-bewusster Browser-Widgets.
- Manueller "Jetzt prüfen"-Trigger (Icon neben dem globalen Timer, nur
Admins): startet rpi-check.service neu -> frisches Logfile + sofortiger
Durchlauf, Countdown synchronisiert sich auf den manuellen Zeitpunkt.
- Dashboard und Live-Log aktualisieren sich jetzt per AJAX im Hintergrund
(kein voller Seiten-Reload mehr): neue Route /dashboard/tiles liefert das
Kachel-Fragment (_dashboard_tiles.html, von index.html eingebunden und vom
Live-Update nachgeladen), get_log() liefert den aktuellen Dateinamen als
Header mit, damit die Anzeige nach einem Service-Neustart aktuell bleibt.
- Root-Cause für "Intervall bleibt immer bei 300s": in der WSL-Testumgebung
lief poe_wrapper.py nur als manuell gestarteter Hintergrundprozess statt
als echter rpi-check.service — "systemctl restart" lief dadurch ins Leere.
Echten systemd-Service in WSL eingerichtet und Intervalländerung end-to-end
verifiziert (Log-Zeilenabstand folgt jetzt dem neu gesetzten Intervall).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -206,11 +206,39 @@
|
||||
} catch (e) { /* noop */ }
|
||||
}
|
||||
|
||||
/* Nav-Gruppen mit Untermenü (z.B. "Logs") auf-/zuklappen */
|
||||
/* Nav-Gruppen mit Untermenü (z.B. "Logs") auf-/zuklappen — der manuell
|
||||
aufgeklappte Zustand bleibt über Seitenwechsel hinweg erhalten
|
||||
(localStorage), zusätzlich zur automatischen Aufklappung der Gruppe
|
||||
der aktuell aktiven Seite (server-seitig via "expanded"-Klasse). */
|
||||
const NAV_EXPANDED_KEY = "poe-nav-expanded";
|
||||
|
||||
function getExpandedNavGroups() {
|
||||
try {
|
||||
return new Set(JSON.parse(localStorage.getItem(NAV_EXPANDED_KEY) || "[]"));
|
||||
} catch (e) {
|
||||
return new Set();
|
||||
}
|
||||
}
|
||||
|
||||
function saveExpandedNavGroups(set) {
|
||||
localStorage.setItem(NAV_EXPANDED_KEY, JSON.stringify(Array.from(set)));
|
||||
}
|
||||
|
||||
function initNavGroups() {
|
||||
const expanded = getExpandedNavGroups();
|
||||
document.querySelectorAll("[data-nav-group]").forEach((group) => {
|
||||
const key = group.dataset.navGroupKey;
|
||||
if (key && expanded.has(key)) group.classList.add("expanded");
|
||||
});
|
||||
document.querySelectorAll("[data-nav-group-toggle]").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
btn.closest("[data-nav-group]").classList.toggle("expanded");
|
||||
const group = btn.closest("[data-nav-group]");
|
||||
const isExpanded = group.classList.toggle("expanded");
|
||||
const key = group.dataset.navGroupKey;
|
||||
if (!key) return;
|
||||
const set = getExpandedNavGroups();
|
||||
if (isExpanded) set.add(key); else set.delete(key);
|
||||
saveExpandedNavGroups(set);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -221,11 +249,16 @@
|
||||
const pill = document.getElementById("global-timer-pill");
|
||||
const timerEl = document.getElementById("global-timer");
|
||||
if (!pill || !timerEl) return;
|
||||
const lastRunMs = parseInt(pill.dataset.lastRunMs, 10);
|
||||
const intervalMs = parseInt(pill.dataset.intervalMs, 10);
|
||||
if (!intervalMs) { timerEl.textContent = "--"; return; }
|
||||
|
||||
// lastRunMs/intervalMs werden bei JEDEM Tick frisch aus dem DOM gelesen
|
||||
// (nicht einmalig gecacht) — so kann z.B. das Dashboard nach einem
|
||||
// Live-Update (siehe dashboard-tiles.js) oder ein manueller "Jetzt
|
||||
// prüfen"-Klick das data-last-run-ms-Attribut aktualisieren und der
|
||||
// global laufende Countdown übernimmt das automatisch.
|
||||
function update() {
|
||||
const lastRunMs = parseInt(pill.dataset.lastRunMs, 10);
|
||||
const intervalMs = parseInt(pill.dataset.intervalMs, 10);
|
||||
if (!intervalMs) { timerEl.textContent = "--"; return; }
|
||||
let remainingMs = intervalMs;
|
||||
if (!isNaN(lastRunMs) && lastRunMs > 0) {
|
||||
const elapsed = Date.now() - lastRunMs;
|
||||
@@ -237,6 +270,42 @@
|
||||
setInterval(update, 1000);
|
||||
}
|
||||
|
||||
/* "Jetzt prüfen"-Button neben dem globalen Timer (nur Admins): startet
|
||||
rpi-check.service neu (frisches Logfile + sofortiger Durchlauf) und
|
||||
meldet das per Custom Event, damit Dashboard/Live-Log sich selbst
|
||||
aktualisieren können, ohne dass app.js die Seiteninhalte kennen muss. */
|
||||
function initRunCheckNow() {
|
||||
const btn = document.getElementById("run-check-now");
|
||||
if (!btn) return;
|
||||
btn.addEventListener("click", () => {
|
||||
btn.disabled = true;
|
||||
btn.classList.add("spinning");
|
||||
fetch("/check/run-now", { method: "POST" })
|
||||
.then((r) => r.json())
|
||||
.then((data) => {
|
||||
if (!data.success) {
|
||||
window.showToast(data.msg || "Fehler beim Starten der Prüfung.", "danger");
|
||||
btn.disabled = false;
|
||||
btn.classList.remove("spinning");
|
||||
return;
|
||||
}
|
||||
window.showToast(data.msg || "Prüfung gestartet.", "success");
|
||||
// Der Service braucht einen Moment, um neu zu starten und den
|
||||
// ersten Durchlauf ins frische Logfile zu schreiben.
|
||||
setTimeout(() => {
|
||||
document.dispatchEvent(new CustomEvent("poe:check-triggered"));
|
||||
btn.disabled = false;
|
||||
btn.classList.remove("spinning");
|
||||
}, 1500);
|
||||
})
|
||||
.catch(() => {
|
||||
window.showToast("Fehler beim Starten der Prüfung.", "danger");
|
||||
btn.disabled = false;
|
||||
btn.classList.remove("spinning");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------------- Sortierbare Tabellen ---------------- */
|
||||
/*
|
||||
* Klick auf <th data-sort-key="..."> sortiert die Tabelle. Zwei Modi:
|
||||
@@ -300,6 +369,7 @@
|
||||
initFlashedMessages();
|
||||
initNavGroups();
|
||||
initCheckTimer();
|
||||
initRunCheckNow();
|
||||
initSortableTables();
|
||||
document.querySelectorAll("[data-theme-toggle]").forEach((btn) => btn.addEventListener("click", toggleTheme));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user