import { db } from "@/lib/db"; import { NotificationType } from "@prisma/client"; export interface CreateNotificationPayload { userId: string; type: NotificationType; title: string; message: string; data?: Record; } /** * 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, }, }); }