Skip to content

Commit

Permalink
feat: integrate seat review list API
Browse files Browse the repository at this point in the history
  • Loading branch information
nxnaxx committed Feb 5, 2025
1 parent f4985b1 commit 598b4ca
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 17 deletions.
8 changes: 6 additions & 2 deletions src/api/concertHallApi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { endPoint } from 'constants/endPoint';
import type { HallDetailResponse } from 'types';
import { publicAxios } from 'utils';
import type { HallDetailResponse, SeatReviewResponse } from 'types';
import { publicAxios, tokenAxios } from 'utils';

export const requestGetHallDetails = async (id: string) => {
return await publicAxios.get<HallDetailResponse>(endPoint.GET_CONCERT_HALL(id));
};

export const requestGetSeatReviews = async (query: string) => {
return await tokenAxios.get<SeatReviewResponse>(`${endPoint.GET_SEAT_REVIEW}?${query}`);
};
1 change: 1 addition & 0 deletions src/constants/endPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const endPoint = {
// Concert Hall API
GET_CONCERT_HALL_LIST: '/concert-halls/list',
GET_CONCERT_HALL: (hallCode: string) => `/concert-halls/${hallCode}`,
GET_SEAT_REVIEW: '/seat-review',

// Search API
GET_CONCERT_SEARCH: '/search/concert',
Expand Down
70 changes: 65 additions & 5 deletions src/pages/concertHallDetail/components/SeatReview.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import styled from '@emotion/styled';
import { useState } from 'react';
import { IoChatbubbleEllipses } from 'react-icons/io5';
import { useParams } from 'react-router-dom';

import SeatReviewItem from './SeatReviewItem';

import { CaptionText } from 'styles/Typography';
import { useIntersectionObserver } from 'hooks';
import { useGetSeatReviews } from 'queries/concertHall/useGetSeatReviews';
import { useAuthStore } from 'stores';
import { BodyRegularText, CaptionText } from 'styles/Typography';
import type { SeatReview, SeatReviewSort } from 'types';

const SeatReviewContainer = styled.div``;

Expand All @@ -21,20 +27,74 @@ const ReviewFilter = styled(CaptionText)<{ isActive?: boolean }>`
cursor: pointer;
`;

const EmptySeatReview = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2.4rem;
padding: 8rem 0;
`;

const SeatReviewList = styled.ul`
list-style: none;
`;

const SeatReview = () => {
const [sortOrder, setSortOrder] = useState('latest');
const { id } = useParams();
const { isLoggedIn } = useAuthStore(['isLoggedIn']);
const [sortOrder, setSortOrder] = useState<SeatReviewSort>('CREATED_AT');
const {
data: reviews,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useGetSeatReviews(id as string, sortOrder);

const targetRef = useIntersectionObserver(() => {
if (hasNextPage && !isFetchingNextPage) void fetchNextPage();
});

if (!isLoggedIn) {
return (
<EmptySeatReview>
<IoChatbubbleEllipses size={80} />
<BodyRegularText>로그인하고 리뷰를 확인해보세요</BodyRegularText>
</EmptySeatReview>
);
}

return (
<SeatReviewContainer>
<ReviewFilterWrapper>
<ReviewFilter isActive={sortOrder === 'latest'} onClick={() => setSortOrder('latest')}>
<ReviewFilter
isActive={sortOrder === 'CREATED_AT'}
onClick={() => setSortOrder('CREATED_AT')}
>
∙ 최신순
</ReviewFilter>
<ReviewFilter isActive={sortOrder === 'oldest'} onClick={() => setSortOrder('oldest')}>
<ReviewFilter
isActive={sortOrder === 'LIKE_COUNT'}
onClick={() => setSortOrder('LIKE_COUNT')}
>
∙ 오래된순
</ReviewFilter>
</ReviewFilterWrapper>
<SeatReviewItem />
{reviews?.pages[0].length === 0 ? (
<EmptySeatReview>
<IoChatbubbleEllipses size={80} />
<BodyRegularText>아직 작성된 리뷰가 없어요</BodyRegularText>
</EmptySeatReview>
) : (
reviews?.pages.map((page, pageIndex) => (
<SeatReviewList key={pageIndex}>
{page.map((item) => (
<SeatReviewItem key={item.reviewId} {...item} />
))}
</SeatReviewList>
))
)}
<div ref={targetRef} />
</SeatReviewContainer>
);
};
Expand Down
13 changes: 7 additions & 6 deletions src/pages/concertHallDetail/components/SeatReviewItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import SeatReviewSheet from './SeatReviewSheet';

import { useModalStore } from 'stores';
import { BodyRegularText } from 'styles/Typography';
import type { SeatReview } from 'types';
import { formatDotDate } from 'utils';

const SeatReviewItemContainer = styled.div`
Expand Down Expand Up @@ -112,9 +113,9 @@ const dummyData = {
date: '2025-01-14',
};

const SeatReviewItem = () => {
const { profileImg, username, isWriter, concertName, seatName, images, content, date } =
dummyData;
const SeatReviewItem = (props: SeatReview) => {
const { profileImg, username, isWriter, concertName } = dummyData;
const { seat, imageUrls, content, viewDate } = props;

const { openModal } = useModalStore(['openModal']);

Expand All @@ -133,16 +134,16 @@ const SeatReviewItem = () => {
)}
</ReviewTop>
<ConcertName>{concertName}</ConcertName>
<SeatName>{seatName}</SeatName>
<SeatName>{seat}</SeatName>
</ReviewInfo>
<ReviewImageWrapper>
{images.map((url) => (
{imageUrls.map((url) => (
<ReviewImage key={url} src={url} />
))}
</ReviewImageWrapper>
<ReviewContent>
<BodyRegularText>{content}</BodyRegularText>
<BodyRegularText>{formatDotDate(date)}</BodyRegularText>
<BodyRegularText>{formatDotDate(viewDate)}</BodyRegularText>
</ReviewContent>
</SeatReviewItemContainer>
);
Expand Down
1 change: 1 addition & 0 deletions src/queries/concertHall/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './useGetConcertHallDetail';
export * from './useGetSeatReviews';
4 changes: 2 additions & 2 deletions src/queries/concertHall/useGetConcertHallDetail.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useQuery } from '@tanstack/react-query';

import { requestGetHallDetails } from 'api';
import type { concertHallDetail } from 'types';
import type { ConcertHallDetail } from 'types';

export const useGetConcertHallDetail = (id: string) => {
const fetchHallDetails = async () => {
const { data } = await requestGetHallDetails(id);
return data.result;
};

return useQuery<concertHallDetail>({
return useQuery<ConcertHallDetail>({
queryKey: ['hallDetails', id],
queryFn: fetchHallDetails,
enabled: !!id,
Expand Down
30 changes: 30 additions & 0 deletions src/queries/concertHall/useGetSeatReviews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useInfiniteQuery } from '@tanstack/react-query';

import { requestGetSeatReviews } from 'api';
import { useAuthStore } from 'stores';
import type { SeatReview, SeatReviewSort } from 'types';

export const useGetSeatReviews = (hallId: string, sortType: SeatReviewSort) => {
const { isLoggedIn } = useAuthStore(['isLoggedIn']);

const fetchSeatReviews = async (lastId: number | null) => {
if (!isLoggedIn) return [];

const query = `${lastId ? `lastId=${lastId}&` : ''}size=3&sortType=${sortType}&hallId=${hallId}`;
const { data } = await requestGetSeatReviews(query);
return data.result;
};

return useInfiniteQuery<SeatReview[]>({
queryKey: ['seatReviews', hallId, sortType],
queryFn: ({ pageParam }) => {
const param = pageParam as number | null;
return fetchSeatReviews(param);
},
initialPageParam: null,
getNextPageParam: (lastPage) => {
const lastData = lastPage[lastPage.length - 1];
return lastPage.length > 0 ? lastData.reviewId : null;
},
});
};
21 changes: 19 additions & 2 deletions src/types/concertHall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,29 @@ export interface ConcertHallLocation {
address: string;
}

export interface concertHallDetail {
export interface ConcertHallDetail {
name: string;
seatScale: number;
star: number;
convenienceInfo: ConvenienceInfo;
location: ConcertHallLocation;
}

export type HallDetailResponse = ApiResponse<concertHallDetail>;
export interface SeatReview {
reviewId: number;
seat: string;
content: string;
star: number;
memberId: number;
hallId: string;
viewDate: string;
createdAt: string;
imageUrls: string[];
likeCount: number;
liked: boolean;
}

export type SeatReviewSort = 'CREATED_AT' | 'LIKE_COUNT';

export type HallDetailResponse = ApiResponse<ConcertHallDetail>;
export type SeatReviewResponse = ApiResponse<SeatReview[]>;

0 comments on commit 598b4ca

Please sign in to comment.