Ergänzung Datenschutzerklärung und Impressum, Fristenformular in Einstellungen

This commit is contained in:
2026-08-10 06:44:11 +02:00
parent 8ffbda875e
commit 777e3e5447
25 changed files with 1310 additions and 139 deletions

View File

@@ -296,57 +296,66 @@ export async function adminView(root, { back }) {
// Automatik
// -------------------------------------------------------------------
function automationCard() {
const deactivate = el("input.months", {
type: "number", min: "0", max: "600",
value: String(config.auto_deactivate_months),
});
const remove = el("input.months", {
type: "number", min: "0", max: "600",
value: String(config.auto_delete_months),
});
const archive = el("input.months", {
type: "number", min: "0", max: "600",
value: String(config.auto_archive_months),
});
const archiveDelete = el("input.months", {
type: "number", min: "0", max: "600",
value: String(config.archive_delete_months),
});
/** Fristen aus dem Server: Beschriftung, Einheit und Grenzen kommen
* von dort, damit nicht das eine steht und das andere gilt. */
function durationsCard() {
const fields = new Map();
const group = (title, keys, lead) => {
const rows = config.durations.filter((d) => keys.includes(d.key));
if (!rows.length) return null;
return el("div.duration-group", {},
el("h3.device-heading", {}, title),
lead ? el("p.sub", {}, lead) : null,
rows.map((entry) => {
const input = el("input.months", {
type: "number",
min: String(entry.minimum),
max: String(entry.maximum),
value: String(entry.value),
title: `${entry.minimum} bis ${entry.maximum} ${entry.unit}`,
});
fields.set(entry.key, input);
return el("div.duration-row", {},
el("div.duration-text", {},
el("span.duration-label", {}, `${entry.label}${entry.unit}`),
el("span.duration-note", {},
entry.note,
entry.zero_means ? ` · 0 = ${entry.zero_means}` : "",
entry.value !== entry.default
? ` · Vorgabe ${entry.default}` : "")),
input);
}));
};
return el("section.card", {},
el("h2", {}, "Automatische Bereinigung"),
el("h2", {}, "Fristen"),
el("p.lead", {},
"Läuft einmal täglich. ",
el("strong", {}, "0 bedeutet abgeschaltet"),
", nicht „sofort“. Administratorkonten sind ausgenommen."),
"Diese Werte gelten zur Laufzeit und werden auch für die " +
"Datenschutzerklärung gebraucht. Der Aufräumlauf setzt sie " +
"einmal täglich um."),
el("label", {}, "Deaktivieren nach … Monaten ohne Anmeldung"),
deactivate,
el("label", {}, "Löschen nach … Monaten Deaktivierung"),
remove,
el("h3.device-heading", {}, "Listen"),
el("label", {}, "Archivieren nach … Monaten ohne Öffnen"),
archive,
el("p.sub", {},
"Gilt je Person: Die Liste verschwindet nur aus der Übersicht " +
"derjenigen, die sie nicht geöffnet hat."),
el("label", {}, "Löschen nach … Monaten im Archiv"),
archiveDelete,
el("p.sub", {},
"Maßgeblich ist allein das Archivdatum des Eigentümers. Er wird " +
"7 Tage und 1 Tag vorher per E-Mail gewarnt."),
group("Konten",
["auto_deactivate_months", "auto_delete_months"]),
group("Listen",
["auto_archive_months", "archive_delete_months"]),
group("Aufbewahrung",
["cleanup_deleted_days", "cleanup_ops_days", "product_cache_days"]),
group("Sitzungen und Links",
["session_days", "invite_days", "welcome_days", "verify_hours",
"reset_hours", "email_change_hours"]),
el("button.primary", {
type: "button",
onclick: () => guarded(async () => {
await put("/api/admin/settings", {
auto_deactivate_months: Number(deactivate.value) || 0,
auto_delete_months: Number(remove.value) || 0,
auto_archive_months: Number(archive.value) || 0,
archive_delete_months: Number(archiveDelete.value) || 0,
});
say("Einstellungen gespeichert.");
const durations = {};
for (const [key, input] of fields) {
durations[key] = Number(input.value) || 0;
}
await put("/api/admin/settings", { durations });
say("Fristen gespeichert.");
}),
}, "Speichern"),
@@ -354,7 +363,19 @@ export async function adminView(root, { back }) {
el("button", {
type: "button",
onclick: () => guarded(() => post("/api/admin/cleanup")),
}, "Jetzt aufräumen"))
}, "Jetzt aufräumen")),
// Was hier nicht einstellbar ist, soll trotzdem sichtbar sein -
// sonst wirkt die Übersicht vollständig, ohne es zu sein.
Object.keys(config.fixed || {}).length
? el("div", {},
el("h3.device-heading", {}, "Fest eingestellt"),
el("ul.fixed-list", {},
Object.entries(config.fixed).map(([label, value]) =>
el("li", {},
el("span.name", {}, label),
el("span.sub", {}, value)))))
: null
);
}
@@ -375,7 +396,7 @@ export async function adminView(root, { back }) {
registrationToggle(),
el("ul.admin-users", {}, users.map(userRow))),
createCard(),
automationCard()
durationsCard()
);
}