249 lines
7.6 KiB
TypeScript
249 lines
7.6 KiB
TypeScript
import { APP_CONFIGS } from '@/frontend/config/appMenus'
|
|
import {
|
|
ActionIcon,
|
|
AppShell,
|
|
Avatar,
|
|
Box,
|
|
Burger,
|
|
Button,
|
|
Group,
|
|
Menu,
|
|
NavLink,
|
|
Select,
|
|
Stack,
|
|
Text,
|
|
ThemeIcon
|
|
} from '@mantine/core'
|
|
import { useDisclosure } from '@mantine/hooks'
|
|
import { useMantineColorScheme, useComputedColorScheme } from '@mantine/core'
|
|
import { Link, useLocation, useMatches, useNavigate, useParams } from '@tanstack/react-router'
|
|
import {
|
|
TbApps,
|
|
TbArrowLeft,
|
|
TbChevronRight,
|
|
TbDashboard,
|
|
TbDeviceMobile,
|
|
TbLogout,
|
|
TbSettings,
|
|
TbUserCircle,
|
|
TbSun,
|
|
TbMoon,
|
|
TbUser,
|
|
TbHistory
|
|
} from 'react-icons/tb'
|
|
|
|
interface DashboardLayoutProps {
|
|
children: React.ReactNode
|
|
}
|
|
|
|
export function DashboardLayout({ children }: DashboardLayoutProps) {
|
|
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure()
|
|
const [desktopOpened, { toggle: toggleDesktop }] = useDisclosure(true)
|
|
const { toggleColorScheme } = useMantineColorScheme()
|
|
const computedColorScheme = useComputedColorScheme('light', { getInitialValueInEffect: true })
|
|
const location = useLocation()
|
|
const navigate = useNavigate()
|
|
const { appId } = useParams({ strict: false }) as { appId?: string }
|
|
|
|
const matches = useMatches()
|
|
const currentPath = matches[matches.length - 1]?.pathname
|
|
|
|
const globalNav = [
|
|
{ label: 'Dashboard', icon: TbDashboard, to: '/dashboard' },
|
|
{ label: 'Applications', icon: TbApps, to: '/apps' },
|
|
{ label: 'Log Activity', icon: TbHistory, to: '/logs' },
|
|
{ label: 'Users', icon: TbUser, to: '/users' },
|
|
]
|
|
|
|
const activeApp = appId ? APP_CONFIGS[appId] : null
|
|
const navLinks = activeApp ? activeApp.menus : globalNav
|
|
|
|
return (
|
|
<AppShell
|
|
header={{ height: 70 }}
|
|
navbar={{
|
|
width: 260,
|
|
breakpoint: 'sm',
|
|
collapsed: { mobile: !mobileOpened, desktop: !desktopOpened },
|
|
}}
|
|
padding="xl"
|
|
styles={(theme) => ({
|
|
main: {
|
|
backgroundColor: computedColorScheme === 'dark' ? theme.colors.dark[9] : theme.colors.gray[0],
|
|
transition: 'background-color 0.2s ease',
|
|
},
|
|
})}
|
|
>
|
|
<AppShell.Header px="xl">
|
|
<Group h="100%" justify="space-between">
|
|
<Group>
|
|
<Burger opened={mobileOpened} onClick={toggleMobile} hiddenFrom="sm" size="sm" />
|
|
<Burger opened={desktopOpened} onClick={toggleDesktop} visibleFrom="sm" size="sm" />
|
|
<Group gap="xs">
|
|
<ThemeIcon
|
|
size={34}
|
|
radius="md"
|
|
variant="gradient"
|
|
gradient={{ from: '#2563EB', to: '#7C3AED', deg: 135 }}
|
|
>
|
|
<TbDeviceMobile size={18} />
|
|
</ThemeIcon>
|
|
<Text
|
|
size="lg"
|
|
fw={700}
|
|
className="gradient-text"
|
|
style={{ letterSpacing: '-0.5px' }}
|
|
>
|
|
Monitoring System
|
|
</Text>
|
|
</Group>
|
|
</Group>
|
|
|
|
<Group gap="md">
|
|
<ActionIcon
|
|
onClick={() => toggleColorScheme()}
|
|
variant="default"
|
|
size="lg"
|
|
aria-label="Toggle color scheme"
|
|
>
|
|
{computedColorScheme === 'dark' ? <TbSun size={18} /> : <TbMoon size={18} />}
|
|
</ActionIcon>
|
|
<Menu shadow="md" width={200} position="bottom-end">
|
|
<Menu.Target>
|
|
<Avatar
|
|
src={undefined}
|
|
alt="User"
|
|
color="brand-blue"
|
|
radius="xl"
|
|
style={{ cursor: 'pointer' }}
|
|
/>
|
|
</Menu.Target>
|
|
|
|
<Menu.Dropdown>
|
|
<Menu.Label>Application</Menu.Label>
|
|
<Menu.Item leftSection={<TbUserCircle size={16} />}>Profile</Menu.Item>
|
|
<Menu.Item leftSection={<TbSettings size={16} />}>Settings</Menu.Item>
|
|
<Menu.Divider />
|
|
<Menu.Label>Danger Zone</Menu.Label>
|
|
<Menu.Item color="red" leftSection={<TbLogout size={16} />}>
|
|
Logout
|
|
</Menu.Item>
|
|
</Menu.Dropdown>
|
|
</Menu>
|
|
</Group>
|
|
</Group>
|
|
</AppShell.Header>
|
|
|
|
<AppShell.Navbar p="md">
|
|
<Stack gap="xs" mt="md">
|
|
{activeApp && (
|
|
<NavLink
|
|
label="Back to Dashboard"
|
|
leftSection={<TbArrowLeft size={20} />}
|
|
component={Link}
|
|
to="/dashboard"
|
|
styles={(theme) => ({
|
|
root: {
|
|
borderRadius: theme.radius.md,
|
|
opacity: 0.7,
|
|
'&:hover': { opacity: 1 },
|
|
},
|
|
})}
|
|
/>
|
|
)}
|
|
|
|
{
|
|
activeApp &&
|
|
<Select
|
|
label="Selected Application"
|
|
value={appId}
|
|
data={[
|
|
{ value: 'desa-plus', label: 'Desa+' },
|
|
{ value: 'e-commerce', label: 'E-Commerce' },
|
|
{ value: 'fitness-app', label: 'Fitness App' },
|
|
]}
|
|
onChange={(val) => val && navigate({ to: '/apps/$appId', params: { appId: val } })}
|
|
radius="md"
|
|
size="sm"
|
|
w={220}
|
|
mb={"md"}
|
|
variant="filled"
|
|
styles={(theme) => ({
|
|
input: { border: computedColorScheme === 'dark' ? '1px solid rgba(255,255,255,0.1)' : '1px solid rgba(0,0,0,0.1)' }
|
|
})}
|
|
/>
|
|
}
|
|
|
|
|
|
|
|
{/* {activeApp && (
|
|
<Text size="xs" fw={700} c="dimmed" px="md" mb="xs" style={{ textTransform: 'uppercase' }}>
|
|
{activeApp.name} Context
|
|
</Text>
|
|
)} */}
|
|
|
|
{navLinks.map((link: any) => {
|
|
const isActive = currentPath === link.to
|
|
|
|
return (
|
|
<NavLink
|
|
key={link.label}
|
|
component={Link}
|
|
to={link.to}
|
|
activeOptions={{ exact: true }}
|
|
label={link.label}
|
|
leftSection={<link.icon size={20} />}
|
|
rightSection={<TbChevronRight size={14} />}
|
|
active={isActive}
|
|
variant="filled"
|
|
color="brand-blue"
|
|
className="sidebar-nav-item"
|
|
styles={(theme) => ({
|
|
root: {
|
|
borderRadius: theme.radius.md,
|
|
transition: 'all 0.2s ease',
|
|
'&[data-active]': {
|
|
background: 'var(--gradient-blue-purple)',
|
|
fontWeight: 600,
|
|
},
|
|
},
|
|
})}
|
|
/>
|
|
)
|
|
})}
|
|
</Stack>
|
|
|
|
<Box style={{ marginTop: 'auto' }}>
|
|
<Stack gap="xs">
|
|
<Box
|
|
p="md"
|
|
className="glass"
|
|
style={{ borderRadius: '12px', border: computedColorScheme === 'dark' ? '1px solid rgba(255,255,255,0.05)' : '1px solid rgba(0,0,0,0.05)' }}
|
|
>
|
|
<Text size="xs" c="dimmed" fw={600} mb="xs">SYSTEM STATUS</Text>
|
|
<Group gap="xs">
|
|
<Box style={{ width: 8, height: 8, borderRadius: '50%', background: '#10b981' }} />
|
|
<Text size="sm" fw={500}>All Systems Operational</Text>
|
|
</Group>
|
|
</Box>
|
|
|
|
<Button
|
|
variant="light"
|
|
color="red"
|
|
fullWidth
|
|
leftSection={<TbLogout size={16} />}
|
|
mt="md"
|
|
>
|
|
Log out
|
|
</Button>
|
|
</Stack>
|
|
</Box>
|
|
</AppShell.Navbar>
|
|
|
|
<AppShell.Main>
|
|
{children}
|
|
</AppShell.Main>
|
|
</AppShell>
|
|
)
|
|
}
|