mmrwinston001's picture
Upload 32 files (#3)
6ce679b verified
import React, { useState, useEffect, useRef } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Menu, X, Sun, Moon, LogOut, User as UserIcon } from 'lucide-react';
import { auth } from '../firebase';
import { onAuthStateChanged, User as FirebaseUser, signOut } from 'firebase/auth';
const Header: React.FC = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [darkMode, setDarkMode] = useState(false);
const [user, setUser] = useState<FirebaseUser | null>(null);
const [profilePic, setProfilePic] = useState<string | null>(null);
const [isProfileOpen, setIsProfileOpen] = useState(false);
const [showSignOutConfirm, setShowSignOutConfirm] = useState(false); // NEW
const profileRef = useRef<HTMLDivElement>(null);
const location = useLocation();
const navLinks = [
{ path: '/', label: 'Humanizer X' },
{ path: '/gpt-checker', label: 'GPT Checker' },
{ path: '/word-counter', label: 'Word Counter' }, // <-- added
{ path: '/about', label: 'About' },
{ path: '/contact', label: 'Contact' },
{ path: '/blogs', label: 'Blog' },
];
// Theme handling
useEffect(() => {
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
setDarkMode(true);
document.documentElement.classList.add('dark');
} else {
setDarkMode(false);
document.documentElement.classList.remove('dark');
}
}, []);
const toggleDarkMode = () => {
const newDark = !darkMode;
setDarkMode(newDark);
if (newDark) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
};
// Auth listener
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
setProfilePic(currentUser?.photoURL || null);
});
return () => unsubscribe();
}, []);
// Close profile dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (profileRef.current && !profileRef.current.contains(e.target as Node)) {
setIsProfileOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const isActive = (path: string) =>
location.pathname === path || location.pathname.startsWith(path + "/");
// Sign out function
const handleSignOut = async () => {
try {
await signOut(auth);
setIsProfileOpen(false);
setIsMenuOpen(false);
setShowSignOutConfirm(false); // Close modal
} catch (err) {
console.error('Sign out error:', err);
}
};
return (
<header className="bg-gray-900 dark:bg-gray-950 text-white py-2 shadow-lg transition-colors duration-300">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<Link to="/" className="flex items-center gap-2">
<img
src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
alt="Humanizer X - AI Text Humanization Platform Logo"
title="Humanizer X - Navigate to homepage"
className="w-10 h-10 object-contain"
/>
<span className="text-2xl font-bold bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
Humanizer X
</span>
</Link>
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center space-x-8">
{navLinks.map((link) => (
<Link
key={link.path}
to={link.path}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
isActive(link.path)
? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20'
: 'text-gray-300 hover:text-blue-600 dark:hover:text-blue-400'
}`}
>
{link.label}
</Link>
))}
</nav>
{/* Right side controls */}
<div className="flex items-center gap-4 relative">
{/* Profile/Auth Section */}
{!user ? (
<div className="hidden md:flex items-center gap-3">
<Link
to="/login"
className="px-4 py-2 text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
>
Sign In
</Link>
<Link
to="/signup"
className="px-4 py-2 bg-gradient-to-r from-indigo-500 to-indigo-700 text-white rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all"
>
Get Started
</Link>
</div>
) : (
<div className="relative" ref={profileRef}>
<button
onClick={() => setIsProfileOpen(!isProfileOpen)}
className="w-10 h-10 rounded-full border-2 border-gray-300 dark:border-gray-600 overflow-hidden flex items-center justify-center bg-gray-200 dark:bg-gray-700"
>
{profilePic ? (
<img src={profilePic} alt="Profile" className="w-full h-full object-cover" />
) : (
<UserIcon size={20} className="text-black dark:text-white" />
)}
</button>
{isProfileOpen && (
<div className="absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-200 dark:border-gray-700 p-4 z-50">
<div className="flex items-center gap-3">
{profilePic ? (
<img src={profilePic} alt="Profile" className="w-12 h-12 rounded-full object-cover" />
) : (
<UserIcon size={40} className="text-black dark:text-white" />
)}
<div>
<h4 className="text-gray-900 dark:text-gray-100 font-semibold">
{user.displayName || 'User'}
</h4>
<p className="text-sm text-gray-500 dark:text-gray-400">{user.email}</p>
</div>
</div>
<hr className="my-3 border-gray-200 dark:border-gray-700" />
<Link
to="/profile"
className="flex items-center gap-2 text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors py-2"
>
<UserIcon size={18} /> My Profile
</Link>
<button
onClick={() => setShowSignOutConfirm(true)} // show modal
className="flex items-center gap-2 text-gray-700 dark:text-gray-300 hover:text-red-600 dark:hover:text-red-400 transition-colors py-2 w-full text-left"
>
<LogOut size={18} /> Sign Out
</button>
</div>
)}
</div>
)}
{/* Mobile menu button */}
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="md:hidden p-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
aria-label={isMenuOpen ? "Close menu" : "Open menu"}
>
{isMenuOpen ? <X size={20} /> : <Menu size={20} />}
</button>
</div>
</div>
{/* Mobile Navigation */}
{isMenuOpen && (
<div className="md:hidden border-t border-gray-200 dark:border-gray-700 py-4">
<nav className="flex flex-col space-y-2">
{navLinks.map((link) => (
<Link
key={link.path}
to={link.path}
onClick={() => setIsMenuOpen(false)}
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
isActive(link.path)
? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20'
: 'text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400'
}`}
>
{link.label}
</Link>
))}
{/* Signed-in user: Show Sign Out */}
{user ? (
<button
onClick={() => {
setIsMenuOpen(false);
setShowSignOutConfirm(true);
}}
className="px-3 py-2 rounded-md text-left text-gray-700 dark:text-gray-300 hover:text-red-600 dark:hover:text-red-400 transition-colors font-medium"
>
<LogOut size={16} className="inline mr-2" /> Sign Out
</button>
) : (
<>
{/* Show Sign In if not signed in */}
<Link
to="/signin"
onClick={() => setIsMenuOpen(false)}
className="px-3 py-2 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
>
Sign In
</Link>
{/* Get Started Button */}
<Link
to="/get-started"
onClick={() => setIsMenuOpen(false)}
className="px-3 py-2 rounded-md text-sm font-semibold text-white bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 transition-colors text-center"
>
Get Started
</Link>
</>
)}
</nav>
</div>
)}
</div>
{/* Sign Out Confirmation Modal */}
{showSignOutConfirm && (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
<div className="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-xl w-80 text-center">
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
Confirm Sign Out
</h2>
<p className="text-gray-700 dark:text-gray-300 mb-6">
Are you sure you want to sign out?
</p>
<div className="flex justify-center gap-4">
<button
onClick={handleSignOut}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition"
>
Yes, Sign Out
</button>
<button
onClick={() => setShowSignOutConfirm(false)}
className="px-4 py-2 bg-gray-300 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-600 transition"
>
Cancel
</button>
</div>
</div>
</div>
)}
</header>
);
};
export default Header;