update
This commit is contained in:
266
frontEnd/components/shared/layout/header/Header.tsx
Normal file
266
frontEnd/components/shared/layout/header/Header.tsx
Normal file
@@ -0,0 +1,266 @@
|
||||
"use client";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import OffcanvasMenu from "./OffcanvasMenu";
|
||||
import { OffcanvasData } from "@/public/data/offcanvas-data";
|
||||
import { useNavigationServices } from "@/lib/hooks/useServices";
|
||||
|
||||
const Header = () => {
|
||||
const [isOffcanvasOpen, setIsOffcanvasOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [isActive, setIsActive] = useState(true);
|
||||
const [openDropdown, setOpenDropdown] = useState<number | null>(null);
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
|
||||
// Fetch services from API
|
||||
const { services: apiServices, loading: servicesLoading, error: servicesError } = useNavigationServices();
|
||||
|
||||
// Create dynamic navigation data with services from API
|
||||
const navigationData = useMemo(() => {
|
||||
const baseNavigation = [...OffcanvasData];
|
||||
|
||||
// Find the Services menu item and update its submenu with API data
|
||||
const servicesIndex = baseNavigation.findIndex(item => item.title === "Services");
|
||||
if (servicesIndex !== -1 && apiServices.length > 0) {
|
||||
baseNavigation[servicesIndex] = {
|
||||
...baseNavigation[servicesIndex],
|
||||
submenu: apiServices.map(service => ({
|
||||
id: service.id + 1000, // Offset to avoid conflicts with existing IDs
|
||||
title: service.title,
|
||||
path: `/services/${service.slug}`,
|
||||
parent_id: baseNavigation[servicesIndex].id,
|
||||
display_order: service.display_order,
|
||||
created_at: service.created_at,
|
||||
updated_at: service.updated_at
|
||||
}))
|
||||
} as any;
|
||||
}
|
||||
|
||||
return baseNavigation;
|
||||
}, [apiServices]);
|
||||
|
||||
// Static header data
|
||||
const headerData = {
|
||||
title: "EnterpriseSoft Solutions",
|
||||
logoUrl: "/images/logo.png",
|
||||
logoLightUrl: "/images/logo-light.png",
|
||||
navigationType: "both",
|
||||
headerClass: "tp-header",
|
||||
scrolledClass: "navbar-active",
|
||||
buttonText: "Support Center",
|
||||
buttonUrl: "/support-center",
|
||||
buttonClass: "btn btn-primary d-none d-sm-flex",
|
||||
isActive: true,
|
||||
displayOrder: 1,
|
||||
metaData: JSON.stringify({
|
||||
mobileBreakpoint: 992,
|
||||
scrollThreshold: 50,
|
||||
hideOnMobile: false,
|
||||
mobileFirst: true,
|
||||
hamburgerMenu: true
|
||||
})
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
setTimeout(() => {
|
||||
setIsOffcanvasOpen(false);
|
||||
}, 900);
|
||||
setIsActive(false);
|
||||
};
|
||||
|
||||
const handleDropdownToggle = (index: number) => {
|
||||
setOpenDropdown(openDropdown === index ? null : index);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const scrollPosition = window.scrollY;
|
||||
if (scrollPosition > 50) {
|
||||
setScrolled(true);
|
||||
} else {
|
||||
setScrolled(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
|
||||
handleScroll();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, [scrolled]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsMobile(window.innerWidth < 992);
|
||||
setTimeout(() => {
|
||||
setIsOffcanvasOpen(false);
|
||||
}, 900);
|
||||
setIsActive(false);
|
||||
};
|
||||
|
||||
handleResize(); // Check on mount
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
// Use static data
|
||||
let logoSrc: string = headerData.logoUrl;
|
||||
let headerClass = headerData.headerClass;
|
||||
let buttonText = headerData.buttonText;
|
||||
let buttonUrl = headerData.buttonUrl;
|
||||
let buttonClass = headerData.buttonClass;
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
// Override logo based on pathname if needed (maintain existing behavior)
|
||||
if (
|
||||
pathname === "/career" ||
|
||||
pathname === "/" ||
|
||||
pathname === "/index" ||
|
||||
pathname === "/services" ||
|
||||
pathname === "/service-single"
|
||||
) {
|
||||
logoSrc = headerData.logoLightUrl;
|
||||
}
|
||||
|
||||
const handleOffCanvas = () => {
|
||||
setIsOffcanvasOpen(true);
|
||||
setIsActive(true);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className={headerClass}>
|
||||
<div className={"primary-navbar" + (scrolled ? " navbar-active" : " ")}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<nav className="navbar p-0">
|
||||
<div className="navbar__logo">
|
||||
<Link href="/" aria-label="go to home" className="logo-img">
|
||||
<Image
|
||||
src={logoSrc}
|
||||
alt="Logo"
|
||||
width={160}
|
||||
height={120}
|
||||
priority
|
||||
className="logo-image"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation Menu */}
|
||||
<div className="navbar__menu d-none d-lg-flex">
|
||||
<ul>
|
||||
{navigationData.map((item) =>
|
||||
item.title === "Support Center" ? null : item.submenu ? (
|
||||
<li
|
||||
className="navbar__item navbar__item--has-children"
|
||||
key={item.id}
|
||||
onMouseEnter={() => !isMobile && setOpenDropdown(item.id)}
|
||||
onMouseLeave={() => !isMobile && setOpenDropdown(null)}
|
||||
>
|
||||
<button
|
||||
aria-label="dropdown menu"
|
||||
className={
|
||||
"navbar__dropdown-label" +
|
||||
(openDropdown === item.id
|
||||
? " navbar__item-active"
|
||||
: " ")
|
||||
}
|
||||
onClick={() => isMobile && handleDropdownToggle(item.id)}
|
||||
>
|
||||
{item.title}
|
||||
{item.title === "Services" && servicesLoading && (
|
||||
<span className="loading-indicator">⏳</span>
|
||||
)}
|
||||
</button>
|
||||
<ul className={`navbar__sub-menu ${openDropdown === item.id ? 'show' : ''}`}>
|
||||
{item.title === "Services" && servicesLoading ? (
|
||||
<li>
|
||||
<span className="text-muted">Loading services...</span>
|
||||
</li>
|
||||
) : item.title === "Services" && servicesError ? (
|
||||
<li>
|
||||
<span className="text-danger">Failed to load services</span>
|
||||
</li>
|
||||
) : (
|
||||
item.submenu.map((subItem, subIndex) => (
|
||||
<li key={subIndex}>
|
||||
<Link
|
||||
href={subItem.path || "#"}
|
||||
className={
|
||||
pathname === subItem.path
|
||||
? " active-current-sub"
|
||||
: " "
|
||||
}
|
||||
>
|
||||
{subItem.title}
|
||||
</Link>
|
||||
</li>
|
||||
))
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
) : (
|
||||
<li
|
||||
className="navbar__item"
|
||||
key={item.id}
|
||||
>
|
||||
<Link
|
||||
href={item.path || "#"}
|
||||
className={
|
||||
pathname === item.path ? " active-current-link" : " "
|
||||
}
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="navbar__options">
|
||||
<Link href={buttonUrl} className={buttonClass}>
|
||||
{buttonText}
|
||||
</Link>
|
||||
<button
|
||||
className="open-offcanvas-nav d-lg-none"
|
||||
aria-label="toggle mobile menu"
|
||||
title="open offcanvas menu"
|
||||
onClick={handleOffCanvas}
|
||||
>
|
||||
<span className="icon-bar top-bar"></span>
|
||||
<span className="icon-bar middle-bar"></span>
|
||||
<span className="icon-bar bottom-bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<OffcanvasMenu
|
||||
isOffcanvasOpen={isOffcanvasOpen}
|
||||
handleClick={handleClick}
|
||||
isActive={isActive}
|
||||
navigationData={navigationData}
|
||||
servicesLoading={servicesLoading}
|
||||
servicesError={servicesError}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
Reference in New Issue
Block a user