diff --git a/client/cms/src/components/profile/edit-profile-dialog.tsx b/client/cms/src/components/profile/edit-profile-dialog.tsx index 3917bf4..e9efd34 100644 --- a/client/cms/src/components/profile/edit-profile-dialog.tsx +++ b/client/cms/src/components/profile/edit-profile-dialog.tsx @@ -19,18 +19,25 @@ import { import { Input, } from '@/components/ui/input'; +import { useUpdateUser } from '@/hooks/data/useUpdateUser'; +import { useUserInfo } from '@/hooks/data/useUserInfo'; const formSchema = z.object({ - email: z.string(), + username: z.string().min(5), nickname: z.string().min(1), subtitle: z.string().min(1), + avatar: z.url().min(1), }); export function EditProfileDialog() { + const { data: user } = useUserInfo(); + const { mutateAsync } = useUpdateUser(); + const form = useForm({ defaultValues: { - email: '', - nickname: '', - subtitle: '', + avatar: user.avatar, + username: user.username, + nickname: user.nickname, + subtitle: user.subtitle, }, validators: { onBlur: formSchema, @@ -39,13 +46,12 @@ export function EditProfileDialog() { value, }) => { try { - toast( - {JSON.stringify(value, null, 2)}, - ); + await mutateAsync(value); + toast.success('个人资料更新成功'); } catch (error) { console.error('Form submission error', error); - toast.error('Failed to submit the form. Please try again.'); + toast.error('更新个人资料失败,请重试'); } }, }); @@ -53,60 +59,94 @@ export function EditProfileDialog() { return ( - + -
{ - e.preventDefault(); - e.stopPropagation(); - void form.handleSubmit(); - }} - > - + + { + e.preventDefault(); + e.stopPropagation(); + void form.handleSubmit(); + }} + className="grid gap-4" + > 编辑个人资料 - - Email - form.setFieldValue('email', e.target.value)} - /> - - - - 昵称 - form.setFieldValue('nickname', e.target.value)} - /> - - - - 副标题 - form.setFieldValue('subtitle', e.target.value)} - /> - - + + {field => ( + + 用户名 + field.handleChange(e.target.value)} + /> + + + )} + + + {field => ( + + 昵称 + field.handleChange(e.target.value)} + /> + + + )} + + + {field => ( + + 副标题 + field.handleChange(e.target.value)} + /> + + + )} + + + {field => ( + + 头像链接 + field.handleChange(e.target.value)} + /> + + + )} + - + + + - - + +
); } diff --git a/client/cms/src/components/profile/edit-profile-form.tsx b/client/cms/src/components/profile/edit-profile-form.tsx deleted file mode 100644 index 771dcae..0000000 --- a/client/cms/src/components/profile/edit-profile-form.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { - useForm, -} from '@tanstack/react-form'; -import { - toast, -} from 'sonner'; -import { - z, -} from 'zod'; -import { - Button, -} from '@/components/ui/button'; -import { - Field, - FieldError, - FieldLabel, -} from '@/components/ui/field'; -import { - Input, -} from '@/components/ui/input'; - -const formSchema = z.object({ - email: z.string(), - nickname: z.string().min(1), - subtitle: z.string().min(1), -}); - -export default function EditProfileForm() { - const form = useForm({ - defaultValues: { - email: '', - nickname: '', - subtitle: '', - }, - validators: { - onBlur: formSchema, - }, - onSubmit: async ({ - value, - }) => { - try { - toast( - {JSON.stringify(value, null, 2)}, - ); - } - catch (error) { - console.error('Form submission error', error); - toast.error('Failed to submit the form. Please try again.'); - } - }, - }); - - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - void form.handleSubmit(); - }} - > - - Email - form.setFieldValue('email', e.target.value)} - /> - - - - 昵称 - form.setFieldValue('nickname', e.target.value)} - /> - - - - - 副标题 - form.setFieldValue('subtitle', e.target.value)} - /> - - - - -
- ); -} diff --git a/client/cms/src/components/profile/main-profile.tsx b/client/cms/src/components/profile/main-profile.tsx index 0618809..da496f0 100644 --- a/client/cms/src/components/profile/main-profile.tsx +++ b/client/cms/src/components/profile/main-profile.tsx @@ -8,25 +8,29 @@ import { EditProfileDialog } from './edit-profile-dialog'; export function MainProfile() { const { data: user } = useUserInfo(); return ( -
-
- - - CN - -
- - +
+
+
+
+
+ + + CN + +
+ + +
+
+
+ + {user.email} +
+
+
- -
-
- - {user.email} -
-
-
+
{/* Bio */} {base64ToUtf8(user.bio)}
diff --git a/client/cms/src/hooks/data/useUpdateUser.ts b/client/cms/src/hooks/data/useUpdateUser.ts new file mode 100644 index 0000000..8958939 --- /dev/null +++ b/client/cms/src/hooks/data/useUpdateUser.ts @@ -0,0 +1,22 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { axiosClient } from '@/lib/axios'; + +interface UpdateUserPayload { + avatar?: string; + bio?: string; + nickname?: string; + subtitle?: string; + username?: string; +} + +export function useUpdateUser() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (payload: UpdateUserPayload) => { + return axiosClient.patch<{ status: string }>('/user/update', payload); + }, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ['userInfo'] }); + }, + }); +} diff --git a/client/cms/src/hooks/data/useUserInfo.ts b/client/cms/src/hooks/data/useUserInfo.ts index 46e6abe..3f8394c 100644 --- a/client/cms/src/hooks/data/useUserInfo.ts +++ b/client/cms/src/hooks/data/useUserInfo.ts @@ -6,6 +6,7 @@ export function useUserInfo() { queryKey: ['userInfo'], queryFn: async () => { const response = await axiosClient.get<{ + username: string; user_id: string; email: string; type: string; diff --git a/client/cms/src/routes/_sidebarLayout/profile.tsx b/client/cms/src/routes/_sidebarLayout/profile.tsx index f28284e..7dfcf31 100644 --- a/client/cms/src/routes/_sidebarLayout/profile.tsx +++ b/client/cms/src/routes/_sidebarLayout/profile.tsx @@ -7,7 +7,7 @@ export const Route = createFileRoute('/_sidebarLayout/profile')({ function RouteComponent() { return ( -
+
);