'use client'
import { DarkModeToggle } from "@/components/admin/DarkModeToggle";
import { useDarkMode } from "@/state/darkModeStore";
import { authStore } from "@/store/authStore";
import { themeTokens } from "@/utils/themeTokens";
import {
ActionIcon,
AppShell,
AppShellHeader,
AppShellMain,
AppShellNavbar,
Burger,
Center,
Flex,
Group,
Image,
Loader,
NavLink,
ScrollArea,
Text,
Tooltip,
rem
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import {
IconChevronLeft,
IconChevronRight,
IconLogout2
} from "@tabler/icons-react";
import _ from "lodash";
import Link from "next/link";
import { useRouter, useSelectedLayoutSegments } from "next/navigation";
import { useEffect, useState } from "react";
import { getNavbar } from "./(dashboard)/user&role/_com/dynamicNavbar";
export default function Layout({ children }: { children: React.ReactNode }) {
const { isDark } = useDarkMode();
const tokens = themeTokens(isDark);
const [mounted, setMounted] = useState(false);
const [opened, { toggle, close }] = useDisclosure();
const [loading, setLoading] = useState(true);
const [isLoggingOut, setIsLoggingOut] = useState(false);
const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true);
const router = useRouter();
const segments = useSelectedLayoutSegments().map((s) => _.lowerCase(s));
// Ensure component is mounted on client side
useEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
const fetchUser = async () => {
try {
const res = await fetch('/api/auth/me', {
credentials: 'include'
});
const data = await res.json();
if (data.user) {
if (!data.user.isActive) {
authStore.setUser(null);
router.replace('/waiting-room');
return;
}
const menuRes = await fetch(`/api/admin/user-menu-access?userId=${data.user.id}`, {
credentials: 'include'
});
const menuData = await menuRes.json();
const menuIds = menuData.success && Array.isArray(menuData.menuIds)
? [...menuData.menuIds]
: null;
authStore.setUser({
id: data.user.id,
name: data.user.name,
roleId: Number(data.user.roleId),
menuIds,
isActive: data.user.isActive
});
const currentPath = window.location.pathname;
if (currentPath === '/admin') {
const expectedPath = getRedirectPath(Number(data.user.roleId));
console.log('🔄 Redirecting from /admin to:', expectedPath);
router.replace(expectedPath);
}
} else {
authStore.setUser(null);
router.replace('/login');
}
} catch (error) {
console.error('Gagal memuat data pengguna:', error);
authStore.setUser(null);
router.replace('/login');
} finally {
setLoading(false);
}
};
fetchUser();
}, [router]);
const getRedirectPath = (roleId: number): string => {
switch (roleId) {
case 0:
case 1:
case 2:
return '/admin/landing-page/profil/program-inovasi';
case 3:
return '/admin/kesehatan/posyandu';
case 4:
return '/admin/pendidikan/info-sekolah/jenjang-pendidikan';
default:
return '/admin';
}
};
if (loading || !mounted) {
return (
);
}
const currentNav = authStore.user
? getNavbar({ roleId: authStore.user.roleId, menuIds: authStore.user.menuIds })
: [];
const handleLogout = async () => {
try {
setIsLoggingOut(true);
const response = await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'include'
});
const result = await response.json();
if (result.success) {
authStore.setUser(null);
localStorage.removeItem('auth_nomor');
localStorage.removeItem('auth_kodeId');
localStorage.removeItem('auth_username');
window.location.href = '/login';
} else {
console.error('Logout failed:', result.message);
authStore.setUser(null);
window.location.href = '/login';
}
} catch (error) {
console.error('Error during logout:', error);
authStore.setUser(null);
window.location.href = '/login';
} finally {
setIsLoggingOut(false);
}
};
const handleNavClick = (path: string) => {
router.push(path);
close();
};
return (
{/*
HEADER / TOPBAR
Spec: Background gradient, border bawah wajib
*/}
Admin Darmasaba
{/* Dark Mode Toggle */}
{!desktopOpened && (
)}
router.push("/darmasaba")}
color={mounted ? tokens.colors.primary : '#3B82F6'}
radius="xl"
size="lg"
variant="gradient"
gradient={mounted ? tokens.colors.gradient : { from: '#3B82F6', to: '#60A5FA' }}
>
{/*
SIDEBAR / NAVBAR
Spec: Background --bg-app, active state dengan accent bar
*/}
{currentNav.map((v, k) => {
const isParentActive = segments.includes(_.lowerCase(v.name));
return (
{v.name}
}
style={{
borderRadius: rem(10),
marginBottom: rem(4),
transition: "background 150ms ease",
...(mounted && isParentActive && !isDark && {
borderLeft: `3px solid ${tokens.colors.primary}`,
}),
}}
styles={{
root: {
'&:hover': {
backgroundColor: mounted && isDark ? '#1E293B' : tokens.colors.bg.hover,
},
...(mounted && isParentActive && isDark && {
backgroundColor: 'rgba(59,130,246,0.25)',
borderLeft: `3px solid ${tokens.colors.primary}`,
}),
}
}}
variant="light"
active={isParentActive}
onClick={(e) => {
e.preventDefault();
if (v.path) handleNavClick(v.path);
}}
href={v.path || undefined}
>
{v.children?.map((child, key) => {
const isChildActive = segments.includes(_.lowerCase(child.name));
return (
{
e.preventDefault();
handleNavClick(child.path);
}}
href={child.path}
c={mounted && isChildActive ? tokens.colors.primary : mounted && isDark ? '#E5E7EB' : tokens.colors.text.secondary}
label={
{child.name}
}
styles={{
root: {
borderRadius: rem(8),
marginBottom: rem(2),
transition: 'background 150ms ease',
padding: '6px 12px',
'&:hover': {
backgroundColor: mounted && isDark ? 'rgba(255, 255, 255, 0.05)' : tokens.colors.bg.hover,
},
...(mounted && isChildActive && isDark && {
backgroundColor: 'rgba(59,130,246,0.15)',
borderLeft: `2px solid ${tokens.colors.primary}`,
}),
...(mounted && isChildActive && !isDark && {
backgroundColor: 'rgba(25, 113, 194, 0.1)',
borderLeft: `2px solid ${tokens.colors.primary}`,
}),
}
}}
active={isChildActive}
variant="subtle"
component={Link}
/>
);
})}
);
})}
{/*
MAIN CONTENT
Spec: Background --bg-base
*/}
{children}
);
}