| 
                        123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 | 
                        - import PasswordInput from '@/components/password-input';
 - import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
 - import { Button } from '@/components/ui/button';
 - import {
 -   Form,
 -   FormControl,
 -   FormField,
 -   FormItem,
 -   FormLabel,
 -   FormMessage,
 - } from '@/components/ui/form';
 - import { Input } from '@/components/ui/input';
 - import {
 -   Select,
 -   SelectContent,
 -   SelectItem,
 -   SelectTrigger,
 -   SelectValue,
 - } from '@/components/ui/select';
 - import { useTranslate } from '@/hooks/common-hooks';
 - import { useFetchUserInfo, useSaveSetting } from '@/hooks/user-setting-hooks';
 - import { TimezoneList } from '@/pages/user-setting/constants';
 - import { rsaPsw } from '@/utils';
 - import { transformFile2Base64 } from '@/utils/file-util';
 - import { zodResolver } from '@hookform/resolvers/zod';
 - import { TFunction } from 'i18next';
 - import { Loader2Icon, Pencil, Upload } from 'lucide-react';
 - import { useEffect, useState } from 'react';
 - import { useForm } from 'react-hook-form';
 - import { z } from 'zod';
 - 
 - function defineSchema(t: TFunction<'translation', string>) {
 -   return z
 -     .object({
 -       userName: z
 -         .string()
 -         .min(1, {
 -           message: t('usernameMessage'),
 -         })
 -         .trim(),
 -       avatarUrl: z.string().trim(),
 -       timeZone: z
 -         .string()
 -         .trim()
 -         .min(1, {
 -           message: t('timezonePlaceholder'),
 -         }),
 -       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.',
 -           },
 -         ),
 -       currPasswd: z
 -         .string()
 -         .trim()
 -         .min(1, {
 -           message: t('currentPasswordMessage'),
 -         }),
 -       newPasswd: z
 -         .string()
 -         .trim()
 -         .min(8, {
 -           message: t('confirmPasswordMessage'),
 -         }),
 -       confirmPasswd: z
 -         .string()
 -         .trim()
 -         .min(8, {
 -           message: t('newPasswordDescription'),
 -         }),
 -     })
 -     .refine((data) => data.newPasswd === data.confirmPasswd, {
 -       message: t('confirmPasswordNonMatchMessage'),
 -       path: ['confirmPasswd'],
 -     });
 - }
 - 
 - export default function Profile() {
 -   const [avatarFile, setAvatarFile] = useState<File | null>(null);
 -   const [avatarBase64Str, setAvatarBase64Str] = useState(''); // Avatar Image base64
 -   const { data: userInfo } = useFetchUserInfo();
 -   const { saveSetting, loading: submitLoading } = useSaveSetting();
 - 
 -   const { t } = useTranslate('setting');
 -   const FormSchema = defineSchema(t);
 - 
 -   const form = useForm<z.infer<typeof FormSchema>>({
 -     resolver: zodResolver(FormSchema),
 -     defaultValues: {
 -       userName: '',
 -       avatarUrl: '',
 -       timeZone: '',
 -       email: '',
 -       currPasswd: '',
 -       newPasswd: '',
 -       confirmPasswd: '',
 -     },
 -   });
 - 
 -   useEffect(() => {
 -     // init user info when mounted
 -     form.setValue('email', userInfo?.email); // email
 -     form.setValue('userName', userInfo?.nickname); // nickname
 -     form.setValue('timeZone', userInfo?.timezone); // time zone
 -     form.setValue('currPasswd', ''); // current password
 -     setAvatarBase64Str(userInfo?.avatar ?? '');
 -   }, [userInfo]);
 - 
 -   useEffect(() => {
 -     if (avatarFile) {
 -       // make use of img compression transformFile2Base64
 -       (async () => {
 -         setAvatarBase64Str(await transformFile2Base64(avatarFile));
 -       })();
 -     }
 -   }, [avatarFile]);
 - 
 -   function onSubmit(data: z.infer<typeof FormSchema>) {
 -     // toast('You submitted the following values', {
 -     //   description: (
 -     //     <pre className="mt-2 w-[320px] rounded-md bg-neutral-950 p-4">
 -     //       <code className="text-white">{JSON.stringify(data, null, 2)}</code>
 -     //     </pre>
 -     //   ),
 -     // });
 -     // console.log('data=', data);
 -     // final submit form
 -     saveSetting({
 -       nickname: data.userName,
 -       password: rsaPsw(data.currPasswd) as string,
 -       new_password: rsaPsw(data.newPasswd) as string,
 -       avatar: avatarBase64Str,
 -       timezone: data.timeZone,
 -     });
 -   }
 - 
 -   return (
 -     <section className="p-8">
 -       <h1 className="text-3xl font-bold">{t('profile')}</h1>
 -       <div className="text-sm text-muted-foreground mb-6">
 -         {t('profileDescription')}
 -       </div>
 -       <div>
 -         <Form {...form}>
 -           <form
 -             onSubmit={form.handleSubmit(onSubmit)}
 -             className="block space-y-6"
 -           >
 -             <FormField
 -               control={form.control}
 -               name="userName"
 -               render={({ field }) => (
 -                 <FormItem className=" items-center space-y-0 ">
 -                   <div className="flex w-[600px]">
 -                     <FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
 -                       <span className="text-red-600">*</span>
 -                       {t('username')}
 -                     </FormLabel>
 -                     <FormControl className="w-3/4">
 -                       <Input
 -                         placeholder=""
 -                         {...field}
 -                         className="bg-colors-background-inverse-weak"
 -                       />
 -                     </FormControl>
 -                   </div>
 -                   <div className="flex w-[600px] pt-1">
 -                     <div className="w-1/4"></div>
 -                     <FormMessage />
 -                   </div>
 -                 </FormItem>
 -               )}
 -             />
 -             <FormField
 -               control={form.control}
 -               name="avatarUrl"
 -               render={({ field }) => (
 -                 <FormItem className="flex items-center space-y-0">
 -                   <div className="flex w-[600px]">
 -                     <FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
 -                       Avatar
 -                     </FormLabel>
 -                     <FormControl className="w-3/4">
 -                       <>
 -                         <div className="relative group">
 -                           {!avatarBase64Str ? (
 -                             <div className="w-[64px] h-[64px] grid place-content-center">
 -                               <div className="flex flex-col items-center">
 -                                 <Upload />
 -                                 <p>Upload</p>
 -                               </div>
 -                             </div>
 -                           ) : (
 -                             <div className="w-[64px] h-[64px] relative grid place-content-center">
 -                               <Avatar className="w-[64px] h-[64px]">
 -                                 <AvatarImage
 -                                   className=" block"
 -                                   src={avatarBase64Str}
 -                                   alt=""
 -                                 />
 -                                 <AvatarFallback></AvatarFallback>
 -                               </Avatar>
 -                               <div className="absolute inset-0 bg-[#000]/20 group-hover:bg-[#000]/60">
 -                                 <Pencil
 -                                   size={20}
 -                                   className="absolute right-2 bottom-0 opacity-50 hidden group-hover:block"
 -                                 />
 -                               </div>
 -                             </div>
 -                           )}
 -                           <Input
 -                             placeholder=""
 -                             {...field}
 -                             type="file"
 -                             title=""
 -                             accept="image/*"
 -                             className="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer"
 -                             onChange={(ev) => {
 -                               const file = ev.target?.files?.[0];
 -                               if (
 -                                 /\.(jpg|jpeg|png|webp|bmp)$/i.test(
 -                                   file?.name ?? '',
 -                                 )
 -                               ) {
 -                                 setAvatarFile(file!);
 -                               }
 -                               ev.target.value = '';
 -                             }}
 -                           />
 -                         </div>
 -                       </>
 -                     </FormControl>
 -                   </div>
 -                   <div className="flex w-[600px] pt-1">
 -                     <div className="w-1/4"></div>
 -                     <FormMessage />
 -                   </div>
 -                 </FormItem>
 -               )}
 -             />
 -             <FormField
 -               control={form.control}
 -               name="timeZone"
 -               render={({ field }) => (
 -                 <FormItem className="items-center space-y-0">
 -                   <div className="flex w-[600px]">
 -                     <FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
 -                       <span className="text-red-600">*</span>
 -                       {t('timezone')}
 -                     </FormLabel>
 -                     <Select onValueChange={field.onChange} value={field.value}>
 -                       <FormControl className="w-3/4">
 -                         <SelectTrigger>
 -                           <SelectValue placeholder="Select a timeZone" />
 -                         </SelectTrigger>
 -                       </FormControl>
 -                       <SelectContent>
 -                         {TimezoneList.map((timeStr) => (
 -                           <SelectItem key={timeStr} value={timeStr}>
 -                             {timeStr}
 -                           </SelectItem>
 -                         ))}
 -                       </SelectContent>
 -                     </Select>
 -                   </div>
 -                   <div className="flex w-[600px] pt-1">
 -                     <div className="w-1/4"></div>
 -                     <FormMessage />
 -                   </div>
 -                 </FormItem>
 -               )}
 -             />
 -             <FormField
 -               control={form.control}
 -               name="email"
 -               render={({ field }) => (
 -                 <div>
 -                   <FormItem className="items-center space-y-0">
 -                     <div className="flex w-[600px]">
 -                       <FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-1/4">
 -                         {t('email')}
 -                       </FormLabel>
 -                       <FormControl className="w-3/4">
 -                         <Input
 -                           placeholder="Alex@gmail.com"
 -                           disabled
 -                           {...field}
 -                         />
 -                       </FormControl>
 -                     </div>
 -                     <div className="flex w-[600px] pt-1">
 -                       <div className="w-1/4"></div>
 -                       <FormMessage />
 -                     </div>
 -                   </FormItem>
 -                   <div className="flex w-[600px] pt-1">
 -                     <p className="w-1/4"> </p>
 -                     <p className="text-sm text-muted-foreground whitespace-nowrap w-3/4">
 -                       {t('emailDescription')}
 -                     </p>
 -                   </div>
 -                 </div>
 -               )}
 -             />
 -             <div className="h-[10px]"></div>
 -             <div className="pb-6">
 -               <h1 className="text-3xl font-bold">{t('password')}</h1>
 -               <div className="text-sm text-muted-foreground">
 -                 {t('passwordDescription')}
 -               </div>
 -             </div>
 -             <div className="h-0 overflow-hidden absolute">
 -               <input type="password" className=" w-0 height-0 opacity-0" />
 -             </div>
 -             <FormField
 -               control={form.control}
 -               name="currPasswd"
 -               render={({ field }) => (
 -                 <FormItem className=" items-center space-y-0">
 -                   <div className="flex w-[600px]">
 -                     <FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
 -                       <span className="text-red-600">*</span>
 -                       {t('currentPassword')}
 -                     </FormLabel>
 -                     <FormControl className="w-3/5">
 -                       <PasswordInput {...field} />
 -                     </FormControl>
 -                   </div>
 -                   <div className="flex w-[600px] pt-1">
 -                     <div className="min-w-[170px] max-w-[170px]"></div>
 -                     <FormMessage />
 -                   </div>
 -                 </FormItem>
 -               )}
 -             />
 -             <FormField
 -               control={form.control}
 -               name="newPasswd"
 -               render={({ field }) => (
 -                 <FormItem className=" items-center space-y-0">
 -                   <div className="flex w-[600px]">
 -                     <FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
 -                       <span className="text-red-600">*</span>
 -                       {t('newPassword')}
 -                     </FormLabel>
 -                     <FormControl className="w-3/5">
 -                       <PasswordInput {...field} />
 -                     </FormControl>
 -                   </div>
 -                   <div className="flex w-[600px] pt-1">
 -                     <div className="min-w-[170px] max-w-[170px]"></div>
 -                     <FormMessage />
 -                   </div>
 -                 </FormItem>
 -               )}
 -             />
 -             <FormField
 -               control={form.control}
 -               name="confirmPasswd"
 -               render={({ field }) => (
 -                 <FormItem className=" items-center space-y-0">
 -                   <div className="flex w-[600px]">
 -                     <FormLabel className="text-sm text-muted-foreground whitespace-nowrap w-2/5">
 -                       <span className="text-red-600">*</span>
 -                       {t('confirmPassword')}
 -                     </FormLabel>
 -                     <FormControl className="w-3/5">
 -                       <PasswordInput
 -                         {...field}
 -                         onBlur={() => {
 -                           form.trigger('confirmPasswd');
 -                         }}
 -                         onChange={(ev) => {
 -                           form.setValue(
 -                             'confirmPasswd',
 -                             ev.target.value.trim(),
 -                           );
 -                         }}
 -                       />
 -                     </FormControl>
 -                   </div>
 -                   <div className="flex w-[600px] pt-1">
 -                     <div className="min-w-[170px] max-w-[170px]"> </div>
 -                     <FormMessage />
 -                   </div>
 -                 </FormItem>
 -               )}
 -             />
 -             <div className="w-[600px] text-right space-x-4">
 -               <Button variant="secondary">{t('cancel')}</Button>
 -               <Button type="submit" disabled={submitLoading}>
 -                 {submitLoading && <Loader2Icon className="animate-spin" />}
 -                 {t('save', { keyPrefix: 'common' })}
 -               </Button>
 -             </div>
 -           </form>
 -         </Form>
 -       </div>
 -     </section>
 -   );
 - }
 
 
  |