From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Mar 2025 15:55:45 +0900 Subject: initial commit --- components/settings/account-form.tsx | 263 +++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 components/settings/account-form.tsx (limited to 'components/settings/account-form.tsx') diff --git a/components/settings/account-form.tsx b/components/settings/account-form.tsx new file mode 100644 index 00000000..97cad9e5 --- /dev/null +++ b/components/settings/account-form.tsx @@ -0,0 +1,263 @@ +"use client" + +import * as React from "react" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { toast } from "@/hooks/use-toast" +import { Button } from "@/components/ui/button" + +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" + +import { findUserById } from "@/lib/admin-users/service" +import { useSession } from "next-auth/react"; + +import { updateUserProfileImage } from "@/lib/users/service" + + + +const accountFormSchema = z.object({ + name: z + .string() + .min(2, { + message: "Name must be at least 2 characters.", + }) + .max(30, { + message: "Name must not be longer than 30 characters.", + }), + email: z.string().email(), + company: z + .string() + .min(2, { + message: "Name must be at least 2 characters.", + }) + .max(30, { + message: "Name must not be longer than 30 characters.", + }), + + imageFile: z.any().optional(), + +}) + +type AccountFormValues = z.infer + + + +export function AccountForm() { + + const { data: session } = useSession(); + const userId = session?.user.id || "" + + + const [previewUrl, setPreviewUrl] = React.useState(null) + + const form = useForm({ + resolver: zodResolver(accountFormSchema), + defaultValues: { + name: "", + company: "", + email: "", + imageFile: null, + }, + }) + + // Fetch data in useEffect + React.useEffect(() => { + console.log("Form state changed: ", form.getValues()); + + async function fetchUser() { + try { + const data = await findUserById(Number(userId)) + if (data) { + // Also reset the form's default values + form.reset({ + name: data.user_name || "", + company: data.company_name || "", + email: data.user_email || "", + imageFile: data.user_image, // no file to begin with + }) + } + } catch (error) { + console.error("Failed to fetch user data:", error) + } + } + + if (userId) { + fetchUser() + } + }, [userId, form]) + + + async function onSubmit(data: AccountFormValues) { + // RHF가 추적한 dirtyFields를 가져옵니다. + const { dirtyFields } = form.formState + + // 변경된 필드가 전혀 없다면 => 업데이트 스킵 + if (Object.keys(dirtyFields).length === 0) { + toast({ + title: "No changes", + description: "Nothing to update", + }) + return + } + + // 바뀐 파일만 업로드 + let imageFile: File | null = null + if (dirtyFields.imageFile && data.imageFile && data.imageFile.length > 0) { + // 새로 업로드한 파일 + imageFile = data.imageFile[0] + } + + // FormData 생성 + const formData = new FormData() + formData.append("userId", userId) + formData.append("name", data.name) + formData.append("company", data.company) + formData.append("email", data.email) + + if (imageFile) { + formData.append("file", imageFile) + } + + try { + // 서버 액션(또는 API) 호출 + await updateUserProfileImage(formData) + + toast({ + title: "Account updated", + description: "User updated successfully!", + }) + + } catch (error: any) { + toast({ + title: "Error", + description: `Error: ${error.message ?? error}`, + variant: "destructive", + }) + } + } + + + return ( +
+ + ( + + Name + + + + + This is the name that will be displayed on your profile and in + emails. + + + + )} + /> + + ( + + Email + + + + + This is the email that will be used on login. If you want change it, please be careful. + + + + )} + /> + + ( + + Company + + + + + This is the name that will be displayed on your profile and in + emails. + + + + )} + /> + + + + {/* 이미지 업로드 */} + ( + + Profile Image + +
+ { + field.onChange(e.target.files) + if (e.target.files && e.target.files.length > 0) { + // 로컬 미리보기 URL + const file = e.target.files[0] + const url = URL.createObjectURL(file) + setPreviewUrl(url) + } + }} + /> + + {previewUrl ? ( + Local Preview + ) : ( + typeof field.value === "string" && + field.value && ( + Server Image + ) + )} +
+
+ + Upload your profile image. + + +
+ )} + /> + + + + + ) +} -- cgit v1.2.3