Agentur: Eintraege gehoeren allen -- und Events bekommen ein eigenes Formular
Gemessen am 07.09.2026, bevor irgendetwas geaendert wurde: Von drei Agentur-Eintraegen sah eine Creatorin genau EINEN -- den, der ihr zugeordnet war. Ein Event fuer alle traf weder `creator_id = ich` noch `erstellt_von = ich`; die Agentur-Seite war fuer jeden Creator leer. Nicht kaputt, nicht fehlerhaft: leer, so wie eine Seite aussieht, auf der noch nichts steht. Die Pruefung dazu war gruen. Sie legte ihre Testeintraege mit `creator_id: idLuna` an und pruefte damit einen Fall, den es im Alltag nicht gibt. - Bereichseinstellung `fuerAlle` + `ohneCreatorBezug`, daraus abgeleitet sichtbarEintrag(). BEWUSST neben sichtbar() statt darin: an derselben Funktion haengen Aufgaben, Dateien, Termine und Calls -- wer dort "1=1" einschleust, gibt nebenbei fremde Akten frei. Eine Gegenprobe mit einer zweiten Creatorin haelt das fest. - Die Zuordnung bietet nur noch "Agentur" an. Der Server verwirft eine Zuordnung ausserdem selbst -- inklusive des frei getippten Namens, und zwar NACH externPruefen: davor haette der Name den Riegel wieder aufgemacht. - "Event & Kampagne" heisst jetzt "Agentur-Events" und hat ein eigenes Formular: Von/Bis, Titel, Beschreibung, Aufgaben (Punkte und Preise), Regeln. Die Karte zeigt den Zustand als WORT (laeuft bis / startet / vorbei seit), nicht nur als Farbe. - Drei neue Spalten -- und sie stehen auch im Tabellenneubau vom 06.09. Der laeuft NACH dem Spaltennachtrag und haette sie samt Inhalt weggeworfen, ohne Fehler und mit stimmender Zeilenzahl. - Creator sehen weiterhin alles und tragen weiterhin nichts ein (403). Die Unterzeile sagt jetzt "alles, was hier steht" statt "alles, was zu dir gehoert" -- eine vollstaendige Liste soll sich nicht wie ein Ausschnitt lesen. pruef-agentur: 61 Pruefungen (vorher 40), alle gruen. Zwei eigene Fehler nebenbei gefunden und behoben: eine Beschriftung mit 11,2 px (Grenze 11,5) und ein Testdatum aus UTC statt Ortszeit. Zusaetzlich gruen: css-klassen, struktur, formulare, barrierefrei-workspace, handy. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -101,19 +101,45 @@ export const BEREICHE = {
|
||||
sieht man den Unterschied in einer Liste nicht.
|
||||
Bewertung: nein. Hier wird nichts benotet; es ist ein
|
||||
Zusammenarbeits-Bereich, kein Analysebereich. */
|
||||
/* FUER ALLE, NICHT FUER EINEN (07.09.2026).
|
||||
|
||||
Die anderen fuenf Bereiche sind Betreuungsakten: Dort steht etwas
|
||||
UEBER einen Creator, und nur er und seine Betreuung sehen es. Die
|
||||
Agentur ist das Gegenteil -- ein Event, eine Schulung, eine
|
||||
Zustaendigkeit gilt fuer alle gleichzeitig.
|
||||
|
||||
Bis heute lief sie trotzdem unter der Akten-Regel, und das hatte
|
||||
eine Folge, die niemandem auffiel: Ein Event ohne Creator-Zuordnung
|
||||
traf weder `creator_id = ich` noch `erstellt_von = ich` -- fuer
|
||||
jeden Creator war die Agentur-Seite schlicht LEER. Nicht "kaputt",
|
||||
nicht "Fehler": leer, so wie eine Seite aussieht, auf der noch
|
||||
nichts steht. Gemessen am 07.09.2026: Von drei Eintraegen sah die
|
||||
Creatorin genau den einen, der ihr zugeordnet war.
|
||||
|
||||
Zwei Schalter, weil es zwei verschiedene Fragen sind:
|
||||
fuerAlle -- jeder sieht jeden Eintrag dieses Bereichs
|
||||
ohneCreatorBezug -- ein Eintrag gehoert hier NIEMANDEM einzeln */
|
||||
agentur: {
|
||||
name: "Agentur",
|
||||
arten: {
|
||||
kampagne: "Event & Kampagne",
|
||||
kampagne: "Agentur-Events",
|
||||
schulung: "Schulung",
|
||||
anliegen: "Anliegen an die Agentur",
|
||||
zustaendig: "Zuständigkeit",
|
||||
},
|
||||
bewertung: false,
|
||||
dringlichkeit: true,
|
||||
fuerAlle: true,
|
||||
ohneCreatorBezug: true,
|
||||
},
|
||||
};
|
||||
|
||||
/* Aus den Bereichseinstellungen abgeleitet, nicht danebengeschrieben.
|
||||
Eine zweite Liste waere beim naechsten Bereich auseinandergelaufen --
|
||||
und zwar still. */
|
||||
const BEREICHE_FUER_ALLE = Object.entries(BEREICHE)
|
||||
.filter(([, e]) => e.fuerAlle).map(([k]) => k);
|
||||
|
||||
const DRINGLICHKEITEN = ["hoch", "mittel", "niedrig"];
|
||||
const STATUS = ["offen", "erledigt"];
|
||||
const TITEL_MAX = 160;
|
||||
@@ -256,10 +282,41 @@ export function sichtbar(person) {
|
||||
: { wo: "e.erstellt_von = ?", werte: [person.id] };
|
||||
}
|
||||
|
||||
/* Dieselbe Regel, aber mit der Ausnahme fuer Bereiche, die allen
|
||||
gehoeren.
|
||||
|
||||
BEWUSST EINE EIGENE FUNKTION, NICHT EINE AENDERUNG AN sichtbar():
|
||||
sichtbar() beantwortet auch Fragen zu Aufgaben, Dateien, Terminen und
|
||||
Calls (siehe die Aufrufe in workspace-aufgaben.js,
|
||||
workspace-dateien.js, workspace-kalender.js, workspace-calls.js). Wer
|
||||
dort "1=1" einschleust, weil er an Agentur-Eintraege dachte, oeffnet
|
||||
nebenbei fremde Dateien und fremde Termine -- ein Loch, das man dem
|
||||
Code nicht ansieht, weil an der geaenderten Stelle nichts davon
|
||||
steht.
|
||||
|
||||
Nur Eintraege gehen hier durch, und nur die aus einem Bereich mit
|
||||
`fuerAlle`. Der Rest faellt woertlich auf die alte Regel zurueck.
|
||||
|
||||
`praefix` ist da, weil die Suche dieselbe Tabelle unter demselben
|
||||
Kuerzel "e" fuehrt -- geht das eines Tages auseinander, faellt es
|
||||
beim Aufruf auf, nicht erst im Betrieb. */
|
||||
export function sichtbarEintrag(person, praefix = "e") {
|
||||
const regel = sichtbar(person);
|
||||
if (!regel) return regel;
|
||||
if (regel.wo === "1=1") return regel; // DogFather sieht ohnehin alles
|
||||
if (!BEREICHE_FUER_ALLE.length) return regel;
|
||||
const liste = BEREICHE_FUER_ALLE.map(() => "?").join(", ");
|
||||
return {
|
||||
wo: `(${praefix}.bereich IN (${liste}) OR ${regel.wo})`,
|
||||
werte: [...BEREICHE_FUER_ALLE, ...regel.werte],
|
||||
};
|
||||
}
|
||||
|
||||
const SPALTEN = `
|
||||
e.id, e.bereich, e.art, e.titel, e.text, e.datum, e.bewertung,
|
||||
e.dringlichkeit, e.status, e.creator_id, e.erstellt, e.erstellt_von, e.geaendert,
|
||||
e.hook, e.format, e.saeule_id, e.geplant,
|
||||
e.event_ende, e.event_aufgaben, e.event_regeln,
|
||||
e.creator_extern,
|
||||
${externSql("pc.name", "e.creator_extern")} AS creator_name,
|
||||
pe.name AS erstellt_name,
|
||||
@@ -289,7 +346,7 @@ bereicheRouter.get("/workspace/api/bereich/:bereich", (req, res) => {
|
||||
const einstellung = BEREICHE[bereich];
|
||||
if (!einstellung) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
|
||||
const regel = sichtbar(req.sicht || req.person);
|
||||
const regel = sichtbarEintrag(req.sicht || req.person);
|
||||
if (!regel) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
|
||||
const eintraege = db().prepare(`
|
||||
@@ -389,6 +446,35 @@ function pruefe(bereich, körper, { neu }) {
|
||||
} else aus.geplant = g;
|
||||
}
|
||||
}
|
||||
/* ---- Agentur-Events: Zeitraum, Aufgaben, Regeln -------------------
|
||||
Nur im Agentur-Bereich, nach demselben Muster wie die vier
|
||||
Content-Felder darueber: anderswo werden sie stillschweigend
|
||||
verworfen. Sonst haenge eines Tages ein Preisausschreiben an einem
|
||||
Schutzvorfall. */
|
||||
if (bereich === "agentur") {
|
||||
if (körper.event_ende !== undefined) {
|
||||
const e = String(körper.event_ende ?? "").trim();
|
||||
if (!e) aus.event_ende = null;
|
||||
else if (!/^\d{4}-\d{2}-\d{2}$/.test(e) || Number.isNaN(Date.parse(e))) {
|
||||
fehler.push("Das Ende des Zeitraums ist ungültig.");
|
||||
} else aus.event_ende = e;
|
||||
}
|
||||
for (const [feld, name] of [["event_aufgaben", "Die Aufgaben"], ["event_regeln", "Die Regeln"]]) {
|
||||
if (körper[feld] === undefined) continue;
|
||||
const t = String(körper[feld] ?? "").trim();
|
||||
if (t.length > TEXT_MAX) fehler.push(`${name} sind zu lang.`);
|
||||
else aus[feld] = t || null;
|
||||
}
|
||||
/* "Bis" vor "von" ist kein Zeitraum, sondern ein Tippfehler -- und
|
||||
einer, den man einer Liste nicht ansieht: Der Eintrag stuende da,
|
||||
liefe aber schon abgelaufen ein. Geprueft wird gegen das Datum,
|
||||
das GERADE gesetzt wird; beim Aendern nur einer der beiden Seiten
|
||||
ergaenzt der Aufrufer die andere mit (siehe PATCH). */
|
||||
if (aus.event_ende && aus.datum && aus.event_ende < aus.datum) {
|
||||
fehler.push("Das Ende liegt vor dem Beginn.");
|
||||
}
|
||||
}
|
||||
|
||||
if (körper.creator_id !== undefined) {
|
||||
const w = körper.creator_id;
|
||||
if (w === null || w === "") aus.creator_id = null;
|
||||
@@ -405,6 +491,27 @@ function pruefe(bereich, körper, { neu }) {
|
||||
Konto; sichtbar bleibt er ueber erstellt_von (siehe sichtbar()). */
|
||||
externPruefen(körper, aus, "creator", fehler);
|
||||
|
||||
/* Ein Agentur-Eintrag gehoert NIEMANDEM einzeln (07.09.2026).
|
||||
|
||||
Er gilt fuer alle, und genau deshalb sieht ihn auch jeder (siehe
|
||||
`fuerAlle` oben). Eine Zuordnung waere hier nicht bloss unnoetig,
|
||||
sie waere irrefuehrend: Ein Event mit dem Namen einer Creatorin
|
||||
daneben liest sich, als sei es ihres. Die Oberflaeche bietet
|
||||
deshalb nur noch "Agentur" an -- und diese Zeilen sorgen dafuer,
|
||||
dass ein Aufruf an der Oberflaeche vorbei zum selben Ergebnis
|
||||
kommt. Eine Regel, die nur im Formular steht, ist keine Regel.
|
||||
|
||||
GANZ ZUM SCHLUSS, und das ist der Punkt: Der freie Name wird eine
|
||||
Zeile darueber gesetzt. Stuende dieser Riegel vorher -- der
|
||||
naheliegende Ort, gleich bei der Creator-Zuordnung --, haette
|
||||
externPruefen ihn danach wieder aufgemacht, und ein getippter Name
|
||||
stuende trotz allem am Event. Zwei richtige Regeln, falsche
|
||||
Reihenfolge, kein Fehler zu sehen. */
|
||||
if (einstellung.ohneCreatorBezug) {
|
||||
aus.creator_id = null;
|
||||
aus.creator_extern = null;
|
||||
}
|
||||
|
||||
return { aus, fehler };
|
||||
}
|
||||
|
||||
@@ -457,12 +564,14 @@ bereicheRouter.post("/workspace/api/bereich/:bereich", gleicheHerkunft, (req, re
|
||||
const { lastInsertRowid } = db().prepare(`
|
||||
INSERT INTO eintraege
|
||||
(bereich, art, titel, text, datum, bewertung, dringlichkeit, status,
|
||||
creator_id, creator_extern, erstellt, erstellt_von, hook, format, saeule_id, geplant)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(
|
||||
creator_id, creator_extern, erstellt, erstellt_von, hook, format, saeule_id, geplant,
|
||||
event_ende, event_aufgaben, event_regeln)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(
|
||||
bereich, aus.art, aus.titel, aus.text ?? null, aus.datum,
|
||||
aus.bewertung ?? null, aus.dringlichkeit ?? "mittel", aus.status ?? "offen",
|
||||
aus.creator_id ?? null, aus.creator_extern ?? null, jetzt(), req.person.id,
|
||||
aus.hook ?? null, aus.format ?? null, aus.saeule_id ?? null, aus.geplant ?? null);
|
||||
aus.hook ?? null, aus.format ?? null, aus.saeule_id ?? null, aus.geplant ?? null,
|
||||
aus.event_ende ?? null, aus.event_aufgaben ?? null, aus.event_regeln ?? null);
|
||||
|
||||
protokolliere("eintrag_angelegt", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
@@ -478,7 +587,7 @@ bereicheRouter.post("/workspace/api/bereich/:bereich", gleicheHerkunft, (req, re
|
||||
/* ---------- Ändern und Löschen ------------------------------------------- */
|
||||
|
||||
function holen(req, id) {
|
||||
const regel = sichtbar(req.person);
|
||||
const regel = sichtbarEintrag(req.person);
|
||||
if (!regel) return null;
|
||||
return db().prepare(`SELECT e.* ${VERBUND} WHERE ${regel.wo} AND e.id = ?`)
|
||||
.get(...regel.werte, id);
|
||||
@@ -497,6 +606,21 @@ bereicheRouter.patch("/workspace/api/bereich/:bereich/:id", gleicheHerkunft, (re
|
||||
if (fehler.length) return res.status(400).json({ fehler: fehler.join(" ") });
|
||||
if (!istLeitung(req.person)) delete aus.creator_id;
|
||||
|
||||
/* Zeitraum beim AENDERN -- genauso wichtig wie beim Anlegen und
|
||||
leichter zu uebersehen: pruefe() sieht nur die Felder, die im
|
||||
Aufruf stehen. Wer nur das Ende verschiebt, schickt kein `datum`
|
||||
mit; die Pruefung dort haette nichts zu vergleichen und liesse
|
||||
jedes Ende durch. Deshalb hier gegen den GESPEICHERTEN Wert, und
|
||||
zwar in beide Richtungen -- man kann den Zeitraum auch dadurch
|
||||
verdrehen, dass man den Beginn nach hinten schiebt. */
|
||||
if (bereich === "agentur" && (aus.event_ende !== undefined || aus.datum !== undefined)) {
|
||||
const von = aus.datum !== undefined ? aus.datum : eintrag.datum;
|
||||
const bis = aus.event_ende !== undefined ? aus.event_ende : eintrag.event_ende;
|
||||
if (von && bis && bis < von) {
|
||||
return res.status(400).json({ fehler: "Das Ende liegt vor dem Beginn." });
|
||||
}
|
||||
}
|
||||
|
||||
/* Beim Aendern gilt dieselbe Regel -- sonst waere die Pruefung beim
|
||||
Anlegen wertlos: Man legt ohne Saeule an und haengt sie danach an. */
|
||||
if (aus.saeule_id !== undefined) {
|
||||
|
||||
Reference in New Issue
Block a user