mirror of
https://github.com/OwethuManagedServices/oms-website-nextjs.git
synced 2025-12-17 15:38:09 +00:00
377 lines
14 KiB
TypeScript
377 lines
14 KiB
TypeScript
// components/HeaderClient.tsx
|
|
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import Link from "next/link";
|
|
import Image from "next/image";
|
|
// Use usePathname only if needed for active state logic not shown here
|
|
// import { usePathname } from "next/navigation";
|
|
import {
|
|
FiChevronDown,
|
|
FiClipboard,
|
|
FiArrowRight,
|
|
FiMenu,
|
|
FiX,
|
|
FiLogIn, // Import the login icon
|
|
} from "react-icons/fi";
|
|
import ThemeToggle from "./ThemeToggle"; // Assuming ThemeToggle component exists
|
|
import type { Session } from "@auth/core/types"; // Import Session type for props
|
|
|
|
const omsLogoUrl = "/oms-logo.svg"; // Ensure this is in your /public folder
|
|
|
|
// --- Basic Dropdown Component ---
|
|
// (Keep the DropdownMenu and DropdownLink component definitions here as before)
|
|
type DropdownMenuProps = {
|
|
trigger: React.ReactNode;
|
|
children: React.ReactNode;
|
|
menuClasses?: string;
|
|
};
|
|
const DropdownMenu = ({
|
|
trigger,
|
|
children,
|
|
menuClasses = "w-48",
|
|
}: DropdownMenuProps) => (
|
|
// Using group-focus-within for better keyboard/touch accessibility
|
|
<div className="relative group">
|
|
<button className="flex items-center space-x-1 text-sm font-medium focus:outline-none inherit-color group">
|
|
{trigger}
|
|
<FiChevronDown className="w-4 h-4 transition-transform duration-200 group-hover:rotate-180 group-focus-within:rotate-180" />
|
|
</button>
|
|
<div
|
|
className={`
|
|
absolute left-0 mt-2 ${menuClasses} origin-top-left rounded-md shadow-lg z-50
|
|
bg-card border border-border {/* USE SEMANTIC VARS */}
|
|
opacity-0 invisible group-hover:opacity-100 group-hover:visible group-focus-within:opacity-100 group-focus-within:visible transition-all duration-200
|
|
`}
|
|
>
|
|
<div className="py-1">{children}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
type DropdownLinkProps = {
|
|
href: string;
|
|
children: React.ReactNode;
|
|
onClick?: () => void; // Added onClick for mobile menu closure
|
|
};
|
|
const DropdownLink = ({ href, children, onClick }: DropdownLinkProps) => (
|
|
<Link
|
|
href={href}
|
|
onClick={onClick} // Call onClick if provided
|
|
className="block w-full text-left px-4 py-2 text-sm text-card-foreground hover:bg-secondary hover:text-primary" // USE SEMANTIC VARS
|
|
>
|
|
{children}
|
|
</Link>
|
|
);
|
|
// --- End Dropdown Component ---
|
|
|
|
// --- Define Props for the Client Component ---
|
|
type HeaderClientProps = {
|
|
session: Session | null; // Accept session from the server component
|
|
handleSignIn: () => void; // Pass sign-in handler
|
|
handleSignOut: () => void; // Pass sign-out handler
|
|
};
|
|
|
|
// --- The Main Client Component ---
|
|
const HeaderClient = ({
|
|
session,
|
|
handleSignIn,
|
|
handleSignOut,
|
|
}: HeaderClientProps) => {
|
|
// --- Client-side state and hooks ---
|
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
// const pathname = usePathname() || "/"; // Uncomment if needed
|
|
// const isActive = (path: string) => pathname === path; // Uncomment if needed
|
|
|
|
const toggleMenu = () => setIsMenuOpen((open) => !open);
|
|
const handleMobileLinkClick = () => setIsMenuOpen(false);
|
|
|
|
const currentLogo = omsLogoUrl;
|
|
|
|
return (
|
|
// Use semantic variables for header background and border
|
|
<header className="sticky top-0 z-50 shadow-md bg-background border-b border-border">
|
|
{/* Top Row */}
|
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="flex justify-between items-center h-16">
|
|
{/* Logo */}
|
|
<Link
|
|
href="/"
|
|
className="flex items-center space-x-2"
|
|
title="OMS Home"
|
|
>
|
|
<Image
|
|
src={currentLogo}
|
|
alt="OMS Logo"
|
|
width={40}
|
|
height={40}
|
|
priority
|
|
/>
|
|
{/* Use semantic variable for text color */}
|
|
<span className="text-xl font-bold text-foreground hidden sm:inline">
|
|
Owethu Managed Services
|
|
</span>
|
|
</Link>
|
|
|
|
{/* Desktop Navigation */}
|
|
<nav className="hidden md:flex items-center space-x-6 lg:space-x-8">
|
|
{/* Use semantic variables for text, hover uses primary */}
|
|
<Link
|
|
href="/"
|
|
className={`text-sm font-medium text-foreground/80 hover:text-primary transition`}
|
|
>
|
|
Home
|
|
</Link>
|
|
<Link
|
|
href="/tech-talk"
|
|
className={`text-sm font-medium text-foreground/80 hover:text-primary transition`}
|
|
>
|
|
Tech Talk
|
|
</Link>
|
|
<Link
|
|
href="/about"
|
|
className={`text-sm font-medium text-foreground/80 hover:text-primary transition`}
|
|
>
|
|
About Us
|
|
</Link>
|
|
<Link
|
|
href="/contact"
|
|
className={`text-sm font-medium text-foreground/80 hover:text-primary transition`}
|
|
>
|
|
Contact Us
|
|
</Link>
|
|
</nav>
|
|
|
|
{/* Desktop Utilities */}
|
|
<div className="hidden md:flex items-center space-x-4">
|
|
<ThemeToggle />
|
|
|
|
{/* Request Demo Button */}
|
|
<Link
|
|
href="/request-demo"
|
|
className="flex items-center text-sm font-medium bg-primary text-primary-foreground px-3 py-1.5 rounded-lg hover:bg-opacity-90 transition-colors"
|
|
title="Request a Demo"
|
|
>
|
|
<FiClipboard className="w-4 h-4 mr-1.5" />
|
|
Request Demo
|
|
</Link>
|
|
|
|
{/* --- Auth Section --- */}
|
|
{session?.user ? (
|
|
// Logged In: User Dropdown
|
|
<DropdownMenu
|
|
menuClasses="w-40 right-0 left-auto" // Align dropdown to the right
|
|
trigger={
|
|
<div className="flex items-center space-x-2 cursor-pointer hover:opacity-80 transition">
|
|
<Image
|
|
src={session.user.image || "/default-avatar.png"} // Provide a fallback avatar
|
|
alt={session.user.name || "User"}
|
|
width={32}
|
|
height={32}
|
|
className="rounded-full ring-1 ring-primary ring-offset-2 ring-offset-background" // Added offset
|
|
/>
|
|
{/* Optionally hide name on smaller desktop screens */}
|
|
<span className="text-sm font-medium text-foreground hidden lg:inline">
|
|
{session.user.name?.split(" ")[0]}
|
|
</span>
|
|
</div>
|
|
}
|
|
>
|
|
{/* Sign Out inside the dropdown */}
|
|
<button
|
|
onClick={() => handleSignOut()}
|
|
className="block w-full text-left px-4 py-2 text-sm text-destructive hover:bg-secondary hover:text-destructive transition" // Destructive color
|
|
>
|
|
Sign Out
|
|
</button>
|
|
</DropdownMenu>
|
|
) : (
|
|
// Logged Out: Icon Button to Sign In
|
|
<button
|
|
onClick={() => handleSignIn()}
|
|
className="p-2 rounded-full text-foreground/70 hover:text-primary hover:bg-secondary focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background transition-colors"
|
|
title="Sign In"
|
|
aria-label="Sign In"
|
|
>
|
|
<FiLogIn className="w-5 h-5" />
|
|
</button>
|
|
)}
|
|
{/* --- End Auth Section --- */}
|
|
</div>
|
|
|
|
{/* Mobile Buttons */}
|
|
<div className="md:hidden flex items-center">
|
|
<ThemeToggle /> {/* Theme toggle remains */}
|
|
<button
|
|
onClick={toggleMenu}
|
|
className="text-foreground/60 hover:text-primary focus:outline-none ml-3" // Use semantic muted color, hover primary
|
|
aria-label="Toggle menu"
|
|
aria-expanded={isMenuOpen}
|
|
aria-controls="mobile-menu"
|
|
>
|
|
{isMenuOpen ? (
|
|
<FiX className="w-6 h-6" />
|
|
) : (
|
|
<FiMenu className="w-6 h-6" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Secondary Row (Desktop Only) */}
|
|
<div className="bg-primary">
|
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="hidden md:flex justify-between items-center h-12">
|
|
<nav className="flex items-center space-x-6 lg:space-x-8 text-primary-foreground">
|
|
<div className="hover:text-opacity-80 transition-opacity">
|
|
<DropdownMenu trigger={<span>Services</span>}>
|
|
<DropdownLink href="/services/resource-augmentation">
|
|
Resource Augmentation
|
|
</DropdownLink>
|
|
<DropdownLink href="/services/project-management">
|
|
Project Management
|
|
</DropdownLink>
|
|
<DropdownLink href="/services/product-development">
|
|
Product Development
|
|
</DropdownLink>
|
|
</DropdownMenu>
|
|
</div>
|
|
<div className="hover:text-opacity-80 transition-opacity">
|
|
<DropdownMenu trigger={<span>Products</span>}>
|
|
<DropdownLink href="/products/obse">OBSE</DropdownLink>
|
|
</DropdownMenu>
|
|
</div>
|
|
<div className="hover:text-opacity-80 transition-opacity">
|
|
<DropdownMenu trigger={<span>Join Our Team</span>}>
|
|
<DropdownLink href="/join-us/vacancies">
|
|
Vacancies
|
|
</DropdownLink>
|
|
<DropdownLink href="/join-us/portal">
|
|
Recruitment Portal
|
|
</DropdownLink>
|
|
</DropdownMenu>
|
|
</div>
|
|
</nav>
|
|
<Link
|
|
href="/services"
|
|
className="flex items-center text-sm font-medium text-primary-foreground hover:text-opacity-80 transition-opacity group"
|
|
>
|
|
Explore Our Offerings
|
|
<FiArrowRight className="w-4 h-4 ml-1.5 transition-transform duration-200 group-hover:translate-x-1" />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Menu Panel */}
|
|
<div
|
|
id="mobile-menu"
|
|
// Use semantic variables for background and border
|
|
className={`md:hidden absolute top-full left-0 w-full shadow-lg transition-all duration-300 ease-in-out overflow-hidden bg-card border-t border-border ${
|
|
isMenuOpen
|
|
? "max-h-[calc(100vh-4rem)] py-4 overflow-y-auto"
|
|
: "max-h-0 py-0" // Animate height, allow scroll
|
|
}`}
|
|
>
|
|
{/* Use semantic variable for text color */}
|
|
<nav className="container mx-auto px-4 sm:px-6 lg:px-8 flex flex-col space-y-1 text-foreground">
|
|
{" "}
|
|
{/* Reduced space-y */}
|
|
{/* Mobile Links */}
|
|
<DropdownLink href="/" onClick={handleMobileLinkClick}>
|
|
Home
|
|
</DropdownLink>
|
|
<DropdownLink href="/about" onClick={handleMobileLinkClick}>
|
|
About Us
|
|
</DropdownLink>
|
|
<span className="pt-3 pb-1 text-xs uppercase text-muted-foreground">
|
|
Services
|
|
</span>
|
|
<DropdownLink
|
|
href="/services/resource-augmentation"
|
|
onClick={handleMobileLinkClick}
|
|
>
|
|
Resource Augmentation
|
|
</DropdownLink>
|
|
<DropdownLink
|
|
href="/services/project-management"
|
|
onClick={handleMobileLinkClick}
|
|
>
|
|
Project Management
|
|
</DropdownLink>
|
|
<DropdownLink
|
|
href="/services/product-development"
|
|
onClick={handleMobileLinkClick}
|
|
>
|
|
Product Development
|
|
</DropdownLink>
|
|
<span className="pt-3 pb-1 text-xs uppercase text-muted-foreground">
|
|
Products
|
|
</span>
|
|
<DropdownLink href="/products/obse" onClick={handleMobileLinkClick}>
|
|
OBSE
|
|
</DropdownLink>
|
|
<span className="pt-3 pb-1 text-xs uppercase text-muted-foreground">
|
|
Join Us
|
|
</span>
|
|
<DropdownLink
|
|
href="/join-us/vacancies"
|
|
onClick={handleMobileLinkClick}
|
|
>
|
|
Vacancies
|
|
</DropdownLink>
|
|
<DropdownLink href="/join-us/portal" onClick={handleMobileLinkClick}>
|
|
Recruitment Portal
|
|
</DropdownLink>
|
|
<DropdownLink href="/contact" onClick={handleMobileLinkClick}>
|
|
Contact Us
|
|
</DropdownLink>
|
|
{/* Demo Button */}
|
|
<div className="pt-4">
|
|
<Link
|
|
href="/request-demo"
|
|
onClick={handleMobileLinkClick}
|
|
// Use semantic variables for button style
|
|
className="flex w-full justify-center items-center text-sm font-medium bg-primary text-primary-foreground px-4 py-2 rounded-lg hover:bg-opacity-90 transition-colors"
|
|
title="Request a Demo"
|
|
>
|
|
<FiClipboard className="w-4 h-4 mr-1.5" />
|
|
Request Demo
|
|
</Link>
|
|
</div>
|
|
{/* Auth Buttons in Mobile Menu */}
|
|
<div className="pt-4 border-t border-border mt-4">
|
|
{session?.user ? (
|
|
// Sign Out Button (Text)
|
|
<button
|
|
onClick={() => {
|
|
handleSignOut();
|
|
handleMobileLinkClick();
|
|
}}
|
|
className="flex items-center w-full text-left px-4 py-2 text-sm font-medium text-destructive hover:bg-secondary transition"
|
|
>
|
|
Sign Out
|
|
</button>
|
|
) : (
|
|
// Sign In Button (Text or Icon+Text)
|
|
<button
|
|
onClick={() => {
|
|
handleSignIn();
|
|
handleMobileLinkClick();
|
|
}}
|
|
className="flex items-center w-full text-left px-4 py-2 text-sm font-medium text-foreground hover:bg-secondary hover:text-primary transition"
|
|
>
|
|
<FiLogIn className="w-4 h-4 mr-2" /> {/* Added icon here too */}
|
|
Sign In
|
|
</button>
|
|
)}
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
);
|
|
};
|
|
|
|
export default HeaderClient;
|