Files
dogfather-universe/workspace/assets/js/start.js
T
DogFatherGit eb87e4a484 Workspace: keine Kachel mehr ohne Ziel -- Automationen bekommen ihren Ort
Filipe: "da sind aber noch zwei kategorien zu". Er hat recht, und es
waren zwei VERSCHIEDENE Fehler.

1) "DASHBOARD" war eine Kachel, die man nicht oeffnen kann -- man steht
   ja schon darauf. Sie stand seit dem ersten Tag als Platzhalter da.
   Ein Eintrag, der nirgendwohin fuehrt, sieht aus wie etwas Unfertiges.
   Entfernt.

2) "AUTOMATIONEN" trug noch "Phase 4", obwohl die Automationen laengst
   liefen -- sie waren nur verteilt (Hinweise auf der Uebersicht, Suche
   ueberall, KI-Vorschlaege an drei Stellen) und hatten keinen Ort.
   Jetzt haben sie einen.

DIE SEITE FUEHRT BEWUSST NICHTS EIGENES. Was dort steht, kommt aus den
Bereichen selbst -- eine Seite, die Automationen doppelt abbildet, waere
die naechste Stelle, die irgendwann etwas anderes behauptet als die
Wirklichkeit. Vier Abschnitte:

- Die lokale KI mit Zustand, Modell, Ort, Kosten und Grenzen -- und dem
  einzigen Schalter der Seite. Sie ist die einzige Automation, die
  Rechenzeit kostet, die der Website gehoert.
- "Was gerade anliegt": dieselben Hinweise wie auf der Uebersicht, hier
  vollstaendig statt gekuerzt.
- "Laeuft dauerhaft im Hintergrund": sieben Regeln im Klartext. Jede
  beschreibt etwas, das tatsaechlich im Code steht.
- "Zuletzt automatisch passiert": aus dem Protokoll, gefiltert auf das,
  was ohne Zutun geschah.

DER KI-SCHALTER WIRD AN ZWEI STELLEN GEPRUEFT: beim Anzeigen der
Knoepfe UND in jedem Endpunkt. Eine Oberflaeche, die etwas versteckt,
ist keine Sperre. Geprueft: abgeschaltet liefert der Endpunkt 403, ein
Creator darf gar nicht schalten (403), die Einstellung ueberdauert einen
Neustart (eigene Tabelle, damit sie dieselbe Sicherung bekommt wie alles
andere).

NEBENBEI DERSELBE FEHLER WIE FRUEHER: Der Schalter-Baustein lag in
wissen.css und fehlte prompt auf der neuen Seite -- dort erschien er als
nacktes Kaestchen. Wie damals bei .knopf-still. Liegt jetzt in gate.css.

Geprueft: 17 Kacheln, KEINE mehr ohne Ziel. Creator wird von der Seite
weggeleitet (302). Handy ohne Ueberlauf, 0 Konsolenfehler.
2026-08-31 12:46:22 +02:00

197 lines
8.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ===================================================================
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: 'DogFather · Gesamtübersicht und Freigaben',
manager: 'Manager · dieselben Rechte, ohne Personenverwaltung',
creator: 'Creator · dein eigener Bereich',
scout: 'Scout · deine Pipeline und die Creator, die du betreust',
};
/* 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.
"Dashboard" stand hier als Kachel, die man nicht öffnen kann -- man
ist ja schon darauf. Ein Eintrag, der nirgendwohin führt, sieht aus
wie etwas Unfertiges. Raus. */
const BEREICHE = [
{ name: 'Personen & Zugänge', phase: 1, rollen: ['admin', 'manager'], ziel: 'personen.html' },
{ name: 'Creator-Profile', phase: 1, rollen: ['admin', 'manager', 'scout'], ziel: 'profil.html' },
{ name: 'Mein Profil', phase: 1, rollen: ['creator'], ziel: 'profil.html' },
{ name: 'Start-Check', phase: 1, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'startcheck.html' },
{ name: 'Wissens-Bibliothek', phase: 1, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'wissen.html' },
{ name: 'Aufgaben', phase: 1, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'aufgaben.html' },
{ name: 'Kalender', phase: 1, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'kalender.html' },
{ name: 'Dateien', phase: 1, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'dateien.html' },
{ name: 'LIVE-Analyse', phase: 2, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=live' },
{ name: 'Content-Planung', phase: 2, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=content' },
{ name: 'Technik', phase: 2, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=technik' },
{ name: 'Community', phase: 2, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=community' },
{ name: 'Schutz & Regeln', phase: 2, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=schutz' },
{ name: 'Reports & Review', phase: 2, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'report.html' },
{ name: 'Calls & Protokolle', phase: 3, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'calls.html' },
{ name: 'Scout-Pipeline', phase: 3, rollen: ['admin', 'manager', 'scout'], ziel: 'scouting.html' },
{ name: 'Automationen', phase: 4, rollen: ['admin', 'manager'], ziel: 'automation.html' },
];
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_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 hinweiseLaden();
await zahlenLaden();
}
/* Was ist dran? Erster Baustein der Automationen aus dem Konzept.
Fuehrt bewusst keine eigenen Daten -- jeder Hinweis ist nur eine
Sicht auf etwas, das schon da ist, und verschwindet von selbst,
sobald die Sache erledigt ist. Nichts zu quittieren. */
const EINZAHL = {
aufgaben_ueberfaellig: (n) => `${n} ${n === 1 ? 'Aufgabe ist' : 'Aufgaben sind'} überfällig`,
aufgaben_heute: (n) => `${n} ${n === 1 ? 'Aufgabe ist' : 'Aufgaben sind'} heute fällig`,
aufgaben_review: (n) => `${n} ${n === 1 ? 'Aufgabe wartet' : 'Aufgaben warten'} auf Freigabe`,
termine_heute: (n) => `${n} ${n === 1 ? 'Termin steht' : 'Termine stehen'} heute an`,
protokoll_fehlt: (n) => `${n} ${n === 1 ? 'Gespräch hat' : 'Gespräche haben'} noch kein Protokoll`,
dateien_review: (n) => `${n} ${n === 1 ? 'Datei liegt' : 'Dateien liegen'} im Review`,
bereiche_dringend: (n) => `${n} ${n === 1 ? 'dringender Punkt' : 'dringende Punkte'} in den Bereichen`,
followup_faellig: (n) => `${n} Follow-up${n === 1 ? '' : 's'} ${n === 1 ? 'ist' : 'sind'} fällig`,
uebergabe_offen: (n) => `${n} übergebene${n === 1 ? 'r Lead wartet' : ' Leads warten'} auf das Onboarding`,
review_faellig: (n) => `${n} Review-Termin${n === 1 ? ' ist' : 'e sind'} überfällig`,
review_bald: (n) => `${n} Review${n === 1 ? ' steht' : 's stehen'} in den nächsten 7 Tagen an`,
ohne_betreuung: (n) => `${n} Creator ${n === 1 ? 'hat' : 'haben'} noch keine zuständige Person`,
ohne_profil: (n) => `${n} Creator-Profil${n === 1 ? ' ist' : 'e sind'} noch leer`,
ohne_startcheck: (n) => `${n} Creator ${n === 1 ? 'hat' : 'haben'} noch keinen Start-Check`,
startcheck_handlung: (n) => `${n} ${n === 1 ? 'Punkt im Start-Check braucht' : 'Punkte im Start-Check brauchen'} Handlung`,
};
async function hinweiseLaden() {
let daten;
try {
const antwort = await fetch('/workspace/api/hinweise', { credentials: 'same-origin' });
if (!antwort.ok) return;
daten = await antwort.json();
} catch { return; }
const liste = $('dran');
liste.textContent = '';
if (!daten.hinweise.length) { $('dran-block').hidden = true; return; }
for (const h of daten.hinweise) {
const li = document.createElement('li');
li.className = 'dran__punkt';
li.dataset.stufe = h.stufe;
/* Jeder Hinweis fuehrt irgendwohin -- ein Hinweis ohne Ziel ist
nur ein schlechtes Gewissen. */
const a = document.createElement('a');
a.className = 'dran__link';
a.href = h.ziel;
const text = document.createElement('span');
text.className = 'dran__text';
text.textContent = (EINZAHL[h.art] || ((n) => `${n} × ${h.text}`))(h.anzahl);
const pfeil = document.createElement('span');
pfeil.className = 'dran__pfeil';
pfeil.setAttribute('aria-hidden', 'true');
pfeil.textContent = '→';
a.append(text, pfeil);
li.append(a);
liste.append(li);
}
$('dran-block').hidden = false;
}
/* 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();
})();