mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Optimize the style and logic of the profile (#8639)
### What problem does this PR solve? Optimize the style and logic of the profile [#3221 ](https://github.com/infiniflow/ragflow/issues/3221) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -552,6 +552,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
|||||||
setting: {
|
setting: {
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
avatar: 'Avatar',
|
avatar: 'Avatar',
|
||||||
|
avatarTip: 'This will be displayed on your profile.',
|
||||||
profileDescription: 'Update your photo and personal details here.',
|
profileDescription: 'Update your photo and personal details here.',
|
||||||
maxTokens: 'Max Tokens',
|
maxTokens: 'Max Tokens',
|
||||||
maxTokensMessage: 'Max Tokens is required',
|
maxTokensMessage: 'Max Tokens is required',
|
||||||
@ -584,6 +585,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
|||||||
currentPassword: 'Current password',
|
currentPassword: 'Current password',
|
||||||
currentPasswordMessage: 'Please input your password!',
|
currentPasswordMessage: 'Please input your password!',
|
||||||
newPassword: 'New password',
|
newPassword: 'New password',
|
||||||
|
changePassword: 'Change Password',
|
||||||
newPasswordMessage: 'Please input your password!',
|
newPasswordMessage: 'Please input your password!',
|
||||||
newPasswordDescription:
|
newPasswordDescription:
|
||||||
'Your new password must be more than 8 characters.',
|
'Your new password must be more than 8 characters.',
|
||||||
|
|||||||
@ -535,6 +535,7 @@ export default {
|
|||||||
setting: {
|
setting: {
|
||||||
profile: '概述',
|
profile: '概述',
|
||||||
avatar: '头像',
|
avatar: '头像',
|
||||||
|
avatarTip: '這會在你的個人主頁展示',
|
||||||
profileDescription: '在此更新您的照片和個人詳細信息。',
|
profileDescription: '在此更新您的照片和個人詳細信息。',
|
||||||
maxTokens: '最大token數',
|
maxTokens: '最大token數',
|
||||||
maxTokensMessage: '最大token數是必填項',
|
maxTokensMessage: '最大token數是必填項',
|
||||||
@ -567,6 +568,7 @@ export default {
|
|||||||
currentPassword: '當前密碼',
|
currentPassword: '當前密碼',
|
||||||
currentPasswordMessage: '請輸入當前密碼',
|
currentPasswordMessage: '請輸入當前密碼',
|
||||||
newPassword: '新密碼',
|
newPassword: '新密碼',
|
||||||
|
changePassword: '修改密碼',
|
||||||
newPasswordMessage: '請輸入新密碼',
|
newPasswordMessage: '請輸入新密碼',
|
||||||
newPasswordDescription: '您的新密碼必須超過 8 個字符。',
|
newPasswordDescription: '您的新密碼必須超過 8 個字符。',
|
||||||
confirmPassword: '確認新密碼',
|
confirmPassword: '確認新密碼',
|
||||||
|
|||||||
@ -556,6 +556,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
setting: {
|
setting: {
|
||||||
profile: '概要',
|
profile: '概要',
|
||||||
avatar: '头像',
|
avatar: '头像',
|
||||||
|
avatarTip: '这会在你的个人主页展示',
|
||||||
profileDescription: '在此更新您的照片和个人详细信息。',
|
profileDescription: '在此更新您的照片和个人详细信息。',
|
||||||
maxTokens: '最大token数',
|
maxTokens: '最大token数',
|
||||||
maxTokensMessage: '最大token数是必填项',
|
maxTokensMessage: '最大token数是必填项',
|
||||||
@ -588,6 +589,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
currentPassword: '当前密码',
|
currentPassword: '当前密码',
|
||||||
currentPasswordMessage: '请输入当前密码',
|
currentPasswordMessage: '请输入当前密码',
|
||||||
newPassword: '新密码',
|
newPassword: '新密码',
|
||||||
|
changePassword: '修改密码',
|
||||||
newPasswordMessage: '请输入新密码',
|
newPasswordMessage: '请输入新密码',
|
||||||
newPasswordDescription: '您的新密码必须超过 8 个字符。',
|
newPasswordDescription: '您的新密码必须超过 8 个字符。',
|
||||||
confirmPassword: '确认新密码',
|
confirmPassword: '确认新密码',
|
||||||
|
|||||||
@ -28,52 +28,49 @@ import { Loader2Icon, Pencil, Upload } from 'lucide-react';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
function defineSchema(
|
||||||
function defineSchema(t: TFunction<'translation', string>) {
|
t: TFunction<'translation', string>,
|
||||||
return z
|
showPasswordForm = false,
|
||||||
.object({
|
) {
|
||||||
|
const baseSchema = z.object({
|
||||||
userName: z
|
userName: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, {
|
.min(1, { message: t('usernameMessage') })
|
||||||
message: t('usernameMessage'),
|
|
||||||
})
|
|
||||||
.trim(),
|
.trim(),
|
||||||
avatarUrl: z.string().trim(),
|
avatarUrl: z.string().trim(),
|
||||||
timeZone: z
|
timeZone: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.min(1, {
|
.min(1, { message: t('timezonePlaceholder') }),
|
||||||
message: t('timezonePlaceholder'),
|
|
||||||
}),
|
|
||||||
email: z
|
email: z
|
||||||
|
.string({ required_error: 'Please select an email to display.' })
|
||||||
|
.trim()
|
||||||
|
.regex(/^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, {
|
||||||
|
message: 'Enter a valid email address.',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (showPasswordForm) {
|
||||||
|
return baseSchema
|
||||||
|
.extend({
|
||||||
|
currPasswd: z
|
||||||
.string({
|
.string({
|
||||||
required_error: 'Please select an email to display.',
|
required_error: t('currentPasswordMessage'),
|
||||||
})
|
})
|
||||||
.trim()
|
.trim()
|
||||||
.regex(
|
.min(1, { message: t('currentPasswordMessage') }),
|
||||||
/^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
|
|
||||||
{
|
|
||||||
message: 'Enter a valid email address.',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
currPasswd: z
|
|
||||||
.string()
|
|
||||||
.trim()
|
|
||||||
.min(1, {
|
|
||||||
message: t('currentPasswordMessage'),
|
|
||||||
}),
|
|
||||||
newPasswd: z
|
newPasswd: z
|
||||||
.string()
|
.string({
|
||||||
|
required_error: t('confirmPasswordMessage'),
|
||||||
|
})
|
||||||
.trim()
|
.trim()
|
||||||
.min(8, {
|
.min(8, { message: t('confirmPasswordMessage') }),
|
||||||
message: t('confirmPasswordMessage'),
|
|
||||||
}),
|
|
||||||
confirmPasswd: z
|
confirmPasswd: z
|
||||||
.string()
|
.string({
|
||||||
|
required_error: t('newPasswordDescription'),
|
||||||
|
})
|
||||||
.trim()
|
.trim()
|
||||||
.min(8, {
|
.min(8, { message: t('newPasswordDescription') }),
|
||||||
message: t('newPasswordDescription'),
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.refine((data) => data.newPasswd === data.confirmPasswd, {
|
.refine((data) => data.newPasswd === data.confirmPasswd, {
|
||||||
message: t('confirmPasswordNonMatchMessage'),
|
message: t('confirmPasswordNonMatchMessage'),
|
||||||
@ -81,15 +78,21 @@ function defineSchema(t: TFunction<'translation', string>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return baseSchema;
|
||||||
|
}
|
||||||
export default function Profile() {
|
export default function Profile() {
|
||||||
const [avatarFile, setAvatarFile] = useState<File | null>(null);
|
const [avatarFile, setAvatarFile] = useState<File | null>(null);
|
||||||
const [avatarBase64Str, setAvatarBase64Str] = useState(''); // Avatar Image base64
|
const [avatarBase64Str, setAvatarBase64Str] = useState(''); // Avatar Image base64
|
||||||
const { data: userInfo } = useFetchUserInfo();
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
const { saveSetting, loading: submitLoading } = useSaveSetting();
|
const {
|
||||||
|
saveSetting,
|
||||||
|
loading: submitLoading,
|
||||||
|
data: saveUserData,
|
||||||
|
} = useSaveSetting();
|
||||||
|
|
||||||
const { t } = useTranslate('setting');
|
const { t } = useTranslate('setting');
|
||||||
const FormSchema = defineSchema(t);
|
const [showPasswordForm, setShowPasswordForm] = useState(false);
|
||||||
|
const FormSchema = defineSchema(t, showPasswordForm);
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(FormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@ -97,10 +100,11 @@ export default function Profile() {
|
|||||||
avatarUrl: '',
|
avatarUrl: '',
|
||||||
timeZone: '',
|
timeZone: '',
|
||||||
email: '',
|
email: '',
|
||||||
currPasswd: '',
|
// currPasswd: '',
|
||||||
newPasswd: '',
|
// newPasswd: '',
|
||||||
confirmPasswd: '',
|
// confirmPasswd: '',
|
||||||
},
|
},
|
||||||
|
shouldUnregister: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -108,10 +112,20 @@ export default function Profile() {
|
|||||||
form.setValue('email', userInfo?.email); // email
|
form.setValue('email', userInfo?.email); // email
|
||||||
form.setValue('userName', userInfo?.nickname); // nickname
|
form.setValue('userName', userInfo?.nickname); // nickname
|
||||||
form.setValue('timeZone', userInfo?.timezone); // time zone
|
form.setValue('timeZone', userInfo?.timezone); // time zone
|
||||||
form.setValue('currPasswd', ''); // current password
|
// form.setValue('currPasswd', ''); // current password
|
||||||
setAvatarBase64Str(userInfo?.avatar ?? '');
|
setAvatarBase64Str(userInfo?.avatar ?? '');
|
||||||
}, [userInfo]);
|
}, [userInfo]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (saveUserData === 0) {
|
||||||
|
setShowPasswordForm(false);
|
||||||
|
form.resetField('currPasswd');
|
||||||
|
form.resetField('newPasswd');
|
||||||
|
form.resetField('confirmPasswd');
|
||||||
|
}
|
||||||
|
console.log('saveUserData', saveUserData);
|
||||||
|
}, [saveUserData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (avatarFile) {
|
if (avatarFile) {
|
||||||
// make use of img compression transformFile2Base64
|
// make use of img compression transformFile2Base64
|
||||||
@ -122,24 +136,34 @@ export default function Profile() {
|
|||||||
}, [avatarFile]);
|
}, [avatarFile]);
|
||||||
|
|
||||||
function onSubmit(data: z.infer<typeof FormSchema>) {
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
// toast('You submitted the following values', {
|
const payload: Partial<{
|
||||||
// description: (
|
nickname: string;
|
||||||
// <pre className="mt-2 w-[320px] rounded-md bg-neutral-950 p-4">
|
password: string;
|
||||||
// <code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
new_password: string;
|
||||||
// </pre>
|
avatar: string;
|
||||||
// ),
|
timezone: string;
|
||||||
// });
|
}> = {
|
||||||
// console.log('data=', data);
|
|
||||||
// final submit form
|
|
||||||
saveSetting({
|
|
||||||
nickname: data.userName,
|
nickname: data.userName,
|
||||||
password: rsaPsw(data.currPasswd) as string,
|
|
||||||
new_password: rsaPsw(data.newPasswd) as string,
|
|
||||||
avatar: avatarBase64Str,
|
avatar: avatarBase64Str,
|
||||||
timezone: data.timeZone,
|
timezone: data.timeZone,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (showPasswordForm && 'currPasswd' in data && 'newPasswd' in data) {
|
||||||
|
payload.password = rsaPsw(data.currPasswd!) as string;
|
||||||
|
payload.new_password = rsaPsw(data.newPasswd!) as string;
|
||||||
|
}
|
||||||
|
saveSetting(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showPasswordForm) {
|
||||||
|
form.register('currPasswd');
|
||||||
|
form.register('newPasswd');
|
||||||
|
form.register('confirmPasswd');
|
||||||
|
} else {
|
||||||
|
form.unregister(['currPasswd', 'newPasswd', 'confirmPasswd']);
|
||||||
|
}
|
||||||
|
}, [showPasswordForm]);
|
||||||
return (
|
return (
|
||||||
<section className="p-8">
|
<section className="p-8">
|
||||||
<h1 className="text-3xl font-bold">{t('profile')}</h1>
|
<h1 className="text-3xl font-bold">{t('profile')}</h1>
|
||||||
@ -152,12 +176,13 @@ export default function Profile() {
|
|||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="block space-y-6"
|
className="block space-y-6"
|
||||||
>
|
>
|
||||||
|
{/* Username Field */}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="userName"
|
name="userName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className=" items-center space-y-0 ">
|
<FormItem className=" items-center space-y-0 ">
|
||||||
<div className="flex w-[600px]">
|
<div className="flex w-[640px]">
|
||||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||||
<span className="text-red-600">*</span>
|
<span className="text-red-600">*</span>
|
||||||
{t('username')}
|
{t('username')}
|
||||||
@ -170,24 +195,26 @@ export default function Profile() {
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<div className="w-1/4"></div>
|
<div className="w-1/4"></div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Avatar Field */}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="avatarUrl"
|
name="avatarUrl"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-y-0">
|
<FormItem className="flex items-center space-y-0">
|
||||||
<div className="flex w-[600px]">
|
<div className="flex w-[640px]">
|
||||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||||
Avatar
|
Avatar
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl className="w-3/4">
|
<FormControl className="w-3/4">
|
||||||
<>
|
<div className="flex justify-start items-end space-x-2">
|
||||||
<div className="relative group">
|
<div className="relative group">
|
||||||
{!avatarBase64Str ? (
|
{!avatarBase64Str ? (
|
||||||
<div className="w-[64px] h-[64px] grid place-content-center">
|
<div className="w-[64px] h-[64px] grid place-content-center">
|
||||||
@ -198,18 +225,18 @@ export default function Profile() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-[64px] h-[64px] relative grid place-content-center">
|
<div className="w-[64px] h-[64px] relative grid place-content-center">
|
||||||
<Avatar className="w-[64px] h-[64px]">
|
<Avatar className="w-[64px] h-[64px] rounded-md">
|
||||||
<AvatarImage
|
<AvatarImage
|
||||||
className="block"
|
className="block"
|
||||||
src={avatarBase64Str}
|
src={avatarBase64Str}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<AvatarFallback></AvatarFallback>
|
<AvatarFallback className="rounded-md"></AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="absolute inset-0 bg-[#000]/20 group-hover:bg-[#000]/60">
|
<div className="absolute inset-0 bg-[#000]/20 group-hover:bg-[#000]/60">
|
||||||
<Pencil
|
<Pencil
|
||||||
size={20}
|
size={16}
|
||||||
className="absolute right-2 bottom-0 opacity-50 hidden group-hover:block"
|
className="absolute right-1 bottom-1 opacity-50 hidden group-hover:block"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -234,22 +261,27 @@ export default function Profile() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
<div className="margin-1 text-muted-foreground">
|
||||||
|
{t('avatarTip')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<div className="w-1/4"></div>
|
<div className="w-1/4"></div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Time Zone Field */}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="timeZone"
|
name="timeZone"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="items-center space-y-0">
|
<FormItem className="items-center space-y-0">
|
||||||
<div className="flex w-[600px]">
|
<div className="flex w-[640px]">
|
||||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||||
<span className="text-red-600">*</span>
|
<span className="text-red-600">*</span>
|
||||||
{t('timezone')}
|
{t('timezone')}
|
||||||
@ -269,37 +301,35 @@ export default function Profile() {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<div className="w-1/4"></div>
|
<div className="w-1/4"></div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Email Address Field */}
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="email"
|
name="email"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div>
|
<div>
|
||||||
<FormItem className="items-center space-y-0">
|
<FormItem className="items-center space-y-0">
|
||||||
<div className="flex w-[600px]">
|
<div className="flex w-[640px]">
|
||||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
|
||||||
{t('email')}
|
{t('email')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl className="w-3/4">
|
<FormControl className="w-3/4">
|
||||||
<Input
|
<>{field.value}</>
|
||||||
placeholder="Alex@gmail.com"
|
|
||||||
disabled
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<div className="w-1/4"></div>
|
<div className="w-1/4"></div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<p className="w-1/4"> </p>
|
<p className="w-1/4"> </p>
|
||||||
<p className="text-sm text-muted-foreground whitespace-nowrap w-3/4">
|
<p className="text-sm text-muted-foreground whitespace-nowrap w-3/4">
|
||||||
{t('emailDescription')}
|
{t('emailDescription')}
|
||||||
@ -308,22 +338,34 @@ export default function Profile() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="h-[10px]"></div>
|
|
||||||
|
{/* Password Section */}
|
||||||
<div className="pb-6">
|
<div className="pb-6">
|
||||||
|
<div className="flex items-center justify-start">
|
||||||
<h1 className="text-3xl font-bold">{t('password')}</h1>
|
<h1 className="text-3xl font-bold">{t('password')}</h1>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
className="bg-transparent hover:bg-transparent border text-muted-foreground hover:text-white ml-10"
|
||||||
|
onClick={() => {
|
||||||
|
setShowPasswordForm(!showPasswordForm);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('changePassword')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
{t('passwordDescription')}
|
{t('passwordDescription')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-0 overflow-hidden absolute">
|
{/* Password Form */}
|
||||||
<input type="password" className=" w-0 height-0 opacity-0" />
|
{showPasswordForm && (
|
||||||
</div>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="currPasswd"
|
name="currPasswd"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="items-center space-y-0">
|
<FormItem className="items-center space-y-0">
|
||||||
<div className="flex w-[600px]">
|
<div className="flex w-[640px]">
|
||||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
|
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
|
||||||
<span className="text-red-600">*</span>
|
<span className="text-red-600">*</span>
|
||||||
{t('currentPassword')}
|
{t('currentPassword')}
|
||||||
@ -332,7 +374,7 @@ export default function Profile() {
|
|||||||
<PasswordInput {...field} />
|
<PasswordInput {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<div className="min-w-[170px] max-w-[170px]"></div>
|
<div className="min-w-[170px] max-w-[170px]"></div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
@ -344,7 +386,7 @@ export default function Profile() {
|
|||||||
name="newPasswd"
|
name="newPasswd"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className=" items-center space-y-0">
|
<FormItem className=" items-center space-y-0">
|
||||||
<div className="flex w-[600px]">
|
<div className="flex w-[640px]">
|
||||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
|
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
|
||||||
<span className="text-red-600">*</span>
|
<span className="text-red-600">*</span>
|
||||||
{t('newPassword')}
|
{t('newPassword')}
|
||||||
@ -353,7 +395,7 @@ export default function Profile() {
|
|||||||
<PasswordInput {...field} />
|
<PasswordInput {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<div className="min-w-[170px] max-w-[170px]"></div>
|
<div className="min-w-[170px] max-w-[170px]"></div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
@ -365,7 +407,7 @@ export default function Profile() {
|
|||||||
name="confirmPasswd"
|
name="confirmPasswd"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className=" items-center space-y-0">
|
<FormItem className=" items-center space-y-0">
|
||||||
<div className="flex w-[600px]">
|
<div className="flex w-[640px]">
|
||||||
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
|
<FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
|
||||||
<span className="text-red-600">*</span>
|
<span className="text-red-600">*</span>
|
||||||
{t('confirmPassword')}
|
{t('confirmPassword')}
|
||||||
@ -385,15 +427,21 @@ export default function Profile() {
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[600px] pt-1">
|
<div className="flex w-[640px] pt-1">
|
||||||
<div className="min-w-[170px] max-w-[170px]"> </div>
|
<div className="min-w-[170px] max-w-[170px]">
|
||||||
|
|
||||||
|
</div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="w-[600px] text-right space-x-4">
|
</>
|
||||||
<Button variant="secondary">{t('cancel')}</Button>
|
)}
|
||||||
|
<div className="w-[640px] text-right space-x-4">
|
||||||
|
<Button type="reset" variant="secondary">
|
||||||
|
{t('cancel')}
|
||||||
|
</Button>
|
||||||
<Button type="submit" disabled={submitLoading}>
|
<Button type="submit" disabled={submitLoading}>
|
||||||
{submitLoading && <Loader2Icon className="animate-spin" />}
|
{submitLoading && <Loader2Icon className="animate-spin" />}
|
||||||
{t('save', { keyPrefix: 'common' })}
|
{t('save', { keyPrefix: 'common' })}
|
||||||
|
|||||||
Reference in New Issue
Block a user