nxtgauge-admin-solid/src/components/AdminSidebar.tsx

199 lines
11 KiB
TypeScript
Raw Normal View History

import { A, useLocation } from '@solidjs/router';
import { For, Show } from 'solid-js';
import {
LayoutGrid, Building2, Briefcase, Users, ShieldCheck, FileText,
LayoutDashboard, ClipboardList, UserRoundSearch, UserCircle,
Camera, Palette, BookOpen, Code2, BriefcaseBusiness, HandHelping,
WalletCards, CreditCard, Tag, Percent, Receipt, ShoppingCart,
FileCheck, Star, HeadphonesIcon, BarChart3, BookMarked, Bell,
ChevronLeft, BadgeCheck, Activity, Film, Utensils, PenTool,
MessageSquare, Megaphone,
} from 'lucide-solid';
type NavItem = {
href: string;
label: string;
icon: any;
aliasPrefix?: string;
};
// Groups match Figma sidebar order with dividers between sections
const GROUPS: NavItem[][] = [
// ── Internal management ──────────────────────────────────────────────────
[
{ href: '/admin', label: 'Dashboard', icon: LayoutGrid },
{ href: '/admin/department', label: 'Department Management', icon: Building2 },
{ href: '/admin/designation', label: 'Designation Management', icon: Briefcase },
{ href: '/admin/internal-role-management', label: 'Internal Role Management', icon: ShieldCheck },
{ href: '/admin/employees', label: 'Employee Management', icon: Users },
],
// ── External configuration ───────────────────────────────────────────────
[
{ href: '/admin/runtime-roles', label: 'External Role Management', icon: ShieldCheck, aliasPrefix: '/admin/external-role-management' },
{ href: '/admin/onboarding-management', label: 'External Onboarding Management', icon: FileText, aliasPrefix: '/admin/onboarding-schemas' },
{ href: '/admin/internal-dashboard-management', label: 'Internal Dashboard Management', icon: LayoutDashboard },
{ href: '/admin/external-dashboard-management', label: 'External Dashboard Management', icon: LayoutDashboard, aliasPrefix: '/admin/role-ui-configs' },
],
// ── Compliance ───────────────────────────────────────────────────────────
[
{ href: '/admin/verification-status', label: 'Verification Management', icon: BadgeCheck },
{ href: '/admin/approval', label: 'Approval Management', icon: ClipboardList },
],
// ── External users ───────────────────────────────────────────────────────
[
{ href: '/admin/users', label: 'Users Management', icon: UserRoundSearch },
{ href: '/admin/company', label: 'Company Management', icon: Building2 },
{ href: '/admin/candidate', label: 'Candidate Management', icon: UserCircle },
{ href: '/admin/customer', label: 'Customer Management', icon: UserCircle },
{ href: '/admin/photographer', label: 'Photographer Management', icon: Camera },
{ href: '/admin/makeup-artist', label: 'Makeup Artist Management', icon: Palette },
{ href: '/admin/tutors', label: 'Tutor Management', icon: BookOpen },
{ href: '/admin/developers', label: 'Developer Management', icon: Code2 },
{ href: '/admin/fitness-trainers', label: 'Fitness Trainer Management', icon: Activity },
{ href: '/admin/graphic-designers', label: 'Graphic Designer Management', icon: PenTool },
{ href: '/admin/social-media-managers', label: 'Social Media Management', icon: Megaphone },
{ href: '/admin/video-editors', label: 'Video Editor Management', icon: Film },
{ href: '/admin/catering-services', label: 'Catering Services Management', icon: Utensils },
],
// ── Business operations ──────────────────────────────────────────────────
[
{ href: '/admin/jobs', label: 'Jobs Management', icon: BriefcaseBusiness },
{ href: '/admin/leads', label: 'Leads Management', icon: HandHelping },
{ href: '/admin/applications', label: 'Applications Management', icon: ClipboardList },
{ href: '/admin/responses', label: 'Responses Management', icon: MessageSquare },
{ href: '/admin/review', label: 'Review Management', icon: Star },
],
// ── Finance ──────────────────────────────────────────────────────────────
[
{ href: '/admin/pricing', label: 'Pricing Management', icon: WalletCards },
{ href: '/admin/credit', label: 'Credit Management', icon: CreditCard },
{ href: '/admin/coupon', label: 'Coupon Management', icon: Tag },
{ href: '/admin/discount', label: 'Discount Management', icon: Percent },
{ href: '/admin/tax', label: 'Tax Management', icon: Receipt },
{ href: '/admin/order', label: 'Order Management', icon: ShoppingCart },
{ href: '/admin/invoice', label: 'Invoice Management', icon: FileCheck },
{ href: '/admin/ledger', label: 'Ledger Management', icon: Receipt },
],
// ── Platform operations ──────────────────────────────────────────────────
[
{ href: '/admin/kb', label: 'Knowledge Base Management', icon: BookMarked },
{ href: '/admin/notifications', label: 'Notifications', icon: Bell },
{ href: '/admin/support', label: 'Support Management', icon: HeadphonesIcon },
{ href: '/admin/report', label: 'Report Management', icon: BarChart3 },
],
];
export default function AdminSidebar(props: {
collapsed: boolean;
onToggle: () => void;
onNavigate?: () => void;
adminName: string;
adminInitials: string;
}) {
const location = useLocation();
const isActive = (item: NavItem) => {
if (item.href === '/admin') return location.pathname === '/admin';
if (item.aliasPrefix && location.pathname.startsWith(item.aliasPrefix)) return true;
return location.pathname === item.href || location.pathname.startsWith(`${item.href}/`);
};
return (
<aside
class={`flex h-full flex-col border-r border-gray-200 bg-white transition-all duration-300 ${
props.collapsed ? 'w-[72px]' : 'w-64'
}`}
>
{/* ── Collapse toggle ── */}
<div
class={`flex shrink-0 items-center border-b border-gray-100 px-3 py-3 ${
props.collapsed ? 'justify-center' : 'justify-end'
}`}
>
<button
type="button"
onClick={props.onToggle}
title={props.collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
class="flex h-7 w-7 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600"
>
<ChevronLeft
size={16}
class={`transition-transform duration-300 ${props.collapsed ? 'rotate-180' : ''}`}
/>
</button>
</div>
{/* ── Navigation ── */}
<nav class="scrollbar min-h-0 flex-1 overflow-y-auto py-2">
<For each={GROUPS}>
{(group, gi) => (
<>
{/* Divider between groups */}
<Show when={gi() > 0}>
<div class="mx-3 my-2 border-t border-gray-100" />
</Show>
<div class="px-2">
<For each={group}>
{(item) => {
const active = () => isActive(item);
const Icon = item.icon;
return (
<A
href={item.href}
onClick={props.onNavigate}
title={props.collapsed ? item.label : undefined}
aria-current={active() ? 'page' : undefined}
class={`relative mb-0.5 flex items-center gap-3 rounded-lg py-2.5 text-[13px] font-medium leading-5 transition-all duration-150 ${
props.collapsed ? 'justify-center px-2' : 'px-3'
} ${
active()
? 'bg-orange-50 text-gray-900'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
}`}
>
{/* Left orange accent bar on active */}
<Show when={active()}>
<span class="absolute left-0 top-1.5 bottom-1.5 w-[3px] rounded-r-full bg-orange-500" />
</Show>
<Icon
size={17}
class={`shrink-0 ${active() ? 'text-orange-500' : 'text-gray-400'}`}
/>
<Show when={!props.collapsed}>
<span class="min-w-0 flex-1 truncate">{item.label}</span>
</Show>
</A>
);
}}
</For>
</div>
</>
)}
</For>
</nav>
{/* ── User info pinned at bottom ── */}
<div class="shrink-0 border-t border-gray-100 p-3">
<div
class={`flex items-center gap-3 rounded-lg px-2 py-2 ${
props.collapsed ? 'justify-center' : ''
}`}
>
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-orange-500 text-sm font-bold text-white">
{props.adminInitials}
</div>
<Show when={!props.collapsed}>
<div class="min-w-0 flex-1">
<p class="truncate text-[13px] font-semibold text-gray-800">{props.adminName}</p>
<p class="text-[11px] text-gray-500">Super Admin</p>
</div>
</Show>
</div>
</div>
</aside>
);
}