From 56bfdb461476bdd4f6bc67491c3e8fe84916a698 Mon Sep 17 00:00:00 2001 From: Timothy Yin Date: Tue, 10 Mar 2026 23:21:50 +0800 Subject: [PATCH] feat(settings): enhance user profile and password management with improved error handling and UI updates --- apps/web/app/dashboard/settings/page.tsx | 234 ++++++++++++++++++++--- 1 file changed, 212 insertions(+), 22 deletions(-) diff --git a/apps/web/app/dashboard/settings/page.tsx b/apps/web/app/dashboard/settings/page.tsx index f6319bd..fedc578 100644 --- a/apps/web/app/dashboard/settings/page.tsx +++ b/apps/web/app/dashboard/settings/page.tsx @@ -2,8 +2,8 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { Alert, Button, CloseButton, Input, Label, Spinner, TextField } from "@heroui/react"; -import { Fingerprint, Pencil, TrashBin, Xmark, Check } from "@gravity-ui/icons"; -import { authClient } from "@/lib/auth-client"; +import { Fingerprint, Lock, Pencil, Person, TrashBin, Xmark, Check } from "@gravity-ui/icons"; +import { authClient, useSession } from "@/lib/auth-client"; type Passkey = { id: string; @@ -13,6 +13,80 @@ type Passkey = { }; export default function SettingsPage() { + const { data: session, refetch: refetchSession } = useSession(); + + // ── Profile ────────────────────────────────────────────────────────────── + const [profileName, setProfileName] = useState(""); + const [savingProfile, setSavingProfile] = useState(false); + const [profileError, setProfileError] = useState(""); + const [profileSuccess, setProfileSuccess] = useState(""); + + // sync name from session once loaded + useEffect(() => { + if (session?.user.name) setProfileName(session.user.name); + }, [session?.user.name]); + + const handleSaveProfile = async () => { + setProfileError(""); + setProfileSuccess(""); + setSavingProfile(true); + try { + const res = await authClient.updateUser({ name: profileName.trim() }); + if (res?.error) { + setProfileError(res.error.message ?? "保存失败"); + } else { + setProfileSuccess("显示名称已更新"); + await refetchSession(); + } + } catch { + setProfileError("保存失败,请重试"); + } finally { + setSavingProfile(false); + } + }; + + // ── Password ───────────────────────────────────────────────────────────── + const [currentPassword, setCurrentPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [savingPw, setSavingPw] = useState(false); + const [pwError, setPwError] = useState(""); + const [pwSuccess, setPwSuccess] = useState(""); + + const handleChangePassword = async () => { + setPwError(""); + setPwSuccess(""); + if (newPassword !== confirmPassword) { + setPwError("两次输入的新密码不一致"); + return; + } + if (newPassword.length < 8) { + setPwError("新密码至少需要 8 位"); + return; + } + setSavingPw(true); + try { + const res = await authClient.changePassword({ + currentPassword, + newPassword, + revokeOtherSessions: false, + }); + if (res?.error) { + setPwError(res.error.message ?? "修改密码失败"); + } else { + setPwSuccess("密码已修改成功"); + setCurrentPassword(""); + setNewPassword(""); + setConfirmPassword(""); + } + } catch { + setPwError("修改密码失败,请重试"); + } finally { + setSavingPw(false); + } + }; + + // ── Passkey ─────────────────────────────────────────────────────────────── const [passkeys, setPasskeys] = useState([]); const [loading, setLoading] = useState(true); const [addingName, setAddingName] = useState(null); // null = collapsed @@ -128,29 +202,126 @@ export default function SettingsPage() {

账号设置

-

管理您的安全凭据

+

管理您的个人信息和安全凭据

- {error && ( - - - - {error} - - setError("")} /> - - )} - {success && ( - - - - {success} - - setSuccess("")} /> - - )} + {/* ── Profile section ─────────────────────────────────────────────── */} +
+
+
+ +
+
+

个人信息

+

修改您的显示名称

+
+
+
+ + + setProfileName(e.target.value)} + onKeyDown={(e) => { if (e.key === "Enter") void handleSaveProfile(); }} + /> + + {profileError && ( + + + {profileError} + setProfileError("")} /> + + )} + {profileSuccess && ( + + + {profileSuccess} + setProfileSuccess("")} /> + + )} +
+ +
+
+
- {/* Passkey section */} + {/* ── Password section ─────────────────────────────────────────────── */} +
+
+
+ +
+
+

修改密码

+

建议定期更换密码以保护账号安全

+
+
+
+ + + setCurrentPassword(e.target.value)} + /> + + + + setNewPassword(e.target.value)} + /> + + + + setConfirmPassword(e.target.value)} + onKeyDown={(e) => { if (e.key === "Enter") void handleChangePassword(); }} + /> + + {pwError && ( + + + {pwError} + setPwError("")} /> + + )} + {pwSuccess && ( + + + {pwSuccess} + setPwSuccess("")} /> + + )} +
+ +
+
+
+ + {/* ── Passkey section ──────────────────────────────────────────────── */}
@@ -194,6 +365,25 @@ export default function SettingsPage() {
)} + {error && ( +
+ + + {error} + setError("")} /> + +
+ )} + {success && ( +
+ + + {success} + setSuccess("")} /> + +
+ )} + {loading ? (