54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
import { useState, useRef, useEffect } from 'react';
|
|
import type { AuthUser } from '../../hooks/useAuth';
|
|
|
|
export default function UserMenu({ user, onLogout }: { user: AuthUser; onLogout: () => void }) {
|
|
const [open, setOpen] = useState(false);
|
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Close on outside click
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const handleClick = (e: MouseEvent) => {
|
|
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
|
setOpen(false);
|
|
}
|
|
};
|
|
document.addEventListener('mousedown', handleClick);
|
|
return () => document.removeEventListener('mousedown', handleClick);
|
|
}, [open]);
|
|
|
|
const initial = user.email[0].toUpperCase();
|
|
|
|
return (
|
|
<div className="relative" ref={menuRef}>
|
|
<button
|
|
onClick={() => setOpen((prev) => !prev)}
|
|
className="flex items-center justify-center w-8 h-8 rounded-full bg-teal-600 text-white text-sm font-medium hover:bg-teal-700"
|
|
title={user.email}
|
|
>
|
|
{initial}
|
|
</button>
|
|
|
|
{open && (
|
|
<div className="absolute right-0 top-10 w-56 bg-white dark:bg-warm-800 border border-warm-200 dark:border-warm-700 rounded-lg shadow-lg z-50">
|
|
<div className="px-4 py-3 border-b border-warm-200 dark:border-warm-700">
|
|
<p className="text-sm font-medium text-navy-950 dark:text-warm-100 truncate">
|
|
{user.email}
|
|
</p>
|
|
</div>
|
|
<div className="p-1">
|
|
<button
|
|
onClick={() => {
|
|
setOpen(false);
|
|
onLogout();
|
|
}}
|
|
className="w-full text-left px-3 py-2 text-sm text-warm-700 dark:text-warm-300 hover:bg-warm-50 dark:hover:bg-warm-700 rounded"
|
|
>
|
|
Log out
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|