Checklisten-Häkchen in Dashboards speichern (localStorage)

Beim Ausliefern von /dashboards/*.html wird ein Skript injiziert, das den
Zustand der Checklisten-Checkboxen im localStorage speichert (Key =
Label-Text, pro Dashboard-URL) und beim Laden wiederherstellt, inkl.
update()-Aufruf für Zähler/Progress-Bar und 'Liste zurücksetzen'-Button.
Funktioniert in allen bestehenden und künftigen Dashboards; Marker
verhindert Doppel-Injektion, path.basename verhindert Traversal.
This commit is contained in:
nico committed 2026-08-28 12:28:58 +02:00
1 parent ab4f8e8570
commit 12f01b86d6
1 file changed
+70
+70
View File
@@ -1,6 +1,7 @@
const path = require('path');
const http = require('http');
const crypto = require('crypto');
const fs = require('fs');
const express = require('express');
const webpush = require('web-push');
@@ -416,6 +417,75 @@ function sendPush(session, result) {
// Statische Dateien & Dashboard-Dateien
// ---------------------------------------------------------------------------
// Wird in jedes ausgelieferte Dashboard-HTML injiziert: speichert den Zustand
// der Checklisten-Häkchen im localStorage und stellt ihn beim Laden wieder her
// (inkl. „Liste zurücksetzen"-Button). Key = Label-Text der Checkbox, damit
// der Stand auch bei Layout-Änderungen stabil bleibt.
const CHECKLIST_SCRIPT = `<script>
(function () {
var KEY = 'rc-checks:' + location.pathname;
var boxes = document.querySelectorAll('input[type=checkbox]');
if (!boxes.length) return;
var saved = {};
try { saved = JSON.parse(localStorage.getItem(KEY) || '{}'); } catch (e) {}
function labelFor(box, i) {
var li = box.closest('li');
var t = li ? li.querySelector('.check-text') : null;
var text = (t || li || {}).textContent || '';
return text.trim() || 'item-' + i;
}
boxes.forEach(function (box, i) {
var k = labelFor(box, i);
if (saved[k] !== undefined) box.checked = !!saved[k];
box.addEventListener('change', function () {
saved[k] = box.checked;
try { localStorage.setItem(KEY, JSON.stringify(saved)); } catch (e) {}
});
});
function refresh() {
if (typeof update === 'function') update();
}
// „Liste zurücksetzen"-Button am Anfang der ersten Checklisten-Section
var firstBox = boxes[0];
var section = firstBox.closest('section') || firstBox.closest('div') || document.body;
var btn = document.createElement('button');
btn.type = 'button';
btn.textContent = '↺ Liste zurücksetzen';
btn.style.cssText =
'margin:0 0 12px;padding:6px 14px;font-size:12px;font-weight:600;border-radius:8px;' +
'border:1px solid rgba(100,116,139,.4);background:transparent;color:inherit;cursor:pointer;';
btn.addEventListener('click', function () {
if (!confirm('Alle Häkchen dieser Liste zurücksetzen?')) return;
try { localStorage.removeItem(KEY); } catch (e) {}
saved = {};
boxes.forEach(function (b) { b.checked = false; });
refresh();
});
section.insertBefore(btn, section.firstChild);
refresh();
})();
</script>`;
const CHECKLIST_MARKER = 'rc-checks';
app.get('/dashboards/:name.html', (req, res, next) => {
const name = path.basename(req.params.name);
const file = path.join(__dirname, 'dashboards', `${name}.html`);
fs.readFile(file, 'utf8', (err, html) => {
if (err) return next();
if (!html.includes(CHECKLIST_MARKER)) {
html = html.replace(/<\/body>/i, `${CHECKLIST_SCRIPT}\n</body>`);
}
res.setHeader('Cache-Control', 'no-cache');
res.type('html').send(html);
});
});
app.use(
'/dashboards',
express.static(path.join(__dirname, 'dashboards'), {