Aufgaben: Rueckmeldungen ("Aufgaben & Feedback")
Letzter offener Punkt aus dem Konzept, Seite 4: Ein Creator hat laut Rollenbeschreibung "Aufgaben & Feedback" -- die Aufgaben gab es, das Feedback nicht. Ohne diese Moeglichkeit endet jede Rueckfrage ausserhalb des Systems, in WhatsApp, und damit ausserhalb dessen, was spaeter noch nachvollziehbar ist. Neue Tabelle aufgaben_notizen. Wer die Aufgabe sehen darf, darf auch mitreden -- geprueft ueber DIESELBE Sichtbarkeitsregel wie fuer die Aufgabe selbst. Eine eigene Regel gibt es bewusst nicht: Zwei Regeln fuer dieselbe Sache laufen irgendwann auseinander, und die Aufgaben- sichtbarkeit ist die schwierigere von beiden (Creator sieht eigene, Scout die seiner betreuten Creator, Leitung alles). 404 statt 403, wenn die Aufgabe nicht sichtbar ist -- wer sie nicht sehen darf, soll nicht erfahren, dass es sie gibt. Die eigene Rueckmeldung darf jeder zuruecknehmen, fremde nur die Leitung. Eine Rueckmeldung ist keine Abstimmung: Wer sich vertippt hat, soll das nicht bei jemandem beantragen muessen. Der Knopf auf der Karte zeigt die ANZAHL, nicht nur ein Symbol -- so sieht man ohne Aufklappen, wo schon etwas besprochen wurde, und die Karte bleibt trotzdem ruhig. Aufgeklappt wird direkt an der Karte, nicht in einem Fenster: Man liest die Rueckmeldungen im Zusammenhang mit der Aufgabe, sonst fehlt beim Antworten die Haelfte. Geprueft: - Chef und Luna schreiben sich gegenseitig, beide sehen beides - Mika (fremder Creator) bekommt 404 beim Lesen UND beim Schreiben - ohne Anmeldung 401, fremde Herkunft 403, leerer Text abgewiesen - Luna kann Chefs Rueckmeldung nicht loeschen, ihre eigene schon - Chef kann alle loeschen - Zaehler auf der Karte stimmt, Handy ohne Ueberlauf, 0 Konsolenfehler
This commit is contained in:
@@ -91,7 +91,9 @@ aufgabenRouter.get("/workspace/api/aufgaben", (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const { wo, werte } = sichtbar(req.person);
|
const { wo, werte } = sichtbar(req.person);
|
||||||
const reihen = db().prepare(`
|
const reihen = db().prepare(`
|
||||||
SELECT ${SPALTEN} ${VERBUND}
|
SELECT ${SPALTEN},
|
||||||
|
(SELECT COUNT(*) FROM aufgaben_notizen n WHERE n.aufgabe_id = a.id) AS notizen
|
||||||
|
${VERBUND}
|
||||||
WHERE ${wo}
|
WHERE ${wo}
|
||||||
ORDER BY
|
ORDER BY
|
||||||
CASE a.prioritaet WHEN 'hoch' THEN 0 WHEN 'mittel' THEN 1 ELSE 2 END,
|
CASE a.prioritaet WHEN 'hoch' THEN 0 WHEN 'mittel' THEN 1 ELSE 2 END,
|
||||||
@@ -186,6 +188,93 @@ function pruefeFelder(körper, { neu }) {
|
|||||||
return { aus, fehler };
|
return { aus, fehler };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Rueckmeldungen ("Aufgaben & Feedback") ------------------------
|
||||||
|
Wer die Aufgabe sieht, darf mitreden. Geprueft wird das ueber dieselbe
|
||||||
|
Sichtbarkeitsregel wie fuer die Aufgabe selbst -- eine zweite Regel
|
||||||
|
waere eine zweite Stelle, an der es irgendwann auseinanderlaeuft.
|
||||||
|
|
||||||
|
404 statt 403, wenn die Aufgabe nicht sichtbar ist: Wer sie nicht
|
||||||
|
sehen darf, soll auch nicht erfahren, dass es sie gibt. */
|
||||||
|
|
||||||
|
const NOTIZ_MAX = 2000;
|
||||||
|
|
||||||
|
function aufgabeSichtbar(person, id) {
|
||||||
|
const { wo, werte } = sichtbar(person);
|
||||||
|
return db().prepare(`SELECT a.id FROM aufgaben a WHERE ${wo} AND a.id = ?`).get(...werte, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
aufgabenRouter.get("/workspace/api/aufgaben/:id/notizen", (req, res) => {
|
||||||
|
try {
|
||||||
|
const id = Number(req.params.id);
|
||||||
|
if (!Number.isInteger(id)) return res.status(400).json({ fehler: "ungueltig" });
|
||||||
|
if (!aufgabeSichtbar(req.person, id)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
notizen: db().prepare(`
|
||||||
|
SELECT n.id, n.text, n.erstellt, n.person_id, p.name AS von, p.rolle AS rolle
|
||||||
|
FROM aufgaben_notizen n LEFT JOIN personen p ON p.id = n.person_id
|
||||||
|
WHERE n.aufgabe_id = ? ORDER BY n.id`).all(id),
|
||||||
|
ich: req.person.id,
|
||||||
|
darf_alles_loeschen: istLeitung(req.person),
|
||||||
|
});
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[workspace] Notizen lesen:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
aufgabenRouter.post("/workspace/api/aufgaben/:id/notizen", gleicheHerkunft, (req, res) => {
|
||||||
|
try {
|
||||||
|
const id = Number(req.params.id);
|
||||||
|
if (!Number.isInteger(id)) return res.status(400).json({ fehler: "ungueltig" });
|
||||||
|
if (!aufgabeSichtbar(req.person, id)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
|
||||||
|
const text = String(req.body?.text ?? "").trim().slice(0, NOTIZ_MAX);
|
||||||
|
if (text.length < 2) return res.status(400).json({ fehler: "Schreib erst etwas." });
|
||||||
|
|
||||||
|
const { lastInsertRowid } = db().prepare(
|
||||||
|
"INSERT INTO aufgaben_notizen (aufgabe_id, person_id, text, erstellt) VALUES (?,?,?,?)")
|
||||||
|
.run(id, req.person.id, text, jetzt());
|
||||||
|
|
||||||
|
protokolliere("aufgabe_notiz", {
|
||||||
|
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||||
|
detail: `Aufgabe #${id}`.slice(0, 120),
|
||||||
|
});
|
||||||
|
res.status(201).json({ id: Number(lastInsertRowid) });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[workspace] Notiz schreiben:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
aufgabenRouter.delete("/workspace/api/notizen/:id", gleicheHerkunft, (req, res) => {
|
||||||
|
try {
|
||||||
|
const id = Number(req.params.id);
|
||||||
|
if (!Number.isInteger(id)) return res.status(400).json({ fehler: "ungueltig" });
|
||||||
|
const notiz = db().prepare(
|
||||||
|
"SELECT id, aufgabe_id, person_id FROM aufgaben_notizen WHERE id = ?").get(id);
|
||||||
|
if (!notiz) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
if (!aufgabeSichtbar(req.person, notiz.aufgabe_id)) {
|
||||||
|
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
}
|
||||||
|
/* Die eigene Rueckmeldung darf jeder zuruecknehmen, fremde nur die
|
||||||
|
Leitung. Eine Rueckmeldung ist keine Abstimmung -- wer sich
|
||||||
|
vertippt hat, soll das nicht bei jemandem beantragen muessen. */
|
||||||
|
if (notiz.person_id !== req.person.id && !istLeitung(req.person)) {
|
||||||
|
return res.status(403).json({ fehler: "Fremde Rückmeldungen löscht nur die Leitung." });
|
||||||
|
}
|
||||||
|
db().prepare("DELETE FROM aufgaben_notizen WHERE id = ?").run(id);
|
||||||
|
protokolliere("aufgabe_notiz_geloescht", {
|
||||||
|
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||||
|
detail: `#${id} zu Aufgabe #${notiz.aufgabe_id}`.slice(0, 120),
|
||||||
|
});
|
||||||
|
res.json({ ok: true });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[workspace] Notiz löschen:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
aufgabenRouter.post("/workspace/api/aufgaben", gleicheHerkunft, (req, res) => {
|
aufgabenRouter.post("/workspace/api/aufgaben", gleicheHerkunft, (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { aus, fehler } = pruefeFelder(req.body || {}, { neu: true });
|
const { aus, fehler } = pruefeFelder(req.body || {}, { neu: true });
|
||||||
|
|||||||
@@ -259,6 +259,23 @@ export function db() {
|
|||||||
CREATE INDEX IF NOT EXISTS idx_aufgaben_status ON aufgaben (status);
|
CREATE INDEX IF NOT EXISTS idx_aufgaben_status ON aufgaben (status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_aufgaben_creator ON aufgaben (creator_id);
|
CREATE INDEX IF NOT EXISTS idx_aufgaben_creator ON aufgaben (creator_id);
|
||||||
|
|
||||||
|
/* Rueckmeldungen zu einer Aufgabe (Konzept: "Aufgaben & Feedback").
|
||||||
|
Ohne sie endet jede Rueckfrage ausserhalb des Systems -- in
|
||||||
|
WhatsApp, und damit ausserhalb dessen, was spaeter noch
|
||||||
|
nachvollziehbar ist.
|
||||||
|
Wer die Aufgabe sehen darf, darf auch die Rueckmeldungen sehen
|
||||||
|
und schreiben. Eine eigene Sichtbarkeitsregel gibt es bewusst
|
||||||
|
NICHT: Zwei Regeln fuer dieselbe Sache laufen irgendwann
|
||||||
|
auseinander. */
|
||||||
|
CREATE TABLE IF NOT EXISTS aufgaben_notizen (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
aufgabe_id INTEGER NOT NULL REFERENCES aufgaben(id) ON DELETE CASCADE,
|
||||||
|
person_id INTEGER REFERENCES personen(id) ON DELETE SET NULL,
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
erstellt TEXT NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_aufgaben_notizen ON aufgaben_notizen (aufgabe_id);
|
||||||
|
|
||||||
/* Creator-Profil (Onboarding aus dem Konzept). Eine Zeile je
|
/* Creator-Profil (Onboarding aus dem Konzept). Eine Zeile je
|
||||||
Creator, entsteht erst beim ersten Speichern.
|
Creator, entsteht erst beim ersten Speichern.
|
||||||
admin_notiz ist bewusst Teil dieser Tabelle, wird aber nur an
|
admin_notiz ist bewusst Teil dieser Tabelle, wird aber nur an
|
||||||
|
|||||||
@@ -165,3 +165,70 @@
|
|||||||
.karte__stift { margin-left: auto; }
|
.karte__stift { margin-left: auto; }
|
||||||
.karte__kopf { display: flex; align-items: flex-start; gap: 8px; }
|
.karte__kopf { display: flex; align-items: flex-start; gap: 8px; }
|
||||||
.karte__kopf .karte__titel { flex: 1 1 auto; }
|
.karte__kopf .karte__titel { flex: 1 1 auto; }
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
Rückmeldungen an einer Aufgabe ("Aufgaben & Feedback", Konzept).
|
||||||
|
|
||||||
|
Direkt an der Karte, nicht in einem Fenster: Man liest sie im
|
||||||
|
Zusammenhang mit der Aufgabe -- sonst fehlt beim Antworten die
|
||||||
|
Hälfte.
|
||||||
|
=================================================================== */
|
||||||
|
|
||||||
|
.karte__rueck {
|
||||||
|
display: inline-flex; align-items: center; gap: 7px;
|
||||||
|
margin-top: 10px; padding: 6px 11px; cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--rand); border-radius: 999px;
|
||||||
|
color: var(--text-still); font: inherit; font-size: .76rem;
|
||||||
|
transition: color .16s ease, border-color .16s ease, background .16s ease;
|
||||||
|
}
|
||||||
|
.karte__rueck:hover { color: var(--text); border-color: rgba(255, 255, 255, .24); }
|
||||||
|
/* Wo schon etwas besprochen wurde, sieht man es ohne Aufklappen. */
|
||||||
|
.karte__rueck[data-hat="ja"] {
|
||||||
|
color: var(--akzent);
|
||||||
|
border-color: rgba(63, 189, 245, .38);
|
||||||
|
background: rgba(63, 189, 245, .08);
|
||||||
|
}
|
||||||
|
.karte__rueck[data-offen="ja"] { border-color: rgba(63, 189, 245, .6); color: #fff; }
|
||||||
|
.karte__blase {
|
||||||
|
width: 14px; height: 14px; flex: none;
|
||||||
|
fill: none; stroke: currentColor; stroke-width: 1.7;
|
||||||
|
stroke-linecap: round; stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rueck {
|
||||||
|
margin-top: 11px; padding-top: 11px;
|
||||||
|
border-top: 1px solid var(--rand);
|
||||||
|
}
|
||||||
|
.rueck__liste { display: grid; gap: 8px; margin-bottom: 11px; }
|
||||||
|
.rueck__eintrag {
|
||||||
|
padding: 9px 12px;
|
||||||
|
background: rgba(255, 255, 255, .035);
|
||||||
|
border: 1px solid var(--rand); border-radius: var(--radius-klein);
|
||||||
|
}
|
||||||
|
.rueck__kopf { display: flex; align-items: baseline; gap: 8px; margin: 0 0 3px; }
|
||||||
|
.rueck__wer { font-size: .79rem; font-weight: 650; color: var(--text); }
|
||||||
|
.rueck__wann { font-size: .72rem; color: var(--text-still); }
|
||||||
|
.rueck__weg {
|
||||||
|
margin-left: auto; padding: 0 5px; cursor: pointer; line-height: 1;
|
||||||
|
background: none; border: 0; color: var(--text-still); font-size: 1rem;
|
||||||
|
}
|
||||||
|
.rueck__weg:hover { color: #ffb4b9; }
|
||||||
|
.rueck__text {
|
||||||
|
margin: 0; font-size: .85rem; color: var(--text-leise);
|
||||||
|
white-space: pre-wrap; line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rueck__form { display: grid; gap: 8px; justify-items: start; }
|
||||||
|
.rueck__feld {
|
||||||
|
width: 100%; padding: 9px 12px;
|
||||||
|
background: rgba(255, 255, 255, .045);
|
||||||
|
border: 1px solid var(--rand); border-radius: var(--radius-klein);
|
||||||
|
color: var(--text); font: inherit; font-size: .86rem; resize: vertical;
|
||||||
|
}
|
||||||
|
.rueck__feld:focus {
|
||||||
|
outline: none; border-color: rgba(63, 189, 245, .65);
|
||||||
|
box-shadow: 0 0 0 3px rgba(63, 189, 245, .15);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) { .karte__rueck { transition: none; } }
|
||||||
|
|||||||
@@ -49,6 +49,93 @@
|
|||||||
return `${t}.${m}.${j.slice(2)}`;
|
return `${t}.${m}.${j.slice(2)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Rückmeldungen ------------------------------------------------
|
||||||
|
Direkt an der Karte, nicht in einem Fenster: Man liest sie im
|
||||||
|
Zusammenhang mit der Aufgabe, sonst fehlt beim Antworten die Hälfte. */
|
||||||
|
|
||||||
|
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())}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function rueckmeldungen(karteEl, a, knopfEl) {
|
||||||
|
const vorhanden = karteEl.querySelector('.rueck');
|
||||||
|
if (vorhanden) { vorhanden.remove(); delete knopfEl.dataset.offen; return; }
|
||||||
|
knopfEl.dataset.offen = 'ja';
|
||||||
|
|
||||||
|
const box = el('div', 'rueck');
|
||||||
|
box.append(el('p', 'leise', 'Wird geladen …'));
|
||||||
|
karteEl.append(box);
|
||||||
|
|
||||||
|
let daten;
|
||||||
|
try {
|
||||||
|
const antwort = await hole(`/workspace/api/aufgaben/${a.id}/notizen`);
|
||||||
|
if (!antwort.ok) {
|
||||||
|
box.textContent = '';
|
||||||
|
box.append(el('p', 'fehler', 'Konnte nicht geladen werden.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
daten = await antwort.json();
|
||||||
|
} catch { return; }
|
||||||
|
|
||||||
|
box.textContent = '';
|
||||||
|
const liste = el('div', 'rueck__liste');
|
||||||
|
if (!daten.notizen.length) liste.append(el('p', 'leise', 'Noch nichts besprochen.'));
|
||||||
|
for (const n of daten.notizen) {
|
||||||
|
const z = el('div', 'rueck__eintrag');
|
||||||
|
const kopf = el('p', 'rueck__kopf');
|
||||||
|
kopf.append(el('span', 'rueck__wer', n.von || 'Unbekannt'));
|
||||||
|
kopf.append(el('span', 'rueck__wann', zeitpunkt(n.erstellt)));
|
||||||
|
/* Die eigene Rückmeldung darf jeder zurücknehmen, fremde nur die
|
||||||
|
Leitung -- wer sich vertippt hat, soll das nicht beantragen
|
||||||
|
müssen. */
|
||||||
|
if (n.person_id === daten.ich || daten.darf_alles_loeschen) {
|
||||||
|
const weg = el('button', 'rueck__weg', '×');
|
||||||
|
weg.type = 'button';
|
||||||
|
weg.title = 'Rückmeldung entfernen';
|
||||||
|
weg.setAttribute('aria-label', 'Rückmeldung entfernen');
|
||||||
|
weg.addEventListener('click', async () => {
|
||||||
|
if (!confirm('Diese Rückmeldung entfernen?')) return;
|
||||||
|
const r = await hole('/workspace/api/notizen/' + n.id, { method: 'DELETE' });
|
||||||
|
if (!r.ok) { melde('Entfernen hat nicht geklappt.'); return; }
|
||||||
|
await laden();
|
||||||
|
});
|
||||||
|
kopf.append(weg);
|
||||||
|
}
|
||||||
|
z.append(kopf, el('p', 'rueck__text', n.text));
|
||||||
|
liste.append(z);
|
||||||
|
}
|
||||||
|
box.append(liste);
|
||||||
|
|
||||||
|
const form = el('form', 'rueck__form');
|
||||||
|
const feld = el('textarea', 'rueck__feld');
|
||||||
|
feld.rows = 2;
|
||||||
|
feld.maxLength = 2000;
|
||||||
|
feld.placeholder = 'Rückmeldung schreiben …';
|
||||||
|
const senden = el('button', 'knopf knopf--klein', 'Abschicken');
|
||||||
|
senden.type = 'submit';
|
||||||
|
form.append(feld, senden);
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const text = feld.value.trim();
|
||||||
|
if (text.length < 2) return;
|
||||||
|
senden.disabled = true;
|
||||||
|
try {
|
||||||
|
const r = await hole(`/workspace/api/aufgaben/${a.id}/notizen`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ text }),
|
||||||
|
});
|
||||||
|
if (!r.ok) { melde((await r.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
|
||||||
|
await laden();
|
||||||
|
} catch { /* umgeleitet */ } finally { senden.disabled = false; }
|
||||||
|
});
|
||||||
|
box.append(form);
|
||||||
|
feld.focus();
|
||||||
|
}
|
||||||
|
|
||||||
function karte(a) {
|
function karte(a) {
|
||||||
const k = el('article', 'karte');
|
const k = el('article', 'karte');
|
||||||
k.dataset.prio = a.prioritaet;
|
k.dataset.prio = a.prioritaet;
|
||||||
@@ -80,6 +167,23 @@
|
|||||||
if (ZURUECK[a.status]) schritte.append(knopf('◀ zurück', a, ZURUECK[a.status]));
|
if (ZURUECK[a.status]) schritte.append(knopf('◀ zurück', a, ZURUECK[a.status]));
|
||||||
if (WEITER[a.status]) schritte.append(knopf(beschriftung(a.status), a, WEITER[a.status]));
|
if (WEITER[a.status]) schritte.append(knopf(beschriftung(a.status), a, WEITER[a.status]));
|
||||||
if (schritte.childElementCount) k.append(schritte);
|
if (schritte.childElementCount) k.append(schritte);
|
||||||
|
|
||||||
|
/* Rückmeldungen ("Aufgaben & Feedback" aus dem Konzept). Der Knopf
|
||||||
|
zeigt gleich die Anzahl -- so sieht man ohne Aufklappen, wo etwas
|
||||||
|
besprochen wurde. */
|
||||||
|
const rueck = el('button', 'karte__rueck');
|
||||||
|
rueck.type = 'button';
|
||||||
|
rueck.dataset.hat = a.notizen ? 'ja' : 'nein';
|
||||||
|
const blase = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
|
blase.setAttribute('viewBox', '0 0 24 24');
|
||||||
|
blase.setAttribute('class', 'karte__blase');
|
||||||
|
blase.setAttribute('aria-hidden', 'true');
|
||||||
|
blase.innerHTML = '<path d="M20.4 12.4c0 3.8-3.8 6.9-8.4 6.9-1 0-2-.15-2.9-.42L4 20.4l1.6-3.6a6.6 6.6 0 0 1-2-4.4C3.6 8.6 7.4 5.5 12 5.5s8.4 3.1 8.4 6.9z"/>';
|
||||||
|
rueck.append(blase, el('span', null, a.notizen
|
||||||
|
? `${a.notizen} ${a.notizen === 1 ? 'Rückmeldung' : 'Rückmeldungen'}`
|
||||||
|
: 'Rückmeldung schreiben'));
|
||||||
|
rueck.addEventListener('click', () => rueckmeldungen(k, a, rueck));
|
||||||
|
k.append(rueck);
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -134,8 +134,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/aufgaben.js?v=202608311225" defer></script>
|
<script src="assets/js/aufgaben.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/bereich.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -98,8 +98,8 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/bereich.js?v=202608311225" defer></script>
|
<script src="assets/js/bereich.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/calls.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/calls.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202608311225" defer></script>
|
<script src="assets/js/ki.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/calls.js?v=202608311225" defer></script>
|
<script src="assets/js/calls.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/dateien.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -88,8 +88,8 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/dateien.js?v=202608311225" defer></script>
|
<script src="assets/js/dateien.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
type="image/webp" media="(max-width: 899px)" />
|
type="image/webp" media="(max-width: 899px)" />
|
||||||
|
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="gate">
|
<body class="gate">
|
||||||
@@ -143,6 +143,6 @@
|
|||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/gate.js?v=202608311225" defer></script>
|
<script src="assets/js/gate.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/kalender.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -94,8 +94,8 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kalender.js?v=202608311225" defer></script>
|
<script src="assets/js/kalender.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/personen.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/personen.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -93,8 +93,8 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/personen.js?v=202608311225" defer></script>
|
<script src="assets/js/personen.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/profil.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/profil.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -127,8 +127,8 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/profil.js?v=202608311225" defer></script>
|
<script src="assets/js/profil.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/report.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/report.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -86,9 +86,9 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202608311225" defer></script>
|
<script src="assets/js/ki.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/report.js?v=202608311225" defer></script>
|
<script src="assets/js/report.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/scouting.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -124,8 +124,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/scouting.js?v=202608311225" defer></script>
|
<script src="assets/js/scouting.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -56,8 +56,8 @@
|
|||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/start.js?v=202608311225" defer></script>
|
<script src="assets/js/start.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/startcheck.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -65,9 +65,9 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202608311225" defer></script>
|
<script src="assets/js/ki.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/startcheck.js?v=202608311225" defer></script>
|
<script src="assets/js/startcheck.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<meta name="theme-color" content="#05070d" />
|
<meta name="theme-color" content="#05070d" />
|
||||||
<link rel="icon" href="assets/img/favicon.png" />
|
<link rel="icon" href="assets/img/favicon.png" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202608311235" />
|
||||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202608311225" />
|
<link rel="stylesheet" href="assets/css/wissen.css?v=202608311235" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -170,8 +170,8 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202608311225" defer></script>
|
<script src="assets/js/wahl.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202608311225" defer></script>
|
<script src="assets/js/kopf.js?v=202608311235" defer></script>
|
||||||
<script src="assets/js/wissen.js?v=202608311225" defer></script>
|
<script src="assets/js/wissen.js?v=202608311235" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user