From a06f1597e07d885f22df2b114dbb822b1a4c75e5 Mon Sep 17 00:00:00 2001 From: ImxYJL <111052302+ImxYJL@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:57:02 +0900 Subject: [PATCH] =?UTF-8?q?[FE]=20feat:=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D/=EB=A6=AC=EB=B7=B0=20=EB=AA=A8=EC=95=84=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=EC=9D=98=20=EA=B3=B5=ED=86=B5=20UI=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8A=A4=EC=9C=84=EC=B9=98=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8,=20=EC=83=88=20URL=20=EB=B0=8F=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=A0=81=EC=9A=A9=20(#776)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Switch 컴포넌트 제작 * chore: Switch 컴포넌트 이름 변경 * feat: 리뷰 모아보기에 대한 라우팅 추가 및 임시 페이지 구현 * feat: 리뷰 모아보기와 리뷰 목록 페이지의 공통 레이아웃 제작 * refactor: 공통 레이아웃 제작에 따른 ReviewList 리팩토링 * feat: 리뷰 목록 반응형 적용 * feat: OptionSwitch 반응형 적용 * refactor: ReviewDisplayLayout 반응형 수정 * fix: px을 rem으로 수정 * fix: 미디어 쿼리 기준 단위를 px으로 수정 * refactor: 스타일을 위한 속성에 $처리 및 스타일 전용 인터페이스 생성 * fix: 불필요한 상속 제거 * refactor: OptionSwitch 리팩토링 - option 배열을 통한 렌더링 및 시맨틱 태그를사용 * refactor: OptionSwitch 컴포넌트 리팩토링에 따른 ReviewDisplayLayout 컴포넌트 리팩토링 및 훅 분리 * feat: 선택하지 않은 option을 hover한 경우의 추가 배경색 지정 * fix: 리뷰 작성 완료 페이지에서 breadCrumb으로 리뷰 작성 페이지로 이동하는 경로를 절대 URL 경로로 수정 --- frontend/src/components/ReviewCard/index.tsx | 25 +++++----- frontend/src/components/ReviewCard/styles.ts | 46 +++++++++--------- .../components/common/OptionSwitch/index.tsx | 36 ++++++++++++++ .../components/common/OptionSwitch/styles.ts | 48 +++++++++++++++++++ frontend/src/components/common/index.tsx | 1 + .../components/layouts/PageLayout/index.tsx | 1 - .../components/ReviewInfoSection/index.tsx | 35 ++++++++++++++ .../components/ReviewInfoSection/styles.ts | 5 +- .../hooks/useReviewDisplayLayoutOptions.ts | 33 +++++++++++++ .../layouts/ReviewDisplayLayout/index.tsx | 34 +++++++++++++ .../layouts/ReviewDisplayLayout/styles.ts | 20 ++++++++ frontend/src/constants/route.ts | 1 + frontend/src/hooks/useBreadcrumbPaths.ts | 19 +++++--- frontend/src/index.tsx | 5 ++ .../src/pages/ReviewCollectionPage/index.tsx | 25 ++++++++++ .../src/pages/ReviewCollectionPage/styles.ts | 2 + .../components/PageContents/index.tsx | 7 ++- .../components/PageContents/styles.ts | 7 --- .../components/ReviewInfoSection/index.tsx | 24 ---------- frontend/src/pages/index.tsx | 1 + 20 files changed, 294 insertions(+), 81 deletions(-) create mode 100644 frontend/src/components/common/OptionSwitch/index.tsx create mode 100644 frontend/src/components/common/OptionSwitch/styles.ts create mode 100644 frontend/src/components/layouts/ReviewDisplayLayout/components/ReviewInfoSection/index.tsx rename frontend/src/{pages/ReviewListPage => components/layouts/ReviewDisplayLayout}/components/ReviewInfoSection/styles.ts (91%) create mode 100644 frontend/src/components/layouts/ReviewDisplayLayout/hooks/useReviewDisplayLayoutOptions.ts create mode 100644 frontend/src/components/layouts/ReviewDisplayLayout/index.tsx create mode 100644 frontend/src/components/layouts/ReviewDisplayLayout/styles.ts create mode 100644 frontend/src/pages/ReviewCollectionPage/index.tsx create mode 100644 frontend/src/pages/ReviewCollectionPage/styles.ts delete mode 100644 frontend/src/pages/ReviewListPage/components/ReviewInfoSection/index.tsx diff --git a/frontend/src/components/ReviewCard/index.tsx b/frontend/src/components/ReviewCard/index.tsx index f86d8c395..f820a55a4 100644 --- a/frontend/src/components/ReviewCard/index.tsx +++ b/frontend/src/components/ReviewCard/index.tsx @@ -10,24 +10,21 @@ interface ReviewCardProps { handleClick: () => void; } -const ReviewCard = ({ projectName, createdAt, contentPreview, categories, handleClick }: ReviewCardProps) => { +const ReviewCard = ({ createdAt, contentPreview, categories, handleClick }: ReviewCardProps) => { return ( - - - - {projectName} - {createdAt} - - - + + {contentPreview} - - {categories.map((category) => ( - {category.content} - ))} - + + + {categories.map((category) => ( + {category.content} + ))} + + {createdAt} + ); diff --git a/frontend/src/components/ReviewCard/styles.ts b/frontend/src/components/ReviewCard/styles.ts index afa98cf55..cabca6485 100644 --- a/frontend/src/components/ReviewCard/styles.ts +++ b/frontend/src/components/ReviewCard/styles.ts @@ -4,13 +4,12 @@ import media from '@/utils/media'; export const Layout = styled.div` display: flex; - flex-direction: column; border: 0.1rem solid ${({ theme }) => theme.colors.lightGray}; - border-radius: 0.8rem; + border-radius: 1rem; &:hover { cursor: pointer; - border: 0.1rem solid ${({ theme }) => theme.colors.lightPurple}; + border: 0.15rem solid ${({ theme }) => theme.colors.primaryHover}; & > div:first-of-type { background-color: ${({ theme }) => theme.colors.lightPurple}; @@ -18,24 +17,10 @@ export const Layout = styled.div` } `; -export const Header = styled.div` - display: flex; - justify-content: space-between; - - height: 6rem; - padding: 1rem 3rem; - +export const LeftLineBorder = styled.div` + width: 5rem; background-color: ${({ theme }) => theme.colors.lightGray}; - border-radius: 0.8rem 0.8rem 0 0; -`; - -export const HeaderContent = styled.div` - display: flex; - gap: 1rem; - - img { - width: 4rem; - } + border-radius: 1rem 0 0 1rem; `; export const Title = styled.div` @@ -43,8 +28,11 @@ export const Title = styled.div` font-weight: 700; `; -export const SubTitle = styled.div` - font-size: 1.2rem; +export const Date = styled.p` + height: fit-content; + padding: 0 1rem; + font-size: 1.3rem; + background-color: ${({ theme }) => theme.colors.lightGray}; `; export const Visibility = styled.div` @@ -74,6 +62,18 @@ export const Main = styled.div` } `; +export const Footer = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + + ${media.small} { + flex-direction: column; + gap: 1.2rem; + align-items: flex-start; + } +`; + export const Keyword = styled.div` display: flex; flex-wrap: wrap; @@ -83,7 +83,7 @@ export const Keyword = styled.div` font-size: 1.4rem; ${media.small} { - gap: 1.6rem; + gap: 1.2rem; } div { diff --git a/frontend/src/components/common/OptionSwitch/index.tsx b/frontend/src/components/common/OptionSwitch/index.tsx new file mode 100644 index 000000000..7b85b495e --- /dev/null +++ b/frontend/src/components/common/OptionSwitch/index.tsx @@ -0,0 +1,36 @@ +import * as S from './styles'; + +export interface OptionSwitchStyleProps { + $isChecked: boolean; +} + +export interface OptionSwitchOption { + label: string; + isChecked: boolean; + handleOptionClick: () => void; +} + +interface OptionSwitchProps { + options: OptionSwitchOption[]; +} + +const OptionSwitch = ({ options }: OptionSwitchProps) => { + const handleSwitchClick = (index: number) => { + const clickedOption = options[index]; + if (clickedOption) clickedOption.handleOptionClick(); + }; + + return ( + + {options.map((option, index) => ( + handleSwitchClick(index)}> + + {option.label} + + + ))} + + ); +}; + +export default OptionSwitch; diff --git a/frontend/src/components/common/OptionSwitch/styles.ts b/frontend/src/components/common/OptionSwitch/styles.ts new file mode 100644 index 000000000..fb7d0fddb --- /dev/null +++ b/frontend/src/components/common/OptionSwitch/styles.ts @@ -0,0 +1,48 @@ +import styled from '@emotion/styled'; + +import media from '@/utils/media'; + +import { OptionSwitchStyleProps } from './index'; + +export const OptionSwitchContainer = styled.ul` + cursor: pointer; + + display: flex; + justify-content: space-between; + + width: 15rem; + height: 4.5rem; + padding: 0.7rem; + + background-color: ${({ theme }) => theme.colors.lightGray}; + border-radius: ${({ theme }) => theme.borderRadius.basic}; + + ${media.small} { + height: 3.5rem; + font-size: 1.2rem; + } +`; + +export const CheckboxWrapper = styled.li` + display: flex; + align-items: center; + justify-content: center; + + width: 50%; + height: 100%; + + background-color: ${({ $isChecked, theme }) => ($isChecked ? theme.colors.white : theme.colors.lightGray)}; + border-radius: ${({ theme }) => theme.borderRadius.basic}; + + transition: background-color 0.2s ease-out; + + &:hover { + background-color: ${({ $isChecked, theme }) => ($isChecked ? theme.colors.white : theme.colors.lightPurple)}; + } +`; + +export const CheckboxButton = styled.button` + user-select: none; + font-size: 1.2rem; + color: ${({ $isChecked, theme }) => ($isChecked ? theme.colors.primary : theme.colors.black)}; +`; diff --git a/frontend/src/components/common/index.tsx b/frontend/src/components/common/index.tsx index f18308ab9..48218fd2f 100644 --- a/frontend/src/components/common/index.tsx +++ b/frontend/src/components/common/index.tsx @@ -7,4 +7,5 @@ export { default as Checkbox } from './Checkbox'; export { default as CheckboxItem } from './CheckboxItem'; export { default as EyeButton } from './EyeButton'; export { default as Carousel } from './Carousel'; +export { default as OptionSwitch } from './OptionSwitch'; export * from './modals'; diff --git a/frontend/src/components/layouts/PageLayout/index.tsx b/frontend/src/components/layouts/PageLayout/index.tsx index 4987e495e..34bba823c 100644 --- a/frontend/src/components/layouts/PageLayout/index.tsx +++ b/frontend/src/components/layouts/PageLayout/index.tsx @@ -1,4 +1,3 @@ -import { TopButton } from '@/components/common'; import Breadcrumb from '@/components/common/Breadcrumb'; import useBreadcrumbPaths from '@/hooks/useBreadcrumbPaths'; import { EssentialPropsWithChildren } from '@/types'; diff --git a/frontend/src/components/layouts/ReviewDisplayLayout/components/ReviewInfoSection/index.tsx b/frontend/src/components/layouts/ReviewDisplayLayout/components/ReviewInfoSection/index.tsx new file mode 100644 index 000000000..d1bf463bf --- /dev/null +++ b/frontend/src/components/layouts/ReviewDisplayLayout/components/ReviewInfoSection/index.tsx @@ -0,0 +1,35 @@ +import { calculateParticle } from '@/utils'; + +import * as S from './styles'; + +export interface ReviewInfoSectionProps { + revieweeName: string; + isReviewList: boolean; + projectName: string; + reviewCount?: number; +} + +const ReviewInfoSection = ({ projectName, revieweeName, reviewCount, isReviewList }: ReviewInfoSectionProps) => { + const revieweeNameWithParticle = calculateParticle({ + target: revieweeName, + particles: { withFinalConsonant: '이', withoutFinalConsonant: '가' }, + }); + + const getReviewInfoMessage = () => { + return isReviewList + ? `${revieweeNameWithParticle} 받은 ${reviewCount}개의 리뷰 목록이에요` + : `${revieweeNameWithParticle} 받은 리뷰를 질문별로 모아봤어요`; + }; + + return ( + + {projectName} + + {revieweeName} + {getReviewInfoMessage()} + + + ); +}; + +export default ReviewInfoSection; diff --git a/frontend/src/pages/ReviewListPage/components/ReviewInfoSection/styles.ts b/frontend/src/components/layouts/ReviewDisplayLayout/components/ReviewInfoSection/styles.ts similarity index 91% rename from frontend/src/pages/ReviewListPage/components/ReviewInfoSection/styles.ts rename to frontend/src/components/layouts/ReviewDisplayLayout/components/ReviewInfoSection/styles.ts index d558c685a..b84fe2ce3 100644 --- a/frontend/src/pages/ReviewListPage/components/ReviewInfoSection/styles.ts +++ b/frontend/src/components/layouts/ReviewDisplayLayout/components/ReviewInfoSection/styles.ts @@ -5,10 +5,11 @@ import media from '@/utils/media'; export const ReviewInfoContainer = styled.div` display: flex; flex-direction: column; - margin: 2rem 0 3rem 1rem; + justify-content: flex-end; + margin: 2rem 0 3rem 0; ${media.small} { - margin-bottom: 1.8rem; + margin-bottom: 1rem; } `; diff --git a/frontend/src/components/layouts/ReviewDisplayLayout/hooks/useReviewDisplayLayoutOptions.ts b/frontend/src/components/layouts/ReviewDisplayLayout/hooks/useReviewDisplayLayoutOptions.ts new file mode 100644 index 000000000..61c3583fd --- /dev/null +++ b/frontend/src/components/layouts/ReviewDisplayLayout/hooks/useReviewDisplayLayoutOptions.ts @@ -0,0 +1,33 @@ +import { useLocation, useNavigate } from 'react-router'; + +import { OptionSwitchOption } from '@/components/common/OptionSwitch'; +import { ROUTE } from '@/constants/route'; +import { useSearchParamAndQuery } from '@/hooks'; + +const useReviewDisplayLayoutOptions = () => { + const { pathname } = useLocation(); + const navigate = useNavigate(); + + const { param: reviewRequestCode } = useSearchParamAndQuery({ + paramKey: 'reviewRequestCode', + }); + + const isReviewCollection = pathname.includes(ROUTE.reviewCollection); + + const reviewDisplayLayoutOptions: OptionSwitchOption[] = [ + { + label: '목록보기', + isChecked: !isReviewCollection, + handleOptionClick: () => navigate(`/${ROUTE.reviewList}/${reviewRequestCode}`), + }, + { + label: '모아보기', + isChecked: isReviewCollection, + handleOptionClick: () => navigate(`/${ROUTE.reviewCollection}/${reviewRequestCode}`), + }, + ]; + + return [...reviewDisplayLayoutOptions]; +}; + +export default useReviewDisplayLayoutOptions; diff --git a/frontend/src/components/layouts/ReviewDisplayLayout/index.tsx b/frontend/src/components/layouts/ReviewDisplayLayout/index.tsx new file mode 100644 index 000000000..8c22cfbfc --- /dev/null +++ b/frontend/src/components/layouts/ReviewDisplayLayout/index.tsx @@ -0,0 +1,34 @@ +import { TopButton, OptionSwitch } from '@/components/common'; +import { EssentialPropsWithChildren } from '@/types'; + +import ReviewInfoSection, { ReviewInfoSectionProps } from './components/ReviewInfoSection'; +import useReviewDisplayLayoutOptions from './hooks/useReviewDisplayLayoutOptions'; +import * as S from './styles'; + +const ReviewDisplayLayout = ({ + revieweeName, + projectName, + reviewCount, + isReviewList, + children, +}: EssentialPropsWithChildren) => { + const reviewDisplayLayoutOptions = useReviewDisplayLayoutOptions(); + + return ( + + + + + + + {children} + + ); +}; + +export default ReviewDisplayLayout; diff --git a/frontend/src/components/layouts/ReviewDisplayLayout/styles.ts b/frontend/src/components/layouts/ReviewDisplayLayout/styles.ts new file mode 100644 index 000000000..2cbf1e1bb --- /dev/null +++ b/frontend/src/components/layouts/ReviewDisplayLayout/styles.ts @@ -0,0 +1,20 @@ +import styled from '@emotion/styled'; + +export const ReviewDisplayLayout = styled.div` + display: flex; + flex-direction: column; + width: 90%; + min-height: inherit; +`; + +export const Container = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + + @media screen and (max-width: 500px) { + flex-direction: column; + align-items: flex-start; + margin-bottom: 2.5rem; + } +`; diff --git a/frontend/src/constants/route.ts b/frontend/src/constants/route.ts index d79e9aed8..0826e9ff8 100644 --- a/frontend/src/constants/route.ts +++ b/frontend/src/constants/route.ts @@ -6,4 +6,5 @@ export const ROUTE = { reviewWritingComplete: 'user/review-writing-complete', detailedReview: 'user/detailed-review', reviewZone: 'user/review-zone', + reviewCollection: 'user/review-collection', }; diff --git a/frontend/src/hooks/useBreadcrumbPaths.ts b/frontend/src/hooks/useBreadcrumbPaths.ts index a36424979..071cbfbf0 100644 --- a/frontend/src/hooks/useBreadcrumbPaths.ts +++ b/frontend/src/hooks/useBreadcrumbPaths.ts @@ -17,25 +17,32 @@ const useBreadcrumbPaths = () => { paramKey: ROUTE_PARAM.reviewId, }); - const breadcrumbPathList: Path[] = [{ pageName: '연결 페이지', path: `${ROUTE.reviewZone}/${reviewRequestCode}` }]; + const breadcrumbPathList: Path[] = [{ pageName: '리뷰 연결', path: `${ROUTE.reviewZone}/${reviewRequestCode}` }]; if (pathname === `/${ROUTE.reviewList}/${reviewRequestCode}`) { - breadcrumbPathList.push({ pageName: '목록 페이지', path: `${ROUTE.reviewList}/${reviewRequestCode}` }); + breadcrumbPathList.push({ pageName: '리뷰 목록', path: `${ROUTE.reviewList}/${reviewRequestCode}` }); + } + + if (pathname === `/${ROUTE.reviewCollection}/${reviewRequestCode}`) { + breadcrumbPathList.push({ pageName: '리뷰 모아보기', path: `${ROUTE.reviewCollection}/${reviewRequestCode}` }); } if (pathname.includes(`/${ROUTE.reviewWriting}/`)) { - breadcrumbPathList.push({ pageName: '작성 페이지', path: pathname }); + breadcrumbPathList.push({ pageName: '리뷰 작성', path: pathname }); } if (pathname.includes(`/${ROUTE.reviewWritingComplete}`)) { - breadcrumbPathList.push({ pageName: '작성 페이지', path: -1 }, { pageName: '작성 완료 페이지', path: pathname }); + breadcrumbPathList.push( + { pageName: '리뷰 작성', path: `${ROUTE.reviewWriting}/${reviewRequestCode}` }, + { pageName: '리뷰 작성 완료 페이지', path: pathname }, + ); } if (pathname.includes(ROUTE.detailedReview)) { breadcrumbPathList.push( - { pageName: '목록 페이지', path: `${ROUTE.reviewList}/${reviewRequestCode}` }, + { pageName: '리뷰 목록', path: `${ROUTE.reviewList}/${reviewRequestCode}` }, { - pageName: '상세 페이지', + pageName: '리뷰 상세', path: `${ROUTE.detailedReview}/${reviewRequestCode}/${reviewId}`, }, ); diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 9257d8998..da75f7b49 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -24,6 +24,7 @@ const ReviewListPage = lazy(() => import('@/pages/ReviewListPage')); const ReviewWritingCompletePage = lazy(() => import('@/pages/ReviewWritingCompletePage')); const ReviewWritingPage = lazy(() => import('@/pages/ReviewWritingPage')); const ReviewZonePage = lazy(() => import('@/pages/ReviewZonePage')); +const ReviewCollectionPage = lazy(() => import('@/pages/ReviewCollectionPage')); const LoadingPage = lazy(() => import('@/pages/LoadingPage')); @@ -100,6 +101,10 @@ const router = createBrowserRouter([ ), }, + { + path: `${ROUTE.reviewCollection}/:${ROUTE_PARAM.reviewRequestCode}`, + element: , + }, ], }, ]); diff --git a/frontend/src/pages/ReviewCollectionPage/index.tsx b/frontend/src/pages/ReviewCollectionPage/index.tsx new file mode 100644 index 000000000..6e6718d31 --- /dev/null +++ b/frontend/src/pages/ReviewCollectionPage/index.tsx @@ -0,0 +1,25 @@ +import { AuthAndServerErrorFallback, ErrorSuspenseContainer, TopButton } from '@/components'; +import ReviewDisplayLayout from '@/components/layouts/ReviewDisplayLayout'; +import { useGetReviewList } from '@/hooks'; + +const ReviewCollectionPage = () => { + // TODO: 추후 리뷰 그룹 정보를 받아오는 API로 대체 + const { data } = useGetReviewList(); + const { revieweeName, projectName } = data.pages[0]; + + return ( + + + 리뷰 모아보기 페이지 children + {Array(50) + .fill('스크롤바 없어서 생기는 layout shift 방지용 + Topbutton 확인용 더미 데이터입니다.') + .map((data, index) => { + return {data}; + })} + + + + ); +}; + +export default ReviewCollectionPage; diff --git a/frontend/src/pages/ReviewCollectionPage/styles.ts b/frontend/src/pages/ReviewCollectionPage/styles.ts new file mode 100644 index 000000000..f404b4253 --- /dev/null +++ b/frontend/src/pages/ReviewCollectionPage/styles.ts @@ -0,0 +1,2 @@ +import styled from '@emotion/styled'; + diff --git a/frontend/src/pages/ReviewListPage/components/PageContents/index.tsx b/frontend/src/pages/ReviewListPage/components/PageContents/index.tsx index c63b68b83..04688cd79 100644 --- a/frontend/src/pages/ReviewListPage/components/PageContents/index.tsx +++ b/frontend/src/pages/ReviewListPage/components/PageContents/index.tsx @@ -1,13 +1,13 @@ import { useNavigate } from 'react-router'; import UndraggableWrapper from '@/components/common/UndraggableWrapper'; +import ReviewDisplayLayout from '@/components/layouts/ReviewDisplayLayout'; import ReviewCard from '@/components/ReviewCard'; import { ROUTE } from '@/constants/route'; import { useGetReviewList, useSearchParamAndQuery } from '@/hooks'; import { useInfiniteScroll } from '../../hooks'; import ReviewEmptySection from '../ReviewEmptySection'; -import ReviewInfoSection from '../ReviewInfoSection'; import * as S from './styles'; @@ -36,8 +36,7 @@ const PageContents = () => { return ( isSuccess && ( - - + {reviews.length === 0 ? ( ) : ( @@ -59,7 +58,7 @@ const PageContents = () => { })} )} - + ) ); }; diff --git a/frontend/src/pages/ReviewListPage/components/PageContents/styles.ts b/frontend/src/pages/ReviewListPage/components/PageContents/styles.ts index 641a8ccaa..08cfe137a 100644 --- a/frontend/src/pages/ReviewListPage/components/PageContents/styles.ts +++ b/frontend/src/pages/ReviewListPage/components/PageContents/styles.ts @@ -1,12 +1,5 @@ import styled from '@emotion/styled'; -export const Layout = styled.div` - display: flex; - flex-direction: column; - width: 90%; - min-height: inherit; -`; - export const ReviewSection = styled.div` display: flex; flex-direction: column; diff --git a/frontend/src/pages/ReviewListPage/components/ReviewInfoSection/index.tsx b/frontend/src/pages/ReviewListPage/components/ReviewInfoSection/index.tsx deleted file mode 100644 index 2b3862075..000000000 --- a/frontend/src/pages/ReviewListPage/components/ReviewInfoSection/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { calculateParticle } from '@/utils'; - -import * as S from './styles'; - -interface ReviewInfoSectionProps { - projectName: string; - revieweeName: string; -} - -const ReviewInfoSection = ({ projectName, revieweeName }: ReviewInfoSectionProps) => { - const reviewMessageSuffix = `${calculateParticle({ target: revieweeName, particles: { withFinalConsonant: '이', withoutFinalConsonant: '가' } })} 받은 리뷰 목록이에요`; - - return ( - - {projectName} - - {revieweeName} - {reviewMessageSuffix} - - - ); -}; - -export default ReviewInfoSection; diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 3c5f182ce..4dc586d19 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -6,3 +6,4 @@ export { default as ReviewListPage } from './ReviewListPage'; export { default as ReviewWritingPage } from './ReviewWritingPage'; export { default as ReviewWritingCompletePage } from './ReviewWritingCompletePage'; export { default as ReviewZonePage } from './ReviewZonePage'; +export { default as ReviewCollectionPage } from './ReviewCollectionPage';
{data}