mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-09-30 10:03:14 +00:00
Three-panel admin workspace: left reference panel (submission details, vendor data), center editor (refined scope, inline line item table with reorder/add/remove/pricing), right similar proposals panel (KB results with pull-to-editor). Admin dashboard with stats cards and proposal queue table. Approval flow with confirmation dialog, mark-as-sent, and create-revision actions. Role-based sidebar navigation.
118 lines
3.9 KiB
TypeScript
118 lines
3.9 KiB
TypeScript
import { useLocation, useNavigate } from 'react-router-dom';
|
|
import { useSelector } from 'react-redux';
|
|
import {
|
|
Drawer,
|
|
List,
|
|
ListItemButton,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
Toolbar,
|
|
Box,
|
|
Divider,
|
|
Typography,
|
|
} from '@mui/material';
|
|
import DashboardIcon from '@mui/icons-material/Dashboard';
|
|
import DescriptionIcon from '@mui/icons-material/Description';
|
|
import AddCircleIcon from '@mui/icons-material/AddCircle';
|
|
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
|
|
import AssignmentIcon from '@mui/icons-material/Assignment';
|
|
import PeopleIcon from '@mui/icons-material/People';
|
|
import { selectSidebarOpen } from '../app/slices/uiSlice';
|
|
import { selectUser } from '../app/slices/authSlice';
|
|
import type { RootState } from '../app/store';
|
|
|
|
const DRAWER_WIDTH = 240;
|
|
|
|
const dispatcherNav = [
|
|
{ label: 'Dashboard', path: '/', icon: <DashboardIcon /> },
|
|
{ label: 'My Proposals', path: '/proposals', icon: <DescriptionIcon /> },
|
|
{ label: 'New Proposal', path: '/proposals/new', icon: <AddCircleIcon /> },
|
|
];
|
|
|
|
const adminNav = [
|
|
{ label: 'Admin Queue', path: '/admin', icon: <AdminPanelSettingsIcon /> },
|
|
{ label: 'All Proposals', path: '/admin/proposals', icon: <AssignmentIcon /> },
|
|
];
|
|
|
|
const sysadminNav = [
|
|
{ label: 'Users', path: '/admin/users', icon: <PeopleIcon /> },
|
|
];
|
|
|
|
export default function Sidebar() {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const open = useSelector((state: RootState) => selectSidebarOpen(state));
|
|
const user = useSelector((state: RootState) => selectUser(state));
|
|
|
|
const isAdmin = user?.role === 'Admin' || user?.role === 'SysAdmin';
|
|
const isSysAdmin = user?.role === 'SysAdmin';
|
|
const navItems = dispatcherNav;
|
|
|
|
return (
|
|
<Drawer
|
|
variant="persistent"
|
|
open={open}
|
|
sx={{
|
|
width: open ? DRAWER_WIDTH : 0,
|
|
flexShrink: 0,
|
|
'& .MuiDrawer-paper': {
|
|
width: DRAWER_WIDTH,
|
|
boxSizing: 'border-box',
|
|
},
|
|
}}
|
|
>
|
|
<Toolbar />
|
|
<Box sx={{ overflow: 'auto', flexGrow: 1 }}>
|
|
<List>
|
|
{navItems.map((item) => (
|
|
<ListItemButton
|
|
key={item.path}
|
|
selected={location.pathname === item.path}
|
|
onClick={() => navigate(item.path)}
|
|
>
|
|
<ListItemIcon>{item.icon}</ListItemIcon>
|
|
<ListItemText primary={item.label} />
|
|
</ListItemButton>
|
|
))}
|
|
</List>
|
|
|
|
{isAdmin && (
|
|
<>
|
|
<Divider />
|
|
<Typography variant="overline" sx={{ px: 2, pt: 1, display: 'block', color: 'text.secondary' }}>
|
|
Admin
|
|
</Typography>
|
|
<List>
|
|
{adminNav.map((item) => (
|
|
<ListItemButton
|
|
key={item.path}
|
|
selected={location.pathname === item.path || location.pathname.startsWith(item.path + '/')}
|
|
onClick={() => navigate(item.path)}
|
|
>
|
|
<ListItemIcon>{item.icon}</ListItemIcon>
|
|
<ListItemText primary={item.label} />
|
|
</ListItemButton>
|
|
))}
|
|
{isSysAdmin && sysadminNav.map((item) => (
|
|
<ListItemButton
|
|
key={item.path}
|
|
selected={location.pathname === item.path}
|
|
onClick={() => navigate(item.path)}
|
|
>
|
|
<ListItemIcon>{item.icon}</ListItemIcon>
|
|
<ListItemText primary={item.label} />
|
|
</ListItemButton>
|
|
))}
|
|
</List>
|
|
</>
|
|
)}
|
|
</Box>
|
|
{user && (
|
|
<Box sx={{ p: 2, mt: 'auto', borderTop: 1, borderColor: 'divider' }}>
|
|
<Box sx={{ fontSize: '0.85rem', fontWeight: 500 }}>{user.displayName}</Box>
|
|
<Box sx={{ fontSize: '0.75rem', color: 'text.secondary' }}>{user.role}</Box>
|
|
</Box>
|
|
)}
|
|
</Drawer>
|
|
);
|
|
}
|