From a6c1d0c264f87584a941243f10d7cafa97305692 Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 31 Oct 2024 16:22:03 +0800 Subject: [PATCH] fix: project Deskripsi: - ganti warna sesuai temq - pencarian anggota - realtime edit tugas - loading saat upload file - order list file by created at - tidak menampilkan anggota dg user role selain coadmin dan user pada tambah anggota project - perbaikan link roouter tombol back pada detail project - memberikan loading pada cancel kegiatan No Issues --- src/app/api/project/[id]/member/route.ts | 18 +++++- src/app/api/project/[id]/route.ts | 10 ++-- src/module/project/lib/api_project.ts | 2 +- .../project/ui/add_file_detail_project.tsx | 18 ++++-- .../project/ui/add_member_detail_project.tsx | 27 ++++----- src/module/project/ui/cancel_project.tsx | 47 ++++++++++++---- .../project/ui/create_users_project.tsx | 55 ++++++++++--------- .../project/ui/edit_detail_task_project.tsx | 11 +++- src/module/project/ui/list_project.tsx | 1 - .../project/ui/navbar_detail_project.tsx | 4 +- .../project/ui/progress_detail_project.tsx | 2 +- 11 files changed, 127 insertions(+), 68 deletions(-) diff --git a/src/app/api/project/[id]/member/route.ts b/src/app/api/project/[id]/member/route.ts index a5719c2..ca9630c 100644 --- a/src/app/api/project/[id]/member/route.ts +++ b/src/app/api/project/[id]/member/route.ts @@ -136,6 +136,10 @@ export async function GET(request: Request, context: { params: { id: string } }) id: { not: String(userId) }, + OR: [ + { idUserRole: 'coadmin', }, + { idUserRole: 'user', } + ], isActive: true, name: { contains: (name == undefined || name == "null") ? "" : name, @@ -147,14 +151,24 @@ export async function GET(request: Request, context: { params: { id: string } }) id: true, name: true, email: true, - img: true + img: true, + UserRole: { + select: { + name: true + } + } }, orderBy: { name: 'asc' } }) - const fixMember = member.map((v: any) => ({ + const omitData = member.map((v: any) => ({ + ..._.omit(v, ["UserRole"]), + userRole: v.UserRole.name + })) + + const fixMember = omitData.map((v: any) => ({ idUser: v.id, name: v.name, email: v.email, diff --git a/src/app/api/project/[id]/route.ts b/src/app/api/project/[id]/route.ts index 1685139..5c2c2d9 100644 --- a/src/app/api/project/[id]/route.ts +++ b/src/app/api/project/[id]/route.ts @@ -65,6 +65,7 @@ export async function GET(request: Request, context: { params: { id: string } }) status: true, dateStart: true, dateEnd: true, + createdAt: true }, orderBy: { createdAt: 'asc' @@ -72,12 +73,13 @@ export async function GET(request: Request, context: { params: { id: string } }) }) const formatData = dataProgress.map((v: any) => ({ - ..._.omit(v, ["dateStart", "dateEnd"]), + ..._.omit(v, ["dateStart", "dateEnd", "createdAt"]), dateStart: moment(v.dateStart).format("DD-MM-YYYY"), dateEnd: moment(v.dateEnd).format("DD-MM-YYYY"), + createdAt: moment(v.createdAt).format("DD-MM-YYYY HH:mm"), })) - - allData = formatData + const dataFix = _.orderBy(formatData, 'createdAt', 'asc') + allData = dataFix } else if (kategori == "file") { const dataFile = await prisma.projectFile.findMany({ @@ -86,7 +88,7 @@ export async function GET(request: Request, context: { params: { id: string } }) idProject: String(id) }, orderBy: { - createdAt: 'desc' + createdAt: 'asc' }, select: { id: true, diff --git a/src/module/project/lib/api_project.ts b/src/module/project/lib/api_project.ts index ef953c3..2b602df 100644 --- a/src/module/project/lib/api_project.ts +++ b/src/module/project/lib/api_project.ts @@ -1,4 +1,4 @@ -import { IFormAddDetailproject, IFormAddMemberProject, IFormDateProject, IFormProject } from "./type_project"; +import { IFormAddDetailproject, IFormAddMemberProject, IFormDateProject } from "./type_project"; export const funGetAllProject = async (path?: string) => { diff --git a/src/module/project/ui/add_file_detail_project.tsx b/src/module/project/ui/add_file_detail_project.tsx index d4be1a3..c6ca3dd 100644 --- a/src/module/project/ui/add_file_detail_project.tsx +++ b/src/module/project/ui/add_file_detail_project.tsx @@ -2,7 +2,7 @@ import { keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; import LayoutModal from "@/module/_global/layout/layout_modal"; import { useHookstate } from "@hookstate/core"; -import { Box, Button, Flex, Group, rem, SimpleGrid, Stack, Text, } from "@mantine/core"; +import { Box, Button, Flex, Group, Loader, rem, SimpleGrid, Stack, Text, } from "@mantine/core"; import { Dropzone } from "@mantine/dropzone"; import _ from "lodash"; import { useParams, useRouter } from "next/navigation"; @@ -10,10 +10,10 @@ import { useRef, useState } from "react"; import toast from "react-hot-toast"; import { FaTrash } from "react-icons/fa6"; import { IoIosArrowDropright } from "react-icons/io"; +import { useWibuRealtime } from "wibu-realtime"; import { funAddFileProject, funCekNamFileUploadProject } from "../lib/api_project"; import { IListFileTaskProject } from "../lib/type_project"; import ResultsFile from "./results_file"; -import { useWibuRealtime } from "wibu-realtime"; export default function AddFileDetailProject() { @@ -26,6 +26,7 @@ export default function AddFileDetailProject() { const [indexDelFile, setIndexDelFile] = useState(0) const [openDrawerFile, setOpenDrawerFile] = useState(false) const tema = useHookstate(TEMA) + const [loadingUpload, setLoadingUpload] = useState(false) const openRef = useRef<() => void>(null) const [dataRealTime, setDataRealtime] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, @@ -41,6 +42,7 @@ export default function AddFileDetailProject() { async function cekFileName(data: any) { try { + setLoadingUpload(true) const fd = new FormData(); fd.append(`file`, data); const res = await funCekNamFileUploadProject(param.id, fd) @@ -53,6 +55,8 @@ export default function AddFileDetailProject() { } catch (error) { console.error(error) toast.error("Gagal menambahkan file, coba lagi nanti") + } finally { + setLoadingUpload(false) } } @@ -92,8 +96,7 @@ export default function AddFileDetailProject() { return ( - - + } + { + loadingUpload && + + + + } + - {loading ? - - : - - } + setOpenModal(false)} diff --git a/src/module/project/ui/cancel_project.tsx b/src/module/project/ui/cancel_project.tsx index 4928419..34fb56f 100644 --- a/src/module/project/ui/cancel_project.tsx +++ b/src/module/project/ui/cancel_project.tsx @@ -13,6 +13,7 @@ export default function CancelProject() { const router = useRouter() const [alasan, setAlasan] = useState("") const [openModal, setOpenModal] = useState(false) + const [loadingModal, setLoadingModal] = useState(false) const param = useParams<{ id: string }>() const tema = useHookstate(TEMA) const [touched, setTouched] = useState({ @@ -23,15 +24,38 @@ export default function CancelProject() { project: "sdm" }) - function onVerification() { - if (alasan == "") - return toast.error("Error! harus memasukkan alasan pembatalan Kegiatan") + function onCheck() { + const cek = checkAll() + if (!cek) + return false setOpenModal(true) } + function checkAll() { + let nilai = true + if (alasan == "") { + setTouched(touched => ({ ...touched, reason: true })) + nilai = false + } + return nilai + } + + + function onValidation(kategori: string, val: string) { + if (kategori == 'reason') { + setAlasan(val) + if (val == "") { + setTouched({ ...touched, reason: true }) + } else { + setTouched({ ...touched, reason: false }) + } + } + } + async function onSubmit() { try { + setLoadingModal(true) const res = await funCancelProject(param.id, { reason: alasan }) if (res.success) { setDataRealtime([{ @@ -39,13 +63,16 @@ export default function CancelProject() { id: param.id, }]) toast.success(res.message) - router.push("/project") + router.push("/project/" + param.id) } else { toast.error(res.message) } } catch (error) { console.error(error) toast.error("Gagal membatalkan Kegiatan, coba lagi nanti") + } finally { + setLoadingModal(false) + setOpenModal(false) } } @@ -63,8 +90,7 @@ export default function CancelProject() { value={alasan} size="md" placeholder='Contoh : Kegiatan tidak sesuai' label="Alasan Pembatalan" onChange={(event) => { - setAlasan(event.target.value) - setTouched({ ...touched, reason: false }) + onValidation('reason', event.target.value) }} error={ touched.reason && ( @@ -86,20 +112,21 @@ export default function CancelProject() { size="lg" radius={30} fullWidth - onClick={() => { onVerification() }} + onClick={() => { onCheck() }} > Simpan - setOpenModal(false)} - description="Apakah Anda yakin ingin membatalkan Kegiatan ini? Pembatalan Kegiatan bersifat permanen" + setOpenModal(false)} + description="Apakah Anda yakin ingin membatalkan kegiatan ini? Pembatalan kegiatan bersifat permanen" onYes={(val) => { if (val) { onSubmit() + } else { + setOpenModal(false) } - setOpenModal(false) }} /> ); diff --git a/src/module/project/ui/create_users_project.tsx b/src/module/project/ui/create_users_project.tsx index 7359613..3ea87cd 100644 --- a/src/module/project/ui/create_users_project.tsx +++ b/src/module/project/ui/create_users_project.tsx @@ -1,27 +1,24 @@ "use client" -import { LayoutNavbarNew, SkeletonList, SkeletonSingle, SkeletonUser, TEMA } from '@/module/_global'; -import { useHookstate } from '@hookstate/core'; -import { ActionIcon, Avatar, Box, Button, Center, Divider, Flex, Grid, Indicator, Input, rem, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import { useRouter } from 'next/navigation'; -import React, { useState } from 'react'; -import { HiChevronLeft, HiMagnifyingGlass } from 'react-icons/hi2'; -import { funGetAllmember, TypeUser } from '@/module/user'; +import { LayoutNavbarNew, SkeletonList, TEMA } from '@/module/_global'; import { funGetUserByCookies } from '@/module/auth'; -import toast from 'react-hot-toast'; -import { globalMemberProject } from '../lib/val_project'; -import { FaCheck } from 'react-icons/fa6'; -import { IoArrowBackOutline, IoClose } from 'react-icons/io5'; +import { funGetAllmember, TypeUser } from '@/module/user'; +import { useHookstate } from '@hookstate/core'; import { Carousel } from '@mantine/carousel'; +import { ActionIcon, Avatar, Box, Button, Center, Divider, Flex, Grid, Indicator, rem, Stack, Text, TextInput } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { FaCheck } from 'react-icons/fa6'; +import { HiChevronLeft, HiMagnifyingGlass } from 'react-icons/hi2'; +import { IoArrowBackOutline, IoClose } from 'react-icons/io5'; +import { globalMemberProject } from '../lib/val_project'; export default function CreateUsersProject({ grup, onClose }: { grup?: string, onClose: (val: any) => void }) { - const router = useRouter() const member = useHookstate(globalMemberProject) const [selectedFiles, setSelectedFiles] = useState([]); const [dataMember, setDataMember] = useState([]) const [loading, setLoading] = useState(true) - const [openTugas, setOpenTugas] = useState(false) const [onClickSearch, setOnClickSearch] = useState(false) const tema = useHookstate(TEMA) const isMobile2 = useMediaQuery("(max-width: 438px)"); @@ -36,21 +33,25 @@ export default function CreateUsersProject({ grup, onClose }: { grup?: string, o async function loadData(search: string) { - setLoading(true) - const res = await funGetAllmember('?active=true&group=' + grup + '&search=' + search); - const user = await funGetUserByCookies(); - - if (res.success) { - setDataMember(res.data.filter((i: any) => i.id != user.id)) - - // cek data member sebelumnya - if (member.length > 0) { - setSelectedFiles(JSON.parse(JSON.stringify(member.get()))) + try { + setLoading(true) + const res = await funGetAllmember('?active=true&group=' + grup + '&search=' + search); + const user = await funGetUserByCookies(); + if (res.success) { + setDataMember(res.data.filter((i: any) => i.id != user.id)) + // cek data member sebelumnya + if (member.length > 0) { + setSelectedFiles(JSON.parse(JSON.stringify(member.get()))) + } + } else { + toast.error("Gagal mendapatkan data, coba lagi nanti") } - } else { - toast.error(res.message) + } catch (error) { + console.error(error) + toast.error("Gagal mendapatkan data, coba lagi nanti") + } finally { + setLoading(false) } - setLoading(false) } diff --git a/src/module/project/ui/edit_detail_task_project.tsx b/src/module/project/ui/edit_detail_task_project.tsx index 7bab401..4a9be91 100644 --- a/src/module/project/ui/edit_detail_task_project.tsx +++ b/src/module/project/ui/edit_detail_task_project.tsx @@ -1,5 +1,5 @@ "use client" -import { LayoutNavbarNew, TEMA } from '@/module/_global'; +import { keyWibu, LayoutNavbarNew, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; import { Box, Button, Flex, Group, rem, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core'; @@ -9,6 +9,7 @@ import moment from 'moment'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import toast from 'react-hot-toast'; +import { useWibuRealtime } from 'wibu-realtime'; import { funEditDetailProject, funGetDetailProject } from '../lib/api_project'; export default function EditDetailTaskProject() { @@ -24,6 +25,10 @@ export default function EditDetailTaskProject() { title: false, date: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) const router = useRouter() async function onSubmit() { @@ -43,6 +48,10 @@ export default function EditDetailTaskProject() { }) if (res.success) { + setDataRealtime([{ + category: "project-detail-task", + id: idProject, + }]) toast.success(res.message); router.push('/project/' + idProject) } else { diff --git a/src/module/project/ui/list_project.tsx b/src/module/project/ui/list_project.tsx index 3e6ae5c..3e50011 100644 --- a/src/module/project/ui/list_project.tsx +++ b/src/module/project/ui/list_project.tsx @@ -55,7 +55,6 @@ export default function ListProject() { } else { toast.error(response.message); } - setLoading(false); } catch (error) { toast.error("Gagal mendapatkan kegiatan, coba lagi nanti"); console.error(error); diff --git a/src/module/project/ui/navbar_detail_project.tsx b/src/module/project/ui/navbar_detail_project.tsx index d6e6676..dcbaba7 100644 --- a/src/module/project/ui/navbar_detail_project.tsx +++ b/src/module/project/ui/navbar_detail_project.tsx @@ -17,6 +17,7 @@ export default function NavbarDetailProject() { const router = useRouter() const param = useParams<{ id: string }>() const [name, setName] = useState('') + const [grup, setGrup] = useState("") const [isOpen, setOpen] = useState(false) const roleLogin = useHookstate(globalRole) const tema = useHookstate(TEMA) @@ -32,6 +33,7 @@ export default function NavbarDetailProject() { if (res.success) { setName(res.data.title); setReason(res.data.reason); + setGrup(res.data.idGroup); } else { toast.error(res.message); } @@ -54,7 +56,7 @@ export default function NavbarDetailProject() { return ( <> -