+ {/* 이미지 영역 */}
+
+
+
+
+ {/* 콘텐츠 영역 */}
+
+ {/* 별점 및 리뷰 */}
+
+
+
+ {rating}
+
+ ({reviewCount})
+
+
+
+
+ {/* 제목 */}
+
+
{title}
+
+
+
+ {/* 가격 */}
+
+ ₩{price.toLocaleString()} / 인
+
+
+ {/* 더보기 옵션 */}
+
+
+
+ {isMenuOpen && (
+ <>
+
setIsMenuOpen(false)}
+ />
+
+ {/* 드롭다운 메뉴 */}
+
+
+
+
+ >
+ )}
+
+
+
+
+ );
+}
diff --git a/src/app/(with-header)/mypage/activities/components/DeleteActivityModal.tsx b/src/app/(with-header)/mypage/activities/components/DeleteActivityModal.tsx
new file mode 100644
index 0000000..0848f2e
--- /dev/null
+++ b/src/app/(with-header)/mypage/activities/components/DeleteActivityModal.tsx
@@ -0,0 +1,61 @@
+'use client';
+
+import Modal from '@/components/Modal';
+import Button from '@/components/Button';
+import CheckIcon from '@assets/svg/check';
+
+interface DeleteActivityModalProps {
+ isOpen: boolean;
+ onCancel: () => void;
+ onConfirm: () => void;
+ isLoading?: boolean;
+}
+
+export default function DeleteActivityModal({
+ isOpen,
+ onCancel,
+ onConfirm,
+ isLoading = false,
+}: DeleteActivityModalProps) {
+ return (
+
!open && onCancel()}>
+
+
+ {/* 체크 아이콘 */}
+
+
+
+
+ {/* 메시지 */}
+
+ 체험을 삭제하시겠어요?
+
+
+ {/* 버튼 */}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/(with-header)/mypage/activities/components/EmptyActivities.tsx b/src/app/(with-header)/mypage/activities/components/EmptyActivities.tsx
new file mode 100644
index 0000000..41e50f8
--- /dev/null
+++ b/src/app/(with-header)/mypage/activities/components/EmptyActivities.tsx
@@ -0,0 +1,17 @@
+import EmptyDocumentIcon from '@assets/svg/empty-document';
+
+export default function EmptyActivities() {
+ return (
+
+ {/* 빈 상태 아이콘 */}
+
+
+
+
+ {/* 빈 상태 메시지 */}
+
+ 아직 등록한 체험이 없어요
+
+
+ );
+}
diff --git a/src/app/(with-header)/mypage/activities/page.tsx b/src/app/(with-header)/mypage/activities/page.tsx
index 4d564d9..c979ad3 100644
--- a/src/app/(with-header)/mypage/activities/page.tsx
+++ b/src/app/(with-header)/mypage/activities/page.tsx
@@ -1,18 +1,175 @@
+'use client';
+
+import { useState } from 'react';
+import { useRouter } from 'next/navigation';
+import {
+ useMyActivitiesInfinite,
+ useDeleteMyActivity,
+} from '@/hooks/useMyActivitiesQueries';
+import useInfiniteScroll from '@/hooks/useInfiniteScroll';
+import Button from '@/components/Button';
+import ActivityCard from './components/ActivityCard';
+import EmptyActivities from './components/EmptyActivities';
+import DeleteActivityModal from './components/DeleteActivityModal';
+
export default function MyActivitiesPage() {
+ const router = useRouter();
+ const [deleteModal, setDeleteModal] = useState<{
+ isOpen: boolean;
+ activityId: number | null;
+ }>({
+ isOpen: false,
+ activityId: null,
+ });
+
+ // 내 체험 리스트 조회
+ const {
+ data,
+ fetchNextPage,
+ hasNextPage,
+ isFetchingNextPage,
+ isLoading,
+ error,
+ } = useMyActivitiesInfinite();
+
+ // 체험 삭제
+ const deleteActivityMutation = useDeleteMyActivity();
+
+ // 무한 스크롤 훅
+ const { lastElementRef } = useInfiniteScroll({
+ hasNextPage,
+ isFetchingNextPage,
+ isLoading,
+ fetchNextPage,
+ });
+
+ // 체험 등록하기
+ const handleCreateActivity = () => {
+ router.push('/myactivity');
+ };
+
+ const handleDeleteClick = (activityId: number) => {
+ setDeleteModal({
+ isOpen: true,
+ activityId,
+ });
+ };
+
+ const handleDeleteConfirm = () => {
+ if (deleteModal.activityId) {
+ deleteActivityMutation.mutate(deleteModal.activityId);
+ }
+ setDeleteModal({ isOpen: false, activityId: null });
+ };
+
+ const handleDeleteClose = () => {
+ setDeleteModal({ isOpen: false, activityId: null });
+ };
+
+ // 전체 체험 목록
+ const allActivities = data?.pages.flatMap((page) => page.activities) ?? [];
+
+ // 로딩 상태
+ if (isLoading) {
+ return (
+
+
+
+ 내 체험 관리
+
+
+
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+
+ );
+ }
+
+ // 에러 상태
+ if (error) {
+ return (
+
+
+
+ 내 체험 관리
+
+
+
+
+
체험 목록을 불러오는데 실패했습니다.
+
{error.message}
+
+
+ );
+ }
+
return (
<>
- {/* 제목 */}
-
-
- 내 체험 관리
-
-
+
+ {/* 제목 */}
+
+
+ 내 체험 관리
+
+
+
- {/* 내 체험 관리 컨텐츠 */}
-
-
내 체험 관리 페이지입니다.
- {/* TODO: 내 체험 관리 컴포넌트 구현 */}
+ {/* 체험 목록 */}
+ {allActivities.length === 0 ? (
+
+ ) : (
+
+ {allActivities.map((activity, index) => (
+
+ ))}
+
+ {/* 무한 스크롤 로딩 */}
+ {isFetchingNextPage && (
+
+ )}
+
+ )}
+
+ {/* 삭제 확인 모달 */}
+
>
);
}
diff --git a/src/app/(with-header)/mypage/reservations/components/CancelReservationModal.tsx b/src/app/(with-header)/mypage/reservations/components/CancelReservationModal.tsx
index eedefe2..b1696bf 100644
--- a/src/app/(with-header)/mypage/reservations/components/CancelReservationModal.tsx
+++ b/src/app/(with-header)/mypage/reservations/components/CancelReservationModal.tsx
@@ -19,16 +19,12 @@ export default function CancelReservationModal({
}: CancelReservationModalProps) {
return (
!open && onCancel()}>
-
-
+
+
{/* 체크 아이콘 */}
@@ -43,7 +39,7 @@ export default function CancelReservationModal({