This commit is contained in:
@@ -8,7 +8,6 @@ import { apiGetCurrentUser } from "@/service/auth";
|
|||||||
import { setUserData } from "@/store/features/userSlice";
|
import { setUserData } from "@/store/features/userSlice";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { usePathname } from "next/navigation";
|
|
||||||
|
|
||||||
export default function AdminLayout({
|
export default function AdminLayout({
|
||||||
children,
|
children,
|
||||||
@@ -17,8 +16,6 @@ export default function AdminLayout({
|
|||||||
}) {
|
}) {
|
||||||
const { isExpanded, isHovered, isMobileOpen } = useSidebar();
|
const { isExpanded, isHovered, isMobileOpen } = useSidebar();
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const pathname = usePathname();
|
|
||||||
const isHomePage = pathname === "/";
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
@@ -34,19 +31,17 @@ export default function AdminLayout({
|
|||||||
|
|
||||||
|
|
||||||
// Dynamic class for main content margin based on sidebar state
|
// Dynamic class for main content margin based on sidebar state
|
||||||
const mainContentMargin = isHomePage
|
const mainContentMargin = isMobileOpen
|
||||||
? "ml-0"
|
|
||||||
: isMobileOpen
|
|
||||||
? "ml-0"
|
? "ml-0"
|
||||||
: isExpanded || isHovered
|
: isExpanded || isHovered
|
||||||
? "lg:ml-[290px]"
|
? "lg:ml-[290px]"
|
||||||
: "lg:ml-[90px]";
|
: "lg:ml-[0px]";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen xl:flex">
|
<div className="min-h-screen xl:flex">
|
||||||
{/* Sidebar and Backdrop */}
|
{/* Sidebar and Backdrop */}
|
||||||
{!isHomePage && <AppSidebar />}
|
<AppSidebar />
|
||||||
{!isHomePage && <Backdrop />}
|
<Backdrop />
|
||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<div
|
<div
|
||||||
className={`flex-1 transition-all duration-300 ease-in-out ${mainContentMargin}`}
|
className={`flex-1 transition-all duration-300 ease-in-out ${mainContentMargin}`}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const useSidebar = () => {
|
|||||||
export const SidebarProvider: React.FC<{ children: React.ReactNode }> = ({
|
export const SidebarProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
const [isMobileOpen, setIsMobileOpen] = useState(false);
|
const [isMobileOpen, setIsMobileOpen] = useState(false);
|
||||||
const [isMobile, setIsMobile] = useState(false);
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|||||||
@@ -5,15 +5,12 @@ import UserDropdown from "@/components/header/UserDropdown";
|
|||||||
import { useSidebar } from "@/context/SidebarContext";
|
import { useSidebar } from "@/context/SidebarContext";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
|
||||||
import React, { useState ,useEffect,useRef} from "react";
|
import React, { useState ,useEffect,useRef} from "react";
|
||||||
|
|
||||||
const AppHeader: React.FC = () => {
|
const AppHeader: React.FC = () => {
|
||||||
const [isApplicationMenuOpen, setApplicationMenuOpen] = useState(false);
|
const [isApplicationMenuOpen, setApplicationMenuOpen] = useState(false);
|
||||||
|
|
||||||
const { isMobileOpen, toggleSidebar, toggleMobileSidebar } = useSidebar();
|
const { isMobileOpen, toggleSidebar, toggleMobileSidebar } = useSidebar();
|
||||||
const pathname = usePathname();
|
|
||||||
const isHomePage = pathname === "/";
|
|
||||||
|
|
||||||
const handleToggle = () => {
|
const handleToggle = () => {
|
||||||
if (window.innerWidth >= 1024) {
|
if (window.innerWidth >= 1024) {
|
||||||
@@ -47,7 +44,6 @@ const AppHeader: React.FC = () => {
|
|||||||
<header className="sticky top-0 flex w-full bg-white border-gray-200 z-99 dark:border-gray-800 dark:bg-gray-900 lg:border-b">
|
<header className="sticky top-0 flex w-full bg-white border-gray-200 z-99 dark:border-gray-800 dark:bg-gray-900 lg:border-b">
|
||||||
<div className="flex flex-col items-center justify-between grow lg:flex-row lg:px-6">
|
<div className="flex flex-col items-center justify-between grow lg:flex-row lg:px-6">
|
||||||
<div className="flex items-center justify-between w-full gap-2 px-3 py-3 border-b border-gray-200 dark:border-gray-800 sm:gap-4 lg:justify-normal lg:border-b-0 lg:px-0 lg:py-4">
|
<div className="flex items-center justify-between w-full gap-2 px-3 py-3 border-b border-gray-200 dark:border-gray-800 sm:gap-4 lg:justify-normal lg:border-b-0 lg:px-0 lg:py-4">
|
||||||
{!isHomePage && (
|
|
||||||
<button
|
<button
|
||||||
className="items-center justify-center w-10 h-10 text-gray-500 border-gray-200 rounded-lg z-99999 dark:border-gray-800 lg:flex dark:text-gray-400 lg:h-11 lg:w-11 lg:border"
|
className="items-center justify-center w-10 h-10 text-gray-500 border-gray-200 rounded-lg z-99999 dark:border-gray-800 lg:flex dark:text-gray-400 lg:h-11 lg:w-11 lg:border"
|
||||||
onClick={handleToggle}
|
onClick={handleToggle}
|
||||||
@@ -86,7 +82,6 @@ const AppHeader: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{/* Cross Icon */}
|
{/* Cross Icon */}
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
|
|
||||||
<Link href="/" className="lg:hidden">
|
<Link href="/" className="lg:hidden">
|
||||||
<Image
|
<Image
|
||||||
|
|||||||
@@ -90,19 +90,22 @@ const AppSidebar: React.FC = () => {
|
|||||||
const { isExpanded, isMobileOpen, isHovered, setIsHovered } = useSidebar();
|
const { isExpanded, isMobileOpen, isHovered, setIsHovered } = useSidebar();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
|
||||||
const [openSubmenu, setOpenSubmenu] = useState<{
|
const [openSubmenu, setOpenSubmenu] = useState<{
|
||||||
type: "main" | "others";
|
type: "main" | "others";
|
||||||
index: number;
|
index: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [subMenuHeight, setSubMenuHeight] = useState<Record<string, number>>({});
|
const [subMenuHeight, setSubMenuHeight] = useState<Record<string, number>>(
|
||||||
|
{},
|
||||||
|
);
|
||||||
const subMenuRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
const subMenuRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
||||||
|
|
||||||
const isActive = useCallback((path: string) => path === pathname, [pathname]);
|
const isActive = useCallback((path: string) => path === pathname, [pathname]);
|
||||||
|
|
||||||
const handleSubmenuToggle = (index: number, menuType: "main" | "others") => {
|
const handleSubmenuToggle = (index: number, menuType: "main" | "others") => {
|
||||||
setOpenSubmenu((prev) =>
|
setOpenSubmenu((prev) =>
|
||||||
prev?.type === menuType && prev?.index === index ? null : { type: menuType, index }
|
prev?.type === menuType && prev?.index === index
|
||||||
|
? null
|
||||||
|
: { type: menuType, index },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,23 +154,41 @@ const AppSidebar: React.FC = () => {
|
|||||||
onClick={() => handleSubmenuToggle(index, menuType)}
|
onClick={() => handleSubmenuToggle(index, menuType)}
|
||||||
className={`menu-item group uppercase ${
|
className={`menu-item group uppercase ${
|
||||||
openSubmenu?.type === menuType && openSubmenu?.index === index
|
openSubmenu?.type === menuType && openSubmenu?.index === index
|
||||||
? "menu-item-active" : "menu-item-inactive"
|
? "menu-item-active"
|
||||||
|
: "menu-item-inactive"
|
||||||
} cursor-pointer ${!isExpanded && !isHovered ? "lg:justify-center" : "lg:justify-start"}`}
|
} cursor-pointer ${!isExpanded && !isHovered ? "lg:justify-center" : "lg:justify-start"}`}
|
||||||
>
|
>
|
||||||
<span className={openSubmenu?.type === menuType && openSubmenu?.index === index ? "menu-item-icon-active" : "menu-item-icon-inactive"}>
|
<span
|
||||||
|
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>
|
||||||
<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 href={nav.path} className={`menu-item group ${isActive(nav.path) ? "menu-item-active" : "menu-item-inactive"}`}>
|
<Link
|
||||||
<span className={isActive(nav.path) ? "menu-item-icon-active" : "menu-item-icon-inactive"}>
|
href={nav.path}
|
||||||
|
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) && (
|
||||||
@@ -178,18 +199,40 @@ const AppSidebar: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{nav.subItems && (isExpanded || isHovered || isMobileOpen) && (
|
{nav.subItems && (isExpanded || isHovered || isMobileOpen) && (
|
||||||
<div
|
<div
|
||||||
ref={(el) => { subMenuRefs.current[`${menuType}-${index}`] = el; }}
|
ref={(el) => {
|
||||||
|
subMenuRefs.current[`${menuType}-${index}`] = el;
|
||||||
|
}}
|
||||||
className="overflow-hidden transition-all duration-300"
|
className="overflow-hidden transition-all duration-300"
|
||||||
style={{ height: openSubmenu?.type === menuType && openSubmenu?.index === index ? `${subMenuHeight[`${menuType}-${index}`]}px` : "0px" }}
|
style={{
|
||||||
|
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 href={subItem.path} className={`menu-dropdown-item ${isActive(subItem.path) ? "menu-dropdown-item-active" : "menu-dropdown-item-inactive"}`}>
|
<Link
|
||||||
|
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 && <span className={`${isActive(subItem.path) ? "menu-dropdown-badge-active" : "menu-dropdown-badge-inactive"} menu-dropdown-badge`}>new</span>}
|
{subItem.new && (
|
||||||
{subItem.pro && <span className={`${isActive(subItem.path) ? "menu-dropdown-badge-active" : "menu-dropdown-badge-inactive"} menu-dropdown-badge`}>pro</span>}
|
<span
|
||||||
|
className={`${isActive(subItem.path) ? "menu-dropdown-badge-active" : "menu-dropdown-badge-inactive"} menu-dropdown-badge`}
|
||||||
|
>
|
||||||
|
new
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{subItem.pro && (
|
||||||
|
<span
|
||||||
|
className={`${isActive(subItem.path) ? "menu-dropdown-badge-active" : "menu-dropdown-badge-inactive"} menu-dropdown-badge`}
|
||||||
|
>
|
||||||
|
pro
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@@ -204,13 +247,15 @@ const AppSidebar: React.FC = () => {
|
|||||||
|
|
||||||
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 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 || isHovered ? "w-[290px] px-5" : "w-0 px-0 border-none overflow-hidden"}
|
||||||
${isMobileOpen ? "translate-x-0" : "-translate-x-full"} lg:translate-x-0`}
|
${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 className={`py-8 flex ${!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"}`}>
|
<div
|
||||||
|
className={`py-8 flex ${!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"}`}
|
||||||
|
>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<Image
|
<Image
|
||||||
src="/images/logo/logo.svg"
|
src="/images/logo/logo.svg"
|
||||||
@@ -224,8 +269,14 @@ const AppSidebar: React.FC = () => {
|
|||||||
<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 className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"}`}>
|
<h2
|
||||||
{isExpanded || isHovered || isMobileOpen ? "Menu" : <HorizontaLDots />}
|
className={`mb-4 text-xs uppercase flex leading-[20px] text-gray-400 ${!isExpanded && !isHovered ? "lg:justify-center" : "justify-start"}`}
|
||||||
|
>
|
||||||
|
{isExpanded || isHovered || isMobileOpen ? (
|
||||||
|
"Menu"
|
||||||
|
) : (
|
||||||
|
<HorizontaLDots />
|
||||||
|
)}
|
||||||
</h2>
|
</h2>
|
||||||
{renderMenuItems(ALL_NAV_ITEMS, "main")}
|
{renderMenuItems(ALL_NAV_ITEMS, "main")}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user