From 51093dee2c87368b0868893dc76f19ea8d438317 Mon Sep 17 00:00:00 2001 From: Kimhyojung0810 Date: Thu, 16 Jan 2025 14:07:22 +0900 Subject: [PATCH 1/7] refactor(fe): refactor settings page --- .../(main)/settings/_libs/apis/profile.ts | 22 +++++ .../(main)/settings/_libs/queries/profile.ts | 15 ++++ .../app/(client)/(main)/settings/page.tsx | 86 ++++++++----------- 3 files changed, 71 insertions(+), 52 deletions(-) create mode 100644 apps/frontend/app/(client)/(main)/settings/_libs/apis/profile.ts create mode 100644 apps/frontend/app/(client)/(main)/settings/_libs/queries/profile.ts diff --git a/apps/frontend/app/(client)/(main)/settings/_libs/apis/profile.ts b/apps/frontend/app/(client)/(main)/settings/_libs/apis/profile.ts new file mode 100644 index 0000000000..b22bce8af0 --- /dev/null +++ b/apps/frontend/app/(client)/(main)/settings/_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)/(main)/settings/_libs/queries/profile.ts b/apps/frontend/app/(client)/(main)/settings/_libs/queries/profile.ts new file mode 100644 index 0000000000..7f5866aeef --- /dev/null +++ b/apps/frontend/app/(client)/(main)/settings/_libs/queries/profile.ts @@ -0,0 +1,15 @@ +import { useSuspenseQuery, useMutation } from '@tanstack/react-query' +import { fetchUserProfile, updateUserProfile } from '../apis/profile' + +export const useFetchUserProfileSuspense = () => { + return useSuspenseQuery({ + queryKey: ['userProfile'], + queryFn: fetchUserProfile + }) +} + +export const useUpdateUserProfile = () => { + return useMutation({ + mutationFn: updateUserProfile + }) +} diff --git a/apps/frontend/app/(client)/(main)/settings/page.tsx b/apps/frontend/app/(client)/(main)/settings/page.tsx index 84772c0b6f..c3ea632024 100644 --- a/apps/frontend/app/(client)/(main)/settings/page.tsx +++ b/apps/frontend/app/(client)/(main)/settings/page.tsx @@ -1,10 +1,9 @@ 'use client' -import { safeFetcherWithAuth } from '@/libs/utils' import type { SettingsFormat } from '@/types/type' import { valibotResolver } from '@hookform/resolvers/valibot' import { useRouter, useSearchParams } from 'next/navigation' -import { useEffect, useRef } from 'react' +import { useRef } from 'react' import { useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' @@ -20,8 +19,11 @@ 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 { + useFetchUserProfileSuspense, + useUpdateUserProfile +} from './_libs/queries/profile' import { getSchema } from './_libs/schemas' import { useConfirmNavigation } from './_libs/utils' @@ -38,31 +40,14 @@ export default function Page() { const updateNow = searchParams.get('updateNow') const router = useRouter() const bypassConfirmation = useRef(false) - const [defaultProfileValues, setdefaultProfileValues] = useState({ - username: '', - userProfile: { - realName: '' - }, - studentId: '', - major: '' - }) - // Fetch default profile values - 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) - } - } - fetchDefaultProfile() - }, []) + // React Query를 사용하여 프로필 데이터 가져오기 + const { data: defaultProfileValues } = useFetchUserProfileSuspense() + + const [majorValue, setMajorValue] = useState( + defaultProfileValues.major || '' + ) + const [isLoading, setIsLoading] = useState(false) const { register, @@ -91,6 +76,7 @@ export default function Page() { const { isConfirmModalOpen, setIsConfirmModalOpen, confirmAction } = useConfirmNavigation(bypassConfirmation, Boolean(updateNow)) + const { isPasswordCorrect, newPasswordAble, @@ -102,8 +88,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,17 +105,19 @@ 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]) + + // 비밀번호 일치 여부에 따라 값 설정 + if (isPasswordsMatch) { + setValue('newPassword', newPassword) + setValue('confirmPassword', confirmPassword) + } + + const updateUserProfile = useUpdateUserProfile() + const onSubmit = async (data: SettingsFormat) => { try { - // 필요 없는 필드 제외 (defaultProfileValues와 값이 같은 것들은 제외) const updatePayload: UpdatePayload = {} + if (data.realName !== defaultProfileValues.userProfile?.realName) { updatePayload.realName = data.realName } @@ -147,20 +133,17 @@ export default function Page() { 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) - } + + await updateUserProfile.mutateAsync(updatePayload) + toast.success('Successfully updated your information') + bypassConfirmation.current = true + setTimeout(() => { + if (updateNow) { + router.push('/') + } else { + window.location.reload() + } + }, 1500) } catch (error) { console.error(error) toast.error('Failed to update your information, Please try again') @@ -214,7 +197,6 @@ export default function Page() { updateNow: Boolean(updateNow), isLoading } - return (
{/* Logo */} From a45abcde3545c015a4ab2c73d2643d3bd650b046 Mon Sep 17 00:00:00 2001 From: Kimhyojung0810 Date: Thu, 16 Jan 2025 14:16:09 +0900 Subject: [PATCH 2/7] fix(fe): fix rendering error --- .../(main)/settings/_components/context.ts | 2 +- .../app/(client)/(main)/settings/page.tsx | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/frontend/app/(client)/(main)/settings/_components/context.ts b/apps/frontend/app/(client)/(main)/settings/_components/context.ts index 8ad3753701..a7fb9cd10c 100644 --- a/apps/frontend/app/(client)/(main)/settings/_components/context.ts +++ b/apps/frontend/app/(client)/(main)/settings/_components/context.ts @@ -38,7 +38,7 @@ export type SettingsContextType = { majorState: MajorState formState: FormState updateNow: boolean - isLoading: boolean + //isLoading: boolean } const SettingsContext = createContext( diff --git a/apps/frontend/app/(client)/(main)/settings/page.tsx b/apps/frontend/app/(client)/(main)/settings/page.tsx index c3ea632024..b6bcb14506 100644 --- a/apps/frontend/app/(client)/(main)/settings/page.tsx +++ b/apps/frontend/app/(client)/(main)/settings/page.tsx @@ -3,7 +3,7 @@ import type { SettingsFormat } from '@/types/type' import { valibotResolver } from '@hookform/resolvers/valibot' import { useRouter, useSearchParams } from 'next/navigation' -import { useRef } from 'react' +import { useEffect, useRef } from 'react' import { useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' @@ -47,7 +47,7 @@ export default function Page() { const [majorValue, setMajorValue] = useState( defaultProfileValues.major || '' ) - const [isLoading, setIsLoading] = useState(false) + //const [isLoading, setIsLoading] = useState(false) const { register, @@ -106,11 +106,13 @@ export default function Page() { const saveAbleUpdateNow = Boolean(studentId) && majorValue !== 'none' && !errors.studentId - // 비밀번호 일치 여부에 따라 값 설정 - if (isPasswordsMatch) { - setValue('newPassword', newPassword) - setValue('confirmPassword', confirmPassword) - } + // 무한 렌더링.. + useEffect(() => { + if (isPasswordsMatch) { + setValue('newPassword', newPassword) + setValue('confirmPassword', confirmPassword) + } + }, [isPasswordsMatch, newPassword, confirmPassword, setValue]) const updateUserProfile = useUpdateUserProfile() @@ -194,8 +196,8 @@ export default function Page() { register, errors }, - updateNow: Boolean(updateNow), - isLoading + updateNow: Boolean(updateNow) + //isLoading } return (
From 86c8792c0277965e07c514e19cba2ba029acf4e4 Mon Sep 17 00:00:00 2001 From: Kimhyojung0810 Date: Mon, 20 Jan 2025 01:33:19 +0900 Subject: [PATCH 3/7] refactor(fe): delete unused apis and queires --- .../(main)/settings/_libs/apis/profile.ts | 22 ------------------- .../(main)/settings/_libs/queries/profile.ts | 15 ------------- 2 files changed, 37 deletions(-) delete mode 100644 apps/frontend/app/(client)/(main)/settings/_libs/apis/profile.ts delete mode 100644 apps/frontend/app/(client)/(main)/settings/_libs/queries/profile.ts diff --git a/apps/frontend/app/(client)/(main)/settings/_libs/apis/profile.ts b/apps/frontend/app/(client)/(main)/settings/_libs/apis/profile.ts deleted file mode 100644 index b22bce8af0..0000000000 --- a/apps/frontend/app/(client)/(main)/settings/_libs/apis/profile.ts +++ /dev/null @@ -1,22 +0,0 @@ -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)/(main)/settings/_libs/queries/profile.ts b/apps/frontend/app/(client)/(main)/settings/_libs/queries/profile.ts deleted file mode 100644 index 7f5866aeef..0000000000 --- a/apps/frontend/app/(client)/(main)/settings/_libs/queries/profile.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useSuspenseQuery, useMutation } from '@tanstack/react-query' -import { fetchUserProfile, updateUserProfile } from '../apis/profile' - -export const useFetchUserProfileSuspense = () => { - return useSuspenseQuery({ - queryKey: ['userProfile'], - queryFn: fetchUserProfile - }) -} - -export const useUpdateUserProfile = () => { - return useMutation({ - mutationFn: updateUserProfile - }) -} From 26bc32a335f48bcac1cfe4ebc26dac1db03e1996 Mon Sep 17 00:00:00 2001 From: Kimhyojung0810 Date: Mon, 20 Jan 2025 01:33:50 +0900 Subject: [PATCH 4/7] refactor(fe): add apis and queries --- .../app/(client)/_libs/apis/profile.ts | 22 +++++++++++++++++++ .../app/(client)/_libs/queries/profile.ts | 9 ++++++++ 2 files changed, 31 insertions(+) create mode 100644 apps/frontend/app/(client)/_libs/apis/profile.ts create mode 100644 apps/frontend/app/(client)/_libs/queries/profile.ts 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 + }) +} From a6577525e205f841f55dbbcbd9e14cd8bac3c5ae Mon Sep 17 00:00:00 2001 From: Kimhyojung0810 Date: Mon, 20 Jan 2025 01:36:01 +0900 Subject: [PATCH 5/7] refactor(fe): use tanstack query for settings page --- .../app/(client)/(main)/settings/page.tsx | 88 ++++++++++--------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/apps/frontend/app/(client)/(main)/settings/page.tsx b/apps/frontend/app/(client)/(main)/settings/page.tsx index b6bcb14506..5ca271cc3a 100644 --- a/apps/frontend/app/(client)/(main)/settings/page.tsx +++ b/apps/frontend/app/(client)/(main)/settings/page.tsx @@ -2,11 +2,14 @@ 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,10 +23,6 @@ import { StudentIdSection } from './_components/StudentIdSection' import { TopicSection } from './_components/TopicSection' import { SettingsProvider } from './_components/context' import { useCheckPassword } from './_libs/hooks/useCheckPassword' -import { - useFetchUserProfileSuspense, - useUpdateUserProfile -} from './_libs/queries/profile' import { getSchema } from './_libs/schemas' import { useConfirmNavigation } from './_libs/utils' @@ -41,13 +40,20 @@ export default function Page() { const router = useRouter() const bypassConfirmation = useRef(false) - // React Query를 사용하여 프로필 데이터 가져오기 - const { data: defaultProfileValues } = useFetchUserProfileSuspense() + const { data: defaultProfileValues } = useQuery({ + ...profileQueries.fetch(), + initialData: { + username: '', + userProfile: { + realName: '' + }, + studentId: '', + major: '' + }, + retry: false + }) - const [majorValue, setMajorValue] = useState( - defaultProfileValues.major || '' - ) - //const [isLoading, setIsLoading] = useState(false) + const [majorValue, setMajorValue] = useState(defaultProfileValues.major) const { register, @@ -106,7 +112,6 @@ export default function Page() { const saveAbleUpdateNow = Boolean(studentId) && majorValue !== 'none' && !errors.studentId - // 무한 렌더링.. useEffect(() => { if (isPasswordsMatch) { setValue('newPassword', newPassword) @@ -114,29 +119,16 @@ export default function Page() { } }, [isPasswordsMatch, newPassword, confirmPassword, setValue]) - const updateUserProfile = useUpdateUserProfile() - - const onSubmit = async (data: SettingsFormat) => { - try { - 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 - } - - await updateUserProfile.mutateAsync(updatePayload) + 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(() => { @@ -146,13 +138,29 @@ export default function Page() { window.location.reload() } }, 1500) - } catch (error) { - console.error(error) - toast.error('Failed to update your information, Please try again') - setTimeout(() => { - 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 = ( From 455cb2369c6bdb58347c8cfe9b030bceb7f17712 Mon Sep 17 00:00:00 2001 From: Kimhyojung0810 Date: Mon, 20 Jan 2025 02:11:05 +0900 Subject: [PATCH 6/7] refactor(fe): fix build error --- .../app/(client)/(main)/settings/_components/context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/app/(client)/(main)/settings/_components/context.ts b/apps/frontend/app/(client)/(main)/settings/_components/context.ts index a7fb9cd10c..8ad3753701 100644 --- a/apps/frontend/app/(client)/(main)/settings/_components/context.ts +++ b/apps/frontend/app/(client)/(main)/settings/_components/context.ts @@ -38,7 +38,7 @@ export type SettingsContextType = { majorState: MajorState formState: FormState updateNow: boolean - //isLoading: boolean + isLoading: boolean } const SettingsContext = createContext( From 9ac9c5adc6e5b860534fd793500926fdc98a394d Mon Sep 17 00:00:00 2001 From: Kimhyojung0810 Date: Mon, 20 Jan 2025 02:11:40 +0900 Subject: [PATCH 7/7] refactor(fe): fix build error and check major api error --- apps/frontend/app/(client)/(main)/settings/page.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/frontend/app/(client)/(main)/settings/page.tsx b/apps/frontend/app/(client)/(main)/settings/page.tsx index 5ca271cc3a..1c9efc4203 100644 --- a/apps/frontend/app/(client)/(main)/settings/page.tsx +++ b/apps/frontend/app/(client)/(main)/settings/page.tsx @@ -40,7 +40,7 @@ export default function Page() { const router = useRouter() const bypassConfirmation = useRef(false) - const { data: defaultProfileValues } = useQuery({ + const { data: defaultProfileValues, isLoading } = useQuery({ ...profileQueries.fetch(), initialData: { username: '', @@ -55,6 +55,13 @@ export default function Page() { const [majorValue, setMajorValue] = useState(defaultProfileValues.major) + useEffect(() => { + if (!isLoading && defaultProfileValues.major) { + console.log('Setting majorValue:', defaultProfileValues.major) + setMajorValue(defaultProfileValues.major) + } + }, [defaultProfileValues.major, isLoading]) + const { register, handleSubmit, @@ -186,6 +193,7 @@ export default function Page() { const settingsContextValue = { defaultProfileValues, + isLoading, passwordState: { passwordShow, setPasswordShow,