fix admin

deskripsi:
- ui layout admin
- navbar & header responsive
This commit is contained in:
2025-04-10 17:42:31 +08:00
parent 47a3f4bb81
commit 94e202db8d
4 changed files with 216 additions and 169 deletions

View File

@@ -14,7 +14,7 @@ import {
SimpleGrid, SimpleGrid,
Stack, Stack,
Text, Text,
Title Title,
} from "@mantine/core"; } from "@mantine/core";
import { import {
IconBell, IconBell,
@@ -22,7 +22,7 @@ import {
IconLogout, IconLogout,
IconReplaceUser, IconReplaceUser,
IconUser, IconUser,
IconUserCircle IconUserCircle,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
@@ -30,12 +30,24 @@ import { Admin_ComponentModal } from "../_admin_global/_component/comp_admin_mod
export function Admin_V3_ComponentButtonUserCircle({ export function Admin_V3_ComponentButtonUserCircle({
dataUser, dataUser,
openPop,
setOpenPop,
setNavbarOpen,
setDrawerNotifikasi,
}: { }: {
dataUser: MODEL_USER | null; dataUser: MODEL_USER | null;
openPop: boolean;
setOpenPop: React.Dispatch<React.SetStateAction<boolean>>;
// setOpenPop: (open: boolean) => void;
setNavbarOpen: React.Dispatch<React.SetStateAction<boolean>>;
// setNavbarOpen: (open: boolean) => void;
setDrawerNotifikasi: React.Dispatch<React.SetStateAction<boolean>>;
// setDrawerNotifikasi: (open: boolean) => void;
}) { }) {
const router = useRouter(); const router = useRouter();
const [isOpenMenuUser, setOpenMenuUser] = useState(false); const [isOpenMenuUser, setOpenMenuUser] = useState(false);
const [openPop, setOpenPop] = useState(false); // const [openPop, setOpenPop] = useState(false);
const [openModalLogout, setOpenModalLogout] = useState(false); const [openModalLogout, setOpenModalLogout] = useState(false);
const [openModalReplaceUser, setOpenModalReplaceUser] = useState(false); const [openModalReplaceUser, setOpenModalReplaceUser] = useState(false);
const [loadingLogout, setLoadingLogout] = useState(false); const [loadingLogout, setLoadingLogout] = useState(false);
@@ -62,7 +74,7 @@ export function Admin_V3_ComponentButtonUserCircle({
icon: IconBell, icon: IconBell,
label: "Notifikasi", label: "Notifikasi",
color: "", color: "",
onClick: () => console.log("Notifikasi"), onClick: () => setDrawerNotifikasi(true),
}, },
{ {
icon: IconReplaceUser, icon: IconReplaceUser,
@@ -100,6 +112,7 @@ export function Admin_V3_ComponentButtonUserCircle({
variant="transparent" variant="transparent"
onClick={() => { onClick={() => {
setOpenPop((o) => !o); setOpenPop((o) => !o);
setNavbarOpen(false);
}} }}
> >
<IconUserCircle color={dataUser ? "white" : "gray"} /> <IconUserCircle color={dataUser ? "white" : "gray"} />
@@ -116,7 +129,7 @@ export function Admin_V3_ComponentButtonUserCircle({
{listMenu.map((e, i) => ( {listMenu.map((e, i) => (
<Group key={i}> <Group key={i}>
<e.icon size={18} /> <e.icon size={18} />
<Text lineClamp={1} >{e.label}</Text> <Text lineClamp={1}>{e.label}</Text>
</Group> </Group>
))} ))}
@@ -131,58 +144,10 @@ export function Admin_V3_ComponentButtonUserCircle({
</Center> </Center>
))} ))}
</SimpleGrid> </SimpleGrid>
{/* <SimpleGrid cols={2}>
<Button
radius={"xl"}
onClick={() => router.push("/dev/home", { scroll: false })}
>
User Access
</Button>
<Button
radius={"xl"}
color="red"
onClick={() => setOpenModal(true)}
>
Keluar
</Button>
</SimpleGrid> */}
</Stack> </Stack>
</Popover.Dropdown> </Popover.Dropdown>
</Popover> </Popover>
{/* <Modal
opened={openModal}
onClose={() => setOpenModal(false)}
centered
withCloseButton={false}
closeOnClickOutside={false}
>
<Stack>
<Title order={6}>Anda yakin ingin keluar ?</Title>
<Group align="center" position="center">
<Button
onClick={() => {
setOpenModal(false);
}}
radius={50}
>
Batal
</Button>
<Button
loaderPosition="center"
loading={loadingLogout ? true : false}
radius={50}
bg={Warna.merah}
color="red"
onClick={() => onClickLogout()}
>
Keluar
</Button>
</Group>
</Stack>
</Modal> */}
<Admin_ComponentModal <Admin_ComponentModal
opened={openModalLogout} opened={openModalLogout}
onClose={() => setOpenModalLogout(false)} onClose={() => setOpenModalLogout(false)}
@@ -205,7 +170,7 @@ export function Admin_V3_ComponentButtonUserCircle({
</Button> </Button>
<Button <Button
loaderPosition="center" loaderPosition="center"
loading={loadingLogout ? true : false} loading={loadingLogout}
radius={50} radius={50}
bg={Warna.merah} bg={Warna.merah}
color="red" color="red"
@@ -225,7 +190,7 @@ export function Admin_V3_ComponentButtonUserCircle({
> >
<Stack> <Stack>
<Title order={5} c={AccentColor.white}> <Title order={5} c={AccentColor.white}>
Anda yakin ingin keluar ? Anda yakin ingin pindah ke tampilan user ?
</Title> </Title>
<Group align="center" position="center"> <Group align="center" position="center">
<Button <Button
@@ -239,15 +204,16 @@ export function Admin_V3_ComponentButtonUserCircle({
</Button> </Button>
<Button <Button
loaderPosition="center" loaderPosition="center"
loading={loadingLogout ? true : false} loading={loadingReplaceUser}
radius={50} radius={50}
bg={AccentColor.softblue} bg={AccentColor.softblue}
color="blue" color="blue"
onClick={() => { onClick={() => {
router.push("/dev/home", { scroll: false }); router.push("/dev/home", { scroll: false });
setLoadingReplaceUser(true)
}} }}
> >
User Akses Ke Tampilan User
</Button> </Button>
</Group> </Group>
</Stack> </Stack>

View File

@@ -6,38 +6,38 @@ import { MODEL_USER } from "@/app_modules/home/model/interface";
import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface"; import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface";
import { gs_admin_ntf } from "@/lib/global_state"; import { gs_admin_ntf } from "@/lib/global_state";
import { import {
AppShell, AppShell,
Burger, Burger,
Divider, Divider,
Group, Drawer,
Header, Group,
MediaQuery, Header,
Navbar, MediaQuery,
ScrollArea, Navbar,
Stack, ScrollArea,
Text, Stack,
useMantineTheme Text,
useMantineTheme,
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure, useShallowEffect } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { import {
IconBriefcase, IconBriefcase,
IconCoin, IconCoin,
IconHome, IconHome,
IconMessage, IconMessage,
IconUser IconUser,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { usePathname, useRouter } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
import type React from "react"; import type React from "react";
import { useState } from "react"; import { useState } from "react";
import { Admin_UiNavbar } from "../_admin_global";
import { import {
Admin_UiNavbar gs_admin_navbar_menu,
} from "../_admin_global"; gs_admin_navbar_subMenu,
import {
gs_admin_navbar_menu,
gs_admin_navbar_subMenu,
} from "../_admin_global/new_global_state"; } from "../_admin_global/new_global_state";
import { Admin_V3_ComponentButtonUserCircle } from "./comp_button_user_circle"; import { Admin_V3_ComponentButtonUserCircle } from "./comp_button_user_circle";
import { Admin_V3_SkeletonNavbar } from "./skeleton_navbar";
export function Admin_V3_MainLayout({ export function Admin_V3_MainLayout({
children, children,
@@ -52,7 +52,6 @@ export function Admin_V3_MainLayout({
listNotifikasi: MODEL_NOTIFIKASI[]; listNotifikasi: MODEL_NOTIFIKASI[];
version: string; version: string;
}) { }) {
const router = useRouter();
const [dataUser, setDataUser] = useState<MODEL_USER | null>(null); const [dataUser, setDataUser] = useState<MODEL_USER | null>(null);
const userRoleId = dataUser?.masterUserRoleId; const userRoleId = dataUser?.masterUserRoleId;
const [activeId, setActiveId] = useAtom(gs_admin_navbar_menu); const [activeId, setActiveId] = useAtom(gs_admin_navbar_menu);
@@ -61,7 +60,6 @@ export function Admin_V3_MainLayout({
useState<MODEL_NOTIFIKASI[]>(listNotifikasi); useState<MODEL_NOTIFIKASI[]>(listNotifikasi);
// Notifikasi // Notifikasi
const [isDrawerNotifikasi, setDrawerNotifikasi] = useState(false);
const [countNtf, setCountNtf] = useState(countNotifikasi); const [countNtf, setCountNtf] = useState(countNotifikasi);
const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf); const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf);
@@ -82,54 +80,74 @@ export function Admin_V3_MainLayout({
console.error("Error fetching user data", error); console.error("Error fetching user data", error);
} }
} }
const [openPop, setOpenPop] = useState(false);
const [opened, handlers] = useDisclosure(false);
const [openedDrawer, handlersDrawer] = useDisclosure(false);
const pathname = usePathname();
const [opened, { toggle, close }] = useDisclosure(false); const navLinks = [
{ icon: IconHome, label: "Home", path: "/" },
{ icon: IconBriefcase, label: "Portfolio", path: "/portfolio" },
{ icon: IconUser, label: "About Me", path: "/about" },
{ icon: IconCoin, label: "Price List", path: "/pricing" },
{ icon: IconMessage, label: "Contact", path: "/contact" },
];
const isActive = (path: string) => {
if (path === "/" && pathname === "/") return true;
if (path !== "/" && pathname.startsWith(path)) return true;
return false;
};
return ( return (
<AppShell <>
bg={MainColor.darkblue} <AppShell
padding={"md"} bg={MainColor.darkblue}
navbarOffsetBreakpoint="md" padding={"md"}
navbar={ navbarOffsetBreakpoint="md"
<Navbar navbar={
p="md" <Navbar
hiddenBreakpoint="md" p="md"
hidden={!opened} hiddenBreakpoint="md"
width={{ base: 250 }} hidden={!opened}
bg={AccentColor.darkblue} width={{ base: 250 }}
style={{ borderColor: "transparent", transition: " ease 1s" }} bg={AccentColor.darkblue}
height={"93vh"} style={{ borderColor: "transparent", transition: " ease 1s" }}
> height={"93vh"}
<Navbar.Section
h={"88vh"}
grow
component={ScrollArea}
style={{ color: "white", transition: "1s" }}
> >
<Stack style={{ color: "white" }} mb={"lg"}> <Navbar.Section
<Admin_UiNavbar h={"88vh"}
userRoleId={userRoleId as any} grow
activeId={activeId as any} component={ScrollArea}
activeChildId={activeChildId as any} style={{ color: "white", transition: "1s" }}
setActiveId={setActiveId} >
setActiveChildId={setActiveChildId} <Stack style={{ color: "white" }} mb={"lg"}>
/> {!dataUser ? (
</Stack> <Admin_V3_SkeletonNavbar />
</Navbar.Section> ) : (
<Admin_UiNavbar
userRoleId={userRoleId as any}
activeId={activeId as any}
activeChildId={activeChildId as any}
setActiveId={setActiveId}
setActiveChildId={setActiveChildId}
/>
)}
</Stack>
</Navbar.Section>
<Navbar.Section h="5"> <Navbar.Section h="5">
<Stack> <Stack>
<Divider /> <Divider />
<Group position="center"> <Group position="center">
<Text fs={"italic"} c={"white"} fz={"xs"}> <Text fs={"italic"} c={"white"} fz={"xs"}>
V {version} V {version}
</Text> </Text>
</Group> </Group>
</Stack> </Stack>
</Navbar.Section> </Navbar.Section>
{/* <Box style={{ flex: "1" }}> {/* <Box style={{ flex: "1" }}>
<Stack spacing="xs"> <Stack spacing="xs">
{navLinks.map((link) => ( {navLinks.map((link) => (
<Anchor <Anchor
@@ -163,51 +181,85 @@ export function Admin_V3_MainLayout({
))} ))}
</Stack> </Stack>
</Box> */} </Box> */}
</Navbar>
}
header={
<Header height={"7vh"} px="md" bg={AccentColor.darkblue}>
<Group style={{ height: "100%" }} position="apart">
<MediaQuery largerThan="md" styles={{ display: "none" }}>
<Burger
disabled={!dataUser}
opened={opened}
onClick={handlers.toggle}
size="sm"
color={!dataUser ? "gray" : AccentColor.white}
/>
</MediaQuery>
{/* <Box mt="auto"> <Text size="lg" weight={700} c={MainColor.white}>
<Divider my="sm" /> HIMPI DASHBOARD
<Group position="center" mt="md"> </Text>
<ThemeIcon size={36} radius="xl" color="blue">
<IconBrandGithub size={18} /> <Admin_V3_ComponentButtonUserCircle
</ThemeIcon> dataUser={dataUser as any}
<ThemeIcon size={36} radius="xl" color="blue"> openPop={openPop}
<IconBrandLinkedin size={18} /> setOpenPop={setOpenPop}
</ThemeIcon> setNavbarOpen={handlers.close}
<ThemeIcon size={36} radius="xl" color="blue"> setDrawerNotifikasi={handlersDrawer.toggle}
<IconMail size={18} />
</ThemeIcon>
</Group>
</Box> */}
</Navbar>
}
header={
<Header
height={"7vh"}
px="md"
bg={AccentColor.darkblue}
// style={{ border: "none" }}
>
<Group style={{ height: "100%" }} position="apart">
<MediaQuery largerThan="md" styles={{ display: "none" }}>
<Burger
disabled={!dataUser}
opened={opened}
onClick={toggle}
size="sm"
color={!dataUser ? "gray" : AccentColor.white}
/> />
</MediaQuery> </Group>
</Header>
}
>
{children}
</AppShell>
<Text size="lg" weight={700} c={MainColor.white}> <Drawer
HIMPI DASHBOARD styles={{
content: {
backgroundColor: AccentColor.blue,
color: AccentColor.white,
},
header: {
backgroundColor: AccentColor.darkblue,
color: AccentColor.white,
},
close: {
color: AccentColor.white,
"&:hover": {
backgroundColor: AccentColor.blue,
color: AccentColor.white,
},
},
}}
title={
<Group position="apart">
<Text fw={"bold"} fz={"lg"}>
Notifikasi
</Text> </Text>
<Admin_V3_ComponentButtonUserCircle dataUser={dataUser as any} />
</Group> </Group>
</Header> }
} opened={openedDrawer}
> onClose={handlersDrawer.toggle}
{children} position="right"
</AppShell> size={"sm"}
>
On Maintenance . . .
{/* <ComponentAdmin_UIDrawerNotifikasi
newAdminNtf={newAdminNtf}
listNotifikasi={dataNotifikasi}
onChangeNavbar={(val: { id: string; childId: string }) => {
setActiveId(val.id as any);
setActiveChildId(val.childId);
}}
onToggleNavbar={(val: any) => {
setDrawerNotifikasi(val);
}}
onLoadCountNotif={(val: any) => {
setCountNtf(val);
}}
/> */}
</Drawer>
</>
); );
} }

View File

@@ -0,0 +1,32 @@
import { Box, NavLink, Text } from "@mantine/core";
import _ from "lodash";
import { newListAdminPage } from "../new_list_page";
export function Admin_V3_SkeletonNavbar() {
const listPage = newListAdminPage.slice(0, -1);
return (
<>
{listPage.map((parent) => (
<Box key={parent.id}>
<NavLink
disabled
style={{
color: "gray",
transition: "0.5s",
}}
sx={{
":hover": {
backgroundColor: "transparent",
},
}}
label={<Text>{parent.name}</Text>}
icon={parent.icon}
>
{!_.isEmpty(parent.child) &&
parent.child.map((child) => <Box key={child.id} />)}
</NavLink>
</Box>
))}
</>
);
}

View File

@@ -16,18 +16,15 @@ export default function AdminMain() {
const [countUser, setCountUser] = useState<number | null>(null); const [countUser, setCountUser] = useState<number | null>(null);
const [countPortofolio, setCountPortofolio] = useState<number | null>(null); const [countPortofolio, setCountPortofolio] = useState<number | null>(null);
// useShallowEffect(() => { useShallowEffect(() => {
// onLoadDataUser(); onLoadDataUser();
// onLoadDataPortofolio(); onLoadDataPortofolio();
// }, []); }, []);
async function onLoadDataUser() { async function onLoadDataUser() {
try { try {
const response = await apiGetCountUserActive(); const response = await apiGetCountUserActive();
if (response) { if (response) {
// console.log(response.data);
// console.log(typeof response.data);
// console.log( response);
setCountUser(response.data); setCountUser(response.data);
} }