"use client"; import React, { useEffect, useRef, useState, useCallback } from "react"; import Link from "next/link"; import Image from "next/image"; import { usePathname } from "next/navigation"; import { useSidebar } from "../context/SidebarContext"; import { BoxCubeIcon, ChevronDownIcon, FileIcon, GridIcon, HorizontaLDots, ListIcon, ShootingStarIcon, } from "../icons/index"; import { apiGetCurrentUser, apiLogout } from "@/service/auth"; import { UserMetaCardProps } from "@/interface/user"; type NavItem = { name: string; icon: React.ReactNode; path?: string; subItems?: { name: string; path: string; pro?: boolean; new?: boolean; }[]; }; const ALL_NAV_ITEMS: NavItem[] = [ { icon: , name: "Trang Chủ", path: "/" }, { icon: , name: "Dự Án", path: "/user/projects" }, { icon: , name: "Thư Viện", path: "/user/library" }, ]; const OTHERS_ITEMS: NavItem[] = [ { icon: , name: "Hỗ trợ", path: "/user/quick-qa" }, ]; const AppSidebar: React.FC = () => { const { isExpanded, isMobileOpen, toggleSidebar, toggleMobileSidebar } = useSidebar(); const pathname = usePathname(); const [user, setUser] = useState(null); const [openSubmenu, setOpenSubmenu] = useState<{ type: "main" | "others"; index: number; } | null>(null); const [subMenuHeight, setSubMenuHeight] = useState>({}); const subMenuRefs = useRef>({}); const isActive = useCallback((path: string) => path === pathname, [pathname]); const isSidebarVisible = isExpanded || isMobileOpen; const handleToggle = () => { if (window.innerWidth >= 1024) { toggleSidebar(); } else { toggleMobileSidebar(); } }; useEffect(() => { let isMounted = true; const fetchUser = async () => { try { const userData = await apiGetCurrentUser(); if (isMounted) setUser(userData); } catch (err) { console.error("Lỗi fetch user:", err); } }; fetchUser(); return () => { isMounted = false; }; }, []); const clearAllCookies = () => { document.cookie.split(";").forEach((c) => { document.cookie = c .replace(/^ +/, "") .replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); }); }; const handleLogout = async (e: React.MouseEvent) => { e.preventDefault(); try { await apiLogout(); } catch (error) { console.error("Logout failed", error); } finally { localStorage.clear(); sessionStorage.clear(); clearAllCookies(); window.location.href = "/signin"; } }; const handleSubmenuToggle = (index: number, menuType: "main" | "others") => { setOpenSubmenu((prev) => prev?.type === menuType && prev?.index === index ? null : { type: menuType, index }, ); }; // Tự động mở submenu nếu route hiện tại nằm trong subItems useEffect(() => { let submenuMatched = false; const menuGroups = [ { items: ALL_NAV_ITEMS, type: "main" as const }, { items: OTHERS_ITEMS, type: "others" as const }, ]; menuGroups.forEach(({ items, type }) => { items.forEach((nav, index) => { nav.subItems?.forEach((sub) => { if (isActive(sub.path)) { setOpenSubmenu((prev) => { if (prev?.type === type && prev?.index === index) return prev; return { type, index }; }); submenuMatched = true; } }); }); }); if (!submenuMatched) { setOpenSubmenu((prev) => (prev !== null ? null : prev)); } }, [pathname, isActive]); // Tính toán chiều cao cho hiệu ứng mượt mà của submenu useEffect(() => { if (openSubmenu !== null) { const key = `${openSubmenu.type}-${openSubmenu.index}`; if (subMenuRefs.current[key]) { requestAnimationFrame(() => { setSubMenuHeight((prev) => ({ ...prev, [key]: subMenuRefs.current[key]?.scrollHeight || 0, })); }); } } }, [openSubmenu]); const renderMenuItems = (items: NavItem[], menuType: "main" | "others") => ( {items.map((nav, index) => { const isOpen = openSubmenu?.type === menuType && openSubmenu?.index === index; const refKey = `${menuType}-${index}`; return ( {nav.subItems ? ( handleSubmenuToggle(index, menuType)} className={`menu-item group capitalize ${ isOpen ? "menu-item-active" : "menu-item-icon-inactive" } ${!isExpanded ? "lg:justify-center px-2" : "lg:justify-start"}`} > {nav.icon} {isSidebarVisible && ( <> {nav.name} > )} ) : ( nav.path && ( {nav.icon} {isSidebarVisible && ( {nav.name} )} ) )} {/* Submenu Area */} {nav.subItems && isSidebarVisible && ( { subMenuRefs.current[refKey] = el; }} className="overflow-hidden ease-in-out transition-all duration-300" style={{ height: isOpen ? `${subMenuHeight[refKey] || 0}px` : "0px", }} > {nav.subItems.map((subItem) => ( {subItem.name} ))} )} ); })} ); return ( ); }; export default AppSidebar;