diff --git a/src/app/reservations/page.tsx b/src/app/reservations/page.tsx
index 1abf4de..abcd270 100644
--- a/src/app/reservations/page.tsx
+++ b/src/app/reservations/page.tsx
@@ -19,7 +19,7 @@ import BottomSheet from '@/components/common/BottomSheet';
import Tabs from '@/components/common/Tabs';
import LoginRequired from '@/components/common/LoginRequired';
import { formatDate } from '@/lib/utils';
-import { useReservationsQuery, useCancelReservationMutation } from '@/lib/reservationQuery';
+import { useReservationsQuery, useCancelReservationMutation, useMarkVisitedMutation } from '@/lib/reservationQuery';
import {
useCreateReviewMutation,
useMyReviewsQuery,
@@ -48,12 +48,16 @@ const STATUS_CONFIG: Record<
function ReservationCard({
reservation,
onCancel,
+ onMarkVisited,
+ isMarkingVisited,
onWriteReview,
onEditReview,
isReviewed,
}: {
reservation: Reservation;
onCancel: (id: number) => void;
+ onMarkVisited: (id: number) => void;
+ isMarkingVisited: boolean;
onWriteReview: (reservation: Reservation) => void;
onEditReview: (reservation: Reservation) => void;
isReviewed: boolean;
@@ -105,23 +109,34 @@ function ReservationCard({
{/* 액션 버튼 */}
{isUpcoming && (
-
-
-
+
+ {reservation.status === 'CONFIRMED' && (
+
+ )}
+
+
+
+
)}
@@ -166,6 +181,7 @@ export default function ReservationsPage() {
const userId = useAuthStore((s) => s.userId) ?? 0;
const { data: reservations = [], isLoading } = useReservationsQuery();
const { mutate: cancelReservation } = useCancelReservationMutation();
+ const { mutate: markVisited, isPending: isMarkingVisited } = useMarkVisitedMutation();
const { data: vacancies = [], isLoading: isVacancyLoading } = useMyVacanciesQuery();
const { mutate: cancelVacancy } = useCancelVacancyMutation();
const [cancelTarget, setCancelTarget] = useState
(null);
@@ -199,6 +215,14 @@ export default function ReservationsPage() {
setCancelTarget(id);
};
+ const handleMarkVisited = (id: number) => {
+ markVisited(id, {
+ onSuccess: () => {
+ toast.success('방문 처리되었습니다.');
+ },
+ });
+ };
+
const confirmCancel = () => {
if (cancelTarget === null) return;
cancelReservation(cancelTarget, {
@@ -370,6 +394,8 @@ export default function ReservationsPage() {
key={reservation.id}
reservation={reservation}
onCancel={handleCancel}
+ onMarkVisited={handleMarkVisited}
+ isMarkingVisited={isMarkingVisited}
onWriteReview={openReviewModal}
onEditReview={openEditReviewModal}
isReviewed={reviewedReservationIds.has(reservation.id)}
diff --git a/src/lib/reservationApi.ts b/src/lib/reservationApi.ts
index 5364cd9..1bbccac 100644
--- a/src/lib/reservationApi.ts
+++ b/src/lib/reservationApi.ts
@@ -92,3 +92,11 @@ export const updateReservation = async (
const response = await api.patch(`/reservations/${reservationId}`, payload);
return unwrap(response, {} as Reservation);
};
+
+/**
+ * 사용자가 직접 "방문 확정" 버튼을 눌러 예약을 VISITED 상태로 전환한다.
+ * 백엔드는 CONFIRMED 상태에서만 허용하며 ReservationVisitedEvent로 알림이 자동 발송된다.
+ */
+export const markReservationVisited = async (reservationId: number): Promise => {
+ await api.patch(`/reservations/${reservationId}/visit`);
+};
diff --git a/src/lib/reservationQuery.ts b/src/lib/reservationQuery.ts
index c91b560..21d1b7e 100644
--- a/src/lib/reservationQuery.ts
+++ b/src/lib/reservationQuery.ts
@@ -1,6 +1,6 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useAuthStore } from '@/stores/authStore';
-import { createReservation, getReservations, cancelReservation, updateReservation } from './reservationApi';
+import { createReservation, getReservations, cancelReservation, updateReservation, markReservationVisited } from './reservationApi';
import type { ReservationRequest, ReservationUpdateRequest } from './reservationApi';
const reservationsKey = (userId: number | null) => ['reservations', userId];
@@ -55,3 +55,15 @@ export const useCancelReservationMutation = () => {
},
});
};
+
+export const useMarkVisitedMutation = () => {
+ const queryClient = useQueryClient();
+ const userId = useAuthStore((s) => s.userId);
+
+ return useMutation({
+ mutationFn: (reservationId: number) => markReservationVisited(reservationId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: reservationsKey(userId) });
+ },
+ });
+};