diff --git a/src/app/(user-page)/my-meeting/_features/CardRightSection.tsx b/src/app/(user-page)/my-meeting/_features/CardRightSection.tsx
index 2efa077b..8836b3a7 100644
--- a/src/app/(user-page)/my-meeting/_features/CardRightSection.tsx
+++ b/src/app/(user-page)/my-meeting/_features/CardRightSection.tsx
@@ -14,13 +14,13 @@ import PublicSelect from './PublicDropdown';
const CardRightSection = ({
memberList,
- isPublic,
+ isPublic = false,
className,
meetingId,
showPublicSelect = false,
}: {
memberList: Member[];
- isPublic: boolean;
+ isPublic?: boolean;
className?: string;
meetingId: number;
showPublicSelect?: boolean;
@@ -61,7 +61,7 @@ const CardRightSection = ({
memberList,
);
router.push(
- `/my-meeting/my/user-list?meetingId=${meetingId}&type=${showPublicSelect ? 'created' : 'joined'}`,
+ `/my-meeting/my/user-list?meetingId=${meetingId}&type=${showPublicSelect ? 'created' : 'participated'}`,
);
};
diff --git a/src/app/(user-page)/my-meeting/_features/Joined.tsx b/src/app/(user-page)/my-meeting/_features/Joined.tsx
deleted file mode 100644
index dad6ab0c..00000000
--- a/src/app/(user-page)/my-meeting/_features/Joined.tsx
+++ /dev/null
@@ -1,191 +0,0 @@
-// 'use client';
-
-// import Dropdown from '@/components/common/Dropdown';
-// import { Button } from '@/components/ui/Button';
-// import HorizonCard from '@/components/ui/HorizonCard';
-// import { Tag } from '@/components/ui/Tag';
-// import Modal from '@/components/ui/modal/Modal';
-// import Image from 'next/image';
-// import { useState } from 'react';
-
-// import { Meeting, Member } from '../my-meeting/my/page';
-// import ModalProfile from './ModalProfile';
-// import ModalUserList from './ModalUserList';
-
-// const CardRightSection = ({
-// memberList,
-// isPublic,
-// className,
-// }: {
-// memberList: Member[];
-// isPublic: boolean;
-// className?: string;
-// }) => {
-// const [selectedFilter, setSelectedFilter] = useState(
-// isPublic ? '공개' : '비공개',
-// );
-// const [isUserListModalOpen, setIsUserListModalOpen] = useState(false);
-// const handleConfirm = () => {
-// setIsUserListModalOpen(false);
-// };
-// const filterAreaOptions = [
-// { value: 'true', label: '공개' },
-// { value: 'false', label: '비공개' },
-// ];
-
-// const [isUserProfileModalOpen, setIsUserProfileModalOpen] = useState(false);
-
-// const handleSecondModalConfirm = () => {
-// // 가입 확인 api 연동
-// setIsUserProfileModalOpen(false);
-// };
-
-// const handleSecondModalCancel = () => {
-// // 가입 거절 api 연동
-// setIsUserProfileModalOpen(false);
-// };
-// return (
-//
-//
-//
참가 중인 멤버
-//
-// {memberList.map((member: Member) => (
-//
-//
-//
-// {member.name}
-//
-//
-//
-// setIsUserProfileModalOpen(true)}
-// >
-// 프로필보기
-//
-//
-//
-// ))}
-//
-//
-//
setIsUserListModalOpen(true)}
-// >
-// 맴버 명단 보기
-//
-//
-//
-//
-//
-//
-//
-//
setIsUserListModalOpen(false)}
-// onConfirm={handleConfirm}
-// showOnly
-// modalClassName="h-[590px] w-[520px] overflow-y-auto"
-// >
-//
-//
-//
-// );
-// };
-
-// const Joined = ({ meetings }: { meetings: Meeting[] }) => {
-// return (
-//
-// {meetings.map((meeting) => {
-// return (
-//
-// {/* 데스크탑 */}
-//
-//
-//
-//
-//
-
-// {/* 태블릿 */}
-//
-//
-//
-//
-
-// {/* 모바일 */}
-//
-//
-//
-//
-//
-// );
-// })}
-//
-// );
-// };
-// export default Joined;
diff --git a/src/app/(user-page)/my-meeting/_features/LeaveMeetingButton.tsx b/src/app/(user-page)/my-meeting/_features/LeaveMeetingButton.tsx
new file mode 100644
index 00000000..f219bd2c
--- /dev/null
+++ b/src/app/(user-page)/my-meeting/_features/LeaveMeetingButton.tsx
@@ -0,0 +1,44 @@
+'use client';
+
+import { useQuitMeetingMutation } from '@/hooks/mutations/useMyMeetingMutation';
+import { LogOut } from 'lucide-react';
+
+interface LeaveMeetingButtonProps {
+ meetingId: number;
+ className?: string;
+}
+
+const LeaveMeetingButton = ({
+ meetingId,
+ className = '',
+}: LeaveMeetingButtonProps) => {
+ const { mutate: quitMeeting, isPending: isLoading } =
+ useQuitMeetingMutation();
+
+ const handleClick = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ quitMeeting(meetingId);
+ };
+
+ return (
+
+
+
+ {isLoading ? '처리 중...' : '모임 탈퇴하기'}
+
+
+ );
+};
+
+export default LeaveMeetingButton;
diff --git a/src/app/(user-page)/my-meeting/_features/Likes.tsx b/src/app/(user-page)/my-meeting/_features/Likes.tsx
new file mode 100644
index 00000000..96f56144
--- /dev/null
+++ b/src/app/(user-page)/my-meeting/_features/Likes.tsx
@@ -0,0 +1,138 @@
+'use client';
+
+import HorizonCard from '@/components/ui/HorizonCard';
+import useInfiniteScroll from '@/hooks/common/useInfiniteScroll';
+import { useInfiniteMyMeetingLikesQueries } from '@/hooks/queries/useMyMeetingQueries';
+import { translateCategoryNameToEng } from '@/util/searchFilter';
+import { useRouter } from 'next/navigation';
+import React from 'react';
+
+import MeetingListSkeleton from './skeletons/SkeletonMeetingList';
+
+const Likes = () => {
+ const router = useRouter();
+
+ const {
+ data: meetingData,
+ fetchNextPage,
+ hasNextPage,
+ isFetchingNextPage,
+ isLoading,
+ } = useInfiniteMyMeetingLikesQueries();
+
+ const lastMeetingRef = useInfiniteScroll({
+ fetchNextPage,
+ isFetchingNextPage,
+ hasNextPage: !!hasNextPage,
+ });
+
+ if (isLoading || !meetingData) {
+ return ;
+ }
+
+ return (
+
+ {meetingData.pages.map((page, pageIdx) => (
+
+ {page.content.map((meeting) => (
+
+ {/* 데스크탑 */}
+
+
+ router.push(
+ `/meeting/${translateCategoryNameToEng(meeting.categoryTitle)}/${meeting.meetingId}`,
+ )
+ }
+ key={meeting.meetingId}
+ title={meeting.title}
+ thumbnailUrl={meeting.thumbnail}
+ location={meeting.location}
+ total={meeting.maxMember}
+ value={meeting.memberCount}
+ className="flex-row"
+ meetingId={meeting.meetingId}
+ category={translateCategoryNameToEng(meeting.categoryTitle)}
+ isLike={true}
+ likesCount={meeting.likesCount}
+ >
+
+
+ {/* 태블릿 */}
+
+
+ router.push(
+ `/meeting/${translateCategoryNameToEng(meeting.categoryTitle)}/${meeting.meetingId}`,
+ )
+ }
+ key={meeting.meetingId}
+ title={meeting.title}
+ thumbnailUrl={meeting.thumbnail}
+ location={meeting.location}
+ total={meeting.maxMember}
+ value={meeting.memberCount}
+ thumbnailHeight={160}
+ thumbnailWidth={160}
+ className=""
+ meetingId={meeting.meetingId}
+ category={translateCategoryNameToEng(meeting.categoryTitle)}
+ isLike={true}
+ likesCount={meeting.likesCount}
+ />
+
+
+ {/* 모바일 */}
+
+
+ router.push(
+ `/meeting/${translateCategoryNameToEng(meeting.categoryTitle)}/${meeting.meetingId}`,
+ )
+ }
+ key={meeting.meetingId}
+ title={meeting.title}
+ thumbnailUrl={meeting.thumbnail}
+ location={meeting.location}
+ total={meeting.maxMember}
+ value={meeting.memberCount}
+ thumbnailHeight={80}
+ thumbnailWidth={80}
+ className=""
+ meetingId={meeting.meetingId}
+ category={translateCategoryNameToEng(meeting.categoryTitle)}
+ isLike={true}
+ likesCount={meeting.likesCount}
+ />
+
+
+ ))}
+
+ ))}
+
+ {/* 무한 스크롤을 위한 별도의 Observer 요소 */}
+ {hasNextPage && (
+
+ )}
+
+ {/* 추가 데이터 로딩 중 표시 */}
+ {isFetchingNextPage &&
}
+
+ {/* 데이터가 없는 경우 표시 */}
+ {meetingData.pages[0].content.length === 0 && (
+
+
+
찜한 모임이 없어요.
+
원하는 모임을 찜해보세요!
+
+
+ )}
+
+ );
+};
+
+export default Likes;
diff --git a/src/app/(user-page)/my-meeting/_features/Participated.tsx b/src/app/(user-page)/my-meeting/_features/Participated.tsx
index 803b6d0b..c332341f 100644
--- a/src/app/(user-page)/my-meeting/_features/Participated.tsx
+++ b/src/app/(user-page)/my-meeting/_features/Participated.tsx
@@ -3,11 +3,20 @@
import HorizonCard from '@/components/ui/HorizonCard';
import useInfiniteScroll from '@/hooks/common/useInfiniteScroll';
import { useInfiniteMyMeetingParticipatedQueries } from '@/hooks/queries/useMyMeetingQueries';
+import { translateCategoryNameToEng } from '@/util/searchFilter';
+import Link from 'next/link';
import { useRouter } from 'next/navigation';
+import { IMyMeetingParticipated } from 'types/myMeeting';
import CardRightSection from './CardRightSection';
+import LeaveMeetingButton from './LeaveMeetingButton';
+import PendingStatusChip from './PendingStatusChip';
import MeetingListSkeleton from './skeletons/SkeletonMeetingList';
+interface IStatusOverlay {
+ meeting: IMyMeetingParticipated;
+}
+
const Participated = () => {
const router = useRouter();
@@ -32,15 +41,46 @@ const Participated = () => {
return ;
}
- // 상세 페이지로 이동하는 핸들러
- const handleMoveDetailPage = (meetingId: number) => {
- /**
- * TODO
- * 추후 category 수정
- */
- router.push(`/meeting/study/${meetingId}`);
+ // 클릭 불가능한 상태인지 확인하는 함수 (오버레이 표시 여부만 결정)
+ const isDisabledStatus = (
+ status: IMyMeetingParticipated['myMemberStatus'],
+ ): boolean => {
+ return status === 'REJECTED' || status === 'EXPEL';
};
+ // 모임 상세 페이지 URL 생성 함수
+ const getMeetingDetailUrl = (meeting: IMyMeetingParticipated) =>
+ `/meeting/${translateCategoryNameToEng(meeting.categoryTitle)}/${meeting.meetingId}`;
+
+ const handleCardClick = (meeting: IMyMeetingParticipated) => {
+ router.push(getMeetingDetailUrl(meeting));
+ };
+
+ // 오버레이 컴포넌트 (수정된 버전)
+ const StatusOverlay = ({ meeting }: IStatusOverlay) => (
+
+
+ {meeting.myMemberStatus === 'REJECTED' ? (
+
+ 죄송합니다. 가입이 거절 된
+ 모임입니다.
+
+ ) : (
+
+ 더 이상 참여가 불가능 한
+ 모임입니다.
+
+ )}
+
+ 상세페이지 보기
+
+
+
+ );
+
return (
{meetingData.pages.map((page, pageIdx) => (
@@ -49,31 +89,52 @@ const Participated = () => {
{/* 데스크탑 */}
-
handleMoveDetailPage(meeting.meetingId)}
- key={meeting.meetingId}
- title={meeting.title}
- thumbnailUrl={meeting.thumbnail}
- location={meeting.location}
- total={meeting.maxMember}
- value={meeting.memberCount}
- className="flex-row"
- meetingId={meeting.meetingId}
- category={''}
- >
-
+ handleCardClick(meeting)}
+ key={meeting.meetingId}
+ title={meeting.title}
+ thumbnailUrl={meeting.thumbnail}
+ location={meeting.location}
+ total={meeting.maxMember}
+ value={meeting.memberCount}
+ className="flex-row"
meetingId={meeting.meetingId}
- />
-
+ showLikeButton={false}
+ category={''}
+ >
+
+
+
+ {/* PENDING 상태일 때 승인 대기중 칩 표시 */}
+ {meeting.myMemberStatus === 'PENDING' && (
+
+ )}
+
+ {/* APPROVED 상태일 때 모임 탈퇴하기 버튼 표시 */}
+ {meeting.myMemberStatus === 'APPROVED' &&
+ !meeting.isMeetingManager && (
+
+ )}
+
+ {/* 비활성화된 상태일 때 오버레이 */}
+ {isDisabledStatus(meeting.myMemberStatus) && (
+
+ )}
+
{/* 태블릿 */}
-
+
handleMoveDetailPage(meeting.meetingId)}
+ onClick={() => handleCardClick(meeting)}
key={meeting.meetingId}
title={meeting.title}
thumbnailUrl={meeting.thumbnail}
@@ -84,20 +145,36 @@ const Participated = () => {
thumbnailWidth={160}
className=""
meetingId={meeting.meetingId}
+ showLikeButton={false}
category={''}
/>
+
+ {meeting.myMemberStatus === 'PENDING' && (
+
+ )}
+
+ {meeting.myMemberStatus === 'APPROVED' &&
+ !meeting.isMeetingManager && (
+
+ )}
+
+ {isDisabledStatus(meeting.myMemberStatus) && (
+
+ )}
{/* 모바일 */}
-
+
handleMoveDetailPage(meeting.meetingId)}
+ onClick={() => handleCardClick(meeting)}
key={meeting.meetingId}
title={meeting.title}
thumbnailUrl={meeting.thumbnail}
@@ -108,14 +185,30 @@ const Participated = () => {
thumbnailWidth={80}
className=""
meetingId={meeting.meetingId}
+ showLikeButton={false}
category={''}
/>
+
+ {meeting.myMemberStatus === 'PENDING' && (
+
+ )}
+
+ {meeting.myMemberStatus === 'APPROVED' &&
+ !meeting.isMeetingManager && (
+
+ )}
+
+ {isDisabledStatus(meeting.myMemberStatus) && (
+
+ )}
))}
@@ -123,34 +216,18 @@ const Participated = () => {
))}
{/* 무한 스크롤을 위한 별도의 Observer 요소 */}
- {hasNextPage && (
-
- )}
+ {hasNextPage &&
}
{/* 추가 데이터 로딩 중 표시 */}
- {isFetchingNextPage && (
-
- )}
+ {isFetchingNextPage &&
}
{/* 데이터가 없는 경우 표시 */}
{meetingData.pages[0].content.length === 0 && (
-
- 참여한 모임이 없습니다.
-
- )}
-
- {/* 더 이상 데이터가 없음을 표시 */}
- {!hasNextPage && meetingData.pages[0].content.length > 0 && (
-
- 모든 모임을 불러왔습니다.
+
+
+
내가 참여하고있는 모임이 없어요.
+
원하는 모임에 참가하세요!
+
)}
diff --git a/src/app/(user-page)/my-meeting/_features/PendingStatusChip.tsx b/src/app/(user-page)/my-meeting/_features/PendingStatusChip.tsx
new file mode 100644
index 00000000..a38dff94
--- /dev/null
+++ b/src/app/(user-page)/my-meeting/_features/PendingStatusChip.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import { useCancelPendingMutation } from '@/hooks/mutations/useMyMeetingMutation';
+import { LogOut } from 'lucide-react';
+
+interface PendingStatusChipProps {
+ meetingId: number;
+ text?: string;
+}
+
+export const PendingStatusChip = ({
+ meetingId,
+ text,
+}: PendingStatusChipProps) => {
+ const { mutate: cancelPending, isPending: isLoading } =
+ useCancelPendingMutation();
+
+ const handleClick = (e: React.MouseEvent
) => {
+ e.stopPropagation();
+ cancelPending(meetingId);
+ };
+
+ return (
+
+
+ 승인 대기중
+
+ .
+ .
+ .
+
+
+
+
+
+ {isLoading ? '취소 중...' : (text ?? '취소')}
+
+
+ );
+};
+
+export default PendingStatusChip;
diff --git a/src/app/(user-page)/my-meeting/likes/page.tsx b/src/app/(user-page)/my-meeting/likes/page.tsx
index 3ae9476e..efbfdbd8 100644
--- a/src/app/(user-page)/my-meeting/likes/page.tsx
+++ b/src/app/(user-page)/my-meeting/likes/page.tsx
@@ -1,5 +1,30 @@
-import NotYet from '@/components/common/NotYet';
+import { myMeetingKeys } from '@/hooks/queries/useMyMeetingQueries';
+import {
+ HydrationBoundary,
+ QueryClient,
+ dehydrate,
+} from '@tanstack/react-query';
+import { getMyMeetingLikes } from 'service/api/mymeeting';
+import { Paginated } from 'types/meeting';
+import { IMyMeetingLikes } from 'types/myMeeting';
-export default function LikesPage() {
- return ;
+import Likes from '../_features/Likes';
+
+export default async function LikesPage() {
+ const queryClient = new QueryClient();
+
+ await queryClient.prefetchInfiniteQuery({
+ queryKey: myMeetingKeys.likes(),
+ queryFn: ({ pageParam }) => getMyMeetingLikes(pageParam),
+ getNextPageParam: (lastPage: Paginated) =>
+ lastPage.nextCursor ?? false,
+ initialPageParam: 0,
+ });
+ return (
+
+
+
+
+
+ );
}
diff --git a/src/app/(user-page)/my-meeting/my/page.tsx b/src/app/(user-page)/my-meeting/my/page.tsx
index 0c599143..8016bed8 100644
--- a/src/app/(user-page)/my-meeting/my/page.tsx
+++ b/src/app/(user-page)/my-meeting/my/page.tsx
@@ -13,7 +13,7 @@ import {
} from 'service/api/mymeeting';
import { getBanner } from 'service/api/mypageProfile';
import { Paginated } from 'types/meeting';
-import { IMyMeetingManage } from 'types/myMeeting';
+import { IMyMeetingManage, IMyMeetingParticipated } from 'types/myMeeting';
import Created from '../_features/Created';
import Participated from '../_features/Participated';
@@ -48,7 +48,7 @@ export default async function Page({
await queryClient.prefetchInfiniteQuery({
queryKey: myMeetingKeys.participated(),
queryFn: ({ pageParam }) => getMyMeetingParticipated(pageParam),
- getNextPageParam: (lastPage: Paginated) =>
+ getNextPageParam: (lastPage: Paginated) =>
lastPage.nextCursor ?? false,
initialPageParam: 0,
});
diff --git a/src/hooks/mutations/useMyMeetingMutation.ts b/src/hooks/mutations/useMyMeetingMutation.ts
index 64c69cba..9c49b118 100644
--- a/src/hooks/mutations/useMyMeetingMutation.ts
+++ b/src/hooks/mutations/useMyMeetingMutation.ts
@@ -1,10 +1,71 @@
import { useToast } from '@/components/common/ToastContext';
+import axiosInstance from '@/lib/axios/axiosInstance';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { putExpel, putIsPublic, putMemberStatus } from 'service/api/mymeeting';
+import { deleteCancel, deleteQuit } from 'service/api/mymeeting';
+import { meetingKeys } from '../queries/useMeetingQueries';
import { myMeetingKeys } from '../queries/useMyMeetingQueries';
+// 참가중인 모임 나가기 훅
+const useQuitMeetingMutation = () => {
+ const { showToast } = useToast();
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (meetingId: number) => deleteQuit(meetingId),
+ onSuccess: (_, meetingId) => {
+ showToast('모임에서 탈퇴했습니다.', 'success');
+ queryClient.invalidateQueries({
+ queryKey: myMeetingKeys.participated(),
+ });
+ queryClient.invalidateQueries({
+ queryKey: meetingKeys.detailInfo(meetingId),
+ });
+ },
+ onError: (error: AxiosError) => {
+ const errorData = error.response?.data as
+ | { data?: { request?: string } }
+ | undefined;
+ const errorMessage = errorData?.data?.request || '';
+
+ if (
+ errorMessage.includes('Meeting manager cannot quit meeting') &&
+ error.response?.status === 400
+ ) {
+ showToast('주최자는 모임을 탈퇴할 수 없습니다.', 'error');
+ } else {
+ showToast('모임 탈퇴에 실패했습니다. 다시 시도해주세요.', 'error');
+ }
+ },
+ });
+};
+
+// 승인 대기중인 모임 취소 훅
+const useCancelPendingMutation = () => {
+ const { showToast } = useToast();
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (meetingId: number) => deleteCancel(meetingId),
+ onSuccess: (_, meetingId) => {
+ showToast('승인 대기를 취소했습니다.', 'success');
+ queryClient.invalidateQueries({
+ queryKey: myMeetingKeys.participated(),
+ });
+ queryClient.invalidateQueries({
+ queryKey: meetingKeys.detailInfo(meetingId),
+ });
+ },
+ onError: (error: AxiosError) => {
+ if (error.response?.status) {
+ showToast('승인 대기 취소에 실패했습니다. 다시 시도해주세요.', 'error');
+ }
+ },
+ });
+};
+
const useMemberStatusMutation = (meetingId: number) => {
const { showToast } = useToast();
const queryClient = useQueryClient();
@@ -95,4 +156,10 @@ const useChangePublic = (meetingId: number) => {
});
};
-export { useMemberStatusMutation, useExpelMutation, useChangePublic };
+export {
+ useMemberStatusMutation,
+ useExpelMutation,
+ useChangePublic,
+ useQuitMeetingMutation,
+ useCancelPendingMutation,
+};
diff --git a/src/hooks/queries/useMyMeetingQueries.ts b/src/hooks/queries/useMyMeetingQueries.ts
index 4fed671a..f1dabf15 100644
--- a/src/hooks/queries/useMyMeetingQueries.ts
+++ b/src/hooks/queries/useMyMeetingQueries.ts
@@ -3,14 +3,22 @@ import {
getMyMeetingManage,
getMyMeetingMemberProfile,
} from 'service/api/mymeeting';
-import { getMyMeetingParticipated } from 'service/api/mymeeting';
+import {
+ getMyMeetingLikes,
+ getMyMeetingParticipated,
+} from 'service/api/mymeeting';
import { Paginated } from 'types/meeting';
-import { IMyMeetingManage } from 'types/myMeeting';
+import {
+ IMyMeetingLikes,
+ IMyMeetingManage,
+ IMyMeetingParticipated,
+} from 'types/myMeeting';
export const myMeetingKeys = {
all: ['mymeeting'] as const,
manage: () => [...myMeetingKeys.all, 'manage'] as const,
participated: () => [...myMeetingKeys.all, 'participated'] as const,
+ likes: () => [...myMeetingKeys.all, 'likes'] as const,
memberProfile: (meetingId: number, userId: number) => [
...myMeetingKeys.all,
'profile',
@@ -18,6 +26,7 @@ export const myMeetingKeys = {
],
};
+// 내가 생성한 모임
export const useInfiniteMyMeetingManageQueries = () => {
return useInfiniteQuery({
queryKey: myMeetingKeys.manage(),
@@ -29,12 +38,25 @@ export const useInfiniteMyMeetingManageQueries = () => {
});
};
+// 내가 참여하고 있는 모임
export const useInfiniteMyMeetingParticipatedQueries = () => {
return useInfiniteQuery({
queryKey: myMeetingKeys.participated(),
queryFn: ({ pageParam }) => getMyMeetingParticipated(pageParam),
initialPageParam: 0,
- getNextPageParam: (lastPage: Paginated) => {
+ getNextPageParam: (lastPage: Paginated) => {
+ return lastPage.nextCursor ?? null;
+ },
+ });
+};
+
+// 내가 찜한 모임
+export const useInfiniteMyMeetingLikesQueries = () => {
+ return useInfiniteQuery({
+ queryKey: myMeetingKeys.likes(),
+ queryFn: ({ pageParam }) => getMyMeetingLikes(pageParam),
+ initialPageParam: 0,
+ getNextPageParam: (lastPage: Paginated) => {
return lastPage.nextCursor ?? null;
},
});
diff --git a/src/service/api/meeting.ts b/src/service/api/meeting.ts
index dc66106f..09aa6a80 100644
--- a/src/service/api/meeting.ts
+++ b/src/service/api/meeting.ts
@@ -7,6 +7,8 @@ import type {
TopMeeting,
} from 'types/meeting';
+import { likesURL, meetingURL, memberURL, myMeetingURL } from './endpoints';
+
const getTopMeetings = async (
categoryTitle: CategoryTitle,
): Promise => {
@@ -24,7 +26,7 @@ const getMeetings = async (
): Promise> => {
const newSearchQueryObj = { ...searchQueryObj, lastMeetingId: pageParams };
const res = await axiosInstance.post(
- `/api/v1/meetings/search?categoryTitle=${category}`,
+ `${meetingURL.search}?categoryTitle=${category}`,
newSearchQueryObj,
);
@@ -32,13 +34,13 @@ const getMeetings = async (
};
const likeMeeting = async (meetingId: number) => {
- const res = await axiosInstance.post(`/api/v1/meetings/${meetingId}/likes`);
+ const res = await axiosInstance.post(likesURL.create(meetingId));
return res.data.data;
};
const cancelLikeMeeting = async (meetingId: number) => {
- const res = await axiosInstance.delete(`/api/v1/meetings/${meetingId}/likes`);
+ const res = await axiosInstance.delete(likesURL.delete(meetingId));
return res.data.data;
};
@@ -69,12 +71,12 @@ export interface MeetingManager {
}
const getMeetingDetail = async (id: number): Promise => {
- const res = await axiosInstance.get(`/api/v1/meetings/detail/${id}`);
+ const res = await axiosInstance.get(meetingURL.detail(id));
return res.data.data;
};
const getMeetingDetailManager = async (id: number): Promise => {
- const res = await axiosInstance.get(`/api/v1/meetings/detail/manager/${id}`);
+ const res = await axiosInstance.get(meetingURL.managerDetail(id));
return res.data.data;
};
@@ -86,7 +88,7 @@ const postMeetingRegister = async ({
meetingId: number;
message: string;
}) => {
- const res = await axiosInstance.post(`/api/v1/members/${meetingId}`, {
+ const res = await axiosInstance.post(memberURL.create(meetingId), {
message,
});
return res.data.data;
@@ -94,9 +96,7 @@ const postMeetingRegister = async ({
// Approve 상태에서 모임 탈퇴
const deleteMeetingQuit = async (meetingId: number) => {
- const res = await axiosInstance.delete(
- `/api/v1/mymeetings/quit/${meetingId}`,
- );
+ const res = await axiosInstance.delete(myMeetingURL.quit(meetingId));
return res.data.data;
};
diff --git a/src/service/api/mymeeting.ts b/src/service/api/mymeeting.ts
index f8995b63..c48b3ba3 100644
--- a/src/service/api/mymeeting.ts
+++ b/src/service/api/mymeeting.ts
@@ -1,6 +1,7 @@
import axiosInstance from '@/lib/axios/axiosInstance';
import { Paginated } from 'types/meeting';
import type { IMemberProfile, IMyMeetingManage } from 'types/myMeeting';
+import { IMyMeetingLikes, IMyMeetingParticipated } from 'types/myMeeting';
import { myMeetingURL } from './endpoints';
@@ -15,10 +16,10 @@ const getMyMeetingManage = async (
return res.data.data;
};
-// 내가 참여하고있는 모임 불러오기기
+// 내가 참여하고있는 모임 불러오기
const getMyMeetingParticipated = async (
lastMeetingId: number,
-): Promise> => {
+): Promise> => {
const res = await axiosInstance.get(
`${myMeetingURL.all}?lastMeetingId=${lastMeetingId}&size=${6}`,
);
@@ -26,6 +27,17 @@ const getMyMeetingParticipated = async (
return res.data.data;
};
+// 내가 찜한 모임 불러오기
+const getMyMeetingLikes = async (
+ lastMeetingId: number,
+): Promise> => {
+ const res = await axiosInstance.get(
+ `${myMeetingURL.likes}?lastMeetingId=${lastMeetingId}&size=${6}`,
+ );
+
+ return res.data.data;
+};
+
// 맴버 프로필 불러오기
const getMyMeetingMemberProfile = async ({
userId,
@@ -85,11 +97,26 @@ const putIsPublic = async (meetingId: number) => {
return res.data.data;
};
+// 참가중인 모임 나가기
+const deleteQuit = async (meetingId: number) => {
+ const res = await axiosInstance.delete(`${myMeetingURL.quit(meetingId)}`);
+ return res.data.data;
+};
+
+// 승인 대기중인 모임 취소하기
+const deleteCancel = async (meetingId: number) => {
+ const res = await axiosInstance.delete(`${myMeetingURL.cancel(meetingId)}`);
+ return res.data.data;
+};
+
export {
getMyMeetingManage,
getMyMeetingMemberProfile,
getMyMeetingParticipated,
+ getMyMeetingLikes,
putMemberStatus,
putExpel,
putIsPublic,
+ deleteQuit,
+ deleteCancel,
};
diff --git a/src/types/myMeeting.ts b/src/types/myMeeting.ts
index 8dece93c..07d5e7ec 100644
--- a/src/types/myMeeting.ts
+++ b/src/types/myMeeting.ts
@@ -1,3 +1,4 @@
+import { CategoryTitle } from './meeting';
import { IContactResponse } from './mypageTypes';
interface Member {
@@ -30,6 +31,31 @@ interface IMyMeetingManage {
memberList: Member[];
}
+interface IMyMeetingParticipated {
+ categoryTitle: CategoryTitle;
+ meetingId: number;
+ title: string;
+ thumbnail: string;
+ location: string;
+ memberCount: number;
+ maxMember: number;
+ likesCount: number;
+ myMemberStatus: 'APPROVED' | 'REJECTED' | 'PENDING' | 'EXPEL';
+ memberList: Member[];
+ isMeetingManager: boolean;
+}
+
+interface IMyMeetingLikes {
+ meetingId: number;
+ categoryTitle: CategoryTitle;
+ title: string;
+ thumbnail: string;
+ location: string;
+ memberCount: number;
+ maxMember: number;
+ likesCount: number;
+}
+
interface IUserProfile {
userId: number;
name: string;
@@ -75,4 +101,6 @@ export type {
IMemberProfile,
IBanner,
UserData,
+ IMyMeetingParticipated,
+ IMyMeetingLikes,
};
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 41406062..faf8f9d5 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -10,6 +10,11 @@ export default {
],
theme: {
extend: {
+ animation: {
+ 'delay-100': 'bounce 1s infinite 100ms',
+ 'delay-200': 'bounce 1s infinite 200ms',
+ 'delay-300': 'bounce 1s infinite 300ms',
+ },
screens: {
sm: {
min: '375px',