Workspace: keine Kachel mehr ohne Ziel -- Automationen bekommen ihren Ort

Filipe: "da sind aber noch zwei kategorien zu". Er hat recht, und es
waren zwei VERSCHIEDENE Fehler.

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

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

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

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

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

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

Geprueft: 17 Kacheln, KEINE mehr ohne Ziel. Creator wird von der Seite
weggeleitet (302). Handy ohne Ueberlauf, 0 Konsolenfehler.
This commit is contained in:
2026-08-31 12:46:22 +02:00
parent 63aea61f39
commit eb87e4a484
21 changed files with 644 additions and 134 deletions
+34 -3
View File
@@ -27,7 +27,9 @@
===================================================================== */
import express from "express";
import { sitzungLesen, protokolliere, echteIp } from "./workspace.js";
import {
sitzungLesen, protokolliere, echteIp, einstellung, einstellungSetzen, istLeitung,
} from "./workspace.js";
export const kiRouter = express.Router();
@@ -38,6 +40,11 @@ const TEXT_MAX = 4000;
/* Nur eine Anfrage zur Zeit -- siehe Regel 3. */
let laeuft = false;
/* Der Schalter aus der Automationen-Seite. Steht bewusst hier oben, vor
der ersten Verwendung -- als const weiter unten waere es zwar zur
Laufzeit gutgegangen, aber beim Lesen eine Stolperfalle. */
const ANGESCHALTET = () => einstellung("ki_an", "1") !== "0";
/* Einfache Bremse je Person: fuenf Anfragen in zehn Minuten. Die KI ist
billig im Geld, aber teuer in Rechenzeit, die der Website gehoert. */
const VERBRAUCH = new Map();
@@ -113,6 +120,11 @@ async function frage(anweisung, text, maxWorte, beispiele = []) {
/* Gemeinsamer Rahmen: Bremse, Sperre, Fehler abfangen. So steht die
Absicherung einmal da und nicht in jedem Endpunkt neu. */
async function mitSchutz(req, res, aufgabe, was) {
/* Der Schalter wird HIER geprueft, nicht nur beim Anzeigen der
Knoepfe. Eine Oberflaeche, die etwas versteckt, ist keine Sperre. */
if (!ANGESCHALTET()) {
return res.status(403).json({ fehler: "Die KI-Vorschläge sind abgeschaltet." });
}
if (zuVielGefragt(req.person.id)) {
return res.status(429).json({
fehler: `Höchstens ${LIMIT} Vorschläge in ${FENSTER_MS / 60000} Minuten – die KI teilt sich den Server mit der Website.`,
@@ -150,18 +162,37 @@ async function mitSchutz(req, res, aufgabe, was) {
als gar kein Knopf. */
kiRouter.get("/workspace/api/ki/status", async (req, res) => {
/* Abgeschaltet heisst abgeschaltet -- dann wird der Dienst gar nicht
erst gefragt, und die Knoepfe erscheinen nirgends. */
if (!ANGESCHALTET()) {
return res.json({ da: false, angeschaltet: false, erreichbar: null, beschaeftigt: false });
}
const abbruch = new AbortController();
const uhr = setTimeout(() => abbruch.abort(), 2500);
try {
const a = await fetch(KI_URL + "/health", { signal: abbruch.signal });
res.json({ da: a.ok, beschaeftigt: laeuft });
res.json({ da: a.ok, angeschaltet: true, erreichbar: a.ok, beschaeftigt: laeuft });
} catch {
res.json({ da: false, beschaeftigt: false });
res.json({ da: false, angeschaltet: true, erreichbar: false, beschaeftigt: false });
} finally {
clearTimeout(uhr);
}
});
/* Ein- und ausschalten. Nur die Leitung -- es ist eine Entscheidung
ueber Rechenzeit, die der Website gehoert. */
kiRouter.put("/workspace/api/ki/schalter", gleicheHerkunft, express.json(), (req, res) => {
try {
if (!istLeitung(req.person)) return res.status(403).json({ fehler: "nicht_erlaubt" });
const an = req.body?.an === true || req.body?.an === "1";
einstellungSetzen("ki_an", an ? "1" : "0", { ...req.person, ip: echteIp(req) });
res.json({ ok: true, angeschaltet: an });
} catch (fehler) {
console.error("[workspace] KI-Schalter:", fehler?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
/* ---------- Aus einem Gespraech To-dos vorschlagen ----------------------- */
kiRouter.post("/workspace/api/ki/todos", gleicheHerkunft, express.json(), (req, res) => {
+38
View File
@@ -259,6 +259,18 @@ export function db() {
CREATE INDEX IF NOT EXISTS idx_aufgaben_status ON aufgaben (status);
CREATE INDEX IF NOT EXISTS idx_aufgaben_creator ON aufgaben (creator_id);
/* Einstellungen, die sich zur Laufzeit aendern lassen. Bisher nur
eine: ob die KI-Vorschlaege angeboten werden. Als Tabelle statt
als Datei, damit sie dieselbe Sicherung bekommt wie alles
andere -- eine Einstellung, die beim Wiederherstellen fehlt,
faellt erst auf, wenn etwas nicht mehr geht. */
CREATE TABLE IF NOT EXISTS einstellungen (
schluessel TEXT PRIMARY KEY,
wert TEXT NOT NULL,
geaendert TEXT,
von INTEGER REFERENCES personen(id) ON DELETE SET NULL
);
/* Rueckmeldungen zu einer Aufgabe (Konzept: "Aufgaben & Feedback").
Ohne sie endet jede Rueckfrage ausserhalb des Systems -- in
WhatsApp, und damit ausserhalb dessen, was spaeter noch
@@ -635,6 +647,7 @@ const GESCHUETZT = {
"/workspace/calls.html": null,
"/workspace/startcheck.html": null,
"/workspace/wissen.html": null,
"/workspace/automation.html": ["admin", "manager"],
};
workspaceRouter.use((req, res, next) => {
@@ -807,6 +820,31 @@ export function betreuungSetzen(creatorId, betreuerId, akteur = null) {
});
}
/* ---------- Einstellungen -------------------------------------------------
Bewusst mit Vorgabewert und ohne Fehlerfall: Faellt die Datenbank aus,
soll eine fehlende Einstellung nicht die Seite mitreissen. */
export function einstellung(schluessel, vorgabe = null) {
try {
return db().prepare("SELECT wert FROM einstellungen WHERE schluessel = ?")
.get(schluessel)?.wert ?? vorgabe;
} catch {
return vorgabe;
}
}
export function einstellungSetzen(schluessel, wert, akteur = null) {
db().prepare(`
INSERT INTO einstellungen (schluessel, wert, geaendert, von) VALUES (?,?,?,?)
ON CONFLICT(schluessel) DO UPDATE SET
wert = excluded.wert, geaendert = excluded.geaendert, von = excluded.von`).run(
schluessel, String(wert), new Date().toISOString(), akteur?.id ?? null);
protokolliere("einstellung_geaendert", {
personId: akteur?.id ?? null, rolle: akteur?.rolle ?? null, ip: akteur?.ip ?? null,
detail: `${schluessel} = ${wert}`.slice(0, 120),
});
}
export function personAnlegen(name, rolle, akteur = null) {
if (!ROLLEN.has(rolle)) throw new Error(`Unbekannte Rolle: ${rolle}`);
const code = codeErzeugen();
+123
View File
@@ -0,0 +1,123 @@
/* ===================================================================
Automationen.
Ein Zustandsbild, keine Bedienoberflaeche: Der einzige Schalter ist
der fuer die KI, weil sie als einzige Rechenzeit kostet, die der
Website gehoert. Alles andere laeuft und wird hier nur gezeigt.
=================================================================== */
.block { margin-bottom: 34px; }
.block__kopf { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.block__titel {
margin: 0; font-size: .78rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase; color: var(--akzent);
}
.block__zahl {
min-width: 22px; padding: 1px 8px; text-align: center;
font-size: .74rem; font-weight: 700; color: var(--text-still);
background: rgba(255, 255, 255, .06); border-radius: 999px;
}
.block__frage { margin: 0 0 14px; font-size: .85rem; color: var(--text-still); max-width: 76ch; }
/* ---------- KI ------------------------------------------------------------ */
.ki-kasten {
margin-bottom: 34px; padding: 20px 22px;
border: 1px solid var(--rand); border-radius: var(--radius);
background: rgba(255, 255, 255, .028);
}
.ki-kasten[data-zustand="laeuft"] {
border-color: rgba(138, 118, 255, .34);
background: rgba(138, 118, 255, .05);
}
.ki-kasten[data-zustand="weg"] {
border-color: rgba(255, 154, 162, .3);
background: rgba(255, 154, 162, .045);
}
.ki-kasten__kopf {
display: flex; gap: 20px; align-items: flex-start;
justify-content: space-between; flex-wrap: wrap;
}
.ki-kasten__titel { margin: 2px 0 6px; font-size: 1.1rem; font-weight: 700; }
.ki-kasten__text { margin: 0; font-size: .88rem; color: var(--text-leise); max-width: 62ch; }
.ki-kasten__daten {
display: grid; gap: 7px; margin: 18px 0 0; padding: 16px 0 0; list-style: none;
border-top: 1px solid var(--rand);
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.ki-kasten__daten li { display: flex; gap: 10px; font-size: .83rem; color: var(--text-leise); }
.ki-kasten__schild {
flex: none; min-width: 74px;
font-size: .7rem; font-weight: 700; letter-spacing: .07em;
text-transform: uppercase; color: var(--text-still); padding-top: 2px;
}
/* ---------- Hinweise ------------------------------------------------------- */
.hinweise { display: grid; gap: 7px; }
.hinweis {
position: relative; display: flex; align-items: center; gap: 12px;
padding: 11px 15px 11px 18px;
text-decoration: none; color: var(--text); font-size: .89rem;
border: 1px solid var(--rand); border-radius: var(--radius-klein);
background: rgba(255, 255, 255, .028);
transition: border-color .18s ease, background .18s ease;
}
.hinweis::before {
content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: 2px;
border-radius: 0 2px 2px 0; background: var(--rand);
}
.hinweis[data-stufe="warnung"] {
border-color: rgba(255, 154, 162, .3); background: rgba(255, 154, 162, .05);
}
.hinweis[data-stufe="warnung"]::before { background: var(--warn); }
.hinweis[data-stufe="offen"]::before { background: var(--akzent); opacity: .7; }
.hinweis[data-stufe="ruhig"] { opacity: .8; }
.hinweis:hover { border-color: rgba(255, 255, 255, .2); background: rgba(255, 255, 255, .05); }
.hinweis__zahl {
flex: none; min-width: 30px; text-align: center;
font-size: 1.05rem; font-weight: 700;
}
.hinweis[data-stufe="warnung"] .hinweis__zahl { color: var(--warn); }
.hinweis__text { flex: 1; color: var(--text-leise); }
.hinweis__pfeil { color: var(--text-still); }
.hinweis:hover .hinweis__pfeil { color: var(--akzent); }
/* ---------- Regeln --------------------------------------------------------- */
.regeln {
display: grid; gap: 10px;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}
.regel {
padding: 15px 17px;
border: 1px solid var(--rand); border-radius: var(--radius-klein);
background: rgba(255, 255, 255, .025);
}
.regel__name { margin: 0 0 5px; font-size: .93rem; font-weight: 650; }
.regel__text { margin: 0; font-size: .83rem; color: var(--text-still); line-height: 1.55; }
/* ---------- Verlauf -------------------------------------------------------- */
.verlauf { display: grid; gap: 2px; }
.verlauf__zeile {
display: grid; gap: 12px; align-items: baseline;
grid-template-columns: 96px minmax(0, 1fr) minmax(0, 1.2fr);
padding: 8px 12px; border-radius: var(--radius-klein);
font-size: .82rem;
}
.verlauf__zeile:nth-child(odd) { background: rgba(255, 255, 255, .022); }
.verlauf__zeit { color: var(--text-still); font-variant-numeric: tabular-nums; }
.verlauf__was { color: var(--text); }
.verlauf__wer { color: var(--text-still); overflow: hidden; text-overflow: ellipsis; }
.leer-hinweis {
padding: 26px; text-align: center; color: var(--text-still); font-size: .9rem;
border: 1px dashed var(--rand); border-radius: var(--radius);
}
@media (max-width: 700px) {
.verlauf__zeile { grid-template-columns: 1fr; gap: 2px; }
}
@media (prefers-reduced-motion: reduce) { .hinweis { transition: none; } }
+51
View File
@@ -784,3 +784,54 @@ body.suche-offen { overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
.wahl2__knopf, .wahl2__pfeil { transition: none; }
}
/* ===================================================================
Schalter (31.08.2026).
Lag zuerst in wissen.css -- und fehlte prompt auf der Automationen-
Seite, wo er als nacktes Kaestchen erschien. Genau derselbe Fehler
wie damals bei .knopf-still: Ein gemeinsamer Baustein in einer
Seiten-Datei fehlt irgendwann dort, wo die Datei nicht geladen wird.
=================================================================== */
/* ---------- Schalter „besonders wichtig“ ---------------------------------
Ein Kästchen von 17 Pixeln ist auf dem Handy kaum zu treffen und sagt
nicht, was passiert. Der Schalter ist groß, zeigt seinen Zustand und
erklärt sich in einer Zeile. */
.schalter {
display: flex; align-items: flex-start; gap: 14px;
margin-top: 18px; padding: 14px 16px; cursor: pointer;
border: 1px solid var(--rand); border-radius: var(--radius);
background: rgba(255, 255, 255, .022);
transition: border-color .18s ease, background .18s ease;
}
.schalter:hover { border-color: rgba(255, 255, 255, .2); }
.schalter input { position: absolute; opacity: 0; width: 0; height: 0; }
.schalter__gleis {
flex: none; position: relative; width: 42px; height: 24px; margin-top: 1px;
border-radius: 999px;
background: rgba(255, 255, 255, .1);
border: 1px solid var(--rand);
transition: background .2s ease, border-color .2s ease;
}
.schalter__punkt {
position: absolute; left: 3px; top: 3px; width: 16px; height: 16px;
border-radius: 50%; background: var(--text-still);
transition: transform .2s ease, background .2s ease;
}
.schalter input:checked + .schalter__gleis {
background: rgba(232, 192, 125, .3);
border-color: rgba(232, 192, 125, .6);
}
.schalter input:checked + .schalter__gleis .schalter__punkt {
transform: translateX(18px); background: #e8c07d;
}
.schalter input:focus-visible + .schalter__gleis {
box-shadow: 0 0 0 3px rgba(232, 192, 125, .28);
}
.schalter:has(input:checked) {
border-color: rgba(232, 192, 125, .35);
background: rgba(232, 192, 125, .05);
}
.schalter__text { display: grid; gap: 2px; font-size: .88rem; }
.schalter__text .leise { font-size: .79rem; }
-42
View File
@@ -242,48 +242,6 @@
.pdf-ablage__text strong { color: var(--akzent); }
.pdf-ablage__klein { margin: 0; font-size: .77rem; color: var(--text-still); }
/* ---------- Schalter „besonders wichtig“ ---------------------------------
Ein Kästchen von 17 Pixeln ist auf dem Handy kaum zu treffen und sagt
nicht, was passiert. Der Schalter ist groß, zeigt seinen Zustand und
erklärt sich in einer Zeile. */
.schalter {
display: flex; align-items: flex-start; gap: 14px;
margin-top: 18px; padding: 14px 16px; cursor: pointer;
border: 1px solid var(--rand); border-radius: var(--radius);
background: rgba(255, 255, 255, .022);
transition: border-color .18s ease, background .18s ease;
}
.schalter:hover { border-color: rgba(255, 255, 255, .2); }
.schalter input { position: absolute; opacity: 0; width: 0; height: 0; }
.schalter__gleis {
flex: none; position: relative; width: 42px; height: 24px; margin-top: 1px;
border-radius: 999px;
background: rgba(255, 255, 255, .1);
border: 1px solid var(--rand);
transition: background .2s ease, border-color .2s ease;
}
.schalter__punkt {
position: absolute; left: 3px; top: 3px; width: 16px; height: 16px;
border-radius: 50%; background: var(--text-still);
transition: transform .2s ease, background .2s ease;
}
.schalter input:checked + .schalter__gleis {
background: rgba(232, 192, 125, .3);
border-color: rgba(232, 192, 125, .6);
}
.schalter input:checked + .schalter__gleis .schalter__punkt {
transform: translateX(18px); background: #e8c07d;
}
.schalter input:focus-visible + .schalter__gleis {
box-shadow: 0 0 0 3px rgba(232, 192, 125, .28);
}
.schalter:has(input:checked) {
border-color: rgba(232, 192, 125, .35);
background: rgba(232, 192, 125, .05);
}
.schalter__text { display: grid; gap: 2px; font-size: .88rem; }
.schalter__text .leise { font-size: .79rem; }
.tag-vorschlag { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 8px; }
.tag-knopf {
+206
View File
@@ -0,0 +1,206 @@
/* ===================================================================
Automationen.
Die Kachel trug lange "Phase 4", obwohl die Automationen laengst
liefen -- sie waren nur verteilt und hatten keinen Ort. Diese Seite
ist dieser Ort.
Sie fuehrt bewusst NICHTS Eigenes: Was hier steht, kommt aus den
Bereichen selbst. Eine Seite, die Automationen doppelt abbildet,
waere die naechste Stelle, die irgendwann etwas anderes behauptet als
die Wirklichkeit.
=================================================================== */
(() => {
'use strict';
const $ = (id) => document.getElementById(id);
const el = (tag, klasse, text) => {
const k = document.createElement(tag);
if (klasse) k.className = klasse;
if (text !== undefined) k.textContent = text;
return k;
};
const melde = (t) => { $('fehler').textContent = t || ''; };
async function hole(pfad, optionen = {}) {
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
return a;
}
/* Was dauerhaft laeuft. Steht hier als Text, weil es Verhalten ist und
keine Daten -- aber jede Zeile beschreibt etwas, das tatsaechlich im
Code steht, nicht etwas Gewuenschtes. */
const REGELN = [
{ name: 'Hinweise auf der Übersicht',
text: 'Überfälliges, heute Fälliges, Gespräche ohne Protokoll, fällige Follow-ups und dringende Punkte werden bei jedem Aufruf neu ermittelt. Sie führen keine eigene Liste – ein Hinweis verschwindet von selbst, sobald die Sache erledigt ist.' },
{ name: '„Neu" in der Bibliothek',
text: 'Eine frisch eingestellte Anleitung steht 48 Stunden unter „Neu & wichtig“ und ist danach nur noch in ihrer Kategorie zu finden. Angepinntes bleibt, bis es jemand abschaltet.' },
{ name: 'Zuständigkeit bei der Übergabe',
text: 'Wird aus einem übergebenen Lead ein Creator angelegt, betreut ihn automatisch der Scout, der ihn gefunden hat. Die Angaben aus der Pipeline wandern dabei ins Creator-Profil.' },
{ name: 'Aufgaben aus Gesprächen',
text: 'To-dos aus einem Protokoll, Entscheidungen aus einem Review und Befunde aus dem Start-Check werden zu echten Aufgaben im Brett – in einem Zug, nicht als Text, den man später abtippt.' },
{ name: 'Sitzungen laufen ab',
text: 'Eine Anmeldung gilt zwölf Stunden. Nach einem neuen Zugangscode oder einer Sperre enden alle offenen Sitzungen der Person sofort.' },
{ name: 'Schutz vor Rateversuchen',
text: 'Acht Fehlversuche je Anschluss in zehn Minuten, danach ist zu. Gezählt wird die echte Adresse, nicht die von Cloudflare.' },
{ name: 'Sicherung vor Umbauten',
text: 'Bevor die Datenbank umgestellt wird, schreibt der Server eine vollständige Sicherung. Ohne Sicherung wird nicht umgestellt.' },
];
const ART = {
ki_gefragt: 'KI um einen Vorschlag gebeten',
einstellung_geaendert: 'Einstellung geändert',
betreuung_gesetzt: 'Zuständigkeit gesetzt',
onboarding_gestartet: 'Creator aus einem Lead angelegt',
protokoll_geschrieben: 'Gesprächsprotokoll festgehalten',
startcheck_aufgabe: 'Aufgabe aus dem Start-Check',
review_entscheidung: 'Aufgabe aus dem Review',
anmeldung_gesperrt: 'Anmeldung wegen zu vieler Versuche gesperrt',
};
const zeitpunkt = (iso) => {
if (!iso) return '';
const d = new Date(iso);
const p = (n) => String(n).padStart(2, '0');
return `${p(d.getDate())}.${p(d.getMonth() + 1)}. · ${p(d.getHours())}:${p(d.getMinutes())}`;
};
/* ---------- KI ---------------------------------------------------------- */
async function kiZustand() {
let d;
try { d = await (await hole('/workspace/api/ki/status')).json(); } catch { return; }
$('ki-schalter-block').hidden = false;
$('ki-schalter').checked = d.angeschaltet !== false;
$('ki-schalter-text').textContent = d.angeschaltet !== false ? 'Angeschaltet' : 'Abgeschaltet';
$('ki-kasten').dataset.zustand = d.da ? 'laeuft' : (d.angeschaltet === false ? 'aus' : 'weg');
$('ki-text').textContent = d.angeschaltet === false
? 'Abgeschaltet. Die Vorschlags-Knöpfe erscheinen nirgends, und der Dienst wird nicht gefragt.'
: d.da
? 'Läuft. Die Vorschlags-Knöpfe erscheinen in Calls, Start-Check und Report.'
: 'Angeschaltet, aber gerade nicht erreichbar. Alles andere funktioniert normal weiter.';
const daten = $('ki-daten');
daten.textContent = '';
for (const [name, wert] of [
['Modell', 'Qwen3.5-4B, Apache 2.0'],
['Ort', 'auf diesem Server, nur über 127.0.0.1'],
['Kosten', '0 € – nichts verlässt die Maschine'],
['Grenzen', 'zwei von vier Kernen, eine Anfrage gleichzeitig, fünf je Person in zehn Minuten'],
['Zustand', d.beschaeftigt ? 'denkt gerade' : (d.da ? 'bereit' : '–')],
]) {
const li = el('li');
li.append(el('span', 'ki-kasten__schild', name), el('span', null, wert));
daten.append(li);
}
}
$('ki-schalter').addEventListener('change', async () => {
const an = $('ki-schalter').checked;
$('ki-schalter').disabled = true;
try {
const a = await hole('/workspace/api/ki/schalter', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ an }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
$('ki-schalter').checked = !an;
return;
}
melde('');
await kiZustand();
} catch { /* umgeleitet */ } finally { $('ki-schalter').disabled = false; }
});
/* ---------- Hinweise ----------------------------------------------------- */
const HINWEISTEXT = {
aufgaben_ueberfaellig: 'überfällige Aufgaben', aufgaben_heute: 'heute fällige Aufgaben',
aufgaben_review: 'Aufgaben warten auf Freigabe', termine_heute: 'Termine heute',
protokoll_fehlt: 'Gespräche ohne Protokoll', dateien_review: 'Dateien im Review',
bereiche_dringend: 'dringende Punkte in den Bereichen', startcheck_handlung: 'Punkte im Start-Check',
followup_faellig: 'fällige Follow-ups', uebergabe_offen: 'übergebene Leads ohne Creator',
review_faellig: 'überfällige Review-Termine', review_bald: 'Reviews in den nächsten 7 Tagen',
ohne_betreuung: 'Creator ohne zuständige Person', ohne_profil: 'leere Creator-Profile',
ohne_startcheck: 'Creator ohne Start-Check',
};
async function hinweise() {
let d;
try { d = await (await hole('/workspace/api/hinweise')).json(); } catch { return; }
const ziel = $('hinweise');
ziel.textContent = '';
$('hinweis-zahl').textContent = String(d.hinweise.length);
if (!d.hinweise.length) {
ziel.append(el('p', 'leer-hinweis', 'Nichts offen. Alles läuft.'));
return;
}
const liste = el('div', 'hinweise');
for (const h of d.hinweise) {
const a = el('a', 'hinweis');
a.href = h.ziel;
a.dataset.stufe = h.stufe;
a.append(el('span', 'hinweis__zahl', String(h.anzahl)));
a.append(el('span', 'hinweis__text', HINWEISTEXT[h.art] || h.text));
a.append(el('span', 'hinweis__pfeil', '→'));
liste.append(a);
}
ziel.append(liste);
}
/* ---------- Verlauf ------------------------------------------------------ */
async function verlauf() {
const ziel = $('verlauf');
let d;
try {
const a = await hole('/workspace/api/verwaltung/protokoll?n=40');
if (!a.ok) { ziel.textContent = ''; ziel.append(el('p', 'leise', 'Kein Zugriff auf das Protokoll.')); return; }
d = await a.json();
} catch { return; }
/* Nur die Eintraege, die tatsaechlich aus einer Automation stammen --
Anmeldungen und Handarbeit gehoeren hier nicht hin. */
const eintraege = (d.eintraege || d.protokoll || []).filter((e) => ART[e.aktion]).slice(0, 12);
ziel.textContent = '';
if (!eintraege.length) {
ziel.append(el('p', 'leer-hinweis', 'Noch nichts passiert, was von allein lief.'));
return;
}
const liste = el('div', 'verlauf');
for (const e of eintraege) {
const z = el('div', 'verlauf__zeile');
z.append(el('span', 'verlauf__zeit', zeitpunkt(e.zeitpunkt)));
z.append(el('span', 'verlauf__was', ART[e.aktion]));
z.append(el('span', 'verlauf__wer', [e.wer, e.detail].filter(Boolean).join(' · ')));
liste.append(z);
}
ziel.append(liste);
}
/* ---------- Start --------------------------------------------------------- */
const regeln = $('regeln');
for (const r of REGELN) {
const k = el('div', 'regel');
k.append(el('h3', 'regel__name', r.name));
k.append(el('p', 'regel__text', r.text));
regeln.append(k);
}
(async () => {
let ich;
try {
const a = await hole('/workspace/api/ich');
if (!a.ok) { location.assign('/workspace/'); return; }
ich = await a.json();
} catch { return; }
$('wer').textContent = ich.name + ' · ' + (ich.rolle_name || ich.rolle);
await Promise.all([kiZustand(), hinweise(), verlauf()]);
})();
})();
+6 -3
View File
@@ -15,9 +15,12 @@
/* Bereiche aus dem Konzept, mit der Phase aus dem Umsetzungsplan.
`rollen` steuert, wem ein Bereich überhaupt angezeigt wird - schon
jetzt, damit die Trennung von Anfang an sichtbar ist. */
jetzt, damit die Trennung von Anfang an sichtbar ist.
"Dashboard" stand hier als Kachel, die man nicht öffnen kann -- man
ist ja schon darauf. Ein Eintrag, der nirgendwohin führt, sieht aus
wie etwas Unfertiges. Raus. */
const BEREICHE = [
{ name: 'Dashboard', phase: 1, rollen: ['admin', 'manager', 'creator', 'scout'] },
{ name: 'Personen & Zugänge', phase: 1, rollen: ['admin', 'manager'], ziel: 'personen.html' },
{ name: 'Creator-Profile', phase: 1, rollen: ['admin', 'manager', 'scout'], ziel: 'profil.html' },
{ name: 'Mein Profil', phase: 1, rollen: ['creator'], ziel: 'profil.html' },
@@ -34,7 +37,7 @@
{ name: 'Reports & Review', phase: 2, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'report.html' },
{ name: 'Calls & Protokolle', phase: 3, rollen: ['admin', 'manager', 'creator', 'scout'], ziel: 'calls.html' },
{ name: 'Scout-Pipeline', phase: 3, rollen: ['admin', 'manager', 'scout'], ziel: 'scouting.html' },
{ name: 'Automationen', phase: 4, rollen: ['admin', 'manager'] },
{ name: 'Automationen', phase: 4, rollen: ['admin', 'manager'], ziel: 'automation.html' },
];
const $ = (id) => document.getElementById(id);
+6 -6
View File
@@ -7,9 +7,9 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
</head>
<body class="start">
@@ -134,8 +134,8 @@
</form>
</dialog>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/aufgaben.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/aufgaben.js?v=202608311245" defer></script>
</body>
</html>
+100
View File
@@ -0,0 +1,100 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>Automationen · Creator Workspace</title>
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/automation.css?v=202608311245" />
</head>
<body class="start">
<header class="kopfleiste">
<button type="button" class="zurueck-knopf" id="zurueck" hidden>
<svg class="zurueck-knopf__pfeil" viewBox="0 0 24 24" aria-hidden="true">
<path d="M14.5 5.5 8 12l6.5 6.5" />
</svg>
<span class="zurueck-knopf__text">Zurück</span>
</button>
<p class="marke"><a class="zurueck" href="start.html">Creator&nbsp;Workspace</a> · Automationen</p>
<div class="kopfleiste__rechts">
<span class="wer" id="wer">…</span>
<button type="button" class="abmelden" id="abmelden">Abmelden</button>
</div>
</header>
<main class="inhalt inhalt--breit">
<section class="kopf-zeile">
<div>
<p class="marke">Was von allein passiert</p>
<h1 class="titel">Automationen</h1>
<p class="unterzeile">
Alles hier läuft ohne Zutun. Diese Seite zeigt, was gerade greift –
und wo du es abschalten kannst.
</p>
</div>
</section>
<p class="fehler" id="fehler" role="alert" aria-live="polite"></p>
<!-- Die KI ist die einzige Automation mit Schalter: Sie kostet
Rechenzeit, die der Website gehört. -->
<section class="ki-kasten" id="ki-kasten">
<div class="ki-kasten__kopf">
<div>
<p class="marke">Lokale KI</p>
<h2 class="ki-kasten__titel">Vorschläge in Calls, Start-Check und Report</h2>
<p class="ki-kasten__text" id="ki-text">Zustand wird geprüft …</p>
</div>
<label class="schalter" id="ki-schalter-block" hidden>
<input type="checkbox" id="ki-schalter" />
<span class="schalter__gleis"><span class="schalter__punkt"></span></span>
<span class="schalter__text"><strong id="ki-schalter-text">Angeschaltet</strong></span>
</label>
</div>
<ul class="ki-kasten__daten" id="ki-daten"></ul>
</section>
<!-- Was gerade tatsächlich anliegt: dieselbe Quelle wie auf dem
Dashboard, hier aber vollständig statt gekürzt. -->
<section class="block">
<div class="block__kopf">
<h2 class="block__titel">Was gerade anliegt</h2>
<span class="block__zahl" id="hinweis-zahl">–</span>
</div>
<p class="block__frage">
Dieselben Hinweise wie auf der Übersicht, hier vollständig. Sie führen keine
eigene Liste – jeder verschwindet, sobald die Sache erledigt ist.
</p>
<div id="hinweise"><p class="leise">Wird geprüft …</p></div>
</section>
<section class="block">
<div class="block__kopf">
<h2 class="block__titel">Läuft dauerhaft im Hintergrund</h2>
</div>
<div class="regeln" id="regeln"></div>
</section>
<section class="block">
<div class="block__kopf">
<h2 class="block__titel">Zuletzt automatisch passiert</h2>
</div>
<p class="block__frage">Aus dem Protokoll – damit nachvollziehbar bleibt, was ohne Zutun geschah.</p>
<div id="verlauf"><p class="leise">Wird geladen …</p></div>
</section>
</main>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/automation.js?v=202608311245" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202608311245" />
</head>
<body class="start">
@@ -98,8 +98,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/bereich.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/bereich.js?v=202608311245" defer></script>
</body>
</html>
+8 -8
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/calls.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/calls.css?v=202608311245" />
</head>
<body class="start">
@@ -49,9 +49,9 @@
</main>
<script src="assets/js/ki.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/calls.js?v=202608311235" defer></script>
<script src="assets/js/ki.js?v=202608311245" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/calls.js?v=202608311245" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202608311245" />
</head>
<body class="start">
@@ -88,8 +88,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/dateien.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/dateien.js?v=202608311245" defer></script>
</body>
</html>
+2 -2
View File
@@ -25,7 +25,7 @@
type="image/webp" media="(max-width: 899px)" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
</head>
<body class="gate">
@@ -143,6 +143,6 @@
</p>
</main>
<script src="assets/js/gate.js?v=202608311235" defer></script>
<script src="assets/js/gate.js?v=202608311245" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202608311245" />
</head>
<body class="start">
@@ -94,8 +94,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/kalender.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/kalender.js?v=202608311245" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/personen.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/personen.css?v=202608311245" />
</head>
<body class="start">
@@ -93,8 +93,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/personen.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/personen.js?v=202608311245" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/profil.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/profil.css?v=202608311245" />
</head>
<body class="start">
@@ -127,8 +127,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/profil.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/profil.js?v=202608311245" defer></script>
</body>
</html>
+8 -8
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/report.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/report.css?v=202608311245" />
</head>
<body class="start">
@@ -86,9 +86,9 @@
</main>
<script src="assets/js/ki.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/report.js?v=202608311235" defer></script>
<script src="assets/js/ki.js?v=202608311245" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/report.js?v=202608311245" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202608311245" />
</head>
<body class="start">
@@ -124,8 +124,8 @@
</div>
</div>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/scouting.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/scouting.js?v=202608311245" defer></script>
</body>
</html>
+5 -5
View File
@@ -7,8 +7,8 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
</head>
<body class="start">
@@ -56,8 +56,8 @@
</p>
</main>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/start.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/start.js?v=202608311245" defer></script>
</body>
</html>
+8 -8
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202608311245" />
</head>
<body class="start">
@@ -65,9 +65,9 @@
</main>
<script src="assets/js/ki.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/startcheck.js?v=202608311235" defer></script>
<script src="assets/js/ki.js?v=202608311245" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/startcheck.js?v=202608311245" defer></script>
</body>
</html>
+7 -7
View File
@@ -7,10 +7,10 @@
<meta name="robots" content="noindex, nofollow" />
<meta name="theme-color" content="#05070d" />
<link rel="icon" href="assets/img/favicon.png" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202608311235" />
<link rel="stylesheet" href="assets/css/gate.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/start.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311245" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202608311245" />
</head>
<body class="start">
@@ -170,8 +170,8 @@
</main>
<script src="assets/js/wahl.js?v=202608311235" defer></script>
<script src="assets/js/kopf.js?v=202608311235" defer></script>
<script src="assets/js/wissen.js?v=202608311235" defer></script>
<script src="assets/js/wahl.js?v=202608311245" defer></script>
<script src="assets/js/kopf.js?v=202608311245" defer></script>
<script src="assets/js/wissen.js?v=202608311245" defer></script>
</body>
</html>