Files
clube67_newwhats.local/clube67/newwhats.local/frontend/components/Sidebar.tsx
T

210 lines
10 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import {
LayoutDashboard,
MessageSquare,
Flame,
Users,
Network,
Send,
Key,
Settings,
LogOut,
Sparkles,
Zap,
ChevronLeft,
ChevronRight,
Layers,
Plug2,
ArrowLeftFromLine,
BrainCircuit,
} from 'lucide-react';
import { useAuth } from '../context/AuthContext';
import { motion, AnimatePresence } from 'framer-motion';
import { useUIStore } from '../store/uiStore';
// Simple Tooltip component specifically for the collapsed sidebar
const SidebarTooltip = ({ text, children }: { text: string, children: React.ReactNode }) => {
const [isVisible, setIsVisible] = useState(false);
return (
<div className="relative flex items-center" onMouseEnter={() => setIsVisible(true)} onMouseLeave={() => setIsVisible(false)}>
{children}
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
className="absolute left-full ml-4 px-3 py-1.5 bg-brand-500 text-white text-xs font-semibold rounded-lg shadow-xl z-[100] whitespace-nowrap pointer-events-none"
>
{text}
<div className="absolute top-1/2 -left-1 -translate-y-1/2 border-y-4 border-y-transparent border-r-4 border-r-brand-500" />
</motion.div>
)}
</AnimatePresence>
</div>
);
};
export default function Sidebar() {
const router = useRouter();
const { user, logout } = useAuth();
const { isSidebarCollapsed, toggleSidebar } = useUIStore();
const [mounted, setMounted] = useState(false);
// Wait for mount to avoid hydration mismatch
useEffect(() => {
setMounted(true);
}, []);
const menuItems = [
{ path: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ path: '/inbox', label: 'Inbox', icon: MessageSquare },
{ path: '/sessions', label: 'Sessões', icon: Users },
{ path: '/warmer', label: 'WhatsApp Warmer', icon: Flame },
{ path: '/phonebook', label: 'Agenda', icon: Users },
{ path: '/flow-builder', label: 'Fluxos', icon: Network },
{ path: '/sending', label: 'Disparos', icon: Send },
{ path: '/sectors', label: 'Setores & Equipes', icon: Layers },
{ path: '/api-access', label: 'Acesso à API', icon: Key },
{ path: '/secretaria', label: 'Secretária IA', icon: BrainCircuit },
];
const adminItems = [
{ path: '/admin/dashboard', label: 'Admin Dashboard', icon: LayoutDashboard },
{ path: '/admin/plans', label: 'Plan Management', icon: Sparkles },
{ path: '/admin/users', label: 'Users', icon: Users },
{ path: '/admin/plugins', label: 'Plugins', icon: Plug2 },
{ path: '/admin/settings', label: 'Global Settings', icon: Settings },
{ path: '/baileys', label: 'Baileys', icon: Zap },
];
const navItems = user?.role === 'admin' && router.pathname.startsWith('/admin')
? adminItems
: menuItems;
if (!mounted) return <aside className="w-64 bg-black" />;
return (
<motion.aside
animate={{ width: isSidebarCollapsed ? 80 : 256 }}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
className={`bg-black border-r border-white/5 h-screen flex-col font-sans shrink-0 z-50 overflow-y-auto relative hidden min-[401px]:flex max-[400px]:hidden transition-all`}
>
{/* Ambient */}
<div className="absolute top-0 left-0 w-full h-[500px] bg-brand-500/5 blur-[100px] pointer-events-none" />
{/* Application Logo */}
<div className={`h-16 flex items-center px-6 border-b border-white/5 relative z-10 shrink-0 ${isSidebarCollapsed ? 'justify-center px-0' : ''}`}>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded bg-gradient-to-br from-brand-500 to-brand-700 flex items-center justify-center shadow-lg shadow-brand-500/20 shrink-0">
<Sparkles className="text-white w-5 h-5 transition-transform duration-300 group-hover:rotate-12" />
</div>
{!isSidebarCollapsed && (
<motion.span
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
className="text-xl font-black text-white tracking-tight"
>
new<span className="text-brand-400">whats</span>
</motion.span>
)}
</div>
</div>
{/* Nav Menu */}
<nav className="flex-1 px-4 py-6 space-y-2 relative z-10 overflow-y-auto custom-scrollbar">
{navItems.map((item) => {
const isActive = router.pathname.startsWith(item.path);
const Icon = item.icon;
const content = (
<Link key={item.path} href={item.path}>
<div className={`relative flex items-center gap-3 px-3 py-2.5 rounded-xl cursor-pointer transition-all group overflow-hidden
${isActive ? 'text-white font-semibold' : 'text-slate-400 hover:text-white hover:bg-white/[0.05]'}
${isSidebarCollapsed ? 'justify-center p-2.5 mx-auto' : ''}`}
>
{isActive && (
<motion.div
layoutId="sidebar-active"
className="absolute inset-0 bg-brand-500/10 border border-brand-500/20 rounded-xl"
initial={false}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
/>
)}
{isActive && (
<div className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-brand-500 rounded-r-full shadow-[0_0_10px_#3b82f6]" />
)}
<Icon size={20} className={`relative z-10 shrink-0 ${isActive ? 'text-brand-400' : 'group-hover:scale-110 transition-transform'}`} />
{!isSidebarCollapsed && (
<motion.span
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
className="relative z-10 text-sm whitespace-nowrap"
>
{item.label}
</motion.span>
)}
</div>
</Link>
);
return isSidebarCollapsed ? (
<SidebarTooltip key={item.path} text={item.label}>
{content}
</SidebarTooltip>
) : (
content
);
})}
</nav>
{/* Bottom Actions & Toggle */}
<div className={`p-4 border-t border-white/5 relative z-10 shrink-0 ${isSidebarCollapsed ? 'items-center flex flex-col' : ''}`}>
{user?.role === 'admin' && router.pathname.startsWith('/admin') && (
<Link href="/dashboard">
<div className={`flex items-center gap-3 px-3 py-2 mb-1 text-sm rounded-xl cursor-pointer transition-all font-semibold group
bg-white/[0.04] border border-white/8 hover:bg-brand-500/10 hover:border-brand-500/30 text-slate-400 hover:text-brand-300
${isSidebarCollapsed ? 'justify-center w-full' : ''}`}>
<ArrowLeftFromLine size={17} className="shrink-0 transition-transform group-hover:-translate-x-0.5" />
{!isSidebarCollapsed && <span>Sair do Admin</span>}
</div>
</Link>
)}
{user?.role === 'admin' && !router.pathname.startsWith('/admin') && (
<Link href="/admin/dashboard">
<div className={`flex items-center gap-3 px-3 py-2 text-sm text-yellow-400 hover:bg-white/5 rounded-xl cursor-pointer transition-colors font-semibold ${isSidebarCollapsed ? 'justify-center w-full' : ''}`}>
<Settings size={18} className="shrink-0" />
{!isSidebarCollapsed && <span>Admin Panel</span>}
</div>
</Link>
)}
<div
onClick={logout}
className={`flex items-center gap-3 px-3 py-2 text-sm text-slate-400 hover:text-red-400 hover:bg-white/5 rounded-xl cursor-pointer transition-colors mb-2 ${isSidebarCollapsed ? 'justify-center w-full' : ''}`}
>
<LogOut size={18} className="shrink-0" />
{!isSidebarCollapsed && <span>Log Out</span>}
</div>
{/* Intelligent Toggle Button */}
<button
onClick={toggleSidebar}
className={`w-full flex items-center bg-white/5 hover:bg-white/10 text-slate-300 hover:text-white rounded-xl py-2 transition-all group ${isSidebarCollapsed ? 'justify-center px-0' : 'px-3 justify-between'}`}
title={isSidebarCollapsed ? 'Expandir' : 'Recolher'}
>
{!isSidebarCollapsed && <span className="text-xs font-bold uppercase tracking-widest opacity-40">Layout</span>}
{isSidebarCollapsed ? <ChevronRight size={18} /> : <ChevronLeft size={18} />}
</button>
</div>
</motion.aside>
);
}