'use client'; import { useRef, useState } from 'react'; import Image from 'next/image'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import toast from 'react-hot-toast'; import { Camera, Loader2, Save, UserRound } from 'lucide-react'; import { getProfile, updateProfile } from '@/api/profile'; import { uploadImage } from '@/api/image'; import WindowSurface from '@/components/ui/WindowSurface'; import { Profile, ProfileUpdateRequest } from '@/types'; const defaultProfile: Profile = { name: 'Dev Park', bio: '풀스택을 꿈꾸는 개발자\n"코드로 세상을 바꾸고 싶은 박개발의 기술 블로그입니다."', imageUrl: 'https://api.dicebear.com/7.x/notionists/svg?seed=Felix', githubUrl: 'https://github.com', email: 'user@example.com', }; const getErrorMessage = (error: unknown, fallback: string) => { if (typeof error === 'object' && error !== null && 'response' in error) { const response = (error as { response?: { data?: { message?: string } } }).response; if (response?.data?.message) return `${fallback}: ${response.data.message}`; } if (error instanceof Error) return `${fallback}: ${error.message}`; return fallback; }; function toProfileForm(profile: Profile): ProfileUpdateRequest { return { name: profile.name, bio: profile.bio, imageUrl: profile.imageUrl || '', githubUrl: profile.githubUrl || '', email: profile.email || '', }; } function ProfileForm({ profile }: { profile: Profile }) { const queryClient = useQueryClient(); const fileInputRef = useRef(null); const [form, setForm] = useState(() => toProfileForm(profile)); const [isUploading, setIsUploading] = useState(false); const updateMutation = useMutation({ mutationFn: updateProfile, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['profile'] }); toast.success('프로필이 저장되었습니다.'); }, onError: (error) => toast.error(getErrorMessage(error, '프로필 저장 실패')), }); const handleImageUpload = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; setIsUploading(true); const uploadToast = toast.loading('이미지 업로드 중...'); try { const response = await uploadImage(file); if (response.code === 'SUCCESS' && response.data) { setForm((previous) => ({ ...previous, imageUrl: response.data })); toast.success('이미지가 업로드되었습니다.', { id: uploadToast }); } else { toast.error(response.message || '이미지 업로드 실패', { id: uploadToast }); } } catch (error) { toast.error(getErrorMessage(error, '이미지 업로드 실패'), { id: uploadToast }); } finally { setIsUploading(false); if (fileInputRef.current) fileInputRef.current.value = ''; } }; const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); updateMutation.mutate({ ...form, name: form.name.trim(), bio: form.bio.trim(), githubUrl: form.githubUrl?.trim(), email: form.email?.trim(), imageUrl: form.imageUrl?.trim(), }); }; const isSaving = updateMutation.isPending || isUploading; return (