99 خطوط
4.9 KiB
JavaScript
99 خطوط
4.9 KiB
JavaScript
import { NavLink } from 'react-router-dom';
|
|
import { useAuth } from '../context/AuthContext';
|
|
import { LayoutDashboard, FolderKanban, CheckSquare, Kanban, Inbox, Timer, Users, Calendar, Paperclip, TrendingUp, Building2, Shield, Settings, PanelRightClose, PanelRightOpen, X } from 'lucide-react';
|
|
|
|
const iconSize = 18;
|
|
|
|
const menuItems = [
|
|
{ path: '/', label: 'داشبورد', icon: LayoutDashboard },
|
|
{ path: '/projects', label: 'پروژهها', icon: FolderKanban },
|
|
{ path: '/tasks', label: 'تسکها', icon: CheckSquare },
|
|
{ path: '/kanban', label: 'برد کاری', icon: Kanban },
|
|
{ path: '/backlog', label: 'بکلاگ', icon: Inbox },
|
|
{ path: '/sprints', label: 'اسپرینتها', icon: Timer },
|
|
{ path: '/team', label: 'اعضای تیم', icon: Users },
|
|
{ path: '/meetings', label: 'جلسات', icon: Calendar },
|
|
{ path: '/files', label: 'فایلها', icon: Paperclip },
|
|
{ path: '/reports', label: 'گزارشها', icon: TrendingUp },
|
|
{ path: '/organization', label: 'ساختار سازمانی', icon: Building2 },
|
|
{ path: '/roles', label: 'نقشها و دسترسیها', icon: Shield },
|
|
{ path: '/settings', label: 'تنظیمات', icon: Settings },
|
|
];
|
|
|
|
export default function Sidebar({ collapsed, onToggle, mobileOpen = false, onMobileClose }) {
|
|
const { user } = useAuth();
|
|
return (
|
|
<aside id="main-navigation-drawer" className={`app-sidebar ${mobileOpen ? 'mobile-open' : ''}`} aria-label="منوی اصلی" style={{
|
|
width: collapsed ? '72px' : 'var(--sidebar-width)',
|
|
height: '100vh',
|
|
position: 'fixed',
|
|
right: 0,
|
|
top: 0,
|
|
background: 'var(--surface)',
|
|
borderLeft: '1px solid var(--gray-200)',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
zIndex: 100,
|
|
transition: 'width 0.2s ease, transform 0.2s ease',
|
|
overflow: 'hidden',
|
|
}}>
|
|
<div style={{ padding: collapsed ? '0.875rem 0.75rem' : '1rem 1rem 1rem 1.25rem', display: 'flex', alignItems: 'center', gap: '0.75rem', borderBottom: '1px solid var(--gray-100)', justifyContent: collapsed ? 'center' : 'space-between' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', minWidth: 0 }}>
|
|
<img className="sidebar-brand-mark" src="/brand/project-mark.png" alt="" />
|
|
{!collapsed && <span style={{ fontWeight: 700, fontSize: '1rem', color: 'var(--gray-900)', whiteSpace: 'nowrap' }}>مدیریت پروژه</span>}
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={onToggle}
|
|
className="sidebar-collapse-toggle"
|
|
title={collapsed ? 'باز کردن منو' : 'بستن منو'}
|
|
style={{
|
|
width: 34,
|
|
height: 34,
|
|
borderRadius: 'var(--radius-sm)',
|
|
background: collapsed ? 'var(--surface-active)' : 'var(--gray-100)',
|
|
border: '1px solid var(--gray-200)',
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
color: 'var(--gray-700)',
|
|
cursor: 'pointer',
|
|
boxShadow: 'var(--shadow)',
|
|
transition: 'var(--transition)',
|
|
flexShrink: 0,
|
|
}}
|
|
>
|
|
{collapsed ? <PanelRightOpen size={18} /> : <PanelRightClose size={18} />}
|
|
</button>
|
|
<button type="button" className="sidebar-mobile-close" onClick={onMobileClose} aria-label="بستن منوی اصلی">
|
|
<X size={20} />
|
|
</button>
|
|
</div>
|
|
<nav style={{ flex: 1, overflowY: 'auto', padding: '0.75rem 0' }}>
|
|
{menuItems.map((item) => (
|
|
<NavLink key={item.path} to={item.path} end={item.path === '/'} onClick={onMobileClose} style={({ isActive }) => ({
|
|
display: 'flex', alignItems: 'center', gap: '0.75rem',
|
|
padding: collapsed ? '0.75rem 1.5rem' : '0.625rem 1.25rem',
|
|
margin: '0 0.5rem',
|
|
borderRadius: 'var(--radius-sm)',
|
|
fontSize: '0.875rem',
|
|
fontWeight: isActive ? 600 : 400,
|
|
color: isActive ? 'var(--primary)' : 'var(--gray-600)',
|
|
background: isActive ? 'var(--surface-active)' : 'transparent',
|
|
justifyContent: collapsed ? 'center' : 'flex-start',
|
|
whiteSpace: 'nowrap',
|
|
transition: 'all 0.15s ease',
|
|
})}>
|
|
<item.icon size={iconSize} />
|
|
{!collapsed && item.label}
|
|
</NavLink>
|
|
))}
|
|
</nav>
|
|
<div style={{ padding: '0.75rem 1.25rem', borderTop: '1px solid var(--gray-100)', display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
|
|
{user?.avatar_url ? <img className="avatar avatar-sm avatar-img" src={user.avatar_url} alt={user?.name || 'avatar'} /> : <div className="avatar avatar-sm">{user?.name?.[0] || '?'}</div>}
|
|
{!collapsed && <div style={{ fontSize: '0.8125rem', color: 'var(--gray-600)', overflow: 'hidden', textOverflow: 'ellipsis' }}>{user?.name}</div>}
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|