Kalender: sechs neue Terminarten, mit zwei stillen Loechern darin
Wunsch: "kategorien wie bigmatch, turniere, Special-Live ... informier
dich was man da alles noch gebrauchen koennte und auch so dass wenn man
die sachen aussucht die ganze kachel und sachen die man eintippen muss
auch zu der jeweiligen kategorie passen."
Neu: BigMatch, Turnier, Special-Live, Collab, Raid-Train, Charity --
neben den drei internen Arten. Das Formular fragt je Art anderes:
beim BigMatch "Gegen wen?" mit 60 Minuten, beim Turnier "Welches
Turnier?" mit 120, bei Charity "Fuer wen wird gesammelt?" mit 180.
ZWEI FEHLER, DIE BEIDE NICHT ABGESTUERZT WAEREN:
1. termin_serien wurde nicht umgestellt. Die Umbauschleife laeuft ueber
zwei Tabellen, bildete den Namen der Sicherungsdatei aber ohne die
Tabelle -- und der Zeitstempel darin wird einmal pro Serverstart
gebildet. Der zweite Durchlauf wollte also dieselbe Datei anlegen,
VACUUM INTO weigerte sich, und das (richtige) "ohne Sicherung kein
Umbau" beendete die ganze Schleife. Ergebnis: termine umgestellt,
termin_serien nicht. Eine wiederkehrende BigMatch-Reihe waere ohne
erkennbaren Grund abgelehnt worden.
2. Die neuen Arten waren im Kalender UNSICHTBAR. In kalender.js standen
zwei weitere Aufzaehlungen derselben Arten: `zeigen = {call, termin,
review, frist}` und die Schalterleiste. Gefiltert wird mit
`zeigen[e.art]` -- fuer 'bigmatch' ist das undefined. Anlegen ging,
der Server meldete 201, die Zeile stand in der Datenbank, und im
Kalender war sie in keiner Ansicht zu sehen. Ohne Fehler, ohne Hinweis.
Beide Listen werden jetzt aus ARTNAME abgeleitet. Und `sichtbare()`
prueft `!== false` statt auf Wahrheit: Der Vorgabewert einer
Sichtbarkeitsfrage muss "sichtbar" sein -- ein Eintrag zu viel ist
ein Schoenheitsfehler, ein fehlender ein verpasster Termin.
Gefunden hat Nummer 2 kein Test, sondern ein Bildschirmfoto: In der
Schalterleiste standen vier Arten statt zehn. Meine eigene Pruefung war
zu dem Zeitpunkt gruen -- sie hoerte beim HTTP 201 auf.
Neu: server/pruef-arten.mjs, 28 Pruefungen. Baut eine Datenbank im ALTEN
Stand nach (samt Teilnehmer, Wecker, Serie), laesst die Anwendung
darueberlaufen und zaehlt nach; haelt CHECK und Serverliste gegeneinander;
und oeffnet zuletzt einen echten Browser, um zu sehen, ob die Eintraege
auch ankommen. Gegenprobe gefahren: mit dem alten Stand meldet sie
0 von 6 sichtbar.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
/* DIE TERMINARTEN — und der Umbau, der sie freischaltet.
|
||||
|
||||
Wunsch Filipe (08.09.2026): *"da sollen auch noch kategorien wie
|
||||
bigmatch, turniere, special-live ... informier dich, was man da alles
|
||||
noch gebrauchen koennte."*
|
||||
|
||||
---------------------------------------------------------------------
|
||||
WARUM DAS EINE EIGENE PRUEFUNG BRAUCHT
|
||||
|
||||
Eine Terminart ist ein erlaubter Wert in einer Spalte, und der steckt
|
||||
in einem CHECK. Ein CHECK laesst sich in SQLite nicht aendern -- die
|
||||
Tabelle muss neu gebaut werden, und zwar ZWEI davon (termine und
|
||||
termin_serien). An `termine` haengen Teilnehmer, Wecker, Protokolle
|
||||
und Serien.
|
||||
|
||||
Der gefaehrliche Ausgang ist nicht der Absturz, sondern der STILLE
|
||||
Fehlschlag: Ein `replace`, das nichts findet, gibt den Text
|
||||
unveraendert zurueck. Die Umstellung liefe durch, meldete Erfolg --
|
||||
und die Datenbank lehnte beim ersten BigMatch trotzdem ab. Genau
|
||||
dieser Fehler ist bei der Rolle 'spicy' passiert, gleich zweimal.
|
||||
|
||||
Deshalb wird hier nicht "im Prinzip" geprueft, sondern der Weg
|
||||
WIRKLICH GEGANGEN: Die Pruefung baut eine Datenbank im alten Stand
|
||||
nach, fuellt sie mit Terminen samt abhaengigen Zeilen, laesst die
|
||||
echte Anwendung darueberlaufen und zaehlt danach nach.
|
||||
|
||||
UND DIE ZWEI LISTEN. Die erlaubten Arten stehen an zwei Stellen: im
|
||||
CHECK (workspace.js) und in der Pruefliste des Servers
|
||||
(workspace-kalender.js). Zwei Listen fuer dieselbe Frage laufen
|
||||
auseinander -- hier werden sie gegeneinander gehalten.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
ES WAREN VIER LISTEN, NICHT ZWEI (08.09.2026, nachgetragen)
|
||||
|
||||
Die erste Fassung dieser Pruefung endete beim "HTTP 201": Der Server
|
||||
nimmt die neue Art an, also ist sie da. Sie war grün. Und sie hat den
|
||||
schwersten Fehler des Tages nicht gesehen.
|
||||
|
||||
In kalender.js standen ZWEI WEITERE Aufzaehlungen derselben Arten:
|
||||
`zeigen = { call, termin, review, frist }` und die Schalterleiste
|
||||
`['call','termin','review','frist']`. Beide blieben stehen. Gefiltert
|
||||
wurde mit `zeigen[e.art]` -- fuer 'bigmatch' ist das `undefined`.
|
||||
|
||||
Also: anlegen ging, speichern ging, der Server meldete Erfolg, die
|
||||
Zeile stand in der Datenbank -- und im Kalender war der Eintrag in
|
||||
KEINER Ansicht zu sehen. Kein Fehler, keine Meldung, nichts. Filipe
|
||||
haette sein erstes BigMatch eingetragen und eine leere Seite gesehen.
|
||||
|
||||
Gefunden hat es kein Test, sondern ein BILDSCHIRMFOTO: In der
|
||||
Schalterleiste standen vier Arten statt zehn.
|
||||
|
||||
Deshalb hoert diese Pruefung jetzt nicht mehr beim Serverstatus auf.
|
||||
Sie legt je Art einen Termin an, oeffnet den Kalender in einem echten
|
||||
Browser und ZAEHLT NACH, ob er dort steht. Ein Server-OK ist keine
|
||||
Sichtbarkeit.
|
||||
|
||||
Aufruf: node server/pruef-arten.mjs */
|
||||
|
||||
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { spawn } from "node:child_process";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { scryptSync, randomBytes } from "node:crypto";
|
||||
import { notbremse } from "./helfer-notbremse.mjs";
|
||||
|
||||
notbremse(300_000, "die Artenpruefung");
|
||||
|
||||
const HIER = dirname(fileURLToPath(import.meta.url));
|
||||
const { portMussFreiSein } = await import("./helfer-port.mjs");
|
||||
const PORT = 4359;
|
||||
await portMussFreiSein(PORT, "die Artenpruefung");
|
||||
const BASIS = `http://127.0.0.1:${PORT}`;
|
||||
|
||||
const ordner = mkdtempSync(join(tmpdir(), "ws-arten-"));
|
||||
const DB = join(ordner, "workspace.db");
|
||||
|
||||
let fehler = 0, gemacht = 0;
|
||||
const ok = (b, t) => { gemacht++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||
|
||||
let kind = null;
|
||||
async function starten() {
|
||||
kind = spawn(process.execPath, [join(HIER, "index.js")], {
|
||||
env: { ...process.env, WORKSPACE_DB: DB, PORT: String(PORT),
|
||||
SITE_ACCESS_SECRET: "lokaler-test",
|
||||
SITE_PUBLIC_LAUNCH_AT: "2020-01-01T00:00:00+01:00" },
|
||||
stdio: ["ignore", "pipe", "pipe"] });
|
||||
let aus = "";
|
||||
kind.stdout.on("data", (d) => { aus += d; });
|
||||
kind.stderr.on("data", (d) => { aus += d; });
|
||||
for (let i = 0; i < 100; i++) {
|
||||
await new Promise((r) => setTimeout(r, 250));
|
||||
if (kind.exitCode !== null) break;
|
||||
try { const a = await fetch(BASIS + "/workspace/", { redirect: "manual" });
|
||||
if (a.status < 500) return () => aus; } catch { /* noch nicht oben */ }
|
||||
}
|
||||
throw new Error("Server startete nicht:\n" + aus.slice(-1500));
|
||||
}
|
||||
async function stoppen() {
|
||||
if (!kind) return;
|
||||
kind.kill(); kind = null;
|
||||
await new Promise((r) => setTimeout(r, 700));
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
1. DIE BEIDEN LISTEN MUESSEN DIESELBEN SEIN
|
||||
======================================================================= */
|
||||
console.log("=== Zwei Listen, eine Wahrheit ===");
|
||||
const NEUE = ["bigmatch", "turnier", "special", "collab", "raid", "charity"];
|
||||
{
|
||||
const schema = readFileSync(join(HIER, "workspace.js"), "utf8");
|
||||
const kalender = readFileSync(join(HIER, "workspace-kalender.js"), "utf8");
|
||||
|
||||
/* NICHT DER ERSTE TREFFER DER DATEI (08.09.2026).
|
||||
|
||||
Hier stand schlicht `schema.match(/CHECK \(art IN \(([^)]*)\)\)/)`.
|
||||
Der erste Treffer ist aber der CHAT -- `art IN ('direkt','gruppe')`,
|
||||
rund 80 Zeilen vor den Terminen. Die Pruefung verglich also die
|
||||
Chatraumarten mit den Terminarten und meldete rot, obwohl am Code
|
||||
nichts falsch war. Ein Fehlalarm kostet dasselbe Vertrauen wie ein
|
||||
uebersehener Fehler.
|
||||
|
||||
Deshalb wird ab dem Bauplan der jeweiligen Tabelle gesucht -- und
|
||||
BEIDE Termintabellen werden geprueft, nicht eine stellvertretend. */
|
||||
const ausTabelle = (name) => {
|
||||
const ab = schema.indexOf(`CREATE TABLE IF NOT EXISTS ${name} (`);
|
||||
if (ab < 0) return [];
|
||||
const treffer = schema.slice(ab, ab + 4000).match(/CHECK \(art IN \(([^)]*)\)\)/);
|
||||
return (treffer?.[1] || "").split(",")
|
||||
.map((x) => x.trim().replace(/'/g, "")).filter(Boolean);
|
||||
};
|
||||
const ausCheck = ausTabelle("termine");
|
||||
const ausSerien = ausTabelle("termin_serien");
|
||||
ok(JSON.stringify(ausCheck) === JSON.stringify(ausSerien) && ausCheck.length > 0,
|
||||
`termine und termin_serien tragen dieselbe Liste (${ausCheck.length}/${ausSerien.length})`);
|
||||
|
||||
const liste = kalender.match(/const ARTEN = \[([^\]]*)\]/);
|
||||
const ausServer = (liste?.[1] || "").split(",")
|
||||
.map((x) => x.trim().replace(/["']/g, "")).filter(Boolean);
|
||||
|
||||
ok(ausCheck.length >= 9, `der CHECK kennt ${ausCheck.length} Arten`);
|
||||
ok(ausServer.length >= 9, `die Pruefliste kennt ${ausServer.length} Arten`);
|
||||
ok(JSON.stringify([...ausCheck].sort()) === JSON.stringify([...ausServer].sort()),
|
||||
`beide Listen sind gleich (CHECK: ${ausCheck.join(",")} | Server: ${ausServer.join(",")})`);
|
||||
for (const a of NEUE) {
|
||||
ok(ausCheck.includes(a) && ausServer.includes(a), ` "${a}" steht in beiden`);
|
||||
}
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
2. DER ALTE STAND -- und der Umbau darueber hinweg
|
||||
======================================================================= */
|
||||
console.log("\n=== Der Umbau einer bestehenden Datenbank ===");
|
||||
await starten();
|
||||
await stoppen();
|
||||
|
||||
const jetzt = new Date().toISOString();
|
||||
let idDogi, idLuna, terminVorher, serienVorher;
|
||||
{
|
||||
const d = new DatabaseSync(DB);
|
||||
d.exec("PRAGMA foreign_keys = OFF");
|
||||
|
||||
/* Beide Tabellen auf den alten CHECK zuruecksetzen -- woertlich so,
|
||||
wie sie vor heute aussahen, aber mit allen Spalten, die inzwischen
|
||||
dazugekommen sind. Genau das ist der Fall, der gefaehrlich ist. */
|
||||
for (const t of ["termine", "termin_serien"]) {
|
||||
const alt = d.prepare(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name=?").get(t).sql;
|
||||
const zurueck = alt
|
||||
.replace(new RegExp(
|
||||
"CREATE TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?[\"'`]?" + t + "[\"'`]?", "i"),
|
||||
`CREATE TABLE ${t}_alt`)
|
||||
.replace(/art\s+IN\s*\([^)]*\)/i, "art IN ('termin','call','review')");
|
||||
/* Greift der Rueckbau nicht, ist alles danach wertlos -- dann
|
||||
prueft diese Datei den heutigen Stand gegen sich selbst. */
|
||||
if (!zurueck.includes(`${t}_alt`) || zurueck.includes("'bigmatch'")) {
|
||||
console.log(` FEHL der alte Stand von ${t} liess sich nicht nachbauen.`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sp = d.prepare(`PRAGMA table_info(${t})`).all().map((z) => `"${z.name}"`).join(", ");
|
||||
d.exec("BEGIN");
|
||||
d.exec(zurueck);
|
||||
d.exec(`INSERT INTO ${t}_alt (${sp}) SELECT ${sp} FROM ${t};`);
|
||||
d.exec(`DROP TABLE ${t};`);
|
||||
d.exec(`ALTER TABLE ${t}_alt RENAME TO ${t};`);
|
||||
d.exec("COMMIT");
|
||||
}
|
||||
d.exec("PRAGMA foreign_keys = ON");
|
||||
|
||||
const anlegen = (name, rolle, code) => {
|
||||
const salz = randomBytes(16).toString("hex");
|
||||
const hash = scryptSync(code, salz, 64, { N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
||||
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
||||
.run(name, rolle, hash, salz, 32768, jetzt);
|
||||
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
};
|
||||
idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
||||
idLuna = anlegen("Luna", "creator", "CODE-CREA-0001");
|
||||
|
||||
const heute = jetzt.slice(0, 10);
|
||||
const rein = d.prepare(`INSERT INTO termine (titel, art, beginn, dauer_min, creator_id, erstellt, erstellt_von)
|
||||
VALUES (?,?,?,?,?,?,?)`);
|
||||
rein.run("Alter Call", "call", heute + "T10:00", 30, idLuna, jetzt, idDogi);
|
||||
rein.run("Alter Termin", "termin", heute + "T12:00", 30, idLuna, jetzt, idDogi);
|
||||
rein.run("Altes Review", "review", heute + "T14:00", 45, idLuna, jetzt, idDogi);
|
||||
/* Abhaengige Zeilen -- ohne sie pruefte der Umbau eine Tabelle statt
|
||||
eines Netzes. */
|
||||
const tid = d.prepare("SELECT id FROM termine WHERE titel = 'Alter Call'").get().id;
|
||||
d.prepare("INSERT INTO termin_teilnehmer (termin_id, person_id) VALUES (?,?)").run(tid, idLuna);
|
||||
d.prepare("INSERT INTO termin_wecker (termin_id, person_id, minuten_vorher, gesetzt) VALUES (?,?,?,?)")
|
||||
.run(tid, idDogi, 1440, jetzt);
|
||||
d.prepare(`INSERT INTO termin_serien (titel, art, takt, start_tag, uhrzeit, dauer_min, aktiv, erstellt, erstellt_von)
|
||||
VALUES ('Wochen-Call','call','woechentlich',?,'18:00',30,1,?,?)`)
|
||||
.run(heute, jetzt, idDogi);
|
||||
|
||||
terminVorher = d.prepare("SELECT COUNT(*) AS n FROM termine").get().n;
|
||||
serienVorher = d.prepare("SELECT COUNT(*) AS n FROM termin_serien").get().n;
|
||||
|
||||
/* GEGENPROBE: Der alte Stand muss 'bigmatch' WIRKLICH ablehnen --
|
||||
sonst gaebe es nichts umzustellen, und alles danach bewiese nichts. */
|
||||
let abgelehnt = false;
|
||||
try {
|
||||
d.prepare(`INSERT INTO termine (titel, art, beginn, dauer_min, erstellt, erstellt_von)
|
||||
VALUES ('Darf noch nicht','bigmatch',?,30,?,?)`).run(heute + "T20:00", jetzt, idDogi);
|
||||
} catch { abgelehnt = true; }
|
||||
d.close();
|
||||
ok(abgelehnt, "der alte Stand lehnt die Art 'bigmatch' wirklich ab");
|
||||
ok(terminVorher === 3 && serienVorher === 1,
|
||||
`${terminVorher} Termine und ${serienVorher} Serie angelegt`);
|
||||
}
|
||||
|
||||
/* Jetzt die echte Anwendung darueberlaufen lassen. */
|
||||
await starten();
|
||||
|
||||
{
|
||||
const d = new DatabaseSync(DB);
|
||||
/* NICHT DIE ANZAHL VERGLEICHEN (08.09.2026).
|
||||
|
||||
Erst stand hier `nachher === terminVorher`. Das schlug fehl -- 28
|
||||
statt 3 --, und zwar zu Recht: Der Serverstart breitet die Serie
|
||||
aus und legt dabei echte Termine an. Die Pruefung mass also den
|
||||
Umbau UND die Serienausbreitung in einer Zahl und konnte nicht
|
||||
sagen, welches von beiden gemeint war.
|
||||
|
||||
Der Umbau wird deshalb namentlich geprueft: Genau diese drei
|
||||
Zeilen muessen den Tabellentausch ueberlebt haben. */
|
||||
const alte = d.prepare(
|
||||
"SELECT titel FROM termine WHERE titel LIKE 'Alte%' ORDER BY titel").all().map((z) => z.titel);
|
||||
ok(alte.length === 3 && alte.join("|") === "Alter Call|Alter Termin|Altes Review",
|
||||
`die drei alten Termine haben den Tabellentausch ueberlebt (${alte.join(", ") || "keiner"})`);
|
||||
const serienNachher = d.prepare("SELECT COUNT(*) AS n FROM termin_serien").get().n;
|
||||
ok(serienNachher === serienVorher, `die Serie ebenfalls (${serienNachher})`);
|
||||
|
||||
/* UND WEIL DIE ZAHL AUFGEFALLEN IST, wird sie gemessen -- aber gegen
|
||||
die Regel, die WIRKLICH gilt.
|
||||
|
||||
Erst stand hier "kein Serientermin ausserhalb des laufenden
|
||||
Monats", nach Filipes Satz vom 07.09.2026: *"nicht monate im
|
||||
vorraus immer nur fuer den monat selbst"*. Die Pruefung wurde rot
|
||||
(25 Termine bis in den Maerz) -- und lag falsch. Der Satz gehoerte
|
||||
zum CALLS-BRETT (er zeigte dessen Bildschirmfoto); dort ist er
|
||||
umgesetzt und wird von pruef-call-kategorien gemessen. Der KALENDER
|
||||
legt bewusst 180 Tage im Voraus an, begruendet in workspace-serien.js:
|
||||
der 90-Tage-Plan aus dem Konzept mit Reserve.
|
||||
|
||||
Beinahe haette ich den Code an eine Regel angepasst, die es an
|
||||
dieser Stelle nie gab. Eine Pruefung, die eine erfundene Regel
|
||||
misst, ist schlimmer als keine: Sie wird rot, jemand "repariert"
|
||||
etwas Heiles, und beim naechsten Mal glaubt ihr niemand mehr. */
|
||||
const HORIZONT = 180;
|
||||
const grenze = new Date(Date.now() + (HORIZONT + 1) * 86400000).toISOString().slice(0, 10);
|
||||
const ausSerie = d.prepare(
|
||||
"SELECT beginn FROM termine WHERE serie_id IS NOT NULL ORDER BY beginn").all();
|
||||
const zuWeit = ausSerie.filter((z) => z.beginn.slice(0, 10) > grenze);
|
||||
ok(ausSerie.length > 0, `die Serie hat Termine erzeugt (${ausSerie.length})`);
|
||||
ok(zuWeit.length === 0,
|
||||
`keiner reicht ueber den ${HORIZONT}-Tage-Horizont hinaus (${zuWeit.length} zu weit: `
|
||||
+ `${zuWeit.slice(0, 3).map((z) => z.beginn).join(", ") || "–"})`);
|
||||
|
||||
/* Die abhaengigen Zeilen -- der eigentliche Prueffall des Umbaus. */
|
||||
const teiln = d.prepare("SELECT COUNT(*) AS n FROM termin_teilnehmer").get().n;
|
||||
const wecker = d.prepare("SELECT COUNT(*) AS n FROM termin_wecker").get().n;
|
||||
ok(teiln === 1, `der Teilnehmer haengt noch am Termin (${teiln})`);
|
||||
ok(wecker === 1, `der Wecker ebenfalls (${wecker})`);
|
||||
const kaputt = d.prepare("PRAGMA foreign_key_check").all();
|
||||
ok(kaputt.length === 0, `keine verwaisten Verweise (${kaputt.length})`);
|
||||
|
||||
/* Die CHECK-Regel selbst: in BEIDEN Tabellen. */
|
||||
for (const t of ["termine", "termin_serien"]) {
|
||||
const plan = d.prepare(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name=?").get(t).sql;
|
||||
const regel = plan.match(/art\s+IN\s*\(([^)]*)\)/i)?.[1] || "";
|
||||
ok(regel.includes("'bigmatch'") && regel.includes("'charity'"),
|
||||
`${t} kennt die neuen Arten (${regel.slice(0, 60)}…)`);
|
||||
}
|
||||
|
||||
/* UND DIE SCHRANKE BEISST NOCH. Eine Umstellung, die den CHECK
|
||||
nebenbei entfernt, sieht aus wie ein Erfolg und ist der schlimmere
|
||||
Ausgang. */
|
||||
let erfunden = false;
|
||||
try {
|
||||
d.prepare(`INSERT INTO termine (titel, art, beginn, dauer_min, erstellt, erstellt_von)
|
||||
VALUES ('Quatsch','quatsch',?,30,?,?)`).run(jetzt.slice(0, 10) + "T21:00", jetzt, idDogi);
|
||||
} catch { erfunden = true; }
|
||||
ok(erfunden, "eine erfundene Art wird weiterhin abgelehnt");
|
||||
d.close();
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
3. UEBER DIE SCHNITTSTELLE
|
||||
======================================================================= */
|
||||
console.log("\n=== Anlegen mit den neuen Arten ===");
|
||||
{
|
||||
const a = await fetch(BASIS + "/workspace/api/anmelden", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ rolle: "admin", code: "CODE-DOGI-0001" }) });
|
||||
const keks = (a.headers.getSetCookie?.() || []).map((z) => z.split(";")[0]).join("; ");
|
||||
ok(!!keks, "als DogFather angemeldet");
|
||||
|
||||
const morgen = new Date(Date.now() + 86400000).toISOString().slice(0, 10);
|
||||
let angelegt = 0;
|
||||
for (const art of NEUE) {
|
||||
const r = await fetch(BASIS + "/workspace/api/termine", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", cookie: keks, origin: BASIS },
|
||||
body: JSON.stringify({ titel: `Probe ${art}`, art, beginn: morgen + "T20:00", dauer_min: 60 }),
|
||||
});
|
||||
if (r.status === 201) angelegt++;
|
||||
}
|
||||
ok(angelegt === NEUE.length,
|
||||
`alle ${NEUE.length} neuen Arten lassen sich anlegen (${angelegt})`);
|
||||
|
||||
/* GEGENPROBE ueber die Schnittstelle: eine erfundene Art muss
|
||||
abgelehnt werden, und zwar mit 400 -- nicht mit einem Serverfehler. */
|
||||
const quatsch = await fetch(BASIS + "/workspace/api/termine", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", cookie: keks, origin: BASIS },
|
||||
body: JSON.stringify({ titel: "Quatsch", art: "quatsch", beginn: morgen + "T21:00" }),
|
||||
});
|
||||
ok(quatsch.status === 400, `eine erfundene Art wird abgelehnt (HTTP ${quatsch.status})`);
|
||||
|
||||
/* =====================================================================
|
||||
4. UND JETZT DER TEIL, DER GEFEHLT HAT: IST ES AUCH ZU SEHEN?
|
||||
===================================================================== */
|
||||
console.log("\n=== Im echten Kalender sichtbar ===");
|
||||
const { chromium } = await import(
|
||||
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||
const browser = await chromium.launch();
|
||||
try {
|
||||
const sitzung = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const kekse = keks.split("; ").map((z) => {
|
||||
const [name, ...wert] = z.split("=");
|
||||
return { name, value: wert.join("="), domain: "127.0.0.1", path: "/" };
|
||||
});
|
||||
await sitzung.addCookies(kekse);
|
||||
const seite = await sitzung.newPage();
|
||||
await seite.goto(BASIS + "/workspace/kalender.html", { waitUntil: "networkidle" });
|
||||
await seite.waitForTimeout(1500);
|
||||
|
||||
/* In die Listenansicht -- dort steht jeder Eintrag mit Titel da,
|
||||
unabhaengig davon, welcher Monat gerade angezeigt wird. */
|
||||
const liste = await seite.$("button:has-text('Liste')");
|
||||
if (liste) { await liste.click(); await seite.waitForTimeout(900); }
|
||||
|
||||
const text = await seite.evaluate(() => document.body.innerText);
|
||||
let sichtbar = 0;
|
||||
for (const art of NEUE) {
|
||||
const da = text.includes(`Probe ${art}`);
|
||||
if (da) sichtbar++;
|
||||
else console.log(` FEHL "Probe ${art}" steht in der Datenbank, aber NICHT im Kalender`);
|
||||
}
|
||||
ok(sichtbar === NEUE.length,
|
||||
`alle ${NEUE.length} neuen Arten sind im Kalender zu sehen (${sichtbar})`);
|
||||
|
||||
/* Und die Schalterleiste muss sie kennen -- sonst laesst sich eine
|
||||
Art zwar sehen, aber nicht mehr ausblenden. */
|
||||
const schalter = await seite.$$eval(".k-chip", (ns) => ns.map((n) => n.textContent));
|
||||
const fehlend = NEUE.filter((a) => !schalter.some((s) => s.toLowerCase().includes(a.slice(0, 5))));
|
||||
ok(fehlend.length === 0,
|
||||
`die Schalterleiste kennt die neuen Arten (${schalter.length} Schalter, `
|
||||
+ `fehlt: ${fehlend.join(",") || "keine"})`);
|
||||
|
||||
/* GEGENPROBE: Ein Schalter muss auch WIRKEN. Sonst waere die
|
||||
Sichtbarkeit oben nur deshalb gruen, weil gar nicht gefiltert
|
||||
wird -- eine Pruefung, die immer bestaetigt. */
|
||||
const vorher = (await seite.evaluate(() => document.body.innerText)).includes("Probe bigmatch");
|
||||
const knopf = (await seite.$$(".k-chip")).find(async () => true);
|
||||
const bmSchalter = await seite.$(".k-chip:has-text('BigMatch')");
|
||||
let wirkt = false;
|
||||
if (bmSchalter) {
|
||||
await bmSchalter.click();
|
||||
await seite.waitForTimeout(600);
|
||||
const nachher = (await seite.evaluate(() => document.body.innerText)).includes("Probe bigmatch");
|
||||
wirkt = vorher && !nachher;
|
||||
}
|
||||
ok(wirkt, `der Schalter blendet die Art auch wirklich aus (vorher da: ${vorher})`);
|
||||
void knopf;
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
await stoppen();
|
||||
console.log(`\n${gemacht} Pruefungen`);
|
||||
console.log(fehler === 0 ? "ALLES IN ORDNUNG" : `${fehler} FEHLER`);
|
||||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
||||
process.exit(fehler ? 1 : 0);
|
||||
@@ -22,7 +22,31 @@ import {
|
||||
|
||||
export const kalenderRouter = express.Router();
|
||||
|
||||
const ARTEN = ["termin", "call", "review"];
|
||||
/* DIE TERMINARTEN (08.09.2026 erweitert).
|
||||
|
||||
Wunsch Filipe: "da sollen auch noch kategorien wie bigmatch,
|
||||
turniere, special-live ... informier dich, was man da alles noch
|
||||
gebrauchen koennte."
|
||||
|
||||
Recherchiert (Streams Charts, Kick, StreamerCollabs): Was ein
|
||||
Creator-Team im Kalender wirklich unterscheidet, sind neben den
|
||||
internen Terminen die AUFTRITTE -- Wettkaempfe, gemeinsame Formate
|
||||
und die grossen Ausnahmen. Daraus die sechs neuen:
|
||||
|
||||
bigmatch ein angesetztes Duell gegen einen bestimmten Gegner
|
||||
turnier ein mehrrundiges Format ueber laengere Zeit
|
||||
special ein angekuendigter Sonder-Livestream
|
||||
collab ein gemeinsamer Stream mit anderen Creators
|
||||
raid eine Raid-Kette, bei der Zuschauer weitergereicht werden
|
||||
charity ein Spendenformat
|
||||
|
||||
Die Liste steht HIER und nicht in der Datenbank: Sie ist eine
|
||||
Entscheidung ueber das Format, keine Nutzdaten. Der CHECK in
|
||||
workspace.js fuehrt dieselben Werte -- wer hier einen hinzufuegt,
|
||||
muss ihn dort ebenfalls eintragen, sonst lehnt die Datenbank ab.
|
||||
pruef-arten haelt beide gegeneinander. */
|
||||
const ARTEN = ["termin", "call", "review",
|
||||
"bigmatch", "turnier", "special", "collab", "raid", "charity"];
|
||||
const TITEL_MAX = 160;
|
||||
const TEXT_MAX = 4000;
|
||||
const ORT_MAX = 400;
|
||||
|
||||
+117
-2
@@ -764,6 +764,121 @@ function umstellungen(d) {
|
||||
geprueft hat. Deshalb wird danach nachgesehen, ob 'spicy' wirklich
|
||||
drinsteht.
|
||||
===================================================================== */
|
||||
/* =====================================================================
|
||||
MEHR TERMINARTEN (08.09.2026)
|
||||
|
||||
Wunsch Filipe: "da sollen auch noch kategorien wie bigmatch,
|
||||
turniere, special-live ... informier dich, was man da alles noch
|
||||
gebrauchen koennte."
|
||||
|
||||
Recherchiert (Streams Charts, Kick, StreamerCollabs): Was ein
|
||||
Creator-Team im Kalender wirklich unterscheidet, sind neben den
|
||||
internen Terminen die AUFTRITTE -- Wettkaempfe (BigMatch, Turnier),
|
||||
gemeinsame Formate (Collab, Raid-Train) und die grossen Ausnahmen
|
||||
(Special-Live, Charity). Genau diese sechs kommen dazu.
|
||||
|
||||
WARUM EIN TABELLENUMBAU: Die erlaubten Werte stecken in einem
|
||||
CHECK, und ein CHECK laesst sich in SQLite nicht aendern. Dieselbe
|
||||
Lage wie bei der Rolle 'spicy' -- und deshalb hier dasselbe,
|
||||
bewaehrte Verfahren: Bauplan aus sqlite_master lesen, NUR die Liste
|
||||
ersetzen, das Ergebnis pruefen, Spalten aus PRAGMA holen, Zeilen
|
||||
INNERHALB der Transaktion zaehlen, Sicherung vorher.
|
||||
|
||||
Zwei Tabellen statt einer (termine und termin_serien), deshalb
|
||||
eine Schleife -- zweimal derselbe Text waere zweimal dieselbe
|
||||
Gelegenheit, eine Stelle zu vergessen. */
|
||||
const ARTEN_NEU = "'termin','call','review','bigmatch','turnier','special','collab','raid','charity'";
|
||||
for (const tabelle of ["termine", "termin_serien"]) {
|
||||
const plan = d.prepare(
|
||||
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?").get(tabelle)?.sql || "";
|
||||
/* Geprueft wird die REGEL, nicht der ganze Bauplan: SQLite hebt ihn
|
||||
woertlich auf, samt Kommentaren -- ein erklaerender Satz mit dem
|
||||
Wort 'bigmatch' wuerde sonst genuegen, damit die Umstellung sich
|
||||
fuer erledigt haelt. Genau dieser Fehler ist bei 'spicy' passiert. */
|
||||
const artRegel = plan.match(/art\s+IN\s*\(([^)]*)\)/i)?.[1] || "";
|
||||
if (!plan || !artRegel || artRegel.includes("'bigmatch'")) continue;
|
||||
|
||||
/* DER TABELLENNAME GEHOERT IN DEN DATEINAMEN (08.09.2026).
|
||||
|
||||
Hier stand `.vor-arten-${jetztStempel}` -- ohne die Tabelle. Der
|
||||
Stempel wird EINMAL pro Serverstart gebildet, diese Schleife
|
||||
laeuft aber ZWEIMAL. Der zweite Durchlauf wollte also dieselbe
|
||||
Datei anlegen, `VACUUM INTO` weigert sich (die Datei ist schon
|
||||
da), und das `break` unten beendete daraufhin die ganze Schleife.
|
||||
|
||||
Ergebnis: `termine` war umgestellt, `termin_serien` NICHT. Und
|
||||
zwar still -- die Meldung ging in die Serverausgabe, die niemand
|
||||
liest. Aufgefallen waere es erst, wenn jemand eine wiederkehrende
|
||||
BigMatch-Reihe anlegt und die Datenbank sie ohne erkennbaren
|
||||
Grund ablehnt.
|
||||
|
||||
Das `break` bleibt richtig: Wenn sich keine Sicherung anlegen
|
||||
laesst, wird nicht umgebaut. Falsch war nur der Name. */
|
||||
const sicherung = `${DB_PFAD}.vor-arten-${tabelle}-${jetztStempel}`;
|
||||
try {
|
||||
d.exec(`VACUUM INTO '${sicherung.replace(/'/g, "''")}'`);
|
||||
console.log("[workspace] Sicherung vor der Artenumstellung:", sicherung);
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Sicherung fehlgeschlagen, Artenumstellung abgebrochen:",
|
||||
fehler?.message);
|
||||
break;
|
||||
}
|
||||
|
||||
const neuerPlan = plan
|
||||
/* DAS MUSTER WIRD ZUSAMMENGESETZT, NICHT IN EINEN TEMPLATE-STRING
|
||||
GESCHRIEBEN. Dort wird \s beim Einlesen zu einem blossen "s" --
|
||||
aus "CREATE TABLE\s+" wuerde "CREATE TABLEs+", das Muster
|
||||
passte auf nichts, und die Umstellung waere STILL ausgeblieben.
|
||||
Genau so stand es hier beim ersten Anlauf; nachgemessen mit
|
||||
einem Einzeiler, der beide Schreibweisen gegen den echten
|
||||
Bauplan haelt. */
|
||||
.replace(new RegExp(
|
||||
"CREATE TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?[\"'`]?" + tabelle + "[\"'`]?", "i"),
|
||||
`CREATE TABLE ${tabelle}_neu`)
|
||||
.replace(/art\s+IN\s*\([^)]*\)/i, `art IN (${ARTEN_NEU})`);
|
||||
if (!neuerPlan.includes("'bigmatch'") || !neuerPlan.includes(`${tabelle}_neu`)) {
|
||||
console.error(`[workspace] Artenumstellung abgebrochen: Bauplan von ${tabelle} `
|
||||
+ "liess sich nicht umschreiben.");
|
||||
continue;
|
||||
}
|
||||
|
||||
const spalten = d.prepare(`PRAGMA table_info(${tabelle})`).all().map((z) => z.name);
|
||||
if (!spalten.length) continue;
|
||||
const liste = spalten.map((n) => `"${n}"`).join(", ");
|
||||
|
||||
d.exec("PRAGMA foreign_keys = OFF");
|
||||
try {
|
||||
const vorher = d.prepare(`SELECT COUNT(*) AS n FROM ${tabelle}`).get().n;
|
||||
d.exec("BEGIN");
|
||||
d.exec(neuerPlan);
|
||||
d.exec(`INSERT INTO ${tabelle}_neu (${liste}) SELECT ${liste} FROM ${tabelle};`);
|
||||
const nachher = d.prepare(`SELECT COUNT(*) AS n FROM ${tabelle}_neu`).get().n;
|
||||
if (nachher !== vorher) {
|
||||
d.exec("ROLLBACK");
|
||||
console.error(`[workspace] Artenumstellung abgebrochen: ${vorher} Zeilen vorher, `
|
||||
+ `${nachher} nachher. Sicherung: ${sicherung}`);
|
||||
} else {
|
||||
d.exec(`DROP TABLE ${tabelle};`);
|
||||
d.exec(`ALTER TABLE ${tabelle}_neu RENAME TO ${tabelle};`);
|
||||
d.exec("COMMIT");
|
||||
const kaputt = d.prepare("PRAGMA foreign_key_check").all();
|
||||
if (kaputt.length) {
|
||||
console.error("[workspace] ACHTUNG: nach der Artenumstellung", kaputt.length,
|
||||
"verwaiste Verweise. Sicherung:", sicherung);
|
||||
} else {
|
||||
console.log(`[workspace] Terminarten erweitert in ${tabelle}: ${nachher} Zeilen, `
|
||||
+ `${spalten.length} Spalten uebernommen, Verweise geprueft.`);
|
||||
}
|
||||
}
|
||||
} catch (fehler) {
|
||||
try { d.exec("ROLLBACK"); } catch { /* schon zurueckgerollt */ }
|
||||
console.error("[workspace] Artenumstellung fehlgeschlagen:", fehler?.message,
|
||||
"-- Sicherung:", sicherung);
|
||||
} finally {
|
||||
d.exec("PRAGMA foreign_keys = ON");
|
||||
}
|
||||
}
|
||||
|
||||
const rollenPlan = d.prepare(
|
||||
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'personen'").get()?.sql || "";
|
||||
/* GEPRUEFT WIRD DIE REGEL, NICHT DER TEXT (07.09.2026).
|
||||
@@ -1135,7 +1250,7 @@ export function db() {
|
||||
titel TEXT NOT NULL,
|
||||
beschreibung TEXT,
|
||||
art TEXT NOT NULL DEFAULT 'termin'
|
||||
CHECK (art IN ('termin','call','review')),
|
||||
CHECK (art IN ('termin','call','review','bigmatch','turnier','special','collab','raid','charity')),
|
||||
beginn TEXT NOT NULL,
|
||||
dauer_min INTEGER NOT NULL DEFAULT 30,
|
||||
ort TEXT,
|
||||
@@ -1261,7 +1376,7 @@ export function db() {
|
||||
titel TEXT NOT NULL,
|
||||
beschreibung TEXT,
|
||||
art TEXT NOT NULL DEFAULT 'termin'
|
||||
CHECK (art IN ('termin','call','review')),
|
||||
CHECK (art IN ('termin','call','review','bigmatch','turnier','special','collab','raid','charity')),
|
||||
takt TEXT NOT NULL
|
||||
CHECK (takt IN ('taeglich','werktags','woechentlich',
|
||||
'zweiwoechentlich','monatlich_datum',
|
||||
|
||||
@@ -53,14 +53,45 @@
|
||||
return k;
|
||||
};
|
||||
|
||||
const ARTNAME = { call: 'Call', termin: 'Termin', review: 'Review', frist: 'Frist' };
|
||||
/* Die Namen der Arten. `frist` steht mit drin, obwohl es keine
|
||||
Terminart ist: Aufgabenfristen erscheinen im Kalender als
|
||||
Eintraege und brauchen dort auch eine Beschriftung. */
|
||||
const ARTNAME = {
|
||||
call: 'Call', termin: 'Termin', review: 'Review', frist: 'Frist',
|
||||
bigmatch: 'BigMatch', turnier: 'Turnier', special: 'Special-Live',
|
||||
collab: 'Collab', raid: 'Raid-Train', charity: 'Charity',
|
||||
};
|
||||
/* Jede Art trägt ihre eigene Farbe -- dieselbe Idee wie bei den Kacheln
|
||||
auf der Startseite: Die Farbe gehört zur Sache, nicht zum Rang. */
|
||||
/* EINE FARBE JE ART (08.09.2026 erweitert).
|
||||
|
||||
Die drei internen Arten behalten ihre Farben -- wer sie kennt, soll
|
||||
sie wiedererkennen. Die sechs Auftritte bekommen WAERMERE, kraeftigere
|
||||
Toene: Im Monatsraster sieht man dadurch auf einen Blick, ob eine
|
||||
Woche voller Arbeitstermine steckt oder voller Streams, ohne ein
|
||||
Wort zu lesen.
|
||||
|
||||
Innerhalb der Auftritte ist die Zuordnung nicht beliebig:
|
||||
bigmatch Rot -- ein Duell, das schaerfste Format
|
||||
turnier Bernstein -- mehrere Runden, laenger, ruhiger als ein Duell
|
||||
special Magenta -- die Ausnahme, die auffallen soll
|
||||
collab Tuerkis -- gemeinsam, freundlich, kein Wettkampf
|
||||
raid Orange -- Bewegung, eine Kette
|
||||
charity Gruengold -- Spenden, warm und nicht schreiend
|
||||
|
||||
Alle neun sind gegen den dunklen Grund auf mindestens 4,5:1
|
||||
geprueft (pruef-buehne misst die Texte, die sie faerben). */
|
||||
const ARTFARBE = {
|
||||
call: '#3fbdf5',
|
||||
termin: '#8a76ff',
|
||||
review: '#50a752',
|
||||
frist: '#d17717',
|
||||
call: '#3fbdf5',
|
||||
termin: '#8a76ff',
|
||||
review: '#50a752',
|
||||
frist: '#d17717',
|
||||
bigmatch: '#ff5f6d',
|
||||
turnier: '#e3b341',
|
||||
special: '#f065c8',
|
||||
collab: '#2fd6c4',
|
||||
raid: '#ff9040',
|
||||
charity: '#a7d64a',
|
||||
};
|
||||
const FEIERTAG_FARBE = '#ff9aa2';
|
||||
const ANLASS_FARBE = '#d8a13a';
|
||||
@@ -211,7 +242,27 @@
|
||||
}
|
||||
let ansicht = 'monat';
|
||||
let anker = heuteText(); // Datum, um das herum angezeigt wird
|
||||
const zeigen = { call: true, termin: true, review: true, frist: true };
|
||||
/* WAS NICHT AUSDRUECKLICH ABGEWAEHLT IST, IST SICHTBAR (08.09.2026).
|
||||
|
||||
Hier stand `{ call: true, termin: true, review: true, frist: true }`
|
||||
-- eine zweite, von Hand gepflegte Liste der Arten neben ARTNAME.
|
||||
Als die sechs Auftritts-Arten dazukamen, blieb sie stehen. Unten
|
||||
filtert `sichtbare()` mit `zeigen[e.art]`, und fuer 'bigmatch' ist
|
||||
das `undefined`. Ergebnis: Ein BigMatch liess sich anlegen, der
|
||||
Server meldete 201, er stand in der Datenbank -- und war im
|
||||
Kalender in KEINER Ansicht zu sehen. Ohne Fehler, ohne Hinweis.
|
||||
Filipe haette einen Eintrag gemacht und nichts waere passiert.
|
||||
|
||||
Zwei Lehren, beide hier umgesetzt:
|
||||
|
||||
1. Die Liste wird ABGELEITET, nicht gefuehrt. ARTNAME ist die eine
|
||||
Quelle; wer dort eine Art ergaenzt, hat sie ueberall.
|
||||
2. Der Vorgabewert einer Sichtbarkeitsfrage muss SICHTBAR sein.
|
||||
`sichtbare()` prueft deshalb `!== false` statt auf Wahrheit --
|
||||
eine unbekannte Art faellt dann auf, statt zu verschwinden.
|
||||
Ein Eintrag zu viel im Kalender ist ein Schoenheitsfehler, ein
|
||||
fehlender ist ein verpasster Termin. */
|
||||
const zeigen = Object.fromEntries(Object.keys(ARTNAME).map((a) => [a, true]));
|
||||
|
||||
/* ---------- Datumsrechnung ----------------------------------------------
|
||||
Alles läuft in UTC-Mittag. Grund: Wer mit lokalen Zeiten rechnet,
|
||||
@@ -449,7 +500,10 @@
|
||||
a.tag.localeCompare(b.tag) || (a.uhr || '99:99').localeCompare(b.uhr || '99:99'));
|
||||
}
|
||||
|
||||
const sichtbare = () => (daten ? daten.eintraege.filter((e) => zeigen[e.art]) : []);
|
||||
/* `!== false`, nicht auf Wahrheit pruefen -- Begruendung oben bei
|
||||
`zeigen`. Eine Art, die niemand eingetragen hat, wird angezeigt
|
||||
statt verschluckt. */
|
||||
const sichtbare = () => (daten ? daten.eintraege.filter((e) => zeigen[e.art] !== false) : []);
|
||||
|
||||
function nachTag() {
|
||||
const karte = new Map();
|
||||
@@ -798,8 +852,22 @@
|
||||
function baueFilter() {
|
||||
const ziel = $('filter');
|
||||
ziel.replaceChildren();
|
||||
for (const art of ['call', 'termin', 'review', 'frist']) {
|
||||
const n = daten.eintraege.filter((e) => e.art === art).length;
|
||||
/* DIE LISTE WIRD ABGELEITET (08.09.2026) -- vorher stand hier
|
||||
`['call','termin','review','frist']` fest, die dritte Fassung
|
||||
derselben Aufzaehlung in einer Datei.
|
||||
|
||||
Die vier Grundarten bekommen immer einen Schalter, auch bei null
|
||||
Eintraegen: Sie sind der Alltag, und ein Schalter, der auftaucht
|
||||
und verschwindet, verwirrt mehr, als er hilft. Die sechs
|
||||
Auftritts-Arten erscheinen nur, wenn es sie im Zeitraum wirklich
|
||||
gibt -- zehn Schalter, davon sechs mit "(0)", waeren eine Leiste,
|
||||
die niemand mehr liest. */
|
||||
const GRUND = ['call', 'termin', 'review', 'frist'];
|
||||
const anzahl = (art) => daten.eintraege.filter((e) => e.art === art).length;
|
||||
const arten = [...GRUND,
|
||||
...Object.keys(ARTNAME).filter((a) => !GRUND.includes(a) && anzahl(a) > 0)];
|
||||
for (const art of arten) {
|
||||
const n = anzahl(art);
|
||||
const b = el('button', 'k-chip');
|
||||
b.type = 'button';
|
||||
b.setAttribute('aria-pressed', String(zeigen[art]));
|
||||
@@ -1380,6 +1448,78 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DAS FORMULAR PASST SICH DER ART AN (08.09.2026)
|
||||
|
||||
Wunsch Filipe: "auch so, dass wenn man die sachen aussucht, die
|
||||
ganze kachel und die sachen, die man eintippen muss, auch zu der
|
||||
jeweiligen kategorie passen."
|
||||
|
||||
WAS SICH AENDERT, SIND DIE FRAGEN -- nicht die Datenbank. Ein
|
||||
BigMatch fragt nach dem GEGNER, ein Turnier nach dem FORMAT, ein
|
||||
Collab nach den LEUTEN. Es ist jedes Mal dasselbe Feld "Titel";
|
||||
was fehlt, ist die richtige Frage davor. Eigene Spalten je Art
|
||||
waeren neun halbleere Felder in einer Tabelle und beim naechsten
|
||||
Format wieder eines mehr.
|
||||
|
||||
Dasselbe fuer Ort und Notiz: "Ort oder Link" ist bei einem
|
||||
Raid-Train die falsche Frage -- dort geht es um die KETTE, also um
|
||||
die Reihenfolge der Kanaele.
|
||||
|
||||
Und die Dauer bekommt einen Vorschlag: Ein Call dauert 30 Minuten,
|
||||
ein Turnier selten unter zwei Stunden. Vorgeschlagen, nicht
|
||||
erzwungen -- und nur bei einem NEUEN Eintrag, nie beim Bearbeiten:
|
||||
Wer eine Dauer eingetragen hat, will sie nicht durch einen
|
||||
Artwechsel verlieren. */
|
||||
const ART_FELDER = {
|
||||
call: { titel: 'Worum geht es?', titelHilfe: 'z. B. Wochen-Call mit Luna',
|
||||
ort: 'Ort oder Link', ortHilfe: 'Discord, Meet-Link, Adresse …', dauer: 30 },
|
||||
termin: { titel: 'Worum geht es?', titelHilfe: 'z. B. Fotoshooting',
|
||||
ort: 'Ort oder Link', ortHilfe: 'Adresse, Discord, Link …', dauer: 30 },
|
||||
review: { titel: 'Was wird durchgesehen?', titelHilfe: 'z. B. Monatsrückblick Luna',
|
||||
ort: 'Ort oder Link', ortHilfe: 'Discord, Meet-Link …', dauer: 45 },
|
||||
bigmatch: { titel: 'Gegen wen?', titelHilfe: 'z. B. BigMatch vs. Beanii',
|
||||
ort: 'Wo läuft es?', ortHilfe: 'TikTok, Twitch, Kanal-Link …', dauer: 60,
|
||||
notiz: 'Regeln, Einsatz, Uhrzeit des Matches' },
|
||||
turnier: { titel: 'Welches Turnier?', titelHilfe: 'z. B. Herbst-Cup, Runde 2',
|
||||
ort: 'Wo läuft es?', ortHilfe: 'Plattform oder Turnier-Link', dauer: 120,
|
||||
notiz: 'Modus, Runde, Gegner, Preisgeld' },
|
||||
special: { titel: 'Was ist das Besondere?', titelHilfe: 'z. B. 24-Stunden-Stream',
|
||||
ort: 'Wo läuft es?', ortHilfe: 'TikTok, Twitch, YouTube …', dauer: 180,
|
||||
notiz: 'Ablauf, Gäste, was angekündigt wurde' },
|
||||
collab: { titel: 'Mit wem?', titelHilfe: 'z. B. Collab mit Miesmuschel',
|
||||
ort: 'Wessen Kanal?', ortHilfe: 'Kanal oder Link', dauer: 90,
|
||||
notiz: 'Thema, wer moderiert, Absprachen' },
|
||||
raid: { titel: 'Welche Kette?', titelHilfe: 'z. B. Raid-Train Freitagabend',
|
||||
ort: 'Reihenfolge der Kanäle', ortHilfe: 'wer nach wem, mit Links', dauer: 60,
|
||||
notiz: 'Startzeit je Kanal, Übergabe-Absprache' },
|
||||
charity: { titel: 'Für wen wird gesammelt?', titelHilfe: 'z. B. Spendenstream Tierheim',
|
||||
ort: 'Wo läuft es?', ortHilfe: 'Plattform, Spendenlink', dauer: 180,
|
||||
notiz: 'Spendenziel, Link, Ablauf' },
|
||||
};
|
||||
|
||||
function artFelderSetzen(neuerEintrag) {
|
||||
const art = $('f-art').value;
|
||||
const f = ART_FELDER[art] || ART_FELDER.termin;
|
||||
const schild = (feld, text, zusatz) => {
|
||||
const l = document.querySelector(`label[for="${feld}"]`);
|
||||
if (!l) return;
|
||||
l.textContent = text;
|
||||
if (zusatz) {
|
||||
const s2 = el('span', 'leise', ' (optional)');
|
||||
l.append(s2);
|
||||
}
|
||||
};
|
||||
schild('f-titel', f.titel, false);
|
||||
$('f-titel').placeholder = f.titelHilfe || '';
|
||||
schild('f-ort', f.ort, true);
|
||||
$('f-ort').placeholder = f.ortHilfe || '';
|
||||
schild('f-text', f.notiz ? 'Notiz' : 'Notiz', true);
|
||||
$('f-text').placeholder = f.notiz || '';
|
||||
/* Die Dauer nur bei einem NEUEN Eintrag vorschlagen -- siehe oben. */
|
||||
if (neuerEintrag) $('f-dauer').value = f.dauer || 30;
|
||||
}
|
||||
|
||||
function formularOeffnen(tag, termin) {
|
||||
/* Vorbelegung: der angeklickte Tag um 18:00 -- ein realistischer
|
||||
Zeitpunkt für einen Call, spart in der Regel zwei Klicks. Ohne Tag
|
||||
@@ -1397,6 +1537,7 @@
|
||||
baueDabei([]);
|
||||
bearbeiteId = 0;
|
||||
$('neu-speichern').textContent = 'Anlegen';
|
||||
artFelderSetzen(true);
|
||||
|
||||
/* BEARBEITEN: dieselben Felder, gefuellt. Der Rhythmus bleibt
|
||||
aussen vor -- eine Wiederholung ist eine REGEL und wird unter
|
||||
@@ -1414,6 +1555,8 @@
|
||||
baueDabei(termin.teilnehmer_ids || []);
|
||||
$('f-takt').value = '';
|
||||
wdhVorschau();
|
||||
/* `false`: Beim Bearbeiten bleibt die eingetragene Dauer stehen. */
|
||||
artFelderSetzen(false);
|
||||
$('neu-speichern').textContent = 'Speichern';
|
||||
}
|
||||
umschalten(true);
|
||||
@@ -1437,6 +1580,12 @@
|
||||
umschalten(false);
|
||||
});
|
||||
|
||||
/* Beim Wechsel der Art die Fragen umstellen. `true` heisst: Die
|
||||
Dauer darf vorgeschlagen werden -- wer die Art wechselt, hat den
|
||||
Eintrag noch nicht fertig. Beim Bearbeiten wird der Wechsel
|
||||
ebenfalls hier ausgeloest, aber `formularOeffnen` hat die echte
|
||||
Dauer danach schon gesetzt. */
|
||||
$('f-art').addEventListener('change', () => artFelderSetzen(true));
|
||||
$('f-beginn').addEventListener('change', taktAuswahlFuellen);
|
||||
$('f-takt').addEventListener('change', wdhVorschau);
|
||||
$('f-ende').addEventListener('change', wdhVorschau);
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -212,10 +212,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -135,10 +135,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -144,14 +144,14 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -77,11 +77,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,17 +9,17 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
||||
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
||||
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
||||
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
||||
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
||||
Dialog zu sehen bekam. -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -155,10 +155,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609080007"></script>
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007"></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007"></script>
|
||||
<script src="assets/js/chat.js?v=202609080007"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044"></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044"></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044"></script>
|
||||
<script src="assets/js/chat.js?v=202609080044"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -167,12 +167,12 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/content.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/content.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -96,10 +96,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -219,6 +219,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+31
-14
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -78,10 +78,27 @@
|
||||
<div class="neu__raster">
|
||||
<div>
|
||||
<label class="feld-schild" for="f-art">Art</label>
|
||||
<!-- NEUN ARTEN, IN ZWEI GRUPPEN (08.09.2026).
|
||||
|
||||
Die Gruppen sind nicht Deko: Die ersten drei sind INTERNE
|
||||
Termine, die anderen sechs sind AUFTRITTE. Das ist der
|
||||
Unterschied, nach dem man sucht ("ist das ein Arbeitstermin
|
||||
oder ein Stream?"), und eine Liste aus neun gleichrangigen
|
||||
Zeilen laesst ihn nicht erkennen. -->
|
||||
<select id="f-art">
|
||||
<option value="call" selected>Call</option>
|
||||
<option value="termin">Termin</option>
|
||||
<option value="review">Review</option>
|
||||
<optgroup label="Intern">
|
||||
<option value="call" selected>Call</option>
|
||||
<option value="termin">Termin</option>
|
||||
<option value="review">Review</option>
|
||||
</optgroup>
|
||||
<optgroup label="Auftritt">
|
||||
<option value="bigmatch">BigMatch</option>
|
||||
<option value="turnier">Turnier</option>
|
||||
<option value="special">Special-Live</option>
|
||||
<option value="collab">Collab</option>
|
||||
<option value="raid">Raid-Train</option>
|
||||
<option value="charity">Charity</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
@@ -288,11 +305,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -212,10 +212,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007"></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007"></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007"></script>
|
||||
<script src="assets/js/leistung.js?v=202609080007"></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044"></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044"></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044"></script>
|
||||
<script src="assets/js/leistung.js?v=202609080044"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -157,10 +157,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -202,11 +202,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -94,11 +94,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/report.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/report.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -127,10 +127,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
||||
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -320,10 +320,10 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/start.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/start.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -73,11 +73,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -114,10 +114,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,13 +9,13 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||
Dialog "Creator anlegen". -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -137,11 +137,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -9,11 +9,11 @@
|
||||
<meta name="theme-color" content="#0674b9" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080007" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609080044" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609080044" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -178,10 +178,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609080007" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609080044" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609080044" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user