diff --git a/apps/frontend/app/(client)/(main)/settings/page.tsx b/apps/frontend/app/(client)/(main)/settings/page.tsx index 84772c0b6f..1c9efc4203 100644 --- a/apps/frontend/app/(client)/(main)/settings/page.tsx +++ b/apps/frontend/app/(client)/(main)/settings/page.tsx @@ -1,13 +1,15 @@ 'use client' -import { safeFetcherWithAuth } from '@/libs/utils' import type { SettingsFormat } from '@/types/type' import { valibotResolver } from '@hookform/resolvers/valibot' +import { useMutation, useQuery } from '@tanstack/react-query' import { useRouter, useSearchParams } from 'next/navigation' import { useEffect, useRef } from 'react' import { useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' +import { updateUserProfile } from '../../_libs/apis/profile' +import { profileQueries } from '../../_libs/queries/profile' import { ConfirmModal } from './_components/ConfirmModal' import { CurrentPwSection } from './_components/CurrentPwSection' import { IdSection } from './_components/IdSection' @@ -20,7 +22,6 @@ import { SaveButton } from './_components/SaveButton' import { StudentIdSection } from './_components/StudentIdSection' import { TopicSection } from './_components/TopicSection' import { SettingsProvider } from './_components/context' -import type { Profile } from './_components/context' import { useCheckPassword } from './_libs/hooks/useCheckPassword' import { getSchema } from './_libs/schemas' import { useConfirmNavigation } from './_libs/utils' @@ -38,31 +39,28 @@ export default function Page() { const updateNow = searchParams.get('updateNow') const router = useRouter() const bypassConfirmation = useRef(false) - const [defaultProfileValues, setdefaultProfileValues] = useState({ - username: '', - userProfile: { - realName: '' + + const { data: defaultProfileValues, isLoading } = useQuery({ + ...profileQueries.fetch(), + initialData: { + username: '', + userProfile: { + realName: '' + }, + studentId: '', + major: '' }, - studentId: '', - major: '' + retry: false }) - // Fetch default profile values + const [majorValue, setMajorValue] = useState(defaultProfileValues.major) + useEffect(() => { - const fetchDefaultProfile = async () => { - try { - const data: Profile = await safeFetcherWithAuth.get('user').json() - setMajorValue(data.major) - setdefaultProfileValues(data) - setIsLoading(false) - } catch (error) { - console.error('Failed to fetch profile:', error) - toast.error('Failed to load profile data') - setIsLoading(false) - } + if (!isLoading && defaultProfileValues.major) { + console.log('Setting majorValue:', defaultProfileValues.major) + setMajorValue(defaultProfileValues.major) } - fetchDefaultProfile() - }, []) + }, [defaultProfileValues.major, isLoading]) const { register, @@ -91,6 +89,7 @@ export default function Page() { const { isConfirmModalOpen, setIsConfirmModalOpen, confirmAction } = useConfirmNavigation(bypassConfirmation, Boolean(updateNow)) + const { isPasswordCorrect, newPasswordAble, @@ -102,8 +101,6 @@ export default function Page() { const [newPasswordShow, setNewPasswordShow] = useState(false) const [confirmPasswordShow, setConfirmPasswordShow] = useState(false) const [majorOpen, setMajorOpen] = useState(false) - const [majorValue, setMajorValue] = useState('') - const [isLoading, setIsLoading] = useState(true) const isPasswordsMatch = newPassword === confirmPassword && newPassword !== '' const saveAblePassword: boolean = @@ -121,53 +118,56 @@ export default function Page() { (!newPassword && !confirmPassword)) const saveAbleUpdateNow = Boolean(studentId) && majorValue !== 'none' && !errors.studentId - // 일치 여부에 따라 New Password Input, Re-enter Password Input 창의 border 색상을 바꿈 + useEffect(() => { if (isPasswordsMatch) { setValue('newPassword', newPassword) setValue('confirmPassword', confirmPassword) } - }, [isPasswordsMatch, newPassword, confirmPassword]) - const onSubmit = async (data: SettingsFormat) => { - try { - // 필요 없는 필드 제외 (defaultProfileValues와 값이 같은 것들은 제외) - const updatePayload: UpdatePayload = {} - if (data.realName !== defaultProfileValues.userProfile?.realName) { - updatePayload.realName = data.realName - } - if (majorValue !== defaultProfileValues.major) { - updatePayload.major = majorValue - } - if (data.currentPassword !== 'tmppassword1') { - updatePayload.password = data.currentPassword - } - if (data.newPassword !== 'tmppassword1') { - updatePayload.newPassword = data.newPassword - } - if (updateNow && data.studentId !== '0000000000') { - updatePayload.studentId = data.studentId - } - const response = await safeFetcherWithAuth.patch('user', { - json: updatePayload - }) - if (response.ok) { - toast.success('Successfully updated your information') - bypassConfirmation.current = true - setTimeout(() => { - if (updateNow) { - router.push('/') - } else { - window.location.reload() - } - }, 1500) - } - } catch (error) { + }, [isPasswordsMatch, newPassword, confirmPassword, setValue]) + + const { mutate } = useMutation({ + mutationFn: updateUserProfile, + onError: (error) => { console.error(error) toast.error('Failed to update your information, Please try again') setTimeout(() => { window.location.reload() }, 1500) + }, + onSuccess: () => { + toast.success('Successfully updated your information') + bypassConfirmation.current = true + setTimeout(() => { + if (updateNow) { + router.push('/') + } else { + window.location.reload() + } + }, 1500) + } + }) + + const onSubmit = (data: SettingsFormat) => { + const updatePayload: UpdatePayload = {} + + if (data.realName !== defaultProfileValues.userProfile?.realName) { + updatePayload.realName = data.realName + } + if (majorValue !== defaultProfileValues.major) { + updatePayload.major = majorValue + } + if (data.currentPassword !== 'tmppassword1') { + updatePayload.password = data.currentPassword } + if (data.newPassword !== 'tmppassword1') { + updatePayload.newPassword = data.newPassword + } + if (updateNow && data.studentId !== '0000000000') { + updatePayload.studentId = data.studentId + } + + mutate(updatePayload) } const resetToSubmittableValue = ( @@ -193,6 +193,7 @@ export default function Page() { const settingsContextValue = { defaultProfileValues, + isLoading, passwordState: { passwordShow, setPasswordShow, @@ -211,10 +212,9 @@ export default function Page() { register, errors }, - updateNow: Boolean(updateNow), - isLoading + updateNow: Boolean(updateNow) + //isLoading } - return (
{/* Logo */} diff --git a/apps/frontend/app/(client)/_libs/apis/profile.ts b/apps/frontend/app/(client)/_libs/apis/profile.ts new file mode 100644 index 0000000000..b22bce8af0 --- /dev/null +++ b/apps/frontend/app/(client)/_libs/apis/profile.ts @@ -0,0 +1,22 @@ +import { safeFetcherWithAuth } from '@/libs/utils' + +export interface Profile { + username: string // ID + userProfile: { + realName: string + } + studentId: string + major: string +} + +export const fetchUserProfile = async (): Promise => { + return await safeFetcherWithAuth.get('user').json() +} + +export const updateUserProfile = async ( + updatePayload: Partial +): Promise => { + return await safeFetcherWithAuth.patch('user', { + json: updatePayload + }) +} diff --git a/apps/frontend/app/(client)/_libs/queries/profile.ts b/apps/frontend/app/(client)/_libs/queries/profile.ts new file mode 100644 index 0000000000..69192e6ca1 --- /dev/null +++ b/apps/frontend/app/(client)/_libs/queries/profile.ts @@ -0,0 +1,9 @@ +import { fetchUserProfile } from '../apis/profile' + +export const profileQueries = { + all: () => ['userProfile'] as const, + fetch: () => ({ + queryKey: profileQueries.all(), + queryFn: fetchUserProfile + }) +}