Kuma/src/modules/notifications/actions/notification-actions.ts
2026-08-28 18:20:28 +02:00

121 lines
3.3 KiB
TypeScript

"use server";
import { prisma } from "@/src/lib/prisma";
import { requireAuthWithData, isManager, isEmployee } from "@/src/lib/auth/getUser";
import type { NotificationType } from "../lib/notification-types";
const MAX_ITEMS = 20;
export interface NotificationDTO {
id: string;
type: NotificationType;
title: string;
body: string;
linkUrl: string | null;
readAt: string | null;
createdAt: string;
}
export interface GetNotificationsResult {
items: NotificationDTO[];
unreadCount: number;
}
const EMPTY: GetNotificationsResult = { items: [], unreadCount: 0 };
/**
* Lädt die letzten Benachrichtigungen des eingeloggten Nutzers plus die
* Anzahl ungelesener. Nur die Studio-Seite (Manager/Employee) hat ein
* Notification-Center - für die User-Rolle (oder ohne Session) wird bewusst
* ein leeres Ergebnis zurückgegeben, damit die Bell-Komponente ohne
* Sonderfall gerendert werden kann.
*/
export async function getNotifications(): Promise<GetNotificationsResult> {
let userData;
try {
userData = await requireAuthWithData();
} catch {
return EMPTY;
}
if (!isManager(userData) && !isEmployee(userData)) return EMPTY;
try {
const [items, unreadCount] = await Promise.all([
prisma.notification.findMany({
where: { recipientId: userData.id },
orderBy: { createdAt: "desc" },
take: MAX_ITEMS,
}),
prisma.notification.count({
where: { recipientId: userData.id, readAt: null },
}),
]);
return {
items: items.map((n) => ({
id: n.id,
type: n.type as NotificationType,
title: n.title,
body: n.body,
linkUrl: n.linkUrl,
readAt: n.readAt?.toISOString() ?? null,
createdAt: n.createdAt.toISOString(),
})),
unreadCount,
};
} catch (error) {
console.error("getNotifications fehlgeschlagen:", error);
return EMPTY;
}
}
/**
* Markiert eine einzelne Benachrichtigung als gelesen. Die recipientId steht
* in der WHERE-Bedingung (updateMany), damit niemand fremde Benachrichtigungen
* manipulieren kann - ein Treffer 0 ist kein Fehler.
*/
export async function markNotificationRead(id: string): Promise<{ success: boolean }> {
let userData;
try {
userData = await requireAuthWithData();
} catch {
return { success: false };
}
if (!isManager(userData) && !isEmployee(userData)) return { success: false };
try {
await prisma.notification.updateMany({
where: { id, recipientId: userData.id, readAt: null },
data: { readAt: new Date() },
});
return { success: true };
} catch (error) {
console.error("markNotificationRead fehlgeschlagen:", error);
return { success: false };
}
}
/**
* Markiert alle ungelesenen Benachrichtigungen des Nutzers als gelesen.
*/
export async function markAllNotificationsRead(): Promise<{ success: boolean }> {
let userData;
try {
userData = await requireAuthWithData();
} catch {
return { success: false };
}
if (!isManager(userData) && !isEmployee(userData)) return { success: false };
try {
await prisma.notification.updateMany({
where: { recipientId: userData.id, readAt: null },
data: { readAt: new Date() },
});
return { success: true };
} catch (error) {
console.error("markAllNotificationsRead fehlgeschlagen:", error);
return { success: false };
}
}