253 lines
6.4 KiB
TypeScript
253 lines
6.4 KiB
TypeScript
import { db } from "@/lib/db";
|
|
import { NotificationType } from "@prisma/client";
|
|
|
|
export interface CreateNotificationPayload {
|
|
userId: string;
|
|
type: NotificationType;
|
|
title: string;
|
|
message: string;
|
|
data?: Record<string, unknown>;
|
|
}
|
|
|
|
/**
|
|
* Create a single in-app notification.
|
|
*/
|
|
export async function createNotification(
|
|
payload: CreateNotificationPayload
|
|
) {
|
|
return db.notification.create({
|
|
data: {
|
|
userId: payload.userId,
|
|
type: payload.type,
|
|
title: payload.title,
|
|
message: payload.message,
|
|
data: (payload.data ?? {}) as any,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create notifications for multiple users at once.
|
|
*/
|
|
export async function createNotifications(
|
|
payloads: CreateNotificationPayload[]
|
|
) {
|
|
return db.notification.createMany({
|
|
data: payloads.map((p) => ({
|
|
userId: p.userId,
|
|
type: p.type,
|
|
title: p.title,
|
|
message: p.message,
|
|
data: (p.data ?? {}) as any,
|
|
})),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Notify all ADMIN, PRODUCER, and SUPERVISOR users about a new version upload.
|
|
*/
|
|
export async function notifyNewVersionUploaded(params: {
|
|
shotCode: string;
|
|
versionNumber: number;
|
|
projectId: string;
|
|
versionId: string;
|
|
artistName: string;
|
|
}) {
|
|
const staff = await db.user.findMany({
|
|
where: {
|
|
role: { in: ["ADMIN", "PRODUCER", "SUPERVISOR"] },
|
|
isActive: true,
|
|
},
|
|
select: { id: true },
|
|
});
|
|
|
|
const label = `v${String(params.versionNumber).padStart(3, "0")}`;
|
|
|
|
await createNotifications(
|
|
staff.map((u) => ({
|
|
userId: u.id,
|
|
type: NotificationType.VERSION_UPLOADED,
|
|
title: "New version uploaded",
|
|
message: `${params.shotCode} ${label} uploaded by ${params.artistName}`,
|
|
data: {
|
|
versionId: params.versionId,
|
|
shotCode: params.shotCode,
|
|
versionNumber: params.versionNumber,
|
|
},
|
|
}))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Notify the shot's artist about new feedback.
|
|
*/
|
|
export async function notifyFeedbackAdded(params: {
|
|
artistId: string;
|
|
shotCode: string;
|
|
frameNumber: number;
|
|
versionId: string;
|
|
authorName: string;
|
|
}) {
|
|
await createNotification({
|
|
userId: params.artistId,
|
|
type: NotificationType.FEEDBACK_ADDED,
|
|
title: "New feedback on your shot",
|
|
message: `${params.authorName} commented on ${params.shotCode} at frame ${params.frameNumber}`,
|
|
data: {
|
|
versionId: params.versionId,
|
|
shotCode: params.shotCode,
|
|
frameNumber: params.frameNumber,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Notify relevant users of an approval status change.
|
|
*/
|
|
export async function notifyApprovalChange(params: {
|
|
artistId: string | null;
|
|
shotCode: string;
|
|
versionId: string;
|
|
status: "APPROVED" | "REJECTED" | "NEEDS_CHANGES";
|
|
reviewerName: string;
|
|
}) {
|
|
if (!params.artistId) return;
|
|
|
|
const typeMap = {
|
|
APPROVED: NotificationType.SHOT_APPROVED,
|
|
REJECTED: NotificationType.SHOT_REJECTED,
|
|
NEEDS_CHANGES: NotificationType.REVISION_REQUESTED,
|
|
} as const;
|
|
|
|
const msgMap = {
|
|
APPROVED: `${params.shotCode} was approved by ${params.reviewerName}`,
|
|
REJECTED: `${params.shotCode} was rejected by ${params.reviewerName}`,
|
|
NEEDS_CHANGES: `${params.shotCode} needs changes — ${params.reviewerName}`,
|
|
} as const;
|
|
|
|
await createNotification({
|
|
userId: params.artistId,
|
|
type: typeMap[params.status],
|
|
title: msgMap[params.status],
|
|
message: msgMap[params.status],
|
|
data: { versionId: params.versionId, shotCode: params.shotCode },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Notify an artist that a task has been assigned to them.
|
|
*/
|
|
export async function notifyTaskAssigned(params: {
|
|
artistId: string;
|
|
taskTitle: string;
|
|
taskId: string;
|
|
contextCode: string | null;
|
|
assignedByName: string;
|
|
}) {
|
|
await createNotification({
|
|
userId: params.artistId,
|
|
type: NotificationType.TASK_ASSIGNED,
|
|
title: "New task assigned to you",
|
|
message: `${params.assignedByName} assigned you: ${params.taskTitle}${
|
|
params.contextCode ? ` (${params.contextCode})` : ""
|
|
}`,
|
|
data: { taskId: params.taskId },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Notify supervisors/producers that a task is ready for internal review.
|
|
*/
|
|
export async function notifyTaskReadyForReview(params: {
|
|
taskId: string;
|
|
taskTitle: string;
|
|
contextCode: string | null;
|
|
artistName: string;
|
|
projectId: string;
|
|
taskUrl: string;
|
|
}) {
|
|
const staff = await db.user.findMany({
|
|
where: {
|
|
role: { in: ["ADMIN", "PRODUCER", "SUPERVISOR"] },
|
|
isActive: true,
|
|
},
|
|
select: { id: true },
|
|
});
|
|
|
|
const label = params.contextCode ? `${params.contextCode} — ` : "";
|
|
await createNotifications(
|
|
staff.map((u) => ({
|
|
userId: u.id,
|
|
type: NotificationType.TASK_READY_FOR_REVIEW,
|
|
title: "Task ready for review",
|
|
message: `${label}${params.taskTitle} marked ready by ${params.artistName}`,
|
|
data: { taskId: params.taskId, taskUrl: params.taskUrl },
|
|
}))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Notify an artist that a task has been approved.
|
|
*/
|
|
export async function notifyTaskApproved(params: {
|
|
artistId: string;
|
|
taskTitle: string;
|
|
taskId: string;
|
|
contextCode: string | null;
|
|
reviewerName: string;
|
|
}) {
|
|
await createNotification({
|
|
userId: params.artistId,
|
|
type: NotificationType.TASK_APPROVED,
|
|
title: "Task approved",
|
|
message: `${params.reviewerName} approved: ${params.taskTitle}${
|
|
params.contextCode ? ` (${params.contextCode})` : ""
|
|
}`,
|
|
data: { taskId: params.taskId },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Notify an artist that changes were requested on their task.
|
|
*/
|
|
export async function notifyTaskChangesRequested(params: {
|
|
artistId: string;
|
|
taskTitle: string;
|
|
taskId: string;
|
|
contextCode: string | null;
|
|
reviewerName: string;
|
|
}) {
|
|
await createNotification({
|
|
userId: params.artistId,
|
|
type: NotificationType.TASK_CHANGES_REQUESTED,
|
|
title: "Changes requested on task",
|
|
message: `${params.reviewerName} requested changes: ${params.taskTitle}${
|
|
params.contextCode ? ` (${params.contextCode})` : ""
|
|
}`,
|
|
data: { taskId: params.taskId },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Notify a comment author about a new reply.
|
|
*/
|
|
export async function notifyCommentReply(params: {
|
|
commentAuthorId: string;
|
|
replierName: string;
|
|
shotCode: string;
|
|
frameNumber: number;
|
|
versionId: string;
|
|
}) {
|
|
await createNotification({
|
|
userId: params.commentAuthorId,
|
|
type: NotificationType.COMMENT_REPLY,
|
|
title: "New reply to your comment",
|
|
message: `${params.replierName} replied on ${params.shotCode} frame ${params.frameNumber}`,
|
|
data: {
|
|
versionId: params.versionId,
|
|
shotCode: params.shotCode,
|
|
frameNumber: params.frameNumber,
|
|
},
|
|
});
|
|
}
|