Willkommen: eine Seite, die erklaert, was es hier gibt

Auftrag vom 21.09.2026, Abschnitte 7 und 8. Aus der Kachel "Der Treff"
wird die Willkommensseite.

WARUM DAS NICHTS VERLIERT, gemessen am echten Bestand: Das Brett
"treff" hatte NULL Eintraege, der Treff-Chat daneben 29 Nachrichten.
Geredet wird im Chat; das Brett war eine Kachel ohne Inhalt.

DIE SEITE ERKLAERT NICHT "DIE WEBSITE", SONDERN DEINE. Sie baut sich
aus derselben Kachelliste wie die Startseite, durch denselben Filter
(darfSeite). Wer morgen eine Kachel bekommt, bekommt automatisch auch
ihre Erklaerung -- und wer eine nicht hat, liest nicht davon. Eine
Liste von Dingen, die man nicht darf, ist keine Orientierung.

Gemessen: DogFather 30 Kacheln, Modi 25, Community 11 -- und keine
einzige davon ist fuer den Betreffenden gesperrt.

DREI FEHLER, DIE ERST DAS BILDSCHIRMFOTO GEZEIGT HAT:

  body class="gate" ist die ANMELDEWAND (display:flex, zentriert).
  Kopfleiste und Inhalt standen dadurch NEBENEINANDER -- am Handy
  blieben dem Text 260 von 390 Pixeln.

  .huelle gibt es im Haus gar nicht. Der Inhalt stand linksbuendig
  statt mittig, ohne Sicherheitsrand an randlosen Geraeten. Jetzt
  .inhalt wie jede andere Seite.

  Der Text stand direkt auf dem Buehnenbild: Kontrast 1.68:1 am
  Rechner, 2.23:1 am Handy (noetig 4.5). Die Hausregel stand daneben
  in start.css -- "der Text steht auf eigenen Flaechen" -- und diese
  Seite hielt sich als einzige nicht daran. Jetzt 5.19 und 7.73.

UND EIN LECK, ZUM DRITTEN MAL DIESELBE URSACHE:

Die Kachel umzuleiten nahm dem Brett "treff" die Zugehoerigkeit --
TREFF_BRETTER wird aus Kachelzielen abgeleitet. Die Schranke laesst
eine AGENTURROLLE unbesehen durch, wenn ein Brett in keiner Liste
steht: Eine Creatorin konnte das Brett des Treffs lesen.

Behoben, und diesmal dauerhaft laut gemacht: Nachgemessen ist die
Trennung exakt und in beide Richtungen deckungsgleich -- die Bretter
mit `fuerAlle` sind genau die des Treffs plus die Agenturablage. Das
ist eine Eigenschaft des BRETTS und wandert nicht, wenn jemand eine
Kachel umleitet. pruef-treff prueft das jetzt.

Mein erster Entwurf der Regel war zu breit und meldete `content` und
`schutz` -- beides Agenturbretter, die eine Agenturrolle zu Recht
liest. Eine Pruefung, die zu viel meldet, wird abgeschaltet.

AUSSERDEM: 16 Kennungen aus workspace-zuteilung.js hatten keinen
deutschen Satz -- auf dem Bildschirm haette woertlich "nicht_zugeteilt"
gestanden. Gefunden von pruef-meldungen am selben Tag.

Und zwei Erwartungen in pruef-treff abgeleitet statt gezaehlt: die
Zahl der Rollenkacheln auf der Wand (die Anmeldung sucht WHERE rolle=?,
eine Rolle ohne Kachel ist unerreichbar) und "die breite Kachel steht
vorne" ueber die Eigenschaft statt ueber den Namen.

Geprueft: pruef-willkommen 66/0 (neu), pruef-treff 80/0 (war 59 ok /
13 FEHL), pruef-buehne fuer die neue Seite 10/0, pruef-meldungen 8/0,
pruef-css-klassen 30/0, pruef-rechtetafel 19/0.
This commit is contained in:
2026-09-22 00:00:14 +02:00
parent b506c7f533
commit 66789aabcd
48 changed files with 1853 additions and 519 deletions
+270
View File
@@ -0,0 +1,270 @@
/* =====================================================================
willkommen.css — die persoenliche Einfuehrung
=====================================================================
Auftrag vom 21.09.2026, Abschnitte 7 und 8.
DIE SEITE IST EIN TEXT, KEIN ARMATURENBRETT. Wer sie oeffnet, ist
meist zum ersten Mal hier und weiss noch gar nicht, wonach er sucht.
Deshalb: eine Spalte, grosszuegige Zeilenhoehe, und die Kacheln
darunter als Liste mit Erklaerung -- nicht als zweites Kachelraster,
das wie die Startseite aussieht und doch etwas anderes tut.
Alle Farben kommen aus den Hausvariablen. Eine eigene Palette hier
waere die, die beim naechsten Farbwechsel stehen bleibt.
===================================================================== */
/* KEIN EIGENER BEHAELTER (berichtigt 21.09.2026).
Hier stand `.huelle { max-width: 54rem }` -- eine Klasse, die es im
ganzen Haus nicht gibt. Der Inhalt stand dadurch linksbuendig statt
mittig, ohne Sicherheitsrand an randlosen Geraeten. Die Seite nimmt
jetzt `.inhalt` wie jede andere; `befinden.html` ist das Vorbild,
weil sie ebenfalls eine Lese- und keine Rasterseite ist. */
/* ---------------------------------------------------------------------
DIE BEGRUESSUNG
---------------------------------------------------------------------
Der Name ist das Erste, was ein Mensch auf einer Seite sucht, die
"Willkommen" heisst. Er steht deshalb gross und allein. */
/* AUF EIGENER FLAECHE, NICHT AUF DEM BILD (berichtigt 21.09.2026).
------------------------------------------------------------------
Gemessen mit pruef-buehne: Kontrast 1.68:1 am Rechner, 2.23:1 am
Handy -- noetig sind 4.5. Der Text stand direkt auf dem
Buehnenbild, und dort ist ein heller Hund.
Die Hausregel steht in start.css neben dem Schleier und sagt es
schon: "Der Text braucht das nicht mehr -- er steht auf eigenen
Flaechen." Genau daran hielt sich diese Seite als einzige nicht. */
.w-gruss {
margin: 6px 0 28px;
padding: 20px 22px;
border: 1px solid var(--rand);
border-radius: var(--radius, 16px);
background: color-mix(in srgb, var(--flaeche) 94%, transparent);
}
.w-gruss__titel {
margin: 0;
font-size: clamp(1.5rem, 5.5vw, 2.1rem);
line-height: 1.22;
letter-spacing: -.01em;
}
/* Die Rolle als ruhige Marke darunter -- sie ordnet ein, ohne zu
ranghoeheln. Kein Grossbuchstaben-Schild, das wie ein Ausweis
aussieht. */
.w-gruss__rolle {
display: inline-block;
margin: 10px 0 0;
padding: 5px 12px;
border: 1px solid var(--rand);
border-radius: 999px;
background: color-mix(in srgb, var(--akzent) 10%, transparent);
color: var(--text-leise);
font-size: .84rem;
}
.w-gruss__was {
margin: 16px 0 0;
font-size: 1.08rem;
line-height: 1.5;
color: var(--text);
}
.w-gruss__dazu {
margin: 8px 0 0;
font-size: .96rem;
line-height: 1.62;
color: var(--text-leise);
max-width: 46rem;
}
/* ---------------------------------------------------------------------
DIE HINWEISE ZUR ROLLE
---------------------------------------------------------------------
Ein paar Saetze, die zu keiner einzelnen Kachel gehoeren. Sie stehen
VOR den Kacheln, weil sie erklaeren, wie man mit ihnen umgeht. */
.w-hinweise {
display: grid;
gap: 10px;
margin: 0 0 30px;
}
.w-hinweis {
padding: 14px 16px;
border: 1px solid var(--rand);
border-left: 3px solid color-mix(in srgb, var(--akzent) 60%, transparent);
border-radius: var(--radius-klein, 12px);
background: color-mix(in srgb, var(--flaeche) 90%, transparent);
}
.w-hinweis__titel {
margin: 0 0 5px;
font-size: .95rem;
font-weight: 600;
}
.w-hinweis__text {
margin: 0;
font-size: .9rem;
line-height: 1.58;
color: var(--text-leise);
}
/* ---------------------------------------------------------------------
WAS ES HIER GIBT
--------------------------------------------------------------------- */
/* Auch die Ueberschrift "Was es hier gibt" und ihr Satz standen frei
auf dem Bild -- sie waren die schlechteste gemessene Stelle. */
.w-bereiche {
padding: 16px 18px;
border: 1px solid var(--rand);
border-radius: var(--radius, 16px);
background: color-mix(in srgb, var(--flaeche) 90%, transparent);
}
.w-bereiche__titel {
margin: 0 0 4px;
font-size: 1.15rem;
}
.w-bereiche__satz {
margin: 0 0 20px;
font-size: .9rem;
color: var(--text-leise);
}
.w-gruppe { margin: 0 0 26px; }
.w-gruppe__titel {
margin: 0 0 10px;
font-size: .78rem;
font-weight: 600;
letter-spacing: .09em;
text-transform: uppercase;
color: var(--text-still);
}
/* EINE SPALTE, NICHT ZWEI. Zwei Spalten haetten hier eine Zeile je
Kachel erzwungen -- und genau die Erklaerung weggekuerzt, wegen der
es die Seite gibt. Ab Tablet duerfen es zwei sein, weil dann beide
Spalten breit genug fuer den langen Satz bleiben. */
.w-raster {
display: grid;
gap: 10px;
grid-template-columns: 1fr;
}
@media (min-width: 900px) {
.w-raster { grid-template-columns: 1fr 1fr; }
}
.w-kachel {
display: flex;
gap: 13px;
align-items: flex-start;
min-height: 44px;
padding: 14px 15px;
border: 1px solid var(--rand);
border-radius: var(--radius-klein, 12px);
background: color-mix(in srgb, var(--flaeche) 88%, transparent);
color: inherit;
text-decoration: none;
transition: border-color var(--tempo, .18s) ease,
background-color var(--tempo, .18s) ease,
transform var(--tempo, .18s) ease;
}
.w-kachel:hover,
.w-kachel:focus-visible {
border-color: color-mix(in srgb, var(--akzent) 55%, var(--rand));
background: color-mix(in srgb, var(--flaeche) 100%, transparent);
transform: translateY(-1px);
}
.w-kachel__zeichen {
flex: 0 0 auto;
display: grid;
place-items: center;
width: 34px;
height: 34px;
border-radius: 10px;
background: color-mix(in srgb, var(--akzent) 14%, transparent);
color: var(--akzent);
}
.w-kachel__zeichen svg {
width: 19px;
height: 19px;
stroke: currentColor;
fill: none;
}
.w-kachel__text {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0; /* sonst sprengt ein langes Wort die Spalte */
}
.w-kachel__name {
font-size: .99rem;
font-weight: 600;
line-height: 1.3;
}
.w-kachel__unter {
font-size: .86rem;
color: var(--text-leise);
line-height: 1.4;
}
/* Der lange Satz ist bewusst etwas kleiner und ruhiger: Er ist die
Vertiefung, nicht die Ueberschrift. */
.w-kachel__lang {
margin-top: 5px;
font-size: .85rem;
line-height: 1.56;
color: var(--text-still);
}
/* ---------------------------------------------------------------------
DIE ZUSTAENDE
--------------------------------------------------------------------- */
.w-laedt,
.w-leer {
margin: 18px 0;
color: var(--text-leise);
font-size: .92rem;
line-height: 1.55;
}
/* ---------------------------------------------------------------------
KONTRASTMODUS UND BEWEGUNGSARMUT
---------------------------------------------------------------------
Im Kontrastmodus werden Fuellfarben ersetzt -- eine Kachel, die sich
nur durch ihren Hintergrund abhebt, verschwindet dort. Deshalb bekommt
sie eine echte Kante, die stehen bleibt. */
@media (forced-colors: active) {
.w-kachel,
.w-hinweis {
border: 1px solid CanvasText;
}
.w-kachel:hover,
.w-kachel:focus-visible {
outline: 2px solid Highlight;
outline-offset: 2px;
}
.w-kachel__zeichen {
border: 1px solid CanvasText;
}
}
@media (prefers-reduced-motion: reduce) {
.w-kachel { transition: none; }
.w-kachel:hover,
.w-kachel:focus-visible { transform: none; }
}
+27
View File
@@ -131,6 +131,33 @@
zurueckgenommen: 'Die Nachricht wurde zurückgenommen.',
nichts_zu_aendern: 'Es gab nichts zu ändern – es steht schon so da.',
nichts_zu_speichern:'Es gab nichts zu speichern.',
/* ---- AUFGABEN VERTEILEN (21.09.2026) -------------------------
Sechzehn Kennungen kamen mit workspace-zuteilung.js dazu, und
keine hatte einen Satz -- `pruef-meldungen` hat sie am selben
Tag gefunden. Ohne sie haette auf dem Bildschirm woertlich
"nicht_zugeteilt" gestanden.
DIE SAETZE SAGEN, WAS JETZT GEHT, nicht nur was schiefging.
"Das ging nicht" schickt einen Menschen zurueck an den Anfang;
"da war jemand schneller -- sie ist schon vergeben" sagt ihm,
dass er nichts falsch gemacht hat. */
ungueltige_aufgabe: 'Diese Aufgabe kennt das System nicht. Lade die Seite neu.',
keine_person: 'Da war niemand ausgewählt. Such eine Person aus und schick es noch einmal ab.',
gibt_es_nicht: 'Das gibt es nicht mehr. Lade die Seite neu.',
nicht_zugeteilt: 'Diese Aufgabe ist nicht dir zugeteilt – deshalb kannst du sie hier nicht annehmen.',
schon_erledigt: 'Die Aufgabe ist schon erledigt. Da ist nichts mehr zu tun.',
grund_fehlt: 'Schreib bitte kurz dazu, warum – ein Satz reicht.',
grund_zu_lang: 'Der Grund ist zu lang. Ein, zwei Sätze genügen.',
kein_pool: 'Diese Aufgabe ist niemandem zum Übernehmen freigegeben.',
schon_vergeben: 'Da war jemand schneller – die Aufgabe ist schon übernommen.',
ungueltiger_zustand:'Dieser Schritt passt gerade nicht. Lade die Seite neu, dann siehst du den aktuellen Stand.',
abgelehnt: 'Du hast diese Aufgabe abgelehnt. Wenn du sie doch machen willst, sag kurz Bescheid.',
darf_nicht_bewerten:'Rückmeldung geben dürfen nur DogFather und die Hände.',
ungueltige_bewertung:'Diese Rückmeldung kennt das System nicht. Lade die Seite neu.',
text_fehlt: 'Zu „Verbesserungsmöglichkeiten" gehört ein Satz – sonst weiß niemand, was gemeint ist.',
text_zu_lang: 'Der Text ist zu lang. Kürze ihn etwas.',
noch_nicht_erledigt:'Rückmeldung gibt es erst, wenn die Aufgabe fertig ist.',
eintrag_nicht_gefunden: 'Den Eintrag gibt es nicht mehr. Lade die Seite neu.',
aufgabe_nicht_gefunden: 'Die Aufgabe gibt es nicht mehr. Lade die Seite neu.',
keine_fassung: 'Dazu gibt es noch keine gespeicherte Fassung.',
+131
View File
@@ -0,0 +1,131 @@
/* =====================================================================
willkommen.js — die persönliche Einführung
=====================================================================
Auftrag vom 21.09.2026, Abschnitte 7 und 8.
DIESE DATEI ENTHÄLT KEINEN EINZIGEN ROLLENNAMEN und keine Liste von
Kacheln. Beides kommt vom Server, der die Rechte ohnehin kennt.
Stünde es hier, stünde es in einer Datei, die jeder herunterlädt —
und wäre am Tag nach der nächsten Änderung falsch.
Die Seite ist damit für jede Rolle dieselbe Datei und trotzdem für
jeden ein anderer Text. Genau das verlangt Abschnitt 8.
===================================================================== */
(() => {
'use strict';
const $ = (id) => document.getElementById(id);
const el = (was, klasse, text) => {
const e = document.createElement(was);
if (klasse) e.className = klasse;
if (text !== undefined) e.textContent = text;
return e;
};
/* Ein Zeichen aus dem Vorrat der Startseite, damit dieselbe Kachel
hier dasselbe Bild trägt wie dort. Fehlt es, bleibt die Stelle
leer -- lieber kein Zeichen als ein falsches. */
function zeichen(name) {
if (!name || !window.Bereiche || !window.Bereiche.zeichenBauen) return null;
try { return window.Bereiche.zeichenBauen(name); } catch (e) { return null; }
}
function kachelBauen(k) {
const a = el('a', 'w-kachel');
a.href = k.ziel;
const z = zeichen(k.zeichen);
if (z) {
const huelle = el('span', 'w-kachel__zeichen');
huelle.setAttribute('aria-hidden', 'true');
huelle.append(z);
a.append(huelle);
}
const text = el('span', 'w-kachel__text');
text.append(el('span', 'w-kachel__name', k.name));
if (k.unter) text.append(el('span', 'w-kachel__unter', k.unter));
/* Der lange Satz nur, wo es einen gibt. Ein leerer Absatz unter
jeder Kachel wäre ein Raster aus Lücken. */
if (k.lang) text.append(el('span', 'w-kachel__lang', k.lang));
a.append(text);
return a;
}
async function bauen() {
const laedt = $('laedt');
try {
const antwort = await fetch('/workspace/api/willkommen');
if (!antwort.ok) {
/* DER DRITTE AUSGANG: nicht "nichts da", sondern "konnte nicht
nachsehen" -- mit einem Weg zurück. */
window.ladefehler?.({ nochmal: bauen });
if (laedt) laedt.hidden = true;
$('haupt')?.removeAttribute('aria-busy');
return;
}
const d = await antwort.json();
$('gruss').textContent = d.gruss || 'Willkommen!';
document.title = `Willkommen, ${d.name} · Team Dogi`;
if (d.rollentext) {
const r = $('rollentext');
r.textContent = d.rollentext;
r.hidden = false;
}
$('einleitung-was').textContent = d.einleitung?.was || '';
$('einleitung-dazu').textContent = d.einleitung?.dazu || '';
/* ---- Die Hinweise zur Rolle ---- */
const hk = $('hinweise');
const hinweise = d.hinweise || [];
if (hinweise.length) {
hk.replaceChildren();
for (const h of hinweise) {
const k = el('div', 'w-hinweis');
k.append(el('h2', 'w-hinweis__titel', h.titel));
k.append(el('p', 'w-hinweis__text', h.text));
hk.append(k);
}
hk.hidden = false;
}
/* ---- Die Kacheln, nach Gruppen ---- */
const ziel = $('gruppen');
ziel.replaceChildren();
const gruppen = d.gruppen || [];
if (!gruppen.length) {
/* KEIN LEERER BILDSCHIRM. Das kommt vor, wenn jemand gerade
erst angelegt wurde -- dann ist das kein Fehler, sondern ein
Zustand, und er gehört erklärt. */
ziel.append(el('p', 'w-leer',
'Für dich ist im Moment noch nichts freigeschaltet. '
+ 'Sag DogFather Bescheid — das ist schnell gemacht.'));
}
for (const g of gruppen) {
const abschnitt = el('section', 'w-gruppe');
abschnitt.append(el('h3', 'w-gruppe__titel', g.name));
const raster = el('div', 'w-raster');
for (const k of (g.kacheln || [])) raster.append(kachelBauen(k));
abschnitt.append(raster);
ziel.append(abschnitt);
}
const satz = $('bereiche-satz');
if (satz && d.kachelzahl) {
satz.textContent = d.kachelzahl + (d.kachelzahl === 1
? ' Bereich ist für dich freigeschaltet — und wofür er da ist. '
: ' Bereiche sind für dich freigeschaltet — und wofür sie da sind. ')
+ 'Tipp einen an, um hinzugehen.';
}
if (laedt) laedt.hidden = true;
$('haupt')?.removeAttribute('aria-busy');
} catch (f) {
window.ladefehler?.({ nochmal: bauen });
if (laedt) laedt.hidden = true;
$('haupt')?.removeAttribute('aria-busy');
}
}
bauen();
})();