From 45095d6ba26a8d96114f98fbf616c2ebd4681d67 Mon Sep 17 00:00:00 2001 From: ihnsy Date: Wed, 29 Jan 2025 19:23:03 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EC=82=AC=EC=A7=84=20=ED=88=AC=EC=96=B4=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=82=AC=EC=A7=84=20=EB=B7=B0=EC=9E=89/?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=AA=A8=EB=8B=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EB=AF=B8=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 41 ++++---- src/components/roomdetail/Info.tsx | 2 +- src/components/roomdetail/PhotoModal.tsx | 109 ++++++++++++-------- src/components/roomdetail/ReviewContext.tsx | 88 ++++++++++++++++ src/components/roomdetail/ReviewModal.tsx | 106 +++++++++---------- src/routes/roomDetail.tsx | 28 ++--- 6 files changed, 242 insertions(+), 132 deletions(-) create mode 100644 src/components/roomdetail/ReviewContext.tsx diff --git a/src/App.tsx b/src/App.tsx index 100d945..ef96047 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import { StyledEngineProvider } from '@mui/material/styles'; import { Route, Routes } from 'react-router-dom'; import { SearchProvider } from '@/components/home/context/SearchContext'; +import { ReviewProvider } from './components/roomdetail/ReviewContext'; import { ApiTest } from '@/routes/ApiTest'; import Home from '@/routes/Home'; import Hosting from '@/routes/Hosting'; @@ -22,25 +23,27 @@ export const App = () => { // useSearch 훅을 이용해서 컴포넌트를 감싸 놨습니다. AppProvider 등의 다른 파일을 만들어서 옮겨두는 것도 가능합니다. - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } - /> - 404 Not Found} /> - + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + 404 Not Found} /> + + ); diff --git a/src/components/roomdetail/Info.tsx b/src/components/roomdetail/Info.tsx index a245084..9c0bc4f 100644 --- a/src/components/roomdetail/Info.tsx +++ b/src/components/roomdetail/Info.tsx @@ -101,7 +101,7 @@ const Info = ({ data }: InfoProps) => {
- {isluggage ? '여행 가방 보관 가능' : '여행 가방 보관 풀가'} + {isluggage ? '여행 가방 보관 가능' : '여행 가방 보관 불가'}
diff --git a/src/components/roomdetail/PhotoModal.tsx b/src/components/roomdetail/PhotoModal.tsx index bf57ad0..3939538 100644 --- a/src/components/roomdetail/PhotoModal.tsx +++ b/src/components/roomdetail/PhotoModal.tsx @@ -1,22 +1,19 @@ -import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; -import PhotoSizeSelectActualIcon from '@mui/icons-material/PhotoSizeSelectActual'; -import { useEffect } from 'react'; +import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; +import PhotoSizeSelectActualIcon from "@mui/icons-material/PhotoSizeSelectActual"; +import { useEffect } from "react"; -import { Shareheart } from '@/components/roomdetail/Shareheart'; +import { Shareheart } from "@/components/roomdetail/Shareheart"; -const PhotoModal = ({ onClose }: { onClose: () => void }) => { - const photos = [ - { title: '거실', icon: }, - { title: '침실', icon: }, - { title: '욕실', icon: }, - { title: '외부', icon: }, - { title: '추가 사진', icon: }, - ]; +type Props = { + onClose: () => void; + UrlList: string[]; +}; +const PhotoModal = ({ onClose, UrlList }: Props) => { useEffect(() => { - document.body.style.overflow = 'hidden'; // 배경 스크롤 비활성화 + document.body.style.overflow = "hidden"; // 배경 스크롤 비활성화 return () => { - document.body.style.overflow = 'auto'; // 모달 닫힐 때 스크롤 복원 + document.body.style.overflow = "auto"; // 모달 닫힐 때 스크롤 복원 }; }, []); @@ -30,14 +27,9 @@ const PhotoModal = ({ onClose }: { onClose: () => void }) => { {/* 헤더 */}
- +
-

- 사진 투어 -

+

사진 투어

@@ -45,34 +37,64 @@ const PhotoModal = ({ onClose }: { onClose: () => void }) => { {/* 사진 목록 */}
- {photos.map((photo, index) => ( -
-
- {photo.icon} + {UrlList.length > 0 ? ( + <> + {UrlList.map((url, index) => ( +
+
+ {`사진 +
+ {index} +
+ ))} + {Array.from({ length: Math.max(5 - UrlList.length, 0) }).map((_, index) => ( +
+
+ +
+ 정보없음 +
+ ))} + + ) : ( + Array.from({ length: 5 }).map((_, index) => ( +
+ + {index}
- {photo.title} -
- ))} + )) + )}
{/* 선택한 사진 보기 */}
- {photos.map((photo, index) => ( -
- - {photo.title} - -
- {photo.icon} + {UrlList.length > 0 ? ( + <> + {UrlList.map((url, index) => ( +
+ {index} + {`선택한 +
+ ))} + {Array.from({ length: Math.max(5 - UrlList.length, 0) }).map((_, index) => ( +
+ 정보없음 + +
+ ))} + + ) : ( + Array.from({ length: 5 }).map((_, index) => ( +
+ 정보없음 +
-
- ))} + )) + )}
@@ -80,3 +102,4 @@ const PhotoModal = ({ onClose }: { onClose: () => void }) => { }; export default PhotoModal; + diff --git a/src/components/roomdetail/ReviewContext.tsx b/src/components/roomdetail/ReviewContext.tsx new file mode 100644 index 0000000..755169b --- /dev/null +++ b/src/components/roomdetail/ReviewContext.tsx @@ -0,0 +1,88 @@ +import type { ReviewsResponse } from '@/types/reviewType'; +import type { roomType } from '@/types/roomType'; +import { createContext, useContext, useState } from 'react'; +import { useCallback } from 'react'; +import type { ReactNode } from 'react'; + +type ReviewContextType = { + isLoading: boolean; + error: string | null; + initReviews: (data: roomType, selectedOption: string, page: number) => Promise; + reviewData: ReviewsResponse | null; +} + +const ReviewContext = createContext(undefined); + +export function ReviewProvider({ children }: { children: ReactNode }) { + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [reviewData, setReviewData] = useState(null); + + + // 초기 리뷰 목록을 불러오는 함수 + const initReviews = useCallback(async (data: roomType, selectedOption: string, page: number) => { + try { + setIsLoading(true); + setError(null); + + const size = 4; + let sort = ''; + + if (selectedOption === '최신순') { + sort = 'createdAt,desc'; + } else if (selectedOption === '오래된순') { + sort = 'rating,asc'; + } else if (selectedOption === '높은 평점순') { + sort = 'rating,desc'; + } else if (selectedOption === '낮은 평점순') { + sort = 'rating,asc'; + } + + const url = `/api/v1/reviews/room/${data.roomId}?page=${page}&size=${size}&sort=${sort}`; + + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error('숙소 리뷰 로딩에 실패했습니다.'); + } + + const responseData = (await response.json()) as ReviewsResponse; + + setReviewData(responseData); + + console.debug(responseData, page) + } catch (err) { + const errorMessage = + err instanceof Error ? err.message : '오류가 발생했습니다.'; + setError(errorMessage); + } finally { + setIsLoading(false); + } + }, []); + + return ( + + {children} + + ); +} + +export function useReview() { + const context = useContext(ReviewContext); + if (context === undefined) { + throw new Error('useReview must be used within a ReviewProvider'); + } + return context; +} \ No newline at end of file diff --git a/src/components/roomdetail/ReviewModal.tsx b/src/components/roomdetail/ReviewModal.tsx index d24f0cc..6ccd029 100644 --- a/src/components/roomdetail/ReviewModal.tsx +++ b/src/components/roomdetail/ReviewModal.tsx @@ -1,13 +1,13 @@ import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline'; import CloseIcon from '@mui/icons-material/Close'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; -import { useCallback, useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import accuracy from '@/assets/icons/reviews/accuracy.svg'; import clean from '@/assets/icons/reviews/clean.svg'; import { CheckinIcon } from '@/components/common/constants/icons'; -import type { ReviewsResponse } from '@/types/reviewType'; import type { roomType } from '@/types/roomType'; +import { useReview } from './ReviewContext'; interface ReviewProps { data: roomType; @@ -15,65 +15,29 @@ interface ReviewProps { } const ReviewModal = ({ onClose, data }: ReviewProps) => { - const [error, setError] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [reviewData, setReviewData] = useState(null); - const [isModalOpen, setIsModalOpen] = useState(false); const [selectedOption, setSelectedOption] = useState('최신순'); + const [page, setPage] = useState(0) + const [isModalOpen, setIsModalOpen] = useState(false); const options = ['최신순', '오래된순', '높은 평점순', '낮은 평점순']; - const handleOptionClick = (option: string) => { setSelectedOption(option); // 선택한 값을 버튼에 표시 setIsModalOpen(false); // 모달 닫기 }; - const fetchReviews = useCallback(async () => { - try { - setIsLoading(true); - setError(null); - - const page = 0; - const size = 10; - let sort = ''; - - if (selectedOption === '최신순') { - sort = 'createdAt,desc'; - } else if (selectedOption === '오래된순') { - sort = 'rating,asc'; - } else if (selectedOption === '높은 평점순') { - sort = 'rating,desc'; - } else if (selectedOption === '낮은 평점순') { - sort = 'rating,asc'; - } + const { isLoading, error, initReviews, reviewData } = + useReview(); - const url = `/api/v1/reviews/room/${data.roomId}?page=${page}&size=${size}&sort=${sort}`; - - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - throw new Error('숙소 리뷰 로딩에 실패했습니다.'); - } - - const responseData = (await response.json()) as ReviewsResponse; - setReviewData(responseData); - } catch (err) { - const errorMessage = - err instanceof Error ? err.message : '오류가 발생했습니다.'; - setError(errorMessage); - } finally { - setIsLoading(false); - } - }, [data.roomId, selectedOption]); + const isInitialMount = useRef(true); useEffect(() => { - void fetchReviews(); - }, [fetchReviews]); - + if (isInitialMount.current) { + void initReviews(data, selectedOption, page); + isInitialMount.current = false; + } + }, [initReviews, data, selectedOption, page]); + if (!reviewData) { + return
리뷰 데이터를 불러오는 중...
; + } return (
{/* 배경 클릭 시 닫힘 */} @@ -160,7 +124,7 @@ const ReviewModal = ({ onClose, data }: ReviewProps) => {
후기{' '} - {reviewData?.content.length} + {reviewData?.totalElements}
@@ -212,6 +176,44 @@ const ReviewModal = ({ onClose, data }: ReviewProps) => {

{review.content}

))} + {reviewData.totalPages > 1 && ( +
+ + + {Array.from({ length: reviewData.totalPages }, (_, i) => ( + + ))} + + +
+ )}
diff --git a/src/routes/roomDetail.tsx b/src/routes/roomDetail.tsx index 910b2aa..9a9e1bb 100644 --- a/src/routes/roomDetail.tsx +++ b/src/routes/roomDetail.tsx @@ -64,11 +64,9 @@ export const Roomdetail = () => { {roomData.imageUrlList.slice(0, 5).map((url, index) => (
{ (_, index) => (
{ Array.from({ length: 5 }).map((_, index) => (
{ onClose={() => { setIsPhotoOpen(false); }} + UrlList={roomData.imageUrlList} /> )}
From 4d2e9e9ed11eb58f182c1b4a80875e32e052c587 Mon Sep 17 00:00:00 2001 From: ihnsy Date: Wed, 29 Jan 2025 20:01:54 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98/RoomGues?= =?UTF-8?q?tsModal=20maxOccupancy=20=EC=B0=B8=EC=A1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- src/components/roomdetail/PhotoModal.tsx | 94 ++++++--- src/components/roomdetail/ReviewContext.tsx | 95 +++++---- src/components/roomdetail/ReviewModal.tsx | 198 +++++++++--------- src/components/roomdetail/RoomGuestsModal.tsx | 8 +- src/routes/roomDetail.tsx | 27 ++- 6 files changed, 238 insertions(+), 186 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ef96047..94d24f3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,6 @@ import { StyledEngineProvider } from '@mui/material/styles'; import { Route, Routes } from 'react-router-dom'; import { SearchProvider } from '@/components/home/context/SearchContext'; -import { ReviewProvider } from './components/roomdetail/ReviewContext'; import { ApiTest } from '@/routes/ApiTest'; import Home from '@/routes/Home'; import Hosting from '@/routes/Hosting'; @@ -10,6 +9,7 @@ import Redirect from '@/routes/Redirect'; import CompleteProfilePage from './components/home/Topbar/Menu/CompleteProfilePage'; import RegisterPage from './components/home/Topbar/Menu/RegisterPage'; +import { ReviewProvider } from './components/roomdetail/ReviewContext'; import MyReservations from './routes/MyReservations'; import MyReviews from './routes/MyReviews'; import ProfileEdit from './routes/ProfileEdit'; diff --git a/src/components/roomdetail/PhotoModal.tsx b/src/components/roomdetail/PhotoModal.tsx index 3939538..1634b9c 100644 --- a/src/components/roomdetail/PhotoModal.tsx +++ b/src/components/roomdetail/PhotoModal.tsx @@ -1,8 +1,8 @@ -import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; -import PhotoSizeSelectActualIcon from "@mui/icons-material/PhotoSizeSelectActual"; -import { useEffect } from "react"; +import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; +import PhotoSizeSelectActualIcon from '@mui/icons-material/PhotoSizeSelectActual'; +import { useEffect } from 'react'; -import { Shareheart } from "@/components/roomdetail/Shareheart"; +import { Shareheart } from '@/components/roomdetail/Shareheart'; type Props = { onClose: () => void; @@ -11,9 +11,9 @@ type Props = { const PhotoModal = ({ onClose, UrlList }: Props) => { useEffect(() => { - document.body.style.overflow = "hidden"; // 배경 스크롤 비활성화 + document.body.style.overflow = 'hidden'; // 배경 스크롤 비활성화 return () => { - document.body.style.overflow = "auto"; // 모달 닫힐 때 스크롤 복원 + document.body.style.overflow = 'auto'; // 모달 닫힐 때 스크롤 복원 }; }, []); @@ -27,9 +27,14 @@ const PhotoModal = ({ onClose, UrlList }: Props) => { {/* 헤더 */}
- +
-

사진 투어

+

+ 사진 투어 +

@@ -40,25 +45,40 @@ const PhotoModal = ({ onClose, UrlList }: Props) => { {UrlList.length > 0 ? ( <> {UrlList.map((url, index) => ( -
+
- {`사진 + {`사진
{index}
))} - {Array.from({ length: Math.max(5 - UrlList.length, 0) }).map((_, index) => ( -
-
- + {Array.from({ length: Math.max(5 - UrlList.length, 0) }).map( + (_, index) => ( +
+
+ +
+ 정보없음
- 정보없음 -
- ))} + ), + )} ) : ( Array.from({ length: 5 }).map((_, index) => ( -
+
{index}
@@ -71,8 +91,13 @@ const PhotoModal = ({ onClose, UrlList }: Props) => { {UrlList.length > 0 ? ( <> {UrlList.map((url, index) => ( -
- {index} +
+ + {index} + { />
))} - {Array.from({ length: Math.max(5 - UrlList.length, 0) }).map((_, index) => ( -
- 정보없음 - -
- ))} + {Array.from({ length: Math.max(5 - UrlList.length, 0) }).map( + (_, index) => ( +
+ + 정보없음 + + +
+ ), + )} ) : ( Array.from({ length: 5 }).map((_, index) => ( -
- 정보없음 +
+ + 정보없음 +
)) @@ -102,4 +139,3 @@ const PhotoModal = ({ onClose, UrlList }: Props) => { }; export default PhotoModal; - diff --git a/src/components/roomdetail/ReviewContext.tsx b/src/components/roomdetail/ReviewContext.tsx index 755169b..ab110d4 100644 --- a/src/components/roomdetail/ReviewContext.tsx +++ b/src/components/roomdetail/ReviewContext.tsx @@ -1,15 +1,20 @@ -import type { ReviewsResponse } from '@/types/reviewType'; -import type { roomType } from '@/types/roomType'; +import type { ReactNode } from 'react'; import { createContext, useContext, useState } from 'react'; import { useCallback } from 'react'; -import type { ReactNode } from 'react'; + +import type { ReviewsResponse } from '@/types/reviewType'; +import type { roomType } from '@/types/roomType'; type ReviewContextType = { isLoading: boolean; error: string | null; - initReviews: (data: roomType, selectedOption: string, page: number) => Promise; + initReviews: ( + data: roomType, + selectedOption: string, + page: number, + ) => Promise; reviewData: ReviewsResponse | null; -} +}; const ReviewContext = createContext(undefined); @@ -18,52 +23,54 @@ export function ReviewProvider({ children }: { children: ReactNode }) { const [isLoading, setIsLoading] = useState(true); const [reviewData, setReviewData] = useState(null); - // 초기 리뷰 목록을 불러오는 함수 - const initReviews = useCallback(async (data: roomType, selectedOption: string, page: number) => { - try { - setIsLoading(true); - setError(null); + const initReviews = useCallback( + async (data: roomType, selectedOption: string, page: number) => { + try { + setIsLoading(true); + setError(null); - const size = 4; - let sort = ''; + const size = 4; + let sort = ''; - if (selectedOption === '최신순') { - sort = 'createdAt,desc'; - } else if (selectedOption === '오래된순') { - sort = 'rating,asc'; - } else if (selectedOption === '높은 평점순') { - sort = 'rating,desc'; - } else if (selectedOption === '낮은 평점순') { - sort = 'rating,asc'; - } + if (selectedOption === '최신순') { + sort = 'createdAt,desc'; + } else if (selectedOption === '오래된순') { + sort = 'rating,asc'; + } else if (selectedOption === '높은 평점순') { + sort = 'rating,desc'; + } else if (selectedOption === '낮은 평점순') { + sort = 'rating,asc'; + } - const url = `/api/v1/reviews/room/${data.roomId}?page=${page}&size=${size}&sort=${sort}`; + const url = `/api/v1/reviews/room/${data.roomId}?page=${page}&size=${size}&sort=${sort}`; - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); - if (!response.ok) { - throw new Error('숙소 리뷰 로딩에 실패했습니다.'); - } + if (!response.ok) { + throw new Error('숙소 리뷰 로딩에 실패했습니다.'); + } - const responseData = (await response.json()) as ReviewsResponse; + const responseData = (await response.json()) as ReviewsResponse; - setReviewData(responseData); + setReviewData(responseData); - console.debug(responseData, page) - } catch (err) { - const errorMessage = - err instanceof Error ? err.message : '오류가 발생했습니다.'; - setError(errorMessage); - } finally { - setIsLoading(false); - } - }, []); + console.debug(responseData, page); + } catch (err) { + const errorMessage = + err instanceof Error ? err.message : '오류가 발생했습니다.'; + setError(errorMessage); + } finally { + setIsLoading(false); + } + }, + [], + ); return ( {children} @@ -85,4 +92,4 @@ export function useReview() { throw new Error('useReview must be used within a ReviewProvider'); } return context; -} \ No newline at end of file +} diff --git a/src/components/roomdetail/ReviewModal.tsx b/src/components/roomdetail/ReviewModal.tsx index 6ccd029..e2e3af2 100644 --- a/src/components/roomdetail/ReviewModal.tsx +++ b/src/components/roomdetail/ReviewModal.tsx @@ -1,12 +1,13 @@ import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline'; import CloseIcon from '@mui/icons-material/Close'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import accuracy from '@/assets/icons/reviews/accuracy.svg'; import clean from '@/assets/icons/reviews/clean.svg'; import { CheckinIcon } from '@/components/common/constants/icons'; import type { roomType } from '@/types/roomType'; + import { useReview } from './ReviewContext'; interface ReviewProps { @@ -16,7 +17,7 @@ interface ReviewProps { const ReviewModal = ({ onClose, data }: ReviewProps) => { const [selectedOption, setSelectedOption] = useState('최신순'); - const [page, setPage] = useState(0) + const [page, setPage] = useState(0); const [isModalOpen, setIsModalOpen] = useState(false); const options = ['최신순', '오래된순', '높은 평점순', '낮은 평점순']; const handleOptionClick = (option: string) => { @@ -24,20 +25,12 @@ const ReviewModal = ({ onClose, data }: ReviewProps) => { setIsModalOpen(false); // 모달 닫기 }; - const { isLoading, error, initReviews, reviewData } = - useReview(); - - const isInitialMount = useRef(true); + const { isLoading, error, initReviews, reviewData } = useReview(); useEffect(() => { - if (isInitialMount.current) { - void initReviews(data, selectedOption, page); - isInitialMount.current = false; - } + void initReviews(data, selectedOption, page); }, [initReviews, data, selectedOption, page]); - if (!reviewData) { - return
리뷰 데이터를 불러오는 중...
; - } + return (
{/* 배경 클릭 시 닫힘 */} @@ -119,102 +112,109 @@ const ReviewModal = ({ onClose, data }: ReviewProps) => {
{/* 오른쪽 후기 목록 */} -
-
-
- 후기{' '} - - {reviewData?.totalElements} - - 개 -
- -
- {isModalOpen && ( -
- {options.map((option) => ( + {reviewData === null ? ( +
리뷰 데이터 없음
+ ) : ( + <> +
+
+
+ 후기{' '} + + {reviewData.totalElements} + + 개 +
- ))} -
- )} - {reviewData?.content.map((review, index) => ( -
-
-
- {review.profileImage !== '' - ? review.nickname - : review.nickname.charAt(0)} -
-
-
- {review.nickname} · {review.rating} -
-

- 숙박 일시 {review.startDate}~{review.endDate} -

-
-

{review.content}

-
- ))} - {reviewData.totalPages > 1 && ( -
- - - {Array.from({ length: reviewData.totalPages }, (_, i) => ( - + ))} +
+ )} + {reviewData.content.map((review, index) => ( +
- {i + 1} - +
+
+ {review.profileImage !== '' + ? review.nickname + : review.nickname.charAt(0)} +
+
+
+ {review.nickname} · {review.rating} +
+

+ 숙박 일시 {review.startDate}~{review.endDate} +

+
+
+

{review.content}

+
))} + {reviewData.totalPages > 1 && ( +
+ + + {Array.from({ length: reviewData.totalPages }, (_, i) => ( + + ))} - + +
+ )}
- )} -
+ + )}
{error !== null && ( diff --git a/src/components/roomdetail/RoomGuestsModal.tsx b/src/components/roomdetail/RoomGuestsModal.tsx index b29953a..68e60a9 100644 --- a/src/components/roomdetail/RoomGuestsModal.tsx +++ b/src/components/roomdetail/RoomGuestsModal.tsx @@ -46,7 +46,9 @@ const GuestsModal = ({ onClose, maxOccupancy }: GuestsModalProps) => { {guestCount} @@ -63,11 +65,11 @@ const GuestsModal = ({ onClose, maxOccupancy }: GuestsModalProps) => { > 저장하기 - {maxOccupancy < guestCount && ( + {maxOccupancy === guestCount && (
- 게스트 인원이 최대 인원을 초과했습니다 + 최대 인원은 {maxOccupancy}명입니다
)} diff --git a/src/routes/roomDetail.tsx b/src/routes/roomDetail.tsx index 9a9e1bb..9cba4d5 100644 --- a/src/routes/roomDetail.tsx +++ b/src/routes/roomDetail.tsx @@ -64,9 +64,11 @@ export const Roomdetail = () => { {roomData.imageUrlList.slice(0, 5).map((url, index) => (
{ (_, index) => (
{ Array.from({ length: 5 }).map((_, index) => (
Date: Thu, 30 Jan 2025 07:18:46 +0900 Subject: [PATCH 3/3] =?UTF-8?q?axios=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/roomdetail/Reservation.tsx | 20 ++++++-------------- src/components/roomdetail/ReviewContext.tsx | 14 +++----------- src/routes/roomDetail.tsx | 14 +++----------- 3 files changed, 12 insertions(+), 36 deletions(-) diff --git a/src/components/roomdetail/Reservation.tsx b/src/components/roomdetail/Reservation.tsx index e84eeca..be158f1 100644 --- a/src/components/roomdetail/Reservation.tsx +++ b/src/components/roomdetail/Reservation.tsx @@ -2,6 +2,7 @@ import FlagIcon from '@mui/icons-material/Flag'; import { useState } from 'react'; import clock from '@/assets/icons/reservation/clock.svg'; +import axiosInstance from '@/axiosInstance'; import BaseModal from '@/components/common/Modal/BaseModal'; import { useSearch } from '@/components/home/context/SearchContext'; import RoomGuestsModal from '@/components/roomdetail/RoomGuestsModal'; @@ -49,22 +50,13 @@ const Reservation = ({ data }: InfoProps) => { numberOfGuests: guests, }; - const response = await fetch('/api/v1/reservations', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify(sendData), - }); + const response = await axiosInstance.post( + '/api/v1/reservations', + sendData, + ); console.debug(sendData); - - if (!response.ok) { - throw new Error('숙소 예약에 실패했습니다.'); - } - const responseData = - (await response.json()) as roomReservationResponseType; + const responseData = response.data; alert( `숙소가 성공적으로 예약되었습니다! ID: ${responseData.reservationId}`, ); diff --git a/src/components/roomdetail/ReviewContext.tsx b/src/components/roomdetail/ReviewContext.tsx index ab110d4..21cf46b 100644 --- a/src/components/roomdetail/ReviewContext.tsx +++ b/src/components/roomdetail/ReviewContext.tsx @@ -1,3 +1,4 @@ +import axios from 'axios'; import type { ReactNode } from 'react'; import { createContext, useContext, useState } from 'react'; import { useCallback } from 'react'; @@ -45,18 +46,9 @@ export function ReviewProvider({ children }: { children: ReactNode }) { const url = `/api/v1/reviews/room/${data.roomId}?page=${page}&size=${size}&sort=${sort}`; - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); + const response = await axios.get(url); - if (!response.ok) { - throw new Error('숙소 리뷰 로딩에 실패했습니다.'); - } - - const responseData = (await response.json()) as ReviewsResponse; + const responseData = response.data; setReviewData(responseData); diff --git a/src/routes/roomDetail.tsx b/src/routes/roomDetail.tsx index 9cba4d5..075b8ed 100644 --- a/src/routes/roomDetail.tsx +++ b/src/routes/roomDetail.tsx @@ -1,4 +1,5 @@ import { PhotoSizeSelectActual as PhotoSizeSelectActualIcon } from '@mui/icons-material'; +import axios from 'axios'; import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; @@ -27,18 +28,9 @@ export const Roomdetail = () => { throw new Error('존재하지 않는 숙소입니다.'); } - const response = await fetch(`/api/v1/rooms/main/${id}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); + const response = await axios.get(`/api/v1/rooms/main/${id}`); - if (!response.ok) { - throw new Error('숙소 상세화면 로딩에 실패했습니다.'); - } - const responseData = (await response.json()) as roomType; - setRoomData(responseData); + setRoomData(response.data); } catch (err) { const errorMessage = err instanceof Error ? err.message : '오류가 발생했습니다.';