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/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-group.ts b/src/hooks/react-query/use-query-group.ts index c668484..a3f835e 100644 --- a/src/hooks/react-query/use-query-group.ts +++ b/src/hooks/react-query/use-query-group.ts @@ -152,3 +152,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/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/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('/'); }; 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/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]);