Files
gitlab-instance-0a899031_cn…/app/components/UserAvatar.tsx
2026-06-08 06:05:31 -05:00

102 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect, useRef } from "react";
import { Link, useTranslation } from "@/i18n/navigation";
import { pbLogout } from "@/app/lib/pocketbase";
function getAvatarLetter(email: string): string {
const local = email.split("@")[0];
if (!local) return "?";
return local.slice(0, 1).toUpperCase();
}
interface UserAvatarProps {
email: string;
isVip?: boolean;
onLogout?: () => void;
/** 设置后点击头像打开外部菜单(如 SiteMenu不再展开内置下拉 */
onOpenMenu?: () => void;
}
/** 圆形头像 + 账户菜单 */
export default function UserAvatar({ email, isVip = false, onLogout, onOpenMenu }: UserAvatarProps) {
const { t } = useTranslation("menu");
const { t: tNav } = useTranslation("nav");
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const handleLogout = async () => {
await pbLogout();
setOpen(false);
onLogout?.();
};
return (
<div className="relative" ref={ref}>
<button
type="button"
onClick={() => (onOpenMenu ? onOpenMenu() : setOpen((o) => !o))}
className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[#ff4d4f] text-sm font-semibold text-white shadow-sm ring-2 ring-white transition hover:bg-[#ff7a45] dark:ring-gray-900"
aria-label={onOpenMenu ? tNav("menu") : t("yourProfile")}
aria-expanded={onOpenMenu ? undefined : open}
>
{getAvatarLetter(email)}
</button>
{isVip && (
<span className="absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-amber-500 px-1 text-[10px] font-bold text-white shadow-sm">
VIP
</span>
)}
{!onOpenMenu && open && (
<div className="absolute right-0 top-full z-50 mt-2 w-[260px] overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xl dark:border-gray-700 dark:bg-gray-800">
<div className="border-b border-gray-100 px-4 py-3 dark:border-gray-700">
<p className="truncate text-sm font-semibold text-gray-900 dark:text-white">{email}</p>
<p className="mt-0.5 text-xs text-gray-500 dark:text-gray-400">{isVip ? "VIP" : t("yourProfile")}</p>
</div>
<div className="px-2 py-2">
<Link
href="/digital/profile"
onClick={() => setOpen(false)}
className="flex w-full items-center gap-2 rounded-lg px-3 py-2.5 text-left text-sm font-medium text-gray-700 transition hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700"
>
<span className="w-5 text-center">👤</span>
<span>{t("yourProfile")}</span>
</Link>
<Link
href="/settings/notifications"
onClick={() => setOpen(false)}
className="flex w-full items-center gap-2 rounded-lg px-3 py-2.5 text-left text-sm font-medium text-gray-700 transition hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700"
>
<span className="w-5 text-center"></span>
<span>{t("settings")}</span>
</Link>
<Link
href="/pricing"
onClick={() => setOpen(false)}
className="flex w-full items-center gap-2 rounded-lg px-3 py-2.5 text-left text-sm font-medium text-gray-700 transition hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700"
>
<span className="w-5 text-center">👑</span>
<span>{t("pricing")}</span>
</Link>
</div>
<button
onClick={handleLogout}
className="flex w-full items-center gap-2 border-t border-gray-100 px-5 py-3 text-left text-sm font-semibold text-red-600 transition hover:bg-red-50 dark:border-gray-700 dark:text-red-300 dark:hover:bg-red-950/30"
>
<span className="w-5 text-center">🚪</span>
{t("logout")}
</button>
</div>
)}
</div>
);
}