102 lines
5.6 KiB
JavaScript
102 lines
5.6 KiB
JavaScript
import React, { useState, useRef, useEffect } from 'react';
|
|
import { Search, Bell, ChevronDown, LogOut, User, Settings, Shield } from 'lucide-react';
|
|
|
|
export default function Header({ profile }) {
|
|
const [isProfileOpen, setIsProfileOpen] = useState(false);
|
|
const dropdownRef = useRef(null);
|
|
|
|
// Close dropdown on click outside
|
|
useEffect(() => {
|
|
function handleClickOutside(event) {
|
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
setIsProfileOpen(false);
|
|
}
|
|
}
|
|
document.addEventListener("mousedown", handleClickOutside);
|
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
}, []);
|
|
|
|
const handleLogout = async () => {
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
|
try {
|
|
const response = await fetch('/logout', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': csrfToken
|
|
}
|
|
});
|
|
if (response.ok || response.status === 419) {
|
|
window.location.href = '/';
|
|
}
|
|
} catch (error) {
|
|
console.error('Logout failed:', error);
|
|
// Fallback for SPA
|
|
window.location.href = '/';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<header className="h-16 bg-white border-b border-gray-100 flex items-center justify-between px-8 sticky top-0 z-50">
|
|
{/* Logo */}
|
|
<div className="flex items-center gap-2">
|
|
<div className="bg-red-500 p-1.5 rounded-lg">
|
|
<svg className="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 2v2m0 16v2m-8-8h2m12 0h2M5.6 5.6l1.4 1.4m10 10l1.4 1.4M5.6 18.4l1.4-1.4m10-10l1.4-1.4M12 6a6 6 0 100 12 6 6 0 000-12z" />
|
|
</svg>
|
|
</div>
|
|
<h1 className="text-xl font-extrabold tracking-tight text-gray-900 leading-none">GymPro</h1>
|
|
</div>
|
|
|
|
{/* User Actions */}
|
|
<div className="flex items-center gap-6 ml-auto">
|
|
<button className="relative p-2 text-gray-400 hover:text-gray-900 hover:bg-gray-100 rounded-full transition-all text-xs font-bold uppercase tracking-widest">
|
|
<Bell size={22} />
|
|
<span className="absolute top-2 right-2 w-2.5 h-2.5 bg-red-500 border-2 border-white rounded-full"></span>
|
|
</button>
|
|
|
|
<div className="relative" ref={dropdownRef}>
|
|
<div
|
|
onClick={() => setIsProfileOpen(!isProfileOpen)}
|
|
className="flex items-center gap-3 pl-6 border-l border-gray-100 cursor-pointer group select-none"
|
|
>
|
|
<div className="flex flex-col items-end">
|
|
<span className="text-sm font-bold text-gray-900">{profile?.user?.name || 'Loading...'}</span>
|
|
<span className="text-[10px] text-gray-400 font-bold uppercase tracking-wider">{profile?.role ? profile.role.charAt(0).toUpperCase() + profile.role.slice(1) : 'User'}</span>
|
|
</div>
|
|
<div className="w-10 h-10 bg-gray-100 rounded-xl overflow-hidden border-2 border-transparent group-hover:border-red-500/20 transition-all flex items-center justify-center relative">
|
|
<div className="w-8 h-8 bg-gray-300 rounded-full flex items-center justify-center text-white text-xs font-bold">
|
|
{profile?.user?.name?.split(' ').map(n => n[0]).join('').toUpperCase() || 'U'}
|
|
</div>
|
|
</div>
|
|
<ChevronDown size={14} className={`text-gray-400 transition-transform duration-200 ${isProfileOpen ? 'rotate-180' : ''}`} />
|
|
</div>
|
|
|
|
{/* Profile Dropdown */}
|
|
{isProfileOpen && (
|
|
<div className="absolute right-0 mt-4 w-64 bg-white rounded-[2rem] shadow-2xl border border-gray-50 overflow-hidden animate-in fade-in slide-in-from-top-4 duration-200 py-3 z-50">
|
|
<div className="px-6 py-4 border-b border-gray-50 mb-2">
|
|
<p className="text-xs font-black text-gray-400 uppercase tracking-widest">Active Account</p>
|
|
<p className="text-sm font-bold text-gray-900 mt-1">{profile?.user?.email || '...'}</p>
|
|
</div>
|
|
|
|
<div className="px-2 space-y-1 text-xs font-bold">
|
|
<div className="h-px bg-gray-50 mx-4 my-2" />
|
|
|
|
<button
|
|
onClick={handleLogout}
|
|
className="w-full flex items-center gap-3 px-4 py-3 text-red-500 hover:bg-red-50 rounded-2xl transition-all group"
|
|
>
|
|
<div className="w-8 h-8 rounded-xl bg-red-50 flex items-center justify-center group-hover:bg-red-100 transition-all">
|
|
<LogOut size={16} />
|
|
</div>
|
|
<span>Sign Out</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|