Notifikasi

# feat:
- Notifikasi di bagian admin

## No issue
This commit is contained in:
2024-06-10 16:22:05 +08:00
parent 0e16d6501f
commit 76f0396005
35 changed files with 618 additions and 200 deletions

View File

@@ -43,15 +43,18 @@ model User {
ProjectCollaboration_Message ProjectCollaboration_Message[] ProjectCollaboration_Message ProjectCollaboration_Message[]
AdminProjectCollaboration_Notifikasi ProjectCollaboration_Notifikasi[] @relation("AdminNotifProjectToUser") AdminProjectCollaboration_Notifikasi ProjectCollaboration_Notifikasi[] @relation("AdminNotifProjectToUser")
UserProjectCollaboration_Notifikasi ProjectCollaboration_Notifikasi[] @relation("UserNotifProjectToUser") UserProjectCollaboration_Notifikasi ProjectCollaboration_Notifikasi[] @relation("UserNotifProjectToUser")
Admin_Notifikasi Notifikasi[] @relation("AdminNotifikasi")
User_Notifikasi Notifikasi[] @relation("UserNotifikasi")
} }
model MasterUserRole { model MasterUserRole {
id String @id id String @id
name String name String
active Boolean @default(true) active Boolean @default(true)
createdAt DateTime? @default(now()) createdAt DateTime? @default(now())
updatedAt DateTime? @updatedAt updatedAt DateTime? @updatedAt
User User[] User User[]
Notifikasi Notifikasi[]
} }
model UserSession { model UserSession {
@@ -847,3 +850,23 @@ model NomorAdmin {
nomor String nomor String
} }
model Notifikasi {
id String @id @default(cuid())
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
isRead Boolean @default(false)
appId String
kategoriApp String
pesan String
title String?
status String?
Role MasterUserRole? @relation(fields: [userRoleId], references: [id])
userRoleId String
User User @relation("UserNotifikasi", fields: [userId], references: [id], map: "NotifikasiUser")
userId String
Admin User @relation("AdminNotifikasi", fields: [adminId], references: [id], map: "NotifikasiAdmin")
adminId String
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -1,4 +1,6 @@
import { AdminLayout } from "@/app_modules/admin/main_dashboard"; import { AdminLayout } from "@/app_modules/admin/main_dashboard";
import adminNotifikasi_countNotifikasi from "@/app_modules/admin/notifikasi/fun/count/count_is_read";
import adminNotifikasi_getByUserId from "@/app_modules/admin/notifikasi/fun/get/get_notifikasi_by_user_id";
import { user_getOneUserId } from "@/app_modules/fun_global/get_user_token"; import { user_getOneUserId } from "@/app_modules/fun_global/get_user_token";
import { user_getOneByUserId } from "@/app_modules/home/fun/get/get_one_user_by_id"; import { user_getOneByUserId } from "@/app_modules/home/fun/get/get_one_user_by_id";
import React from "react"; import React from "react";
@@ -8,13 +10,20 @@ export default async function Layout({
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const userId = await user_getOneUserId() const userId = await user_getOneUserId();
const dataUser = await user_getOneByUserId(userId) const dataUser = await user_getOneByUserId(userId);
const userRole = dataUser?.masterUserRoleId const listNotif = await adminNotifikasi_getByUserId();
const countNotifikasi = await adminNotifikasi_countNotifikasi();
return ( return (
<> <>
<AdminLayout userRole={userRole as any}>{children}</AdminLayout> <AdminLayout
listNotif={listNotif as any}
dataUser={dataUser as any}
countNotifikasi={countNotifikasi}
>
{children}
</AdminLayout>
</> </>
); );
} }

View File

@@ -0,0 +1,9 @@
import { Notifikasi_MainView } from "@/app_modules/admin/notifikasi";
export default async function Page() {
return (
<>
<Notifikasi_MainView />
</>
);
}

View File

@@ -1,7 +1,7 @@
export const RouterAdminJob = { export const RouterAdminJob = {
main: "/dev/admin/job/main", main: "/dev/admin/job/main",
table_publish: "/dev/admin/job/child/table_publish", publish: "/dev/admin/job/child/publish",
table_review: "/dev/admin/job/child/table_review", review: "/dev/admin/job/child/review",
table_reject: "/dev/admin/job/child/table_reject", reject: "/dev/admin/job/child/reject",
arsip: "/dev/admin/job/child/arsip", arsip: "/dev/admin/job/child/arsip",
}; };

View File

@@ -0,0 +1,3 @@
export const RouterNotifikasi = {
main: "/dev/notifikasi",
};

View File

@@ -5,6 +5,14 @@ import { atomWithStorage } from "jotai/utils";
* @type number * @type number
* @ * @
*/ */
export const gs_admin_hotMenu = atomWithStorage("gs_admin_hotMenu", 1) export const gs_admin_hotMenu = atomWithStorage("gs_admin_hotMenu", 1);
export const gs_admin_subMenu = atomWithStorage<number | null>("gs_admin_subMenu",null) export const gs_admin_subMenu = atomWithStorage<number | null>(
"gs_admin_subMenu",
null
);
export const gs_layout_admin_isNavbarOpen = atomWithStorage(
"gs_layout_admin_isNavbarOpen",
false
);

View File

@@ -1,6 +1,6 @@
import AdminJob_Main from "./main"; import AdminJob_Main from "./main";
import AdminJob_TablePublish from "./child/table_publish"; import AdminJob_TablePublish from "./child/publish";
import AdminJob_TableReview from "./child/table_review"; import AdminJob_TableReview from "./child/review";
import AdminJob_TableReject from "./child/table_reject"; import AdminJob_TableReject from "./child/reject";
export { AdminJob_Main, AdminJob_TablePublish, AdminJob_TableReview, AdminJob_TableReject }; export { AdminJob_Main, AdminJob_TablePublish, AdminJob_TableReview, AdminJob_TableReject };

View File

@@ -3,69 +3,90 @@
import { import {
ActionIcon, ActionIcon,
AppShell, AppShell,
Badge,
Box, Box,
Burger, Burger,
Button,
Card,
Center,
Divider, Divider,
Drawer, Drawer,
Group, Group,
Header, Header,
Indicator,
MediaQuery, MediaQuery,
NavLink, NavLink,
Navbar, Navbar,
Paper,
ScrollArea, ScrollArea,
Stack, Stack,
Text, Text,
Title, Title,
useMantineTheme useMantineTheme,
} from "@mantine/core"; } from "@mantine/core";
import { import {
IconBell, IconBell,
IconCheck,
IconChecks,
IconCircleDot, IconCircleDot,
IconCircleDotFilled, IconCircleDotFilled,
IconDashboard IconDashboard,
IconUserSquareRounded,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import _ from "lodash"; import _ from "lodash";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { auth_Logout } from "../auth/fun/fun_logout"; import { auth_Logout } from "../auth/fun/fun_logout";
import { gs_kodeId } from "../auth/state/state"; import { gs_kodeId } from "../auth/state/state";
import { ComponentGlobal_NotifikasiBerhasil } from "../component_global/notif_global/notifikasi_berhasil"; import { ComponentGlobal_NotifikasiBerhasil } from "../component_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiPeringatan } from "../component_global/notif_global/notifikasi_peringatan"; import { ComponentGlobal_NotifikasiPeringatan } from "../component_global/notif_global/notifikasi_peringatan";
import Admin_Logout from "./component_global/logout"; import Admin_Logout from "./component_global/logout";
import { gs_admin_hotMenu, gs_admin_subMenu } from "./global_state"; import {
gs_admin_hotMenu,
gs_admin_subMenu,
gs_layout_admin_isNavbarOpen,
} from "./global_state";
import { listAdminPage } from "./list_page"; import { listAdminPage } from "./list_page";
import { MODEL_NOTIFIKASI } from "../notifikasi/model/interface";
import { MODEL_USER } from "../home/model/interface";
import { useHover, useShallowEffect, useToggle } from "@mantine/hooks";
import moment from "moment";
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
import { RouterAdminJob } from "@/app/lib/router_admin/router_admin_job";
import adminNotifikasi_funUpdateIsReadById from "./notifikasi/fun/update/fun_update_is_read_by_id";
import adminNotifikasi_getByUserId from "./notifikasi/fun/get/get_notifikasi_by_user_id";
import adminNotifikasi_countNotifikasi from "./notifikasi/fun/count/count_is_read";
import mqtt_client from "@/util/mqtt_client";
export default function AdminLayout({ export default function AdminLayout({
userRole,
children, children,
listNotif,
dataUser,
countNotifikasi,
}: { }: {
userRole: string;
children: React.ReactNode; children: React.ReactNode;
listNotif: MODEL_NOTIFIKASI[];
dataUser: MODEL_USER;
countNotifikasi: number;
}) { }) {
const theme = useMantineTheme(); const theme = useMantineTheme();
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
const router = useRouter(); const router = useRouter();
const [active, setActive] = useAtom(gs_admin_hotMenu); const [activeId, setActiveId] = useAtom(gs_admin_hotMenu);
const [activeChild, setActiveChild] = useAtom(gs_admin_subMenu); const [activeChild, setActiveChild] = useAtom(gs_admin_subMenu);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [kodeId, setKodeId] = useAtom(gs_kodeId);
async function onClickLogout() { const [user, setUser] = useState(dataUser);
// await auth_Logout(kodeId).then((res) => { const userRoleId = user.masterUserRoleId;
// ComponentGlobal_NotifikasiBerhasil("Berhasil Logout");
// });
await auth_Logout(kodeId).then((res) => {
if (res.status === 200) {
ComponentGlobal_NotifikasiBerhasil(res.message);
setKodeId("");
} else {
ComponentGlobal_NotifikasiPeringatan(res.message);
}
});
}
const navbarItems = listAdminPage.map((e, i) => ( const [isNotif, setIsNotif] = useState(false);
const [dataNotif, setDataNotif] = useState(listNotif);
const [countNotif, setCountNotif] = useState(countNotifikasi);
const [isNavbarOpen, setIsNavbarOpen] = useAtom(gs_layout_admin_isNavbarOpen);
const developerNavbar = listAdminPage.map((e, i) => (
<Box key={e.id}> <Box key={e.id}>
<NavLink <NavLink
sx={{ sx={{
@@ -73,7 +94,7 @@ export default function AdminLayout({
backgroundColor: "transparent", backgroundColor: "transparent",
}, },
}} }}
fw={active === e.id ? "bold" : "normal"} fw={activeId === e.id ? "bold" : "normal"}
icon={ icon={
// active === e.id ? loading ? <Loader size={10} /> : e.icon : e.icon // active === e.id ? loading ? <Loader size={10} /> : e.icon : e.icon
e.icon e.icon
@@ -81,7 +102,7 @@ export default function AdminLayout({
label={<Text size={"sm"}>{e.name}</Text>} label={<Text size={"sm"}>{e.name}</Text>}
onClick={() => { onClick={() => {
setLoading(true); setLoading(true);
setActive(e.id); setActiveId(e.id);
setActiveChild(null); setActiveChild(null);
e.path === "" ? router.push(e.child[0].path) : router.push(e.path); e.path === "" ? router.push(e.child[0].path) : router.push(e.path);
e.path === "" ? setActiveChild(e.child[0].id) : ""; e.path === "" ? setActiveChild(e.child[0].id) : "";
@@ -109,7 +130,7 @@ export default function AdminLayout({
) )
} }
onClick={() => { onClick={() => {
setActive(e.id); setActiveId(e.id);
setActiveChild(v.id); setActiveChild(v.id);
router.push(v.path); router.push(v.path);
}} }}
@@ -123,26 +144,26 @@ export default function AdminLayout({
)); ));
const bukanDeveloper = listAdminPage.slice(0, -1); const bukanDeveloper = listAdminPage.slice(0, -1);
const notAdminDev = bukanDeveloper.map((e) => ( const adminNavbar = bukanDeveloper.map((e) => (
<Box key={e.id}> <Box key={e.id}>
<NavLink <NavLink
opened={e?.id === activeId && isNavbarOpen ? true : false}
sx={{ sx={{
":hover": { ":hover": {
backgroundColor: "transparent", backgroundColor: "transparent",
}, },
}} }}
fw={active === e.id ? "bold" : "normal"} fw={activeId === e.id ? "bold" : "normal"}
icon={ icon={e.icon}
// active === e.id ? loading ? <Loader size={10} /> : e.icon : e.icon
e.icon
}
label={<Text size={"sm"}>{e.name}</Text>} label={<Text size={"sm"}>{e.name}</Text>}
onClick={() => { onClick={() => {
setLoading(true); setLoading(true);
setActive(e.id); setActiveId(e.id);
setActiveChild(null); setActiveChild(null);
e.path === "" ? router.push(e.child[0].path) : router.push(e.path); e.path === "" ? router.push(e.child[0].path) : router.push(e.path);
e.path === "" ? setActiveChild(e.child[0].id) : ""; e.path === "" ? setActiveChild(e.child[0].id) : "";
setIsNavbarOpen(true);
}} }}
> >
{_.isEmpty(e.child) ? ( {_.isEmpty(e.child) ? (
@@ -167,7 +188,7 @@ export default function AdminLayout({
) )
} }
onClick={() => { onClick={() => {
setActive(e.id); setActiveId(e.id);
setActiveChild(v.id); setActiveChild(v.id);
router.push(v.path); router.push(v.path);
}} }}
@@ -180,20 +201,20 @@ export default function AdminLayout({
</Box> </Box>
)); ));
const navbarAdmin = ( async function onLoadNotifikasi() {
<Box> const loadNotif = await adminNotifikasi_getByUserId();
<NavLink setDataNotif(loadNotif as any);
c="orange" }
icon={<IconDashboard />}
label="Developer" useEffect(() => {
sx={{ mqtt_client.subscribe("ADMIN");
":hover": {
backgroundColor: "transparent", mqtt_client.on("message", (topic: any, message: any) => {
}, const data = JSON.parse(message.toString());
}} // console.log(data);
/> setCountNotif(countNotif + data.count);
</Box> });
); }, [countNotif]);
return ( return (
<> <>
@@ -201,59 +222,34 @@ export default function AdminLayout({
padding="md" padding="md"
navbarOffsetBreakpoint="md" navbarOffsetBreakpoint="md"
asideOffsetBreakpoint="sm" asideOffsetBreakpoint="sm"
navbar={
<MediaQuery smallerThan={"md"} styles={{ display: "none" }}>
<Navbar
h={"95%"}
width={{ lg: 250, md: 200, sm: 200, base: 250 }}
hiddenBreakpoint="md"
hidden={!opened}
p="xs"
bg={"gray.2"}
>
{/* <Navbar.Section>
<Center h={50}>
<Title order={4} ff={"sans-serif"}>
Dashboard Admin
</Title>
</Center>
<Divider />
</Navbar.Section> */}
<Navbar.Section grow component={ScrollArea}>
<Stack>{userRole === "3" ? navbarItems : notAdminDev}</Stack>
</Navbar.Section>
<Navbar.Section>
<Stack>
<Divider />
<Group position="apart">
<Text fs={"italic"} c={"gray"} fz={"xs"}>
V 1.0.0
</Text>
<Admin_Logout />
</Group>
</Stack>
</Navbar.Section>
</Navbar>
</MediaQuery>
}
header={ header={
<Header height={"5vh"} bg={"gray.2"}> <Header height={"6vh"} bg={"gray.2"}>
{/* Web View */} {/* Web View */}
<MediaQuery smallerThan={"md"} styles={{ display: "none" }}> <MediaQuery smallerThan={"md"} styles={{ display: "none" }}>
<Group position="apart" align="center" h={"100%"} px={"md"}> <Group position="apart" align="center" h={"100%"} px={"md"}>
<Text fw={"lighter"}>Dashboard Admin</Text> <Title order={3}>Dashboard Admin</Title>
<Title order={4}> HIPMI</Title>
{/* <Group> <Group>
{listAdminPage.map((e) => ( <ActionIcon
<Text key={e.id} onClick={() => router.push(e.route)}> radius={"xl"}
{e.name} onClick={() => {
</Text> setIsNotif(true);
))} onLoadNotifikasi();
</Group> */} }}
{/* <Admin_Logout /> */} >
<ActionIcon radius={"xl"}> <Indicator
<IconBell /> processing
</ActionIcon> label={<Text fz={10}>{countNotif}</Text>}
>
<IconBell />
</Indicator>
</ActionIcon>
<Divider orientation="vertical" color="dark" />
<Group>
<Text>{user?.username}</Text>
<IconUserSquareRounded />
</Group>
</Group>
</Group> </Group>
</MediaQuery> </MediaQuery>
@@ -276,10 +272,39 @@ export default function AdminLayout({
</MediaQuery> </MediaQuery>
</Header> </Header>
} }
navbar={
<MediaQuery smallerThan={"md"} styles={{ display: "none" }}>
<Navbar
h={"94vh"}
width={{ lg: 250, md: 200, sm: 200, base: 250 }}
hiddenBreakpoint="md"
hidden={!opened}
p="xs"
bg={"gray.2"}
>
<Navbar.Section grow component={ScrollArea}>
<Stack>
{userRoleId === "3" ? developerNavbar : adminNavbar}
</Stack>
</Navbar.Section>
<Navbar.Section>
<Stack>
<Divider />
<Group position="apart">
<Text fs={"italic"} c={"gray"} fz={"xs"}>
V 1.0.0
</Text>
<Admin_Logout />
</Group>
</Stack>
</Navbar.Section>
</Navbar>
</MediaQuery>
}
> >
{/* {JSON.stringify(active)} */}
{children} {children}
</AppShell> </AppShell>
{/* Drawer Mobile View */}
<Drawer opened={opened} onClose={() => setOpened(false)} size={"50%"}> <Drawer opened={opened} onClose={() => setOpened(false)} size={"50%"}>
<Stack spacing={"xl"}> <Stack spacing={"xl"}>
{listAdminPage.map((e) => ( {listAdminPage.map((e) => (
@@ -289,6 +314,183 @@ export default function AdminLayout({
))} ))}
</Stack> </Stack>
</Drawer> </Drawer>
{/* Drawer Notifikasi */}
<Drawer
title={
<Group position="apart">
<Text fw={"bold"} fz={"lg"}>
Notifikasi
</Text>
{/* <Button compact radius={"xl"} fz={10}>Tandai terliha</Button> */}
</Group>
}
opened={isNotif}
onClose={() => setIsNotif(false)}
position="right"
size={"xs"}
>
<DrawerNotifikasi
data={dataNotif}
onLoadReadNotif={(val: any) => {
setDataNotif(val);
}}
onChangeNavbar={(val: any) => {
setActiveId(val.id);
setActiveChild(val.childId);
}}
onToggleNavbar={setIsNavbarOpen}
onLoadCountNotif={(val: any) => {
setCountNotif(val);
}}
/>
</Drawer>
</> </>
); );
} }
function DrawerNotifikasi({
data,
onLoadReadNotif,
onChangeNavbar,
onToggleNavbar,
onLoadCountNotif,
}: {
data: MODEL_NOTIFIKASI[];
onLoadReadNotif: (val: any) => void;
onChangeNavbar: (val: any) => void;
onToggleNavbar: (val: any) => void;
onLoadCountNotif: (val: any) => void;
}) {
const router = useRouter();
if (_.isEmpty(data)){
return (
<>
<Center>
<Text c={"gray"} fz={"xs"}>
Tidak ada notifikasi
</Text>
</Center>
</>
);
}
return (
<>
<Paper h={"100%"}>
<Stack>
{data.map((e, i) => (
<Card
key={e?.id}
// withBorder
bg={e?.isRead ? "gray.1" : "gray.4"}
sx={{
borderColor: "gray",
borderStyle: "solid",
borderWidth: "0.5px",
":hover": {
borderColor: "gray",
borderStyle: "solid",
borderWidth: "1.5px",
},
}}
onClick={async () => {
e?.kategoriApp === "JOB" &&
findRouterJob({
data: e,
router: router,
onChangeNavbar2: (val: any) => {
onChangeNavbar(val);
},
onToggleNavbar2: onToggleNavbar,
});
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);
}}
>
<Card.Section p={"sm"}>
<Group position="apart">
<Text fw={"bold"} fz={"xs"}>
# {e?.kategoriApp}
</Text>
{e?.status ? <Badge w={70}>{e?.status}</Badge> : ""}
</Group>
</Card.Section>
<Card.Section p={"sm"}>
<Text lineClamp={2}>{e?.pesan}</Text>
</Card.Section>
<Card.Section p={"sm"}>
<Group position="apart">
<Text fz={10} color="gray">
{new Intl.DateTimeFormat("id-ID", {
dateStyle: "long",
}).format(e?.createdAt)}
<Text span inherit fz={10} color="gray">
{", "}
{new Intl.DateTimeFormat("id-ID", {
timeStyle: "short",
}).format(e?.createdAt)}
</Text>
</Text>
{e?.isRead ? (
<Group spacing={5}>
<IconChecks color="gray" size={10} />
<Text fz={10} color="gray">
Sudah dilihat
</Text>
</Group>
) : (
<Group spacing={5}>
<IconCheck color="gray" size={10} />
<Text fz={10} color="gray">
Belum dilihat
</Text>
</Group>
)}
</Group>
</Card.Section>
</Card>
))}
</Stack>
</Paper>
</>
);
}
async function findRouterJob({
data,
router,
onChangeNavbar2,
onToggleNavbar2,
}: {
data: MODEL_NOTIFIKASI;
router: AppRouterInstance;
onChangeNavbar2: (val: any) => void;
onToggleNavbar2: (val: any) => void;
}) {
const routeName = "/dev/admin/job/child/";
router.push(routeName + _.lowerCase(data.status));
onChangeNavbar2({
id: 6,
childId: 63,
});
onToggleNavbar2(true);
}

View File

@@ -194,17 +194,17 @@ export const listAdminPage = [
{ {
id: 62, id: 62,
name: "Table Publish", name: "Table Publish",
path: RouterAdminJob.table_publish, path: RouterAdminJob.publish,
}, },
{ {
id: 63, id: 63,
name: "Table Review", name: "Table Review",
path: RouterAdminJob.table_review, path: RouterAdminJob.review,
}, },
{ {
id: 64, id: 64,
name: "Table Reject", name: "Table Reject",
path: RouterAdminJob.table_reject, path: RouterAdminJob.reject,
}, },
{ {
id: 65, id: 65,

View File

@@ -0,0 +1,17 @@
"use server";
import prisma from "@/app/lib/prisma";
import { user_getOneUserId } from "@/app_modules/fun_global/get_user_token";
export default async function adminNotifikasi_countNotifikasi() {
const userId = await user_getOneUserId();
const data = await prisma.notifikasi.findMany({
where: {
adminId: userId,
isRead: false,
},
});
return data.length;
}

View File

@@ -0,0 +1,19 @@
"use server";
import prisma from "@/app/lib/prisma";
import { user_getOneUserId } from "@/app_modules/fun_global/get_user_token";
export default async function adminNotifikasi_getByUserId() {
const adminId = await user_getOneUserId();
const data = await prisma.notifikasi.findMany({
orderBy:{
createdAt: "desc"
},
where: {
adminId: adminId,
userRoleId: "2",
},
});
return data;
}

View File

@@ -0,0 +1,21 @@
"use server";
import prisma from "@/app/lib/prisma";
export default async function adminNotifikasi_funUpdateIsReadById({
notifId,
}: {
notifId: string;
}) {
const updt = await prisma.notifikasi.update({
where: {
id: notifId,
},
data: {
isRead: true,
},
});
if (!updt) return { status: 400 };
return { status: 200 };
}

View File

@@ -0,0 +1,5 @@
// test notif
import Notifikasi_MainView from "./main";
export { Notifikasi_MainView };

View File

@@ -0,0 +1,25 @@
"use client";
import AppComponentGlobal_LayoutTamplate from "@/app_modules/component_global/component_layout_tamplate";
import ComponentGlobal_HeaderTamplate from "@/app_modules/component_global/header_tamplate";
import { Text } from "@mantine/core";
export default function Notifikasi_MainView() {
return (
<>
<AppComponentGlobal_LayoutTamplate
header={<ComponentGlobal_HeaderTamplate title="Notifikasi" />}
>
<MainView />
</AppComponentGlobal_LayoutTamplate>
</>
);
}
function MainView() {
return (
<>
<Text>notif</Text>
</>
);
}

View File

@@ -12,7 +12,6 @@ export async function user_getOneUserId() {
const c = cookies().get("ssn"); const c = cookies().get("ssn");
if (!c?.value || c.value === "") return redirect(RouterAuth.login); if (!c?.value || c.value === "") return redirect(RouterAuth.login);
const token = JSON.parse( const token = JSON.parse(
await unsealData(c?.value as string, { await unsealData(c?.value as string, {
password: config.server.password, password: config.server.password,

View File

@@ -0,0 +1 @@
// index

View File

@@ -23,6 +23,7 @@ import {
IconAward, IconAward,
IconQrcode, IconQrcode,
IconUserCircle, IconUserCircle,
IconBell,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { Logout } from "../auth"; import { Logout } from "../auth";
import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog";
@@ -32,6 +33,7 @@ import { useRouter } from "next/navigation";
import { ComponentGlobal_NotifikasiPeringatan } from "../component_global/notif_global/notifikasi_peringatan"; import { ComponentGlobal_NotifikasiPeringatan } from "../component_global/notif_global/notifikasi_peringatan";
import { ComponentGlobal_NotifikasiBerhasil } from "../component_global/notif_global/notifikasi_berhasil"; import { ComponentGlobal_NotifikasiBerhasil } from "../component_global/notif_global/notifikasi_berhasil";
import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search"; import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search";
import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi";
export default function HomeLayout({ export default function HomeLayout({
dataUser, dataUser,
@@ -157,11 +159,22 @@ export default function HomeLayout({
top={0} top={0}
h={50} h={50}
> >
<Center h={"100%"}> <Group position="apart" h={"100%"} px={"md"}>
<Title order={4} c={"white"}> <ActionIcon variant="transparent" disabled></ActionIcon>
HIPMI <Center>
</Title> <Title order={4} c={"white"}>
</Center> HIPMI
</Title>
</Center>
<ActionIcon
variant="transparent"
onClick={() => {
router.push(RouterNotifikasi.main);
}}
>
<IconBell />
</ActionIcon>
</Group>
</Box> </Box>
{/* Children */} {/* Children */}

View File

@@ -1,13 +1,9 @@
"use client"; "use client";
import { RouterJob } from "@/app/lib/router_hipmi/router_job";
import { import {
AspectRatio,
Box,
Button, Button,
Center, Center,
FileButton, FileButton,
Flex,
Group, Group,
Image, Image,
Loader, Loader,
@@ -15,18 +11,15 @@ import {
Stack, Stack,
Text, Text,
TextInput, TextInput,
Textarea,
} from "@mantine/core"; } from "@mantine/core";
import { IconCamera, IconUpload } from "@tabler/icons-react"; import { IconCamera, IconUpload } from "@tabler/icons-react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import _ from "lodash";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React, { useState } from "react"; import { useState } from "react";
import { gs_job_hot_menu, gs_job_status } from "../global_state"; import { gs_job_hot_menu, gs_job_status } from "../global_state";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import "react-quill/dist/quill.snow.css";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import "react-quill/dist/quill.snow.css";
const ReactQuill = dynamic( const ReactQuill = dynamic(
() => { () => {
return import("react-quill"); return import("react-quill");
@@ -34,16 +27,20 @@ const ReactQuill = dynamic(
{ ssr: false } { ssr: false }
); );
import { useShallowEffect, useToggle } from "@mantine/hooks";
import { Job_funCreate } from "../fun/create/fun_create";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/component_global/notif_global/notifikasi_peringatan";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
import { MODEL_JOB } from "../model/interface";
import toast from "react-simple-toasts";
import ComponentJob_NotedBox from "../component/detail/noted_box";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { defaultDeskripsi, defaultSyarat } from "../component/default_value";
import ComponentGlobal_InputCountDown from "@/app_modules/component_global/input_countdown"; import ComponentGlobal_InputCountDown from "@/app_modules/component_global/input_countdown";
import ComponentGlobal_V2_LoadingPage from "@/app_modules/component_global/loading_page_v2";
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/component_global/notif_global/notifikasi_peringatan";
import mqtt_client from "@/util/mqtt_client";
import { useShallowEffect } from "@mantine/hooks";
import { defaultDeskripsi, defaultSyarat } from "../component/default_value";
import ComponentJob_NotedBox from "../component/detail/noted_box";
import { MODEL_JOB } from "../model/interface";
import { Job_funCreate } from "../fun/create/fun_create";
import notifikasi_funCreate from "@/app_modules/notifikasi/fun/create/create_notif";
import { RouterJob } from "@/app/lib/router_hipmi/router_job";
import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/component_global/notif_global/notifikasi_gagal";
import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface";
export default function Job_Create() { export default function Job_Create() {
const [value, setValue] = useState({ const [value, setValue] = useState({
@@ -225,25 +222,40 @@ function ButtonAction({ value, file }: { value: MODEL_JOB; file: FormData }) {
const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu); const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu);
const [status, setStatus] = useAtom(gs_job_status); const [status, setStatus] = useAtom(gs_job_status);
const [preview, setPreview] = useToggle();
async function onAction() { async function onCreate() {
const gambar = new FormData(); const gambar = new FormData();
gambar.append("file", file as any); gambar.append("file", file as any);
// console.log(value); const create = await Job_funCreate(value as any, gambar);
if (create.status === 201) {
const dataNotif = {
appId: create.data?.id as any,
kategoriApp: "JOB",
status: create.data?.MasterStatus?.name as any,
userId: create.data?.authorId as any,
pesan: create.data?.title as any,
title: "Job baru",
};
const notif = await notifikasi_funCreate({ data: dataNotif as any });
if (notif.status === 201) {
mqtt_client.publish(
"ADMIN",
JSON.stringify({
count: 1,
})
);
await Job_funCreate(value as any, gambar).then((res) => {
if (res.status === 201) {
setHotMenu(2); setHotMenu(2);
setStatus("Review"); setStatus("Review");
router.replace(RouterJob.status); router.replace(RouterJob.status);
setIsLoading(true); setIsLoading(true);
ComponentGlobal_NotifikasiBerhasil("Tambah Lowongan Berhasil"); ComponentGlobal_NotifikasiBerhasil(create.message);
} else {
ComponentGlobal_NotifikasiGagal(res.message);
} }
}); } else {
ComponentGlobal_NotifikasiGagal(create.message);
}
} }
return ( return (
@@ -251,11 +263,6 @@ function ButtonAction({ value, file }: { value: MODEL_JOB; file: FormData }) {
<Stack> <Stack>
<Group grow mt={"lg"} mb={70}> <Group grow mt={"lg"} mb={70}>
<Button <Button
style={{
transition: "0.5s",
}}
loaderPosition="center"
loading={isLoading ? true : false}
disabled={ disabled={
value.title === "" || value.title === "" ||
value.content === "" || value.content === "" ||
@@ -267,10 +274,15 @@ function ButtonAction({ value, file }: { value: MODEL_JOB; file: FormData }) {
? true ? true
: false : false
} }
style={{
transition: "0.5s",
}}
loaderPosition="center"
loading={isLoading ? true : false}
w={"100%"} w={"100%"}
radius={"xl"} radius={"xl"}
onClick={() => { onClick={() => {
onAction(); onCreate();
}} }}
> >
Simpan Simpan

View File

@@ -55,11 +55,22 @@ export async function Job_funCreate(req: MODEL_JOB, file: FormData) {
deskripsi: req.deskripsi, deskripsi: req.deskripsi,
authorId: authorId, authorId: authorId,
}, },
select: {
id: true,
authorId: true,
MasterStatus: {
select: {
name: true,
},
},
title: true,
},
}); });
if (!create) return { status: 400, message: "Gagal Disimpan" }; if (!create) return { status: 400, message: "Gagal Disimpan" };
revalidatePath("/dev/job/main/status"); revalidatePath("/dev/job/main/status");
return { return {
data: create,
status: 201, status: 201,
message: "Berhasil Disimpan", message: "Berhasil Disimpan",
}; };

View File

@@ -0,0 +1,35 @@
"use server";
import prisma from "@/app/lib/prisma";
import { MODEL_NOTIFIKASI } from "../../model/interface";
export default async function notifikasi_funCreate({
data,
}: {
data: MODEL_NOTIFIKASI;
}) {
const getAdmin = await prisma.user.findMany({
where: {
active: true,
masterUserRoleId: "2",
},
});
for (let a of getAdmin) {
const create = await prisma.notifikasi.create({
data: {
adminId: a.id,
userId: data.userId,
appId: data.appId,
status: data.status,
title: data.title,
pesan: data.pesan,
kategoriApp: data.kategoriApp,
userRoleId: "2",
},
});
if (!create) return { status: 400, message: "Gagal mengirim notifikasi" };
}
return { status: 201, message: "Berhasil mengirim notifikasi" };
}

View File

@@ -0,0 +1,9 @@
"use server"
import { user_getOneUserId } from "@/app_modules/fun_global/get_user_token"
export default async function notifikasi_getByUserId() {
const userId = await user_getOneUserId()
}

View File

@@ -0,0 +1 @@
// test notif

View File

@@ -0,0 +1,20 @@
import { MODEL_USER } from "@/app_modules/home/model/interface";
import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface";
export interface MODEL_NOTIFIKASI {
id: string;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
appId: string;
kategoriApp: string;
isRead: boolean;
title: string,
pesan: string;
User: MODEL_USER;
userId: string;
Admin: MODEL_USER;
adminId: string;
status?: string;
Role: MODEL_NEW_DEFAULT_MASTER;
userRoleId: String;
}

View File

@@ -2,11 +2,21 @@
{ {
"name": "banuna", "name": "banuna",
"nomor": "6282340374412", "nomor": "6282340374412",
"masterUserRoleId": "3" "masterUserRoleId": "2"
}, },
{ {
"name": "firman", "name": "firman",
"nomor": "6281339158911", "nomor": "6281339158911",
"masterUserRoleId": "2"
},
{
"name": "amalia",
"nomor": "628980185458",
"masterUserRoleId": "1"
},
{
"name": "lukman",
"nomor": "6287701790942",
"masterUserRoleId": "1" "masterUserRoleId": "1"
} }
] ]

View File

@@ -10,51 +10,17 @@ import { Button, Stack } from "@mantine/core";
export default function MqttLoader() { export default function MqttLoader() {
useEffect(() => { useEffect(() => {
mqtt_client.on("connect", () => { mqtt_client.on("connect", () => {
try { console.log("connected");
console.log("connected");
} catch (error) {
console.log(error)
};
mqtt_client.subscribe("pesan");
mqtt_client.subscribe("pesan2");
// fetch("").then((res) => {
// mqtt_client.subscribe("pesan");
// });
});
mqtt_client.on("message", (topic: any, message: any) => {
// console.log(itu)
// evnPesan.emit("pesan", itu);
const data = JSON.parse(message.toString());
if (data) {
if (data.id === "1") {
console.log("ini untuk id satu", data.data);
}
}
}); });
}, []); }, []);
const onClick = async () => { return null;
mqtt_client.publish("pesan2", "apa pesannya 2");
};
const onClick2 = () => { // <>
mqtt_client.publish( // <Stack>
"pesan", // <Button onClick={onClick}>Tekan</Button>
JSON.stringify({ // <Button onClick={onClick2}>Tekan 2</Button>
id: "2", // </Stack>
title: "donasi", // </>
data: "databta",
})
);
};
return null
// (
// <Stack>
// <Button onClick={onClick}>Tekan</Button>
// <Button onClick={onClick2}>Tekan 2</Button>
// </Stack>
// ); // );
} }