From 0d389e417d2c85063a66464aa470dfc9475f4580 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 2 Sep 2024 13:37:32 +0800 Subject: [PATCH] upd : task divisi Deskripsi: - update task detail divisi No Issues --- .../task/[detail]/add-file/page.tsx | 7 + src/app/api/task/file/[id]/route.ts | 164 +++++++++++++++ src/module/task/index.ts | 4 +- src/module/task/lib/api_task.ts | 17 +- src/module/task/ui/add_file_detail_task.tsx | 189 ++++++++++++++++++ .../task/ui/navbar_detail_division_task.tsx | 25 ++- 6 files changed, 400 insertions(+), 6 deletions(-) create mode 100644 src/app/(application)/division/[id]/(fitur-division)/task/[detail]/add-file/page.tsx create mode 100644 src/module/task/ui/add_file_detail_task.tsx diff --git a/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/add-file/page.tsx b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/add-file/page.tsx new file mode 100644 index 0000000..a18e3be --- /dev/null +++ b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/add-file/page.tsx @@ -0,0 +1,7 @@ +import { AddFileDetailTask } from "@/module/task"; + +export default function Page() { + return ( + + ) +} \ No newline at end of file diff --git a/src/app/api/task/file/[id]/route.ts b/src/app/api/task/file/[id]/route.ts index f61d76b..73eebbd 100644 --- a/src/app/api/task/file/[id]/route.ts +++ b/src/app/api/task/file/[id]/route.ts @@ -3,6 +3,7 @@ import { funGetUserByCookies } from "@/module/auth"; import _ from "lodash"; import { NextResponse } from "next/server"; import fs from "fs"; +import path from "path"; // HAPUS DETAIL FILE, HAPUS FILE DI ASSETS DAN DATABASE (BUKAN PAKE ISACTIVE) export async function DELETE(request: Request, context: { params: { id: string } }) { @@ -68,4 +69,167 @@ export async function DELETE(request: Request, context: { params: { id: string } console.log(error); return NextResponse.json({ success: false, message: "Gagal menghapus file, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); } +} + + +// TAMBAH FILE TASK DIVISI +export async function POST(request: Request, context: { params: { id: string } }) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + + const { id } = context.params; + const body = await request.formData() + const cekFile = body.has("file0") + + const dataCek = await prisma.divisionProject.count({ + where: { + id: id, + }, + }); + + if (dataCek == 0) { + return NextResponse.json( + { + success: false, + message: "Tambah file tugas gagal, data tugas tidak ditemukan", + }, + { status: 404 } + ); + } + + const dataProject = await prisma.divisionProject.findUnique({ + where: { + id: id, + }, + }); + + + let fileFix: any[] = [] + + if (cekFile) { + let a = 0 + const root = path.join(process.cwd(), "./public/file/task/"); + for (var pair of body.entries()) { + if (String(pair[0]) == "file" + a) { + const file = body.get(pair[0]) as File + const fName = file.name.split(".")[0] + const fExt = file.name.split(".").pop() + + + const insertToContainer = await prisma.containerFileDivision.create({ + data: { + idDivision: String(dataProject?.idDivision), + name: fName, + extension: String(fExt) + }, + select: { + id: true + } + }) + + const nameFix = insertToContainer.id + '.' + fExt + const filePath = path.join(root, nameFix) + // Konversi ArrayBuffer ke Buffer + const buffer = Buffer.from(await file.arrayBuffer()); + // Tulis file ke sistem + fs.writeFileSync(filePath, buffer); + + + const dataFile = { + idProject: id, + idDivision: dataProject?.idDivision, + idFile: insertToContainer.id, + createdBy: user.id, + } + + + fileFix.push(dataFile) + } + a++ + } + + const insertFile = await prisma.divisionProjectFile.createMany({ + data: fileFix + }) + } + + + return NextResponse.json({ success: true, message: "Berhasil membuat tugas divisi" }, { status: 200 }); + + } catch (error) { + console.log(error); + return NextResponse.json({ success: false, message: "Gagal membuat tugas divisi, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} + + + +// CEK FILE TASK DIVISI APAKAH PERNAH DIUPLOAD PADA TASK YG SAMA +export async function PUT(request: Request, context: { params: { id: string } }) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + + const { id } = context.params; + const body = await request.formData() + const file = body.get("file") as File + const fileName = file.name + + const dataCek = await prisma.divisionProject.count({ + where: { + id: id, + }, + }); + + if (dataCek == 0) { + return NextResponse.json( + { + success: false, + message: "Upload file gagal, data tugas tidak ditemukan", + }, + { status: 404 } + ); + } + + const dataTaskFile = await prisma.divisionProjectFile.findMany({ + where: { + idProject: id, + isActive: true + }, + select: { + ContainerFileDivision: { + select: { + name: true, + extension: true + } + } + } + }) + + const dataOmit = dataTaskFile.map((v: any) => ({ + ..._.omit(v, ["ContainerFileDivision"]), + file: v.ContainerFileDivision.name + '.' + v.ContainerFileDivision.extension, + })) + + + const cek = dataOmit.some((i: any) => i.file == fileName) + + + if (cek) { + return NextResponse.json({ success: true, message: "Cek berhasil" }, { status: 200 }); + } else { + return NextResponse.json({ success: false, message: "File sudah pernah diupload" }, { status: 400 }); + } + + } catch (error) { + console.log(error); + return NextResponse.json({ success: false, message: "Upload file gagal, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } } \ No newline at end of file diff --git a/src/module/task/index.ts b/src/module/task/index.ts index 159e6b9..fcaa02e 100644 --- a/src/module/task/index.ts +++ b/src/module/task/index.ts @@ -1,4 +1,5 @@ import AddDetailTask from "./ui/add_detail_task"; +import AddFileDetailTask from "./ui/add_file_detail_task"; import AddMemberDetailTask from "./ui/add_member_detail_task"; import CancelTask from "./ui/cancel_task"; import ViewDateEndTask from "./ui/create_date_end_task"; @@ -30,4 +31,5 @@ export { EditDetailTask } export { AddDetailTask } export { AddMemberDetailTask } export { CancelTask } -export { EditTask } \ No newline at end of file +export { EditTask } +export { AddFileDetailTask } \ No newline at end of file diff --git a/src/module/task/lib/api_task.ts b/src/module/task/lib/api_task.ts index fa5bafd..afe2fac 100644 --- a/src/module/task/lib/api_task.ts +++ b/src/module/task/lib/api_task.ts @@ -7,7 +7,6 @@ export const funGetAllTask = async (path?: string) => { export const funCreateTask = async (data: FormData) => { - const response = await fetch("/api/task", { method: "POST", body: data, @@ -129,4 +128,20 @@ export const funDeleteFileTask = async (path: string) => { }, }); return await response.json().catch(() => null); +}; + +export const funCekNamFileUploadTask = async (path: string, data: FormData) => { + const response = await fetch(`/api/task/file/${path}`, { + method: "PUT", + body: data, + }); + return await response.json().catch(() => null); +}; + +export const funAddFileTask = async (path: string, data: FormData) => { + const response = await fetch(`/api/task/file/${path}`, { + method: "POST", + body: data, + }); + return await response.json().catch(() => null); }; \ No newline at end of file diff --git a/src/module/task/ui/add_file_detail_task.tsx b/src/module/task/ui/add_file_detail_task.tsx new file mode 100644 index 0000000..bbbe383 --- /dev/null +++ b/src/module/task/ui/add_file_detail_task.tsx @@ -0,0 +1,189 @@ +"use client"; +import { LayoutDrawer, LayoutNavbarNew, WARNA } from "@/module/_global"; +import { + Box, + Button, + Flex, + Group, + rem, + SimpleGrid, + Stack, + Text, +} from "@mantine/core"; +import React, { useRef, useState } from "react"; +import { useParams, useRouter } from "next/navigation"; +import toast from "react-hot-toast"; +import { IoIosArrowDropright } from "react-icons/io"; +import { Dropzone } from "@mantine/dropzone"; +import _ from "lodash"; +import { IListFileTask } from "../lib/type_task"; +import ResultsFile from "./results_file"; +import { FaTrash } from "react-icons/fa6"; +import { funAddFileTask, funCekNamFileUploadTask } from "../lib/api_task"; + + +export default function AddFileDetailTask() { + const router = useRouter() + const [title, setTitle] = useState("") + const [openModal, setOpenModal] = useState(false) + const [fileForm, setFileForm] = useState([]) + const [listFile, setListFile] = useState([]) + const param = useParams<{ id: string, detail: string }>() + const [indexDelFile, setIndexDelFile] = useState(0) + const [openDrawerFile, setOpenDrawerFile] = useState(false) + const openRef = useRef<() => void>(null) + + function deleteFile(index: number) { + setListFile([...listFile.filter((val, i) => i !== index)]) + setFileForm([...fileForm.filter((val, i) => i !== index)]) + setOpenDrawerFile(false) + } + + + async function cekFileName(data: any) { + try { + const fd = new FormData(); + fd.append(`file`, data); + const res = await funCekNamFileUploadTask(param.detail, fd) + if (res.success) { + setFileForm([...fileForm, data]) + setListFile([...listFile, { name: data.name, extension: data.type.split("/")[1] }]) + } else { + toast.error(res.message) + } + } catch (error) { + console.log(error) + toast.error("Gagal menambahkan file, coba lagi nanti") + } + } + + async function onSubmit() { + try { + const fd = new FormData(); + for (let i = 0; i < fileForm.length; i++) { + fd.append(`file${i}`, fileForm[i]); + } + + const response = await funAddFileTask(param.detail, fd) + if (response.success) { + toast.success(response.message) + setFileForm([]) + setListFile([]) + router.push(`/division/${param.id}/task/${param.detail}`) + } else { + toast.error(response.message) + } + } catch (error) { + console.log(error) + toast.error("Gagal menambahkan tugas divisi, coba lagi nanti"); + } + } + + + + return ( + + + + + + { + if (!files || _.isEmpty(files)) + return toast.error('Tidak ada file yang dipilih') + cekFileName(files[0]) + }} + activateOnClick={false} + maxSize={3 * 1024 ** 2} + accept={['text/csv', 'image/png', 'image/jpeg', 'image/heic', 'application/pdf']} + onReject={(files) => { + return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf dengan ukuran maksimal 3 MB') + }} + > + + openRef.current?.()} + > + Upload File + + + + { + listFile.length > 0 && + + File + + { + listFile.map((v, i) => { + return ( + { + setIndexDelFile(i) + setOpenDrawerFile(true) + }}> + + + ) + }) + } + + + } + + + + + + + + setOpenDrawerFile(false)} + title={""} + > + + + deleteFile(indexDelFile)}> + + + + + Hapus File + + + + + + + + ); +} diff --git a/src/module/task/ui/navbar_detail_division_task.tsx b/src/module/task/ui/navbar_detail_division_task.tsx index 438f1ee..f7f2356 100644 --- a/src/module/task/ui/navbar_detail_division_task.tsx +++ b/src/module/task/ui/navbar_detail_division_task.tsx @@ -8,7 +8,7 @@ import { funGetTaskDivisionById } from "../lib/api_task"; import { useShallowEffect } from "@mantine/hooks"; import { HiMenu } from "react-icons/hi"; import { IoAddCircle } from "react-icons/io5"; -import { FaPencil, FaUsers } from "react-icons/fa6"; +import { FaFileCirclePlus, FaPencil, FaUsers } from "react-icons/fa6"; import { MdCancel } from "react-icons/md"; export default function NavbarDetailDivisionTask() { @@ -95,13 +95,15 @@ export default function NavbarDetailDivisionTask() { style={{ cursor: 'pointer' }} - onClick={() => { router.push(param.detail + '/cancel') }} + onClick={() => { + router.push(param.detail + '/add-file') + }} > - + - Batal + Tambah file @@ -118,6 +120,21 @@ export default function NavbarDetailDivisionTask() { Edit + + { router.push(param.detail + '/cancel') }} + > + + + + + Batal + + +