- Remove node_modules, dist, .next from git tracking - Add .next and *.tsbuildinfo to .gitignore - Remove Iranian mirrors from Dockerfiles (arvancloud.ir) - Add npm ci step for dependency installation - Ready for VPN-based internet access workflow
113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||
import { Users, ShoppingCart, Tag, Settings, LogOut, TrendingUp, FolderTree, FileText, BookOpen, Heart, Package, LayoutDashboard, Languages, Image } from 'lucide-react';
|
||
|
||
const menuGroups = [
|
||
{
|
||
title: 'اصلی',
|
||
items: [
|
||
{ icon: LayoutDashboard, label: 'داشبورد', path: '/' },
|
||
{ icon: TrendingUp, label: 'گزارشات', path: '/reports' },
|
||
]
|
||
},
|
||
{
|
||
title: 'فروشگاه',
|
||
items: [
|
||
{ icon: ShoppingCart, label: 'سفارشات', path: '/orders' },
|
||
{ icon: Package, label: 'محصولات', path: '/products' },
|
||
{ icon: FolderTree, label: 'دستهبندیها', path: '/categories' },
|
||
{ icon: Tag, label: 'کدهای تخفیف', path: '/coupons' },
|
||
]
|
||
},
|
||
{
|
||
title: 'مدیریت و کاربران',
|
||
items: [
|
||
{ icon: Users, label: 'کاربران', path: '/users' },
|
||
{ icon: Heart, label: 'حیوانات (Pets)', path: '/pets' },
|
||
]
|
||
},
|
||
{
|
||
title: 'محتوا',
|
||
items: [
|
||
{ icon: FileText, label: 'وبلاگ', path: '/blogs' },
|
||
{ icon: BookOpen, label: 'دانشنامه', path: '/wiki' },
|
||
]
|
||
},
|
||
{
|
||
title: 'سیستم',
|
||
items: [
|
||
{ icon: Settings, label: 'تنظیمات', path: '/settings' },
|
||
{ icon: Languages, label: 'متون رابط کاربری', path: '/ui-texts' },
|
||
{ icon: Image, label: 'مدیریت رسانه', path: '/media' },
|
||
]
|
||
}
|
||
];
|
||
|
||
interface SidebarProps {
|
||
isOpen: boolean;
|
||
setIsOpen: (val: boolean) => void;
|
||
}
|
||
|
||
export default function Sidebar({ isOpen, setIsOpen }: SidebarProps) {
|
||
const location = useLocation();
|
||
const navigate = useNavigate();
|
||
|
||
const handleLogout = () => {
|
||
localStorage.removeItem('adminToken');
|
||
navigate('/login');
|
||
};
|
||
|
||
return (
|
||
<>
|
||
{/* Mobile Backdrop */}
|
||
{isOpen && (
|
||
<div
|
||
className="fixed inset-0 bg-black/50 z-40 lg:hidden backdrop-blur-sm"
|
||
onClick={() => setIsOpen(false)}
|
||
/>
|
||
)}
|
||
|
||
<aside className={`w-64 bg-white border-l border-gray-200 h-screen fixed lg:sticky top-0 flex flex-col font-vazir shadow-sm z-50 transform transition-transform duration-300 ease-in-out ${isOpen ? 'translate-x-0' : 'translate-x-full lg:translate-x-0'
|
||
}`}>
|
||
<div className="h-16 flex items-center justify-center border-b border-gray-200 shrink-0">
|
||
<h1 className="text-xl font-black text-purple-600">کانینا | ادمینپنل</h1>
|
||
</div>
|
||
|
||
<nav className="flex-1 overflow-y-auto py-4 px-3 space-y-6">
|
||
{menuGroups.map((group, idx) => (
|
||
<div key={idx}>
|
||
<h3 className="px-3 mb-2 text-xs font-black text-gray-400 uppercase tracking-wider">{group.title}</h3>
|
||
<div className="space-y-1">
|
||
{group.items.map((item) => {
|
||
const isActive = location.pathname === item.path || (item.path !== '/' && location.pathname.startsWith(item.path));
|
||
const Icon = item.icon;
|
||
|
||
return (
|
||
<Link
|
||
key={item.path}
|
||
to={item.path}
|
||
className={`flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all font-bold ${isActive
|
||
? 'bg-purple-50 text-purple-600'
|
||
: 'text-gray-500 hover:bg-gray-50 hover:text-gray-900'
|
||
}`}
|
||
>
|
||
<Icon className="w-5 h-5" />
|
||
<span>{item.label}</span>
|
||
</Link>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</nav>
|
||
|
||
<div className="p-4 border-t border-gray-200">
|
||
<button onClick={handleLogout} className="flex items-center gap-3 px-3 py-3 w-full text-red-500 hover:bg-red-50 rounded-xl transition-all font-bold">
|
||
<LogOut className="w-5 h-5" />
|
||
<span>خروج از حساب</span>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
</>
|
||
);
|
||
}
|