From aa612d24bc7cb729db66841bd3a48379cf875f70 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 11 Nov 2024 17:02:42 +0800 Subject: [PATCH] Fix: Job Deskripsi: - Fix notifikasi admi to user - Fix count notifikasi di admin dan user ## No Issue --- package.json | 1 + src/app/dev/admin/layout.tsx | 11 +- src/app/layout.tsx | 2 + src/app/lib/global_state.ts | 23 ++ src/app/lib/index.ts | 2 + src/app/lib/realtime_provider.tsx | 49 +++ src/app/zCoba/test1/page.tsx | 55 +++ src/app/zCoba/test2/page.tsx | 48 +++ src/app/zz-makuro/page.tsx | 19 +- .../_component/comp_button_user_on_navbar.tsx | 75 ++++ .../_admin_global/_ui/ui_navbar_admin.tsx | 203 ++++++++++ src/app_modules/admin/_admin_global/index.ts | 5 +- .../admin/job/child/review/index.tsx | 52 +-- src/app_modules/admin/layout.tsx | 4 +- src/app_modules/admin/new_layout.tsx | 351 ++++-------------- src/app_modules/admin/new_list_page.tsx | 12 +- .../fun/create/fun_create_notif_user.ts | 3 +- .../fun/get/fun_check_status_job.ts | 20 + .../fun/get/get_notifikasi_by_user_id.ts | 1 + .../admin/notifikasi/route_setting/job.ts | 39 +- .../route_setting/type_of_select_page.ts | 36 ++ .../admin/notifikasi/ui_drawer_notifikasi.tsx | 259 +++++++------ .../home/component/button_header.tsx | 69 ++-- src/app_modules/home/view_home.tsx | 3 + .../button/comp_button_save_create.tsx | 43 +-- src/app_modules/job/create/view.tsx | 1 - src/app_modules/job/detail/arsip/view.tsx | 4 +- src/app_modules/job/detail/publish/view.tsx | 6 +- .../job/fun/create/fun_create_with_file.ts | 82 +--- src/app_modules/job/global_state/index.ts | 2 - src/app_modules/job/splash/ui_splash.tsx | 7 +- .../notifikasi/component/card_view.tsx | 157 ++++---- .../notifikasi/component/path/job.ts | 51 +-- .../fun/check/fun_check_job_status.ts | 25 ++ .../fun/create/create_notif_to_admin.ts | 5 +- .../fun/get/get_notifiaksi_by_id.ts | 2 +- .../notifikasi/ui/ui_notifiaksi.tsx | 50 +-- src/app_modules/zCoba/coba_realtime.tsx | 32 +- src/app_modules/zCoba/ui_coba_upload_file.tsx | 9 +- src/bin/seeder/user_seeder.json | 2 +- src/util/mqtt_loader.tsx | 2 +- yarn.lock | 57 +++ 42 files changed, 1114 insertions(+), 765 deletions(-) create mode 100644 src/app/lib/global_state.ts create mode 100644 src/app/lib/realtime_provider.tsx create mode 100644 src/app/zCoba/test1/page.tsx create mode 100644 src/app/zCoba/test2/page.tsx create mode 100644 src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx create mode 100644 src/app_modules/admin/_admin_global/_ui/ui_navbar_admin.tsx create mode 100644 src/app_modules/admin/notifikasi/fun/get/fun_check_status_job.ts create mode 100644 src/app_modules/admin/notifikasi/route_setting/type_of_select_page.ts create mode 100644 src/app_modules/notifikasi/fun/check/fun_check_job_status.ts diff --git a/package.json b/package.json index ef27deb7..dcaedb90 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "utf-8-validate": "^6.0.3", "uuid": "^9.0.1", "wibu": "bipproduction/wibu", + "wibu-pkg": "^1.0.3", "yaml": "^2.3.2" } } diff --git a/src/app/dev/admin/layout.tsx b/src/app/dev/admin/layout.tsx index 40feb1d4..bc8fd499 100644 --- a/src/app/dev/admin/layout.tsx +++ b/src/app/dev/admin/layout.tsx @@ -13,9 +13,10 @@ export default async function Layout({ const userLoginId = await funGetUserIdByToken(); const dataUser = await funGlobal_getUserById({ userId: userLoginId }); - const listNotif = await adminNotifikasi_getByUserId(); + const listNotifikasi = await adminNotifikasi_getByUserId(); const countNotifikasi = await adminNotifikasi_countNotifikasi(); + return ( <> {/* {children} */} - {children} + + {children} + ); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3da2327d..5b55d385 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,6 +4,7 @@ import "./globals.css"; import { TokenProvider } from "./lib/token"; import dotenv from "dotenv"; import { ServerEnv } from "./lib/server_env"; +import { RealtimeProvider } from "./lib"; dotenv.config({ path: ".env", }); @@ -45,6 +46,7 @@ export default function RootLayout({ + {children} ); diff --git a/src/app/lib/global_state.ts b/src/app/lib/global_state.ts new file mode 100644 index 00000000..63963eb7 --- /dev/null +++ b/src/app/lib/global_state.ts @@ -0,0 +1,23 @@ +import { atom } from "jotai"; + +export type IRealtimeData = { + status?: "Publish" | "Review" | "Draft" | "Reject"; + appId: string; + userId: string; + pesan: string; + title: string; + kategoriApp: + | "JOB" + | "VOTING" + | "EVENT" + | "DONASI" + | "INVESTASI" + | "COLLABORATION" + | "FORUM"; + userRole?: "USER" | "ADMIN"; +}; + +export const gs_realtimeData = atom(null); +export const gs_admin_ntf = atom(0); +export const gs_user_ntf = atom(0); + diff --git a/src/app/lib/index.ts b/src/app/lib/index.ts index 4d002a66..976acb98 100644 --- a/src/app/lib/index.ts +++ b/src/app/lib/index.ts @@ -4,11 +4,13 @@ import prisma from "./prisma"; import { pathAssetImage } from "./path_asset_image"; import { RouterImagePreview } from "./router_hipmi/router_image_preview"; import { RouterAdminGlobal } from "./router_admin/router_admin_global"; +import RealtimeProvider from "./realtime_provider"; export { DIRECTORY_ID }; export { prisma }; export { APIs }; export { pathAssetImage }; +export { RealtimeProvider }; // Router export { RouterImagePreview }; diff --git a/src/app/lib/realtime_provider.tsx b/src/app/lib/realtime_provider.tsx new file mode 100644 index 00000000..361c4a43 --- /dev/null +++ b/src/app/lib/realtime_provider.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { useShallowEffect } from "@mantine/hooks"; +import { useAtom } from "jotai"; +import { WibuRealtime } from "wibu-pkg"; +import { + gs_admin_ntf, + gs_realtimeData, + gs_user_ntf, + IRealtimeData, +} from "./global_state"; + +export type TypeNotification = { + type: "message" | "notification" + pushNotificationTo: "ADMIN" | "USER"; + dataMessage?: IRealtimeData; + userLoginId?: string; +}; + +const WIBU_REALTIME_TOKEN: any = process.env.NEXT_PUBLIC_WIBU_REALTIME_TOKEN; +export default function RealtimeProvider() { + const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData); + const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf); + const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); + + useShallowEffect(() => { + WibuRealtime.init({ + onData(data: TypeNotification) { + if (data.type == "notification" && data.pushNotificationTo == "ADMIN") { + setNewAdminNtf((e) => e + 1); + } + + if (data.type == "notification" && data.pushNotificationTo == "USER") { + setNewUserNtf((e) => e + 1); + setDataRealtime(data.dataMessage as any); + } + + if (data.type == "message") { + // console.log(data.dataMessage); + setDataRealtime(data.dataMessage as any); + } + }, + project: "hipmi", + WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN, + }); + }, []); + + return null; +} diff --git a/src/app/zCoba/test1/page.tsx b/src/app/zCoba/test1/page.tsx new file mode 100644 index 00000000..4439d229 --- /dev/null +++ b/src/app/zCoba/test1/page.tsx @@ -0,0 +1,55 @@ +"use client"; +import { + gs_admin_ntf, + gs_realtimeData, + IRealtimeData, +} from "@/app/lib/global_state"; +import { Button, Stack } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { useAtom } from "jotai"; +import { WibuRealtime } from "wibu-pkg"; +import { v4 } from "uuid"; +import { useState } from "react"; +const angka = 10; +export default function Page() { + const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData); + const [adminNtf, setAdminNtf] = useAtom(gs_admin_ntf); + const [notif, setNotif] = useState(angka); + + useShallowEffect(() => { + if (adminNtf) { + setNotif((e) => e + 1); + } + }, [adminNtf]); + + async function onSend() { + const newData: IRealtimeData = { + appId: v4(), + status: "Publish", + userId: "user1", + pesan: "apa kabar", + title: "coba", + kategoriApp: "INVESTASI", + userRole: "ADMIN", + }; + + WibuRealtime.setData({ + type: "message", + pushNotificationTo: "USER", + dataMessage: newData, + }); + } + + return ( + + {notif} + + + ); +} diff --git a/src/app/zCoba/test2/page.tsx b/src/app/zCoba/test2/page.tsx new file mode 100644 index 00000000..e717b3f3 --- /dev/null +++ b/src/app/zCoba/test2/page.tsx @@ -0,0 +1,48 @@ +"use client"; +import { gs_realtimeData, IRealtimeData } from "@/app/lib/global_state"; +import { Button, Stack } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { useAtom } from "jotai"; +import { WibuRealtime } from "wibu-pkg"; +import { v4 } from "uuid"; + +export default function Page() { + const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData); + + useShallowEffect(() => { + console.log( + dataRealtime?.userId == "user2" + ? console.log("") + : console.log(dataRealtime) + ); + }, [dataRealtime]); + + async function onSend() { + const newData: IRealtimeData = { + appId: v4(), + status: "Publish", + userId: "user2", + pesan: "apa kabar", + title: "coba", + kategoriApp: "INVESTASI", + userRole: "USER", + }; + + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "ADMIN", + }); + } + + return ( + + + + ); +} diff --git a/src/app/zz-makuro/page.tsx b/src/app/zz-makuro/page.tsx index a984807d..665c3a1f 100644 --- a/src/app/zz-makuro/page.tsx +++ b/src/app/zz-makuro/page.tsx @@ -1,13 +1,14 @@ "use client"; -import { useShallowEffect } from "@mantine/hooks"; -import { useState } from "react"; +import { useHookstate } from "@hookstate/core"; +import { Button, Stack } from "@mantine/core"; + export default function Page() { - const [origin, setOrigin] = useState(""); - useShallowEffect(() => { - if (typeof window !== "undefined") { - setOrigin(window.location.origin); - } - }, []); - return
{origin}
; + + return ( + + + + + ); } diff --git a/src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx b/src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx new file mode 100644 index 00000000..c9240494 --- /dev/null +++ b/src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx @@ -0,0 +1,75 @@ +import { AccentColor } from "@/app_modules/_global/color"; +import { MODEL_USER } from "@/app_modules/home/model/interface"; +import { Menu, ActionIcon, Stack, Grid, Center, Text } from "@mantine/core"; +import { IconUserCircle, IconUser, IconPhone } from "@tabler/icons-react"; +import { useState } from "react"; +import Admin_Logout from "../logout"; + +export function Admin_ComponentButtonUserCircle({ dataUser }: { dataUser: MODEL_USER }) { + const [isOpenMenuUser, setOpenMenuUser] = useState(false); + return ( + <> + + + console.log("test")}> + + + + + + + + + + + + + {dataUser.username} + + + + + + + + + + +{dataUser.nomor} + + + + + +
+ +
+
+
+
+ + ); +} \ No newline at end of file diff --git a/src/app_modules/admin/_admin_global/_ui/ui_navbar_admin.tsx b/src/app_modules/admin/_admin_global/_ui/ui_navbar_admin.tsx new file mode 100644 index 00000000..d71b65f8 --- /dev/null +++ b/src/app_modules/admin/_admin_global/_ui/ui_navbar_admin.tsx @@ -0,0 +1,203 @@ +import { MainColor } from "@/app_modules/_global/color"; +import { Box, NavLink, Text } from "@mantine/core"; +import { IconCircleDot, IconCircleDotFilled } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import _ from "lodash"; +import { useRouter } from "next/navigation"; +import { newListAdminPage } from "../../new_list_page"; +import { gs_admin_navbar_isActive_dropdown } from "../new_global_state"; + +export default function Admin_UiNavbar({ + userRoleId, + activeId, + activeChildId, + setActiveId, + setActiveChildId, +}: { + userRoleId: string; + activeId: string; + activeChildId: string; + setActiveId: (val: any) => void; + setActiveChildId: (val: any) => void; +}) { + const router = useRouter(); + // global state + const [openDropdown, setOpenDropdown] = useAtom( + gs_admin_navbar_isActive_dropdown + ); + + // const [activeId, setActiveId] = useAtom(gs_admin_navbar_menu); + // const [activeChildId, setActiveChildId] = useAtom(gs_admin_navbar_subMenu); + + // Kalau fix developer navbar, fix juga navbar admin, dan berlaku sebaliknya + const developerNavbar = newListAdminPage.map((parent) => ( + + {parent.name}} + icon={parent.icon} + onClick={() => { + setActiveId(parent.id); + setActiveChildId(""); + + parent.path == "" ? setActiveChildId(parent.child[0].id) : ""; + parent.path == "" + ? router.push(parent.child[0].path) + : router.push(parent.path); + + openDropdown && activeId === parent.id + ? setOpenDropdown(false) + : setOpenDropdown(true); + }} + // active={activeId === parent.id} + > + {/* Navlink Children */} + {!_.isEmpty(parent.child) && + parent.child.map((child) => ( + + {child.name}} + icon={ + activeChildId === child.id ? ( + + ) : ( + + ) + } + onClick={() => { + setActiveChildId(child.id); + router.push(child.path); + }} + active={activeId === child.id} + /> + + ))} + + + )); + + const bukanDeveloper = newListAdminPage.slice(0, -1); + const adminNavbar = bukanDeveloper.map((parent) => ( + + {parent.name}} + icon={parent.icon} + onClick={() => { + setActiveId(parent.id); + setActiveChildId(""); + + parent.path == "" ? setActiveChildId(parent.child[0].id) : ""; + parent.path == "" + ? router.push(parent.child[0].path) + : router.push(parent.path); + + openDropdown && activeId === parent.id + ? setOpenDropdown(false) + : setOpenDropdown(true); + }} + // active={activeId === parent.id} + > + {/* Navlink Children */} + {!_.isEmpty(parent.child) && + parent.child.map((child) => ( + + {child.name}} + icon={ + activeChildId === child.id ? ( + + ) : ( + + ) + } + onClick={() => { + setActiveChildId(child.id); + router.push(child.path); + }} + active={activeId === child.id} + /> + + ))} + + + )); + + return userRoleId == "2" ? adminNavbar : developerNavbar; +} diff --git a/src/app_modules/admin/_admin_global/index.ts b/src/app_modules/admin/_admin_global/index.ts index 022101bc..0a0b68d1 100644 --- a/src/app_modules/admin/_admin_global/index.ts +++ b/src/app_modules/admin/_admin_global/index.ts @@ -1,5 +1,8 @@ import { Admin_ComponentLoadImageLandscape } from "./_component/comp_admin_load_image"; import { Admin_ComponentSkeletonNavbar } from "./_component/comp_admin_skeleton_navbar"; +import { Admin_ComponentButtonUserCircle } from "./_component/comp_button_user_on_navbar"; +import Admin_UiNavbar from "./_ui/ui_navbar_admin"; export { Admin_ComponentLoadImageLandscape, Admin_ComponentSkeletonNavbar }; - +export { Admin_UiNavbar }; +export {Admin_ComponentButtonUserCircle} \ No newline at end of file diff --git a/src/app_modules/admin/job/child/review/index.tsx b/src/app_modules/admin/job/child/review/index.tsx index 7bc27680..1a897b0c 100644 --- a/src/app_modules/admin/job/child/review/index.tsx +++ b/src/app_modules/admin/job/child/review/index.tsx @@ -36,6 +36,8 @@ import { useState } from "react"; import { AdminJob_funEditCatatanById } from "../../fun/edit/fun_edit_catatan_by_id"; import { AdminJob_funEditStatusPublishById } from "../../fun/edit/fun_edit_status_publish_by_id"; import adminJob_getListReview from "../../fun/get/get_list_review"; +import { IRealtimeData } from "@/app/lib/global_state"; +import { WibuRealtime } from "wibu-pkg"; export default function AdminJob_TableReview({ dataReview, @@ -334,7 +336,7 @@ async function onPublish({ const loadData = await adminJob_getListReview({ page: 1 }); onLoadData(loadData); - const dataNotif = { + const dataNotifikasi: IRealtimeData = { appId: publish.data?.id as any, status: publish.data?.MasterStatus?.name as any, userId: publish.data?.authorId as any, @@ -343,20 +345,16 @@ async function onPublish({ title: "Job publish", }; - const notif = await adminNotifikasi_funCreateToUser({ - data: dataNotif as any, + const createNotifikasi = await adminNotifikasi_funCreateToUser({ + data: dataNotifikasi as any, }); - if (notif.status === 201) { - mqtt_client.publish( - "USER", - JSON.stringify({ userId: publish?.data?.authorId, count: 1 }) - ); - - mqtt_client.publish( - "Job_new_post", - JSON.stringify({ isNewPost: true, count: 1 }) - ); + if (createNotifikasi.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); } ComponentGlobal_NotifikasiBerhasil(publish.message); @@ -382,24 +380,34 @@ async function onReject({ ComponentGlobal_NotifikasiBerhasil(reject.message); - const dataNotif = { + // const dataNotif = { + // appId: reject.data?.id as any, + // status: reject.data?.MasterStatus?.name as any, + // userId: reject.data?.authorId as any, + // pesan: reject.data?.title as any, + // kategoriApp: "JOB", + // title: "Job anda ditolak !", + // }; + + const dataNotifikasi: IRealtimeData = { appId: reject.data?.id as any, status: reject.data?.MasterStatus?.name as any, userId: reject.data?.authorId as any, pesan: reject.data?.title as any, kategoriApp: "JOB", - title: "Job anda ditolak !", + title: "Job reject", }; - const notif = await adminNotifikasi_funCreateToUser({ - data: dataNotif as any, + const createRejectNotifikasi = await adminNotifikasi_funCreateToUser({ + data: dataNotifikasi as any, }); - if (notif.status === 201) { - mqtt_client.publish( - "USER", - JSON.stringify({ userId: dataNotif.userId, count: 1 }) - ); + if (createRejectNotifikasi.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); } } else { ComponentGlobal_NotifikasiGagal(reject.message); diff --git a/src/app_modules/admin/layout.tsx b/src/app_modules/admin/layout.tsx index 20a1fbbc..b94357f0 100644 --- a/src/app_modules/admin/layout.tsx +++ b/src/app_modules/admin/layout.tsx @@ -326,7 +326,7 @@ export default function AdminLayout({ position="right" size={"xs"} > - { setDataNotif(val); @@ -339,7 +339,7 @@ export default function AdminLayout({ onLoadCountNotif={(val: any) => { setCountNotif(val); }} - /> + /> */} ); diff --git a/src/app_modules/admin/new_layout.tsx b/src/app_modules/admin/new_layout.tsx index 443839b1..d4fbddce 100644 --- a/src/app_modules/admin/new_layout.tsx +++ b/src/app_modules/admin/new_layout.tsx @@ -1,62 +1,74 @@ "use client"; +import { gs_admin_ntf } from "@/app/lib/global_state"; import { ActionIcon, AppShell, - Box, - Center, Divider, Drawer, Grid, Group, - Menu, + Indicator, Navbar, - NavLink, ScrollArea, Stack, Text, Title, } from "@mantine/core"; -import { useMediaQuery } from "@mantine/hooks"; -import { - IconBell, - IconCircleDot, - IconCircleDotFilled, - IconPhone, - IconUser, - IconUserCircle, -} from "@tabler/icons-react"; +import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; +import { IconBell } from "@tabler/icons-react"; import { useAtom } from "jotai"; -import _ from "lodash"; -import { useRouter } from "next/navigation"; import { useState } from "react"; -import { AccentColor, MainColor } from "../_global/color"; +import { AccentColor } from "../_global/color"; import { MODEL_USER } from "../home/model/interface"; -import Admin_Logout from "./_admin_global/logout"; +import { MODEL_NOTIFIKASI } from "../notifikasi/model/interface"; +import { + Admin_ComponentButtonUserCircle, + Admin_ComponentSkeletonNavbar, + Admin_UiNavbar, +} from "./_admin_global"; import { - gs_admin_navbar_isActive_dropdown, gs_admin_navbar_menu, gs_admin_navbar_subMenu, } from "./_admin_global/new_global_state"; -import { newListAdminPage } from "./new_list_page"; +import adminNotifikasi_getByUserId from "./notifikasi/fun/get/get_notifikasi_by_user_id"; import { ComponentAdmin_UIDrawerNotifikasi } from "./notifikasi/ui_drawer_notifikasi"; -import { Admin_ComponentSkeletonNavbar } from "./_admin_global"; export function Admin_NewLayout({ children, user, + countNotifikasi, + listNotifikasi, }: { children: React.ReactNode; user: MODEL_USER; + countNotifikasi: number; + listNotifikasi: MODEL_NOTIFIKASI[]; }) { const matches = useMediaQuery("(min-width: 1024px)"); const [dataUser, setDataUser] = useState(user); const userRoleId = dataUser.masterUserRoleId; const [opened, setOpened] = useState(false); + const [activeId, setActiveId] = useAtom(gs_admin_navbar_menu); + const [activeChildId, setActiveChildId] = useAtom(gs_admin_navbar_subMenu); + const [dataNotifikasi, setDataNotifikasi] = + useState(listNotifikasi); // Notifikasi const [isDrawerNotifikasi, setDrawerNotifikasi] = useState(false); - const [isNavbarOpen, setIsNavbarOpen] = useState(false); + const [countNtf, setCountNtf] = useState(countNotifikasi); + const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf); + + useShallowEffect(() => { + setCountNtf((e) => e + newAdminNtf), setNewAdminNtf(0); + }, [newAdminNtf, setNewAdminNtf]); + + async function onLoadListNotifikasi() { + const loadNotifikasi = await adminNotifikasi_getByUserId(); + + setDataNotifikasi(loadNotifikasi as []); + setDrawerNotifikasi(true); + } return ( <> @@ -65,7 +77,7 @@ export function Admin_NewLayout({ navbarOffsetBreakpoint={1024} navbar={