"use server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { revalidatePath } from "next/cache"; import { z } from "zod"; import { notifyFeedbackAdded, notifyCommentReply } from "@/lib/notifications"; import { slackNotifyNewFeedback } from "@/lib/slack"; const addCommentSchema = z.object({ versionId: z.string().cuid(), frameNumber: z.number().int().min(0), timestamp: z.number().min(0), text: z.string().min(1).max(5000), }); export async function addComment(data: z.infer) { const session = await auth(); if (!session?.user) throw new Error("Unauthorized"); const parsed = addCommentSchema.parse(data); const comment = await db.comment.create({ data: { versionId: parsed.versionId, authorId: session.user.id, frameNumber: parsed.frameNumber, timestamp: parsed.timestamp, text: parsed.text, }, include: { author: { select: { id: true, name: true, email: true, image: true, role: true } }, replies: true, annotations: true, }, }); // Notify the shot artist if commenter is not the artist const version = await db.version.findUnique({ where: { id: parsed.versionId }, include: { shot: { include: { project: true } }, task: { include: { project: true, shot: true } }, }, }); const slackWebhook = version?.shot?.project?.slackWebhook ?? version?.task?.project?.slackWebhook ?? null; const shotCode = version?.shot?.shotCode ?? version?.task?.shot?.shotCode ?? version?.task?.title ?? "Task"; if (version?.shot?.artistId && version.shot.artistId !== session.user.id) { const user = await db.user.findUnique({ where: { id: session.user.id } }); await notifyFeedbackAdded({ artistId: version.shot.artistId, shotCode: version.shot.shotCode, frameNumber: parsed.frameNumber, versionId: parsed.versionId, authorName: user?.name ?? session.user.email ?? "Someone", }); } // Slack notification if (slackWebhook) { const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"; const user = await db.user.findUnique({ where: { id: session.user.id } }); await slackNotifyNewFeedback(slackWebhook, { shotCode, frameNumber: parsed.frameNumber, authorName: user?.name ?? "Someone", commentText: parsed.text, reviewUrl: `${appUrl}/review/${parsed.versionId}`, }); } revalidatePath(`/review/${parsed.versionId}`); return { success: true, comment }; } export async function addReply(commentId: string, text: string) { const session = await auth(); if (!session?.user) throw new Error("Unauthorized"); if (!text?.trim()) throw new Error("Reply text is required"); const reply = await db.commentReply.create({ data: { commentId, authorId: session.user.id, text: text.trim(), }, include: { author: { select: { id: true, name: true, email: true, image: true, role: true } }, }, }); // Notify the original comment author const comment = await db.comment.findUnique({ where: { id: commentId }, include: { version: { include: { shot: true, task: { select: { title: true } } } }, }, }); if (comment && comment.authorId && comment.authorId !== session.user.id) { const user = await db.user.findUnique({ where: { id: session.user.id } }); await notifyCommentReply({ commentAuthorId: comment.authorId, replierName: user?.name ?? "Someone", shotCode: comment.version.shot?.shotCode ?? comment.version.task?.title ?? "Task", frameNumber: comment.frameNumber, versionId: comment.versionId, }); } revalidatePath(`/review/${comment?.versionId}`); return { success: true, reply }; } export async function resolveComment(commentId: string, resolved: boolean) { const session = await auth(); if (!session?.user) throw new Error("Unauthorized"); const comment = await db.comment.update({ where: { id: commentId }, data: { isResolved: resolved }, }); revalidatePath(`/review/${comment.versionId}`); return { success: true }; } export async function deleteComment(commentId: string) { const session = await auth(); if (!session?.user) throw new Error("Unauthorized"); const comment = await db.comment.findUnique({ where: { id: commentId } }); if (!comment) throw new Error("Comment not found"); // Only the author or admin can delete if (comment.authorId !== session.user.id && session.user.role !== "ADMIN") { throw new Error("Unauthorized"); } await db.comment.delete({ where: { id: commentId } }); revalidatePath(`/review/${comment.versionId}`); return { success: true }; }