From a6812b49e3de5353a7ae4db1a12a81076e3bdc1b Mon Sep 17 00:00:00 2001 From: hae-on Date: Fri, 13 Oct 2023 10:21:27 +0900 Subject: [PATCH 01/11] =?UTF-8?q?refactor:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A6=AC=EB=B7=B0=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Members/MemberReviewList/MemberReviewList.tsx | 2 +- .../Rank/ReviewRankingItem/ReviewRankingItem.tsx | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx b/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx index 50398fedf..11921e36f 100644 --- a/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx +++ b/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx @@ -53,7 +53,7 @@ const MemberReviewList = ({ isMemberPage = false }: MemberReviewListProps) => { to={`${PATH.PRODUCT_LIST}/${reviewRanking.categoryType}/${reviewRanking.productId}`} block > - + ))} diff --git a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx b/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx index 70fd89c32..38877ff56 100644 --- a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx +++ b/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx @@ -7,13 +7,14 @@ import type { ReviewRanking } from '@/types/ranking'; interface ReviewRankingItemProps { reviewRanking: ReviewRanking; + isMemberPage?: boolean; } -const ReviewRankingItem = ({ reviewRanking }: ReviewRankingItemProps) => { +const ReviewRankingItem = ({ reviewRanking, isMemberPage = false }: ReviewRankingItemProps) => { const { productName, content, rating, favoriteCount } = reviewRanking; return ( - + {productName} @@ -41,13 +42,14 @@ const ReviewRankingItem = ({ reviewRanking }: ReviewRankingItemProps) => { export default memo(ReviewRankingItem); -const ReviewRankingItemContainer = styled.div` +const ReviewRankingItemContainer = styled.div<{ isMemberPage: boolean }>` display: flex; flex-direction: column; gap: 4px; padding: 12px; - border: 1px solid ${({ theme }) => theme.borderColors.disabled}; - border-radius: ${({ theme }) => theme.borderRadius.sm}; + border: ${({ isMemberPage, theme }) => (isMemberPage ? 'none' : `1px solid ${theme.borderColors.disabled}`)}; + border-bottom: ${({ isMemberPage, theme }) => (isMemberPage ? `1px solid ${theme.borderColors.disabled}` : 'none')}; + border-radius: ${({ isMemberPage, theme }) => (isMemberPage ? 0 : theme.borderRadius.sm)}; `; const ReviewText = styled(Text)` From 2bc411a08cec7e68166c3be990ff9830e2a7d058 Mon Sep 17 00:00:00 2001 From: hae-on Date: Fri, 13 Oct 2023 10:29:26 +0900 Subject: [PATCH 02/11] =?UTF-8?q?refactor:=20border=20bottom=20=EB=AC=B4?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx b/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx index 38877ff56..c2004c20e 100644 --- a/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx +++ b/frontend/src/components/Rank/ReviewRankingItem/ReviewRankingItem.tsx @@ -48,7 +48,7 @@ const ReviewRankingItemContainer = styled.div<{ isMemberPage: boolean }>` gap: 4px; padding: 12px; border: ${({ isMemberPage, theme }) => (isMemberPage ? 'none' : `1px solid ${theme.borderColors.disabled}`)}; - border-bottom: ${({ isMemberPage, theme }) => (isMemberPage ? `1px solid ${theme.borderColors.disabled}` : 'none')}; + border-bottom: ${({ theme }) => `1px solid ${theme.borderColors.disabled}`}; border-radius: ${({ isMemberPage, theme }) => (isMemberPage ? 0 : theme.borderRadius.sm)}; `; From baf67a34a53b5a9c6e36f05cf3adb39e6bc82cf8 Mon Sep 17 00:00:00 2001 From: hae-on Date: Fri, 13 Oct 2023 15:49:16 +0900 Subject: [PATCH 03/11] =?UTF-8?q?refactor:=20bookmark=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/SectionTitle/SectionTitle.stories.tsx | 2 -- .../components/Common/SectionTitle/SectionTitle.tsx | 11 +---------- frontend/src/mocks/data/productDetail.json | 1 - frontend/src/mocks/data/productDetails.json | 2 -- frontend/src/pages/ProductDetailPage.tsx | 4 ++-- frontend/src/types/product.ts | 1 - 6 files changed, 3 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx b/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx index edec003ca..05883aea3 100644 --- a/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx +++ b/frontend/src/components/Common/SectionTitle/SectionTitle.stories.tsx @@ -13,13 +13,11 @@ type Story = StoryObj; export const Default: Story = { args: { name: '사이다', - bookmark: false, }, }; export const Bookmarked: Story = { args: { name: '사이다', - bookmark: true, }, }; diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx b/frontend/src/components/Common/SectionTitle/SectionTitle.tsx index 106c60759..4e121a4b8 100644 --- a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx +++ b/frontend/src/components/Common/SectionTitle/SectionTitle.tsx @@ -6,10 +6,9 @@ import { useRoutePage } from '@/hooks/common'; interface SectionTitleProps { name: string; - bookmark?: boolean; } -const SectionTitle = ({ name, bookmark = false }: SectionTitleProps) => { +const SectionTitle = ({ name }: SectionTitleProps) => { const { routeBack } = useRoutePage(); return ( @@ -22,14 +21,6 @@ const SectionTitle = ({ name, bookmark = false }: SectionTitleProps) => { {name} - {bookmark && ( - - )} ); }; diff --git a/frontend/src/mocks/data/productDetail.json b/frontend/src/mocks/data/productDetail.json index 386c7ae71..2695b51b1 100644 --- a/frontend/src/mocks/data/productDetail.json +++ b/frontend/src/mocks/data/productDetail.json @@ -5,7 +5,6 @@ "image": "https://i.namu.wiki/i/9wnvUaEa1EkDqG-M0Pbwfdf19FJQQXV_-bnlU2SYaNcG05y2wbabiIrfrGES1M4xSgDjY39RwOvLNggDd3Huuw.webp", "content": "할머니가 먹을 거 같은 맛입니다.\n1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", "averageRating": 4.5, - "bookmark": false, "reviewCount": 100, "tags": [ { diff --git a/frontend/src/mocks/data/productDetails.json b/frontend/src/mocks/data/productDetails.json index e3c68dab0..c386ce680 100644 --- a/frontend/src/mocks/data/productDetails.json +++ b/frontend/src/mocks/data/productDetails.json @@ -6,7 +6,6 @@ "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", "content": "할머니가 먹을 거 같은 맛입니다.\n1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", "averageRating": 4.5, - "bookmark": false, "reviewCount": 100, "tags": [ { @@ -33,7 +32,6 @@ "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", "averageRating": 4.0, - "bookmark": true, "reviewCount": 55, "tags": [ { diff --git a/frontend/src/pages/ProductDetailPage.tsx b/frontend/src/pages/ProductDetailPage.tsx index f41985856..3ab92e45f 100644 --- a/frontend/src/pages/ProductDetailPage.tsx +++ b/frontend/src/pages/ProductDetailPage.tsx @@ -50,7 +50,7 @@ export const ProductDetailPage = () => { return null; } - const { name, bookmark, reviewCount } = productDetail; + const { name, reviewCount } = productDetail; const tabMenus = [`리뷰 ${reviewCount}`, '꿀조합']; const sortOptions = isReviewTab ? REVIEW_SORT_OPTIONS : RECIPE_SORT_OPTIONS; @@ -75,7 +75,7 @@ export const ProductDetailPage = () => { return ( - + diff --git a/frontend/src/types/product.ts b/frontend/src/types/product.ts index 391cb103d..b3389a32f 100644 --- a/frontend/src/types/product.ts +++ b/frontend/src/types/product.ts @@ -17,7 +17,6 @@ export interface ProductDetail { content: string; averageRating: number; reviewCount: number; - bookmark: boolean; tags: Tag[]; } From 345abbe8bd91ad24bb5a97bcb4c1dd995ddc5f53 Mon Sep 17 00:00:00 2001 From: hae-on Date: Fri, 13 Oct 2023 15:49:41 +0900 Subject: [PATCH 04/11] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=94=A9=20rotate?= =?UTF-8?q?=20=EB=B0=A9=ED=96=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Common/Loading/Loading.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Common/Loading/Loading.tsx b/frontend/src/components/Common/Loading/Loading.tsx index 4ef7d374e..7c58614ae 100644 --- a/frontend/src/components/Common/Loading/Loading.tsx +++ b/frontend/src/components/Common/Loading/Loading.tsx @@ -40,7 +40,7 @@ const rotate = keyframes` } 100% { - transform: rotate(-360deg); + transform: rotate(360deg); } `; From 69655d6bb3f7bb515ef453a104c1a1f9a5435cfb Mon Sep 17 00:00:00 2001 From: hae-on Date: Fri, 13 Oct 2023 23:51:13 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20link=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.storybook/preview-body.html | 10 ++++++++++ frontend/src/components/Common/Svg/SvgIcon.tsx | 1 + frontend/src/components/Common/Svg/SvgSprite.tsx | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html index c74febeca..4c6e6e33b 100644 --- a/frontend/.storybook/preview-body.html +++ b/frontend/.storybook/preview-body.html @@ -100,6 +100,16 @@ d="M3 4V1h2v3h3v2H5v3H3V6H0V4m6 6V7h3V4h7l1.8 2H21c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2V10m10 9c4.45 0 6.69-5.38 3.54-8.54C13.39 7.31 8 9.55 8 14c0 2.76 2.24 5 5 5m-3.2-5c0 2.85 3.45 4.28 5.46 2.26c2.02-2.01.59-5.46-2.26-5.46A3.21 3.21 0 0 0 9.8 14Z" /> + + + + + +
diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/frontend/src/components/Common/Svg/SvgIcon.tsx index 7287cbd5e..13a2aa1f5 100644 --- a/frontend/src/components/Common/Svg/SvgIcon.tsx +++ b/frontend/src/components/Common/Svg/SvgIcon.tsx @@ -21,6 +21,7 @@ export const SVG_ICON_VARIANTS = [ 'plus', 'pencil', 'camera', + 'link', ] as const; export type SvgIconVariant = (typeof SVG_ICON_VARIANTS)[number]; diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx index f4d7a1937..ec2c8f2e0 100644 --- a/frontend/src/components/Common/Svg/SvgSprite.tsx +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -74,6 +74,12 @@ const SvgSprite = () => { + + + + + + ); }; From 630c1d031ebfc7391303cedbfd89c76a843b5039 Mon Sep 17 00:00:00 2001 From: hae-on Date: Sat, 14 Oct 2023 00:37:27 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/index.ts | 1 + .../Common/SectionTitle/SectionTitle.tsx | 26 +++- .../MemberReviewList/MemberReviewList.tsx | 6 +- .../ReviewRankingList/ReviewRankingList.tsx | 2 +- frontend/src/constants/path.ts | 1 + frontend/src/hooks/queries/review/index.ts | 1 + .../queries/review/useReviewDetailQuery.ts | 16 +++ frontend/src/mocks/data/reviewDetail.json | 29 ++++ frontend/src/mocks/handlers/reviewHandlers.ts | 5 + frontend/src/pages/ReviewDetailPage.tsx | 129 ++++++++++++++++++ frontend/src/router/index.tsx | 9 ++ frontend/src/types/response.ts | 6 +- frontend/src/types/review.ts | 6 + 13 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 frontend/src/hooks/queries/review/useReviewDetailQuery.ts create mode 100644 frontend/src/mocks/data/reviewDetail.json create mode 100644 frontend/src/pages/ReviewDetailPage.tsx diff --git a/frontend/src/apis/index.ts b/frontend/src/apis/index.ts index 73d9e4928..4df8ec8b4 100644 --- a/frontend/src/apis/index.ts +++ b/frontend/src/apis/index.ts @@ -9,3 +9,4 @@ export const memberApi = new ApiClient('/members'); export const recipeApi = new ApiClient('/recipes'); export const searchApi = new ApiClient('/search'); export const logoutApi = new ApiClient('/logout'); +export const reviewApi = new ApiClient('/reviews'); diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx b/frontend/src/components/Common/SectionTitle/SectionTitle.tsx index 4e121a4b8..14bfea677 100644 --- a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx +++ b/frontend/src/components/Common/SectionTitle/SectionTitle.tsx @@ -1,4 +1,5 @@ -import { Button, Heading, theme } from '@fun-eat/design-system'; +import { Button, Heading, Link, theme } from '@fun-eat/design-system'; +import { Link as RouterLink } from 'react-router-dom'; import styled from 'styled-components'; import { SvgIcon } from '@/components/Common'; @@ -6,9 +7,10 @@ import { useRoutePage } from '@/hooks/common'; interface SectionTitleProps { name: string; + link?: string; } -const SectionTitle = ({ name }: SectionTitleProps) => { +const SectionTitle = ({ name, link }: SectionTitleProps) => { const { routeBack } = useRoutePage(); return ( @@ -17,9 +19,18 @@ const SectionTitle = ({ name }: SectionTitleProps) => { - - {name} - + {link ? ( + + + {name} + + + ) : ( + + {name} + + )} + {link && } ); @@ -36,9 +47,12 @@ const SectionTitleContainer = styled.div` const SectionTitleWrapper = styled.div` display: flex; align-items: center; - column-gap: 16px; svg { padding-top: 2px; } `; + +const ProductName = styled(Heading)` + margin: 0 5px 0 16px; +`; diff --git a/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx b/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx index 11921e36f..fde211413 100644 --- a/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx +++ b/frontend/src/components/Members/MemberReviewList/MemberReviewList.tsx @@ -48,11 +48,7 @@ const MemberReviewList = ({ isMemberPage = false }: MemberReviewListProps) => { {reviewsToDisplay.map((reviewRanking) => (
  • - +
  • diff --git a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx b/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx index 7b6c8272c..99f10d5f3 100644 --- a/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx +++ b/frontend/src/components/Rank/ReviewRankingList/ReviewRankingList.tsx @@ -28,7 +28,7 @@ const ReviewRankingList = ({ isHomePage = false }: ReviewRankingListProps) => {
  • diff --git a/frontend/src/constants/path.ts b/frontend/src/constants/path.ts index 6fd8735c8..f729a74b2 100644 --- a/frontend/src/constants/path.ts +++ b/frontend/src/constants/path.ts @@ -4,5 +4,6 @@ export const PATH = { PRODUCT_LIST: '/products', MEMBER: '/members', RECIPE: '/recipes', + REVIEW: '/reviews', LOGIN: '/login', } as const; diff --git a/frontend/src/hooks/queries/review/index.ts b/frontend/src/hooks/queries/review/index.ts index e8bb44d4a..78fd628d2 100644 --- a/frontend/src/hooks/queries/review/index.ts +++ b/frontend/src/hooks/queries/review/index.ts @@ -1,3 +1,4 @@ export { default as useReviewTagsQuery } from './useReviewTagsQuery'; export { default as useReviewFavoriteMutation } from './useReviewFavoriteMutation'; export { default as useReviewRegisterFormMutation } from './useReviewRegisterFormMutation'; +export { default as useReviewDetailQuery } from './useReviewDetailQuery'; diff --git a/frontend/src/hooks/queries/review/useReviewDetailQuery.ts b/frontend/src/hooks/queries/review/useReviewDetailQuery.ts new file mode 100644 index 000000000..0b9c7be20 --- /dev/null +++ b/frontend/src/hooks/queries/review/useReviewDetailQuery.ts @@ -0,0 +1,16 @@ +import { useSuspendedQuery } from '../useSuspendedQuery'; + +import { reviewApi } from '@/apis'; +import type { ReviewDetailResponse } from '@/types/response'; + +const fetchReviewDetail = async () => { + const response = await reviewApi.get({ params: '/reviews' }); + const data: ReviewDetailResponse = await response.json(); + return data; +}; + +const useReviewDetailQuery = () => { + return useSuspendedQuery(['review'], () => fetchReviewDetail()); +}; + +export default useReviewDetailQuery; diff --git a/frontend/src/mocks/data/reviewDetail.json b/frontend/src/mocks/data/reviewDetail.json new file mode 100644 index 000000000..4439f7d43 --- /dev/null +++ b/frontend/src/mocks/data/reviewDetail.json @@ -0,0 +1,29 @@ +{ + "reviews": { + "id": 1, + "userName": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "image": "https://i.namu.wiki/i/9wnvUaEa1EkDqG-M0Pbwfdf19FJQQXV_-bnlU2SYaNcG05y2wbabiIrfrGES1M4xSgDjY39RwOvLNggDd3Huuw.webp", + "rating": 4.5, + "tags": [ + { + "id": 5, + "name": "단짠단짠", + "tagType": "TASTE" + }, + { + "id": 1, + "name": "망고망고", + "tagType": "TASTE" + } + ], + "content": "맛있어용~!~!", + "rebuy": true, + "favoriteCount": 1320, + "favorite": true, + "createdAt": "2023-10-13T00:00:00", + "categoryType": "food", + "productId": 1, + "productName": "칠성 사이다" + } +} diff --git a/frontend/src/mocks/handlers/reviewHandlers.ts b/frontend/src/mocks/handlers/reviewHandlers.ts index 455714f52..5c007c605 100644 --- a/frontend/src/mocks/handlers/reviewHandlers.ts +++ b/frontend/src/mocks/handlers/reviewHandlers.ts @@ -1,6 +1,7 @@ import { rest } from 'msw'; import { isReviewSortOption, isSortOrder } from './utils'; +import mockReviewDetail from '../data/reviewDetail.json'; import mockReviewRanking from '../data/reviewRankingList.json'; import mockReviews from '../data/reviews.json'; import mockReviewTags from '../data/reviewTagList.json'; @@ -73,4 +74,8 @@ export const reviewHandlers = [ rest.get('/api/tags', (_, res, ctx) => { return res(ctx.status(200), ctx.json(mockReviewTags)); }), + + rest.get('/api/reviews/:reviewId', (_, res, ctx) => { + return res(ctx.status(200), ctx.json(mockReviewDetail)); + }), ]; diff --git a/frontend/src/pages/ReviewDetailPage.tsx b/frontend/src/pages/ReviewDetailPage.tsx new file mode 100644 index 000000000..92286ddc5 --- /dev/null +++ b/frontend/src/pages/ReviewDetailPage.tsx @@ -0,0 +1,129 @@ +import { Badge, Spacing, Text, useTheme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { SectionTitle, SvgIcon, TagList } from '@/components/Common'; +import { PATH } from '@/constants/path'; +import { useReviewDetailQuery } from '@/hooks/queries/review'; +import { getRelativeDate } from '@/utils/date'; + +export const ReviewDetailPage = () => { + const { data: reviewDetail } = useReviewDetailQuery(); + const { + productName, + categoryType, + productId, + profileImage, + userName, + rating, + createdAt, + rebuy, + image, + tags, + content, + favoriteCount, + } = reviewDetail.reviews; + + const theme = useTheme(); + + return ( + + + + + + + +
    + {userName} + + {Array.from({ length: 5 }, (_, index) => ( + + ))} + + {getRelativeDate(createdAt)} + + +
    +
    + {rebuy && ( + + 😝 또 살래요 + + )} +
    + {image && } + + {content} + + + + {favoriteCount} + + +
    +
    + ); +}; + +const ReviewDetailPageContainer = styled.div` + padding: 20px 20px 0; +`; + +const ReviewItemContainer = styled.div` + display: flex; + flex-direction: column; + row-gap: 20px; +`; + +const ReviewerWrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +const ReviewerInfoWrapper = styled.div` + display: flex; + align-items: center; + column-gap: 10px; +`; + +const RebuyBadge = styled(Badge)` + font-weight: ${({ theme }) => theme.fontWeights.bold}; +`; + +const ReviewerImage = styled.img` + border: 2px solid ${({ theme }) => theme.colors.primary}; + border-radius: 50%; + object-fit: cover; +`; + +const RatingIconWrapper = styled.div` + display: flex; + align-items: center; + margin-left: -2px; + + & > span { + margin-left: 12px; + } +`; + +const ReviewImage = styled.img` + align-self: center; +`; + +const ReviewContent = styled(Text)` + white-space: pre-wrap; +`; + +const FavoriteWrapper = styled.div` + display: flex; + align-items: center; + padding: 0; + column-gap: 8px; +`; diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 0103d0fc7..44cb6b9a8 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -60,6 +60,15 @@ const router = createBrowserRouter([ return { Component: MemberRecipePage }; }, }, + { + path: `${PATH.REVIEW}/:reviewId`, + async lazy() { + const { ReviewDetailPage } = await import( + /* webpackChunkName: "ReviewDetailPage" */ '@/pages/ReviewDetailPage' + ); + return { Component: ReviewDetailPage }; + }, + }, ], }, { diff --git a/frontend/src/types/response.ts b/frontend/src/types/response.ts index 7ab144bc0..6cfa8dd50 100644 --- a/frontend/src/types/response.ts +++ b/frontend/src/types/response.ts @@ -1,7 +1,7 @@ import type { Product } from './product'; import type { ProductRanking, RecipeRanking, ReviewRanking } from './ranking'; import type { MemberRecipe, Recipe } from './recipe'; -import type { Review } from './review'; +import type { Review, ReviewDetail } from './review'; import type { ProductSearchResult, ProductSearchAutocomplete } from './search'; export interface Page { @@ -63,3 +63,7 @@ export interface MemberRecipeResponse { page: Page; recipes: MemberRecipe[]; } + +export interface ReviewDetailResponse { + reviews: ReviewDetail; +} diff --git a/frontend/src/types/review.ts b/frontend/src/types/review.ts index debf6f65e..db0f4b2ee 100644 --- a/frontend/src/types/review.ts +++ b/frontend/src/types/review.ts @@ -14,6 +14,12 @@ export interface Review { favorite: boolean; } +export interface ReviewDetail extends Review { + categoryType: string; + productId: number; + productName: string; +} + export interface ReviewTag { tagType: TagVariants; tags: Tag[]; From 1be450536e34a389f3e906dac0d327f2b22e15e4 Mon Sep 17 00:00:00 2001 From: hae-on Date: Sun, 15 Oct 2023 01:33:45 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor:=20categotyType=20string?= =?UTF-8?q?=EC=9D=84=20CategoryVaraint=20type=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/types/ranking.ts | 3 ++- frontend/src/types/review.ts | 4 ++-- frontend/src/types/search.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/types/ranking.ts b/frontend/src/types/ranking.ts index 9f0707f68..01c669e0f 100644 --- a/frontend/src/types/ranking.ts +++ b/frontend/src/types/ranking.ts @@ -1,3 +1,4 @@ +import type { CategoryVariant } from './common'; import type { Member } from './member'; import type { Product } from './product'; @@ -10,7 +11,7 @@ export interface ReviewRanking { content: string; rating: number; favoriteCount: number; - categoryType: string; + categoryType: CategoryVariant; } export interface RecipeRanking { diff --git a/frontend/src/types/review.ts b/frontend/src/types/review.ts index db0f4b2ee..ced1d2f58 100644 --- a/frontend/src/types/review.ts +++ b/frontend/src/types/review.ts @@ -1,4 +1,4 @@ -import type { Tag, TagVariants } from './common'; +import type { CategoryVariant, Tag, TagVariants } from './common'; export interface Review { id: number; @@ -15,7 +15,7 @@ export interface Review { } export interface ReviewDetail extends Review { - categoryType: string; + categoryType: CategoryVariant; productId: number; productName: string; } diff --git a/frontend/src/types/search.ts b/frontend/src/types/search.ts index 75d5d816d..78f293f7f 100644 --- a/frontend/src/types/search.ts +++ b/frontend/src/types/search.ts @@ -1,7 +1,8 @@ +import type { CategoryVariant } from './common'; import type { Product } from './product'; export interface ProductSearchResult extends Product { - categoryType: string; + categoryType: CategoryVariant; } export type ProductSearchAutocomplete = Pick; From 2401b6f63785aaf9f66680019380af4fc5449e7f Mon Sep 17 00:00:00 2001 From: hae-on Date: Sun, 15 Oct 2023 20:39:46 +0900 Subject: [PATCH 08/11] =?UTF-8?q?chore:=20yarn=20=EC=9E=AC=EC=84=A4?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 3 ++- frontend/yarn.lock | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 51619c2d1..ef994ca5e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,7 +13,7 @@ "test:coverage": "jest --watchAll --coverage" }, "dependencies": { - "@fun-eat/design-system": "^0.3.13", + "@fun-eat/design-system": "^0.3.15", "@tanstack/react-query": "^4.32.6", "@tanstack/react-query-devtools": "^4.32.6", "dayjs": "^1.11.9", @@ -46,6 +46,7 @@ "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "babel-plugin-styled-components": "^2.1.4", + "browser-image-compression": "^2.0.2", "copy-webpack-plugin": "^11.0.0", "dotenv-webpack": "^8.0.1", "eslint": "^8.44.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index da508d2b9..3cdae7d06 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1375,10 +1375,10 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== -"@fun-eat/design-system@^0.3.13": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.3.13.tgz#fbb48efff05c95883889dff280e118204de6d459" - integrity sha512-+wlTfWAJ3Z0ZmnJ2GyxX+HSQB8eB3g9PY8Blemv8nAk5ppuWbB9UKjnhebNgdtbtq+AN4HezKmbNl1Y+prxcWA== +"@fun-eat/design-system@^0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.3.15.tgz#61a9a01a82f84fa5627c49bd646cb72ca9e648c8" + integrity sha512-uhn5UZWfvQhNz/2sOoMwDr7Hj7SSx94bN35jifuYpm7ju0A8LHfivmu0mAbrMojuQ6XKYf0ZUME8FMMHwpw9Fg== "@humanwhocodes/config-array@^0.11.11": version "0.11.11" @@ -4497,6 +4497,13 @@ browser-assert@^1.2.1: resolved "https://registry.yarnpkg.com/browser-assert/-/browser-assert-1.2.1.tgz#9aaa5a2a8c74685c2ae05bfe46efd606f068c200" integrity sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ== +browser-image-compression@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/browser-image-compression/-/browser-image-compression-2.0.2.tgz#4d5ef8882e9e471d6d923715ceb9034499d14eaa" + integrity sha512-pBLlQyUf6yB8SmmngrcOw3EoS4RpQ1BcylI3T9Yqn7+4nrQTXJD4sJDe5ODnJdrvNMaio5OicFo75rDyJD2Ucw== + dependencies: + uzip "0.20201231.0" + browserify-zlib@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" @@ -11068,6 +11075,11 @@ uuid@^9.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== +uzip@0.20201231.0: + version "0.20201231.0" + resolved "https://registry.yarnpkg.com/uzip/-/uzip-0.20201231.0.tgz#9e64b065b9a8ebf26eb7583fe8e77e1d9a15ed14" + integrity sha512-OZeJfZP+R0z9D6TmBgLq2LHzSSptGMGDGigGiEe0pr8UBe/7fdflgHlHBNDASTXB5jnFuxHpNaJywSg8YFeGng== + v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1: version "9.1.2" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.2.tgz#51168df21c8ca01c83285f27316549b2c51a5b46" From 2c9dab9cc64738dc207846d1beb0c3bd67deb4e8 Mon Sep 17 00:00:00 2001 From: hae-on Date: Sun, 15 Oct 2023 20:40:28 +0900 Subject: [PATCH 09/11] =?UTF-8?q?refactor:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20css=20=EA=B0=92=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Common/SectionTitle/SectionTitle.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx b/frontend/src/components/Common/SectionTitle/SectionTitle.tsx index 14bfea677..c9d649ddb 100644 --- a/frontend/src/components/Common/SectionTitle/SectionTitle.tsx +++ b/frontend/src/components/Common/SectionTitle/SectionTitle.tsx @@ -21,14 +21,10 @@ const SectionTitle = ({ name, link }: SectionTitleProps) => { {link ? ( - - {name} - + {name} ) : ( - - {name} - + {name} )} {link && } From eceaf7a11f45f0036c692c8444b10ed882d99434 Mon Sep 17 00:00:00 2001 From: hae-on Date: Sun, 15 Oct 2023 20:40:42 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor:=20params=20review=20id=20?= =?UTF-8?q?=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/queries/review/useReviewDetailQuery.ts | 8 ++++---- frontend/src/pages/ReviewDetailPage.tsx | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/src/hooks/queries/review/useReviewDetailQuery.ts b/frontend/src/hooks/queries/review/useReviewDetailQuery.ts index 0b9c7be20..60675ccd6 100644 --- a/frontend/src/hooks/queries/review/useReviewDetailQuery.ts +++ b/frontend/src/hooks/queries/review/useReviewDetailQuery.ts @@ -3,14 +3,14 @@ import { useSuspendedQuery } from '../useSuspendedQuery'; import { reviewApi } from '@/apis'; import type { ReviewDetailResponse } from '@/types/response'; -const fetchReviewDetail = async () => { - const response = await reviewApi.get({ params: '/reviews' }); +const fetchReviewDetail = async (reviewId: number) => { + const response = await reviewApi.get({ params: `/${reviewId}` }); const data: ReviewDetailResponse = await response.json(); return data; }; -const useReviewDetailQuery = () => { - return useSuspendedQuery(['review'], () => fetchReviewDetail()); +const useReviewDetailQuery = (reviewId: number) => { + return useSuspendedQuery(['review'], () => fetchReviewDetail(reviewId)); }; export default useReviewDetailQuery; diff --git a/frontend/src/pages/ReviewDetailPage.tsx b/frontend/src/pages/ReviewDetailPage.tsx index 92286ddc5..7447e88e2 100644 --- a/frontend/src/pages/ReviewDetailPage.tsx +++ b/frontend/src/pages/ReviewDetailPage.tsx @@ -1,4 +1,5 @@ import { Badge, Spacing, Text, useTheme } from '@fun-eat/design-system'; +import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import { SectionTitle, SvgIcon, TagList } from '@/components/Common'; @@ -7,7 +8,9 @@ import { useReviewDetailQuery } from '@/hooks/queries/review'; import { getRelativeDate } from '@/utils/date'; export const ReviewDetailPage = () => { - const { data: reviewDetail } = useReviewDetailQuery(); + const { reviewId } = useParams(); + const { data: reviewDetail } = useReviewDetailQuery(Number(reviewId)); + const { productName, categoryType, From 38b1fe6ea46672e6b278ffdf69e9eb3cd200aa40 Mon Sep 17 00:00:00 2001 From: hae-on Date: Sun, 15 Oct 2023 21:15:28 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor:=20query=20key=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/queries/review/useReviewDetailQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/hooks/queries/review/useReviewDetailQuery.ts b/frontend/src/hooks/queries/review/useReviewDetailQuery.ts index 60675ccd6..a80914053 100644 --- a/frontend/src/hooks/queries/review/useReviewDetailQuery.ts +++ b/frontend/src/hooks/queries/review/useReviewDetailQuery.ts @@ -10,7 +10,7 @@ const fetchReviewDetail = async (reviewId: number) => { }; const useReviewDetailQuery = (reviewId: number) => { - return useSuspendedQuery(['review'], () => fetchReviewDetail(reviewId)); + return useSuspendedQuery(['review', reviewId, 'detail'], () => fetchReviewDetail(reviewId)); }; export default useReviewDetailQuery;