From 54581a58337c5ec80f874c191188b38b2298737a Mon Sep 17 00:00:00 2001 From: skoo1100 Date: Fri, 19 Jul 2024 15:27:50 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/group/group-request.ts | 17 +++++++++++++++++ src/hooks/react-query/use-query-group.ts | 17 +++++++++++++++++ .../components/group-modal/group-modal.tsx | 6 +++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/api/group/group-request.ts b/src/api/group/group-request.ts index 6ca191f..80dd16c 100644 --- a/src/api/group/group-request.ts +++ b/src/api/group/group-request.ts @@ -147,6 +147,23 @@ const groupRequest = { return error; } }, + closeGroup: async (groupId: number) => { + try { + const response = await axios.delete(`user/group/close/${groupId}`); + return response.data; + } catch (error) { + if (isAxiosError(error)) { + if (error.response) { + throw { + errorMessage: error.response.data.errorMessage || 'errorMessage', + errorCode: error.response.data.errorCode || 'UNKNOWN_ERROR', + statusCode: error.response.status, + }; + } + } + return error; + } + }, } as const; export default groupRequest; diff --git a/src/hooks/react-query/use-query-group.ts b/src/hooks/react-query/use-query-group.ts index 05b6352..cb81ca7 100644 --- a/src/hooks/react-query/use-query-group.ts +++ b/src/hooks/react-query/use-query-group.ts @@ -151,3 +151,20 @@ export const useWithdrawGroupMutation = (groupId: number) => { return mutation; }; + +export const useCloseGroupMutation = (groupId: number) => { + const { toast } = useToast(); + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: async () => await groupRequest.closeGroup(groupId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.groupInfo, groupId] }); + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.groupList] }); + toast('그룹이 삭제되었습니다.'); + }, + onError: (error: TAxiosError) => toast(error.errorMessage, true), + }); + + return mutation; +}; diff --git a/src/pages/group-home/components/group-modal/group-modal.tsx b/src/pages/group-home/components/group-modal/group-modal.tsx index a870d14..8d11827 100644 --- a/src/pages/group-home/components/group-modal/group-modal.tsx +++ b/src/pages/group-home/components/group-modal/group-modal.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import ConfirmModal from '@components/modal/confirm-modal/confirm-modal'; import Modal from '@components/modal/modal'; import ModalButton from '@components/modal/modal-button'; -import { useExileGroupMemberMutation } from '@hooks/react-query/use-query-group'; +import { useCloseGroupMutation, useExileGroupMemberMutation } from '@hooks/react-query/use-query-group'; import GroupInvite from '@pages/group-home/components/group-modal/group-invite'; import GroupMemberList from '@pages/group-home/components/group-modal/group-member-list'; import { useGroupStore } from '@stores/group'; @@ -22,6 +22,7 @@ interface GroupModalProps { const GroupModal = ({ isOpen, onGroupClose, onConfirmClose, onConfirmOpen }: GroupModalProps) => { const navigate = useNavigate(); const { mutate: exileGroupMemberMutate } = useExileGroupMemberMutation(useGroupStore((state) => state.groupId)); + const { mutate: closeGroupMutate } = useCloseGroupMutation(useGroupStore((state) => state.groupId)); const [exileMemberList, setExileMemberList] = useState([]); const handleAddExileMemberButtonClick = (id: number) => { @@ -38,8 +39,7 @@ const GroupModal = ({ isOpen, onGroupClose, onConfirmClose, onConfirmOpen }: Gro }; const handleDeleteButtonClick = () => { - //그룹 삭제 api 작성 - + closeGroupMutate(); navigate('/'); }; From 49f1099b652ff942520faa151aea7de562b59956 Mon Sep 17 00:00:00 2001 From: skoo1100 Date: Sat, 3 Aug 2024 20:58:06 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9D=98=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C,=20=ED=9A=8C=EC=9B=90=20=ED=83=88=ED=87=B4=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/axios.ts | 3 +++ src/api/meeting/meeting-request.ts | 16 ++++++++++++++ src/api/user/user-request.ts | 16 ++++++++++++++ .../modal/profile-modal/profile-modal.tsx | 7 ++++--- src/hooks/react-query/use-query-meeting.ts | 21 +++++++++++++++++-- src/hooks/react-query/use-query-user.ts | 16 ++++++++++++++ .../meeting-form/meeting-form.tsx | 19 +++++++++++++++-- .../meeting-submit-button-box.tsx | 2 +- src/pages/group-home/index.tsx | 5 +++++ 9 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/api/axios.ts b/src/api/axios.ts index 099d6da..1e1addf 100644 --- a/src/api/axios.ts +++ b/src/api/axios.ts @@ -1,3 +1,4 @@ +import { useGroupStore } from '@stores/group'; import { getCookie } from '@utils/cookie'; import axios from 'axios'; @@ -52,6 +53,8 @@ instance.interceptors.response.use( //서버 오류 if (error.response.status === 404) { window.location.href = '/404'; + const initGroupId = useGroupStore((state) => state.initGroupId); + initGroupId(); } return Promise.reject(error); }, diff --git a/src/api/meeting/meeting-request.ts b/src/api/meeting/meeting-request.ts index 317adb4..3ed4984 100644 --- a/src/api/meeting/meeting-request.ts +++ b/src/api/meeting/meeting-request.ts @@ -37,6 +37,22 @@ const meetingRequest = { return error; } }, + withdrawMeeting: async (groupId: number, meetingId: number | undefined) => { + try { + await axios.delete(`user/group/${groupId}/meeting/${meetingId}/withdraw`); + } catch (error) { + if (isAxiosError(error)) { + if (error.response) { + throw { + errorMessage: error.response.data.errorMessage || 'errorMessage', + errorCode: error.response.data.errorCode || 'UNKNOWN_ERROR', + statusCode: error.response.status, + }; + } + } + return error; + } + }, fetchMeetingList: async (groupId: number) => { try { const { data } = await axios.get(`user/group/${groupId}/meeting/view/available`); diff --git a/src/api/user/user-request.ts b/src/api/user/user-request.ts index c02d8f4..02fbf2c 100644 --- a/src/api/user/user-request.ts +++ b/src/api/user/user-request.ts @@ -68,6 +68,22 @@ const userRequest = { return error; } }, + withdrawUserData: async () => { + try { + await axios.delete(`user/withdraw`); + } catch (error) { + if (isAxiosError(error)) { + if (error.response) { + throw { + errorMessage: error.response.data.errorMessage || 'errorMessage', + errorCode: error.response.data.errorCode || 'UNKNOWN_ERROR', + statusCode: error.response.status, + }; + } + } + return error; + } + }, } as const; export default userRequest; diff --git a/src/components/modal/profile-modal/profile-modal.tsx b/src/components/modal/profile-modal/profile-modal.tsx index 12e18f9..f0d6f03 100644 --- a/src/components/modal/profile-modal/profile-modal.tsx +++ b/src/components/modal/profile-modal/profile-modal.tsx @@ -3,10 +3,12 @@ import Modal from '@components/modal/modal'; import ModalButton from '@components/modal/modal-button'; import InvitedList from '@components/modal/profile-modal/invited-list'; import NicknameInput from '@components/modal/profile-modal/nickname-input'; +import { useWithdrawUserDataMutation } from '@hooks/react-query/use-query-user'; import { deleteCookie } from '@utils/cookie'; import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; import ProfileImageInput from './profile-image-input'; + interface ProfileModalProps { isOpen: { profile: boolean; @@ -19,6 +21,7 @@ interface ProfileModalProps { const ProfileModal = ({ isOpen, onProfileClose, onConfirmClose, onConfirmOpen }: ProfileModalProps) => { const navigate = useNavigate(); + const { mutate: withdrawUserMutate } = useWithdrawUserDataMutation(); const handleLogoutClick = () => { deleteCookie('accessToken'); @@ -26,9 +29,7 @@ const ProfileModal = ({ isOpen, onProfileClose, onConfirmClose, onConfirmOpen }: }; const handleDeleteButtonClick = () => { - //회원 탈퇴 api 작성 - - //일단 로그인 페이지로 이동하게 작성 + withdrawUserMutate(); navigate('/login'); }; diff --git a/src/hooks/react-query/use-query-meeting.ts b/src/hooks/react-query/use-query-meeting.ts index 93ee23c..34dd399 100644 --- a/src/hooks/react-query/use-query-meeting.ts +++ b/src/hooks/react-query/use-query-meeting.ts @@ -7,7 +7,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; export const useMeetingCreateMutation = (groupId: number) => { const { toast } = useToast(); - const queryClient = useQueryClient(); const mutation = useMutation({ @@ -22,9 +21,9 @@ export const useMeetingCreateMutation = (groupId: number) => { return mutation; }; + export const useMeetingUpdateMutation = (groupId: number, meetingId: number | undefined) => { const { toast } = useToast(); - const queryClient = useQueryClient(); const mutation = useMutation({ @@ -40,6 +39,24 @@ export const useMeetingUpdateMutation = (groupId: number, meetingId: number | un return mutation; }; + +export const useMeetingWithdrawMutation = (groupId: number, meetingId: number | undefined) => { + const { toast } = useToast(); + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: async () => await meetingRequest.withdrawMeeting(groupId, meetingId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.meetingList, groupId] }); + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.meetingInfo, meetingId] }); + toast('회의가 삭제되었습니다'); + }, + onError: (error: TAxiosError) => toast(error.errorMessage, true), + }); + + return mutation; +}; + export const useMeetingListQuery = (groupId: number) => { const query = useQuery({ queryKey: [QUERY_KEY.meetingList, groupId], diff --git a/src/hooks/react-query/use-query-user.ts b/src/hooks/react-query/use-query-user.ts index e5fc43a..0854fe0 100644 --- a/src/hooks/react-query/use-query-user.ts +++ b/src/hooks/react-query/use-query-user.ts @@ -61,3 +61,19 @@ export const useUserProfileImgDefaultMutation = () => { return mutation; }; + +export const useWithdrawUserDataMutation = () => { + const { toast } = useToast(); + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: async () => await userRequest.withdrawUserData(), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.userInfo] }); + toast('회원 탈퇴가 완료되었습니다'); + }, + onError: (error: TAxiosError) => toast(error.errorMessage, true), + }); + + return mutation; +}; diff --git a/src/pages/group-home/components/meeting-modal/meeting-form/meeting-form.tsx b/src/pages/group-home/components/meeting-modal/meeting-form/meeting-form.tsx index 7d1072e..62d3625 100644 --- a/src/pages/group-home/components/meeting-modal/meeting-form/meeting-form.tsx +++ b/src/pages/group-home/components/meeting-modal/meeting-form/meeting-form.tsx @@ -1,6 +1,10 @@ import { useEffect, useState } from 'react'; import { TMeetingInfo } from '@api/meeting/meeting-request.type'; -import { useMeetingCreateMutation, useMeetingUpdateMutation } from '@hooks/react-query/use-query-meeting'; +import { + useMeetingCreateMutation, + useMeetingUpdateMutation, + useMeetingWithdrawMutation, +} from '@hooks/react-query/use-query-meeting'; import { calculateEndDate } from '@pages/group-home/utils/calculate-end-date'; import { formatDateToISOStringWithOffset } from '@pages/group-home/utils/format-date-to-string'; import roundTo15minutes from '@pages/group-home/utils/round-to-15minutes'; @@ -27,6 +31,7 @@ const MeetingForm = ({ data, onClose }: TMeetingFormProps) => { const groupId = useGroupStore((state) => state.groupId) || lobbyGroupId; const meetingCreate = useMeetingCreateMutation(groupId); const meetingUpdate = useMeetingUpdateMutation(groupId, data?.id); + const { mutate: withdrawMeetingMutate } = useMeetingWithdrawMutation(groupId, data?.id); const dateTime = data ? TimeString(TimeCalculate(new Date(data.startDate), new Date(data.expectedEndDate))).trim() : '회의 시간을 선택해 주세요!'; @@ -95,6 +100,11 @@ const MeetingForm = ({ data, onClose }: TMeetingFormProps) => { } }; + const handleDeleteButtonClick = () => { + withdrawMeetingMutate(); + onClose(); + }; + useEffect(() => { if (data) { setTitle(data.meetingTitle || ''); @@ -122,7 +132,12 @@ const MeetingForm = ({ data, onClose }: TMeetingFormProps) => { onClick={handleAddTopicClick} /> - + ); }; diff --git a/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx b/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx index 8d005bf..4bdf3b4 100644 --- a/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx +++ b/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx @@ -5,7 +5,7 @@ interface MeetingSubmitButtonBoxProps { isEditMode: boolean; isConfirm: boolean; onSubmit?: () => void; - onDelete?: () => void; + onDelete: () => void; } const MeetingSubmitButtonBox = ({ isEditMode, isConfirm, onSubmit, onDelete }: MeetingSubmitButtonBoxProps) => { diff --git a/src/pages/group-home/index.tsx b/src/pages/group-home/index.tsx index ebcae7d..8bc86d1 100644 --- a/src/pages/group-home/index.tsx +++ b/src/pages/group-home/index.tsx @@ -12,12 +12,14 @@ import { device } from '@styles/breakpoints'; import { navBarHeight } from '@styles/subsection-size'; import { zIndex } from '@styles/z-index'; import { useQueryClient } from '@tanstack/react-query'; +import { useNavigate } from 'react-router-dom'; import styled, { useTheme } from 'styled-components'; import AdminHOC from './components/admin-hoc'; import GroupHomeSidebarSkeleton from './components/skeleton/group-home-sidebar-skeleton'; const GroupHome = () => { const theme = useTheme(); + const navigate = useNavigate(); const { data: groupData, isError, isLoading } = useGroupMemberQuery(useGroupStore((state) => state.groupId)); const userInfo = useQueryClient().getQueryData([QUERY_KEY.userInfo]); const [isAdmin, setIsAdmin] = useState(false); @@ -31,6 +33,9 @@ const GroupHome = () => { useEffect(() => { if (!isLoading) { const { id: adminId } = groupData.memberList.find((data: TGroupFetchMemberInfo) => data.admin); + if (!adminId) { + navigate(-1); + } setIsAdmin(adminId === userInfo?.id); } }, [isLoading, userInfo, groupData]); From 7dda50f4f633a2eaf2c9977e763ba6b4bb5f148b Mon Sep 17 00:00:00 2001 From: skoo1100 Date: Sat, 3 Aug 2024 21:01:12 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20axios=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=B4=88=EA=B8=B0=20groupid=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/axios.ts | 3 --- .../meeting-modal/meeting-form/meeting-submit-button-box.tsx | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/api/axios.ts b/src/api/axios.ts index 1e1addf..099d6da 100644 --- a/src/api/axios.ts +++ b/src/api/axios.ts @@ -1,4 +1,3 @@ -import { useGroupStore } from '@stores/group'; import { getCookie } from '@utils/cookie'; import axios from 'axios'; @@ -53,8 +52,6 @@ instance.interceptors.response.use( //서버 오류 if (error.response.status === 404) { window.location.href = '/404'; - const initGroupId = useGroupStore((state) => state.initGroupId); - initGroupId(); } return Promise.reject(error); }, diff --git a/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx b/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx index 4bdf3b4..8d005bf 100644 --- a/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx +++ b/src/pages/group-home/components/meeting-modal/meeting-form/meeting-submit-button-box.tsx @@ -5,7 +5,7 @@ interface MeetingSubmitButtonBoxProps { isEditMode: boolean; isConfirm: boolean; onSubmit?: () => void; - onDelete: () => void; + onDelete?: () => void; } const MeetingSubmitButtonBox = ({ isEditMode, isConfirm, onSubmit, onDelete }: MeetingSubmitButtonBoxProps) => {