From 9ea95eef2ca47b6c1db823b7f14e79fef254100e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=B4=EC=8A=B9=EC=A4=80?= Date: Tue, 16 Jan 2024 19:37:32 +0900 Subject: [PATCH 01/13] =?UTF-8?q?Fix:=20tailwind=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EC=84=A4=EC=A0=95,=20=EC=97=AC=ED=96=89?= =?UTF-8?q?=EC=B7=A8=ED=96=A5=20=EB=84=88=EB=B9=84=20=EC=9E=AC=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Trip/TripPreference.tsx | 2 +- src/components/common/nav/InputComment.tsx | 6 +++--- tailwind.config.js | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Trip/TripPreference.tsx b/src/components/Trip/TripPreference.tsx index 0a59f8c7..b1e26c6a 100644 --- a/src/components/Trip/TripPreference.tsx +++ b/src/components/Trip/TripPreference.tsx @@ -130,7 +130,7 @@ const TripPreference: React.FC = () => { } return ( -
+
n명 참여
diff --git a/src/components/common/nav/InputComment.tsx b/src/components/common/nav/InputComment.tsx index b186b9d4..3e3bb64d 100644 --- a/src/components/common/nav/InputComment.tsx +++ b/src/components/common/nav/InputComment.tsx @@ -106,8 +106,8 @@ export const InputComment: React.FC = () => { }; return ( -
-
+
+
= () => { onKeyPress={handleKeyPress} />
-
+
- + 23.12.23 ~ 23.12.25
- 23.12.23 ~ 23.12.25 -
+ + {modalChildren === 'TripSurveyMember' && } + + ); }; diff --git a/src/components/Trip/TripParticipant.tsx b/src/components/Trip/TripParticipant.tsx new file mode 100644 index 00000000..fd70334c --- /dev/null +++ b/src/components/Trip/TripParticipant.tsx @@ -0,0 +1,56 @@ +import { getTripsSurveyMembers } from '@api/trips'; +import { useQuery } from '@tanstack/react-query'; +import { tripIdState } from '@recoil/socket'; +import { useRecoilValue } from 'recoil'; +import { ReactComponent as NullUser } from '@assets/images/NullUser.svg'; + +interface ParticipantStatusProps { + status: string; +} + +const ParticipantList: React.FC<{ infos: any[] }> = ({ infos }) => ( +
+ {infos.map((info: any) => ( +
+ {info.thumbnail && info.thumbnail !== 'http://asiduheimage.jpg' ? ( + 유저 프로필 + ) : ( + + )} +
{info.nickname}
+
+ ))} +
+); + +export const ParticipantStatus: React.FC = ({ + status, +}) => { + const tripId = Number(useRecoilValue(tripIdState)); + + const { data: tripsSurveyMembers } = useQuery({ + queryKey: ['tripsSurveyMembers', tripId], + queryFn: () => getTripsSurveyMembers(tripId), + }); + + const participants = + status === '참여' + ? tripsSurveyMembers?.data?.data?.tripSurveySetMemberInfos + : tripsSurveyMembers?.data?.data?.nonTripSurveySetMemberInfos; + + return ( +
+
+ {participants && + `${participants.length}명 ${status === '참여' ? '참여' : '미참여'}`} +
+ {participants && } +
+ ); +}; diff --git a/src/components/Trip/TripPreference.tsx b/src/components/Trip/TripPreference.tsx index b1e26c6a..86bada0d 100644 --- a/src/components/Trip/TripPreference.tsx +++ b/src/components/Trip/TripPreference.tsx @@ -8,6 +8,8 @@ import { calculatePercentage, calculatePercentageRemain, } from '@utils/calculatePercentage'; +import { useSetRecoilState } from 'recoil'; +import { modalChildrenState, isModalOpenState } from '@recoil/modal'; interface RatioBarParams { value: number; @@ -94,6 +96,8 @@ const TripPreference: React.FC = () => { const [C, setC] = useState<[number, number]>([0, 0]); const [D, setD] = useState<[number, number]>([0, 0]); const [E, setE] = useState<[number, number]>([0, 0]); + const setModalChildren = useSetRecoilState(modalChildrenState); + const setIsModalOpen = useSetRecoilState(isModalOpenState); const { data: tripPreference, isLoading } = useQuery({ queryKey: ['tripPreference', tripId], @@ -129,10 +133,17 @@ const TripPreference: React.FC = () => { return
Loading...
; } + const handleButtonClick = () => { + setModalChildren('TripSurveyMember'); + setIsModalOpen(true); + }; + return (
-
+
n명 참여
diff --git a/src/components/common/modal/Modal.tsx b/src/components/common/modal/Modal.tsx index 34060d49..c8ec7ed8 100644 --- a/src/components/common/modal/Modal.tsx +++ b/src/components/common/modal/Modal.tsx @@ -69,5 +69,25 @@ export const getModalStyles = (modalChildren: string) => { zIndex: 1, // 이거 해줘야 kakao-map도 dimmed됨 }, }; + } else if (modalChildren === 'TripSurveyMember') { + return { + content: { + top: 'auto', + left: '50%', + right: 'auto', + bottom: '0', + marginRight: '-50%', + transform: 'translate(-50%, 0)', + maxWidth: '412px', + width: '100%', + height: '280px', + borderTopLeftRadius: '2rem', + borderTopRightRadius: '2rem', + }, + overlay: { + backgroundColor: 'rgba(0, 0, 0, 0.25)', + zIndex: 1, // 이거 해줘야 kakao-map도 dimmed됨 + }, + }; } }; diff --git a/src/components/common/modal/children/TripSurveyMember.tsx b/src/components/common/modal/children/TripSurveyMember.tsx new file mode 100644 index 00000000..ee6551c6 --- /dev/null +++ b/src/components/common/modal/children/TripSurveyMember.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { ParticipantStatus } from '@components/Trip/TripParticipant'; +import Tab from '@components/common/tab/Tab'; + +const TripSurveyMember: React.FC = () => { + return ( +
+ , + , + ]} + /> +
+ ); +}; + +export default TripSurveyMember; diff --git a/src/recoil/socket.ts b/src/recoil/socket.ts index 89cd5b90..d722c733 100644 --- a/src/recoil/socket.ts +++ b/src/recoil/socket.ts @@ -7,7 +7,7 @@ export const tripIdState = atom({ export const visitDateState = atom<{ visitDate: string } | null>({ key: 'visitDateState', - default: { visitDate: '2024-01-05' }, + default: { visitDate: '2024-01-03' }, }); export const memberIdState = atom<{ memberId: number } | null>({ From 327a8e85cc173e1aae90333136154000ba6687e5 Mon Sep 17 00:00:00 2001 From: joanShim Date: Wed, 17 Jan 2024 23:15:47 +0900 Subject: [PATCH 03/13] =?UTF-8?q?Feat:=20=EC=9A=B0=EB=A6=AC=EC=9D=98=20?= =?UTF-8?q?=EA=B4=80=EC=8B=AC=EB=AA=A9=EB=A1=9D=20=EC=B6=94=EA=B0=80=20=20?= =?UTF-8?q?1=EC=B0=A8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=8E=98=EC=B9=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/button/ListSelectBtn.tsx | 57 +++++++++++ src/pages/plan/OurLikedList.tsx | 97 +++++++++++++++++++ src/pages/plan/addPlace/MyLiked.tsx | 80 +++++++++++++++ src/pages/plan/addPlace/MyLikedList.tsx | 62 ++++++++++++ src/pages/plan/addPlace/MyLikedListItem.tsx | 57 +++++++++++ src/pages/plan/addPlace/PlanAddPlace.page.tsx | 32 ++++++ .../plan/addPlace/ResultCategoryPlan.tsx | 45 +++++++++ src/pages/plan/addPlace/ResultItem.tsx | 33 +++++++ src/pages/plan/addPlace/SearchResult.tsx | 94 ++++++++++++++++++ src/pages/plan/planPlaceTrip.page.tsx | 5 - src/pages/trip/AddOurList.tsx | 32 ++++++ src/recoil/listItem.ts | 6 ++ src/router/socketRouter.tsx | 6 +- 13 files changed, 599 insertions(+), 7 deletions(-) create mode 100644 src/components/common/button/ListSelectBtn.tsx create mode 100644 src/pages/plan/OurLikedList.tsx create mode 100644 src/pages/plan/addPlace/MyLiked.tsx create mode 100644 src/pages/plan/addPlace/MyLikedList.tsx create mode 100644 src/pages/plan/addPlace/MyLikedListItem.tsx create mode 100644 src/pages/plan/addPlace/PlanAddPlace.page.tsx create mode 100644 src/pages/plan/addPlace/ResultCategoryPlan.tsx create mode 100644 src/pages/plan/addPlace/ResultItem.tsx create mode 100644 src/pages/plan/addPlace/SearchResult.tsx delete mode 100644 src/pages/plan/planPlaceTrip.page.tsx create mode 100644 src/pages/trip/AddOurList.tsx create mode 100644 src/recoil/listItem.ts diff --git a/src/components/common/button/ListSelectBtn.tsx b/src/components/common/button/ListSelectBtn.tsx new file mode 100644 index 00000000..4328e6d2 --- /dev/null +++ b/src/components/common/button/ListSelectBtn.tsx @@ -0,0 +1,57 @@ +import { useState, ReactNode } from 'react'; +import { CheckIcon } from '../icons/Icons'; + +interface ListSelectBtnProps { + children: ReactNode; + onClick?: () => void; +} + +export const ListSelectBtn = ({ children, onClick }: ListSelectBtnProps) => { + const [isActive, setIsActive] = useState(false); + + const handleClick = () => { + setIsActive(!isActive); + if (onClick) { + onClick(); + } + }; + + return ( + + ); +}; + +interface ListCheckBtnProps { + onClick?: () => void; +} + +export const ListCheckBtn = ({ onClick }: ListCheckBtnProps) => { + const [isActive, setIsActive] = useState(false); + + const handleClick = () => { + setIsActive(!isActive); + if (onClick) { + onClick(); + } + }; + + return ( +
+ +
+ ); +}; diff --git a/src/pages/plan/OurLikedList.tsx b/src/pages/plan/OurLikedList.tsx new file mode 100644 index 00000000..86fc475f --- /dev/null +++ b/src/pages/plan/OurLikedList.tsx @@ -0,0 +1,97 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; +import ToursCategoryItem from '@components/Tours/ToursCategoryItem'; +import { useEffect, useState } from 'react'; +import { Spinner } from '@components/common/spinner/Spinner'; +import { getMemberTours } from '@api/member'; +import WishList from '@components/Wish/WishList'; + +export const OurLikedList = () => { + const categories = ['전체', '숙소', '식당', '관광지']; + + const [selectedContentTypeId, setSelectedContentTypeId] = useState< + null | number + >(null); + + const [selectedCategory, setSelectedCategory] = useState('전체'); + useEffect(() => { + console.log(selectedCategory); + }, [selectedCategory]); + + const handleSelectCategory = (category: string) => { + setSelectedCategory(category); + }; + + // useEffect(() => { + // console.log('searchWord: ' + searchWord); + // }, [searchWord]); + // console.log(); + + const { fetchNextPage, hasNextPage, data, isLoading, isError } = + useInfiniteQuery({ + queryKey: ['wishList'], + queryFn: ({ pageParam = 0 }) => getMemberTours(pageParam, 10), + initialPageParam: 0, + getNextPageParam: (lastPage) => { + if ( + lastPage && + lastPage.data && + lastPage.data && + lastPage.data.pageable + ) { + const currentPage = lastPage.data.pageable.pageNumber; + const totalPages = lastPage.data.totalPages; + + if (currentPage < totalPages - 1) { + return currentPage + 1; + } + } + return undefined; + }, + }); + + const handleCategoryClick = (contentTypeId: number | null) => { + setSelectedContentTypeId(contentTypeId); + }; + + if (isLoading) { + return ; + } + if (isError) { + console.log('error fetching search result '); + } + + // console.log(data?.pages[0].data.content); + const searchResults = data?.pages.flatMap((page) => page.data.content) || []; + console.log('searchResults', searchResults); + const noResults = searchResults && searchResults.length === 0; + + return ( + <> +
우리의 관심 목록
+
+ {categories.map((category) => ( + + ))} +
+ {noResults ? ( +
+ 나의 관심목록이 없습니다. +
+ ) : ( +
+ // + )} + + ); +}; diff --git a/src/pages/plan/addPlace/MyLiked.tsx b/src/pages/plan/addPlace/MyLiked.tsx new file mode 100644 index 00000000..ea917e60 --- /dev/null +++ b/src/pages/plan/addPlace/MyLiked.tsx @@ -0,0 +1,80 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; +import { useState } from 'react'; +import { Spinner } from '@components/common/spinner/Spinner'; +import { getMemberTours } from '@api/member'; +import { MyLikedList } from './MyLikedList'; +import { ButtonPrimary } from '@components/common/button/Button'; +import WishCategory from '@components/Wish/WishCategory'; + +export const MyLiked = () => { + const [selectedContentTypeId, setSelectedContentTypeId] = useState< + null | number + >(null); + + // const [selectedCategory, setSelectedCategory] = useState('전체'); + // useEffect(() => { + // console.log(selectedCategory); + // }, [selectedCategory]); + + const handleCategoryClick = (contentTypeId: number | null) => { + setSelectedContentTypeId(contentTypeId); + }; + + const { fetchNextPage, hasNextPage, data, isLoading, isError } = + useInfiniteQuery({ + queryKey: ['wishList'], + queryFn: ({ pageParam = 0 }) => getMemberTours(pageParam, 10), + initialPageParam: 0, + getNextPageParam: (lastPage) => { + if ( + lastPage && + lastPage.data && + lastPage.data && + lastPage.data.pageable + ) { + const currentPage = lastPage.data.pageable.pageNumber; + const totalPages = lastPage.data.totalPages; + + if (currentPage < totalPages - 1) { + return currentPage + 1; + } + } + return undefined; + }, + }); + + if (isLoading) { + return ; + } + if (isError) { + console.log('error fetching search result '); + } + + // console.log(data?.pages[0].data.content); + const searchResults = data?.pages.flatMap((page) => page.data.content) || []; + console.log('searchResults', searchResults); + const noResults = searchResults && searchResults.length === 0; + + return ( + <> +
나의 관심 목록
+ + {noResults ? ( +
+ 나의 관심목록이 없습니다. +
+ ) : ( + + )} +
+ 추가하기 +
+ + ); +}; diff --git a/src/pages/plan/addPlace/MyLikedList.tsx b/src/pages/plan/addPlace/MyLikedList.tsx new file mode 100644 index 00000000..295328c6 --- /dev/null +++ b/src/pages/plan/addPlace/MyLikedList.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import InfiniteScroll from 'react-infinite-scroller'; +import { v4 as uuidv4 } from 'uuid'; +import { MyLikedListItem } from './MyLikedListItem'; +import { TourType } from '@/@types/tours.types'; +import { Spinner } from '@components/common/spinner/Spinner'; + +interface WishListProps { + toursData: { pages: Array<{ data: { content: TourType[] } }> }; + fetchNextPage: () => void; + hasNextPage: boolean; + isLoading: boolean; + selectedContentTypeId: number | null; +} + +export const MyLikedList: React.FC = ({ + toursData, + fetchNextPage, + hasNextPage, + isLoading, + selectedContentTypeId, +}) => { + if (!toursData || toursData.pages.length === 0) { + return
데이터를 불러오는 중 오류가 발생했습니다.
; + } + + const filteredData = + selectedContentTypeId !== null + ? toursData.pages.map((group) => ({ + data: { + content: group.data.content.filter( + (item) => item.contentTypeId === selectedContentTypeId, + ), + }, + })) + : toursData.pages; + + return ( +
+ fetchNextPage()} + hasMore={hasNextPage} + loader={ +
+ +
+ }> +
+ {!isLoading && + filteredData.map((group) => ( + + {group?.data.content.map((wishList: TourType) => ( + + ))} + + ))} +
+
+
+ ); +}; diff --git a/src/pages/plan/addPlace/MyLikedListItem.tsx b/src/pages/plan/addPlace/MyLikedListItem.tsx new file mode 100644 index 00000000..c1cdcfa7 --- /dev/null +++ b/src/pages/plan/addPlace/MyLikedListItem.tsx @@ -0,0 +1,57 @@ +import { TourType } from '@/@types/tours.types'; +import { ListCheckBtn } from '@components/common/button/ListSelectBtn'; +import { StarIcon } from '@components/common/icons/Icons'; +import { selectedItemsState } from '@recoil/listItem'; +import { useRecoilState } from 'recoil'; + +interface WishItemProps { + wishList: TourType; +} + +export const MyLikedListItem: React.FC = ({ wishList }) => { + const { + id, + title, + ratingAverage, + reviewCount, + smallThumbnailUrl, + tourAddress, + } = wishList; + + const [selectedItems, setSelectedItems] = useRecoilState(selectedItemsState); + + const handleSelect = () => { + if (selectedItems.includes(id)) { + setSelectedItems(selectedItems.filter((item) => item !== id)); + } else { + setSelectedItems([...selectedItems, id]); + } + }; + + return ( +
+
+ {title} +
+
+
{title}
+
+
+ +
+ + {ratingAverage}({reviewCount}) + + + {tourAddress} + +
+
+ +
+ ); +}; diff --git a/src/pages/plan/addPlace/PlanAddPlace.page.tsx b/src/pages/plan/addPlace/PlanAddPlace.page.tsx new file mode 100644 index 00000000..98002a06 --- /dev/null +++ b/src/pages/plan/addPlace/PlanAddPlace.page.tsx @@ -0,0 +1,32 @@ +import SearchInput from '@components/search/SearchInput'; +import { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { SearchResultForPlan } from './SearchResult'; +import { OurLikedList } from '../OurLikedList'; + +export const PlanAddPlace = () => { + const location = useLocation(); + + const queryParams = new URLSearchParams(location.search); + const searchWordFromQuery = queryParams.get('searchWord'); + + const [searchWord, setSearchWord] = useState(''); + + useEffect(() => { + if (searchWordFromQuery) { + setSearchWord(searchWordFromQuery); + } else { + setSearchWord(''); + } + }, [location, searchWordFromQuery]); + return ( + <> + + {searchWord ? ( + + ) : ( + + )} + + ); +}; diff --git a/src/pages/plan/addPlace/ResultCategoryPlan.tsx b/src/pages/plan/addPlace/ResultCategoryPlan.tsx new file mode 100644 index 00000000..294c29bd --- /dev/null +++ b/src/pages/plan/addPlace/ResultCategoryPlan.tsx @@ -0,0 +1,45 @@ +import { ButtonPrimary, ButtonWhite } from '@components/common/button/Button'; +import { TourType } from '@/@types/tours.types'; +import { InfiniteQueryObserverResult } from '@tanstack/react-query'; +import { ResultItemPlan } from './ResultItem'; + +interface ResultCategoryProps { + data: TourType[]; + category: string; + fetchNextPage: (() => Promise>) | null; + hasNextPage: boolean; + isFetchingNextPage: boolean; +} + +export const ResultCategoryPlan = ({ + data, + fetchNextPage, + hasNextPage, + isFetchingNextPage, +}: ResultCategoryProps) => { + // console.log('hasNextPage', hasNextPage); + return ( + <> +

ResultCategoryPlan

+ {data.map((item) => ( + + ))} + {hasNextPage && !isFetchingNextPage ? ( + fetchNextPage && fetchNextPage()}> + 더보기 + + ) : isFetchingNextPage ? ( + + Loading... + + ) : ( +
+ )} +
+ 추가하기 +
+ + ); +}; diff --git a/src/pages/plan/addPlace/ResultItem.tsx b/src/pages/plan/addPlace/ResultItem.tsx new file mode 100644 index 00000000..cb2849aa --- /dev/null +++ b/src/pages/plan/addPlace/ResultItem.tsx @@ -0,0 +1,33 @@ +import { TourType } from '@/@types/tours.types'; +import { ListSelectBtn } from '@components/common/button/ListSelectBtn'; +import { StarIcon } from '@components/common/icons/Icons'; + +export const ResultItemPlan = ({ result }: { result: TourType }) => { + console.log(result); + return ( +
+
+ {result.title} +
+
+
{result.title}
+
+
+ +
+ + {result.ratingAverage}({result.reviewCount}) + + + {result.tourAddress} + +
+
+ 선택 +
+ ); +}; diff --git a/src/pages/plan/addPlace/SearchResult.tsx b/src/pages/plan/addPlace/SearchResult.tsx new file mode 100644 index 00000000..485f37e8 --- /dev/null +++ b/src/pages/plan/addPlace/SearchResult.tsx @@ -0,0 +1,94 @@ +import { getToursSearch } from '@api/tours'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import ToursCategoryItem from '@components/Tours/ToursCategoryItem'; +import { useEffect, useState } from 'react'; +import { Spinner } from '@components/common/spinner/Spinner'; +import { ResultCategoryPlan } from './ResultCategoryPlan'; + +interface SearchResultProps { + searchWord: string; +} + +export const SearchResultForPlan = ({ searchWord }: SearchResultProps) => { + const categories = ['전체', '숙소', '식당', '관광지']; + const [selectedCategory, setSelectedCategory] = useState('전체'); + useEffect(() => { + console.log(selectedCategory); + }, [selectedCategory]); + + const handleSelectCategory = (category: string) => { + setSelectedCategory(category); + }; + + useEffect(() => { + console.log('searchWord: ' + searchWord); + }, [searchWord]); + console.log(); + + const { + data, + fetchNextPage, + hasNextPage, + isLoading, + isError, + isFetchingNextPage, + } = useInfiniteQuery({ + queryKey: ['searchResults', searchWord, selectedCategory], + queryFn: ({ pageParam = 0 }) => + getToursSearch({ + region: '', + searchWord: searchWord, + category: selectedCategory !== '전체' ? selectedCategory : undefined, + page: pageParam, + size: 20, + }), + initialPageParam: 0, + getNextPageParam: (lastPage, allPages) => { + if (!lastPage.data.data.last) { + return allPages.length; + } + return undefined; + }, + enabled: !!searchWord, + retry: 2, + }); + + if (isLoading) { + return ; + } + if (isError) { + console.log('error fetching search result '); + } + + const searchResults = + data?.pages.flatMap((page) => page.data.data.content) || []; + console.log('searchResults', searchResults); + const noResults = searchResults && searchResults.length === 0; + + return ( + <> +
+ {categories.map((category) => ( + + ))} +
+ + {noResults ? ( +
검색결과가 없습니다.
+ ) : ( + + )} + + ); +}; diff --git a/src/pages/plan/planPlaceTrip.page.tsx b/src/pages/plan/planPlaceTrip.page.tsx deleted file mode 100644 index 4ff978c4..00000000 --- a/src/pages/plan/planPlaceTrip.page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const PlanPlaceTrip = () => { - return
여행계획 - 장소 추가 페이지
; -}; - -export default PlanPlaceTrip; diff --git a/src/pages/trip/AddOurList.tsx b/src/pages/trip/AddOurList.tsx new file mode 100644 index 00000000..80bce838 --- /dev/null +++ b/src/pages/trip/AddOurList.tsx @@ -0,0 +1,32 @@ +import SearchInput from '@components/search/SearchInput'; +import { MyLiked } from '@pages/plan/addPlace/MyLiked'; +import { SearchResultForPlan } from '@pages/plan/addPlace/SearchResult'; +import { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; + +export const AddOurList = () => { + const location = useLocation(); + + const queryParams = new URLSearchParams(location.search); + const searchWordFromQuery = queryParams.get('searchWord'); + + const [searchWord, setSearchWord] = useState(''); + + useEffect(() => { + if (searchWordFromQuery) { + setSearchWord(searchWordFromQuery); + } else { + setSearchWord(''); + } + }, [location, searchWordFromQuery]); + return ( +
+ + {searchWord ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/src/recoil/listItem.ts b/src/recoil/listItem.ts new file mode 100644 index 00000000..72eb7456 --- /dev/null +++ b/src/recoil/listItem.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const selectedItemsState = atom({ + key: 'selectedItemsState', + default: [], +}); diff --git a/src/router/socketRouter.tsx b/src/router/socketRouter.tsx index 0d3b67d9..80faf2db 100644 --- a/src/router/socketRouter.tsx +++ b/src/router/socketRouter.tsx @@ -1,12 +1,13 @@ import { Route, Routes } from 'react-router-dom'; import { useSocket, socketContext } from '@hooks/useSocket'; import PlanTrip from '@pages/plan/planTrip.page'; -import PlanPlaceTrip from '@pages/plan/planPlaceTrip.page'; +import { PlanAddPlace } from '@pages/plan/addPlace/PlanAddPlace.page'; import PlanPlaceSearch from '@pages/plan/planPlaceSearch.page'; import Trip from '@pages/trip/trip.page'; import MainLayout from './routerLayout'; import { useRecoilValue } from 'recoil'; import { tripIdState, visitDateState } from '@recoil/socket'; +import { AddOurList } from '@pages/trip/AddOurList'; const SocketRoutes = () => { const tripId = useRecoilValue(tripIdState); @@ -20,7 +21,7 @@ const SocketRoutes = () => { } /> - } /> + } /> } /> @@ -32,6 +33,7 @@ const SocketRouter = () => { }> } /> + } /> } /> From 87c5f3d948c3bb00d163289028a50c31a8b25465 Mon Sep 17 00:00:00 2001 From: joanShim Date: Thu, 18 Jan 2024 01:03:32 +0900 Subject: [PATCH 04/13] =?UTF-8?q?Feat:=20=EC=9A=B0=EB=A6=AC=EC=9D=98=20?= =?UTF-8?q?=EA=B4=80=EC=8B=AC=EB=AA=A9=EB=A1=9D=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/trips.ts | 8 ++- src/pages/plan/OurLikedList.tsx | 57 ++++++++++--------- src/pages/plan/addPlace/AddtoListBtn.tsx | 36 ++++++++++++ src/pages/plan/addPlace/MyLiked.tsx | 11 +--- .../plan/addPlace/ResultCategoryPlan.tsx | 8 ++- src/pages/plan/addPlace/ResultItem.tsx | 14 ++++- 6 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 src/pages/plan/addPlace/AddtoListBtn.tsx diff --git a/src/api/trips.ts b/src/api/trips.ts index 3441cb6b..84d5ed51 100644 --- a/src/api/trips.ts +++ b/src/api/trips.ts @@ -47,7 +47,13 @@ export const getTripsLike = async ( // 우리의 관심 목록 등록 export const postTripsLike = async (tripId: number, tourItemIds: number[]) => { - const res = await client.post(`trips/${tripId}/tripLikedTours`, tourItemIds); + const requestBody = { + tourItemIds: tourItemIds, + }; + const res = await authClient.post( + `trips/${tripId}/tripLikedTours`, + requestBody, + ); return res; }; diff --git a/src/pages/plan/OurLikedList.tsx b/src/pages/plan/OurLikedList.tsx index 86fc475f..bb192296 100644 --- a/src/pages/plan/OurLikedList.tsx +++ b/src/pages/plan/OurLikedList.tsx @@ -3,14 +3,13 @@ import ToursCategoryItem from '@components/Tours/ToursCategoryItem'; import { useEffect, useState } from 'react'; import { Spinner } from '@components/common/spinner/Spinner'; import { getMemberTours } from '@api/member'; -import WishList from '@components/Wish/WishList'; export const OurLikedList = () => { const categories = ['전체', '숙소', '식당', '관광지']; - const [selectedContentTypeId, setSelectedContentTypeId] = useState< - null | number - >(null); + // const [selectedContentTypeId, setSelectedContentTypeId] = useState< + // null | number + // >(null); const [selectedCategory, setSelectedCategory] = useState('전체'); useEffect(() => { @@ -26,32 +25,36 @@ export const OurLikedList = () => { // }, [searchWord]); // console.log(); - const { fetchNextPage, hasNextPage, data, isLoading, isError } = - useInfiniteQuery({ - queryKey: ['wishList'], - queryFn: ({ pageParam = 0 }) => getMemberTours(pageParam, 10), - initialPageParam: 0, - getNextPageParam: (lastPage) => { - if ( - lastPage && - lastPage.data && - lastPage.data && - lastPage.data.pageable - ) { - const currentPage = lastPage.data.pageable.pageNumber; - const totalPages = lastPage.data.totalPages; + const { + // fetchNextPage, hasNextPage, + data, + isLoading, + isError, + } = useInfiniteQuery({ + queryKey: ['wishList'], + queryFn: ({ pageParam = 0 }) => getMemberTours(pageParam, 10), + initialPageParam: 0, + getNextPageParam: (lastPage) => { + if ( + lastPage && + lastPage.data && + lastPage.data && + lastPage.data.pageable + ) { + const currentPage = lastPage.data.pageable.pageNumber; + const totalPages = lastPage.data.totalPages; - if (currentPage < totalPages - 1) { - return currentPage + 1; - } + if (currentPage < totalPages - 1) { + return currentPage + 1; } - return undefined; - }, - }); + } + return undefined; + }, + }); - const handleCategoryClick = (contentTypeId: number | null) => { - setSelectedContentTypeId(contentTypeId); - }; + // const handleCategoryClick = (contentTypeId: number | null) => { + // setSelectedContentTypeId(contentTypeId); + // }; if (isLoading) { return ; diff --git a/src/pages/plan/addPlace/AddtoListBtn.tsx b/src/pages/plan/addPlace/AddtoListBtn.tsx new file mode 100644 index 00000000..9e54f742 --- /dev/null +++ b/src/pages/plan/addPlace/AddtoListBtn.tsx @@ -0,0 +1,36 @@ +import { useRecoilValue } from 'recoil'; +import { selectedItemsState } from '@recoil/listItem'; +import { ButtonPrimary } from '@components/common/button/Button'; +import { postTripsLike } from '@api/trips'; +// import { useNavigate } from 'react-router-dom'; + +const AddToListButton = () => { + const selectedTourItemIds = useRecoilValue(selectedItemsState); + // const navigate = useNavigate(); + + const getTripIdFromUrl = () => { + const pathSegments = window.location.pathname.split('/'); + const tripIdIndex = + pathSegments.findIndex((segment) => segment === 'trip') + 1; + return pathSegments[tripIdIndex] + ? parseInt(pathSegments[tripIdIndex], 10) + : null; + }; + + const handleAddClick = async () => { + const tripId = getTripIdFromUrl(); + if (tripId) { + try { + const response = await postTripsLike(tripId, selectedTourItemIds); + console.log('API response:', response); + // navigate(`/trip/${tripId}`); + } catch (error) { + console.error('API error:', error); + } + } + }; + + return 추가하기; +}; + +export default AddToListButton; diff --git a/src/pages/plan/addPlace/MyLiked.tsx b/src/pages/plan/addPlace/MyLiked.tsx index ea917e60..5e65a8d3 100644 --- a/src/pages/plan/addPlace/MyLiked.tsx +++ b/src/pages/plan/addPlace/MyLiked.tsx @@ -3,19 +3,14 @@ import { useState } from 'react'; import { Spinner } from '@components/common/spinner/Spinner'; import { getMemberTours } from '@api/member'; import { MyLikedList } from './MyLikedList'; -import { ButtonPrimary } from '@components/common/button/Button'; import WishCategory from '@components/Wish/WishCategory'; +import AddToListButton from './AddtoListBtn'; export const MyLiked = () => { const [selectedContentTypeId, setSelectedContentTypeId] = useState< null | number >(null); - // const [selectedCategory, setSelectedCategory] = useState('전체'); - // useEffect(() => { - // console.log(selectedCategory); - // }, [selectedCategory]); - const handleCategoryClick = (contentTypeId: number | null) => { setSelectedContentTypeId(contentTypeId); }; @@ -50,9 +45,7 @@ export const MyLiked = () => { console.log('error fetching search result '); } - // console.log(data?.pages[0].data.content); const searchResults = data?.pages.flatMap((page) => page.data.content) || []; - console.log('searchResults', searchResults); const noResults = searchResults && searchResults.length === 0; return ( @@ -73,7 +66,7 @@ export const MyLiked = () => { /> )}
- 추가하기 +
); diff --git a/src/pages/plan/addPlace/ResultCategoryPlan.tsx b/src/pages/plan/addPlace/ResultCategoryPlan.tsx index 294c29bd..7e3f8706 100644 --- a/src/pages/plan/addPlace/ResultCategoryPlan.tsx +++ b/src/pages/plan/addPlace/ResultCategoryPlan.tsx @@ -1,7 +1,8 @@ -import { ButtonPrimary, ButtonWhite } from '@components/common/button/Button'; +import { ButtonWhite } from '@components/common/button/Button'; import { TourType } from '@/@types/tours.types'; import { InfiniteQueryObserverResult } from '@tanstack/react-query'; import { ResultItemPlan } from './ResultItem'; +import AddToListButton from './AddtoListBtn'; interface ResultCategoryProps { data: TourType[]; @@ -17,7 +18,6 @@ export const ResultCategoryPlan = ({ hasNextPage, isFetchingNextPage, }: ResultCategoryProps) => { - // console.log('hasNextPage', hasNextPage); return ( <>

ResultCategoryPlan

@@ -38,7 +38,9 @@ export const ResultCategoryPlan = ({
)}
- 추가하기 +
+ +
); diff --git a/src/pages/plan/addPlace/ResultItem.tsx b/src/pages/plan/addPlace/ResultItem.tsx index cb2849aa..9ad70dde 100644 --- a/src/pages/plan/addPlace/ResultItem.tsx +++ b/src/pages/plan/addPlace/ResultItem.tsx @@ -1,9 +1,19 @@ import { TourType } from '@/@types/tours.types'; import { ListSelectBtn } from '@components/common/button/ListSelectBtn'; import { StarIcon } from '@components/common/icons/Icons'; +import { selectedItemsState } from '@recoil/listItem'; +import { useRecoilState } from 'recoil'; export const ResultItemPlan = ({ result }: { result: TourType }) => { - console.log(result); + const [selectedItems, setSelectedItems] = useRecoilState(selectedItemsState); + const id = result.id; + const handleSelect = () => { + if (selectedItems.includes(id)) { + setSelectedItems(selectedItems.filter((item) => item !== id)); + } else { + setSelectedItems([...selectedItems, id]); + } + }; return (
@@ -27,7 +37,7 @@ export const ResultItemPlan = ({ result }: { result: TourType }) => {
- 선택 + 선택
); }; From 0f80a54f597ddf3a38f88234d59d89f133acc0d4 Mon Sep 17 00:00:00 2001 From: Hojin Date: Thu, 18 Jan 2024 01:34:21 +0900 Subject: [PATCH 05/13] =?UTF-8?q?Fix:=20key=20=EC=9C=84=EB=B0=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Plan/PlanItemBox.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/Plan/PlanItemBox.tsx b/src/components/Plan/PlanItemBox.tsx index 217e96c2..15b4646d 100644 --- a/src/components/Plan/PlanItemBox.tsx +++ b/src/components/Plan/PlanItemBox.tsx @@ -1,6 +1,7 @@ import { PenIcon, CarIcon, BusIcon } from '@components/common/icons/Icons'; import { TripItem, Paths } from '@/@types/service'; import { v4 as uuidv4 } from 'uuid'; +import React from 'react'; type PlanItemBoxProps = { item: TripItem[]; @@ -19,10 +20,8 @@ const PlanItemBox = ({ item, paths, transportation }: PlanItemBoxProps) => { <>
{item.map((item, index) => ( - <> -
+ +
{
))} - + ))}
From 7c06eb11ca410e0a30a2d6c7d6c47c2f32c86335 Mon Sep 17 00:00:00 2001 From: Hojin Date: Thu, 18 Jan 2024 03:19:55 +0900 Subject: [PATCH 06/13] =?UTF-8?q?Feat:=20=ED=8E=B8=EC=A7=91=EC=A0=84=20?= =?UTF-8?q?=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Plan/PlanItemBox.tsx | 99 ++++++++++-------- src/components/common/icons/Icons.tsx | 144 ++++++++++++++++++++++++++ src/utils/utils.ts | 2 +- 3 files changed, 202 insertions(+), 43 deletions(-) diff --git a/src/components/Plan/PlanItemBox.tsx b/src/components/Plan/PlanItemBox.tsx index 15b4646d..38b24b83 100644 --- a/src/components/Plan/PlanItemBox.tsx +++ b/src/components/Plan/PlanItemBox.tsx @@ -1,7 +1,11 @@ -import { PenIcon, CarIcon, BusIcon } from '@components/common/icons/Icons'; +import { + PenIcon, + CarIcon, + BusIcon, + SequenceIcon, +} from '@components/common/icons/Icons'; import { TripItem, Paths } from '@/@types/service'; import { v4 as uuidv4 } from 'uuid'; -import React from 'react'; type PlanItemBoxProps = { item: TripItem[]; @@ -19,51 +23,62 @@ const PlanItemBox = ({ item, paths, transportation }: PlanItemBoxProps) => { return ( <>
+
DAY 1
{item.map((item, index) => ( - -
- img -
-
- {item.name} - -
-
- {item.category} -
-
- {item.price} 원 +
+
+ {index !== 0 ? ( +
+ ) : ( +
+ )} + +
+
+
+ img +
+
+ {item.name} + +
+
+ {item.category} +
+
+ {item.price} 원 +
-
- {index < itemLength - 1 && - paths - .filter((path) => path.fromSeqNum === item.seqNum) - .map((path) => ( -
-
-
- {transportation === 'CAR' ? ( - - ) : transportation === 'PUBLIC_TRANSPORTATION' ? ( - - ) : null} -
-
- {(path.pathInfo.totalDistance / 1000).toFixed(2)}km,{' '} - {path.pathInfo.totalTime}분,{' '} - {path.pathInfo.price.toLocaleString()}원 + {index < itemLength - 1 && + paths + .filter((path) => path.fromSeqNum === item.seqNum) + .map((path) => ( +
+
+
+ {transportation === 'CAR' ? ( + + ) : transportation === 'PUBLIC_TRANSPORTATION' ? ( + + ) : null} +
+
+ {(path.pathInfo.totalDistance / 1000).toFixed(2)}km,{' '} + {path.pathInfo.totalTime}분,{' '} + {path.pathInfo.price.toLocaleString()}원 +
-
- ))} - + ))} +
+
))}
diff --git a/src/components/common/icons/Icons.tsx b/src/components/common/icons/Icons.tsx index 3d70d69e..8e01e17a 100644 --- a/src/components/common/icons/Icons.tsx +++ b/src/components/common/icons/Icons.tsx @@ -7,6 +7,7 @@ interface IconProps { isHalf?: boolean; cursor?: string; children?: React.ReactNode; + number?: number; } export const HomeIcon: React.FC = ({ @@ -1297,3 +1298,146 @@ export const CounterIcon: React.FC< ); }; + +export const RedIcon: React.FC = ({ size = 20, className }) => { + return ( + + + + + ); +}; + +export const VioletIcon: React.FC = ({ size = 20, className }) => { + return ( + + + + + ); +}; + +export const CyanIcon: React.FC = ({ size = 20, className }) => { + return ( + + + + + ); +}; + +export const OrangeIcon: React.FC = ({ size = 20, className }) => { + return ( + + + + + ); +}; + +export const GreenIcon: React.FC = ({ size = 20, className }) => { + return ( + + + + + ); +}; + +export const SequenceIcon: React.FC = ({ + size = 20, + className, + number, +}) => { + return ( + + + {number !== undefined && ( + + {number} + + )} + + ); +}; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 35bb10df..b371a609 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -35,7 +35,7 @@ export function calculateDayAndDate(startDate: string, endDate: string) { const DayArr = Array.from( { length: Math.ceil(differenceInDays) + 1 }, - (_, i) => `Day${i + 1}`, + (_, i) => `DAY ${i + 1}`, ); const DateArr = []; From bc321498473b373bd03c9c44dda5fcca9ea50b49 Mon Sep 17 00:00:00 2001 From: Hojin Date: Thu, 18 Jan 2024 04:29:36 +0900 Subject: [PATCH 07/13] =?UTF-8?q?Feat:=20=EC=97=AC=EC=A0=95=ED=8E=B8?= =?UTF-8?q?=EC=A7=91=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A7=88=ED=81=AC?= =?UTF-8?q?=EC=97=85=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/socket.ts | 1 - src/components/Plan/PlanEditItemBox.tsx | 58 +++++++++++ src/components/Plan/PlanItem.tsx | 129 ++++++++++++++++-------- src/components/Plan/PlanItemBox.tsx | 10 +- src/components/Plan/PlanSectionTop.tsx | 6 +- src/components/common/icons/Icons.tsx | 20 ++++ 6 files changed, 174 insertions(+), 50 deletions(-) create mode 100644 src/components/Plan/PlanEditItemBox.tsx diff --git a/src/api/socket.ts b/src/api/socket.ts index 94033e68..d95a4bde 100644 --- a/src/api/socket.ts +++ b/src/api/socket.ts @@ -163,7 +163,6 @@ export const pubGetPathAndItems = ( pubGetPathAndItems: pubGetPathAndItems, tripId: string, ) => { - console.log('펍내부',pubGetPathAndItems); socketClient.publish({ destination: `/pub/trips/${tripId}/getPathAndItems`, body: JSON.stringify(pubGetPathAndItems), diff --git a/src/components/Plan/PlanEditItemBox.tsx b/src/components/Plan/PlanEditItemBox.tsx new file mode 100644 index 00000000..a1bf8db8 --- /dev/null +++ b/src/components/Plan/PlanEditItemBox.tsx @@ -0,0 +1,58 @@ +import { PenIcon, DragAndDropIcon } from '@components/common/icons/Icons'; +import { TripItem, Paths } from '@/@types/service'; + +type PlanItemBoxProps = { + item: TripItem[]; + paths: Paths[]; + day: string; +}; + +const PlanEditItemBox = ({ item, paths, day }: PlanItemBoxProps) => { + if (!item || !paths) { + return
Missing data
; + } + + return ( + <> +
+
{day}
+ {item.map((item) => ( +
+
+ +
+
+
+ img +
+
+ {item.name} + +
+
+ {item.category} +
+
+ {item.price} 원 +
+
+
+
+
+ +
+
+ ))} +
+ + ); +}; + +export default PlanEditItemBox; diff --git a/src/components/Plan/PlanItem.tsx b/src/components/Plan/PlanItem.tsx index 89a28752..77c4ac9d 100644 --- a/src/components/Plan/PlanItem.tsx +++ b/src/components/Plan/PlanItem.tsx @@ -3,7 +3,8 @@ import { PlusIcon, CarIcon, BusIcon } from '@components/common/icons/Icons'; import { useNavigate } from 'react-router-dom'; import TripMap from './TripMap'; import PlanItemBox from './PlanItemBox'; -import { useContext, useEffect } from 'react'; +import PlanEditItemBox from './PlanEditItemBox'; +import { useContext, useEffect, useState } from 'react'; import { socketContext } from '@hooks/useSocket'; import { useRecoilState } from 'recoil'; import { visitDateState } from '@recoil/socket'; @@ -11,15 +12,22 @@ import { pubGetPathAndItems, pubUpdateTransportation } from '@api/socket'; import { tripIdState } from '@recoil/socket'; import { useRecoilValue } from 'recoil'; -const PlanItem = (date: any) => { +type PlanItemProps = { + date: string; + day: string; +}; + +const PlanItem: React.FC = ({ date, day }) => { const navigate = useNavigate(); + const [isEdit, SetIsEdit] = useState(false); + const tripId = useRecoilValue(tripIdState); const [visitDate, setVisitDate] = useRecoilState(visitDateState); const { tripItem, tripPath, callBackPub } = useContext(socketContext); useEffect(() => { - setVisitDate({ visitDate: date.date }); - }, [date.date]); + setVisitDate({ visitDate: date }); + }, [date]); useEffect(() => { if (visitDate && tripId) { @@ -27,6 +35,10 @@ const PlanItem = (date: any) => { } }, [visitDate]); + const handleEdit = () => { + SetIsEdit((prev) => !prev); + }; + const handleTranspo = ( transportation: 'CAR' | 'PUBLIC_TRANSPORTATION', visitDate: string, @@ -52,55 +64,84 @@ const PlanItem = (date: any) => { {tripPath && }
-
-
- handleTranspo('CAR', visitDate?.visitDate || '', tripId || '') - } - className="flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-l-md border border-solid border-gray3"> - -
-
- handleTranspo( - 'PUBLIC_TRANSPORTATION', - visitDate?.visitDate || '', - tripId || '', - ) - } - className="pointer-cursor -ml-[1px] flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-r-md border border-solid border-gray3"> - + ) : ( +
+
+ handleTranspo('CAR', visitDate?.visitDate || '', tripId || '') } - /> + className="flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-l-md border border-solid border-gray3"> + +
+
+ handleTranspo( + 'PUBLIC_TRANSPORTATION', + visitDate?.visitDate || '', + tripId || '', + ) + } + className="pointer-cursor -ml-[1px] flex h-[32px] w-[32px] cursor-pointer items-center justify-center rounded-r-md border border-solid border-gray3"> + +
-
-
- + {isEdit ? ( + + ) : ( + + )}
- navigate('./place')} - className="h-[40px] w-full"> -
- -
장소 추가하기
+ {isEdit ? ( +
+
+
+ 삭제하기 +
+
+ 날짜 이동 +
+
- + ) : ( + navigate('./place')} + className="h-[40px] w-full"> +
+ +
장소 추가하기
+
+
+ )}
); diff --git a/src/components/Plan/PlanItemBox.tsx b/src/components/Plan/PlanItemBox.tsx index 38b24b83..d54a922c 100644 --- a/src/components/Plan/PlanItemBox.tsx +++ b/src/components/Plan/PlanItemBox.tsx @@ -11,9 +11,15 @@ type PlanItemBoxProps = { item: TripItem[]; paths: Paths[]; transportation: string; + day: string; }; -const PlanItemBox = ({ item, paths, transportation }: PlanItemBoxProps) => { +const PlanItemBox = ({ + item, + paths, + transportation, + day, +}: PlanItemBoxProps) => { if (!item || !paths) { return
Missing data
; } @@ -23,7 +29,7 @@ const PlanItemBox = ({ item, paths, transportation }: PlanItemBoxProps) => { return ( <>
-
DAY 1
+
{day}
{item.map((item, index) => (
diff --git a/src/components/Plan/PlanSectionTop.tsx b/src/components/Plan/PlanSectionTop.tsx index 37224884..6f971919 100644 --- a/src/components/Plan/PlanSectionTop.tsx +++ b/src/components/Plan/PlanSectionTop.tsx @@ -36,7 +36,7 @@ const PlanSectionTop = () => { if (startDate && endDate) { ({ DayArr, DateArr } = calculateDayAndDate(startDate, endDate)); } - + return (
{ ( - + contents={DateArr.map((date, index) => ( + ))} />
diff --git a/src/components/common/icons/Icons.tsx b/src/components/common/icons/Icons.tsx index 8e01e17a..46debb1b 100644 --- a/src/components/common/icons/Icons.tsx +++ b/src/components/common/icons/Icons.tsx @@ -1441,3 +1441,23 @@ export const SequenceIcon: React.FC = ({ ); }; + +export const DragAndDropIcon: React.FC = ({}) => { + return ( + + + + ); +}; From e90bf3a085b1bfba11109cead3a3a1f87db4ee54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=96=B4=EC=8A=B9=EC=A4=80?= Date: Thu, 18 Jan 2024 06:31:27 +0900 Subject: [PATCH 08/13] =?UTF-8?q?Feat:=20=EA=B3=B5=EC=9C=A0/=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Trip/PlanTripButton.tsx | 14 ++- src/components/Trip/TripInfo.tsx | 95 ++++++++++++++++++- src/components/Trip/TripParticipant.tsx | 31 +++--- src/components/Trip/TripPreference.tsx | 37 ++++++-- src/components/common/accordion/Accordion.tsx | 4 +- src/recoil/trip.ts | 22 +++++ 6 files changed, 164 insertions(+), 39 deletions(-) create mode 100644 src/recoil/trip.ts diff --git a/src/components/Trip/PlanTripButton.tsx b/src/components/Trip/PlanTripButton.tsx index b2d5012b..9cfa2dfd 100644 --- a/src/components/Trip/PlanTripButton.tsx +++ b/src/components/Trip/PlanTripButton.tsx @@ -2,12 +2,16 @@ import { PlanIcon, RightIcon } from '@components/common/icons/Icons'; const PlanTripButton = () => { return ( - ); }; diff --git a/src/components/Trip/TripInfo.tsx b/src/components/Trip/TripInfo.tsx index 8c437979..5594c89a 100644 --- a/src/components/Trip/TripInfo.tsx +++ b/src/components/Trip/TripInfo.tsx @@ -3,22 +3,107 @@ import { useRecoilValue, useRecoilState } from 'recoil'; import { isModalOpenState, modalChildrenState } from '@recoil/modal'; import TripSurveyMember from '@components/common/modal/children/TripSurveyMember'; import { Modal } from '@components/common/modal'; +import { useQuery } from '@tanstack/react-query'; +import { getTripsMembers } from '@api/trips'; +import { tripIdState } from '@recoil/socket'; +import { ReactComponent as NullUser } from '@assets/images/NullUser.svg'; +import { DownIcon } from '@components/common/icons/Icons'; +import { useState } from 'react'; + +const ShareList = () => { + const tripId = Number(useRecoilValue(tripIdState)); + const { data: tripsMembers } = useQuery({ + queryKey: ['tripsMembers', tripId], + queryFn: () => getTripsMembers(tripId), + }); + const members = tripsMembers?.data?.data?.tripMemberSimpleInfos; + + return ( + <> +
+
+ {members.map((member: any, index: number) => { + return ( +
+ {member.profileImageUrl && + member.profileImageUrl !== 'http://asiduheimage.jpg' ? ( + 유저 프로필 + ) : ( + + )} +
{member.nickname}
+
+ ); + })} +
+ + ); +}; const TripInfo = () => { const modalChildren = useRecoilValue(modalChildrenState); const [isModalOpen, setIsModalOpen] = useRecoilState(isModalOpenState); + const tripId = Number(useRecoilValue(tripIdState)); + const [isAccordion, setIsAccordion] = useState(false); + + const { data: tripsMembers } = useQuery({ + queryKey: ['tripsMembers', tripId], + queryFn: () => getTripsMembers(tripId), + }); + const members = tripsMembers?.data?.data?.tripMemberSimpleInfos; const closeModal = () => { setIsModalOpen(false); }; + const handleClickButton = () => { + setIsAccordion((prev) => !prev); + }; + return ( <>
-
프로필
-
1명과 공유중
+
+ {members?.map((member: any, index: number) => ( +
+ {member.profileImageUrl && + member.profileImageUrl !== 'http://asiduheimage.jpg' ? ( + 유저 프로필 + ) : ( + + )} +
+ ))} +
+ +
+

+ {members?.length}명과 공유중 +

+
+ +
+
+ + {isAccordion && }
@@ -28,9 +113,11 @@ const TripInfo = () => { 5
- +
- 23.12.23 ~ 23.12.25 + 23.12.23 - 23.12.25
{modalChildren === 'TripSurveyMember' && } diff --git a/src/components/Trip/TripParticipant.tsx b/src/components/Trip/TripParticipant.tsx index fd70334c..934aa95e 100644 --- a/src/components/Trip/TripParticipant.tsx +++ b/src/components/Trip/TripParticipant.tsx @@ -1,8 +1,6 @@ -import { getTripsSurveyMembers } from '@api/trips'; -import { useQuery } from '@tanstack/react-query'; -import { tripIdState } from '@recoil/socket'; -import { useRecoilValue } from 'recoil'; import { ReactComponent as NullUser } from '@assets/images/NullUser.svg'; +import { useRecoilValue } from 'recoil'; +import { participantsState } from '@recoil/trip'; interface ParticipantStatusProps { status: string; @@ -32,25 +30,22 @@ const ParticipantList: React.FC<{ infos: any[] }> = ({ infos }) => ( export const ParticipantStatus: React.FC = ({ status, }) => { - const tripId = Number(useRecoilValue(tripIdState)); - - const { data: tripsSurveyMembers } = useQuery({ - queryKey: ['tripsSurveyMembers', tripId], - queryFn: () => getTripsSurveyMembers(tripId), - }); - - const participants = - status === '참여' - ? tripsSurveyMembers?.data?.data?.tripSurveySetMemberInfos - : tripsSurveyMembers?.data?.data?.nonTripSurveySetMemberInfos; + const participants = useRecoilValue(participantsState); return (
- {participants && - `${participants.length}명 ${status === '참여' ? '참여' : '미참여'}`} + {status == '참여' ? ( + <>{participants?.tripSurveyMemberCount}명 참여 + ) : ( + <>{participants?.nonTripSurveySetMemberInfos?.length}명 미참여 + )}
- {participants && } + {status == '참여' ? ( + + ) : ( + + )}
); }; diff --git a/src/components/Trip/TripPreference.tsx b/src/components/Trip/TripPreference.tsx index 86bada0d..02f71976 100644 --- a/src/components/Trip/TripPreference.tsx +++ b/src/components/Trip/TripPreference.tsx @@ -1,16 +1,16 @@ import React, { useState, useEffect } from 'react'; import { getTripsSurvey } from '@api/trips'; import { useQuery } from '@tanstack/react-query'; -import { useParams } from 'react-router-dom'; -import { MoreIcon } from '@components/common/icons/Icons'; -import { RightIcon } from '@components/common/icons/Icons'; +import { MoreIcon, RightIcon, HeartIcon } from '@components/common/icons/Icons'; import { calculatePercentage, calculatePercentageRemain, } from '@utils/calculatePercentage'; -import { useSetRecoilState } from 'recoil'; import { modalChildrenState, isModalOpenState } from '@recoil/modal'; - +import { getTripsSurveyMembers } from '@api/trips'; +import { tripIdState } from '@recoil/socket'; +import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil'; +import { participantsState } from '@recoil/trip'; interface RatioBarParams { value: number; total: number; @@ -28,7 +28,12 @@ interface PercentageParams { const TripPreferenceButton: React.FC = () => { return ( + + +
+
+ ); }; export default PlanEditItemBox; - -{ - /* <> -
-
{day}
- {item.map((item) => ( -
-
- -
-
-
- img -
-
- {item.name} - -
-
- {item.category} -
-
- {item.price} 원 -
-
-
-
-
- -
-
- ))} -
- */ -} diff --git a/src/components/Plan/PlanItem.tsx b/src/components/Plan/PlanItem.tsx index 4a65b522..c27cf354 100644 --- a/src/components/Plan/PlanItem.tsx +++ b/src/components/Plan/PlanItem.tsx @@ -59,8 +59,6 @@ const PlanItem: React.FC = ({ date, day }) => { const transpo = tripItem?.data?.transportation || ''; - console.log(tripItem); - return ( <> {tripPath && } @@ -100,6 +98,7 @@ const PlanItem: React.FC = ({ date, day }) => { )}
{isEdit ? ( -
-
-
- 삭제하기 -
-
- 날짜 이동 -
-
-
+ '' ) : ( navigate('./place')} diff --git a/src/components/Plan/PlanMoveItem.tsx b/src/components/Plan/PlanMoveItem.tsx new file mode 100644 index 00000000..c4a3f2c6 --- /dev/null +++ b/src/components/Plan/PlanMoveItem.tsx @@ -0,0 +1,123 @@ +import * as Dialog from '@radix-ui/react-dialog'; +import { PaperIcon } from '@components/common/icons/Icons'; +import { useNavigate } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; +import { dayState, dateState } from '@recoil/plan'; +import { pubUpdateVisitDate } from '@api/socket'; +import { useContext } from 'react'; +import { socketContext } from '@hooks/useSocket'; +import { useState, useEffect } from 'react'; +import ToastPopUp from '@components/common/toastpopup/ToastPopUp'; + +interface PlanMoveItemProps { + isCheck: number | null; + tripId: string | null; + visitDate: string | null; +} + +const PlanMoveItem: React.FC = ({ + isCheck, + tripId, + visitDate, +}) => { + const { callBackPub } = useContext(socketContext); + const day = useRecoilValue(dayState); + const date = useRecoilValue(dateState); + + const [toastPopUp, setToastPopUp] = useState({ + isPopUp: false, + noun: '', + verb: '', + }); + + const handleMoveItem = (newVisitDate: string) => { + if (visitDate === newVisitDate) { + return; + } + if (tripId && isCheck && visitDate) { + callBackPub(() => + pubUpdateVisitDate( + { + tripId: tripId, + oldVisitDate: visitDate, + newVisitDate: newVisitDate, + }, + isCheck, + ), + ); + } + setToastPopUp(() => ({ + isPopUp: true, + noun: '날짜 이동', + verb: '완료', + })); + }; + + useEffect(() => { + if (toastPopUp.isPopUp) { + const timer = setTimeout(() => { + setToastPopUp(() => ({ + isPopUp: false, + noun: '', + verb: '', + })); + }, 2000); + return () => clearTimeout(timer); + } + }, [toastPopUp]); + + return ( + <> + {toastPopUp.isPopUp && ( + + )} + + + + + + + + + +
+
+
+
+

+ 날짜 이동 +

+
+
+
+ {day.map((day, index) => ( + + + + ))} +
+
+
+
+
+
+
+
+
+ + ); +}; + +export default PlanMoveItem; diff --git a/src/components/Plan/PlanSectionTop.tsx b/src/components/Plan/PlanSectionTop.tsx index 6f971919..4f7f4d8e 100644 --- a/src/components/Plan/PlanSectionTop.tsx +++ b/src/components/Plan/PlanSectionTop.tsx @@ -8,7 +8,8 @@ import { socketContext } from '@hooks/useSocket'; import { useContext } from 'react'; import { pubEnterMember } from '@api/socket'; import { useEffect } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useRecoilValue, useRecoilState } from 'recoil'; +import { dayState, dateState } from '@recoil/plan'; import { tripIdState, memberIdState } from '@recoil/socket'; import { calculateDayAndDate } from '@utils/utils'; @@ -16,6 +17,8 @@ const PlanSectionTop = () => { const navigate = useNavigate(); const tripId = useRecoilValue(tripIdState); const pubMember = useRecoilValue(memberIdState); + const [_, setDay] = useRecoilState(dayState); + const [date, setDate] = useRecoilState(dateState); if (!pubMember || !tripId) { return
에러
; @@ -37,6 +40,11 @@ const PlanSectionTop = () => { ({ DayArr, DateArr } = calculateDayAndDate(startDate, endDate)); } + useEffect(() => { + setDay(DayArr); + setDate(DateArr); + }, [startDate, endDate]); + return (
void) | ((e: React.MouseEvent) => void); onCancel?: (() => void) | ((e: React.MouseEvent) => void); children: ReactNode; + closeOnConfirm?: boolean; + isCheck?: number | null; } const Alert: FC = ({ @@ -15,10 +17,13 @@ const Alert: FC = ({ onConfirm, onCancel, children, + closeOnConfirm = false, + isCheck = true, }) => ( - {children} - + + {children} + @@ -35,11 +40,21 @@ const Alert: FC = ({ 취소 - + {closeOnConfirm ? ( + + + + ) : ( + + )}
diff --git a/src/components/common/icons/Icons.tsx b/src/components/common/icons/Icons.tsx index 65259be1..34987b1f 100644 --- a/src/components/common/icons/Icons.tsx +++ b/src/components/common/icons/Icons.tsx @@ -1433,7 +1433,7 @@ export const SequenceIcon: React.FC = ({ y="54%" dominantBaseline="middle" textAnchor="middle" - fontSize="small" // 이 부분은 SVG의 크기에 맞게 조정하세요 + fontSize="small" fill="white"> {number} @@ -1461,3 +1461,41 @@ export const DragAndDropIcon: React.FC = ({}) => { ); }; + +export const PaperIcon: React.FC = ({}) => { + return ( + + + + + + + ); +}; diff --git a/src/components/common/toastpopup/ToastPopUp.tsx b/src/components/common/toastpopup/ToastPopUp.tsx index e57ab098..1abfa1d9 100644 --- a/src/components/common/toastpopup/ToastPopUp.tsx +++ b/src/components/common/toastpopup/ToastPopUp.tsx @@ -21,9 +21,11 @@ const ToastPopUp: React.FC = ({ noun, verb }) => { return () => clearTimeout(timeout); }, []); - if (noun === '일정') { - setParticle('이'); - } + useEffect(() => { + if (noun === '일정' || noun === '날짜 이동') { + setParticle('이'); + } + }, [noun]); return (
= ({ noun, verb }) => { transition: 'transform 0.5s ease-in-out, opacity 0.5s ease-in-out', opacity: visible ? 1 : 0, zIndex: 1, // 이거 해줘야 kakao-map도 dimmed됨 + marginTop: '56px', }}>

{noun} - {particle} {verb}되었습니다 + {particle} {verb}되었습니다.

); diff --git a/src/recoil/plan.ts b/src/recoil/plan.ts new file mode 100644 index 00000000..c938efaa --- /dev/null +++ b/src/recoil/plan.ts @@ -0,0 +1,11 @@ +import { atom } from 'recoil'; + +export const dayState = atom({ + key: 'dayState', + default: [''], +}); + +export const dateState = atom({ + key: 'dateState', + default: [''], +}); From 64c71e0ad395f341905917ac02969932f553660a Mon Sep 17 00:00:00 2001 From: Hojin Date: Thu, 18 Jan 2024 15:50:25 +0900 Subject: [PATCH 13/13] =?UTF-8?q?Fix:=20=EB=B9=8C=EB=93=9C=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Plan/PlanMoveItem.tsx | 1 - src/components/Plan/PlanSectionTop.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/Plan/PlanMoveItem.tsx b/src/components/Plan/PlanMoveItem.tsx index c4a3f2c6..9601061a 100644 --- a/src/components/Plan/PlanMoveItem.tsx +++ b/src/components/Plan/PlanMoveItem.tsx @@ -1,6 +1,5 @@ import * as Dialog from '@radix-ui/react-dialog'; import { PaperIcon } from '@components/common/icons/Icons'; -import { useNavigate } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import { dayState, dateState } from '@recoil/plan'; import { pubUpdateVisitDate } from '@api/socket'; diff --git a/src/components/Plan/PlanSectionTop.tsx b/src/components/Plan/PlanSectionTop.tsx index 4f7f4d8e..c8ace510 100644 --- a/src/components/Plan/PlanSectionTop.tsx +++ b/src/components/Plan/PlanSectionTop.tsx @@ -17,8 +17,8 @@ const PlanSectionTop = () => { const navigate = useNavigate(); const tripId = useRecoilValue(tripIdState); const pubMember = useRecoilValue(memberIdState); - const [_, setDay] = useRecoilState(dayState); - const [date, setDate] = useRecoilState(dateState); + const [, setDay] = useRecoilState(dayState); + const [, setDate] = useRecoilState(dateState); if (!pubMember || !tripId) { return
에러
;