-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #187 from FinalDoubleTen/FE-69--feat/create/whereWhen
Fe 69 feat/create/where when
- Loading branch information
Showing
15 changed files
with
650 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<button | ||
onClick={handleClick} | ||
className={`focus:shadow-outline-blue body3 shrink-0 rounded-[32px] px-[14px] py-[7px] text-center leading-normal transition-colors duration-100 focus:outline-none ${ | ||
isActive | ||
? 'border border-solid border-main2 bg-white text-main2' | ||
: 'border border-solid border-gray1 bg-gray1 text-gray4' | ||
}`}> | ||
{children} | ||
</button> | ||
); | ||
}; | ||
|
||
interface ListCheckBtnProps { | ||
onClick?: () => void; | ||
} | ||
|
||
export const ListCheckBtn = ({ onClick }: ListCheckBtnProps) => { | ||
const [isActive, setIsActive] = useState(false); | ||
|
||
const handleClick = () => { | ||
setIsActive(!isActive); | ||
if (onClick) { | ||
onClick(); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="flex items-center justify-center"> | ||
<button | ||
onClick={handleClick} | ||
className={`focus:shadow-outline-blue body3 flex size-[20px] shrink-0 items-center justify-center rounded-[32px] text-white transition-colors duration-100 focus:outline-none ${ | ||
isActive ? ' bg-black ' : 'bg-gray3 ' | ||
}`}> | ||
<CheckIcon color="white" /> | ||
</button> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
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'; | ||
|
||
export const OurLikedList = () => { | ||
const categories = ['전체', '숙소', '식당', '관광지']; | ||
|
||
// const [selectedContentTypeId, setSelectedContentTypeId] = useState< | ||
// null | number | ||
// >(null); | ||
|
||
const [selectedCategory, setSelectedCategory] = useState<string>('전체'); | ||
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 <Spinner />; | ||
} | ||
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 ( | ||
<> | ||
<div className="title3 pt-3">우리의 관심 목록 </div> | ||
<div className="mt-3 flex"> | ||
{categories.map((category) => ( | ||
<ToursCategoryItem | ||
key={category} | ||
name={category} | ||
isSelected={category === selectedCategory} | ||
onSelect={handleSelectCategory} | ||
/> | ||
))} | ||
</div> | ||
{noResults ? ( | ||
<div className="my-10 text-center text-gray3"> | ||
나의 관심목록이 없습니다. | ||
</div> | ||
) : ( | ||
<div></div> | ||
// <WishList | ||
// toursData={data || { pages: [] }} | ||
// fetchNextPage={fetchNextPage} | ||
// hasNextPage={hasNextPage} | ||
// isLoading={isLoading} | ||
// selectedContentTypeId={selectedContentTypeId} | ||
// /> | ||
)} | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <ButtonPrimary onClick={handleAddClick}>추가하기</ButtonPrimary>; | ||
}; | ||
|
||
export default AddToListButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
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 WishCategory from '@components/Wish/WishCategory'; | ||
import AddToListButton from './AddtoListBtn'; | ||
|
||
export const MyLiked = () => { | ||
const [selectedContentTypeId, setSelectedContentTypeId] = useState< | ||
null | number | ||
>(null); | ||
|
||
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 <Spinner />; | ||
} | ||
if (isError) { | ||
console.log('error fetching search result '); | ||
} | ||
|
||
const searchResults = data?.pages.flatMap((page) => page.data.content) || []; | ||
const noResults = searchResults && searchResults.length === 0; | ||
|
||
return ( | ||
<> | ||
<div className="title3 pt-3">나의 관심 목록 </div> | ||
<WishCategory onCategoryClick={handleCategoryClick} /> | ||
{noResults ? ( | ||
<div className="my-10 text-center text-gray3"> | ||
나의 관심목록이 없습니다. | ||
</div> | ||
) : ( | ||
<MyLikedList | ||
toursData={data || { pages: [] }} | ||
fetchNextPage={fetchNextPage} | ||
hasNextPage={hasNextPage} | ||
isLoading={isLoading} | ||
selectedContentTypeId={selectedContentTypeId} | ||
/> | ||
)} | ||
<div className="sticky bottom-0 bg-white py-[20px]"> | ||
<AddToListButton /> | ||
</div> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<WishListProps> = ({ | ||
toursData, | ||
fetchNextPage, | ||
hasNextPage, | ||
isLoading, | ||
selectedContentTypeId, | ||
}) => { | ||
if (!toursData || toursData.pages.length === 0) { | ||
return <div>데이터를 불러오는 중 오류가 발생했습니다.</div>; | ||
} | ||
|
||
const filteredData = | ||
selectedContentTypeId !== null | ||
? toursData.pages.map((group) => ({ | ||
data: { | ||
content: group.data.content.filter( | ||
(item) => item.contentTypeId === selectedContentTypeId, | ||
), | ||
}, | ||
})) | ||
: toursData.pages; | ||
|
||
return ( | ||
<div className="min-h-[70vh]"> | ||
<InfiniteScroll | ||
pageStart={0} | ||
loadMore={() => fetchNextPage()} | ||
hasMore={hasNextPage} | ||
loader={ | ||
<div key={uuidv4()} className="flex justify-center"> | ||
<Spinner /> | ||
</div> | ||
}> | ||
<div className="no-scrollbar grid grid-cols-1 gap-[16px] overflow-y-scroll"> | ||
{!isLoading && | ||
filteredData.map((group) => ( | ||
<React.Fragment key={uuidv4()}> | ||
{group?.data.content.map((wishList: TourType) => ( | ||
<MyLikedListItem key={uuidv4()} wishList={wishList} /> | ||
))} | ||
</React.Fragment> | ||
))} | ||
</div> | ||
</InfiniteScroll> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.