import React, { useState, useEffect, useRef } from 'react'; import { NavLink, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useAuth } from '@/lib/auth'; import { useTenant } from '@/lib/tenant'; import { BarChart2, TrendingUp, Users, BookOpen, Mic, Building2, Activity, Lightbulb, Database, Megaphone, LogOut, LayoutTemplate, MessageSquare, Bot, Menu, X, CreditCard } from 'lucide-react'; import RoleGuard from '@/components/RoleGuard'; import LanguageSwitcher from '@/components/LanguageSwitcher'; import AdminChat, { type AdminChatPage } from '@/components/AdminChat'; interface MainLayoutProps { children: React.ReactNode; isSuperAdmin: boolean; orgs: any[]; } function useAdminChatPage(): AdminChatPage | null { const { pathname } = useLocation(); if (pathname === '/billing') return null; // billing has its own inline chat if (pathname.startsWith('/settings')) return 'settings'; if (pathname.startsWith('/whatsapp-templates')) return 'templates'; if (pathname.startsWith('/ai-setup') || pathname.startsWith('/kb')) return 'agent'; if (pathname.startsWith('/clients/new') || pathname.startsWith('/onboarding')) return 'onboarding'; return 'general'; } export default function MainLayout({ children, isSuperAdmin, orgs }: MainLayoutProps) { const { t } = useTranslation(); const { logout, user, token } = useAuth(); const { selectedOrgId, setSelectedOrgId, currentOrg } = useTenant(); const [sidebarOpen, setSidebarOpen] = useState(false); const [unreadCount, setUnreadCount] = useState(0); const chatPage = useAdminChatPage(); const { pathname } = useLocation(); const esRef = useRef(null); const isCrmActive = !!currentOrg?.isCrmActive; const isEdTechActive = !!currentOrg?.isEdTechActive; // Reset unread count when user is on the conversations page useEffect(() => { if (pathname.startsWith('/conversations') || pathname.startsWith('/crm')) { setUnreadCount(0); } }, [pathname]); // SSE β€” track new inbound messages for the notification badge useEffect(() => { if (!selectedOrgId || !token) return; const apiBase = import.meta.env.VITE_API_URL || ''; const es = new EventSource(`${apiBase}/v1/organizations/${selectedOrgId}/stream?token=${encodeURIComponent(token)}`); esRef.current = es; es.addEventListener('message', (event) => { try { const payload = JSON.parse(event.data); if (payload.type === 'new-message') { // Only increment if not currently viewing conversations const isViewingConversations = window.location.pathname.startsWith('/conversations') || window.location.pathname.startsWith('/crm'); if (!isViewingConversations) { setUnreadCount(n => n + 1); } } } catch { /* ignore */ } }); es.onerror = () => es.close(); return () => { es.close(); esRef.current = null; }; }, [selectedOrgId, token]); const navItems = [ { to: '/', label: t('nav.home'), icon: , end: true }, { to: '/analytics', label: t('common.analytics'), icon: }, { to: '/conversations', label: t('nav.conversations'), icon: }, // CRM items { to: '/contacts', label: t('common.clients'), icon: , show: isCrmActive }, { to: '/campaign-history', label: t('nav.campaigns'), icon: , show: isCrmActive }, { to: '/whatsapp-templates',label: t('nav.templates'), icon: , show: isCrmActive }, // EdTech items { to: '/content', label: t('nav.content'), icon: , show: isEdTechActive }, { to: '/live-feed', label: t('nav.moderation'), icon: , show: isEdTechActive }, { to: '/users', label: t('nav.users'), icon: , show: isEdTechActive }, // Shared β€” accessible to all org members { to: '/kb', label: t('nav.kb'), icon: }, { to: '/ai-setup', label: t('nav.ai_setup'), icon: }, // Super admin only { to: '/clients', label: t('nav.b2b'), icon: , show: isSuperAdmin }, { to: '/training', label: t('nav.training'), icon: , show: isSuperAdmin }, // Always last { to: '/billing', label: t('nav.billing'), icon: }, { to: '/settings', label: t('common.settings'), icon: }, ].filter(item => item.show !== false); const linkClass = ({ isActive }: { isActive: boolean }) => `flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all ${ isActive ? 'bg-white/15 text-white' : 'text-slate-400 hover:text-white hover:bg-white/10' }`; const avatarInitial = (user?.name?.trim()?.[0] ?? user?.email?.[0] ?? 'U').toUpperCase(); const displayName = user?.name || user?.email || 'β€”'; const displayRole = (user?.role ?? '').toLowerCase().replace(/_/g, ' '); const sidebarInner = ( <> {/* Logo / org name */}
{currentOrg?.brandingData?.logoUrl ? ( Logo ) : ( πŸŽ“ )} {currentOrg?.name || 'Admin'}
{/* Super-admin org selector */}
{/* Nav β€” scrollable so it never overflows */} {/* Footer */}
{avatarInitial}

{displayName}

{displayRole}

); return (
{/* ── Desktop sidebar (always visible β‰₯ lg) ── */} {/* ── Mobile: backdrop overlay ── */} {sidebarOpen && (
setSidebarOpen(false)} /> )} {/* ── Mobile: sliding sidebar ── */} {/* ── Main content area ── */}
{/* Mobile top bar */}
{currentOrg?.name || 'Admin'}
{children}
{/* Global AI assistant β€” hidden on /billing (has its own inline chat) */} {chatPage && selectedOrgId && }
); }