From 162d5617f3845e91e0f5b3b86122e46c914277f5 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Wed, 11 Oct 2023 16:00:33 +0900 Subject: [PATCH 01/22] =?UTF-8?q?feat:=20CommentItem=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommentItem/CommentItem.stories.tsx | 18 +++++++ .../Recipe/CommentItem/CommentItem.tsx | 49 +++++++++++++++++++ frontend/src/components/Recipe/index.ts | 1 + frontend/src/types/recipe.ts | 7 +++ 4 files changed, 75 insertions(+) create mode 100644 frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx create mode 100644 frontend/src/components/Recipe/CommentItem/CommentItem.tsx diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx new file mode 100644 index 000000000..fa4af163f --- /dev/null +++ b/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import CommentItem from './CommentItem'; + +import comments from '@/mocks/data/comments.json'; + +const meta: Meta = { + title: 'recipe/CommentItem', + component: CommentItem, + args: { + comment: comments[0], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.tsx new file mode 100644 index 000000000..8993c1b32 --- /dev/null +++ b/frontend/src/components/Recipe/CommentItem/CommentItem.tsx @@ -0,0 +1,49 @@ +import { Divider, Text, useTheme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import type { Comment } from '@/types/recipe'; +import { getFormattedDate } from '@/utils/date'; + +interface CommentItemProps { + comment: Comment; +} + +const CommentItem = ({ comment }: CommentItemProps) => { + const theme = useTheme(); + const { author, content, createdAt } = comment; + + return ( + <> + + +
+ + {author.nickname} 님 + + + {getFormattedDate(createdAt)} + +
+
+ {content} + + + ); +}; + +export default CommentItem; + +const AuthorWrapper = styled.div` + display: flex; + gap: 12px; + align-items: center; +`; + +const AuthorProfileImage = styled.img` + border: 1px solid ${({ theme }) => theme.colors.primary}; + border-radius: 50%; +`; + +const CommentContent = styled(Text)` + margin: 16px 0; +`; diff --git a/frontend/src/components/Recipe/index.ts b/frontend/src/components/Recipe/index.ts index f0ecdf5f6..395f5c692 100644 --- a/frontend/src/components/Recipe/index.ts +++ b/frontend/src/components/Recipe/index.ts @@ -5,3 +5,4 @@ export { default as RecipeItem } from './RecipeItem/RecipeItem'; export { default as RecipeList } from './RecipeList/RecipeList'; export { default as RecipeRegisterForm } from './RecipeRegisterForm/RecipeRegisterForm'; export { default as RecipeFavorite } from './RecipeFavorite/RecipeFavorite'; +export { default as CommentItem } from './CommentItem/CommentItem'; diff --git a/frontend/src/types/recipe.ts b/frontend/src/types/recipe.ts index 3fec2ba91..269058fef 100644 --- a/frontend/src/types/recipe.ts +++ b/frontend/src/types/recipe.ts @@ -39,3 +39,10 @@ export interface RecipeFavoriteRequestBody { type RecipeProductWithPrice = Pick; export type RecipeProduct = Omit; + +export interface Comment { + id: number; + author: Member; + content: string; + createdAt: string; +} From 6a479966281b89e2f2737da94c3bf075fd0dd165 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Wed, 11 Oct 2023 16:06:42 +0900 Subject: [PATCH 02/22] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/queries/recipe/index.ts | 1 + .../queries/recipe/useRecipeCommentQuery.ts | 16 ++++++++++ frontend/src/mocks/data/comments.json | 29 +++++++++++++++++++ frontend/src/mocks/handlers/recipeHandlers.ts | 5 ++++ 4 files changed, 51 insertions(+) create mode 100644 frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts create mode 100644 frontend/src/mocks/data/comments.json diff --git a/frontend/src/hooks/queries/recipe/index.ts b/frontend/src/hooks/queries/recipe/index.ts index ef871dadd..59162e25b 100644 --- a/frontend/src/hooks/queries/recipe/index.ts +++ b/frontend/src/hooks/queries/recipe/index.ts @@ -2,3 +2,4 @@ export { default as useRecipeDetailQuery } from './useRecipeDetailQuery'; export { default as useRecipeRegisterFormMutation } from './useRecipeRegisterFormMutation'; export { default as useRecipeFavoriteMutation } from './useRecipeFavoriteMutation'; export { default as useInfiniteRecipesQuery } from './useInfiniteRecipesQuery'; +export { default as useRecipeCommentQuery } from './useRecipeCommentQuery'; diff --git a/frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts b/frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts new file mode 100644 index 000000000..1fd1451a0 --- /dev/null +++ b/frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts @@ -0,0 +1,16 @@ +import { useSuspendedQuery } from '../useSuspendedQuery'; + +import { recipeApi } from '@/apis'; +import type { Comment } from '@/types/recipe'; + +const fetchRecipeComments = async (recipeId: number) => { + const response = await recipeApi.get({ params: `/${recipeId}/comments` }); + const data: Comment[] = await response.json(); + return data; +}; + +const useRecipeCommentQuery = (recipeId: number) => { + return useSuspendedQuery(['recipeComment', recipeId], () => fetchRecipeComments(recipeId)); +}; + +export default useRecipeCommentQuery; diff --git a/frontend/src/mocks/data/comments.json b/frontend/src/mocks/data/comments.json new file mode 100644 index 000000000..559b40cfd --- /dev/null +++ b/frontend/src/mocks/data/comments.json @@ -0,0 +1,29 @@ +[ + { + "author": { + "nickname": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" + }, + "content": "저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. ", + "createdAt": "2023-08-09T10:10:10", + "id": 1 + }, + { + "author": { + "nickname": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" + }, + "content": "string", + "createdAt": "2023-08-09T10:10:10", + "id": 1 + }, + { + "author": { + "nickname": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" + }, + "content": "string", + "createdAt": "2023-08-09T10:10:10", + "id": 1 + } +] diff --git a/frontend/src/mocks/handlers/recipeHandlers.ts b/frontend/src/mocks/handlers/recipeHandlers.ts index 0a37b22f0..b50d6beb2 100644 --- a/frontend/src/mocks/handlers/recipeHandlers.ts +++ b/frontend/src/mocks/handlers/recipeHandlers.ts @@ -1,6 +1,7 @@ import { rest } from 'msw'; import { isRecipeSortOption, isSortOrder } from './utils'; +import comments from '../data/comments.json'; import recipeDetail from '../data/recipeDetail.json'; import mockRecipes from '../data/recipes.json'; @@ -88,4 +89,8 @@ export const recipeHandlers = [ ctx.json({ ...sortedRecipes, recipes: sortedRecipes.recipes.slice(page * 5, (page + 1) * 5) }) ); }), + + rest.get('/api/recipes/:recipeId/comments', (req, res, ctx) => { + return res(ctx.status(200), ctx.json(comments)); + }), ]; From 7d849bc9ea0bae945dd27377bd4037aa8ce84c9d Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Wed, 11 Oct 2023 16:07:12 +0900 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=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 --- .../Recipe/CommentItem/CommentItem.tsx | 3 ++- frontend/src/pages/RecipeDetailPage.tsx | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.tsx index 8993c1b32..519cfd6af 100644 --- a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx +++ b/frontend/src/components/Recipe/CommentItem/CommentItem.tsx @@ -1,4 +1,4 @@ -import { Divider, Text, useTheme } from '@fun-eat/design-system'; +import { Divider, Spacing, Text, useTheme } from '@fun-eat/design-system'; import styled from 'styled-components'; import type { Comment } from '@/types/recipe'; @@ -27,6 +27,7 @@ const CommentItem = ({ comment }: CommentItemProps) => { {content} + ); }; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index 61b1ab585..66102408b 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -1,17 +1,18 @@ -import { Heading, Spacing, Text, theme } from '@fun-eat/design-system'; +import { Divider, Heading, Spacing, Text, theme } from '@fun-eat/design-system'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import RecipePreviewImage from '@/assets/plate.svg'; import { SectionTitle } from '@/components/Common'; -import { RecipeFavorite } from '@/components/Recipe'; -import { useRecipeDetailQuery } from '@/hooks/queries/recipe'; +import { CommentItem, RecipeFavorite } from '@/components/Recipe'; +import { useRecipeCommentQuery, useRecipeDetailQuery } from '@/hooks/queries/recipe'; import { getFormattedDate } from '@/utils/date'; export const RecipeDetailPage = () => { const { recipeId } = useParams(); const { data: recipeDetail } = useRecipeDetailQuery(Number(recipeId)); + const { data: recipeComments } = useRecipeCommentQuery(Number(recipeId)); const { id, images, title, content, author, products, totalPrice, favoriteCount, favorite, createdAt } = recipeDetail; return ( @@ -65,7 +66,16 @@ export const RecipeDetailPage = () => { {content} - + + + + + 댓글 ({recipeComments.length}개) + + + {recipeComments.map((comment) => ( + + ))} ); }; From 5dc497fcdc1f33ff8e762e2363af929d52352b0a Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 10:17:03 +0900 Subject: [PATCH 04/22] =?UTF-8?q?feat:=20Input=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=86=8D=EC=84=B1=EC=97=90=20minWidth?= =?UTF-8?q?=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Common/Input/Input.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Common/Input/Input.tsx b/frontend/src/components/Common/Input/Input.tsx index 59f86743e..c3b3b40f1 100644 --- a/frontend/src/components/Common/Input/Input.tsx +++ b/frontend/src/components/Common/Input/Input.tsx @@ -8,6 +8,10 @@ interface InputProps extends ComponentPropsWithRef<'input'> { * Input 컴포넌트의 너비값입니다. */ customWidth?: string; + /** + * Input 컴포넌트의 최소 너비값입니다. + */ + minWidth?: string; /** * Input value에 에러가 있는지 여부입니다. */ @@ -24,12 +28,12 @@ interface InputProps extends ComponentPropsWithRef<'input'> { const Input = forwardRef( ( - { customWidth = '300px', isError = false, rightIcon, errorMessage, ...props }: InputProps, + { customWidth = '300px', minWidth, isError = false, rightIcon, errorMessage, ...props }: InputProps, ref: ForwardedRef ) => { return ( <> - + {rightIcon && {rightIcon}} @@ -43,11 +47,12 @@ Input.displayName = 'Input'; export default Input; -type InputContainerStyleProps = Pick; +type InputContainerStyleProps = Pick; type CustomInputStyleProps = Pick; const InputContainer = styled.div` position: relative; + min-width: ${({ minWidth }) => minWidth ?? 0}; max-width: ${({ customWidth }) => customWidth}; text-align: center; `; From e713a04ce68efbdba25d269385b902a78fa9db74 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 10:18:33 +0900 Subject: [PATCH 05/22] =?UTF-8?q?feat:=20CommentInput=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommentInput/CommentInput.stories.tsx | 13 ++++ .../Recipe/CommentInput/CommentInput.tsx | 72 +++++++++++++++++++ frontend/src/components/Recipe/index.ts | 1 + 3 files changed, 86 insertions(+) create mode 100644 frontend/src/components/Recipe/CommentInput/CommentInput.stories.tsx create mode 100644 frontend/src/components/Recipe/CommentInput/CommentInput.tsx diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.stories.tsx b/frontend/src/components/Recipe/CommentInput/CommentInput.stories.tsx new file mode 100644 index 000000000..8058a36a4 --- /dev/null +++ b/frontend/src/components/Recipe/CommentInput/CommentInput.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import CommentInput from './CommentInput'; + +const meta: Meta = { + title: 'recipe/CommentInput', + component: CommentInput, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx new file mode 100644 index 000000000..ff63c3c81 --- /dev/null +++ b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx @@ -0,0 +1,72 @@ +import { Button } from '@fun-eat/design-system'; +import type { ChangeEventHandler, FormEventHandler } from 'react'; +import { useState } from 'react'; +import styled from 'styled-components'; + +import { Input } from '@/components/Common'; +import { useToastActionContext } from '@/hooks/context'; +import useRecipeCommentMutation from '@/hooks/queries/recipe/useRecipeCommentMutation'; + +interface CommentInputProps { + recipeId: number; +} + +const CommentInput = ({ recipeId }: CommentInputProps) => { + const [commentValue, setCommentValue] = useState(''); + const { mutate } = useRecipeCommentMutation(recipeId); + + const { toast } = useToastActionContext(); + + const handleCommentInput: ChangeEventHandler = (e) => { + setCommentValue(e.target.value); + }; + + const handleSubmitComment: FormEventHandler = async (e) => { + e.preventDefault(); + + mutate( + { content: commentValue }, + { + onSuccess: () => { + setCommentValue(''); + toast.success('댓글이 등록되었습니다.'); + }, + onError: (error) => { + if (error instanceof Error) { + alert(error.message); + return; + } + + toast.error('댓글을 등록하는데 오류가 발생했습니다.'); + }, + } + ); + }; + + return ( + + + + 등록 + + + ); +}; + +export default CommentInput; + +const CommentInputForm = styled.form` + display: flex; + gap: 4px; + justify-content: space-around; +`; + +const SubmitButton = styled(Button)` + background: ${({ theme, disabled }) => (disabled ? theme.colors.gray2 : theme.colors.primary)}; + cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; +`; diff --git a/frontend/src/components/Recipe/index.ts b/frontend/src/components/Recipe/index.ts index 395f5c692..10d86c1c1 100644 --- a/frontend/src/components/Recipe/index.ts +++ b/frontend/src/components/Recipe/index.ts @@ -6,3 +6,4 @@ export { default as RecipeList } from './RecipeList/RecipeList'; export { default as RecipeRegisterForm } from './RecipeRegisterForm/RecipeRegisterForm'; export { default as RecipeFavorite } from './RecipeFavorite/RecipeFavorite'; export { default as CommentItem } from './CommentItem/CommentItem'; +export { default as CommentInput } from './CommentInput/CommentInput'; From f5c90fbda509a24e78941f0858e8f1038ea4ee04 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 10:18:46 +0900 Subject: [PATCH 06/22] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recipe/useRecipeCommentMutation.ts | 24 +++++++++++++++++++ frontend/src/mocks/handlers/recipeHandlers.ts | 4 ++++ frontend/src/pages/RecipeDetailPage.tsx | 4 +++- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts diff --git a/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts b/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts new file mode 100644 index 000000000..233e70d5d --- /dev/null +++ b/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts @@ -0,0 +1,24 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { recipeApi } from '@/apis'; + +interface RecipeCommentRequestBody { + content: string; +} + +const headers = { 'Content-Type': 'application/json' }; + +const postRecipeComment = (recipeId: number, body: RecipeCommentRequestBody) => { + return recipeApi.post({ params: `/${recipeId}/comments`, credentials: true }, headers, body); +}; + +const useRecipeCommentMutation = (recipeId: number) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (body: RecipeCommentRequestBody) => postRecipeComment(recipeId, body), + onSuccess: () => queryClient.invalidateQueries({ queryKey: ['recipeComment', recipeId] }), + }); +}; + +export default useRecipeCommentMutation; diff --git a/frontend/src/mocks/handlers/recipeHandlers.ts b/frontend/src/mocks/handlers/recipeHandlers.ts index b50d6beb2..b213e5926 100644 --- a/frontend/src/mocks/handlers/recipeHandlers.ts +++ b/frontend/src/mocks/handlers/recipeHandlers.ts @@ -93,4 +93,8 @@ export const recipeHandlers = [ rest.get('/api/recipes/:recipeId/comments', (req, res, ctx) => { return res(ctx.status(200), ctx.json(comments)); }), + + rest.post('/api/recipes/:recipeId/comments', (req, res, ctx) => { + return res(ctx.status(201)); + }), ]; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index 66102408b..56114ace3 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components'; import RecipePreviewImage from '@/assets/plate.svg'; import { SectionTitle } from '@/components/Common'; -import { CommentItem, RecipeFavorite } from '@/components/Recipe'; +import { CommentInput, CommentItem, RecipeFavorite } from '@/components/Recipe'; import { useRecipeCommentQuery, useRecipeDetailQuery } from '@/hooks/queries/recipe'; import { getFormattedDate } from '@/utils/date'; @@ -76,6 +76,8 @@ export const RecipeDetailPage = () => { {recipeComments.map((comment) => ( ))} + + ); }; From 33b36c03fcc5eaaa274d3851297e8fc9c00f6066 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 10:26:27 +0900 Subject: [PATCH 07/22] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EC=9E=85=EB=A0=A5=ED=95=9C=20=EA=B8=80=EC=9E=90?= =?UTF-8?q?=EC=88=98=20UI=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Recipe/CommentInput/CommentInput.tsx | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx index ff63c3c81..e1ce4b543 100644 --- a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx +++ b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx @@ -1,4 +1,4 @@ -import { Button } from '@fun-eat/design-system'; +import { Button, Text, useTheme } from '@fun-eat/design-system'; import type { ChangeEventHandler, FormEventHandler } from 'react'; import { useState } from 'react'; import styled from 'styled-components'; @@ -11,10 +11,13 @@ interface CommentInputProps { recipeId: number; } +const MAX_COMMENT_LENGTH = 200; + const CommentInput = ({ recipeId }: CommentInputProps) => { const [commentValue, setCommentValue] = useState(''); const { mutate } = useRecipeCommentMutation(recipeId); + const theme = useTheme(); const { toast } = useToastActionContext(); const handleCommentInput: ChangeEventHandler = (e) => { @@ -44,17 +47,22 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { }; return ( - - - - 등록 - - + <> + + + + 등록 + + + + {commentValue.length} / {MAX_COMMENT_LENGTH} + + ); }; From 9a09d112de37814a61f4ff8cb8c6b166ecd23be0 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 13:11:31 +0900 Subject: [PATCH 08/22] =?UTF-8?q?feat:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Recipe/CommentInput/CommentInput.tsx | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx index e1ce4b543..a0174b7fa 100644 --- a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx +++ b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx @@ -1,9 +1,8 @@ -import { Button, Text, useTheme } from '@fun-eat/design-system'; +import { Button, Text, Textarea, useTheme } from '@fun-eat/design-system'; import type { ChangeEventHandler, FormEventHandler } from 'react'; import { useState } from 'react'; import styled from 'styled-components'; -import { Input } from '@/components/Common'; import { useToastActionContext } from '@/hooks/context'; import useRecipeCommentMutation from '@/hooks/queries/recipe/useRecipeCommentMutation'; @@ -20,11 +19,11 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { const theme = useTheme(); const { toast } = useToastActionContext(); - const handleCommentInput: ChangeEventHandler = (e) => { + const handleCommentInput: ChangeEventHandler = (e) => { setCommentValue(e.target.value); }; - const handleSubmitComment: FormEventHandler = async (e) => { + const handleSubmitComment: FormEventHandler = (e) => { e.preventDefault(); mutate( @@ -36,7 +35,7 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { }, onError: (error) => { if (error instanceof Error) { - alert(error.message); + toast.error(error.message); return; } @@ -48,19 +47,14 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { return ( <> - - - + + + 등록 - + - {commentValue.length} / {MAX_COMMENT_LENGTH} + {commentValue.length}자 / {MAX_COMMENT_LENGTH}자 ); @@ -68,12 +62,16 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { export default CommentInput; -const CommentInputForm = styled.form` +const CommentForm = styled.form` display: flex; gap: 4px; justify-content: space-around; `; +const CommentTextarea = styled(Textarea)` + padding: 8px; +`; + const SubmitButton = styled(Button)` background: ${({ theme, disabled }) => (disabled ? theme.colors.gray2 : theme.colors.primary)}; cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; From b0a25cc078f0f22c18db1f65d3f04b76283fa5ba Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 13:32:37 +0900 Subject: [PATCH 09/22] =?UTF-8?q?feat:=20text=20area=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=81=AC=EA=B8=B0=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/Recipe/CommentInput/CommentInput.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx index a0174b7fa..cbdb7628f 100644 --- a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx +++ b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx @@ -70,6 +70,7 @@ const CommentForm = styled.form` const CommentTextarea = styled(Textarea)` padding: 8px; + font-size: 1.4rem; `; const SubmitButton = styled(Button)` From 0e5fa3785eb3d4e8ad4fd13f5bbc95aa9b8bc42b Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 13:32:57 +0900 Subject: [PATCH 10/22] =?UTF-8?q?feat:=20CommentList=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommentList/CommentList.stories.tsx | 13 +++++++++++++ .../Recipe/CommentList/CommentList.tsx | 19 +++++++++++++++++++ frontend/src/components/Recipe/index.ts | 1 + frontend/src/pages/RecipeDetailPage.tsx | 16 +++++++++++----- 4 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/Recipe/CommentList/CommentList.stories.tsx create mode 100644 frontend/src/components/Recipe/CommentList/CommentList.tsx diff --git a/frontend/src/components/Recipe/CommentList/CommentList.stories.tsx b/frontend/src/components/Recipe/CommentList/CommentList.stories.tsx new file mode 100644 index 000000000..ebad218de --- /dev/null +++ b/frontend/src/components/Recipe/CommentList/CommentList.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import CommentList from './CommentList'; + +const meta: Meta = { + title: 'recipe/CommentList', + component: CommentList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Recipe/CommentList/CommentList.tsx b/frontend/src/components/Recipe/CommentList/CommentList.tsx new file mode 100644 index 000000000..9bd8a0701 --- /dev/null +++ b/frontend/src/components/Recipe/CommentList/CommentList.tsx @@ -0,0 +1,19 @@ +import CommentItem from '../CommentItem/CommentItem'; + +import type { Comment } from '@/types/recipe'; + +interface CommentListProps { + comments: Comment[]; +} + +const CommentList = ({ comments }: CommentListProps) => { + return ( + <> + {comments.map((comment) => ( + + ))} + + ); +}; + +export default CommentList; diff --git a/frontend/src/components/Recipe/index.ts b/frontend/src/components/Recipe/index.ts index 10d86c1c1..f77f35a68 100644 --- a/frontend/src/components/Recipe/index.ts +++ b/frontend/src/components/Recipe/index.ts @@ -7,3 +7,4 @@ export { default as RecipeRegisterForm } from './RecipeRegisterForm/RecipeRegist export { default as RecipeFavorite } from './RecipeFavorite/RecipeFavorite'; export { default as CommentItem } from './CommentItem/CommentItem'; export { default as CommentInput } from './CommentInput/CommentInput'; +export { default as CommentList } from './CommentList/CommentList'; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index 56114ace3..452719e59 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -1,10 +1,12 @@ import { Divider, Heading, Spacing, Text, theme } from '@fun-eat/design-system'; +import { useQueryErrorResetBoundary } from '@tanstack/react-query'; +import { Suspense } from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import RecipePreviewImage from '@/assets/plate.svg'; -import { SectionTitle } from '@/components/Common'; -import { CommentInput, CommentItem, RecipeFavorite } from '@/components/Recipe'; +import { ErrorBoundary, ErrorComponent, Loading, SectionTitle } from '@/components/Common'; +import { CommentInput, CommentList, RecipeFavorite } from '@/components/Recipe'; import { useRecipeCommentQuery, useRecipeDetailQuery } from '@/hooks/queries/recipe'; import { getFormattedDate } from '@/utils/date'; @@ -13,6 +15,8 @@ export const RecipeDetailPage = () => { const { data: recipeDetail } = useRecipeDetailQuery(Number(recipeId)); const { data: recipeComments } = useRecipeCommentQuery(Number(recipeId)); + const { reset } = useQueryErrorResetBoundary(); + const { id, images, title, content, author, products, totalPrice, favoriteCount, favorite, createdAt } = recipeDetail; return ( @@ -73,9 +77,11 @@ export const RecipeDetailPage = () => { 댓글 ({recipeComments.length}개) - {recipeComments.map((comment) => ( - - ))} + + }> + + + From 02001e19053037d3653cea410f61513eb9d1c83a Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 13:41:09 +0900 Subject: [PATCH 11/22] =?UTF-8?q?feat:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Recipe/CommentInput/CommentInput.tsx | 11 +++++++++-- frontend/src/pages/RecipeDetailPage.tsx | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx index cbdb7628f..eb0dc45b7 100644 --- a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx +++ b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx @@ -1,4 +1,4 @@ -import { Button, Text, Textarea, useTheme } from '@fun-eat/design-system'; +import { Button, Spacing, Text, Textarea, useTheme } from '@fun-eat/design-system'; import type { ChangeEventHandler, FormEventHandler } from 'react'; import { useState } from 'react'; import styled from 'styled-components'; @@ -48,11 +48,17 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { return ( <> - + 등록 + {commentValue.length}자 / {MAX_COMMENT_LENGTH}자 @@ -69,6 +75,7 @@ const CommentForm = styled.form` `; const CommentTextarea = styled(Textarea)` + width: calc(100% - 50px); padding: 8px; font-size: 1.4rem; `; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index 452719e59..98cca75c6 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -70,9 +70,9 @@ export const RecipeDetailPage = () => { {content} - - - + + + 댓글 ({recipeComments.length}개) From 78a91ba5f1d1aca994ff990191dfd8e6fd81566d Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 13:46:36 +0900 Subject: [PATCH 12/22] =?UTF-8?q?feat:=20api=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Recipe/CommentInput/CommentInput.tsx | 2 +- .../components/Recipe/CommentItem/CommentItem.stories.tsx | 2 +- .../src/components/Recipe/CommentItem/CommentItem.tsx | 8 ++++---- .../src/components/Recipe/CommentList/CommentList.tsx | 2 +- .../src/hooks/queries/recipe/useRecipeCommentMutation.ts | 2 +- frontend/src/mocks/data/comments.json | 6 +++--- frontend/src/types/recipe.ts | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx index eb0dc45b7..211fa551d 100644 --- a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx +++ b/frontend/src/components/Recipe/CommentInput/CommentInput.tsx @@ -27,7 +27,7 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { e.preventDefault(); mutate( - { content: commentValue }, + { comment: commentValue }, { onSuccess: () => { setCommentValue(''); diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx index fa4af163f..789f0eb26 100644 --- a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx +++ b/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx @@ -8,7 +8,7 @@ const meta: Meta = { title: 'recipe/CommentItem', component: CommentItem, args: { - comment: comments[0], + recipeComment: comments[0], }, }; diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.tsx index 519cfd6af..847194b75 100644 --- a/frontend/src/components/Recipe/CommentItem/CommentItem.tsx +++ b/frontend/src/components/Recipe/CommentItem/CommentItem.tsx @@ -5,12 +5,12 @@ import type { Comment } from '@/types/recipe'; import { getFormattedDate } from '@/utils/date'; interface CommentItemProps { - comment: Comment; + recipeComment: Comment; } -const CommentItem = ({ comment }: CommentItemProps) => { +const CommentItem = ({ recipeComment }: CommentItemProps) => { const theme = useTheme(); - const { author, content, createdAt } = comment; + const { author, comment, createdAt } = recipeComment; return ( <> @@ -25,7 +25,7 @@ const CommentItem = ({ comment }: CommentItemProps) => { - {content} + {comment} diff --git a/frontend/src/components/Recipe/CommentList/CommentList.tsx b/frontend/src/components/Recipe/CommentList/CommentList.tsx index 9bd8a0701..0ab88ca8a 100644 --- a/frontend/src/components/Recipe/CommentList/CommentList.tsx +++ b/frontend/src/components/Recipe/CommentList/CommentList.tsx @@ -10,7 +10,7 @@ const CommentList = ({ comments }: CommentListProps) => { return ( <> {comments.map((comment) => ( - + ))} ); diff --git a/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts b/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts index 233e70d5d..fc599b15e 100644 --- a/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts +++ b/frontend/src/hooks/queries/recipe/useRecipeCommentMutation.ts @@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { recipeApi } from '@/apis'; interface RecipeCommentRequestBody { - content: string; + comment: string; } const headers = { 'Content-Type': 'application/json' }; diff --git a/frontend/src/mocks/data/comments.json b/frontend/src/mocks/data/comments.json index 559b40cfd..a3c5d1938 100644 --- a/frontend/src/mocks/data/comments.json +++ b/frontend/src/mocks/data/comments.json @@ -4,7 +4,7 @@ "nickname": "펀잇", "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" }, - "content": "저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. ", + "comment": "저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. ", "createdAt": "2023-08-09T10:10:10", "id": 1 }, @@ -13,7 +13,7 @@ "nickname": "펀잇", "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" }, - "content": "string", + "comment": "string", "createdAt": "2023-08-09T10:10:10", "id": 1 }, @@ -22,7 +22,7 @@ "nickname": "펀잇", "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" }, - "content": "string", + "comment": "string", "createdAt": "2023-08-09T10:10:10", "id": 1 } diff --git a/frontend/src/types/recipe.ts b/frontend/src/types/recipe.ts index 269058fef..da336d7b5 100644 --- a/frontend/src/types/recipe.ts +++ b/frontend/src/types/recipe.ts @@ -43,6 +43,6 @@ export type RecipeProduct = Omit; export interface Comment { id: number; author: Member; - content: string; + comment: string; createdAt: string; } From 7dba7c3ccc169bdaa8461544060b4f7e607de38b Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 13:51:19 +0900 Subject: [PATCH 13/22] =?UTF-8?q?refactor:=20CommentInput=20->=20CommentFo?= =?UTF-8?q?rm=EC=9C=BC=EB=A1=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=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 --- .../CommentForm.stories.tsx} | 8 ++++---- .../CommentInput.tsx => CommentForm/CommentForm.tsx} | 12 ++++++------ frontend/src/components/Recipe/index.ts | 2 +- frontend/src/pages/RecipeDetailPage.tsx | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) rename frontend/src/components/Recipe/{CommentInput/CommentInput.stories.tsx => CommentForm/CommentForm.stories.tsx} (51%) rename frontend/src/components/Recipe/{CommentInput/CommentInput.tsx => CommentForm/CommentForm.tsx} (90%) diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.stories.tsx b/frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx similarity index 51% rename from frontend/src/components/Recipe/CommentInput/CommentInput.stories.tsx rename to frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx index 8058a36a4..e65b87225 100644 --- a/frontend/src/components/Recipe/CommentInput/CommentInput.stories.tsx +++ b/frontend/src/components/Recipe/CommentForm/CommentForm.stories.tsx @@ -1,10 +1,10 @@ import type { Meta, StoryObj } from '@storybook/react'; -import CommentInput from './CommentInput'; +import CommentForm from './CommentForm'; -const meta: Meta = { - title: 'recipe/CommentInput', - component: CommentInput, +const meta: Meta = { + title: 'recipe/CommentForm', + component: CommentForm, }; export default meta; diff --git a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx similarity index 90% rename from frontend/src/components/Recipe/CommentInput/CommentInput.tsx rename to frontend/src/components/Recipe/CommentForm/CommentForm.tsx index 211fa551d..ac53b1a40 100644 --- a/frontend/src/components/Recipe/CommentInput/CommentInput.tsx +++ b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx @@ -6,13 +6,13 @@ import styled from 'styled-components'; import { useToastActionContext } from '@/hooks/context'; import useRecipeCommentMutation from '@/hooks/queries/recipe/useRecipeCommentMutation'; -interface CommentInputProps { +interface CommentFormProps { recipeId: number; } const MAX_COMMENT_LENGTH = 200; -const CommentInput = ({ recipeId }: CommentInputProps) => { +const CommentForm = ({ recipeId }: CommentFormProps) => { const [commentValue, setCommentValue] = useState(''); const { mutate } = useRecipeCommentMutation(recipeId); @@ -47,7 +47,7 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { return ( <> - +
{ 등록 - + {commentValue.length}자 / {MAX_COMMENT_LENGTH}자 @@ -66,9 +66,9 @@ const CommentInput = ({ recipeId }: CommentInputProps) => { ); }; -export default CommentInput; +export default CommentForm; -const CommentForm = styled.form` +const Form = styled.form` display: flex; gap: 4px; justify-content: space-around; diff --git a/frontend/src/components/Recipe/index.ts b/frontend/src/components/Recipe/index.ts index f77f35a68..b79761203 100644 --- a/frontend/src/components/Recipe/index.ts +++ b/frontend/src/components/Recipe/index.ts @@ -6,5 +6,5 @@ export { default as RecipeList } from './RecipeList/RecipeList'; export { default as RecipeRegisterForm } from './RecipeRegisterForm/RecipeRegisterForm'; export { default as RecipeFavorite } from './RecipeFavorite/RecipeFavorite'; export { default as CommentItem } from './CommentItem/CommentItem'; -export { default as CommentInput } from './CommentInput/CommentInput'; +export { default as CommentForm } from './CommentForm/CommentForm'; export { default as CommentList } from './CommentList/CommentList'; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index 98cca75c6..5a1807e57 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -6,7 +6,7 @@ import styled from 'styled-components'; import RecipePreviewImage from '@/assets/plate.svg'; import { ErrorBoundary, ErrorComponent, Loading, SectionTitle } from '@/components/Common'; -import { CommentInput, CommentList, RecipeFavorite } from '@/components/Recipe'; +import { CommentForm, CommentList, RecipeFavorite } from '@/components/Recipe'; import { useRecipeCommentQuery, useRecipeDetailQuery } from '@/hooks/queries/recipe'; import { getFormattedDate } from '@/utils/date'; @@ -82,7 +82,7 @@ export const RecipeDetailPage = () => { - + ); From 89af090d14730bee88bb12431dcde2f8829d1f7d Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 14:42:00 +0900 Subject: [PATCH 14/22] =?UTF-8?q?feat:=20data=20fetching=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=9D=84=20CommentList=EB=82=B4=EB=B6=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Recipe/CommentList/CommentList.tsx | 14 +++++++++++--- frontend/src/pages/RecipeDetailPage.tsx | 10 +++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Recipe/CommentList/CommentList.tsx b/frontend/src/components/Recipe/CommentList/CommentList.tsx index 0ab88ca8a..6e0535294 100644 --- a/frontend/src/components/Recipe/CommentList/CommentList.tsx +++ b/frontend/src/components/Recipe/CommentList/CommentList.tsx @@ -1,14 +1,22 @@ +import { Heading, Spacing } from '@fun-eat/design-system'; + import CommentItem from '../CommentItem/CommentItem'; -import type { Comment } from '@/types/recipe'; +import { useRecipeCommentQuery } from '@/hooks/queries/recipe'; interface CommentListProps { - comments: Comment[]; + recipeId: number; } -const CommentList = ({ comments }: CommentListProps) => { +const CommentList = ({ recipeId }: CommentListProps) => { + const { data: comments } = useRecipeCommentQuery(Number(recipeId)); + return ( <> + + 댓글 ({comments.length}개) + + {comments.map((comment) => ( ))} diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index 5a1807e57..fab578792 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -7,14 +7,14 @@ import styled from 'styled-components'; import RecipePreviewImage from '@/assets/plate.svg'; import { ErrorBoundary, ErrorComponent, Loading, SectionTitle } from '@/components/Common'; import { CommentForm, CommentList, RecipeFavorite } from '@/components/Recipe'; -import { useRecipeCommentQuery, useRecipeDetailQuery } from '@/hooks/queries/recipe'; +import { useRecipeDetailQuery } from '@/hooks/queries/recipe'; import { getFormattedDate } from '@/utils/date'; export const RecipeDetailPage = () => { const { recipeId } = useParams(); const { data: recipeDetail } = useRecipeDetailQuery(Number(recipeId)); - const { data: recipeComments } = useRecipeCommentQuery(Number(recipeId)); + const { reset } = useQueryErrorResetBoundary(); const { id, images, title, content, author, products, totalPrice, favoriteCount, favorite, createdAt } = recipeDetail; @@ -73,13 +73,9 @@ export const RecipeDetailPage = () => { - - 댓글 ({recipeComments.length}개) - - }> - + From 67a109ba0b4e5a80504b3c7bb31a79e28e02dec4 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 16:37:25 +0900 Subject: [PATCH 15/22] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EB=AC=B4?= =?UTF-8?q?=ED=95=9C=20=EC=8A=A4=ED=81=AC=EB=A1=A4=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommentItem/CommentItem.stories.tsx | 2 +- .../Recipe/CommentList/CommentList.tsx | 12 +++- frontend/src/hooks/queries/recipe/index.ts | 2 +- .../recipe/useInfiniteRecipeCommentQuery.ts | 25 ++++++++ .../queries/recipe/useRecipeCommentQuery.ts | 16 ------ frontend/src/mocks/data/comments.json | 57 ++++++++++--------- frontend/src/types/response.ts | 7 ++- 7 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts delete mode 100644 frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts diff --git a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx b/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx index 789f0eb26..70bf1f9a6 100644 --- a/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx +++ b/frontend/src/components/Recipe/CommentItem/CommentItem.stories.tsx @@ -8,7 +8,7 @@ const meta: Meta = { title: 'recipe/CommentItem', component: CommentItem, args: { - recipeComment: comments[0], + recipeComment: comments.comments[0], }, }; diff --git a/frontend/src/components/Recipe/CommentList/CommentList.tsx b/frontend/src/components/Recipe/CommentList/CommentList.tsx index 6e0535294..abfef2d9e 100644 --- a/frontend/src/components/Recipe/CommentList/CommentList.tsx +++ b/frontend/src/components/Recipe/CommentList/CommentList.tsx @@ -1,15 +1,22 @@ import { Heading, Spacing } from '@fun-eat/design-system'; +import { useRef } from 'react'; import CommentItem from '../CommentItem/CommentItem'; -import { useRecipeCommentQuery } from '@/hooks/queries/recipe'; +import { useIntersectionObserver } from '@/hooks/common'; +import { useInfiniteRecipeCommentQuery } from '@/hooks/queries/recipe'; interface CommentListProps { recipeId: number; } const CommentList = ({ recipeId }: CommentListProps) => { - const { data: comments } = useRecipeCommentQuery(Number(recipeId)); + const scrollRef = useRef(null); + + const { fetchNextPage, hasNextPage, data } = useInfiniteRecipeCommentQuery(Number(recipeId)); + useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); + + const comments = data.pages.flatMap((page) => page.comments); return ( <> @@ -20,6 +27,7 @@ const CommentList = ({ recipeId }: CommentListProps) => { {comments.map((comment) => ( ))} +
); }; diff --git a/frontend/src/hooks/queries/recipe/index.ts b/frontend/src/hooks/queries/recipe/index.ts index 59162e25b..31cab9b8b 100644 --- a/frontend/src/hooks/queries/recipe/index.ts +++ b/frontend/src/hooks/queries/recipe/index.ts @@ -2,4 +2,4 @@ export { default as useRecipeDetailQuery } from './useRecipeDetailQuery'; export { default as useRecipeRegisterFormMutation } from './useRecipeRegisterFormMutation'; export { default as useRecipeFavoriteMutation } from './useRecipeFavoriteMutation'; export { default as useInfiniteRecipesQuery } from './useInfiniteRecipesQuery'; -export { default as useRecipeCommentQuery } from './useRecipeCommentQuery'; +export { default as useInfiniteRecipeCommentQuery } from './useInfiniteRecipeCommentQuery'; diff --git a/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts b/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts new file mode 100644 index 000000000..85cc1d4d9 --- /dev/null +++ b/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts @@ -0,0 +1,25 @@ +import { useSuspendedInfiniteQuery } from '../useSuspendedInfiniteQuery'; + +import { recipeApi } from '@/apis'; +import type { CommentResponse } from '@/types/response'; + +const fetchRecipeComments = async (pageParam: number, recipeId: number) => { + const response = await recipeApi.get({ params: `/${recipeId}/comments`, queries: `?lastId=${pageParam}` }); + const data: CommentResponse = await response.json(); + return data; +}; + +const useInfiniteRecipeCommentQuery = (recipeId: number) => { + return useSuspendedInfiniteQuery( + ['recipeComment', recipeId], + ({ pageParam = 0 }) => fetchRecipeComments(pageParam, recipeId), + { + getNextPageParam: (prevResponse: CommentResponse) => { + const lastCursor = prevResponse.comments[prevResponse.comments.length - 1].id; + return prevResponse.hasNext ? lastCursor : undefined; + }, + } + ); +}; + +export default useInfiniteRecipeCommentQuery; diff --git a/frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts b/frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts deleted file mode 100644 index 1fd1451a0..000000000 --- a/frontend/src/hooks/queries/recipe/useRecipeCommentQuery.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useSuspendedQuery } from '../useSuspendedQuery'; - -import { recipeApi } from '@/apis'; -import type { Comment } from '@/types/recipe'; - -const fetchRecipeComments = async (recipeId: number) => { - const response = await recipeApi.get({ params: `/${recipeId}/comments` }); - const data: Comment[] = await response.json(); - return data; -}; - -const useRecipeCommentQuery = (recipeId: number) => { - return useSuspendedQuery(['recipeComment', recipeId], () => fetchRecipeComments(recipeId)); -}; - -export default useRecipeCommentQuery; diff --git a/frontend/src/mocks/data/comments.json b/frontend/src/mocks/data/comments.json index a3c5d1938..acf2f9b08 100644 --- a/frontend/src/mocks/data/comments.json +++ b/frontend/src/mocks/data/comments.json @@ -1,29 +1,32 @@ -[ - { - "author": { - "nickname": "펀잇", - "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" +{ + "hasNext": false, + "comments": [ + { + "author": { + "nickname": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" + }, + "comment": "저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. ", + "createdAt": "2023-08-09T10:10:10", + "id": 1 }, - "comment": "저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. 저도 먹어봤는데 맛있었어요. ", - "createdAt": "2023-08-09T10:10:10", - "id": 1 - }, - { - "author": { - "nickname": "펀잇", - "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" + { + "author": { + "nickname": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" + }, + "comment": "string", + "createdAt": "2023-08-09T10:10:10", + "id": 1 }, - "comment": "string", - "createdAt": "2023-08-09T10:10:10", - "id": 1 - }, - { - "author": { - "nickname": "펀잇", - "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" - }, - "comment": "string", - "createdAt": "2023-08-09T10:10:10", - "id": 1 - } -] + { + "author": { + "nickname": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34" + }, + "comment": "string", + "createdAt": "2023-08-09T10:10:10", + "id": 1 + } + ] +} diff --git a/frontend/src/types/response.ts b/frontend/src/types/response.ts index 7ab144bc0..7f6fd28fd 100644 --- a/frontend/src/types/response.ts +++ b/frontend/src/types/response.ts @@ -1,6 +1,6 @@ import type { Product } from './product'; import type { ProductRanking, RecipeRanking, ReviewRanking } from './ranking'; -import type { MemberRecipe, Recipe } from './recipe'; +import type { Comment, MemberRecipe, Recipe } from './recipe'; import type { Review } from './review'; import type { ProductSearchResult, ProductSearchAutocomplete } from './search'; @@ -63,3 +63,8 @@ export interface MemberRecipeResponse { page: Page; recipes: MemberRecipe[]; } + +export interface CommentResponse { + hasNext: boolean; + comments: Comment[]; +} From 996e9d0606ea8e78efa4ba7634c9708b89e99ad1 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 17:36:55 +0900 Subject: [PATCH 16/22] =?UTF-8?q?fix:=20=ED=86=A0=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EA=B0=80=20=EA=B0=80?= =?UTF-8?q?=EC=9A=B4=EB=8D=B0=20=EC=A0=95=EB=A0=AC=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Common/Toast/Toast.tsx | 2 +- frontend/src/contexts/ToastContext.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Common/Toast/Toast.tsx b/frontend/src/components/Common/Toast/Toast.tsx index 28571c6af..c9d9dc46f 100644 --- a/frontend/src/components/Common/Toast/Toast.tsx +++ b/frontend/src/components/Common/Toast/Toast.tsx @@ -27,7 +27,7 @@ type ToastStyleProps = Pick & { isAnimating?: boolean }; const ToastWrapper = styled.div` position: relative; - width: 100%; + width: calc(100% - 20px); height: 55px; max-width: 560px; border-radius: 10px; diff --git a/frontend/src/contexts/ToastContext.tsx b/frontend/src/contexts/ToastContext.tsx index 0bfdb0837..f14148646 100644 --- a/frontend/src/contexts/ToastContext.tsx +++ b/frontend/src/contexts/ToastContext.tsx @@ -75,6 +75,6 @@ const ToastContainer = styled.div` display: flex; flex-direction: column; align-items: center; - width: calc(100% - 20px); + width: 100%; transform: translate(0, -10px); `; From 6b4e689d3497ae28cca21294c4ab1740aff0e33e Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 17:37:09 +0900 Subject: [PATCH 17/22] =?UTF-8?q?feat:=20=EC=A0=84=EC=86=A1=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=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 | 5 +++++ frontend/src/components/Common/Svg/SvgIcon.tsx | 1 + frontend/src/components/Common/Svg/SvgSprite.tsx | 3 +++ 3 files changed, 9 insertions(+) diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html index c74febeca..4e44004eb 100644 --- a/frontend/.storybook/preview-body.html +++ b/frontend/.storybook/preview-body.html @@ -100,6 +100,11 @@ 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..d8d11d326 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', + 'plane', ] 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..b4811ca73 100644 --- a/frontend/src/components/Common/Svg/SvgSprite.tsx +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -74,6 +74,9 @@ const SvgSprite = () => { + + + ); }; From d6ed26b23a90ef2552dd41d455e11201ea9d335c Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 17:37:40 +0900 Subject: [PATCH 18/22] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20fixed=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 --- .../Recipe/CommentForm/CommentForm.tsx | 27 ++++++++++++++----- frontend/src/pages/RecipeDetailPage.tsx | 8 ++---- frontend/src/router/index.tsx | 18 ++++++------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx index ac53b1a40..28820c71a 100644 --- a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx +++ b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx @@ -3,6 +3,7 @@ import type { ChangeEventHandler, FormEventHandler } from 'react'; import { useState } from 'react'; import styled from 'styled-components'; +import { SvgIcon } from '@/components/Common'; import { useToastActionContext } from '@/hooks/context'; import useRecipeCommentMutation from '@/hooks/queries/recipe/useRecipeCommentMutation'; @@ -46,7 +47,7 @@ const CommentForm = ({ recipeId }: CommentFormProps) => { }; return ( - <> +
{ onChange={handleCommentInput} maxLength={MAX_COMMENT_LENGTH} /> - - 등록 + + {commentValue.length}자 / {MAX_COMMENT_LENGTH}자 - +
); }; export default CommentForm; +const CommentFormContainer = styled.div` + position: fixed; + bottom: 0; + width: calc(100% - 40px); + max-width: 540px; + padding: 16px 0; + background: ${({ theme }) => theme.backgroundColors.default}; +`; + const Form = styled.form` display: flex; gap: 4px; justify-content: space-around; + align-items: center; `; const CommentTextarea = styled(Textarea)` - width: calc(100% - 50px); + height: 50px; padding: 8px; font-size: 1.4rem; `; const SubmitButton = styled(Button)` - background: ${({ theme, disabled }) => (disabled ? theme.colors.gray2 : theme.colors.primary)}; cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; `; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index fab578792..da5f050bf 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -20,7 +20,7 @@ export const RecipeDetailPage = () => { const { id, images, title, content, author, products, totalPrice, favoriteCount, favorite, createdAt } = recipeDetail; return ( - + <> {images.length > 0 ? ( @@ -80,14 +80,10 @@ export const RecipeDetailPage = () => { - + ); }; -const RecipeDetailPageContainer = styled.div` - padding: 20px 20px 0; -`; - const RecipeImageContainer = styled.ul` display: flex; flex-direction: column; diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 0103d0fc7..1f0ee78a9 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -17,15 +17,6 @@ const router = createBrowserRouter([ ), errorElement: , children: [ - { - path: `${PATH.RECIPE}/:recipeId`, - async lazy() { - const { RecipeDetailPage } = await import( - /* webpackChunkName: "RecipeDetailPage" */ '@/pages/RecipeDetailPage' - ); - return { Component: RecipeDetailPage }; - }, - }, { path: PATH.MEMBER, async lazy() { @@ -119,6 +110,15 @@ const router = createBrowserRouter([ return { Component: ProductDetailPage }; }, }, + { + path: `${PATH.RECIPE}/:recipeId`, + async lazy() { + const { RecipeDetailPage } = await import( + /* webpackChunkName: "RecipeDetailPage" */ '@/pages/RecipeDetailPage' + ); + return { Component: RecipeDetailPage }; + }, + }, ], }, { From a860bb60960f2dd66370a2c73f9d7173e7e329f1 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 17:46:47 +0900 Subject: [PATCH 19/22] =?UTF-8?q?feat:=20=EB=8C=93=EA=B8=80=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=82=AC=EC=9D=B4=20=EA=B3=B5?= =?UTF-8?q?=EB=B0=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Recipe/CommentList/CommentList.tsx | 4 ++-- frontend/src/pages/RecipeDetailPage.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Recipe/CommentList/CommentList.tsx b/frontend/src/components/Recipe/CommentList/CommentList.tsx index abfef2d9e..5a34feb95 100644 --- a/frontend/src/components/Recipe/CommentList/CommentList.tsx +++ b/frontend/src/components/Recipe/CommentList/CommentList.tsx @@ -19,7 +19,7 @@ const CommentList = ({ recipeId }: CommentListProps) => { const comments = data.pages.flatMap((page) => page.comments); return ( - <> +
댓글 ({comments.length}개) @@ -28,7 +28,7 @@ const CommentList = ({ recipeId }: CommentListProps) => { ))}
- +
); }; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index da5f050bf..ed68acb7b 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -78,6 +78,7 @@ export const RecipeDetailPage = () => { + From 838d38e7e9d5e4ca53e7c0efb1f0b4981af4c172 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 21:38:39 +0900 Subject: [PATCH 20/22] =?UTF-8?q?feat:=20Response=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EC=97=90=20totalElements=20=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recipe/useInfiniteRecipeCommentQuery.ts | 19 +++++++++++++++---- frontend/src/types/response.ts | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts b/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts index 85cc1d4d9..994d830ef 100644 --- a/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts +++ b/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts @@ -3,8 +3,17 @@ import { useSuspendedInfiniteQuery } from '../useSuspendedInfiniteQuery'; import { recipeApi } from '@/apis'; import type { CommentResponse } from '@/types/response'; -const fetchRecipeComments = async (pageParam: number, recipeId: number) => { - const response = await recipeApi.get({ params: `/${recipeId}/comments`, queries: `?lastId=${pageParam}` }); +interface PageParam { + lastId: number; + totalElements: number | null; +} + +const fetchRecipeComments = async (pageParam: PageParam, recipeId: number) => { + const { lastId, totalElements } = pageParam; + const response = await recipeApi.get({ + params: `/${recipeId}/comments`, + queries: `?lastId=${lastId}&totalElements=${totalElements}`, + }); const data: CommentResponse = await response.json(); return data; }; @@ -12,10 +21,12 @@ const fetchRecipeComments = async (pageParam: number, recipeId: number) => { const useInfiniteRecipeCommentQuery = (recipeId: number) => { return useSuspendedInfiniteQuery( ['recipeComment', recipeId], - ({ pageParam = 0 }) => fetchRecipeComments(pageParam, recipeId), + ({ pageParam }) => fetchRecipeComments(pageParam, recipeId), { getNextPageParam: (prevResponse: CommentResponse) => { - const lastCursor = prevResponse.comments[prevResponse.comments.length - 1].id; + const lastId = prevResponse.comments[prevResponse.comments.length - 1].id; + const totalElements = prevResponse.totalElements; + const lastCursor = { lastId: lastId, totalElements: totalElements }; return prevResponse.hasNext ? lastCursor : undefined; }, } diff --git a/frontend/src/types/response.ts b/frontend/src/types/response.ts index 7f6fd28fd..fa17b469a 100644 --- a/frontend/src/types/response.ts +++ b/frontend/src/types/response.ts @@ -66,5 +66,6 @@ export interface MemberRecipeResponse { export interface CommentResponse { hasNext: boolean; + totalElements: number | null; comments: Comment[]; } From 980e205985e4fe46c752a807431b3ce10aa782e8 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 12 Oct 2023 21:43:21 +0900 Subject: [PATCH 21/22] =?UTF-8?q?feat:=20pageParam=EC=9D=98=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts b/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts index 994d830ef..460b11e92 100644 --- a/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts +++ b/frontend/src/hooks/queries/recipe/useInfiniteRecipeCommentQuery.ts @@ -21,7 +21,7 @@ const fetchRecipeComments = async (pageParam: PageParam, recipeId: number) => { const useInfiniteRecipeCommentQuery = (recipeId: number) => { return useSuspendedInfiniteQuery( ['recipeComment', recipeId], - ({ pageParam }) => fetchRecipeComments(pageParam, recipeId), + ({ pageParam = { lastId: 0, totalElements: null } }) => fetchRecipeComments(pageParam, recipeId), { getNextPageParam: (prevResponse: CommentResponse) => { const lastId = prevResponse.comments[prevResponse.comments.length - 1].id; From a08dd8e1c954d1c57703d76099c6b42c76cedfa6 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Fri, 13 Oct 2023 14:57:03 +0900 Subject: [PATCH 22/22] =?UTF-8?q?feat:=20index.ts=EC=97=90=EC=84=9C=20expo?= =?UTF-8?q?rt=EB=AC=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Recipe/CommentForm/CommentForm.tsx | 2 +- frontend/src/hooks/queries/recipe/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx index 28820c71a..33c03d1e2 100644 --- a/frontend/src/components/Recipe/CommentForm/CommentForm.tsx +++ b/frontend/src/components/Recipe/CommentForm/CommentForm.tsx @@ -5,7 +5,7 @@ import styled from 'styled-components'; import { SvgIcon } from '@/components/Common'; import { useToastActionContext } from '@/hooks/context'; -import useRecipeCommentMutation from '@/hooks/queries/recipe/useRecipeCommentMutation'; +import { useRecipeCommentMutation } from '@/hooks/queries/recipe'; interface CommentFormProps { recipeId: number; diff --git a/frontend/src/hooks/queries/recipe/index.ts b/frontend/src/hooks/queries/recipe/index.ts index 31cab9b8b..0cd5db9f6 100644 --- a/frontend/src/hooks/queries/recipe/index.ts +++ b/frontend/src/hooks/queries/recipe/index.ts @@ -3,3 +3,4 @@ export { default as useRecipeRegisterFormMutation } from './useRecipeRegisterFor export { default as useRecipeFavoriteMutation } from './useRecipeFavoriteMutation'; export { default as useInfiniteRecipesQuery } from './useInfiniteRecipesQuery'; export { default as useInfiniteRecipeCommentQuery } from './useInfiniteRecipeCommentQuery'; +export { default as useRecipeCommentMutation } from './useRecipeCommentMutation';