120 lines
No EOL
4 KiB
TypeScript
120 lines
No EOL
4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useRouter, usePathname } from "next/navigation";
|
|
import Link from "next/link";
|
|
|
|
interface NavItem {
|
|
name: string;
|
|
href: string;
|
|
icon: string;
|
|
}
|
|
|
|
const navItems: NavItem[] = [
|
|
{ name: "Dashboard", href: "/admin", icon: "📊" },
|
|
{ name: "Families", href: "/admin/families", icon: "🏠" },
|
|
{ name: "Users", href: "/admin/users", icon: "👥" },
|
|
{ name: "Children", href: "/admin/children", icon: "👶" },
|
|
{ name: "Revenue", href: "/admin/revenue", icon: "💰" },
|
|
{ name: "Analytics", href: "/admin/analytics", icon: "📈" },
|
|
{ name: "Support", href: "/admin/support", icon: "🎫" },
|
|
{ name: "Settings", href: "/admin/settings", icon: "⚙️" },
|
|
];
|
|
|
|
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const [sidebarOpen, setSidebarOpen] = useState(true);
|
|
const [admin, setAdmin] = useState<{ username: string; role: string } | null>(null);
|
|
|
|
// Check if this is the login page - don't show sidebar
|
|
const isLoginPage = pathname === "/admin-login";
|
|
|
|
useEffect(() => {
|
|
// Only check auth if not on login page
|
|
if (isLoginPage) return;
|
|
|
|
const token = localStorage.getItem("admin_token");
|
|
if (!token) {
|
|
router.push("/admin/login");
|
|
return;
|
|
}
|
|
const stored = localStorage.getItem("admin_user");
|
|
if (stored) {
|
|
setAdmin(JSON.parse(stored));
|
|
}
|
|
}, [router, isLoginPage]);
|
|
|
|
const handleLogout = () => {
|
|
localStorage.removeItem("admin_token");
|
|
localStorage.removeItem("admin_user");
|
|
router.push("/admin/login");
|
|
};
|
|
|
|
// Login page - render without sidebar
|
|
if (isLoginPage) {
|
|
return (
|
|
<div className="min-h-screen bg-gray-900 text-white">
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Main layout with sidebar
|
|
return (
|
|
<div className="min-h-screen bg-gray-900 text-white flex">
|
|
{/* Sidebar */}
|
|
<aside className={`${sidebarOpen ? "w-64" : "w-16"} bg-gray-800 flex-shrink-0 transition-all duration-300 flex flex-col`}>
|
|
{/* Header */}
|
|
<div className="p-4 flex items-center justify-between border-b border-gray-700">
|
|
{sidebarOpen && (
|
|
<Link href="/admin" className="text-lg font-bold text-rose-400">
|
|
Tia Admin
|
|
</Link>
|
|
)}
|
|
<button onClick={() => setSidebarOpen(!sidebarOpen)} className="text-gray-400 hover:text-white">
|
|
{sidebarOpen ? "◀" : "▶"}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 p-2 space-y-1 overflow-y-auto">
|
|
{navItems.map((item) => {
|
|
const isActive = pathname === item.href || (item.href !== "/admin" && pathname.startsWith(item.href));
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
href={item.href}
|
|
className={`flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors ${
|
|
isActive ? "bg-rose-500/20 text-rose-400" : "text-gray-400 hover:bg-gray-700 hover:text-white"
|
|
}`}
|
|
>
|
|
<span className="text-lg">{item.icon}</span>
|
|
{sidebarOpen && <span className="font-medium">{item.name}</span>}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* Footer */}
|
|
<div className="mt-auto p-4 border-t border-gray-700">
|
|
{sidebarOpen && admin && (
|
|
<div className="mb-3">
|
|
<div className="text-sm font-medium">{admin.username}</div>
|
|
<div className="text-xs text-gray-400">{admin.role}</div>
|
|
</div>
|
|
)}
|
|
<button
|
|
onClick={handleLogout}
|
|
className="w-full px-3 py-2 bg-gray-700 text-gray-400 hover:text-white rounded-lg text-sm"
|
|
>
|
|
{sidebarOpen ? "Logout" : "🚪"}
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Main Content */}
|
|
<main className="flex-1 overflow-auto">{children}</main>
|
|
</div>
|
|
);
|
|
} |