upd: tampilan
This commit is contained in:
228
src/frontend/components/DashboardLayout.tsx
Normal file
228
src/frontend/components/DashboardLayout.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
import { APP_CONFIGS } from '@/frontend/config/appMenus'
|
||||
import {
|
||||
AppShell,
|
||||
Avatar,
|
||||
Box,
|
||||
Burger,
|
||||
Button,
|
||||
Group,
|
||||
Menu,
|
||||
NavLink,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
ThemeIcon
|
||||
} from '@mantine/core'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import { Link, useLocation, useMatches, useNavigate, useParams } from '@tanstack/react-router'
|
||||
import {
|
||||
TbApps,
|
||||
TbArrowLeft,
|
||||
TbChevronRight,
|
||||
TbDashboard,
|
||||
TbDeviceMobile,
|
||||
TbLogout,
|
||||
TbSettings,
|
||||
TbUserCircle
|
||||
} from 'react-icons/tb'
|
||||
|
||||
interface DashboardLayoutProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export function DashboardLayout({ children }: DashboardLayoutProps) {
|
||||
const [opened, { toggle }] = useDisclosure()
|
||||
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: 'Settings', icon: TbSettings, to: '/settings' },
|
||||
]
|
||||
|
||||
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: !opened },
|
||||
}}
|
||||
padding="xl"
|
||||
styles={(theme) => ({
|
||||
main: {
|
||||
backgroundColor: theme.colors.dark[7], // Dark mode background
|
||||
},
|
||||
})}
|
||||
>
|
||||
<AppShell.Header px="xl">
|
||||
<Group h="100%" justify="space-between">
|
||||
<Group>
|
||||
<Burger opened={opened} onClick={toggle} hiddenFrom="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">
|
||||
<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: '1px solid rgba(255,255,255,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: '1px solid rgba(255,255,255,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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user