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 {
|
||||
const { wo, werte } = sichtbar(req.person);
|
||||
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}
|
||||
ORDER BY
|
||||
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 };
|
||||
}
|
||||
|
||||
/* ---------- 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) => {
|
||||
try {
|
||||
const { aus, fehler } = pruefeFelder(req.body || {}, { neu: true });
|
||||
|
||||
Reference in New Issue
Block a user