From efdb2c5774645724d5ba900a6f342f1321dbb62f Mon Sep 17 00:00:00 2001 From: kyuran kim <57716832+gxxrxn@users.noreply.github.com> Date: Wed, 24 Apr 2024 16:55:11 +0900 Subject: [PATCH] =?UTF-8?q?[#505]=20[=EB=AA=A8=EC=9E=84=20=EC=83=81?= =?UTF-8?q?=EC=84=B8]=20=EB=AA=A8=EC=9E=84=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#537)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: toast default 지연시간 2초로 늘림 * style: button foucs outline 스타일 수정 * feat: 모임 삭제 기능 구현 - 모임 상세 페이지 error boundary 추가 - 존재하지않는 모임 페이지 접근 시 not-found 페이지로 이동 - Query refetchOnWindowFocus 옵션 false --- src/app/group/[groupId]/not-found.tsx | 19 +++ src/app/group/[groupId]/page.tsx | 14 ++- src/components/ReactQueryProvider.tsx | 3 +- .../group/useDeleteBookGroupMutation.ts | 14 +++ src/v1/base/Button.tsx | 2 +- src/v1/base/Toast/ToastProvider.tsx | 2 +- src/v1/bookGroup/BookGroupNavigation.tsx | 109 ++++++++++++++++-- 7 files changed, 143 insertions(+), 20 deletions(-) create mode 100644 src/app/group/[groupId]/not-found.tsx create mode 100644 src/queries/group/useDeleteBookGroupMutation.ts diff --git a/src/app/group/[groupId]/not-found.tsx b/src/app/group/[groupId]/not-found.tsx new file mode 100644 index 000000000..86ded8335 --- /dev/null +++ b/src/app/group/[groupId]/not-found.tsx @@ -0,0 +1,19 @@ +import Button from '@/v1/base/Button'; +import Image from 'next/image'; +import Link from 'next/link'; + +export default function NotFound() { + return ( +
+ loading +

+ 다독이가 길을 잃었어요. +

+ + + +
+ ); +} diff --git a/src/app/group/[groupId]/page.tsx b/src/app/group/[groupId]/page.tsx index f69e14003..0d7caa166 100644 --- a/src/app/group/[groupId]/page.tsx +++ b/src/app/group/[groupId]/page.tsx @@ -1,13 +1,16 @@ 'use client'; +import { notFound } from 'next/navigation'; +import { ErrorBoundary } from 'react-error-boundary'; + import { checkAuthentication } from '@/utils/helpers'; import SSRSafeSuspense from '@/components/SSRSafeSuspense'; -import BookGroupInfo from '@/v1/bookGroup/detail/BookGroupInfo'; -import BookGroupCommentList from '@/v1/comment/BookGroupCommentList'; +import LoginBottomActionButton from '@/v1/base/LoginBottomActionButton'; import BookGroupNavigation from '@/v1/bookGroup/BookGroupNavigation'; +import BookGroupInfo from '@/v1/bookGroup/detail/BookGroupInfo'; import JoinBookGroupButton from '@/v1/bookGroup/detail/JoinBookGroupButton'; -import LoginBottomActionButton from '@/v1/base/LoginBottomActionButton'; +import BookGroupCommentList from '@/v1/comment/BookGroupCommentList'; const DetailBookGroupPage = ({ params: { groupId }, @@ -15,8 +18,9 @@ const DetailBookGroupPage = ({ params: { groupId: number }; }) => { const isAuthenticated = checkAuthentication(); + return ( - <> + @@ -39,7 +43,7 @@ const DetailBookGroupPage = ({ )} - + ); }; diff --git a/src/components/ReactQueryProvider.tsx b/src/components/ReactQueryProvider.tsx index 7e72f6535..fff2f4587 100644 --- a/src/components/ReactQueryProvider.tsx +++ b/src/components/ReactQueryProvider.tsx @@ -13,7 +13,8 @@ const ReactQueryProvider: NextPage = ({ children }) => { new QueryClient({ defaultOptions: { queries: { - retry: 0, + refetchOnWindowFocus: false, + retry: false, }, }, }) diff --git a/src/queries/group/useDeleteBookGroupMutation.ts b/src/queries/group/useDeleteBookGroupMutation.ts new file mode 100644 index 000000000..0f748ed25 --- /dev/null +++ b/src/queries/group/useDeleteBookGroupMutation.ts @@ -0,0 +1,14 @@ +import { useMutation } from '@tanstack/react-query'; + +import type { APIGroup } from '@/types/group'; + +import groupAPI from '@/apis/group'; + +const useDeleteBookGroupMutation = () => { + return useMutation({ + mutationFn: (bookGroupId: APIGroup['bookGroupId']) => + groupAPI.deleteGroup({ bookGroupId }).then(({ data }) => data), + }); +}; + +export default useDeleteBookGroupMutation; diff --git a/src/v1/base/Button.tsx b/src/v1/base/Button.tsx index ad67ae7a7..7de2e2499 100644 --- a/src/v1/base/Button.tsx +++ b/src/v1/base/Button.tsx @@ -60,7 +60,7 @@ const getSchemeClasses = (theme: ColorScheme, isFill: boolean) => { }; const BASE_BUTTON_CLASSES = - 'cursor-pointer border-[0.1rem] leading-none inline-block font-bold'; + 'cursor-pointer border-[0.1rem] leading-none inline-block font-bold focus:outline-none focus:ring-2'; const Button = ({ size = 'medium', diff --git a/src/v1/base/Toast/ToastProvider.tsx b/src/v1/base/Toast/ToastProvider.tsx index 4e5eea85a..cafaa4680 100644 --- a/src/v1/base/Toast/ToastProvider.tsx +++ b/src/v1/base/Toast/ToastProvider.tsx @@ -21,7 +21,7 @@ const ToastProvider = ({ children }: { children?: ReactNode }) => { const controller = useMemo( () => ({ - show: ({ type, message, duration = 1000 }) => { + show: ({ type, message, duration = 2000 }) => { setToast({ type, message, duration }); setAnimation('slide-init'); diff --git a/src/v1/bookGroup/BookGroupNavigation.tsx b/src/v1/bookGroup/BookGroupNavigation.tsx index 14c8a8b04..ca97b28b0 100644 --- a/src/v1/bookGroup/BookGroupNavigation.tsx +++ b/src/v1/bookGroup/BookGroupNavigation.tsx @@ -1,27 +1,33 @@ +import { useRouter } from 'next/navigation'; import { - ReactNode, Children, createContext, isValidElement, + ReactNode, useContext, useRef, } from 'react'; -import { useRouter } from 'next/navigation'; -import SSRSafeSuspense from '@/components/SSRSafeSuspense'; -import TopNavigation from '@/v1/base/TopNavigation'; -import CommentDrawer from '@/v1/comment/CommentDrawer'; -import { IconArrowLeft, IconHamburger, IconPost } from '@public/icons'; +import { SERVICE_ERROR_MESSAGE } from '@/constants'; +import { IconArrowLeft, IconPost } from '@public/icons'; +import { isAxiosErrorWithCustomCode } from '@/utils/helpers'; + +import useDisclosure from '@/hooks/useDisclosure'; import { + useBookGroupJoinInfo, useBookGroupOwner, useBookGroupTitle, - useBookGroupJoinInfo, } from '@/queries/group/useBookGroupQuery'; -import useToast from '@/v1/base/Toast/useToast'; -import useDisclosure from '@/hooks/useDisclosure'; import useCreateBookGroupCommentMutation from '@/queries/group/useCreateBookGroupCommentMutation'; -import { isAxiosErrorWithCustomCode } from '@/utils/helpers'; -import { SERVICE_ERROR_MESSAGE } from '@/constants'; +import useDeleteBookGroupMutation from '@/queries/group/useDeleteBookGroupMutation'; + +import SSRSafeSuspense from '@/components/SSRSafeSuspense'; +import Button from '@/v1/base/Button'; +import Menu from '@/v1/base/Menu'; +import Modal from '@/v1/base/Modal'; +import useToast from '@/v1/base/Toast/useToast'; +import TopNavigation from '@/v1/base/TopNavigation'; +import CommentDrawer from '@/v1/comment/CommentDrawer'; const NavigationContext = createContext({} as { groupId: number }); @@ -102,7 +108,52 @@ const MenuButton = () => { const { groupId } = useContext(NavigationContext); const { data: owner } = useBookGroupOwner(groupId); - return <>{owner.isMe && }; + const router = useRouter(); + + const deleteBookGroup = useDeleteBookGroupMutation(); + + const { show: showToast } = useToast(); + const { isOpen, onClose, onOpen } = useDisclosure(); + + const handleModalConfirm = async () => { + await deleteBookGroup.mutateAsync(groupId, { + onSuccess: () => { + showToast({ type: 'success', message: '모임을 삭제했어요' }); + router.replace('/group'); + }, + onError: error => { + if (isAxiosErrorWithCustomCode(error)) { + const { code } = error.response.data; + const message = SERVICE_ERROR_MESSAGE[code]; + showToast({ type: 'error', message }); + return; + } + + showToast({ type: 'error', message: '잠시 후 다시 시도해주세요' }); + }, + }); + }; + + return ( + <> + {owner.isMe && ( + <> + + + + 수졍하기 + 삭제하기 + + + + + )} + + ); }; const WriteButton = () => { @@ -173,6 +224,40 @@ const TitleSkeleton = () => (
); +const DeleteBookGroupModal = ({ + isOpen, + onClose, + onConfirm, +}: { + isOpen: boolean; + onClose: () => void; + onConfirm?: () => void; +}) => { + const handleConfirm = () => { + onConfirm && onConfirm(); + onClose(); + }; + + return ( + +
+ 모임을 정말 삭제할까요? +

+ 참여 중인 모임원이 있는 경우, 모임을 삭제할 수 없어요. +

+
+
+ + +
+
+ ); +}; + const BackButtonType = ().type; const TitleType = ().type; const MenuButtonType = (<MenuButton />).type;