Merge pull request #123 from bipproduction/notifikasi/job

Notifikasi/job
This commit is contained in:
Bagasbanuna02
2024-11-11 17:04:42 +08:00
committed by GitHub
50 changed files with 1679 additions and 1234 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "hipmi", "name": "hipmi",
"version": "1.0.12", "version": "1.1.0",
"private": true, "private": true,
"prisma": { "prisma": {
"seed": "npx tsx prisma/seed.ts" "seed": "npx tsx prisma/seed.ts"
@@ -88,7 +88,7 @@
"utf-8-validate": "^6.0.3", "utf-8-validate": "^6.0.3",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"wibu": "bipproduction/wibu", "wibu": "bipproduction/wibu",
"wibu-realtime": "bipproduction/wibu-realtime", "wibu-pkg": "^1.0.3",
"yaml": "^2.3.2" "yaml": "^2.3.2"
} }
} }

View File

@@ -13,9 +13,10 @@ export default async function Layout({
const userLoginId = await funGetUserIdByToken(); const userLoginId = await funGetUserIdByToken();
const dataUser = await funGlobal_getUserById({ userId: userLoginId }); const dataUser = await funGlobal_getUserById({ userId: userLoginId });
const listNotif = await adminNotifikasi_getByUserId(); const listNotifikasi = await adminNotifikasi_getByUserId();
const countNotifikasi = await adminNotifikasi_countNotifikasi(); const countNotifikasi = await adminNotifikasi_countNotifikasi();
return ( return (
<> <>
{/* <AdminLayout {/* <AdminLayout
@@ -25,7 +26,13 @@ export default async function Layout({
> >
{children} {children}
</AdminLayout> */} </AdminLayout> */}
<Admin_NewLayout user={dataUser as any}>{children}</Admin_NewLayout> <Admin_NewLayout
user={dataUser as any}
countNotifikasi={countNotifikasi as any}
listNotifikasi={listNotifikasi as []}
>
{children}
</Admin_NewLayout>
</> </>
); );
} }

View File

@@ -6,7 +6,7 @@ import { RouterAuth } from "@/app/lib/router_hipmi/router_auth";
export default async function Page({ params }: { params: { id: string } }) { export default async function Page({ params }: { params: { id: string } }) {
let kodeId = params.id; let kodeId = params.id;
const dataOtp = await auth_getCodeOtpByNumber({ kodeId: kodeId }); const dataOtp = await auth_getCodeOtpByNumber({ kodeId: kodeId });
if (dataOtp === null) return redirect(RouterAuth.login); // if (dataOtp === null) return redirect(RouterAuth.login);
return <Validasi dataOtp={dataOtp} />; return <Validasi dataOtp={dataOtp as any} />;
} }

View File

@@ -4,6 +4,7 @@ import "./globals.css";
import { TokenProvider } from "./lib/token"; import { TokenProvider } from "./lib/token";
import dotenv from "dotenv"; import dotenv from "dotenv";
import { ServerEnv } from "./lib/server_env"; import { ServerEnv } from "./lib/server_env";
import { RealtimeProvider } from "./lib";
dotenv.config({ dotenv.config({
path: ".env", path: ".env",
}); });
@@ -45,6 +46,7 @@ export default function RootLayout({
<RootStyleRegistry> <RootStyleRegistry>
<MqttLoader /> <MqttLoader />
<TokenProvider token={token} envObject={envObject} /> <TokenProvider token={token} envObject={envObject} />
<RealtimeProvider />
{children} {children}
</RootStyleRegistry> </RootStyleRegistry>
); );

View File

@@ -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<IRealtimeData | null>(null);
export const gs_admin_ntf = atom<number>(0);
export const gs_user_ntf = atom<number>(0);

View File

@@ -4,11 +4,13 @@ import prisma from "./prisma";
import { pathAssetImage } from "./path_asset_image"; import { pathAssetImage } from "./path_asset_image";
import { RouterImagePreview } from "./router_hipmi/router_image_preview"; import { RouterImagePreview } from "./router_hipmi/router_image_preview";
import { RouterAdminGlobal } from "./router_admin/router_admin_global"; import { RouterAdminGlobal } from "./router_admin/router_admin_global";
import RealtimeProvider from "./realtime_provider";
export { DIRECTORY_ID }; export { DIRECTORY_ID };
export { prisma }; export { prisma };
export { APIs }; export { APIs };
export { pathAssetImage }; export { pathAssetImage };
export { RealtimeProvider };
// Router // Router
export { RouterImagePreview }; export { RouterImagePreview };

View File

@@ -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;
}

View File

@@ -1,16 +1,17 @@
import ComponentGlobal_HeaderTamplate from "@/app_modules/_global/header_tamplate"; import { funGetUserIdByToken } from "@/app_modules/_global/fun/get";
import UIGlobal_LayoutTamplate from "@/app_modules/_global/ui/ui_layout_tamplate"; import { CobaRealtime } from "@/app_modules/zCoba/coba_realtime";
import Coba_TestLoading from "@/app_modules/zCoba";
import { Text } from "@mantine/core";
export default async function Page() { export default async function Page() {
await new Promise((a, b) => { await new Promise((a, b) => {
setTimeout(a, 3000); setTimeout(a, 3000);
}); });
const userLoginId = await funGetUserIdByToken();
return ( return (
<> <>
<Coba_TestLoading /> <CobaRealtime userLoginId={userLoginId} />
{/* <Coba_TestLoading userLoginId={userLoginId} /> */}
{/* <ComponentGlobal_UI_LayoutTamplate /> */} {/* <ComponentGlobal_UI_LayoutTamplate /> */}
</> </>
); );

View File

@@ -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 (
<Stack p={"md"} align="center" justify="center" h={"80vh"}>
{notif}
<Button
onClick={() => {
onSend();
}}
>
Dari test 1
</Button>
</Stack>
);
}

View File

@@ -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 (
<Stack p={"md"} align="center" justify="center" h={"80vh"}>
<Button
onClick={() => {
onSend();
}}
>
Dari test 2 cuma notif
</Button>
</Stack>
);
}

View File

@@ -1,13 +1,14 @@
"use client"; "use client";
import { useShallowEffect } from "@mantine/hooks"; import { useHookstate } from "@hookstate/core";
import { useState } from "react"; import { Button, Stack } from "@mantine/core";
export default function Page() { export default function Page() {
const [origin, setOrigin] = useState("");
useShallowEffect(() => { return (
if (typeof window !== "undefined") { <Stack>
setOrigin(window.location.origin);
} <Button onClick={() => {}}>tekan</Button>
}, []); </Stack>
return <div>{origin}</div>; );
} }

View File

@@ -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 (
<>
<Menu
withArrow
arrowPosition="center"
opened={isOpenMenuUser}
onChange={setOpenMenuUser}
shadow="md"
width={250}
position="bottom-start"
styles={{
dropdown: {
backgroundColor: AccentColor.blue,
border: `1px solid ${AccentColor.skyblue}`,
},
item: {
color: "white",
":hover": {
backgroundColor: "gray",
},
},
arrow: {
borderTopColor: AccentColor.skyblue,
borderLeftColor: AccentColor.skyblue,
},
}}
>
<Menu.Target>
<ActionIcon variant="transparent" onClick={() => console.log("test")}>
<IconUserCircle color="white" />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Stack spacing={5} px={"xs"}>
<Menu.Item>
<Grid>
<Grid.Col span={2}>
<IconUser />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text lineClamp={1}>{dataUser.username}</Text>
</Grid.Col>
</Grid>
</Menu.Item>
<Menu.Item>
<Grid>
<Grid.Col span={2}>
<IconPhone />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text lineClamp={1}>+{dataUser.nomor}</Text>
</Grid.Col>
</Grid>
</Menu.Item>
<Menu.Divider />
<Center py={"xs"}>
<Admin_Logout />
</Center>
</Stack>
</Menu.Dropdown>
</Menu>
</>
);
}

View File

@@ -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) => (
<Box key={parent.id}>
<NavLink
opened={openDropdown && activeId === parent.id}
styles={{
icon: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
label: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeId === parent.id ? "bold" : "normal"}
label={<Text>{parent.name}</Text>}
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) => (
<Box key={child.id}>
<NavLink
styles={{
icon: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
label: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeChildId === child.id ? "bold" : "normal"}
label={<Text>{child.name}</Text>}
icon={
activeChildId === child.id ? (
<IconCircleDotFilled size={10} />
) : (
<IconCircleDot size={10} />
)
}
onClick={() => {
setActiveChildId(child.id);
router.push(child.path);
}}
active={activeId === child.id}
/>
</Box>
))}
</NavLink>
</Box>
));
const bukanDeveloper = newListAdminPage.slice(0, -1);
const adminNavbar = bukanDeveloper.map((parent) => (
<Box key={parent.id}>
<NavLink
opened={openDropdown && activeId === parent.id}
styles={{
icon: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
label: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeId === parent.id ? "bold" : "normal"}
label={<Text>{parent.name}</Text>}
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) => (
<Box key={child.id}>
<NavLink
styles={{
icon: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
label: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeChildId === child.id ? "bold" : "normal"}
label={<Text>{child.name}</Text>}
icon={
activeChildId === child.id ? (
<IconCircleDotFilled size={10} />
) : (
<IconCircleDot size={10} />
)
}
onClick={() => {
setActiveChildId(child.id);
router.push(child.path);
}}
active={activeId === child.id}
/>
</Box>
))}
</NavLink>
</Box>
));
return userRoleId == "2" ? adminNavbar : developerNavbar;
}

View File

@@ -1,5 +1,8 @@
import { Admin_ComponentLoadImageLandscape } from "./_component/comp_admin_load_image"; import { Admin_ComponentLoadImageLandscape } from "./_component/comp_admin_load_image";
import { Admin_ComponentSkeletonNavbar } from "./_component/comp_admin_skeleton_navbar"; 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_ComponentLoadImageLandscape, Admin_ComponentSkeletonNavbar };
export { Admin_UiNavbar };
export {Admin_ComponentButtonUserCircle}

View File

@@ -36,6 +36,8 @@ import { useState } from "react";
import { AdminJob_funEditCatatanById } from "../../fun/edit/fun_edit_catatan_by_id"; 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_funEditStatusPublishById } from "../../fun/edit/fun_edit_status_publish_by_id";
import adminJob_getListReview from "../../fun/get/get_list_review"; 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({ export default function AdminJob_TableReview({
dataReview, dataReview,
@@ -334,7 +336,7 @@ async function onPublish({
const loadData = await adminJob_getListReview({ page: 1 }); const loadData = await adminJob_getListReview({ page: 1 });
onLoadData(loadData); onLoadData(loadData);
const dataNotif = { const dataNotifikasi: IRealtimeData = {
appId: publish.data?.id as any, appId: publish.data?.id as any,
status: publish.data?.MasterStatus?.name as any, status: publish.data?.MasterStatus?.name as any,
userId: publish.data?.authorId as any, userId: publish.data?.authorId as any,
@@ -343,20 +345,16 @@ async function onPublish({
title: "Job publish", title: "Job publish",
}; };
const notif = await adminNotifikasi_funCreateToUser({ const createNotifikasi = await adminNotifikasi_funCreateToUser({
data: dataNotif as any, data: dataNotifikasi as any,
}); });
if (notif.status === 201) { if (createNotifikasi.status === 201) {
mqtt_client.publish( WibuRealtime.setData({
"USER", type: "notification",
JSON.stringify({ userId: publish?.data?.authorId, count: 1 }) pushNotificationTo: "USER",
); dataMessage: dataNotifikasi,
});
mqtt_client.publish(
"Job_new_post",
JSON.stringify({ isNewPost: true, count: 1 })
);
} }
ComponentGlobal_NotifikasiBerhasil(publish.message); ComponentGlobal_NotifikasiBerhasil(publish.message);
@@ -382,24 +380,34 @@ async function onReject({
ComponentGlobal_NotifikasiBerhasil(reject.message); 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, appId: reject.data?.id as any,
status: reject.data?.MasterStatus?.name as any, status: reject.data?.MasterStatus?.name as any,
userId: reject.data?.authorId as any, userId: reject.data?.authorId as any,
pesan: reject.data?.title as any, pesan: reject.data?.title as any,
kategoriApp: "JOB", kategoriApp: "JOB",
title: "Job anda ditolak !", title: "Job reject",
}; };
const notif = await adminNotifikasi_funCreateToUser({ const createRejectNotifikasi = await adminNotifikasi_funCreateToUser({
data: dataNotif as any, data: dataNotifikasi as any,
}); });
if (notif.status === 201) { if (createRejectNotifikasi.status === 201) {
mqtt_client.publish( WibuRealtime.setData({
"USER", type: "notification",
JSON.stringify({ userId: dataNotif.userId, count: 1 }) pushNotificationTo: "USER",
); dataMessage: dataNotifikasi,
});
} }
} else { } else {
ComponentGlobal_NotifikasiGagal(reject.message); ComponentGlobal_NotifikasiGagal(reject.message);

View File

@@ -326,7 +326,7 @@ export default function AdminLayout({
position="right" position="right"
size={"xs"} size={"xs"}
> >
<ComponentAdmin_UIDrawerNotifikasi {/* <ComponentAdmin_UIDrawerNotifikasi
data={dataNotif} data={dataNotif}
onLoadReadNotif={(val: any) => { onLoadReadNotif={(val: any) => {
setDataNotif(val); setDataNotif(val);
@@ -339,7 +339,7 @@ export default function AdminLayout({
onLoadCountNotif={(val: any) => { onLoadCountNotif={(val: any) => {
setCountNotif(val); setCountNotif(val);
}} }}
/> /> */}
</Drawer> </Drawer>
</> </>
); );

View File

@@ -1,62 +1,74 @@
"use client"; "use client";
import { gs_admin_ntf } from "@/app/lib/global_state";
import { import {
ActionIcon, ActionIcon,
AppShell, AppShell,
Box,
Center,
Divider, Divider,
Drawer, Drawer,
Grid, Grid,
Group, Group,
Menu, Indicator,
Navbar, Navbar,
NavLink,
ScrollArea, ScrollArea,
Stack, Stack,
Text, Text,
Title, Title,
} from "@mantine/core"; } from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks";
import { import { IconBell } from "@tabler/icons-react";
IconBell,
IconCircleDot,
IconCircleDotFilled,
IconPhone,
IconUser,
IconUserCircle,
} from "@tabler/icons-react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import _ from "lodash";
import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import { AccentColor, MainColor } from "../_global/color"; import { AccentColor } from "../_global/color";
import { MODEL_USER } from "../home/model/interface"; 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 { import {
gs_admin_navbar_isActive_dropdown,
gs_admin_navbar_menu, gs_admin_navbar_menu,
gs_admin_navbar_subMenu, gs_admin_navbar_subMenu,
} from "./_admin_global/new_global_state"; } 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 { ComponentAdmin_UIDrawerNotifikasi } from "./notifikasi/ui_drawer_notifikasi";
import { Admin_ComponentSkeletonNavbar } from "./_admin_global";
export function Admin_NewLayout({ export function Admin_NewLayout({
children, children,
user, user,
countNotifikasi,
listNotifikasi,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
user: MODEL_USER; user: MODEL_USER;
countNotifikasi: number;
listNotifikasi: MODEL_NOTIFIKASI[];
}) { }) {
const matches = useMediaQuery("(min-width: 1024px)"); const matches = useMediaQuery("(min-width: 1024px)");
const [dataUser, setDataUser] = useState(user); const [dataUser, setDataUser] = useState(user);
const userRoleId = dataUser.masterUserRoleId; const userRoleId = dataUser.masterUserRoleId;
const [opened, setOpened] = useState(false); 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<MODEL_NOTIFIKASI[]>(listNotifikasi);
// Notifikasi // Notifikasi
const [isDrawerNotifikasi, setDrawerNotifikasi] = useState(false); 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 ( return (
<> <>
@@ -65,7 +77,7 @@ export function Admin_NewLayout({
navbarOffsetBreakpoint={1024} navbarOffsetBreakpoint={1024}
navbar={ navbar={
<Navbar <Navbar
height={"100vh"} height={"100%"}
width={{ base: 250 }} width={{ base: 250 }}
hiddenBreakpoint={1024} hiddenBreakpoint={1024}
hidden={!opened} hidden={!opened}
@@ -88,13 +100,26 @@ export function Admin_NewLayout({
<Grid.Col span={5}> <Grid.Col span={5}>
<Stack h={"100%"} justify="center"> <Stack h={"100%"} justify="center">
<Group position="right" spacing={5}> <Group position="right" spacing={5}>
<ButtonUserCircle dataUser={dataUser} /> <Admin_ComponentButtonUserCircle
dataUser={dataUser}
/>
<ActionIcon <ActionIcon
variant="transparent" variant="transparent"
onClick={() => setDrawerNotifikasi(true)} onClick={() => {
onLoadListNotifikasi();
}}
> >
<IconBell color="white" /> {countNtf == 0 ? (
<IconBell color="white" />
) : (
<Indicator
processing
label={<Text fz={10}>{countNtf}</Text>}
>
<IconBell color="white" />
</Indicator>
)}
</ActionIcon> </ActionIcon>
</Group> </Group>
</Stack> </Stack>
@@ -110,7 +135,13 @@ export function Admin_NewLayout({
style={{ color: "white", transition: "0.5s" }} style={{ color: "white", transition: "0.5s" }}
> >
<Stack style={{ color: "white" }}> <Stack style={{ color: "white" }}>
<NavbarAdmin userRoleId={userRoleId} /> <Admin_UiNavbar
userRoleId={userRoleId}
activeId={activeId}
activeChildId={activeChildId as any}
setActiveId={setActiveId}
setActiveChildId={setActiveChildId}
/>
</Stack> </Stack>
</Navbar.Section> </Navbar.Section>
@@ -156,272 +187,20 @@ export function Admin_NewLayout({
size={"xs"} size={"xs"}
> >
<ComponentAdmin_UIDrawerNotifikasi <ComponentAdmin_UIDrawerNotifikasi
data={[]} listNotifikasi={dataNotifikasi}
onLoadReadNotif={(val: any) => { onChangeNavbar={(val: { id: string; childId: string }) => {
// setDataNotif(val); setActiveId(val.id);
setActiveChildId(val.childId);
}} }}
onChangeNavbar={(val: any) => { onToggleNavbar={(val: any) => {
// setActiveId(val.id); // console.log(val, "toggle navbar");
// setActiveChild(val.childId); // setDrawerNotifikasi(val);
}} }}
onToggleNavbar={setIsNavbarOpen}
onLoadCountNotif={(val: any) => { onLoadCountNotif={(val: any) => {
// setCountNotif(val); setCountNtf(val);
}} }}
/> />
</Drawer> </Drawer>
</> </>
); );
} }
function NavbarAdmin({ userRoleId }: { userRoleId: string }) {
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) => (
<Box key={parent.id}>
<NavLink
opened={openDropdown && activeId === parent.id}
styles={{
icon: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
label: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeId === parent.id ? "bold" : "normal"}
label={<Text>{parent.name}</Text>}
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) => (
<Box key={child.id}>
<NavLink
styles={{
icon: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
label: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeChildId === child.id ? "bold" : "normal"}
label={<Text>{child.name}</Text>}
icon={
activeChildId === child.id ? (
<IconCircleDotFilled size={10} />
) : (
<IconCircleDot size={10} />
)
}
onClick={() => {
setActiveChildId(child.id);
router.push(child.path);
}}
active={activeId === child.id}
/>
</Box>
))}
</NavLink>
</Box>
));
const bukanDeveloper = newListAdminPage.slice(0, -1);
const adminNavbar = bukanDeveloper.map((parent) => (
<Box key={parent.id}>
<NavLink
opened={openDropdown && activeId === parent.id}
styles={{
icon: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
label: {
color: activeId === parent.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeId === parent.id ? "bold" : "normal"}
label={<Text>{parent.name}</Text>}
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) => (
<Box key={child.id}>
<NavLink
styles={{
icon: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
label: {
color:
activeChildId === child.id ? MainColor.yellow : "white",
},
}}
style={{
color: "white",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
fw={activeChildId === child.id ? "bold" : "normal"}
label={<Text>{child.name}</Text>}
icon={
activeChildId === child.id ? (
<IconCircleDotFilled size={10} />
) : (
<IconCircleDot size={10} />
)
}
onClick={() => {
setActiveChildId(child.id);
router.push(child.path);
}}
active={activeId === child.id}
/>
</Box>
))}
</NavLink>
</Box>
));
return userRoleId == "2" ? adminNavbar : developerNavbar;
}
function ButtonUserCircle({ dataUser }: { dataUser: MODEL_USER }) {
const [isOpenMenuUser, setOpenMenuUser] = useState(false);
return (
<>
<Menu
withArrow
arrowPosition="center"
opened={isOpenMenuUser}
onChange={setOpenMenuUser}
shadow="md"
width={250}
position="bottom-start"
styles={{
dropdown: {
backgroundColor: AccentColor.blue,
border: `1px solid ${AccentColor.skyblue}`,
},
item: {
color: "white",
":hover": {
backgroundColor: "gray",
},
},
arrow: {
borderTopColor: AccentColor.skyblue,
borderLeftColor: AccentColor.skyblue,
},
}}
>
<Menu.Target>
<ActionIcon variant="transparent" onClick={() => console.log("test")}>
<IconUserCircle color="white" />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Stack spacing={5} px={"xs"}>
<Menu.Item>
<Grid>
<Grid.Col span={2}>
<IconUser />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text lineClamp={1}>{dataUser.username}</Text>
</Grid.Col>
</Grid>
</Menu.Item>
<Menu.Item>
<Grid>
<Grid.Col span={2}>
<IconPhone />
</Grid.Col>
<Grid.Col span={"auto"}>
<Text lineClamp={1}>+{dataUser.nomor}</Text>
</Grid.Col>
</Grid>
</Menu.Item>
<Menu.Divider />
<Center py={"xs"}>
<Admin_Logout />
</Center>
</Stack>
</Menu.Dropdown>
</Menu>
</>
);
}

View File

@@ -67,33 +67,33 @@ export const newListAdminPage = [
//Donasi //Donasi
{ {
id: "Donaasi", id: "Donasi",
name: "Donasi", name: "Donasi",
path: "", path: "",
icon: <IconHeartHandshake />, icon: <IconHeartHandshake />,
child: [ child: [
{ {
id: "Donaasi_1", id: "Donasi_1",
name: "Dashboard", name: "Dashboard",
path: RouterAdminDonasi.main, path: RouterAdminDonasi.main,
}, },
{ {
id: "Donaasi_2", id: "Donasi_2",
name: "Table Publish", name: "Table Publish",
path: RouterAdminDonasi.table_publish, path: RouterAdminDonasi.table_publish,
}, },
{ {
id: "Donaasi_3", id: "Donasi_3",
name: "Table Review", name: "Table Review",
path: RouterAdminDonasi.table_review, path: RouterAdminDonasi.table_review,
}, },
{ {
id: "Donaasi_4", id: "Donasi_4",
name: "Table Reject", name: "Table Reject",
path: RouterAdminDonasi.table_reject, path: RouterAdminDonasi.table_reject,
}, },
{ {
id: "Donaasi_5", id: "Donasi_5",
name: "Table Kategori", name: "Table Kategori",
path: RouterAdminDonasi.table_kategori, path: RouterAdminDonasi.table_kategori,
}, },

View File

@@ -24,5 +24,6 @@ export default async function adminNotifikasi_funCreateToUser({
}, },
}); });
if (!create) return { status: 400, message: "Gagal mengirim notifikasi" };
return { status: 201, message: "Berhasil mengirim notifikasi" }; return { status: 201, message: "Berhasil mengirim notifikasi" };
} }

View File

@@ -0,0 +1,20 @@
"use server";
import { prisma } from "@/app/lib";
export async function admin_funCheckStatusJob({ id }: { id: string }) {
const data = await prisma.job.findUnique({
where: {
id: id,
},
select: {
MasterStatus: true,
},
});
if (data?.MasterStatus?.name === "Review") {
return true;
} else {
return false;
}
}

View File

@@ -20,5 +20,6 @@ export default async function adminNotifikasi_getByUserId() {
userRoleId: "2", userRoleId: "2",
}, },
}); });
return data; return data;
} }

View File

@@ -1,35 +1,26 @@
import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface"; import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface";
import _ from "lodash"; import { ComponentAdminGlobal_NotifikasiPeringatan } from "../../_admin_global/admin_notifikasi/notifikasi_peringatan";
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import { admin_funCheckStatusJob } from "../fun/get/fun_check_status_job";
import adminNotifikasi_funUpdateIsReadById from "../fun/update/fun_update_is_read_by_id";
export default async function adminNotifikasi_findRouterJob({ export async function adminNotifikasi_findRouterJob({
data, data,
router,
onChangeNavbar,
onToggleNavbar,
}: { }: {
data: MODEL_NOTIFIKASI; data: MODEL_NOTIFIKASI;
router: AppRouterInstance;
onChangeNavbar: (val: any) => void;
onToggleNavbar: (val: any) => void;
}) { }) {
const routeName = "/dev/admin/job/child/"; const check = await admin_funCheckStatusJob({ id: data.appId });
if (data.status === "Review") { if (check) {
router.push(routeName + _.lowerCase(data.status)); const udpateReadNotifikasi = await adminNotifikasi_funUpdateIsReadById({
onChangeNavbar({ notifId: data?.id,
id: 6,
childId: 63,
}); });
}
if (data.status === "Draft") { if (udpateReadNotifikasi.status == 200) {
router.push(routeName + "review"); return true;
onChangeNavbar({ } else {
id: 6, return false;
childId: 63, }
}); } else {
ComponentAdminGlobal_NotifikasiPeringatan("Status telah dirubah oleh user");
} }
onToggleNavbar(true);
} }

View File

@@ -0,0 +1,36 @@
export type IAdmin_ActivePage = "Investasi" | "Donasi" | "Event" | "Vote" | "Job" | "Forum" | "Collaboration"
export type IAdmin_ActiveChildId =
| "Investasi_1"
| "Investasi_2"
| "Investasi_3"
| "Investasi_4"
| "Donasi_1"
| "Donasi_2"
| "Donasi_3"
| "Donasi_4"
| "Donasi_5"
| "Event_1"
| "Event_2"
| "Event_3"
| "Event_4"
| "Event_5"
| "Event_6"
| "Voting_1"
| "Voting_2"
| "Voting_3"
| "Voting_4"
| "Voting_5"
| "Job_1"
| "Job_2"
| "Job_3"
| "Job_4"
| "Forum_1"
| "Forum_2"
| "Forum_3"
| "Forum_4"
| "Collaboration_1"
| "Collaboration_2"
| "Collaboration_3"
| "Collaboration_4";

View File

@@ -1,41 +1,142 @@
import { AccentColor } from "@/app_modules/_global/color";
import { ComponentGlobal_CardLoadingOverlay } from "@/app_modules/_global/component";
import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface"; import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface";
import { import {
Badge,
Card,
Center, Center,
Divider,
Group,
Paper, Paper,
Stack, Stack,
Card,
Group,
Badge,
Divider,
Text, Text,
} from "@mantine/core"; } from "@mantine/core";
import { IconChecks, IconCheck } from "@tabler/icons-react"; import { IconCheck, IconChecks } from "@tabler/icons-react";
import _ from "lodash"; import _ from "lodash";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react";
import adminNotifikasi_countNotifikasi from "./fun/count/count_is_read"; import adminNotifikasi_countNotifikasi from "./fun/count/count_is_read";
import adminNotifikasi_getByUserId from "./fun/get/get_notifikasi_by_user_id"; import adminNotifikasi_getByUserId from "./fun/get/get_notifikasi_by_user_id";
import adminNotifikasi_funUpdateIsReadById from "./fun/update/fun_update_is_read_by_id"; import { adminNotifikasi_findRouterJob } from "./route_setting/job";
import adminNotifikasi_findRouterDonasi from "./route_setting/donasi"; import {
import { adminNotifikasi_findRouterEvent } from "./route_setting/event"; IAdmin_ActiveChildId,
import adminNotifikasi_findRouterForum from "./route_setting/forum"; IAdmin_ActivePage,
import adminNotifikasi_findRouterJob from "./route_setting/job"; } from "./route_setting/type_of_select_page";
import { adminNotifikasi_findRouterVoting } from "./route_setting/voting";
import adminNotifikasi_findRouterInvestasi from "./route_setting/investasi";
export function ComponentAdmin_UIDrawerNotifikasi({ export function ComponentAdmin_UIDrawerNotifikasi({
data, listNotifikasi,
onLoadReadNotif,
onChangeNavbar, onChangeNavbar,
onToggleNavbar, onToggleNavbar,
onLoadCountNotif, onLoadCountNotif,
}: { }: {
data: MODEL_NOTIFIKASI[]; listNotifikasi: MODEL_NOTIFIKASI[];
onLoadReadNotif: (val: any) => void; onChangeNavbar: (val: {
onChangeNavbar: (val: any) => void; id: IAdmin_ActivePage;
childId: IAdmin_ActiveChildId;
}) => void;
onToggleNavbar: (val: any) => void; onToggleNavbar: (val: any) => void;
onLoadCountNotif: (val: any) => void; onLoadCountNotif: (val: any) => void;
}) { }) {
const router = useRouter(); const router = useRouter();
const [data, setData] = useState<MODEL_NOTIFIKASI[]>(listNotifikasi);
const [visible, setVisible] = useState(false);
const [dataId, setDataId] = useState<string>("");
async function onRead({ data }: { data: MODEL_NOTIFIKASI }) {
// JOB
if (data?.kategoriApp === "JOB") {
const checkJob = await adminNotifikasi_findRouterJob({
data: data,
});
if (checkJob) {
setVisible(true);
setDataId(data.id);
const loadCountNotif = await adminNotifikasi_countNotifikasi();
onLoadCountNotif(loadCountNotif);
const loadListNotifikasi = await adminNotifikasi_getByUserId();
setData(loadListNotifikasi as any);
if (loadCountNotif && loadListNotifikasi) {
onChangeNavbar({
id: "Job",
childId: "Job_3",
});
router.push("/dev/admin/job/child/review");
setVisible(false);
setDataId("");
}
}
}
// // FORUM
// e?.kategoriApp === "FORUM" &&
// adminNotifikasi_findRouterForum({
// data: e,
// router: router,
// onChangeNavbar(val) {
// onChangeNavbar(val);
// },
// onToggleNavbar(val) {
// onToggleNavbar(val);
// },
// });
// // VOTE
// e?.kategoriApp === "VOTING" &&
// adminNotifikasi_findRouterVoting({
// data: e,
// router: router,
// onChangeNavbar(val) {
// onChangeNavbar(val);
// },
// onToggleNavbar(val) {
// onToggleNavbar(val);
// },
// });
// // EVENT
// e?.kategoriApp === "EVENT" &&
// adminNotifikasi_findRouterEvent({
// data: e,
// router: router,
// onChangeNavbar(val) {
// onChangeNavbar(val);
// },
// onToggleNavbar(val) {
// onToggleNavbar(val);
// },
// });
// // DONASI
// e.kategoriApp === "DONASI" &&
// adminNotifikasi_findRouterDonasi({
// data: e,
// router: router,
// onChangeNavbar(val) {
// onChangeNavbar(val);
// },
// onToggleNavbar(val) {
// onToggleNavbar(val);
// },
// });
// // INVESTASI
// e.kategoriApp === "INVESTASI" &&
// adminNotifikasi_findRouterInvestasi({
// data: e,
// router: router,
// onChangeNavbar(val) {
// onChangeNavbar(val);
// },
// onToggleNavbar(val) {
// onToggleNavbar(val);
// },
// });
}
if (_.isEmpty(data)) { if (_.isEmpty(data)) {
return ( return (
@@ -58,109 +159,22 @@ export function ComponentAdmin_UIDrawerNotifikasi({
style={{ style={{
transition: "0.5s", transition: "0.5s",
}} }}
c={"white"}
key={e?.id} key={e?.id}
// withBorder bg={e?.isRead ? "gray" : AccentColor.darkblue}
bg={e?.isRead ? "gray.1" : "gray.4"}
sx={{ sx={{
borderColor: "gray", borderColor: AccentColor.blue,
borderStyle: "solid", borderStyle: "solid",
borderWidth: "0.5px", borderWidth: "2px",
":hover": { ":hover": {
backgroundColor: "#C1C2C5", backgroundColor: AccentColor.blue,
borderColor: AccentColor.softblue,
borderStyle: "solid",
borderWidth: "2px",
}, },
}} }}
onClick={async () => { onClick={async () => {
// JOB onRead({ data: e });
e?.kategoriApp === "JOB" &&
adminNotifikasi_findRouterJob({
data: e,
router: router,
onChangeNavbar: (val: any) => {
onChangeNavbar(val);
},
onToggleNavbar: onToggleNavbar,
});
// FORUM
e?.kategoriApp === "FORUM" &&
adminNotifikasi_findRouterForum({
data: e,
router: router,
onChangeNavbar(val) {
onChangeNavbar(val);
},
onToggleNavbar(val) {
onToggleNavbar(val);
},
});
// VOTE
e?.kategoriApp === "VOTING" &&
adminNotifikasi_findRouterVoting({
data: e,
router: router,
onChangeNavbar(val) {
onChangeNavbar(val);
},
onToggleNavbar(val) {
onToggleNavbar(val);
},
});
// EVENT
e?.kategoriApp === "EVENT" &&
adminNotifikasi_findRouterEvent({
data: e,
router: router,
onChangeNavbar(val) {
onChangeNavbar(val);
},
onToggleNavbar(val) {
onToggleNavbar(val);
},
});
// DONASI
e.kategoriApp === "DONASI" &&
adminNotifikasi_findRouterDonasi({
data: e,
router: router,
onChangeNavbar(val) {
onChangeNavbar(val);
},
onToggleNavbar(val) {
onToggleNavbar(val);
},
});
// INVESTASI
e.kategoriApp === "INVESTASI" &&
adminNotifikasi_findRouterInvestasi({
data: e,
router: router,
onChangeNavbar(val) {
onChangeNavbar(val);
},
onToggleNavbar(val) {
onToggleNavbar(val);
},
});
const updateIsRead = await adminNotifikasi_funUpdateIsReadById({
notifId: e?.id,
});
if (updateIsRead) {
const loadCountNotif =
await adminNotifikasi_countNotifikasi();
onLoadCountNotif(loadCountNotif);
const loadDataNotif = await adminNotifikasi_getByUserId();
onLoadReadNotif(loadDataNotif);
} else {
return null;
}
// callBackIsNotifikasi(false); // callBackIsNotifikasi(false);
}} }}
> >
@@ -193,12 +207,12 @@ export function ComponentAdmin_UIDrawerNotifikasi({
</Card.Section> </Card.Section>
<Card.Section p={"sm"}> <Card.Section p={"sm"}>
<Group position="apart"> <Group position="apart">
<Text fz={10} color="gray"> <Text fz={10}>
{new Intl.DateTimeFormat("id-ID", { {new Intl.DateTimeFormat("id-ID", {
dateStyle: "long", dateStyle: "long",
}).format(e?.createdAt)} }).format(e?.createdAt)}
<Text span inherit fz={10} color="gray"> <Text span inherit fz={10}>
{", "} {", "}
{new Intl.DateTimeFormat("id-ID", { {new Intl.DateTimeFormat("id-ID", {
timeStyle: "short", timeStyle: "short",
@@ -207,20 +221,19 @@ export function ComponentAdmin_UIDrawerNotifikasi({
</Text> </Text>
{e?.isRead ? ( {e?.isRead ? (
<Group spacing={5}> <Group spacing={5}>
<IconChecks color="gray" size={10} /> <IconChecks size={10} />
<Text fz={10} color="gray"> <Text fz={10}>Sudah dilihat</Text>
Sudah dilihat
</Text>
</Group> </Group>
) : ( ) : (
<Group spacing={5}> <Group spacing={5}>
<IconCheck color="gray" size={10} /> <IconCheck size={10} />
<Text fz={10} color="gray"> <Text fz={10}>Belum dilihat</Text>
Belum dilihat
</Text>
</Group> </Group>
)} )}
</Group> </Group>
{visible && dataId === e?.id && (
<ComponentGlobal_CardLoadingOverlay />
)}
</Card.Section> </Card.Section>
</Card> </Card>
))} ))}

View File

@@ -0,0 +1,40 @@
"use server";
import { prisma } from "@/app/lib";
import { randomOTP } from "./rondom_otp";
export async function auth_funResendCode({ nomor }: { nomor: string }) {
const codeOtp = randomOTP();
try {
const res = await fetch(
`https://wa.wibudev.com/code?nom=${nomor}&text=HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya.
\n
>> Kode OTP anda: ${codeOtp}.
`
);
const sendWa = await res.json();
if (sendWa.status !== "success")
return { status: 400, message: "WA Tidak Terdaftar", kodeId: {} };
const createOtpId = await prisma.kodeOtp.create({
data: {
nomor: nomor,
otp: codeOtp,
},
});
if (!createOtpId)
return { status: 400, message: "Gagal Membuat Kode OTP", kodeId: {} };
return {
status: 200,
message: "Kode Verifikasi Dikirim",
kodeId: createOtpId.id,
};
} catch (error) {
console.log(error);
return { status: 500, message: "Server Error !!!", kodeId: {} };
}
}

View File

@@ -0,0 +1,3 @@
import { auth_funResendCode } from "./fun_resend_code";
export { auth_funResendCode };

View File

@@ -3,15 +3,16 @@
import { RouterAdminDashboard } from "@/app/lib/router_hipmi/router_admin"; import { RouterAdminDashboard } from "@/app/lib/router_hipmi/router_admin";
import { RouterAuth } from "@/app/lib/router_hipmi/router_auth"; import { RouterAuth } from "@/app/lib/router_hipmi/router_auth";
import { RouterHome } from "@/app/lib/router_hipmi/router_home"; import { RouterHome } from "@/app/lib/router_hipmi/router_home";
import { GlobalEnv } from "@/app/lib/token";
import { import {
AccentColor, AccentColor,
MainColor, MainColor,
} from "@/app_modules/_global/color/color_pallet"; } from "@/app_modules/_global/color/color_pallet";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan";
import { UIGlobal_LayoutDefault } from "@/app_modules/_global/ui";
import { import {
ActionIcon, ActionIcon,
BackgroundImage,
Box, Box,
Button, Button,
Center, Center,
@@ -20,23 +21,33 @@ import {
Text, Text,
Title, Title,
} from "@mantine/core"; } from "@mantine/core";
import { useFocusTrap } from "@mantine/hooks"; import { useFocusTrap, useShallowEffect } from "@mantine/hooks";
import { Prisma } from "@prisma/client";
import { IconChevronLeft } from "@tabler/icons-react"; import { IconChevronLeft } from "@tabler/icons-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import { auth_funResendCode } from "../fun";
import { auth_funDeleteAktivasiKodeOtpById } from "../fun/fun_edit_aktivasi_kode_otp_by_id"; import { auth_funDeleteAktivasiKodeOtpById } from "../fun/fun_edit_aktivasi_kode_otp_by_id";
import { auth_funValidasi } from "../fun/fun_validasi"; import { auth_funValidasi } from "../fun/fun_validasi";
import { GlobalEnv } from "@/app/lib/token";
import { UIGlobal_LayoutDefault } from "@/app_modules/_global/ui";
export default function Validasi({ dataOtp }: { dataOtp: any }) { export default function Validasi({
dataOtp,
}: {
dataOtp: Prisma.KodeOtpSelect;
}) {
const router = useRouter(); const router = useRouter();
const [nomor, setnomor] = useState(dataOtp.nomor); const nomor = dataOtp.nomor as any;
const [code, setCode] = useState(dataOtp.otp); const code = dataOtp.otp as any;
const [inputCode, setInputOtp] = useState(""); const [inputCode, setInputOtp] = useState("");
const focusTrapRef = useFocusTrap(); const focusTrapRef = useFocusTrap();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [counter, setCounter] = useState(60);
useShallowEffect(() => {
counter > 0 && setTimeout(() => setCounter(counter - 1), 1000);
}, [counter]);
async function onVerifikasi() { async function onVerifikasi() {
if (!inputCode) if (!inputCode)
return ComponentGlobal_NotifikasiPeringatan("Lengkapi Kode"); return ComponentGlobal_NotifikasiPeringatan("Lengkapi Kode");
@@ -48,7 +59,9 @@ export default function Validasi({ dataOtp }: { dataOtp: any }) {
HIPMI_PWD: GlobalEnv.value?.WIBU_PWD as string, HIPMI_PWD: GlobalEnv.value?.WIBU_PWD as string,
}); });
if (res.status === 200) { if (res.status === 200) {
const resAktivasi = await auth_funDeleteAktivasiKodeOtpById(dataOtp.id); const resAktivasi = await auth_funDeleteAktivasiKodeOtpById(
dataOtp.id as any
);
if (resAktivasi.status === 200) { if (resAktivasi.status === 200) {
if (res.role === "1") { if (res.role === "1") {
ComponentGlobal_NotifikasiBerhasil(res.message); ComponentGlobal_NotifikasiBerhasil(res.message);
@@ -85,6 +98,16 @@ export default function Validasi({ dataOtp }: { dataOtp: any }) {
router.back(); router.back();
} }
async function onResendCode() {
const res = await auth_funResendCode({ nomor: nomor });
if (res.status === 200) {
ComponentGlobal_NotifikasiBerhasil(res.message, 2000);
router.push(RouterAuth.validasi + res.kodeId, { scroll: false });
} else {
ComponentGlobal_NotifikasiPeringatan(res.message);
}
}
return ( return (
<> <>
<UIGlobal_LayoutDefault> <UIGlobal_LayoutDefault>
@@ -102,24 +125,25 @@ export default function Validasi({ dataOtp }: { dataOtp: any }) {
</ActionIcon> </ActionIcon>
</Box> </Box>
<Stack align="center" justify="center" h={"100vh"} spacing={70}> <Stack align="center" justify="center" h={"100vh"} spacing={50}>
<Title order={2} color={MainColor.yellow}> <Title order={2} color={MainColor.yellow}>
Verifikasi Kode OTP Verifikasi Kode OTP
</Title> </Title>
<Stack spacing={0} align="center"> <Stack spacing={"md"} align="center">
<Text fz={"xs"} c={"white"}> <Stack spacing={0} align="center">
Masukan 4 digit kode otp <Text c={"white"}>Masukan 4 digit kode otp</Text>
</Text> <Text c={"white"}>
<Text fz={"xs"} c={"white"}> Yang dikirim ke{" "}
Yang dikirim ke{" "} <Text span inherit fw={"bold"}>
<Text span inherit fw={"bold"}> {" "}
{" "} +{nomor}
+{nomor} </Text>
</Text> </Text>
</Text> </Stack>
<Center> <Center>
<PinInput <PinInput
size="xl"
type={"number"} type={"number"}
ref={focusTrapRef} ref={focusTrapRef}
spacing={"md"} spacing={"md"}
@@ -129,6 +153,19 @@ export default function Validasi({ dataOtp }: { dataOtp: any }) {
}} }}
/> />
</Center> </Center>
<Text fs="italic" mt={"sm"} c={"white"}>
Tidak menerima kode ?{" "}
{counter > 0 ? (
<Text fw={"bold"} inherit span>
{counter + "s"}
</Text>
) : (
<Text inherit span onClick={() => onResendCode()}>
Kirim ulang
</Text>
)}
</Text>
</Stack> </Stack>
<Button <Button
w={300} w={300}

View File

@@ -11,15 +11,15 @@ export function CheckCookies_UiLayout({
}) { }) {
const router = useRouter(); const router = useRouter();
useShallowEffect(() => { // useShallowEffect(() => {
onCheckCookies(); // onCheckCookies();
}, []); // }, []);
async function onCheckCookies() { async function onCheckCookies() {
const cek = await fetch("/api/check-cookies"); const cek = await fetch("/api/check-cookies");
const result = await cek.json(); const result = await cek.json();
if (result.success === false) { if (result.success === false) {
router.push(RouterAuth.login); router.push(RouterAuth.login, { scroll: false });
} }
} }

View File

@@ -1,23 +1,22 @@
"use client"; "use client";
import UIGlobal_LayoutHeaderTamplate from "@/app_modules/_global/ui/ui_header_tamplate"; import { gs_realtimeData, gs_user_ntf } from "@/app/lib/global_state";
import { ActionIcon, Indicator, Loader, Text } from "@mantine/core"; import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi";
import { MODEL_USER } from "../model/interface";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search"; import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search";
import { import {
AccentColor, AccentColor,
MainColor, MainColor,
} from "@/app_modules/_global/color/color_pallet"; } from "@/app_modules/_global/color/color_pallet";
import { IconBell, IconUserSearch } from "@tabler/icons-react"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan";
import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi";
import { useShallowEffect } from "@mantine/hooks";
import notifikasi_countUserNotifikasi from "@/app_modules/notifikasi/fun/count/fun_count_by_id"; import notifikasi_countUserNotifikasi from "@/app_modules/notifikasi/fun/count/fun_count_by_id";
import mqtt_client from "@/util/mqtt_client";
import { useAtom } from "jotai";
import { gs_notifikasi_kategori_app } from "@/app_modules/notifikasi/lib"; import { gs_notifikasi_kategori_app } from "@/app_modules/notifikasi/lib";
import { ActionIcon, Indicator, Loader, Text } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { IconBell, IconUserSearch } from "@tabler/icons-react";
import { useAtom } from "jotai";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { MODEL_USER } from "../model/interface";
export function ComponentHome_ButtonHeaderLeft({ export function ComponentHome_ButtonHeaderLeft({
dataUser, dataUser,
@@ -59,30 +58,46 @@ export function ComponentHome_ButtonHeaderRight({
countNotifikasi: number; countNotifikasi: number;
}) { }) {
const router = useRouter(); const router = useRouter();
const [count, setCount] = useState(countNotifikasi);
const [isLoadingBell, setIsLoadingBell] = useState(false); const [isLoadingBell, setIsLoadingBell] = useState(false);
const [activeKategori, setActiveKategori] = useAtom( const [activeKategori, setActiveKategori] = useAtom(
gs_notifikasi_kategori_app gs_notifikasi_kategori_app
); );
// Notifikasi
const [countNtf, setCountNtf] = useState(countNotifikasi);
const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf);
const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData);
useShallowEffect(() => { useShallowEffect(() => {
mqtt_client.subscribe("USER"); if (dataRealtime?.userId == dataUser.id) {
setCountNtf((e) => e + newUserNtf), setNewUserNtf(0);
mqtt_client.on("message", (topic: any, message: any) => { }
// console.log(topic);
const data = JSON.parse(message.toString());
if (data.userId === dataUser.id) {
setCount(count + data.count);
}
});
onLoadNotifikasi({ onLoadNotifikasi({
onLoad(val) { onLoad(val) {
setCount(val); setCountNtf(val);
}, },
}); });
}, [count]); }, [newUserNtf, setCountNtf]);
// useShallowEffect(() => {
// mqtt_client.subscribe("USER");
// mqtt_client.on("message", (topic: any, message: any) => {
// // console.log(topic);
// const data = JSON.parse(message.toString());
// if (data.userId === dataUser.id) {
// setCountNtf(countNtf + data.count);
// }
// });
// onLoadNotifikasi({
// onLoad(val) {
// setCountNtf(val);
// },
// });
// }, [countNtf]);
async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) {
const loadNotif = await notifikasi_countUserNotifikasi(); const loadNotif = await notifikasi_countUserNotifikasi();
@@ -99,13 +114,13 @@ export function ComponentHome_ButtonHeaderRight({
} else { } else {
router.push(RouterNotifikasi.main, { scroll: false }); router.push(RouterNotifikasi.main, { scroll: false });
setIsLoadingBell(true); setIsLoadingBell(true);
setActiveKategori("Semua") setActiveKategori("Semua");
} }
}} }}
> >
{isLoadingBell ? ( {isLoadingBell ? (
<Loader color={AccentColor.yellow} size={20} /> <Loader color={AccentColor.yellow} size={20} />
) : count === 0 ? ( ) : countNtf === 0 ? (
<IconBell color="white" /> <IconBell color="white" />
) : ( ) : (
<Indicator <Indicator
@@ -113,7 +128,7 @@ export function ComponentHome_ButtonHeaderRight({
color={MainColor.yellow} color={MainColor.yellow}
label={ label={
<Text fz={10} c={MainColor.darkblue}> <Text fz={10} c={MainColor.darkblue}>
{count > 99 ? "99+" : count} {countNtf > 99 ? "99+" : countNtf}
</Text> </Text>
} }
> >

View File

@@ -17,6 +17,9 @@ export default function HomeView({
dataJob: MODEL_JOB[]; dataJob: MODEL_JOB[];
countNotifikasi: number; countNotifikasi: number;
}) { }) {
return ( return (
<> <>
<UIGlobal_LayoutTamplate <UIGlobal_LayoutTamplate

View File

@@ -1,6 +1,7 @@
"use client"; "use client";
import { DIRECTORY_ID } from "@/app/lib"; import { DIRECTORY_ID } from "@/app/lib";
import { IRealtimeData } from "@/app/lib/global_state";
import { RouterJob } from "@/app/lib/router_hipmi/router_job"; import { RouterJob } from "@/app/lib/router_hipmi/router_job";
import { AccentColor, MainColor } from "@/app_modules/_global/color"; import { AccentColor, MainColor } from "@/app_modules/_global/color";
import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun";
@@ -10,13 +11,13 @@ import {
ComponentGlobal_NotifikasiPeringatan, ComponentGlobal_NotifikasiPeringatan,
} from "@/app_modules/_global/notif_global"; } from "@/app_modules/_global/notif_global";
import { notifikasiToAdmin_funCreate } from "@/app_modules/notifikasi/fun"; import { notifikasiToAdmin_funCreate } from "@/app_modules/notifikasi/fun";
import mqtt_client from "@/util/mqtt_client";
import { Button } from "@mantine/core"; import { Button } from "@mantine/core";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import { WibuRealtime } from "wibu-pkg";
import { job_funCreateNoFile, job_funCreateWithFile } from "../../fun"; import { job_funCreateNoFile, job_funCreateWithFile } from "../../fun";
import { gs_job_hot_menu, gs_job_status } from "../../global_state"; import { gs_job_hot_menu } from "../../global_state";
import { MODEL_JOB } from "../../model/interface"; import { MODEL_JOB } from "../../model/interface";
function Job_ComponentButtonSaveCreate({ function Job_ComponentButtonSaveCreate({
@@ -28,9 +29,7 @@ function Job_ComponentButtonSaveCreate({
}) { }) {
const router = useRouter(); const router = useRouter();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu); const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu);
const [status, setStatus] = useAtom(gs_job_status);
async function onCreate() { async function onCreate() {
if (file === null) { if (file === null) {
@@ -39,7 +38,8 @@ function Job_ComponentButtonSaveCreate({
}); });
if (createNoFile.status === 201) { if (createNoFile.status === 201) {
const dataNotif: any = { const dataNotifikasi: IRealtimeData = {
userRole: "ADMIN",
appId: createNoFile.data?.id as any, appId: createNoFile.data?.id as any,
status: createNoFile.data?.MasterStatus?.name as any, status: createNoFile.data?.MasterStatus?.name as any,
userId: createNoFile.data?.authorId as any, userId: createNoFile.data?.authorId as any,
@@ -49,18 +49,16 @@ function Job_ComponentButtonSaveCreate({
}; };
const notif = await notifikasiToAdmin_funCreate({ const notif = await notifikasiToAdmin_funCreate({
data: dataNotif as any, data: dataNotifikasi as any,
}); });
if (notif.status === 201) { if (notif.status === 201) {
mqtt_client.publish( WibuRealtime.setData({
"ADMIN", type: "notification",
JSON.stringify({ pushNotificationTo: "ADMIN",
count: 1, });
})
);
setHotMenu(2); setHotMenu(2);
setStatus("Review");
router.replace(RouterJob.status({ id: "2" })); router.replace(RouterJob.status({ id: "2" }));
setIsLoading(true); setIsLoading(true);
ComponentGlobal_NotifikasiBerhasil(createNoFile.message); ComponentGlobal_NotifikasiBerhasil(createNoFile.message);
@@ -83,9 +81,10 @@ function Job_ComponentButtonSaveCreate({
}); });
if (createWithFile.status === 201) { if (createWithFile.status === 201) {
const dataNotif: any = { const dataNotifikasi: IRealtimeData = {
userRole: "ADMIN",
appId: createWithFile.data?.id as any, appId: createWithFile.data?.id as any,
status: createWithFile.data?.MasterStatus?.name as any, status: "Review",
userId: createWithFile.data?.authorId as any, userId: createWithFile.data?.authorId as any,
pesan: createWithFile.data?.title as any, pesan: createWithFile.data?.title as any,
kategoriApp: "JOB", kategoriApp: "JOB",
@@ -93,18 +92,16 @@ function Job_ComponentButtonSaveCreate({
}; };
const notif = await notifikasiToAdmin_funCreate({ const notif = await notifikasiToAdmin_funCreate({
data: dataNotif as any, data: dataNotifikasi as any,
}); });
if (notif.status === 201) { if (notif.status === 201) {
mqtt_client.publish( WibuRealtime.setData({
"ADMIN", type: "notification",
JSON.stringify({ pushNotificationTo: "ADMIN",
count: 1, });
})
);
setHotMenu(2); setHotMenu(2);
setStatus("Review");
router.replace(RouterJob.status({ id: "2" })); router.replace(RouterJob.status({ id: "2" }));
setIsLoading(true); setIsLoading(true);
ComponentGlobal_NotifikasiBerhasil(createWithFile.message); ComponentGlobal_NotifikasiBerhasil(createWithFile.message);

View File

@@ -41,7 +41,6 @@ export default function Job_Create() {
content: "", content: "",
deskripsi: "", deskripsi: "",
}); });
const [reload, setReload] = useState(false);
const [file, setFile] = useState<File | null>(null); const [file, setFile] = useState<File | null>(null);
const [img, setImg] = useState<any | null>(); const [img, setImg] = useState<any | null>();

View File

@@ -9,7 +9,7 @@ import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { Job_funEditArsipById } from "../../fun/edit/fun_edit_arsip_by_id"; import { Job_funEditArsipById } from "../../fun/edit/fun_edit_arsip_by_id";
import { gs_job_status, gs_job_hot_menu } from "../../global_state"; import { gs_job_hot_menu } from "../../global_state";
import { useState } from "react"; import { useState } from "react";
import { useDisclosure } from "@mantine/hooks"; import { useDisclosure } from "@mantine/hooks";
import UIGlobal_Modal from "@/app_modules/_global/ui/ui_modal"; import UIGlobal_Modal from "@/app_modules/_global/ui/ui_modal";
@@ -28,14 +28,12 @@ export default function Job_DetailArsip({ dataJob }: { dataJob: MODEL_JOB }) {
function ButtonAction({ jobId }: { jobId: string }) { function ButtonAction({ jobId }: { jobId: string }) {
const router = useRouter(); const router = useRouter();
const [isLoading, setLoading] = useState(false); const [isLoading, setLoading] = useState(false);
const [status, setStatus] = useAtom(gs_job_status);
const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu); const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu);
const [opened, { open, close }] = useDisclosure(); const [opened, { open, close }] = useDisclosure();
async function onPublish() { async function onPublish() {
await Job_funEditArsipById(jobId, false).then((res) => { await Job_funEditArsipById(jobId, false).then((res) => {
if (res.status === 200) { if (res.status === 200) {
setStatus("Publish");
setHotMenu(1); setHotMenu(1);
ComponentGlobal_NotifikasiBerhasil("Berhasil Dipublish"); ComponentGlobal_NotifikasiBerhasil("Berhasil Dipublish");
setLoading(true); setLoading(true);

View File

@@ -14,7 +14,7 @@ import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import ComponentJob_DetailData from "../../component/detail/detail_data"; import ComponentJob_DetailData from "../../component/detail/detail_data";
import { Job_funEditArsipById } from "../../fun/edit/fun_edit_arsip_by_id"; import { Job_funEditArsipById } from "../../fun/edit/fun_edit_arsip_by_id";
import { gs_job_hot_menu, gs_job_status } from "../../global_state"; import { gs_job_hot_menu, } from "../../global_state";
import { MODEL_JOB } from "../../model/interface"; import { MODEL_JOB } from "../../model/interface";
import UIGlobal_Modal from "@/app_modules/_global/ui/ui_modal"; import UIGlobal_Modal from "@/app_modules/_global/ui/ui_modal";
@@ -34,13 +34,13 @@ function ButtonAction({ jobId }: { jobId: string }) {
const [isLoading, setLoading] = useState(false); const [isLoading, setLoading] = useState(false);
const [opened, { open, close }] = useDisclosure(); const [opened, { open, close }] = useDisclosure();
const [status, setStatus] = useAtom(gs_job_status);
const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu); const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu);
async function onArsipkan() { async function onArsipkan() {
await Job_funEditArsipById(jobId, true).then((res) => { await Job_funEditArsipById(jobId, true).then((res) => {
if (res.status === 200) { if (res.status === 200) {
setStatus("Publish");
setHotMenu(3); setHotMenu(3);
ComponentGlobal_NotifikasiBerhasil("Berhasil Diarsipkan"); ComponentGlobal_NotifikasiBerhasil("Berhasil Diarsipkan");
setLoading(true); setLoading(true);

View File

@@ -35,90 +35,10 @@ export async function job_funCreateWithFile({
}); });
if (!createDataWithoutImg) return { status: 400, message: "Gagal Disimpan" }; if (!createDataWithoutImg) return { status: 400, message: "Gagal Disimpan" };
revalidatePath("/dev/job/main/status"); revalidatePath("/dev/job/main/status/2");
return { return {
data: createDataWithoutImg, data: createDataWithoutImg,
status: 201, status: 201,
message: "Berhasil Disimpan", message: "Berhasil Disimpan",
}; };
// const dataImage: any = file.get("file");
// if (dataImage !== "null") {
// const fileName = dataImage.name;
// const fileExtension = _.lowerCase(dataImage.name.split(".").pop());
// const fRandomName = v4(fileName) + "." + fileExtension;
// const upload = await prisma.images.create({
// data: {
// url: fRandomName,
// label: "JOB",
// },
// select: {
// id: true,
// url: true,
// },
// });
// if (!upload) return { status: 400, message: "Gagal upload gambar" };
// const uploadFolder = Buffer.from(await dataImage.arrayBuffer());
// fs.writeFileSync(
// path.join(root, `public/job/${upload.url}`),
// uploadFolder
// );
// const createDataWithImg = await prisma.job.create({
// data: {
// title: req.title,
// content: req.content,
// deskripsi: req.deskripsi,
// authorId: authorId,
// imagesId: upload.id,
// },
// select: {
// id: true,
// authorId: true,
// MasterStatus: {
// select: {
// name: true,
// },
// },
// title: true,
// },
// });
// if (!createDataWithImg) return { status: 400, message: "Gagal Disimpan" };
// revalidatePath("/dev/job/main/status");
// return {
// data: createDataWithImg,
// status: 201,
// message: "Berhasil Disimpan",
// };
// } else {
// const createDataWithoutImg = await prisma.job.create({
// data: {
// title: req.title,
// content: req.content,
// deskripsi: req.deskripsi,
// authorId: authorId,
// },
// select: {
// id: true,
// authorId: true,
// MasterStatus: {
// select: {
// name: true,
// },
// },
// title: true,
// },
// });
// if (!createDataWithoutImg)
// return { status: 400, message: "Gagal Disimpan" };
// revalidatePath("/dev/job/main/status");
// return {
// data: createDataWithoutImg,
// status: 201,
// message: "Berhasil Disimpan",
// };
// }
} }

View File

@@ -5,5 +5,3 @@ import { atomWithStorage } from "jotai/utils";
* @returns halaman pada layout * @returns halaman pada layout
*/ */
export const gs_job_hot_menu = atomWithStorage("gs_jobs_hot_menu", 1); export const gs_job_hot_menu = atomWithStorage("gs_jobs_hot_menu", 1);
export const gs_job_status = atomWithStorage<any>("gs_job_status", "Publish");

View File

@@ -5,19 +5,18 @@ import { useShallowEffect } from "@mantine/hooks";
import { IconBriefcase } from "@tabler/icons-react"; import { IconBriefcase } from "@tabler/icons-react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { gs_job_hot_menu, gs_job_status } from "../global_state";
import { useRouter } from "next/navigation";
import { RouterJob } from "@/app/lib/router_hipmi/router_job"; import { RouterJob } from "@/app/lib/router_hipmi/router_job";
import { useRouter } from "next/navigation";
import { gs_job_hot_menu } from "../global_state";
export function Job_UiSplash() { export function Job_UiSplash() {
const router = useRouter(); const router = useRouter();
const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu); const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu);
const [status, setStatus] = useAtom(gs_job_status);
useShallowEffect(() => { useShallowEffect(() => {
setTimeout(() => { setTimeout(() => {
setHotMenu(1); setHotMenu(1);
setStatus("Publish");
router.replace(RouterJob.beranda, { scroll: false }); router.replace(RouterJob.beranda, { scroll: false });
}, 1000); }, 1000);
}, []); }, []);

View File

@@ -1,35 +1,45 @@
"use client"; "use client";
import { Badge, Card, Divider, Group, Stack, Text } from "@mantine/core"; import { RouterJob } from "@/app/lib/router_hipmi/router_job";
import { IconCheck, IconChecks } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
import notifikasi_funUpdateIsReadById from "../fun/update/fun_update_is_read_by_user_id";
import { MODEL_NOTIFIKASI } from "../model/interface";
import { redirectDetailForumPage } from "./path/forum";
import { redirectJobPage } from "./path/job";
import { import {
AccentColor, AccentColor,
MainColor, MainColor,
} from "@/app_modules/_global/color/color_pallet"; } from "@/app_modules/_global/color/color_pallet";
import { gs_job_hot_menu } from "@/app_modules/job/global_state";
import { Badge, Card, Divider, Group, Stack, Text } from "@mantine/core";
import { IconCheck, IconChecks } from "@tabler/icons-react";
import { useAtom } from "jotai";
import { useRouter } from "next/navigation";
import notifikasi_getByUserId from "../fun/get/get_notifiaksi_by_id"; import notifikasi_getByUserId from "../fun/get/get_notifiaksi_by_id";
import { redirectVotingPage } from "./path/voting"; import { MODEL_NOTIFIKASI } from "../model/interface";
import { redirectEventPage } from "./path/event";
import { redirectDetailCollaborationPage } from "./path/collaboration"; import { redirectDetailCollaborationPage } from "./path/collaboration";
import { redirectDonasiPage } from "./path/donasi"; import { redirectDonasiPage } from "./path/donasi";
import { redirectEventPage } from "./path/event";
import { redirectDetailForumPage } from "./path/forum";
import { redirectInvestasiPage } from "./path/investasi"; import { redirectInvestasiPage } from "./path/investasi";
import { notifikasi_jobCheckStatus } from "./path/job";
import { redirectVotingPage } from "./path/voting";
import { useState } from "react";
import { ComponentGlobal_CardLoadingOverlay } from "@/app_modules/_global/component";
export function ComponentNotifiaksi_CardView({ export function ComponentNotifiaksi_CardView({
data, data,
onLoadData, onLoadData,
activePage, activePage,
onSetMenu, // onSetMenu,
activeKategori,
}: { }: {
data: MODEL_NOTIFIKASI; data: MODEL_NOTIFIKASI;
onLoadData: (val: any) => void; onLoadData: (val: any) => void;
activePage: number; activePage: number;
onSetMenu: (val: any) => void; // onSetMenu: (val: any) => void;
activeKategori: string;
}) { }) {
const router = useRouter(); const router = useRouter();
const [visible, setVisible] = useState(false);
const [jobMenuId, setJobMenuId] = useAtom(gs_job_hot_menu);
return ( return (
<> <>
<Card <Card
@@ -45,73 +55,77 @@ export function ComponentNotifiaksi_CardView({
}} }}
my={"xs"} my={"xs"}
onClick={async () => { onClick={async () => {
await notifikasi_funUpdateIsReadById({ if (data?.kategoriApp === "JOB") {
notifId: data?.id, const checkStatus = await notifikasi_jobCheckStatus({
}); appId: data.appId,
dataId: data.id,
});
// if (updateIsRead.status === 200) { if (checkStatus?.success) {
// const loadData = await notifikasi_getByUserId({ const loadListNotifikasi = await notifikasi_getByUserId({
// page: activePage + 1, page: 1,
kategoriApp: activeKategori,
});
onLoadData(loadListNotifikasi);
const path = RouterJob.status({
id: checkStatus.statusId as string,
});
setJobMenuId(2);
router.push(path);
setVisible(true);
}
}
// data?.kategoriApp === "FORUM" &&
// redirectDetailForumPage({
// data: data,
// router: router,
// }); // });
// onLoadData(loadData);
// }
data?.kategoriApp === "JOB" && // data?.kategoriApp === "VOTING" &&
redirectJobPage({ // redirectVotingPage({
data: data, // data: data,
router: router, // router: router,
onSetPage(val) { // onSetPage(val) {
onSetMenu(val); // // onSetMenu(val);
}, // },
}); // });
data?.kategoriApp === "FORUM" && // data?.kategoriApp === "EVENT" &&
redirectDetailForumPage({ // redirectEventPage({
data: data, // data: data,
router: router, // router: router,
}); // onSetPage(val) {
// // onSetMenu(val);
// },
// });
data?.kategoriApp === "VOTING" && // data?.kategoriApp === "COLLABORATION" &&
redirectVotingPage({ // redirectDetailCollaborationPage({
data: data, // data: data,
router: router, // router: router,
onSetPage(val) { // });
onSetMenu(val);
},
});
data?.kategoriApp === "EVENT" && // data.kategoriApp === "DONASI" &&
redirectEventPage({ // redirectDonasiPage({
data: data, // data: data,
router: router, // router: router,
onSetPage(val) { // onSetPage(val) {
onSetMenu(val); // // onSetMenu(val);
}, // },
}); // });
data?.kategoriApp === "COLLABORATION" && // data.kategoriApp === "INVESTASI" &&
redirectDetailCollaborationPage({ // redirectInvestasiPage({
data: data, // data: data,
router: router, // router: router,
}); // onSetPage(val) {
// // onSetMenu(val);
data.kategoriApp === "DONASI" && // },
redirectDonasiPage({ // });
data: data,
router: router,
onSetPage(val) {
onSetMenu(val);
},
});
data.kategoriApp === "INVESTASI" &&
redirectInvestasiPage({
data: data,
router: router,
onSetPage(val) {
onSetMenu(val);
},
});
}} }}
> >
{/* <pre>{JSON.stringify(e, null, 2)}</pre> */} {/* <pre>{JSON.stringify(e, null, 2)}</pre> */}
@@ -181,6 +195,7 @@ export function ComponentNotifiaksi_CardView({
</Group> </Group>
)} )}
</Group> </Group>
{visible && <ComponentGlobal_CardLoadingOverlay />}
</Card.Section> </Card.Section>
</Card> </Card>
</> </>

View File

@@ -1,31 +1,36 @@
import { RouterJob } from "@/app/lib/router_hipmi/router_job"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global";
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import { notifikasi_funJobCheckStatus } from "../../fun/check/fun_check_job_status";
import { MODEL_NOTIFIKASI } from "../../model/interface"; import notifikasi_funUpdateIsReadById from "../../fun/update/fun_update_is_read_by_user_id";
export function redirectJobPage({ export async function notifikasi_jobCheckStatus({
data, appId,
router, dataId,
onSetPage,
}: { }: {
data: MODEL_NOTIFIKASI; appId: string;
router: AppRouterInstance; dataId: string;
onSetPage: (val: any) => void;
}) { }) {
const path = RouterJob.status({id: "1"}); const check = await notifikasi_funJobCheckStatus({
id: appId,
});
if (data.status === "Publish") { console.log(check);
onSetPage({
menuId: 2, if (check) {
status: data.status, const updateReadNotifikasi = await notifikasi_funUpdateIsReadById({
notifId: dataId,
}); });
}
if (data.status === "Reject") { if (updateReadNotifikasi.status == 200) {
onSetPage({ return {
menuId: 2, success: true,
status: data.status, statusId: check.statusId,
}); };
} else {
return {
success: false,
};
}
} else {
ComponentGlobal_NotifikasiPeringatan("Status tidak ditemukan");
} }
router.push(path, {scroll: false});
} }

View File

@@ -0,0 +1,25 @@
"use server";
"use server";
import { prisma } from "@/app/lib";
export async function notifikasi_funJobCheckStatus({ id }: { id: string }) {
const data = await prisma.job.findUnique({
where: {
id: id,
},
select: {
masterStatusId: true
},
});
if (!data)
return { status: 400, message: "Job tidak ditemukan", statusId: "" };
return {
status: 200,
message: "Berhasil di cek",
statusId: data.masterStatusId,
};
}

View File

@@ -8,12 +8,11 @@ export default async function notifikasiToAdmin_funCreate({
}: { }: {
data: MODEL_NOTIFIKASI; data: MODEL_NOTIFIKASI;
}) { }) {
const getAdmin = await prisma.user.findMany({ const getAdmin = await prisma.user.findMany({
where: { where: {
active: true, active: true,
NOT: { masterUserRoleId: "2",
masterUserRoleId: "1",
},
}, },
}); });

View File

@@ -15,7 +15,7 @@ export default async function notifikasi_getByUserId({
const takeData = 10; const takeData = 10;
const skipData = page * takeData - takeData; const skipData = page * takeData - takeData;
if (kategoriApp === "Semua") { if (kategoriApp === "Semua" ) {
const data = await prisma.notifikasi.findMany({ const data = await prisma.notifikasi.findMany({
take: takeData, take: takeData,
skip: skipData, skip: skipData,

View File

@@ -15,7 +15,7 @@ import {
gs_investas_menu, gs_investas_menu,
gs_investasi_status, gs_investasi_status,
} from "@/app_modules/investasi/g_state"; } from "@/app_modules/investasi/g_state";
import { gs_job_hot_menu, gs_job_status } from "@/app_modules/job/global_state"; import { gs_job_hot_menu } from "@/app_modules/job/global_state";
import { import {
gs_vote_hotMenu, gs_vote_hotMenu,
gs_vote_status, gs_vote_status,
@@ -46,8 +46,6 @@ export function Notifikasi_UiView({
); );
// Kategori App // Kategori App
const [jobMenuId, setJobMenuId] = useAtom(gs_job_hot_menu);
const [jobStatus, setJobStatus] = useAtom(gs_job_status);
const [voteMenu, setVoteMenu] = useAtom(gs_vote_hotMenu); const [voteMenu, setVoteMenu] = useAtom(gs_vote_hotMenu);
const [voteStatus, setVoteStatus] = useAtom(gs_vote_status); const [voteStatus, setVoteStatus] = useAtom(gs_vote_status);
const [eventMenu, setEventMenu] = useAtom(gs_event_hotMenu); const [eventMenu, setEventMenu] = useAtom(gs_event_hotMenu);
@@ -133,32 +131,34 @@ export function Notifikasi_UiView({
data={item} data={item}
onLoadData={setData} onLoadData={setData}
activePage={activePage} activePage={activePage}
onSetMenu={(val) => { activeKategori={activeKategori}
if (item?.kategoriApp === "JOB") { // onSetMenu={(val) => {
setJobMenuId(val.menuId); // if (item?.kategoriApp === "JOB") {
setJobStatus(val.status);
}
if (item?.kategoriApp === "VOTING") { // setJobMenuId(val.menuId);
setVoteMenu(val.menuId); // // setJobStatus(val.status);
setVoteStatus(val.status); // }
}
if (item?.kategoriApp === "EVENT") { // // if (item?.kategoriApp === "VOTING") {
setEventMenu(val.menuId); // // setVoteMenu(val.menuId);
setEventStatus(val.status); // // setVoteStatus(val.status);
} // // }
if (item?.kategoriApp === "DONASI") { // // if (item?.kategoriApp === "EVENT") {
setDonasiMenu(val.menuId); // // setEventMenu(val.menuId);
setDonasiStatus(val.status); // // setEventStatus(val.status);
} // // }
if (item?.kategoriApp === "INVESTASI") { // // if (item?.kategoriApp === "DONASI") {
setInvestasiMenu(val.menuId); // // setDonasiMenu(val.menuId);
setInvestasiStatus(val.status); // // setDonasiStatus(val.status);
} // // }
}}
// // if (item?.kategoriApp === "INVESTASI") {
// // setInvestasiMenu(val.menuId);
// // setInvestasiStatus(val.status);
// // }
// }}
/> />
)} )}
</ScrollOnly> </ScrollOnly>

View File

@@ -0,0 +1,54 @@
"use client";
import { WibuRealtime } from "wibu-pkg";
import { v4 } from "uuid";
import { Stack, Title, Button } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import Countdown from "react-countdown";
import { useHookstate } from "@hookstate/core";
const WIBU_REALTIME_TOKEN: any = process.env.NEXT_PUBLIC_WIBU_REALTIME_TOKEN;
export function CobaRealtime({ userLoginId }: { userLoginId: string }) {
// const [dataRealTime, setDataRealTime] = useWibuRealtime({
// WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN,
// project: "hipmi",
// });
useShallowEffect(() => {
WibuRealtime.init({
onData: (data) => {
console.log(data)
},
project: "hipmi",
WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN,
});
}, []);
return (
<>
<Stack w={200} p={"lg"}>
<Title order={6}>User {userLoginId}</Title>
<Button
onClick={() => {
// WibuRealtime.setData({
// id: v4(),
// name: "bagas",
// age: 28,
// });
}}
>
Cek
</Button>
</Stack>
</>
);
}

View File

@@ -1,17 +1,14 @@
"use client"; "use client";
import { import { ActionIcon, Box, Button, Stack, Title } from "@mantine/core";
ActionIcon,
Box,
Button,
Stack
} from "@mantine/core";
import { useState } from "react"; import { useState } from "react";
import { IconPencilPlus } from "@tabler/icons-react"; import { IconPencilPlus } from "@tabler/icons-react";
import _ from "lodash"; import _ from "lodash";
import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate"; import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate";
import { useShallowEffect } from "@mantine/hooks";
import { WibuRealtime } from "wibu";
import { v4 } from "uuid";
const newData = Array(20) const newData = Array(20)
.fill(0) .fill(0)
@@ -35,89 +32,84 @@ const data2 = [
}, },
]; ];
export default function Coba_TestLoading() { export default function Coba_TestLoading({
const [data, setData] = useState(data2); userLoginId,
}: {
userLoginId: string;
}) {
// const [data, setData] = useState(data2);
const [openDrawer, setOpenDrawer] = useState(false); const [openDrawer, setOpenDrawer] = useState(false);
const [newData, setNewData] = useState({});
useShallowEffect(() => {
WibuRealtime.init({
WIBU_REALTIME_TOKEN: process.env.WIBU_REALTIME_KEY as any,
project: "hipmi",
onData(data) {
console.log(data);
},
});
return () => {
WibuRealtime.cleanup();
};
}, []);
return ( return (
<> <>
<UIGlobal_LayoutTamplate> <Stack w={200} p={"lg"}>
{/* <CreateButton /> */} <Title>User {userLoginId}</Title>
<Button onClick={() => setOpenDrawer(true)}>Click</Button> <Button
</UIGlobal_LayoutTamplate> onClick={() => {
WibuRealtime.setData({
id: v4(),
userId: userLoginId,
data: `Ini dari user ${userLoginId}`,
});
}}
>
Cek
</Button>
</Stack>
</> </>
); );
// return (
// <>
// <UIGlobal_LayoutTamplate>
// {/* <CreateButton /> */}
// <Button onClick={() => setOpenDrawer(true)}>Click</Button>
// </UIGlobal_LayoutTamplate>
// </>
// );
// Clone data // Clone data
return ( // return (
<> // <>
<Box mt={"lg"}> // <Box mt={"lg"}>
<Stack> // <Stack>
<Button // <Button
onClick={() => { // onClick={() => {
const clone = _.clone(data); // const clone = _.clone(data);
const dataBaru = clone.map( // const dataBaru = clone.map(
(e) => ( // (e) => (
e.id === 1, // e.id === 1,
{ // {
...e, // ...e,
name: e.id === 1 ? "firman" : e.name, // name: e.id === 1 ? "firman" : e.name,
age: e.id === 1 ? 30 : e.age, // age: e.id === 1 ? 30 : e.age,
} // }
) // )
); // );
setData(dataBaru); // setData(dataBaru);
}} // }}
> // >
Update // Update
</Button> // </Button>
</Stack> // </Stack>
<pre>{JSON.stringify(data, null, 2)}</pre> // <pre>{JSON.stringify(data, null, 2)}</pre>
</Box> // </Box>
</> // </>
); // );
}
function CreateButton() {
return (
<>
<ActionIcon
p={3}
variant="filled"
radius={"xl"}
size={"xl"}
style={{
position: "absolute",
zIndex: 1,
bottom: 150,
right: 30,
}}
>
<IconPencilPlus size={30} />
</ActionIcon>
{/* <Affix
bg={"blue"}
withinPortal
portalProps={{}}
position={{ bottom: rem(150), right: rem(30) }}
>
<ActionIcon
style={{
transition: "0.5s",
border: `1px solid ${AccentColor.skyblue}`,
}}
bg={AccentColor.blue}
size={"xl"}
radius={"xl"}
variant="transparent"
onClick={() => {}}
>
<IconPencilPlus color="white" />
</ActionIcon>
</Affix> */}
</>
);
} }

View File

@@ -1,8 +1,7 @@
import { DIRECTORY_ID } from "@/app/lib";
import { TokenStorage } from "@/app/lib/token"; import { TokenStorage } from "@/app/lib/token";
import { import {
Box,
Button, Button,
Card,
Center, Center,
FileButton, FileButton,
Image, Image,
@@ -14,12 +13,8 @@ import { useShallowEffect } from "@mantine/hooks";
import { IconCamera, IconUpload } from "@tabler/icons-react"; import { IconCamera, IconUpload } from "@tabler/icons-react";
import { useState } from "react"; import { useState } from "react";
import { AccentColor, MainColor } from "../_global/color"; import { AccentColor, MainColor } from "../_global/color";
import {
ComponentGlobal_NotifikasiBerhasil,
ComponentGlobal_NotifikasiPeringatan,
} from "../_global/notif_global";
import { DIRECTORY_ID } from "@/app/lib";
import { funGlobal_UploadToStorage } from "../_global/fun"; import { funGlobal_UploadToStorage } from "../_global/fun";
import { ComponentGlobal_NotifikasiPeringatan } from "../_global/notif_global";
export default function Coba_UploadFile() { export default function Coba_UploadFile() {
const [data, setData] = useState<any>(); const [data, setData] = useState<any>();

View File

@@ -2,7 +2,7 @@
{ {
"name": "bagas_admin", "name": "bagas_admin",
"nomor": "6282340374412", "nomor": "6282340374412",
"masterUserRoleId": "3" "masterUserRoleId": "2"
}, },
{ {
"name": "fahmi_admin", "name": "fahmi_admin",

View File

@@ -5,5 +5,5 @@ Client_KEY : process.env.Client_KEY,
Server_KEY : process.env.Server_KEY, Server_KEY : process.env.Server_KEY,
MAPBOX_TOKEN : process.env.MAPBOX_TOKEN, MAPBOX_TOKEN : process.env.MAPBOX_TOKEN,
WS_APIKEY : process.env.WS_APIKEY, WS_APIKEY : process.env.WS_APIKEY,
WIBU_REALTIME_KEY : process.env.WIBU_REALTIME_KEY NEXT_PUBLIC_WIBU_REALTIME_TOKEN : process.env.NEXT_PUBLIC_WIBU_REALTIME_TOKEN
} }

View File

@@ -3,7 +3,7 @@
import { useEffect } from "react"; import { useEffect } from "react";
import mqtt_client from "./mqtt_client"; import mqtt_client from "./mqtt_client";
export default function MqttLoader() { export default function MqttLoader() {
useEffect(() => { useEffect(() => {
mqtt_client.on("connect", () => { mqtt_client.on("connect", () => {
console.log("connected"); console.log("connected");

763
yarn.lock

File diff suppressed because it is too large Load Diff