Stufe 8: Der Kreislauf -- ein Wunsch kommt als Clip zurueck
Bisher waren die acht Bretter acht Silos. Ein Wunsch wurde angenommen,
irgendwann passierte etwas im Stream, irgendwann lag ein Clip bei den
Highlights -- und nichts davon wusste voneinander. Wer den Wunsch
geschrieben hatte, erfuhr NIE, dass er erfuellt wurde.
Wunsch -> Termin bei "Was ansteht" -> Clip bei "Highlights"
Das ist der Unterschied zwischen einem Briefkasten und einer Community.
Jemand schreibt einen Wunsch und sieht ihn vier Wochen spaeter als Clip
wieder -- mit seinem Namen daneben.
ZUM DRITTEN MAL LAG DER PLAN DANEBEN. Dort stand "`aus_eintrag_id`
gibt es in der Tabelle bereits". Gibt es -- an den AUFGABEN. Die
Eintraege hatten nichts dergleichen. Dreimal an einem Tag, und jedes
Mal hat es das Messen gefunden, nicht das Nachdenken.
EINE HANDLUNG, KEIN AUSWAHLFELD. Der Zusammenhang entsteht durch
"daraus wird ein Termin" -- als Knopf an dem Wunsch, um den es geht.
Wer ein Formular ausfuellt, denkt nicht an den Wunsch von letzter
Woche, und ein Auswahlfeld mit vierzig Eintraegen benutzt niemand.
Die Kette steht auf beiden Karten: "Aus Wunschliste: ... von Lena"
(ruhig, es ist Herkunft) und "Daraus wurde: Highlights ..." (gruen, das
ist die Nachricht, auf die jemand gewartet hat).
ON DELETE SET NULL, nicht CASCADE: Wird der Wunsch geloescht, bleibt
der Clip. Er ist ja trotzdem passiert.
UND DIE PRUEFUNG VON STUFE 2 HAT STUFE 5 ERWISCHT.
Der Startkatalog stand ueber dem Countdown und schob die Antwort auf
"wann ist der naechste Stream" von 458 px auf 1488 px -- aus dem
ersten Handybildschirm heraus. Gefunden hat das nicht das Auge,
sondern das Abnahmekriterium aus §7 des Plans, das seit heute Vormittag
in pruef-countdown steht.
DIE REGEL DARAUS: Ein WERKZEUG (fuer das Team) draengt nie eine
ANTWORT (fuer alle) nach unten.
Der Countdown steht jetzt ganz oben, noch vor den Filtern -- gemessen
342 px statt 458.
NEU: pruef-kreislauf (23 Pruefungen)
Lena schreibt einen Wunsch, daraus wird ein Termin, daraus ein
Highlight. Geprueft: Der Titel wandert mit, der NAME wandert mit, beide
Enden sehen die Kette, Lena sieht sie auch (ein Kreislauf, den nur das
Team sieht, ist keiner), zweimal derselbe Weg verdoppelt nichts -- und
die Gegenprobe, dass ein ANDERER Weg vom selben Wunsch sehr wohl geht.
Und wohin sie NICHT fuehrt: nicht in die Content-Planung eines
Creators. Dort steht Arbeit, die die Community nichts angeht.
pruef-countdown 22, pruef-treff-start 27, pruef-wunschliste 30,
pruef-galerie 26, pruef-treff 66, pruef-bremse 16, pruef-css-klassen:
alle gruen.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -1040,7 +1040,7 @@ function sichtbarRoh2(person, praefix = "e") {
|
||||
}
|
||||
|
||||
const SPALTEN = `
|
||||
e.id, e.bereich, e.art, e.titel, e.text, e.datum, e.uhrzeit, e.bewertung,
|
||||
e.id, e.bereich, e.art, e.titel, e.text, e.datum, e.uhrzeit, e.aus_eintrag_id, 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,
|
||||
@@ -1174,6 +1174,36 @@ bereicheRouter.get("/workspace/api/bereich/:bereich", (req, res) => {
|
||||
je.get(b.eintrag_id).push({ id: b.id, name: b.name_original });
|
||||
}
|
||||
for (const e of eintraege) e.bilder = je.get(e.id) || [];
|
||||
|
||||
/* DIE KETTE IN BEIDE RICHTUNGEN (17.09.2026). Woher kam dieser
|
||||
Eintrag -- und was ist aus ihm geworden? Beides in je EINER
|
||||
Abfrage; vierzig Eintraege einzeln zu fragen waeren achtzig
|
||||
Anfragen.
|
||||
|
||||
WER DEN WUNSCH GESCHRIEBEN HAT, kommt mit: Das ist der ganze
|
||||
Punkt. Ein Clip, unter dem steht "Euer Wunsch -- von Lena",
|
||||
ist etwas anderes als ein Clip. */
|
||||
const quellen = eintraege.map((e) => e.aus_eintrag_id).filter(Boolean);
|
||||
if (quellen.length) {
|
||||
const p2 = quellen.map(() => "?").join(", ");
|
||||
const her = new Map(db().prepare(
|
||||
`SELECT e.id, e.bereich, e.titel, p.name AS von_name
|
||||
FROM eintraege e LEFT JOIN personen p ON p.id = e.erstellt_von
|
||||
WHERE e.id IN (${p2})`).all(...quellen).map((z) => [z.id, z]));
|
||||
for (const e of eintraege) {
|
||||
if (e.aus_eintrag_id) e.aus = her.get(e.aus_eintrag_id) || null;
|
||||
}
|
||||
}
|
||||
const p3 = eintraege.map(() => "?").join(", ");
|
||||
const draus = db().prepare(
|
||||
`SELECT id, bereich, titel, aus_eintrag_id FROM eintraege
|
||||
WHERE aus_eintrag_id IN (${p3})`).all(...eintraege.map((e) => e.id));
|
||||
const jeQuelle = new Map();
|
||||
for (const z of draus) {
|
||||
if (!jeQuelle.has(z.aus_eintrag_id)) jeQuelle.set(z.aus_eintrag_id, []);
|
||||
jeQuelle.get(z.aus_eintrag_id).push({ id: z.id, bereich: z.bereich, titel: z.titel });
|
||||
}
|
||||
for (const e of eintraege) e.wurde = jeQuelle.get(e.id) || [];
|
||||
}
|
||||
|
||||
/* DASS DIES EIN BRETT DES TREFFS IST, SAGT DER SERVER (11.09.2026).
|
||||
@@ -1579,6 +1609,82 @@ bereicheRouter.post("/workspace/api/bereich/:bereich/start", gleicheHerkunft, (r
|
||||
}
|
||||
});
|
||||
|
||||
/* =====================================================================
|
||||
DER KREISLAUF (17.09.2026) -- aus dem Plan im Vault, Stufe 8.
|
||||
|
||||
Bisher waren die acht Bretter acht Silos. Ein Wunsch wurde
|
||||
angenommen, irgendwann passierte etwas im Stream, irgendwann lag ein
|
||||
Clip bei den Highlights -- und nichts davon wusste voneinander. Wer
|
||||
den Wunsch geschrieben hatte, erfuhr nie, dass er erfuellt wurde.
|
||||
|
||||
Wunsch -> Termin bei "Was ansteht" -> Clip bei "Highlights"
|
||||
|
||||
DAS IST DER UNTERSCHIED ZWISCHEN EINEM BRIEFKASTEN UND EINER
|
||||
COMMUNITY. Jemand schreibt einen Wunsch und sieht ihn vier Wochen
|
||||
spaeter als Clip wieder, mit seinem Namen daneben.
|
||||
|
||||
EINE EIGENE ROUTE UND KEIN FELD AM FORMULAR: Der Zusammenhang
|
||||
entsteht durch eine HANDLUNG ("daraus wird ein Termin"), nicht durch
|
||||
ein Auswahlfeld, in dem man einen von vierzig Eintraegen sucht. Wer
|
||||
ein Formular ausfuellt, denkt nicht an den Wunsch von letzter Woche.
|
||||
===================================================================== */
|
||||
bereicheRouter.post("/workspace/api/bereich/:bereich/:id/daraus",
|
||||
gleicheHerkunft, (req, res) => {
|
||||
try {
|
||||
const vonBereich = String(req.params.bereich);
|
||||
const id = Number(req.params.id);
|
||||
const nach = String(req.body?.nach || "");
|
||||
if (!BEREICHE[vonBereich] || !BEREICHE[nach] || !Number.isInteger(id)) {
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
}
|
||||
/* NUR INNERHALB DER COMMUNITY. Eine Kette, die in die
|
||||
Content-Planung eines Creators fuehrt, waere ein Leck: Dort
|
||||
steht Arbeit, die die Community nichts angeht. */
|
||||
if (!TREFF_BRETTER.includes(vonBereich) || !TREFF_BRETTER.includes(nach)) {
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
}
|
||||
const warum = darfSchreiben(req.person, nach);
|
||||
if (warum) return res.status(403).json({ fehler: warum });
|
||||
|
||||
const regel = sichtbar(req.person);
|
||||
if (!regel) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
/* DIE QUELLE MUSS SICHTBAR SEIN. Sonst liesse sich durch
|
||||
Ausprobieren herausfinden, welche Nummern es gibt -- und der
|
||||
Titel kaeme im neuen Eintrag gleich mit. */
|
||||
const quelle = db().prepare(
|
||||
"SELECT id, bereich, titel, erstellt_von FROM eintraege WHERE id = ? AND bereich = ?")
|
||||
.get(id, vonBereich);
|
||||
if (!quelle) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
|
||||
/* SCHON GEMACHT? Dann dieselbe Antwort wie beim Startkatalog:
|
||||
nichts Neues, kein Fehler. Zwei Termine aus einem Wunsch
|
||||
waeren ein Versehen, kein Wunsch. */
|
||||
const schon = db().prepare(
|
||||
"SELECT id FROM eintraege WHERE aus_eintrag_id = ? AND bereich = ?").get(id, nach);
|
||||
if (schon) return res.status(200).json({ id: schon.id, schon: true });
|
||||
|
||||
const arten = Object.keys(BEREICHE[nach].arten);
|
||||
const art = arten.includes(String(req.body?.art)) ? String(req.body.art) : arten[0];
|
||||
|
||||
const { lastInsertRowid } = db().prepare(`
|
||||
INSERT INTO eintraege (bereich, art, titel, text, datum, status,
|
||||
erstellt, erstellt_von, aus_eintrag_id)
|
||||
VALUES (?,?,?,?,?, 'offen', ?,?,?)`)
|
||||
.run(nach, art, quelle.titel,
|
||||
String(req.body?.text ?? "").trim().slice(0, TEXT_MAX) || null,
|
||||
jetzt().slice(0, 10), jetzt(), req.person.id, quelle.id);
|
||||
|
||||
protokolliere("bereich_daraus", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
detail: `${vonBereich}#${id} -> ${nach}`,
|
||||
});
|
||||
res.status(201).json({ id: Number(lastInsertRowid), schon: false });
|
||||
} catch (fehler) {
|
||||
console.error("[workspace] Kreislauf:", fehler?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/* ---------- Anlegen ------------------------------------------------------ */
|
||||
|
||||
bereicheRouter.post("/workspace/api/bereich/:bereich", gleicheHerkunft, (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user