67 lines
2.2 KiB
TypeScript
67 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useRef } from "react";
|
|
import { 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;
|
|
}
|
|
|
|
/** 圆形头像 + VIP 标识 + 点击展开退出按钮 */
|
|
export default function UserAvatar({ email, isVip = false, onLogout }: UserAvatarProps) {
|
|
const { t } = useTranslation("common");
|
|
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={() => 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 dark:ring-gray-900 hover:bg-[#ff7a45]"
|
|
aria-label={t("logout")}
|
|
>
|
|
{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>
|
|
)}
|
|
{open && (
|
|
<div className="absolute right-0 top-full z-50 mt-2 min-w-[120px] rounded-lg border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800">
|
|
<button
|
|
onClick={handleLogout}
|
|
className="flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700"
|
|
>
|
|
{t("logout")}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|