Files
2026-05-21 11:59:02 +07:00

183 lines
6.1 KiB
TypeScript

"use client";
import { useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import Image from "next/image";
import { signOut } from "next-auth/react";
import {
ArrowLeft,
ArrowUpRight,
Banknote,
Compass,
IdCard,
LayoutDashboard,
Mail,
Menu,
ScrollText,
Settings,
Users,
X,
type LucideIcon,
} from "lucide-react";
import { AdminSearchBar } from "@/features/admin/components/admin-search-bar";
const NAV_ITEMS: { href: string; label: string; icon: LucideIcon }[] = [
{ href: "/admin", label: "Dashboard", icon: LayoutDashboard },
{ href: "/admin/trips", label: "Trips", icon: Compass },
{ href: "/admin/users", label: "Users", icon: Users },
{ href: "/admin/verifications", label: "Verifikasi", icon: IdCard },
{ href: "/admin/refunds", label: "Refund", icon: ArrowLeft },
{ href: "/admin/payouts", label: "Payout", icon: Banknote },
{ href: "/admin/emails", label: "Email", icon: Mail },
{ href: "/admin/audit-log", label: "Audit Log", icon: ScrollText },
{ href: "/admin/system", label: "System", icon: Settings },
];
interface AdminSidebarProps {
user: { name: string; email: string };
}
export function AdminSidebar({ user }: AdminSidebarProps) {
const pathname = usePathname();
const [open, setOpen] = useState(false);
return (
<>
{/* Mobile top bar */}
<header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-neutral-200 bg-white px-4 lg:hidden">
<Link href="/admin" className="flex items-center gap-2">
<Image
src="/images/SeTrip.png"
alt=""
width={28}
height={28}
className="h-7 w-7 object-contain"
/>
<span className="text-sm font-bold text-neutral-800">
SeTrip <span className="text-primary-600">Admin</span>
</span>
</Link>
<button
type="button"
onClick={() => setOpen((v) => !v)}
className="flex h-9 w-9 items-center justify-center rounded-lg text-neutral-600 hover:bg-neutral-100"
aria-label="Toggle menu"
aria-expanded={open}
>
{open ? (
<X size={20} strokeWidth={2} aria-hidden />
) : (
<Menu size={20} strokeWidth={2} aria-hidden />
)}
</button>
</header>
{/* Mobile drawer backdrop */}
{open && (
<button
type="button"
aria-label="Tutup menu"
onClick={() => setOpen(false)}
className="fixed inset-0 z-30 bg-neutral-900/30 lg:hidden"
/>
)}
{/* Sidebar */}
<aside
className={`fixed inset-y-0 left-0 z-40 flex w-64 flex-col border-r border-neutral-200 bg-white transition-transform lg:sticky lg:top-0 lg:h-screen lg:translate-x-0 ${
open ? "translate-x-0" : "-translate-x-full"
}`}
>
<div className="flex h-16 items-center gap-2.5 border-b border-neutral-200 px-5">
<Image
src="/images/SeTrip.png"
alt=""
width={32}
height={32}
className="h-8 w-8 object-contain"
/>
<div className="leading-tight">
<p className="text-base font-bold text-neutral-800">
SeTrip
</p>
<p className="text-[10px] font-semibold uppercase tracking-wider text-primary-600">
Admin Panel
</p>
</div>
</div>
<div className="border-b border-neutral-100 p-3">
<AdminSearchBar />
</div>
<nav className="flex-1 overflow-y-auto p-3">
<ul className="space-y-1">
{NAV_ITEMS.map((item) => {
const isActive =
pathname === item.href ||
(item.href !== "/admin" && pathname?.startsWith(item.href));
const Icon = item.icon;
return (
<li key={item.href}>
<Link
href={item.href}
onClick={() => setOpen(false)}
className={`flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors ${
isActive
? "bg-primary-600 text-white"
: "text-neutral-700 hover:bg-neutral-100"
}`}
>
<Icon size={20} strokeWidth={1.75} aria-hidden />
<span>{item.label}</span>
</Link>
</li>
);
})}
</ul>
<div className="my-4 border-t border-neutral-100" />
<ul className="space-y-1">
<li>
<Link
href="/"
onClick={() => setOpen(false)}
className="flex items-center gap-3 rounded-lg px-3 py-2 text-xs font-medium text-neutral-500 hover:bg-neutral-100 hover:text-neutral-700"
>
<ArrowUpRight size={16} strokeWidth={1.75} aria-hidden />
<span>Lihat situs publik</span>
</Link>
</li>
</ul>
</nav>
<div className="border-t border-neutral-100 p-3">
<div className="flex items-center gap-2 rounded-lg px-2 py-2">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary-600 text-xs font-bold text-white">
{user.name.charAt(0).toUpperCase()}
</div>
<div className="min-w-0 flex-1">
<p className="truncate text-xs font-semibold text-neutral-800">
{user.name}
</p>
<p className="truncate text-[10px] text-neutral-500">
{user.email}
</p>
</div>
<button
type="button"
onClick={() => signOut({ callbackUrl: "/" })}
className="rounded-lg px-2 py-1 text-[11px] font-medium text-neutral-500 hover:bg-red-50 hover:text-red-600"
title="Keluar"
>
Keluar
</button>
</div>
</div>
</aside>
</>
);
}