153 lines
5.3 KiB
TypeScript
153 lines
5.3 KiB
TypeScript
'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>
|
|
|
|
{/* Footer */}
|
|
<div className="px-4 py-4 border-t border-slate-100">
|
|
<p className="text-[11px] text-slate-400 text-center">
|
|
© 2026 Plagat.uz
|
|
</p>
|
|
</div>
|
|
</aside>
|
|
</>
|
|
);
|