"use server"; import { revalidatePath } from "next/cache"; import { requireUserId } from "@/lib/auth/session"; import { withUser } from "@/lib/db"; import type { ActionResult } from "@/lib/db/rpc"; /** * Notizen einer Kollegin oder eines Kollegen hinzuwählen oder abwählen. * * Kein Aufruf einer SQL-Funktion und kein Protokolleintrag, anders als bei * allem, was Personaldaten ändert: das hier ist eine persönliche * Anzeigeeinstellung. Ein Prüfprotokoll, das jeden Haken mitschreibt, machte * die Suche nach echten Änderungen mühsamer, ohne etwas nachzuweisen. * Dasselbe Muster wie bei gespeicherten Auswertungen und Entwürfen * (actions/reports.ts, actions/hireDrafts.ts). * * Abgesichert ist es trotzdem: die Regel `note_subscriptions_owner` lässt nur Zeilen * zu, deren `user_id` die angemeldete Person ist. Eine fremde Einstellung * liesse sich auch mit erfundenen Werten nicht schreiben. */ export async function setNotizSichtbarkeit(payload: { kollegeId: string; sichtbar: boolean; }): Promise { const userId = await requireUserId(); // Die eigenen Notizen sind ohnehin immer dabei. Die Prüfbedingung der // Tabelle weist das ab; hier kommt die Meldung heraus, die jemand lesen // kann, statt einer Verletzungsmeldung aus der Datenbank. if (payload.kollegeId === userId) { return { success: false, error: "Die eigenen Notizen sind immer dabei." }; } try { await withUser(userId, async (tx) => { if (payload.sichtbar) { // `on conflict do nothing`: zweimal dasselbe Hinzuwählen ist kein // Fehler, sondern derselbe Wunsch — etwa wenn zwei Reiter offen sind. await tx .insertInto("note_subscriptions") .values({ user_id: userId, author_user_id: payload.kollegeId }) .onConflict((oc) => oc.columns(["user_id", "author_user_id"]).doNothing()) .execute(); } else { await tx .deleteFrom("note_subscriptions") .where("user_id", "=", userId) .where("author_user_id", "=", payload.kollegeId) .execute(); } }); } catch (err) { return { success: false, error: err instanceof Error ? err.message : "Unbekannter Fehler." }; } // Die Glocke steckt in der Hülle jeder Seite, die Karte „Anstehend" auf der // Übersicht. Beide zeigen dieselbe Menge und müssen gemeinsam nachziehen. revalidatePath("/", "layout"); return { success: true }; }