/workspace/scouting.html. Erster Baustein aus Phase 3 und der Bereich,
in dem Scouts bisher praktisch nichts hatten.
Der Merksatz von Seite 15 ist hier die Sicherheitsregel, nicht nur eine
Beschreibung: "Scouts sehen ihre Pipeline -- die Admin-Rolle die
Gesamtuebersicht -- Creator keine Scout-internen Daten."
- Creator bekommen 404, nicht 403, und zwar auf Schnittstelle UND Seite.
Sie sollen nicht einmal erfahren, dass es den Bereich gibt.
- Ein Scout sieht ausschliesslich die eigene Pipeline. Geprueft: Patrick
bekommt auf Sams Lead 404 beim Lesen, Aendern und Loeschen.
- Das Management sieht alles, kann nach Scout filtern und Leads einem
Scout zuordnen.
Pipeline als gruppierte Liste, nicht als Board: Sechs Spalten waeren auf
dem Handy unbedienbar, und Scouts arbeiten unterwegs. Jede Stufe traegt
eine eigene Farbe an der linken Kante, die von kuehl (neu entdeckt) nach
gruen (uebergeben) laeuft -- die Richtung der Pipeline wird sichtbar,
ohne Ampel-Geblinke.
Jede Karte hat genau EINEN naheliegenden Schritt ("Weiter zu ..."), der
Rest steckt im aufklappbaren Teil. Der Weiter-Knopf traegt bewusst nur
die Farbe seiner Stufe statt des vollen Farbverlaufs, sonst waere die
Seite ein Streifenmuster aus identischen Leuchtbalken.
Faellige Follow-ups stehen als eigene Leiste ganz oben. Das ist der
Teil, der ohne System am ehesten untergeht -- nicht der Kontakt selbst,
sondern das Nachfassen.
Uebergabe ("Creator-Onboarding starten", Seite 15): Aus einem
uebergebenen Lead legt das Management mit einem Klick eine echte Person
mit Zugangscode an. Der Code wird genau einmal angezeigt, wie in der
Personenverwaltung. Die Qualifizierung des Scouts (Plattform, Handle,
Potenzial, LIVE-Aktivitaet) wandert dabei automatisch ins Creator-Profil
-- sonst muesste das Management abtippen, was laengst dasteht. Geprueft:
Lead -> Person -> Profil, alle drei verbunden, zweiter Versuch wird
abgelehnt.
Ein uebergebener Lead ist Teil der Betreuungsgeschichte -- den loescht
nur das Management, nicht der Scout (403).
Nebenbei: .knopf-still gab es im Baukasten noch gar nicht, die Zweit-
knoepfe waren nackte Systemknoepfe. Jetzt sauber definiert.
129 lines
5.1 KiB
JavaScript
129 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'], ziel: 'kalender.html' },
|
|
{ name: 'Dateien', phase: 1, rollen: ['admin', 'creator', 'scout'], ziel: 'dateien.html' },
|
|
{ name: 'LIVE-Analyse', phase: 2, rollen: ['admin', 'creator'], ziel: 'bereich.html?b=live' },
|
|
{ name: 'Content-Planung', phase: 2, rollen: ['admin', 'creator'], ziel: 'bereich.html?b=content' },
|
|
{ name: 'Technik', phase: 2, rollen: ['admin', 'creator'], ziel: 'bereich.html?b=technik' },
|
|
{ name: 'Community', phase: 2, rollen: ['admin', 'creator'], ziel: 'bereich.html?b=community' },
|
|
{ name: 'Schutz & Regeln', phase: 2, rollen: ['admin', 'creator'], ziel: 'bereich.html?b=schutz' },
|
|
{ name: 'Reports & Review', phase: 2, rollen: ['admin', 'creator'], ziel: 'report.html' },
|
|
{ name: 'Calls', phase: 3, rollen: ['admin', 'creator', 'scout'] },
|
|
{ name: 'Scout-Pipeline', phase: 3, rollen: ['admin', 'scout'], ziel: 'scouting.html' },
|
|
{ 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);
|
|
}
|
|
}
|
|
|
|
|
|
laden();
|
|
})();
|