/workspace/profil.html -- Stammdaten, Ziele und 90-Tage-Plan, genau nach Seite 5 des Konzepts. Management waehlt oben den Creator aus, ein Creator sieht nur sein eigenes Profil. Sicherheitskern ist das Feld admin_notiz. Das Konzept fordert "private Admin-Notizen separat". Die Notiz wird deshalb nicht im Browser ausgeblendet, sondern gar nicht erst gesendet: Die Spaltenliste der Abfrage haengt an der Rolle (FELDER_OFFEN / FELDER_ADMIN). Dasselbe gilt fuer Plan-Start und Review-Termin. Geprueft: - In der kompletten Rohantwort an den Creator kommt der Inhalt der internen Notiz 0-mal vor - Creator auf fremdes Profil: 404 (lesend wie schreibend) - Scout auf ein Profil: 404, profil.html leitet ihn weg - Creator setzt admin_notiz selbst: wird stillschweigend ignoriert, der Inhalt bleibt unveraendert - Profil einer Nicht-Creator-Person: 404 Dabei ist ein aelterer Fehler aufgefallen: /api/ich lieferte nur Name und Rolle, nicht die eigene Nummer. Dadurch rief die Profilseite eines Creators /api/profil/undefined auf und blieb leer. Derselbe Fehler machte in der Personenverwaltung den Selbstvergleich unwirksam -- beim eigenen Eintrag erschien ein "Sperren"-Knopf, den der Server dann ablehnte. /api/ich liefert jetzt zusaetzlich die id. Im Protokoll landen nur die Feldnamen, nie die Inhalte: Im Profil stehen persoenliche Angaben, die nicht zusaetzlich im Audit-Log auftauchen sollen.
135 lines
5.1 KiB
JavaScript
135 lines
5.1 KiB
JavaScript
/* ===================================================================
|
|
Startseite nach dem Anmelden.
|
|
Die Seite ist bereits serverseitig geschützt (workspace.js). Dieses
|
|
Skript holt nur noch, WER angemeldet ist, und regelt das Abmelden.
|
|
=================================================================== */
|
|
(() => {
|
|
'use strict';
|
|
|
|
const ROLLENTEXT = {
|
|
admin: 'Management · Gesamtübersicht und Freigaben',
|
|
creator: 'Creator · dein eigener Bereich',
|
|
scout: 'Scout · deine Pipeline und Kontakte',
|
|
};
|
|
|
|
/* Bereiche aus dem Konzept, mit der Phase aus dem Umsetzungsplan.
|
|
`rollen` steuert, wem ein Bereich überhaupt angezeigt wird - schon
|
|
jetzt, damit die Trennung von Anfang an sichtbar ist. */
|
|
const BEREICHE = [
|
|
{ name: 'Dashboard', phase: 1, rollen: ['admin', 'creator', 'scout'] },
|
|
{ name: 'Personen & Zugänge', phase: 1, rollen: ['admin'], ziel: 'personen.html' },
|
|
{ name: 'Creator-Profile', phase: 1, rollen: ['admin'], ziel: 'profil.html' },
|
|
{ name: 'Mein Profil', phase: 1, rollen: ['creator'], ziel: 'profil.html' },
|
|
{ name: 'Aufgaben', phase: 1, rollen: ['admin', 'creator', 'scout'], ziel: 'aufgaben.html' },
|
|
{ name: 'Kalender', phase: 1, rollen: ['admin', 'creator', 'scout'] },
|
|
{ name: 'Dateien', phase: 1, rollen: ['admin', 'creator'] },
|
|
{ name: 'LIVE-Analyse', phase: 2, rollen: ['admin', 'creator'] },
|
|
{ name: 'Content-Planung', phase: 2, rollen: ['admin', 'creator'] },
|
|
{ name: 'Technik', phase: 2, rollen: ['admin', 'creator'] },
|
|
{ name: 'Community', phase: 2, rollen: ['admin', 'creator'] },
|
|
{ name: 'Schutz & Regeln', phase: 2, rollen: ['admin', 'creator'] },
|
|
{ name: 'Reports & Review', phase: 2, rollen: ['admin', 'creator'] },
|
|
{ name: 'Calls', phase: 3, rollen: ['admin', 'creator', 'scout'] },
|
|
{ name: 'Scout-Pipeline', phase: 3, rollen: ['admin', 'scout'] },
|
|
{ name: 'Automationen', phase: 4, rollen: ['admin'] },
|
|
];
|
|
|
|
const $ = (id) => document.getElementById(id);
|
|
|
|
async function laden() {
|
|
let ich;
|
|
try {
|
|
const antwort = await fetch('/workspace/api/ich', { credentials: 'same-origin' });
|
|
if (!antwort.ok) { location.assign('/workspace/'); return; }
|
|
ich = await antwort.json();
|
|
} catch {
|
|
location.assign('/workspace/');
|
|
return;
|
|
}
|
|
|
|
$('wer').textContent = `${ich.name} · ${ich.rolle}`;
|
|
$('gruss').textContent = `Willkommen, ${ich.name}`;
|
|
$('rollentext').textContent = ROLLENTEXT[ich.rolle] || ich.rolle;
|
|
|
|
const liste = $('bereiche');
|
|
for (const b of BEREICHE.filter((b) => b.rollen.includes(ich.rolle))) {
|
|
const li = document.createElement('li');
|
|
li.className = 'bereich';
|
|
li.dataset.phase = String(b.phase);
|
|
if (b.ziel) li.dataset.fertig = 'ja';
|
|
|
|
const name = document.createElement('span');
|
|
name.className = 'bereich__name';
|
|
name.textContent = b.name;
|
|
|
|
const stand = document.createElement('span');
|
|
stand.className = 'bereich__stand';
|
|
stand.textContent = b.ziel ? 'öffnen' : (b.phase === 1 ? 'als Nächstes' : `Phase ${b.phase}`);
|
|
|
|
if (b.ziel) {
|
|
/* Fertige Bereiche sind echte Verweise -- Tastatur und
|
|
Rechtsklick funktionieren damit wie gewohnt. */
|
|
const a = document.createElement('a');
|
|
a.className = 'bereich__link';
|
|
a.href = b.ziel;
|
|
a.append(name, stand);
|
|
li.append(a);
|
|
} else {
|
|
li.append(name, stand);
|
|
}
|
|
liste.append(li);
|
|
}
|
|
|
|
await zahlenLaden();
|
|
}
|
|
|
|
/* Die Zahlen kommen aus derselben Quelle wie das Brett und sind bereits
|
|
serverseitig auf die eigene Rolle eingegrenzt. */
|
|
const ZAHLEN = [
|
|
{ schluessel: 'ueberfaellig', name: 'Überfällig', warn: true },
|
|
{ schluessel: 'heute', name: 'Heute fällig' },
|
|
{ schluessel: 'offen', name: 'Offen' },
|
|
{ schluessel: 'arbeit', name: 'In Arbeit' },
|
|
{ schluessel: 'review', name: 'Review' },
|
|
{ schluessel: 'erledigt', name: 'Erledigt' },
|
|
];
|
|
|
|
async function zahlenLaden() {
|
|
let daten;
|
|
try {
|
|
const antwort = await fetch('/workspace/api/uebersicht', { credentials: 'same-origin' });
|
|
if (!antwort.ok) return;
|
|
daten = await antwort.json();
|
|
} catch { return; }
|
|
|
|
const liste = $('zahlen');
|
|
liste.textContent = '';
|
|
for (const z of ZAHLEN) {
|
|
const wert = daten[z.schluessel] ?? 0;
|
|
const li = document.createElement('li');
|
|
li.className = 'zahl';
|
|
if (z.warn && wert > 0) li.dataset.warn = 'ja';
|
|
|
|
const n = document.createElement('span');
|
|
n.className = 'zahl__wert';
|
|
n.textContent = String(wert);
|
|
|
|
const t = document.createElement('span');
|
|
t.className = 'zahl__name';
|
|
t.textContent = z.name;
|
|
|
|
li.append(n, t);
|
|
liste.append(li);
|
|
}
|
|
}
|
|
|
|
$('abmelden').addEventListener('click', async () => {
|
|
try {
|
|
await fetch('/workspace/api/abmelden', { method: 'POST', credentials: 'same-origin' });
|
|
} catch { /* Auch ohne Antwort weiterleiten - das Cookie ist dann spätestens abgelaufen. */ }
|
|
location.assign('/workspace/');
|
|
});
|
|
|
|
laden();
|
|
})();
|