diff --git a/src/app/(application)/division/[id]/(fitur-division)/calender/[detail]/page.tsx b/src/app/(application)/division/[id]/(fitur-division)/calender/[detail]/page.tsx index eca6613..db733bc 100644 --- a/src/app/(application)/division/[id]/(fitur-division)/calender/[detail]/page.tsx +++ b/src/app/(application)/division/[id]/(fitur-division)/calender/[detail]/page.tsx @@ -1,10 +1,9 @@ import { DetailEventDivision } from '@/module/calender'; -import React from 'react'; function Page() { return ( <> - + ); } diff --git a/src/app/api/calender/[id]/route.ts b/src/app/api/calender/[id]/route.ts index 7c177b6..c080ed9 100644 --- a/src/app/api/calender/[id]/route.ts +++ b/src/app/api/calender/[id]/route.ts @@ -100,7 +100,7 @@ export async function GET(request: Request, context: { params: { id: string } }) } - return NextResponse.json({ success: true, message: "Berhasil mendapatkan kalender", data: dataFix }, { status: 200 }); + return NextResponse.json({ success: true, message: "Berhasil mendapatkan kalender", data: dataFix, user: user.id }, { status: 200 }); } catch (error) { return NextResponse.json({ success: false, message: "Gagal mendapatkan kalender, data tidak ditemukan (error: 500)", }, { status: 500 }); @@ -139,13 +139,16 @@ export async function DELETE(request: Request, context: { params: { id: string } }, data: { isActive: false + }, + select: { + dateStart: true } }); // create log user const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus data acara kalender', table: 'divisionCalendar', data: id }) - return NextResponse.json({ success: true, message: "Berhasil menghapus acara kalender", data }, { status: 200 }); + return NextResponse.json({ success: true, message: "Berhasil menghapus acara kalender", data, user: user.id }, { status: 200 }); } catch (error) { return NextResponse.json({ success: false, message: "Gagal menghapus kalender, coba lagi nanti (error: 500)", }, { status: 500 } 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/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/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/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/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/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 80e9de8..b821ea4 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: "0.1.8", mode: "staging" }, { status: 200 }); + return NextResponse.json({ success: true, version: "0.1.9", mode: "staging" }, { 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/app/page.tsx b/src/app/page.tsx index 4bec541..132e428 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,4 @@ -import { pwd_key_config } from "@/module/_global"; import { funDetectCookies, ViewLogin } from "@/module/auth"; -import { unsealData } from "iron-session"; -import _ from "lodash"; -import { cookies } from "next/headers"; import { redirect } from "next/navigation"; export default async function Home() { diff --git a/src/module/auth/login/view/view_login.tsx b/src/module/auth/login/view/view_login.tsx index 8fba247..4d6e0af 100644 --- a/src/module/auth/login/view/view_login.tsx +++ b/src/module/auth/login/view/view_login.tsx @@ -1,28 +1,14 @@ "use client" import { LayoutLogin, WARNA } from "@/module/_global"; -import { - Box, - Button, - Checkbox, - Image, - rem, - Stack, - Text, - TextInput, - Title, -} from "@mantine/core"; -import { useRouter } from "next/navigation"; -import React, { useState } from "react"; +import { Box, Button, Stack, Text, TextInput } from "@mantine/core"; +import { useFocusTrap } from "@mantine/hooks"; +import { useState } from "react"; import toast from "react-hot-toast"; import ViewVerification from "../../varification/view/view_verification"; -import { useFocusTrap } from "@mantine/hooks"; function ViewLogin() { const focusTrapRef = useFocusTrap() - const router = useRouter() - const textInfo = - "Kami akan mengirimkan kode verifikasi melalui WhatsApp untuk mengonfirmasi nomor Anda."; - + const textInfo = "Kami akan mengirimkan kode verifikasi melalui WhatsApp untuk mengonfirmasi nomor Anda."; const [isPhone, setPhone] = useState("") const [isOTP, setOTP] = useState(null) const [isValPhone, setValPhone] = useState(null) @@ -37,46 +23,50 @@ function ViewLogin() { if (isPhone.toString().length <= 11) return toast.error('Nomor telepon tidak valid') - const cek = await fetch('/api/auth/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ phone: isPhone }) - }) - const cekLogin = await cek.json() - - if (cekLogin.success) { - const code = Math.floor(Math.random() * 1000) + 1000 + try { setLoading(true) - try { - const res = await fetch(`https://wa.wibudev.com/code?nom=${cekLogin.phone}&text=*DARMASABA*%0A%0A - JANGAN BERIKAN KODE RAHASIA ini kepada siapa pun TERMASUK PIHAK DARMASABA. Masukkan otentikasi: *${encodeURIComponent(code)}*`).then( - async (res) => { - if (res.status == 200) { - setValPhone(cekLogin.phone) - setOTP(code) - setUser(cekLogin.id) - setVerif(true) - setLoading(false) - toast.success('Kode verifikasi telah dikirim') - } else { - console.error(res.status) - toast.error('Internal Server Error') - setLoading(false) + const cek = await fetch('/api/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ phone: isPhone }) + }) + const cekLogin = await cek.json() + if (cekLogin.success) { + const code = Math.floor(Math.random() * 1000) + 1000 + try { + const res = await fetch(`https://wa.wibudev.com/code?nom=${cekLogin.phone}&text=*DARMASABA*%0A%0A + JANGAN BERIKAN KODE RAHASIA ini kepada siapa pun TERMASUK PIHAK DARMASABA. Masukkan otentikasi: *${encodeURIComponent(code)}*`).then( + async (res) => { + if (res.status == 200) { + setValPhone(cekLogin.phone) + setOTP(code) + setUser(cekLogin.id) + setVerif(true) + toast.success('Kode verifikasi telah dikirim') + } else { + console.error(res.status) + toast.error('Internal Server Error') + } } - } - ) - } catch (error) { - console.error(error) - toast.error('Internal Server Error') - }finally{ - setLoading(false) + ) + } catch (error) { + console.error(error) + toast.error('Internal Server Error') + } + } else { + return toast.error(cekLogin.message) } - } else { - return toast.error(cekLogin.message) + } catch (error) { + console.error(error) + toast.error('Internal Server Error') + } finally { + setLoading(false) } + + } if (isVerif) return diff --git a/src/module/calender/ui/create_calender_division_caleder.tsx b/src/module/calender/ui/create_calender_division_caleder.tsx index f73c1a3..fd3f4d0 100644 --- a/src/module/calender/ui/create_calender_division_caleder.tsx +++ b/src/module/calender/ui/create_calender_division_caleder.tsx @@ -211,6 +211,7 @@ export default function CreateCalenderDivisionCaleder() { } /> void }) { - const router = useRouter() const param = useParams<{ id: string }>() const [selectedFiles, setSelectedFiles] = useState([]) const [isData, setData] = useState([]) @@ -34,11 +33,9 @@ export default function CreateUserCalender({ onClose }: { onClose: (val: any) => if (member.length > 0) { setSelectedFiles(JSON.parse(JSON.stringify(member.get()))) } - setLoading(false) } else { toast.error(response.message) } - } catch (error) { console.error(error) toast.error("Gagal mendapatkan anggota, coba lagi nanti"); @@ -103,6 +100,7 @@ export default function CreateUserCalender({ 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) @@ -111,6 +109,8 @@ export default function CreateUserCalender({ onClose }: { onClose: (val: any) => } } catch (error) { console.error(error); + } finally { + setLoading(false) } } diff --git a/src/module/calender/ui/create_user_detail_calender.tsx b/src/module/calender/ui/create_user_detail_calender.tsx index 54d1603..1d57395 100644 --- a/src/module/calender/ui/create_user_detail_calender.tsx +++ b/src/module/calender/ui/create_user_detail_calender.tsx @@ -1,18 +1,19 @@ "use client" -import React, { useState } from 'react'; -import { useParams, useRouter } from 'next/navigation'; +import { keyWibu, LayoutNavbarNew, SkeletonList, TEMA } from '@/module/_global'; import { funGetSearchMemberDivision, IDataMemberDivision } from '@/module/division_new'; -import toast from 'react-hot-toast'; +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 { LayoutNavbarNew, SkeletonList, SkeletonSingle, TEMA } from '@/module/_global'; -import { ActionIcon, Avatar, Box, Button, Center, Divider, Flex, Grid, Group, Indicator, rem, Stack, Text, TextInput } from '@mantine/core'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; import { FaCheck } from 'react-icons/fa6'; import { HiMagnifyingGlass } from 'react-icons/hi2'; import { IoArrowBackOutline, IoClose } from 'react-icons/io5'; -import { Carousel } from '@mantine/carousel'; import { funAddMemberCalender, funGetOneCalender } from '../lib/api_calender'; import { IDataDetailByIdCalender, IDataDetailByIdMember } from '../lib/type_calender'; -import { useHookstate } from '@hookstate/core'; +import { useWibuRealtime } from 'wibu-realtime'; export default function CreateUserDetailCalender() { const router = useRouter() @@ -27,6 +28,11 @@ export default function CreateUserDetailCalender() { const [searchQuery, setSearchQuery] = useState('') const tema = useHookstate(TEMA) const isMobile2 = useMediaQuery("(max-width: 438px)"); + const [loadingSubmit, setLoadingSubmit] = useState(false) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getData() { try { @@ -88,9 +94,13 @@ export default function CreateUserDetailCalender() { if (selectedFiles.length == 0) { return toast.error("Error! silahkan pilih anggota") } - + setLoadingSubmit(true) const res = await funAddMemberCalender(String(isDataCalender?.idCalendar), selectedFiles) if (res.success) { + setDataRealtime([{ + category: "calendar-detail", + id: isDataCalender?.idCalendar, + }]) toast.success(res.message) router.push('./') } else { @@ -99,6 +109,8 @@ export default function CreateUserDetailCalender() { } catch (error) { console.error(error); toast.error("Gagal menambahkan anggota, coba lagi nanti"); + } finally { + setLoadingSubmit(false) } } @@ -290,6 +302,7 @@ export default function CreateUserDetailCalender() { backgroundColor: `${tema.get().bgUtama}`, }}> - 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/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() { - } + 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 ( <> - 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..89bb91a 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); @@ -56,17 +54,16 @@ export default function ListDivisionTask() { } }; + useShallowEffect(() => { + fetchData(false) + }, [isPage]) + useShallowEffect(() => { setPage(1) fetchData(true); }, [status, searchQuery]); - - useShallowEffect(() => { - fetchData(false) - }, [isPage]) - useEffect(() => { const handleScroll = async () => { if (containerRef && containerRef.current) { @@ -149,7 +146,7 @@ export default function ListDivisionTask() { Total Tugas - {totalData} + {loading ? 0 : totalData} {isList ? ( @@ -173,7 +170,7 @@ export default function ListDivisionTask() { isData.map((v, i) => { return ( - + router.push(`task/${v.id}`)}> - router.push(`task/${v.id}`)}> +
- - {v.progress}% + + {_.isNull(v.progress) ? 0 : v.progress}% {v.desc} diff --git a/src/module/task/ui/tabs_division_task.tsx b/src/module/task/ui/tabs_division_task.tsx index 7d74a22..8214273 100644 --- a/src/module/task/ui/tabs_division_task.tsx +++ b/src/module/task/ui/tabs_division_task.tsx @@ -1,14 +1,13 @@ 'use client' -import { Box, Button, Flex, rem, Tabs } from "@mantine/core"; +import { TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; +import { Box, Button, Flex, rem } from "@mantine/core"; +import { useRouter, useSearchParams } from "next/navigation"; import { IoIosCheckmarkCircleOutline } from "react-icons/io"; import { IoCloseCircleOutline } from "react-icons/io5"; import { RiProgress3Line } from "react-icons/ri"; import { TbClockPause } from "react-icons/tb"; import ListDivisionTask from "./list_division_task"; -import { useRouter, useSearchParams } from "next/navigation"; -import { Carousel } from "@mantine/carousel"; -import { TEMA } from "@/module/_global"; -import { useHookstate } from "@hookstate/core"; export default function TabsDivisionTask() { const iconStyle = { width: rem(20), height: rem(20) }; @@ -43,40 +42,40 @@ export default function TabsDivisionTask() { return ( - - {dataStatus.map((item, index) => ( - - ))} - - + style={{ + display: "flex", + gap: "20px", + position: "relative", + overflowX: "scroll", + scrollbarWidth: "none", + maxWidth: "550px" + }} + > + + {dataStatus.map((item, index) => ( + + ))} + + ) 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'] } });