import { ChevronDown, GalleryVerticalEnd, House, LightbulbIcon, ListTodo, LogOut, Radar, Settings, Star, Workflow, DiscAlbum, LucideLightbulb } from "lucide-react"; import React, { useState } from "react"; import { Link, useLocation } from "react-router"; import { useAuth } from "~/contexts/auth-context"; import { cn } from "~/lib/utils"; interface TitleInfo { title: string; icon?: React.ComponentType<{ className?: string }> | null; } interface SidebarProps { isCollapsed?: boolean; onToggleCollapse?: () => void; className?: string; onStrategicAlignmentClick?: () => void; onTitleChange?: (info: TitleInfo) => void; } interface MenuItem { id: string; label: string; icon: React.ComponentType<{ className?: string }> | null; href?: string; children?: MenuItem[]; badge?: string | number; } const menuItems: MenuItem[] = [ { id: "dashboard", label: "صفحه اصلی", icon: House, href: "/dashboard", }, { id: "project-management", label: "مدیریت اجرای پروژه‌ها", icon: ListTodo, href: "/dashboard/project-management", }, { id: "innovation-basket", label: "سبد فناوری و نوآوری", icon: LightbulbIcon, children: [ { id: "product-innovation", label: "نوآوری در محصول", icon: null, href: "/dashboard/innovation-basket/product-innovation", }, { id: "process-innovation", label: "نوآوری در فرآیند", icon: null, href: "/dashboard/innovation-basket/process-innovation", }, { id: "digital-innovation", label: "نوآوری دیجیتال", icon: null, href: "/dashboard/innovation-basket/digital-innovation", }, { id: "green-innovation", label: "نوآوری سبز", icon: null, href: "/dashboard/innovation-basket/green-innovation", }, { id: "internal-innovation", label: "نوآوری ساخت داخل", icon: null, href: "/dashboard/innovation-basket/internal-innovation", }, ], }, { id: "ecosystem", label: "زیست بوم فناوری و نوآوری", icon: Radar, href: "/dashboard/ecosystem", }, { id: "ideas", label: "ایده‌های فناوری و نوآوری", icon: LucideLightbulb, href: "/dashboard/manage-ideas-tech", }, { id: "strategic-alignment", label: "میزان انطباق راهبردی", icon: null, href: "#", // This is not a route, it opens a popup }, ]; const bottomMenuItems: MenuItem[] = [ { id: "settings", label: "تنظیمات", icon: Settings, href: "/dashboard/settings", }, { id: "logout", label: "خروج", icon: LogOut, href: "#", }, ]; export function Sidebar({ isCollapsed = false, onToggleCollapse, className, onStrategicAlignmentClick, onTitleChange, }: SidebarProps) { const location = useLocation(); const [expandedItems, setExpandedItems] = useState([]); const { logout } = useAuth(); // Auto-expand parent sections when their children are active React.useEffect(() => { const autoExpandParents = () => { const newExpandedItems: string[] = []; menuItems.forEach((item) => { if (item.children) { const hasActiveChild = item.children.some( (child) => child.href && location.pathname === child.href ); if (hasActiveChild) { newExpandedItems.push(item.id); } } }); setExpandedItems(newExpandedItems); // Update header title based on current route // If a child route is active, use that child's label prefixed by parent label let activeTitle: string | undefined = undefined; let activeIcon: | React.ComponentType<{ className?: string }> | null | undefined = undefined; menuItems.forEach((item) => { if (item.children) { const activeChild = item.children.find( (child) => child.href && location.pathname === child.href ); if (activeChild) { activeTitle = `${item.label}-${activeChild.label}`; // prefer child icon for the page; fallback to parent activeIcon = activeChild.icon ?? item.icon ?? null; } } if (!activeTitle && item.href && location.pathname === item.href) { activeTitle = item.label; activeIcon = item.icon ?? null; } }); if (onTitleChange) { onTitleChange({ title: activeTitle ?? "صفحه اول", icon: activeIcon ?? null, }); } }; autoExpandParents(); }, [location.pathname]); const toggleExpanded = (itemId: string) => { setExpandedItems((prev) => { // If trying to collapse, check if any child is active if (prev.includes(itemId)) { const item = menuItems.find((menuItem) => menuItem.id === itemId); if (item?.children) { const hasActiveChild = item.children.some( (child) => child.href && location.pathname === child.href ); // Don't collapse if a child is active if (hasActiveChild) { return prev; } } return prev.filter((id) => id !== itemId); } else { return [...prev, itemId]; } }); }; const isActiveRoute = (href?: string, children?: MenuItem[]) => { if (href && location.pathname === href) return true; if (children) { return children.some( (child) => child.href && location.pathname === child.href ); } return false; }; const renderMenuItem = (item: MenuItem, level = 0) => { const isActive = isActiveRoute(item.href, item.children); const isExpanded = expandedItems.includes(item.id) || (item.children && item.children.some( (child) => child.href && location.pathname === child.href )); const hasChildren = item.children && item.children.length > 0; const ItemIcon = item.icon; const handleClick = () => { // Only update header title for navigable items (those with href) if (item.href && item.href !== "#") { const icon = item.icon ?? null; onTitleChange?.({ title: item.label, icon }); } if (item.id === "strategic-alignment") { onStrategicAlignmentClick?.(); } else if (item.id === "logout") { logout(); } else if (hasChildren) { toggleExpanded(item.id); } }; if (item.id === "strategic-alignment") { return ( ); } return (
{item.href && item.href !== "#" ? (
{ItemIcon && ( )} {!isCollapsed && ( {item.label} )}
{!isCollapsed && (
{item.badge && ( {item.badge} )} {hasChildren && ( )}
)}
) : ( )} {/* Submenu */} {hasChildren && isExpanded && !isCollapsed && (
{item.children?.map((child) => renderMenuItem(child, level + 1))}
)} {/* Tooltip for collapsed state */} {isCollapsed && level === 0 && (
{item.label}
)}
); }; return (
{/* Header */}
{!isCollapsed ? (
داشبورد مدیریت فناوری و نوآوری
{/*
نسخه ۰.۱
*/}
) : (
)}
{/* Main Menu */}
{/* Bottom Menu */}
{/* Collapse Toggle */} {/* {onToggleCollapse && (
)} */}
); } export default Sidebar;