-
체험 후기
+ 체험 후기
후기를 불러오는 중 오류가 발생했습니다: {error instanceof Error ? error.message : '알 수 없는 오류'}
@@ -60,16 +41,16 @@ export default function ReviewSection({ activityId }: ReviewSectionProps) {
return (
-
-
체험 후기
-
{totalCount.toLocaleString()}개
+
+
체험 후기
+ {totalCount.toLocaleString()}개
{totalCount > 0 && (
-
{averageRating.toFixed(1)}
-
{getSatisfactionText(averageRating)}
-
+
{averageRating.toFixed(1)}
+
{getSatisfactionText(averageRating)}
+
{totalCount.toLocaleString()}개 후기
@@ -80,25 +61,19 @@ export default function ReviewSection({ activityId }: ReviewSectionProps) {
{allReviews.length > 0 ? (
allReviews.map((review: ActivityReview) =>
)
) : (
-
아직 등록된 후기가 없습니다.
+
+
+
)}
{/* 무한스크롤 트리거 */}
- {/* 더보기 버튼 (옵션) */}
- {hasNextPage && (
-
-
-
- )}
-
{isFetchingNextPage && (
-
-
후기를 더 불러오는 중...
+
+
+
)}
diff --git a/apps/what-today/src/components/activities/reservation/HeadCountSelector.tsx b/apps/what-today/src/components/activities/reservation/HeadCountSelector.tsx
index 7b075ef6..2b8fd998 100644
--- a/apps/what-today/src/components/activities/reservation/HeadCountSelector.tsx
+++ b/apps/what-today/src/components/activities/reservation/HeadCountSelector.tsx
@@ -10,15 +10,15 @@ interface HeadCountSelectorProps {
export default function HeadCountSelector({ headCount, onIncrease, onDecrease }: HeadCountSelectorProps) {
return (
-
참여 인원 수
+
참여 인원 수
-
-
diff --git a/apps/what-today/src/components/activities/reservation/MobileReservationSheet.tsx b/apps/what-today/src/components/activities/reservation/MobileReservationSheet.tsx
index 7d060aed..ab6c9cfb 100644
--- a/apps/what-today/src/components/activities/reservation/MobileReservationSheet.tsx
+++ b/apps/what-today/src/components/activities/reservation/MobileReservationSheet.tsx
@@ -1,4 +1,4 @@
-import { BottomSheet, Button } from '@what-today/design-system';
+import { BottomSheet, Button, ClockIcon } from '@what-today/design-system';
import { ArrowIcon } from '@what-today/design-system';
import { useState } from 'react';
@@ -81,10 +81,8 @@ export default function MobileReservationSheet({
{/* 1단계: 날짜/시간 선택 */}
{currentStep === 'dateTime' && (
-
-
-
날짜 및 시간 선택
-
+
+
날짜 및 시간 선택
{/* 캘린더 */}
@@ -100,28 +98,28 @@ export default function MobileReservationSheet({
{/* 시간 선택 */}
{selectedDate && (
-
예약 가능한 시간
-
+
예약 가능한 시간
+
+
+
+
+ {/* 확인 버튼*/}
+
+ {buttonText}
+
)}
-
- {/* 확인 버튼 */}
-
-
- {buttonText}
-
-
)}
@@ -129,13 +127,13 @@ export default function MobileReservationSheet({
{/* 2단계: 인원 선택 */}
{currentStep === 'headCount' && (
-
+
@@ -143,8 +141,11 @@ export default function MobileReservationSheet({
{/* 선택된 날짜/시간 요약 */}
{selectedDate && selectedScheduleId && (
-
선택된 날짜/시간
-
+
+
+ 선택된 날짜/시간
+
+
{selectedDate.replace(/-/g, '/')}{' '}
{availableTimes.find((t) => t.id === selectedScheduleId)?.startTime} ~{' '}
{availableTimes.find((t) => t.id === selectedScheduleId)?.endTime}
@@ -156,11 +157,10 @@ export default function MobileReservationSheet({
{/* 총 금액 */}
-
-
- 총 금액
- ₩ {totalPrice.toLocaleString()}
-
+
+
+ 총 금액
+ ₩ {totalPrice.toLocaleString()}
diff --git a/apps/what-today/src/components/activities/reservation/ReservationForm.tsx b/apps/what-today/src/components/activities/reservation/ReservationForm.tsx
index 8e6d9714..ff86765c 100644
--- a/apps/what-today/src/components/activities/reservation/ReservationForm.tsx
+++ b/apps/what-today/src/components/activities/reservation/ReservationForm.tsx
@@ -1,6 +1,8 @@
import { useQueryClient } from '@tanstack/react-query';
import { Button, useToast } from '@what-today/design-system';
+import Divider from '@/components/activities/Divider';
+
import CalendarSelector from './CalendarSelector';
import HeadCountSelector from './HeadCountSelector';
import { useReservation } from './hooks/useReservation';
@@ -88,9 +90,9 @@ export default function ReservationForm({
const content = (
{/* 가격 표시 */}
-
+ {/*
₩ {price.toLocaleString()} / 인
-
+ */}
{/* 캘린더 */}
@@ -98,22 +100,24 @@ export default function ReservationForm({
{/* 시간 선택 */}
{selectedDate && (
<>
-
예약 가능한 시간
-
+
예약 가능한 시간
+
+
+
>
)}
{/* 인원 선택 */}
-
+
{/* 총 합계 */}
-
- 총 합계 ₩ {totalPrice.toLocaleString()}
+
+ 총 합계 ₩ {totalPrice.toLocaleString()}
{showSubmitButton && (
@@ -130,9 +134,5 @@ export default function ReservationForm({
);
- return (
-
- );
+ return
;
}
diff --git a/apps/what-today/src/components/activities/reservation/TabletReservationSheet.tsx b/apps/what-today/src/components/activities/reservation/TabletReservationSheet.tsx
index 5047baca..8f7f36c5 100644
--- a/apps/what-today/src/components/activities/reservation/TabletReservationSheet.tsx
+++ b/apps/what-today/src/components/activities/reservation/TabletReservationSheet.tsx
@@ -38,41 +38,43 @@ export default function TabletReservationSheet({
return (
-
+
{/* 좌측: 캘린더 */}
{
setSelectedDate(date);
- setSelectedScheduleId(null); // 날짜 바뀌면 시간 초기화
+ setSelectedScheduleId(null);
}}
/>
{/* 우측: 시간 & 인원 */}
-
예약 가능한 시간
+
예약 가능한 시간
{selectedDate ? (
-
+
+
+
) : (
-
날짜를 선택해주세요.
+
날짜를 선택해주세요.
)}
-
- 총 합계 ₩ {totalPrice.toLocaleString()}
+
+ 총 합계 ₩ {totalPrice.toLocaleString()}
-
+
+
{availableTimes.map((s) => (
+ {/* 섹션 제목 */}
+
+
+ {/* 설명 텍스트 라인들 */}
+
+
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/ActivitiesInformationSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/ActivitiesInformationSkeleton.tsx
new file mode 100644
index 00000000..75aaf0cb
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/ActivitiesInformationSkeleton.tsx
@@ -0,0 +1,28 @@
+export default function ActivitiesInformationSkeleton() {
+ return (
+
+ {/* 카테고리 */}
+
+
+ {/* 제목 */}
+
+
+ {/* 평점 */}
+
+
+ {/* 주소 */}
+
+
+ {/* 가격 */}
+
+
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/ActivitiesMapSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/ActivitiesMapSkeleton.tsx
new file mode 100644
index 00000000..179b5bff
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/ActivitiesMapSkeleton.tsx
@@ -0,0 +1,16 @@
+export default function ActivitiesMapSkeleton() {
+ return (
+
+ {/* 섹션 제목 */}
+
+
+ {/* 주소 */}
+
+
+ {/* 지도 영역 */}
+
+
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/ActivityDetailPageSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/ActivityDetailPageSkeleton.tsx
new file mode 100644
index 00000000..bbbeeb2c
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/ActivityDetailPageSkeleton.tsx
@@ -0,0 +1,64 @@
+import { useResponsive } from '@/hooks/useResponsive';
+
+import ActivitiesDescriptionSkeleton from './ActivitiesDescriptionSkeleton';
+import ActivitiesInformationSkeleton from './ActivitiesInformationSkeleton';
+import ActivitiesMapSkeleton from './ActivitiesMapSkeleton';
+import ActivityImagesSkeleton from './ActivityImagesSkeleton';
+import DividerSkeleton from './DividerSkeleton';
+import ReservationFormSkeleton from './ReservationFormSkeleton';
+import ReviewSectionSkeleton from './ReviewSectionSkeleton';
+
+export default function ActivityDetailPageSkeleton() {
+ const { isDesktop } = useResponsive();
+
+ return (
+ <>
+
+ {isDesktop ? (
+
+ {/* 왼쪽 컬럼 */}
+
+
+ {/* 오른쪽 사이드바 */}
+
+
+ ) : (
+
+ )}
+
+
+ {/* 모바일/태블릿 하단바 스켈레톤 */}
+ {!isDesktop && (
+
+ )}
+ >
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/ActivityImagesSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/ActivityImagesSkeleton.tsx
new file mode 100644
index 00000000..d6b915fa
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/ActivityImagesSkeleton.tsx
@@ -0,0 +1,30 @@
+export default function ActivityImagesSkeleton() {
+ return (
+
+ {/* 왼쪽 메인 이미지 스켈레톤 */}
+
+
+ {/* 오른쪽 서브 이미지들 스켈레톤 */}
+
+
+ {/* 서브 이미지 1 */}
+
+
+ {/* 서브 이미지 2 */}
+
+
+ {/* 서브 이미지 3 - col-span-2 */}
+
+
+
+
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/DividerSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/DividerSkeleton.tsx
new file mode 100644
index 00000000..188a2410
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/DividerSkeleton.tsx
@@ -0,0 +1,3 @@
+export default function DividerSkeleton() {
+ return ;
+}
diff --git a/apps/what-today/src/components/skeletons/activities/ReservationFormSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/ReservationFormSkeleton.tsx
new file mode 100644
index 00000000..f2cb601d
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/ReservationFormSkeleton.tsx
@@ -0,0 +1,46 @@
+export default function ReservationFormSkeleton() {
+ return (
+
+
+ {/* 캘린더 스켈레톤 */}
+
+ {/* 캘린더 헤더 */}
+
+
+ {/* 캘린더 그리드 */}
+
+ {Array.from({ length: 35 }).map((_, index) => (
+
+ ))}
+
+
+
+ {/* 인원 선택 스켈레톤 */}
+
+
+ {/* 구분선 */}
+
+
+ {/* 총 합계 */}
+
+
+
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/ReviewCardSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/ReviewCardSkeleton.tsx
new file mode 100644
index 00000000..2e7333d9
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/ReviewCardSkeleton.tsx
@@ -0,0 +1,27 @@
+export default function ReviewCardSkeleton() {
+ return (
+
+ {/* 리뷰 헤더 */}
+
+
+
+
+
+
+ {Array.from({ length: 5 }).map((_, starIndex) => (
+
+ ))}
+
+
+
+
+
+
+ {/* 리뷰 내용 */}
+
+
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/ReviewSectionSkeleton.tsx b/apps/what-today/src/components/skeletons/activities/ReviewSectionSkeleton.tsx
new file mode 100644
index 00000000..d1df232c
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/ReviewSectionSkeleton.tsx
@@ -0,0 +1,30 @@
+import ReviewCardSkeleton from './ReviewCardSkeleton';
+
+export default function ReviewSectionSkeleton() {
+ return (
+
+ {/* 섹션 헤더 */}
+
+
+ {/* 평점 통계 */}
+
+
+ {/* 리뷰 카드들 */}
+
+ {Array.from({ length: 3 }).map((_, index) => (
+
+ ))}
+
+
+ );
+}
diff --git a/apps/what-today/src/components/skeletons/activities/index.ts b/apps/what-today/src/components/skeletons/activities/index.ts
new file mode 100644
index 00000000..bafbd27d
--- /dev/null
+++ b/apps/what-today/src/components/skeletons/activities/index.ts
@@ -0,0 +1,9 @@
+export { default as ActivitiesDescriptionSkeleton } from './ActivitiesDescriptionSkeleton';
+export { default as ActivitiesInformationSkeleton } from './ActivitiesInformationSkeleton';
+export { default as ActivitiesMapSkeleton } from './ActivitiesMapSkeleton';
+export { default as ActivityDetailPageSkeleton } from './ActivityDetailPageSkeleton';
+export { default as ActivityImagesSkeleton } from './ActivityImagesSkeleton';
+export { default as DividerSkeleton } from './DividerSkeleton';
+export { default as ReservationFormSkeleton } from './ReservationFormSkeleton';
+export { default as ReviewCardSkeleton } from './ReviewCardSkeleton';
+export { default as ReviewSectionSkeleton } from './ReviewSectionSkeleton';
diff --git a/apps/what-today/src/pages/activities/index.tsx b/apps/what-today/src/pages/activities/index.tsx
index 31144763..c8ac07be 100644
--- a/apps/what-today/src/pages/activities/index.tsx
+++ b/apps/what-today/src/pages/activities/index.tsx
@@ -1,5 +1,5 @@
import { useQueryClient } from '@tanstack/react-query';
-import { SpinIcon, useToast } from '@what-today/design-system';
+import { useToast } from '@what-today/design-system';
import { useState } from 'react';
import { useParams } from 'react-router-dom';
@@ -15,6 +15,7 @@ import TabletReservationSheet from '@/components/activities/reservation/TabletRe
import type { ReservationSummary } from '@/components/activities/reservation/types';
import ReservationBottomBar from '@/components/activities/ReservationBottomBar';
import ReviewSection from '@/components/activities/ReviewSection';
+import { ActivityDetailPageSkeleton } from '@/components/skeletons/activities';
import { useActivityDetail } from '@/hooks/activityDetail';
import { useResponsive } from '@/hooks/useResponsive';
import NotFoundPage from '@/pages/not-found-page';
@@ -36,12 +37,7 @@ export default function ActivityDetailPage() {
const { data: activity, isLoading: loading, error } = useActivityDetail(id);
- if (loading)
- return (
-
-
-
- );
+ if (loading) return ;
if (error) return ;
if (!activity) return ;
@@ -96,7 +92,7 @@ export default function ActivityDetailPage() {
<>
{isDesktop ? (
-
+
@@ -111,6 +107,7 @@ export default function ActivityDetailPage() {
category={activity.category}
id={id}
isAuthor={user?.id ? activity?.userId === user.id : false}
+ price={activity.price}
rating={activity.rating}
reviewCount={activity.reviewCount}
title={activity.title}
@@ -133,15 +130,16 @@ export default function ActivityDetailPage() {
category={activity.category}
id={id}
isAuthor={user?.id ? activity?.userId === user.id : false}
+ price={activity.price}
rating={activity.rating}
reviewCount={activity.reviewCount}
title={activity.title}
/>
-
+
-
+
-
+
)}