-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 태그 검색 기능 구현 #64
Changes from 14 commits
92c3b11
34a3314
95c3679
6f9c62e
c5626f1
902dda0
aa3bc0d
8709619
453b953
c1f3f7d
56bcc3b
d66ad0e
a3922e8
e67a69a
f7199a5
fbc062e
d0c4582
fbdb3aa
185455e
281395c
ca2d24f
e40319b
5a08969
e423552
12546c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import ProductSearchResultList from './ProductSearchResultList'; | ||
|
||
const meta: Meta<typeof ProductSearchResultList> = { | ||
title: 'search/ProductSearchResultList', | ||
component: ProductSearchResultList, | ||
args: { | ||
searchQuery: '꼬북칩', | ||
}, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Default: Story = {}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import SearchNotFound from './SearchNotFound'; | ||
|
||
const meta: Meta<typeof SearchNotFound> = { | ||
title: 'search/SearchNotFound', | ||
component: SearchNotFound, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Default: Story = {}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { container } from './searchNotFound.css'; | ||
|
||
import SearchNotFoundImage from '@/assets/search-notfound.png'; | ||
import { Text } from '@/components/Common'; | ||
|
||
|
||
const SearchNotFound = () => { | ||
return ( | ||
<> | ||
<div className={container}> | ||
<img src={SearchNotFoundImage} width={335} alt="검색 결과 없음" /> | ||
<Text color="sub" size="headline" weight="semiBold"> | ||
검색 결과가 없어요 | ||
</Text> | ||
<div style={{ height: '6px' }} /> | ||
<Text color="disabled" size="caption4"> | ||
다른 키워드로 검색해보세요! | ||
</Text> | ||
</div> | ||
</> | ||
); | ||
}; | ||
|
||
export default SearchNotFound; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
|
||
export const container = style({ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
alignItems: 'center', | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import TagSearchResultList from './TagSearchResultList'; | ||
|
||
const meta: Meta<typeof TagSearchResultList> = { | ||
title: 'search/TagSearchResultList', | ||
component: TagSearchResultList, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Default: Story = {}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { useRef } from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
|
||
import { container } from './tagSearchResult.css'; | ||
import SearchNotFound from '../SearchNotFound/SearchNotFound'; | ||
|
||
import { ProductItem } from '@/components/Product'; | ||
import { PATH } from '@/constants/path'; | ||
import { useIntersectionObserver } from '@/hooks/common'; | ||
import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; | ||
|
||
interface TagSearchResultListProps { | ||
searchQuery: string; | ||
} | ||
|
||
const TagSearchResultList = ({ searchQuery }: TagSearchResultListProps) => { | ||
const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery, true); | ||
|
||
const scrollRef = useRef<HTMLDivElement>(null); | ||
useIntersectionObserver<HTMLDivElement>(fetchNextPage, scrollRef, hasNextPage); | ||
|
||
if (!searchResponse) { | ||
return null; | ||
} | ||
|
||
const products = searchResponse.pages.flatMap((page) => page.products); | ||
|
||
if (products.length === 0) { | ||
return <SearchNotFound />; | ||
} | ||
|
||
return ( | ||
<> | ||
<ul className={container}> | ||
{products.map((product) => ( | ||
<li key={product.id}> | ||
<Link to={`${PATH.PRODUCT_LIST}/${product.categoryType}/${product.id}`}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 타미 이거 혹시 상품 상세로 넘어가는건가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 맞다!!!! 이거 고친다해놓고 깜빡했네요 |
||
<ProductItem product={product} /> | ||
</Link> | ||
</li> | ||
))} | ||
</ul> | ||
<div ref={scrollRef} aria-hidden /> | ||
</> | ||
); | ||
}; | ||
|
||
export default TagSearchResultList; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
|
||
export const container = style({ | ||
display: 'grid', | ||
gridTemplateColumns: 'repeat(2, 1fr)', | ||
rowGap: 20, | ||
columnGap: 10, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export { default as ProductSearchResultList } from './ProductSearchResultList/ProductSearchResultList'; | ||
export { default as RecommendList } from './RecommendList/RecommendList'; | ||
export { default as RecipeSearchResultList } from './RecipeSearchResultList/RecipeSearchResultList'; | ||
export { default as TagSearchResultList } from './TagSearchResultList/TagSearchResultList'; | ||
export { default as SearchInput } from './SearchInput/SearchInput'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,20 +3,22 @@ import { useSuspendedInfiniteQuery } from '..'; | |
import { searchApi } from '@/apis'; | ||
import type { ProductSearchResultResponse } from '@/types/response'; | ||
|
||
const fetchProductSearchResults = async (query: string, pageParam: number) => { | ||
const fetchProductSearchResults = async (query: string, endpoint: 'tags' | 'products', pageParam: number) => { | ||
const response = await searchApi.get({ | ||
params: '/products/results', | ||
params: `/${endpoint}/results`, | ||
queries: `?query=${query}&lastProductId=${pageParam}`, | ||
}); | ||
const data: ProductSearchResultResponse = await response.json(); | ||
|
||
return data; | ||
}; | ||
|
||
const useInfiniteProductSearchResultsQuery = (query: string) => { | ||
const useInfiniteProductSearchResultsQuery = (query: string, isTagSearch = false) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. true, false는 좀 추상적이여서 endpoint 자체를 인자로 받는건 어떤가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정하고 머지하겠슴니당 |
||
const endpoint = isTagSearch ? 'tags' : 'products'; | ||
|
||
return useSuspendedInfiniteQuery( | ||
['search', 'products', 'results', query], | ||
({ pageParam = 0 }) => fetchProductSearchResults(query, pageParam), | ||
['search', endpoint, 'results', query], | ||
({ pageParam = 0 }) => fetchProductSearchResults(query, endpoint, pageParam), | ||
{ | ||
getNextPageParam: (prevResponse: ProductSearchResultResponse) => { | ||
const lastCursor = prevResponse.products.length | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 컴포넌트의 이름을 보고 떠오른거는 전체 상품 검색한 리스트를 다 뽑아오는건가 생각이 드는데
실제로는 전체중에 2개, 더보기 버튼 이렇게 나오더라구요.
그래서 아예 이 컴포넌트 이름을 변경했으면 좋겠어요!
밑에도 적어놨는데 이거 제 레시피 PR에 있는 ProductOverviewList 재사용하면 좋을 거 같은데,
중간에 자르는게 들어가있는게 걸리네요...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그냥 products에 productsToDisplay 넘겨주는거 어때여?! 컴포넌트 이름은 변경할게요!