Files
HRM-System/resources/js/pages/landing-page/components/Header.tsx
2026-04-13 08:16:56 +08:00

286 lines
11 KiB
TypeScript
Executable File

import React, { useState, useEffect } from 'react';
import { Link, usePage } from '@inertiajs/react';
import { Menu, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { isUserRegistrationEnabled, getImagePath, getCookie, isDemoMode } from '@/utils/helpers';
interface CustomPage {
id: number;
title: string;
slug: string;
}
interface HeaderProps {
brandColor?: string;
settings: {
company_name: string;
};
sectionData?: any;
customPages?: CustomPage[];
}
export default function Header({ settings, sectionData, customPages = [], brandColor = '#3b82f6' }: HeaderProps) {
const { t } = useTranslation();
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
const { props } = usePage();
const isSaas = (props as any).globalSettings?.is_saas;
const auth = (props as any).auth;
const globalSettings = (props as any).globalSettings || {};
const isDemo = isDemoMode(props);
const logoLight = globalSettings.logoLight || 'logo/logo-light.png';
const logoDark = globalSettings.logoDark || 'logo/logo-dark.png';
let themeMode = 'light';
if (isDemo) {
const themeSettings = getCookie('themeSettings');
if (themeSettings) {
try {
const parsed = JSON.parse(themeSettings);
themeMode = parsed.appearance || 'light'
} catch {
themeMode = 'light';
}
}
} else {
themeMode = globalSettings.themeMode || 'light';
}
const isDark = themeMode === 'dark';
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const menuItems = customPages.map(page => ({
name: page.title,
href: route('custom-page.show', page.slug)
}));
const isTransparent = sectionData?.transparent;
const backgroundColor = sectionData?.background_color || '#ffffff';
const buttonStyle = sectionData?.button_style || 'filled'; // 'filled' or 'outline'
const textColor = sectionData?.text_color || '';
const navTextColor = textColor || (isDark ? '#d1d5db' : '#4b5563');
const getButtonStyles = (isHovered: boolean) => {
if (buttonStyle === 'outline') {
return isHovered
? { backgroundColor: brandColor, color: 'white', borderColor: brandColor }
: { backgroundColor: 'transparent', color: brandColor, borderColor: brandColor };
}
return isHovered
? { backgroundColor: 'white', color: brandColor, borderColor: brandColor }
: { backgroundColor: brandColor, color: 'white', borderColor: brandColor };
};
const getHeaderClasses = () => {
if (isTransparent) {
return isScrolled
? `bg-white/95 ${isDark ? 'dark:bg-gray-900/95' : ''} backdrop-blur-xl shadow-lg border-b border-gray-200/50 ${isDark ? 'dark:border-gray-700/50' : ''}`
: 'bg-transparent';
}
return isScrolled
? `shadow-lg border-b border-gray-200/50 ${isDark ? 'dark:border-gray-700/50' : ''}`
: '';
};
const getHeaderStyle = () => {
if (isTransparent) return {};
return { backgroundColor };
};
return (
<header
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${getHeaderClasses()}`}
style={getHeaderStyle()}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<div className="flex-shrink-0">
<Link href={route("home")} className="flex items-center">
<img
src={getImagePath(isDark ? logoLight : logoDark)}
alt={settings.company_name}
className="h-8 w-auto"
onError={(e) => {
e.currentTarget.style.display = 'none';
e.currentTarget.nextElementSibling?.classList.remove('hidden');
}}
/>
<span className={`hidden text-2xl font-bold ${isDark ? 'text-white' : 'text-gray-900'} transition-colors`}>
{settings.company_name}
</span>
</Link>
</div>
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center space-x-8" role="navigation" aria-label="Main navigation">
<Link
href={route('home')}
className="text-sm font-medium transition-colors relative group"
style={{ color: navTextColor }}
onMouseEnter={(e) => e.currentTarget.style.color = brandColor}
onMouseLeave={(e) => e.currentTarget.style.color = navTextColor}
>
{t('Home')}
<span
className="absolute -bottom-1 left-0 w-0 h-0.5 transition-all group-hover:w-full"
style={{ backgroundColor: brandColor }}
aria-hidden="true"
></span>
</Link>
{menuItems.map((item) => (
<Link
key={item.name}
href={item.href}
className="text-sm font-medium transition-colors relative group"
style={{ color: navTextColor }}
onMouseEnter={(e) => e.currentTarget.style.color = brandColor}
onMouseLeave={(e) => e.currentTarget.style.color = navTextColor}
>
{item.name}
<span
className="absolute -bottom-1 left-0 w-0 h-0.5 transition-all group-hover:w-full"
style={{ backgroundColor: brandColor }}
aria-hidden="true"
></span>
</Link>
))}
</nav>
{/* Auth Buttons */}
<div className="hidden md:flex items-center gap-4">
{auth?.user ? (
<Link
href={route('dashboard')}
className="px-6 py-2.5 rounded-lg text-sm font-semibold transition-colors border"
style={getButtonStyles(false)}
onMouseEnter={(e) => Object.assign(e.currentTarget.style, getButtonStyles(true))}
onMouseLeave={(e) => Object.assign(e.currentTarget.style, getButtonStyles(false))}
>
{t('Dashboard')}
</Link>
) : (
<>
<Link
href={route('login')}
className="text-sm font-medium transition-colors"
style={{ color: navTextColor }}
onMouseEnter={(e) => e.currentTarget.style.color = brandColor}
onMouseLeave={(e) => e.currentTarget.style.color = navTextColor}
>
{t('Login')}
</Link>
{isSaas && isUserRegistrationEnabled() && (
<Link
href={route('register')}
className="px-6 py-2.5 rounded-lg text-sm font-semibold transition-colors border"
style={getButtonStyles(false)}
onMouseEnter={(e) => Object.assign(e.currentTarget.style, getButtonStyles(true))}
onMouseLeave={(e) => Object.assign(e.currentTarget.style, getButtonStyles(false))}
>
{t('Get Started')}
</Link>
)}
</>
)}
</div>
{/* Mobile menu button */}
<div className="md:hidden">
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className={`p-2 ${isDark ? 'text-gray-300 hover:text-white hover:bg-gray-800' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-100'} rounded-lg transition-colors`}
aria-label={isMenuOpen ? 'Close navigation menu' : 'Open navigation menu'}
aria-expanded={isMenuOpen}
aria-controls="mobile-menu"
>
{isMenuOpen ? <X size={20} /> : <Menu size={20} />}
</button>
</div>
</div>
{/* Mobile Navigation */}
{isMenuOpen && (
<div className={`md:hidden border-t ${isDark ? 'border-gray-700' : 'border-gray-200'}`} id="mobile-menu">
<div
className={`px-4 py-6 space-y-4 ${isDark ? 'bg-gray-900' : 'bg-white'}`}
style={isTransparent ? {} : { backgroundColor }}
>
<Link
href={route('home')}
className="block text-base font-medium transition-colors"
style={{ color: navTextColor }}
onClick={() => setIsMenuOpen(false)}
onMouseEnter={(e) => e.currentTarget.style.color = brandColor}
onMouseLeave={(e) => e.currentTarget.style.color = navTextColor}
>
{t('Home')}
</Link>
{menuItems.map((item) => (
<Link
key={item.name}
href={item.href}
className="block text-base font-medium transition-colors"
style={{ color: navTextColor }}
onClick={() => setIsMenuOpen(false)}
onMouseEnter={(e) => e.currentTarget.style.color = brandColor}
onMouseLeave={(e) => e.currentTarget.style.color = navTextColor}
>
{item.name}
</Link>
))}
<div className={`pt-4 space-y-3 border-t ${isDark ? 'border-gray-700' : 'border-gray-200'}`}>
{auth?.user ? (
<Link
href={route('dashboard')}
className="block w-full text-center py-2.5 rounded-lg text-sm font-semibold transition-colors border"
style={getButtonStyles(false)}
onMouseEnter={(e) => Object.assign(e.currentTarget.style, getButtonStyles(true))}
onMouseLeave={(e) => Object.assign(e.currentTarget.style, getButtonStyles(false))}
>
{t('Dashboard')}
</Link>
) : (
<>
<Link
href={route('login')}
className="block w-full text-center py-2.5 text-sm font-medium transition-colors"
style={{ color: navTextColor }}
onMouseEnter={(e) => e.currentTarget.style.color = brandColor}
onMouseLeave={(e) => e.currentTarget.style.color = navTextColor}
>
{t('Login')}
</Link>
{isSaas && isUserRegistrationEnabled() && (
<Link
href={route('register')}
className="block w-full text-center py-2.5 rounded-lg text-sm font-semibold transition-colors border"
style={getButtonStyles(false)}
onMouseEnter={(e) => Object.assign(e.currentTarget.style, getButtonStyles(true))}
onMouseLeave={(e) => Object.assign(e.currentTarget.style, getButtonStyles(false))}
>
{t('Get Started')}
</Link>
)}
</>
)}
</div>
</div>
</div>
)}
</div>
</header>
);
}