perfect-postcode/frontend/src/components/ui/UserMenu.tsx
2026-03-18 22:46:08 +00:00

104 lines
3.7 KiB
TypeScript

import { useState, useRef, useEffect } from 'react';
import type { AuthUser } from '../../hooks/useAuth';
import type { Page } from './Header';
import { PAGE_PATHS } from './Header';
import { SunIcon } from './icons/SunIcon';
import { MoonIcon } from './icons/MoonIcon';
export default function UserMenu({
user,
theme,
onToggleTheme,
onLogout,
onNavigate,
}: {
user: AuthUser;
theme: 'light' | 'dark';
onToggleTheme: () => void;
onLogout: () => void;
onNavigate: (page: Page) => 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">
<div className="flex items-center justify-between gap-2">
<p className="text-sm font-medium text-navy-950 dark:text-warm-100 truncate">
{user.email}
</p>
<span
className={`shrink-0 text-xs font-medium px-1.5 py-0.5 rounded ${
user.subscription === 'licensed' || user.isAdmin
? 'bg-teal-100 text-teal-700 dark:bg-teal-900/30 dark:text-teal-400'
: 'bg-warm-100 text-warm-500 dark:bg-warm-700 dark:text-warm-400'
}`}
>
{user.subscription === 'licensed' || user.isAdmin ? 'Full Access' : 'Inner London'}
</span>
</div>
</div>
<div className="p-1">
<button
onClick={onToggleTheme}
className="w-full flex items-center gap-2 px-3 py-2 text-sm text-warm-700 dark:text-warm-300 hover:bg-warm-50 dark:hover:bg-warm-700 rounded"
>
{theme === 'light' ? (
<SunIcon className="w-4 h-4" />
) : (
<MoonIcon className="w-4 h-4" />
)}
Theme: {theme === 'light' ? 'Light' : 'Dark'}
</button>
<a
href={PAGE_PATHS.account}
onClick={(e) => {
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
e.preventDefault();
setOpen(false);
onNavigate('account');
}}
className="block 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"
>
Account
</a>
<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>
);
}