first commit

This commit is contained in:
2026-06-05 17:28:05 +05:30
commit a162fa89e5
62 changed files with 8729 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
import { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import {
Drawer,
Box,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Typography,
Collapse,
Tooltip,
Toolbar
} from '@mui/material';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import navItems from '@/menu/navItems';
import Logo from '@/components/Logo';
export const DRAWER_WIDTH = 264;
export const MINI_WIDTH = 78;
const RED = '#C01227';
function NavLeaf({ item, open, active, depth = 0, onClick }) {
const Icon = item.icon;
const button = (
<ListItemButton
selected={active}
onClick={onClick}
sx={{
minHeight: 44,
my: 0.25,
mx: open ? 1 : 0.75,
px: open ? 1.5 : 0,
justifyContent: open ? 'flex-start' : 'center',
borderRadius: 2,
color: 'rgba(255,255,255,0.78)',
'& .MuiListItemIcon-root': { color: 'inherit' },
'&:hover': { bgcolor: 'rgba(255,255,255,0.12)', color: '#fff' },
'&.Mui-selected': {
bgcolor: 'rgba(255,255,255,0.18)',
color: '#fff',
'&:hover': { bgcolor: 'rgba(255,255,255,0.22)' }
}
}}
>
<ListItemIcon sx={{ minWidth: open ? 34 : 'auto', justifyContent: 'center' }}>
{depth > 0 && !Icon ? <FiberManualRecordIcon sx={{ fontSize: 8 }} /> : Icon ? <Icon fontSize="small" /> : null}
</ListItemIcon>
{open && (
<ListItemText
primary={item.title}
primaryTypographyProps={{ fontSize: '0.875rem', fontWeight: active ? 700 : 500 }}
/>
)}
</ListItemButton>
);
return open ? button : <Tooltip title={item.title} placement="right">{button}</Tooltip>;
}
export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) {
const location = useLocation();
const navigate = useNavigate();
const isActive = (url) => url && location.pathname.startsWith(url);
const expanded = open || isMobile;
const initialOpen = navItems
.flatMap((g) => g.items)
.filter((i) => i.children && i.children.some((c) => isActive(c.url)))
.map((i) => i.id);
const [collapse, setCollapse] = useState(initialOpen);
const go = (url) => {
navigate(url);
if (isMobile) onMobileClose();
};
const content = (
<Box sx={{ bgcolor: RED, height: '100%', color: '#fff', display: 'flex', flexDirection: 'column' }}>
<Toolbar sx={{ px: expanded ? 2.5 : 0, justifyContent: expanded ? 'flex-start' : 'center', minHeight: 64 }}>
<Logo onDark compact={!expanded} />
</Toolbar>
<Box sx={{ overflowY: 'auto', overflowX: 'hidden', flexGrow: 1, pb: 2 }}>
{navItems.map((grp) => (
<Box key={grp.group} sx={{ mt: 1 }}>
{expanded && (
<Typography
variant="overline"
sx={{ px: 2.5, color: 'rgba(255,255,255,0.55)', fontSize: '0.6875rem', letterSpacing: '0.08em' }}
>
{grp.group}
</Typography>
)}
<List disablePadding sx={{ mt: 0.5 }}>
{grp.items.map((item) => {
if (item.children) {
const opened = collapse.includes(item.id);
const childActive = item.children.some((c) => isActive(c.url));
const Icon = item.icon;
const head = (
<ListItemButton
onClick={() =>
expanded
? setCollapse((p) => (p.includes(item.id) ? p.filter((x) => x !== item.id) : [...p, item.id]))
: go(item.children[0].url)
}
sx={{
minHeight: 44,
my: 0.25,
mx: expanded ? 1 : 0.75,
px: expanded ? 1.5 : 0,
justifyContent: expanded ? 'flex-start' : 'center',
borderRadius: 2,
color: childActive ? '#fff' : 'rgba(255,255,255,0.78)',
bgcolor: childActive && !opened ? 'rgba(255,255,255,0.12)' : 'transparent',
'&:hover': { bgcolor: 'rgba(255,255,255,0.12)', color: '#fff' }
}}
>
<ListItemIcon sx={{ minWidth: expanded ? 34 : 'auto', justifyContent: 'center', color: 'inherit' }}>
<Icon fontSize="small" />
</ListItemIcon>
{expanded && (
<>
<ListItemText primary={item.title} primaryTypographyProps={{ fontSize: '0.875rem', fontWeight: 500 }} />
{opened ? <ExpandLess fontSize="small" /> : <ExpandMore fontSize="small" />}
</>
)}
</ListItemButton>
);
return (
<Box key={item.id}>
{expanded ? head : <Tooltip title={item.title} placement="right">{head}</Tooltip>}
{expanded && (
<Collapse in={opened} timeout="auto" unmountOnExit>
<Box sx={{ pl: 1.5 }}>
{item.children.map((c) => (
<NavLeaf key={c.id} item={c} open depth={1} active={isActive(c.url)} onClick={() => go(c.url)} />
))}
</Box>
</Collapse>
)}
</Box>
);
}
return (
<NavLeaf key={item.id} item={item} open={expanded} active={isActive(item.url)} onClick={() => go(item.url)} />
);
})}
</List>
</Box>
))}
</Box>
{expanded && (
<Box sx={{ p: 2, borderTop: '1px solid rgba(255,255,255,0.12)' }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.55)' }}>
Doormile Console v1.0
</Typography>
</Box>
)}
</Box>
);
if (isMobile) {
return (
<Drawer
variant="temporary"
open={mobileOpen}
onClose={onMobileClose}
ModalProps={{ keepMounted: true }}
sx={{ '& .MuiDrawer-paper': { width: DRAWER_WIDTH, border: 'none' } }}
>
{content}
</Drawer>
);
}
return (
<Drawer
variant="permanent"
sx={{
width: open ? DRAWER_WIDTH : MINI_WIDTH,
flexShrink: 0,
whiteSpace: 'nowrap',
'& .MuiDrawer-paper': {
width: open ? DRAWER_WIDTH : MINI_WIDTH,
border: 'none',
overflowX: 'hidden',
transition: (t) => t.transitions.create('width', { duration: t.transitions.duration.standard })
}
}}
open={open}
>
{content}
</Drawer>
);
}