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 (
+
+
+
+ 다독이가 길을 잃었어요.
+
+
+
+
+
+ );
+}
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 = ().type;