diff --git a/src/api/detail.ts b/src/api/detail.ts new file mode 100644 index 00000000..811f0e18 --- /dev/null +++ b/src/api/detail.ts @@ -0,0 +1,86 @@ +import {MySpaces, PlacesNearby, Reviews, ReviewsRating, Wishes, placeInfoData} from '@/types/detail'; +import axios from 'axios'; + +// --------------------------- GET --------------------------- + +export const getPlaceInfo = async (id: number, typeId: number): Promise => { + const response = await axios.get('/api/places/info', { + params: {placeId: id, placeTypeId: typeId}, + }); + + return response.data; +}; + +export const getReviewsRating = async (id: number, typeId: number, title: string): Promise => { + const response = await axios.get('/api/reviews/rating', { + params: {placeId: id, contentTypeId: typeId, placeTitle: title}, + }); + + return response.data; +}; + +export const getReviews = async (id: number, typeId: number, title: string): Promise => { + const response = await axios.get('/api/reviews', { + params: {placeId: id, contentTypeId: typeId, placeTitle: title}, + }); + + return response.data; +}; + +export const getIsWish = async (id: number, setIsWish: React.Dispatch>) => { + const response = await axios.get(`/api/wishes/${id}`, { + withCredentials: true, + }); + + setIsWish(response.data.data); + + return response.data; +}; + +export const getPlacesNearby = async ( + page: number, + size: number, + areaCode: number, + sigunguCode: number, + placeTypeId: number, + sort: string, + categoryCode: string, +): Promise => { + const response = await axios.get('/api/places/nearby', { + params: { + page: page, + size: size, + areaCode: areaCode, + sigunguCode: sigunguCode, + placeTypeId: placeTypeId, + sort: sort, + categoryCode: categoryCode, + }, + }); + + return response.data; +}; + +export const getMySpaces = async (page: number, size: number, sort: string): Promise => { + const response = await axios.get('/api/members/my-spaces/upcoming', { + params: { + page: page, + size: size, + sort: sort, + }, + }); + + return response.data; +}; + +// --------------------------- POST --------------------------- + +export const PostWishes = async ({placeId, contentTypeId}: Wishes) => { + try { + const response = await axios.post('/api/wishes', {placeId, contentTypeId}); + console.log('axios 포스트 성공', response); + return response.data; + } catch (error) { + console.error(error); + } +}; diff --git a/src/assets/ic_google.svg b/src/assets/ic_google.svg new file mode 100644 index 00000000..0c303944 --- /dev/null +++ b/src/assets/ic_google.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/chakra/chakraCustomTheme.ts b/src/chakra/chakraCustomTheme.ts index 15944b72..ef286eea 100644 --- a/src/chakra/chakraCustomTheme.ts +++ b/src/chakra/chakraCustomTheme.ts @@ -1,11 +1,12 @@ import {extendTheme} from '@chakra-ui/react'; - import {avatarTheme} from './avatarCustom'; import {buttonTheme} from './buttonCustom'; import {checkboxTheme} from './checkboxCustom'; +import {drawerTheme} from './drawerCustom'; import {modalTheme} from './modalCustom'; import {tabsTheme} from './tabsCustom'; import {tagTheme} from './tagCustom'; + export const customTheme = extendTheme({ styles: { global: { @@ -215,6 +216,7 @@ export const customTheme = extendTheme({ Modal: modalTheme, Checkbox: checkboxTheme, Button: buttonTheme, + Drawer: drawerTheme, }, fonts: { diff --git a/src/chakra/drawerCustom.ts b/src/chakra/drawerCustom.ts new file mode 100644 index 00000000..cd0b55db --- /dev/null +++ b/src/chakra/drawerCustom.ts @@ -0,0 +1,23 @@ +import {drawerAnatomy} from '@chakra-ui/anatomy'; +import {createMultiStyleConfigHelpers} from '@chakra-ui/react'; + +const {defineMultiStyleConfig} = createMultiStyleConfigHelpers(drawerAnatomy.keys); + +export const drawerTheme = defineMultiStyleConfig({ + sizes: { + mapModal: { + dialogContainer: { + width: '100%', + maxWidth: '45rem', + minWidth: '36rem', + left: '50%', + transform: 'translateX(-50%)', + + overflow: 'clip', + }, + dialog: { + width: '100%', + }, + }, + }, +}); diff --git a/src/components/Detail/BottomFixedBtn/RegistrationSlide/RegistrationSlide.tsx b/src/components/Detail/BottomFixedBtn/RegistrationSlide/RegistrationSlide.tsx index e2f08032..e77d34dc 100644 --- a/src/components/Detail/BottomFixedBtn/RegistrationSlide/RegistrationSlide.tsx +++ b/src/components/Detail/BottomFixedBtn/RegistrationSlide/RegistrationSlide.tsx @@ -1,28 +1,35 @@ -import { useDisclosure } from "@chakra-ui/react"; -import { useState } from "react"; -import { CiEdit } from "react-icons/ci"; -import { useRecoilValue } from "recoil"; +import {useDisclosure} from '@chakra-ui/react'; +import {useState} from 'react'; +import {CiEdit} from 'react-icons/ci'; +import {useRecoilValue} from 'recoil'; -import styles from "./RegistrationSlide.module.scss"; +import styles from './RegistrationSlide.module.scss'; -import CustomToast from "@/components/CustomToast/CustomToast"; +import CustomToast from '@/components/CustomToast/CustomToast'; -import CloseIcon from "@/assets/close.svg?react"; -import { isRegistrationSelectedState } from "@/recoil/detail/detail"; +import CloseIcon from '@/assets/close.svg?react'; +import {isRegistrationSelectedState} from '@/recoil/detail/detail'; -import RegistrationListItem from "./RegistrationListItem/RegistrationListItem"; -import RegistrationModal from "./RegistrationModal/RegistrationModal"; -import RegistrationTripSpace from "./RegistrationTripSpace/RegistrationTripSpace"; +import RegistrationListItem from './RegistrationListItem/RegistrationListItem'; +import RegistrationModal from './RegistrationModal/RegistrationModal'; +import RegistrationTripSpace from './RegistrationTripSpace/RegistrationTripSpace'; -import { RegistrationSlideProps } from "@/types/detail"; +import {RegistrationSlideProps} from '@/types/detail'; +import {useGetSpacesCity} from '@/hooks/Detail/useSpaces'; -function RegistrationSlide({ slideOnClose }: RegistrationSlideProps) { +function RegistrationSlide({slideOnClose}: RegistrationSlideProps) { const isValuedArray = useRecoilValue(isRegistrationSelectedState); - const [TripSelected, setTripSelected] = useState(""); - const { isOpen, onOpen, onClose } = useDisclosure(); + const [TripSelected, setTripSelected] = useState(''); + const {isOpen, onOpen, onClose} = useDisclosure(); const toast = CustomToast(); - // useEffect fetchData + const { + data: { + data: {spaces: spaces}, + }, + } = useGetSpacesCity(0, 15, ''); + + console.log(spaces); const exArray = []; @@ -32,10 +39,10 @@ function RegistrationSlide({ slideOnClose }: RegistrationSlideProps) {
{TripSelected ? ( @@ -45,12 +52,12 @@ function RegistrationSlide({ slideOnClose }: RegistrationSlideProps) { console.log(1); }} > - + 새 투표 생성 ) : ( )} @@ -65,29 +72,16 @@ function RegistrationSlide({ slideOnClose }: RegistrationSlideProps) { 후보로 등록할 투표 제목을 선택해주세요
- + {/* 초과 글씨 ... 처리 해야함*/} - - + +
) : (
-

- 생성된 투표가 없습니다. -

-

- 투표 생성을 눌러 새로운 투표를 만들어주세요! -

+

생성된 투표가 없습니다.

+

투표 생성을 눌러 새로운 투표를 만들어주세요!

) ) : ( @@ -99,14 +93,14 @@ function RegistrationSlide({ slideOnClose }: RegistrationSlideProps) { className={styles.container__bottomFixedBtn} style={ isValuedArray.length > 0 - ? { backgroundColor: "#2388FF", color: "#FFFFFF" } - : { backgroundColor: "#E3E5E5", color: "#979C9E" } + ? {backgroundColor: '#2388FF', color: '#FFFFFF'} + : {backgroundColor: '#E3E5E5', color: '#979C9E'} } onClick={() => { if (isValuedArray.length > 0) { - toast("이 장소가 후보로 등록되었습니다."); + toast('이 장소가 후보로 등록되었습니다.'); slideOnClose(); - document.body.style.removeProperty("overflow"); + document.body.style.removeProperty('overflow'); } }} > diff --git a/src/components/Detail/Contents/Contents.tsx b/src/components/Detail/Contents/Contents.tsx index 86e37b26..087ae9d9 100644 --- a/src/components/Detail/Contents/Contents.tsx +++ b/src/components/Detail/Contents/Contents.tsx @@ -1,69 +1,82 @@ -import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; -import { useEffect } from "react"; -import { useRecoilState, useSetRecoilState } from "recoil"; +import {Tab, TabList, TabPanel, TabPanels, Tabs} from '@chakra-ui/react'; +import {useEffect} from 'react'; +import {useRecoilState, useSetRecoilState} from 'recoil'; -import styles from "./Contents.module.scss"; +import styles from './Contents.module.scss'; -import { TabIndexState, TabYPosition } from "@/recoil/detail/detail"; +import {TabIndexState, TabYPosition} from '@/recoil/detail/detail'; -import Information from "./Information/Information"; -import Reviews from "./Reviews/Reviews"; +import Information from './Information/Information'; +import Reviews from './Reviews/Reviews'; +import {placeInfoDataPlace} from '@/types/detail'; +import {useGetReviews} from '@/hooks/Detail/useReviews'; +import {useParams} from 'react-router-dom'; -import { ContentsProps } from "@/types/detail"; +interface ContentsProps { + data: placeInfoDataPlace; + onOpen: () => void; + reviewsRating: { + rating: number; + userRatingCount: number; + }; +} -function Contents({ onOpen }: ContentsProps) { +function Contents({data, onOpen, reviewsRating}: ContentsProps) { const [tabIndex, setTabIndex] = useRecoilState(TabIndexState); const setTabPosition = useSetRecoilState(TabYPosition); + const {id: params} = useParams(); + const handleTabsChange = (index: number) => { setTabIndex(index); }; + const { + data: { + data: {reviews: reviews}, + }, + } = useGetReviews(Number(params?.split(' ')[0]), Number(params?.split(' ')[1]), data.title); + console.log(reviews, '!!'); + useEffect(() => { - const tabRef = document.getElementById("tab"); + const tabRef = document.getElementById('tab'); if (tabRef) { - setTabPosition(tabRef.getBoundingClientRect().top + window.scrollY - 32); + setTabPosition(tabRef.getBoundingClientRect().top + window.scrollY - 56); } }); return ( - + 상품정보 리뷰 - - + + - - + + diff --git a/src/components/Detail/Contents/Information/BasicInformation/BasicInformation.tsx b/src/components/Detail/Contents/Information/BasicInformation/BasicInformation.tsx index e20fe598..d72cc2dd 100644 --- a/src/components/Detail/Contents/Information/BasicInformation/BasicInformation.tsx +++ b/src/components/Detail/Contents/Information/BasicInformation/BasicInformation.tsx @@ -1,35 +1,65 @@ -import { AiOutlineLink } from "react-icons/ai"; -import { FaRegClock } from "react-icons/fa"; -import { IoMdCall } from "react-icons/io"; -import { MdPlace } from "react-icons/md"; +import {AiOutlineLink} from 'react-icons/ai'; +import {FaRegClock} from 'react-icons/fa'; +import {IoMdCall} from 'react-icons/io'; +import {MdPlace} from 'react-icons/md'; -import styles from "./BasicInformation.module.scss"; +import styles from './BasicInformation.module.scss'; -import MapInDetail from "./MapInDetail/MapInDetail"; +import MapInDetail from './MapInDetail/MapInDetail'; -function BasicInformation() { +interface BasicInformationProps { + location: { + address: string; + addressDetail: string; + phone: string; + areaCode: number; + sigunguCode: number; + zipCode: number; + latitude: number; + longitude: number; + }; + openTime: string; + title: string; + thumbnail: string; + contentTypeId: number; +} + +function BasicInformation({location, openTime, title, thumbnail, contentTypeId}: BasicInformationProps) { return (
기본정보
- +
- - 경기도 양평군 양평읍 백안리 9 -
-
- - 031-771-1234 -
- -
- {/* 시계 아이콘 다름 */} - - 매일 09:00~18:00 + + {location.address}
+ {location.phone && ( +
+ + {location.phone} +
+ )} + {false && ( + + )} + {openTime && ( +
+ {/* 시계 아이콘 다름 */} + + {openTime} +
+ )}
); diff --git a/src/components/Detail/Contents/Information/BasicInformation/MapInDetail/MapInDetail.tsx b/src/components/Detail/Contents/Information/BasicInformation/MapInDetail/MapInDetail.tsx index b6fcfb1a..2dd25b34 100644 --- a/src/components/Detail/Contents/Information/BasicInformation/MapInDetail/MapInDetail.tsx +++ b/src/components/Detail/Contents/Information/BasicInformation/MapInDetail/MapInDetail.tsx @@ -1,40 +1,39 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { useDisclosure } from "@chakra-ui/react"; -import { useEffect, useState } from "react"; -import { CustomOverlayMap, Map } from "react-kakao-maps-sdk"; +import {useDisclosure} from '@chakra-ui/react'; +import {CustomOverlayMap, Map} from 'react-kakao-maps-sdk'; -import styles from "./MapInDetail.module.scss"; +import styles from './MapInDetail.module.scss'; -import BigHomeMarker from "@/assets/homeIcons/map/house_big.svg?react"; +import BigHomeMarker from '@/assets/homeIcons/map/house_big.svg?react'; -import MapModal from "../MapModal/MapModal"; +import MapModal from '../MapModal/MapModal'; -interface Coordinate { +interface MapInDetailProps { lat: number; lng: number; + title: string; + thumbnail: string; + contentTypeId: number; + areaCode: number; } // 장소 정보에 따라 마커 다르게 표시 -function MapInDetail() { - const [coordinate, setCoordinate] = useState({ - lat: 33.5563, - lng: 126.79581, - }); - const { isOpen, onOpen, onClose } = useDisclosure(); +function MapInDetail({lat, lng, title, thumbnail, contentTypeId, areaCode}: MapInDetailProps) { + const {isOpen, onOpen, onClose} = useDisclosure(); - useEffect(() => { - const geocoder = new kakao.maps.services.Geocoder(); + // useEffect(() => { + // const geocoder = new kakao.maps.services.Geocoder(); - const callback = (result: any, status: any) => { - if (status === kakao.maps.services.Status.OK) { - const newSearch = result[0]; - setCoordinate({ lat: newSearch.y, lng: newSearch.x }); - } - }; + // const callback = (result: any, status: any) => { + // if (status === kakao.maps.services.Status.OK) { + // const newSearch = result[0]; + // setCoordinate({ lat: newSearch.y, lng: newSearch.x }); + // } + // }; - geocoder.addressSearch("경기도 양평군 양평읍 백안리 9", callback); - }, []); + // geocoder.addressSearch("경기도 양평군 양평읍 백안리 9", callback); + // }, []); const handleMapDoubleClick = (event: any) => { event.preventDefault(); @@ -43,7 +42,7 @@ function MapInDetail() { return ( <> - + ); diff --git a/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.module.scss b/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.module.scss index 22c73749..3813f7a3 100644 --- a/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.module.scss +++ b/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.module.scss @@ -1,4 +1,4 @@ -@use "@/sass" as *; +@use '@/sass' as *; .header { position: fixed; @@ -34,28 +34,23 @@ z-index: 10; - .flexBox { - width: 100%; - display: flex; - } - &__card { width: 100%; height: 12rem; background-color: $neutral0; border-radius: 16px; + padding: 12px; display: flex; align-items: center; gap: 12px; - max-width: 41rem; - position: relative; img { width: 9.6rem; height: 9.6rem; + border-radius: 8px; } &__textWrapper { diff --git a/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.tsx b/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.tsx index 43a55c1a..c1092eea 100644 --- a/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.tsx +++ b/src/components/Detail/Contents/Information/BasicInformation/MapModal/MapModal.tsx @@ -1,63 +1,62 @@ -import { - Drawer, - DrawerBody, - DrawerContent, - DrawerFooter, - DrawerHeader, -} from "@chakra-ui/react"; -import { AiOutlineLeft } from "react-icons/ai"; -import { FaRegHeart } from "react-icons/fa"; -import { CustomOverlayMap, Map } from "react-kakao-maps-sdk"; +import {Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader} from '@chakra-ui/react'; +import {AiOutlineLeft} from 'react-icons/ai'; +import {FaRegHeart} from 'react-icons/fa'; +import {CustomOverlayMap, Map} from 'react-kakao-maps-sdk'; -import styles from "./MapModal.module.scss"; +import styles from './MapModal.module.scss'; -import BigHomeMarker from "@/assets/homeIcons/map/house_big.svg?react"; +import BigHomeMarker from '@/assets/homeIcons/map/house_big.svg?react'; interface MapModalProps { isOpen: boolean; onClose: () => void; lat: number; lng: number; - name: string; + title: string; + thumbnail: string; + contentTypeId: number; + areaCode: number; } -function MapModal({ isOpen, onClose, lat, lng, name }: MapModalProps) { +function MapModal({isOpen, onClose, lat, lng, title, thumbnail, contentTypeId, areaCode}: MapModalProps) { return ( - + - - {name} + + {title} - - - + + + -
-
- -
-

{name}

-

- 숙소 서울 -

-
- {" "} +
+ +
+

{title}

+

+ {contentTypeId} {areaCode} +

+
diff --git a/src/components/Detail/Contents/Information/Information.tsx b/src/components/Detail/Contents/Information/Information.tsx index 230c7343..fd7a6b60 100644 --- a/src/components/Detail/Contents/Information/Information.tsx +++ b/src/components/Detail/Contents/Information/Information.tsx @@ -1,15 +1,38 @@ -import BasicInformation from "./BasicInformation/BasicInformation"; -import Others from "./Others/Others"; -import ShortReviews from "./ShortReveiws/ShortReviews"; +import {placeInfoDataPlace} from '@/types/detail'; +import BasicInformation from './BasicInformation/BasicInformation'; +import Others from './Others/Others'; +import ShortReviews from './ShortReveiws/ShortReviews'; -import { ContentsInformationProps } from "@/types/detail"; +export interface InformationProps { + data: placeInfoDataPlace; + onOpen: () => void; + reviewsRating: { + rating: number; + userRatingCount: number; + }; + reviews: { + content: string; + images: string[]; + isGoogle: boolean; + nickname: string; + profileImage: string; + rating: number; + visitedAt: string; + }[]; +} -function Information({ onOpen }: ContentsInformationProps) { +function Information({data, onOpen, reviewsRating, reviews}: InformationProps) { return (
- - - + + +
); } diff --git a/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.module.scss b/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.module.scss index 7dd44631..7a940fcd 100644 --- a/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.module.scss +++ b/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.module.scss @@ -1,4 +1,4 @@ -@use "@/sass" as *; +@use '@/sass' as *; .container { display: flex; @@ -13,6 +13,12 @@ width: 14.4rem; height: 14.4rem; border-radius: 1.6rem; + + img { + width: 100%; + height: 100%; + border-radius: 1.6rem; + } } &__contents { diff --git a/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.tsx b/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.tsx index f6d22cac..f08d9743 100644 --- a/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.tsx +++ b/src/components/Detail/Contents/Information/Others/OtherCard/OtherCard.tsx @@ -1,26 +1,25 @@ -import { GoStarFill } from "react-icons/go"; +import {GoStarFill} from 'react-icons/go'; -import styles from "./OtherCard.module.scss"; +import styles from './OtherCard.module.scss'; -import { OtherCardPropsType } from "@/types/detail"; +import {OtherCardPropsType} from '@/types/detail'; +import {Link} from 'react-router-dom'; -function OtherCard({ image, name, location, point }: OtherCardPropsType) { +function OtherCard({image, name, category, point, id, contentTypeId}: OtherCardPropsType) { return ( -
+
- # + #

{name}

-

{location}

+

{category}

- - {point} - + {point}
-
+ ); } diff --git a/src/components/Detail/Contents/Information/Others/Others.tsx b/src/components/Detail/Contents/Information/Others/Others.tsx index a43e3872..8446b39b 100644 --- a/src/components/Detail/Contents/Information/Others/Others.tsx +++ b/src/components/Detail/Contents/Information/Others/Others.tsx @@ -1,59 +1,38 @@ -import { useState } from "react"; +import {useState} from 'react'; -import styles from "./Others.module.scss"; +import styles from './Others.module.scss'; -import useComponentSize from "@/hooks/useComponetSize"; +import useComponentSize from '@/hooks/useComponetSize'; -import SlideButton from "@/components/SlideButton/SlideButton"; +import SlideButton from '@/components/SlideButton/SlideButton'; -import OtherCard from "./OtherCard/OtherCard"; +import OtherCard from './OtherCard/OtherCard'; +import {useGetPlacesNearby} from '@/hooks/Detail/usePlaces'; +import {placeInfoDataPlace} from '@/types/detail'; -function Others() { +interface OthersProps { + data: placeInfoDataPlace; +} + +function Others({data}: OthersProps) { const [slideLocation, setSlideLocation] = useState(0); const [componentRef, size] = useComponentSize(); - const othersData = [ - { - image: - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - name: "호텔 loft", - location: "제주", - point: "5.0", - count: 803, - }, - { - image: - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - name: "호텔 loft", - location: "제주", - point: "5.0", - count: 803, - }, - { - image: - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - name: "호텔 loft", - location: "제주", - point: "5.0", - count: 803, + const { + data: { + data: {places: othersData}, }, - { - image: - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - name: "호텔 loft", - location: "제주", - point: "5.0", - count: 803, - }, - { - image: - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - name: "호텔 loft", - location: "제주", - point: "5.0", - count: 803, - }, - ]; + } = useGetPlacesNearby( + 0, + 5, + data.location.areaCode, + data.location.sigunguCode, + data.contentTypeId, + '', + data.category, + ); + + console.log(othersData, '23'); return (
@@ -81,16 +60,19 @@ function Others() { className={styles.container__slideContainer__slide} ref={componentRef} style={{ - overflow: size.width < 410 ? "scroll" : "visible", - left: slideLocation + "px", + overflow: size.width < 410 ? 'scroll' : 'visible', + left: slideLocation + 'px', }} > {othersData.map((data) => ( ))}
diff --git a/src/components/Detail/Contents/Information/ShortReveiws/ShortReviews.tsx b/src/components/Detail/Contents/Information/ShortReveiws/ShortReviews.tsx index 11bc91d2..5675fbf0 100644 --- a/src/components/Detail/Contents/Information/ShortReveiws/ShortReviews.tsx +++ b/src/components/Detail/Contents/Information/ShortReveiws/ShortReviews.tsx @@ -1,21 +1,17 @@ -import { CiEdit } from "react-icons/ci"; -import { GoStarFill } from "react-icons/go"; -import { useRecoilValue, useSetRecoilState } from "recoil"; +import {CiEdit} from 'react-icons/ci'; +import {GoStarFill} from 'react-icons/go'; +import {useRecoilValue, useSetRecoilState} from 'recoil'; -import styles from "./ShortReviews.module.scss"; +import styles from './ShortReviews.module.scss'; -import Review from "@/components/Detail/Contents/Review/Review"; +import Review from '@/components/Detail/Contents/Review/Review'; -import { - IsLoginState, - TabIndexState, - TabYPosition, -} from "@/recoil/detail/detail"; -import { isModalOpenState, modalContentState } from "@/recoil/vote/alertModal"; +import {IsLoginState, TabIndexState, TabYPosition} from '@/recoil/detail/detail'; +import {isModalOpenState, modalContentState} from '@/recoil/vote/alertModal'; -import { ContentsShortReviewsProps } from "@/types/detail"; +import {ContentsShortReviewsProps} from '@/types/detail'; -function ShortReviews({ onOpen }: ContentsShortReviewsProps) { +function ShortReviews({onOpen, reviewsRating, reviews}: ContentsShortReviewsProps) { const setIsModalOpen = useSetRecoilState(isModalOpenState); const setModalContent = useSetRecoilState(modalContentState); const isLogin = useRecoilValue(IsLoginState); @@ -23,47 +19,18 @@ function ShortReviews({ onOpen }: ContentsShortReviewsProps) { const tabPosition = useRecoilValue(TabYPosition); const notLoginContent = { - title: "로그인이 필요한 기능입니다.", - subText: "로그인하고 모든 서비스를 이용해 보세요! ", - cancelText: "닫기", - actionButton: "로그인하기", + title: '로그인이 필요한 기능입니다.', + subText: '로그인하고 모든 서비스를 이용해 보세요! ', + cancelText: '닫기', + actionButton: '로그인하기', isSmallSize: true, }; const showNotLoginModal = () => { setIsModalOpen(true); - setModalContent({ ...notLoginContent }); + setModalContent({...notLoginContent}); }; - const reviewData = [ - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요", - images: [ - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - ], - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요 아주 좋아요", - }, - ]; - return (
@@ -78,23 +45,23 @@ function ShortReviews({ onOpen }: ContentsShortReviewsProps) { } }} > - + 리뷰쓰기
- 5.0 - - (13,052) - + {reviewsRating.rating} + {`(${reviewsRating.userRatingCount})`}
- {reviewData.map((data) => ( + {reviews.slice(0, 2).map((data, i) => ( -
- +
+ #
{name} - {isGoogle && 구글} + {isGoogle && }
- - - {point} - - - {visitedAt} - + + {rating} + {visitedAt}
{content}
{images && } diff --git a/src/components/Detail/Contents/Reviews/Reviews.tsx b/src/components/Detail/Contents/Reviews/Reviews.tsx index c6af09e3..b4c53e15 100644 --- a/src/components/Detail/Contents/Reviews/Reviews.tsx +++ b/src/components/Detail/Contents/Reviews/Reviews.tsx @@ -1,140 +1,34 @@ -import { CiEdit } from "react-icons/ci"; -import { GoStarFill } from "react-icons/go"; -import { useRecoilValue, useSetRecoilState } from "recoil"; +import {CiEdit} from 'react-icons/ci'; +import {GoStarFill} from 'react-icons/go'; +import {useRecoilValue, useSetRecoilState} from 'recoil'; -import styles from "./Reviews.module.scss"; +import styles from './Reviews.module.scss'; -import Review from "@/components/Detail/Contents/Review/Review"; +import Review from '@/components/Detail/Contents/Review/Review'; -import { IsLoginState } from "@/recoil/detail/detail"; -import { isModalOpenState, modalContentState } from "@/recoil/vote/alertModal"; +import {IsLoginState} from '@/recoil/detail/detail'; +import {isModalOpenState, modalContentState} from '@/recoil/vote/alertModal'; -import { ContentsReviewsProps } from "@/types/detail"; +import {ContentsReviewsProps} from '@/types/detail'; // 무한 스크롤 구현 필요 -function Reviews({ onOpen }: ContentsReviewsProps) { +function Reviews({onOpen, reviewsRating, reviews}: ContentsReviewsProps) { const setIsModalOpen = useSetRecoilState(isModalOpenState); const setModalContent = useSetRecoilState(modalContentState); const isLogin = useRecoilValue(IsLoginState); const notLoginContent = { - title: "로그인이 필요한 기능입니다.", - subText: "로그인하고 모든 서비스를 이용해 보세요! ", - cancelText: "닫기", - actionButton: "로그인하기", + title: '로그인이 필요한 기능입니다.', + subText: '로그인하고 모든 서비스를 이용해 보세요! ', + cancelText: '닫기', + actionButton: '로그인하기', isSmallSize: true, }; const showNotLoginModal = () => { setIsModalOpen(true); - setModalContent({ ...notLoginContent }); + setModalContent({...notLoginContent}); }; - const reviewData = [ - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요", - images: [ - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - ], - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요 아주 좋아요", - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요", - images: [ - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - ], - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요 아주 좋아요", - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요", - images: [ - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - ], - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요 아주 좋아요", - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요", - images: [ - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - "https://m.eejmall.com/web/product/big/201708/211_shop1_627935.jpg", - "https://img-cf.kurly.com/shop/data/goodsview/20210218/gv30000159355_1.jpg", - ], - }, - { - name: "강자밭", - isGoogle: false, - point: "5.0", - visitedAt: "2024년 1월 방문", - content: - "아주 좋아요. 자주 다니고 있어요. 친구들이랑 저녁에 운동하기 좋아요 다음에 또 가고 싶네요 아주 좋아요", - }, - ]; - return (
@@ -149,23 +43,23 @@ function Reviews({ onOpen }: ContentsReviewsProps) { } }} > - + 리뷰쓰기
- 5.0 - - (13,052) - + {reviewsRating.rating} + {`(${reviewsRating.userRatingCount})`}
- {reviewData.map((data) => ( + {reviews.map((data, i) => ( (0); - const { isOpen, onOpen, onClose } = useDisclosure(); + const {isOpen, onOpen, onClose} = useDisclosure(); return ( <> { @@ -33,11 +33,8 @@ function ImageSwiper() { }} > {images.map((data) => ( - - # + + # ))} diff --git a/src/components/Detail/Main/Main.module.scss b/src/components/Detail/Main/Main.module.scss index 10702a24..614c866d 100644 --- a/src/components/Detail/Main/Main.module.scss +++ b/src/components/Detail/Main/Main.module.scss @@ -1,3 +1,3 @@ .container { - padding-top: 32px; + padding-top: 56px; } diff --git a/src/components/Detail/Main/Main.tsx b/src/components/Detail/Main/Main.tsx index 550d3ab9..120f742b 100644 --- a/src/components/Detail/Main/Main.tsx +++ b/src/components/Detail/Main/Main.tsx @@ -1,13 +1,30 @@ -import styles from "./Main.module.scss"; +import styles from './Main.module.scss'; -import ImageSwiper from "./ImageSwiper/Swiper"; -import Title from "./Title/Title"; +import ImageSwiper from './ImageSwiper/Swiper'; +import Title from './Title/Title'; -function Main() { +interface MainProps { + id: number; + contentTypeId: number; + images: string[]; + title: string; + category: string; + rating: number; + reviewsCount: number; +} + +function Main({id, contentTypeId, images, title, category, rating, reviewsCount}: MainProps) { return (
- - + <ImageSwiper images={images} /> + <Title + id={id} + contentTypeId={contentTypeId} + title={title} + category={category} + rating={rating} + reviewsCount={reviewsCount} + /> </div> ); } diff --git a/src/components/Detail/Main/Title/Title.tsx b/src/components/Detail/Main/Title/Title.tsx index b72aa497..ab791230 100644 --- a/src/components/Detail/Main/Title/Title.tsx +++ b/src/components/Detail/Main/Title/Title.tsx @@ -1,44 +1,58 @@ -import { FaRegHeart } from "react-icons/fa"; -import { FaHeart } from "react-icons/fa"; -import { GoStarFill } from "react-icons/go"; -import { IoShareSocialOutline } from "react-icons/io5"; -import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; +import {FaRegHeart} from 'react-icons/fa'; +import {FaHeart} from 'react-icons/fa'; +import {GoStarFill} from 'react-icons/go'; +import {IoShareSocialOutline} from 'react-icons/io5'; +import {useRecoilState, useRecoilValue, useSetRecoilState} from 'recoil'; -import styles from "./Title.module.scss"; +import styles from './Title.module.scss'; -import CustomToast from "@/components/CustomToast/CustomToast"; +import CustomToast from '@/components/CustomToast/CustomToast'; -import { IsHeartValued, IsLoginState } from "@/recoil/detail/detail"; -import { isModalOpenState, modalContentState } from "@/recoil/vote/alertModal"; +import {IsHeartValued, IsLoginState} from '@/recoil/detail/detail'; +import {isModalOpenState, modalContentState} from '@/recoil/vote/alertModal'; +import {useGetIsWish, usePostWishes} from '@/hooks/Detail/useWish'; -function Title() { - const [isHeart, setIsHeart] = useRecoilState(IsHeartValued); +interface TitleProps { + id: number; + contentTypeId: number; + title: string; + category: string; + rating: number; + reviewsCount: number; +} + +function Title({id, contentTypeId, title, category, rating, reviewsCount}: TitleProps) { + const [isWish, setIsWish] = useRecoilState(IsHeartValued); const setIsModalOpen = useSetRecoilState(isModalOpenState); const setModalContent = useSetRecoilState(modalContentState); + useGetIsWish(id, setIsWish); + const postWishes = usePostWishes(); + const isLogin = useRecoilValue(IsLoginState); const notLoginContent = { - title: "로그인이 필요한 기능입니다.", - subText: "로그인하고 모든 서비스를 이용해 보세요! ", - cancelText: "닫기", - actionButton: "로그인하기", + title: '로그인이 필요한 기능입니다.', + subText: '로그인하고 모든 서비스를 이용해 보세요! ', + cancelText: '닫기', + actionButton: '로그인하기', isSmallSize: true, }; const showNotLoginModal = () => { setIsModalOpen(true); - setModalContent({ ...notLoginContent }); + setModalContent({...notLoginContent}); }; const showToast = CustomToast(); const handleHeartClick = () => { if (isLogin) { - if (!isHeart) { - showToast("찜 목록에 저장되었습니다."); + if (!isWish) { + showToast('찜 목록에 저장되었습니다.'); + postWishes.mutate({placeId: id, contentTypeId: contentTypeId}); } - setIsHeart(!isHeart); + setIsWish(!isWish); } else { showNotLoginModal(); } @@ -46,36 +60,25 @@ function Title() { return ( <div className={styles.container}> - <h2 className={styles.container__header}>롯데시티 호텔</h2> - <p className={styles.container__category}>숙소</p> + <h2 className={styles.container__header}>{title}</h2> + <p className={styles.container__category}>{category}</p> <div className={styles.container__alignCenter}> <GoStarFill className={styles.container__alignCenter__star} /> - <span className={styles.container__alignCenter__point}>5.0</span> - <span className={styles.container__alignCenter__reviewsCount}> - (13,052) - </span> + <span className={styles.container__alignCenter__point}>{rating}</span> + <span className={styles.container__alignCenter__reviewsCount}>{`(${reviewsCount})`}</span> </div> <div className={styles.container__positionAbsoluteIcons}> - {isHeart ? ( - <FaHeart - fontSize="2.4rem" - cursor="pointer" - color="#E23774" - onClick={handleHeartClick} - /> + {isWish ? ( + <FaHeart fontSize='2.4rem' cursor='pointer' color='#E23774' onClick={handleHeartClick} /> ) : ( - <FaRegHeart - fontSize="2.4rem" - cursor="pointer" - onClick={handleHeartClick} - /> + <FaRegHeart fontSize='2.4rem' cursor='pointer' onClick={handleHeartClick} /> )} <IoShareSocialOutline - fontSize="2.4rem" - cursor="pointer" + fontSize='2.4rem' + cursor='pointer' onClick={() => { - showToast("링크가 복사되었습니다."); + showToast('링크가 복사되었습니다.'); }} /> </div> diff --git a/src/components/Detail/Navigation/Navigation.module.scss b/src/components/Detail/Navigation/Navigation.module.scss index 873bbee5..54afd8c0 100644 --- a/src/components/Detail/Navigation/Navigation.module.scss +++ b/src/components/Detail/Navigation/Navigation.module.scss @@ -10,9 +10,8 @@ width: 100%; max-width: 45rem; - height: 3.2rem; - padding: 16px; + padding: 16px 20px; &__pointer { cursor: pointer; } diff --git a/src/hooks/Detail/usePlaces.ts b/src/hooks/Detail/usePlaces.ts new file mode 100644 index 00000000..acba1c68 --- /dev/null +++ b/src/hooks/Detail/usePlaces.ts @@ -0,0 +1,25 @@ +import {getPlacesNearby} from '@/api/detail'; +import {useSuspenseQuery} from '@tanstack/react-query'; +import {getPlaceInfo} from '@/api/detail'; + +export const useGetPlaceInfo = (id: number, typeId: number) => { + return useSuspenseQuery({ + queryKey: ['place', id, typeId], + queryFn: () => getPlaceInfo(id, typeId), + }); +}; + +export const useGetPlacesNearby = ( + page: number, + size: number, + areaCode: number, + sigunguCode: number, + placeTypeId: number, + sort: string, + categoryCode: string, +) => { + return useSuspenseQuery({ + queryKey: ['placesNearby', page, size, areaCode, sigunguCode, placeTypeId, sort, categoryCode], + queryFn: () => getPlacesNearby(page, size, areaCode, sigunguCode, placeTypeId, sort, categoryCode), + }); +}; diff --git a/src/hooks/Detail/useReviews.ts b/src/hooks/Detail/useReviews.ts new file mode 100644 index 00000000..b8f77feb --- /dev/null +++ b/src/hooks/Detail/useReviews.ts @@ -0,0 +1,16 @@ +import {getReviews, getReviewsRating} from '@/api/detail'; +import {useSuspenseQuery} from '@tanstack/react-query'; + +export const useGetReviewsRating = (id: number, typeId: number, title: string) => { + return useSuspenseQuery({ + queryKey: ['reviewsRating', id, typeId, title], + queryFn: () => getReviewsRating(id, typeId, title), + }); +}; + +export const useGetReviews = (id: number, typeId: number, title: string) => { + return useSuspenseQuery({ + queryKey: ['reviews', id, typeId, title], + queryFn: () => getReviews(id, typeId, title), + }); +}; diff --git a/src/hooks/Detail/useSpaces.ts b/src/hooks/Detail/useSpaces.ts new file mode 100644 index 00000000..115a63a1 --- /dev/null +++ b/src/hooks/Detail/useSpaces.ts @@ -0,0 +1,9 @@ +import {getMySpaces} from '@/api/detail'; +import {useSuspenseQuery} from '@tanstack/react-query'; + +export const useGetSpacesCity = (page: number, size: number, sort: string) => { + return useSuspenseQuery({ + queryKey: ['spacesCity', page, size, sort], + queryFn: () => getMySpaces(page, size, sort), + }); +}; diff --git a/src/hooks/Detail/useWish.ts b/src/hooks/Detail/useWish.ts new file mode 100644 index 00000000..f0854f56 --- /dev/null +++ b/src/hooks/Detail/useWish.ts @@ -0,0 +1,14 @@ +import {PostWishes, getIsWish} from '@/api/detail'; +import {useSuspenseQuery} from '@tanstack/react-query'; +import {useCustomMutation} from '../Votes/vote'; + +export const useGetIsWish = (id: number, setIsWish: React.Dispatch<React.SetStateAction<boolean>>) => { + return useSuspenseQuery({ + queryKey: ['isWish', id], + queryFn: () => getIsWish(id, setIsWish), + }); +}; + +export const usePostWishes = () => { + return useCustomMutation(PostWishes, ['isWish']); +}; diff --git a/src/hooks/Places/usePlaces.ts b/src/hooks/Places/usePlaces.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/pages/Detail/Detail.tsx b/src/pages/Detail/Detail.tsx index a877af7f..e111e55e 100644 --- a/src/pages/Detail/Detail.tsx +++ b/src/pages/Detail/Detail.tsx @@ -1,33 +1,52 @@ -import { useDisclosure } from "@chakra-ui/react"; -import { ReactNode, useState } from "react"; -import { useRecoilValue } from "recoil"; +import {useDisclosure} from '@chakra-ui/react'; +import {ReactNode, useState} from 'react'; +import {useRecoilValue} from 'recoil'; -import styles from "./Detail.module.scss"; +import styles from './Detail.module.scss'; -import AlertModal from "@/components/AlertModal/AlertModal"; -import BottomFixedBtn from "@/components/Detail/BottomFixedBtn/BottomFixedBtn"; -import RegistrationSlide from "@/components/Detail/BottomFixedBtn/RegistrationSlide/RegistrationSlide"; -import BottomSlideDetail from "@/components/Detail/BottomSlideDetail/BottomSlideDetail"; -import Contents from "@/components/Detail/Contents/Contents"; -import ReviewBottomSlide from "@/components/Detail/Contents/ReviewBottomSlide/ReviewBottomSlide"; -import Main from "@/components/Detail/Main/Main"; -import MeatballBottomSlide from "@/components/Detail/Navigation/MeatballBottomSlide/MeatballBottomSlide"; -import Navigation from "@/components/Detail/Navigation/Navigation"; +import AlertModal from '@/components/AlertModal/AlertModal'; +import BottomFixedBtn from '@/components/Detail/BottomFixedBtn/BottomFixedBtn'; +import RegistrationSlide from '@/components/Detail/BottomFixedBtn/RegistrationSlide/RegistrationSlide'; +import BottomSlideDetail from '@/components/Detail/BottomSlideDetail/BottomSlideDetail'; +import Contents from '@/components/Detail/Contents/Contents'; +import ReviewBottomSlide from '@/components/Detail/Contents/ReviewBottomSlide/ReviewBottomSlide'; +import Main from '@/components/Detail/Main/Main'; +import MeatballBottomSlide from '@/components/Detail/Navigation/MeatballBottomSlide/MeatballBottomSlide'; +import Navigation from '@/components/Detail/Navigation/Navigation'; -import { modalContentState } from "@/recoil/vote/alertModal"; +import {modalContentState} from '@/recoil/vote/alertModal'; + +import {useParams} from 'react-router-dom'; +import {useGetReviewsRating} from '@/hooks/Detail/useReviews'; +import {useGetPlaceInfo} from '@/hooks/Detail/usePlaces'; function Detail() { - const { isOpen, onOpen, onClose } = useDisclosure(); - const [bottomSlideContent, setBottomSlideContent] = - useState<ReactNode | null>(null); + const {isOpen, onOpen, onClose} = useDisclosure(); + const [bottomSlideContent, setBottomSlideContent] = useState<ReactNode | null>(null); const modalContent = useRecoilValue(modalContentState); const [isReviewModal, setIsReviewModal] = useState<boolean>(false); + const {id: params} = useParams(); + + const { + data: { + data: {place: placeInfo}, + }, + } = useGetPlaceInfo(Number(params?.split(' ')[0]), Number(params?.split(' ')[1])); + + const { + data: {data: reviewsRating}, + } = useGetReviewsRating(Number(params?.split(' ')[0]), Number(params?.split(' ')[1]), placeInfo.title); + + console.log(reviewsRating); + + console.log(placeInfo); + const onBottomSlideOpen = (content: ReactNode, isReview: boolean) => { setIsReviewModal(isReview); setBottomSlideContent(content); onOpen(); - document.body.style.overflow = "hidden"; + document.body.style.overflow = 'hidden'; }; const handleSlideOnClose = () => { @@ -40,30 +59,27 @@ function Detail() { <Navigation onOpen={() => onBottomSlideOpen( - <MeatballBottomSlide - onBottomSlideOpen={onBottomSlideOpen} - onClose={handleSlideOnClose} - />, + <MeatballBottomSlide onBottomSlideOpen={onBottomSlideOpen} onClose={handleSlideOnClose} />, false, ) } /> - <Main /> + <Main + id={placeInfo.id} + contentTypeId={placeInfo.contentTypeId} + images={placeInfo.gallery} + title={placeInfo.title} + category={placeInfo.category} + rating={reviewsRating.rating} + reviewsCount={reviewsRating.userRatingCount} + /> <Contents - onOpen={() => - onBottomSlideOpen( - <ReviewBottomSlide slideOnClose={handleSlideOnClose} />, - true, - ) - } + data={placeInfo} + reviewsRating={reviewsRating} + onOpen={() => onBottomSlideOpen(<ReviewBottomSlide slideOnClose={handleSlideOnClose} />, true)} /> <BottomFixedBtn - onOpen={() => - onBottomSlideOpen( - <RegistrationSlide slideOnClose={handleSlideOnClose} />, - false, - ) - } + onOpen={() => onBottomSlideOpen(<RegistrationSlide slideOnClose={handleSlideOnClose} />, false)} /> <BottomSlideDetail isOpen={isOpen} diff --git a/src/recoil/detail/detail.ts b/src/recoil/detail/detail.ts index 2447bbed..24de54cf 100644 --- a/src/recoil/detail/detail.ts +++ b/src/recoil/detail/detail.ts @@ -1,36 +1,36 @@ -import { atom } from "recoil"; +import {atom} from 'recoil'; export const isRegistrationSelectedState = atom<string[]>({ - key: "isRegistrationSelectedState", + key: 'isRegistrationSelectedState', default: [], }); export const DatePickerState = atom<Date>({ - key: "DatePickerState", + key: 'DatePickerState', default: new Date(), }); export const DatePickerIsValued = atom<boolean>({ - key: "DatePickerIsValued", + key: 'DatePickerIsValued', default: false, }); export const IsHeartValued = atom<boolean>({ - key: "IsHeartValued", + key: 'IsHeartValued', default: false, }); export const IsLoginState = atom<boolean>({ - key: "IsLoginState", + key: 'IsLoginState', default: true, }); export const TabIndexState = atom<number>({ - key: "TabIndexState", + key: 'TabIndexState', default: 0, }); export const TabYPosition = atom<number>({ - key: "TabYPosition", + key: 'TabYPosition', default: 0, }); diff --git a/src/types/detail.ts b/src/types/detail.ts index 40034893..a9c81e91 100644 --- a/src/types/detail.ts +++ b/src/types/detail.ts @@ -1,19 +1,22 @@ -import { ReactNode } from "react"; +import {ReactNode} from 'react'; export interface OtherCardPropsType { image: string; name: string; - location: string; - point: string; + category: string; + point: number; + id: number; + contentTypeId: number; } export interface ReviewPropsTypes { name: string; isGoogle: boolean; - point: string; + rating: number; visitedAt: string; content: string; images: string[] | undefined; + profileImage: string; } export interface SwiperButtonPropsType { @@ -39,20 +42,39 @@ export interface NavigationMeatballProps { // Main // Contents -export interface ContentsProps { - onOpen: () => void; -} - -export interface ContentsInformationProps { - onOpen: () => void; -} export interface ContentsShortReviewsProps { onOpen: () => void; + reviewsRating: { + rating: number; + userRatingCount: number; + }; + reviews: { + content: string; + images: string[]; + isGoogle: boolean; + nickname: string; + profileImage: string; + rating: number; + visitedAt: string; + }[]; } export interface ContentsReviewsProps { onOpen: () => void; + reviewsRating: { + rating: number; + userRatingCount: number; + }; + reviews: { + content: string; + images: string[]; + isGoogle: boolean; + nickname: string; + profileImage: string; + rating: number; + visitedAt: string; + }[]; } export interface ReviewBottomSlideProps { @@ -100,3 +122,113 @@ export interface BottomSlideDetailProps { isReviewModal: boolean; setBottomSlideContent: React.Dispatch<React.SetStateAction<ReactNode | null>>; } + +// ---------- api ----------- + +// Places/info +export interface placeInfoData { + status: number; + message: string; + data: { + place: placeInfoDataPlace; + }; +} + +export interface placeInfoDataPlace { + id: number; + contentTypeId: number; + title: string; + location: { + address: string; + addressDetail: string; + phone: string; + areaCode: number; + sigunguCode: number; + zipCode: number; + latitude: number; + longitude: number; + }; + category: string; + thumbnail: string; + originalImage: string; + gallery: string[]; + createdTime: string; + modifiedTime: string; + openTime: string; +} + +//reveiws/rating + +export interface ReviewsRating { + status: number; + message: string; + data: { + rating: number; + userRatingCount: number; + }; +} + +//reviews +export interface Reviews { + status: number; + message: string; + data: { + reviews: { + content: string; + images: string[]; + isGoogle: boolean; + nickname: string; + profileImage: string; + rating: number; + visitedAt: string; + }[]; + }; +} + +// wishes/{placeId} + +export interface PlacesNearby { + status: number; + message: string; + data: { + places: { + id: number; + contentTypeId: number; + title: string; + thumbnail: string; + areaCode: string; + sigunguCode: number; + category: string; + rating: number; + }[]; + }; +} + +// spaces/city + +export interface MySpaces { + status: number; + message: string; + data: { + spaces: { + id: number; + title: string; + startDate: string; + endDate: string; + dueDate: number; + }[]; + pageNumber: number; + pageSize: number; + totalPages: number; + totalResult: number; + first: boolean; + last: boolean; + }; +} + +// wishes + +export interface Wishes { + placeId: number; + contentTypeId: number; +} diff --git a/tsconfig.json b/tsconfig.json index 6608f77b..83fe1aba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,5 +35,5 @@ } }, "include": ["src", "svg.d.ts"], - "references": [{ "path": "./tsconfig.node.json" }] + "references": [{"path": "./tsconfig.node.json"}] }