113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
import { useState } from "react"
|
|
import { Link, Outlet, useLocation } from "react-router-dom"
|
|
import {
|
|
LayoutDashboard,
|
|
Package,
|
|
Zap,
|
|
Info,
|
|
Phone,
|
|
Share2,
|
|
Gift,
|
|
Menu,
|
|
X,
|
|
Home,
|
|
Image,
|
|
LogOut,
|
|
} from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { cn } from "@/utils/helpers"
|
|
import { useAuth } from "@/context/AuthContext"
|
|
|
|
const menuItems = [
|
|
{ href: "/admin", icon: LayoutDashboard, label: "Dashboard" },
|
|
{ href: "/admin/hero", icon: Home, label: "Konten" },
|
|
{ href: "/admin/packages", icon: Package, label: "Paket Internet" },
|
|
{ href: "/admin/benefits", icon: Zap, label: "Keunggulan" },
|
|
{ href: "/admin/about", icon: Info, label: "Tentang Kami" },
|
|
{ href: "/admin/gallery", icon: Image, label: "Galeri" },
|
|
{ href: "/admin/contact", icon: Phone, label: "Kontak" },
|
|
{ href: "/admin/social", icon: Share2, label: "Media Sosial" },
|
|
{ href: "/admin/promo", icon: Gift, label: "Promo Pop-up" },
|
|
]
|
|
|
|
export default function AdminLayout() {
|
|
const [sidebarOpen, setSidebarOpen] = useState(false)
|
|
const location = useLocation()
|
|
const { logout } = useAuth()
|
|
|
|
const handleSignOut = async () => {
|
|
await logout()
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-100">
|
|
<div className="bg-yellow-300 border-b-4 border-black px-4 h-14 flex items-center justify-between lg:hidden">
|
|
<span className="font-black">Admin Panel</span>
|
|
<div className="flex items-center gap-2">
|
|
<Button variant="brutal" size="icon" onClick={handleSignOut} aria-label="Logout">
|
|
<LogOut className="h-4 w-4" />
|
|
</Button>
|
|
<Button variant="brutal" size="icon" onClick={() => setSidebarOpen(!sidebarOpen)}>
|
|
{sidebarOpen ? <X className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex">
|
|
<aside
|
|
className={cn(
|
|
"fixed inset-y-0 left-0 z-40 w-64 bg-white border-r-4 border-black transform transition-transform lg:translate-x-0 lg:static lg:inset-auto",
|
|
sidebarOpen ? "translate-x-0" : "-translate-x-full"
|
|
)}
|
|
>
|
|
<div className="h-14 flex items-center justify-between px-4 border-b-4 border-black bg-yellow-300">
|
|
<Link to="/" className="font-black text-lg">
|
|
LJN Admin
|
|
</Link>
|
|
<Button
|
|
variant="brutal"
|
|
size="icon"
|
|
onClick={handleSignOut}
|
|
aria-label="Logout"
|
|
className="hidden lg:inline-flex"
|
|
>
|
|
<LogOut className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
<nav className="p-2 space-y-1">
|
|
{menuItems.map((item) => {
|
|
const isActive = location.pathname === item.href
|
|
return (
|
|
<Link
|
|
key={item.href}
|
|
to={item.href}
|
|
onClick={() => setSidebarOpen(false)}
|
|
className={cn(
|
|
"flex items-center gap-3 px-3 py-2.5 rounded-md font-bold text-sm transition-colors",
|
|
isActive
|
|
? "bg-black text-yellow-300"
|
|
: "hover:bg-gray-100 text-gray-700"
|
|
)}
|
|
>
|
|
<item.icon className="h-4 w-4" />
|
|
{item.label}
|
|
</Link>
|
|
)
|
|
})}
|
|
</nav>
|
|
</aside>
|
|
|
|
{sidebarOpen && (
|
|
<div
|
|
className="fixed inset-0 z-30 bg-black/50 lg:hidden"
|
|
onClick={() => setSidebarOpen(false)}
|
|
/>
|
|
)}
|
|
|
|
<main className="flex-1 min-h-screen p-4 sm:p-6 lg:p-8">
|
|
<Outlet />
|
|
</main>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |