Skip to content

Commit

Permalink
Merge pull request #187 from FinalDoubleTen/FE-69--feat/create/whereWhen
Browse files Browse the repository at this point in the history
Fe 69  feat/create/where when
  • Loading branch information
LeHiHo authored Jan 18, 2024
2 parents 85f7c9b + 87c5f3d commit 810a1b2
Show file tree
Hide file tree
Showing 15 changed files with 650 additions and 8 deletions.
8 changes: 7 additions & 1 deletion src/api/trips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
57 changes: 57 additions & 0 deletions src/components/common/button/ListSelectBtn.tsx
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>
);
};
100 changes: 100 additions & 0 deletions src/pages/plan/OurLikedList.tsx
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}
// />
)}
</>
);
};
36 changes: 36 additions & 0 deletions src/pages/plan/addPlace/AddtoListBtn.tsx
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;
73 changes: 73 additions & 0 deletions src/pages/plan/addPlace/MyLiked.tsx
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>
</>
);
};
62 changes: 62 additions & 0 deletions src/pages/plan/addPlace/MyLikedList.tsx
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>
);
};
Loading

0 comments on commit 810a1b2

Please sign in to comment.