fix: enhance localization messages and improve account settings UI

- Added new success and failure messages for password updates in English and Chinese localization files.
- Updated `UpdateAvatarCard`, `UpdateNameCard`, and `UpdatePasswordCard` components to improve error handling and user feedback with toast notifications.
- Enhanced styling for the user avatar fallback icon in `UpdateAvatarCard` for better visual consistency.
This commit is contained in:
javayhu 2025-03-16 00:20:12 +08:00
parent c258cc7d0d
commit d51a22e030
6 changed files with 69 additions and 14 deletions

View File

@ -324,7 +324,9 @@
"newMinLength": "Password must be at least 8 characters",
"hint": "Please use at least 8 characters for password",
"showPassword": "Show password",
"hidePassword": "Hide password"
"hidePassword": "Hide password",
"success": "Password updated successfully",
"fail": "Failed to update password"
},
"save": "Save",
"saving": "Saving..."

View File

@ -319,7 +319,9 @@
"newMinLength": "密码必须至少包含 8 个字符",
"hint": "请使用至少 8 个字符",
"showPassword": "显示密码",
"hidePassword": "隐藏密码"
"hidePassword": "隐藏密码",
"success": "密码更新成功",
"fail": "更新密码失败"
},
"save": "保存",
"saving": "保存中..."

View File

@ -75,7 +75,7 @@ export function UpdateAvatarCard() {
<Avatar className="h-16 w-16 border">
<AvatarImage src={avatarUrl ?? ''} alt={user.name} />
<AvatarFallback>
<User2Icon className="h-8 w-8" />
<User2Icon className="h-8 w-8 text-muted-foreground" />
</AvatarFallback>
</Avatar>

View File

@ -1,5 +1,6 @@
'use client';
import { FormError } from '@/components/shared/form-error';
import { Button } from '@/components/ui/button';
import {
Card,
@ -30,12 +31,15 @@ import { z } from 'zod';
*
* NOTICE: we update username instead of name in user table
*
* TODO: by default, username is empty, how can we show the username in user-button?
*
* https://www.better-auth.com/docs/plugins/username
*/
export function UpdateNameCard() {
const t = useTranslations('Dashboard.sidebar.settings.items.account');
const [isSaving, setIsSaving] = useState(false);
const { data: session, refetch, error } = authClient.useSession();
const [error, setError] = useState<string | undefined>('');
const { data: session, refetch } = authClient.useSession();
// Create a schema for name validation
const formSchema = z.object({
@ -81,6 +85,7 @@ export function UpdateNameCard() {
onRequest: (ctx) => {
// console.log('update name, request:', ctx.url);
setIsSaving(true);
setError('');
},
onResponse: (ctx) => {
// console.log('update name, response:', ctx.response);
@ -91,10 +96,12 @@ export function UpdateNameCard() {
// console.log("update name, success:", ctx.data);
toast.success(t('name.success'));
refetch();
form.reset();
},
onError: (ctx) => {
// update name fail, display the error message
console.error('update name, error:', ctx.error);
setError(`${ctx.error.status}: ${ctx.error.message}`);
toast.error(t('name.fail'));
},
});
@ -126,6 +133,7 @@ export function UpdateNameCard() {
</FormItem>
)}
/>
<FormError message={error} />
</CardContent>
<CardFooter className="px-6 py-4 flex justify-between items-center bg-muted">
<p className="text-sm text-muted-foreground">

View File

@ -1,5 +1,6 @@
'use client';
import { FormError } from '@/components/shared/form-error';
import { Button } from '@/components/ui/button';
import {
Card,
@ -18,20 +19,29 @@ import {
FormMessage
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { useLocaleRouter } from '@/i18n/navigation';
import { authClient } from '@/lib/auth-client';
import { zodResolver } from '@hookform/resolvers/zod';
import { EyeIcon, EyeOffIcon } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast } from 'sonner';
import { z } from 'zod';
/**
* update user password
*
* https://www.better-auth.com/docs/authentication/email-password#update-password
*/
export function UpdatePasswordCard() {
const router = useLocaleRouter();
const t = useTranslations('Dashboard.sidebar.settings.items.account');
const [isSaving, setIsSaving] = useState(false);
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
const [showNewPassword, setShowNewPassword] = useState(false);
const { data: session, error } = authClient.useSession();
const [error, setError] = useState<string | undefined>('');
const { data: session } = authClient.useSession();
// Create a schema for password validation
const formSchema = z.object({
@ -58,17 +68,41 @@ export function UpdatePasswordCard() {
return null;
}
// TODO: if user account provider is not credential, we should not allow user to update password
// Handle form submission
const onSubmit = async (values: z.infer<typeof formSchema>) => {
setIsSaving(true);
// Here you would typically send the data to your server
// For now, we're just simulating the request
setTimeout(() => {
setIsSaving(false);
form.reset();
// In a real implementation, you would handle the response from your server
}, 1000);
const { data, error } = await authClient.changePassword(
{
currentPassword: values.currentPassword,
newPassword: values.newPassword,
revokeOtherSessions: true,
},
{
onRequest: (ctx) => {
// console.log('update password, request:', ctx.url);
setIsSaving(true);
setError('');
},
onResponse: (ctx) => {
// console.log('update password, response:', ctx.response);
setIsSaving(false);
},
onSuccess: (ctx) => {
// update password success, user information stored in ctx.data
// console.log("update password, success:", ctx.data);
toast.success(t('password.success'));
router.refresh();
form.reset();
},
onError: (ctx) => {
// update password fail, display the error message
// { "message": "Invalid password", "code": "INVALID_PASSWORD", "status": 400, "statusText": "BAD_REQUEST" }
console.error('update password, error:', ctx.error);
setError(`${ctx.error.status}: ${ctx.error.message}`);
toast.error(t('password.fail'));
},
});
};
return (
@ -154,6 +188,7 @@ export function UpdatePasswordCard() {
</FormItem>
)}
/>
<FormError message={error} />
</CardContent>
<CardFooter className="px-6 py-4 flex justify-between items-center bg-muted">
<p className="text-sm text-muted-foreground">

View File

@ -8,6 +8,8 @@ import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { admin, username } from 'better-auth/plugins';
/**
* TODO: init username when user signup
*
* https://www.better-auth.com/docs/reference/options
*/
export const auth = betterAuth({
@ -102,6 +104,12 @@ export const auth = betterAuth({
username({
minUsernameLength: 3,
maxUsernameLength: 30,
usernameValidator: (username) => {
if (username === "admin" || username === "administrator") {
return false;
}
return true;
}
}),
// https://www.better-auth.com/docs/plugins/admin
admin(),