Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ Fix ] 약속 리스트 페이지 에러 처리 #287

Merged
merged 10 commits into from
Oct 18, 2024
79 changes: 62 additions & 17 deletions src/pages/promiseDetail/PromiseDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { extractMonthAndDay } from '@pages/promiseList/utils/extractMonthAndDay';
import { ModalRejectImg, ModalAcceptImg } from '@assets/svgs';
import Loading from '@components/commons/Loading';
import ErrorPage from '@pages/errorPage/ErrorPage';

const PromiseDetail = () => {
const location = useLocation();
Expand All @@ -29,8 +30,17 @@ const PromiseDetail = () => {
const { tap, myNickname, appointmentId } = location.state;
const userRole = 'SENIOR';

const { juniorInfo, seniorInfo, timeList1, timeList2, timeList3, topic, personalTopic, isSuccess, isLoading } =
useGetPromiseDetail(appointmentId);
const {
juniorInfo,
seniorInfo,
timeList1,
timeList2,
timeList3,
topic,
personalTopic,
isLoading,
isError: getPromiseDetailError,
} = useGetPromiseDetail(appointmentId);

// 기본뷰 / 거절뷰
const [viewType, setViewType] = useState('DEFAULT');
Expand All @@ -54,7 +64,11 @@ const PromiseDetail = () => {
};

// 선배 약속 수락
const { mutate: patchSeniorAccept } = usePatchSeniorAccept(() => handleModalOpen(true));
const {
mutate: patchSeniorAccept,
isPending: isPatchSeniorAcceptPending,
isError: patchSeniorAcceptError,
} = usePatchSeniorAccept(() => handleModalOpen(true));

// 구글밋 링크 patch 콜백 함수
const handleSuccessCallback = (link: string) => {
Expand All @@ -73,7 +87,11 @@ const PromiseDetail = () => {
};

// 구글밋 링크 받아오기(post) 후 약속 수락 patch
const { mutate: postGoogleMeetLink } = usePostGoogleMeetLink((link) => {
const {
mutate: postGoogleMeetLink,
isPending: isPostGoogleMeetLinkPending,
isError: postGoogleMeetLinkError,
} = usePostGoogleMeetLink((link) => {
handleSuccessCallback(link);
});

Expand All @@ -94,7 +112,9 @@ const PromiseDetail = () => {
};

// 선배 약속 거절
const { mutate: patchSeniorReject } = usePatchSeniorReject(() => handleModalOpen(true));
const { mutate: patchSeniorReject, isError: patchSeniorRejectError } = usePatchSeniorReject(() =>
handleModalOpen(true)
);
const handleRejectBtn = () => {
patchSeniorReject({
appointmentId: appointmentId,
Expand All @@ -112,7 +132,11 @@ const PromiseDetail = () => {
window.open(link, '_blank');
};

useGetGoogleMeetLink(appointmentId, isEnterBtnClicked, handleClickEnterBtn);
const { isError: getGoogleMeetLinkError } = useGetGoogleMeetLink(
appointmentId,
isEnterBtnClicked,
handleClickEnterBtn
);

const handleBottomSheetOpen = () => {
setIsBottomSheetOpen(true);
Expand All @@ -133,16 +157,27 @@ const PromiseDetail = () => {
const handleRejectDetailReason = (detailReason: string) => {
setRejectDetail(detailReason);
};
// 훅을 조건문 밖에서 호출

const countdown = useCountdown(timeList1?.date, timeList1?.startTime);
const dateInfo = extractMonthAndDay(timeList1?.date + '');

if (isLoading) {
if (tap === undefined || myNickname === undefined) {
navigate('/promiseList');
}

if (isLoading || isPatchSeniorAcceptPending || isPostGoogleMeetLinkPending) {
return <Loading />; // 로딩 중일 때 표시
}

if (!isSuccess || !timeList1) {
return <div>데이터 없음</div>; // 데이터가 없을 때 표시
if (
getPromiseDetailError ||
!timeList1 ||
postGoogleMeetLinkError ||
patchSeniorAcceptError ||
patchSeniorRejectError ||
getGoogleMeetLinkError
) {
return <ErrorPage />;
}

// 조건부로 훅의 결과를 처리
Expand All @@ -153,11 +188,13 @@ const PromiseDetail = () => {
<>
<Header
LeftSvg={ArrowLeftIc}
onClickLeft={() => navigate(`/promiseList`, {
state: {
prevTap: tap
}
})}
onClickLeft={() =>
navigate(`/promiseList`, {
state: {
prevTap: tap,
},
})
}
title={viewType === 'DEFAULT' ? '자세히 보기' : '거절하기'}
/>
<hr />
Expand Down Expand Up @@ -289,11 +326,19 @@ const PromiseDetail = () => {
</Wrapper>

{viewType === 'DECLINE' ? (
<AutoCloseModal text="선약이 거절되었어요" showModal={isModalOpen} handleShowModal={handleModalOpen} path="/promiseList">
<AutoCloseModal
text="선약이 거절되었어요"
showModal={isModalOpen}
handleShowModal={handleModalOpen}
path="/promiseList">
<ModalRejectImg />
</AutoCloseModal>
) : (
<AutoCloseModal text="선약이 수락되었어요" showModal={isModalOpen} handleShowModal={handleModalOpen} path="/promiseList">
<AutoCloseModal
text="선약이 수락되었어요"
showModal={isModalOpen}
handleShowModal={handleModalOpen}
path="/promiseList">
<ModalAcceptImg />
</AutoCloseModal>
)}
Copy link
Member

@j-nary j-nary Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viewType에 따라 text랑 Img만 조건부 넣어주면 더 깔끔해질 것 같아요!

<AutoCloseModal
  text={viewType === 'DECLINE' ? "거절" : "수락"}
  ...
/>
{viewType === 'DECLINE' ? <RejectImg/> : <AcceptImg/>}

Expand Down
29 changes: 22 additions & 7 deletions src/pages/promiseDetail/PromiseDetailPageJunior.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import { useState } from 'react';
import PreView from '@pages/seniorProfile/components/preView';
import Loading from '@components/commons/Loading';
import { useGetGoogleMeetLink } from '@pages/promiseList/hooks/queries';
import ErrorPage from '@pages/errorPage/ErrorPage';

const PromiseDetailPageJunior = () => {
// 라우터 이동할 때 location으로 약속id, 눌린 탭 상태값(pending, sheduled, ..) 받아와야함
const navigate = useNavigate();
const location = useLocation();
const { tap, myNickname, appointmentId, seniorId } = location.state;
Expand All @@ -28,25 +28,40 @@ const PromiseDetailPageJunior = () => {
navigate('/juniorPromise');
};

useGetGoogleMeetLink(appointmentId, isEnterBtnClicked, handleClickEnterBtn);
const { isError: getGoogleMeetLinkError } = useGetGoogleMeetLink(
appointmentId,
isEnterBtnClicked,
handleClickEnterBtn
);

const handleSetIsDetailClicked = (type: boolean) => {
setIsDetailClicked(type);
};

const { juniorInfo, seniorInfo, timeList1, topic, personalTopic, isSuccess, isLoading } =
useGetPromiseDetail(appointmentId);
const {
juniorInfo,
seniorInfo,
timeList1,
topic,
personalTopic,
isLoading,
isError: getPromiseDetailError,
} = useGetPromiseDetail(appointmentId);

const countdown = useCountdown(timeList1?.date, timeList1?.startTime);

const { diffText, diff } = countdown;

if (tap === undefined || myNickname === undefined || appointmentId === undefined || seniorId === undefined) {
navigate('/promiseList');
}

if (isLoading) {
return <Loading />; // 로딩 중일 때 표시
return <Loading />;
}

if (!isSuccess || !timeList1) {
return <div>데이터 없음</div>; // 데이터가 없을 때 표시
if (getPromiseDetailError || !timeList1 || getGoogleMeetLinkError) {
return <ErrorPage />;
}

const handleClickBackArrow = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/promiseDetail/apis/postGoogleMeetLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export const postGoogleMeetLink = async () => {
// console.log(response.data.data.googleMeet);
return response.data.data.googleMeet;
} catch (err) {
console.log(err);
console.error('구글밋 회의실 개설 에러 ', err);
}
};
15 changes: 8 additions & 7 deletions src/pages/promiseDetail/hooks/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const usePatchSeniorReject = (onSuccessCallback?: () => void) => {
const queryClient = useQueryClient();

const navigate = useNavigate();
const { mutate, data } = useMutation({
const { mutate, data, isError } = useMutation({
mutationKey: [QUERY_KEY_PROMISE_DETAIL.patchSeniorReject],
mutationFn: ({ appointmentId, rejectReason, rejectDetail }: patchSeniorRejectRequestType) =>
patchSeniorReject({ appointmentId, rejectReason, rejectDetail }),
Expand All @@ -34,12 +34,12 @@ export const usePatchSeniorReject = (onSuccessCallback?: () => void) => {
},
});

return { mutate, data };
return { mutate, data, isError };
};

// 구글밋 링크 받기
export const usePostGoogleMeetLink = (onSuccessCallback?: (data: string) => void) => {
const { mutate, data } = useMutation({
const { mutate, data, isPending, isError } = useMutation({
mutationKey: [QUERY_KEY_PROMISE_DETAIL.postGoogleMeetLink],
mutationFn: postGoogleMeetLink,
onSuccess: (data) => {
Expand All @@ -49,15 +49,15 @@ export const usePostGoogleMeetLink = (onSuccessCallback?: (data: string) => void
},
});

return { mutate, data };
return { mutate, data, isPending, isError };
};

// 선배 약속 수락
export const usePatchSeniorAccept = (onSuccessCallback?: () => void) => {
const navigate = useNavigate();
const queryClient = useQueryClient();

const { mutate, data } = useMutation({
const { mutate, data, isPending, isError } = useMutation({
mutationKey: [QUERY_KEY_PROMISE_DETAIL.patchSeniorAccept],
mutationFn: ({ appointmentId, googleMeetLink, timeList }: patchSeniorAcceptRequestType) =>
patchSeniorAccept({ appointmentId, googleMeetLink, timeList }),
Expand All @@ -74,11 +74,11 @@ export const usePatchSeniorAccept = (onSuccessCallback?: () => void) => {
},
});

return { mutate, data };
return { mutate, data, isPending, isError };
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hook 폴더로 빼서 파일 분리 해주면 좋을 것 같아요!


export const useGetPromiseDetail = (appointmentId: number) => {
const { data, isSuccess, isLoading } = useQuery({
const { data, isSuccess, isLoading, isError } = useQuery({
queryKey: [QUERY_KEY_PROMISE_DETAIL.getPromiseDetail, appointmentId],
queryFn: () => getPromiseDetail(appointmentId),
});
Expand Down Expand Up @@ -116,5 +116,6 @@ export const useGetPromiseDetail = (appointmentId: number) => {
personalTopic,
isSuccess,
isLoading,
isError
};
};
5 changes: 3 additions & 2 deletions src/pages/promiseList/PromiseListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import Title from './components/Title';
import { useGetPromiseList } from './hooks/queries';
import Loading from '@components/commons/Loading';
import { getRole } from '@utils/storage';
import ErrorPage from '@pages/errorPage/ErrorPage';

const PromiseListPage = () => {
// 유저가 선배일 경우
const userRole = getRole() + '';

const { myNickname, pending, scheduled, past, isLoading } = useGetPromiseList();
const { myNickname, pending, scheduled, past, isLoading, isError } = useGetPromiseList();

if (isLoading) return <Loading />;
if (isError) return <ErrorPage />;

return (
<>
Expand Down
7 changes: 6 additions & 1 deletion src/pages/promiseList/components/ProfileContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,14 @@ const ProfileContainer = (props: ProfileContainerPropType) => {

// 상세 페이지 라우팅
const handleClickProfileContainer = (tap: string, userRole: string) => {
// [선배] 확정대기 - 약속 상세페이지
if (userRole === 'SENIOR' && tap === 'pending') {
navigate('/promiseList/promiseDetail', {
state: { tap: 'pending', myNickname: myNickname, appointmentId: profileCardData?.appointmentId },
});
}

// [후배] 확정대기 - 약속 상세페이지
if (userRole === 'JUNIOR' && tap === 'pending') {
navigate('/promiseList/promiseDetailJunior', {
state: {
Expand All @@ -64,12 +67,14 @@ const ProfileContainer = (props: ProfileContainerPropType) => {
});
}

// [선배] 예정약속, 가장가까운약속 - 약속 상세페이지
if (userRole === 'SENIOR' && (tap === 'scheduled' || tap === 'default') && detail !== 'detail') {
navigate('/promiseList/promiseDetail', {
state: { tap: 'scheduled', myNickname: myNickname, appointmentId: profileCardData?.appointmentId },
});
}

// [후배] 예정약속, 가장가까운약속 - 약속 상세페이지
if (userRole === 'JUNIOR' && (tap === 'scheduled' || tap === 'default') && detail !== 'detail') {
navigate('/promiseList/promiseDetailJunior', {
state: {
Expand All @@ -81,7 +86,7 @@ const ProfileContainer = (props: ProfileContainerPropType) => {
});
}

// 실제 선배 아이디로 연결 필요
// [후배] 약속 상세페이지 - 선배 상세페이지
if (
userRole === 'JUNIOR' &&
(tap === 'scheduled' || tap === 'default' || tap === 'pending') &&
Expand Down
5 changes: 3 additions & 2 deletions src/pages/promiseList/components/PromiseTap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ interface PromiseTapPropType {
const PromiseTap = (props: PromiseTapPropType) => {
const location = useLocation();
const navigate = useNavigate();
const [tap, setTap] = useState('pending');
const { userRole, pending, scheduled, past, myNickname } = props;

const { userRole, pending, scheduled, past, myNickname } = props;
const { prevTap } = location.state || {};

const [tap, setTap] = useState('pending');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4) 개인적인 생각으로 tap 이라는 변수명이 의도가 직관적으로 와닿지 않아서 appointmentStatus 등의 의미가 드러나는 이름을 사용하면 좋을 것 같습니다!


useEffect(() => {
if (prevTap && Object.keys(prevTap).length !== 0) {
setTap(prevTap);
Expand Down
11 changes: 10 additions & 1 deletion src/pages/promiseList/components/RecentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import PromiseTimerBtn from './PromiseTimerBtn';
import { profileCardDataType } from '../types/type';
import { useGetGoogleMeetLink } from '../hooks/queries';
import { useState } from 'react';
import ErrorPage from '@pages/errorPage/ErrorPage';

interface RecentCardPropType {
userRole: string;
Expand All @@ -24,14 +25,22 @@ const RecentCard = (props: RecentCardPropType) => {
setIsEnterBtnClicked(false);
};

useGetGoogleMeetLink(recentAppointment?.appointmentId, isEnterBtnClicked, handleClickEnterBtn);
const { isError: getGoogleMeetLinkError } = useGetGoogleMeetLink(
recentAppointment?.appointmentId,
isEnterBtnClicked,
handleClickEnterBtn
);

const handleClickUserGuide = () => {
userRole === 'SENIOR'
? window.open('https://cumbersome-cactus-843.notion.site/c5f4f494d3ee41c6836a9f4828a7bde6?pvs=4', '_blank')
: window.open('https://cumbersome-cactus-843.notion.site/d394be50d2b44a03878debd0e19bdb2f?pvs=4', '_blank');
};

if (getGoogleMeetLinkError) {
return <ErrorPage />;
}

return (
<Wrapper $userRole={userRole}>
<RecentNav>
Expand Down
Loading
Loading