extract account menu

This commit is contained in:
2026-07-16 11:34:55 +08:00
parent cbe1285248
commit 2c1d4c8775
+139 -58
View File
@@ -2,12 +2,13 @@ import {
CircleHelp,
FolderKanban,
Home,
LogOut,
Plus,
Settings,
User,
} from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { getClientProfileDashboard } from '../services/clientApi';
import BRAND_ICON from '../../../../packages/shared/src/icons/taonier-product-ip.png';
function cx(...classNames: Array<string | false | null | undefined>) {
return classNames.filter(Boolean).join(' ');
@@ -44,6 +45,131 @@ type LauncherSidebarProps = {
onLogout: () => void;
};
type SidebarAccountMenuProps = {
currentUser: SidebarUserInfo;
onClose: () => void;
onLogout: () => void;
onNoticeRequest: (title: string) => void;
};
function formatMudPoints(value: number | null | undefined) {
if (typeof value !== 'number' || !Number.isFinite(value)) {
return '--';
}
return Math.max(0, Math.floor(value)).toLocaleString('zh-CN');
}
function SidebarAccountMenu({
currentUser,
onClose,
onLogout,
onNoticeRequest,
}: SidebarAccountMenuProps) {
const [walletBalanceLabel, setWalletBalanceLabel] = useState('--');
const [walletBalanceStatus, setWalletBalanceStatus] =
useState('正在读取泥点余额');
useEffect(() => {
let disposed = false;
void getClientProfileDashboard()
.then((dashboard) => {
if (disposed) {
return;
}
setWalletBalanceLabel(formatMudPoints(dashboard.walletBalance));
setWalletBalanceStatus('泥点余额已读取');
})
.catch((error) => {
if (disposed) {
return;
}
setWalletBalanceLabel('--');
setWalletBalanceStatus(
error instanceof Error ? error.message : '泥点余额读取失败',
);
});
return () => {
disposed = true;
};
}, []);
function showNotice(title: string) {
onClose();
onNoticeRequest(title);
}
return (
<div
className="absolute bottom-[-4px] left-14 z-[90] grid w-[218px] gap-1.5 rounded-lg border border-(--platform-subpanel-border) bg-(--platform-subpanel-fill) p-2.5 text-(--platform-text-strong) shadow-[0_16px_38px_rgb(15_23_42_/_14%)]"
role="menu"
>
<strong className="text-[13px]">
{currentUser.displayName || '陶泥儿用户'}
</strong>
<span className="text-[11px] text-(--platform-text-soft)">
{currentUser.phoneNumberMasked ||
currentUser.publicUserCode ||
'账号信息读取中'}
</span>
<div
className="grid gap-2 rounded-xl bg-[linear-gradient(135deg,#eaa06a,#cf7a4a_58%,#b55c3b)] p-3 text-white shadow-[0_18px_38px_rgb(189_103_60_/_22%)]"
role="group"
aria-label="会员方案"
>
<div className="flex items-center justify-between gap-2">
<span
className="min-w-0 text-[12px] font-bold"
title={walletBalanceStatus}
>
{walletBalanceLabel}
</span>
<button
type="button"
className="grid size-7 shrink-0 place-items-center rounded-full border border-white/70 bg-white/15 text-white"
aria-label="充值泥点"
onClick={onClose}
>
<Plus size={14} aria-hidden="true" />
</button>
</div>
</div>
<button
type="button"
role="menuitem"
className={sidebarMenuItemClass}
onClick={
() => showNotice('使用指南')
// TODO open url https://kcnz41bksl1c.feishu.cn/wiki/YN5TwLxreiYHrYk9DNqcjz8QnZc
}
>
使
</button>
<button
type="button"
role="menuitem"
className={sidebarMenuItemClass}
onClick={
() => showNotice('反馈联系')
// TODO open url https://kcnz41bksl1c.feishu.cn/share/base/form/shrcn1p9WpuZMDxrI5ysLLJhw7k
}
>
</button>
<button
type="button"
role="menuitem"
className={sidebarMenuItemClass}
onClick={() => {
onClose();
onLogout();
}}
>
退
</button>
</div>
);
}
export function Sidebar({
activeView,
currentUser,
@@ -96,11 +222,6 @@ export function Sidebar({
setAccountMenuOpen(false);
}
function showNotice(title: string) {
closeMenus();
onNoticeRequest(title);
}
function changeView(view: LauncherView) {
closeMenus();
onViewChange(view);
@@ -110,10 +231,13 @@ export function Sidebar({
<aside className="w-16 sticky top-0 z-40 flex h-screen flex-col items-center gap-4 overflow-visible border-r border-(--platform-surface-border) bg-(--platform-nav-fill) px-0 pb-4.5 pt-4">
<div className="grid place-items-center" aria-label="陶泥儿">
<span className="grid size-12 place-items-center rounded-full bg-(--platform-button-primary-fill) text-[10px] font-bold tracking-normal text-(--platform-button-primary-text) shadow-(--platform-profile-action-shadow)">
<img src={BRAND_ICON} alt="brand logo"/>
<img src={BRAND_ICON} alt="brand logo" />
</span>
</div>
<nav className="grid justify-items-center gap-3.25" aria-label="客户端导航">
<nav
className="grid justify-items-center gap-3.25"
aria-label="客户端导航"
>
<button
type="button"
aria-label="首页"
@@ -156,56 +280,12 @@ export function Sidebar({
<User size={16} aria-hidden="true" />
</button>
{accountMenuOpen ? (
<div
className="absolute bottom-[-4px] left-14 z-[90] grid w-[218px] gap-1.5 rounded-lg border border-(--platform-subpanel-border) bg-(--platform-subpanel-fill) p-2.5 text-(--platform-text-strong) shadow-[0_16px_38px_rgb(15_23_42_/_14%)]"
role="menu"
>
<strong className="text-[13px]">
{currentUser.displayName || '陶泥儿用户'}
</strong>
<span className="text-[11px] text-(--platform-text-soft)">
{currentUser.phoneNumberMasked ||
currentUser.publicUserCode ||
'账号信息读取中'}
</span>
<button
type="button"
role="menuitem"
className={sidebarMenuItemClass}
onClick={() => showNotice('会员与升级')}
>
</button>
<button
type="button"
role="menuitem"
className={sidebarMenuItemClass}
onClick={() => showNotice('使用指南')}
>
使
</button>
<button
type="button"
role="menuitem"
className={sidebarMenuItemClass}
onClick={() => showNotice('反馈联系')}
>
</button>
<button
type="button"
role="menuitem"
className={sidebarMenuItemClass}
onClick={() => {
closeMenus();
onLogout();
}}
>
{/*// TODO align*/}
<LogOut size={14} aria-hidden="true" />
退
</button>
</div>
<SidebarAccountMenu
currentUser={currentUser}
onClose={closeMenus}
onLogout={onLogout}
onNoticeRequest={onNoticeRequest}
/>
) : null}
</div>
<div className="relative" ref={helpMenuRef}>
@@ -216,6 +296,7 @@ export function Sidebar({
className={sidebarHelpButtonClass}
onClick={() => {
// TODO open the guide
// TODO open url https://kcnz41bksl1c.feishu.cn/wiki/YN5TwLxreiYHrYk9DNqcjz8QnZc
}}
>
<CircleHelp size={17} aria-hidden="true" />