'use client'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { useForm, Controller } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { Box, Container, TextField, Button, Typography, Card, CardContent, Avatar, Select, MenuItem, FormControl, InputLabel, InputAdornment, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, } from '@mui/material'; import { Person, Visibility, VisibilityOff, CheckCircle } from '@mui/icons-material'; import { useAuthContext } from '@/features/auth'; import { useUIStore } from '@/stores/uiStore'; import { userApi } from '@/entities/user'; import Header from '@/shared/ui/Header'; import { cardStyles, colors, responsiveText } from '@/shared/lib/button-styles'; // 기본 정보 스키마 const basicInfoSchema = z.object({ name: z.string().min(2, '이름은 2자 이상이어야 합니다'), phone: z .string() .min(1, '휴대폰 번호를 입력해주세요') .regex(/^010-\d{4}-\d{4}$/, '올바른 휴대폰 번호 형식이 아닙니다'), email: z.string().email('올바른 이메일 형식이 아닙니다'), }); // 사업장 정보 스키마 const businessInfoSchema = z.object({ businessName: z.string().min(2, '매장명은 2자 이상이어야 합니다'), businessType: z.string().min(1, '업종을 선택해주세요'), businessLocation: z.string().optional(), businessHours: z.string().optional(), }); // 비밀번호 변경 스키마 const passwordSchema = z .object({ currentPassword: z.string().min(1, '현재 비밀번호를 입력해주세요'), newPassword: z .string() .min(8, '비밀번호는 8자 이상이어야 합니다') .max(100, '비밀번호는 100자 이하여야 합니다'), confirmPassword: z.string(), }) .refine((data) => data.newPassword === data.confirmPassword, { message: '새 비밀번호가 일치하지 않습니다', path: ['confirmPassword'], }); type BasicInfoData = z.infer; type BusinessInfoData = z.infer; type PasswordData = z.infer; export default function ProfilePage() { const router = useRouter(); const { user, logout, refreshProfile } = useAuthContext(); const { showToast, setLoading } = useUIStore(); const [showCurrentPassword, setShowCurrentPassword] = useState(false); const [showNewPassword, setShowNewPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [successDialogOpen, setSuccessDialogOpen] = useState(false); const [logoutDialogOpen, setLogoutDialogOpen] = useState(false); const [profileLoaded, setProfileLoaded] = useState(false); // 기본 정보 폼 const { control: basicControl, handleSubmit: handleBasicSubmit, formState: { errors: basicErrors }, reset: resetBasic, } = useForm({ resolver: zodResolver(basicInfoSchema), defaultValues: { name: '', phone: '', email: '', }, }); // 사업장 정보 폼 const { control: businessControl, handleSubmit: handleBusinessSubmit, formState: { errors: businessErrors }, reset: resetBusiness, } = useForm({ resolver: zodResolver(businessInfoSchema), defaultValues: { businessName: '', businessType: '', businessLocation: '', businessHours: '', }, }); // 비밀번호 변경 폼 const { control: passwordControl, handleSubmit: handlePasswordSubmit, formState: { errors: passwordErrors }, reset: resetPassword, } = useForm({ resolver: zodResolver(passwordSchema), defaultValues: { currentPassword: '', newPassword: '', confirmPassword: '', }, }); // 프로필 데이터 로드 useEffect(() => { const loadProfile = async () => { console.log('📋 프로필 페이지: 프로필 데이터 로드 시작'); if (!user) { console.log('❌ 사용자 정보 없음, 로그인 페이지로 이동'); router.push('/login'); return; } if (profileLoaded) { console.log('✅ 프로필 이미 로드됨'); return; } try { setLoading(true); console.log('📡 프로필 조회 API 호출'); const profile = await userApi.getProfile(); console.log('📥 프로필 조회 성공:', profile); // 전화번호 형식 변환: 01012345678 → 010-1234-5678 const formattedPhone = profile.phoneNumber ? `${profile.phoneNumber.slice(0, 3)}-${profile.phoneNumber.slice(3, 7)}-${profile.phoneNumber.slice(7, 11)}` : ''; // 기본 정보 폼 초기화 resetBasic({ name: profile.userName || '', phone: formattedPhone, email: profile.email || '', }); // 사업장 정보 폼 초기화 resetBusiness({ businessName: profile.storeName || '', businessType: profile.industry || '', businessLocation: profile.address || '', businessHours: profile.businessHours || '', }); setProfileLoaded(true); console.log('✅ 프로필 폼 초기화 완료'); } catch (error: any) { console.error('❌ 프로필 로드 실패:', error); if (error.response?.status === 401) { showToast('로그인이 필요합니다', 'error'); router.push('/login'); } else { showToast('프로필 정보를 불러오는데 실패했습니다', 'error'); } } finally { setLoading(false); } }; loadProfile(); }, [user, profileLoaded, router, resetBasic, resetBusiness, setLoading, showToast]); const formatPhoneNumber = (value: string) => { const numbers = value.replace(/[^\d]/g, ''); if (numbers.length <= 3) return numbers; if (numbers.length <= 7) return `${numbers.slice(0, 3)}-${numbers.slice(3)}`; return `${numbers.slice(0, 3)}-${numbers.slice(3, 7)}-${numbers.slice(7, 11)}`; }; const onSaveProfile = async (data: BasicInfoData & BusinessInfoData) => { console.log('💾 프로필 저장 시작'); console.log('📦 저장 데이터:', { ...data, phone: data.phone }); try { setLoading(true); // 전화번호 형식 변환: 010-1234-5678 → 01012345678 const phoneNumber = data.phone.replace(/-/g, ''); console.log('📞 전화번호 변환:', data.phone, '->', phoneNumber); const updateData = { userName: data.name, phoneNumber: phoneNumber, storeName: data.businessName, industry: data.businessType, address: data.businessLocation || '', businessHours: data.businessHours || '', }; console.log('📡 프로필 업데이트 API 호출:', updateData); await userApi.updateProfile(updateData); console.log('✅ 프로필 업데이트 성공'); // 최신 프로필 정보 다시 가져오기 console.log('🔄 프로필 새로고침'); await refreshProfile(); console.log('✅ 프로필 새로고침 완료'); setSuccessDialogOpen(true); showToast('프로필이 저장되었습니다', 'success'); } catch (error: any) { console.error('❌ 프로필 저장 실패:', error); let errorMessage = '프로필 저장에 실패했습니다'; if (error.response) { errorMessage = error.response.data?.message || error.response.data?.error || `서버 오류 (${error.response.status})`; } else if (error.request) { errorMessage = '서버로부터 응답이 없습니다'; } showToast(errorMessage, 'error'); } finally { setLoading(false); } }; const onChangePassword = async (data: PasswordData) => { console.log('🔐 비밀번호 변경 시작'); try { setLoading(true); const passwordData = { currentPassword: data.currentPassword, newPassword: data.newPassword, }; console.log('📡 비밀번호 변경 API 호출'); await userApi.changePassword(passwordData); console.log('✅ 비밀번호 변경 성공'); showToast('비밀번호가 변경되었습니다', 'success'); resetPassword(); } catch (error: any) { console.error('❌ 비밀번호 변경 실패:', error); let errorMessage = '비밀번호 변경에 실패했습니다'; if (error.response) { errorMessage = error.response.data?.message || error.response.data?.error || `서버 오류 (${error.response.status})`; } else if (error.request) { errorMessage = '서버로부터 응답이 없습니다'; } showToast(errorMessage, 'error'); } finally { setLoading(false); } }; const handleSave = () => { handleBasicSubmit((basicData) => { handleBusinessSubmit((businessData) => { onSaveProfile({ ...basicData, ...businessData }); })(); })(); }; const handleLogout = async () => { console.log('🚪 로그아웃 시작'); setLoading(true); try { await logout(); showToast('로그아웃되었습니다', 'success'); } catch (error) { console.error('❌ 로그아웃 중 예상치 못한 에러:', error); showToast('로그아웃되었습니다', 'success'); } finally { setLoading(false); // 로그아웃은 항상 로그인 페이지로 이동 router.push('/login'); } }; return ( <>
{/* 사용자 정보 섹션 */} {user?.userName} {user?.email} {/* 기본 정보 */} 기본 정보 ( )} /> ( { const formatted = formatPhoneNumber(e.target.value); field.onChange(formatted); }} error={!!basicErrors.phone} helperText={basicErrors.phone?.message} /> )} /> ( )} /> {/* 매장 정보 */} 매장 정보 ( )} /> ( 업종 {businessErrors.businessType && ( {businessErrors.businessType.message} )} )} /> ( )} /> ( )} /> {/* 비밀번호 변경 */} 비밀번호 변경 ( setShowCurrentPassword(!showCurrentPassword)} edge="end" > {showCurrentPassword ? : } ), }} /> )} /> ( setShowNewPassword(!showNewPassword)} edge="end" > {showNewPassword ? : } ), }} /> )} /> ( setShowConfirmPassword(!showConfirmPassword)} edge="end" > {showConfirmPassword ? : } ), }} /> )} /> {/* 액션 버튼 */} {/* 저장 완료 다이얼로그 */} setSuccessDialogOpen(false)} PaperProps={{ sx: { borderRadius: 4, minWidth: 300, }, }} > 저장 완료 프로필 정보가 업데이트되었습니다. {/* 로그아웃 확인 다이얼로그 */} setLogoutDialogOpen(false)} PaperProps={{ sx: { borderRadius: 4, minWidth: 300, }, }} > 로그아웃 로그아웃 하시겠습니까? ); }