From a7dbc61ab958bd99c36244fc65df58bcd94d0623 Mon Sep 17 00:00:00 2001 From: Sung Ji Hyun <69228045+jhsung23@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:54:59 +0900 Subject: [PATCH] =?UTF-8?q?[FEAT]=20=EB=82=B4=20=ED=88=AC=ED=91=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20(#197)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/mypage/_components/MyVote.tsx | 114 +++++++++++++++++- .../[slug]/_component/VoteExtraDetail.tsx | 79 ++++++++++-- .../create/_components/CreateVoteForm.tsx | 2 +- src/hooks/api/vote/index.ts | 1 - src/hooks/auth/useGetUser.ts | 3 +- src/hooks/index.ts | 1 + src/hooks/useBottomSheetState.ts | 17 +++ src/hooks/vote/index.ts | 4 +- .../{api => }/vote/useCreateVoteMutation.ts | 2 +- src/hooks/vote/useDeleteVoteMutation.ts | 32 +++++ src/hooks/vote/useGetMyVote.ts | 22 ++++ 11 files changed, 256 insertions(+), 21 deletions(-) delete mode 100644 src/hooks/api/vote/index.ts create mode 100644 src/hooks/useBottomSheetState.ts rename src/hooks/{api => }/vote/useCreateVoteMutation.ts (95%) create mode 100644 src/hooks/vote/useDeleteVoteMutation.ts create mode 100644 src/hooks/vote/useGetMyVote.ts diff --git a/src/app/mypage/_components/MyVote.tsx b/src/app/mypage/_components/MyVote.tsx index ce9f92c7..4ae462d8 100644 --- a/src/app/mypage/_components/MyVote.tsx +++ b/src/app/mypage/_components/MyVote.tsx @@ -1,9 +1,119 @@ -import { EmptyVote } from '@/components/shared'; +'use client'; + +import dayjs from 'dayjs'; +import Link from 'next/link'; +import { useRef } from 'react'; + +import { Button } from '@/components/common/button'; +import { VoteCard } from '@/components/features/vote'; +import { ConfirmBottomSheet, EmptyVote, OptionBottomSheet } from '@/components/shared'; +import { Typography } from '@/foundations/typography'; +import { useBottomSheetState } from '@/hooks'; +import { useDeleteVoteMutation, useGetMyVote } from '@/hooks/vote'; +import { VoteType } from '@/types/vote'; +import { fromNowOf } from '@/utils/dates'; + +type BottomSheetType = 'askDelete' | 'selectOption'; const MyVote = () => { + const { data, status } = useGetMyVote(); + const { mutate: onDelete, isPending } = useDeleteVoteMutation(); + const { onOpenSheet, openedSheet, onCloseSheet } = useBottomSheetState(); + const deleteTarget = useRef(null); + + // TODO Suspense or ssr return ( <> - + {status === 'pending' ? ( + <> + ) : status === 'error' ? ( + <> + ) : data.length > 0 ? ( +
+ {data.map((vote) => ( + +
+
+ + {vote.category} + + + {fromNowOf(dayjs(vote.closeDate).endOf('day'))} + +
+
+ + + + 상세보기 + +
+ ))} + { + onCloseSheet(); + deleteTarget.current = null; + }} + options={[ + { + variant: 'warning', + optionLabel: '삭제', + onClick: () => onOpenSheet('askDelete'), + }, + ]} + /> + { + onCloseSheet(); + deleteTarget.current = null; + }} + title="투표를 삭제하시겠어요?" + description="투표를 삭제하면 되돌릴 수 없어요." + PrimaryButton={ + + } + SecondaryButton={ + + } + /> +
+ ) : ( + + )} ); }; diff --git a/src/app/vote/[slug]/_component/VoteExtraDetail.tsx b/src/app/vote/[slug]/_component/VoteExtraDetail.tsx index ced7422d..d99eefd5 100644 --- a/src/app/vote/[slug]/_component/VoteExtraDetail.tsx +++ b/src/app/vote/[slug]/_component/VoteExtraDetail.tsx @@ -1,9 +1,14 @@ 'use client'; +import { useRouter } from 'next/navigation'; + import { Button } from '@/components/common/button'; +import { ConfirmBottomSheet, OptionBottomSheet } from '@/components/shared'; import Profile from '@/components/shared/profile/Profile'; import { Typography } from '@/foundations/typography'; +import { useBottomSheetState } from '@/hooks'; import { useGetUser } from '@/hooks/auth'; +import { useDeleteVoteMutation } from '@/hooks/vote'; import { VoteType } from '@/types/vote'; type Props = { @@ -13,24 +18,72 @@ type Props = { category: string; }; -const VoteExtraDetail = ({ author, views, category }: Props) => { +type BottomSheetType = 'askDelete' | 'replyOption'; + +const VoteExtraDetail = ({ author, views, category, voteId }: Props) => { const { data: user } = useGetUser(); + const { mutate: onDelete, isPending } = useDeleteVoteMutation(); + + const router = useRouter(); + const { onOpenSheet, openedSheet, onCloseSheet } = useBottomSheetState(); return ( - + ); }; diff --git a/src/app/vote/create/_components/CreateVoteForm.tsx b/src/app/vote/create/_components/CreateVoteForm.tsx index 8225b648..2572f78f 100644 --- a/src/app/vote/create/_components/CreateVoteForm.tsx +++ b/src/app/vote/create/_components/CreateVoteForm.tsx @@ -12,7 +12,7 @@ import { Textarea as ContentInput } from '@/components/common/textarea'; import { Header } from '@/components/layout/header'; import { Typography } from '@/foundations/typography'; import { useToast } from '@/hooks'; -import { useCreateVoteMutation } from '@/hooks/api/vote'; +import { useCreateVoteMutation } from '@/hooks/vote'; import { createVoteSchema } from '@/schema/CreateVoteSchema'; import { CategorySelector, TitleInput, VoteDateForm, VoteItemForm } from '.'; diff --git a/src/hooks/api/vote/index.ts b/src/hooks/api/vote/index.ts deleted file mode 100644 index 12fba1c4..00000000 --- a/src/hooks/api/vote/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as useCreateVoteMutation } from './useCreateVoteMutation'; diff --git a/src/hooks/auth/useGetUser.ts b/src/hooks/auth/useGetUser.ts index 24936175..fb49025b 100644 --- a/src/hooks/auth/useGetUser.ts +++ b/src/hooks/auth/useGetUser.ts @@ -1,5 +1,4 @@ import { useQuery } from '@tanstack/react-query'; -import { isAxiosError } from 'axios'; import { hasCookie } from 'cookies-next'; import { IS_LOGIN } from '@/constants/auth'; @@ -17,7 +16,7 @@ const useGetUser = () => { queryKey: ['user'], queryFn: getUser, enabled: hasCookie(IS_LOGIN), - throwOnError: (error) => isAxiosError(error), + throwOnError: true, staleTime: Infinity, gcTime: Infinity, }); diff --git a/src/hooks/index.ts b/src/hooks/index.ts index f1e20c13..620e56e5 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,3 +1,4 @@ +export { default as useBottomSheetState } from './useBottomSheetState'; export { useIsMounted } from './useIsMounted'; export { default as useToast } from './useToast'; export { default as useUploadImage } from './useUploadImage'; diff --git a/src/hooks/useBottomSheetState.ts b/src/hooks/useBottomSheetState.ts new file mode 100644 index 00000000..8442f0bc --- /dev/null +++ b/src/hooks/useBottomSheetState.ts @@ -0,0 +1,17 @@ +import { useState } from 'react'; + +const useBottomSheetState = () => { + const [openedSheet, setOpenedSheet] = useState(); + + const onOpenSheet = (bottomSheetType: TBottomSheetType) => { + setOpenedSheet(bottomSheetType); + }; + + const onCloseSheet = () => { + setOpenedSheet(null); + }; + + return { onOpenSheet, openedSheet, onCloseSheet }; +}; + +export default useBottomSheetState; diff --git a/src/hooks/vote/index.ts b/src/hooks/vote/index.ts index f5f47c2f..d85a9f81 100644 --- a/src/hooks/vote/index.ts +++ b/src/hooks/vote/index.ts @@ -1,6 +1,9 @@ +export { default as useCreateVoteMutation } from './useCreateVoteMutation'; export { default as useCreateVoteReplyMutation } from './useCreateVoteReplyMutation'; +export { default as useDeleteVoteMutation } from './useDeleteVoteMutation'; export { default as useDeleteVoteReplyMutation } from './useDeleteVoteReplyMutation'; export { useGetAllVotes } from './useGetAllVotes'; +export { default as useGetMyVote } from './useGetMyVote'; export { useGetVoteById } from './useGetVoteById'; export { useGetVoteBySearch } from './useGetVoteBySearch'; export { default as useGetVoteReplies } from './useGetVoteReplies'; @@ -8,4 +11,3 @@ export { default as useLikeVoteMutation } from './useLikeVoteMutation'; export { default as useLikeVoteReplyMutation } from './useLikeVoteReplyMutation'; export { default as useUpdateVoteReplyMutation } from './useUpdateVoteReplyMutation'; export { default as useVotingMutation } from './useVotingMutation'; - diff --git a/src/hooks/api/vote/useCreateVoteMutation.ts b/src/hooks/vote/useCreateVoteMutation.ts similarity index 95% rename from src/hooks/api/vote/useCreateVoteMutation.ts rename to src/hooks/vote/useCreateVoteMutation.ts index 8871bb7f..0c586440 100644 --- a/src/hooks/api/vote/useCreateVoteMutation.ts +++ b/src/hooks/vote/useCreateVoteMutation.ts @@ -47,7 +47,7 @@ const useCreateVoteMutation = () => { onSuccess: (data) => { const { id } = data.data.data; toast({ message: 'VOTE_UPLOAD_SUCCESS' }); - queryClient.invalidateQueries({ queryKey: VOTE_KEY.ALL }); + queryClient.invalidateQueries({ queryKey: VOTE_KEY.ALL, refetchType: 'all' }); router.replace(`/vote/${id}`); }, onError: () => { diff --git a/src/hooks/vote/useDeleteVoteMutation.ts b/src/hooks/vote/useDeleteVoteMutation.ts new file mode 100644 index 00000000..c4e61ac9 --- /dev/null +++ b/src/hooks/vote/useDeleteVoteMutation.ts @@ -0,0 +1,32 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; + +import { useToast } from '@/hooks'; +import { del } from '@/lib/axios'; +import { SuccessResponse } from '@/types/response'; + +type DeleteVoteRequest = { + voteId: number; +}; + +const deleteVote = async ({ voteId }: DeleteVoteRequest) => { + const response = await del>(`/vote/${voteId}`); + return response.data; +}; + +const useDeleteVoteMutation = () => { + const toast = useToast(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: deleteVote, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ['votes'], refetchType: 'all' }); + toast({ message: 'VOTE_DELETE_SUCCESS' }); + }, + onError: () => { + toast({ message: 'VOTE_DELETE_FAIL' }); + }, + }); +}; + +export default useDeleteVoteMutation; diff --git a/src/hooks/vote/useGetMyVote.ts b/src/hooks/vote/useGetMyVote.ts new file mode 100644 index 00000000..4415ccdb --- /dev/null +++ b/src/hooks/vote/useGetMyVote.ts @@ -0,0 +1,22 @@ +import { useQuery } from '@tanstack/react-query'; + +import { get } from '@/lib/axios'; +import { SuccessResponse } from '@/types/response'; +import { VoteType } from '@/types/vote'; + +const getMyVote = async () => { + const response = await get>('/vote/mine'); + return response.data.data; +}; + +const useGetMyVote = () => { + return useQuery({ + queryFn: getMyVote, + queryKey: ['votes', 'mine'], + retry: 1, + staleTime: Infinity, + gcTime: Infinity, + }); +}; + +export default useGetMyVote;