Der Handy-Durchgang: drei echte Bedienfehler, alle vom selben Ursprung
Filipe: "ich will dass du die komplette seite auf dem handy abcheckst.
ich will dass alles perfekt aussieht und bedienbar ist."
DER SCHWERSTE FUND: Auf ZWOELF Seiten war der "Meine Sicht"-Umschalter
nicht bedienbar -- wer darauf tippte, landete im Chat.
Bei 412 px (der haeufigsten Android-Breite ueberhaupt) brach die
Kopfleiste nicht um, der Umschalter wurde auf 50 px zusammengedrueckt,
sein Knopf behielt aber seine 104 px Mindestbreite und lag damit quer
ueber dem Chat-Knopf. Sichtbar war davon nichts.
Und die Ursache steht seit dem 05.09. woertlich im Kommentar daneben:
"Die Rechnung ging genau auf, solange rechts VIER Dinge standen. Mit der
Glocke sind es FUENF, und bei 320 px passte es nicht mehr." Am 06.09.
habe ich den Chat-Knopf dazugesetzt -- SECHS -- und die Schwelle bei 380
gelassen. Derselbe Fehler, eine Position weiter.
Die Lehre ist nicht "380 auf 430 erhoehen"; das waere er ein drittes
Mal, nur mit einer anderen Zahl. Eine feste Schwelle ist eine Rechnung,
die jemand einmal aufgestellt hat und die beim naechsten Knopf still
falsch wird. `flex-wrap: wrap` OHNE Schwelle rechnet nicht, sondern
misst -- es bricht genau dann um, wenn der Platz nicht reicht.
DASSELBE NOCH EINMAL, am anderen Ende: Bei 1280 px brauchte die Leiste
1235 px (Marke 375 + Bedienelemente 844 + Abstand 16) und hatte 1200.
Auch hier war der Chat-Knopf der Tropfen. Sie brach um, sobald das
Skript die Bedienelemente eingehaengt hatte, und schob die ganze Seite
52 px nach unten -- CLS 0,94. Jetzt gibt die MARKE nach (sie darf
gekuerzt werden, ein Knopf nicht), und umgebrochen wird nur noch auf
sehr schmalen Geraeten.
WEITERE ECHTE FUNDE:
* /workspace/api/chat/ungelesen wurde auf JEDER Seite ZWEIMAL geholt:
einmal beim Laden, einmal Millisekunden spaeter beim Aufgehen des
Ereignisstroms. Der 'open'-Zuhoerer war fuer Wiederverbindungen
gedacht und feuerte auch beim ersten Mal.
* Drei Kacheln teilten sich einen Farbton mit einer anderen (18 Farben
auf 21 Kacheln). Filipe wollte ausdruecklich, dass jede ihre eigene
hat. Nicht eine Farbe dazuerfunden -- der ganze Farbkreis ist mit
tools/kachel-farben.mjs neu in 21 geteilt; kleinster Abstand zweier
Nachbarn jetzt 120 Grad (vorher 106). Dabei fiel eine feste 16 in
der Mischschleife auf: Bei 18 Kacheln wurden die letzten beiden nie
mitgemischt.
* Der Agentur-Untertitel wurde auf dem Handy abgeschnitten.
* Ein zugeklappter <details>-Kasten (Kalender-Abo) verdeckte einen
Filter-Chip: Chromium versteckt dessen Inhalt ueber
`content-visibility`, nicht ueber `display` -- die Kaesten behalten
eine Groesse. Dieselbe Falle wie bei den Zeitbloecken, dieselbe
Loesung.
UND DREI PRUEFUNGEN, DIE SELBST FALSCH LAGEN:
* "achtzehn Kacheln" stand als feste Zahl im Test. Eine Pruefung, die
bei jeder neuen Kachel rot wird, erzieht dazu, ihr Rotwerden zu
ignorieren. Sie zaehlt jetzt aus bereiche.js.
* "das Licht der Kalender-Kachel (tuerkis) muss mehr Blau als Rot
haben" -- Wissen von aussen, und nach dem Neurechnen war der
Kalender rosa. Gemessen wird jetzt gegen den Ton der Kachel selbst.
* pruef-workspace-umzug meldete sein Ergebnis in eigenen Worten. Der
Gesamtlauf las daraus NULL Pruefungen und schrieb bei JEDEM Lauf ein
FEHL, obwohl alle 31 Punkte bestanden. Ein Fehlalarm, der immer
kommt, macht den einen echten unsichtbar.
Und weil "BUTTON 'Meine Sicht' verdeckt" mich eine Stunde gekostet hat,
sagen pruef-handy und pruef-tempo jetzt DAZU, was verdeckt und was
springt -- mit Elternkette und Koordinaten. Ein Befund, den man nicht
verorten kann, ist ein halber.
NEU: der Kalender zum Abonnieren (Stufe 3.2). Persoenlicher, jederzeit
widerrufbarer Link fuer Google, Apple und Outlook. 36 Pruefungen, die
das ICS ZURUECKLESEN statt es anzusehen -- entfaltet, entschluesselt,
verglichen. Wichtigster Punkt: Der Weg ohne Anmeldung darf nichts
zeigen, was der Weg mit Anmeldung nicht zeigt.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -802,3 +802,95 @@
|
||||
.k-dabei__zeichen { border: 1px solid CanvasText; }
|
||||
.k-dabei__haken path { stroke: CanvasText; }
|
||||
}
|
||||
|
||||
/* ---------- Kalender abonnieren (06.09.2026) -----------------------------
|
||||
|
||||
Zugeklappt und unauffaellig: Man richtet die Sache EINMAL ein und
|
||||
sieht sie danach nie wieder an. Ein dauerhaft offener Kasten draengte
|
||||
sich vor die Termine, um die es hier geht. */
|
||||
.k-abo {
|
||||
margin: 0 0 22px;
|
||||
background: var(--flaeche);
|
||||
border: 1px solid var(--rand);
|
||||
border-radius: var(--radius-klein);
|
||||
overflow: hidden;
|
||||
}
|
||||
.k-abo__kopf {
|
||||
display: grid; gap: 2px;
|
||||
padding: 13px 16px; cursor: pointer;
|
||||
list-style: none;
|
||||
transition: background var(--tempo);
|
||||
}
|
||||
.k-abo__kopf::-webkit-details-marker { display: none; }
|
||||
.k-abo__kopf:hover, .k-abo__kopf:focus-visible { background: rgba(150, 186, 220, .05); }
|
||||
.k-abo__titel { font-size: .95rem; color: var(--text); }
|
||||
.k-abo__unter { font-size: .8rem; color: var(--text-still); }
|
||||
|
||||
/* Ein Pfeil, der sich dreht -- das einzige Zeichen, das sagt "hier ist
|
||||
mehr". Ohne ihn sieht die Zeile aus wie eine Ueberschrift. */
|
||||
.k-abo__kopf::after {
|
||||
content: ''; position: absolute; right: 18px; margin-top: 6px;
|
||||
width: 8px; height: 8px;
|
||||
border-right: 2px solid var(--text-still);
|
||||
border-bottom: 2px solid var(--text-still);
|
||||
transform: rotate(45deg);
|
||||
transition: transform var(--tempo);
|
||||
}
|
||||
.k-abo { position: relative; }
|
||||
.k-abo[open] .k-abo__kopf::after { transform: rotate(-135deg); }
|
||||
|
||||
/* ZUGEKLAPPT HEISST WEG -- ausdruecklich, nicht nur laut Browser.
|
||||
|
||||
Dasselbe wie bei den Zeitbloecken in calls.css, und aus demselben
|
||||
Grund: Chromium versteckt den Inhalt eines geschlossenen <details>
|
||||
ueber `content-visibility`, nicht ueber `display`. Die Kaesten
|
||||
behalten dabei eine Groesse -- gemessen von pruef-breiten: Der Knopf
|
||||
"Link erzeugen" lag bei 768 px unter einem Filter-Chip, obwohl der
|
||||
Kasten zugeklappt war. Fuer das Auge unsichtbar, fuer jede Messung
|
||||
(und fuer die Suchfunktion des Browsers) vorhanden. */
|
||||
.k-abo:not([open]) .k-abo__inhalt { display: none; }
|
||||
|
||||
.k-abo__inhalt {
|
||||
padding: 0 16px 16px;
|
||||
border-top: 1px solid var(--rand);
|
||||
padding-top: 14px;
|
||||
}
|
||||
.k-abo__satz {
|
||||
margin: 0 0 14px; font-size: .85rem; line-height: 1.65; color: var(--text-leise);
|
||||
}
|
||||
|
||||
.k-abo__zeile { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
|
||||
.k-abo__feld {
|
||||
flex: 1 1 320px; min-width: 0; height: 42px;
|
||||
padding: 0 12px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: .8rem; color: var(--text-leise);
|
||||
background: rgba(0, 0, 0, .25);
|
||||
border: 1px solid var(--rand); border-radius: var(--radius-klein);
|
||||
}
|
||||
|
||||
.k-abo__stand {
|
||||
margin: 0 0 14px; font-size: .8rem; color: var(--text-still);
|
||||
}
|
||||
/* Solange noch nie abgeholt wurde, ist das eine offene Frage -- kein
|
||||
Fehler, aber auch keine Bestaetigung. */
|
||||
.k-abo__stand[data-warten="ja"] { color: #d9a441; }
|
||||
|
||||
.k-abo__knoepfe { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
|
||||
.k-abo__hilfe {
|
||||
margin: 16px 0 0; padding-top: 14px;
|
||||
border-top: 1px solid var(--rand);
|
||||
display: grid; gap: 8px;
|
||||
font-size: .82rem; line-height: 1.6; color: var(--text-leise);
|
||||
}
|
||||
.k-abo__weg { display: block; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.k-abo__feld { flex: 1 1 100%; }
|
||||
.k-abo__knoepfe > * { flex: 1 1 auto; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.k-abo__kopf, .k-abo__kopf::after { transition: none; }
|
||||
}
|
||||
|
||||
@@ -187,6 +187,21 @@ body.start > main { position: relative; z-index: 1; }
|
||||
|
||||
/* ---------- Kopfleiste ------------------------------------------------ */
|
||||
|
||||
/* DIE AEUSSERE LEISTE BRICHT NICHT UM -- der Schriftzug gibt nach.
|
||||
|
||||
Ein erster Versuch am 06.09.2026 setzte auch hier `flex-wrap: wrap`.
|
||||
Ergebnis, gemessen: Bei 1280 px brauchte die Leiste 1235 px (Marke
|
||||
375 + Bedienelemente 844 + Abstand 16) und hatte 1200 -- also brach
|
||||
sie in zwei Zeilen um, und zwar erst NACHDEM das Skript den
|
||||
Umschalter und den Chat-Knopf eingehaengt hatte. Die ganze Seite
|
||||
sprang dabei 52 px nach unten (CLS 0,94).
|
||||
|
||||
Flexbox zieht Umbrechen dem Schrumpfen vor. Wer hier `wrap` erlaubt,
|
||||
bekommt also eine zweite Zeile, obwohl der Schriftzug daneben
|
||||
bereitwillig kuerzer wuerde -- und ein gekuerzter Schriftzug ist
|
||||
allemal besser als eine Seite, die beim Laden springt. Umgebrochen
|
||||
wird nur noch auf sehr schmalen Geraeten (siehe unten), wo es
|
||||
wirklich keinen anderen Weg gibt. */
|
||||
.kopfleiste {
|
||||
position: sticky; top: 0; z-index: 5;
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
||||
@@ -282,7 +297,41 @@ body.start > main { position: relative; z-index: 1; }
|
||||
.zurueck-knopf:hover .zurueck-knopf__pfeil { transform: none; }
|
||||
}
|
||||
|
||||
.kopfleiste__rechts { display: flex; align-items: center; gap: 12px; flex: 0 1 auto; min-width: 0; }
|
||||
/* UMBRECHEN IST DIE VORGABE, NICHT DIE AUSNAHME (06.09.2026).
|
||||
|
||||
Hier stand `flex-wrap` erst ab 380 px -- und der Kommentar weiter
|
||||
unten erzaehlt selbst, warum das schon einmal schiefging: "Die
|
||||
Rechnung ging genau auf, solange rechts VIER Dinge standen. Mit der
|
||||
Glocke sind es FUENF, und bei 320 px passte es nicht mehr."
|
||||
|
||||
Am 06.09.2026 kam der Chat-Knopf dazu. SECHS Dinge, und die Schwelle
|
||||
blieb bei 380. Ergebnis, von pruef-handy gemessen: Bei 412 px -- der
|
||||
haeufigsten Android-Breite ueberhaupt -- brach nichts um, der
|
||||
Umschalter wurde auf 50 px zusammengedrueckt, sein Knopf behielt
|
||||
seine 104 px und lag damit quer ueber dem Chat-Knopf. Wer auf "Meine
|
||||
Sicht" tippte, landete im Chat. Auf ZWOELF Seiten.
|
||||
|
||||
Die Lehre ist nicht "380 auf 430 erhoehen" -- das waere derselbe
|
||||
Fehler ein drittes Mal, nur mit einer anderen Zahl. Eine feste
|
||||
Schwelle ist eine Rechnung, die jemand einmal aufgestellt hat und die
|
||||
beim naechsten Knopf still falsch wird.
|
||||
|
||||
`flex-wrap: wrap` OHNE Schwelle rechnet nicht, sondern misst: Es
|
||||
bricht genau dann um, wenn der Platz nicht reicht, und sonst nie. Auf
|
||||
dem Rechner aendert sich dadurch nichts. */
|
||||
.kopfleiste__rechts {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
/* NICHT SCHRUMPFEN -- die MARKE gibt nach, nicht die Bedienelemente.
|
||||
Mit `flex: 0 1 auto` wurde dieser Kasten schmaler als sein Inhalt,
|
||||
und der brach dann schon bei 1280 px in eine zweite Zeile um. Das
|
||||
war nicht nur haesslich, es war ein Sprung: Die Leiste wuchs, kaum
|
||||
dass das Skript den Umschalter eingehaengt hatte, und schob die
|
||||
ganze Seite 52 px nach unten (gemessen von pruef-tempo-workspace,
|
||||
CLS 1,0). Der Schriftzug daneben darf gekuerzt werden, ein Knopf
|
||||
nicht. */
|
||||
flex: 0 0 auto; min-width: 0;
|
||||
flex-wrap: wrap; row-gap: 6px; justify-content: flex-end;
|
||||
}
|
||||
.kopfleiste__rechts > * { flex: 0 0 auto; }
|
||||
|
||||
.wer {
|
||||
@@ -599,37 +648,40 @@ body.start > main { position: relative; z-index: 1; }
|
||||
Die ZUORDNUNG ist ebenfalls gerechnet. Das Auge vergleicht nur, was
|
||||
NEBENEINANDER liegt -- gesucht wurde deshalb die Anordnung mit dem
|
||||
größtmöglichen kleinsten Abstand zweier Nachbarn im Raster. Ergebnis:
|
||||
mindestens 105,9 Grad zwischen allen Nachbarn. Zum Vergleich: Der
|
||||
mindestens 120,0 Grad zwischen allen Nachbarn (06.09.2026 mit 21 Kacheln neu gerechnet). Zum Vergleich: Der
|
||||
erste Versuch mit frei gewählten Farben hatte Paare bei 1,4 Grad.
|
||||
|
||||
Unter allen Anordnungen, die das schaffen, wurde die passendste
|
||||
genommen -- LIVE rot, Technik gelb, Reports grün, Personen rot-gold.
|
||||
|
||||
Geprüft gegen genau diesen Hintergrund: Helligkeitsband, Farbstärke
|
||||
und Kontrast bestehen für alle siebzehn.
|
||||
und Kontrast bestehen für alle einundzwanzig.
|
||||
|
||||
Die Töne hängen bewusst NICHT an .kachel, sondern nur am Merkmal
|
||||
data-ton: Dieselbe Nummer färbt die Kachel und die Hinweiszeile, die
|
||||
dorthin führt. Eine Farbe gehört hier zu einem Bereich, nicht zu einem
|
||||
Bauteil. */
|
||||
[data-ton="1"] { --ton: #3e95e7; }
|
||||
[data-ton="2"] { --ton: #6e89eb; }
|
||||
[data-ton="3"] { --ton: #917ee4; }
|
||||
[data-ton="4"] { --ton: #ac74d3; }
|
||||
[data-ton="5"] { --ton: #c26cbb; }
|
||||
[data-ton="6"] { --ton: #d1679d; }
|
||||
[data-ton="7"] { --ton: #da657c; }
|
||||
[data-ton="8"] { --ton: #dc6958; }
|
||||
[data-ton="9"] { --ton: #d67130; }
|
||||
[data-ton="10"] { --ton: #c67f09; }
|
||||
[data-ton="11"] { --ton: #b08c09; }
|
||||
[data-ton="12"] { --ton: #97970e; }
|
||||
[data-ton="13"] { --ton: #72a234; }
|
||||
[data-ton="14"] { --ton: #3ea95e; }
|
||||
[data-ton="15"] { --ton: #0ba985; }
|
||||
[data-ton="16"] { --ton: #08a6a0; }
|
||||
[data-ton="17"] { --ton: #0aa3b6; }
|
||||
[data-ton="18"] { --ton: #0f9ecf; } /* Start-Check */
|
||||
[data-ton="1"] { --ton: #3e95e7; } /* Dashboard */
|
||||
[data-ton="2"] { --ton: #688beb; } /* Zahlen */
|
||||
[data-ton="3"] { --ton: #8881e7; } /* LIVE */
|
||||
[data-ton="4"] { --ton: #a178dc; } /* Chat */
|
||||
[data-ton="5"] { --ton: #b670ca; } /* Schutz */
|
||||
[data-ton="6"] { --ton: #c76ab3; } /* Community */
|
||||
[data-ton="7"] { --ton: #d36699; } /* Reports */
|
||||
[data-ton="8"] { --ton: #da657c; } /* Kalender */
|
||||
[data-ton="9"] { --ton: #dc685d; } /* Bibliothek */
|
||||
[data-ton="10"] { --ton: #d96e3c; } /* Mein Steckbrief */
|
||||
[data-ton="11"] { --ton: #d0780e; } /* Personen */
|
||||
[data-ton="12"] { --ton: #bd8501; } /* Content */
|
||||
[data-ton="13"] { --ton: #a98f0d; } /* Aufgaben */
|
||||
[data-ton="14"] { --ton: #929909; } /* Technik */
|
||||
[data-ton="15"] { --ton: #72a234; } /* Calls */
|
||||
[data-ton="16"] { --ton: #47a858; } /* Start-Check */
|
||||
[data-ton="17"] { --ton: #11aa7a; } /* Agentur */
|
||||
[data-ton="18"] { --ton: #0da795; } /* Automationen */
|
||||
[data-ton="19"] { --ton: #16a4a9; } /* Dateien */
|
||||
[data-ton="20"] { --ton: #01a2bd; } /* Creator-Profile */
|
||||
[data-ton="21"] { --ton: #119dd4; } /* Scout */
|
||||
|
||||
/* Rückfallwert für alles, was einen Ton braucht, aber keine Nummer hat.
|
||||
Steht bewusst in :where() -- damit zählt die Zeile beim Wettstreit der
|
||||
@@ -2781,9 +2833,16 @@ body.start #protokoll {
|
||||
dadurch um 46 px (Rechner) bis 117 px (Handy) aus dem Bild, und der
|
||||
Abmelden-Knopf war nicht mehr erreichbar. Gemeldet von
|
||||
pruef-workspace-seiten und pruef-handy, bevor es jemand gesehen hat.
|
||||
Diese Regel steht spaeter in der Datei und gewinnt deshalb. */
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
Diese Regel steht spaeter in der Datei und gewinnt deshalb.
|
||||
|
||||
NACHTRAG 06.09.2026: Seit die Kopfleiste an JEDER Breite umbrechen
|
||||
darf (siehe .kopfleiste__rechts oben), ist Schrumpfen der falsche
|
||||
Ausweg geworden. Ein Umschalter, der auf 50 px zusammenfaellt,
|
||||
schiebt seinen eigenen Knopf ueber den Nachbarn -- das ist kein
|
||||
Ueberlauf weniger, sondern einer an unsichtbarer Stelle. Er
|
||||
behaelt jetzt seine Breite und wandert stattdessen in die zweite
|
||||
Zeile, wenn es eng wird. */
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
/* DAS WORT "SICHT" IST WEG (06.09.2026, Wunsch Filipe: "das soll viel
|
||||
@@ -2949,7 +3008,7 @@ body[data-fremde-sicht="ja"]::after {
|
||||
zwischen Suche und Glocke um. */
|
||||
.kopfleiste > .marke { flex: 1 0 100%; }
|
||||
.kopfleiste__rechts {
|
||||
width: 100%; justify-content: flex-end;
|
||||
width: 100%;
|
||||
/* AUCH DIE ZWEITE ZEILE DARF UMBRECHEN. Ohne das reichten die
|
||||
294 Pixel weiterhin nicht: Umschalter (104) + Suche (47) +
|
||||
Glocke (37) + Plakette + Abmelden sind zusammen mehr. Die
|
||||
@@ -2959,10 +3018,8 @@ body[data-fremde-sicht="ja"]::after {
|
||||
falsch aussieht. */
|
||||
flex-wrap: wrap; row-gap: 6px;
|
||||
}
|
||||
/* Und der Umschalter behaelt seine Breite, statt gequetscht zu
|
||||
werden: Sein Inhalt kann ohnehin nicht unter 104 px, also waere
|
||||
jedes Schrumpfen nur ein Ueberlauf mit anderem Namen. */
|
||||
.sicht { flex: 0 0 auto; }
|
||||
/* Der Umschalter behaelt seine Breite -- das gilt seit dem 06.09.2026
|
||||
an jeder Breite und steht deshalb oben bei .sicht selbst. */
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
body[data-fremde-sicht="ja"]::after { border-color: Highlight; }
|
||||
|
||||
@@ -309,22 +309,22 @@
|
||||
name: 'Täglich',
|
||||
unter: 'Was du sowieso jeden Tag aufmachst',
|
||||
bereiche: [
|
||||
{ name: 'Dashboard', unter: 'Alle Creator auf einen Blick', zeichen: 'dashboard', ton: 3,
|
||||
{ name: 'Dashboard', unter: 'Alle Creator auf einen Blick', zeichen: 'dashboard', ton: 1,
|
||||
gross: true, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'uebersicht.html', szene: 'showbuehne' },
|
||||
{ name: 'Aufgaben', unter: 'Offen, in Arbeit, Review', zeichen: 'aufgaben', ton: 11,
|
||||
{ name: 'Aufgaben', unter: 'Offen, in Arbeit, Review', zeichen: 'aufgaben', ton: 13,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'aufgaben.html', szene: 'garage' },
|
||||
{ name: 'Kalender', unter: 'Termine und Calls', zeichen: 'kalender', ton: 16,
|
||||
{ name: 'Kalender', unter: 'Termine und Calls', zeichen: 'kalender', ton: 8,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'kalender.html', szene: 'skyline' },
|
||||
{ name: 'Calls & Protokolle', unter: 'Gespräche mit To-dos', zeichen: 'calls', ton: 9,
|
||||
{ name: 'Calls & Protokolle', unter: 'Gespräche mit To-dos', zeichen: 'calls', ton: 15,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'calls.html', szene: 'lounge' },
|
||||
/* CHAT (06.09.2026). Steht neben "Calls & Protokolle", weil
|
||||
beides Gespraech ist -- das eine geplant und protokolliert,
|
||||
das andere zwischendurch. Ton 14 (Gruen), weil die taegliche
|
||||
Gruppe sonst 3, 11, 16, 9 und 2 traegt und sich der Chat
|
||||
davon abheben soll. */
|
||||
{ name: 'Chat', unter: 'Nachrichten mit deinem Team', zeichen: 'chat', ton: 14,
|
||||
{ name: 'Chat', unter: 'Nachrichten mit deinem Team', zeichen: 'chat', ton: 4,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'chat.html', szene: 'lounge' },
|
||||
{ name: 'Dateien', unter: 'Ablage und Freigaben', zeichen: 'dateien', ton: 2,
|
||||
{ name: 'Dateien', unter: 'Ablage und Freigaben', zeichen: 'dateien', ton: 19,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'dateien.html', szene: 'arena' },
|
||||
],
|
||||
},
|
||||
@@ -364,25 +364,25 @@
|
||||
(Orangerot) darunter. Wer diese Gruppe umsortiert, lässt
|
||||
tools/kachel-farben.mjs neu laufen -- die Nachbarschaften
|
||||
haben sich durch diese Kachel geändert. */
|
||||
{ name: 'Zahlen', unter: 'Diamanten, LIVE-Tage, Verweildauer', zeichen: 'zahlen', ton: 4,
|
||||
{ name: 'Zahlen', unter: 'Diamanten, LIVE-Tage, Verweildauer', zeichen: 'zahlen', ton: 2,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'leistung.html', szene: 'arena' },
|
||||
{ name: 'Mein Steckbrief', unter: 'Dein Bild und deine Kanäle', zeichen: 'steckbrief', ton: 15,
|
||||
{ name: 'Mein Steckbrief', unter: 'Dein Bild und deine Kanäle', zeichen: 'steckbrief', ton: 10,
|
||||
rollen: ['admin', 'manager', 'scout'], ziel: 'steckbrief.html', szene: 'halle' },
|
||||
{ name: 'Creator-Profile', unter: 'Ziele und 90-Tage-Plan', zeichen: 'profil', ton: 8,
|
||||
{ name: 'Creator-Profile', unter: 'Ziele und 90-Tage-Plan', zeichen: 'profil', ton: 20,
|
||||
rollen: ['admin', 'manager', 'scout'], ziel: 'profil.html', szene: 'halle' },
|
||||
{ name: 'Mein Profil', unter: 'Dein Steckbrief, Ziele und Pläne', zeichen: 'profil', ton: 8,
|
||||
{ name: 'Mein Profil', unter: 'Dein Steckbrief, Ziele und Pläne', zeichen: 'profil', ton: 20,
|
||||
rollen: ['creator'], ziel: 'profil.html', szene: 'halle' },
|
||||
{ name: 'Start-Check', unter: 'Erstanalyse, 16 Punkte', zeichen: 'startcheck', ton: 18,
|
||||
{ name: 'Start-Check', unter: 'Erstanalyse, 16 Punkte', zeichen: 'startcheck', ton: 16,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'startcheck.html', szene: 'wald' },
|
||||
{ name: 'LIVE-Analyse', unter: 'Vorher, während, danach', zeichen: 'live', ton: 7,
|
||||
{ name: 'LIVE-Analyse', unter: 'Vorher, während, danach', zeichen: 'live', ton: 3,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=live', szene: 'portal' },
|
||||
{ name: 'Content-Ideen', unter: 'Idee bis Upload', zeichen: 'content', ton: 13,
|
||||
{ name: 'Content-Ideen', unter: 'Idee bis Upload', zeichen: 'content', ton: 12,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'content.html', szene: 'portal' },
|
||||
{ name: 'Community', unter: 'Moderation und Konflikte', zeichen: 'community', ton: 6,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=community', szene: 'lounge' },
|
||||
{ name: 'Technik', unter: 'Setup, Fehler, Lösungen', zeichen: 'technik', ton: 12,
|
||||
{ name: 'Technik', unter: 'Setup, Fehler, Lösungen', zeichen: 'technik', ton: 14,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=technik', szene: 'garage' },
|
||||
{ name: 'Schutz & Regeln', unter: 'Richtlinien und Vorfälle', zeichen: 'schutz', ton: 1,
|
||||
{ name: 'Schutz & Regeln', unter: 'Richtlinien und Vorfälle', zeichen: 'schutz', ton: 5,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=schutz', szene: 'halle' },
|
||||
/* DER SECHSTE BEREICH (06.09.2026). Kernprinzip 04 des Konzepts
|
||||
lautet "Die Agentur bleibt angebunden" -- und dafuer gab es
|
||||
@@ -392,9 +392,13 @@
|
||||
die andere Seite des Tisches die Agentur ist.
|
||||
Ton 9 (Orange) -- deutlich von seinen Nachbarn 1 (Blau) und
|
||||
14 (Gruen) entfernt. */
|
||||
{ name: 'Agentur', unter: 'Kampagnen, Schulungen, offizieller Weg', zeichen: 'agentur', ton: 9,
|
||||
/* Untertitel gekuerzt (06.09.2026): "Kampagnen, Schulungen,
|
||||
offizieller Weg" wurde auf dem Handy abgeschnitten -- gemessen
|
||||
von pruef-start-ansicht. Ein Untertitel, der mitten im Wort
|
||||
endet, ist schlechter als ein kuerzerer. */
|
||||
{ name: 'Agentur', unter: 'Kampagnen und offizielle Wege', zeichen: 'agentur', ton: 17,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=agentur', szene: 'halle' },
|
||||
{ name: 'Reports & Review', unter: 'Woche und Entscheidung', zeichen: 'reports', ton: 14,
|
||||
{ name: 'Reports & Review', unter: 'Woche und Entscheidung', zeichen: 'reports', ton: 7,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'report.html', szene: 'showbuehne' },
|
||||
],
|
||||
},
|
||||
@@ -420,16 +424,16 @@
|
||||
anderes sagt als die Software, ist schlimmer als beides
|
||||
einzeln: Man weiss danach nicht mehr, welcher von beiden man
|
||||
glauben soll. */
|
||||
{ name: 'Personen & Zugänge', unter: 'Rollen und Codes', zeichen: 'personen', ton: 10,
|
||||
{ name: 'Personen & Zugänge', unter: 'Rollen und Codes', zeichen: 'personen', ton: 11,
|
||||
rollen: ['admin'], ziel: 'personen.html', szene: 'halle' },
|
||||
{ name: 'Scout-Pipeline', unter: 'Entdeckt bis übergeben', zeichen: 'scouting', ton: 17,
|
||||
{ name: 'Scout-Pipeline', unter: 'Entdeckt bis übergeben', zeichen: 'scouting', ton: 21,
|
||||
rollen: ['admin', 'manager', 'scout'], ziel: 'scouting.html', szene: 'wald' },
|
||||
{ name: 'Wissens-Bibliothek', unter: 'Anleitungen und PDFs', zeichen: 'wissen', ton: 4,
|
||||
{ name: 'Wissens-Bibliothek', unter: 'Anleitungen und PDFs', zeichen: 'wissen', ton: 9,
|
||||
rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'wissen.html', szene: 'arena' },
|
||||
/* Ebenfalls nur DogFather (02.09.2026). Was von allein passiert,
|
||||
legt fest, was ALLEN anderen automatisch zugeschoben wird --
|
||||
das gehört in dieselbe Hand wie die Rollenvergabe. */
|
||||
{ name: 'Automationen', unter: 'Was von allein passiert', zeichen: 'automation', ton: 5,
|
||||
{ name: 'Automationen', unter: 'Was von allein passiert', zeichen: 'automation', ton: 18,
|
||||
rollen: ['admin'], ziel: 'automation.html', szene: 'garage' },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/* ===================================================================
|
||||
kalender-abo.js — den eigenen Kalender abonnierbar machen.
|
||||
|
||||
Eine eigene Datei und nicht ein Anhängsel in kalender.js: Der
|
||||
Kalender selbst ist die größte Datei der Anwendung, und diese Sache
|
||||
hier hat mit dem Zeichnen von Monaten nichts zu tun. Fällt sie aus,
|
||||
steht der Kalender trotzdem.
|
||||
|
||||
Der Link wird NICHT beim Öffnen der Seite angelegt. Ein Geheimnis,
|
||||
das entsteht, ohne dass jemand es wollte, liegt danach herum -- und
|
||||
niemand weiß, dass es existiert. Angelegt wird es beim Klick.
|
||||
=================================================================== */
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
const $ = (id) => document.getElementById(id);
|
||||
if (!$('abo-block')) return; // Seite ohne diesen Block
|
||||
|
||||
const melde = (t) => { $('abo-fehler').textContent = t || ''; };
|
||||
|
||||
async function hole(weg, einst) {
|
||||
const a = await fetch(weg, { credentials: 'same-origin', ...einst });
|
||||
if (a.status === 401) { location.assign('/workspace/'); throw new Error('weg'); }
|
||||
return a;
|
||||
}
|
||||
|
||||
function zeige(daten) {
|
||||
const hat = !!daten?.link;
|
||||
$('abo-hat').hidden = !hat;
|
||||
$('abo-hilfe').hidden = !hat;
|
||||
$('abo-erzeugen').hidden = hat;
|
||||
$('abo-neu').hidden = !hat;
|
||||
$('abo-aus').hidden = !hat;
|
||||
if (hat) $('abo-link').value = daten.link;
|
||||
|
||||
/* WANN ZULETZT ABGEHOLT -- die einzige Auskunft, die wirklich sagt,
|
||||
ob die Sache funktioniert. "Link erzeugt" beweist nur, dass ein
|
||||
Link da ist; erst der Abruf beweist, dass ein Kalender ihn auch
|
||||
benutzt. Ohne diese Zeile sucht man bei "kommt nicht an" an der
|
||||
falschen Stelle. */
|
||||
const stand = $('abo-stand');
|
||||
if (!hat) { stand.textContent = ''; return; }
|
||||
if (!daten.zuletzt) {
|
||||
stand.textContent = 'Noch nie abgerufen – sobald dein Kalender ihn holt, steht es hier.';
|
||||
stand.dataset.warten = 'ja';
|
||||
return;
|
||||
}
|
||||
delete stand.dataset.warten;
|
||||
const d = new Date(daten.zuletzt);
|
||||
const her = Math.round((Date.now() - d.getTime()) / 60000);
|
||||
const wann = her < 2 ? 'gerade eben'
|
||||
: her < 60 ? `vor ${her} Minuten`
|
||||
: her < 60 * 48 ? `vor ${Math.round(her / 60)} Stunden`
|
||||
: `am ${d.toLocaleDateString('de-DE')}`;
|
||||
stand.textContent = `Dein Kalender hat die Termine zuletzt ${wann} abgeholt.`;
|
||||
}
|
||||
|
||||
async function laden() {
|
||||
try {
|
||||
const a = await hole('/workspace/api/ics');
|
||||
if (!a.ok) return;
|
||||
zeige(await a.json());
|
||||
} catch { /* umgeleitet */ }
|
||||
}
|
||||
|
||||
async function erzeugen(erneuern) {
|
||||
if (erneuern && !window.confirm(
|
||||
'Neuen Link erzeugen?\n\nDer alte hört im selben Moment auf zu funktionieren. '
|
||||
+ 'Ein Kalender, der ihn abonniert hat, bekommt danach keine Termine mehr, '
|
||||
+ 'bis du ihm den neuen gibst.')) return;
|
||||
melde('');
|
||||
try {
|
||||
const a = await hole('/workspace/api/ics', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}',
|
||||
});
|
||||
if (!a.ok) { melde('Ging gerade nicht. Bitte später noch einmal.'); return; }
|
||||
const d = await a.json();
|
||||
zeige({ link: d.link, zuletzt: null });
|
||||
/* Nach dem Erzeugen die Anleitung aufklappen -- wer gerade einen
|
||||
Link bekommen hat, will als Nächstes wissen, wohin damit. */
|
||||
$('abo-hilfe').hidden = false;
|
||||
} catch { /* umgeleitet */ }
|
||||
}
|
||||
|
||||
async function abschalten() {
|
||||
if (!window.confirm(
|
||||
'Abschalten?\n\nDeine Termine verschwinden dann aus jedem Kalender, '
|
||||
+ 'der sie abonniert hat.')) return;
|
||||
melde('');
|
||||
try {
|
||||
const a = await hole('/workspace/api/ics', { method: 'DELETE' });
|
||||
if (!a.ok) { melde('Ging gerade nicht.'); return; }
|
||||
zeige(null);
|
||||
} catch { /* umgeleitet */ }
|
||||
}
|
||||
|
||||
async function kopieren() {
|
||||
const feld = $('abo-link');
|
||||
const knopf = $('abo-kopieren');
|
||||
try {
|
||||
await navigator.clipboard.writeText(feld.value);
|
||||
} catch {
|
||||
/* Ohne Zwischenablage-Recht (unsicherer Kontext, alter Browser)
|
||||
wenigstens markieren -- dann reicht Strg+C. Wortlos scheitern
|
||||
wäre das Schlechteste: Man klickt dreimal und weiß nicht, ob
|
||||
etwas passiert ist. */
|
||||
feld.focus();
|
||||
feld.select();
|
||||
knopf.textContent = 'Jetzt Strg+C';
|
||||
setTimeout(() => { knopf.textContent = 'Kopieren'; }, 3000);
|
||||
return;
|
||||
}
|
||||
knopf.textContent = 'Kopiert';
|
||||
setTimeout(() => { knopf.textContent = 'Kopieren'; }, 2000);
|
||||
}
|
||||
|
||||
$('abo-erzeugen').addEventListener('click', () => erzeugen(false));
|
||||
$('abo-neu').addEventListener('click', () => erzeugen(true));
|
||||
$('abo-aus').addEventListener('click', abschalten);
|
||||
$('abo-kopieren').addEventListener('click', kopieren);
|
||||
/* Erst laden, wenn jemand aufklappt -- die Seite hat beim Öffnen
|
||||
Wichtigeres zu tun, und die meisten klappen nie auf. */
|
||||
$('abo-block').addEventListener('toggle', () => {
|
||||
if ($('abo-block').open) laden();
|
||||
}, { once: true });
|
||||
})();
|
||||
@@ -529,8 +529,23 @@
|
||||
const strom = new EventSource('/workspace/api/chat/strom');
|
||||
strom.addEventListener('neu', holen);
|
||||
/* Nach einem Abriss verbindet der Browser selbst neu -- dann
|
||||
einmal nachzählen, was inzwischen kam. */
|
||||
strom.addEventListener('open', holen);
|
||||
einmal nachzählen, was inzwischen kam.
|
||||
|
||||
NUR AB DEM ZWEITEN MAL (06.09.2026). Hier stand schlicht
|
||||
`strom.addEventListener('open', holen)` -- und damit fragte
|
||||
JEDE Seite die Zahl zweimal ab: einmal direkt beim Laden und
|
||||
einmal Millisekunden später, sobald der Strom aufging.
|
||||
Gemessen von pruef-tempo-workspace auf fünf Seiten.
|
||||
|
||||
Zwei Anfragen für dieselbe Zahl sind kein Fehler, den jemand
|
||||
bemerkt -- und genau deshalb bleibt so etwas jahrelang
|
||||
stehen. Bei zehn Angemeldeten ist es die doppelte Last für
|
||||
nichts. */
|
||||
let schonOffen = false;
|
||||
strom.addEventListener('open', () => {
|
||||
if (schonOffen) holen();
|
||||
schonOffen = true;
|
||||
});
|
||||
} catch { /* dann eben nur beim Laden */ }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user