diff --git a/src/Router.tsx b/src/Router.tsx index 9882279d..ff5c72c5 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -95,7 +95,7 @@ const router = createBrowserRouter([ }, { path: 'seniorOnboarding', - element: , + element: , children: [ { index: true, diff --git a/src/pages/home/HomePage.tsx b/src/pages/home/HomePage.tsx index 0226852e..b71f826f 100644 --- a/src/pages/home/HomePage.tsx +++ b/src/pages/home/HomePage.tsx @@ -9,7 +9,12 @@ const HomePage = () => { useEffect(() => { if (token && role) { - navigate(role === 'SENIOR' ? '/promiseList' : '/juniorPromise'); + // 온보딩 완료 후 이탈한 선배 (프로필 등록 안 한 선배) + if (role === 'SENIOR_PENDING') { + navigate('/seniorProfile'); + } else { + navigate(role === 'SENIOR' ? '/promiseList' : '/juniorPromise'); + } } else { navigate('/join'); } diff --git a/src/pages/join/components/Button.tsx b/src/pages/join/components/Button.tsx index 3f5f28d8..d1dd40ad 100644 --- a/src/pages/join/components/Button.tsx +++ b/src/pages/join/components/Button.tsx @@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom'; const JoinButton = () => { const navigate = useNavigate(); const handleSeniorClick = () => { - navigate('/signup', { state: { role: 'SENIOR' } }); + navigate('/signup', { state: { role: 'SENIOR_PENDING' } }); }; const handleJuniorClick = () => { diff --git a/src/pages/juniorPromiseRequest/hooks/queries.ts b/src/pages/juniorPromiseRequest/hooks/queries.ts index 4ea3c170..9521e5fc 100644 --- a/src/pages/juniorPromiseRequest/hooks/queries.ts +++ b/src/pages/juniorPromiseRequest/hooks/queries.ts @@ -24,9 +24,10 @@ export const usePostAppointment = (onSuccess?: () => void, onError?: (error: str }); }; -export const useSeniorTimeQuery = (seniorId: number) => { +export const useSeniorTimeQuery = (seniorId: number, isJuniorRequest: boolean) => { return useQuery({ queryKey: [QUERY_KEYS.getSeniorTime, seniorId], queryFn: () => getSeniorTimeAxios(seniorId), + enabled: !!isJuniorRequest, }); }; diff --git a/src/pages/login/SignupPage.tsx b/src/pages/login/SignupPage.tsx index 3c612986..0d89281c 100644 --- a/src/pages/login/SignupPage.tsx +++ b/src/pages/login/SignupPage.tsx @@ -9,11 +9,11 @@ import { JoinHbBgSvg, JoinSbBgSvg } from '@assets/svgs'; const SignupPage = () => { const role = useLocation().state.role; - const isSenior = role === 'SENIOR'; + const isSenior = role === 'SENIOR_PENDING'; return ( <> - {role === 'SENIOR' ? : } + {isSenior ? : } 반가워요! @@ -35,7 +35,7 @@ const SignupPage = () => { )} - {role === 'SENIOR' ? : } + {isSenior ? : } googleLogin(role)}> 구글로 시작하기 diff --git a/src/pages/login/hooks/useGoogleLoginMutation.ts b/src/pages/login/hooks/useGoogleLoginMutation.ts index e11b8699..ca7ed4a4 100644 --- a/src/pages/login/hooks/useGoogleLoginMutation.ts +++ b/src/pages/login/hooks/useGoogleLoginMutation.ts @@ -1,7 +1,7 @@ import { useMutation } from '@tanstack/react-query'; import { loginAxios } from '../apis/loginAxios'; import { useNavigate } from 'react-router-dom'; -import { clearStorage, setRole, setToken } from '@utils/storage'; +import { clearStorage, setRole, setToken, setSeniorNickname } from '@utils/storage'; interface useGoogleLoginPropType { role?: string; @@ -14,15 +14,27 @@ const useGoogleLoginMutation = ({ role }: useGoogleLoginPropType) => { setToken(data.data.data.accessToken); const responseRole = data.data.data.role; + + // 로그인 (이미 가입된 회원) + // 서버에서 받아오는 role if (responseRole) { - // 로그인 (이미 가입된 회원) setRole(responseRole); - navigate(responseRole === 'SENIOR' ? '/promiseList' : '/juniorPromise'); - } else if (role) { + // 온보딩 완료 후 이탈한 선배 경우 + if (responseRole === 'SENIOR_PENDING') { + setSeniorNickname(data.data.data.nickname); + navigate('/seniorProfile'); + // 가입 완료된 경우 + } else { + navigate(responseRole === 'SENIOR' ? '/promiseList' : '/juniorPromise'); + } + // 회원가입 - navigate(role === 'SENIOR' ? '/seniorOnboarding' : '/juniorOnboarding'); - } else { + // join 페이지에서 navigation state로 받아온 role + } else if (role) { + navigate(role === 'SENIOR_PENDING' ? '/seniorOnboarding' : '/juniorOnboarding'); + // 존재하지 않는 계정으로 로그인을 시도했을 경우 + } else { console.error('🔴 존재하지 않는 계정'); alert('존재하지 않는 계정이예요. 회원가입을 진행해주세요.'); navigate('/'); diff --git a/src/pages/login/utils/googleLogin.ts b/src/pages/login/utils/googleLogin.ts index d74be3a8..abd480ef 100644 --- a/src/pages/login/utils/googleLogin.ts +++ b/src/pages/login/utils/googleLogin.ts @@ -1,4 +1,4 @@ -const googleLogin = (role?: 'SENIOR' | 'JUNIOR') => { +const googleLogin = (role?: 'SENIOR' | 'JUNIOR' | 'SENIOR_PENDING') => { window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${role ? `state=${role}&` : ''}client_id=${import.meta.env.VITE_APP_GOOGLE_CLIENT_ID}&redirect_uri=${import.meta.env.VITE_APP_GOOGLE_AUTH_REDIRECT_URI}&response_type=code&scope=email`; }; diff --git a/src/pages/onboarding/components/Layout.tsx b/src/pages/onboarding/components/Layout.tsx index c40721f3..ae4cb6c2 100644 --- a/src/pages/onboarding/components/Layout.tsx +++ b/src/pages/onboarding/components/Layout.tsx @@ -9,11 +9,11 @@ import { Outlet, useLocation, useNavigate } from 'react-router-dom'; import { useEffect, useState } from 'react'; import { JoinPropType } from '../type'; -const Layout = ({ userRole }: { userRole: 'SENIOR' | 'JUNIOR' }) => { +const Layout = ({ userRole }: { userRole: 'JUNIOR' | 'SENIOR_PENDING' }) => { const location = useLocation(); const navigate = useNavigate(); const step = +location.pathname.slice(18); - const onboardingStep = userRole === 'SENIOR' ? SENIOR_ONBOARDING_STEPS : JUNIOR_ONBOARDING_STEPS; + const onboardingStep = userRole === 'SENIOR_PENDING' ? SENIOR_ONBOARDING_STEPS : JUNIOR_ONBOARDING_STEPS; const { title, description } = onboardingStep[step ? step - 1 : 0]; const GROUP_STEP = convertToGroupStep(userRole, step); @@ -31,7 +31,7 @@ const Layout = ({ userRole }: { userRole: 'SENIOR' | 'JUNIOR' }) => { useEffect(() => { // 리로드 시 1단계로 back if (location.pathname.slice(-1) !== '1') { - userRole === 'SENIOR' ? navigate('/seniorOnboarding/1') : navigate('/juniorOnboarding/1'); + userRole === 'SENIOR_PENDING' ? navigate('/seniorOnboarding/1') : navigate('/juniorOnboarding/1'); } }, []); @@ -45,7 +45,7 @@ const Layout = ({ userRole }: { userRole: 'SENIOR' | 'JUNIOR' }) => { step === 1 ? navigate('/') : navigate(-1); }} /> - + diff --git "a/src/pages/onboarding/components/commonOnboarding/Step\353\262\210\355\230\270\354\236\205\353\240\245.tsx" "b/src/pages/onboarding/components/commonOnboarding/Step\353\262\210\355\230\270\354\236\205\353\240\245.tsx" index 4a207da4..431f983d 100644 --- "a/src/pages/onboarding/components/commonOnboarding/Step\353\262\210\355\230\270\354\236\205\353\240\245.tsx" +++ "b/src/pages/onboarding/components/commonOnboarding/Step\353\262\210\355\230\270\354\236\205\353\240\245.tsx" @@ -16,10 +16,11 @@ import { SuccessImg } from '@assets/images'; import { JoinContextType } from '@pages/onboarding/type'; import useJoinQuery from '@pages/onboarding/hooks/useJoinQuery'; import googleLogin from '@pages/login/utils/googleLogin'; +import { setSeniorNickname } from '@utils/storage'; const Step번호입력 = () => { - const { data, setData } = useOutletContext(); - const mutate = useJoinQuery(); + const { data: contextData, setData } = useOutletContext(); + const joinMutate = useJoinQuery(); const { pathname } = useLocation(); const navigate = useNavigate(); @@ -43,7 +44,7 @@ const Step번호입력 = () => { })); }; - const [phoneNumber, setPhoneNumber] = useState(data.phoneNumber); + const [phoneNumber, setPhoneNumber] = useState(contextData.phoneNumber); const [verificationCode, setVerificationCode] = useState(''); const [isDoneModalOpen, setIsDoneModalOpen] = useState(false); @@ -70,6 +71,7 @@ const Step번호입력 = () => { return () => clearInterval(id); }, [isActive, timeLeft]); + // 인증번호 전송 const handleClickSend = () => { if (isActive) { setValidCodeError(false); @@ -114,16 +116,14 @@ const Step번호입력 = () => { setIsAlreadyModalOpen(type); }; + // SENIOR_PENDING - 프로필 등록 이동 + // JUNIOR - 온보딩 다음 단계로 이동 const handleClickLink = () => { if (pathname.includes('senior')) { - mutate.mutate(data, { - onSuccess: (res) => { - navigate('/seniorProfile', { - state: { - seniorId: res.data.data.seniorId, - nickname: data.nickname, - }, - }); + joinMutate.mutate(contextData, { + onSuccess: () => { + setSeniorNickname(contextData.nickname); + navigate('/seniorProfile'); }, onError: (err) => { console.log(err); @@ -132,7 +132,8 @@ const Step번호입력 = () => { } else navigate('/juniorOnboarding/4'); }; - const handleClickButton = () => { + // 인증확인 버튼 + const handleClickVerifyButton = () => { verifycodeMutation.mutate( { phoneNumber: phoneNumber, verificationCode }, { @@ -200,7 +201,7 @@ const Step번호입력 = () => { 0 && verificationCode.length == 4 && !isError.isValidCodeError} - onClick={handleClickButton} + onClick={handleClickVerifyButton} /> diff --git a/src/pages/onboarding/hooks/useJoinQuery.ts b/src/pages/onboarding/hooks/useJoinQuery.ts index 5c286214..aeef96e7 100644 --- a/src/pages/onboarding/hooks/useJoinQuery.ts +++ b/src/pages/onboarding/hooks/useJoinQuery.ts @@ -1,13 +1,14 @@ import { useMutation } from '@tanstack/react-query'; import { joinAxios } from '../apis/joinAxios'; import { JoinPropType } from '../type'; -import { setRole } from '@utils/storage'; +import { setRole, setSeniorId } from '@utils/storage'; const useJoinQuery = () => { const mutation = useMutation({ mutationFn: (requestBody: JoinPropType) => joinAxios(requestBody), onSuccess: (data) => { - setRole(data.data.data.userType); + setRole(data.data.data.role); + setSeniorId(data.data.data.seniorId + ''); }, onError: (error) => { console.log('🔴 join patch Error: ', error); diff --git a/src/pages/onboarding/type.ts b/src/pages/onboarding/type.ts index ceb423f3..acc40b21 100644 --- a/src/pages/onboarding/type.ts +++ b/src/pages/onboarding/type.ts @@ -9,7 +9,7 @@ export interface BizInfoType { } export interface JoinPropType { - role: 'SENIOR' | 'JUNIOR'; + role: 'SENIOR' | 'JUNIOR' | 'SENIOR_PENDING'; isSubscribed: boolean[]; nickname: string; isNicknameValid?: boolean; diff --git a/src/pages/onboarding/utils/convertToGroupStep.ts b/src/pages/onboarding/utils/convertToGroupStep.ts index a1d26e5d..3c0236cd 100644 --- a/src/pages/onboarding/utils/convertToGroupStep.ts +++ b/src/pages/onboarding/utils/convertToGroupStep.ts @@ -1,5 +1,5 @@ -const convertToGroupStep = (role: 'SENIOR' | 'JUNIOR', step: number): number => { - if (role === 'SENIOR') { +const convertToGroupStep = (role: 'JUNIOR' | 'SENIOR_PENDING', step: number): number => { + if (role === 'SENIOR_PENDING') { if (step === 1) return 1; if (step === 2) return 2; if (step <= 6 && step >= 1) return 3; diff --git a/src/pages/seniorProfile/SeniorProfilePage.tsx b/src/pages/seniorProfile/SeniorProfilePage.tsx index e5fdf1bb..77a4baa2 100644 --- a/src/pages/seniorProfile/SeniorProfilePage.tsx +++ b/src/pages/seniorProfile/SeniorProfilePage.tsx @@ -14,19 +14,21 @@ import { SENIOR_PROFILE_STEPS } from './constants'; import { Header } from '../../components/commons/Header'; import ProgressBar from '../../components/commons/ProgressBar'; import theme from '../../styles/theme'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; +import { getSeniorId, getSeniorNickname } from '@utils/storage'; const SeniorProfilePage = () => { const [step, setStep] = useState(0); const [profile, setProfile] = useState(seniorProfileInitial); - const location = useLocation(); + const navigate = useNavigate(); - const { seniorId, nickname } = location.state || {}; + const nickname = getSeniorNickname(); + const seniorId = getSeniorId() ?? ''; const userName = step >= 2 && step <= 4 ? nickname : ''; useEffect(() => { - if (!seniorId || !nickname) navigate('/'); - }, [seniorId, nickname, navigate]); + if (seniorId === '' || !nickname) navigate('/'); + }, [nickname, seniorId]); const getComponent = () => { switch (step) { diff --git a/src/pages/seniorProfile/components/Example.tsx b/src/pages/seniorProfile/components/Example.tsx index 9c185da3..6c9d2adc 100644 --- a/src/pages/seniorProfile/components/Example.tsx +++ b/src/pages/seniorProfile/components/Example.tsx @@ -15,9 +15,9 @@ import { useNavigate } from 'react-router-dom'; const Example = ({ setStep }: { setStep: React.Dispatch> }) => { const navigate = useNavigate(); const [seniorId, setSeniorId] = useState(0); - const { data: data1, isLoading: isLoading1, isError: isError1 } = useSeniorCardQuery('1'); - const { data: data2, isLoading: isLoading2, isError: isError2 } = useSeniorCardQuery('2'); - const { data: data3, isLoading: isLoading3, isError: isError3 } = useSeniorCardQuery('3'); + const { data: data1, isLoading: isLoading1, isError: isError1 } = useSeniorCardQuery('1', true); + const { data: data2, isLoading: isLoading2, isError: isError2 } = useSeniorCardQuery('2', true); + const { data: data3, isLoading: isLoading3, isError: isError3 } = useSeniorCardQuery('3', true); const dummayData = [data1, data2, data3]; diff --git a/src/pages/seniorProfile/components/Init.tsx b/src/pages/seniorProfile/components/Init.tsx index 932590ae..5c58bf92 100644 --- a/src/pages/seniorProfile/components/Init.tsx +++ b/src/pages/seniorProfile/components/Init.tsx @@ -44,6 +44,7 @@ const SeniorRefreshImg = styled.img` position: absolute; top: 50%; left: 50%; + width: 25rem; height: 34rem; transform: translate(-40%, -40%); diff --git a/src/pages/seniorProfile/components/preView/index.tsx b/src/pages/seniorProfile/components/preView/index.tsx index 0474ceeb..02ee8976 100644 --- a/src/pages/seniorProfile/components/preView/index.tsx +++ b/src/pages/seniorProfile/components/preView/index.tsx @@ -14,6 +14,7 @@ import { dayOfWeekTimeList, seniorProfileRegisterType } from '@pages/seniorProfi import { deleteProfileField } from '@pages/seniorProfile/utils/deleteProfileField'; import { weekToDay } from '@pages/seniorProfile/utils/weekToDay'; import { useNavigate } from 'react-router-dom'; +import { setRole } from '@utils/storage'; interface preViewPropType { seniorId: string; @@ -25,20 +26,26 @@ interface preViewPropType { } const PreView = ({ seniorId, profile, setStep, variant = 'default' }: preViewPropType) => { - const { data: cardData, error: cardDataError, isLoading: isCardDataLoading } = useSeniorCardQuery(seniorId); + // 선배 카드 정보 조회 (온보딩 정보) + const { data: cardData, error: cardDataError, isLoading: isCardDataLoading } = useSeniorCardQuery(seniorId, true); + // 선배 상세 프로필 조회 (프로필 정보) const { data: profileData, error: profileDataError, isLoading: isProfileDataLoading, - } = useGetSeniorProfileQuery(seniorId); + } = useGetSeniorProfileQuery(seniorId, variant === 'secondary'); + // 선배 선호 시간대 조회 const { data: secondaryPreferredTimeList, isError: secondTimeListError, isLoading: isSecondTimeListLoading, - } = useSeniorTimeQuery(+seniorId); + } = useSeniorTimeQuery(+seniorId, variant === 'secondary'); + const navigate = useNavigate(); const isRegister = variant === 'default'; + // profile : 선배가 프로필 등록할 때 넘어온 값 + // profileData : 이미 가입된 선배의 서버값 (후배가 카드 클릭시 받아오는 값) const career = (isRegister ? profile?.career : profileData?.career) + ''; const award = (isRegister ? profile?.award : profileData?.award) + ''; const catchphrase = (isRegister ? profile?.catchphrase : profileData?.catchphrase) + ''; @@ -47,6 +54,7 @@ const PreView = ({ seniorId, profile, setStep, variant = 'default' }: preViewPro isRegister ? profile && weekToDay(profile.isDayOfWeek, profile.preferredTimeList) : secondaryPreferredTimeList ) as dayOfWeekTimeList; + // 선배 프로필 등록 const mutation = useSeniorProfileHook(); const handleRegisterClick = () => { mutation.mutate( @@ -60,6 +68,11 @@ const PreView = ({ seniorId, profile, setStep, variant = 'default' }: preViewPro { onSuccess: () => { setStep && setStep((prev) => prev + 1); + // 온보딩 + 프로필 등록 완료 + // 선배 role SENIOR_PENDING -> SENIOR로 변경 + // 선배 닉네임 local storage에서 제거 + setRole('SENIOR'); + localStorage.removeItem('seniorNickname'); }, } ); @@ -69,7 +82,7 @@ const PreView = ({ seniorId, profile, setStep, variant = 'default' }: preViewPro cardDataError || (!isRegister && profileDataError) || (!isRegister && secondTimeListError) || - (!isCardDataLoading && !cardData) || + (!isRegister && !isCardDataLoading && !cardData) || (!isRegister && !isProfileDataLoading && !profileData) || (!isRegister && !isSecondTimeListLoading && !secondaryPreferredTimeList) ) { diff --git a/src/pages/seniorProfile/hooks/useGetSeniorProfileQuery.ts b/src/pages/seniorProfile/hooks/useGetSeniorProfileQuery.ts index e522edf7..8ff48857 100644 --- a/src/pages/seniorProfile/hooks/useGetSeniorProfileQuery.ts +++ b/src/pages/seniorProfile/hooks/useGetSeniorProfileQuery.ts @@ -1,11 +1,11 @@ -import { getSeniorProfileAxios } from "@pages/seniorProfile/apis/getSeniorProfileAxios"; -import { SeniorProfileAPIResType } from "@pages/seniorProfile/types"; -import { useQuery } from "@tanstack/react-query"; +import { getSeniorProfileAxios } from '@pages/seniorProfile/apis/getSeniorProfileAxios'; +import { SeniorProfileAPIResType } from '@pages/seniorProfile/types'; +import { useQuery } from '@tanstack/react-query'; -export const useGetSeniorProfileQuery = (seniorId: string) => { +export const useGetSeniorProfileQuery = (seniorId: string, isJuniorRequest: boolean) => { return useQuery({ queryKey: ['seniorProfile', seniorId], - queryFn: () => getSeniorProfileAxios(seniorId).then(response => response.data.data), - }) + queryFn: () => getSeniorProfileAxios(seniorId).then((response) => response.data.data), + enabled: !!isJuniorRequest, + }); }; - diff --git a/src/pages/seniorProfile/hooks/useSeniorCardQuery.ts b/src/pages/seniorProfile/hooks/useSeniorCardQuery.ts index 1895cbbf..fb5b3a03 100644 --- a/src/pages/seniorProfile/hooks/useSeniorCardQuery.ts +++ b/src/pages/seniorProfile/hooks/useSeniorCardQuery.ts @@ -1,10 +1,11 @@ -import { seniorCardAxios } from "@pages/seniorProfile/apis/seniorCardAxios" -import { SeniorCardAPIResType } from "@pages/seniorProfile/types"; -import { useQuery } from "@tanstack/react-query" +import { seniorCardAxios } from '@pages/seniorProfile/apis/seniorCardAxios'; +import { SeniorCardAPIResType } from '@pages/seniorProfile/types'; +import { useQuery } from '@tanstack/react-query'; -export const useSeniorCardQuery = (seniorId: string) => { +export const useSeniorCardQuery = (seniorId: string, isJuniorRequest: boolean) => { return useQuery({ queryKey: ['seniorCard', seniorId], - queryFn: () => seniorCardAxios(seniorId).then(response => response.data.data), - }) + queryFn: () => seniorCardAxios(seniorId).then((response) => response.data.data), + enabled: !!isJuniorRequest, + }); }; diff --git a/src/utils/storage.ts b/src/utils/storage.ts index 78b079a2..49b7ca65 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -17,4 +17,23 @@ export const getRole = () => { export const clearStorage = () => { localStorage.removeItem('seonyakToken'); localStorage.removeItem('seonyakRole'); + localStorage.removeItem('seniorNickname'); + localStorage.removeItem('seniorId'); }; + +// 온보딩 완료 후 프로필 등록 안 하고 이탈한 선배 정보 저장 +export const setSeniorNickname = (nickname: string) => { + localStorage.setItem('seniorNickname', nickname); +}; + +export const getSeniorNickname = () => { + return localStorage.getItem('seniorNickname'); +}; + +export const setSeniorId = (id: string) => { + localStorage.setItem('seniorId', id); +} + +export const getSeniorId = () => { + return localStorage.getItem('seniorId'); +} \ No newline at end of file