![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.quebec/private_html/src/components/ |
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import Link from 'next/link';
import { motion, AnimatePresence } from 'framer-motion';
import {
Home,
User,
FileText,
MessageSquare,
Users,
BarChart3,
Star,
Settings,
Building,
Scale,
HelpCircle,
Info,
Mail,
UserPlus,
ChevronDown,
ChevronRight,
Menu,
X,
Lock,
Eye,
EyeOff,
CheckCircle,
AlertTriangle,
Clock,
Calendar,
DollarSign,
Phone,
Globe,
MapPin,
Linkedin,
Upload,
Edit,
Trash2,
Plus,
Search,
Filter,
Download,
RefreshCw,
RotateCcw,
RotateCw,
ZoomIn,
ZoomOut,
Maximize,
Minimize,
Move,
Grid,
List,
Columns,
Rows,
Layout,
MoreHorizontal,
MoreVertical,
Circle,
Square,
Triangle,
Hexagon,
Octagon,
ThumbsUp,
ThumbsDown,
Flag,
Bookmark,
Tag,
Hash,
AtSign,
Percent,
} from 'lucide-react';
import { Command } from 'cmdk';
import { useSession } from 'next-auth/react';
interface SidebarProps {
isFrench?: boolean;
}
export const Sidebar: React.FC<SidebarProps> = ({ isFrench = false }) => {
const router = useRouter();
const { data: session } = useSession();
const [isCollapsed, setIsCollapsed] = useState(false);
const [isCommandOpen, setIsCommandOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
// Determine user role
const userRole = session?.user?.role;
// Store sidebar state in localStorage
useEffect(() => {
const savedState = localStorage.getItem('sidebarCollapsed');
if (savedState) {
setIsCollapsed(JSON.parse(savedState));
}
}, []);
const toggleSidebar = () => {
const newState = !isCollapsed;
setIsCollapsed(newState);
localStorage.setItem('sidebarCollapsed', JSON.stringify(newState));
};
// Build navigation with role-aware Cases link
const navigation = [
{ name: isFrench ? 'Accueil' : 'Home', href: '/', icon: Home },
{ name: isFrench ? 'À Propos' : 'About', href: '/about', icon: Info },
{ name: isFrench ? 'Qui Est Concerné' : 'Who Is Concerned', href: '/who', icon: User },
{ name: isFrench ? 'Ressources' : 'Resources', href: '/resources', icon: FileText },
{ name: isFrench ? 'FAQ' : 'FAQ', href: '/faq', icon: HelpCircle },
{ name: isFrench ? 'Contact' : 'Contact', href: '/contact', icon: Mail },
{ name: isFrench ? 'Groupe de discussion' : 'Group Chat', href: '/group-chat', icon: MessageSquare },
];
// Add role-based Cases link
if (userRole === 'SUPERADMIN' || userRole === 'ADMIN') {
navigation.push({ name: isFrench ? 'Dossiers' : 'Cases', href: '/admin/cases', icon: Scale });
} else if (userRole === 'LAWYER') {
navigation.push({ name: isFrench ? 'Dossiers' : 'Cases', href: '/lawyer/cases', icon: Scale });
} else if (userRole === 'CLIENT' || userRole === 'USER') {
navigation.push({ name: isFrench ? 'Dossiers' : 'Cases', href: '/client/cases', icon: Scale });
}
const filteredNavigation = navigation.filter(item =>
item.name.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<>
<aside
className={
isCollapsed ? 'w-16' : 'w-64'
}
>
<div className="flex flex-col h-full">
{/* Logo and Toggle */}
<div className="flex items-center justify-between p-4">
{!isCollapsed && (
<img src="/images/Logo_w.png" alt="Logo" className="h-8" />
)}
<button
onClick={toggleSidebar}
className="p-2 rounded-lg hover:bg-white/10 transition-colors"
>
{isCollapsed ? (
<ChevronRight className="h-5 w-5" />
) : (
<ChevronDown className="h-5 w-5" />
)}
</button>
</div>
{/* Command Palette Trigger */}
<button
onClick={() => setIsCommandOpen(true)}
className="mx-4 mb-4 flex items-center space-x-2 px-4 py-2 bg-white/10 rounded-lg hover:bg-white/20 transition-colors"
>
<Search className="h-5 w-5" />
{!isCollapsed && <span>{isFrench ? 'Rechercher...' : 'Search...'}</span>}
</button>
{/* Navigation and Sticky Logout */}
<div className="flex-1 flex flex-col min-h-0">
<nav className="flex-1 overflow-y-auto min-h-0">
<ul className="space-y-2 px-4">
{navigation.map((item) => (
<motion.li
key={item.href}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<Link
href={item.href}
className={
router.pathname === item.href
? 'bg-white/10 text-white'
: 'text-white/70 hover:bg-white/5 hover:text-white'
}
>
<item.icon className="h-5 w-5 flex-shrink-0" />
{!isCollapsed && <span>{item.name}</span>}
</Link>
</motion.li>
))}
</ul>
</nav>
{/* Sticky Logout Button */}
<div className="sticky bottom-0 bg-gradient-to-t from-primary to-transparent p-4 mt-4">
<button
onClick={() => router.push('/auth/logout')}
className="w-full flex items-center justify-center px-4 py-3 rounded-lg bg-white/20 hover:bg-white/30 text-white font-semibold transition-colors"
>
<Lock className="h-5 w-5 mr-2" />
{!isCollapsed && (isFrench ? 'Déconnexion' : 'Logout')}
</button>
</div>
</div>
</div>
</aside>
{/* Command Palette */}
<AnimatePresence>
{isCommandOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"
onClick={() => setIsCommandOpen(false)}
>
<motion.div
initial={{ scale: 0.95, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.95, opacity: 0 }}
className="bg-white rounded-xl shadow-xl w-full max-w-md overflow-hidden"
onClick={e => e.stopPropagation()}
>
<Command className="p-4">
<div className="flex items-center space-x-2 border-b pb-4">
<Search className="h-5 w-5 text-gray-400" />
<input
type="text"
placeholder={isFrench ? "Rechercher..." : "Search..."}
className="flex-1 outline-none"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<div className="mt-4">
<Command.List>
{filteredNavigation.map((item) => (
<Command.Item
key={item.href}
onSelect={() => {
router.push(item.href);
setIsCommandOpen(false);
}}
className="flex items-center space-x-3 px-4 py-3 rounded-lg hover:bg-gray-100 cursor-pointer"
>
<item.icon className="h-5 w-5 text-primary" />
<span>{item.name}</span>
</Command.Item>
))}
</Command.List>
</div>
</Command>
</motion.div>
</motion.div>
)}
</AnimatePresence>
{/* Main Content Spacer */}
<div className={`transition-all duration-300 ease-in-out ${isCollapsed ? 'ml-16' : 'ml-64'}
</>
);
};