import { useEffect, useState } from "react"; import { Link, useLocation } from "react-router-dom"; import { LayoutDashboard, Building2, Users, Package, FileText, Settings, HelpCircle, X, Shield, BadgeCheck, GitBranch, ChevronDown, ChevronRight, Bell, Paperclip, } from "lucide-react"; import { cn } from "@/lib/utils"; import { useAppSelector } from "@/hooks/redux-hooks"; import { useAppTheme } from "@/hooks/useAppTheme"; import { AuthenticatedImage } from "@/components/shared"; interface MenuItem { icon: React.ComponentType<{ className?: string; strokeWidth?: number }>; label: string; path?: string; isGroup?: boolean; children?: Array<{ label: string; path: string; requiredPermission?: { resource: string; action?: string; }; }>; requiredPermission?: { resource: string; action?: string; // If not provided, checks for '*' or 'read' }; } interface SidebarProps { isOpen: boolean; onClose: () => void; } // Super Admin menu items const superAdminPlatformMenu: MenuItem[] = [ { icon: LayoutDashboard, label: "Dashboard", path: "/dashboard" }, { icon: Building2, label: "Tenants", path: "/tenants" }, // { icon: Users, label: 'User Management', path: '/users' }, // { icon: Shield, label: 'Roles', path: '/roles' }, { icon: Package, label: "Modules", path: "/modules" }, ]; const superAdminSystemMenu: MenuItem[] = [ { icon: Bell, label: "Notifications", isGroup: true, children: [ { label: "Notifications List", path: "/notifications" }, { label: "Master Management", path: "/notification-master" }, { label: "Global Templates", path: "/notification-templates" }, ], }, { icon: FileText, label: "Audit Logs", path: "/audit-logs" }, { icon: Shield, label: "Audit Resources", path: "/audit-resource-types" }, { icon: Settings, label: "Settings", isGroup: true, children: [ { label: "SMTP Config", path: "/settings/smtp" }, { label: "Failed Emails", path: "/settings/failed-emails" }, ], }, ]; // Tenant Admin menu items const tenantAdminPlatformMenu: MenuItem[] = [ { icon: LayoutDashboard, label: "Dashboard", path: "/tenant" }, { icon: Shield, label: "Roles", path: "/tenant/roles", requiredPermission: { resource: "roles" }, }, { icon: Building2, label: "Departments", path: "/tenant/departments", requiredPermission: { resource: "departments" }, }, { icon: BadgeCheck, label: "Designations", path: "/tenant/designations", requiredPermission: { resource: "designations" }, }, { icon: Users, label: "Suppliers", path: "/tenant/suppliers", requiredPermission: { resource: "supplier" }, }, { icon: Users, label: "Users", path: "/tenant/users", requiredPermission: { resource: "users" }, }, ]; const tenantAdminPlatformServiceMenu: MenuItem[] = [ { icon: Paperclip, label: "File Attachments", isGroup: true, children: [ { label: "Files List", path: "/tenant/files", requiredPermission: { resource: "files" }, }, { label: "Storage Dashboard", path: "/tenant/files/storage-dashboard", requiredPermission: { resource: "files" }, }, ], requiredPermission: { resource: "files" }, }, { icon: GitBranch, label: "Workflows", isGroup: true, children: [ { label: "Definitions", path: "/tenant/workflows/definitions", requiredPermission: { resource: "workflow" }, }, { label: "Tasks", path: "/tenant/workflows/tasks", requiredPermission: { resource: "workflow" }, }, ], requiredPermission: { resource: "workflow" }, }, { icon: FileText, label: "Documents", isGroup: true, children: [ { label: "Document Lists", path: "/tenant/documents", requiredPermission: { resource: "document" }, }, { label: "Create Document", path: "/tenant/documents/create", requiredPermission: { resource: "document", action: "create" }, }, { label: "Categories", path: "/tenant/documents/categories", requiredPermission: { resource: "document" }, }, { label: "Due for Review", path: "/tenant/documents/due-for-review", requiredPermission: { resource: "document" }, }, ], requiredPermission: { resource: "document" }, }, { icon: Package, label: "Modules", path: "/tenant/modules" }, ]; const tenantAdminSystemMenu: MenuItem[] = [ { icon: Bell, label: "Notifications", path: "/tenant/notifications", requiredPermission: { resource: "notifications" }, }, { icon: FileText, label: "Audit Logs", path: "/tenant/audit-logs", }, { icon: Settings, label: "Settings", isGroup: true, children: [ { label: "General Settings", path: "/tenant/settings", }, { label: "Notification Settings", path: "/tenant/settings/notifications", }, { label: "Notification Templates", path: "/tenant/settings/notification-templates", }, { label: "SMTP Settings", path: "/tenant/settings/smtp", }, { label: "Failed Emails", path: "/tenant/settings/failed-emails", } ], requiredPermission: { resource: "tenants" }, }, ]; const GroupMenuItem = ({ item, childrenItems, location, primaryColor, secondaryColor, onClose, }: { item: MenuItem; childrenItems: any[]; location: any; primaryColor: string; secondaryColor: string; onClose: () => void; }) => { const isChildActive = (path: string) => { // Special handling for Document Lists to NOT show as active when sub-actions are active if (path === "/tenant/documents") { const subActions = ["/create", "/categories", "/due-for-review", "/edit"]; const isSubActionActive = subActions.some((sub) => location.pathname.startsWith(path + sub), ); if (isSubActionActive) return false; } // Special handling for Files List to NOT show as active when Storage Dashboard is active if (path === "/tenant/files") { if (location.pathname.startsWith("/tenant/files/storage-dashboard")) { return false; } } return ( location.pathname === path || location.pathname.startsWith(`${path}/`) ); }; const isAnyChildActive = childrenItems.some((child) => isChildActive(child.path), ); const [isExpanded, setIsExpanded] = useState(isAnyChildActive); useEffect(() => { if (isAnyChildActive) setIsExpanded(true); }, [isAnyChildActive]); const Icon = item.icon; return (
{isExpanded && (
{childrenItems.map((child) => { const isActive = isChildActive(child.path); return ( { if (window.innerWidth < 768) { onClose(); } }} className={cn( "flex items-center px-4 py-2 rounded-r-md text-[13px] font-medium transition-all", isActive ? "text-[#112868] font-bold bg-gray-50" : "text-[#475569] hover:text-[#0f1724] hover:bg-gray-50", )} style={ isActive ? { color: primaryColor, } : undefined } > {child.label} ); })}
)}
); }; export const Sidebar = ({ isOpen, onClose }: SidebarProps) => { const { primaryColor, secondaryColor, accentColor, logoUrl } = useAppTheme(); const location = useLocation(); const { roles, permissions } = useAppSelector((state) => state.auth); // Fetch theme for tenant admin const isSuperAdminCheck = () => { let rolesArray: string[] = []; if (Array.isArray(roles)) { rolesArray = roles; } else if (typeof roles === "string") { try { rolesArray = JSON.parse(roles); } catch { rolesArray = []; } } return rolesArray.includes("super_admin"); }; const isSuperAdmin = isSuperAdminCheck(); // Get role name for display const getRoleName = (): string => { if (isSuperAdmin) { return "Super Admin"; } let rolesArray: string[] = []; if (Array.isArray(roles)) { rolesArray = roles; } else if (typeof roles === "string") { try { rolesArray = JSON.parse(roles); } catch { rolesArray = []; } } // Get the first role and format it if (rolesArray.length > 0) { const role = rolesArray[0]; // Convert snake_case to Title Case return role .split("_") .map( (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), ) .join(" "); } return "User"; }; const roleName = getRoleName(); // Fetch theme if tenant admin // if (!isSuperAdmin) { // useTenantTheme(); // } // Helper function to check if user has permission for a resource const hasPermission = ( resource: string, requiredAction?: string, ): boolean => { if (isSuperAdmin) { return true; // Super admin has all permissions } const allowedActions = requiredAction ? [requiredAction] : ["*", "read"]; return permissions.some((perm) => { // Check if resource matches (exact match or wildcard) const resourceMatches = perm.resource === resource || perm.resource === "*"; // Check if action matches (exact match or wildcard) const actionMatches = allowedActions.some( (allowedAction) => perm.action === allowedAction || perm.action === "*", ); return resourceMatches && actionMatches; }); }; const filterMenuItems = (items: MenuItem[]): MenuItem[] => { if (isSuperAdmin) { return items; } return items.filter((item) => { if (!item.requiredPermission) { return true; } const hasParentPermission = hasPermission( item.requiredPermission.resource, item.requiredPermission.action, ); if (!hasParentPermission) return false; if (item.isGroup && item.children) { // Deep copy children to avoid mutating original menu arrays const filteredChildren = item.children.filter((child) => { if (!child.requiredPermission) return true; return hasPermission( child.requiredPermission.resource, child.requiredPermission.action, ); }); // We need to return a new object to avoid issues if (filteredChildren.length > 0) { (item as any)._filteredChildren = filteredChildren; return true; } return false; } return true; }); }; // Select and filter menu items based on role and permissions const platformMenu = filterMenuItems( isSuperAdmin ? superAdminPlatformMenu : tenantAdminPlatformMenu, ); const platformServiceMenu = filterMenuItems( isSuperAdmin ? [] : tenantAdminPlatformServiceMenu, ); const systemMenu = filterMenuItems( isSuperAdmin ? superAdminSystemMenu : tenantAdminSystemMenu, ); const MenuSection = ({ title, items, }: { title: string; items: MenuItem[]; }) => (
{title}
{items.map((item) => { if (item.isGroup) { const children = (item as any)._filteredChildren || item.children || []; return ( ); } const Icon = item.icon; const isTenantDashboardPath = item.path === "/tenant"; const isActive = isTenantDashboardPath ? location.pathname === "/tenant" : item.path && (location.pathname === item.path || location.pathname.startsWith(`${item.path}/`)); return ( { // Close sidebar on mobile when navigating if (window.innerWidth < 768) { onClose(); } }} className={cn( "flex items-center gap-2 md:gap-2 lg:gap-2.5 px-2 md:px-2 lg:px-3 py-2 rounded-md transition-colors min-h-[44px]", isActive ? "shadow-[0px_2px_8px_0px_rgba(15,23,42,0.15)]" : "text-[#0f1724] hover:bg-gray-50", )} style={ isActive ? { backgroundColor: primaryColor, color: secondaryColor, } : undefined } > {item.label} ); })}
); return ( <> {/* Mobile Sidebar */} {/* Desktop Sidebar */} ); };