Files
titipin/src/components/Sidebar.tsx
T

176 lines
7.8 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { cn } from '@/lib/utils'
import {
Store,
ClipboardList,
ShoppingBag,
UserCircle,
Sparkles,
X,
ArrowUpRight,
Info,
BarChart2
} from 'lucide-react'
import { checkUser } from '@/app/actions'
const menuItems = [
{ name: 'Open Order', href: '/', icon: Store, desc: 'Daftar PO live hari ini' },
{ name: 'Jasa Order Saya', href: '/my-orders', icon: ClipboardList, desc: 'Kelola PO buatan Anda' },
{ name: 'Pesanan Saya', href: '/my-purchases', icon: ShoppingBag, desc: 'Riwayat titipan Anda' },
{ name: 'Laporan', href: '/reports', icon: BarChart2, desc: 'Ringkasan transaksi' },
]
export function Sidebar({ isOpen, onClose }: { isOpen?: boolean; onClose?: () => void }) {
const pathname = usePathname()
const [userName, setUserName] = useState<string>('Pengguna')
const [userPhoto, setUserPhoto] = useState<string | null>(null)
const [userId, setUserId] = useState<string | null>(null)
useEffect(() => {
const id = localStorage.getItem('user_id')
if (id) {
setUserId(id)
checkUser(id).then((u) => {
if (u) {
setUserName(u.name)
if (u.photo) setUserPhoto(u.photo)
}
})
}
}, [pathname])
return (
<>
{/* Mobile Backdrop */}
{isOpen && (
<div
onClick={onClose}
className="fixed inset-0 bg-slate-900/40 backdrop-blur-sm z-40 lg:hidden transition-opacity"
/>
)}
<aside className={cn(
"fixed lg:sticky top-0 left-0 z-50 h-screen w-72 bg-white dark:bg-slate-900 border-r border-slate-200/80 dark:border-slate-800 flex flex-col justify-between transition-transform duration-300 ease-in-out p-5",
isOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0"
)}>
<div className="space-y-6">
{/* Brand Header */}
<div className="flex items-center justify-between px-1">
<Link href="/" className="flex items-center gap-3 group">
<div className="w-11 h-11 rounded-2xl bg-gradient-to-br from-[#1B2CC1] to-[#121E85] flex items-center justify-center text-white shadow-lg shadow-[#1B2CC1]/25 transition-all duration-300 group-hover:scale-105 group-hover:shadow-[#1B2CC1]/40">
<Sparkles className="w-5 h-5" />
</div>
<div className="flex flex-col">
<div className="flex items-center gap-1.5">
<span className="font-black text-xl tracking-tight text-slate-900 dark:text-white leading-none">
TitipIn
</span>
<span className="text-[9px] font-extrabold uppercase px-1.5 py-0.5 rounded-md bg-[#1B2CC1]/10 text-[#1B2CC1] dark:bg-blue-900/40 dark:text-blue-300">
Pro
</span>
</div>
<span className="text-[11px] font-medium text-slate-400 mt-1">Sistem Titip Pesanan</span>
</div>
</Link>
{onClose && (
<button onClick={onClose} className="lg:hidden p-1.5 rounded-lg text-slate-400 hover:text-slate-600 hover:bg-slate-100">
<X className="w-5 h-5" />
</button>
)}
</div>
{/* User Profile Card */}
<Link
href="/profile"
onClick={onClose}
className="flex items-center gap-3 p-3 rounded-2xl border border-slate-200/90 dark:border-slate-800 bg-slate-50/70 dark:bg-slate-800/40 hover:bg-slate-100/80 dark:hover:bg-slate-800 transition-all duration-200 group shadow-xs"
>
<div className="relative">
{userPhoto ? (
<img src={userPhoto} alt={userName} className="w-10 h-10 rounded-xl object-cover ring-2 ring-white dark:ring-slate-700 shadow-sm" />
) : (
<div className="w-10 h-10 rounded-xl bg-[#1B2CC1]/10 text-[#1B2CC1] flex items-center justify-center font-black text-sm ring-2 ring-white dark:ring-slate-700 shadow-xs">
{userName.charAt(0).toUpperCase()}
</div>
)}
<span className="absolute -bottom-0.5 -right-0.5 w-3 h-3 bg-emerald-500 rounded-full ring-2 ring-white dark:ring-slate-900 shadow-xs" />
</div>
<div className="flex flex-col min-w-0 flex-1">
<div className="flex items-center justify-between">
<span className="text-xs font-bold text-slate-800 dark:text-slate-200 truncate group-hover:text-[#1B2CC1] transition-colors">
{userName}
</span>
<ArrowUpRight className="w-3.5 h-3.5 text-slate-400 opacity-0 group-hover:opacity-100 transition-opacity" />
</div>
<span className="text-[10px] font-mono text-slate-400 truncate">
{userId ? `ID: ${userId.slice(0, 10)}...` : 'Aktif'}
</span>
</div>
</Link>
{/* Main Navigation */}
<div className="space-y-1.5">
<div className="px-3 pb-1.5 text-[10px] font-black uppercase tracking-widest text-slate-400 dark:text-slate-500">
Navigasi Utama
</div>
<nav className="space-y-1">
{menuItems.map((item) => {
const Icon = item.icon
const isActive = pathname === item.href
return (
<Link
key={item.href}
href={item.href}
onClick={onClose}
className={cn(
"flex items-center gap-3 px-3.5 py-3 rounded-2xl text-sm font-semibold transition-all duration-200 group relative",
isActive
? "bg-[#1B2CC1] text-white shadow-md shadow-[#1B2CC1]/25 font-bold"
: "text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100/80 dark:hover:bg-slate-800/60"
)}
>
<div className={cn(
"w-8 h-8 rounded-xl flex items-center justify-center transition-colors",
isActive
? "bg-white/15 text-white"
: "bg-slate-100 dark:bg-slate-800 text-slate-500 group-hover:text-[#1B2CC1] group-hover:bg-[#1B2CC1]/10"
)}>
<Icon className="w-4 h-4" />
</div>
<div className="flex flex-col min-w-0">
<span className="leading-tight">{item.name}</span>
<span className={cn(
"text-[10px] font-normal truncate mt-0.5",
isActive ? "text-blue-100" : "text-slate-400"
)}>
{item.desc}
</span>
</div>
</Link>
)
})}
</nav>
</div>
</div>
{/* Bottom Feature Card */}
<div className="p-4 rounded-2xl bg-gradient-to-br from-blue-50/80 to-slate-50 dark:from-slate-800/60 dark:to-slate-900 border border-blue-100/80 dark:border-slate-800 space-y-2">
<div className="flex items-center gap-2 text-xs font-bold text-slate-800 dark:text-slate-200">
<div className="w-5 h-5 rounded-lg bg-[#1B2CC1]/10 text-[#1B2CC1] flex items-center justify-center">
<Info className="w-3 h-3" />
</div>
<span>Auto Rekap</span>
</div>
<p className="text-[11px] text-slate-500 dark:text-slate-400 leading-relaxed">
Gunakan Generator Rekap pada detail PO untuk salin ringkasan belanja otomatis ke chat grup.
</p>
</div>
</aside>
</>
)
}