292 lines
12 KiB
TypeScript
292 lines
12 KiB
TypeScript
import React, { useState } from "react";
|
|
import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
|
|
import { useThemeStore } from "../../hooks/use-theme";
|
|
import { useAuthStore } from "../../hooks/use-auth";
|
|
import {
|
|
ShieldCheck,
|
|
ClipboardCheck,
|
|
FolderGit2,
|
|
Users,
|
|
LogOut,
|
|
Menu,
|
|
X,
|
|
Sun,
|
|
Moon,
|
|
ChevronRight,
|
|
ChevronLeft,
|
|
Globe
|
|
} from "lucide-react";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import { ChatDrawer } from "../../components/ui/ChatDrawer";
|
|
|
|
export const AdminLayout: React.FC = () => {
|
|
const { user, logout } = useAuthStore();
|
|
const { theme, toggleTheme } = useThemeStore();
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
|
|
const handleLogout = () => {
|
|
logout();
|
|
navigate("/login");
|
|
};
|
|
|
|
const navItems = [
|
|
{ name: "Partners", path: "/admin/partners", icon: Users },
|
|
{ name: "Approvals Queue", path: "/admin/approvals", icon: ClipboardCheck },
|
|
{ name: "Legal Templates", path: "/admin/legal", icon: ShieldCheck },
|
|
{ name: "Manage Catalog", path: "/admin/assets", icon: FolderGit2 },
|
|
{ name: "Ecosystem Manager", path: "/admin/ecosystem", icon: Globe },
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen flex flex-col md:flex-row bg-ink-50 text-ink-900 font-sans transition-colors duration-500 selection:bg-ink-900/10 overflow-hidden">
|
|
{/* ── Desktop Sidebar ── */}
|
|
<aside
|
|
className={`hidden md:flex md:flex-col md:sticky md:top-0 md:h-screen bg-ink-0 border-r border-ink-200 shrink-0 z-20 transition-all duration-300 relative ${isCollapsed ? "md:w-[80px]" : "md:w-[280px]"}`}
|
|
>
|
|
{/* Toggle Button Floating on the Border */}
|
|
<button
|
|
onClick={() => setIsCollapsed(!isCollapsed)}
|
|
className="hidden md:flex absolute top-9 -right-3 w-6 h-6 rounded-full border border-ink-200 bg-ink-0 text-ink-500 hover:text-ink-900 hover:shadow-md items-center justify-center transition-all z-30 cursor-pointer shadow-sm"
|
|
title={isCollapsed ? "Expand Sidebar" : "Collapse Sidebar"}
|
|
>
|
|
{isCollapsed ? (
|
|
<ChevronRight className="w-3.5 h-3.5" />
|
|
) : (
|
|
<ChevronLeft className="w-3.5 h-3.5" />
|
|
)}
|
|
</button>
|
|
|
|
{/* Branding */}
|
|
<div
|
|
className={`h-24 flex items-center border-b border-ink-200 transition-all duration-300 ${isCollapsed ? "justify-center px-4" : "px-8"}`}
|
|
>
|
|
<Link to="/admin" className="flex items-center gap-3 shrink-0 group">
|
|
<div className="w-10 h-10 rounded-xl flex items-center justify-center bg-ink-0 shadow-sm border border-ink-200 group-hover:scale-105 transition-all duration-300 p-1">
|
|
<img src="/logo.png" alt="Tech4Biz" className="w-full h-full object-contain" />
|
|
</div>
|
|
{!isCollapsed && (
|
|
<div className="flex flex-col animate-fade-in">
|
|
<span className="text-lg font-extrabold tracking-tight leading-none text-ink-900">
|
|
Tech4Biz
|
|
</span>
|
|
<span className="text-[10px] font-bold uppercase tracking-widest text-ink-900 mt-1">
|
|
Admin Console
|
|
</span>
|
|
</div>
|
|
)}
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav
|
|
className={`flex-1 py-8 space-y-2 overflow-y-auto transition-all duration-300 ${isCollapsed ? "px-2" : "px-4"}`}
|
|
>
|
|
{navItems.map((item) => {
|
|
const Icon = item.icon;
|
|
const isActive = location.pathname === item.path;
|
|
return (
|
|
<Link
|
|
key={item.path}
|
|
to={item.path}
|
|
className={`flex items-center gap-3 py-3 rounded-xl transition-all duration-300 font-semibold tracking-wide text-sm group relative ${
|
|
isCollapsed ? "justify-center px-0" : "px-4"
|
|
} ${
|
|
isActive
|
|
? "bg-ink-100 text-ink-900 border border-ink-300 shadow-sm"
|
|
: "text-ink-500 hover:text-ink-900 hover:bg-ink-100"
|
|
}`}
|
|
title={isCollapsed ? item.name : undefined}
|
|
>
|
|
<Icon
|
|
className={`w-5 h-5 transition-transform group-hover:scale-110 ${isActive ? "text-ink-900" : "text-ink-400 group-hover:text-ink-900"}`}
|
|
/>
|
|
{!isCollapsed && <span>{item.name}</span>}
|
|
{!isCollapsed && isActive && (
|
|
<ChevronRight className="w-4 h-4 ml-auto opacity-50" />
|
|
)}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* Footer */}
|
|
<div
|
|
className={`border-t border-ink-200 bg-ink-50 transition-all duration-300 ${isCollapsed ? "p-3" : "p-5"}`}
|
|
>
|
|
<div
|
|
className={`flex items-center mb-4 transition-all duration-300 ${isCollapsed ? "justify-center" : "justify-between"}`}
|
|
>
|
|
{!isCollapsed && (
|
|
<p className="text-[10px] font-bold text-ink-400 uppercase tracking-widest">
|
|
Appearance
|
|
</p>
|
|
)}
|
|
<button
|
|
onClick={toggleTheme}
|
|
className="p-2 rounded-lg bg-ink-0 border border-ink-200 text-ink-500 hover:text-ink-900 hover:shadow-sm transition-all group cursor-pointer"
|
|
>
|
|
{theme === "dark" ? (
|
|
<Sun className="w-4 h-4 group-hover:rotate-90 transition-transform" />
|
|
) : (
|
|
<Moon className="w-4 h-4 group-hover:-rotate-12 transition-transform" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
{!isCollapsed ? (
|
|
<div className="flex items-center gap-3 p-3 rounded-2xl bg-ink-0 border border-ink-200 mb-4 shadow-sm">
|
|
<div className="w-9 h-9 rounded-full bg-ink-900 flex items-center justify-center text-ink-0 font-bold shadow-sm">
|
|
{user?.email?.charAt(0).toUpperCase()}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-bold text-ink-900 truncate">
|
|
{user?.email}
|
|
</p>
|
|
<p className="text-[10px] uppercase font-bold text-ink-500 tracking-wider truncate">
|
|
Administrator
|
|
</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div
|
|
className="w-9 h-9 rounded-full bg-ink-900 flex items-center justify-center text-ink-0 font-bold shadow-sm mx-auto mb-4 animate-fade-in"
|
|
title={user?.email || ""}
|
|
>
|
|
{user?.email?.charAt(0).toUpperCase()}
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
onClick={handleLogout}
|
|
className={`w-full flex items-center justify-center gap-2 rounded-xl text-sm font-bold tracking-wide text-red-600 hover:bg-red-500/10 transition-colors border border-transparent hover:border-red-200 cursor-pointer ${isCollapsed ? "py-2 px-0" : "px-4 py-2.5"}`}
|
|
title={isCollapsed ? "Sign Out" : undefined}
|
|
>
|
|
<LogOut className="w-4 h-4" />
|
|
{!isCollapsed && <span>Sign Out</span>}
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* ── Main Content Area ── */}
|
|
<main className="flex-1 flex flex-col relative w-full h-screen overflow-hidden bg-ink-50">
|
|
{/* Ambient Background Glows */}
|
|
<div className="fixed top-0 right-0 w-[600px] h-[600px] bg-ink-900/5 rounded-full blur-[150px] pointer-events-none -z-10" />
|
|
|
|
{/* Mobile Header */}
|
|
<header className="md:hidden sticky top-0 z-40 h-16 flex items-center justify-between px-4 bg-ink-0 border-b border-ink-200 shadow-sm shrink-0">
|
|
<Link to="/admin" className="flex items-center gap-2">
|
|
<div className="w-8 h-8 rounded-lg flex items-center justify-center bg-ink-0 border border-ink-200 p-1 shadow-sm">
|
|
<img src="/logo.png" alt="Tech4Biz" className="w-full h-full object-contain" />
|
|
</div>
|
|
<span className="text-sm font-extrabold tracking-tight text-ink-900">
|
|
Tech4Biz
|
|
</span>
|
|
</Link>
|
|
<button
|
|
onClick={() => setMobileOpen(true)}
|
|
className="p-2 rounded-lg border border-ink-200 text-ink-600"
|
|
>
|
|
<Menu className="w-5 h-5" />
|
|
</button>
|
|
</header>
|
|
|
|
{/* Mobile Menu Drawer */}
|
|
<AnimatePresence>
|
|
{mobileOpen && (
|
|
<>
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
onClick={() => setMobileOpen(false)}
|
|
className="fixed inset-0 bg-ink-900/40 backdrop-blur-sm z-50 md:hidden"
|
|
/>
|
|
<motion.div
|
|
initial={{ x: "100%" }}
|
|
animate={{ x: 0 }}
|
|
exit={{ x: "100%" }}
|
|
transition={{ type: "spring", damping: 25, stiffness: 200 }}
|
|
className="fixed right-0 top-0 bottom-0 w-72 bg-ink-0 shadow-2xl z-50 border-l border-ink-200 flex flex-col md:hidden"
|
|
>
|
|
<div className="p-4 border-b border-ink-200 flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-6 h-6 rounded-md flex items-center justify-center bg-ink-0 border border-ink-200 p-0.5 shadow-sm">
|
|
<img src="/logo.png" alt="Tech4Biz" className="w-full h-full object-contain" />
|
|
</div>
|
|
<span className="font-extrabold text-ink-900 text-sm">Tech4Biz</span>
|
|
</div>
|
|
<button
|
|
onClick={() => setMobileOpen(false)}
|
|
className="p-2 rounded-lg bg-ink-100"
|
|
>
|
|
<X className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
<nav className="flex-1 overflow-y-auto p-4 space-y-2">
|
|
{navItems.map((item) => (
|
|
<Link
|
|
key={item.path}
|
|
to={item.path}
|
|
onClick={() => setMobileOpen(false)}
|
|
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-semibold ${location.pathname === item.path ? "bg-ink-100 text-ink-900 border border-ink-300" : "text-ink-500 hover:text-ink-900"}`}
|
|
>
|
|
<item.icon className="w-5 h-5" />
|
|
<span>{item.name}</span>
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
<div className="p-4 border-t border-ink-200 space-y-4">
|
|
<button
|
|
onClick={toggleTheme}
|
|
className="flex items-center justify-between w-full p-3 rounded-xl bg-ink-50 border border-ink-200 text-sm font-bold text-ink-500 hover:text-ink-900"
|
|
>
|
|
Theme{" "}
|
|
{theme === "dark" ? (
|
|
<Sun className="w-4 h-4" />
|
|
) : (
|
|
<Moon className="w-4 h-4" />
|
|
)}
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
setMobileOpen(false);
|
|
handleLogout();
|
|
}}
|
|
className="w-full py-3 rounded-xl bg-red-500/10 text-red-650 font-bold text-sm"
|
|
>
|
|
Sign Out
|
|
</button>
|
|
</div>
|
|
</motion.div>
|
|
</>
|
|
)}
|
|
</AnimatePresence>
|
|
|
|
<div className="flex-1 w-full max-w-[1600px] px-4 py-4 md:px-8 mx-auto relative z-10 overflow-hidden flex flex-col min-h-0">
|
|
<Outlet />
|
|
</div>
|
|
|
|
{/* Footer */}
|
|
<footer className="border-t border-ink-200 bg-ink-0/50 backdrop-blur-md shrink-0">
|
|
<div className="max-w-[1600px] mx-auto px-4 md:px-8 py-3 flex flex-col md:flex-row justify-between items-center gap-4 text-xs font-medium text-ink-500">
|
|
<p>© 2026 Tech4Biz Solutions Inc. Admin Console.</p>
|
|
<div className="flex gap-6">
|
|
<span className="hover:text-ink-900 cursor-pointer transition-colors">
|
|
Security Compliance
|
|
</span>
|
|
<span className="hover:text-ink-900 cursor-pointer transition-colors">
|
|
System Status
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
</main>
|
|
<ChatDrawer />
|
|
</div>
|
|
);
|
|
};
|
|
export default AdminLayout;
|