diff --git a/public/assets/img/banner/bg-10.png b/public/assets/img/banner/bg-10.png new file mode 100644 index 0000000..1077397 Binary files /dev/null and b/public/assets/img/banner/bg-10.png differ diff --git a/src/app/api/division/[id]/detail/route.ts b/src/app/api/division/[id]/detail/route.ts index 730e272..da6d7f5 100644 --- a/src/app/api/division/[id]/detail/route.ts +++ b/src/app/api/division/[id]/detail/route.ts @@ -100,6 +100,7 @@ export async function GET(request: Request, context: { params: { id: string } }) }, select: { id: true, + idProject: true, title: true, dateStart: true, dateEnd: true, @@ -166,7 +167,7 @@ export async function GET(request: Request, context: { params: { id: string } }) allData = diskusi.map((v: any) => ({ ..._.omit(v, ["createdAt", "User"]), - date: moment(v.dateStart).format("ll"), + date: moment(v.createdAt).format("ll"), user: v.User.name })) } diff --git a/src/app/api/home/route.ts b/src/app/api/home/route.ts index 6f6b3d2..95342f9 100644 --- a/src/app/api/home/route.ts +++ b/src/app/api/home/route.ts @@ -420,7 +420,7 @@ export async function GET(request: Request) { allData = data.map((v: any) => ({ ..._.omit(v, ["createdAt", "User"]), - date: moment(v.dateStart).format("ll"), + date: moment(v.createdAt).format("ll"), user: v.User.name })) } else if (kategori == "header") { diff --git a/src/app/api/position/[id]/route.ts b/src/app/api/position/[id]/route.ts index 5d0d165..f469d63 100644 --- a/src/app/api/position/[id]/route.ts +++ b/src/app/api/position/[id]/route.ts @@ -120,7 +120,6 @@ export async function PUT(request: Request, context: { params: { id: string } }) }, data: { name: data.name, - // idGroup: data.idGroup, }, }); diff --git a/src/app/api/position/route.ts b/src/app/api/position/route.ts index 9112a5f..be5acd7 100644 --- a/src/app/api/position/route.ts +++ b/src/app/api/position/route.ts @@ -113,6 +113,7 @@ export async function POST(request: Request) { select: { id: true, name: true, + idGroup: true }, }); diff --git a/src/app/api/user/[id]/route.ts b/src/app/api/user/[id]/route.ts index 7936848..f1d96c0 100644 --- a/src/app/api/user/[id]/route.ts +++ b/src/app/api/user/[id]/route.ts @@ -111,6 +111,10 @@ export async function DELETE(request: Request, context: { params: { id: string } data: { isActive: !isActive, }, + select: { + id: true, + idGroup: true, + }, }); // create log user @@ -120,7 +124,7 @@ export async function DELETE(request: Request, context: { params: { id: string } { success: true, message: "Berhasil mengupdate status anggota", - result, + data: result, }, { status: 200 } ); diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts index 80ea26a..493ef89 100644 --- a/src/app/api/user/route.ts +++ b/src/app/api/user/route.ts @@ -186,7 +186,8 @@ export async function POST(request: Request) { idUserRole: data.idUserRole, }, select: { - id: true + id: true, + idGroup: true, }, }); @@ -211,7 +212,7 @@ export async function POST(request: Request) { // create log user const log = await createLogUser({ act: 'CREATE', desc: 'User membuat data user baru', table: 'user', data: users.id }) - return Response.json({ success: true, message: 'Sukses membuat user' }, { status: 200 }); + return Response.json({ success: true, message: 'Sukses membuat user', data: users}, { status: 200 }); } else { return Response.json({ success: false, message: "User sudah ada" }, { status: 400 }); } diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 480f39a..a8fa37d 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.2", mode: "staging" }, { status: 200 }); + return NextResponse.json({ success: true, version: "0.1.4", mode: "staging" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); diff --git a/src/module/_global/components/reload_button_top.tsx b/src/module/_global/components/reload_button_top.tsx index cf2dd68..ca94071 100644 --- a/src/module/_global/components/reload_button_top.tsx +++ b/src/module/_global/components/reload_button_top.tsx @@ -13,7 +13,7 @@ export default function ReloadButtonTop({ onReload, title }: { onReload: () => v useShallowEffect(() => { const timer = setTimeout(() => { setOpened(true); - }, 2000); + }, 500); return () => clearTimeout(timer); }, []); diff --git a/src/module/_global/components/skeleton_banner.tsx b/src/module/_global/components/skeleton_banner.tsx new file mode 100644 index 0000000..4267665 --- /dev/null +++ b/src/module/_global/components/skeleton_banner.tsx @@ -0,0 +1,35 @@ +import { useHookstate } from '@hookstate/core'; +import { Box, Flex, Group, Paper, Skeleton } from '@mantine/core'; +import { TEMA } from '../bin/val_global'; + +export default function SkeletonBanner() { + const tema = useHookstate(TEMA) + return ( + + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/module/_global/components/wrap_layout.tsx b/src/module/_global/components/wrap_layout.tsx index 816c315..1902a2a 100644 --- a/src/module/_global/components/wrap_layout.tsx +++ b/src/module/_global/components/wrap_layout.tsx @@ -5,7 +5,9 @@ import { useShallowEffect } from "@mantine/hooks"; import { useEffect, useState } from "react"; import { useWibuRealtime } from "wibu-realtime"; import NotificationCustome from "./notification_custome"; -import { useRouter } from "next/navigation"; +import { usePathname, useRouter } from "next/navigation"; +import { globalParamJumlahNotif } from "@/module/home"; +import ReloadButtonTop from "./reload_button_top"; export default function WrapLayout({ children, role, theme, user }: { children: React.ReactNode, role: any, theme: any, user: any }) { const router = useRouter() @@ -13,10 +15,13 @@ export default function WrapLayout({ children, role, theme, user }: { children: const tema = useHookstate(TEMA) const notifLoadPage = useHookstate(globalNotifPage) const [tampilNotif, setTampilNotif] = useState(false) + const paramNotif = useHookstate(globalParamJumlahNotif) const [data, setData] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" }) + const path = usePathname() + useEffect(() => { roleLogin.set(role) @@ -26,10 +31,18 @@ export default function WrapLayout({ children, role, theme, user }: { children: useShallowEffect(() => { if (data && data.some((i: any) => i.idUserTo == user)) { - setTampilNotif(true) - setTimeout(() => { - setTampilNotif(false); - }, 4000); + if (data.some((i: any) => i.category == path.substring(1))) { + notifLoadPage.set({ + category: path.substring(1), + load: true + }) + } else { + setTampilNotif(true) + paramNotif.set(!paramNotif.get()) + setTimeout(() => { + setTampilNotif(false); + }, 4000); + } } }, [data]) @@ -40,7 +53,6 @@ export default function WrapLayout({ children, role, theme, user }: { children: return ( <> - {/* {JSON.stringify(data)} */} { tampilNotif && { '' }} /> } - {children} ); diff --git a/src/module/announcement/ui/list_announcement.tsx b/src/module/announcement/ui/list_announcement.tsx index 93f3f89..5a0c2c6 100644 --- a/src/module/announcement/ui/list_announcement.tsx +++ b/src/module/announcement/ui/list_announcement.tsx @@ -1,28 +1,29 @@ 'use client' -import { currentScroll, globalNotifPage, SkeletonSingle, SkeletonUser, TEMA, WARNA } from '@/module/_global'; -import { ActionIcon, Box, Center, Divider, Grid, Group, Spoiler, Stack, Text, TextInput } from '@mantine/core'; -import React, { useEffect, useState } from 'react'; -import { TfiAnnouncement } from "react-icons/tfi"; -import { HiMagnifyingGlass } from 'react-icons/hi2'; -import { useRouter, useSearchParams } from 'next/navigation'; -import { useShallowEffect } from '@mantine/hooks'; -import { IListDataAnnouncement } from '../lib/type_announcement'; -import { funGetAllAnnouncement } from '../lib/api_announcement'; -import toast from 'react-hot-toast'; +import { currentScroll, globalNotifPage, ReloadButtonTop, SkeletonUser, TEMA } from '@/module/_global'; +import { funGetUserByCookies } from '@/module/auth'; import { useHookstate } from '@hookstate/core'; +import { ActionIcon, Box, Center, Divider, Grid, Spoiler, Stack, Text, TextInput } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import toast from 'react-hot-toast'; +import { HiMagnifyingGlass } from 'react-icons/hi2'; +import { TfiAnnouncement } from "react-icons/tfi"; +import { funGetAllAnnouncement } from '../lib/api_announcement'; +import { IListDataAnnouncement } from '../lib/type_announcement'; export default function ListAnnouncement() { const [isData, setIsData] = useState([]) + const [user, setUser] = useState('') const [searchQuery, setSearchQuery] = useState('') const router = useRouter() const [loading, setLoading] = useState(true); const tema = useHookstate(TEMA) - const load = useHookstate(globalNotifPage) - - // ini + const notifLoadPage = useHookstate(globalNotifPage) const { value: containerRef } = useHookstate(currentScroll); const [isPage, setPage] = useState(1) + const [isRefresh, setRefresh] = useState(false) const fetchData = async (loading: boolean) => { @@ -31,11 +32,11 @@ export default function ListAnnouncement() { setLoading(true) const response = await funGetAllAnnouncement('?search=' + searchQuery + '&page=' + isPage) if (response.success) { - if (isPage == 1) { - setIsData(response?.data) - } else { - setIsData([...isData, ...response?.data]) - } + if (isPage == 1) { + setIsData(response?.data) + } else { + setIsData([...isData, ...response?.data]) + } } else { toast.error(response.message); } @@ -48,7 +49,12 @@ export default function ListAnnouncement() { } } - function onSearch(val:string){ + async function onUser() { + const user = await funGetUserByCookies() + setUser(String(user.id)) + } + + function onSearch(val: string) { setSearchQuery(val) setPage(1) } @@ -63,6 +69,11 @@ export default function ListAnnouncement() { }, [isPage]) + useShallowEffect(() => { + onUser() + }, []) + + useEffect(() => { const handleScroll = async () => { @@ -84,8 +95,35 @@ export default function ListAnnouncement() { }; }, [containerRef, isPage]); + + useShallowEffect(() => { + if (notifLoadPage.get().category == 'announcement' && notifLoadPage.get().load == true) { + setRefresh(true) + } + }, [notifLoadPage.get().load]) + + function onRefresh() { + notifLoadPage.set({ + category: '', + load: false + }) + setRefresh(false) + setPage(1) + setTimeout(() => { + fetchData(true) + }, 500) + } + return ( + { + isRefresh && + { onRefresh() }} + title='UPDATE' + /> + + } ({ ...touched, title: true })) + nilai = false + } + + if (!imgForm) { + setTouched(touched => ({ ...touched, image: true })) + nilai = false + } + + + return nilai + } + function onValidation(kategori: string, val: any) { + if (kategori == 'title') { + setListData({ ...listData, title: val }) + if (val === "") { + setTouched({ ...touched, title: true }) + } else { + setTouched({ ...touched, title: false }) + } + } else if (kategori == 'image') { + if (imgForm) { + setTouched({ ...touched, image: true }) + } else { + setTouched({ ...touched, image: false }) + } + } + } return ( @@ -102,7 +120,7 @@ function CreateBanner() { }} activateOnClick={false} maxSize={1 * 1024 ** 2} - accept={['image/png', 'imagfe/jpeg', 'image/heic']} + accept={['image/png', 'image/jpeg', 'image/heic']} onReject={(files) => { return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB') }} @@ -111,13 +129,20 @@ function CreateBanner() { { img ? - + + : @@ -133,12 +158,18 @@ function CreateBanner() { /> -
- +
+ Klik Untuk Upload Image - - Ukuran Foto Tidak Boleh Lebih Dari 1MB + + Mohon unggah gambar dalam resolusi + + + 1535 x 450 piksel untuk memastikan + + + tampilan sesuai dengan kebutuhan desain.
@@ -146,12 +177,19 @@ function CreateBanner() { + + {touched.image && !imgForm && ( + + Silahkan Pilih Gambar + + )} + - Judul Banner
} - value={listData.title} + label="Judul Banner" placeholder='Judul Banner' + value={listData.title} onChange={(e) => { setListData({ ...listData, title: e.target.value }) onValidation('title', e.target.value) @@ -162,8 +200,13 @@ function CreateBanner() { borderRadius: 10, }, }} - - + required + size='md' + error={ + touched.title && ( + listData.title == "" ? "Judul Banner Tidak Boleh Kosong" : null + ) + } /> { - if (touched.title || touched.image) { - toast.error("Mohon Isi Semua Data") - } else { - setModal(true) - } - }} + bg={tema.get().utama} + radius={30} + fullWidth + onClick={() => { onCheck() }} >Simpan diff --git a/src/module/banner/ui/edit_banner.tsx b/src/module/banner/ui/edit_banner.tsx index 411c731..30c5d91 100644 --- a/src/module/banner/ui/edit_banner.tsx +++ b/src/module/banner/ui/edit_banner.tsx @@ -2,7 +2,7 @@ import { LayoutNavbarNew, TEMA, WARNA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; -import { Box, Button, Image, Paper, rem, TextInput } from '@mantine/core'; +import { Box, Button, Image, Paper, rem, Text, TextInput } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { useShallowEffect } from '@mantine/hooks'; import _ from 'lodash'; @@ -44,14 +44,20 @@ export default function EditBanner() { } } else if (kategori == 'image') { if (imgForm) { - setTouched({ ...touched, image: false }) - } else { setTouched({ ...touched, image: true }) + } else { + setTouched({ ...touched, image: false }) } } } - + function onCheck() { + if (Object.values(touched).some((v) => v == true)) + return false + setModal(true) + } + + async function getOneData() { try { const res = await funGetOneBanner(param.id) @@ -128,7 +134,7 @@ export default function EditBanner() { { setData({ ...data, title: e.target.value }) @@ -142,6 +148,11 @@ export default function EditBanner() { }} required size='md' + error={ + touched.title && ( + data.title == "" ? "Judul Banner Tidak Boleh Kosong" : null + ) + } /> diff --git a/src/module/banner/ui/git.keep b/src/module/banner/ui/git.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/module/banner/ui/list_banner.tsx b/src/module/banner/ui/list_banner.tsx index 4d8de51..05b2c2b 100644 --- a/src/module/banner/ui/list_banner.tsx +++ b/src/module/banner/ui/list_banner.tsx @@ -1,22 +1,21 @@ 'use client' -import { currentScroll, LayoutDrawer, LayoutModalViewFile, TEMA, WARNA } from '@/module/_global'; +import { LayoutDrawer, LayoutModalViewFile, TEMA, WARNA } from '@/module/_global'; +import SkeletonBanner from '@/module/_global/components/skeleton_banner'; import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; -import { ActionIcon, Anchor, Box, Flex, Group, Image, Paper, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; -import { useParams, useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; -import { FaFile, FaPencil, FaTrash } from 'react-icons/fa6'; -import { IDataBanner } from '../lib/type_banner'; -import toast from 'react-hot-toast'; +import { ActionIcon, Box, Flex, Group, Image, Paper, SimpleGrid, Stack, Text } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { funDeleteBanner, funGetAllBanner, funGetOneBanner } from '../lib/api_banner'; -import { HiMagnifyingGlass } from 'react-icons/hi2'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { FaFile, FaPencil, FaTrash } from 'react-icons/fa6'; +import { funDeleteBanner, funGetAllBanner } from '../lib/api_banner'; +import { IDataBanner } from '../lib/type_banner'; function ListBanner() { - const [isList, setIsList] = useState(false) + const tema = useHookstate(TEMA) const router = useRouter(); - const param = useParams<{ id: string }>() const [isOpenModalView, setOpenModalView] = useState(false) const [isOpenModal, setOpenModal] = useState(false) const [openDrawer, setOpenDrawer] = useState(false); @@ -26,18 +25,12 @@ function ListBanner() { const [isData, setData] = useState([]) const [idData, setIdData] = useState('') const [isPage, setPage] = useState(1) - const [searchQuerry, setSearchQuerry] = useState('') - // const { value: containerRef } = useHookstate(currentScroll); - - const handleList = () => { - setIsList(!isList) - } const fetchData = async (loading: boolean) => { try { if (loading) setLoading(true) - const response = await funGetAllBanner('?search=' + searchQuerry) + const response = await funGetAllBanner('?page=' + isPage) if (response.success) { setData(response.data.map((banner: { image: any; }) => ({ ...banner, image: banner.image }))); } else { @@ -52,15 +45,10 @@ function ListBanner() { } } - function searchBanner(search: string) { - setSearchQuerry(search) - setPage(1) - } - - useShallowEffect(() => { fetchData(true) - }, [searchQuerry]) + setPage(1) + }, [isPage]) useShallowEffect(() => { fetchData(false) @@ -92,65 +80,61 @@ function ListBanner() { return ( - } - placeholder='pencarian' - value={searchQuerry} - onChange={(val) => { searchBanner(val.target.value) }} - /> + {loading + ? + Array(7) + .fill(null) + .map((_, i) => ( + + + + )) + : + + {isData.length == 0 ? + + Tidak ada Banner + + : + isData.map((v, i) => { + return ( + + { + setIdData(v.id); + setIdDataStorage(v.image); + setExtension(v.extension); + setOpenDrawer(true) + } + } + style={{ + width: '100%', + maxWidth: 550, + height: 85, + backgroundColor: 'transparent', + border: `1px solid ${tema.get().bgTotalKegiatan}` + + }}> + + + + + {v.title} + + + + ) + }) + } + + } - - - - - {isData.map((v, index) => ( - { - setIdData(v.id); - setIdDataStorage(v.image); - setExtension(v.extension); - setOpenDrawer(true) - } - } - style={{ - width: '100%', - maxWidth: 550, - height: 85, - backgroundColor: 'transparent', - border: `1px solid ${tema.get().bgTotalKegiatan}` - - }}> - - - - - - {v.title} - - - - ))} - - - - - - setOpenDrawer(false)}> @@ -161,22 +145,22 @@ function ListBanner() { alignItems: "flex-start" }} > - router.push(`/banner/edit/${idData}`)} direction="column" align="center" justify="center" pb={20}> - - - - - Edit - - - + router.push(`/banner/edit/${idData}`)} direction="column" align="center" justify="center" pb={20}> + + + + + Edit + + + { setOpenModalView(true) }} direction={"column"} align={"center"} justify={"center"}> - Lihat File + Lihat File @@ -185,7 +169,7 @@ function ListBanner() { - Hapus + Hapus @@ -210,28 +194,3 @@ function ListBanner() { } export default ListBanner; - - - - -// useEffect(() => { -// const handleScroll = async () => { -// if (containerRef && containerRef.current) { -// const scrollTop = containerRef.current.scrollTop; -// const containerHeight = containerRef.current.clientHeight; -// const scrollHeight = containerRef.current.scrollHeight; - -// if (scrollTop + containerHeight + 1 >= scrollHeight) { -// setPage(isPage + 1) -// } -// } -// }; - -// const container = containerRef?.current; -// container?.addEventListener("scroll", handleScroll); - -// return () => { -// container?.removeEventListener("scroll", handleScroll); -// }; -// }, [containerRef, isPage]); - diff --git a/src/module/calender/ui/create_calender_division_caleder.tsx b/src/module/calender/ui/create_calender_division_caleder.tsx index 9ec3e2a..f73c1a3 100644 --- a/src/module/calender/ui/create_calender_division_caleder.tsx +++ b/src/module/calender/ui/create_calender_division_caleder.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 { Avatar, Box, Button, Divider, Grid, Group, rem, Select, SimpleGrid, Stack, Text, Textarea, TextInput } from '@mantine/core'; @@ -10,6 +10,7 @@ import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import toast from 'react-hot-toast'; import { IoIosArrowDropright } from 'react-icons/io'; +import { useWibuRealtime } from 'wibu-realtime'; import { funCreateCalender } from '../lib/api_calender'; import { IFormMemberCalender } from '../lib/type_calender'; import { globalCalender } from '../lib/val_calender'; @@ -47,6 +48,11 @@ export default function CreateCalenderDivisionCaleder() { desc: "", repeatValue: "1" }) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + async function onSubmit(val: boolean) { try { @@ -65,6 +71,11 @@ export default function CreateCalenderDivisionCaleder() { }) if (response.success) { + setDataRealtime([{ + category: "calendar-event", + division: param.id, + date: isData.dateStart + }]) setModal(false) router.push(`/division/${param.id}/calender`) toast.success(response.message) diff --git a/src/module/calender/ui/date_event_division.tsx b/src/module/calender/ui/date_event_division.tsx index 125241b..04a495f 100644 --- a/src/module/calender/ui/date_event_division.tsx +++ b/src/module/calender/ui/date_event_division.tsx @@ -1,14 +1,16 @@ +import { keyWibu } from '@/module/_global'; import { Box, Divider, Flex, Grid, Group, Indicator, Skeleton, Text } from '@mantine/core'; import { DatePicker, DatePickerProps } from '@mantine/dates'; -import { useParams, useRouter } from 'next/navigation'; -import React, { useState } from 'react'; -import { funGetAllCalender, funGetIndicatorCalender } from '../lib/api_calender'; import { useMediaQuery, useSetState, useShallowEffect } from '@mantine/hooks'; -import { IDataCalender } from '../lib/type_calender'; -import moment from 'moment'; +import 'dayjs/locale/id'; import _ from 'lodash'; +import moment from 'moment'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; import toast from 'react-hot-toast'; -import 'dayjs/locale/id' +import { useWibuRealtime } from 'wibu-realtime'; +import { funGetAllCalender, funGetIndicatorCalender } from '../lib/api_calender'; +import { IDataCalender } from '../lib/type_calender'; export default function DateEventDivision() { @@ -20,11 +22,15 @@ export default function DateEventDivision() { const [isMonth, setMonth] = useState(moment().month() + 1) const [loading, setLoading] = useState(true) const isMobile = useMediaQuery('(max-width: 369px)'); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) - const getData = async (tgl: any) => { + const getData = async (tgl: any, loading: boolean) => { try { - setLoading(true) + setLoading(loading) const response = await funGetAllCalender('?division=' + param.id + '&date=' + tgl) if (response.success) { setData(response.data) @@ -66,12 +72,12 @@ export default function DateEventDivision() { function change(val: Date) { const a: any = moment(new Date(val)).format('YYYY-MM-DD') setDate(a) - getData(a) + getData(a, true) } useShallowEffect(() => { - getData(isDate) + getData(isDate, true) getIndicator(isDate) }, []) @@ -88,6 +94,15 @@ export default function DateEventDivision() { ); }; + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'calendar-event' && i.division == param.id && i.date == isDate)) { + getIndicator(isDate) + getData(isDate, false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'calendar-event' && i.division == param.id && i.date != isDate)) { + getIndicator(isDate) + } + }, [dataRealTime]) + diff --git a/src/module/discussion/ui/detail_discussion.tsx b/src/module/discussion/ui/detail_discussion.tsx index 6e6cadd..78e11a4 100644 --- a/src/module/discussion/ui/detail_discussion.tsx +++ b/src/module/discussion/ui/detail_discussion.tsx @@ -1,21 +1,22 @@ "use client" +import { globalRole, keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; +import { globalIsAdminDivision } from "@/module/division_new"; +import { useHookstate } from "@hookstate/core"; import { ActionIcon, Avatar, Badge, Box, Center, Divider, Flex, Grid, Group, rem, Skeleton, Spoiler, Text, TextInput } from "@mantine/core"; -import { globalRole, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; -import { GrChatOption } from "react-icons/gr"; -import { LuSendHorizonal } from "react-icons/lu"; -import { useState } from "react"; -import { funCreateComent, funGetDiscussionById } from "../lib/api_discussion"; -import { useShallowEffect } from "@mantine/hooks"; -import { IDetailDiscussion } from "../lib/type_discussion"; +import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import moment from "moment"; import "moment/locale/id"; import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; import toast from "react-hot-toast"; -import { useHookstate } from "@hookstate/core"; -import { globalRefreshDiscussion } from "../lib/val_discussion"; +import { GrChatOption } from "react-icons/gr"; import { HiMenu } from "react-icons/hi"; +import { LuSendHorizonal } from "react-icons/lu"; +import { useWibuRealtime } from "wibu-realtime"; +import { funCreateComent, funGetDiscussionById } from "../lib/api_discussion"; +import { IDetailDiscussion } from "../lib/type_discussion"; +import { globalRefreshDiscussion } from "../lib/val_discussion"; import DrawerDetailDiscussion from "./drawer_detail_discussion"; -import {globalIsAdminDivision } from "@/module/division_new"; export default function DetailDiscussion({ id, idDivision }: { id: string, idDivision: string }) { const [isData, setData] = useState() @@ -28,10 +29,16 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv const [isCreator, setCreator] = useState(false) const adminLogin = useHookstate(globalIsAdminDivision) const tema = useHookstate(TEMA) + const isMobile = useMediaQuery('(max-width: 369px)'); + const isMobile2 = useMediaQuery("(max-width: 438px)"); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) - const getData = async () => { + const getData = async (loading: boolean) => { try { - setIsLoad(true) + setIsLoad(loading) const response = await funGetDiscussionById(id) setData(response.data) setIsLoad(false) @@ -44,9 +51,15 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv } useShallowEffect(() => { - getData() + getData(true) }, [refresh.get()]) + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'discussion-detail' && i.id == id)) { + getData(false) + } + }, [dataRealTime]) + async function reloadData() { try { const response = await funGetDiscussionById(id) @@ -65,6 +78,10 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv if (response.success) { setIsComent("") + setDataRealtime([{ + category: "discussion-detail", + id: id, + }]) reloadData() } else { toast.error(response.message) @@ -125,24 +142,22 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv <> {isData?.totalComments == 0 ? - - {isData?.username ? - - - - - {isData?.username} - - {isData?.status === 1 ? "BUKA" : "TUTUP"} - - : "" - } - {isData?.createdAt} - + + + + + + + {isData?.username} + {isData?.status === 1 ? "BUKA" : "TUTUP"} + + + + + {isData?.createdAt} + + + : - - - + + + - - - - {isData?.username} - + + + {isData?.username} {isData?.status === 1 ? "BUKA" : "TUTUP"} - - {isData?.createdAt} + + + {isData?.createdAt} + @@ -245,17 +254,17 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv return ( - + - + - + {v.username} - + {moment(v.createdAt).format("ll")} diff --git a/src/module/discussion/ui/drawer_detail_discussion.tsx b/src/module/discussion/ui/drawer_detail_discussion.tsx index a4b5513..90859c6 100644 --- a/src/module/discussion/ui/drawer_detail_discussion.tsx +++ b/src/module/discussion/ui/drawer_detail_discussion.tsx @@ -1,14 +1,15 @@ -import { TEMA } from "@/module/_global"; +import { keyWibu, TEMA } from "@/module/_global"; import LayoutModal from "@/module/_global/layout/layout_modal"; -import { Box, Stack, SimpleGrid, Flex, Text } from "@mantine/core"; +import { useHookstate } from "@hookstate/core"; +import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; +import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { BsTrash3 } from "react-icons/bs"; import { FaCheck, FaPencil } from "react-icons/fa6"; import { MdClose } from "react-icons/md"; +import { useWibuRealtime } from "wibu-realtime"; import { funDeleteDiscussion, funEditStatusDiscussion } from "../lib/api_discussion"; -import { useParams, useRouter } from "next/navigation"; -import { useHookstate } from "@hookstate/core"; import { globalRefreshDiscussion } from "../lib/val_discussion"; export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivision }: { onSuccess: (val: boolean) => void, id: string, status: number, idDivision: string }) { @@ -18,6 +19,10 @@ export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivisi const param = useParams<{ id: string, detail: string }>() const refresh = useHookstate(globalRefreshDiscussion) const tema = useHookstate(TEMA) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function fetchStatusDiscussion(val: boolean) { @@ -28,6 +33,10 @@ export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivisi if (response.success) { toast.success(response.message) refresh.set(!refresh.get()) + setDataRealtime([{ + category: "discussion-detail", + id: id, + }]) onSuccess(false) setValModalStatus(false) } else { diff --git a/src/module/discussion/ui/form_edit_discussion.tsx b/src/module/discussion/ui/form_edit_discussion.tsx index 66cf5d7..54dfe18 100644 --- a/src/module/discussion/ui/form_edit_discussion.tsx +++ b/src/module/discussion/ui/form_edit_discussion.tsx @@ -1,5 +1,5 @@ 'use client' -import { TEMA } from "@/module/_global" +import { keyWibu, TEMA } from "@/module/_global" import LayoutModal from "@/module/_global/layout/layout_modal" import { Box, Group, Avatar, Textarea, Button, Grid, rem, Skeleton } from "@mantine/core" import { useParams, useRouter } from "next/navigation" @@ -9,6 +9,7 @@ import { funEditDiscussion, funGetDiscussionById } from "../lib/api_discussion" import { useShallowEffect } from "@mantine/hooks" import { funGetProfileByCookies } from "@/module/user/profile/lib/api_profile" import { useHookstate } from "@hookstate/core" +import { useWibuRealtime } from "wibu-realtime" export default function FormEditDiscussion() { const [isValModal, setValModal] = useState(false) @@ -22,6 +23,10 @@ export default function FormEditDiscussion() { const [touched, setTouched] = useState({ desc: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function fetchGetOneDiscussion() { try { @@ -45,6 +50,10 @@ export default function FormEditDiscussion() { if (response.success) { toast.success(response.message) setValModal(false) + setDataRealtime([{ + category: "discussion-detail", + id: param.detail, + }]) router.push(`/division/${param.id}/discussion/${param.detail}`) } else { toast.error(response.message) diff --git a/src/module/division_new/lib/type_division.ts b/src/module/division_new/lib/type_division.ts index 4bd051a..4846b1f 100644 --- a/src/module/division_new/lib/type_division.ts +++ b/src/module/division_new/lib/type_division.ts @@ -31,6 +31,7 @@ export interface IDataJumlahDetailDivision { export interface IDataTaskOnDetailDivision { id: string, + idProject: string, title: string, dateStart: string, dateEnd: string, diff --git a/src/module/division_new/ui/carousel_division.tsx b/src/module/division_new/ui/carousel_division.tsx index 5b85c22..f314ea4 100644 --- a/src/module/division_new/ui/carousel_division.tsx +++ b/src/module/division_new/ui/carousel_division.tsx @@ -1,13 +1,46 @@ 'use client' -import React, { useRef } from 'react'; -import { Carousel } from '@mantine/carousel'; import { TEMA } from '@/module/_global'; -import Autoplay from 'embla-carousel-autoplay'; -import { Flex, Text } from '@mantine/core'; +import { funGetAllBanner, IDataBanner } from '@/module/banner'; +import { funGetHome } from '@/module/home'; import { useHookstate } from '@hookstate/core'; +import { Carousel } from '@mantine/carousel'; +import { Flex, Image, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import Autoplay from 'embla-carousel-autoplay'; +import { useRef, useState } from 'react'; +import toast from 'react-hot-toast'; export default function CarouselDivision() { const autoplay = useRef(Autoplay({ delay: 5000 })); const tema = useHookstate(TEMA) + const [isDesa, setDesa] = useState("") + const [isData, setData] = useState([]) + + + const fetchData = async () => { + try { + const res_banner = await funGetAllBanner() + if (res_banner.success) { + setData(res_banner.data) + } else { + toast.error(res_banner.message); + } + const response = await funGetHome('?cat=header') + if (response.success) { + setDesa(response.data.village) + } else { + toast.error(response.message); + } + } catch (error) { + toast.error("Gagal mendapatkan data, coba lagi nanti"); + console.error(error); + } + }; + + + useShallowEffect(() => { + fetchData(); + }, []); + return ( <> - - - INFORMASI DARMASABA - - - - - INFORMASI DARMASABA - - - - - INFORMASI DARMASABA - - + { + isData.length > 0 ? + isData.map((item, index) => ( + + {item.title} + + )) + : + [...Array(3)].map((_, index) => ( + + + INFORMASI {isDesa.toUpperCase()} + + + )) + } ); diff --git a/src/module/division_new/ui/list_division.tsx b/src/module/division_new/ui/list_division.tsx index 0a0a257..da33735 100644 --- a/src/module/division_new/ui/list_division.tsx +++ b/src/module/division_new/ui/list_division.tsx @@ -1,18 +1,18 @@ 'use client' -import { currentScroll, globalRole, LayoutDrawer, LayoutNavbarNew, SkeletonList, SkeletonSingle, TEMA } from '@/module/_global'; +import { currentScroll, globalNotifPage, globalRole, LayoutDrawer, LayoutNavbarNew, 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'; +import _ from 'lodash'; import { useRouter, useSearchParams } from 'next/navigation'; -import React, { useEffect, useState } from 'react'; +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 DrawerDivision from './drawer_division'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import { IDataDivison } from '../lib/type_division'; import { funGetAllDivision } from '../lib/api_division'; -import toast from 'react-hot-toast'; -import { useHookstate } from '@hookstate/core'; -import _ from 'lodash'; +import { IDataDivison } from '../lib/type_division'; +import DrawerDivision from './drawer_division'; export default function ListDivision() { const [isList, setIsList] = useState(false) @@ -29,8 +29,9 @@ export default function ListDivision() { const tema = useHookstate(TEMA) const { value: containerRef } = useHookstate(currentScroll); const [isPage, setPage] = useState(1) - const paddingLift = useMediaQuery('(max-width: 505px)') + const [isRefresh, setRefresh] = useState(false) + const notifLoadPage = useHookstate(globalNotifPage) const handleList = () => { @@ -39,21 +40,23 @@ export default function ListDivision() { const fetchData = async (loading: boolean) => { try { - if (loading) - setLoading(true); + + setLoading(loading); + if (isPage == 1) { + setData([]) + } const response = await funGetAllDivision('?search=' + searchQuery + '&group=' + group + '&page=' + isPage) if (response.success) { setJumlah(response.total) setNameGroup(response.filter.name) if (isPage == 1) { setData(response.data) - }else{ - setData([...data, ...response.data]) + } else { + setData((data) => [...data, ...response.data]) } } else { toast.error(response.message); } - setLoading(false); } catch (error) { toast.error("Gagal mendapatkan divisi, coba lagi nanti"); console.error(error); @@ -74,28 +77,46 @@ export default function ListDivision() { useShallowEffect(() => { fetchData(false) - }, [isPage]) + }, [isPage]) - useEffect(() => { - const handleScroll = async () => { - if (containerRef && containerRef.current) { + useEffect(() => { + const handleScroll = async () => { + if (containerRef && containerRef.current) { const scrollTop = containerRef.current.scrollTop; const containerHeight = containerRef.current.clientHeight; const scrollHeight = containerRef.current.scrollHeight; if (scrollTop + containerHeight >= scrollHeight) { - setPage(isPage + 1) + setPage(isPage + 1) } - } - }; + } + }; - const container = containerRef?.current; - container?.addEventListener("scroll", handleScroll); + const container = containerRef?.current; + container?.addEventListener("scroll", handleScroll); - return () => { - container?.removeEventListener("scroll", handleScroll); - }; -}, [containerRef, isPage]); + return () => { + container?.removeEventListener("scroll", handleScroll); + }; + }, [containerRef, isPage]); + + useShallowEffect(() => { + if (notifLoadPage.get().category == 'division' && notifLoadPage.get().load == true) { + setRefresh(true) + } + }, [notifLoadPage.get().load]) + + function onRefresh() { + notifLoadPage.set({ + category: '', + load: false + }) + setRefresh(false) + setPage(1) + setTimeout(() => { + fetchData(false) + }, 500) + } return ( @@ -109,6 +130,14 @@ export default function ListDivision() { } /> + { + isRefresh && + { onRefresh() }} + title='UPDATE' + /> + + } {roleLogin.get() == 'supadmin' && Filter by: {nameGroup}} - - Total Divisi - - {jumlah} - - + + Total Divisi + + {jumlah} + + {isList ? ( @@ -153,66 +182,66 @@ export default function ListDivision() { .fill(null) .map((_, i) => ( - + )) : _.isEmpty(data) - ? - - Tidak ada Divisi - - : - data?.map((v: any, i: any) => { - return ( - - router.push(`/division/${v.id}`)}> - - -
- - - -
-
-
- - - - - {v.name} - + ? + + Tidak ada Divisi + + : + data?.map((v: any, i: any) => { + return ( + + router.push(`/division/${v.id}`)}> + + +
+ + + +
+
+
+ + + + + {v.name} + + -
-
-
- -
- ); - }) +
+
+ +
+ ); + }) }
) : ( diff --git a/src/module/division_new/ui/list_task.tsx b/src/module/division_new/ui/list_task.tsx index 892d35b..76b70ae 100644 --- a/src/module/division_new/ui/list_task.tsx +++ b/src/module/division_new/ui/list_task.tsx @@ -55,7 +55,7 @@ export default function ListTaskOnDetailDivision() { .map((_, i) => ( - + )) : @@ -72,8 +72,8 @@ export default function ListTaskOnDetailDivision() { router.push(`/task/${v.id}`)} bg={"white"} style={{ borderRadius: 10, border: `1px solid ${"#D6D8F6"}` }}> - {v.title+' - '+ v.projectTitle} + }} onClick={() => router.push(`/division/${param.id}/task/${v.idProject}`)} bg={"white"} style={{ borderRadius: 10, border: `1px solid ${"#D6D8F6"}` }}> + {v.title + ' - ' + v.projectTitle} diff --git a/src/module/document/ui/drawer_cut_documents.tsx b/src/module/document/ui/drawer_cut_documents.tsx index 7bd5ac4..16a70c0 100644 --- a/src/module/document/ui/drawer_cut_documents.tsx +++ b/src/module/document/ui/drawer_cut_documents.tsx @@ -1,47 +1,18 @@ -import { TEMA } from "@/module/_global"; -import { - Box, - Breadcrumbs, - Button, - Divider, - Flex, - Grid, - Group, - Modal, - ScrollArea, - Skeleton, - Text, - TextInput, -} from "@mantine/core"; -import React, { useState } from "react"; +import { keyWibu, TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; +import { Box, Breadcrumbs, Button, Divider, Flex, Grid, Group, Modal, ScrollArea, Skeleton, Text, TextInput, } from "@mantine/core"; +import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; +import { useParams } from "next/navigation"; +import { useState } from "react"; import toast from "react-hot-toast"; import { FcFolder } from "react-icons/fc"; -import { - funCreateFolder, - funGetAllDocument, - funMoveDocument, -} from "../lib/api_document"; -import { useParams } from "next/navigation"; -import { - IDataDocument, - IFormDetailMoreItem, - IJalurItem, -} from "../lib/type_document"; -import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; -import { MdFolder } from "react-icons/md"; -import router from "next/router"; import { GoChevronRight } from "react-icons/go"; -import { useHookstate } from "@hookstate/core"; +import { MdFolder } from "react-icons/md"; +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, onChoosePath, data, }: { category: string; data: IFormDetailMoreItem[]; onChoosePath: (val: string) => void; }) { const [opened, setOpened] = useState(false); const param = useParams<{ id: string }>(); const [path, setPath] = useState("home"); @@ -51,6 +22,10 @@ export default function DrawerCutDocuments({ const tema = useHookstate(TEMA); const [loading, setLoading] = useState(true); const isMobile = useMediaQuery("(max-width: 369px)"); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function onCreateFolder() { try { @@ -60,7 +35,11 @@ export default function DrawerCutDocuments({ idDivision: param.id, }); if (res.success) { - getOneData(); + getOneData(false); + setDataRealtime([{ + category: "division-document", + id: path, + }]) } else { toast.error(res.message); } @@ -72,9 +51,9 @@ export default function DrawerCutDocuments({ setOpened(false); } - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true); + setLoading(loading); const respon = await funGetAllDocument( "?division=" + param.id + "&path=" + path + "&category=folder" ); @@ -84,7 +63,6 @@ export default function DrawerCutDocuments({ } else { toast.error(respon.message); } - setLoading(false); } catch (error) { console.error(error); toast.error("Gagal mendapatkan item, coba lagi nanti"); @@ -94,7 +72,7 @@ export default function DrawerCutDocuments({ } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.id, path]); return ( @@ -133,7 +111,7 @@ export default function DrawerCutDocuments({
- + } diff --git a/src/module/document/ui/drawer_menu_document_division.tsx b/src/module/document/ui/drawer_menu_document_division.tsx index beb8563..3539432 100644 --- a/src/module/document/ui/drawer_menu_document_division.tsx +++ b/src/module/document/ui/drawer_menu_document_division.tsx @@ -1,31 +1,33 @@ "use clent" -import { LayoutDrawer, TEMA } from '@/module/_global'; -import { ActionIcon, Box, Button, Center, Divider, Flex, Grid, Modal, Progress, rem, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; -import { useParams, useRouter, useSearchParams } from 'next/navigation'; -import React, { useRef, useState } from 'react'; -import toast from 'react-hot-toast'; -import { FaFolderClosed, FaRegImage } from 'react-icons/fa6'; -import { HiDocumentText } from 'react-icons/hi2'; -import { IoAddCircle, IoDocumentText } from 'react-icons/io5'; -import { funCreateFolder, funUploadFileDocument } from '../lib/api_document'; +import { keyWibu, LayoutDrawer, TEMA } from '@/module/_global'; import { useHookstate } from '@hookstate/core'; -import { globalRefreshDocument } from '../lib/val_document'; +import { ActionIcon, Box, Button, Flex, Grid, Modal, Progress, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import _ from 'lodash'; +import { useParams, useSearchParams } from 'next/navigation'; +import { useRef, useState } from 'react'; +import toast from 'react-hot-toast'; +import { FaFolderClosed } from 'react-icons/fa6'; +import { HiDocumentText } from 'react-icons/hi2'; +import { IoAddCircle } from 'react-icons/io5'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funCreateFolder, funUploadFileDocument } from '../lib/api_document'; +import { globalRefreshDocument } from '../lib/val_document'; export default function DrawerMenuDocumentDivision() { const [openDrawerDocument, setOpenDrawerDocument] = useState(false) const [openModal, setOpenModal] = useState(false) - const router = useRouter() const param = useParams<{ id: string }>() const searchParams = useSearchParams() const path = searchParams.get('path') const refresh = useHookstate(globalRefreshDocument) const openRef = useRef<() => void>(null) - const [fileForm, setFileForm] = useState() const tema = useHookstate(TEMA) const [loading, setLoading] = useState(false) - + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) const [bodyFolder, setBodyFolder] = useState({ name: '', path: (path == undefined || path == '' || path == null) ? 'home' : path, @@ -37,6 +39,11 @@ export default function DrawerMenuDocumentDivision() { const res = await funCreateFolder(bodyFolder) if (!res.success) { toast.error(res.message) + } else { + setDataRealtime([{ + category: "division-document", + id: path, + }]) } } catch (error) { console.error(error); @@ -63,6 +70,11 @@ export default function DrawerMenuDocumentDivision() { if (!res.success) { toast.error(res.message) + } else { + setDataRealtime([{ + category: "division-document", + id: path, + }]) } setLoading(false) } catch (error) { diff --git a/src/module/document/ui/drawer_more.tsx b/src/module/document/ui/drawer_more.tsx index 6c42e71..56c6d3d 100644 --- a/src/module/document/ui/drawer_more.tsx +++ b/src/module/document/ui/drawer_more.tsx @@ -1,15 +1,16 @@ -import { LayoutDrawer, TEMA } from "@/module/_global"; -import { Box, Flex, Group, SimpleGrid, Stack, Text } from "@mantine/core"; -import React, { useState } from "react"; -import { LuFolders, LuFolderSymlink } from "react-icons/lu"; -import DrawerCutDocuments from "./drawer_cut_documents"; -import { IDataDocument, IFormDetailMoreItem } from "../lib/type_document"; -import toast from "react-hot-toast"; -import { funCopyDocument, funMoveDocument } from "../lib/api_document"; +import { keyWibu, LayoutDrawer, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; -import { globalRefreshDocument } from "../lib/val_document"; -import { useParams } from "next/navigation"; +import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { LuFolders, LuFolderSymlink } from "react-icons/lu"; +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) @@ -18,12 +19,20 @@ export default function DrawerMore({ data }: { data: IDataDocument[] }) { const param = useParams<{ id: string }>() const [forbidCopy, setForbidCopy] = useState(true) const tema = useHookstate(TEMA) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function onMoveItem(path: string) { try { const res = await funMoveDocument({ path, dataItem: data }) if (res.success) { + setDataRealtime([{ + category: "division-document", + id: path, + }]) toast.success(res.message) refresh.set(true) } else { @@ -41,6 +50,10 @@ export default function DrawerMore({ data }: { data: IDataDocument[] }) { try { const res = await funCopyDocument({ idDivision: param.id, path, dataItem: data }) if (res.success) { + setDataRealtime([{ + category: "division-document", + id: path, + }]) toast.success(res.message) refresh.set(true) } else { diff --git a/src/module/document/ui/drawer_share_document.tsx b/src/module/document/ui/drawer_share_document.tsx index 91c1311..77640ca 100644 --- a/src/module/document/ui/drawer_share_document.tsx +++ b/src/module/document/ui/drawer_share_document.tsx @@ -1,22 +1,7 @@ import { TEMA } from "@/module/_global"; -import { - funGetListDivisionByIdDivision, - IDataDivison, -} from "@/module/division_new"; +import { funGetListDivisionByIdDivision, IDataDivison, } from "@/module/division_new"; import { useHookstate } from "@hookstate/core"; -import { - ActionIcon, - Box, - Button, - Divider, - Flex, - Grid, - Group, - ScrollArea, - Skeleton, - Stack, - Text -} from "@mantine/core"; +import { ActionIcon, Box, Button, Divider, Flex, Grid, Group, ScrollArea, Skeleton, Stack, Text } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import { useParams } from "next/navigation"; import { useState } from "react"; @@ -26,11 +11,7 @@ import { funShareDocument } from "../lib/api_document"; import { IShareDivision } from "../lib/type_document"; import { globalRefreshDocument } from "../lib/val_document"; -export default function DrawerShareDocument({ - data, -}: { - data: IShareDivision[]; -}) { +export default function DrawerShareDocument({ data, }: { data: IShareDivision[]; }) { const [selectedFiles, setSelectedFiles] = useState([]); const [isData, setData] = useState([]); const param = useParams<{ id: string }>(); diff --git a/src/module/document/ui/navbar_document_division.tsx b/src/module/document/ui/navbar_document_division.tsx index 3db2a16..7fe8071 100644 --- a/src/module/document/ui/navbar_document_division.tsx +++ b/src/module/document/ui/navbar_document_division.tsx @@ -1,58 +1,30 @@ "use client"; -import { - LayoutDrawer, - LayoutModalViewFile, - LayoutNavbarNew, - TEMA, -} from "@/module/_global"; -import { - ActionIcon, - Anchor, - Box, - Breadcrumbs, - Button, - Checkbox, - Divider, - Flex, - Grid, - Group, - Indicator, - Menu, - Modal, - rem, - Select, - SimpleGrid, - Skeleton, - Text, - TextInput, -} from "@mantine/core"; -import React, { useState } from "react"; -import { HiMenu } from "react-icons/hi"; -import { FcDocument, FcFolder, FcImageFile } from "react-icons/fc"; -import { BsDownload, BsListCheck } from "react-icons/bs"; +import { keyWibu, LayoutDrawer, LayoutModalViewFile, LayoutNavbarNew, TEMA, } from "@/module/_global"; +import LayoutModal from "@/module/_global/layout/layout_modal"; +import { funGetDivisionById } from "@/module/division_new"; +import { useHookstate } from "@hookstate/core"; +import { ActionIcon, Box, Breadcrumbs, Button, Checkbox, Divider, Flex, Grid, Group, Indicator, Menu, Modal, rem, SimpleGrid, Skeleton, Text, TextInput } from "@mantine/core"; +import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; +import { useParams, useRouter, useSearchParams } from "next/navigation"; +import { useState } from "react"; +import toast from "react-hot-toast"; import { AiOutlineDelete } from "react-icons/ai"; +import { BsDownload, BsListCheck } from "react-icons/bs"; import { CgRename } from "react-icons/cg"; +import { FaShare } from "react-icons/fa6"; +import { FcDocument, FcFolder, FcImageFile } from "react-icons/fc"; +import { GoChevronRight } from "react-icons/go"; +import { HiMenu } from "react-icons/hi"; import { LuShare2 } from "react-icons/lu"; import { MdClose, MdOutlineMoreHoriz } from "react-icons/md"; -import LayoutModal from "@/module/_global/layout/layout_modal"; -import toast from "react-hot-toast"; -import { useParams, useRouter, useSearchParams } from "next/navigation"; +import { RiListCheck } from "react-icons/ri"; +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 { funGetDivisionById } from "@/module/division_new"; -import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; -import { - funDeleteDocument, - funGetAllDocument, - funRenameDocument, -} from "../lib/api_document"; -import { IDataDocument, IJalurItem } from "../lib/type_document"; -import { useHookstate } from "@hookstate/core"; -import { globalRefreshDocument } from "../lib/val_document"; -import { RiListCheck } from "react-icons/ri"; -import { GoChevronRight } from "react-icons/go"; import DrawerShareDocument from "./drawer_share_document"; -import { FaShare } from "react-icons/fa6"; +import { useWibuRealtime } from "wibu-realtime"; export default function NavbarDocumentDivision() { const router = useRouter(); @@ -80,6 +52,10 @@ export default function NavbarDocumentDivision() { const isMobile2 = useMediaQuery("(max-width: 496px)"); const tema = useHookstate(TEMA); const [loading, setLoading] = useState(true); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) const [bodyRename, setBodyRename] = useState({ id: "", name: "", @@ -166,7 +142,11 @@ export default function NavbarDocumentDivision() { try { const respon = await funDeleteDocument(selectedFiles); if (respon.success) { - getOneData(); + getOneData(false); + setDataRealtime([{ + category: "division-document", + id: path, + }]) } else { toast.error(respon.message); } @@ -185,7 +165,11 @@ export default function NavbarDocumentDivision() { try { const res = await funRenameDocument(bodyRename); if (res.success) { - getOneData(); + setDataRealtime([{ + category: "division-document", + id: path, + }]) + getOneData(false); } else { toast.error(res.message); } @@ -199,21 +183,25 @@ export default function NavbarDocumentDivision() { setRename(false); } - async function getOneData() { + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'division-document' && i.id == path)) { + getOneData(false) + } + }, [dataRealTime]) + + async function getOneData(loading: boolean) { try { - setLoading(true); + setLoading(loading); const respon = await funGetAllDocument( "?division=" + param.id + "&path=" + path ); if (respon.success) { setDataDocument(respon.data); setDataJalur(respon.jalur); - setLoading(false); } else { toast.error(respon.message); setDataDocument([]); setDataJalur([]); - setLoading(false); } const res = await funGetDivisionById(param.id); if (res.success) { @@ -242,7 +230,7 @@ export default function NavbarDocumentDivision() { }, [selectedFiles]); useShallowEffect(() => { - getOneData(); + getOneData(true); resetRefresh(); }, [param.id, path, refresh.get()]); diff --git a/src/module/home/index.ts b/src/module/home/index.ts index 1ff8487..ea9c7e8 100644 --- a/src/module/home/index.ts +++ b/src/module/home/index.ts @@ -1,3 +1,5 @@ +import { funGetHome } from "./lib/api_home"; +import { globalParamJumlahNotif } from "./lib/val_home"; import ViewDetailFeature from "./ui/view_detail_feature"; import ViewHome from "./ui/view_home"; import ViewNotification from "./ui/view_notification"; @@ -6,4 +8,6 @@ import ViewSearch from "./ui/view_search"; export { ViewHome } export { ViewDetailFeature } export { ViewSearch } -export { ViewNotification } \ No newline at end of file +export { ViewNotification } +export { funGetHome } +export { globalParamJumlahNotif } \ No newline at end of file diff --git a/src/module/home/lib/val_home.ts b/src/module/home/lib/val_home.ts new file mode 100644 index 0000000..8bb1e91 --- /dev/null +++ b/src/module/home/lib/val_home.ts @@ -0,0 +1,3 @@ +import { hookstate } from "@hookstate/core"; + +export const globalParamJumlahNotif = hookstate(false) diff --git a/src/module/home/ui/carosole.tsx b/src/module/home/ui/carosole.tsx index 5f8fdb5..91ee799 100644 --- a/src/module/home/ui/carosole.tsx +++ b/src/module/home/ui/carosole.tsx @@ -1,13 +1,46 @@ 'use client' -import React, { useRef } from 'react'; -import { Carousel } from '@mantine/carousel'; -import { TEMA, WARNA } from '@/module/_global'; -import Autoplay from 'embla-carousel-autoplay'; -import { Flex, Text } from '@mantine/core'; +import { TEMA } from '@/module/_global'; +import { funGetAllBanner, IDataBanner } from '@/module/banner'; import { useHookstate } from '@hookstate/core'; +import { Carousel } from '@mantine/carousel'; +import { Flex, Image, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import Autoplay from 'embla-carousel-autoplay'; +import { useRef, useState } from 'react'; +import toast from 'react-hot-toast'; +import { funGetHome } from '../lib/api_home'; export default function Carosole() { const autoplay = useRef(Autoplay({ delay: 5000 })); const tema = useHookstate(TEMA) + const [isDesa, setDesa] = useState("") + const [isData, setData] = useState([]) + + + const fetchData = async () => { + try { + const res_banner = await funGetAllBanner() + if (res_banner.success) { + setData(res_banner.data) + } else { + toast.error(res_banner.message); + } + const response = await funGetHome('?cat=header') + if (response.success) { + setDesa(response.data.village) + } else { + toast.error(response.message); + } + } catch (error) { + toast.error("Gagal mendapatkan data, coba lagi nanti"); + console.error(error); + } + }; + + + useShallowEffect(() => { + fetchData(); + }, []); + return ( <> - - - INFORMASI DARMASABA - - - - - INFORMASI DARMASABA - - - - - INFORMASI DARMASABA - - + { + isData.length > 0 ? + isData.map((item, index) => ( + + {item.title} + + )) + : + [...Array(3)].map((_, index) => ( + + + INFORMASI {isDesa.toUpperCase()} + + + )) + } ); diff --git a/src/module/home/ui/header_home.tsx b/src/module/home/ui/header_home.tsx index d0fadaa..3ccd63a 100644 --- a/src/module/home/ui/header_home.tsx +++ b/src/module/home/ui/header_home.tsx @@ -2,22 +2,26 @@ import { LayoutNavbarHome, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; import { ActionIcon, Box, Group, Indicator, Text } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; import { useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { HiMagnifyingGlass, HiOutlineBell, HiOutlineUser } from "react-icons/hi2"; import { funGetHome } from "../lib/api_home"; -import { useShallowEffect } from "@mantine/hooks"; +import { globalParamJumlahNotif } from "../lib/val_home"; export default function HeaderHome() { const router = useRouter() const tema = useHookstate(TEMA) const [isDesa, setDesa] = useState("") const [isNotif, setNotif] = useState(0) + const paramNotif = useHookstate(globalParamJumlahNotif) + const [loading, setLoading] = useState(true) const fetchData = async () => { try { + setLoading(true) const response = await funGetHome('?cat=header') if (response.success) { setDesa(response.data.village) @@ -28,6 +32,8 @@ export default function HeaderHome() { } catch (error) { toast.error("Gagal mendapatkan data, coba lagi nanti"); console.error(error); + } finally { + setLoading(false) } }; @@ -36,6 +42,12 @@ export default function HeaderHome() { fetchData(); }, []); + useShallowEffect(() => { + if (!loading) { + setNotif(isNotif + 1) + } + }, [paramNotif.get()]) + return ( diff --git a/src/module/home/ui/view_home.tsx b/src/module/home/ui/view_home.tsx index a91b619..3879290 100644 --- a/src/module/home/ui/view_home.tsx +++ b/src/module/home/ui/view_home.tsx @@ -18,14 +18,6 @@ export default function ViewHome() { return ( <> - {/* { - '' - } - } - title='UPDATE' - /> */} diff --git a/src/module/position/ui/drawer_detail_position.tsx b/src/module/position/ui/drawer_detail_position.tsx index 7ed430e..d81ad4e 100644 --- a/src/module/position/ui/drawer_detail_position.tsx +++ b/src/module/position/ui/drawer_detail_position.tsx @@ -1,4 +1,4 @@ -import { LayoutDrawer, TEMA, WARNA } from "@/module/_global" +import { keyWibu, LayoutDrawer, TEMA, WARNA } from "@/module/_global" import LayoutModal from "@/module/_global/layout/layout_modal" import { funGetAllGroup, IDataGroup } from "@/module/group" import { Box, Stack, SimpleGrid, Flex, Text, Select, TextInput, Button, Skeleton } from "@mantine/core" @@ -10,6 +10,7 @@ import { funEditPosition, funEditStatusPosition, funGetOnePosition } from "../li import { IDataPosition } from "../lib/type_position" import { useHookstate } from "@hookstate/core" import { globalRefreshPosition } from "../lib/val_posisition" +import { useWibuRealtime } from "wibu-realtime" export default function DrawerDetailPosition({ onUpdated, id, isActive }: { onUpdated: (val: boolean) => void, id: string, isActive: boolean; @@ -29,6 +30,10 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: { const [touched, setTouched] = useState({ name: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) function onCLose() { onUpdated(true) @@ -78,6 +83,10 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: { if (res.success) { toast.success(res.message); + setDataRealtime([{ + category: "data-position", + group: data.idGroup, + }]) refresh.set(!refresh.get()) onUpdated(true); onCLose(); @@ -122,6 +131,10 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: { const res = await funEditStatusPosition(id, { isActive: isActive }) if (res.success) { toast.success(res.message); + setDataRealtime([{ + category: "data-position", + group: data.idGroup, + }]) refresh.set(!refresh.get()) onUpdated(true); } else { diff --git a/src/module/position/ui/drawer_list_position.tsx b/src/module/position/ui/drawer_list_position.tsx index 83d8ed2..78a647b 100644 --- a/src/module/position/ui/drawer_list_position.tsx +++ b/src/module/position/ui/drawer_list_position.tsx @@ -1,4 +1,4 @@ -import { WARNA, LayoutDrawer, globalRole, TEMA } from "@/module/_global"; +import { WARNA, LayoutDrawer, globalRole, TEMA, keyWibu } from "@/module/_global"; import { funGetAllGroup, IDataGroup } from "@/module/group"; import { Box, Stack, SimpleGrid, Flex, TextInput, Button, Text, Select } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; @@ -10,6 +10,7 @@ import { RiFilter2Line } from "react-icons/ri"; import { funCreatePosition } from "../lib/api_position"; import { useHookstate } from "@hookstate/core"; import { globalRefreshPosition } from "../lib/val_posisition"; +import { useWibuRealtime } from "wibu-realtime"; export default function DrawerListPosition({ onCreated }: { onCreated: (val: boolean) => void }) { @@ -26,11 +27,14 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo name: false, idGroup: false }); - const [listData, setListData] = useState({ name: "", idGroup: "", }) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getAllGroup() { try { @@ -60,10 +64,14 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo }) if (res.success) { - setOpenDrawerGroup(false) toast.success(res.message) + setDataRealtime([{ + category: "data-position", + group: res.positions.idGroup, + }]) refresh.set(!refresh.get()) onCreated(true) + setOpenDrawerGroup(false) } else { toast.error(res.message) setOpenDrawerGroup(false) diff --git a/src/module/position/ui/list_position_active.tsx b/src/module/position/ui/list_position_active.tsx index ef7bd3e..c1c049a 100644 --- a/src/module/position/ui/list_position_active.tsx +++ b/src/module/position/ui/list_position_active.tsx @@ -1,17 +1,17 @@ -import { globalRole, LayoutDrawer, SkeletonSingle, TEMA, WARNA } from "@/module/_global"; +import { globalRole, keyWibu, LayoutDrawer, TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; import { ActionIcon, Box, Flex, Grid, Group, Skeleton, Text, TextInput } from "@mantine/core"; -import React, { useState } from "react"; -import { FaUserTie } from "react-icons/fa6"; -import { HiMagnifyingGlass } from "react-icons/hi2"; -import DrawerDetailPosition from "./drawer_detail_position"; -import toast from "react-hot-toast"; -import _ from "lodash"; import { useShallowEffect } from "@mantine/hooks"; import { useSearchParams } from "next/navigation"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { FaUserTie } from "react-icons/fa6"; +import { HiMagnifyingGlass } from "react-icons/hi2"; +import { useWibuRealtime } from "wibu-realtime"; import { funGetAllPosition } from "../lib/api_position"; import { IDataPosition } from "../lib/type_position"; -import { useHookstate } from "@hookstate/core"; import { globalRefreshPosition } from "../lib/val_posisition"; +import DrawerDetailPosition from "./drawer_detail_position"; export default function ListPositionActive() { @@ -29,10 +29,14 @@ export default function ListPositionActive() { const roleLogin = useHookstate(globalRole) const [nameGroup, setNameGroup] = useState('') const tema = useHookstate(TEMA) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) - async function getAllPosition() { + async function getAllPosition(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetAllPosition('?active=' + status + '&group=' + group + '&search=' + searchQuery) setDataPosition(res.data); setNameGroup(res.filter.name) @@ -46,9 +50,15 @@ export default function ListPositionActive() { } useShallowEffect(() => { - getAllPosition(); + getAllPosition(true); }, [status, group, searchQuery, refresh.get()]) + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'data-position' && i.group == group)) { + getAllPosition(false) + } + }, [dataRealTime]) + return ( diff --git a/src/module/project/ui/add_detail_task_project.tsx b/src/module/project/ui/add_detail_task_project.tsx index 5219b26..5d4caf4 100644 --- a/src/module/project/ui/add_detail_task_project.tsx +++ b/src/module/project/ui/add_detail_task_project.tsx @@ -1,14 +1,15 @@ "use client" -import { useParams, useRouter } from 'next/navigation'; -import React, { useState } from 'react'; -import toast from 'react-hot-toast'; -import { funCreateDetailProject } from '../lib/api_project'; -import { Box, Button, Group, Input, rem, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; -import { LayoutNavbarNew, TEMA, WARNA } from '@/module/_global'; -import { DatePicker } from '@mantine/dates'; -import moment from 'moment'; +import { keyWibu, LayoutNavbarNew, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; +import { Box, Button, Group, rem, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; +import { DatePicker } from '@mantine/dates'; +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 { funCreateDetailProject } from '../lib/api_project'; export default function AddDetailTaskProject() { const [value, setValue] = useState<[Date | null, Date | null]>([null, null]); @@ -20,6 +21,10 @@ export default function AddDetailTaskProject() { const [touched, setTouched] = useState({ name: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) function onVerification() { if (value[0] == null || value[1] == null) @@ -40,6 +45,10 @@ export default function AddDetailTaskProject() { }) if (res.success) { + setDataRealtime([{ + category: "project-detail-task", + id: param.id, + }]) toast.success(res.message) setOpenModal(false) router.push(`/project/${param.id}`) diff --git a/src/module/project/ui/add_file_detail_project.tsx b/src/module/project/ui/add_file_detail_project.tsx index c9d102f..befa6ea 100644 --- a/src/module/project/ui/add_file_detail_project.tsx +++ b/src/module/project/ui/add_file_detail_project.tsx @@ -1,28 +1,19 @@ "use client"; -import { LayoutDrawer, LayoutNavbarNew } from "@/module/_global"; -import { - Box, - Button, - Flex, - Group, - rem, - SimpleGrid, - Stack, - Text, -} from "@mantine/core"; -import React, { useRef, useState } from "react"; -import { useParams, useRouter } from "next/navigation"; -import toast from "react-hot-toast"; -import { IoIosArrowDropright } from "react-icons/io"; +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 { Dropzone } from "@mantine/dropzone"; import _ from "lodash"; -import ResultsFile from "./results_file"; +import { useParams, useRouter } from "next/navigation"; +import { useRef, useState } from "react"; +import toast from "react-hot-toast"; import { FaTrash } from "react-icons/fa6"; -import LayoutModal from "@/module/_global/layout/layout_modal"; -import { IListFileTaskProject } from "../lib/type_project"; +import { IoIosArrowDropright } from "react-icons/io"; import { funAddFileProject, funCekNamFileUploadProject } from "../lib/api_project"; -import { TEMA } from "@/module/_global"; -import { useHookstate } from "@hookstate/core"; +import { IListFileTaskProject } from "../lib/type_project"; +import ResultsFile from "./results_file"; +import { useWibuRealtime } from "wibu-realtime"; export default function AddFileDetailProject() { @@ -35,6 +26,10 @@ export default function AddFileDetailProject() { const [openDrawerFile, setOpenDrawerFile] = useState(false) const tema = useHookstate(TEMA) const openRef = useRef<() => void>(null) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) function deleteFile(index: number) { setListFile([...listFile.filter((val, i) => i !== index)]) @@ -70,6 +65,10 @@ export default function AddFileDetailProject() { const response = await funAddFileProject(param.id, fd) console.group(response) if (response.success) { + setDataRealtime([{ + category: "project-detail-file", + id: param.id, + }]) toast.success(response.message) setFileForm([]) setListFile([]) diff --git a/src/module/project/ui/add_member_detail_project.tsx b/src/module/project/ui/add_member_detail_project.tsx index 198b6b2..01f1079 100644 --- a/src/module/project/ui/add_member_detail_project.tsx +++ b/src/module/project/ui/add_member_detail_project.tsx @@ -1,18 +1,19 @@ "use client" -import { useParams, useRouter } from 'next/navigation'; -import React, { useState } from 'react'; -import { IDataMemberProject, IDataMemberProjectDetail } from '../lib/type_project'; -import toast from 'react-hot-toast'; -import { funAddMemberProject, funGetAllMemberById, funGetOneProjectById } from '../lib/api_project'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import { ActionIcon, Avatar, Box, Button, Center, Divider, Flex, Grid, Group, Indicator, rem, Skeleton, Stack, Text, TextInput } from '@mantine/core'; -import { LayoutNavbarNew, SkeletonList, SkeletonSingle, TEMA, WARNA } from '@/module/_global'; -import { FaCheck } from 'react-icons/fa6'; +import { keyWibu, LayoutNavbarNew, SkeletonList, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useHookstate } from '@hookstate/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 { 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 { useHookstate } from '@hookstate/core'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funAddMemberProject, funGetAllMemberById, funGetOneProjectById } from '../lib/api_project'; +import { IDataMemberProject, IDataMemberProjectDetail } from '../lib/type_project'; export default function AddMemberDetailProject() { const router = useRouter() @@ -27,6 +28,10 @@ export default function AddMemberDetailProject() { const [searchQuery, setSearchQuery] = useState('') const tema = useHookstate(TEMA) const isMobile2 = useMediaQuery("(max-width: 438px)"); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getData() { @@ -99,6 +104,10 @@ export default function AddMemberDetailProject() { try { const res = await funAddMemberProject(param.id, { member: selectedFiles }); if (res.success) { + setDataRealtime([{ + category: "project-detail-anggota", + id: param.id, + }]) toast.success(res.message) router.back() } else { diff --git a/src/module/project/ui/cancel_project.tsx b/src/module/project/ui/cancel_project.tsx index e400067..4928419 100644 --- a/src/module/project/ui/cancel_project.tsx +++ b/src/module/project/ui/cancel_project.tsx @@ -1,12 +1,13 @@ "use client" -import { useParams, useRouter } from 'next/navigation'; -import React, { useState } from 'react'; -import toast from 'react-hot-toast'; -import { funCancelProject } from '../lib/api_project'; -import { Box, Button, rem, Stack, Textarea } from '@mantine/core'; -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, rem, Stack, Textarea } from '@mantine/core'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funCancelProject } from '../lib/api_project'; export default function CancelProject() { const router = useRouter() @@ -17,6 +18,10 @@ export default function CancelProject() { const [touched, setTouched] = useState({ reason: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) function onVerification() { if (alasan == "") @@ -29,6 +34,10 @@ export default function CancelProject() { try { const res = await funCancelProject(param.id, { reason: alasan }) if (res.success) { + setDataRealtime([{ + category: "project-detail-status", + id: param.id, + }]) toast.success(res.message) router.push("/project") } else { diff --git a/src/module/project/ui/edit_task_project.tsx b/src/module/project/ui/edit_task_project.tsx index d747bd5..58503b1 100644 --- a/src/module/project/ui/edit_task_project.tsx +++ b/src/module/project/ui/edit_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, rem, Skeleton, Stack, TextInput } from '@mantine/core'; @@ -8,6 +8,7 @@ import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import toast from 'react-hot-toast'; import { funEditProject, funGetOneProjectById } from '../lib/api_project'; +import { useWibuRealtime } from 'wibu-realtime'; export default function EditTaskProject() { const router = useRouter() @@ -19,11 +20,19 @@ export default function EditTaskProject() { const [touched, setTouched] = useState({ name: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function onSubmit() { try { const res = await funEditProject(param.id, { name }) if (res.success) { + setDataRealtime([{ + category: "project-detail", + id: param.id, + }]) toast.success(res.message) router.push("./") } else { diff --git a/src/module/project/ui/list_anggota_detail_project.tsx b/src/module/project/ui/list_anggota_detail_project.tsx index ed459c4..9759377 100644 --- a/src/module/project/ui/list_anggota_detail_project.tsx +++ b/src/module/project/ui/list_anggota_detail_project.tsx @@ -1,16 +1,17 @@ 'use client' -import { globalRole, LayoutDrawer, SkeletonList, SkeletonSingle, TEMA } from '@/module/_global'; -import { Avatar, Box, Divider, Flex, Grid, Group, SimpleGrid, Stack, Text } from '@mantine/core'; -import React, { useState } from 'react'; -import { funDeleteMemberProject, funGetOneProjectById } from '../lib/api_project'; -import toast from 'react-hot-toast'; -import { useParams, useRouter } from 'next/navigation'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import { IDataMemberProject } from '../lib/type_project'; -import { FaUser } from 'react-icons/fa6'; -import { IoIosCloseCircle } from 'react-icons/io'; +import { globalRole, keyWibu, LayoutDrawer, SkeletonList, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; +import { Avatar, Box, Divider, Flex, Grid, Group, SimpleGrid, 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 { FaUser } from 'react-icons/fa6'; +import { IoIosCloseCircle } from 'react-icons/io'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funDeleteMemberProject, funGetOneProjectById } from '../lib/api_project'; +import { IDataMemberProject } from '../lib/type_project'; export default function ListAnggotaDetailProject() { @@ -25,6 +26,11 @@ export default function ListAnggotaDetailProject() { const tema = useHookstate(TEMA) const [reason, setReason] = useState("") const isMobile2 = useMediaQuery("(max-width: 438px)"); + const isMobile = useMediaQuery('(max-width: 369px)'); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneDataCancel() { try { @@ -45,9 +51,9 @@ export default function ListAnggotaDetailProject() { getOneDataCancel(); }, [param.id]) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetOneProjectById(param.id, 'member'); if (res.success) { setData(res.data) @@ -64,7 +70,7 @@ export default function ListAnggotaDetailProject() { } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.id]) @@ -72,9 +78,13 @@ export default function ListAnggotaDetailProject() { try { const res = await funDeleteMemberProject(param.id, { idUser: dataChoose.id }); if (res.success) { + setDataRealtime([{ + category: "project-detail-anggota", + id: param.id, + }]) toast.success(res.message) setDataChoose({ id: '', name: '' }) - getOneData() + getOneData(false) setOpenDrawer(false) } else { toast.error(res.message) @@ -84,7 +94,14 @@ export default function ListAnggotaDetailProject() { toast.error("Gagal menghapus anggota Kegiatan, coba lagi nanti"); } } - const isMobile = useMediaQuery('(max-width: 369px)'); + + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-anggota' && i.id == param.id)) { + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-status' && i.id == param.id)) { + getOneDataCancel() + } + }, [dataRealTime]) return ( @@ -128,11 +145,11 @@ export default function ListAnggotaDetailProject() { - {v.name} - {v.email} + {v.name} + {v.email} diff --git a/src/module/project/ui/list_file_detail_project.tsx b/src/module/project/ui/list_file_detail_project.tsx index 88ad231..cc07271 100644 --- a/src/module/project/ui/list_file_detail_project.tsx +++ b/src/module/project/ui/list_file_detail_project.tsx @@ -1,16 +1,17 @@ 'use client' -import { LayoutDrawer, LayoutModalViewFile, TEMA } from '@/module/_global'; -import { Box, Center, Flex, Grid, Group, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core'; -import React, { useState } from 'react'; -import toast from 'react-hot-toast'; -import { funDeleteFileProject, funGetOneProjectById } from '../lib/api_project'; -import { useParams } from 'next/navigation'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import { IDataFileProject } from '../lib/type_project'; -import { BsFileTextFill, BsFiletypeCsv, BsFiletypeHeic, BsFiletypeJpg, BsFiletypePdf, BsFiletypePng } from 'react-icons/bs'; +import { keyWibu, LayoutDrawer, LayoutModalViewFile, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; -import { FaTrash } from 'react-icons/fa6'; import { useHookstate } from '@hookstate/core'; +import { Box, Flex, Grid, Group, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { useParams } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { BsFileTextFill, BsFiletypeCsv, BsFiletypeHeic, BsFiletypeJpg, BsFiletypePdf, BsFiletypePng } from 'react-icons/bs'; +import { FaTrash } from 'react-icons/fa6'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funDeleteFileProject, funGetOneProjectById } from '../lib/api_project'; +import { IDataFileProject } from '../lib/type_project'; export default function ListFileDetailProject() { const [isData, setData] = useState([]) @@ -26,6 +27,10 @@ export default function ListFileDetailProject() { const tema = useHookstate(TEMA) const isMobile = useMediaQuery("(max-width: 350px)"); const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneDataCancel() { try { @@ -46,9 +51,9 @@ export default function ListFileDetailProject() { getOneDataCancel(); }, [param.id]) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetOneProjectById(param.id, 'file'); if (res.success) { setData(res.data) @@ -65,7 +70,7 @@ export default function ListFileDetailProject() { } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.id]) @@ -73,8 +78,12 @@ export default function ListFileDetailProject() { try { const res = await funDeleteFileProject(idData); if (res.success) { + setDataRealtime([{ + category: "project-detail-file", + id: param.id, + }]) toast.success(res.message) - getOneData() + getOneData(false) setIdData("") setIdStorage("") setOpenDrawer(false) @@ -88,6 +97,14 @@ export default function ListFileDetailProject() { } + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-file' && i.id == param.id)) { + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-status' && i.id == param.id)) { + getOneDataCancel() + } + }, [dataRealTime]) + return ( <> diff --git a/src/module/project/ui/list_project.tsx b/src/module/project/ui/list_project.tsx index adf23f3..3e6ae5c 100644 --- a/src/module/project/ui/list_project.tsx +++ b/src/module/project/ui/list_project.tsx @@ -1,17 +1,16 @@ "use client" -import { currentScroll, globalRole, SkeletonList, TEMA } from '@/module/_global'; +import { currentScroll, globalNotifPage, globalRole, ReloadButtonTop, SkeletonList, TEMA } from '@/module/_global'; +import { useHookstate } from '@hookstate/core'; import { ActionIcon, Avatar, Badge, Box, Card, Center, Divider, Flex, Grid, Group, Skeleton, Text, TextInput, Title } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import _ from 'lodash'; import { useRouter, useSearchParams } from 'next/navigation'; -import React, { useEffect, useState } from 'react'; +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 { RiCircleFill } from 'react-icons/ri'; import { funGetAllProject } from '../lib/api_project'; -import toast from 'react-hot-toast'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; import { IDataProject } from '../lib/type_project'; -import { useHookstate } from '@hookstate/core'; -import _ from 'lodash'; export default function ListProject() { const [isList, setIsList] = useState(false) @@ -30,6 +29,8 @@ export default function ListProject() { const [totalData, setTotalData] = useState(0) const isMobile = useMediaQuery('(max-width: 369px)'); const paddingLift = useMediaQuery('(max-width: 505px)') + const [isRefresh, setRefresh] = useState(false) + const notifLoadPage = useHookstate(globalNotifPage) const handleList = () => { setIsList(!isList) @@ -39,6 +40,9 @@ export default function ListProject() { try { if (loading) setLoading(true) + if (isPage == 1) { + setData([]) + } const response = await funGetAllProject('?status=' + status + '&search=' + searchQuery + '&group=' + group + '&page=' + isPage) if (response.success) { setNameGroup(response.filter.name) @@ -46,7 +50,7 @@ export default function ListProject() { if (isPage == 1) { setData(response.data) } else { - setData([...isData, ...response.data]) + setData((isData) => [...isData, ...response.data]) } } else { toast.error(response.message); @@ -92,8 +96,34 @@ export default function ListProject() { }; }, [containerRef, isPage]); + useShallowEffect(() => { + if (notifLoadPage.get().category == 'project' && notifLoadPage.get().load == true) { + setRefresh(true) + } + }, [notifLoadPage.get().load]) + + function onRefresh() { + notifLoadPage.set({ + category: '', + load: false + }) + setRefresh(false) + setPage(1) + setTimeout(() => { + fetchData(true) + }, 500) + } + return ( + { + isRefresh && + { onRefresh() }} + title='UPDATE' + /> + + } ( - + )) : @@ -147,59 +177,59 @@ export default function ListProject() { Tidak ada Kegiatan : - isData.map((v, i) => { - return ( - - router.push(`/project/${v.id}`)}> - - -
- - - -
-
-
- - - - - - {v.title} - - + isData.map((v, i) => { + return ( + + router.push(`/project/${v.id}`)}> + + +
+ + + +
+
+
+ + + + + + {v.title} + - - -
- -
- ); - }) - } +
+
+
+
+ +
+ ); + }) + }
) : ( diff --git a/src/module/project/ui/list_tugas_detail_project.tsx b/src/module/project/ui/list_tugas_detail_project.tsx index 5babd36..e32f21e 100644 --- a/src/module/project/ui/list_tugas_detail_project.tsx +++ b/src/module/project/ui/list_tugas_detail_project.tsx @@ -1,17 +1,18 @@ 'use client' -import { LayoutDrawer, SkeletonDetailListTugasTask, TEMA } from '@/module/_global'; +import { keyWibu, LayoutDrawer, SkeletonDetailListTugasTask, TEMA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useHookstate } from '@hookstate/core'; import { Box, Center, Checkbox, Divider, Flex, Grid, Group, SimpleGrid, Stack, Text } from '@mantine/core'; -import React, { useState } from 'react'; +import { useShallowEffect } from '@mantine/hooks'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; import toast from 'react-hot-toast'; import { AiOutlineFileDone, AiOutlineFileSync } from 'react-icons/ai'; -import { funDeleteDetailProject, funGetOneProjectById, funUpdateStatusProject } from '../lib/api_project'; -import { useParams, useRouter } from 'next/navigation'; -import { useShallowEffect } from '@mantine/hooks'; -import { IDataListTaskProject } from '../lib/type_project'; -import { useHookstate } from '@hookstate/core'; -import { globalRefreshProject, valStatusDetailProject } from '../lib/val_project'; import { FaCheck, FaPencil, FaTrash } from 'react-icons/fa6'; -import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funDeleteDetailProject, funGetOneProjectById, funUpdateStatusProject } from '../lib/api_project'; +import { IDataListTaskProject } from '../lib/type_project'; +import { globalRefreshProject, valStatusDetailProject } from '../lib/val_project'; export default function ListTugasDetailProject() { const [isData, setData] = useState([]) @@ -26,6 +27,10 @@ export default function ListTugasDetailProject() { const router = useRouter() const tema = useHookstate(TEMA) const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneDataCancel() { try { @@ -46,9 +51,9 @@ export default function ListTugasDetailProject() { getOneDataCancel(); }, [param.id]) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetOneProjectById(param.id, 'task'); if (res.success) { setData(res.data) @@ -65,15 +70,19 @@ export default function ListTugasDetailProject() { } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.id]) async function onDelete() { try { const res = await funDeleteDetailProject(idData, { idProject: param.id }); if (res.success) { + setDataRealtime([{ + category: "project-detail-task", + id: param.id, + }]) toast.success(res.message); - getOneData(); + getOneData(false); setIdData("") setOpenDrawer(false) refresh.set(true) @@ -90,8 +99,12 @@ export default function ListTugasDetailProject() { try { const res = await funUpdateStatusProject(idData, { status: val, idProject: param.id }); if (res.success) { + setDataRealtime([{ + category: "project-detail-task", + id: param.id, + }]) toast.success(res.message); - getOneData(); + getOneData(false); setIdData("") setOpenDrawer(false) setOpenDrawerStatus(false) @@ -105,6 +118,15 @@ export default function ListTugasDetailProject() { } } + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-task' && i.id == param.id)) { + refresh.set(true) + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-status' && i.id == param.id)) { + getOneDataCancel() + } + }, [dataRealTime]) + return ( <> @@ -124,21 +146,21 @@ export default function ListTugasDetailProject() { { loading ? <> - + : isData.length === 0 ? Tidak ada tugas : isData.map((item, index) => { return ( - { + { setIdData(item.id) setStatusData(item.status) reason == null ? setOpenDrawer(true) - : setOpenDrawer(false) + : setOpenDrawer(false) }} my={18}> - @@ -187,7 +209,7 @@ export default function ListTugasDetailProject() { - + ) }) @@ -203,7 +225,7 @@ export default function ListTugasDetailProject() { alignItems: 'flex-start', }} > - { setOpenDrawerStatus(true) }} justify={'center'} align={'center'} direction={'column'} pb={20}> + { setOpenDrawerStatus(true) }} justify={'center'} align={'center'} direction={'column'} pb={20}> @@ -245,36 +267,36 @@ export default function ListTugasDetailProject() { setOpenDrawerStatus(false)}> - { - valStatusDetailProject.map((item, index) => { - return ( - { onUpdateStatus(item.value) }}> - - - - {item.name} - - - - {statusData === item.value ? : ""} + { + valStatusDetailProject.map((item, index) => { + return ( + { onUpdateStatus(item.value) }}> + + + + {item.name} - - - - ) - }) - } + + + {statusData === item.value ? : ""} + + + + + ) + }) + } diff --git a/src/module/project/ui/navbar_detail_project.tsx b/src/module/project/ui/navbar_detail_project.tsx index 6683118..0568bf5 100644 --- a/src/module/project/ui/navbar_detail_project.tsx +++ b/src/module/project/ui/navbar_detail_project.tsx @@ -1,16 +1,17 @@ 'use client' -import { globalRole, LayoutDrawer, LayoutNavbarNew, TEMA } from '@/module/_global'; +import { globalRole, keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from '@/module/_global'; +import { useHookstate } from '@hookstate/core'; import { ActionIcon, Box, Flex, SimpleGrid, Stack, Text } from '@mantine/core'; +import { 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 { FaFileCirclePlus, FaPencil, FaUsers } from 'react-icons/fa6'; import { HiMenu } from 'react-icons/hi'; import { IoAddCircle } from 'react-icons/io5'; import { MdCancel } from 'react-icons/md'; import { funGetOneProjectById } from '../lib/api_project'; -import { useShallowEffect } from '@mantine/hooks'; -import { useHookstate } from '@hookstate/core'; +import { useWibuRealtime } from 'wibu-realtime'; export default function NavbarDetailProject() { const router = useRouter() @@ -20,6 +21,10 @@ export default function NavbarDetailProject() { const roleLogin = useHookstate(globalRole) const tema = useHookstate(TEMA) const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneData() { try { @@ -41,6 +46,12 @@ export default function NavbarDetailProject() { getOneData(); }, [param.id]) + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => (i.category == 'project-detail' || i.category == 'project-detail-status') && i.id == param.id)) { + getOneData() + } + }, [dataRealTime]) + return ( <> { - getOneData(); + getOneData(true); }, [param.id]) + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-status' && i.id == param.id)) { + getOneDataCancel() + getOneData(false) + } + }, [dataRealTime]) + return ( <> diff --git a/src/module/task/ui/add_detail_task.tsx b/src/module/task/ui/add_detail_task.tsx index 6c31db5..eea33f3 100644 --- a/src/module/task/ui/add_detail_task.tsx +++ b/src/module/task/ui/add_detail_task.tsx @@ -1,28 +1,15 @@ "use client"; -import { LayoutNavbarNew, TEMA } from "@/module/_global"; -import { - Avatar, - Box, - Button, - Flex, - Group, - Input, - rem, - SimpleGrid, - Stack, - Text, - TextInput, -} from "@mantine/core"; -import React, { useState } from "react"; -import { DatePicker } from "@mantine/dates"; -import { useParams, useRouter } from "next/navigation"; -import toast from "react-hot-toast"; -import { IFormDateTask } from "../lib/type_task"; -import moment from "moment"; -import { funCreateDetailTask } from "../lib/api_task"; +import { keyWibu, LayoutNavbarNew, TEMA } from "@/module/_global"; import LayoutModal from "@/module/_global/layout/layout_modal"; import { useHookstate } from "@hookstate/core"; - +import { Box, Button, Group, rem, SimpleGrid, Stack, Text, TextInput } from "@mantine/core"; +import { DatePicker } from "@mantine/dates"; +import moment from "moment"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { funCreateDetailTask } from "../lib/api_task"; +import { useWibuRealtime } from "wibu-realtime"; export default function AddDetailTask() { const [value, setValue] = useState<[Date | null, Date | null]>([null, null]); @@ -34,6 +21,11 @@ export default function AddDetailTask() { const [touched, setTouched] = useState({ title: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + function onVerification() { if (value[0] == null || value[1] == null) @@ -55,6 +47,10 @@ export default function AddDetailTask() { }) if (res.success) { + setDataRealtime([{ + category: "tugas-detail-task", + id: param.detail, + }]) toast.success(res.message) setOpenModal(false) router.push(`/division/${param.id}/task/${param.detail}`) @@ -139,22 +135,22 @@ export default function AddDetailTask() { />
- - - + + + setOpenModal(false)} description="Apakah Anda yakin ingin menambahkan tugas?" diff --git a/src/module/task/ui/add_file_detail_task.tsx b/src/module/task/ui/add_file_detail_task.tsx index 389baa3..72d09cb 100644 --- a/src/module/task/ui/add_file_detail_task.tsx +++ b/src/module/task/ui/add_file_detail_task.tsx @@ -1,27 +1,19 @@ "use client"; -import { LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; -import { - Box, - Button, - Flex, - Group, - rem, - SimpleGrid, - Stack, - Text, -} from "@mantine/core"; -import React, { useRef, useState } from "react"; -import { useParams, useRouter } from "next/navigation"; -import toast from "react-hot-toast"; -import { IoIosArrowDropright } from "react-icons/io"; -import { Dropzone } from "@mantine/dropzone"; -import _ from "lodash"; -import { IListFileTask } from "../lib/type_task"; -import ResultsFile from "./results_file"; -import { FaTrash } from "react-icons/fa6"; -import { funAddFileTask, funCekNamFileUploadTask } from "../lib/api_task"; +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 { Dropzone } from "@mantine/dropzone"; +import _ from "lodash"; +import { useParams, useRouter } from "next/navigation"; +import { useRef, useState } from "react"; +import toast from "react-hot-toast"; +import { FaTrash } from "react-icons/fa6"; +import { IoIosArrowDropright } from "react-icons/io"; +import { funAddFileTask, funCekNamFileUploadTask } from "../lib/api_task"; +import { IListFileTask } from "../lib/type_task"; +import ResultsFile from "./results_file"; +import { useWibuRealtime } from "wibu-realtime"; export default function AddFileDetailTask() { @@ -34,6 +26,10 @@ export default function AddFileDetailTask() { const [openDrawerFile, setOpenDrawerFile] = useState(false) const tema = useHookstate(TEMA) const openRef = useRef<() => void>(null) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) function deleteFile(index: number) { setListFile([...listFile.filter((val, i) => i !== index)]) @@ -68,6 +64,10 @@ export default function AddFileDetailTask() { const response = await funAddFileTask(param.detail, fd) if (response.success) { + setDataRealtime([{ + category: "tugas-detail-file", + id: param.detail, + }]) toast.success(response.message) setFileForm([]) setListFile([]) diff --git a/src/module/task/ui/add_member_detail_task.tsx b/src/module/task/ui/add_member_detail_task.tsx index 2b812c5..a19b9f1 100644 --- a/src/module/task/ui/add_member_detail_task.tsx +++ b/src/module/task/ui/add_member_detail_task.tsx @@ -1,36 +1,20 @@ "use client" -import { LayoutNavbarNew, SkeletonList, SkeletonSingle, TEMA } from "@/module/_global"; -import { funGetDivisionById, funGetSearchMemberDivision, IDataMemberDivision } from "@/module/division_new"; -import { - ActionIcon, - Anchor, - Avatar, - Box, - Button, - Center, - Checkbox, - Divider, - Flex, - Grid, - Group, - Indicator, - rem, - Stack, - Text, - TextInput, -} from "@mantine/core"; +import { keyWibu, LayoutNavbarNew, SkeletonList, TEMA } from "@/module/_global"; +import LayoutModal from "@/module/_global/layout/layout_modal"; +import { funGetSearchMemberDivision, IDataMemberDivision } from "@/module/division_new"; +import { useHookstate } from "@hookstate/core"; +import { Carousel } from "@mantine/carousel"; +import { ActionIcon, Avatar, Box, Button, Center, Divider, Flex, Grid, Group, Indicator, rem, 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 { FaCheck } from "react-icons/fa6"; -import { funAddMemberTask, funGetTaskDivisionById } from "../lib/api_task"; -import { IDataMemberTaskDivision } from "../lib/type_task"; -import LayoutModal from "@/module/_global/layout/layout_modal"; import { HiMagnifyingGlass } from "react-icons/hi2"; import { IoArrowBackOutline, IoClose } from "react-icons/io5"; -import { Carousel } from "@mantine/carousel"; -import { useHookstate } from "@hookstate/core"; +import { funAddMemberTask, funGetTaskDivisionById } from "../lib/api_task"; +import { IDataMemberTaskDivision } from "../lib/type_task"; +import { useWibuRealtime } from "wibu-realtime"; export default function AddMemberDetailTask() { const router = useRouter() @@ -45,6 +29,10 @@ export default function AddMemberDetailTask() { const [searchQuery, setSearchQuery] = useState('') const tema = useHookstate(TEMA) const isMobile2 = useMediaQuery("(max-width: 438px)"); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getData() { @@ -120,6 +108,10 @@ export default function AddMemberDetailTask() { try { const res = await funAddMemberTask(param.detail, { idDivision: param.id, member: selectedFiles }); if (res.success) { + setDataRealtime([{ + category: "tugas-detail-anggota", + id: param.detail, + }]) toast.success(res.message) router.back() } else { diff --git a/src/module/task/ui/cancel_task.tsx b/src/module/task/ui/cancel_task.tsx index e3af172..aa67902 100644 --- a/src/module/task/ui/cancel_task.tsx +++ b/src/module/task/ui/cancel_task.tsx @@ -1,18 +1,13 @@ "use client"; -import { LayoutNavbarNew, TEMA } from "@/module/_global"; -import { - Box, - Button, - rem, - Stack, - Textarea, -} from "@mantine/core"; -import React, { useState } from "react"; -import { useParams, useRouter } from "next/navigation"; -import toast from "react-hot-toast"; -import { funCancelTask } from "../lib/api_task"; +import { keyWibu, LayoutNavbarNew, TEMA } from "@/module/_global"; import LayoutModal from "@/module/_global/layout/layout_modal"; import { useHookstate } from "@hookstate/core"; +import { Box, Button, rem, Stack, Textarea, } from "@mantine/core"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { funCancelTask } from "../lib/api_task"; +import { useWibuRealtime } from "wibu-realtime"; export default function CancelTask() { @@ -24,6 +19,10 @@ export default function CancelTask() { const [touched, setTouched] = useState({ reason: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) function onVerification() { if (alasan == "") @@ -36,6 +35,10 @@ export default function CancelTask() { try { const res = await funCancelTask(param.detail, { reason: alasan }) if (res.success) { + setDataRealtime([{ + category: "tugas-detail-status", + id: param.detail, + }]) toast.success(res.message) router.push("./") } else { diff --git a/src/module/task/ui/detail_list_anggota_task.tsx b/src/module/task/ui/detail_list_anggota_task.tsx index 2751d75..b461880 100644 --- a/src/module/task/ui/detail_list_anggota_task.tsx +++ b/src/module/task/ui/detail_list_anggota_task.tsx @@ -1,17 +1,18 @@ 'use client' -import { globalRole, LayoutDrawer, SkeletonList, SkeletonSingle, TEMA } from "@/module/_global"; -import { Box, Group, Flex, Avatar, Text, SimpleGrid, Stack, Grid, Divider } from "@mantine/core"; +import { globalRole, keyWibu, LayoutDrawer, SkeletonList, TEMA } from "@/module/_global"; +import LayoutModal from "@/module/_global/layout/layout_modal"; +import { globalIsAdminDivision } from "@/module/division_new"; +import { useHookstate } from "@hookstate/core"; +import { Avatar, Box, Divider, Flex, Grid, Group, SimpleGrid, 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 { funDeleteMemberTask, funGetTaskDivisionById } from "../lib/api_task"; -import { IDataMemberTaskDivision } from "../lib/type_task"; import { FaUser } from "react-icons/fa6"; import { IoIosCloseCircle } from "react-icons/io"; -import LayoutModal from "@/module/_global/layout/layout_modal"; -import { useHookstate } from "@hookstate/core"; -import { globalIsAdminDivision } from "@/module/division_new"; +import { funDeleteMemberTask, funGetTaskDivisionById } from "../lib/api_task"; +import { IDataMemberTaskDivision } from "../lib/type_task"; +import { useWibuRealtime } from "wibu-realtime"; export default function ListAnggotaDetailTask() { @@ -28,6 +29,10 @@ export default function ListAnggotaDetailTask() { const isMobile2 = useMediaQuery("(max-width: 438px)"); const tema = useHookstate(TEMA) const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneDataCancel() { try { @@ -48,16 +53,15 @@ export default function ListAnggotaDetailTask() { getOneDataCancel(); }, [param.detail]) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetTaskDivisionById(param.detail, 'member'); if (res.success) { setData(res.data) } else { toast.error(res.message); } - } catch (error) { console.error(error); toast.error("Gagal mendapatkan member tugas divisi, coba lagi nanti"); @@ -67,17 +71,30 @@ export default function ListAnggotaDetailTask() { } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.detail]) + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-anggota' && i.id == param.detail)) { + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-status' && i.id == param.detail)) { + getOneDataCancel() + } + }, [dataRealTime]) + + async function onSubmit() { try { const res = await funDeleteMemberTask(param.detail, { idUser: dataChoose.id }); if (res.success) { + setDataRealtime([{ + category: "tugas-detail-anggota", + id: param.detail, + }]) toast.success(res.message) setDataChoose({ id: '', name: '' }) - getOneData() + getOneData(false) setOpenDrawer(false) } else { toast.error(res.message) diff --git a/src/module/task/ui/detail_list_file_task.tsx b/src/module/task/ui/detail_list_file_task.tsx index fb441dd..2bc5299 100644 --- a/src/module/task/ui/detail_list_file_task.tsx +++ b/src/module/task/ui/detail_list_file_task.tsx @@ -1,16 +1,17 @@ 'use client' -import { LayoutDrawer, LayoutModalViewFile, SkeletonDetailListTugasTask, TEMA } from "@/module/_global"; +import { keyWibu, LayoutDrawer, LayoutModalViewFile, TEMA } from "@/module/_global"; +import LayoutModal from "@/module/_global/layout/layout_modal"; +import { useHookstate } from "@hookstate/core"; import { Box, Center, Flex, Grid, Group, SimpleGrid, Skeleton, Stack, Text } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { useParams } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { BsFileTextFill, BsFiletypeCsv, BsFiletypeHeic, BsFiletypeJpg, BsFiletypePdf, BsFiletypePng } from "react-icons/bs"; +import { FaTrash } from "react-icons/fa6"; import { funDeleteFileTask, funGetTaskDivisionById } from "../lib/api_task"; import { IDataFileTaskDivision } from "../lib/type_task"; -import { FaTrash } from "react-icons/fa6"; -import LayoutModal from "@/module/_global/layout/layout_modal"; -import { useHookstate } from "@hookstate/core"; +import { useWibuRealtime } from "wibu-realtime"; export default function ListFileDetailTask() { const [isData, setData] = useState([]) @@ -26,6 +27,10 @@ export default function ListFileDetailTask() { const [isExtension, setExtension] = useState('') const tema = useHookstate(TEMA) const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneDataCancel() { try { @@ -46,9 +51,9 @@ export default function ListFileDetailTask() { getOneDataCancel(); }, [param.detail]) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetTaskDivisionById(param.detail, 'file'); if (res.success) { setData(res.data) @@ -64,7 +69,7 @@ export default function ListFileDetailTask() { } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.detail]) @@ -72,8 +77,12 @@ export default function ListFileDetailTask() { try { const res = await funDeleteFileTask(idData); if (res.success) { + setDataRealtime([{ + category: "tugas-detail-file", + id: param.detail, + }]) toast.success(res.message) - getOneData() + getOneData(false) setIdData("") setIdDataStorage("") setOpenDrawer(false) @@ -84,9 +93,16 @@ export default function ListFileDetailTask() { console.error(error); toast.error("Gagal menghapus file, coba lagi nanti"); } - } + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-file' && i.id == param.detail)) { + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-status' && i.id == param.detail)) { + getOneDataCancel() + } + }, [dataRealTime]) + return ( File diff --git a/src/module/task/ui/detail_list_tugas_task.tsx b/src/module/task/ui/detail_list_tugas_task.tsx index 8d159ad..ddca576 100644 --- a/src/module/task/ui/detail_list_tugas_task.tsx +++ b/src/module/task/ui/detail_list_tugas_task.tsx @@ -1,18 +1,19 @@ 'use client' -import { LayoutDrawer, SkeletonDetailListTugasTask, TEMA } from "@/module/_global" -import { Box, Grid, Center, Checkbox, Group, SimpleGrid, Text, Stack, Flex, Divider } from "@mantine/core" +import { keyWibu, LayoutDrawer, SkeletonDetailListTugasTask, TEMA } from "@/module/_global" +import LayoutModal from "@/module/_global/layout/layout_modal" +import { useHookstate } from "@hookstate/core" +import { Box, Center, Checkbox, Divider, Flex, Grid, Group, SimpleGrid, Stack, Text } from "@mantine/core" import { useShallowEffect } from "@mantine/hooks" +import "moment/locale/id" import { useParams, useRouter } from "next/navigation" +import { useState } from "react" import toast from "react-hot-toast" import { AiOutlineFileDone, AiOutlineFileSync } from "react-icons/ai" -import { funDeleteDetailTask, funGetTaskDivisionById, funUpdateStatusDetailTask } from "../lib/api_task" -import { useState } from "react" -import { IDataListTaskDivision } from "../lib/type_task" import { FaCheck, FaPencil, FaTrash } from "react-icons/fa6" -import LayoutModal from "@/module/_global/layout/layout_modal" +import { useWibuRealtime } from "wibu-realtime" +import { funDeleteDetailTask, funGetTaskDivisionById, funUpdateStatusDetailTask } from "../lib/api_task" +import { IDataListTaskDivision } from "../lib/type_task" import { globalRefreshTask, valStatusDetailTask } from "../lib/val_task" -import { useHookstate } from "@hookstate/core" -import "moment/locale/id" export default function ListTugasDetailTask() { const [openDrawer, setOpenDrawer] = useState(false) @@ -27,6 +28,10 @@ export default function ListTugasDetailTask() { const refresh = useHookstate(globalRefreshTask) const tema = useHookstate(TEMA) const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneDataCancel() { try { @@ -47,9 +52,9 @@ export default function ListTugasDetailTask() { getOneDataCancel(); }, [param.detail]) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetTaskDivisionById(param.detail, 'task'); if (res.success) { setData(res.data) @@ -66,7 +71,7 @@ export default function ListTugasDetailTask() { } useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.detail]) @@ -74,9 +79,13 @@ export default function ListTugasDetailTask() { try { const res = await funDeleteDetailTask(idData, { idProject: param.detail }); if (res.success) { + setDataRealtime([{ + category: "tugas-detail-task", + id: param.detail, + }]) toast.success(res.message); refresh.set(true) - getOneData(); + getOneData(false); setIdData("") setOpenDrawer(false) } else { @@ -93,9 +102,13 @@ export default function ListTugasDetailTask() { try { const res = await funUpdateStatusDetailTask(idData, { status: val, idProject: param.detail }); if (res.success) { + setDataRealtime([{ + category: "tugas-detail-task", + id: param.detail, + }]) toast.success(res.message); refresh.set(true) - getOneData(); + getOneData(false); setIdData("") setOpenDrawerStatus(false) setOpenDrawer(false) @@ -108,6 +121,15 @@ export default function ListTugasDetailTask() { } } + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-task' && i.id == param.detail)) { + refresh.set(true) + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-status' && i.id == param.detail)) { + getOneDataCancel() + } + }, [dataRealTime]) + return ( @@ -118,7 +140,7 @@ export default function ListTugasDetailTask() { style={{ borderRadius: 10, border: `1px solid ${"#D6D8F6"}`, - padding:20 + padding: 20 }} > { diff --git a/src/module/task/ui/detail_progress_task.tsx b/src/module/task/ui/detail_progress_task.tsx index 5b7216e..0609774 100644 --- a/src/module/task/ui/detail_progress_task.tsx +++ b/src/module/task/ui/detail_progress_task.tsx @@ -1,15 +1,16 @@ 'use client' -import { TEMA } from "@/module/_global"; -import { Box, Grid, ActionIcon, Progress, Text, Skeleton, Group } from "@mantine/core"; +import { keyWibu, TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; +import { ActionIcon, Box, Grid, Group, Progress, Skeleton, Text } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; import { useParams } from "next/navigation"; +import { useState } from "react"; import toast from "react-hot-toast"; import { HiMiniPresentationChartBar } from "react-icons/hi2"; -import { funGetTaskDivisionById } from "../lib/api_task"; -import { useState } from "react"; -import { globalRefreshTask } from "../lib/val_task"; -import { useHookstate } from "@hookstate/core"; import { IoIosWarning } from "react-icons/io"; +import { useWibuRealtime } from "wibu-realtime"; +import { funGetTaskDivisionById } from "../lib/api_task"; +import { globalRefreshTask } from "../lib/val_task"; export default function ProgressDetailTask() { const [valProgress, setValProgress] = useState(0) @@ -20,6 +21,10 @@ export default function ProgressDetailTask() { const isMobile = useMediaQuery('(max-width: 369px)'); const tema = useHookstate(TEMA) const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function getOneDataCancel() { try { @@ -41,9 +46,9 @@ export default function ProgressDetailTask() { }, [param.detail]) - async function getOneData() { + async function getOneData(loading: boolean) { try { - setLoading(true) + setLoading(loading) const res = await funGetTaskDivisionById(param.detail, 'progress'); if (res.success) { setValProgress(res.data.progress); @@ -51,7 +56,6 @@ export default function ProgressDetailTask() { } else { toast.error(res.message); } - setLoading(false) } catch (error) { console.error(error); toast.error("Gagal mendapatkan progress tugas divisi, coba lagi nanti"); @@ -62,7 +66,7 @@ export default function ProgressDetailTask() { function onRefresh() { if (refresh.get()) { - getOneData() + getOneData(false) refresh.set(false) } } @@ -73,9 +77,16 @@ export default function ProgressDetailTask() { }, [refresh.get()]) useShallowEffect(() => { - getOneData(); + getOneData(true); }, [param.detail]) + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-status' && i.id == param.detail)) { + getOneDataCancel() + getOneData(false) + } + }, [dataRealTime]) + return ( {loading ? "" : diff --git a/src/module/task/ui/edit_task.tsx b/src/module/task/ui/edit_task.tsx index 6aae983..fe56537 100644 --- a/src/module/task/ui/edit_task.tsx +++ b/src/module/task/ui/edit_task.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, rem, Skeleton, Stack, TextInput } from "@mantine/core"; @@ -8,6 +8,7 @@ import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { funEditTask, funGetTaskDivisionById } from "../lib/api_task"; +import { useWibuRealtime } from "wibu-realtime"; export default function EditTask() { @@ -20,6 +21,10 @@ export default function EditTask() { const [touched, setTouched] = useState({ title: false, }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) function onVerification() { if (Object.values(touched).some((v) => v == true)) @@ -42,6 +47,10 @@ export default function EditTask() { try { const res = await funEditTask(param.detail, { title }) if (res.success) { + setDataRealtime([{ + category: "tugas-detail", + id: param.detail, + }]) toast.success(res.message) router.push("./") } else { @@ -97,7 +106,7 @@ export default function EditTask() { label="Judul Tugas" size="md" value={title} - onChange={(e) => { onValidation('title', e.target.value)}} + onChange={(e) => { onValidation('title', e.target.value) }} error={ touched.title && ( title == "" ? "Error! harus memasukkan judul tugas" : null diff --git a/src/module/task/ui/list_division_task.tsx b/src/module/task/ui/list_division_task.tsx index 4653f22..1ec13f5 100644 --- a/src/module/task/ui/list_division_task.tsx +++ b/src/module/task/ui/list_division_task.tsx @@ -1,4 +1,4 @@ -import { currentScroll, SkeletonList, TEMA } from "@/module/_global"; +import { currentScroll, globalNotifPage, ReloadButtonTop, SkeletonList, TEMA } from "@/module/_global"; 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"; @@ -25,6 +25,8 @@ export default function ListDivisionTask() { const { value: containerRef } = useHookstate(currentScroll) const [isPage, setPage] = useState(1) const [totalData, setTotalData] = useState(0) + const [isRefresh, setRefresh] = useState(false) + const notifLoadPage = useHookstate(globalNotifPage) const handleList = () => { setIsList(!isList) @@ -32,15 +34,14 @@ export default function ListDivisionTask() { const fetchData = async (loading: boolean) => { try { - if (loading) - setLoading(true) + setLoading(loading) const response = await funGetAllTask('?division=' + param.id + '&status=' + status + '&search=' + searchQuery + '&page=' + isPage) if (response.success) { setTotalData(response.total) if (isPage == 1) { setData(response?.data) } else { - setData([...isData, ...response.data]) + setData((isData) => [...isData, ...response.data]) } } else { toast.error(response.message); @@ -87,8 +88,36 @@ export default function ListDivisionTask() { }; }, [containerRef, isPage]); + + useShallowEffect(() => { + if (notifLoadPage.get().category == 'division/' + param.id + '/task' && notifLoadPage.get().load == true) { + setRefresh(true) + } + }, [notifLoadPage.get().load]) + + function onRefresh() { + notifLoadPage.set({ + category: '', + load: false + }) + setRefresh(false) + setPage(1) + setTimeout(() => { + fetchData(false) + }, 500) + } + + return ( + { + isRefresh && + { onRefresh() }} + title='UPDATE' + /> + + } { + if (dataRealTime && dataRealTime.some((i: any) => (i.category == 'tugas-detail' || i.category == 'tugas-detail-status') && i.id == param.detail)) { + getOneData() + } + }, [dataRealTime]) + return ( <> () const openRef = useRef<() => void>(null) const tema = useHookstate(TEMA) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) const [touched, setTouched] = useState({ nik: false, name: false, @@ -118,6 +123,10 @@ export default function CreateMember() { )) const res = await funCreateMember(fd); if (res.success) { + setDataRealtime([{ + category: "data-member", + group: res.data.idGroup, + }]) toast.success(res.message); router.push("/member?active=true"); } else { diff --git a/src/module/user/member/ui/drawer_detail_member.tsx b/src/module/user/member/ui/drawer_detail_member.tsx index b8b4988..bb537fd 100644 --- a/src/module/user/member/ui/drawer_detail_member.tsx +++ b/src/module/user/member/ui/drawer_detail_member.tsx @@ -1,28 +1,23 @@ "use client"; -import { TEMA, WARNA } from "@/module/_global"; +import { keyWibu, TEMA } from "@/module/_global"; import LayoutModal from "@/module/_global/layout/layout_modal"; +import { useHookstate } from "@hookstate/core"; import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; -import { useShallowEffect } from "@mantine/hooks"; import { useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; import { FaPencil, FaToggleOff } from "react-icons/fa6"; -import { ImUserCheck } from "react-icons/im"; +import { useWibuRealtime } from "wibu-realtime"; import { funEditStatusMember } from "../lib/api_member"; -import { useHookstate } from "@hookstate/core"; -export default function DrawerDetailMember({ - onDeleted, - id, - status, -}: { - onDeleted: (val: boolean) => void; - id: string; - status: boolean; -}) { +export default function DrawerDetailMember({ onDeleted, id, status, }: { onDeleted: (val: boolean) => void; id: string; status: boolean; }) { const router = useRouter(); const [isModal, setModal] = useState(false); const tema = useHookstate(TEMA) + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) async function nonActive(val: boolean) { try { @@ -31,6 +26,10 @@ export default function DrawerDetailMember({ isActive: status ? true : false, }); if (res.success) { + setDataRealtime([{ + category: "data-member", + group: res.data.idGroup, + }]) toast.success(res.message); router.push("/member?active=true"); onDeleted(true); diff --git a/src/module/user/member/ui/tab_list_member.tsx b/src/module/user/member/ui/tab_list_member.tsx index e370035..a6b0c47 100644 --- a/src/module/user/member/ui/tab_list_member.tsx +++ b/src/module/user/member/ui/tab_list_member.tsx @@ -1,14 +1,15 @@ -import { currentScroll, globalRole, SkeletonSingle, SkeletonUser, TEMA } from "@/module/_global" -import { Box, Text, TextInput, Divider, Avatar, Grid, Group, ActionIcon, Skeleton } from "@mantine/core" +import { currentScroll, globalRole, keyWibu, ReloadButtonTop, SkeletonUser, TEMA } from "@/module/_global" +import { useHookstate } from "@hookstate/core" +import { Avatar, Box, Divider, Grid, Text, TextInput } from "@mantine/core" import { useMediaQuery, useShallowEffect } from "@mantine/hooks" +import _ from "lodash" import { useRouter, useSearchParams } from "next/navigation" import { useEffect, useState } from "react" -import { HiMagnifyingGlass } from "react-icons/hi2" -import { IListMember } from "../lib/type_member" -import { funGetAllmember } from "../lib/api_member" import toast from "react-hot-toast" -import _ from "lodash" -import { useHookstate } from "@hookstate/core" +import { HiMagnifyingGlass } from "react-icons/hi2" +import { useWibuRealtime } from "wibu-realtime" +import { funGetAllmember } from "../lib/api_member" +import { IListMember } from "../lib/type_member" export default function TabListMember() { @@ -21,10 +22,16 @@ export default function TabListMember() { const status = searchParams.get('active') const roleLogin = useHookstate(globalRole) const [nameGroup, setNameGroup] = useState('') + const [idGroup, setIdGroup] = useState('') const tema = useHookstate(TEMA) const { value: containerRef } = useHookstate(currentScroll); const [isPage, setPage] = useState(1) const isMobile2 = useMediaQuery("(max-width: 438px)"); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + const [isRefresh, setRefresh] = useState(false) async function getAllUser(loading: boolean) { @@ -34,10 +41,11 @@ export default function TabListMember() { const res = await funGetAllmember('?active=' + status + '&group=' + group + '&search=' + searchQuery + '&page=' + isPage) if (res.success) { setNameGroup(res.filter.name) + setIdGroup(res.filter.id) if (isPage == 1) { setDataMember(res.data) } else { - setDataMember([...dataMember, ...res.data]) + setDataMember((dataMember) => [...dataMember, ...res.data]) } } else { @@ -81,9 +89,30 @@ export default function TabListMember() { }; }, [containerRef, isPage]); + + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'data-member' && i.group == idGroup)) { + setRefresh(true) + } + }, [dataRealTime]) + + function onRefresh() { + setRefresh(false) + setPage(1) + getAllUser(true) + } + return ( <> + { + isRefresh && + { onRefresh() }} + title='UPDATE' + /> + + }