import { IconArrowLeft as ArrowLeft, IconChartBar as BarChart2, IconBell as Bell, IconBuilding as Building2, IconSelector as ChevronsUpDown, IconClock as Clock, IconCreditCard as CreditCard, IconExternalLink as ExternalLink, IconFileText as FileText, IconLayoutDashboard as LayoutDashboard, IconLogout as LogOut, IconMail as Mail, IconMessage2 as MessageSquare, IconMoon as Moon, IconPalette as Palette, IconPlus as Plus, IconRobot as Robot, IconSettings as Settings, IconShield as Shield, IconSun as Sun, IconCursorText as TextCursorInput, IconUser as User, IconUsers as Users, } from '@tabler/icons-react'; import { useTheme } from 'next-themes'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar, } from '@/components/ui/sidebar'; import { useOrganizations } from '@/hooks/useOrganizations'; import { useEnabledServices } from '@/hooks/useRunningServices'; import { getServiceUrl } from '@/lib/admin-services'; import { cn, getUserInitials } from '@/lib/utils'; import { useAuth } from './auth/AuthProvider'; import { Logo, WordmarkText } from './Logo'; const prefetchMap: Record Promise> = { '/dashboard': () => import('@/pages/Dashboard'), '/ui-components': () => import('@/pages/UIComponents'), '/charts': () => import('@/pages/Charts'), '/settings/profile': () => import('@/pages/settings/Profile'), '/settings/billing': () => import('@/pages/settings/Billing'), '/settings/security': () => import('@/pages/settings/Security'), '/admin/dashboard': () => import('@/pages/admin/Dashboard'), '/admin/organizations': () => import('@/pages/admin/Organizations'), '/admin/users': () => import('@/pages/admin/Users'), '/admin/notifications': () => import('@/pages/admin/Notifications'), '/admin/logs': () => import('@/pages/admin/Logs'), '/admin/theme': () => import('@/pages/admin/Theme'), '/admin/settings': () => import('@/pages/admin/Settings'), '/admin/infrastructure': () => import('@/pages/admin/Infrastructure'), '/admin/jobs': () => import('@/pages/admin/Jobs'), '/admin/crawlers': () => import('@/pages/admin/Crawlers'), '/admin/contact': () => import('@/pages/admin/ContactSubmissions'), '/admin/newsletter': () => import('@/pages/admin/Newsletter'), }; function prefetch(path: string) { prefetchMap[path]?.(); } const menuItems = [ { titleKey: 'sidebar.dashboard', icon: LayoutDashboard, path: '/dashboard', }, { titleKey: 'sidebar.uiComponents', icon: TextCursorInput, path: '/ui-components', }, { titleKey: 'sidebar.charts', icon: BarChart2, path: '/charts', }, ]; // Fixed-width icon container matching collapsed SidebarMenuButton size (40px) function IconSlot({ children, className = '' }: { children: React.ReactNode; className?: string }) { return ( {children} ); } export function AppSidebar() { const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); const { user, isSuperAdmin, signOut } = useAuth(); const { resolvedTheme, setTheme } = useTheme(); const { state, toggleSidebar } = useSidebar(); const { organizations, selectedOrganization, selectOrganization, createOrganization } = useOrganizations(); const [isCreateOrgOpen, setIsCreateOrgOpen] = useState(false); const [newOrgName, setNewOrgName] = useState(''); const [isCreating, setIsCreating] = useState(false); const [createError, setCreateError] = useState(null); // Derive sidebar mode from current path const sidebarMode = location.pathname.startsWith('/admin') ? 'admin' : location.pathname.startsWith('/settings') ? 'settings' : location.pathname.startsWith('/organizations/') ? 'org-settings' : 'user'; // Extract orgId from path for org-settings mode const orgIdMatch = location.pathname.match(/^\/organizations\/([^/]+)/); const currentOrgId = orgIdMatch?.[1]; const currentOrg = organizations.find((org) => org.id === currentOrgId); const { services: enabledServices } = useEnabledServices(); const isCollapsed = state === 'collapsed'; const handleSignOut = async () => { await signOut(); navigate('/'); }; const handleExternalLink = (url: string) => { window.open(url, '_blank', 'noopener,noreferrer'); }; const toggleTheme = () => { setTheme(resolvedTheme === 'dark' ? 'light' : 'dark'); }; const handleCreateOrganization = async () => { if (!newOrgName.trim()) return; setIsCreating(true); setCreateError(null); try { await createOrganization(newOrgName.trim()); setNewOrgName(''); setIsCreateOrgOpen(false); } catch (err) { setCreateError(err instanceof Error ? err.message : 'Failed to create organization'); } finally { setIsCreating(false); } }; const displayName = user?.user_metadata?.full_name || 'User'; return ( <> {sidebarMode === 'user' && ( <> {/* Organization Switcher */} {selectedOrganization?.name || t('sidebar.selectOrganization')} {organizations.map((org) => { const isSelected = selectedOrganization?.id === org.id; return ( selectOrganization(org)} className={`flex items-center justify-between ${isSelected ? 'bg-accent' : ''}`} > {org.name} ); })} setIsCreateOrgOpen(true)} > {t('sidebar.createOrganization')} {/* Menu */} {t('sidebar.menu')} {menuItems.map((item) => ( navigate(item.path)} onMouseEnter={() => prefetch(item.path)} onFocus={() => prefetch(item.path)} tooltip={t(item.titleKey)} className="gap-0 p-0" > {t(item.titleKey)} ))} )} {sidebarMode === 'settings' && ( <> {/* Back to App */} navigate('/dashboard')} tooltip={t('common.back')} className="gap-0 p-0" > {t('common.back')} {/* Settings Menu */} {t('sidebar.userSettings')} navigate('/settings/profile')} onMouseEnter={() => prefetch('/settings/profile')} onFocus={() => prefetch('/settings/profile')} tooltip={t('sidebar.profile')} className="gap-0 p-0" > {t('sidebar.profile')} navigate('/settings/billing')} onMouseEnter={() => prefetch('/settings/billing')} onFocus={() => prefetch('/settings/billing')} tooltip={t('sidebar.billing')} className="gap-0 p-0" > {t('sidebar.billing')} navigate('/settings/security')} onMouseEnter={() => prefetch('/settings/security')} onFocus={() => prefetch('/settings/security')} tooltip={t('sidebar.security')} className="gap-0 p-0" > {t('sidebar.security')} )} {sidebarMode === 'org-settings' && currentOrg && ( <> {/* Back to App */} navigate('/dashboard')} tooltip={t('common.back')} className="gap-0 p-0" > {t('common.back')} {/* Org Settings Menu */} {currentOrg.name} navigate(`/organizations/${currentOrgId}/details`)} tooltip={t('sidebar.orgDetails')} className="gap-0 p-0" > {t('sidebar.orgDetails')} navigate(`/organizations/${currentOrgId}/members`)} tooltip={t('sidebar.members')} className="gap-0 p-0" > {t('sidebar.members')} )} {sidebarMode === 'admin' && ( <> {/* Back to App */} navigate('/dashboard')} tooltip={t('common.back')} className="gap-0 p-0" > {t('common.back')} {/* Admin Menu */} {t('sidebar.superAdmin')} navigate('/admin/dashboard')} onMouseEnter={() => prefetch('/admin/dashboard')} onFocus={() => prefetch('/admin/dashboard')} tooltip={t('sidebar.overview')} className="gap-0 p-0" > {t('sidebar.overview')} navigate('/admin/organizations')} onMouseEnter={() => prefetch('/admin/organizations')} onFocus={() => prefetch('/admin/organizations')} tooltip={t('sidebar.organizations')} className="gap-0 p-0" > {t('sidebar.organizations')} navigate('/admin/users')} onMouseEnter={() => prefetch('/admin/users')} onFocus={() => prefetch('/admin/users')} tooltip={t('sidebar.users')} className="gap-0 p-0" > {t('sidebar.users')} navigate('/admin/notifications')} onMouseEnter={() => prefetch('/admin/notifications')} onFocus={() => prefetch('/admin/notifications')} tooltip={t('sidebar.notifications')} className="gap-0 p-0" > {t('sidebar.notifications')} navigate('/admin/logs')} onMouseEnter={() => prefetch('/admin/logs')} onFocus={() => prefetch('/admin/logs')} tooltip={t('sidebar.logs')} className="gap-0 p-0" > {t('sidebar.logs')} navigate('/admin/theme')} onMouseEnter={() => prefetch('/admin/theme')} onFocus={() => prefetch('/admin/theme')} tooltip={t('sidebar.theme')} className="gap-0 p-0" > {t('sidebar.theme')} navigate('/admin/jobs')} onMouseEnter={() => prefetch('/admin/jobs')} onFocus={() => prefetch('/admin/jobs')} tooltip="Background Jobs" className="gap-0 p-0" > Jobs navigate('/admin/crawlers')} onMouseEnter={() => prefetch('/admin/crawlers')} onFocus={() => prefetch('/admin/crawlers')} tooltip="AI Visibility" className="gap-0 p-0" > AI Visibility navigate('/admin/contact')} onMouseEnter={() => prefetch('/admin/contact')} onFocus={() => prefetch('/admin/contact')} tooltip="Contact Submissions" className="gap-0 p-0" > Contact navigate('/admin/newsletter')} onMouseEnter={() => prefetch('/admin/newsletter')} onFocus={() => prefetch('/admin/newsletter')} tooltip="Newsletter" className="gap-0 p-0" > Newsletter navigate('/admin/settings')} onMouseEnter={() => prefetch('/admin/settings')} onFocus={() => prefetch('/admin/settings')} tooltip={t('sidebar.settings')} className="gap-0 p-0" > {t('sidebar.settings')} {enabledServices.map((service) => { const url = getServiceUrl(service); return ( handleExternalLink(url) : undefined} tooltip={ url ? service.description : 'Internal only: kubectl port-forward svc/traefik-dashboard 8080:8080 -n vibecarbon' } className={cn('gap-0 p-0', !url && 'cursor-default opacity-50')} > {service.name} {url && ( )} ); })} )} {/* User Menu */} {getUserInitials(user)}
{displayName} {user?.email}

{displayName}

{user?.email}

navigate('/settings/profile')}> {t('common.settings')} {isSuperAdmin && ( navigate('/admin/dashboard')}> {t('sidebar.superAdmin')} )} {t('common.signOut')}
{t('createOrg.title')} {t('createOrg.description')}
{ setNewOrgName(e.target.value); setCreateError(null); }} onKeyDown={(e) => { if (e.key === 'Enter' && !isCreating) { handleCreateOrganization(); } }} disabled={isCreating} /> {createError &&

{createError}

}
); }