This commit is contained in:
@@ -118,6 +118,17 @@ export default function ApplicationDetailPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const formatDate = (dateString: string | null | undefined) => {
|
||||||
|
if (!dateString) return "-";
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return date.toLocaleDateString("vi-VN", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// console.log("Application Detail:", application);
|
// console.log("Application Detail:", application);
|
||||||
|
|
||||||
@@ -247,7 +258,7 @@ export default function ApplicationDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[11px] text-zinc-400 dark:text-zinc-500 tabular-nums">
|
<span className="text-[11px] text-zinc-400 dark:text-zinc-500 tabular-nums">
|
||||||
{application?.reviewed_at}
|
{formatDate(application?.reviewed_at)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React, { useEffect, useRef, useState, useCallback } from "react";
|
import React, { useEffect, useRef, useState, useCallback, useMemo } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "@/store/store";
|
||||||
import { useSidebar } from "../context/SidebarContext";
|
import { useSidebar } from "../context/SidebarContext";
|
||||||
import {
|
import {
|
||||||
BoxCubeIcon,
|
BoxCubeIcon,
|
||||||
@@ -17,16 +19,24 @@ import {
|
|||||||
TableIcon,
|
TableIcon,
|
||||||
UserCircleIcon,
|
UserCircleIcon,
|
||||||
} from "../icons/index";
|
} from "../icons/index";
|
||||||
import SidebarWidget from "./SidebarWidget";
|
|
||||||
|
type RoleName = "ADMIN" | "MOD" | "USER" | "HISTORIAN";
|
||||||
|
|
||||||
type NavItem = {
|
type NavItem = {
|
||||||
name: string;
|
name: string;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
path?: string;
|
path?: string;
|
||||||
subItems?: { name: string; path: string; pro?: boolean; new?: boolean }[];
|
roles?: RoleName[];
|
||||||
|
subItems?: {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
pro?: boolean;
|
||||||
|
new?: boolean;
|
||||||
|
roles?: RoleName[];
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const navItems: NavItem[] = [
|
const ALL_NAV_ITEMS: NavItem[] = [
|
||||||
{
|
{
|
||||||
icon: <GridIcon />,
|
icon: <GridIcon />,
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
@@ -42,19 +52,21 @@ const navItems: NavItem[] = [
|
|||||||
name: "User Profile",
|
name: "User Profile",
|
||||||
path: "/profile",
|
path: "/profile",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "Forms",
|
name: "Forms",
|
||||||
icon: <ListIcon />,
|
icon: <ListIcon />,
|
||||||
subItems: [{ name: "Form Elements", path: "/form-elements", pro: false }, { name: "Role Upgrade", path: "/role-upgrade", pro: false }],
|
subItems: [
|
||||||
|
// { name: "Form Elements", path: "/form-elements", pro: false },
|
||||||
|
{ name: "Role Upgrade", path: "/role-upgrade", pro: false }
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Tables",
|
name: "Tables",
|
||||||
icon: <TableIcon />,
|
icon: <TableIcon />,
|
||||||
subItems: [
|
subItems: [
|
||||||
{ name: "Basic Tables", path: "/basic-tables", pro: false },
|
// { name: "Basic Tables", path: "/basic-tables", pro: false },
|
||||||
{ name: "User Tables", path: "/user-table", pro: false },
|
{ name: "User Tables", path: "/user-table", pro: false, roles: ["ADMIN", "MOD"] },
|
||||||
{ name: "Applications Tables", path: "/applications-tables", pro: false },
|
{ name: "Applications Tables", path: "/applications-tables", pro: false, roles: ["ADMIN", "MOD"] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -67,7 +79,7 @@ const navItems: NavItem[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const othersItems: NavItem[] = [
|
const OTHERS_ITEMS: NavItem[] = [
|
||||||
{
|
{
|
||||||
icon: <PieChartIcon />,
|
icon: <PieChartIcon />,
|
||||||
name: "Charts",
|
name: "Charts",
|
||||||
@@ -102,64 +114,114 @@ const AppSidebar: React.FC = () => {
|
|||||||
const { isExpanded, isMobileOpen, isHovered, setIsHovered } = useSidebar();
|
const { isExpanded, isMobileOpen, isHovered, setIsHovered } = useSidebar();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
const renderMenuItems = (
|
// Lấy data gốc từ Redux (không bị render lại vô cớ)
|
||||||
navItems: NavItem[],
|
const rolesData = useSelector((state: RootState) => state.user.data?.roles);
|
||||||
menuType: "main" | "others",
|
|
||||||
) => (
|
// Chỉ tạo mảng map mới khi rolesData thực sự thay đổi
|
||||||
|
const userRoles = useMemo(() => {
|
||||||
|
return rolesData?.map((r: any) => r.name) || [];
|
||||||
|
}, [rolesData]);
|
||||||
|
|
||||||
|
// 2. Logic lọc Menu theo Role
|
||||||
|
const filterMenuByRole = useCallback((items: NavItem[]) => {
|
||||||
|
return items
|
||||||
|
.map((item) => ({
|
||||||
|
...item,
|
||||||
|
subItems: item.subItems?.filter((sub) => {
|
||||||
|
if (!sub.roles) return true; // Ai cũng xem được nếu không định nghĩa role
|
||||||
|
return sub.roles.some((role) => userRoles.includes(role));
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
.filter((item) => {
|
||||||
|
// Ẩn mục cha nếu nó yêu cầu role mà user không có
|
||||||
|
if (item.roles && !item.roles.some((role) => userRoles.includes(role))) return false;
|
||||||
|
// Ẩn mục cha nếu các mục con đã bị lọc sạch (đối với menu dạng dropdown)
|
||||||
|
if (item.subItems && item.subItems.length === 0 && !item.path) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}, [userRoles]);
|
||||||
|
|
||||||
|
const filteredNavItems = useMemo(() => filterMenuByRole(ALL_NAV_ITEMS), [filterMenuByRole]);
|
||||||
|
const filteredOthersItems = useMemo(() => filterMenuByRole(OTHERS_ITEMS), [filterMenuByRole]);
|
||||||
|
|
||||||
|
// --- State quản lý đóng mở Submenu ---
|
||||||
|
const [openSubmenu, setOpenSubmenu] = useState<{
|
||||||
|
type: "main" | "others";
|
||||||
|
index: number;
|
||||||
|
} | null>(null);
|
||||||
|
const [subMenuHeight, setSubMenuHeight] = useState<Record<string, number>>({});
|
||||||
|
const subMenuRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
||||||
|
|
||||||
|
const isActive = useCallback((path: string) => path === pathname, [pathname]);
|
||||||
|
|
||||||
|
const handleSubmenuToggle = (index: number, menuType: "main" | "others") => {
|
||||||
|
setOpenSubmenu((prev) =>
|
||||||
|
prev?.type === menuType && prev?.index === index ? null : { type: menuType, index }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let submenuMatched = false;
|
||||||
|
[
|
||||||
|
{ items: filteredNavItems, type: "main" },
|
||||||
|
{ items: filteredOthersItems, type: "others" },
|
||||||
|
].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: type as "main" | "others", index };
|
||||||
|
});
|
||||||
|
submenuMatched = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!submenuMatched) {
|
||||||
|
setOpenSubmenu((prev) => (prev !== null ? null : prev));
|
||||||
|
}
|
||||||
|
}, [pathname, isActive, filteredNavItems, filteredOthersItems]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (openSubmenu !== null) {
|
||||||
|
const key = `${openSubmenu.type}-${openSubmenu.index}`;
|
||||||
|
if (subMenuRefs.current[key]) {
|
||||||
|
setSubMenuHeight((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[key]: subMenuRefs.current[key]?.scrollHeight || 0,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [openSubmenu]);
|
||||||
|
|
||||||
|
const renderMenuItems = (items: NavItem[], menuType: "main" | "others") => (
|
||||||
<ul className="flex flex-col gap-4">
|
<ul className="flex flex-col gap-4">
|
||||||
{navItems.map((nav, index) => (
|
{items.map((nav, index) => (
|
||||||
<li key={nav.name}>
|
<li key={nav.name}>
|
||||||
{nav.subItems ? (
|
{nav.subItems ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubmenuToggle(index, menuType)}
|
onClick={() => handleSubmenuToggle(index, menuType)}
|
||||||
className={`menu-item group ${
|
className={`menu-item group ${
|
||||||
openSubmenu?.type === menuType && openSubmenu?.index === index
|
openSubmenu?.type === menuType && openSubmenu?.index === index
|
||||||
? "menu-item-active"
|
? "menu-item-active" : "menu-item-inactive"
|
||||||
: "menu-item-inactive"
|
} cursor-pointer ${!isExpanded && !isHovered ? "lg:justify-center" : "lg:justify-start"}`}
|
||||||
} cursor-pointer ${
|
|
||||||
!isExpanded && !isHovered
|
|
||||||
? "lg:justify-center"
|
|
||||||
: "lg:justify-start"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<span
|
<span className={openSubmenu?.type === menuType && openSubmenu?.index === index ? "menu-item-icon-active" : "menu-item-icon-inactive"}>
|
||||||
className={` ${
|
|
||||||
openSubmenu?.type === menuType && openSubmenu?.index === index
|
|
||||||
? "menu-item-icon-active"
|
|
||||||
: "menu-item-icon-inactive"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{nav.icon}
|
{nav.icon}
|
||||||
</span>
|
</span>
|
||||||
{(isExpanded || isHovered || isMobileOpen) && (
|
{(isExpanded || isHovered || isMobileOpen) && (
|
||||||
<span className={`menu-item-text`}>{nav.name}</span>
|
<>
|
||||||
)}
|
<span className={`menu-item-text`}>{nav.name}</span>
|
||||||
{(isExpanded || isHovered || isMobileOpen) && (
|
<ChevronDownIcon className={`ml-auto w-5 h-5 transition-transform duration-200 ${openSubmenu?.type === menuType && openSubmenu?.index === index ? "rotate-180 text-brand-500" : ""}`} />
|
||||||
<ChevronDownIcon
|
</>
|
||||||
className={`ml-auto w-5 h-5 transition-transform duration-200 ${
|
|
||||||
openSubmenu?.type === menuType &&
|
|
||||||
openSubmenu?.index === index
|
|
||||||
? "rotate-180 text-brand-500"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
nav.path && (
|
nav.path && (
|
||||||
<Link
|
<Link href={nav.path} className={`menu-item group ${isActive(nav.path) ? "menu-item-active" : "menu-item-inactive"}`}>
|
||||||
href={nav.path}
|
<span className={isActive(nav.path) ? "menu-item-icon-active" : "menu-item-icon-inactive"}>
|
||||||
className={`menu-item group ${
|
|
||||||
isActive(nav.path) ? "menu-item-active" : "menu-item-inactive"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`${
|
|
||||||
isActive(nav.path)
|
|
||||||
? "menu-item-icon-active"
|
|
||||||
: "menu-item-icon-inactive"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{nav.icon}
|
{nav.icon}
|
||||||
</span>
|
</span>
|
||||||
{(isExpanded || isHovered || isMobileOpen) && (
|
{(isExpanded || isHovered || isMobileOpen) && (
|
||||||
@@ -170,52 +232,18 @@ const AppSidebar: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{nav.subItems && (isExpanded || isHovered || isMobileOpen) && (
|
{nav.subItems && (isExpanded || isHovered || isMobileOpen) && (
|
||||||
<div
|
<div
|
||||||
ref={(el) => {
|
ref={(el) => { subMenuRefs.current[`${menuType}-${index}`] = el; }}
|
||||||
subMenuRefs.current[`${menuType}-${index}`] = el;
|
|
||||||
}}
|
|
||||||
className="overflow-hidden transition-all duration-300"
|
className="overflow-hidden transition-all duration-300"
|
||||||
style={{
|
style={{ height: openSubmenu?.type === menuType && openSubmenu?.index === index ? `${subMenuHeight[`${menuType}-${index}`]}px` : "0px" }}
|
||||||
height:
|
|
||||||
openSubmenu?.type === menuType && openSubmenu?.index === index
|
|
||||||
? `${subMenuHeight[`${menuType}-${index}`]}px`
|
|
||||||
: "0px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<ul className="mt-2 space-y-1 ml-9">
|
<ul className="mt-2 space-y-1 ml-9">
|
||||||
{nav.subItems.map((subItem) => (
|
{nav.subItems.map((subItem) => (
|
||||||
<li key={subItem.name}>
|
<li key={subItem.name}>
|
||||||
<Link
|
<Link href={subItem.path} className={`menu-dropdown-item ${isActive(subItem.path) ? "menu-dropdown-item-active" : "menu-dropdown-item-inactive"}`}>
|
||||||
href={subItem.path}
|
|
||||||
className={`menu-dropdown-item ${
|
|
||||||
isActive(subItem.path)
|
|
||||||
? "menu-dropdown-item-active"
|
|
||||||
: "menu-dropdown-item-inactive"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{subItem.name}
|
{subItem.name}
|
||||||
<span className="flex items-center gap-1 ml-auto">
|
<span className="flex items-center gap-1 ml-auto">
|
||||||
{subItem.new && (
|
{subItem.new && <span className={`${isActive(subItem.path) ? "menu-dropdown-badge-active" : "menu-dropdown-badge-inactive"} menu-dropdown-badge`}>new</span>}
|
||||||
<span
|
{subItem.pro && <span className={`${isActive(subItem.path) ? "menu-dropdown-badge-active" : "menu-dropdown-badge-inactive"} menu-dropdown-badge`}>pro</span>}
|
||||||
className={`ml-auto ${
|
|
||||||
isActive(subItem.path)
|
|
||||||
? "menu-dropdown-badge-active"
|
|
||||||
: "menu-dropdown-badge-inactive"
|
|
||||||
} menu-dropdown-badge `}
|
|
||||||
>
|
|
||||||
new
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{subItem.pro && (
|
|
||||||
<span
|
|
||||||
className={`ml-auto ${
|
|
||||||
isActive(subItem.path)
|
|
||||||
? "menu-dropdown-badge-active"
|
|
||||||
: "menu-dropdown-badge-inactive"
|
|
||||||
} menu-dropdown-badge `}
|
|
||||||
>
|
|
||||||
pro
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@@ -228,160 +256,44 @@ const AppSidebar: React.FC = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
|
||||||
const [openSubmenu, setOpenSubmenu] = useState<{
|
|
||||||
type: "main" | "others";
|
|
||||||
index: number;
|
|
||||||
} | null>(null);
|
|
||||||
const [subMenuHeight, setSubMenuHeight] = useState<Record<string, number>>(
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const subMenuRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
|
||||||
|
|
||||||
// const isActive = (path: string) => path === pathname;
|
|
||||||
const isActive = useCallback((path: string) => path === pathname, [pathname]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Check if the current path matches any submenu item
|
|
||||||
let submenuMatched = false;
|
|
||||||
["main", "others"].forEach((menuType) => {
|
|
||||||
const items = menuType === "main" ? navItems : othersItems;
|
|
||||||
items.forEach((nav, index) => {
|
|
||||||
if (nav.subItems) {
|
|
||||||
nav.subItems.forEach((subItem) => {
|
|
||||||
if (isActive(subItem.path)) {
|
|
||||||
setOpenSubmenu({
|
|
||||||
type: menuType as "main" | "others",
|
|
||||||
index,
|
|
||||||
});
|
|
||||||
submenuMatched = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// If no submenu item matches, close the open submenu
|
|
||||||
if (!submenuMatched) {
|
|
||||||
setOpenSubmenu(null);
|
|
||||||
}
|
|
||||||
}, [pathname, isActive]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Set the height of the submenu items when the submenu is opened
|
|
||||||
if (openSubmenu !== null) {
|
|
||||||
const key = `${openSubmenu.type}-${openSubmenu.index}`;
|
|
||||||
if (subMenuRefs.current[key]) {
|
|
||||||
setSubMenuHeight((prevHeights) => ({
|
|
||||||
...prevHeights,
|
|
||||||
[key]: subMenuRefs.current[key]?.scrollHeight || 0,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [openSubmenu]);
|
|
||||||
|
|
||||||
const handleSubmenuToggle = (index: number, menuType: "main" | "others") => {
|
|
||||||
setOpenSubmenu((prevOpenSubmenu) => {
|
|
||||||
if (
|
|
||||||
prevOpenSubmenu &&
|
|
||||||
prevOpenSubmenu.type === menuType &&
|
|
||||||
prevOpenSubmenu.index === index
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return { type: menuType, index };
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
className={`fixed mt-16 flex flex-col lg:mt-0 top-0 px-5 left-0 bg-white dark:bg-gray-900 dark:border-gray-800 text-gray-900 h-screen transition-all duration-300 ease-in-out z-50 border-r border-gray-200
|
className={`fixed mt-16 flex flex-col lg:mt-0 top-0 px-5 left-0 bg-white dark:bg-gray-900 dark:border-gray-800 text-gray-900 h-screen transition-all duration-300 ease-in-out z-50 border-r border-gray-200
|
||||||
${
|
${isExpanded || isMobileOpen ? "w-[290px]" : isHovered ? "w-[290px]" : "w-[90px]"}
|
||||||
isExpanded || isMobileOpen
|
${isMobileOpen ? "translate-x-0" : "-translate-x-full"} lg:translate-x-0`}
|
||||||
? "w-[290px]"
|
|
||||||
: isHovered
|
|
||||||
? "w-[290px]"
|
|
||||||
: "w-[90px]"
|
|
||||||
}
|
|
||||||
${isMobileOpen ? "translate-x-0" : "-translate-x-full"}
|
|
||||||
lg:translate-x-0`}
|
|
||||||
onMouseEnter={() => !isExpanded && setIsHovered(true)}
|
onMouseEnter={() => !isExpanded && setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
>
|
>
|
||||||
<div
|
<div className={`py-8 flex ${!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"}`}>
|
||||||
className={`py-8 flex ${
|
|
||||||
!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
{isExpanded || isHovered || isMobileOpen ? (
|
<Image
|
||||||
<>
|
src="/images/logo/logo.svg"
|
||||||
<Image
|
alt="Logo"
|
||||||
className="dark:hidden"
|
width={isExpanded || isHovered || isMobileOpen ? 80 : 32}
|
||||||
src="/images/logo/logo.svg"
|
height={isExpanded || isHovered || isMobileOpen ? 50 : 32}
|
||||||
alt="Logo"
|
/>
|
||||||
width={80}
|
|
||||||
height={50}
|
|
||||||
/>
|
|
||||||
<Image
|
|
||||||
className="hidden dark:block"
|
|
||||||
src="/images/logo/logo.svg"
|
|
||||||
alt="Logo"
|
|
||||||
width={150}
|
|
||||||
height={40}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Image
|
|
||||||
src="/images/logo/logo.svg"
|
|
||||||
alt="Logo"
|
|
||||||
width={32}
|
|
||||||
height={32}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col overflow-y-auto duration-300 ease-linear no-scrollbar">
|
<div className="flex flex-col overflow-y-auto duration-300 ease-linear no-scrollbar">
|
||||||
<nav className="mb-6">
|
<nav className="mb-6">
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h2
|
<h2 className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"}`}>
|
||||||
className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${
|
{isExpanded || isHovered || isMobileOpen ? "Menu" : <HorizontaLDots />}
|
||||||
!isExpanded && !isHovered
|
|
||||||
? "lg:justify-center"
|
|
||||||
: "justify-start"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{isExpanded || isHovered || isMobileOpen ? (
|
|
||||||
"Menu"
|
|
||||||
) : (
|
|
||||||
<HorizontaLDots />
|
|
||||||
)}
|
|
||||||
</h2>
|
</h2>
|
||||||
{renderMenuItems(navItems, "main")}
|
{renderMenuItems(filteredNavItems, "main")}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div className="">
|
<h2 className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"}`}>
|
||||||
<h2
|
{isExpanded || isHovered || isMobileOpen ? "Others" : <HorizontaLDots />}
|
||||||
className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${
|
|
||||||
!isExpanded && !isHovered
|
|
||||||
? "lg:justify-center"
|
|
||||||
: "justify-start"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{isExpanded || isHovered || isMobileOpen ? (
|
|
||||||
"Others"
|
|
||||||
) : (
|
|
||||||
<HorizontaLDots />
|
|
||||||
)}
|
|
||||||
</h2>
|
</h2>
|
||||||
{renderMenuItems(othersItems, "others")}
|
{renderMenuItems(filteredOthersItems, "others")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
{/* {isExpanded || isHovered || isMobileOpen ? <SidebarWidget /> : null} */}
|
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AppSidebar;
|
export default AppSidebar;
|
||||||
Reference in New Issue
Block a user