From 5207cfe00922aeee8a8f6e087319344a37a92bc9 Mon Sep 17 00:00:00 2001 From: evaain706 Date: Tue, 22 Jul 2025 06:10:00 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=EC=B2=B4=ED=97=98=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=84=EC=B2=B4=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=201=EC=B0=A8=EC=99=84=EC=84=B1/?= =?UTF-8?q?=ED=83=9C=EB=B8=94=EB=A6=BF=ED=99=94=EB=A9=B4=20=ED=8C=9D?= =?UTF-8?q?=EC=97=85=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/assets/svg/star.tsx | 19 +++ .../[activitiesId]/components/ReviewCard.tsx | 4 +- .../[activitiesId]/components/ReviewTitle.tsx | 20 +++ .../[activitiesId]/components/Title.tsx | 7 +- .../activities/[activitiesId]/page.tsx | 49 +++++-- src/components/DatePicker/DatePicker.tsx | 2 +- .../FloatingBox/BookingInterface.tsx | 137 +++++++++--------- src/components/FloatingBox/TabletPopup.tsx | 29 +++- src/components/LocationMap.tsx | 2 +- src/ui/TabletBookingModal.tsx | 48 ++++-- 10 files changed, 208 insertions(+), 109 deletions(-) create mode 100644 public/assets/svg/star.tsx create mode 100644 src/app/(with-header)/activities/[activitiesId]/components/ReviewTitle.tsx diff --git a/public/assets/svg/star.tsx b/public/assets/svg/star.tsx new file mode 100644 index 0000000..56656ad --- /dev/null +++ b/public/assets/svg/star.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const Star = ({ size = 20, color = '#fff', ...props }) => ( + + + +); + +export default Star; diff --git a/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx b/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx index e44a42c..9cebacd 100644 --- a/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx +++ b/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx @@ -14,10 +14,10 @@ export default function ReviewCard({ avatarSrc, }: UserReviewProps) { return ( -
+
-
+

{userName}

|

{date}

diff --git a/src/app/(with-header)/activities/[activitiesId]/components/ReviewTitle.tsx b/src/app/(with-header)/activities/[activitiesId]/components/ReviewTitle.tsx new file mode 100644 index 0000000..720e2dc --- /dev/null +++ b/src/app/(with-header)/activities/[activitiesId]/components/ReviewTitle.tsx @@ -0,0 +1,20 @@ +import Star from '@assets/svg/star'; + +export default function ReviewTitle() { + return ( +
+

후기

+ +
+

4.2

+
+

매우 만족

+
+ +

1300개 후기

+
+
+
+
+ ); +} diff --git a/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx b/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx index bc4efc8..20c386d 100644 --- a/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx +++ b/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx @@ -1,5 +1,6 @@ import React from 'react'; import IconDropdown from '@assets/svg/dropdown'; +import Star from '@assets/svg/star'; interface TitleProps { title: string; @@ -25,9 +26,9 @@ export default function Title({

{title}

-
+
- + {rating.toFixed(1)} ({reviewCount}명) @@ -37,7 +38,7 @@ export default function Title({
{isDropDown && ( -
+
)} diff --git a/src/app/(with-header)/activities/[activitiesId]/page.tsx b/src/app/(with-header)/activities/[activitiesId]/page.tsx index c6bc385..f1e6e7c 100644 --- a/src/app/(with-header)/activities/[activitiesId]/page.tsx +++ b/src/app/(with-header)/activities/[activitiesId]/page.tsx @@ -4,6 +4,9 @@ import { mockActivity } from './mock/mock'; import Title from './components/Title'; import ImageGrid from './components/ImageGrid'; import ReviewCard from './components/ReviewCard'; +import BookingInterface from '@/components/FloatingBox/BookingInterface'; +import LocationMap from '@/components/LocationMap'; +import ReviewTitle from './components/ReviewTitle'; export default function ActivityDetailPage() { const { @@ -31,18 +34,46 @@ export default function ActivityDetailPage() { /> -
+
-

체험 설명

-

{description}

+

체험 설명

+

{description}

+
+
+ +
+ +
+

체험 장소

+ + + + + + +
-
); } diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx index e02fbfd..10077e3 100644 --- a/src/components/DatePicker/DatePicker.tsx +++ b/src/components/DatePicker/DatePicker.tsx @@ -54,7 +54,7 @@ export default function DatePicker() { const highlightDates = availableDates.map((item) => dayjs(item.date)); return ( -
+
{ @@ -20,86 +21,82 @@ export default function BookingInterface() { const [openTablet, setOpenTablet] = useState(false); return ( -
-
- {/* PC */} -
-
+
+ {/* PC */} +
+
+ +
+ +
+ + + 예약하기 + +
+
+ + {/* 태블릿 */} +
+
+
-
- -
- +

날짜

+ +
+
+ + 예약하기
+
- {/* 태블릿 */} -
-
-
- -

날짜

- + {/* 모바일 */} +
+
+
+
+ ₩ 10,000{' '} + + / 총 {participants}인 +
-
- - - {openTablet && ( -
- -
+
setIsOpen(true)} + className='mb-4 text-sm text-gray-600' + > + {selectedDate && selectedTime ? ( +

+ {selectedDate instanceof Date + ? selectedDate.toLocaleDateString() + : selectedDate} + /{selectedTime} +

+ ) : ( + '날짜 선택하기' )} - 예약하기 - -
-
-
- - {/* 모바일 */} -
-
-
-
- ₩ 10,000{' '} - - / 총 {participants}인 - -
-
setIsOpen(true)} - className='mb-4 text-sm text-gray-600' - > - {selectedDate && selectedTime ? ( -

- {selectedDate instanceof Date - ? selectedDate.toLocaleDateString() - : selectedDate} - /{selectedTime} -

- ) : ( - '날짜 선택하기' - )} -
- - 예약하기
+ +
diff --git a/src/components/FloatingBox/TabletPopup.tsx b/src/components/FloatingBox/TabletPopup.tsx index d6ba379..0bad5de 100644 --- a/src/components/FloatingBox/TabletPopup.tsx +++ b/src/components/FloatingBox/TabletPopup.tsx @@ -1,15 +1,28 @@ +'use client'; + +import useBookingStore from '@/stores/Booking/useBookingStore'; +import IconClose from '@assets/svg/close'; import DatePicker from '../DatePicker/DatePicker'; +import TimeSelector from './TimeSelector'; + +export default function TabletPopup({}) { + const isOpen = useBookingStore((state) => state.isOpen); + const setIsOpen = useBookingStore((state) => state.setIsOpen); + + if (!isOpen) return null; -export default function TabletPopup() { return ( -
-
- +
+
+

날짜

+
+ + + +
); } diff --git a/src/components/LocationMap.tsx b/src/components/LocationMap.tsx index 11c1266..2c7dd78 100644 --- a/src/components/LocationMap.tsx +++ b/src/components/LocationMap.tsx @@ -66,7 +66,7 @@ const LocationMap = ({ address }: LocationMapProps) => { return ( <> -
+
{/* 지도 */} state.isOpen); const setIsOpen = useBookingStore((state) => state.setIsOpen); + if (!isOpen) return null; + return ( - - - - 날짜 - - - -
- -
+
+
+

날짜

+ +
+ +
+
+ +
+ +
+

+ 예약 가능한 시간 +

- +
- - - + +
+
); } From 3c91491c795f647cef8321e9f6091174ed77d0a9 Mon Sep 17 00:00:00 2001 From: evaain706 Date: Tue, 22 Jul 2025 06:32:57 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[#64]=20chore:=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[activitiesId]/components/ImageGrid.tsx | 8 ++----- .../[activitiesId]/components/ReviewCard.tsx | 10 ++------ .../[activitiesId]/components/Title.tsx | 10 +------- src/components/DatePicker/CalendarBody.tsx | 8 +------ src/components/DatePicker/CalendarHeader.tsx | 7 +----- src/types/activityDetailType.ts | 23 +++++++++++++++++++ src/types/calendarType.ts | 16 +++++++++++++ 7 files changed, 46 insertions(+), 36 deletions(-) create mode 100644 src/types/activityDetailType.ts create mode 100644 src/types/calendarType.ts diff --git a/src/app/(with-header)/activities/[activitiesId]/components/ImageGrid.tsx b/src/app/(with-header)/activities/[activitiesId]/components/ImageGrid.tsx index dfbb559..e3a597e 100644 --- a/src/app/(with-header)/activities/[activitiesId]/components/ImageGrid.tsx +++ b/src/app/(with-header)/activities/[activitiesId]/components/ImageGrid.tsx @@ -2,16 +2,12 @@ import Image from 'next/image'; import React, { useState } from 'react'; - -interface ActivityImageProps { - mainImage: string; - subImages: string[]; -} +import { ImageGridProps } from '@/types/activityDetailType'; export default function ImageGrid({ mainImage, subImages, -}: ActivityImageProps) { +}: ImageGridProps) { const images = [mainImage, ...subImages]; const [currentIndex, setCurrentIndex] = useState(0); //캐러셀 구현용 state diff --git a/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx b/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx index 9cebacd..0e35241 100644 --- a/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx +++ b/src/app/(with-header)/activities/[activitiesId]/components/ReviewCard.tsx @@ -1,18 +1,12 @@ import Avatar from '@/components/Avatar'; - -interface UserReviewProps { - userName: string; - date: string; - reviewText: string; - avatarSrc: string; -} +import { ReviewCardProps } from '@/types/activityDetailType'; export default function ReviewCard({ userName, date, reviewText, avatarSrc, -}: UserReviewProps) { +}: ReviewCardProps) { return (
diff --git a/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx b/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx index 20c386d..02091e3 100644 --- a/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx +++ b/src/app/(with-header)/activities/[activitiesId]/components/Title.tsx @@ -1,15 +1,7 @@ import React from 'react'; import IconDropdown from '@assets/svg/dropdown'; import Star from '@assets/svg/star'; - -interface TitleProps { - title: string; - category: string; - rating: number; - reviewCount: number; - address: string; - isDropDown?: boolean; -} +import { TitleProps } from '@/types/activityDetailType'; export default function Title({ title, diff --git a/src/components/DatePicker/CalendarBody.tsx b/src/components/DatePicker/CalendarBody.tsx index a8ab86f..7fe1e6f 100644 --- a/src/components/DatePicker/CalendarBody.tsx +++ b/src/components/DatePicker/CalendarBody.tsx @@ -2,13 +2,7 @@ import type dayjs from 'dayjs'; -interface CalendarBodyProps { - viewDate: dayjs.Dayjs; - today: dayjs.Dayjs; - selectedDate: dayjs.Dayjs; - onSelectDate: (date: dayjs.Dayjs) => void; - highlightDates?: dayjs.Dayjs[]; -} +import { CalendarBodyProps } from '@/types/datePickerTypes'; export default function CalendarBody({ viewDate, diff --git a/src/components/DatePicker/CalendarHeader.tsx b/src/components/DatePicker/CalendarHeader.tsx index 747e528..d27a6b7 100644 --- a/src/components/DatePicker/CalendarHeader.tsx +++ b/src/components/DatePicker/CalendarHeader.tsx @@ -1,11 +1,6 @@ 'use client'; -import type dayjs from 'dayjs'; - -interface CalendarHeaderProps { - viewDate: dayjs.Dayjs; - onMonthChange: (direction: 'add' | 'subtract') => void; -} +import { CalendarHeaderProps } from '@/types/datePickerTypes'; export default function CalendarHeader({ viewDate, diff --git a/src/types/activityDetailType.ts b/src/types/activityDetailType.ts new file mode 100644 index 0000000..1529396 --- /dev/null +++ b/src/types/activityDetailType.ts @@ -0,0 +1,23 @@ +export interface ImageGridProps { + mainImage: string; + subImages: string[]; +} + + +export interface ReviewCardProps { + userName: string; + date: string; + reviewText: string; + avatarSrc: string; +} + + + +export interface TitleProps { + title: string; + category: string; + rating: number; + reviewCount: number; + address: string; + isDropDown?: boolean; +} \ No newline at end of file diff --git a/src/types/calendarType.ts b/src/types/calendarType.ts new file mode 100644 index 0000000..49c2626 --- /dev/null +++ b/src/types/calendarType.ts @@ -0,0 +1,16 @@ +import type dayjs from 'dayjs'; + +export interface CalendarHeaderProps { + viewDate: dayjs.Dayjs; + onMonthChange: (direction: 'add' | 'subtract') => void; +} + + + +export interface CalendarBodyProps { + viewDate: dayjs.Dayjs; + today: dayjs.Dayjs; + selectedDate: dayjs.Dayjs; + onSelectDate: (date: dayjs.Dayjs) => void; + highlightDates?: dayjs.Dayjs[]; +} \ No newline at end of file From 8b0b366bf20618db9dca4d40d8c9a13c69fef32f Mon Sep 17 00:00:00 2001 From: evaain706 Date: Tue, 22 Jul 2025 06:52:11 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[#64]=20fix:=20=EB=B9=8C=EB=93=9C=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FloatingBox/BookingInterface.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/FloatingBox/BookingInterface.tsx b/src/components/FloatingBox/BookingInterface.tsx index 568f679..04a92b4 100644 --- a/src/components/FloatingBox/BookingInterface.tsx +++ b/src/components/FloatingBox/BookingInterface.tsx @@ -8,7 +8,6 @@ import TimeSelector from './TimeSelector'; import TotalPriceDisplay from './TotalPriceDisplay'; import BookingModal from '@/ui/BookingModal'; import { useState } from 'react'; -import TabletPopup from './TabletPopup'; import DatePicker from '../DatePicker/DatePicker'; import Button from '../Button'; @@ -18,7 +17,7 @@ export default function BookingInterface() { }; const setIsOpen = useBookingStore((state) => state.setIsOpen); const { selectedDate, selectedTime, participants } = useBookingStore(); - const [openTablet, setOpenTablet] = useState(false); + return (
From cb5970c997820585dcd3a1075051771259d797fa Mon Sep 17 00:00:00 2001 From: evaain706 Date: Tue, 22 Jul 2025 06:54:04 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[#64]=20fix:=20=EB=B9=8C=EB=93=9C=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FloatingBox/BookingInterface.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/FloatingBox/BookingInterface.tsx b/src/components/FloatingBox/BookingInterface.tsx index 04a92b4..4aaa495 100644 --- a/src/components/FloatingBox/BookingInterface.tsx +++ b/src/components/FloatingBox/BookingInterface.tsx @@ -7,7 +7,6 @@ import PriceDisplay from './PriceDisplay'; import TimeSelector from './TimeSelector'; import TotalPriceDisplay from './TotalPriceDisplay'; import BookingModal from '@/ui/BookingModal'; -import { useState } from 'react'; import DatePicker from '../DatePicker/DatePicker'; import Button from '../Button'; @@ -18,7 +17,6 @@ export default function BookingInterface() { const setIsOpen = useBookingStore((state) => state.setIsOpen); const { selectedDate, selectedTime, participants } = useBookingStore(); - return (
{/* PC */} @@ -68,7 +66,7 @@ export default function BookingInterface() {
{/* 모바일 */} -
+
From ad939c0f3458c2247afcf97e0386bdfc1ced173e Mon Sep 17 00:00:00 2001 From: evaain706 Date: Wed, 23 Jul 2025 00:54:58 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[#64]=20feat:=20BookingInterface=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/assets/svg/location.tsx | 16 +++++++------ public/assets/svg/star.tsx | 3 ++- src/components/FloatingBox/BookingButton.tsx | 6 +++-- .../FloatingBox/BookingInterface.tsx | 21 +++++++++++++---- src/components/FloatingBox/TimeSelector.tsx | 6 ++++- src/components/LocationMap.tsx | 23 ++++++++++++------- src/stores/Booking/useBookingStore.ts | 5 ++-- src/types/bookingStoreTypes.ts | 8 ++++++- src/types/svgType.ts | 5 ++++ 9 files changed, 67 insertions(+), 26 deletions(-) create mode 100644 src/types/svgType.ts diff --git a/public/assets/svg/location.tsx b/public/assets/svg/location.tsx index d11c83a..d873b0b 100644 --- a/public/assets/svg/location.tsx +++ b/public/assets/svg/location.tsx @@ -1,22 +1,24 @@ import React from 'react'; +import { SvgProps } from '@/types/svgType'; -const Location = ({ size = 24, color = '#9fa6b2', ...props }) => ( +const Location = ({ size = 24, color = '#9fa6b2', className }: SvgProps) => ( + /> + /> ); diff --git a/public/assets/svg/star.tsx b/public/assets/svg/star.tsx index 56656ad..16e3daa 100644 --- a/public/assets/svg/star.tsx +++ b/public/assets/svg/star.tsx @@ -1,6 +1,7 @@ import React from 'react'; +import { SvgProps } from '@/types/svgType'; -const Star = ({ size = 20, color = '#fff', ...props }) => ( +const Star = ({ size = 20, color = '#fff', ...props }:SvgProps) => ( void; children: React.ReactNode; + disabled?: boolean; } - export default function BookingButton({ onClick, children, + disabled = false, }: BookingButtonProps) { return ( diff --git a/src/components/FloatingBox/BookingInterface.tsx b/src/components/FloatingBox/BookingInterface.tsx index 4aaa495..cefba9e 100644 --- a/src/components/FloatingBox/BookingInterface.tsx +++ b/src/components/FloatingBox/BookingInterface.tsx @@ -15,7 +15,11 @@ export default function BookingInterface() { alert('예약이 완료되었습니다!'); }; const setIsOpen = useBookingStore((state) => state.setIsOpen); - const { selectedDate, selectedTime, participants } = useBookingStore(); + const { selectedDate, selectedTime, participants, selectedTimeId } = + useBookingStore(); + + const isBookable = + !!selectedDate && !!selectedTime && !!selectedTimeId && !!participants; return (
@@ -28,7 +32,9 @@ export default function BookingInterface() {
- 예약하기 + + 예약하기 +
@@ -59,7 +65,9 @@ export default function BookingInterface() { - 예약하기 + + 예약하기 +
@@ -91,7 +99,12 @@ export default function BookingInterface() { )}
-
diff --git a/src/components/FloatingBox/TimeSelector.tsx b/src/components/FloatingBox/TimeSelector.tsx index f74068a..08a815f 100644 --- a/src/components/FloatingBox/TimeSelector.tsx +++ b/src/components/FloatingBox/TimeSelector.tsx @@ -6,6 +6,7 @@ export default function TimeSelector() { const setSelectedTime = useBookingStore((state) => state.setSelectedTime); const selectedDate = useBookingStore((state) => state.selectedDate); const availableDates = useBookingStore((state) => state.availableDates); + const setSelectedTimeId = useBookingStore((state) => state.setSelectedTimeId); const selectedDateStr = selectedDate ? format(selectedDate, 'yyyy-MM-dd') @@ -26,7 +27,10 @@ export default function TimeSelector() { return (
{/* 주소 텍스트 */} -
+
- -

{address}

+ +

{address}

diff --git a/src/stores/Booking/useBookingStore.ts b/src/stores/Booking/useBookingStore.ts index 1b2a7a7..a665b56 100644 --- a/src/stores/Booking/useBookingStore.ts +++ b/src/stores/Booking/useBookingStore.ts @@ -3,12 +3,13 @@ import { BookingState } from '@/types/bookingStoreTypes'; const useBookingStore = create((set) => ({ selectedDate: new Date(), - selectedTime: '14:00-15:00', + selectedTime: null, participants: 1, isOpen: false, availableDates: [], + selectedTimeId: null, setAvailableDates: (data) => set({ availableDates: data }), - + setSelectedTimeId: (id) => set({ selectedTimeId: id }), setSelectedDate: (date) => set({ selectedDate: date }), setSelectedTime: (time) => set({ selectedTime: time }), incrementParticipants: () => diff --git a/src/types/bookingStoreTypes.ts b/src/types/bookingStoreTypes.ts index dfef85a..412f400 100644 --- a/src/types/bookingStoreTypes.ts +++ b/src/types/bookingStoreTypes.ts @@ -1,8 +1,11 @@ export interface BookingState { selectedDate: Date | null; - selectedTime: string; + selectedTime: string | null; + selectedTimeId: number | null; + participants: number; isOpen: boolean; + availableDates: { date: string; times: { id: number; startTime: string; endTime: string }[]; @@ -10,8 +13,11 @@ export interface BookingState { setSelectedDate: (date: Date | null) => void; setSelectedTime: (time: string) => void; + setSelectedTimeId: (id: number | null) => void; + incrementParticipants: () => void; decrementParticipants: () => void; + setIsOpen: (open: boolean) => void; setAvailableDates: (data: BookingState['availableDates']) => void; } diff --git a/src/types/svgType.ts b/src/types/svgType.ts new file mode 100644 index 0000000..abd5e10 --- /dev/null +++ b/src/types/svgType.ts @@ -0,0 +1,5 @@ +export interface SvgProps { + className?: string; + size?: number; + color?: string; +}