From 1f856ad3abd31c628a7be156b1766c89f42e94bf Mon Sep 17 00:00:00 2001 From: amal Date: Thu, 14 Aug 2025 16:50:02 +0800 Subject: [PATCH] upd: laporan kegiatan Deskripsi: - update struktur database - api update laporan kegiatan project - tampilan laporan kegiatan project - form update laporan kegiatan project - integrasi api update laporan kegiatan project - api update laporan kegiatan tugas divisi - tampilan laporan kegiatan tugas divisi - form update laporan kegiatan tugas divisi - integrasi api update laporan kegiatan tugas divisi No Issues --- prisma/schema.prisma | 52 +++--- .../(fitur-division)/task/[detail]/page.tsx | 3 +- .../task/[detail]/report/page.tsx | 8 + src/app/(application)/project/[id]/page.tsx | 3 +- .../project/[id]/report/page.tsx | 9 + src/app/api/project/[id]/lainnya/route.ts | 46 +++++ src/app/api/task/[id]/lainnya/route.ts | 48 ++++++ src/module/project/index.ts | 7 +- src/module/project/lib/api_project.ts | 11 ++ src/module/project/ui/add_report_project.tsx | 157 ++++++++++++++++++ src/module/project/ui/list_report_project.tsx | 82 +++++++++ .../project/ui/navbar_detail_project.tsx | 23 ++- src/module/task/index.ts | 6 +- src/module/task/lib/api_task.ts | 11 ++ src/module/task/ui/add_report_detail_task.tsx | 156 +++++++++++++++++ .../task/ui/detail_list_report_task.tsx | 83 +++++++++ .../task/ui/navbar_detail_division_task.tsx | 23 ++- 17 files changed, 689 insertions(+), 39 deletions(-) create mode 100644 src/app/(application)/division/[id]/(fitur-division)/task/[detail]/report/page.tsx create mode 100644 src/app/(application)/project/[id]/report/page.tsx create mode 100644 src/module/project/ui/add_report_project.tsx create mode 100644 src/module/project/ui/list_report_project.tsx create mode 100644 src/module/task/ui/add_report_detail_task.tsx create mode 100644 src/module/task/ui/detail_list_report_task.tsx diff --git a/prisma/schema.prisma b/prisma/schema.prisma index f111c5b..452fb0c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -113,7 +113,6 @@ model User { Announcement Announcement[] Project Project[] ProjectMember ProjectMember[] - ProjectComment ProjectComment[] UserLog UserLog[] Division Division[] DivisionMember DivisionMember[] @@ -184,25 +183,25 @@ model AnnouncementMember { } model Project { - id String @id @default(cuid()) - Village Village @relation(fields: [idVillage], references: [id]) - idVillage String - Group Group @relation(fields: [idGroup], references: [id]) - idGroup String - title String - status Int @default(0) // 0 = pending, 1 = ongoing, 2 = done, 3 = cancelled - desc String? @db.Text - reason String? @db.Text - isActive Boolean @default(true) - User User @relation(fields: [createdBy], references: [id]) - createdBy String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - ProjectMember ProjectMember[] - ProjectFile ProjectFile[] - ProjectComment ProjectComment[] - ProjectTask ProjectTask[] - ProjectLink ProjectLink[] + id String @id @default(cuid()) + Village Village @relation(fields: [idVillage], references: [id]) + idVillage String + Group Group @relation(fields: [idGroup], references: [id]) + idGroup String + title String + status Int @default(0) // 0 = pending, 1 = ongoing, 2 = done, 3 = cancelled + desc String? @db.Text + reason String? @db.Text + report String? @db.Text + isActive Boolean @default(true) + User User @relation(fields: [createdBy], references: [id]) + createdBy String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + ProjectMember ProjectMember[] + ProjectFile ProjectFile[] + ProjectTask ProjectTask[] + ProjectLink ProjectLink[] } model ProjectMember { @@ -254,18 +253,6 @@ model ProjectTask { updatedAt DateTime @updatedAt } -model ProjectComment { - id String @id @default(cuid()) - Project Project @relation(fields: [idProject], references: [id]) - idProject String - User User @relation(fields: [createdBy], references: [id]) - createdBy String - comment String @db.Text - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt -} - model Division { id String @id @default(cuid()) Village Village @relation(fields: [idVillage], references: [id]) @@ -314,6 +301,7 @@ model DivisionProject { title String desc String? @db.Text reason String? @db.Text + report String? @db.Text status Int @default(0) // 0 = pending, 1 = ongoing, 2 = done, 3 = cancelled isActive Boolean @default(true) createdAt DateTime @default(now()) diff --git a/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx index 37e2386..f964f3b 100644 --- a/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx +++ b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx @@ -1,4 +1,4 @@ -import { ListAnggotaDetailTask, ListFileDetailTask, ListLinkDetailTask, ListTugasDetailTask, NavbarDetailDivisionTask, ProgressDetailTask } from "@/module/task" +import { ListAnggotaDetailTask, ListFileDetailTask, ListLinkDetailTask, ListReportDetailTask, ListTugasDetailTask, NavbarDetailDivisionTask, ProgressDetailTask } from "@/module/task" import { Box } from "@mantine/core" function Page() { @@ -7,6 +7,7 @@ function Page() { + diff --git a/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/report/page.tsx b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/report/page.tsx new file mode 100644 index 0000000..9da9b27 --- /dev/null +++ b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/report/page.tsx @@ -0,0 +1,8 @@ +import { AddReportTask } from "@/module/task" + +function Page() { + return ( + + ) +} +export default Page diff --git a/src/app/(application)/project/[id]/page.tsx b/src/app/(application)/project/[id]/page.tsx index c54659b..715f53a 100644 --- a/src/app/(application)/project/[id]/page.tsx +++ b/src/app/(application)/project/[id]/page.tsx @@ -1,4 +1,4 @@ -import { ListAnggotaDetailProject, ListFileDetailProject, ListLinkDetailProject, ListTugasDetailProject, NavbarDetailProject, ProgressDetailProject } from '@/module/project'; +import { ListAnggotaDetailProject, ListFileDetailProject, ListLinkDetailProject, ListReportDetailProject, ListTugasDetailProject, NavbarDetailProject, ProgressDetailProject } from '@/module/project'; import { Box } from '@mantine/core'; function Page() { @@ -7,6 +7,7 @@ function Page() { + diff --git a/src/app/(application)/project/[id]/report/page.tsx b/src/app/(application)/project/[id]/report/page.tsx new file mode 100644 index 0000000..c3b9021 --- /dev/null +++ b/src/app/(application)/project/[id]/report/page.tsx @@ -0,0 +1,9 @@ +import { AddReportProject } from "@/module/project"; + +function Page() { + return ( + + ); +} + +export default Page; diff --git a/src/app/api/project/[id]/lainnya/route.ts b/src/app/api/project/[id]/lainnya/route.ts index bf3f042..1e28df9 100644 --- a/src/app/api/project/[id]/lainnya/route.ts +++ b/src/app/api/project/[id]/lainnya/route.ts @@ -45,4 +45,50 @@ export async function DELETE(request: Request, context: { params: { id: string } console.error(error); return NextResponse.json({ success: false, message: "Gagal menghapus kegiatan, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); } +} + +// EDIT PROJECT REPORT +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 { report } = await request.json() + + const data = await prisma.project.count({ + where: { + id: id + } + }) + + if (data == 0) { + return NextResponse.json( + { + success: false, message: "Gagal mendapatkan kegiatan, data tidak ditemukan", + }, + { status: 404 } + ); + } + + const dataCreate = await prisma.project.update({ + where: { + id + }, + data: { + report: report + } + }) + + // create log user + const log = await createLogUser({ act: 'UPDATE', desc: 'User mengupdate laporan kegiatan', table: 'project', data: String(id) }) + + return NextResponse.json({ success: true, message: "Laporan kegiatan berhasil diupdate" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mengupdate kegiatan, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } } \ No newline at end of file diff --git a/src/app/api/task/[id]/lainnya/route.ts b/src/app/api/task/[id]/lainnya/route.ts index cc67eeb..585a0ac 100644 --- a/src/app/api/task/[id]/lainnya/route.ts +++ b/src/app/api/task/[id]/lainnya/route.ts @@ -45,4 +45,52 @@ export async function DELETE(request: Request, context: { params: { id: string } console.error(error); return NextResponse.json({ success: false, message: "Gagal menghapus tugas, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); } +} + + +// EDIT TASK REPORT +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 { report } = await request.json() + + + const data = await prisma.divisionProject.count({ + where: { + id: id + } + }) + + if (data == 0) { + return NextResponse.json( + { + success: false, message: "Gagal mendapatkan kegiatan, data tidak ditemukan", + }, + { status: 404 } + ); + } + + const dataCreate = await prisma.divisionProject.update({ + where: { + id + }, + data: { + report: report + } + }) + + // create log user + const log = await createLogUser({ act: 'UPDATE', desc: 'User mengupdate laporan tugas divisi', table: 'divisionProject', data: String(id) }) + + return NextResponse.json({ success: true, message: "Laporan tugas divisi berhasil diupdate" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mengupdate laporan tugas divisi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } } \ No newline at end of file diff --git a/src/module/project/index.ts b/src/module/project/index.ts index e4b0d43..35107a1 100644 --- a/src/module/project/index.ts +++ b/src/module/project/index.ts @@ -5,7 +5,6 @@ import NavbarDetailProject from "./ui/navbar_detail_project"; import ProgressDetailProject from "./ui/progress_detail_project"; import ListTugasDetailProject from "./ui/list_tugas_detail_project"; import ListFileDetailProject from "./ui/list_file_detail_project"; -import LiatAnggotaDetailProject from "./ui/list_anggota_detail_project"; import EditTaskProject from "./ui/edit_task_project"; import EditDetailTaskProject from "./ui/edit_detail_task_project"; import ListAnggotaDetailProject from "./ui/list_anggota_detail_project"; @@ -16,6 +15,8 @@ import CreateProject from "./ui/create_project"; import AddFileDetailProject from "./ui/add_file_detail_project"; import WrapLayoutProject from "./ui/wrap_project"; import ListLinkDetailProject from "./ui/list_link_detail_project"; +import AddReportProject from "./ui/add_report_project"; +import ListReportDetailProject from "./ui/list_report_project"; export { ViewDateEndTask } export { CreateUsersProject } @@ -33,4 +34,6 @@ export { AddMemberDetailProject } export { CreateProject } export { AddFileDetailProject } export { WrapLayoutProject } -export { ListLinkDetailProject } \ No newline at end of file +export { ListLinkDetailProject } +export { AddReportProject } +export { ListReportDetailProject } \ No newline at end of file diff --git a/src/module/project/lib/api_project.ts b/src/module/project/lib/api_project.ts index b64563b..ab349ef 100644 --- a/src/module/project/lib/api_project.ts +++ b/src/module/project/lib/api_project.ts @@ -122,6 +122,17 @@ export const funEditProject = async (path: string, data: { name: string }) => { return await response.json().catch(() => null); } +export const funEditReportProject = async (path: string, data: { report: string }) => { + const response = await fetch(`/api/project/${path}/lainnya`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +} + export const funDeleteFileProject = async (path: string) => { const response = await fetch(`/api/project/file/${path}`, { diff --git a/src/module/project/ui/add_report_project.tsx b/src/module/project/ui/add_report_project.tsx new file mode 100644 index 0000000..593e4b0 --- /dev/null +++ b/src/module/project/ui/add_report_project.tsx @@ -0,0 +1,157 @@ +"use client" +import { keyWibu, LayoutNavbarNew, TEMA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useHookstate } from '@hookstate/core'; +import { Box, Button, rem, Skeleton, Stack, Textarea } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funEditReportProject, funGetOneProjectById } from '../lib/api_project'; + +export default function AddReportProject() { + const router = useRouter() + const [report, setReport] = useState("") + const [openModal, setOpenModal] = useState(false) + const param = useParams<{ id: string }>() + const [loading, setLoading] = useState(true) + const [loadingSubmit, setLoadingSubmit] = useState(false) + const tema = useHookstate(TEMA) + const [touched, setTouched] = useState({ + report: false, + }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + + async function onSubmit() { + try { + setLoadingSubmit(true) + const res = await funEditReportProject(param.id, { report }) + if (res.success) { + setDataRealtime([{ + category: "project-report", + id: param.id, + }]) + toast.success(res.message) + router.push("./") + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error) + toast.error("Gagal mengedit Laporan Kegiatan, coba lagi nanti") + } finally { + setLoadingSubmit(false) + setOpenModal(false) + } + } + + function onCheck() { + if (report == "") { + setTouched({ ...touched, report: true }) + return false + } + setOpenModal(true) + } + + + + function onValidation(kategori: string, val: string) { + if (kategori == 'report') { + setReport(val) + if (val === "") { + setTouched({ ...touched, report: true }) + } else { + setTouched({ ...touched, report: false }) + } + } + } + + async function getOneData() { + try { + setLoading(true) + const res = await funGetOneProjectById(param.id, 'data'); + if (res.success) { + setReport(res.data.report); + } else { + toast.error(res.message); + } + setLoading(false); + } catch (error) { + console.error(error); + toast.error("Gagal mendapatkan data Kegiatan, coba lagi nanti"); + } finally { + setLoading(false); + } + } + + useShallowEffect(() => { + getOneData(); + }, [param.id]) + + + return ( + + + + + {loading ? + + : +