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

[FE] feat: 받은 리뷰 정보를 가져오는 API 연동 #816

Merged
merged 7 commits into from
Oct 10, 2024
1 change: 1 addition & 0 deletions frontend/src/apis/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const REVIEW_GROUP_DATA_API_URL = `${serverUrl}/${VERSION2}/${REVIEW_GROU

const endPoint = {
postingReview: `${serverUrl}/${VERSION2}/reviews`,
gettingReviewInfoData: `${serverUrl}/${VERSION2}/reviews/summary`,
gettingDetailedReview: (reviewId: number) => `${DETAILED_REVIEW_API_URL}/${reviewId}`,
gettingDataToWriteReview: (reviewRequestCode: string) =>
`${REVIEW_WRITING_API_URL}/${REVIEW_WRITING_API_PARAMS.queryString.write}?${REVIEW_WRITING_API_PARAMS.queryString.reviewRequestCode}=${reviewRequestCode}`,
Expand Down
20 changes: 19 additions & 1 deletion frontend/src/apis/review.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DetailReviewData, ReviewList, ReviewWritingFormResult, ReviewWritingFormData } from '@/types';
import { DetailReviewData, ReviewList, ReviewWritingFormResult, ReviewWritingFormData, ReviewInfoData } from '@/types';

import createApiErrorMessage from './apiErrorMessageCreator';
import endPoint from './endpoints';
Expand Down Expand Up @@ -32,6 +32,24 @@ export const postReviewApi = async (formResult: ReviewWritingFormResult) => {
return;
};

// 받은 리뷰들에 대한 정보(프로젝트 이름, 리뷰이, 받은 리뷰 개수)
export const getReviewInfoDataApi = async () => {
const response = await fetch(endPoint.gettingReviewInfoData, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
});

if (!response.ok) {
throw new Error(createApiErrorMessage(response.status));
}

const data = await response.json();
return data as ReviewInfoData;
};

interface GetDetailedReviewApi {
reviewId: number;
}
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/ReviewCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Category } from '@/types';
import * as S from './styles';

interface ReviewCardProps {
projectName: string;
createdAt: string;
contentPreview: string;
categories: Category[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ export interface ReviewInfoSectionProps {
revieweeName: string;
isReviewList: boolean;
projectName: string;
reviewCount?: number;
totalReviewCount?: number;
}

const ReviewInfoSection = ({ projectName, revieweeName, reviewCount, isReviewList }: ReviewInfoSectionProps) => {
const ReviewInfoSection = ({ projectName, revieweeName, totalReviewCount, isReviewList }: ReviewInfoSectionProps) => {
const revieweeNameWithParticle = calculateParticle({
target: revieweeName,
particles: { withFinalConsonant: '이', withoutFinalConsonant: '가' },
});

const getReviewInfoMessage = () => {
return isReviewList
? `${revieweeNameWithParticle} 받은 ${reviewCount}개의 리뷰 목록이에요`
? `${revieweeNameWithParticle} 받은 ${totalReviewCount}개의 리뷰 목록이에요`
: `${revieweeNameWithParticle} 받은 리뷰를 질문별로 모아봤어요`;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as useReviewDisplayLayoutOptions } from './useReviewDisplayLayoutOptions/index';
export { default as useReviewInfoData } from './useReviewInfoData/index';
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useSuspenseQuery } from '@tanstack/react-query';

import { getReviewInfoDataApi } from '@/apis/review';
import { REVIEW_QUERY_KEY } from '@/constants';
import { ReviewInfoData } from '@/types';

const useReviewInfoData = () => {
const fetchReviewInfoData = async () => {
return await getReviewInfoDataApi();
};

const { data } = useSuspenseQuery<ReviewInfoData>({
queryKey: [REVIEW_QUERY_KEY.reviewInfoData],
queryFn: () => fetchReviewInfoData(),
staleTime: 60 * 60 * 1000,
});

return data;
};

export default useReviewInfoData;
19 changes: 9 additions & 10 deletions frontend/src/components/layouts/ReviewDisplayLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import { TopButton, OptionSwitch } from '@/components/common';
import { EssentialPropsWithChildren } from '@/types';

import ReviewInfoSection, { ReviewInfoSectionProps } from './components/ReviewInfoSection';
import useReviewDisplayLayoutOptions from './hooks/useReviewDisplayLayoutOptions';
import ReviewInfoSection from './components/ReviewInfoSection';
import { useReviewInfoData, useReviewDisplayLayoutOptions } from './hooks';
import * as S from './styles';

const ReviewDisplayLayout = ({
revieweeName,
projectName,
reviewCount,
isReviewList,
children,
}: EssentialPropsWithChildren<ReviewInfoSectionProps>) => {
interface ReviewDisplayLayoutProps {
isReviewList: boolean;
}

const ReviewDisplayLayout = ({ isReviewList, children }: EssentialPropsWithChildren<ReviewDisplayLayoutProps>) => {
const reviewDisplayLayoutOptions = useReviewDisplayLayoutOptions();
const { revieweeName, projectName, totalReviewCount } = useReviewInfoData();

return (
<S.ReviewDisplayLayout>
<S.Container>
<ReviewInfoSection
revieweeName={revieweeName}
projectName={projectName}
reviewCount={reviewCount}
totalReviewCount={totalReviewCount}
isReviewList={isReviewList}
/>
<OptionSwitch options={reviewDisplayLayoutOptions} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/constants/queryKey.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// TODO: 내용이 배열이 아니므로 단수형으로 수정하기
export const REVIEW_QUERY_KEY = {
detailedReview: 'detailedReview',
reviews: 'reviews',
writingReviewInfo: 'writingReviewInfo',
postReview: 'postReview',
reviewInfoData: 'reviewInfoData',
};

export const GROUP_QUERY_KEY = {
Expand Down
44 changes: 32 additions & 12 deletions frontend/src/mocks/handlers/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@ import {
REVIEW_QUESTION_DATA,
REVIEW_LIST,
MOCK_AUTH_TOKEN_NAME,
MOCK_REVIEW_INFO_DATA,
} from '../mockData';

export const PAGE = {
firstPageNumber: 1,
firstPageStartIndex: 0,
};

const getReviewInfoData = () =>
http.get(endPoint.gettingReviewInfoData, async ({ cookies }) => {
if (!cookies[MOCK_AUTH_TOKEN_NAME]) {
return HttpResponse.json({ error: '인증 관련 쿠키 없음' }, { status: 401 });
}

return HttpResponse.json(MOCK_REVIEW_INFO_DATA);
});

const getDetailedReview = () =>
http.get(new RegExp(`^${DETAILED_REVIEW_API_URL}/\\d+$`), async ({ request, cookies }) => {
// authToken 쿠키 확인
Expand All @@ -43,17 +53,21 @@ const getDetailedReview = () =>
});

const getDataToWriteReview = () =>
http.get(new RegExp(`^${REVIEW_WRITING_API_URL}`), async ({ request }) => {
//요청 url에서 reviewId, memberId 추출
const url = new URL(request.url);
const urlRequestCode = url.searchParams.get(REVIEW_WRITING_API_PARAMS.queryString.reviewRequestCode);

if (REVIEW_REQUEST_CODE === urlRequestCode) {
return HttpResponse.json(REVIEW_QUESTION_DATA);
}
return HttpResponse.json({ error: '잘못된 리뷰 작성 데이터 요청' }, { status: 404 });
});

http.get(
new RegExp(`^${REVIEW_WRITING_API_URL}/${REVIEW_WRITING_API_PARAMS.queryString.write}`),
async ({ request }) => {
//요청 url에서 reviewId, memberId 추출
const url = new URL(request.url);
const urlRequestCode = url.searchParams.get(REVIEW_WRITING_API_PARAMS.queryString.reviewRequestCode);

if (REVIEW_REQUEST_CODE === urlRequestCode) {
return HttpResponse.json(REVIEW_QUESTION_DATA);
}
return HttpResponse.json({ error: '잘못된 리뷰 작성 데이터 요청' }, { status: 404 });
},
);

// TODO: 추후 getReviewList API에서 리뷰 정보(이름, 개수...)를 내려주지 않는 경우 핸들러도 수정 필요
const getReviewList = (lastReviewId: number | null, size: number) => {
return http.get(endPoint.gettingReviewList(lastReviewId, size), async ({ request, cookies }) => {
// authToken 쿠키 확인
Expand Down Expand Up @@ -90,6 +104,12 @@ const postReview = () =>
return HttpResponse.json({ message: 'post 성공' }, { status: 201 });
});

const reviewHandler = [getDetailedReview(), getReviewList(null, 10), getDataToWriteReview(), postReview()];
const reviewHandler = [
getDetailedReview(),
getReviewList(null, 10),
getDataToWriteReview(),
postReview(),
getReviewInfoData(),
];

export default reviewHandler;
1 change: 1 addition & 0 deletions frontend/src/mocks/mockData/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './group';
export * from './reviewListMockData';
export * from './reviewWriting/reviewFormResultData';
export * from './reviewWriting/reviewQuestionData';
export * from './reviewInfoData';
5 changes: 5 additions & 0 deletions frontend/src/mocks/mockData/reviewInfoData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const MOCK_REVIEW_INFO_DATA = {
projectName: '리뷰미',
revieweeName: '산초',
totalReviewCount: 500,
};
2 changes: 1 addition & 1 deletion frontend/src/pages/ReviewCollectionPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const ReviewCollectionPage = () => {

return (
<ErrorSuspenseContainer fallback={AuthAndServerErrorFallback}>
<ReviewDisplayLayout projectName={projectName} revieweeName={revieweeName} isReviewList={false}>
<ReviewDisplayLayout isReviewList={false}>
<S.ReviewCollectionContainer>
<S.ReviewSectionDropdown>
<Dropdown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const PageContents = () => {
navigate(`/${ROUTE.detailedReview}/${reviewRequestCode}/${id}`);
};

const { projectName, revieweeName } = data.pages[0];
const isLastPage = data.pages[data.pages.length - 1].isLastPage;
const reviews = data.pages.flatMap((page) => page.reviews);

Expand All @@ -36,7 +35,7 @@ const PageContents = () => {

return (
isSuccess && (
<ReviewDisplayLayout projectName={projectName} revieweeName={revieweeName} isReviewList={true} reviewCount={5}>
<ReviewDisplayLayout isReviewList={true}>
{reviews.length === 0 ? (
<ReviewEmptySection />
) : (
Expand All @@ -46,7 +45,6 @@ const PageContents = () => {
return (
<UndraggableWrapper key={review.reviewId}>
<ReviewCard
projectName={projectName}
createdAt={review.createdAt}
contentPreview={review.contentPreview}
categories={review.categories}
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/types/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,9 @@ export interface ReviewWritingFormResult {
reviewRequestCode: string;
answers: ReviewWritingAnswer[];
}

export interface ReviewInfoData {
projectName: string;
revieweeName: string;
totalReviewCount: number;
}