s
This commit is contained in:
@@ -29,10 +29,12 @@ export default memo(function SiteMenu({
|
||||
const { t } = useTranslation("menu");
|
||||
const pathname = usePathname();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [userEmail, setUserEmail] = useState<string | null>(null);
|
||||
const [userEmail, setUserEmail] = useState<string | null>(() => getStoredUserEmail());
|
||||
|
||||
useEffect(() => {
|
||||
setUserEmail(getStoredUserEmail());
|
||||
const onAuthUpdated = () => setUserEmail(getStoredUserEmail());
|
||||
window.addEventListener("auth:updated", onAuthUpdated);
|
||||
return () => window.removeEventListener("auth:updated", onAuthUpdated);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -94,8 +96,8 @@ export default memo(function SiteMenu({
|
||||
];
|
||||
|
||||
const profileItems: MenuItem[] = [
|
||||
{ labelKey: "yourProfile", href: "/join", icon: "👤" },
|
||||
{ labelKey: "settings", href: "/join", icon: "⚙️" },
|
||||
{ labelKey: "yourProfile", href: "/digital/profile", icon: "👤" },
|
||||
{ labelKey: "settings", href: "/settings/notifications", icon: "⚙️" },
|
||||
{ labelKey: "favorites", href: "/", icon: "❤️" },
|
||||
{ labelKey: "pricing", href: "/pricing", icon: "👑" },
|
||||
{ labelKey: "nomadInsurance", href: "#", icon: "🛡️", ad: true },
|
||||
@@ -196,11 +198,12 @@ export default memo(function SiteMenu({
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
{userEmail && (
|
||||
<div className="p-4 border-t border-gray-100 dark:border-gray-800">
|
||||
<div className="p-4 border-t border-gray-100 dark:border-gray-800">
|
||||
{userEmail ? (
|
||||
<button
|
||||
onClick={() => {
|
||||
pbLogout();
|
||||
setUserEmail(null);
|
||||
onClose();
|
||||
}}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 rounded-lg text-sm text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
|
||||
@@ -208,8 +211,19 @@ export default memo(function SiteMenu({
|
||||
<span>🚪</span>
|
||||
<span>{t("logout")}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
) : (
|
||||
<button
|
||||
onClick={() => {
|
||||
onLoginClick();
|
||||
onClose();
|
||||
}}
|
||||
className="flex items-center gap-2 w-full px-3 py-2 rounded-lg text-sm text-[#ff4d4f] hover:bg-[#ff4d4f]/10 transition-colors"
|
||||
>
|
||||
<span>👤</span>
|
||||
<span>{t("login")}</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useTranslation } from "@/i18n/navigation";
|
||||
import { Link, useTranslation } from "@/i18n/navigation";
|
||||
import { pbLogout } from "@/app/lib/pocketbase";
|
||||
|
||||
function getAvatarLetter(email: string): string {
|
||||
@@ -16,9 +16,9 @@ interface UserAvatarProps {
|
||||
onLogout?: () => void;
|
||||
}
|
||||
|
||||
/** 圆形头像 + VIP 标识 + 点击展开退出按钮 */
|
||||
/** 圆形头像 + 账户菜单 */
|
||||
export default function UserAvatar({ email, isVip = false, onLogout }: UserAvatarProps) {
|
||||
const { t } = useTranslation("common");
|
||||
const { t } = useTranslation("menu");
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -41,8 +41,9 @@ export default function UserAvatar({ email, isVip = false, onLogout }: UserAvata
|
||||
<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")}
|
||||
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={t("yourProfile")}
|
||||
aria-expanded={open}
|
||||
>
|
||||
{getAvatarLetter(email)}
|
||||
</button>
|
||||
@@ -52,11 +53,42 @@ export default function UserAvatar({ email, isVip = false, onLogout }: UserAvata
|
||||
</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">
|
||||
<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 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-700"
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user