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

192 lines
12 KiB
TypeScript

import { A, useLocation } from '@solidjs/router';
import { For, Show, createMemo } 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,
ChevronLeft, BadgeCheck, Activity, Film, Utensils, PenTool, Mail,
Megaphone, Bell, Video,
} from 'lucide-solid';
type NavItem = {
href: string;
label: string;
icon: any;
aliasPrefix?: string;
moduleKeys?: string[];
};
const GROUPS: NavItem[][] = [
[
{ href: '/admin', label: 'Dashboard', icon: LayoutGrid, moduleKeys: ['ADMIN_DASHBOARD', 'DASHBOARD'] },
],
[
{ href: '/admin/department', label: 'Department Management', icon: Building2, moduleKeys: ['DEPARTMENT_MANAGEMENT', 'DEPARTMENTS'] },
{ href: '/admin/designation', label: 'Designation Management', icon: Briefcase, moduleKeys: ['DESIGNATION_MANAGEMENT', 'DESIGNATIONS'] },
{ href: '/admin/roles', label: 'Internal Role Management', icon: ShieldCheck, moduleKeys: ['INTERNAL_ROLE_MANAGEMENT', 'ROLES'] },
{ href: '/admin/employees', label: 'Employee Management', icon: Users, moduleKeys: ['EMPLOYEE_MANAGEMENT', 'EMPLOYEES'] },
],
[
{ href: '/admin/external-roles', label: 'External Role Management', icon: ShieldCheck, moduleKeys: ['EXTERNAL_ROLE_MANAGEMENT', 'EXTERNAL_ROLES'] },
{ href: '/admin/internal-dashboard-management', label: 'Internal Dashboard Management', icon: LayoutDashboard, moduleKeys: ['INTERNAL_DASHBOARD_MANAGEMENT', 'INTERNAL_DASHBOARDS', 'INTERNAL_DASHBOARD_CONFIG'] },
{ href: '/admin/external-dashboard-management', label: 'External Dashboard Management', icon: LayoutDashboard, aliasPrefix: '/admin/role-ui-configs', moduleKeys: ['DASHBOARD_CONFIG_MANAGEMENT', 'EXTERNAL_DASHBOARD_MANAGEMENT', 'EXTERNAL_DASHBOARDS', 'EXTERNAL_DASHBOARD_CONFIG', 'RUNTIME_ROLES'] },
],
[
{ href: '/admin/verification', label: 'Verification Management', icon: BadgeCheck, moduleKeys: ['VERIFICATION_MANAGEMENT', 'VERIFICATIONS'] },
{ href: '/admin/approval', label: 'Approval Management', icon: ClipboardList, moduleKeys: ['APPROVAL_MANAGEMENT', 'APPROVALS'] },
],
[
{ href: '/admin/users', label: 'Users Management', icon: UserRoundSearch, moduleKeys: ['USER_MANAGEMENT', 'USERS'] },
{ href: '/admin/company', label: 'Company Management', icon: Building2, moduleKeys: ['COMPANY_MANAGEMENT', 'COMPANIES'] },
{ href: '/admin/candidate', label: 'Candidate Management', icon: UserCircle, moduleKeys: ['CANDIDATE_MANAGEMENT', 'CANDIDATES'] },
{ href: '/admin/customer', label: 'Customer Management', icon: UserCircle, moduleKeys: ['CUSTOMER_MANAGEMENT', 'CUSTOMERS'] },
],
[
{ href: '/admin/photographer', label: 'Photographer Management', icon: Camera, moduleKeys: ['PHOTOGRAPHER_MANAGEMENT', 'PHOTOGRAPHERS'] },
{ href: '/admin/makeup-artist', label: 'Makeup Artist Management', icon: Palette, moduleKeys: ['MAKEUP_ARTIST_MANAGEMENT', 'MAKEUP_ARTISTS'] },
{ href: '/admin/tutors', label: 'Tutors Management', icon: BookOpen, moduleKeys: ['TUTOR_MANAGEMENT', 'TUTORS'] },
{ href: '/admin/developers', label: 'Developers Management', icon: Code2, moduleKeys: ['DEVELOPER_MANAGEMENT', 'DEVELOPERS'] },
{ href: '/admin/video-editors', label: 'Video Editor Management', icon: Film, moduleKeys: ['VIDEO_EDITOR_MANAGEMENT', 'VIDEO_EDITORS'] },
{ href: '/admin/fitness-trainers', label: 'Fitness Trainer Management', icon: Activity, moduleKeys: ['FITNESS_TRAINER_MANAGEMENT', 'FITNESS_TRAINERS'] },
{ href: '/admin/catering-services', label: 'Catering Services Management', icon: Utensils, moduleKeys: ['CATERING_SERVICES_MANAGEMENT', 'CATERING_SERVICES'] },
{ href: '/admin/graphic-designers', label: 'Graphics Designer Management', icon: PenTool, moduleKeys: ['GRAPHIC_DESIGNER_MANAGEMENT', 'GRAPHIC_DESIGNERS'] },
{ href: '/admin/social-media-managers', label: 'Social Media Manager Management', icon: Megaphone, moduleKeys: ['SOCIAL_MEDIA_MANAGEMENT', 'SOCIAL_MEDIA_MANAGER_MANAGEMENT', 'SOCIAL_MEDIA_MANAGERS'] },
{ href: '/admin/ugc-content-creator', label: 'UGC Content Creator Management', icon: Video, moduleKeys: ['UGC_CONTENT_CREATOR_MANAGEMENT', 'UGC_CONTENT_CREATOR'] },
],
[
{ href: '/admin/jobs', label: 'Jobs Management', icon: BriefcaseBusiness, moduleKeys: ['JOBS_MANAGEMENT', 'JOBS'] },
{ href: '/admin/leads', label: 'Leads Management', icon: HandHelping, moduleKeys: ['LEADS_MANAGEMENT', 'LEADS', 'REQUIREMENTS_MANAGEMENT', 'REQUIREMENTS'] },
],
[
{ href: '/admin/pricing', label: 'Pricing Management', icon: WalletCards, moduleKeys: ['PRICING_MANAGEMENT', 'PRICING'] },
{ href: '/admin/credit', label: 'Credit Management', icon: CreditCard, moduleKeys: ['CREDIT_MANAGEMENT', 'CREDITS'] },
{ href: '/admin/coupon', label: 'Coupon Management', icon: Tag, moduleKeys: ['COUPON_MANAGEMENT', 'COUPONS'] },
{ href: '/admin/discount', label: 'Discount Management', icon: Percent, moduleKeys: ['DISCOUNT_MANAGEMENT', 'DISCOUNTS'] },
{ href: '/admin/tax', label: 'Tax Management', icon: Receipt, moduleKeys: ['TAX_MANAGEMENT', 'TAXES'] },
{ href: '/admin/order', label: 'Order Management', icon: ShoppingCart, moduleKeys: ['ORDER_MANAGEMENT', 'ORDERS'] },
{ href: '/admin/invoice', label: 'Invoice Management', icon: FileCheck, moduleKeys: ['INVOICE_MANAGEMENT', 'INVOICES'] },
{ href: '/admin/payment-gateway', label: 'Payment Gateway Management', icon: CreditCard, moduleKeys: ['PAYMENT_GATEWAY_MANAGEMENT', 'PAYMENT_GATEWAY'] },
{ href: '/admin/smtp', label: 'SMTP Management', icon: Mail, moduleKeys: ['SMTP_MANAGEMENT', 'SMTP'] },
],
[
{ href: '/admin/kb', label: 'Knowledge Base Management', icon: BookOpen, moduleKeys: ['KNOWLEDGE_BASE_MANAGEMENT', 'KNOWLEDGE_BASE', 'KB'] },
{ href: '/admin/notifications', label: 'Notifications', icon: Bell, moduleKeys: ['NOTIFICATIONS_MANAGEMENT', 'NOTIFICATIONS'] },
{ href: '/admin/review', label: 'Review Management', icon: Star, moduleKeys: ['REVIEW_MANAGEMENT', 'REVIEWS'] },
{ href: '/admin/support', label: 'Support Management', icon: HeadphonesIcon, moduleKeys: ['SUPPORT_MANAGEMENT', 'SUPPORT'] },
{ href: '/admin/report', label: 'Report Management', icon: BarChart3, moduleKeys: ['REPORT_MANAGEMENT', 'REPORTS'] },
{ href: '/admin/ledger', label: 'Ledger Management', icon: Receipt, moduleKeys: ['LEDGER', 'LEDGER_MANAGEMENT'] },
],
];
export default function AdminSidebar(props: {
collapsed: boolean;
onToggle: () => void;
onNavigate?: () => void;
adminName: string;
adminInitials: string;
theme?: 'light' | 'dark';
allowedModules?: string[] | null;
isSuperAdmin?: boolean;
}) {
const location = useLocation();
const allowed = createMemo(() => new Set((props.allowedModules || []).map((m) => String(m || '').trim().toUpperCase()).filter(Boolean)));
const canShowItem = (item: NavItem) => {
if (props.isSuperAdmin) return true;
if (!props.allowedModules || props.allowedModules.length === 0) return true;
if (item.href === '/admin') return true;
const keys = item.moduleKeys || [];
for (const k of keys) if (allowed().has(String(k).toUpperCase())) return true;
return false;
};
const visibleGroups = createMemo(() => GROUPS.map((group) => group.filter((item) => canShowItem(item))).filter((group) => group.length > 0));
const isActive = (item: NavItem) => {
if (location.pathname === '/admin') return item.href === '/admin';
if (item.href === '/admin') return false;
if (item.aliasPrefix && location.pathname.startsWith(item.aliasPrefix)) return true;
return location.pathname === item.href || location.pathname.startsWith(`${item.href}/`);
};
const isDark = () => props.theme === 'dark';
return (
<aside
style={{
overflow: 'hidden',
display: 'flex',
'flex-direction': 'column',
height: '100%',
background: isDark() ? '#0F172A' : 'white',
'border-right': `1px solid ${isDark() ? '#1F2937' : '#E5E7EB'}`,
transition: 'width 0.3s',
'flex-shrink': 0,
width: props.collapsed ? '64px' : '220px'
}}
>
{/* Logo area */}
<div style={`position:relative;height:64px;display:flex;align-items:center;border-bottom:1px solid ${isDark() ? '#1F2937' : '#E5E7EB'};flex-shrink:0;padding:0 14px`}>
<A href="/admin" onClick={props.onNavigate} style="display:flex;align-items:center;gap:10px;text-decoration:none;overflow:hidden">
<Show
when={!props.collapsed}
fallback={
<img src="/nxtgauge-icon.png" alt="Nxtgauge" style="width:32px;height:32px;object-fit:contain;flex-shrink:0" />
}
>
<img src="/nxtgauge-logo.png" alt="Nxtgauge" style="height:44px;object-fit:contain;flex-shrink:0;max-width:180px" />
</Show>
</A>
<button
type="button"
onClick={props.onToggle}
style={`position:absolute;right:-10px;top:50%;transform:translateY(-50%);width:20px;height:20px;border-radius:50%;border:1px solid ${isDark() ? '#1F2937' : '#E5E7EB'};background:${isDark() ? '#111827' : 'white'};box-shadow:0 1px 4px rgba(0,0,0,0.1);display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:10;color:${isDark() ? '#CBD5E1' : '#6B7280'}`}
aria-label={props.collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
>
<ChevronLeft size={11} style={`transition:transform 0.3s;${props.collapsed ? 'transform:rotate(180deg)' : ''}`} />
</button>
</div>
{/* Navigation */}
<nav style="flex:1;min-height:0;overflow-y:auto;padding:10px 8px">
<For each={visibleGroups()}>
{(group, gi) => (
<>
<Show when={gi() > 0}>
<div style={`height:1px;background:${isDark() ? '#1F2937' : '#F3F4F6'};margin:6px 4px`} />
</Show>
<div style="display:flex;flex-direction:column;gap:1px">
<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}
style={`display:flex;align-items:center;height:36px;border-radius:8px;text-decoration:none;padding:0 ${props.collapsed ? '0' : '10px'};transition:background 140ms ease,color 140ms ease;${props.collapsed ? 'justify-content:center;' : ''}${active() ? 'background:#FFF3EE;color:#FF5E13;' : `color:${isDark() ? '#CBD5E1' : '#6B7280'};`}`}
aria-current={active() ? 'page' : undefined}
>
<Icon
size={16}
style={`flex-shrink:0;${active() ? 'color:#FF5E13' : `color:${isDark() ? '#94A3B8' : '#9CA3AF'}`}`}
strokeWidth={active() ? 2.5 : 2}
/>
<Show when={!props.collapsed}>
<span style="margin-left:9px;font-size:12.5px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{item.label}</span>
</Show>
</A>
);
}}
</For>
</div>
</>
)}
</For>
</nav>
</aside>
);
}