import {useActiveProject} from '../lib/contexts/ActiveProjectProvider'; import {useUser} from '../lib/hooks/useUser'; import {network} from '../lib/network'; import { Activity, BarChart3, ChevronDown, FileText, Layers, LayoutDashboard, LogOut, Megaphone, Menu, Plus, Settings, User, Users, Workflow, } from 'lucide-react'; import Image from 'next/image'; import Link from 'next/link'; import {useRouter} from 'next/router'; import {useCallback, useEffect, useRef, useState} from 'react'; interface DashboardLayoutProps { children: React.ReactNode; } interface NavItem { name: string; href: string; icon: React.ComponentType<{className?: string}>; } interface NavSection { title?: string; items: NavItem[]; } const navigation: NavSection[] = [ { items: [ {name: 'Dashboard', href: '/', icon: LayoutDashboard}, {name: 'Contacts', href: '/contacts', icon: Users}, {name: 'Segments', href: '/segments', icon: Layers}, {name: 'Activity', href: '/activity', icon: Activity}, {name: 'Analytics', href: '/analytics', icon: BarChart3}, ], }, { title: 'Automations', items: [ {name: 'Templates', href: '/templates', icon: FileText}, {name: 'Workflows', href: '/workflows', icon: Workflow}, ], }, { title: 'Campaigns', items: [{name: 'Campaigns', href: '/campaigns', icon: Megaphone}], }, ]; export function DashboardLayout({children}: DashboardLayoutProps) { const router = useRouter(); const {data: user, mutate: mutateUser} = useUser(); const {activeProject, availableProjects, setActiveProject} = useActiveProject(); const [showProjectMenu, setShowProjectMenu] = useState(false); const [showUserMenu, setShowUserMenu] = useState(false); const [showMobileMenu, setShowMobileMenu] = useState(false); const desktopProjectMenuRef = useRef(null); const desktopUserMenuRef = useRef(null); const mobileProjectMenuRef = useRef(null); const mobileUserMenuRef = useRef(null); // Handle click outside for project menu useEffect(() => { function handleClickOutside(event: MouseEvent) { const isClickOutsideProjectMenu = desktopProjectMenuRef.current && !desktopProjectMenuRef.current.contains(event.target as Node) && mobileProjectMenuRef.current && !mobileProjectMenuRef.current.contains(event.target as Node); const isClickOutsideUserMenu = desktopUserMenuRef.current && !desktopUserMenuRef.current.contains(event.target as Node) && mobileUserMenuRef.current && !mobileUserMenuRef.current.contains(event.target as Node); if (isClickOutsideProjectMenu) { setShowProjectMenu(false); } if (isClickOutsideUserMenu) { setShowUserMenu(false); } } if (showProjectMenu || showUserMenu) { document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; } }, [showProjectMenu, showUserMenu]); const handleLogout = useCallback(async () => { try { // Call the logout endpoint to clear the cookie await network.fetch('GET', '/auth/logout'); // Clear local storage localStorage.removeItem('token'); localStorage.removeItem('activeProjectId'); // Clear SWR cache for user data await mutateUser(null, false); // Close the menu setShowUserMenu(false); // Redirect to login await router.push('/auth/login'); } catch (error) { console.error('Logout failed:', error); // Even if the API call fails, try to redirect to login localStorage.removeItem('token'); localStorage.removeItem('activeProjectId'); await mutateUser(null, false); await router.push('/auth/login'); } }, [mutateUser, router]); const handleToggleProjectMenu = useCallback(() => { setShowProjectMenu(prev => !prev); }, []); const handleToggleUserMenu = useCallback(() => { setShowUserMenu(prev => !prev); }, []); const handleLogoutClick = useCallback( (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); void handleLogout(); }, [handleLogout], ); // Sidebar content (reusable for both desktop and mobile) const getSidebarContent = ( projectMenuRef: React.RefObject, userMenuRef: React.RefObject, ) => ( <> {/* Logo */}
Plunk

Plunk

{/* Project Switcher */}
{/* Project Dropdown */} {showProjectMenu && (
{availableProjects.map(project => ( ))}
setShowProjectMenu(false)} className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-50 transition-colors text-neutral-700" > Create project
)}
{/* Navigation */} {/* Settings & User Menu */}
setShowMobileMenu(false)} className={`flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-neutral-700 ${ router.pathname.startsWith('/settings') ? 'bg-neutral-100' : 'hover:bg-neutral-50 hover:text-neutral-900' }`} > Settings
{/* User Dropdown */} {showUserMenu && (
)}
); return (
{/* Desktop Sidebar - Hidden on mobile */}
{getSidebarContent(desktopProjectMenuRef, desktopUserMenuRef)}
{/* Mobile Sidebar Overlay */} {showMobileMenu && (
setShowMobileMenu(false)}>
)} {/* Mobile Sidebar */}
{getSidebarContent(mobileProjectMenuRef, mobileUserMenuRef)}
{/* Main Content */}
{/* Mobile Header - Only visible on mobile */}
Plunk

Plunk

{/* Page Content */}
{children}
); }