Files
Edu/apps/portal-shell/src/widgets/topbar/user-menu/index.tsx
SpecialX a28a6bd6ea feat(portal-shell): clean widget design tokens and fix lint:tokens (P1-6)
516 mechanical replacements across 25 widget files:
- spacing xs/sm/md/lg/xl to numeric 1/2/3/4/6
- text-heading-3 to text-lg font-semibold
- bg-danger to bg-destructive
- border border dedup

Fix .eslintrc.tokens.js to use typescript-eslint parser (was importing
uninstalled @typescript-eslint/parser). lint:tokens now passes.
2026-07-22 15:07:38 +08:00

57 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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";
/**
* user-menutopbar / top
*
* 通过 useCurrentUser 查询 apollo-router → iam 子图的 me 数据。
* 展示用户头像 + 下拉菜单(昵称 / 邮箱 / 角色)。
*
* 关联portal-shell spec §5.6 统一 Hook、M8 验收
*/
import { useState } from "react";
import { useCurrentUser } from "@/lib/api/topbar";
import { useAuth } from "@/providers/AuthProvider";
import type { PluginProps } from "@/lib/types";
export default function UserMenu(_props: PluginProps): React.ReactElement {
const { user } = useAuth();
const [open, setOpen] = useState(false);
const { data: me } = useCurrentUser();
const displayName = me?.name ?? user.name;
const displayEmail = me?.email ?? user.email;
const displayRole = me?.role ?? user.role;
return (
<div className="relative">
<button
type="button"
aria-label="用户菜单"
aria-expanded={open}
onClick={() => setOpen((v) => !v)}
className="flex items-center gap-2 rounded-md bg-card px-2 py-1 text-foreground"
>
<span
aria-hidden
className="flex h-heading-3 w-heading-3 items-center justify-center rounded-full bg-primary text-primary-foreground"
>
{displayName.charAt(0).toUpperCase()}
</span>
<span className="text-sm">{displayName}</span>
</button>
{open ? (
<ul className="absolute right-0 z-50 mt-2 w-56 rounded-xl border bg-card p-2 shadow-md">
<li className="border-b border py-1">
<p className="text-sm text-foreground">{displayName}</p>
<p className="text-xs text-muted-foreground">{displayEmail}</p>
</li>
<li className="py-1 text-sm text-muted-foreground">
{displayRole}
</li>
</ul>
) : null}
</div>
);
}