From a6c1d0c264f87584a941243f10d7cafa97305692 Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 31 Oct 2024 16:22:03 +0800 Subject: [PATCH 1/9] 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 ( <> - Date: Fri, 1 Nov 2024 16:59:04 +0800 Subject: [PATCH 2/9] fix: divisi Deskripsi: - tampilan kosong saat list grid - tombol back pada detail divisi - format tgl pada input laporan divisi - format desimal pada chart progres tugas laporan divisi - perbaikan kata event pada laporan divisi - perbaikan inputan grup pada laporan divisi yg hanya bisa di akses oleh super admin - perbaikan kalimat belum ada menjadi tidak ada - menampilkan hanya user dg role coadmin dan user aja saat tambah anggota divisi - perbaikan pencarian - loading saat tambah anggota - tidak memmakai skeleton pada saat hapus dan ganti sstatus admin divisi - saat edit berhasil maka diarahkan ke halaman detail info divisi - perbaikan saat bilangan desimal 100.00 pada halaman home No Issues --- src/app/api/division/report/route.ts | 17 +- src/app/api/home/route.ts | 2 +- src/app/api/user/route.ts | 2 + .../ui/create_anggota_division.tsx | 134 +++++++----- src/module/division_new/ui/create_report.tsx | 73 ++++--- .../ui/drawer_detail_division.tsx | 3 +- .../division_new/ui/echart_pai_report.tsx | 11 +- src/module/division_new/ui/edit_division.tsx | 3 +- src/module/division_new/ui/event_report.tsx | 7 +- .../division_new/ui/information_division.tsx | 24 ++- .../division_new/ui/list_discussion.tsx | 199 +++++++++--------- src/module/division_new/ui/list_division.tsx | 68 +++--- src/module/division_new/ui/list_document.tsx | 13 +- src/module/division_new/ui/list_task.tsx | 10 +- .../ui/navbar_detail_division.tsx | 8 +- .../division_new/ui/report_division_id.tsx | 16 +- src/module/user/lib/type_user.ts | 3 +- src/module/user/member/lib/api_member.ts | 2 +- 18 files changed, 319 insertions(+), 276 deletions(-) diff --git a/src/app/api/division/report/route.ts b/src/app/api/division/report/route.ts index 1f7fe32..38c11e3 100644 --- a/src/app/api/division/report/route.ts +++ b/src/app/api/division/report/route.ts @@ -8,14 +8,20 @@ export async function GET(request: Request) { try { const user = await funGetUserByCookies() const { searchParams } = new URL(request.url) - const group = searchParams.get("group") + const idGroup = searchParams.get("group") const division = searchParams.get("division") const date = searchParams.get("date") + let grup if (user.id == undefined) { return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }) } + if (idGroup == "null" || idGroup == undefined || idGroup == "") { + grup = user.idGroup + } else { + grup = idGroup + } // CHART PROGRESS let kondisiProgress @@ -26,7 +32,7 @@ export async function GET(request: Request) { lte: new Date(String(date)) }, Division: { - idGroup: String(group) + idGroup: String(grup) } } } else { @@ -52,9 +58,10 @@ export async function GET(request: Request) { const cek = data.some((i: any) => i.status == dataStatus[index].status) if (cek) { const find = ((Number(data.find((i: any) => i.status == dataStatus[index].status)?._count) * 100) / data.reduce((n, { _count }) => n + _count, 0)).toFixed(2) + const fix = find != "100.00" ? find.substr(-2, 2) == "00" ? find.substr(0, 2) : find : "100" input = { name: dataStatus[index].name, - value: find + value: fix } } else { input = { @@ -75,7 +82,7 @@ export async function GET(request: Request) { isActive: true, category: 'FILE', Division: { - idGroup: String(group) + idGroup: String(grup) }, createdAt: { lte: new Date(String(date)) @@ -137,7 +144,7 @@ export async function GET(request: Request) { kondisiEvent = { isActive: true, Division: { - idGroup: String(group) + idGroup: String(grup) }, dateStart: new Date(String(date)) } diff --git a/src/app/api/home/route.ts b/src/app/api/home/route.ts index d2cde94..f900c8d 100644 --- a/src/app/api/home/route.ts +++ b/src/app/api/home/route.ts @@ -185,7 +185,7 @@ export async function GET(request: Request) { const cek = data.some((i: any) => i.status == dataStatus[index].status) if (cek) { const find = ((Number(data.find((i: any) => i.status == dataStatus[index].status)?._count) * 100) / data.reduce((n, { _count }) => n + _count, 0)).toFixed(2) - const fix = find.substr(-2, 2) == "00" ? find.substr(0, 2) : find + const fix = find != "100.00" ? find.substr(-2, 2) == "00" ? find.substr(0, 2) : find : "100" input = { name: dataStatus[index].name, value: fix diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts index ac6250e..420c818 100644 --- a/src/app/api/user/route.ts +++ b/src/app/api/user/route.ts @@ -50,6 +50,7 @@ export async function GET(request: Request) { }, select: { id: true, + idUserRole: true, isActive: true, nik: true, name: true, @@ -92,6 +93,7 @@ export async function GET(request: Request) { }, select: { id: true, + idUserRole: true, isActive: true, nik: true, name: true, diff --git a/src/module/division_new/ui/create_anggota_division.tsx b/src/module/division_new/ui/create_anggota_division.tsx index 3e0590e..6275e5f 100644 --- a/src/module/division_new/ui/create_anggota_division.tsx +++ b/src/module/division_new/ui/create_anggota_division.tsx @@ -28,7 +28,8 @@ export default function CreateAnggotaDivision() { const [loading, setLoading] = useState(true) const [onClickSearch, setOnClickSearch] = useState(false) const tema = useHookstate(TEMA) - const isMobile2 = useMediaQuery("(max-width: 438px)"); + const isMobile2 = useMediaQuery("(max-width: 438px)") + const [loadingModal, setLoadingModal] = useState(false) const handleFileClick = (index: number) => { if (selectedFiles.some((i: any) => i.idUser == dataMember[index].id)) { @@ -44,16 +45,25 @@ export default function CreateAnggotaDivision() { async function loadMember(group: string, search: string) { - setLoading(true) - const res = await funGetAllmember('?active=true&group=' + group + '&search=' + search); - const user = await funGetUserByCookies(); + try { + setLoading(true) + const res = await funGetAllmember('?active=true&group=' + group + '&search=' + search); + const user = await funGetUserByCookies(); - if (res.success) { - setDataMember(res.data.filter((i: any) => i.id != user.id)) - } else { - toast.error(res.message) + if (res.success) { + const dariUserLogin = res.data.filter((i: any) => i.id != user.id) + const fixListUser = dariUserLogin.filter((i: any) => i.idUserRole == 'coadmin' || i.idUserRole == 'user') + setDataMember(fixListUser) + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error) + toast.error("Gagal memuat data, coba lagi nanti") + } finally { + setLoading(false) } - setLoading(false) + } async function loadFirst() { @@ -69,6 +79,7 @@ export default function CreateAnggotaDivision() { async function addMember() { try { + setLoadingModal(true) const res = await funAddDivisionMember(param.id, selectedFiles) if (res.success) { toast.success(res.message) @@ -76,15 +87,22 @@ export default function CreateAnggotaDivision() { } else { toast.error(res.message) } - setOpen(false) } catch (error) { - setOpen(false) console.error(error); toast.error("Gagal menambahkan anggota divisi, coba lagi nanti"); - + } finally { + setLoadingModal(false) + setOpen(false) } } + function onCheck() { + if (selectedFiles.length == 0) { + return toast.error("Error! silahkan pilih anggota") + } + setOpen(true) + } + useShallowEffect(() => { loadFirst() @@ -149,7 +167,7 @@ export default function CreateAnggotaDivision() { borderBottom: `1px solid ${"#E0DFDF"}` }}> {selectedFiles.length > 0 ? ( - + {selectedFiles.map((v: any, i: any) => { return ( @@ -195,51 +213,51 @@ export default function CreateAnggotaDivision() { Tidak ada anggota : - - {dataMember.map((v: any, index: any) => { - const isSelected = selectedFiles.some((i: any) => i.idUser == dataMember[index].id) - const found = memberDb.some((i: any) => i.idUser == v.id) - return ( - (!found) ? handleFileClick(index) : null}> - - - - - - - - {v.name} - {(found) ? "sudah menjadi anggota divisi" : ""} + + {dataMember.map((v: any, index: any) => { + const isSelected = selectedFiles.some((i: any) => i.idUser == dataMember[index].id) + const found = memberDb.some((i: any) => i.idUser == v.id) + return ( + (!found) ? handleFileClick(index) : null}> + + + + + + + + {v.name} + {(found) ? "sudah menjadi anggota divisi" : ""} + + {isSelected ? : null} - {isSelected ? : null} - - - - - + + + + + - - ) - })} - + ) + })} + } { setOpen(true) }} + onClick={() => { onCheck() }} > Simpan - setOpen(false)} + setOpen(false)} description="Apakah Anda yakin ingin menambahkan anggota divisi?" onYes={(val) => { if (val) { diff --git a/src/module/division_new/ui/create_report.tsx b/src/module/division_new/ui/create_report.tsx index 766a0ef..59d50fe 100644 --- a/src/module/division_new/ui/create_report.tsx +++ b/src/module/division_new/ui/create_report.tsx @@ -1,19 +1,18 @@ "use client"; -import { LayoutNavbarNew, TEMA } from "@/module/_global"; -import { Box, Select, Skeleton, Stack, Text } from "@mantine/core"; -import { DateInput } from "@mantine/dates"; -import React, { useState } from "react"; -import EchartPaiReport from "./echart_pai_report"; -import EchartBarReport from "./echart_bar_report"; -import EventReport from "./event_report"; -import DiscussionReport from "./discussion_report"; +import { globalRole, LayoutNavbarNew, TEMA } from "@/module/_global"; import { funGetAllGroup, IDataGroup } from "@/module/group"; -import toast from "react-hot-toast"; -import { useShallowEffect } from "@mantine/hooks"; -import { funGetReportDivision } from "../lib/api_division"; -import { useParams } from "next/navigation"; -import moment from "moment"; import { useHookstate } from "@hookstate/core"; +import { Box, Select, Skeleton, Stack } from "@mantine/core"; +import { DateInput } from "@mantine/dates"; +import { useShallowEffect } from "@mantine/hooks"; +import moment from "moment"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { funGetReportDivision } from "../lib/api_division"; +import EchartBarReport from "./echart_bar_report"; +import EchartPaiReport from "./echart_pai_report"; +import EventReport from "./event_report"; export default function CreateReport() { const [value, setValue] = useState(null); @@ -23,6 +22,7 @@ export default function CreateReport() { const [isGroup, setIsGroup] = useState(""); const param = useParams<{ id: string }>() const tema = useHookstate(TEMA) + const roleLogin = useHookstate(globalRole) const [report, setReport] = useState({ progress: [], dokumen: [], @@ -74,13 +74,20 @@ export default function CreateReport() { } function onChangeDate(val: any) { - if (val != null && val != "" && isGroup != "" && isGroup != "null") { - onReport(isGroup, val) - } - if (isGroup == null || isGroup == "") { - setTampil(false) - toast.error("Error! harus memilih grup") + if (roleLogin.get() == "supadmin") { + if (val != null && val != "" && isGroup != "" && isGroup != "null") { + onReport(isGroup, val) + } + + if (isGroup == null || isGroup == "") { + setTampil(false) + toast.error("Error! harus memilih grup") + } + } else { + if (val != null && val != "") { + onReport(isGroup, val) + } } setValue(val) @@ -95,19 +102,23 @@ export default function CreateReport() { - ({ + value: String(pro.id), + label: pro.name + }))} + onChange={(val) => { onChooseGroup(val) }} + /> + } + { onChangeDate(val) }} radius={10} diff --git a/src/module/division_new/ui/drawer_detail_division.tsx b/src/module/division_new/ui/drawer_detail_division.tsx index 8cb498d..d1088c0 100644 --- a/src/module/division_new/ui/drawer_detail_division.tsx +++ b/src/module/division_new/ui/drawer_detail_division.tsx @@ -1,10 +1,9 @@ "use client" import { TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; -import { Box, Stack, SimpleGrid, Flex, Text } from "@mantine/core"; +import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; import { useParams, useRouter } from "next/navigation"; import { BsInfoCircle } from "react-icons/bs"; -import { FaPencil } from "react-icons/fa6"; import { TbReportAnalytics } from "react-icons/tb"; export default function DrawerDetailDivision() { diff --git a/src/module/division_new/ui/echart_pai_report.tsx b/src/module/division_new/ui/echart_pai_report.tsx index ac99f52..3d940bb 100644 --- a/src/module/division_new/ui/echart_pai_report.tsx +++ b/src/module/division_new/ui/echart_pai_report.tsx @@ -1,11 +1,10 @@ -import React, { useState } from 'react'; -import { EChartsOption, color } from "echarts"; -import EChartsReact from "echarts-for-react"; -import { useShallowEffect } from '@mantine/hooks'; -import * as echarts from 'echarts'; -import { Box } from '@mantine/core'; import { TEMA } from '@/module/_global'; import { useHookstate } from '@hookstate/core'; +import { Box } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { EChartsOption } from "echarts"; +import EChartsReact from "echarts-for-react"; +import { useState } from 'react'; export default function EchartPaiReport({ data }: { data: any }) { const [options, setOptions] = useState({}); diff --git a/src/module/division_new/ui/edit_division.tsx b/src/module/division_new/ui/edit_division.tsx index a1d09fd..5a3336a 100644 --- a/src/module/division_new/ui/edit_division.tsx +++ b/src/module/division_new/ui/edit_division.tsx @@ -70,8 +70,6 @@ export default function EditDivision() { } else { toast.error(res.message); } - setLoading(false); - } catch (error) { console.error(error); toast.error("Gagal mendapatkan divisi, coba lagi nanti"); @@ -87,6 +85,7 @@ export default function EditDivision() { const res = await funEditDivision(param.id, body) if (res.success) { toast.success(res.message) + router.push("/division/info/" + param.id) } else { toast.error(res.message) } diff --git a/src/module/division_new/ui/event_report.tsx b/src/module/division_new/ui/event_report.tsx index 85fafb1..6ba23b4 100644 --- a/src/module/division_new/ui/event_report.tsx +++ b/src/module/division_new/ui/event_report.tsx @@ -1,8 +1,7 @@ -import { Box, Divider, Group, ScrollArea, Stack, Text } from '@mantine/core'; -import React from 'react'; -import { IDataReportDivision } from '../lib/type_division'; +import { Box, Divider, Group, Text } from '@mantine/core'; import _ from 'lodash'; import { useRouter } from 'next/navigation'; +import { IDataReportDivision } from '../lib/type_division'; export default function EventReport({ data, tgl }: { data: IDataReportDivision[], tgl: string }) { const router = useRouter() @@ -14,7 +13,7 @@ export default function EventReport({ data, tgl }: { data: IDataReportDivision[] _.isEmpty(data) ? - Tidak ada event + Tidak ada acara : data.map((event, index) => { diff --git a/src/module/division_new/ui/information_division.tsx b/src/module/division_new/ui/information_division.tsx index 929e530..ae0f7fc 100644 --- a/src/module/division_new/ui/information_division.tsx +++ b/src/module/division_new/ui/information_division.tsx @@ -37,10 +37,11 @@ export default function InformationDivision() { const isMobile2 = useMediaQuery("(max-width: 438px)"); const tema = useHookstate(TEMA) const [loadingStatus, setLoadingStatus] = useState(false) + const [loadingDelete, setLoadingDelete] = useState(false) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true); + setLoading(loading) const res = await funGetDivisionById(param.id); const login = await funGetUserByCookies() if (res.success) { @@ -53,18 +54,17 @@ export default function InformationDivision() { } else { toast.error(res.message); } - setLoading(false); } catch (error) { console.error(error); toast.error("Gagal mendapatkan divisi, coba lagi nanti"); } finally { - setLoading(false); + setLoading(false) } } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.id]) @@ -77,19 +77,21 @@ export default function InformationDivision() { async function deleteMember() { try { + setLoadingDelete(true) const res = await funDeleteMemberDivision(param.id, { id: valChooseMember }) if (res.success) { toast.success(res.message) setDrawer(false) - getOneData() + getOneData(false) } else { toast.error(res.message) } - setOpenModal(false) } catch (error) { console.error(error); - setOpenModal(false) toast.error("Gagal mendapatkan divisi, coba lagi nanti"); + } finally { + setLoadingDelete(false) + setOpenModal(false) } } @@ -99,7 +101,7 @@ export default function InformationDivision() { const res = await funEditStatusAdminDivision(param.id, { id: valChooseMember, isAdmin: valChooseMemberStatus }) if (res.success) { toast.success(res.message) - getOneData() + getOneData(false) } else { toast.error(res.message) } @@ -116,7 +118,7 @@ export default function InformationDivision() { const res = await funUpdateStatusDivision(param.id, { isActive: valActive }) if (res.success) { toast.success(res.message) - getOneData() + getOneData(false) } else { toast.error(res.message) } @@ -276,7 +278,7 @@ export default function InformationDivision() { - setOpenModal(false)} + setOpenModal(false)} description="Apakah Anda yakin ingin mengeluarkan anggota?" onYes={(val) => { if (!val) { diff --git a/src/module/division_new/ui/list_discussion.tsx b/src/module/division_new/ui/list_discussion.tsx index 7f25a2c..1c55838 100644 --- a/src/module/division_new/ui/list_discussion.tsx +++ b/src/module/division_new/ui/list_discussion.tsx @@ -1,15 +1,15 @@ "use client" import { TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; import { Box, Grid, Group, Skeleton, Stack, Text } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; -import { CiUser, CiClock2 } from "react-icons/ci"; +import { CiClock2, CiUser } from "react-icons/ci"; import { GoDiscussionClosed } from "react-icons/go"; import { funGetDetailDivisionById } from "../lib/api_division"; import { IDataDiscussionOnDetailDivision } from "../lib/type_division"; -import { useHookstate } from "@hookstate/core"; export default function ListDiscussionOnDetailDivision() { @@ -30,7 +30,6 @@ export default function ListDiscussionOnDetailDivision() { } else { toast.error(res.message); } - setLoading(false); } catch (error) { console.error(error); toast.error("Gagal mendapatkan divisi, coba lagi nanti"); @@ -49,106 +48,108 @@ export default function ListDiscussionOnDetailDivision() { Diskusi Terbaru - + { + !loading && data.length === 0 ? + + Tidak ada diskusi + + : + - { - loading ? - Array(2) - .fill(null) - .map((_, i) => ( - - - - - - - - )) - : - (data.length === 0) ? - - Belum ada diskusi - - : <> - } - {data.map((v, i) => { - return ( - - router.push(`${param.id}/discussion/${v.id}`)} - > - - ( + + + + + + + + )) + : <> + } + {data.map((v, i) => { + return ( + + router.push(`${param.id}/discussion/${v.id}`)} > - - - - - {v.desc} - - - - - - - - - - {v.user} + + + + + + + {v.desc} - - - - - - - {v.date} - - - - - + + + + + + + + + {v.user} + + + + + + + + {v.date} + + + + + - ); - })} - + ); + })} + + } ); diff --git a/src/module/division_new/ui/list_division.tsx b/src/module/division_new/ui/list_division.tsx index f38041e..7b89d85 100644 --- a/src/module/division_new/ui/list_division.tsx +++ b/src/module/division_new/ui/list_division.tsx @@ -1,5 +1,5 @@ 'use client' -import { currentScroll, globalNotifPage, globalRole, LayoutDrawer, LayoutNavbarNew, ReloadButtonTop, SkeletonList, TEMA } from '@/module/_global'; +import { currentScroll, globalNotifPage, globalRole, ReloadButtonTop, SkeletonList, TEMA } from '@/module/_global'; import { useHookstate } from '@hookstate/core'; import { ActionIcon, Avatar, Box, Card, Center, Divider, Flex, Grid, Group, Skeleton, Text, TextInput, Title } from '@mantine/core'; import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; @@ -7,12 +7,10 @@ import _ from 'lodash'; import { useRouter, useSearchParams } from 'next/navigation'; import { useEffect, useState } from 'react'; import toast from 'react-hot-toast'; -import { HiMenu } from 'react-icons/hi'; import { HiMagnifyingGlass, HiMiniUserGroup, HiOutlineListBullet, HiSquares2X2 } from 'react-icons/hi2'; import { MdAccountCircle } from 'react-icons/md'; import { funGetAllDivision } from '../lib/api_division'; import { IDataDivison } from '../lib/type_division'; -import DrawerDivision from './drawer_division'; export default function ListDivision() { const [isList, setIsList] = useState(false) @@ -243,36 +241,42 @@ export default function ListDivision() { )) : - data?.map((v: any, i: any) => { - return ( - - router.push(`/division/${v.id}`)}> - - - - {v.name} - + _.isEmpty(data) + ? + + Tidak ada Divisi + + : + data?.map((v: any, i: any) => { + return ( + + router.push(`/division/${v.id}`)}> + + + + {v.name} + + + + + {v.desc} + + + + + + + { + (v.jumlah_member == 0) ? "0" : "+" + (v.jumlah_member - 1) + } + + + - - - {v.desc} - - - - - - - { - (v.jumlah_member == 0) ? "0" : "+" + (v.jumlah_member - 1) - } - - - - - - - ); - }) + + + ); + }) } )} diff --git a/src/module/division_new/ui/list_document.tsx b/src/module/division_new/ui/list_document.tsx index 23c6d02..7409732 100644 --- a/src/module/division_new/ui/list_document.tsx +++ b/src/module/division_new/ui/list_document.tsx @@ -1,15 +1,15 @@ 'use client' import { TEMA, } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; import { Carousel } from "@mantine/carousel"; -import { Box, Image, Text, Center, Paper, Stack, UnstyledButton, Skeleton, Group } from "@mantine/core"; -import * as ICON from '../lib/file_icon' -import { useParams, useRouter } from "next/navigation"; +import { Box, Center, Group, Image, Skeleton, Stack, Text, UnstyledButton } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; import toast from "react-hot-toast"; import { funGetDetailDivisionById } from "../lib/api_division"; +import * as ICON from '../lib/file_icon'; import { IDataKalenderOnDetailDivision } from "../lib/type_division"; -import { useState } from "react"; -import { useHookstate } from "@hookstate/core"; const iconContainer = (icon: string) => 'data:image/svg+xml;base64,' + btoa(icon) @@ -29,7 +29,6 @@ export default function ListDocumentOnDetailDivision() { } else { toast.error(res.message); } - setLoading(false); } catch (error) { console.error(error); toast.error("Gagal mendapatkan divisi, coba lagi nanti"); @@ -60,7 +59,7 @@ export default function ListDocumentOnDetailDivision() { )) : (data.length === 0) ? - Belum ada file + Tidak ada dokumen : <> } diff --git a/src/module/division_new/ui/list_task.tsx b/src/module/division_new/ui/list_task.tsx index 76b70ae..51b984c 100644 --- a/src/module/division_new/ui/list_task.tsx +++ b/src/module/division_new/ui/list_task.tsx @@ -1,16 +1,15 @@ 'use client' import { TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; import { Carousel } from "@mantine/carousel"; -import { Avatar, Box, Group, Skeleton, Stack, Text } from "@mantine/core"; +import { Box, Group, Skeleton, Stack, Text } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; import toast from "react-hot-toast"; import { CiClock2 } from "react-icons/ci"; import { funGetDetailDivisionById } from "../lib/api_division"; -import { useState } from "react"; import { IDataTaskOnDetailDivision } from "../lib/type_division"; -import _ from "lodash"; -import { useHookstate } from "@hookstate/core"; export default function ListTaskOnDetailDivision() { @@ -29,7 +28,6 @@ export default function ListTaskOnDetailDivision() { } else { toast.error(res.message); } - setLoading(false); } catch (error) { console.error(error); toast.error("Gagal mendapatkan divisi, coba lagi nanti"); @@ -61,7 +59,7 @@ export default function ListTaskOnDetailDivision() { : (data.length === 0) ? - Belum ada tugas hari ini + Tidak ada tugas hari ini : <> } diff --git a/src/module/division_new/ui/navbar_detail_division.tsx b/src/module/division_new/ui/navbar_detail_division.tsx index 007c0bd..1655cd3 100644 --- a/src/module/division_new/ui/navbar_detail_division.tsx +++ b/src/module/division_new/ui/navbar_detail_division.tsx @@ -1,5 +1,5 @@ 'use client' -import { LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; +import { globalRole, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; import { ActionIcon } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; @@ -15,12 +15,16 @@ export default function NavbarDetailDivision() { const param = useParams<{ id: string }>() const [name, setName] = useState('') const tema = useHookstate(TEMA) + const roleLogin = useHookstate(globalRole) + const [grup, setGroup] = useState('') + async function getOneData() { try { const res = await funGetDivisionById(param.id); if (res.success) { setName(res.data.division.name); + setGroup(res.data.division.idGroup) } else { toast.error(res.message); } @@ -37,7 +41,7 @@ export default function NavbarDetailDivision() { return ( <> - (setOpenDrawer(true))} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> diff --git a/src/module/division_new/ui/report_division_id.tsx b/src/module/division_new/ui/report_division_id.tsx index 17b3fb0..d495fb8 100644 --- a/src/module/division_new/ui/report_division_id.tsx +++ b/src/module/division_new/ui/report_division_id.tsx @@ -1,18 +1,17 @@ "use client" import { LayoutNavbarNew, TEMA } from '@/module/_global'; +import { useHookstate } from '@hookstate/core'; import { Box, Skeleton, Stack } from '@mantine/core'; import { DateInput } from '@mantine/dates'; -import React, { useState } from 'react'; -import EchartPaiReport from './echart_pai_report'; -import EchartBarReport from './echart_bar_report'; -import EventReport from './event_report'; -import DiscussionReport from './discussion_report'; -import { useParams } from 'next/navigation'; -import { funGetReportDivision } from '../lib/api_division'; import moment from 'moment'; import "moment/locale/id"; +import { useParams } from 'next/navigation'; +import { useState } from 'react'; import toast from 'react-hot-toast'; -import { useHookstate } from '@hookstate/core'; +import { funGetReportDivision } from '../lib/api_division'; +import EchartBarReport from './echart_bar_report'; +import EchartPaiReport from './echart_pai_report'; +import EventReport from './event_report'; export default function ReportDivisionId() { @@ -64,6 +63,7 @@ export default function ReportDivisionId() { { onChangeDate(val) }} radius={10} diff --git a/src/module/user/lib/type_user.ts b/src/module/user/lib/type_user.ts index d9f1081..ecdc302 100644 --- a/src/module/user/lib/type_user.ts +++ b/src/module/user/lib/type_user.ts @@ -1,11 +1,12 @@ export type TypeUser = { id: string + idUserRole: string name: string nik: string phone: string email: string gender: string - img:string + img: string isActive: boolean, group: string, position: string diff --git a/src/module/user/member/lib/api_member.ts b/src/module/user/member/lib/api_member.ts index a637cb6..0834e83 100644 --- a/src/module/user/member/lib/api_member.ts +++ b/src/module/user/member/lib/api_member.ts @@ -1,4 +1,4 @@ -import { IEditDataMember, IFormMember, IStatusmember } from "./type_member"; +import { IStatusmember } from "./type_member"; export const funGetAllmember = async (path?: string) => { const response = await fetch(`/api/user${(path) ? path : ''}`, { next: { tags: ['member'] } }); From 5758d1926728847990a933d3c40c06d5b6c3998c Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 4 Nov 2024 13:48:30 +0800 Subject: [PATCH 3/9] fix: tugas divisi Deskripsi; - klo lagi loading, total = 0 - pencarian - ga bisa semua di klik saat tampilan list - pencarian saat tambah anggota tambah tugas - loading skeleton saat pencarian anggota - menghapus fitur pilih semua pada pilih anggota - mengganti label error pada edit judul tugas - memberikan loading pada saat upload file No Issues --- src/module/task/ui/add_file_detail_task.tsx | 12 +- src/module/task/ui/add_member_detail_task.tsx | 12 +- src/module/task/ui/create_task.tsx | 1 - src/module/task/ui/create_users_project.tsx | 118 ++++++++---------- src/module/task/ui/edit_task.tsx | 2 +- src/module/task/ui/list_division_task.tsx | 24 ++-- src/module/task/ui/tabs_division_task.tsx | 77 ++++++------ 7 files changed, 119 insertions(+), 127 deletions(-) diff --git a/src/module/task/ui/add_file_detail_task.tsx b/src/module/task/ui/add_file_detail_task.tsx index cce9d78..7bc6884 100644 --- a/src/module/task/ui/add_file_detail_task.tsx +++ b/src/module/task/ui/add_file_detail_task.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"; @@ -27,6 +27,7 @@ export default function AddFileDetailTask() { const [openDrawerFile, setOpenDrawerFile] = useState(false) const tema = useHookstate(TEMA) const openRef = useRef<() => void>(null) + const [loadingUpload, setLoadingUpload] = useState(false) const [dataRealTime, setDataRealtime] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" @@ -41,6 +42,7 @@ export default function AddFileDetailTask() { async function cekFileName(data: any) { try { + setLoadingUpload(true) const fd = new FormData(); fd.append(`file`, data); const res = await funCekNamFileUploadTask(param.detail, fd) @@ -53,6 +55,8 @@ export default function AddFileDetailTask() { } catch (error) { console.error(error) toast.error("Gagal menambahkan file, coba lagi nanti") + } finally { + setLoadingUpload(false) } } @@ -146,6 +150,12 @@ export default function AddFileDetailTask() { } + { + loadingUpload && + + + + } ([]) const openRef = useRef<() => void>(null) const [fileForm, setFileForm] = useState([]) - const [imgForm, setImgForm] = useState() const [listFile, setListFile] = useState([]) const [indexDelFile, setIndexDelFile] = useState(0) const [indexDelTask, setIndexDelTask] = useState(0) diff --git a/src/module/task/ui/create_users_project.tsx b/src/module/task/ui/create_users_project.tsx index ab44a50..93eb957 100644 --- a/src/module/task/ui/create_users_project.tsx +++ b/src/module/task/ui/create_users_project.tsx @@ -1,38 +1,20 @@ "use client" -import { LayoutNavbarNew, SkeletonList, SkeletonSingle, TEMA } from "@/module/_global"; -import { funGetDivisionById, funGetSearchMemberDivision, IDataMemberDivision } from "@/module/division_new"; +import { LayoutNavbarNew, SkeletonList, TEMA } from "@/module/_global"; +import { funGetSearchMemberDivision, IDataMemberDivision } from "@/module/division_new"; import { useHookstate } from "@hookstate/core"; -import { - ActionIcon, - Avatar, - Box, - Button, - Center, - Divider, - Flex, - Grid, - Group, - Indicator, - rem, - Skeleton, - Stack, - Text, - TextInput, -} from "@mantine/core"; +import { Carousel } from "@mantine/carousel"; +import { ActionIcon, Avatar, Box, Button, Center, Divider, Flex, Grid, Group, Indicator, rem, Skeleton, Stack, Text, TextInput } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import { useParams, useRouter } from "next/navigation"; -import React, { useState } from "react"; +import { useState } from "react"; import toast from "react-hot-toast"; -import { globalMemberTask } from "../lib/val_task"; -import { FaCheck } from "react-icons/fa6"; -import { RiListCheck } from "react-icons/ri"; import { BsListCheck } from "react-icons/bs"; +import { FaCheck } from "react-icons/fa6"; import { HiChevronLeft, HiMagnifyingGlass } from "react-icons/hi2"; import { IoArrowBackOutline, IoClose } from "react-icons/io5"; -import { Carousel } from "@mantine/carousel"; +import { globalMemberTask } from "../lib/val_task"; export default function CreateUsersProject({ onClose }: { onClose: (val: any) => void }) { - const router = useRouter() const param = useParams<{ id: string }>() const [selectedFiles, setSelectedFiles] = useState([]) const [isData, setData] = useState([]) @@ -57,7 +39,6 @@ export default function CreateUsersProject({ onClose }: { onClose: (val: any) => } else { toast.error(response.message) } - setLoading(false) } catch (error) { console.error(error) toast.error("Gagal mendapatkan anggota, coba lagi nanti"); @@ -122,6 +103,7 @@ export default function CreateUsersProject({ onClose }: { onClose: (val: any) => async function fetchGetMember(val: string) { setSearchQuery(val) try { + setLoading(true) const res = await funGetSearchMemberDivision('?search=' + val, param.id); if (res.success) { setData(res.data) @@ -130,6 +112,8 @@ export default function CreateUsersProject({ onClose }: { onClose: (val: any) => } } catch (error) { console.error(error); + } finally { + setLoading(false) } } @@ -228,7 +212,7 @@ export default function CreateUsersProject({ onClose }: { onClose: (val: any) => - {loading ? + {/* {loading ? : @@ -237,7 +221,7 @@ export default function CreateUsersProject({ onClose }: { onClose: (val: any) => - } + } */} {loading ? Array(8) @@ -249,47 +233,47 @@ export default function CreateUsersProject({ onClose }: { onClose: (val: any) => )) : (isData.length === 0) ? - - Tidak ada anggota - - : - isData.map((v, i) => { - const isSelected = selectedFiles.some((i: any) => i?.idUser == v.idUser); - return ( - handleFileClick(i)}> - - - - - - - - {v.name} + + Tidak ada anggota + + : + isData.map((v, i) => { + const isSelected = selectedFiles.some((i: any) => i?.idUser == v.idUser); + return ( + handleFileClick(i)} mt={i===0 ? 100 : 0}> + + + + + + + + {v.name} + + {isSelected ? : null} - {isSelected ? : null} - - - - - + + + + + - - ); - }) + ); + }) } diff --git a/src/module/task/ui/edit_task.tsx b/src/module/task/ui/edit_task.tsx index 957f508..4b0e320 100644 --- a/src/module/task/ui/edit_task.tsx +++ b/src/module/task/ui/edit_task.tsx @@ -114,7 +114,7 @@ export default function EditTask() { onChange={(e) => { onValidation('title', e.target.value) }} error={ touched.title && ( - title == "" ? "Error! harus memasukkan judul tugas" : null + title == "" ? "Judul Tugas Tidak Boleh Kosong" : null ) } /> diff --git a/src/module/task/ui/list_division_task.tsx b/src/module/task/ui/list_division_task.tsx index 1ec13f5..2c2ba17 100644 --- a/src/module/task/ui/list_division_task.tsx +++ b/src/module/task/ui/list_division_task.tsx @@ -1,15 +1,15 @@ 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 { useParams, useRouter, useSearchParams } from "next/navigation"; -import { useEffect, useState } from "react"; -import { HiMagnifyingGlass, HiMiniPresentationChartBar, HiOutlineListBullet, HiSquares2X2 } from "react-icons/hi2"; -import { MdAccountCircle } from "react-icons/md"; -import { IDataTask } from "../lib/type_task"; -import { funGetAllTask } from "../lib/api_task"; -import toast from "react-hot-toast"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import _ from "lodash"; -import { useHookstate } from "@hookstate/core"; +import { useParams, useRouter, useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; +import toast from "react-hot-toast"; +import { HiMagnifyingGlass, HiMiniPresentationChartBar, HiOutlineListBullet, HiSquares2X2 } from "react-icons/hi2"; +import { MdAccountCircle } from "react-icons/md"; +import { funGetAllTask } from "../lib/api_task"; +import { IDataTask } from "../lib/type_task"; export default function ListDivisionTask() { const [isList, setIsList] = useState(false) @@ -46,8 +46,6 @@ export default function ListDivisionTask() { } else { toast.error(response.message); } - - setLoading(false); } catch (error) { toast.error("Gagal mendapatkan tugas divisi, coba lagi nanti"); console.error(error); @@ -149,7 +147,7 @@ export default function ListDivisionTask() { Total Tugas - {totalData} + {loading ? 0 : totalData} {isList ? ( @@ -173,7 +171,7 @@ export default function ListDivisionTask() { isData.map((v, i) => { return ( - + router.push(`task/${v.id}`)}> - router.push(`task/${v.id}`)}> +
- - {dataStatus.map((item, index) => ( - - ))} - - + style={{ + display: "flex", + gap: "20px", + position: "relative", + overflowX: "scroll", + scrollbarWidth: "none", + maxWidth: "550px" + }} + > + + {dataStatus.map((item, index) => ( + + ))} + + ) From 9f8cf53dd8de3ed9e0de382b809647c4fa9b3b0f Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 4 Nov 2024 16:02:45 +0800 Subject: [PATCH 4/9] fix: diskusi deskripsi: -pencarian - loading pada update status dan hapus diskusi No Issues --- .../discussion/ui/detail_discussion.tsx | 3 +- .../ui/drawer_detail_discussion.tsx | 17 ++++--- .../discussion/ui/form_create_discussion.tsx | 27 ++++++----- src/module/discussion/ui/list_discussion.tsx | 48 +++++++++++++++---- 4 files changed, 62 insertions(+), 33 deletions(-) diff --git a/src/module/discussion/ui/detail_discussion.tsx b/src/module/discussion/ui/detail_discussion.tsx index 78e11a4..69b6ff9 100644 --- a/src/module/discussion/ui/detail_discussion.tsx +++ b/src/module/discussion/ui/detail_discussion.tsx @@ -6,7 +6,7 @@ import { ActionIcon, Avatar, Badge, Box, Center, Divider, Flex, Grid, Group, rem import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import moment from "moment"; import "moment/locale/id"; -import { useParams, useRouter } from "next/navigation"; +import { useParams } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { GrChatOption } from "react-icons/gr"; @@ -23,7 +23,6 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv const [isComent, setIsComent] = useState("") const param = useParams<{ id: string, detail: string }>() const [isLoad, setIsLoad] = useState(true) - const router = useRouter() const refresh = useHookstate(globalRefreshDiscussion) const roleLogin = useHookstate(globalRole) const [isCreator, setCreator] = useState(false) diff --git a/src/module/discussion/ui/drawer_detail_discussion.tsx b/src/module/discussion/ui/drawer_detail_discussion.tsx index 90859c6..ecaa105 100644 --- a/src/module/discussion/ui/drawer_detail_discussion.tsx +++ b/src/module/discussion/ui/drawer_detail_discussion.tsx @@ -23,13 +23,15 @@ export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivisi WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" }) + const [loadingUpdate, setLoadingUpdate] = useState(false) + const [loadingDelete, setLoadingDelete] = useState(false) async function fetchStatusDiscussion(val: boolean) { try { + setLoadingUpdate(true) if (val) { const response = await funEditStatusDiscussion(id, { status: status }) - if (response.success) { toast.success(response.message) refresh.set(!refresh.get()) @@ -38,40 +40,37 @@ export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivisi id: id, }]) onSuccess(false) - setValModalStatus(false) } else { toast.error(response.message) } } - setValModalStatus(false) } catch (error) { console.error(error); - setValModalStatus(false) toast.error("Gagal menambahkan diskusi, coba lagi nanti"); } finally { + setLoadingUpdate(false) setValModalStatus(false) } } async function fetchDeleteDiscussion(val: boolean) { try { + setLoadingDelete(true) if (val) { const response = await funDeleteDiscussion(id) if (response.success) { toast.success(response.message) - setValModal(false) onSuccess(false) router.push(`/division/${param.id}/discussion`) } else { toast.error(response.message) } } - setValModal(false) } catch (error) { console.error(error); - setValModal(false) toast.error("Gagal hapus diskusi, coba lagi nanti"); } finally { + setLoadingDelete(false) setValModal(false) } } @@ -130,12 +129,12 @@ export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivisi - setValModal(false)} + setValModal(false)} description="Apakah Anda yakin ingin menghapus diskusi ini?" onYes={(val) => { fetchDeleteDiscussion(val) }} /> - setValModalStatus(false)} + setValModalStatus(false)} description="Apakah Anda yakin ingin mengubah status diskusi ini?" onYes={(val) => { fetchStatusDiscussion(val) }} /> diff --git a/src/module/discussion/ui/form_create_discussion.tsx b/src/module/discussion/ui/form_create_discussion.tsx index e53f844..dfce046 100644 --- a/src/module/discussion/ui/form_create_discussion.tsx +++ b/src/module/discussion/ui/form_create_discussion.tsx @@ -15,7 +15,6 @@ export default function FormCreateDiscussion({ id }: { id: string }) { const [isValModal, setValModal] = useState(false) const [loadingModal, setLoadingModal] = useState(false) const router = useRouter() - const [isImg, setImg] = useState("") const param = useParams<{ id: string, detail: string }>() const [loading, setLoading] = useState(true) const [img, setIMG] = useState() @@ -51,25 +50,27 @@ export default function FormCreateDiscussion({ id }: { id: string }) { async function createDiscussion(val: boolean) { try { - setLoadingModal(true) - const response = await funCreateDiscussion({ - desc: isData.desc, - idDivision: id - }) + if (val) { + setLoadingModal(true) + const response = await funCreateDiscussion({ + desc: isData.desc, + idDivision: id + }) - if (response.success) { - setDataRealtime(response.notif) - toast.success(response.message) - router.push(`/division/${param.id}/discussion/`) - } else { - toast.error(response.message) + if (response.success) { + setDataRealtime(response.notif) + toast.success(response.message) + router.push(`/division/${param.id}/discussion/`) + } else { + toast.error(response.message) + } } } catch (error) { console.error(error); toast.error("Gagal menambahkan diskusi, coba lagi nanti"); } finally { - setValModal(false) setLoadingModal(false) + setValModal(false) } } diff --git a/src/module/discussion/ui/list_discussion.tsx b/src/module/discussion/ui/list_discussion.tsx index 43b9c47..500420d 100644 --- a/src/module/discussion/ui/list_discussion.tsx +++ b/src/module/discussion/ui/list_discussion.tsx @@ -1,16 +1,17 @@ 'use client' -import { currentScroll, TEMA } from "@/module/_global"; +import { currentScroll, globalNotifPage, ReloadButtonTop, TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; import { Avatar, Badge, Box, Divider, Flex, Grid, Group, Skeleton, Spoiler, Text, TextInput } from "@mantine/core"; -import { useParams, useRouter, useSearchParams } from "next/navigation"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { useParams, useRouter } from "next/navigation"; import { useEffect, useState } from "react"; +import toast from "react-hot-toast"; import { GrChatOption } from "react-icons/gr"; import { HiMagnifyingGlass } from "react-icons/hi2"; import { funGetAllDiscussion } from "../lib/api_discussion"; -import { useShallowEffect } from "@mantine/hooks"; import { IDataDiscussion } from "../lib/type_discussion"; -import toast from "react-hot-toast"; -import _ from "lodash"; -import { useHookstate } from "@hookstate/core"; + export default function ListDiscussion({ id }: { id: string }) { const [isData, setData] = useState([]) @@ -21,11 +22,12 @@ export default function ListDiscussion({ id }: { id: string }) { const router = useRouter() const { value: containerRef } = useHookstate(currentScroll); const [isPage, setPage] = useState(1) + const notifLoadPage = useHookstate(globalNotifPage) + const [isRefresh, setRefresh] = useState(false) const getData = async (loading: boolean) => { try { - if (loading) - setLoading(true) + setLoading(loading) const response = await funGetAllDiscussion('?division=' + id + '&search=' + searchQuery + '&page=' + isPage) if (response.success) { if (isPage == 1) { @@ -36,7 +38,6 @@ export default function ListDiscussion({ id }: { id: string }) { } else { toast.error(response.message) } - setLoading(false) } catch (error) { console.error(error) } finally { @@ -75,8 +76,37 @@ export default function ListDiscussion({ id }: { id: string }) { }, [containerRef, isPage]); + + useShallowEffect(() => { + console.log(notifLoadPage.get()) + if (notifLoadPage.get().category == 'division/' + param.id + '/discussion' && notifLoadPage.get().load == true) { + setRefresh(true) + } + }, [notifLoadPage.get().load]) + + function onRefresh() { + notifLoadPage.set({ + category: '', + load: false + }) + setRefresh(false) + setPage(1) + setTimeout(() => { + getData(false) + }, 500) + } + + return ( + { + isRefresh && + { onRefresh() }} + title='UPDATE' + /> + + } Date: Tue, 5 Nov 2024 10:55:19 +0800 Subject: [PATCH 5/9] fix: dokumen divisi Deskripsi: - update order by - loading rename - loading create - loading hapus - loading move - loading copy - realtime move di asal itemnya No Issues --- src/app/api/document/route.ts | 9 +++-- .../document/ui/drawer_cut_documents.tsx | 5 +-- .../ui/drawer_menu_document_division.tsx | 12 ++++--- src/module/document/ui/drawer_more.tsx | 36 +++++++++++++------ .../document/ui/navbar_document_division.tsx | 34 +++++++++++------- 5 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/app/api/document/route.ts b/src/app/api/document/route.ts index d8ef815..8576245 100644 --- a/src/app/api/document/route.ts +++ b/src/app/api/document/route.ts @@ -125,6 +125,11 @@ export async function GET(request: Request) { updatedAt: true } } + }, + orderBy: { + DivisionDocumentFolderFile: { + createdAt: 'desc' + } } }) @@ -169,7 +174,7 @@ export async function GET(request: Request) { updatedAt: true }, orderBy: { - name: 'asc' + createdAt: 'desc' } }) @@ -185,7 +190,7 @@ export async function GET(request: Request) { allData.push(...formatDataShare) } - const formatData = _.orderBy(allData, ['category', 'name'], ['desc', 'asc']); + const formatData = _.orderBy(allData, ['category', 'createdAt'], ['desc', 'asc']); let pathNow = path let jalur = [] diff --git a/src/module/document/ui/drawer_cut_documents.tsx b/src/module/document/ui/drawer_cut_documents.tsx index 16a70c0..161111a 100644 --- a/src/module/document/ui/drawer_cut_documents.tsx +++ b/src/module/document/ui/drawer_cut_documents.tsx @@ -8,11 +8,11 @@ import toast from "react-hot-toast"; import { FcFolder } from "react-icons/fc"; import { GoChevronRight } from "react-icons/go"; import { MdFolder } from "react-icons/md"; +import { useWibuRealtime } from "wibu-realtime"; import { funCreateFolder, funGetAllDocument } from "../lib/api_document"; import { IDataDocument, IFormDetailMoreItem, IJalurItem, } from "../lib/type_document"; -import { useWibuRealtime } from "wibu-realtime"; -export default function DrawerCutDocuments({ category, onChoosePath, data, }: { category: string; data: IFormDetailMoreItem[]; onChoosePath: (val: string) => void; }) { +export default function DrawerCutDocuments({ category, loadingAction, onChoosePath, data, }: { category: string; loadingAction: boolean; data: IFormDetailMoreItem[]; onChoosePath: (val: string) => void; }) { const [opened, setOpened] = useState(false); const param = useParams<{ id: string }>(); const [path, setPath] = useState("home"); @@ -100,6 +100,7 @@ export default function DrawerCutDocuments({ category, onChoosePath, data, }: { - + diff --git a/src/module/document/ui/drawer_more.tsx b/src/module/document/ui/drawer_more.tsx index 56c6d3d..494132a 100644 --- a/src/module/document/ui/drawer_more.tsx +++ b/src/module/document/ui/drawer_more.tsx @@ -2,15 +2,15 @@ import { keyWibu, LayoutDrawer, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; -import { useParams } from "next/navigation"; +import { useParams, useSearchParams } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { LuFolders, LuFolderSymlink } from "react-icons/lu"; +import { useWibuRealtime } from "wibu-realtime"; import { funCopyDocument, funMoveDocument } from "../lib/api_document"; import { IDataDocument } from "../lib/type_document"; import { globalRefreshDocument } from "../lib/val_document"; import DrawerCutDocuments from "./drawer_cut_documents"; -import { useWibuRealtime } from "wibu-realtime"; export default function DrawerMore({ data }: { data: IDataDocument[] }) { const [isCut, setIsCut] = useState(false) @@ -19,20 +19,31 @@ export default function DrawerMore({ data }: { data: IDataDocument[] }) { const param = useParams<{ id: string }>() const [forbidCopy, setForbidCopy] = useState(true) const tema = useHookstate(TEMA) + const [loadingMove, setLoadingMove] = useState(false) + const [loadingCopy, setLoadingCopy] = useState(false) const [dataRealTime, setDataRealtime] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" }) + const searchParams = useSearchParams() + const pathAwal = searchParams.get('path') async function onMoveItem(path: string) { try { + setLoadingMove(true) const res = await funMoveDocument({ path, dataItem: data }) if (res.success) { - setDataRealtime([{ - category: "division-document", - id: path, - }]) + setDataRealtime([ + { + category: "division-document", + id: path, + }, + { + category: "division-document", + id: pathAwal, + } + ]) toast.success(res.message) refresh.set(true) } else { @@ -41,13 +52,16 @@ export default function DrawerMore({ data }: { data: IDataDocument[] }) { } catch (error) { console.error(error) toast.error("Gagal memindahkan item, coba lagi nanti") + } finally { + setLoadingMove(false) + setIsCut(false) } - setIsCut(false) } async function onCopyItem(path: string) { try { + setLoadingCopy(true) const res = await funCopyDocument({ idDivision: param.id, path, dataItem: data }) if (res.success) { setDataRealtime([{ @@ -62,8 +76,10 @@ export default function DrawerMore({ data }: { data: IDataDocument[] }) { } catch (error) { console.error(error) toast.error("Gagal memindahkan item, coba lagi nanti") + } finally { + setLoadingCopy(false) + setIsCopy(false) } - setIsCopy(false) } @@ -108,11 +124,11 @@ export default function DrawerMore({ data }: { data: IDataDocument[] }) { setIsCut(false)} title={'Pilih Lokasi Pemindahan'} size="lg"> - { onMoveItem(val) }} category="move" /> + { onMoveItem(val) }} category="move" /> setIsCopy(false)} title={'Pilih Lokasi Salin'} size="lg"> - { onCopyItem(val) }} category="copy" /> + { onCopyItem(val) }} category="copy" /> ); diff --git a/src/module/document/ui/navbar_document_division.tsx b/src/module/document/ui/navbar_document_division.tsx index 7fe8071..c6e929a 100644 --- a/src/module/document/ui/navbar_document_division.tsx +++ b/src/module/document/ui/navbar_document_division.tsx @@ -18,13 +18,13 @@ import { HiMenu } from "react-icons/hi"; import { LuShare2 } from "react-icons/lu"; import { MdClose, MdOutlineMoreHoriz } from "react-icons/md"; import { RiListCheck } from "react-icons/ri"; +import { useWibuRealtime } from "wibu-realtime"; import { funDeleteDocument, funGetAllDocument, funRenameDocument, } from "../lib/api_document"; import { IDataDocument, IJalurItem } from "../lib/type_document"; import { globalRefreshDocument } from "../lib/val_document"; import DrawerMenuDocumentDivision from "./drawer_menu_document_division"; import DrawerMore from "./drawer_more"; import DrawerShareDocument from "./drawer_share_document"; -import { useWibuRealtime } from "wibu-realtime"; export default function NavbarDocumentDivision() { const router = useRouter(); @@ -52,6 +52,8 @@ export default function NavbarDocumentDivision() { const isMobile2 = useMediaQuery("(max-width: 496px)"); const tema = useHookstate(TEMA); const [loading, setLoading] = useState(true); + const [loadingRename, setLoadingRename] = useState(false); + const [loadingDelete, setLoadingDelete] = useState(false); const [dataRealTime, setDataRealtime] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" @@ -138,8 +140,9 @@ export default function NavbarDocumentDivision() { }; async function onConfirmDelete(val: boolean) { - if (val) { - try { + try { + if (val) { + setLoadingDelete(true) const respon = await funDeleteDocument(selectedFiles); if (respon.success) { getOneData(false); @@ -150,19 +153,20 @@ export default function NavbarDocumentDivision() { } else { toast.error(respon.message); } - } catch (error) { - console.error(error); - toast.error("Gagal menghapus item, coba lagi nanti"); + handleBatal(); } - - handleBatal(); + } catch (error) { + console.error(error); + toast.error("Gagal menghapus item, coba lagi nanti"); + } finally { + setLoadingDelete(false) + setIsDelete(false); } - - setIsDelete(false); } async function onRenameSubmit() { try { + setLoadingRename(true); const res = await funRenameDocument(bodyRename); if (res.success) { setDataRealtime([{ @@ -176,11 +180,13 @@ export default function NavbarDocumentDivision() { } catch (error) { console.error(error); toast.error("Gagal mengganti nama item, coba lagi nanti"); + } finally { + setLoadingRename(false) + setSelectedFiles([]); + setDariSelectAll(false); + setRename(false); } - setSelectedFiles([]); - setDariSelectAll(false); - setRename(false); } useShallowEffect(() => { @@ -802,6 +808,7 @@ export default function NavbarDocumentDivision() { {/* MODAL KONFIRMASI DELETE */} setIsDelete(false)} description="Apakah Anda yakin ingin menghapus item?" @@ -861,6 +868,7 @@ export default function NavbarDocumentDivision() {