From 17a0da4cb30c4ac95f4cabe68a7c7ac15e801420 Mon Sep 17 00:00:00 2001 From: chsua <113416448+chsua@users.noreply.github.com> Date: Fri, 4 Aug 2023 10:06:12 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20api=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B8=B0=ED=83=80=20=EB=93=B1=EB=93=B1=5FFeat/#238?= =?UTF-8?q?=20(#247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: (#238) 카테고리 api 속성이 달라 발생하는 오류 수정 - api 필드 isFavorite이 favorite으로 되어있었음 * fix: 카테고리 토글에 따른 즉시 렌더링 안되는 오류 수정 - 리액트 쿼리 키에서 favorite 삭제 * fix: 댓글 수정 시 발생하는 오류 해결 - 수정된 댓글 콘텐츠만 전달하여 발생한 id 못찾는 오류를 댓글 객체 전체를 전달하는 방식으로 수정하여 해결 * fix: 글 작성자는 투표 결과를 바로 볼 수 있고 투표 할 수 없도록 수정 * feat: 통계 api response 형식 수정에 따른 반영 --- frontend/__test__/api/categoryList.test.ts | 4 +- frontend/src/api/categoryList.ts | 2 +- frontend/src/api/comment.ts | 4 +- frontend/src/api/example.ts | 2 +- frontend/src/api/voteResult.ts | 10 ++-- .../OneLineGraph/OneLineGraph.stories.tsx | 8 +-- .../VoteStatistics/OneLineGraph/index.tsx | 19 +++---- .../TwoLineGraph/TwoLineGraph.stories.tsx | 8 +-- .../VoteStatistics/TwoLineGraph/index.tsx | 25 ++++------ .../VoteStatistics/VoteStatistics.stories.tsx | 6 +-- .../src/components/VoteStatistics/index.tsx | 18 +++++-- .../src/components/VoteStatistics/type.ts | 50 ++++++++++++------- .../src/components/VoteStatistics/util.ts | 22 ++++++++ .../comment/CommentList/CommentItem/index.tsx | 2 +- .../CommentTextForm.stories.tsx | 15 +++++- .../CommentList/CommentTextForm/index.tsx | 29 ++++++----- .../components/comment/CommentList/index.tsx | 13 ++++- frontend/src/components/common/Post/index.tsx | 9 +++- .../WrittenVoteOptionList.stories.tsx | 4 ++ .../WrittenVoteOptionList/index.tsx | 4 +- .../category/useCategoryFavoriteToggle.ts | 2 +- .../hooks/query/category/useCategoryList.ts | 2 +- .../src/hooks/query/comment/useEditComment.ts | 10 ++-- frontend/src/hooks/useText.ts | 6 ++- frontend/src/mocks/categoryList.ts | 4 +- frontend/src/mocks/mockData/categoryList.ts | 40 +++++++-------- frontend/src/mocks/mockData/voteResult.ts | 39 +++++++++------ .../VoteStatistics/OptionStatistics/index.tsx | 4 +- frontend/src/pages/VoteStatistics/index.tsx | 11 ++-- frontend/src/pages/post/PostDetail/index.tsx | 5 +- frontend/src/types/category.ts | 2 +- frontend/src/utils/fetch.ts | 20 ++++---- 32 files changed, 246 insertions(+), 153 deletions(-) create mode 100644 frontend/src/components/VoteStatistics/util.ts diff --git a/frontend/__test__/api/categoryList.test.ts b/frontend/__test__/api/categoryList.test.ts index ccc34acb5..58a757f2a 100644 --- a/frontend/__test__/api/categoryList.test.ts +++ b/frontend/__test__/api/categoryList.test.ts @@ -26,7 +26,7 @@ describe('카테고리에 대한 통신(카테고리 목록 조회, 즐겨찾기 }); test('회원이 카테고리 즐겨찾기를 할 수 있다.', async () => { - MOCK_CATEGORY_LIST[1].favorite = false; + MOCK_CATEGORY_LIST[1].isFavorite = false; await addFavoriteCategory(MOCK_CATEGORY_LIST[1].id); @@ -36,7 +36,7 @@ describe('카테고리에 대한 통신(카테고리 목록 조회, 즐겨찾기 }); test('회원이 카테고리 즐겨찾기를 해제할 수 있다.', async () => { - MOCK_CATEGORY_LIST[0].favorite = true; + MOCK_CATEGORY_LIST[0].isFavorite = true; await removeFavoriteCategory(MOCK_CATEGORY_LIST[0].id); diff --git a/frontend/src/api/categoryList.ts b/frontend/src/api/categoryList.ts index 7b329df08..20fc5776f 100644 --- a/frontend/src/api/categoryList.ts +++ b/frontend/src/api/categoryList.ts @@ -6,7 +6,7 @@ export const transformCategoryListResponse = (categoryList: CategoryResponse[]) return categoryList.map(category => ({ id: category.id, name: category.name, - isFavorite: category.favorite, + isFavorite: category.isFavorite, })); }; diff --git a/frontend/src/api/comment.ts b/frontend/src/api/comment.ts index 570b15e85..adcc67ab1 100644 --- a/frontend/src/api/comment.ts +++ b/frontend/src/api/comment.ts @@ -29,7 +29,9 @@ export const editComment = async ( commentId: number, updatedComment: CommentRequest ) => { - return await putFetch(`${BASE_URL}/posts/${postId}/comments/${commentId}`, updatedComment); + return await putFetch(`${BASE_URL}/posts/${postId}/comments/${commentId}`, { + content: updatedComment.content, + }); }; export const deleteComment = async (postId: number, commentId: number) => { diff --git a/frontend/src/api/example.ts b/frontend/src/api/example.ts index 00a060f29..e96f9169a 100644 --- a/frontend/src/api/example.ts +++ b/frontend/src/api/example.ts @@ -16,7 +16,7 @@ export const getCartList = async () => { }; export const createCart = async () => { - return await postFetch('api/cart', { id: 12, text: '생성' }); + return await postFetch('api/cart', { id: 12, text: '생성' }); }; export const editCart = async () => { diff --git a/frontend/src/api/voteResult.ts b/frontend/src/api/voteResult.ts index 614ba5e0f..e3960c0f5 100644 --- a/frontend/src/api/voteResult.ts +++ b/frontend/src/api/voteResult.ts @@ -1,11 +1,11 @@ -import { VoteResult } from '@components/VoteStatistics/type'; +import { VoteResultResponse } from '@components/VoteStatistics/type'; import { getFetch } from '@utils/fetch'; const BASE_URL = process.env.VOTOGETHER_BASE_URL; -export const getPostStatistics = async (postId: number): Promise => { - return await getFetch(`${BASE_URL}/posts/${postId}/options`); +export const getPostStatistics = async (postId: number): Promise => { + return await getFetch(`${BASE_URL}/posts/${postId}/options`); }; export const getOptionStatistics = async ({ @@ -14,6 +14,6 @@ export const getOptionStatistics = async ({ }: { postId: number; optionId: number; -}): Promise => { - return await getFetch(`${BASE_URL}/posts/${postId}/options/${optionId}`); +}): Promise => { + return await getFetch(`${BASE_URL}/posts/${postId}/options/${optionId}`); }; diff --git a/frontend/src/components/VoteStatistics/OneLineGraph/OneLineGraph.stories.tsx b/frontend/src/components/VoteStatistics/OneLineGraph/OneLineGraph.stories.tsx index e50dbe74c..dcc064535 100644 --- a/frontend/src/components/VoteStatistics/OneLineGraph/OneLineGraph.stories.tsx +++ b/frontend/src/components/VoteStatistics/OneLineGraph/OneLineGraph.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { MOCK_VOTE_RESULT } from '@mocks/mockData/voteResult'; +import { MOCK_DETAIL_VOTE_RESULT } from '@mocks/mockData/voteResult'; import OneLineGraph from '.'; @@ -12,13 +12,13 @@ export default meta; type Story = StoryObj; export const SizeSm: Story = { - render: () => , + render: () => , }; export const SizeMd: Story = { - render: () => , + render: () => , }; export const SizeLg: Story = { - render: () => , + render: () => , }; diff --git a/frontend/src/components/VoteStatistics/OneLineGraph/index.tsx b/frontend/src/components/VoteStatistics/OneLineGraph/index.tsx index 158eb11d8..5ce6552c3 100644 --- a/frontend/src/components/VoteStatistics/OneLineGraph/index.tsx +++ b/frontend/src/components/VoteStatistics/OneLineGraph/index.tsx @@ -1,25 +1,22 @@ import * as GS from '../GraphStyle'; -import { AGE_OPTION, GraphProps } from '../type'; +import { GraphProps } from '../type'; import * as S from './style'; -export default function OneLineGraph({ voteResult, size }: GraphProps) { - const maxVoteAmount = Math.max( - ...Object.values(voteResult.age).map(voteResult => voteResult.total) - ); +export default function OneLineGraph({ ageGroup, size }: GraphProps) { + const maxVoteAmount = Math.max(...ageGroup.map(age => age.total)); return ( - {AGE_OPTION.map(option => { - const voteResultFilteredByAge = voteResult.age[option]; - const amount = Math.floor((voteResultFilteredByAge.total / maxVoteAmount) * 100); + {ageGroup.map(ageResult => { + const amount = Math.floor((ageResult.total / maxVoteAmount) * 100); return ( - - {voteResultFilteredByAge.total} + + {ageResult.total} - {voteResultFilteredByAge.name} + {ageResult.name} ); })} diff --git a/frontend/src/components/VoteStatistics/TwoLineGraph/TwoLineGraph.stories.tsx b/frontend/src/components/VoteStatistics/TwoLineGraph/TwoLineGraph.stories.tsx index 949d0803a..2d420eb34 100644 --- a/frontend/src/components/VoteStatistics/TwoLineGraph/TwoLineGraph.stories.tsx +++ b/frontend/src/components/VoteStatistics/TwoLineGraph/TwoLineGraph.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { MOCK_VOTE_RESULT } from '@mocks/mockData/voteResult'; +import { MOCK_DETAIL_VOTE_RESULT } from '@mocks/mockData/voteResult'; import TwoLineGraph from '.'; @@ -12,13 +12,13 @@ export default meta; type Story = StoryObj; export const SizeSm: Story = { - render: () => , + render: () => , }; export const SizeMd: Story = { - render: () => , + render: () => , }; export const SizeLg: Story = { - render: () => , + render: () => , }; diff --git a/frontend/src/components/VoteStatistics/TwoLineGraph/index.tsx b/frontend/src/components/VoteStatistics/TwoLineGraph/index.tsx index 0fb36d861..01581d475 100644 --- a/frontend/src/components/VoteStatistics/TwoLineGraph/index.tsx +++ b/frontend/src/components/VoteStatistics/TwoLineGraph/index.tsx @@ -1,38 +1,33 @@ import * as GS from '../GraphStyle'; -import { AGE_OPTION, GraphProps } from '../type'; +import { GraphProps } from '../type'; import * as S from './style'; -export default function TwoLineGraph({ voteResult, size }: GraphProps) { +export default function TwoLineGraph({ ageGroup, size }: GraphProps) { const maxVoteAmount = Math.max( - ...Object.values(voteResult.age).map(voteResult => Math.max(voteResult.female, voteResult.male)) + ...ageGroup.map(voteResult => Math.max(voteResult.female, voteResult.male)) ); return ( - {AGE_OPTION.map(option => { - const voteResultFilteredByAge = voteResult.age[option]; - + {ageGroup.map(ageResult => { return ( - + - {voteResultFilteredByAge.female} + {ageResult.female} - {voteResultFilteredByAge.male} - + {ageResult.male} + - {voteResultFilteredByAge.name} + {ageResult.name} ); })} diff --git a/frontend/src/components/VoteStatistics/VoteStatistics.stories.tsx b/frontend/src/components/VoteStatistics/VoteStatistics.stories.tsx index 76ac4b4bf..acabbe502 100644 --- a/frontend/src/components/VoteStatistics/VoteStatistics.stories.tsx +++ b/frontend/src/components/VoteStatistics/VoteStatistics.stories.tsx @@ -12,13 +12,13 @@ export default meta; type Story = StoryObj; export const SizeSm: Story = { - render: () => , + render: () => , }; export const SizeMd: Story = { - render: () => , + render: () => , }; export const SizeLg: Story = { - render: () => , + render: () => , }; diff --git a/frontend/src/components/VoteStatistics/index.tsx b/frontend/src/components/VoteStatistics/index.tsx index 219107aaa..ded6552e3 100644 --- a/frontend/src/components/VoteStatistics/index.tsx +++ b/frontend/src/components/VoteStatistics/index.tsx @@ -1,15 +1,23 @@ import { MouseEvent, useState } from 'react'; +import { Size } from '@type/style'; + import OneLineGraph from './OneLineGraph'; import * as S from './style'; import TwoLineGraph from './TwoLineGraph'; -import { GraphProps } from './type'; +import { VoteResultResponse } from './type'; +import { transVoteStatisticsFormat } from './util'; interface RadioMode { all: string; gender: string; } +export interface VoteStatisticsProps { + voteResultResponse: VoteResultResponse; + size: Size; +} + const radioMode: RadioMode = { all: '전체보기', gender: '성별보기', @@ -17,11 +25,13 @@ const radioMode: RadioMode = { type RadioCategory = keyof RadioMode; -export default function VoteStatistics({ voteResult, size }: GraphProps) { +export default function VoteStatistics({ voteResultResponse, size }: VoteStatisticsProps) { const [currentRadioMode, setCurrentRadioMode] = useState('all'); const radioModeKey = Object.keys(radioMode) as RadioCategory[]; + const voteResult = transVoteStatisticsFormat(voteResultResponse); + const changeMode = (e: MouseEvent) => { const target = e.target as HTMLInputElement; const targetCategory = target.value as RadioCategory; @@ -48,8 +58,8 @@ export default function VoteStatistics({ voteResult, size }: GraphProps) { ); })} - {currentRadioMode === 'all' && } - {currentRadioMode === 'gender' && } + {currentRadioMode === 'all' && } + {currentRadioMode === 'gender' && } ); } diff --git a/frontend/src/components/VoteStatistics/type.ts b/frontend/src/components/VoteStatistics/type.ts index f1d9fe116..2ee24305b 100644 --- a/frontend/src/components/VoteStatistics/type.ts +++ b/frontend/src/components/VoteStatistics/type.ts @@ -1,31 +1,45 @@ import { Size } from '@type/style'; export interface GraphProps { - voteResult: VoteResult; + ageGroup: VoteDetailResult[]; size: Size; } -interface VoteDetailResult { - name: string; - total: number; - female: number; - male: number; -} - export const AGE_OPTION = [ - 'underTeenager', - 'teenager', - 'twenties', - 'thirties', - 'forties', - 'fifties', - 'aboveFifties', + '10대 미만', + '10대', + '20대', + '30대', + '40대', + '50대', + '60대 이상', ] as const; export type AgeCategory = (typeof AGE_OPTION)[number]; -export type VoteResultAge = Record; +export interface VoteDetailResult { + name: AgeCategory; + total: number; + female: number; + male: number; +} -export interface VoteResult extends VoteDetailResult { - age: VoteResultAge; +export interface VoteResult { + ageGroup: VoteDetailResult[]; + total: number; + female: number; + male: number; +} + +export interface VoteDetailResultResponse { + ageGroup: AgeCategory; + voteCount: number; + femaleCount: number; + maleCount: number; +} +export interface VoteResultResponse { + ageGroup: VoteDetailResultResponse[]; + totalVoteCount: number; + totalFemaleCount: number; + totalMaleCount: number; } diff --git a/frontend/src/components/VoteStatistics/util.ts b/frontend/src/components/VoteStatistics/util.ts new file mode 100644 index 000000000..003fbb104 --- /dev/null +++ b/frontend/src/components/VoteStatistics/util.ts @@ -0,0 +1,22 @@ +import { VoteResult, VoteResultResponse, VoteDetailResultResponse, VoteDetailResult } from './type'; + +const transDetailVoteStatisticsFormat = ( + voteResult: VoteDetailResultResponse +): VoteDetailResult => { + const { ageGroup, femaleCount, maleCount, voteCount } = voteResult; + + return { name: ageGroup, female: femaleCount, male: maleCount, total: voteCount }; +}; + +export const transVoteStatisticsFormat = (voteResult: VoteResultResponse): VoteResult => { + const { ageGroup, totalFemaleCount, totalMaleCount, totalVoteCount } = voteResult; + + const newAgeGroup = ageGroup.map(ageResult => transDetailVoteStatisticsFormat(ageResult)); + + return { + ageGroup: newAgeGroup, + female: totalFemaleCount, + male: totalMaleCount, + total: totalVoteCount, + }; +}; diff --git a/frontend/src/components/comment/CommentList/CommentItem/index.tsx b/frontend/src/components/comment/CommentList/CommentItem/index.tsx index b0bc772c0..79b076951 100644 --- a/frontend/src/components/comment/CommentList/CommentItem/index.tsx +++ b/frontend/src/components/comment/CommentList/CommentItem/index.tsx @@ -76,7 +76,7 @@ export default function CommentItem({ comment, userType }: CommentItemProps) { diff --git a/frontend/src/components/comment/CommentList/CommentTextForm/CommentTextForm.stories.tsx b/frontend/src/components/comment/CommentList/CommentTextForm/CommentTextForm.stories.tsx index 10cdcf88b..dac625494 100644 --- a/frontend/src/components/comment/CommentList/CommentTextForm/CommentTextForm.stories.tsx +++ b/frontend/src/components/comment/CommentList/CommentTextForm/CommentTextForm.stories.tsx @@ -11,15 +11,26 @@ const meta: Meta = { export default meta; type Story = StoryObj; +const initialComment = { + id: -1, + member: { + id: -1, + nickname: '', + }, + content: '', + createdAt: '', + isEdit: false, +}; + export const InitForm: Story = { - render: () => , + render: () => , }; export const EditForm: Story = { render: () => ( {}} /> ), diff --git a/frontend/src/components/comment/CommentList/CommentTextForm/index.tsx b/frontend/src/components/comment/CommentList/CommentTextForm/index.tsx index e12b938f6..fd91c3708 100644 --- a/frontend/src/components/comment/CommentList/CommentTextForm/index.tsx +++ b/frontend/src/components/comment/CommentList/CommentTextForm/index.tsx @@ -1,6 +1,8 @@ import { ChangeEvent } from 'react'; import { useParams } from 'react-router-dom'; +import { Comment } from '@type/comment'; + import { useCreateComment } from '@hooks/query/comment/useCreateComment'; import { useEditComment } from '@hooks/query/comment/useEditComment'; import { useText } from '@hooks/useText'; @@ -10,10 +12,9 @@ import SquareButton from '@components/common/SquareButton'; import { COMMENT_MAX_LENGTH } from '@constants/comment'; import * as S from './style'; - interface CommentTextFormProps { commentId: number; - initialComment: string; + initialComment: Comment; handleCancelClick?: () => void; } @@ -22,21 +23,25 @@ export default function CommentTextForm({ initialComment, handleCancelClick, }: CommentTextFormProps) { - const { handleTextChange, text: content } = useText(initialComment); + const { handleTextChange, text: content, resetText } = useText(initialComment.content); const params = useParams() as { postId: string }; const postId = Number(params.postId); const { mutate: createComment } = useCreateComment(postId); - const { mutate: editComment } = useEditComment(postId, commentId, { content: initialComment }); - - const updateComment = initialComment - ? () => { - editComment(); - } - : () => { - createComment({ content }); - }; + const { mutate: editComment } = useEditComment(postId, commentId, { ...initialComment, content }); + + const updateComment = + initialComment.id !== -1 + ? () => { + editComment(); + handleCancelClick && handleCancelClick(); + } + : () => { + resetText(); + createComment({ content }); + handleCancelClick && handleCancelClick(); + }; return ( diff --git a/frontend/src/components/comment/CommentList/index.tsx b/frontend/src/components/comment/CommentList/index.tsx index 43371c6b6..5a90df2f1 100644 --- a/frontend/src/components/comment/CommentList/index.tsx +++ b/frontend/src/components/comment/CommentList/index.tsx @@ -19,6 +19,17 @@ interface CommentListProps { postWriterName: string; } +const initialComment = { + id: -1, + member: { + id: -1, + nickname: '', + }, + content: '', + createdAt: '', + isEdit: false, +}; + export default function CommentList({ commentList, memberId, @@ -45,7 +56,7 @@ export default function CommentList({ {isGuest ? ( ) : ( - + )} diff --git a/frontend/src/components/common/Post/index.tsx b/frontend/src/components/common/Post/index.tsx index fdd775411..62b6351a3 100644 --- a/frontend/src/components/common/Post/index.tsx +++ b/frontend/src/components/common/Post/index.tsx @@ -1,7 +1,8 @@ -import { MouseEvent } from 'react'; +import { MouseEvent, useContext } from 'react'; import { PostInfo } from '@type/post'; +import { AuthContext } from '@hooks/context/auth'; import { useCreateVote } from '@hooks/query/post/useCreateVote'; import { useEditVote } from '@hooks/query/post/useEditVote'; @@ -19,9 +20,13 @@ interface PostProps { export default function Post({ postInfo, isPreview }: PostProps) { const { postId, category, title, writer, createTime, deadline, content, voteInfo } = postInfo; + const { loggedInfo } = useContext(AuthContext); const { mutate: createVote } = useCreateVote({ isPreview, postId }); const { mutate: editVote } = useEditVote({ isPreview, postId }); + const handleVoteClick = (newOptionId: number) => { + if (writer.nickname === loggedInfo.userInfo?.nickname) return; + if (voteInfo.selectedOptionId === newOptionId) return; if (voteInfo.selectedOptionId === POST.NOT_VOTE) { @@ -68,6 +73,8 @@ export default function Post({ postInfo, isPreview }: PostProps) { ( {}} isPreview={true} @@ -102,6 +103,7 @@ export const PreviewVoted: Story = { render: () => ( {}} isPreview={true} @@ -115,6 +117,7 @@ export const DetailNotVoted: Story = { render: () => ( {}} isPreview={false} @@ -128,6 +131,7 @@ export const DetailVoted: Story = { render: () => ( {}} isPreview={false} diff --git a/frontend/src/components/optionList/WrittenVoteOptionList/index.tsx b/frontend/src/components/optionList/WrittenVoteOptionList/index.tsx index 4d27b9469..2b86faa3e 100644 --- a/frontend/src/components/optionList/WrittenVoteOptionList/index.tsx +++ b/frontend/src/components/optionList/WrittenVoteOptionList/index.tsx @@ -7,6 +7,7 @@ import WrittenVoteOption from './WrittenVoteOption'; interface WrittenVoteOptionListProps { isPreview: boolean; + isWriter: boolean; selectedOptionId: number; voteOptionList: WrittenVoteOptionType[]; handleVoteClick: (newOptionId: number) => void; @@ -14,6 +15,7 @@ interface WrittenVoteOptionListProps { export default function WrittenVoteOptionList({ isPreview, + isWriter, voteOptionList, selectedOptionId, handleVoteClick, @@ -26,7 +28,7 @@ export default function WrittenVoteOptionList({ key={voteOption.id} {...voteOption} isPreview={isPreview} - isVoted={selectedOptionId !== POST.NOT_VOTE} + isVoted={selectedOptionId !== POST.NOT_VOTE || isWriter} isSelected={selectedOptionId === voteOption.id} handleVoteClick={() => handleVoteClick(voteOption.id)} /> diff --git a/frontend/src/hooks/query/category/useCategoryFavoriteToggle.ts b/frontend/src/hooks/query/category/useCategoryFavoriteToggle.ts index 6c45fd49a..13fa830ca 100644 --- a/frontend/src/hooks/query/category/useCategoryFavoriteToggle.ts +++ b/frontend/src/hooks/query/category/useCategoryFavoriteToggle.ts @@ -13,7 +13,7 @@ export const useCategoryFavoriteToggle = () => { isFavorite ? removeFavoriteCategory(id) : addFavoriteCategory(id), { onSuccess: () => { - queryClient.invalidateQueries([QUERY_KEY.CATEGORIES, 'favorite']); + queryClient.invalidateQueries([QUERY_KEY.CATEGORIES]); }, onError: error => { window.console.log('Category favorite toggle error', error); diff --git a/frontend/src/hooks/query/category/useCategoryList.ts b/frontend/src/hooks/query/category/useCategoryList.ts index 475f1e835..545fdac5c 100644 --- a/frontend/src/hooks/query/category/useCategoryList.ts +++ b/frontend/src/hooks/query/category/useCategoryList.ts @@ -8,7 +8,7 @@ import { QUERY_KEY } from '@constants/queryKey'; export const useCategoryList = (isLoggedIn: boolean) => { const { data, error, isLoading, isError } = useQuery( - [QUERY_KEY.CATEGORIES], + [QUERY_KEY.CATEGORIES, isLoggedIn], isLoggedIn ? getUserCategoryList : getGuestCategoryList ); diff --git a/frontend/src/hooks/query/comment/useEditComment.ts b/frontend/src/hooks/query/comment/useEditComment.ts index 57e27259a..99d2883f0 100644 --- a/frontend/src/hooks/query/comment/useEditComment.ts +++ b/frontend/src/hooks/query/comment/useEditComment.ts @@ -1,16 +1,12 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { CommentRequest, CommentResponse } from '@type/comment'; +import { Comment, CommentResponse } from '@type/comment'; import { editComment } from '@api/comment'; import { QUERY_KEY } from '@constants/queryKey'; -export const useEditComment = ( - postId: number, - commentId: number, - updatedComment: CommentRequest -) => { +export const useEditComment = (postId: number, commentId: number, updatedComment: Comment) => { const queryClient = useQueryClient(); const queryKey = [QUERY_KEY.POSTS, postId, QUERY_KEY.COMMENTS]; @@ -22,11 +18,11 @@ export const useEditComment = ( await queryClient.cancelQueries(queryKey); const oldCommentList: CommentResponse[] | undefined = queryClient.getQueryData(queryKey); // 기존 댓글 데이터의 snapshot - window.console.log('기존 댓글 데이터: ', oldCommentList); if (oldCommentList) { const updatedCommentList = oldCommentList.map(comment => { if (comment.id === commentId) return updatedComment; + return comment; }); queryClient.setQueryData(queryKey, updatedCommentList); // 낙관적 업데이트 실시 diff --git a/frontend/src/hooks/useText.ts b/frontend/src/hooks/useText.ts index c5f17aa82..5b5e3bf71 100644 --- a/frontend/src/hooks/useText.ts +++ b/frontend/src/hooks/useText.ts @@ -19,5 +19,9 @@ export const useText = (originalText: string) => { event.target.setCustomValidity(''); }; - return { text, handleTextChange }; + const resetText = () => { + setText(''); + }; + + return { text, handleTextChange, resetText }; }; diff --git a/frontend/src/mocks/categoryList.ts b/frontend/src/mocks/categoryList.ts index 71bedf18a..90423b109 100644 --- a/frontend/src/mocks/categoryList.ts +++ b/frontend/src/mocks/categoryList.ts @@ -12,13 +12,13 @@ export const mockCategoryHandlers = [ }), rest.post('/categories/:categoryId/like', (req, res, ctx) => { - MOCK_CATEGORY_LIST[1].favorite = true; + MOCK_CATEGORY_LIST[1].isFavorite = true; return res(ctx.status(201), ctx.json({ message: '카테고리 즐겨찾기 등록 성공' })); }), rest.delete('/categories/:categoryId/like', (req, res, ctx) => { - MOCK_CATEGORY_LIST[0].favorite = false; + MOCK_CATEGORY_LIST[0].isFavorite = false; return res(ctx.status(204)); }), diff --git a/frontend/src/mocks/mockData/categoryList.ts b/frontend/src/mocks/mockData/categoryList.ts index b8633d038..0e6efb246 100644 --- a/frontend/src/mocks/mockData/categoryList.ts +++ b/frontend/src/mocks/mockData/categoryList.ts @@ -1,27 +1,27 @@ import { CategoryResponse } from '@type/category'; export const MOCK_CATEGORY_LIST: CategoryResponse[] = [ - { id: 1, name: '음식', favorite: false }, - { id: 2, name: '연애', favorite: true }, - { id: 3, name: '패션', favorite: false }, - { id: 4, name: '금융', favorite: false }, - { id: 5, name: '여행', favorite: false }, - { id: 6, name: '게임', favorite: false }, - { id: 7, name: '재테크', favorite: false }, - { id: 8, name: '요리', favorite: true }, - { id: 9, name: '개발', favorite: true }, - { id: 10, name: '전자기기', favorite: true }, + { id: 1, name: '음식', isFavorite: false }, + { id: 2, name: '연애', isFavorite: true }, + { id: 3, name: '패션', isFavorite: false }, + { id: 4, name: '금융', isFavorite: false }, + { id: 5, name: '여행', isFavorite: false }, + { id: 6, name: '게임', isFavorite: false }, + { id: 7, name: '재테크', isFavorite: false }, + { id: 8, name: '요리', isFavorite: true }, + { id: 9, name: '개발', isFavorite: true }, + { id: 10, name: '전자기기', isFavorite: true }, ]; export const MOCK_GUEST_CATEGORY_LIST: CategoryResponse[] = [ - { id: 1, name: '음식', favorite: false }, - { id: 2, name: '연애', favorite: false }, - { id: 3, name: '패션', favorite: false }, - { id: 4, name: '금융', favorite: false }, - { id: 5, name: '여행', favorite: false }, - { id: 6, name: '게임', favorite: false }, - { id: 7, name: '재테크', favorite: false }, - { id: 8, name: '요리', favorite: false }, - { id: 9, name: '개발', favorite: false }, - { id: 10, name: '전자기기', favorite: false }, + { id: 1, name: '음식', isFavorite: false }, + { id: 2, name: '연애', isFavorite: false }, + { id: 3, name: '패션', isFavorite: false }, + { id: 4, name: '금융', isFavorite: false }, + { id: 5, name: '여행', isFavorite: false }, + { id: 6, name: '게임', isFavorite: false }, + { id: 7, name: '재테크', isFavorite: false }, + { id: 8, name: '요리', isFavorite: false }, + { id: 9, name: '개발', isFavorite: false }, + { id: 10, name: '전자기기', isFavorite: false }, ]; diff --git a/frontend/src/mocks/mockData/voteResult.ts b/frontend/src/mocks/mockData/voteResult.ts index 7d9601b1c..579c4cef4 100644 --- a/frontend/src/mocks/mockData/voteResult.ts +++ b/frontend/src/mocks/mockData/voteResult.ts @@ -1,15 +1,26 @@ -export const MOCK_VOTE_RESULT = { - total: 100, - female: 30, - name: '총합', - male: 70, - age: { - underTeenager: { total: 10, female: 10, male: 0, name: '10대 미만' }, - teenager: { total: 20, female: 10, male: 10, name: '10대' }, - twenties: { total: 10, female: 2, male: 8, name: '20대' }, - thirties: { total: 20, female: 16, male: 4, name: '30대' }, - forties: { total: 40, female: 30, male: 10, name: '40대' }, - fifties: { total: 2, female: 1, male: 1, name: '50대' }, - aboveFifties: { total: 3, female: 2, male: 1, name: '60대 이상' }, - }, +import { VoteDetailResult, VoteResultResponse } from '@components/VoteStatistics/type'; + +export const MOCK_VOTE_RESULT: VoteResultResponse = { + totalVoteCount: 100, + totalFemaleCount: 30, + totalMaleCount: 70, + ageGroup: [ + { voteCount: 10, femaleCount: 10, maleCount: 0, ageGroup: '10대 미만' }, + { voteCount: 20, femaleCount: 10, maleCount: 10, ageGroup: '10대' }, + { voteCount: 10, femaleCount: 2, maleCount: 8, ageGroup: '20대' }, + { voteCount: 20, femaleCount: 16, maleCount: 4, ageGroup: '30대' }, + { voteCount: 40, femaleCount: 30, maleCount: 10, ageGroup: '40대' }, + { voteCount: 2, femaleCount: 1, maleCount: 1, ageGroup: '50대' }, + { voteCount: 3, femaleCount: 2, maleCount: 1, ageGroup: '60대 이상' }, + ], }; + +export const MOCK_DETAIL_VOTE_RESULT: VoteDetailResult[] = [ + { total: 10, female: 10, male: 0, name: '10대 미만' }, + { total: 20, female: 10, male: 10, name: '10대' }, + { total: 10, female: 2, male: 8, name: '20대' }, + { total: 20, female: 16, male: 4, name: '30대' }, + { total: 40, female: 30, male: 10, name: '40대' }, + { total: 2, female: 1, male: 1, name: '50대' }, + { total: 3, female: 2, male: 1, name: '60대 이상' }, +]; diff --git a/frontend/src/pages/VoteStatistics/OptionStatistics/index.tsx b/frontend/src/pages/VoteStatistics/OptionStatistics/index.tsx index e5ecd99e6..d7242af01 100644 --- a/frontend/src/pages/VoteStatistics/OptionStatistics/index.tsx +++ b/frontend/src/pages/VoteStatistics/OptionStatistics/index.tsx @@ -49,7 +49,9 @@ export default function OptionStatistics({ handleVoteClick={toggleOptionStatistics} /> - {isStatisticsOpen && voteResult && } + {isStatisticsOpen && voteResult && ( + + )} {isStatisticsOpen && isLoading && ( diff --git a/frontend/src/pages/VoteStatistics/index.tsx b/frontend/src/pages/VoteStatistics/index.tsx index caa30abad..8f610b4b0 100644 --- a/frontend/src/pages/VoteStatistics/index.tsx +++ b/frontend/src/pages/VoteStatistics/index.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import { useFetch } from '@hooks/useFetch'; @@ -15,7 +15,8 @@ import OptionStatistics from './OptionStatistics'; import * as S from './style'; export default function VoteStatisticsPage() { - const postId = 1; + const params = useParams() as { postId: string }; + const postId = Number(params.postId); const navigate = useNavigate(); @@ -25,7 +26,7 @@ export default function VoteStatisticsPage() { isLoading: isPostLoading, } = useFetch(() => getPost(postId)); const { - data: voteResult, + data: voteResultResponse, errorMessage: voteResultError, isLoading: isVoteResultLoading, } = useFetch(() => getPostStatistics(postId)); @@ -57,7 +58,9 @@ export default function VoteStatisticsPage() { )} - {voteResult && } + {voteResultResponse && ( + + )} {postDetail.voteInfo.options.map(option => { const { postId, voteInfo } = postDetail; diff --git a/frontend/src/pages/post/PostDetail/index.tsx b/frontend/src/pages/post/PostDetail/index.tsx index 088450e4a..0c9dec408 100644 --- a/frontend/src/pages/post/PostDetail/index.tsx +++ b/frontend/src/pages/post/PostDetail/index.tsx @@ -51,7 +51,6 @@ export default function PostDetailPage() { return
로딩중
; } - window.console.log(postData); const isWriter = postData.writer.id === memberId; const isClosed = checkClosedPost(postData.deadline); @@ -72,11 +71,11 @@ export default function PostDetailPage() { const controlPost = { setEarlyClosePost: async () => { await setEarlyClosePost(postId) - .catch(error => alert(error.message)) .then(res => { alert('게시물을 즉시마감했습니다.'); refetch(); - }); + }) + .catch(error => alert(error.message)); }, removePost: async () => { if (!isClosed) alert('마감된 게시물만 삭제 가능합니다.'); diff --git a/frontend/src/types/category.ts b/frontend/src/types/category.ts index 417bf7633..24362818e 100644 --- a/frontend/src/types/category.ts +++ b/frontend/src/types/category.ts @@ -7,5 +7,5 @@ export interface Category { export interface CategoryResponse { id: number; name: string; - favorite: boolean; + isFavorite: boolean; } diff --git a/frontend/src/utils/fetch.ts b/frontend/src/utils/fetch.ts index c59dcd3e7..e8cc04b57 100644 --- a/frontend/src/utils/fetch.ts +++ b/frontend/src/utils/fetch.ts @@ -33,25 +33,23 @@ export const getFetch = async (url: string): Promise => { } const data = await response.json(); - return data; }; -export const postFetch = async (url: string, body: T): Promise => { +export const postFetch = async (url: string, body: T): Promise => { const response = await fetch(url, { method: 'POST', body: JSON.stringify(body), headers: makeFetchHeaders(), }); - window.console.log(response); if (!response.ok) { throw new Error('에러'); } - const data = await response.json(); + // const data = await response.json(); - return data; + return; }; export const putFetch = async (url: string, body: T): Promise => { @@ -61,13 +59,13 @@ export const putFetch = async (url: string, body: T): Promise => headers: makeFetchHeaders(), }); - const data = await response.json(); + // const data = await response.json(); if (!response.ok) { - throw new Error(data.message); + throw new Error('error'); } - return data; + return; }; export const patchFetch = async (url: string, body?: T) => { @@ -100,13 +98,13 @@ export const multiPostFetch = async (url: string, body: FormData) => { headers: makeFetchMultiHeaders(), }); - const data = await response.json(); + // const data = await response.json(); if (!response.ok) { - throw new Error(data.message); + throw new Error('error'); } - return data; + return; }; export const multiPutFetch = async (url: string, body: FormData) => {