diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-26 00:37:41 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-26 00:37:41 +0000 |
| commit | e0dfb55c5457aec489fc084c4567e791b4c65eb1 (patch) | |
| tree | 68543a65d88f5afb3a0202925804103daa91bc6f /components/settings/account-form.tsx | |
3/25 까지의 대표님 작업사항
Diffstat (limited to 'components/settings/account-form.tsx')
| -rw-r--r-- | components/settings/account-form.tsx | 263 |
1 files changed, 263 insertions, 0 deletions
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<typeof accountFormSchema> + + + +export function AccountForm() { + + const { data: session } = useSession(); + const userId = session?.user.id || "" + + + const [previewUrl, setPreviewUrl] = React.useState<string | null>(null) + + const form = useForm<AccountFormValues>({ + 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 ( + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> + <FormField + control={form.control} + name="name" + render={({ field }) => ( + <FormItem> + <FormLabel>Name</FormLabel> + <FormControl> + <Input placeholder="Your name" {...field} /> + </FormControl> + <FormDescription> + This is the name that will be displayed on your profile and in + emails. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="email" + render={({ field }) => ( + <FormItem> + <FormLabel>Email</FormLabel> + <FormControl> + <Input placeholder="Your Email" {...field} /> + </FormControl> + <FormDescription> + This is the email that will be used on login. If you want change it, please be careful. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="company" + render={({ field }) => ( + <FormItem> + <FormLabel>Company</FormLabel> + <FormControl> + <Input + placeholder="Your Company name" + {...field} + readOnly + className="cursor-not-allowed bg-slate-50" + /> + </FormControl> + <FormDescription> + This is the name that will be displayed on your profile and in + emails. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + + + {/* 이미지 업로드 */} + <FormField + control={form.control} + name="imageFile" + render={({ field }) => ( + <FormItem> + <FormLabel>Profile Image</FormLabel> + <FormControl> + <div className="space-y-2"> + <Input + type="file" + accept="image/*" + onChange={(e) => { + 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 ? ( + <img src={previewUrl} alt="Local Preview" width={200}/> + ) : ( + typeof field.value === "string" && + field.value && ( + <img + src={`/profiles/${field.value}`} + alt="Server Image" + width={200} + /> + ) + )} + </div> + </FormControl> + <FormDescription> + Upload your profile image. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + <Button type="submit">Update account</Button> + </form> + </Form> + ) +} |
