diff --git a/bun.lockb b/bun.lockb index 0b76528..9862c7b 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 86623f1..1d48414 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "react-dom": "^18", "react-hot-toast": "^2.4.1", "react-icons": "^5.2.1", + "react-zoom-pan-pinch": "^3.7.0", "readdirp": "^3.6.0", "recharts": "2", "rrule": "^2.8.1", diff --git a/src/app/api/project/[id]/lainnya/route.ts b/src/app/api/project/[id]/lainnya/route.ts index 2344a09..bf3f042 100644 --- a/src/app/api/project/[id]/lainnya/route.ts +++ b/src/app/api/project/[id]/lainnya/route.ts @@ -3,6 +3,8 @@ import { funGetUserByCookies } from "@/module/auth"; import { createLogUser } from "@/module/user"; import { NextResponse } from "next/server"; + +// HAPUS PROJECT YG TELAH DIBATALKAN export async function DELETE(request: Request, context: { params: { id: string } }) { try { const user = await funGetUserByCookies() @@ -37,7 +39,7 @@ export async function DELETE(request: Request, context: { params: { id: string } // create log user const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus data kegiatan', table: 'project', data: String(id) }) - return NextResponse.json({ success: true, message: "Kegiatan berhasil dihapus" }, { status: 200 }); + return NextResponse.json({ success: true, message: "Kegiatan berhasil dihapus", user: user.id }, { status: 200 }); } catch (error) { console.error(error); diff --git a/src/app/api/project/route.ts b/src/app/api/project/route.ts index 1c7a7dd..1e04e0a 100644 --- a/src/app/api/project/route.ts +++ b/src/app/api/project/route.ts @@ -1,7 +1,7 @@ import { DIR, funSendWebPush, funUploadFile, prisma } from "@/module/_global"; import { funGetUserByCookies } from "@/module/auth"; import { createLogUser } from "@/module/user"; -import _ from "lodash"; +import _, { ceil } from "lodash"; import { NextResponse } from "next/server"; @@ -94,6 +94,15 @@ export async function GET(request: Request) { select: { idUser: true } + }, + ProjectTask: { + where: { + isActive: true + }, + select: { + title: true, + status: true + } } }, orderBy: { @@ -102,11 +111,11 @@ export async function GET(request: Request) { }) const omitData = data.map((v: any) => ({ - ..._.omit(v, ["ProjectMember"]), + ..._.omit(v, ["ProjectMember", "ProjectTask"]), + progress: ceil((v.ProjectTask.filter((i: any) => i.status == 1).length * 100) / v.ProjectTask.length), member: v.ProjectMember.length })) - const totalData = await prisma.project.count({ where: kondisi }) diff --git a/src/app/api/task/[id]/lainnya/route.ts b/src/app/api/task/[id]/lainnya/route.ts index 62a4265..cc67eeb 100644 --- a/src/app/api/task/[id]/lainnya/route.ts +++ b/src/app/api/task/[id]/lainnya/route.ts @@ -3,7 +3,7 @@ import { funGetUserByCookies } from "@/module/auth"; import { createLogUser } from "@/module/user"; import { NextResponse } from "next/server"; -// PEMBATALAN TASK DIVISI +// PENGHAPUSAN TUGAS DIVISI export async function DELETE(request: Request, context: { params: { id: string } }) { try { const user = await funGetUserByCookies() @@ -40,7 +40,7 @@ export async function DELETE(request: Request, context: { params: { id: string } // create log user const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus tugas divisi', table: 'divisionProject', data: id }) - return NextResponse.json({ success: true, message: "Tugas berhasil dihapuskan", }, { status: 200 }); + return NextResponse.json({ success: true, message: "Tugas berhasil dihapuskan", user: user.id }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, message: "Gagal menghapus tugas, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 12b3be1..52bbc69 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "1.2.3", tahap: "beta", update:"-nama grup darmasaba jadi lembaga desa, -menampilkan user role pada profile pada detail anggota, -fitur hapus pada data yg telah dibatalkan pada fitur kegiatan dan tugas divisi" }, { status: 200 }); + return NextResponse.json({ success: true, version: "1.2.3", tahap: "beta", update:"-nama grup darmasaba jadi lembaga desa, -menampilkan user role pada profile pada detail anggota, -fitur hapus pada data yg telah dibatalkan pada fitur kegiatan dan tugas divisi, -zoom in out gesture pada view file pdf dan image" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); diff --git a/src/module/_global/layout/layout_modal_view_file.tsx b/src/module/_global/layout/layout_modal_view_file.tsx index 507e4a1..e443f33 100644 --- a/src/module/_global/layout/layout_modal_view_file.tsx +++ b/src/module/_global/layout/layout_modal_view_file.tsx @@ -4,6 +4,7 @@ import { useShallowEffect } from '@mantine/hooks'; import dynamic from 'next/dynamic'; import { useState } from 'react'; import toast from 'react-hot-toast'; +import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch"; import { funReadPdf } from '../fun/read_pdf'; const PdfToImage = dynamic(() => import('./../components/pdf_viewer').then((mod) => mod.default), { ssr: false }); @@ -42,6 +43,8 @@ export default function LayoutModal({ opened, onClose, extension, fitur, name, f useShallowEffect(() => { if (extension == "pdf") { getPdfToImg(1) + } else { + setLoadingPdf(false) } }, [file, extension]) @@ -74,12 +77,11 @@ export default function LayoutModal({ opened, onClose, extension, fitur, name, f zIndex: 999, }} > - - + + {/* - - + */} { extension == "pdf" && @@ -98,7 +100,47 @@ export default function LayoutModal({ opened, onClose, extension, fitur, name, f paddingLeft: 10, paddingRight: 10 }}> -
+ { + loadingPdf ? + + {[...Array(1)].map((_, index) => ( + + ))} + + : + + + { + extension === 'pdf' ? + <> + {dataPdf.map((item: any, index: any) => ( + {file} + ))} + + : + {file} + } + + + } + {/*
{ extension === 'pdf' ? // @@ -130,7 +172,7 @@ export default function LayoutModal({ opened, onClose, extension, fitur, name, f alt={file} /> } -
+
*/} diff --git a/src/module/project/lib/type_project.ts b/src/module/project/lib/type_project.ts index fcceeae..8c1bb4c 100644 --- a/src/module/project/lib/type_project.ts +++ b/src/module/project/lib/type_project.ts @@ -4,6 +4,7 @@ export interface IDataProject { desc: string status: number member: number + progress: number } export interface IDataListTaskProject { diff --git a/src/module/project/ui/list_project.tsx b/src/module/project/ui/list_project.tsx index 4e2a27b..02cf0fc 100644 --- a/src/module/project/ui/list_project.tsx +++ b/src/module/project/ui/list_project.tsx @@ -1,7 +1,7 @@ "use client" import { currentScroll, globalNotifPage, globalRole, ReloadButtonTop, SkeletonList, TEMA } from '@/module/_global'; import { useHookstate } from '@hookstate/core'; -import { ActionIcon, Avatar, Badge, Box, Card, Center, Divider, Flex, Grid, Group, Skeleton, Text, TextInput, Title } from '@mantine/core'; +import { ActionIcon, Avatar, Badge, Box, Card, Center, Divider, Flex, Grid, Group, Progress, Skeleton, Text, TextInput, Title } from '@mantine/core'; import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; import _ from 'lodash'; import { useRouter, useSearchParams } from 'next/navigation'; @@ -260,8 +260,16 @@ export default function ListProject() {
- - {v.desc} + + { + v.status > 0 && + + + {_.isNull(v.progress) ? 0 : v.progress}% + + + } + 0 ? 10 : 0}>{v.desc} diff --git a/src/module/project/ui/navbar_detail_project.tsx b/src/module/project/ui/navbar_detail_project.tsx index 8d06e0c..f3ff1ea 100644 --- a/src/module/project/ui/navbar_detail_project.tsx +++ b/src/module/project/ui/navbar_detail_project.tsx @@ -1,6 +1,7 @@ 'use client' import { globalRole, keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; +import { funGetUserByCookies } from '@/module/auth'; import { useHookstate } from '@hookstate/core'; import { ActionIcon, Box, Flex, SimpleGrid, Stack, Text } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; @@ -23,6 +24,7 @@ export default function NavbarDetailProject() { const [isOpen, setOpen] = useState(false) const roleLogin = useHookstate(globalRole) const memberProject = useHookstate(globalIsMemberProject) + const [isUser, setUser] = useState('') const tema = useHookstate(TEMA) const [reason, setReason] = useState("") const [openModal, setOpenModal] = useState(false) @@ -35,10 +37,12 @@ export default function NavbarDetailProject() { async function getOneData() { try { const res = await funGetOneProjectById(param.id, 'data'); + const userLogin = await funGetUserByCookies() if (res.success) { setName(res.data.title); setReason(res.data.reason); setGrup(res.data.idGroup); + setUser(String(userLogin?.id)) } else { toast.error(res.message); } @@ -56,6 +60,7 @@ export default function NavbarDetailProject() { setDataRealtime([{ category: "project-delete", id: param.id, + user: res.user }]) toast.success(res.message) router.push("/project") @@ -80,7 +85,7 @@ export default function NavbarDetailProject() { getOneData() } - if (dataRealTime && dataRealTime.some((i: any) => (i.category == 'project-delete') && i.id == param.id)) { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-delete' && i.id == param.id && i.user != isUser)) { toast.error("Data telah dihapus, anda akan beralih ke halaman list kegiatan") setTimeout(() => { router.push("/project") diff --git a/src/module/task/ui/list_division_task.tsx b/src/module/task/ui/list_division_task.tsx index 89bb91a..e449cce 100644 --- a/src/module/task/ui/list_division_task.tsx +++ b/src/module/task/ui/list_division_task.tsx @@ -1,6 +1,6 @@ import { currentScroll, globalNotifPage, ReloadButtonTop, SkeletonList, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; -import { ActionIcon, Avatar, Box, Card, Center, Divider, Flex, Grid, Group, Progress, Skeleton, Text, TextInput, Title } from "@mantine/core"; +import { ActionIcon, Avatar, Badge, Box, Card, Center, Divider, Flex, Grid, Group, Progress, Skeleton, Text, TextInput, Title } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import _ from "lodash"; import { useParams, useRouter, useSearchParams } from "next/navigation"; @@ -246,14 +246,30 @@ export default function ListDivisionTask() { - - - - {_.isNull(v.progress) ? 0 : v.progress}% - - - {v.desc} + + { + v.status > 0 && + + + {_.isNull(v.progress) ? 0 : v.progress}% + + + } + 0 ? 10 : 0}>{v.desc} + { + v.status === 0 ? 'Segera' : + v.status === 1 ? 'Dikerjakan' : + v.status === 2 ? 'Selesai' : + v.status === 3 ? 'Batal' : + "" + } diff --git a/src/module/task/ui/navbar_detail_division_task.tsx b/src/module/task/ui/navbar_detail_division_task.tsx index ca8b9b1..fb3131a 100644 --- a/src/module/task/ui/navbar_detail_division_task.tsx +++ b/src/module/task/ui/navbar_detail_division_task.tsx @@ -14,6 +14,7 @@ import { MdCancel } from "react-icons/md"; import { funDeleteTask, funGetTaskDivisionById } from "../lib/api_task"; import { useWibuRealtime } from "wibu-realtime"; import LayoutModal from "@/module/_global/layout/layout_modal"; +import { funGetUserByCookies } from "@/module/auth"; export default function NavbarDetailDivisionTask() { const router = useRouter() @@ -25,6 +26,7 @@ export default function NavbarDetailDivisionTask() { const memberDivision = useHookstate(globalIsMemberDivision) const tema = useHookstate(TEMA) const [reason, setReason] = useState("") + const [isUser, setUser] = useState('') const [loadingModal, setLoadingModal] = useState(false) const [openModal, setOpenModal] = useState(false) const [dataRealTime, setDataRealtime] = useWibuRealtime({ @@ -35,9 +37,11 @@ export default function NavbarDetailDivisionTask() { async function getOneData() { try { const res = await funGetTaskDivisionById(param.detail, 'data'); + const userLogin = await funGetUserByCookies() if (res.success) { setName(res.data.title); setReason(res.data.reason); + setUser(String(userLogin?.id)) } else { toast.error(res.message); } @@ -56,6 +60,7 @@ export default function NavbarDetailDivisionTask() { setDataRealtime([{ category: "tugas-delete", id: param.detail, + user: res.user }]) toast.success(res.message) router.push("/division/" + param.id + "/task") @@ -81,7 +86,7 @@ export default function NavbarDetailDivisionTask() { getOneData() } - if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-delete' && i.id == param.detail)) { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-delete' && i.id == param.detail && i.user != isUser)) { toast.error("Data telah dihapus, anda akan beralih ke halaman list tugas divisi") setTimeout(() => { router.push("/division/" + param.id + "/task")