170 lines
7.9 KiB
JavaScript
170 lines
7.9 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { Link, usePage } from '@inertiajs/react';
|
|
import {
|
|
LayoutDashboard,
|
|
Users,
|
|
Briefcase,
|
|
CreditCard,
|
|
Megaphone,
|
|
Settings,
|
|
LogOut,
|
|
Menu,
|
|
Bell,
|
|
Shield,
|
|
BadgeDollarSign,
|
|
ShieldCheck,
|
|
ShieldAlert,
|
|
BellRing,
|
|
BarChart3,
|
|
History,
|
|
List,
|
|
LifeBuoy,
|
|
Heart
|
|
} from 'lucide-react';
|
|
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
|
|
|
export default function AdminLayout({ children, title = 'Dashboard' }) {
|
|
const { url, props } = usePage();
|
|
const { auth } = props;
|
|
const user = auth?.user || { name: 'Admin Portal', email: 'admin@marketplace.com' };
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const navItems = [
|
|
{ name: 'Dashboard', href: '/admin/dashboard', icon: LayoutDashboard },
|
|
{ name: 'Worker Profiles', href: '/admin/workers', icon: Users },
|
|
{ name: 'Employers', href: '/admin/employers', icon: Briefcase },
|
|
{ name: 'OCR Verification', href: '/admin/workers/verifications', icon: ShieldCheck },
|
|
{ name: 'Safety & Moderation', href: '/admin/safety', icon: ShieldAlert },
|
|
{ name: 'Support Tickets', href: '/admin/tickets', icon: LifeBuoy },
|
|
{ name: 'Payments', href: '/admin/payments', icon: BadgeDollarSign },
|
|
{ name: 'Campaigns & Alerts', href: '/admin/notifications', icon: BellRing },
|
|
{ name: 'Reports & Analytics', href: '/admin/analytics', icon: BarChart3 },
|
|
{ name: 'System Audit Logs', href: '/admin/audit-logs', icon: History },
|
|
{ name: 'Charity Events', href: '/admin/announcements', icon: Heart },
|
|
{ name: 'Skills', href: '/admin/master-data/skills', icon: Settings },
|
|
{ name: 'Reason Master', href: '/admin/master-data/reasons', icon: List },
|
|
];
|
|
|
|
const getInitials = (name) => {
|
|
return name
|
|
?.split(' ')
|
|
.map((n) => n[0])
|
|
.join('')
|
|
.toUpperCase() || 'A';
|
|
};
|
|
|
|
const SidebarContent = ({ isMobile }) => (
|
|
<div className="flex flex-col h-full bg-white font-sans select-none">
|
|
{/* Top: App Logo + Admin Label */}
|
|
<div className="flex items-center space-x-3 px-6 h-16 border-b border-gray-100 flex-shrink-0">
|
|
<div className="w-9 h-9 bg-[#0F6E56]/10 rounded-xl flex items-center justify-center shadow-inner">
|
|
<Shield className="w-5 h-5 text-[#0F6E56]" />
|
|
</div>
|
|
<span className="font-semibold text-gray-800 text-lg tracking-tight">Admin Portal</span>
|
|
</div>
|
|
|
|
{/* Nav Items */}
|
|
<div className="flex-1 overflow-y-auto px-3 py-4 space-y-1.5">
|
|
{navItems.map((item) => {
|
|
const Icon = item.icon;
|
|
const active = url.startsWith(item.href);
|
|
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
href={item.href}
|
|
onClick={() => isMobile && setOpen(false)}
|
|
className={`flex items-center space-x-3 px-4 py-3 rounded-xl text-sm transition-all duration-200 group ${
|
|
active
|
|
? 'bg-[#0F6E56] text-white font-bold shadow-lg shadow-teal-900/20 scale-[1.02]'
|
|
: 'text-gray-500 hover:bg-gray-50 hover:text-gray-900'
|
|
}`}
|
|
>
|
|
<Icon className={`w-5 h-5 flex-shrink-0 transition-colors ${active ? 'text-white' : 'text-gray-400 group-hover:text-gray-900'}`} />
|
|
<span className="tracking-tight">{item.name}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Bottom: Admin Avatar, Name, Email, Logout */}
|
|
<div className="p-4 border-t border-gray-100 flex-shrink-0">
|
|
<div className="flex items-center space-x-3 mb-3 px-2">
|
|
<div className="w-9 h-9 rounded-full bg-[#0F6E56] text-white flex items-center justify-center font-medium text-sm flex-shrink-0 shadow-sm">
|
|
{getInitials(user.name)}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-medium text-gray-900 truncate">{user.name}</p>
|
|
<p className="text-xs text-gray-500 truncate">{user.email}</p>
|
|
</div>
|
|
</div>
|
|
<Link
|
|
href="/admin/logout"
|
|
method="post"
|
|
as="button"
|
|
className="w-full flex items-center space-x-3 px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg transition-colors font-medium justify-start focus:outline-none"
|
|
>
|
|
<LogOut className="w-5 h-5 flex-shrink-0" />
|
|
<span>Log Out</span>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 flex font-sans">
|
|
{/* Fixed Left Sidebar (Desktop lg+) */}
|
|
<aside className="hidden lg:flex lg:flex-col lg:w-[240px] lg:fixed lg:inset-y-0 lg:border-r lg:border-gray-100 lg:bg-white lg:z-30">
|
|
<SidebarContent isMobile={false} />
|
|
</aside>
|
|
|
|
{/* Main Content Area */}
|
|
<div className="flex-1 flex flex-col min-w-0 lg:ml-[240px]">
|
|
{/* Top Bar */}
|
|
<header className="h-16 bg-white border-b border-gray-100 flex items-center justify-between px-6 sticky top-0 z-20 shadow-sm">
|
|
<div className="flex items-center space-x-4">
|
|
{/* Mobile menu trigger */}
|
|
<div className="lg:hidden">
|
|
<Sheet open={open} onOpenChange={setOpen}>
|
|
<SheetTrigger asChild>
|
|
<button
|
|
type="button"
|
|
className="p-2 text-gray-600 hover:text-gray-900 focus:outline-none rounded-lg hover:bg-gray-50 transition-colors flex items-center justify-center"
|
|
>
|
|
<Menu className="w-6 h-6" />
|
|
</button>
|
|
</SheetTrigger>
|
|
<SheetContent side="left" className="p-0 w-[240px] bg-white border-r border-gray-100">
|
|
<SidebarContent isMobile={true} />
|
|
</SheetContent>
|
|
</Sheet>
|
|
</div>
|
|
<h1 className="text-xl font-semibold text-gray-800 tracking-tight">{title}</h1>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-4">
|
|
<button
|
|
type="button"
|
|
className="p-2 text-gray-500 hover:text-gray-800 rounded-full hover:bg-gray-50 relative focus:outline-none transition-colors"
|
|
>
|
|
<Bell className="w-5 h-5" />
|
|
<span className="absolute top-2 right-2 w-2 h-2 bg-[#0F6E56] rounded-full ring-2 ring-white" />
|
|
</button>
|
|
|
|
<div className="flex items-center space-x-3 border-l border-gray-100 pl-4">
|
|
<div className="w-8 h-8 rounded-full bg-[#0F6E56] text-white flex items-center justify-center font-medium text-xs shadow-sm">
|
|
{getInitials(user.name)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Content Slot */}
|
|
<main className="flex-1 p-6 max-w-7xl w-full mx-auto">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|