Files
Jyotisha/frontend/src/components/profile-panel.tsx
T
Jesse_ChenandCursor dc6598d7e4 fix(web): keep the settings dialog one size and move billing into it (BUG-554)
The four account panes now share a fixed frame, chart profiles open as list then detail, and membership lives in the homepage dialog instead of a separate page.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 18:29:51 +08:00

76 lines
3.0 KiB
TypeScript

"use client";
import { UserAvatar } from "@/components/user-avatar";
import { beamAvatarPalettes, type BeamAvatarPatch } from "@/lib/beam-avatar";
import type { Account, Profile } from "@/lib/home-types";
export type ProfilePanelProps = {
readonly account: Account;
readonly profile: Profile;
readonly avatarSaving: boolean;
readonly avatarNotice: string;
readonly accountError: string;
readonly persistAvatar: (patch: BeamAvatarPatch) => void;
};
export function ProfilePanel({
account,
profile,
avatarSaving,
avatarNotice,
accountError,
persistAvatar,
}: ProfilePanelProps) {
return (
<>
{accountError && <p className="form-error" role="alert">{accountError}</p>}
{account.avatar && (
<section className="sheet-section avatar-section" aria-labelledby="avatar-section-title">
<div className="section-heading">
<b id="avatar-section-title">头像</b>
<small>Beam 形象由随机种子生成,刷新和换设备后保持一致</small>
</div>
<div className="avatar-editor">
<UserAvatar avatar={account.avatar} size={48} label="当前头像预览" />
<div className="avatar-editor-controls">
<div className="avatar-palette-list" role="radiogroup" aria-label="头像配色">
{beamAvatarPalettes.map((palette, index) => (
<button
key={palette.name}
className="avatar-palette"
type="button"
role="radio"
aria-checked={account.avatar?.palette === index}
aria-label={palette.name}
title={palette.name}
disabled={avatarSaving}
onClick={() => void persistAvatar({ action: "set_palette", palette: index })}
>
{palette.colors.map((color) => <span key={color} style={{ backgroundColor: color }} />)}
</button>
))}
</div>
<button
className="button-secondary avatar-randomize"
type="button"
disabled={avatarSaving}
onClick={() => void persistAvatar({ action: "randomize" })}
>
{avatarSaving ? "保存中" : "换一个形象"}
</button>
</div>
</div>
{avatarNotice && <p className="form-success avatar-notice" role="status">{avatarNotice}</p>}
</section>
)}
<section className="sheet-section" aria-labelledby="account-info-title">
<div className="section-heading"><b id="account-info-title">账户信息</b><small>登录与账户识别信息</small></div>
<dl className="account-info-list">
<div><dt>昵称</dt><dd>{profile.name.trim() || "尚未设置"}<small>与“我的星盘”名称同步</small></dd></div>
<div><dt>登录邮箱</dt><dd>{account.user.email || "尚未读取邮箱"}</dd></div>
</dl>
</section>
</>
);
}