176 lines
7.8 KiB
TypeScript
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>
|
|
</>
|
|
)
|
|
}
|