138 lines
5.0 KiB
TypeScript
138 lines
5.0 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
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';
|
||
import api from '../services/api';
|
||
|
||
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 [newOrdersCount, setNewOrdersCount] = useState(0);
|
||
|
||
useEffect(() => {
|
||
const fetchOrdersCount = async () => {
|
||
try {
|
||
const response = await api.get('/admin/dashboard/stats');
|
||
if (response.data?.success) {
|
||
setNewOrdersCount(response.data.data.newOrders);
|
||
}
|
||
} catch (err) {
|
||
console.error('Failed to fetch stats in sidebar', err);
|
||
}
|
||
};
|
||
fetchOrdersCount();
|
||
// Poll stats every 60 seconds
|
||
const interval = setInterval(fetchOrdersCount, 60000);
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
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:fixed lg:right-0 lg:top-0 lg:bottom-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>
|
||
{item.label === 'سفارشات' && newOrdersCount > 0 && (
|
||
<span className="mr-auto bg-purple-600 text-white text-[11px] font-black px-2 py-0.5 rounded-full min-w-[20px] text-center">
|
||
{newOrdersCount}
|
||
</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>
|
||
</>
|
||
);
|
||
}
|