diff --git a/prisma/migrations/20260609083038_add_discussion_comment_file/migration.sql b/prisma/migrations/20260609083038_add_discussion_comment_file/migration.sql new file mode 100644 index 0000000..7a9f602 --- /dev/null +++ b/prisma/migrations/20260609083038_add_discussion_comment_file/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "DiscussionCommentFile" ( + "id" TEXT NOT NULL, + "idComment" TEXT NOT NULL, + "name" TEXT NOT NULL, + "extension" TEXT NOT NULL, + "idStorage" TEXT, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "DiscussionCommentFile_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "DiscussionCommentFile" ADD CONSTRAINT "DiscussionCommentFile_idComment_fkey" FOREIGN KEY ("idComment") REFERENCES "DiscussionComment"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2bec87b..c69de86 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -669,16 +669,29 @@ model DiscussionMember { } model DiscussionComment { - id String @id @default(cuid()) - Discussion Discussion @relation(fields: [idDiscussion], references: [id]) - idDiscussion String - User User @relation(fields: [idUser], references: [id]) - idUser String - comment String @db.Text - isActive Boolean @default(true) - isEdited Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(cuid()) + Discussion Discussion @relation(fields: [idDiscussion], references: [id]) + idDiscussion String + User User @relation(fields: [idUser], references: [id]) + idUser String + comment String @db.Text + isActive Boolean @default(true) + isEdited Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + DiscussionCommentFile DiscussionCommentFile[] +} + +model DiscussionCommentFile { + id String @id @default(cuid()) + Comment DiscussionComment @relation(fields: [idComment], references: [id]) + idComment String + name String + extension String + idStorage String? + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model DiscussionFile { diff --git a/src/app/api/mobile/discussion-general/[id]/comment/route.ts b/src/app/api/mobile/discussion-general/[id]/comment/route.ts index 71b8dd2..96255d8 100644 --- a/src/app/api/mobile/discussion-general/[id]/comment/route.ts +++ b/src/app/api/mobile/discussion-general/[id]/comment/route.ts @@ -1,4 +1,4 @@ -import { prisma } from "@/module/_global"; +import { DIR, funDeleteFile, funUploadFile, prisma } from "@/module/_global"; import { funGetUserById } from "@/module/auth"; import { createLogUserMobile } from "@/module/user"; import _ from "lodash"; @@ -10,7 +10,16 @@ import { sendFCMNotificationMany } from "../../../../../../../xsendMany"; export async function POST(request: Request, context: { params: { id: string } }) { try { const { id } = context.params - const { desc, user } = (await request.json()); + const contentType = request.headers.get("content-type") + let desc, user, cekFile, body: FormData | undefined + if (contentType?.includes("multipart/form-data")) { + body = await request.formData() + const dataBody = body.get("data") + cekFile = body.has("file0") + ;({ desc, user } = JSON.parse(dataBody as string)) + } else { + ;({ desc, user } = await request.json()) + } const userMobile = await funGetUserById({ id: String(user) }) if (userMobile.id == "null" || userMobile.id == undefined || userMobile.id == "") { @@ -37,6 +46,28 @@ export async function POST(request: Request, context: { params: { id: string } } } }) + if (cekFile && body) { + body.delete("data") + for (const pair of body.entries()) { + if (String(pair[0]).substring(0, 4) == "file") { + const file = body.get(pair[0]) as File + const fExt = file.name.split(".").pop() + const fName = decodeURIComponent(file.name.replace("." + fExt, "")) + const upload = await funUploadFile({ file, dirId: DIR.discussion }) + if (upload.success) { + await prisma.discussionCommentFile.create({ + data: { + idComment: data.id, + name: fName, + extension: String(fExt), + idStorage: upload.data.id + } + }) + } + } + } + } + const dataDiscussion = await prisma.discussion.findUnique({ where: { id @@ -153,7 +184,7 @@ export async function POST(request: Request, context: { params: { id: string } } export async function PUT(request: Request, context: { params: { id: string } }) { try { const { id } = context.params - const { desc, user } = (await request.json()); + const { desc, user, filesToRemove = [] } = (await request.json()); const userMobile = await funGetUserById({ id: String(user) }) if (userMobile.id == "null" || userMobile.id == undefined || userMobile.id == "") { @@ -161,25 +192,30 @@ export async function PUT(request: Request, context: { params: { id: string } }) } const cek = await prisma.discussionComment.count({ - where: { - id, - isActive: true - } + where: { id, isActive: true } }) if (cek == 0) { return NextResponse.json({ success: false, message: "Gagal mengedit komentar, data tidak ditemukan" }, { status: 200 }); } - - const data = await prisma.discussionComment.update({ - where: { - id - }, - data: { - comment: desc, - isEdited: true + if (filesToRemove.length > 0) { + const files = await prisma.discussionCommentFile.findMany({ + where: { id: { in: filesToRemove }, idComment: id, isActive: true }, + select: { id: true, idStorage: true } + }) + for (const file of files) { + if (file.idStorage) await funDeleteFile({ fileId: file.idStorage }) } + await prisma.discussionCommentFile.updateMany({ + where: { id: { in: filesToRemove }, idComment: id }, + data: { isActive: false } + }) + } + + await prisma.discussionComment.update({ + where: { id }, + data: { comment: desc, isEdited: true } }) // create log user @@ -216,18 +252,30 @@ export async function DELETE(request: Request, context: { params: { id: string } } - const data = await prisma.discussionComment.update({ - where: { - id - }, - data: { - isActive: false - } + const commentFiles = await prisma.discussionCommentFile.findMany({ + where: { idComment: id, isActive: true }, + select: { id: true, idStorage: true } + }) + + for (const file of commentFiles) { + if (file.idStorage) await funDeleteFile({ fileId: file.idStorage }) + } + + if (commentFiles.length > 0) { + await prisma.discussionCommentFile.updateMany({ + where: { idComment: id }, + data: { isActive: false } + }) + } + + await prisma.discussionComment.update({ + where: { id }, + data: { isActive: false } }) // create log user const log = await createLogUserMobile({ act: 'DELETE', desc: 'User menghapus komentar pada diskusi umum', table: 'discussionComment', data: id, user: userMobile.id }) - return NextResponse.json({ success: true, message: "Berhasil mengedit komentar" }, { status: 200 }); + return NextResponse.json({ success: true, message: "Berhasil menghapus komentar" }, { status: 200 }); } catch (error) { console.error(error) diff --git a/src/app/api/mobile/discussion-general/[id]/route.ts b/src/app/api/mobile/discussion-general/[id]/route.ts index 2b5d1ee..fa3a114 100644 --- a/src/app/api/mobile/discussion-general/[id]/route.ts +++ b/src/app/api/mobile/discussion-general/[id]/route.ts @@ -76,6 +76,15 @@ export async function GET(request: Request, context: { params: { id: string } }) name: true, img: true } + }, + DiscussionCommentFile: { + where: { isActive: true }, + select: { + id: true, + name: true, + extension: true, + idStorage: true + } } }, orderBy: { @@ -84,11 +93,12 @@ export async function GET(request: Request, context: { params: { id: string } }) }) dataFix = data.map((v: any) => ({ - ..._.omit(v, ["createdAt", "User", "updatedAt"]), + ..._.omit(v, ["createdAt", "User", "updatedAt", "DiscussionCommentFile"]), createdAt: countTime(v.createdAt), updatedAt: moment(v.updatedAt).format("ll"), username: v.User.name, - img: v.User.img + img: v.User.img, + files: v.DiscussionCommentFile })) } else if (kategori == "anggota") {