/* =================================================================== Startseite nach dem Anmelden. Sie war eine Wand aus siebzehn gleichen Rechtecken, auf denen jeweils "ÖFFNEN" stand. Wenn alles gleich aussieht, ist alles gleich wichtig -- also nichts. Und "ÖFFNEN" ist keine Information, das weiss man von einer Kachel ohnehin. Drei Dinge machen daraus etwas Brauchbares: 1. GRUPPEN. Was man täglich braucht, was zu einem Creator gehört, was den Laden am Laufen hält -- das sind drei verschiedene Absichten und nicht eine Liste. 2. ZAHLEN STATT "ÖFFNEN". Auf der Kachel steht, wo Arbeit liegt. Dieselben Quellen wie die Hinweise darüber, kein zweiter Zähler. Eine Kachel, die "3" sagt, ist zehnmal mehr wert als eine, die "öffnen" sagt. 3. ZEICHEN UND FARBE. Ein eigenes Zeichen je Bereich und ein eigener Farbton -- man findet sie nach kurzer Zeit blind. Die Seite ist bereits serverseitig geschützt (workspace.js). Dieses Skript holt nur noch, WER angemeldet ist. =================================================================== */ (() => { 'use strict'; /** Hat diese Person ueberhaupt einen Arbeitsplatz hier? GEFRAGT WIRD NICHT NACH DER ROLLE. Rollennamen der Community gehoeren nicht in eine Datei, die jeder herunterladen kann -- darueber wacht pruef-modi-wortleck, und ROLLENTEXT unten laesst sie aus demselben Grund aus. Gefragt wird nach etwas, das der Server ohnehin schon beantwortet hat: Hat sie die Aufgaben-Kachel? Er schickt nur, was jemand sehen darf. Keine zweite Wahrheit, kein Rollenname -- und es bleibt richtig, wenn sich die Rechte aendern. */ function hatArbeitsplatz() { const liste = ich && ich.bereiche; if (!Array.isArray(liste)) return true; /* im Zweifel wie bisher */ return liste.some((b) => String(b.ziel || '').split('?')[0] === 'aufgaben.html'); } const ROLLENTEXT = { spicy: 'Spicy Media · Überblick über alle, ohne Automationen', admin: 'DogFather · Gesamtübersicht und Freigaben', /* Der Satz stimmte bis zum 07.09.2026. Seither legen Manager Creator an -- und ein Rollentext, der etwas anderes verspricht als die Oberflaeche kann, ist schlimmer als gar keiner. */ manager: 'Manager · dieselben Rechte, ohne Personenverwaltung – legt Creator an', creator: 'Creator · dein eigener Bereich', scout: 'Scout · deine Pipeline und die Creator, die du betreust', }; /* Zeichen und Bereiche kommen aus assets/js/bereiche.js -- derselben Quelle, aus der auch die Kopfleisten der Unterseiten ihr Zeichen und ihre Farbe nehmen. Zwei Listen wären der sichere Weg dazu, dass ein Bereich hier gelb und dort grün ist. */ const { ZEICHEN, GRUPPEN, zeichenBauen } = window.Bereiche; /* Welcher Hinweis gehört auf welche Kachel. Die Zahl auf der Kachel ist damit KEIN neuer Zähler, sondern dieselbe Quelle wie die Liste darüber -- sonst stünden irgendwann zwei verschiedene Wahrheiten übereinander auf einem Bildschirm. */ const HINWEIS_ZU_KACHEL = { aufgaben_ueberfaellig: 'Aufgaben', aufgaben_heute: 'Aufgaben', aufgaben_review: 'Aufgaben', termine_heute: 'Kalender', protokoll_fehlt: 'Calls & Protokolle', dateien_review: 'Dateien', /* DAS ZIEL STATT DES NAMENS (berichtigt 20.09.2026). Hier stand „LIVE-Analyse". So heisst die Kachel auf der AGENTURSEITE -- auf der Crew-Adresse heisst dieselbe Seite „Live-Ablauf". Ein Name trifft immer nur ein Haus; ich hatte ihn erst einfach umgedreht und damit das andere kaputt gemacht (pruef-start-ansicht wurde sofort rot). Das ZIEL gilt in beiden. */ bereiche_dringend: 'bereich.html?b=live', startcheck_handlung: 'Start-Check', ohne_startcheck: 'Start-Check', followup_faellig: 'Scout-Pipeline', uebergabe_offen: 'Scout-Pipeline', review_faellig: 'Reports & Review', review_bald: 'Reports & Review', ohne_betreuung: 'Personen & Zugänge', ohne_profil: 'Creator-Profile', markiert_live: 'bereich.html?b=live', markiert_content: 'Content-Ideen', markiert_community: 'Community', markiert_technik: 'Technik', /* MANAGER-ZIELE (02.10.2026). Ueber das ZIEL und nicht ueber den Namen -- ein Name wandert, ein Ziel nicht (gelernt am 20.09.2026, als "LIVE-Analyse" auf der Crew-Adresse anders heisst). Hier faellt es ohnehin nicht auf: Die Kachel gibt es nur im Agenturhaus. Genau deshalb steht sie trotzdem so da -- die naechste koennte es anders sein. */ mz_dringend: 'manager-ziele.html', mz_offen: 'manager-ziele.html', }; /* Diese Hinweise bekommen zusätzlich das Merkzeichen auf die Kachel. Sie sind etwas anderes als "3 Aufgaben offen": Da hat jemand persönlich etwas markiert und wartet auf eine Reaktion. Eine Zahl allein macht diesen Unterschied nicht sichtbar. */ const MIT_MERKZEICHEN = new Set([ 'markiert_live', 'markiert_content', 'markiert_community', 'markiert_technik', ]); /* Findet einen Bereich über seinen Namen -- damit Hinweis und Kachel dasselbe Zeichen und denselben Farbton benutzen, ohne dass die Zuordnung ein zweites Mal irgendwo stünde. */ /** Die Kachel zu einem Hinweis -- GEFUNDEN UEBER DAS ZIEL, NICHT * UEBER DEN NAMEN (20.09.2026). * * Hier stand `x.name === name`, und gesucht wurde nur in `GRUPPEN` * -- der Kachelliste der AGENTUR aus bereiche.js. Zwei Fehler in * einer Zeile, und beide waren unsichtbar: * * 1. Die Kacheln von Team Dogi schickt der SERVER (`ich.bereiche`). * In GRUPPEN stehen sie nicht. Fuer einen Modi fand diese Zeile * also entweder nichts -- oder eine AGENTUR-Kachel, die er gar * nicht hat, und uebernahm deren Farbe. * * 2. Namen aendern sich. Gemessen am 20.09.2026: Sieben der * dreizehn Hinweisziele hatten auf der Crew-Adresse keine Kachel * dieses Namens. „LIVE-Analyse" zum Beispiel heisst dort * „Live-Ablauf". * * Das Ziel bleibt. `bereich.js` sagt denselben Satz an einer * anderen Stelle schon: „Gefunden wird ueber das ZIEL, nicht ueber * den Namen: Der Name aendert sich, das Ziel bleibt." */ const bereichZu = (was) => { if (!was) return null; const passt = (k) => k && (k.name === was || String(k.ziel || '').split('#')[0] === was); /* ZUERST DIE EIGENEN KACHELN. Hier wurde nur `GRUPPEN` durchsucht -- die Liste der AGENTUR aus bereiche.js. Die Kacheln von Team Dogi schickt der SERVER (`ich.bereiche`), und die standen dort nie drin. Fuer einen Modi fand diese Zeile entweder nichts oder eine Agenturkachel, die er gar nicht hat -- und uebernahm deren Farbe. Dieselbe Fehlerklasse wie bei der Seitenfarbe am 19.09.2026, nur an einer anderen Stelle. */ for (const k of (ich?.bereiche || [])) if (passt(k)) return k; for (const k of (ich?.bereiche_zusatz || [])) if (passt(k)) return k; for (const g of GRUPPEN) { const k = g.bereiche.find(passt); if (k) return k; } return null; }; const $ = (id) => document.getElementById(id); const el = (tag, klasse, text) => { const k = document.createElement(tag); if (klasse) k.className = klasse; if (text !== undefined) k.textContent = text; return k; }; let ich = null; /* WESSEN ARBEITSPLATZ WIRD GEZEIGT (02.09.2026). `ich` ist, WER ANGEMELDET ist -- das bleibt so und gilt fuer alles, was schreibt oder Rechte hat. `zeigt` ist, WESSEN Arbeitsplatz auf dem Bildschirm steht. Im Normalfall dasselbe. Vorher gab es nur `ich`, und die Folge war ein halb gebauter Umschalter: Die Listen kamen bereits von der gewaehlten Person, Kacheln, Rolle und Begruessung aber weiterhin von DogFather. Er sah einen Creator-Arbeitsplatz in seiner eigenen Verkleidung -- gemeldet mit "ich seh immer noch die Seite genau wie meine". */ let zeigt = null; let hinweise = []; /* ---------- Aufbau ------------------------------------------------------- */ function bereicheBauen() { const ziel = $('bereiche'); ziel.textContent = ''; /* Zahlen je Kachel aus den Hinweisen zusammenzählen. Warnstufe schlägt durch: Eine Kachel mit etwas Überfälligem ist rot, auch wenn daneben Harmloses mitgezählt wird. */ /* DIE ZAHL LANDET AUF DER AUFGELOESTEN KACHEL (20.09.2026). ---------------------------------------------------------------- Hier wurde nach dem SCHLUESSEL aus `HINWEIS_ZU_KACHEL` abgelegt und unten nach `b.name` gesucht. Das ging genau so lange gut, wie beide dasselbe Wort waren. Dasselbe Brett heisst aber in den zwei Haeusern verschieden: auf der Agenturseite „LIVE-Analyse", auf der Crew-Adresse „Live-Ablauf". Ein Name trifft immer nur ein Haus -- ich hatte den Schluessel erst einfach umgedreht und damit das andere kaputt gemacht (`pruef-start-ansicht` wurde sofort rot). Abgelegt wird jetzt unter dem ZIEL der Kachel. Das ist in beiden Haeusern dasselbe und aendert sich nicht, wenn jemand umbenennt. */ const zahlen = new Map(); for (const h of hinweise) { const treffer = bereichZu(HINWEIS_ZU_KACHEL[h.art]); const kachel = treffer?.ziel || HINWEIS_ZU_KACHEL[h.art]; if (!kachel) continue; const bisher = zahlen.get(kachel) || { n: 0, warn: false, merk: 0 }; bisher.n += h.anzahl; if (h.stufe === 'warnung') bisher.warn = true; if (MIT_MERKZEICHEN.has(h.art)) bisher.merk += h.anzahl; zahlen.set(kachel, bisher); } /* ================================================================= WELCHE KACHELN DIESE PERSON SIEHT (10.09.2026) ZWEI QUELLEN, und die Reihenfolge ist die Aussage: 1. Schickt der Server eine Liste, gilt sie. Das ist der Weg fuer Rollen, die in bereiche.js gar nicht vorkommen duerfen -- diese Datei bekommt jeder ausgeliefert, der die Seite oeffnet, und ein Rollenname darin waere mit einem Blick in den Quelltext gefunden. 2. Sonst wie bisher: die Rollenliste an der Kachel selbst. Nach der ANGESEHENEN Rolle, nicht der eigenen -- sonst stuenden bei der Sicht auf einen Creator weiterhin "Personen & Zugaenge" und "Automationen" da, die er gar nicht hat. Genau daran war zu sehen, dass die Sicht nicht greift. Der Server richtet sich beim Bauen seiner Liste nach derselben Person. `null` heisst "nimm deine eigene Liste", eine LEERE Liste hiesse "keine Kacheln". Die beiden auseinanderzuhalten ist kein Feinschliff: Verwechselte man sie, haetten die fuenf bekannten Rollen ab sofort eine leere Startseite. */ /* Ist die Seite hinter diesem Ziel fuer mich offen? Ohne Auskunft vom Server: ja -- siehe oben. */ function seiteOffen(ziel) { const offen = ich?.seiten; if (!Array.isArray(offen) || !offen.length) return true; const z2 = String(ziel || ""); if (!z2) return true; return offen.includes('/workspace/' + z2.split('?')[0]); } /* ===================================================================== DIE REIHENFOLGE DER GRUPPEN (19.09.2026) Filipe: „ich will das oben die sachen für dogfather sind, dan die sachen für modis und dan community bereich, und so soll die reihenfolge überall perfekt ist, also mit dem was jede rolle eben halt sieht. was sie nicht sehen sehen sie nicht." BIS HEUTE ENTSCHIED DER ZUFALL. Die Gruppen kamen in der Reihenfolge, in der die Kacheln zusammengesetzt wurden — und die hängt davon ab, welche Liste an welche angehängt wurde. Beim Team stand „Für dich" deshalb mitten zwischen der Arbeit, und die Moderation ganz unten hinter allen sieben Brettern der Community. JETZT GILT EINE ORDNUNG, und sie folgt Filipes Satz: 1. die eigene Arbeit (Täglich, Live, Creator, Entwicklung …) 2. die Arbeit am Treff (Moderation) — sieht nur, wer moderiert 3. der Raum der Zuschauer (Community) 4. das Eigene (Für dich) „Was sie nicht sehen, sehen sie nicht" bleibt unberührt: Diese Funktion sortiert nur, was ohnehin schon da ist. Wer keine Moderationskachel bekommt, bekommt auch keine Gruppe dafür — leere Gruppen fallen vorher heraus. UNBEKANNTE GRUPPEN LANDEN BEI DER ARBEIT, nicht am Ende. Eine neu erfundene Gruppe ist fast immer Arbeit; und sie am Ende einzusortieren hieße, sie hinter dem Persönlichen zu verstecken, wo sie niemand sucht. */ /* NUR DAS ENDE WIRD FESTGELEGT -- die Arbeitsgruppen behalten ihre Reihenfolge. Der erste Versuch schrieb auch die Arbeitsgruppen fest (Täglich, Rund um das Team, …). Das war zu viel: Ihre Reihenfolge ist an mehreren Stellen bewusst gewählt und mit Gründen versehen, und eine Prüfung hat sie sofort als geändert gemeldet — zu Recht. Wer eine bestehende Entscheidung überschreibt, ohne sie gelesen zu haben, repariert nichts, sondern ersetzt sie durch seine eigene. Also: Alles, was hier NICHT steht, bleibt, wo es war. Nur diese drei wandern ans Ende, in genau dieser Folge. */ const GRUPPEN_ZULETZT = ['Moderation', 'Community', 'Für dich']; function gruppenPlatz(name) { const z = GRUPPEN_ZULETZT.indexOf(name); return z >= 0 ? 1 + z : 0; } /** Sortiert die Gruppen, ohne innerhalb einer Gruppe etwas zu * verschieben. `sort` ist seit ES2019 stabil — Kacheln gleicher * Gruppe behalten also ihre Reihenfolge, und die ist an mehreren * Stellen bewusst gewählt (die breite Treff-Kachel vorn). */ function nachOrdnung(gruppen) { return [...gruppen].sort((a, b) => gruppenPlatz(a.name) - gruppenPlatz(b.name)); } function eigeneGruppen(wer) { const vomServer = ich && ich.bereiche; if (Array.isArray(vomServer)) { /* Nach Gruppen buendeln, in der Reihenfolge, in der sie kommen -- eine Map behaelt sie, ein Objekt sortierte Zahlenschluessel um. Hier sind es Woerter, aber darauf zu bauen waere die Art Annahme, die spaeter jemanden Zeit kostet. */ const nach = new Map(); for (const b of vomServer) { if (!nach.has(b.gruppe)) { nach.set(b.gruppe, { name: b.gruppe, unter: b.gruppeUnter || '', bereiche: [] }); } nach.get(b.gruppe).bereiche.push(b); } return nachOrdnung([...nach.values()]); } /* KACHELN, DIE DAZUKOMMEN (10.09.2026). Anders als `ich.bereiche` (das die Liste ERSETZT) haengen diese sich an eine vorhandene Gruppe an. Gebraucht wird das fuer Kacheln, die nur eine einzige Rolle bekommt: In dieser Datei duerften sie nicht stehen -- sie wird an jeden ausgeliefert, und eine Kachel, die nur bei einem erscheint, wirft die Frage auf, fuer wen sie ist. Findet sich die genannte Gruppe nicht, wird eine eigene angelegt. Sonst faellt die Kachel lautlos weg, sobald jemand eine Gruppe umbenennt -- und niemand merkt es, weil nichts kaputtgeht. */ const zusatz = Array.isArray(ich?.bereiche_zusatz) ? ich.bereiche_zusatz : []; const gruppen = GRUPPEN /* UND GEGEN DIE RECHTETAFEL (11.09.2026). `rollen` an der Kachel ist die Liste in dieser Datei -- sie wird von Hand gepflegt und kennt nichts davon, dass DogFather eine Seite umgestellt hat. `ich.seiten` kommt vom Server und ist dieselbe Auskunft, aus der auch die Schranke ihre Entscheidung holt. BEIDES UND NICHT NUR EINES: Die Liste hier entscheidet, was ueberhaupt angeboten wird; die Tafel, ob es noch offen ist. Faellt `ich.seiten` aus (alte Fassung im Zwischenspeicher), bleibt es beim alten Verhalten -- eine Kachel zu viel ist aergerlich, eine fehlende sieht aus wie ein Fehler. */ .map((g) => ({ ...g, bereiche: g.bereiche.filter((b) => b.rollen.includes(wer.rolle) && seiteOffen(b.ziel)) })) .filter((g) => g.bereiche.length); for (const b of zusatz) { const g = gruppen.find((x) => x.name === b.gruppe); if (g) { g.bereiche = [...g.bereiche, b]; continue; } /* EINE NEUE GRUPPE DARF SAGEN, WO SIE HINGEHOERT (10.09.2026). Bis hierhin wurde sie hinten angehaengt. Filipe: "ich will dass die kacheln ... eine eigene kategorie haben. damit ich nicht zwischen den kacheln suchen muss." Ganz unten waere zwar eine eigene Kategorie -- aber immer noch Scrollen. `gruppeNach` nennt die Gruppe, HINTER der sie stehen soll. Findet sie sich nicht (umbenannt, fuer diese Rolle nicht dabei), bleibt es beim Anhaengen: Eine Kachel, die wegen einer Reihenfolge verschwindet, waere der schlechtere Tausch. */ const neuGruppe = { name: b.gruppe, unter: b.gruppeUnter || '', bereiche: [b] }; const nach = b.gruppeNach ? gruppen.findIndex((x) => x.name === b.gruppeNach) : -1; if (nach >= 0) gruppen.splice(nach + 1, 0, neuGruppe); else gruppen.push(neuGruppe); } return nachOrdnung(gruppen); } let laufNr = 0; /* Alle Kacheln, die zum Live fuehren -- gesammelt, weil die Antwort des Live-Dienstes erst nach dem Aufbau der Wand da ist (ein Netzabruf, die Wand steht in Millisekunden). */ const liveKacheln = []; for (const gruppe of eigeneGruppen(zeigt)) { const meine = gruppe.bereiche; if (!meine.length) continue; const abschnitt = el('section', 'gruppe'); /* Manche Gruppen heißen für einen Creator anders -- siehe GRUPPEN. */ const istCreator = zeigt.rolle === 'creator'; /* =============================================================== JEDE GRUPPE LAESST SICH ZUKLAPPEN (07.09.2026) Wunsch Filipe: "diese kategorien soll man auch zu und aufmachen koennen so wie es einem lieb ist gerade." DER KOPF IST DER SCHALTER, nicht ein Knopf daneben. Eine Ueberschrift, neben der ein kleines Dreieck sitzt, laedt zum Zielen ein; eine Ueberschrift, die SELBST der Schalter ist, trifft man immer. Deshalb