changed
This commit is contained in:
145
src/widgets/cabinet/ui/Sidebar.tsx
Normal file
145
src/widgets/cabinet/ui/Sidebar.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import {
|
||||
LayoutDashboard,
|
||||
FileSearch,
|
||||
BrainCircuit,
|
||||
CreditCard,
|
||||
User,
|
||||
Home,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { Link } from '@/shared/config/i18n/navigation';
|
||||
import type { CabinetSection } from '../lib/types';
|
||||
|
||||
// ─── Nav items ─────────────────────────────────────────────────────────────────
|
||||
|
||||
type NavItemDef =
|
||||
| { id: CabinetSection; label: string; icon: React.ElementType; href?: never }
|
||||
| { id: 'home'; label: string; icon: React.ElementType; href: string };
|
||||
|
||||
const NAV_ITEMS: NavItemDef[] = [
|
||||
{ id: 'home', label: 'Bosh sahifa', icon: Home, href: '/' },
|
||||
{ id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
||||
{ id: 'plagiat', label: 'Plagiat', icon: FileSearch },
|
||||
{ id: 'si', label: 'SI detektor', icon: BrainCircuit },
|
||||
{ id: 'payments', label: "To'lovlar tarixi", icon: CreditCard },
|
||||
{ id: 'profile', label: 'Profil', icon: User },
|
||||
];
|
||||
|
||||
// ─── Props ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
interface SidebarProps {
|
||||
active: CabinetSection;
|
||||
onNavigate: (section: CabinetSection) => void;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
// ─── Component ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export const Sidebar: React.FC<SidebarProps> = ({
|
||||
active,
|
||||
onNavigate,
|
||||
isOpen,
|
||||
onClose,
|
||||
userName,
|
||||
}) => (
|
||||
<>
|
||||
{/* Mobile backdrop */}
|
||||
{isOpen && (
|
||||
<div
|
||||
className="fixed inset-0 z-30 bg-black/30 backdrop-blur-sm lg:hidden"
|
||||
onClick={onClose}
|
||||
/>
|
||||
)}
|
||||
|
||||
<aside
|
||||
className={`
|
||||
fixed top-0 left-0 z-40 h-full w-60 bg-white border-r border-slate-100
|
||||
flex flex-col shadow-xl
|
||||
transition-transform duration-300 ease-out
|
||||
lg:sticky lg:top-0 lg:h-screen lg:translate-x-0 lg:shadow-none lg:z-auto
|
||||
${isOpen ? 'translate-x-0' : '-translate-x-full'}
|
||||
`}
|
||||
>
|
||||
{/* Brand */}
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-slate-100">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="w-7 h-7 rounded-lg bg-blue-500 flex items-center justify-center">
|
||||
<span className="text-white text-xs font-bold">P</span>
|
||||
</div>
|
||||
<span className="font-semibold text-slate-800 text-sm">Plagat</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="lg:hidden p-1.5 rounded-lg text-slate-400 hover:text-slate-600 hover:bg-slate-100 transition-colors"
|
||||
aria-label="Yopish"
|
||||
>
|
||||
<X size={15} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* User pill */}
|
||||
<div className="px-3 pt-4 pb-2">
|
||||
<div className="flex items-center gap-3 px-3 py-2.5 rounded-xl bg-slate-50 border border-slate-100">
|
||||
<div className="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center shrink-0">
|
||||
<span className="text-blue-600 text-xs font-semibold">
|
||||
{userName.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium text-slate-800 truncate">
|
||||
{userName}
|
||||
</p>
|
||||
<p className="text-[11px] text-slate-400">Shaxsiy kabinet</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex-1 px-3 py-2 space-y-0.5 overflow-y-auto">
|
||||
{NAV_ITEMS.map((item) => {
|
||||
const Icon = item.icon;
|
||||
|
||||
if (item.id === 'home') {
|
||||
return (
|
||||
<Link
|
||||
key="home"
|
||||
href={item.href}
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium text-slate-500 hover:text-slate-800 hover:bg-slate-50 transition-all duration-150"
|
||||
>
|
||||
<Icon size={17} />
|
||||
<span>{item.label}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
const isActive = item.id === active;
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => onNavigate(item.id as CabinetSection)}
|
||||
className={`
|
||||
w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium
|
||||
transition-all duration-150
|
||||
${
|
||||
isActive
|
||||
? 'bg-blue-50 text-blue-600'
|
||||
: 'text-slate-500 hover:text-slate-800 hover:bg-slate-50'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Icon size={17} />
|
||||
<span>{item.label}</span>
|
||||
{isActive && (
|
||||
<span className="ml-auto w-1.5 h-1.5 rounded-full bg-blue-500 shrink-0" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</aside>
|
||||
</>
|
||||
);
|
||||
Reference in New Issue
Block a user