import { LayoutDashboard, FileText, LogOut, Users, ChevronLeft, ChevronRight, Search, UserMinus, ChevronDown, ChevronUp, FolderOpen, Settings, RefreshCcw, MapPin, ClipboardList } from 'lucide-react'; import { useState, useRef, useCallback, useEffect } from 'react'; import ReactDOM from 'react-dom'; import { useNavigate, useLocation } from 'react-router-dom'; import { useSelector } from 'react-redux'; import { RootState } from '../../store'; import { Input } from '../ui/input'; import { Button } from '../ui/button'; interface SidebarProps { onLogout: () => void; } interface FlyoutState { submenuKey: string; top: number; left: number; } export function Sidebar({ onLogout }: SidebarProps) { const navigate = useNavigate(); const location = useLocation(); const activeView = location.pathname.substring(1) || 'dashboard'; const { user: currentUser } = useSelector((state: RootState) => state.auth); const [collapsed, setCollapsed] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [offboardingExpanded, setOffboardingExpanded] = useState(false); const [allRequestsExpanded, setAllRequestsExpanded] = useState(false); const [flyout, setFlyout] = useState(null); const hoverTimeout = useRef | null>(null); const currentRole = currentUser?.role || currentUser?.roleCode || ''; const normalizedRole = String(currentRole).trim().toLowerCase(); const hasRole = (roles: string[]) => roles.map((r) => r.toLowerCase()).includes(normalizedRole); const resignationRoles = ['DD Admin', 'ASM', 'RBM', 'DD Lead', 'ZBH', 'NBH', 'Legal', 'Legal Admin', 'Super Admin']; const terminationRoles = ['ASM', 'RBM', 'ZBH', 'DD Lead', 'DD Head', 'NBH', 'Legal Admin', 'Legal', 'DD Admin', 'CCO', 'CEO', 'Super Admin']; const fnfRoles = ['DD Admin', 'DD Lead', 'NBH', 'Finance', 'Finance Admin', 'Super Admin']; const canSeeResignation = hasRole(resignationRoles); const canSeeTermination = hasRole(terminationRoles); const canSeeFnF = hasRole(fnfRoles); const offboardingSubmenu = [ canSeeResignation ? { id: 'resignation', label: 'Resignation' } : null, canSeeTermination ? { id: 'termination', label: 'Termination' } : null, canSeeFnF ? { id: 'fnf', label: 'F&F' } : null ].filter(Boolean) as { id: string; label: string }[]; const menuItems = hasRole(['Finance', 'Finance Admin']) ? [ { id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard }, { id: 'finance-onboarding', label: 'Onboarding', icon: FileText }, { id: 'finance-fnf', label: 'F&F', icon: UserMinus }, ] : hasRole(['Dealer']) ? [ { id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard }, { id: 'dealer-resignation', label: 'My Resignations', icon: UserMinus }, { id: 'dealer-constitutional', label: 'Constitutional Change', icon: RefreshCcw }, { id: 'dealer-relocation', label: 'Relocation Requests', icon: MapPin }, ] : hasRole(['FDD']) ? [ { id: 'fdd-dashboard', label: 'FDD Dashboard', icon: LayoutDashboard }, ] : [ { id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard }, { id: 'applications', label: 'Dealership Requests', icon: FileText }, ...(offboardingSubmenu.length > 0 ? [{ id: 'offboarding', label: 'Offboarding', icon: UserMinus, hasSubmenu: true, submenuKey: 'offboarding', submenu: offboardingSubmenu }] : []), { id: 'constitutional-change', label: 'Constitutional Change', icon: RefreshCcw }, { id: 'relocation-requests', label: 'Relocation Requests', icon: MapPin }, ]; if (hasRole(['DD Lead', 'DD Admin', 'Super Admin'])) { menuItems.splice(1, 0, { id: 'all-requests', label: 'All Requests', icon: FolderOpen, hasSubmenu: true, submenuKey: 'allRequests', submenu: [ { id: 'opportunity-requests', label: 'Opportunity Requests' }, { id: 'non-opportunities', label: 'Non-opportunities' } ] }); } if (hasRole(['Super Admin', 'DD Admin', 'DD Lead'])) { menuItems.push({ id: 'master', label: 'Master', icon: Settings }); menuItems.push({ id: 'sla-configurations', label: 'SLA Matrix', icon: RefreshCcw }); } if (hasRole(['Super Admin'])) { menuItems.push({ id: 'users', label: 'User Management', icon: Users }); menuItems.push({ id: 'questionnaires', label: 'Questionnaire Templates', icon: ClipboardList }); } const handleSearch = (e: React.FormEvent) => { e.preventDefault(); if (searchQuery.trim()) navigate('/applications'); }; const openFlyout = useCallback((submenuKey: string, triggerEl: HTMLElement) => { if (hoverTimeout.current) clearTimeout(hoverTimeout.current); const rect = triggerEl.getBoundingClientRect(); setFlyout({ submenuKey, top: rect.top, left: rect.right + 8 }); }, []); const closeFlyout = useCallback((immediate = false) => { if (immediate) { if (hoverTimeout.current) clearTimeout(hoverTimeout.current); setFlyout(null); } else { hoverTimeout.current = setTimeout(() => setFlyout(null), 150); } }, []); const keepFlyoutOpen = useCallback(() => { if (hoverTimeout.current) clearTimeout(hoverTimeout.current); }, []); // Close flyout when sidebar expands useEffect(() => { if (!collapsed) setFlyout(null); }, [collapsed]); return ( <>
{/* Header with Logo */}
{collapsed ? ( /* Collapsed header: logo + toggle stacked, centered */
RE
) : ( /* Expanded header: logo + subtitle + collapse toggle */
Royal Enfield Dealer Onboarding
)}
{/* Search Bar (expanded only) */} {!collapsed && (
setSearchQuery(e.target.value)} className="w-full pl-10 bg-white/5 border-white/10 text-white placeholder:text-slate-500" />
)} {/* Menu Items */} {/* User Profile & Logout */}
{!collapsed && currentUser && (
{currentUser.name.charAt(0)}

{currentUser.name}

{currentUser.role}

)} {collapsed && currentUser && (
{currentUser.name.charAt(0)}
)}
{/* Flyout submenu portal — rendered outside sidebar to bypass overflow:hidden */} {flyout && collapsed && (() => { const flyoutItem = menuItems.find( (m) => (m as any).submenuKey === flyout.submenuKey ); if (!flyoutItem || !flyoutItem.submenu) return null; return ReactDOM.createPortal(
closeFlyout()} >
{flyoutItem.label}
{flyoutItem.submenu.map((subItem) => { const isSubActive = activeView === subItem.id; return ( ); })}
, document.body ); })()} ); }