Notifikasi
# feat: - Notifikasi di bagian admin ## No issue
This commit is contained in:
@@ -43,15 +43,18 @@ model User {
|
||||
ProjectCollaboration_Message ProjectCollaboration_Message[]
|
||||
AdminProjectCollaboration_Notifikasi ProjectCollaboration_Notifikasi[] @relation("AdminNotifProjectToUser")
|
||||
UserProjectCollaboration_Notifikasi ProjectCollaboration_Notifikasi[] @relation("UserNotifProjectToUser")
|
||||
Admin_Notifikasi Notifikasi[] @relation("AdminNotifikasi")
|
||||
User_Notifikasi Notifikasi[] @relation("UserNotifikasi")
|
||||
}
|
||||
|
||||
model MasterUserRole {
|
||||
id String @id
|
||||
name String
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
User User[]
|
||||
id String @id
|
||||
name String
|
||||
active Boolean @default(true)
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
User User[]
|
||||
Notifikasi Notifikasi[]
|
||||
}
|
||||
|
||||
model UserSession {
|
||||
@@ -847,3 +850,23 @@ model NomorAdmin {
|
||||
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 |
BIN
public/profile/foto/627e7971-d74e-4d98-b69f-40b227f4e16a.jpeg
Normal file
BIN
public/profile/foto/627e7971-d74e-4d98-b69f-40b227f4e16a.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
BIN
public/profile/foto/9319cabd-e485-44ea-b186-7c08616c00fe.jpeg
Normal file
BIN
public/profile/foto/9319cabd-e485-44ea-b186-7c08616c00fe.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -1,4 +1,6 @@
|
||||
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_getOneByUserId } from "@/app_modules/home/fun/get/get_one_user_by_id";
|
||||
import React from "react";
|
||||
@@ -8,13 +10,20 @@ export default async function Layout({
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const userId = await user_getOneUserId()
|
||||
const dataUser = await user_getOneByUserId(userId)
|
||||
const userRole = dataUser?.masterUserRoleId
|
||||
const userId = await user_getOneUserId();
|
||||
const dataUser = await user_getOneByUserId(userId);
|
||||
const listNotif = await adminNotifikasi_getByUserId();
|
||||
const countNotifikasi = await adminNotifikasi_countNotifikasi();
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdminLayout userRole={userRole as any}>{children}</AdminLayout>
|
||||
<AdminLayout
|
||||
listNotif={listNotif as any}
|
||||
dataUser={dataUser as any}
|
||||
countNotifikasi={countNotifikasi}
|
||||
>
|
||||
{children}
|
||||
</AdminLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
9
src/app/dev/notifikasi/page.tsx
Normal file
9
src/app/dev/notifikasi/page.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Notifikasi_MainView } from "@/app_modules/admin/notifikasi";
|
||||
|
||||
export default async function Page() {
|
||||
return (
|
||||
<>
|
||||
<Notifikasi_MainView />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
export const RouterAdminJob = {
|
||||
main: "/dev/admin/job/main",
|
||||
table_publish: "/dev/admin/job/child/table_publish",
|
||||
table_review: "/dev/admin/job/child/table_review",
|
||||
table_reject: "/dev/admin/job/child/table_reject",
|
||||
publish: "/dev/admin/job/child/publish",
|
||||
review: "/dev/admin/job/child/review",
|
||||
reject: "/dev/admin/job/child/reject",
|
||||
arsip: "/dev/admin/job/child/arsip",
|
||||
};
|
||||
|
||||
3
src/app/lib/router_hipmi/router_notifikasi.ts
Normal file
3
src/app/lib/router_hipmi/router_notifikasi.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const RouterNotifikasi = {
|
||||
main: "/dev/notifikasi",
|
||||
};
|
||||
@@ -5,6 +5,14 @@ import { atomWithStorage } from "jotai/utils";
|
||||
* @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
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AdminJob_Main from "./main";
|
||||
import AdminJob_TablePublish from "./child/table_publish";
|
||||
import AdminJob_TableReview from "./child/table_review";
|
||||
import AdminJob_TableReject from "./child/table_reject";
|
||||
import AdminJob_TablePublish from "./child/publish";
|
||||
import AdminJob_TableReview from "./child/review";
|
||||
import AdminJob_TableReject from "./child/reject";
|
||||
|
||||
export { AdminJob_Main, AdminJob_TablePublish, AdminJob_TableReview, AdminJob_TableReject };
|
||||
|
||||
@@ -3,69 +3,90 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
AppShell,
|
||||
Badge,
|
||||
Box,
|
||||
Burger,
|
||||
Button,
|
||||
Card,
|
||||
Center,
|
||||
Divider,
|
||||
Drawer,
|
||||
Group,
|
||||
Header,
|
||||
Indicator,
|
||||
MediaQuery,
|
||||
NavLink,
|
||||
Navbar,
|
||||
Paper,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
useMantineTheme
|
||||
useMantineTheme,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
IconBell,
|
||||
IconCheck,
|
||||
IconChecks,
|
||||
IconCircleDot,
|
||||
IconCircleDotFilled,
|
||||
IconDashboard
|
||||
IconDashboard,
|
||||
IconUserSquareRounded,
|
||||
} from "@tabler/icons-react";
|
||||
import { useAtom } from "jotai";
|
||||
import _ from "lodash";
|
||||
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 { gs_kodeId } from "../auth/state/state";
|
||||
import { ComponentGlobal_NotifikasiBerhasil } from "../component_global/notif_global/notifikasi_berhasil";
|
||||
import { ComponentGlobal_NotifikasiPeringatan } from "../component_global/notif_global/notifikasi_peringatan";
|
||||
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 { 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({
|
||||
userRole,
|
||||
children,
|
||||
listNotif,
|
||||
dataUser,
|
||||
countNotifikasi,
|
||||
}: {
|
||||
userRole: string;
|
||||
children: React.ReactNode;
|
||||
listNotif: MODEL_NOTIFIKASI[];
|
||||
dataUser: MODEL_USER;
|
||||
countNotifikasi: number;
|
||||
}) {
|
||||
const theme = useMantineTheme();
|
||||
const [opened, setOpened] = useState(false);
|
||||
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 [loading, setLoading] = useState(false);
|
||||
const [kodeId, setKodeId] = useAtom(gs_kodeId);
|
||||
|
||||
async function onClickLogout() {
|
||||
// await auth_Logout(kodeId).then((res) => {
|
||||
// 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 [user, setUser] = useState(dataUser);
|
||||
const userRoleId = user.masterUserRoleId;
|
||||
|
||||
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}>
|
||||
<NavLink
|
||||
sx={{
|
||||
@@ -73,7 +94,7 @@ export default function AdminLayout({
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
}}
|
||||
fw={active === e.id ? "bold" : "normal"}
|
||||
fw={activeId === e.id ? "bold" : "normal"}
|
||||
icon={
|
||||
// active === e.id ? loading ? <Loader size={10} /> : e.icon : e.icon
|
||||
e.icon
|
||||
@@ -81,7 +102,7 @@ export default function AdminLayout({
|
||||
label={<Text size={"sm"}>{e.name}</Text>}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
setActive(e.id);
|
||||
setActiveId(e.id);
|
||||
setActiveChild(null);
|
||||
e.path === "" ? router.push(e.child[0].path) : router.push(e.path);
|
||||
e.path === "" ? setActiveChild(e.child[0].id) : "";
|
||||
@@ -109,7 +130,7 @@ export default function AdminLayout({
|
||||
)
|
||||
}
|
||||
onClick={() => {
|
||||
setActive(e.id);
|
||||
setActiveId(e.id);
|
||||
setActiveChild(v.id);
|
||||
router.push(v.path);
|
||||
}}
|
||||
@@ -123,26 +144,26 @@ export default function AdminLayout({
|
||||
));
|
||||
|
||||
const bukanDeveloper = listAdminPage.slice(0, -1);
|
||||
const notAdminDev = bukanDeveloper.map((e) => (
|
||||
const adminNavbar = bukanDeveloper.map((e) => (
|
||||
<Box key={e.id}>
|
||||
<NavLink
|
||||
opened={e?.id === activeId && isNavbarOpen ? true : false}
|
||||
sx={{
|
||||
":hover": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
}}
|
||||
fw={active === e.id ? "bold" : "normal"}
|
||||
icon={
|
||||
// active === e.id ? loading ? <Loader size={10} /> : e.icon : e.icon
|
||||
e.icon
|
||||
}
|
||||
fw={activeId === e.id ? "bold" : "normal"}
|
||||
icon={e.icon}
|
||||
label={<Text size={"sm"}>{e.name}</Text>}
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
setActive(e.id);
|
||||
setActiveId(e.id);
|
||||
setActiveChild(null);
|
||||
e.path === "" ? router.push(e.child[0].path) : router.push(e.path);
|
||||
e.path === "" ? setActiveChild(e.child[0].id) : "";
|
||||
|
||||
setIsNavbarOpen(true);
|
||||
}}
|
||||
>
|
||||
{_.isEmpty(e.child) ? (
|
||||
@@ -167,7 +188,7 @@ export default function AdminLayout({
|
||||
)
|
||||
}
|
||||
onClick={() => {
|
||||
setActive(e.id);
|
||||
setActiveId(e.id);
|
||||
setActiveChild(v.id);
|
||||
router.push(v.path);
|
||||
}}
|
||||
@@ -180,20 +201,20 @@ export default function AdminLayout({
|
||||
</Box>
|
||||
));
|
||||
|
||||
const navbarAdmin = (
|
||||
<Box>
|
||||
<NavLink
|
||||
c="orange"
|
||||
icon={<IconDashboard />}
|
||||
label="Developer"
|
||||
sx={{
|
||||
":hover": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
async function onLoadNotifikasi() {
|
||||
const loadNotif = await adminNotifikasi_getByUserId();
|
||||
setDataNotif(loadNotif as any);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
mqtt_client.subscribe("ADMIN");
|
||||
|
||||
mqtt_client.on("message", (topic: any, message: any) => {
|
||||
const data = JSON.parse(message.toString());
|
||||
// console.log(data);
|
||||
setCountNotif(countNotif + data.count);
|
||||
});
|
||||
}, [countNotif]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -201,59 +222,34 @@ export default function AdminLayout({
|
||||
padding="md"
|
||||
navbarOffsetBreakpoint="md"
|
||||
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 height={"5vh"} bg={"gray.2"}>
|
||||
<Header height={"6vh"} bg={"gray.2"}>
|
||||
{/* Web View */}
|
||||
<MediaQuery smallerThan={"md"} styles={{ display: "none" }}>
|
||||
<Group position="apart" align="center" h={"100%"} px={"md"}>
|
||||
<Text fw={"lighter"}>Dashboard Admin</Text>
|
||||
<Title order={4}> HIPMI</Title>
|
||||
{/* <Group>
|
||||
{listAdminPage.map((e) => (
|
||||
<Text key={e.id} onClick={() => router.push(e.route)}>
|
||||
{e.name}
|
||||
</Text>
|
||||
))}
|
||||
</Group> */}
|
||||
{/* <Admin_Logout /> */}
|
||||
<ActionIcon radius={"xl"}>
|
||||
<IconBell />
|
||||
</ActionIcon>
|
||||
<Title order={3}>Dashboard Admin</Title>
|
||||
|
||||
<Group>
|
||||
<ActionIcon
|
||||
radius={"xl"}
|
||||
onClick={() => {
|
||||
setIsNotif(true);
|
||||
onLoadNotifikasi();
|
||||
}}
|
||||
>
|
||||
<Indicator
|
||||
processing
|
||||
label={<Text fz={10}>{countNotif}</Text>}
|
||||
>
|
||||
<IconBell />
|
||||
</Indicator>
|
||||
</ActionIcon>
|
||||
<Divider orientation="vertical" color="dark" />
|
||||
<Group>
|
||||
<Text>{user?.username}</Text>
|
||||
<IconUserSquareRounded />
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</MediaQuery>
|
||||
|
||||
@@ -276,10 +272,39 @@ export default function AdminLayout({
|
||||
</MediaQuery>
|
||||
</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}
|
||||
</AppShell>
|
||||
{/* Drawer Mobile View */}
|
||||
<Drawer opened={opened} onClose={() => setOpened(false)} size={"50%"}>
|
||||
<Stack spacing={"xl"}>
|
||||
{listAdminPage.map((e) => (
|
||||
@@ -289,6 +314,183 @@ export default function AdminLayout({
|
||||
))}
|
||||
</Stack>
|
||||
</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);
|
||||
}
|
||||
|
||||
@@ -194,17 +194,17 @@ export const listAdminPage = [
|
||||
{
|
||||
id: 62,
|
||||
name: "Table Publish",
|
||||
path: RouterAdminJob.table_publish,
|
||||
path: RouterAdminJob.publish,
|
||||
},
|
||||
{
|
||||
id: 63,
|
||||
name: "Table Review",
|
||||
path: RouterAdminJob.table_review,
|
||||
path: RouterAdminJob.review,
|
||||
},
|
||||
{
|
||||
id: 64,
|
||||
name: "Table Reject",
|
||||
path: RouterAdminJob.table_reject,
|
||||
path: RouterAdminJob.reject,
|
||||
},
|
||||
{
|
||||
id: 65,
|
||||
|
||||
17
src/app_modules/admin/notifikasi/fun/count/count_is_read.ts
Normal file
17
src/app_modules/admin/notifikasi/fun/count/count_is_read.ts
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 };
|
||||
}
|
||||
5
src/app_modules/admin/notifikasi/index.ts
Normal file
5
src/app_modules/admin/notifikasi/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// test notif
|
||||
|
||||
import Notifikasi_MainView from "./main";
|
||||
|
||||
export { Notifikasi_MainView };
|
||||
25
src/app_modules/admin/notifikasi/main/index.tsx
Normal file
25
src/app_modules/admin/notifikasi/main/index.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -12,7 +12,6 @@ export async function user_getOneUserId() {
|
||||
const c = cookies().get("ssn");
|
||||
if (!c?.value || c.value === "") return redirect(RouterAuth.login);
|
||||
|
||||
|
||||
const token = JSON.parse(
|
||||
await unsealData(c?.value as string, {
|
||||
password: config.server.password,
|
||||
|
||||
1
src/app_modules/global_state/index.ts
Normal file
1
src/app_modules/global_state/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// index
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
IconAward,
|
||||
IconQrcode,
|
||||
IconUserCircle,
|
||||
IconBell,
|
||||
} from "@tabler/icons-react";
|
||||
import { Logout } from "../auth";
|
||||
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_NotifikasiBerhasil } from "../component_global/notif_global/notifikasi_berhasil";
|
||||
import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search";
|
||||
import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi";
|
||||
|
||||
export default function HomeLayout({
|
||||
dataUser,
|
||||
@@ -157,11 +159,22 @@ export default function HomeLayout({
|
||||
top={0}
|
||||
h={50}
|
||||
>
|
||||
<Center h={"100%"}>
|
||||
<Title order={4} c={"white"}>
|
||||
HIPMI
|
||||
</Title>
|
||||
</Center>
|
||||
<Group position="apart" h={"100%"} px={"md"}>
|
||||
<ActionIcon variant="transparent" disabled></ActionIcon>
|
||||
<Center>
|
||||
<Title order={4} c={"white"}>
|
||||
HIPMI
|
||||
</Title>
|
||||
</Center>
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
onClick={() => {
|
||||
router.push(RouterNotifikasi.main);
|
||||
}}
|
||||
>
|
||||
<IconBell />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Box>
|
||||
|
||||
{/* Children */}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { RouterJob } from "@/app/lib/router_hipmi/router_job";
|
||||
import {
|
||||
AspectRatio,
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
FileButton,
|
||||
Flex,
|
||||
Group,
|
||||
Image,
|
||||
Loader,
|
||||
@@ -15,18 +11,15 @@ import {
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Textarea,
|
||||
} from "@mantine/core";
|
||||
import { IconCamera, IconUpload } from "@tabler/icons-react";
|
||||
import { useAtom } from "jotai";
|
||||
import _ from "lodash";
|
||||
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 { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/component_global/notif_global/notifikasi_berhasil";
|
||||
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
import dynamic from "next/dynamic";
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
const ReactQuill = dynamic(
|
||||
() => {
|
||||
return import("react-quill");
|
||||
@@ -34,16 +27,20 @@ const ReactQuill = dynamic(
|
||||
{ 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_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() {
|
||||
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 [status, setStatus] = useAtom(gs_job_status);
|
||||
const [preview, setPreview] = useToggle();
|
||||
|
||||
async function onAction() {
|
||||
async function onCreate() {
|
||||
const gambar = new FormData();
|
||||
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);
|
||||
setStatus("Review");
|
||||
router.replace(RouterJob.status);
|
||||
setIsLoading(true);
|
||||
ComponentGlobal_NotifikasiBerhasil("Tambah Lowongan Berhasil");
|
||||
} else {
|
||||
ComponentGlobal_NotifikasiGagal(res.message);
|
||||
ComponentGlobal_NotifikasiBerhasil(create.message);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ComponentGlobal_NotifikasiGagal(create.message);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -251,11 +263,6 @@ function ButtonAction({ value, file }: { value: MODEL_JOB; file: FormData }) {
|
||||
<Stack>
|
||||
<Group grow mt={"lg"} mb={70}>
|
||||
<Button
|
||||
style={{
|
||||
transition: "0.5s",
|
||||
}}
|
||||
loaderPosition="center"
|
||||
loading={isLoading ? true : false}
|
||||
disabled={
|
||||
value.title === "" ||
|
||||
value.content === "" ||
|
||||
@@ -267,10 +274,15 @@ function ButtonAction({ value, file }: { value: MODEL_JOB; file: FormData }) {
|
||||
? true
|
||||
: false
|
||||
}
|
||||
style={{
|
||||
transition: "0.5s",
|
||||
}}
|
||||
loaderPosition="center"
|
||||
loading={isLoading ? true : false}
|
||||
w={"100%"}
|
||||
radius={"xl"}
|
||||
onClick={() => {
|
||||
onAction();
|
||||
onCreate();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
|
||||
@@ -55,11 +55,22 @@ export async function Job_funCreate(req: MODEL_JOB, file: FormData) {
|
||||
deskripsi: req.deskripsi,
|
||||
authorId: authorId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
authorId: true,
|
||||
MasterStatus: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
title: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!create) return { status: 400, message: "Gagal Disimpan" };
|
||||
revalidatePath("/dev/job/main/status");
|
||||
return {
|
||||
data: create,
|
||||
status: 201,
|
||||
message: "Berhasil Disimpan",
|
||||
};
|
||||
|
||||
35
src/app_modules/notifikasi/fun/create/create_notif.tsx
Normal file
35
src/app_modules/notifikasi/fun/create/create_notif.tsx
Normal 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" };
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
|
||||
}
|
||||
1
src/app_modules/notifikasi/index.ts
Normal file
1
src/app_modules/notifikasi/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// test notif
|
||||
20
src/app_modules/notifikasi/model/interface.ts
Normal file
20
src/app_modules/notifikasi/model/interface.ts
Normal 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;
|
||||
}
|
||||
@@ -2,11 +2,21 @@
|
||||
{
|
||||
"name": "banuna",
|
||||
"nomor": "6282340374412",
|
||||
"masterUserRoleId": "3"
|
||||
"masterUserRoleId": "2"
|
||||
},
|
||||
{
|
||||
"name": "firman",
|
||||
"nomor": "6281339158911",
|
||||
"masterUserRoleId": "2"
|
||||
},
|
||||
{
|
||||
"name": "amalia",
|
||||
"nomor": "628980185458",
|
||||
"masterUserRoleId": "1"
|
||||
},
|
||||
{
|
||||
"name": "lukman",
|
||||
"nomor": "6287701790942",
|
||||
"masterUserRoleId": "1"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -10,51 +10,17 @@ import { Button, Stack } from "@mantine/core";
|
||||
export default function MqttLoader() {
|
||||
useEffect(() => {
|
||||
mqtt_client.on("connect", () => {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
console.log("connected");
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onClick = async () => {
|
||||
mqtt_client.publish("pesan2", "apa pesannya 2");
|
||||
};
|
||||
return null;
|
||||
|
||||
const onClick2 = () => {
|
||||
mqtt_client.publish(
|
||||
"pesan",
|
||||
JSON.stringify({
|
||||
id: "2",
|
||||
title: "donasi",
|
||||
data: "databta",
|
||||
})
|
||||
);
|
||||
};
|
||||
return null
|
||||
// (
|
||||
// <Stack>
|
||||
// <Button onClick={onClick}>Tekan</Button>
|
||||
// <Button onClick={onClick2}>Tekan 2</Button>
|
||||
// </Stack>
|
||||
// <>
|
||||
// <Stack>
|
||||
// <Button onClick={onClick}>Tekan</Button>
|
||||
// <Button onClick={onClick2}>Tekan 2</Button>
|
||||
// </Stack>
|
||||
// </>
|
||||
// );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user