Wunsch: "ich will auch immer bei jeder seite auch immer einen knopf
haben damit ich zurueck gehen kann auf die seite vorher."
Neues gemeinsames Modul assets/js/kopf.js. Blindes history.back() reicht
dafuer nicht: Wer die Adresse direkt eingibt oder gerade von der
Anmeldung kommt, landet damit ausserhalb des Workspace oder wieder im
Anmeldeformular. Deshalb wird zuerst geprueft, woher der Aufruf kam:
* vorherige Seite im Workspace -> "Zurueck", history.back()
(fuehrt wirklich dorthin zurueck, samt Bildlaufposition)
* kein Verlauf, aber Unterseite -> "Uebersicht", geht zur Startseite
* kein Verlauf auf der Startseite -> Knopf bleibt verborgen
Ein Knopf, der nichts tut, ist schlimmer als keiner.
Der Knopf sitzt links in der Kopfleiste, an derselben Stelle wie im
Browser.
Nebenbei aufgeraeumt: Das Abmelden stand bisher in jeder der fuenf
Seitendateien noch einmal -- fuenfmal derselbe Block, fuenfmal eine
Stelle zum Vergessen. Liegt jetzt ebenfalls in kopf.js.
Geprueft: Start nach Login -> verborgen; Aufgaben von Start ->
"Zurueck", fuehrt zurueck; Kalender direkt aufgerufen -> "Uebersicht".
Keine Konsolenfehler, Abmelden funktioniert weiterhin.
Nur statische Dateien, kein Neustart noetig.
198 lines
7.3 KiB
JavaScript
198 lines
7.3 KiB
JavaScript
/* ===================================================================
|
|
Personen und Zugangscodes verwalten. Nur für Management -- der Server
|
|
liefert allen anderen ohnehin 404 auf diese Schnittstellen.
|
|
=================================================================== */
|
|
(() => {
|
|
'use strict';
|
|
|
|
const $ = (id) => document.getElementById(id);
|
|
const ROLLENNAME = { admin: 'Management', creator: 'Creator', scout: 'Scout' };
|
|
let ich = null;
|
|
|
|
const el = (tag, klasse, text) => {
|
|
const k = document.createElement(tag);
|
|
if (klasse) k.className = klasse;
|
|
if (text !== undefined) k.textContent = text;
|
|
return k;
|
|
};
|
|
|
|
const melde = (t) => { $('fehler').textContent = t || ''; };
|
|
|
|
async function hole(pfad, optionen = {}) {
|
|
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
|
|
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
|
|
if (a.status === 404 && pfad.includes('/verwaltung/')) {
|
|
location.assign('/workspace/start.html'); throw new Error('nicht erlaubt');
|
|
}
|
|
return a;
|
|
}
|
|
|
|
const datum = (iso) => (iso ? iso.slice(0, 16).replace('T', ' ') : '—');
|
|
|
|
/* ---------- Code anzeigen -------------------------------------------- */
|
|
|
|
function zeigeCode(p) {
|
|
$('code-name').textContent = `${p.name} · ${ROLLENNAME[p.rolle] || p.rolle}`;
|
|
$('code-wert').textContent = p.code;
|
|
$('code-kasten').hidden = false;
|
|
$('code-kasten').scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
}
|
|
|
|
$('code-schliessen').addEventListener('click', () => {
|
|
$('code-kasten').hidden = true;
|
|
/* Den Code auch aus dem Dokument entfernen, damit er nicht in einer
|
|
Bildschirmaufnahme oder im Verlauf hängen bleibt. */
|
|
$('code-wert').textContent = '';
|
|
});
|
|
|
|
$('code-kopieren').addEventListener('click', async () => {
|
|
try {
|
|
await navigator.clipboard.writeText($('code-wert').textContent);
|
|
$('code-kopieren').textContent = 'Kopiert ✓';
|
|
setTimeout(() => { $('code-kopieren').textContent = 'Kopieren'; }, 1800);
|
|
} catch {
|
|
melde('Kopieren ging nicht — bitte von Hand markieren.');
|
|
}
|
|
});
|
|
|
|
/* ---------- Liste ----------------------------------------------------- */
|
|
|
|
function zeile(p) {
|
|
const k = el('div', 'person');
|
|
k.dataset.aktiv = p.aktiv ? 'ja' : 'nein';
|
|
|
|
const links = el('div');
|
|
links.append(el('div', 'person__name', p.name + (p.id === ich.id ? ' (du)' : '')));
|
|
|
|
const z = el('div', 'person__zeile');
|
|
const rolle = el('span', 'marke-rolle', ROLLENNAME[p.rolle] || p.rolle);
|
|
rolle.dataset.rolle = p.rolle;
|
|
z.append(rolle);
|
|
if (!p.aktiv) z.append(el('span', null, 'gesperrt'));
|
|
z.append(el('span', null, `letzte Anmeldung: ${datum(p.letzter_login)}`));
|
|
z.append(el('span', null, `offene Aufgaben: ${p.offene_aufgaben}`));
|
|
if (p.sitzungen) z.append(el('span', null, `${p.sitzungen} offene Sitzung(en)`));
|
|
links.append(z);
|
|
|
|
const knoepfe = el('div', 'person__knoepfe');
|
|
knoepfe.append(tuKnopf('Neuer Code', async () => {
|
|
const eigen = p.id === ich.id;
|
|
const frage = eigen
|
|
? `Neuer Code für dich selbst?\n\nDeine aktuelle Sitzung wird sofort beendet und du musst dich mit dem neuen Code neu anmelden.`
|
|
: `Neuer Code für ${p.name}?\n\nDer alte Code gilt dann nicht mehr, offene Sitzungen werden beendet.`;
|
|
if (!confirm(frage)) return;
|
|
const a = await hole(`/workspace/api/verwaltung/personen/${p.id}/code`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(eigen ? { auch_mich: true } : {}),
|
|
});
|
|
if (!a.ok) { melde('Neuer Code hat nicht geklappt.'); return; }
|
|
zeigeCode(await a.json());
|
|
await laden();
|
|
}));
|
|
|
|
if (p.id !== ich.id) {
|
|
knoepfe.append(tuKnopf(p.aktiv ? 'Sperren' : 'Entsperren', async () => {
|
|
if (p.aktiv && !confirm(`${p.name} sperren?\n\nOffene Sitzungen werden sofort beendet.`)) return;
|
|
const a = await hole(`/workspace/api/verwaltung/personen/${p.id}`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ aktiv: !p.aktiv }),
|
|
});
|
|
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
|
|
await laden();
|
|
}));
|
|
}
|
|
|
|
k.append(links, knoepfe);
|
|
return k;
|
|
}
|
|
|
|
function tuKnopf(text, tu) {
|
|
const b = el('button', 'schritt', text);
|
|
b.type = 'button';
|
|
b.addEventListener('click', async () => {
|
|
b.disabled = true; melde('');
|
|
try { await tu(); } catch { /* umgeleitet */ } finally { b.disabled = false; }
|
|
});
|
|
return b;
|
|
}
|
|
|
|
async function laden() {
|
|
const ziel = $('liste');
|
|
try {
|
|
const a = await hole('/workspace/api/verwaltung/personen');
|
|
if (!a.ok) { melde('Personen konnten nicht geladen werden.'); return; }
|
|
const { personen } = await a.json();
|
|
ziel.textContent = '';
|
|
ziel.setAttribute('aria-busy', 'false');
|
|
for (const p of personen) ziel.append(zeile(p));
|
|
await protokollLaden();
|
|
} catch { /* umgeleitet */ }
|
|
}
|
|
|
|
async function protokollLaden() {
|
|
try {
|
|
const a = await hole('/workspace/api/verwaltung/protokoll?anzahl=25');
|
|
if (!a.ok) return;
|
|
const { eintraege } = await a.json();
|
|
const ziel = $('protokoll');
|
|
ziel.textContent = '';
|
|
for (const e of eintraege) {
|
|
const z = el('div', 'protokoll-zeile');
|
|
z.append(el('span', 'protokoll-zeit', datum(e.zeitpunkt)));
|
|
z.append(el('span', null, e.aktion));
|
|
z.append(el('span', null, [e.wer, e.detail].filter(Boolean).join(' · ')));
|
|
z.append(el('span', 'protokoll-ip', e.ip || ''));
|
|
ziel.append(z);
|
|
}
|
|
} catch { /* umgeleitet */ }
|
|
}
|
|
|
|
/* ---------- Anlegen ---------------------------------------------------- */
|
|
|
|
const umschalten = (auf) => {
|
|
$('neu').hidden = !auf; $('neu-oeffnen').hidden = auf;
|
|
if (auf) $('f-name').focus();
|
|
};
|
|
$('neu-oeffnen').addEventListener('click', () => umschalten(true));
|
|
$('neu-abbrechen').addEventListener('click', () => {
|
|
$('neu').reset(); $('neu-fehler').textContent = ''; umschalten(false);
|
|
});
|
|
|
|
$('neu').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
$('neu-fehler').textContent = '';
|
|
const knopf = $('neu-speichern');
|
|
knopf.disabled = true;
|
|
try {
|
|
const a = await hole('/workspace/api/verwaltung/personen', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: $('f-name').value, rolle: $('f-rolle').value }),
|
|
});
|
|
if (!a.ok) {
|
|
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
|
|
return;
|
|
}
|
|
$('neu').reset(); umschalten(false);
|
|
zeigeCode(await a.json());
|
|
await laden();
|
|
} catch { /* umgeleitet */ } finally { knopf.disabled = false; }
|
|
});
|
|
|
|
/* ---------- Start ------------------------------------------------------- */
|
|
|
|
|
|
(async () => {
|
|
try {
|
|
const a = await hole('/workspace/api/ich');
|
|
if (!a.ok) { location.assign('/workspace/'); return; }
|
|
ich = await a.json();
|
|
} catch { return; }
|
|
if (ich.rolle !== 'admin') { location.assign('/workspace/start.html'); return; }
|
|
$('wer').textContent = `${ich.name} · ${ich.rolle}`;
|
|
await laden();
|
|
})();
|
|
})();
|