diff --git a/frontend/src/api/ranking.ts b/frontend/src/api/ranking.ts index 40af0b986..4f3f3e27f 100644 --- a/frontend/src/api/ranking.ts +++ b/frontend/src/api/ranking.ts @@ -11,9 +11,9 @@ export const getUserRanking = async (isLoggedIn: boolean) => { }; export const getPassionUserRanking = async () => { - return await getFetch(`${BASE_URL}/members/ranking/passion/guest`); + return await getFetch(`${BASE_URL}/members/ranking/passion/guest`); }; export const getPopularPostRanking = async () => { - return await getFetch(`${BASE_URL}/posts/ranking/popular/guest`); + return await getFetch(`${BASE_URL}/posts/ranking/popular/guest`); }; diff --git a/frontend/src/hooks/query/ranking/usePassionUserRanking.ts b/frontend/src/hooks/query/ranking/usePassionUserRanking.ts index ea761d8c8..84a745c5b 100644 --- a/frontend/src/hooks/query/ranking/usePassionUserRanking.ts +++ b/frontend/src/hooks/query/ranking/usePassionUserRanking.ts @@ -7,7 +7,7 @@ import { getPassionUserRanking } from '@api/ranking'; import { QUERY_KEY } from '@constants/queryKey'; export const usePassionUserRanking = () => { - const { data, error, isLoading, isError } = useQuery( + const { data, error, isLoading, isError } = useQuery( [QUERY_KEY.PASSION_RANKING], getPassionUserRanking, { diff --git a/frontend/src/hooks/query/ranking/usePopularPostRanking.ts b/frontend/src/hooks/query/ranking/usePopularPostRanking.ts index 1660dd774..a8951aa63 100644 --- a/frontend/src/hooks/query/ranking/usePopularPostRanking.ts +++ b/frontend/src/hooks/query/ranking/usePopularPostRanking.ts @@ -7,7 +7,7 @@ import { getPopularPostRanking } from '@api/ranking'; import { QUERY_KEY } from '@constants/queryKey'; export const usePopularPostRanking = () => { - const { data, error, isLoading, isError } = useQuery( + const { data, error, isLoading, isError } = useQuery( [QUERY_KEY.POPULAR_RANKING], getPopularPostRanking, { diff --git a/frontend/src/hooks/query/ranking/useUserRanking.ts b/frontend/src/hooks/query/ranking/useUserRanking.ts index 4cca6b67f..71d9b9c2b 100644 --- a/frontend/src/hooks/query/ranking/useUserRanking.ts +++ b/frontend/src/hooks/query/ranking/useUserRanking.ts @@ -6,7 +6,7 @@ import { getUserRanking } from '@api/ranking'; import { QUERY_KEY } from '@constants/queryKey'; -export const usePassionUserRanking = (isLoggedIn: boolean) => { +export const useUserRanking = (isLoggedIn: boolean) => { const { data, error, isLoading, isError } = useQuery( [QUERY_KEY.USER_INFO, isLoggedIn, QUERY_KEY.PASSION_RANKING], () => getUserRanking(isLoggedIn), diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index 3dabb8bea..c21f2dc02 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -4,6 +4,7 @@ import { example } from './example/get'; import { mockVoteResult } from './getVoteDetail'; import { mockPost } from './post'; import { mockPostList } from './postList'; +import { mockRanking } from './ranking'; import { mockReport } from './report'; import { mockUserInfo } from './userInfo'; import { mockVote } from './vote'; @@ -18,4 +19,5 @@ export const handlers = [ ...mockUserInfo, ...mockComment, ...mockReport, + ...mockRanking, ]; diff --git a/frontend/src/mocks/ranking.ts b/frontend/src/mocks/ranking.ts new file mode 100644 index 000000000..b6e2eb392 --- /dev/null +++ b/frontend/src/mocks/ranking.ts @@ -0,0 +1,50 @@ +import { rest } from 'msw'; + +import { PassionUser, RankingPost } from '@type/ranking'; + +const userRankingInfo: PassionUser = { + ranking: 1111, + nickname: 'wow', + postCount: 1, + voteCount: 3, + score: 8, +}; + +const rankerInfo: PassionUser = { + ranking: 1, + nickname: 'gil-dong', + postCount: 11, + voteCount: 79, + score: 134, +}; + +const rankerList: PassionUser[] = new Array(10) + .fill(rankerInfo) + .map((ranker, index) => ({ ...ranker, ranking: index + 1 })); + +const rankingPostInfo: RankingPost = { + ranking: 1, + post: { + id: 29, + writer: 'writer', + title: '이것은 엄청나게 많은 투표가 이루어진 대단한 글이지요', + voteCount: 10, + }, +}; +const rankingPostList: RankingPost[] = new Array(10) + .fill(rankingPostInfo) + .map((post, index) => ({ ...post, ranking: index + 1 })); + +export const mockRanking = [ + rest.get('/members/me/ranking', (req, res, ctx) => { + return res(ctx.status(200), ctx.delay(500), ctx.json(userRankingInfo)); + }), + + rest.get('/members/ranking/passion/guest', (req, res, ctx) => { + return res(ctx.status(200), ctx.delay(1000), ctx.json(rankerList)); + }), + + rest.get('/posts/ranking/popular/guest', (req, res, ctx) => { + return res(ctx.status(200), ctx.delay(500), ctx.json(rankingPostList)); + }), +]; diff --git a/frontend/src/pages/Ranking/PassionUser/PassionUser.stories.tsx b/frontend/src/pages/Ranking/PassionUser/PassionUser.stories.tsx index 449e00687..3edac80fd 100644 --- a/frontend/src/pages/Ranking/PassionUser/PassionUser.stories.tsx +++ b/frontend/src/pages/Ranking/PassionUser/PassionUser.stories.tsx @@ -1,7 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { PassionUser } from '@type/ranking'; - import PassionUserRanking from '.'; const meta: Meta = { @@ -11,30 +9,10 @@ const meta: Meta = { export default meta; type Story = StoryObj; -const rankerInfo: PassionUser = { - rank: 1, - nickname: 'gil-dong', - postCount: 11, - voteCount: 79, - score: 134, -}; - -const userRankingInfo: PassionUser = { - rank: 1111, - nickname: 'wow', - postCount: 1, - voteCount: 3, - score: 8, -}; - -const rankerList: PassionUser[] = new Array(10) - .fill(rankerInfo) - .map((ranker, index) => ({ ...ranker, rank: index + 1 })); - export const User: Story = { - render: () => , + render: () => , }; export const Guest: Story = { - render: () => , + render: () => , }; diff --git a/frontend/src/pages/Ranking/PassionUser/UserRanking/index.tsx b/frontend/src/pages/Ranking/PassionUser/UserRanking/index.tsx new file mode 100644 index 000000000..431290cbe --- /dev/null +++ b/frontend/src/pages/Ranking/PassionUser/UserRanking/index.tsx @@ -0,0 +1,25 @@ +import React, { useContext } from 'react'; + +import { AuthContext } from '@hooks/context/auth'; +import { useUserRanking } from '@hooks/query/ranking/useUserRanking'; + +import * as S from '../style'; + +export default function UserRanking() { + const { loggedInfo } = useContext(AuthContext); + const { isLoggedIn } = loggedInfo; + + const { data: userRanking } = useUserRanking(isLoggedIn); + + return ( + userRanking && ( + + {userRanking.ranking} + {userRanking.nickname} + {userRanking.postCount} + {userRanking.voteCount} + {userRanking.score} + + ) + ); +} diff --git a/frontend/src/pages/Ranking/PassionUser/index.tsx b/frontend/src/pages/Ranking/PassionUser/index.tsx index b9f500806..4737b180e 100644 --- a/frontend/src/pages/Ranking/PassionUser/index.tsx +++ b/frontend/src/pages/Ranking/PassionUser/index.tsx @@ -1,12 +1,17 @@ -import { PassionUser } from '@type/ranking'; +import { Suspense } from 'react'; + +import { usePassionUserRanking } from '@hooks/query/ranking/usePassionUserRanking'; + +import ErrorBoundary from '@pages/ErrorBoundary'; + +import LoadingSpinner from '@components/common/LoadingSpinner'; import firstRankIcon from '@assets/first-rank.svg'; import secondRankIcon from '@assets/second-rank.svg'; import thirdRankIcon from '@assets/third-rank.svg'; -import * as RS from '../RankingTableStyle'; - import * as S from './style'; +import UserRanking from './UserRanking'; const columnNameList = ['등수', '닉네임', '작성글 수', '투표 수', '점수']; @@ -16,28 +21,25 @@ const rankIconUrl: Record = { 3: thirdRankIcon, }; -interface PassionUserRankingProps { - rankerList: PassionUser[]; - userRanking?: PassionUser; -} +export default function PassionUserRanking() { + const { data: rankerList } = usePassionUserRanking(); -export default function PassionUserRanking({ rankerList, userRanking }: PassionUserRankingProps) { return ( - - - - {columnNameList.map(text => ( - {text} - ))} - - {rankerList.map(ranker => { - const rankIcon = rankIconUrl[ranker.rank] && ( - {ranker.rank.toString()} + + + {columnNameList.map(text => ( + {text} + ))} + + {rankerList && + rankerList.map(ranker => { + const rankIcon = rankIconUrl[ranker.ranking] && ( + {ranker.ranking.toString()} ); return ( - - {rankIcon ?? ranker.rank} + + {rankIcon ?? ranker.ranking} {ranker.nickname} {ranker.postCount} {ranker.voteCount} @@ -45,16 +47,17 @@ export default function PassionUserRanking({ rankerList, userRanking }: PassionU ); })} - {userRanking && ( - - {userRanking.rank} - {userRanking.nickname} - {userRanking.postCount} - {userRanking.voteCount} - {userRanking.score} - - )} - - + + + + + } + > + + + + ); } diff --git a/frontend/src/pages/Ranking/PassionUser/style.ts b/frontend/src/pages/Ranking/PassionUser/style.ts index 116b71090..2058d7b3b 100644 --- a/frontend/src/pages/Ranking/PassionUser/style.ts +++ b/frontend/src/pages/Ranking/PassionUser/style.ts @@ -39,3 +39,12 @@ export const RankingTd = styled.td` export const Td = styled.td` padding: 10px 0; `; + +export const LoadingSpinnerWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + + height: 50px; + padding: 0; +`; diff --git a/frontend/src/pages/Ranking/PopularPost/PopularPost.stories.tsx b/frontend/src/pages/Ranking/PopularPost/PopularPost.stories.tsx index c7605bee5..f6c50d974 100644 --- a/frontend/src/pages/Ranking/PopularPost/PopularPost.stories.tsx +++ b/frontend/src/pages/Ranking/PopularPost/PopularPost.stories.tsx @@ -1,7 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { RankingPost } from '@type/ranking'; - import PopularPost from '.'; const meta: Meta = { @@ -11,19 +9,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -const rankingPostInfo: RankingPost = { - rank: 1, - post: { - id: 29, - writer: 'writer', - title: '이것은 엄청나게 많은 투표가 이루어진 대단한 글이지요', - voteCount: 10, - }, -}; -const rankingPostList: RankingPost[] = new Array(10) - .fill(rankingPostInfo) - .map((post, index) => ({ ...post, ranking: index + 1 })); - export const Default: Story = { - render: () => , + render: () => , }; diff --git a/frontend/src/pages/Ranking/PopularPost/index.tsx b/frontend/src/pages/Ranking/PopularPost/index.tsx index 6d08aeae3..76c95f383 100644 --- a/frontend/src/pages/Ranking/PopularPost/index.tsx +++ b/frontend/src/pages/Ranking/PopularPost/index.tsx @@ -1,6 +1,6 @@ import { Link } from 'react-router-dom'; -import { RankingPost } from '@type/ranking'; +import { usePopularPostRanking } from '@hooks/query/ranking/usePopularPostRanking'; import { PATH } from '@constants/path'; @@ -8,8 +8,6 @@ import firstRankIcon from '@assets/first-rank.svg'; import secondRankIcon from '@assets/second-rank.svg'; import thirdRankIcon from '@assets/third-rank.svg'; -import * as RS from '../RankingTableStyle'; - import * as S from './style'; const rankIconUrl: Record = { @@ -20,27 +18,25 @@ const rankIconUrl: Record = { const columnNameList = ['등수', '닉네임', '글 제목', '투표 수']; -interface PopularPostProps { - rankingPostList: RankingPost[]; -} +export default function PopularPost() { + const { data: rankingPostList } = usePopularPostRanking(); -export default function PopularPost({ rankingPostList }: PopularPostProps) { return ( - - - - {columnNameList.map(text => ( - {text} - ))} - - {rankingPostList.map(rankingPost => { - const rankIcon = rankIconUrl[rankingPost.rank] && ( - {rankingPost.rank.toString()} + + + {columnNameList.map(text => ( + {text} + ))} + + {rankingPostList && + rankingPostList.map(rankingPost => { + const rankIcon = rankIconUrl[rankingPost.ranking] && ( + {rankingPost.ranking.toString()} ); return ( - - {rankIcon ?? rankingPost.rank} + + {rankIcon ?? rankingPost.ranking} {rankingPost.post.writer} {rankingPost.post.title} @@ -49,7 +45,6 @@ export default function PopularPost({ rankingPostList }: PopularPostProps) { ); })} - - + ); } diff --git a/frontend/src/pages/Ranking/RankingTableStyle.ts b/frontend/src/pages/Ranking/RankingTableStyle.ts index 8b2064e18..b6734f4f7 100644 --- a/frontend/src/pages/Ranking/RankingTableStyle.ts +++ b/frontend/src/pages/Ranking/RankingTableStyle.ts @@ -3,7 +3,12 @@ import { styled } from 'styled-components'; import { theme } from '@styles/theme'; export const Background = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: fit-content; + min-height: 500px; border-radius: 4px; background-color: var(--gray); diff --git a/frontend/src/pages/Ranking/index.tsx b/frontend/src/pages/Ranking/index.tsx index bb603a15b..3afe86d49 100644 --- a/frontend/src/pages/Ranking/index.tsx +++ b/frontend/src/pages/Ranking/index.tsx @@ -1,51 +1,21 @@ +import { Suspense } from 'react'; import { useNavigate } from 'react-router-dom'; -import { PassionUser, RankingPost } from '@type/ranking'; - import { useToggleSwitch } from '@hooks/useToggleSwitch'; +import ErrorBoundary from '@pages/ErrorBoundary'; + import IconButton from '@components/common/IconButton'; import Layout from '@components/common/Layout'; +import LoadingSpinner from '@components/common/LoadingSpinner'; import NarrowTemplateHeader from '@components/common/NarrowTemplateHeader'; import ToggleSwitch from '@components/common/ToggleSwitch'; import PassionUserRanking from './PassionUser'; import PopularPost from './PopularPost'; +import * as RS from './RankingTableStyle'; import * as S from './style'; -const rankerInfo: PassionUser = { - rank: 1, - nickname: 'gil-dong', - postCount: 11, - voteCount: 79, - score: 134, -}; - -const userRankingInfo: PassionUser = { - rank: 1111, - nickname: 'wow', - postCount: 1, - voteCount: 3, - score: 8, -}; - -const rankerList: PassionUser[] = new Array(10) - .fill(rankerInfo) - .map((ranker, index) => ({ ...ranker, rank: index + 1 })); - -const rankingPostInfo: RankingPost = { - rank: 1, - post: { - id: 29, - writer: 'writer', - title: '이것은 엄청나게 많은 투표가 이루어진 대단한 글이지요', - voteCount: 10, - }, -}; -const rankingPostList: RankingPost[] = new Array(10) - .fill(rankingPostInfo) - .map((post, index) => ({ ...post, ranking: index + 1 })); - export default function Ranking() { const navigate = useNavigate(); const { selectedButton, firstButton, secondButton } = useToggleSwitch('열정 유저', '인기글 유저'); @@ -72,9 +42,23 @@ export default function Ranking() { secondButton={secondButton} /> {selectedButton === '열정 유저' && ( - + + + }> + + + + + )} + {selectedButton === '인기글 유저' && ( + + + }> + + + + )} - {selectedButton === '인기글 유저' && } diff --git a/frontend/src/types/ranking.ts b/frontend/src/types/ranking.ts index aed7af182..804ee8655 100644 --- a/frontend/src/types/ranking.ts +++ b/frontend/src/types/ranking.ts @@ -1,5 +1,5 @@ export interface PassionUser { - rank: number; + ranking: number; nickname: string; postCount: number; voteCount: number; @@ -7,7 +7,7 @@ export interface PassionUser { } export interface RankingPost { - rank: number; + ranking: number; post: { id: number; writer: string;