fix: enhance localization messages and update user interface components
- Added new localization messages for logout failure and success notifications in English and Chinese. - Updated `SidebarUser` and `UserButton` components to display the username instead of the name for better clarity. - Improved `UpdateNameCard` to handle username updates with appropriate success and error messages using toast notifications. - Refactored `MainMobileMenu` to utilize the translation function directly for login and sign-up links, enhancing consistency across the application.
This commit is contained in:
parent
c497d71757
commit
c0c60f2f2f
@ -12,7 +12,8 @@
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"system": "System",
|
||||
"copy": "Copy"
|
||||
"copy": "Copy",
|
||||
"logoutFailed": "Failed to log out"
|
||||
},
|
||||
"Metadata": {
|
||||
"title": "MkSaaS - The Best AI SaaS Boilerplate",
|
||||
@ -304,7 +305,9 @@
|
||||
"placeholder": "Enter your name",
|
||||
"minLength": "Please use 3 characters at minimum",
|
||||
"maxLength": "Please use 30 characters at maximum",
|
||||
"hint": "Please use 3-30 characters for your name"
|
||||
"hint": "Please use 3-30 characters for your name",
|
||||
"success": "Name updated successfully",
|
||||
"fail": "Failed to update name"
|
||||
},
|
||||
"password": {
|
||||
"title": "Change Password",
|
||||
|
||||
@ -295,12 +295,14 @@
|
||||
"uploadAvatar": "上传头像"
|
||||
},
|
||||
"name": {
|
||||
"title": "姓名",
|
||||
"title": "名字",
|
||||
"description": "请输入您的名字",
|
||||
"placeholder": "输入您的名字",
|
||||
"placeholder": "名字",
|
||||
"minLength": "请至少使用 3 个字符",
|
||||
"maxLength": "请最多使用 30 个字符",
|
||||
"hint": "请使用 3-30 个字符"
|
||||
"hint": "请使用 3-30 个字符",
|
||||
"success": "名字更新成功",
|
||||
"fail": "更新名字失败"
|
||||
},
|
||||
"password": {
|
||||
"title": "修改密码",
|
||||
|
||||
@ -35,6 +35,7 @@ import { useTheme } from 'next-themes';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useTransition } from 'react';
|
||||
import { UserAvatar } from '../shared/user-avatar';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export function SidebarUser() {
|
||||
const { setTheme } = useTheme();
|
||||
@ -75,7 +76,7 @@ export function SidebarUser() {
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('sign out error:', error);
|
||||
// TODO: show error message
|
||||
toast.error(t('Common.logoutFailed'));
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -92,14 +93,14 @@ export function SidebarUser() {
|
||||
data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<UserAvatar
|
||||
name={user.name || undefined}
|
||||
name={user.username || undefined}
|
||||
image={user.image || undefined}
|
||||
className="size-8 border"
|
||||
/>
|
||||
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-semibold">
|
||||
{user.name}
|
||||
{user.username}
|
||||
</span>
|
||||
<span className="truncate text-xs">
|
||||
{user.email}
|
||||
@ -117,12 +118,12 @@ export function SidebarUser() {
|
||||
<DropdownMenuLabel className="p-0 font-normal">
|
||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<UserAvatar
|
||||
name={user.name || undefined}
|
||||
name={user.username || undefined}
|
||||
image={user.image || undefined}
|
||||
className="size-8 border"
|
||||
/>
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-semibold">{user.name}</span>
|
||||
<span className="truncate font-semibold">{user.username}</span>
|
||||
<span className="truncate text-xs">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -130,7 +130,6 @@ function MainMobileMenu({ userLoggedIn, onLinkClicked }: MainMobileMenuProps) {
|
||||
const t = useTranslations();
|
||||
const translator = createTranslator(t);
|
||||
const menuLinks = getMenuLinks(translator);
|
||||
const commonTranslations = useTranslations('Common');
|
||||
const localePathname = useLocalePathname();
|
||||
|
||||
return (
|
||||
@ -153,7 +152,7 @@ function MainMobileMenu({ userLoggedIn, onLinkClicked }: MainMobileMenuProps) {
|
||||
'w-full'
|
||||
)}
|
||||
>
|
||||
{commonTranslations('login')}
|
||||
{t('Common.login')}
|
||||
</LocaleLink>
|
||||
<LocaleLink
|
||||
href={Routes.Register}
|
||||
@ -166,7 +165,7 @@ function MainMobileMenu({ userLoggedIn, onLinkClicked }: MainMobileMenuProps) {
|
||||
)}
|
||||
onClick={onLinkClicked}
|
||||
>
|
||||
{commonTranslations('signUp')}
|
||||
{t('Common.signUp')}
|
||||
</LocaleLink>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -32,7 +32,6 @@ export function UserButton() {
|
||||
const t = useTranslations();
|
||||
const translator = createTranslator(t);
|
||||
const avatarLinks = getAvatarLinks(translator);
|
||||
const commonTranslations = useTranslations('Common');
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await authClient.signOut({
|
||||
@ -63,7 +62,7 @@ export function UserButton() {
|
||||
<Drawer open={open} onClose={closeDrawer}>
|
||||
<DrawerTrigger onClick={() => setOpen(true)}>
|
||||
<UserAvatar
|
||||
name={user?.name || undefined}
|
||||
name={user?.username || undefined}
|
||||
image={user?.image || undefined}
|
||||
className="size-8 border"
|
||||
/>
|
||||
@ -79,12 +78,14 @@ export function UserButton() {
|
||||
</DrawerHeader>
|
||||
<div className="flex items-center justify-start gap-4 p-2">
|
||||
<UserAvatar
|
||||
name={user?.name || undefined}
|
||||
name={user?.username || undefined}
|
||||
image={user?.image || undefined}
|
||||
className="size-8 border"
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
{user?.name && <p className="font-medium">{user.name}</p>}
|
||||
{user?.username && <p className="font-medium">
|
||||
{user.username}
|
||||
</p>}
|
||||
{user?.email && (
|
||||
<p className="w-[200px] truncate text-muted-foreground">
|
||||
{user?.email}
|
||||
@ -125,7 +126,7 @@ export function UserButton() {
|
||||
className="flex w-full items-center gap-3 px-2.5 py-2"
|
||||
>
|
||||
<LogOutIcon className="size-4" />
|
||||
<p className="text-sm">{commonTranslations('logout')}</p>
|
||||
<p className="text-sm">{t('Common.logout')}</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@ -140,7 +141,7 @@ export function UserButton() {
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger>
|
||||
<UserAvatar
|
||||
name={user?.name || undefined}
|
||||
name={user?.username || undefined}
|
||||
image={user?.image || undefined}
|
||||
className="size-8 border"
|
||||
/>
|
||||
@ -148,7 +149,9 @@ export function UserButton() {
|
||||
<DropdownMenuContent align="end">
|
||||
<div className="flex items-center justify-start gap-2 p-2">
|
||||
<div className="flex flex-col space-y-1 leading-none">
|
||||
{user?.name && <p className="font-medium">{user.name}</p>}
|
||||
{user?.username && <p className="font-medium">
|
||||
{user.username}
|
||||
</p>}
|
||||
{user?.email && (
|
||||
<p className="w-[200px] truncate text-sm text-muted-foreground">
|
||||
{user?.email}
|
||||
@ -186,7 +189,7 @@ export function UserButton() {
|
||||
>
|
||||
<div className="flex items-center space-x-2.5">
|
||||
<LogOutIcon className="size-4" />
|
||||
<p className="text-sm">{commonTranslations('logout')}</p>
|
||||
<p className="text-sm">{t('Common.logout')}</p>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
@ -20,14 +20,22 @@ import { Input } from '@/components/ui/input';
|
||||
import { authClient } from '@/lib/auth-client';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* update user name
|
||||
*
|
||||
* NOTICE: we update username instead of name in user table
|
||||
*
|
||||
* 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, error } = authClient.useSession();
|
||||
const { data: session, refetch, error } = authClient.useSession();
|
||||
|
||||
// Create a schema for name validation
|
||||
const formSchema = z.object({
|
||||
@ -41,10 +49,16 @@ export function UpdateNameCard() {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: session?.user?.name || '',
|
||||
name: session?.user?.username || '',
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (session?.user?.username) {
|
||||
form.setValue('name', session.user.username);
|
||||
}
|
||||
}, [session, form]);
|
||||
|
||||
// Check if user exists after all hooks are initialized
|
||||
const user = session?.user;
|
||||
if (!user) {
|
||||
@ -53,14 +67,37 @@ export function UpdateNameCard() {
|
||||
|
||||
// Handle form submission
|
||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||
setIsSaving(true);
|
||||
// Don't update if the name hasn't changed
|
||||
if (values.name === session?.user?.username) {
|
||||
console.log("No changes to save");
|
||||
return;
|
||||
}
|
||||
|
||||
// Here you would typically send the data to your server
|
||||
// For now, we're just simulating the request
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
// In a real implementation, you would handle the response from your server
|
||||
}, 1000);
|
||||
const { data, error } = await authClient.updateUser(
|
||||
{
|
||||
username: values.name,
|
||||
},
|
||||
{
|
||||
onRequest: (ctx) => {
|
||||
// console.log('update name, request:', ctx.url);
|
||||
setIsSaving(true);
|
||||
},
|
||||
onResponse: (ctx) => {
|
||||
// console.log('update name, response:', ctx.response);
|
||||
setIsSaving(false);
|
||||
},
|
||||
onSuccess: (ctx) => {
|
||||
// update name success, user information stored in ctx.data
|
||||
// console.log("update name, success:", ctx.data);
|
||||
toast.success(t('name.success'));
|
||||
refetch();
|
||||
},
|
||||
onError: (ctx) => {
|
||||
// update name fail, display the error message
|
||||
console.error('update name, error:', ctx.error);
|
||||
toast.error(t('name.fail'));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user