- Add package.json with development and production dependencies - Create postcss.config.js for Tailwind CSS integration - Implement AdminController for handling admin routes - Add admin index template with React root element - Create base template for consistent layout - Configure TypeScript with tsconfig.json - Set up Webpack configuration for asset management
168 lines
5.8 KiB
TypeScript
168 lines
5.8 KiB
TypeScript
import React from 'react';
|
|
import { NavLink, useNavigate } from 'react-router-dom';
|
|
import {
|
|
ChartBarIcon,
|
|
UserGroupIcon,
|
|
HeartIcon,
|
|
BuildingOffice2Icon,
|
|
CalendarDaysIcon,
|
|
CreditCardIcon,
|
|
BanknotesIcon,
|
|
UsersIcon,
|
|
ChatBubbleLeftEllipsisIcon,
|
|
StarIcon,
|
|
DevicePhoneMobileIcon,
|
|
TagIcon,
|
|
DocumentTextIcon,
|
|
KeyIcon,
|
|
ChevronRightIcon,
|
|
ChevronLeftIcon,
|
|
MagnifyingGlassIcon,
|
|
ArrowLeftOnRectangleIcon,
|
|
} from '@heroicons/react/24/outline';
|
|
import { useUiStore } from '../../stores/uiStore';
|
|
import { useAuthStore } from '../../stores/authStore';
|
|
|
|
const sections = [
|
|
{
|
|
label: 'عمومی',
|
|
items: [
|
|
{ to: '/admin/dashboard', icon: ChartBarIcon, label: 'داشبورد' },
|
|
{ to: '/admin/users', icon: UserGroupIcon, label: 'کاربران' },
|
|
{ to: '/admin/doctors', icon: HeartIcon, label: 'پزشکان' },
|
|
{ to: '/admin/clinics', icon: BuildingOffice2Icon, label: 'کلینیکها' },
|
|
],
|
|
},
|
|
{
|
|
label: 'مدیریت',
|
|
items: [
|
|
{ to: '/admin/appointments', icon: CalendarDaysIcon, label: 'نوبتها' },
|
|
{ to: '/admin/payments', icon: CreditCardIcon, label: 'پرداختها' },
|
|
{ to: '/admin/settlements', icon: BanknotesIcon, label: 'تسویهحساب' },
|
|
],
|
|
},
|
|
{
|
|
label: 'محتوا',
|
|
items: [
|
|
{ to: '/admin/comments', icon: ChatBubbleLeftEllipsisIcon, label: 'نظرات' },
|
|
{ to: '/admin/ratings', icon: StarIcon, label: 'امتیازها' },
|
|
{ to: '/admin/blogs', icon: DocumentTextIcon, label: 'بلاگ' },
|
|
{ to: '/admin/sms', icon: DevicePhoneMobileIcon, label: 'پیامک' },
|
|
],
|
|
},
|
|
{
|
|
label: 'سیستم',
|
|
items: [
|
|
{ to: '/admin/categories', icon: TagIcon, label: 'دستهبندیها' },
|
|
{ to: '/admin/representations', icon: UsersIcon, label: 'نمایندگان' },
|
|
{ to: '/admin/secretaries', icon: KeyIcon, label: 'منشیها' },
|
|
],
|
|
},
|
|
];
|
|
|
|
export default function Sidebar() {
|
|
const open = useUiStore((s) => s.sidebarOpen);
|
|
const toggle = useUiStore((s) => s.toggleSidebar);
|
|
const logout = useAuthStore((s) => s.logout);
|
|
const navigate = useNavigate();
|
|
|
|
const handleLogout = () => {
|
|
logout();
|
|
navigate('/admin/login');
|
|
};
|
|
|
|
return (
|
|
<aside
|
|
style={{ transition: 'width 300ms cubic-bezier(0.4,0,0.2,1)' }}
|
|
className={`${open ? 'w-[260px]' : 'w-[72px]'} shrink-0 bg-[#0f172a] flex flex-col h-screen overflow-hidden`}
|
|
>
|
|
{/* Logo */}
|
|
<div className="flex items-center justify-between px-4 py-5 border-b border-slate-700/50">
|
|
{open && (
|
|
<span className="text-white font-bold text-lg tracking-tight">ClinicPro</span>
|
|
)}
|
|
<button
|
|
onClick={toggle}
|
|
className="text-slate-400 hover:text-white p-1 rounded-md hover:bg-slate-700/50 transition-colors"
|
|
>
|
|
{open ? <ChevronRightIcon className="w-5 h-5" /> : <ChevronLeftIcon className="w-5 h-5" />}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Search */}
|
|
{open && (
|
|
<div className="px-3 py-3 border-b border-slate-700/50">
|
|
<div className="flex items-center gap-2 bg-slate-800 rounded-lg px-3 py-2">
|
|
<MagnifyingGlassIcon className="w-4 h-4 text-slate-400 shrink-0" />
|
|
<input
|
|
type="text"
|
|
placeholder="جستجوی سریع..."
|
|
className="bg-transparent text-sm text-slate-300 placeholder-slate-500 outline-none w-full"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 overflow-y-auto py-3 space-y-1 scrollbar-thin">
|
|
{sections.map((section) => (
|
|
<div key={section.label} className="mb-2">
|
|
{open && (
|
|
<p className="text-[11px] font-semibold text-slate-500 uppercase tracking-wider px-4 py-1">
|
|
{section.label}
|
|
</p>
|
|
)}
|
|
{section.items.map(({ to, icon: Icon, label }) => (
|
|
<NavLink
|
|
key={to}
|
|
to={to}
|
|
className={({ isActive }) =>
|
|
`flex items-center gap-3 mx-2 px-3 py-2.5 rounded-lg text-sm transition-colors group ${
|
|
isActive
|
|
? 'bg-primary-500/15 text-primary-400 font-semibold border-r-4 border-primary-500'
|
|
: 'text-slate-400 hover:text-slate-200 hover:bg-slate-700/40'
|
|
}`
|
|
}
|
|
title={!open ? label : undefined}
|
|
>
|
|
<Icon className="w-5 h-5 shrink-0" />
|
|
{open && <span>{label}</span>}
|
|
</NavLink>
|
|
))}
|
|
</div>
|
|
))}
|
|
</nav>
|
|
|
|
{/* User card */}
|
|
<div className="border-t border-slate-700/50 p-3">
|
|
{open ? (
|
|
<div className="flex items-center gap-3 bg-slate-800 rounded-xl p-3">
|
|
<div className="w-8 h-8 rounded-full bg-primary-600 flex items-center justify-center text-white text-sm font-bold shrink-0">
|
|
A
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-white text-sm font-medium truncate">Admin</p>
|
|
<p className="text-slate-400 text-xs truncate">مدیر سیستم</p>
|
|
</div>
|
|
<button
|
|
onClick={handleLogout}
|
|
className="text-slate-400 hover:text-red-400 transition-colors"
|
|
title="خروج"
|
|
>
|
|
<ArrowLeftOnRectangleIcon className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<button
|
|
onClick={handleLogout}
|
|
className="w-full flex justify-center py-2 text-slate-400 hover:text-red-400 transition-colors"
|
|
title="خروج"
|
|
>
|
|
<ArrowLeftOnRectangleIcon className="w-5 h-5" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|