From 92c3b114fc43cab7b71472310e6e27f10c1228d8 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sat, 23 Mar 2024 23:46:32 +0900 Subject: [PATCH 01/22] =?UTF-8?q?refactor:=20=EA=B2=80=EC=83=89=20api?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=83=9C=EA=B7=B8=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=9D=BC=EB=95=8C=EC=9D=98=20endpoint=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductSearchResultList.tsx | 9 +++++++-- .../search/useInfiniteProductSearchResultsQuery.ts | 12 +++++++----- src/mocks/handlers/searchHandlers.ts | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx index 59784df59..7c196c97c 100644 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx @@ -10,10 +10,15 @@ import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; interface ProductSearchResultListProps { searchQuery: string; + isTagSearch: boolean; } -const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) => { - const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery); +const ProductSearchResultList = ({ searchQuery, isTagSearch }: ProductSearchResultListProps) => { + const { + data: searchResponse, + fetchNextPage, + hasNextPage, + } = useInfiniteProductSearchResultsQuery(searchQuery, isTagSearch); const scrollRef = useRef(null); useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); diff --git a/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts b/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts index 0b3668167..6e5d94430 100644 --- a/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts +++ b/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts @@ -3,9 +3,9 @@ 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(); @@ -13,10 +13,12 @@ const fetchProductSearchResults = async (query: string, pageParam: number) => { return data; }; -const useInfiniteProductSearchResultsQuery = (query: string) => { +const useInfiniteProductSearchResultsQuery = (query: string, isTagSearch: boolean) => { + 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 diff --git a/src/mocks/handlers/searchHandlers.ts b/src/mocks/handlers/searchHandlers.ts index 82b1217e6..b1591ef57 100644 --- a/src/mocks/handlers/searchHandlers.ts +++ b/src/mocks/handlers/searchHandlers.ts @@ -14,7 +14,7 @@ export const searchHandlers = [ return res(ctx.status(400)); } - if (searchId === 'products') { + if (searchId === 'products' || searchId === 'tags') { const filteredProducts = { page: { ...productSearchResults.page }, products: productSearchResults.products From 34a33146cf9ed52f1b58b8e8ea205ca5e6a49d5b Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sat, 23 Mar 2024 23:48:30 +0900 Subject: [PATCH 02/22] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/search/useSearch.ts | 18 +++++++++---- src/pages/SearchPage/SearchPage.tsx | 41 ++++++++++++++++------------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/hooks/search/useSearch.ts b/src/hooks/search/useSearch.ts index ac8a671f7..32bf57459 100644 --- a/src/hooks/search/useSearch.ts +++ b/src/hooks/search/useSearch.ts @@ -16,6 +16,7 @@ const useSearch = () => { const [searchQuery, setSearchQuery] = useState(currentSearchQuery || ''); const [isSubmitted, setIsSubmitted] = useState(!!currentSearchQuery); const [isAutocompleteOpen, setIsAutocompleteOpen] = useState(searchQuery.length > 0); + const [isTagSearch, setIsTagSearch] = useState(false); const { toast } = useToastActionContext(); @@ -41,7 +42,7 @@ const useSearch = () => { setSearchParams({ query: value }); }; - const handleSearch: FormEventHandler = (event) => { + const handleSearchForm: FormEventHandler = (event) => { event.preventDefault(); gaEvent({ category: 'submit', action: '검색 페이지에서 검색', label: '검색' }); @@ -62,7 +63,7 @@ const useSearch = () => { setLocalStorage('recentSearchedKeywords', [trimmedSearchQuery, ...recentSearchedKeywords.slice(0, 7)]); }; - const handleSearchClick: MouseEventHandler = (event) => { + const handleSearchByClick: MouseEventHandler = (event) => { const { value } = event.currentTarget; searchKeyword(value); }; @@ -71,6 +72,12 @@ const useSearch = () => { setIsAutocompleteOpen(false); }; + const handleTagSearch: MouseEventHandler = (event) => { + const { value } = event.currentTarget; + searchKeyword(value); + setIsTagSearch(true); + }; + const resetSearchQuery = () => { setSearchQuery(''); setIsSubmitted(false); @@ -84,10 +91,11 @@ const useSearch = () => { isSubmitted, isAutocompleteOpen, handleSearchQuery, - handleSearch, - handleSearchClick, + handleSearchForm, + handleSearchByClick, handleAutocompleteClose, - searchKeyword, + isTagSearch, + handleTagSearch, resetSearchQuery, }; }; diff --git a/src/pages/SearchPage/SearchPage.tsx b/src/pages/SearchPage/SearchPage.tsx index e4512a503..8a3ae2663 100644 --- a/src/pages/SearchPage/SearchPage.tsx +++ b/src/pages/SearchPage/SearchPage.tsx @@ -17,10 +17,11 @@ export const SearchPage = () => { isSubmitted, isAutocompleteOpen, handleSearchQuery, - handleSearch, - handleSearchClick, + handleSearchForm, + handleSearchByClick, handleAutocompleteClose, - searchKeyword, + isTagSearch, + handleTagSearch, resetSearchQuery, } = useSearch(); const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery || ''); @@ -47,7 +48,7 @@ export const SearchPage = () => {
-
+ {!isSubmitted && debouncedSearchQuery && isAutocompleteOpen && ( @@ -55,7 +56,7 @@ export const SearchPage = () => { }> @@ -64,30 +65,34 @@ export const SearchPage = () => {
{isSubmitted && searchQuery ? ( -
+ <>

상품 바로가기

}> - + {/* divider 컴포넌트 */}
-

꿀!조합 바로가기

- - }> - - - -
+ {!isTagSearch && ( + <> +

꿀!조합 바로가기

+ + }> + + + + + )} + ) : ( -
+ <>

최근 검색어

{recentSearchedKeywords?.map((keyword, index) => ( - ))}
-
+ )}
From 6f9c62ecb34111f48074aee3e728fe6ffa69daf3 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Wed, 27 Mar 2024 20:20:47 +0100 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=97=86=EC=9D=84=20=EB=95=8C=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/search-notfound.png | Bin 0 -> 9509 bytes .../ProductSearchResultList.tsx | 12 +++------ .../RecipeSearchResultList.tsx | 9 ++++--- .../SearchNotFound/SearchNotFound.stories.tsx | 13 ++++++++++ .../Search/SearchNotFound/SearchNotFound.tsx | 24 ++++++++++++++++++ .../SearchNotFound/searchNotFound.css.ts | 7 +++++ 6 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 src/assets/search-notfound.png create mode 100644 src/components/Search/SearchNotFound/SearchNotFound.stories.tsx create mode 100644 src/components/Search/SearchNotFound/SearchNotFound.tsx create mode 100644 src/components/Search/SearchNotFound/searchNotFound.css.ts diff --git a/src/assets/search-notfound.png b/src/assets/search-notfound.png new file mode 100644 index 0000000000000000000000000000000000000000..a8bb7e024cb062cf29e2780d118310ad3e8bfa77 GIT binary patch literal 9509 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 b;{B=0=Yh^R7k4D=Yy#R4eT^zL+ZX=_^;Ji` literal 0 HcmV?d00001 diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx index 7c196c97c..2a5c98db2 100644 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx @@ -2,6 +2,7 @@ import { useRef } from 'react'; import { Link } from 'react-router-dom'; import { container } from './productSearchResultList.css'; +import SearchNotFound from '../SearchNotFound/SearchNotFound'; import { ProductOverviewItem } from '@/components/Product'; import { PATH } from '@/constants/path'; @@ -10,15 +11,10 @@ import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; interface ProductSearchResultListProps { searchQuery: string; - isTagSearch: boolean; } -const ProductSearchResultList = ({ searchQuery, isTagSearch }: ProductSearchResultListProps) => { - const { - data: searchResponse, - fetchNextPage, - hasNextPage, - } = useInfiniteProductSearchResultsQuery(searchQuery, isTagSearch); +const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) => { + const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery); const scrollRef = useRef(null); useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); @@ -29,7 +25,7 @@ const ProductSearchResultList = ({ searchQuery, isTagSearch }: ProductSearchResu const products = searchResponse.pages.flatMap((page) => page.products); if (products.length === 0) { - return

검색한 상품을 찾을 수 없습니다.

; + return ; } return ( diff --git a/src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx b/src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx index 89496d2c2..bdc5134b9 100644 --- a/src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx +++ b/src/components/Search/RecipeSearchResultList/RecipeSearchResultList.tsx @@ -1,8 +1,9 @@ -import { Link, Text } from '@fun-eat/design-system'; import { useRef } from 'react'; -import { Link as RouterLink } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import { styled } from 'styled-components'; +import SearchNotFound from '../SearchNotFound/SearchNotFound'; + import { RecipeItem } from '@/components/Recipe'; import { PATH } from '@/constants/path'; import { useIntersectionObserver } from '@/hooks/common'; @@ -20,7 +21,7 @@ const RecipeSearchResultList = ({ searchQuery }: RecipeSearchResultListProps) => const recipes = searchResponse.pages.flatMap((page) => page.recipes); if (recipes.length === 0) { - return 검색한 꿀조합을 찾을 수 없습니다.; + return ; } return ( @@ -28,7 +29,7 @@ const RecipeSearchResultList = ({ searchQuery }: RecipeSearchResultListProps) => {recipes.map((recipe) => (
  • - +
  • diff --git a/src/components/Search/SearchNotFound/SearchNotFound.stories.tsx b/src/components/Search/SearchNotFound/SearchNotFound.stories.tsx new file mode 100644 index 000000000..533a7c7e6 --- /dev/null +++ b/src/components/Search/SearchNotFound/SearchNotFound.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import SearchNotFound from './SearchNotFound'; + +const meta: Meta = { + title: 'search/SearchNotFound', + component: SearchNotFound, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/src/components/Search/SearchNotFound/SearchNotFound.tsx b/src/components/Search/SearchNotFound/SearchNotFound.tsx new file mode 100644 index 000000000..764f3d969 --- /dev/null +++ b/src/components/Search/SearchNotFound/SearchNotFound.tsx @@ -0,0 +1,24 @@ +import { container } from './searchNotFound.css'; + +import SearchNotFoundImage from '@/assets/search-notfound.png'; +import { Text } from '@/components/Common'; + + +const SearchNotFound = () => { + return ( + <> +
    + 검색 결과 없음 + + 검색 결과가 없어요 + +
    + + 다른 키워드로 검색해보세요! + +
    + + ); +}; + +export default SearchNotFound; diff --git a/src/components/Search/SearchNotFound/searchNotFound.css.ts b/src/components/Search/SearchNotFound/searchNotFound.css.ts new file mode 100644 index 000000000..d22ba2914 --- /dev/null +++ b/src/components/Search/SearchNotFound/searchNotFound.css.ts @@ -0,0 +1,7 @@ +import { style } from '@vanilla-extract/css'; + +export const container = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +}); From c5626f1db9299cefb8d8a388df911fc63372fccc Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Wed, 27 Mar 2024 20:21:34 +0100 Subject: [PATCH 04/22] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B2=B0=EA=B3=BC=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TagSearchResultList.stories.tsx | 13 +++++ .../TagSearchResultList.tsx | 48 +++++++++++++++++++ .../tagSearchResult.css.ts | 8 ++++ src/components/Search/index.ts | 1 + 4 files changed, 70 insertions(+) create mode 100644 src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx create mode 100644 src/components/Search/TagSearchResultList/TagSearchResultList.tsx create mode 100644 src/components/Search/TagSearchResultList/tagSearchResult.css.ts diff --git a/src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx b/src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx new file mode 100644 index 000000000..1199821e3 --- /dev/null +++ b/src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import TagSearchResultList from './TagSearchResultList'; + +const meta: Meta = { + title: 'search/TagSearchResultList', + component: TagSearchResultList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/src/components/Search/TagSearchResultList/TagSearchResultList.tsx b/src/components/Search/TagSearchResultList/TagSearchResultList.tsx new file mode 100644 index 000000000..907338a9b --- /dev/null +++ b/src/components/Search/TagSearchResultList/TagSearchResultList.tsx @@ -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(null); + useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); + + if (!searchResponse) { + return null; + } + + const products = searchResponse.pages.flatMap((page) => page.products); + + if (products.length === 0) { + return ; + } + + return ( + <> +
      + {products.map((product) => ( +
    • + + + +
    • + ))} +
    +
    + + ); +}; + +export default TagSearchResultList; diff --git a/src/components/Search/TagSearchResultList/tagSearchResult.css.ts b/src/components/Search/TagSearchResultList/tagSearchResult.css.ts new file mode 100644 index 000000000..8a68a6c02 --- /dev/null +++ b/src/components/Search/TagSearchResultList/tagSearchResult.css.ts @@ -0,0 +1,8 @@ +import { style } from '@vanilla-extract/css'; + +export const container = style({ + display: 'grid', + gridTemplateColumns: 'repeat(2, 1fr)', + rowGap: 20, + columnGap: 10, +}); diff --git a/src/components/Search/index.ts b/src/components/Search/index.ts index fb37d967d..9ec924cc6 100644 --- a/src/components/Search/index.ts +++ b/src/components/Search/index.ts @@ -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'; From 902dda0466ec673d5934dbfcd9ea2175bdb0832f Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sat, 30 Mar 2024 20:18:17 +0100 Subject: [PATCH 05/22] =?UTF-8?q?feat:=20Text=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=99=B8=EB=B6=80=20cla?= =?UTF-8?q?ssName=EC=9D=84=20=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/Text/Text.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Common/Text/Text.tsx b/src/components/Common/Text/Text.tsx index 8c404f586..3cdf7af24 100644 --- a/src/components/Common/Text/Text.tsx +++ b/src/components/Common/Text/Text.tsx @@ -1,3 +1,5 @@ +import cx from 'classnames'; + import { text } from './text.css'; import type { TextElement, TextVariants, OverridableComponentPropsWithoutRef } from './text.types'; @@ -8,10 +10,11 @@ const Text = ({ size = 'body', weight = 'regular', color = 'default', + className, ...props }: TextProps) => { return ( -

    +

    {children}

    ); From aa3bc0d4ea69f22cdb429d25f90b72a993c639f1 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sat, 30 Mar 2024 20:28:00 +0100 Subject: [PATCH 06/22] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=EC=9D=BC=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EB=B6=84=EA=B8=B0=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SearchPage/SearchPage.tsx | 71 ++++++++++++++++++-------- src/pages/SearchPage/searchPage.css.ts | 18 ++----- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/pages/SearchPage/SearchPage.tsx b/src/pages/SearchPage/SearchPage.tsx index 8a3ae2663..4c8cd9410 100644 --- a/src/pages/SearchPage/SearchPage.tsx +++ b/src/pages/SearchPage/SearchPage.tsx @@ -1,13 +1,20 @@ import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import { Suspense, useEffect, useState } from 'react'; -import { badgeContainer, searchResultTitle, searchSection, showMoreButton, subTitle } from './searchPage.css'; +import { badgeContainer, searchWrapper, searchResultTitle, searchSection, subTitle } from './searchPage.css'; -import { Badge, ErrorBoundary, ErrorComponent, Loading, PageHeader } from '@/components/Common'; -import { ProductSearchResultList, RecipeSearchResultList, RecommendList, SearchInput } from '@/components/Search'; +import { Text, Badge, ErrorBoundary, ErrorComponent, Loading, PageHeader } from '@/components/Common'; +import { + ProductSearchResultList, + RecipeSearchResultList, + RecommendList, + SearchInput, + TagSearchResultList, +} from '@/components/Search'; import { RECOMMENDED_TAGS } from '@/constants'; import { useDebounce } from '@/hooks/common'; import { useSearch } from '@/hooks/search'; +import { vars } from '@/styles/theme.css'; import { getLocalStorage } from '@/utils/localStorage'; export const SearchPage = () => { @@ -63,33 +70,51 @@ export const SearchPage = () => { )} -
    +
    {isSubmitted && searchQuery ? ( <> -

    상품 바로가기

    - - }> - - - - - {/* divider 컴포넌트 */} -
    -
    - {!isTagSearch && ( - <> -

    꿀!조합 바로가기

    + {isTagSearch ? ( +
    + + 태그 '{searchQuery}'가 포함된 상품 + }> - + +
    + ) : ( + <> +
    + + 상품 바로가기 + + + }> + + + +
    +
    +
    + + 꿀!조합 바로가기 + + + }> + + + +
    )} ) : ( - <> -

    최근 검색어

    +
    + + 최근 검색어 +
    {recentSearchedKeywords?.map((keyword, index) => ( ))}
    -

    추천 태그

    + + 추천 태그 +
    {RECOMMENDED_TAGS.map(({ id, name }) => ( ))}
    - +
    )}
    diff --git a/src/pages/SearchPage/searchPage.css.ts b/src/pages/SearchPage/searchPage.css.ts index f7d6e2ef9..c6e911353 100644 --- a/src/pages/SearchPage/searchPage.css.ts +++ b/src/pages/SearchPage/searchPage.css.ts @@ -4,25 +4,17 @@ export const searchSection = style({ padding: '0 20px', }); +export const searchWrapper = style({ + padding: '0 20px', + marginBottom: 20, +}); + export const subTitle = style({ - fontSize: 14, marginTop: 28, }); export const searchResultTitle = style({ - fontSize: 12, - color: '#808080', - margin: '20px 0', -}); - -export const showMoreButton = style({ - width: '100%', - height: 38, - padding: '9px 0', margin: '20px 0', - background: '#efefef', - fontSize: 14, - borderRadius: 6, }); export const badgeContainer = style({ From 8709619d7fa0da7275a7c40f1da3fa33593df781 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sat, 30 Mar 2024 20:32:04 +0100 Subject: [PATCH 07/22] =?UTF-8?q?feat:=20=EB=8D=94=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=EC=9D=84=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductSearchResultList.tsx | 8 +++++--- .../productSearchResultList.css.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx index 2a5c98db2..35b5c60c3 100644 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx @@ -1,7 +1,7 @@ import { useRef } from 'react'; import { Link } from 'react-router-dom'; -import { container } from './productSearchResultList.css'; +import { container, showMoreButton } from './productSearchResultList.css'; import SearchNotFound from '../SearchNotFound/SearchNotFound'; import { ProductOverviewItem } from '@/components/Product'; @@ -31,7 +31,7 @@ const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) return ( <>
      - {products.map(({ id, categoryType, image, name, price, averageRating }) => ( + {products.slice(0, 2).map(({ id, categoryType, image, name, price, averageRating }) => (
    • @@ -39,7 +39,9 @@ const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps)
    • ))}
    -
    + + 더보기 + ); }; diff --git a/src/components/Search/ProductSearchResultList/productSearchResultList.css.ts b/src/components/Search/ProductSearchResultList/productSearchResultList.css.ts index d48c09e7b..1d89fd9d0 100644 --- a/src/components/Search/ProductSearchResultList/productSearchResultList.css.ts +++ b/src/components/Search/ProductSearchResultList/productSearchResultList.css.ts @@ -5,3 +5,16 @@ export const container = style({ flexDirection: 'column', gap: 20, }); + +export const showMoreButton = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + height: 38, + padding: '9px 0', + margin: '20px 0', + background: '#efefef', + fontSize: 14, + borderRadius: 6, +}); From 453b9536834b1974aeef6da36d52638846af8f3c Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sat, 30 Mar 2024 20:35:05 +0100 Subject: [PATCH 08/22] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=9D=84=20=EC=9D=BC=EB=B0=98=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EA=B3=BC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queries/search/useInfiniteProductSearchResultsQuery.ts | 2 +- src/hooks/search/useSearch.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts b/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts index 6e5d94430..781f5131e 100644 --- a/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts +++ b/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts @@ -13,7 +13,7 @@ const fetchProductSearchResults = async (query: string, endpoint: 'tags' | 'prod return data; }; -const useInfiniteProductSearchResultsQuery = (query: string, isTagSearch: boolean) => { +const useInfiniteProductSearchResultsQuery = (query: string, isTagSearch = false) => { const endpoint = isTagSearch ? 'tags' : 'products'; return useSuspendedInfiniteQuery( diff --git a/src/hooks/search/useSearch.ts b/src/hooks/search/useSearch.ts index 32bf57459..f13dd7d04 100644 --- a/src/hooks/search/useSearch.ts +++ b/src/hooks/search/useSearch.ts @@ -74,7 +74,9 @@ const useSearch = () => { const handleTagSearch: MouseEventHandler = (event) => { const { value } = event.currentTarget; - searchKeyword(value); + setSearchQuery(value); + setIsSubmitted(true); + setSearchParams({ tag: value }); setIsTagSearch(true); }; From c1f3f7dd2a644e1f8e14e542c77c012ce922a0d1 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sat, 30 Mar 2024 21:04:05 +0100 Subject: [PATCH 09/22] =?UTF-8?q?feat:=20=EC=83=81=ED=92=88=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EB=8D=94=EB=B3=B4=EA=B8=B0=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductSearchResultList.tsx | 4 +- .../ProductSearchListPage.tsx | 42 +++++++++++++++++++ .../productSearchListPage.css.ts | 6 +++ src/router/index.tsx | 9 ++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/pages/ProductSearchListPage/ProductSearchListPage.tsx create mode 100644 src/pages/ProductSearchListPage/productSearchListPage.css.ts diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx index 35b5c60c3..0a5427c6e 100644 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx @@ -8,6 +8,7 @@ import { ProductOverviewItem } from '@/components/Product'; import { PATH } from '@/constants/path'; import { useIntersectionObserver } from '@/hooks/common'; import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; +import displaySlice from '@/utils/displaySlice'; interface ProductSearchResultListProps { searchQuery: string; @@ -23,6 +24,7 @@ const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) } const products = searchResponse.pages.flatMap((page) => page.products); + const productToDisplay = displaySlice(true, products); if (products.length === 0) { return ; @@ -31,7 +33,7 @@ const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) return ( <>
      - {products.slice(0, 2).map(({ id, categoryType, image, name, price, averageRating }) => ( + {productToDisplay.map(({ id, categoryType, image, name, price, averageRating }) => (
    • diff --git a/src/pages/ProductSearchListPage/ProductSearchListPage.tsx b/src/pages/ProductSearchListPage/ProductSearchListPage.tsx new file mode 100644 index 000000000..768b5c310 --- /dev/null +++ b/src/pages/ProductSearchListPage/ProductSearchListPage.tsx @@ -0,0 +1,42 @@ +import { useRef } from 'react'; +import { Link, useSearchParams } from 'react-router-dom'; + +import { container } from './productSearchListPage.css'; + +import { PageHeader } from '@/components/Common'; +import { ProductOverviewItem } from '@/components/Product'; +import { PATH } from '@/constants/path'; +import { useIntersectionObserver } from '@/hooks/common'; +import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; + +export const ProductSearchListPage = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const searchQuery = searchParams.get('query') || ''; + + const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery); + const scrollRef = useRef(null); + useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); + + const products = searchResponse.pages.flatMap((page) => page.products); + + return ( + <> + +
        + {products.map(({ id, categoryType, image, name, price, averageRating }) => ( +
      • + + + +
        +
        +
        +
      • + ))} +
      +
      + + ); +}; + +export default ProductSearchListPage; diff --git a/src/pages/ProductSearchListPage/productSearchListPage.css.ts b/src/pages/ProductSearchListPage/productSearchListPage.css.ts new file mode 100644 index 000000000..e9c77ff4d --- /dev/null +++ b/src/pages/ProductSearchListPage/productSearchListPage.css.ts @@ -0,0 +1,6 @@ +import { style } from '@vanilla-extract/css'; + +export const container = style({ + padding: '0 20px', + marginTop: 28, +}); diff --git a/src/router/index.tsx b/src/router/index.tsx index 677acc24c..f8c4e0261 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -158,6 +158,15 @@ const router = createBrowserRouter([ return { Component: SearchPage }; }, }, + { + path: `${PATH.SEARCH}/products`, + async lazy() { + const { ProductSearchListPage } = await import( + /* webpackChunkName: "ProductSearchListPage" */ '@/pages/ProductSearchListPage/ProductSearchListPage' + ); + return { Component: ProductSearchListPage }; + }, + }, ], }, /** 네비게이션과 헤더(검색 아이콘이 없는)가 있는 레이아웃 */ From 56bcc3b4f96696faa94213a7fbafc50a0989d23e Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 31 Mar 2024 22:53:56 +0200 Subject: [PATCH 10/22] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=9D=84=20=ED=96=88=EC=9D=84=20=EB=95=8C=20input=20v?= =?UTF-8?q?alue=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Search/SearchInput/SearchInput.tsx | 29 ++++++++++++++----- .../Search/SearchInput/searchInput.css.ts | 16 +++++++++- src/hooks/search/useSearch.ts | 7 ++++- src/pages/SearchPage/SearchPage.tsx | 21 +++++++++++--- src/pages/SearchPage/searchPage.css.ts | 6 ++++ 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/components/Search/SearchInput/SearchInput.tsx b/src/components/Search/SearchInput/SearchInput.tsx index 1a15dec38..88a51406a 100644 --- a/src/components/Search/SearchInput/SearchInput.tsx +++ b/src/components/Search/SearchInput/SearchInput.tsx @@ -1,16 +1,17 @@ import type { ComponentPropsWithRef, ForwardedRef } from 'react'; import { forwardRef } from 'react'; -import { iconWrapperButton, inputContainer, searchInput } from './searchInput.css'; +import { iconWrapperButton, inputContainer, searchInput, tagInputWrapper } from './searchInput.css'; -import { SvgIcon } from '@/components/Common'; +import { Text, SvgIcon } from '@/components/Common'; interface SearchInputProps extends ComponentPropsWithRef<'input'> { isInputSubmitted: boolean; + isTagSearch: boolean; } const SearchInput = forwardRef( - ({ value, isInputSubmitted, ...props }: SearchInputProps, ref: ForwardedRef) => { + ({ value, isInputSubmitted, isTagSearch, ...props }: SearchInputProps, ref: ForwardedRef) => { return (
      + {isTagSearch && isInputSubmitted && ( +
      + + {value} + + +
      + )}
      diff --git a/src/components/Search/SearchInput/searchInput.css.ts b/src/components/Search/SearchInput/searchInput.css.ts index acb525f19..b05790d16 100644 --- a/src/components/Search/SearchInput/searchInput.css.ts +++ b/src/components/Search/SearchInput/searchInput.css.ts @@ -6,6 +6,7 @@ export const inputContainer = style({ }); export const searchInput = style({ + position: 'absolute', width: '90%', height: 40, padding: '10px 0 10px 18px', @@ -22,6 +23,19 @@ export const searchInput = style({ }, }); +export const tagInputWrapper = style({ + position: 'relative', + top: 6, + left: 6, + display: 'flex', + alignItems: 'baseline', + gap: 6, + width: 'fit-content', + padding: '5px 12px', + background: '#FFFFFF', + borderRadius: 20, +}); + export const iconWrapperButton = style({ position: 'absolute', top: 0, @@ -29,7 +43,7 @@ export const iconWrapperButton = style({ display: 'flex', alignItems: 'center', width: '10%', - height: '100%', + height: 40, paddingLeft: 4, background: '#efefef', borderRadius: '0 20px 20px 0', diff --git a/src/hooks/search/useSearch.ts b/src/hooks/search/useSearch.ts index f13dd7d04..08d8b39f1 100644 --- a/src/hooks/search/useSearch.ts +++ b/src/hooks/search/useSearch.ts @@ -50,7 +50,11 @@ const useSearch = () => { if (!trimmedSearchQuery) { toast.error('검색어를 입력해주세요'); - focusInput(); + resetSearchQuery(); + return; + } + + if (searchQuery) { resetSearchQuery(); return; } @@ -83,6 +87,7 @@ const useSearch = () => { const resetSearchQuery = () => { setSearchQuery(''); setIsSubmitted(false); + setIsTagSearch(false); setSearchParams({}); focusInput(); }; diff --git a/src/pages/SearchPage/SearchPage.tsx b/src/pages/SearchPage/SearchPage.tsx index 4c8cd9410..51e9c628c 100644 --- a/src/pages/SearchPage/SearchPage.tsx +++ b/src/pages/SearchPage/SearchPage.tsx @@ -1,7 +1,14 @@ import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import { Suspense, useEffect, useState } from 'react'; -import { badgeContainer, searchWrapper, searchResultTitle, searchSection, subTitle } from './searchPage.css'; +import { + badgeContainer, + searchWrapper, + searchResultTitle, + searchSection, + subTitle, + tagSearchWrapper, +} from './searchPage.css'; import { Text, Badge, ErrorBoundary, ErrorComponent, Loading, PageHeader } from '@/components/Common'; import { @@ -55,8 +62,14 @@ export const SearchPage = () => {
      -
      - + + {!isSubmitted && debouncedSearchQuery && isAutocompleteOpen && ( @@ -74,7 +87,7 @@ export const SearchPage = () => { {isSubmitted && searchQuery ? ( <> {isTagSearch ? ( -
      +
      태그 '{searchQuery}'가 포함된 상품 diff --git a/src/pages/SearchPage/searchPage.css.ts b/src/pages/SearchPage/searchPage.css.ts index c6e911353..6bc5b065c 100644 --- a/src/pages/SearchPage/searchPage.css.ts +++ b/src/pages/SearchPage/searchPage.css.ts @@ -5,6 +5,12 @@ export const searchSection = style({ }); export const searchWrapper = style({ + padding: '0 20px', + marginTop: 60, + marginBottom: 20, +}); + +export const tagSearchWrapper = style({ padding: '0 20px', marginBottom: 20, }); From d66ad0eba03e9184044c6e9cdc5808b0ce563fc2 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 31 Mar 2024 22:55:53 +0200 Subject: [PATCH 11/22] =?UTF-8?q?style:=20=EC=82=AC=EC=9A=A9=20=EC=95=88?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=BD=94=EB=93=9C=20import=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/search/useSearch.ts | 1 - src/pages/SearchPage/SearchPage.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/hooks/search/useSearch.ts b/src/hooks/search/useSearch.ts index 08d8b39f1..5e20b8b67 100644 --- a/src/hooks/search/useSearch.ts +++ b/src/hooks/search/useSearch.ts @@ -103,7 +103,6 @@ const useSearch = () => { handleAutocompleteClose, isTagSearch, handleTagSearch, - resetSearchQuery, }; }; diff --git a/src/pages/SearchPage/SearchPage.tsx b/src/pages/SearchPage/SearchPage.tsx index 51e9c628c..44fb99fb6 100644 --- a/src/pages/SearchPage/SearchPage.tsx +++ b/src/pages/SearchPage/SearchPage.tsx @@ -36,7 +36,6 @@ export const SearchPage = () => { handleAutocompleteClose, isTagSearch, handleTagSearch, - resetSearchQuery, } = useSearch(); const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery || ''); const { reset } = useQueryErrorResetBoundary(); From a3922e84530793b4056cd14660066e8e1ad9ff68 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 31 Mar 2024 23:02:31 +0200 Subject: [PATCH 12/22] =?UTF-8?q?feat:=20ProductSearchResultList=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=EB=B6=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductSearchResultList.stories.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx new file mode 100644 index 000000000..5c3eba714 --- /dev/null +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx @@ -0,0 +1,15 @@ +import { Meta, StoryObj } from '@storybook/react'; +import ProductSearchResultList from './ProductSearchResultList'; + +const meta: Meta = { + title: 'search/ProductSearchResultList', + component: ProductSearchResultList, + args: { + searchQuery: '꼬북칩', + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; From e67a69a74a82b0bdc48e3df155b21df8cf8e6b22 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 31 Mar 2024 23:04:30 +0200 Subject: [PATCH 13/22] =?UTF-8?q?fix:=20=EA=B2=80=EC=83=89=EC=9D=B4=20?= =?UTF-8?q?=EB=90=98=EC=97=88=EC=9D=84=20=EB=95=8C=20resetQuery=EB=A5=BC?= =?UTF-8?q?=20=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/search/useSearch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/search/useSearch.ts b/src/hooks/search/useSearch.ts index 5e20b8b67..c37ab2bb4 100644 --- a/src/hooks/search/useSearch.ts +++ b/src/hooks/search/useSearch.ts @@ -54,7 +54,7 @@ const useSearch = () => { return; } - if (searchQuery) { + if (isSubmitted) { resetSearchQuery(); return; } From f7199a58d493ca2f0bfd0f1a73668f78f4f58eec Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 7 Apr 2024 14:32:32 +0200 Subject: [PATCH 14/22] style: fix lint error --- .../ProductSearchResultList.stories.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx index 5c3eba714..58dc91809 100644 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx @@ -1,4 +1,5 @@ -import { Meta, StoryObj } from '@storybook/react'; +import type { Meta, StoryObj } from '@storybook/react'; + import ProductSearchResultList from './ProductSearchResultList'; const meta: Meta = { From fbc062e1176a88bde387e397dd05600326e6c35c Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 7 Apr 2024 14:32:50 +0200 Subject: [PATCH 15/22] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Search/SearchInput/SearchInput.tsx | 19 ++--- src/hooks/search/useSearch.ts | 13 +-- src/pages/SearchPage/SearchPage.tsx | 83 ++++++------------- src/pages/SearchPage/TagSearchResultPage.tsx | 35 ++++++++ src/router/index.tsx | 9 ++ 5 files changed, 84 insertions(+), 75 deletions(-) create mode 100644 src/pages/SearchPage/TagSearchResultPage.tsx diff --git a/src/components/Search/SearchInput/SearchInput.tsx b/src/components/Search/SearchInput/SearchInput.tsx index 88a51406a..092a878ce 100644 --- a/src/components/Search/SearchInput/SearchInput.tsx +++ b/src/components/Search/SearchInput/SearchInput.tsx @@ -7,11 +7,14 @@ import { Text, SvgIcon } from '@/components/Common'; interface SearchInputProps extends ComponentPropsWithRef<'input'> { isInputSubmitted: boolean; - isTagSearch: boolean; + isTagSearch?: boolean; } const SearchInput = forwardRef( - ({ value, isInputSubmitted, isTagSearch, ...props }: SearchInputProps, ref: ForwardedRef) => { + ( + { value, isInputSubmitted, isTagSearch = false, ...props }: SearchInputProps, + ref: ForwardedRef + ) => { return (
      )}
      diff --git a/src/hooks/search/useSearch.ts b/src/hooks/search/useSearch.ts index c37ab2bb4..9b90580bf 100644 --- a/src/hooks/search/useSearch.ts +++ b/src/hooks/search/useSearch.ts @@ -1,10 +1,11 @@ import { useToastActionContext } from '@fun-eat/design-system'; import type { ChangeEventHandler, FormEventHandler, MouseEventHandler } from 'react'; import { useRef, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { useGA } from '../common'; +import { PATH } from '@/constants/path'; import { getLocalStorage, setLocalStorage } from '@/utils/localStorage'; const useSearch = () => { @@ -12,11 +13,11 @@ const useSearch = () => { const [searchParams, setSearchParams] = useSearchParams(); const currentSearchQuery = searchParams.get('query'); + const navigate = useNavigate(); const [searchQuery, setSearchQuery] = useState(currentSearchQuery || ''); const [isSubmitted, setIsSubmitted] = useState(!!currentSearchQuery); const [isAutocompleteOpen, setIsAutocompleteOpen] = useState(searchQuery.length > 0); - const [isTagSearch, setIsTagSearch] = useState(false); const { toast } = useToastActionContext(); @@ -80,16 +81,17 @@ const useSearch = () => { const { value } = event.currentTarget; setSearchQuery(value); setIsSubmitted(true); - setSearchParams({ tag: value }); - setIsTagSearch(true); + + navigate(`${PATH.SEARCH}/tags?query=${value}`); }; const resetSearchQuery = () => { setSearchQuery(''); setIsSubmitted(false); - setIsTagSearch(false); setSearchParams({}); focusInput(); + + navigate(PATH.SEARCH); }; return { @@ -101,7 +103,6 @@ const useSearch = () => { handleSearchForm, handleSearchByClick, handleAutocompleteClose, - isTagSearch, handleTagSearch, }; }; diff --git a/src/pages/SearchPage/SearchPage.tsx b/src/pages/SearchPage/SearchPage.tsx index 44fb99fb6..254a83ba2 100644 --- a/src/pages/SearchPage/SearchPage.tsx +++ b/src/pages/SearchPage/SearchPage.tsx @@ -1,23 +1,10 @@ import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import { Suspense, useEffect, useState } from 'react'; -import { - badgeContainer, - searchWrapper, - searchResultTitle, - searchSection, - subTitle, - tagSearchWrapper, -} from './searchPage.css'; +import { badgeContainer, searchWrapper, searchResultTitle, searchSection, subTitle } from './searchPage.css'; import { Text, Badge, ErrorBoundary, ErrorComponent, Loading, PageHeader } from '@/components/Common'; -import { - ProductSearchResultList, - RecipeSearchResultList, - RecommendList, - SearchInput, - TagSearchResultList, -} from '@/components/Search'; +import { ProductSearchResultList, RecipeSearchResultList, RecommendList, SearchInput } from '@/components/Search'; import { RECOMMENDED_TAGS } from '@/constants'; import { useDebounce } from '@/hooks/common'; import { useSearch } from '@/hooks/search'; @@ -34,7 +21,6 @@ export const SearchPage = () => { handleSearchForm, handleSearchByClick, handleAutocompleteClose, - isTagSearch, handleTagSearch, } = useSearch(); const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery || ''); @@ -62,13 +48,7 @@ export const SearchPage = () => {
      - + {!isSubmitted && debouncedSearchQuery && isAutocompleteOpen && ( @@ -85,42 +65,27 @@ export const SearchPage = () => {
      {isSubmitted && searchQuery ? ( <> - {isTagSearch ? ( -
      - - 태그 '{searchQuery}'가 포함된 상품 - - - }> - - - -
      - ) : ( - <> -
      - - 상품 바로가기 - - - }> - - - -
      -
      -
      - - 꿀!조합 바로가기 - - - }> - - - -
      - - )} +
      + + 상품 바로가기 + + + }> + + + +
      +
      +
      + + 꿀!조합 바로가기 + + + }> + + + +
      ) : (
      diff --git a/src/pages/SearchPage/TagSearchResultPage.tsx b/src/pages/SearchPage/TagSearchResultPage.tsx new file mode 100644 index 000000000..52bd54d2e --- /dev/null +++ b/src/pages/SearchPage/TagSearchResultPage.tsx @@ -0,0 +1,35 @@ +import { Suspense } from 'react'; + +import { searchResultTitle, searchSection } from './searchPage.css'; + +import { ErrorBoundary, ErrorComponent, Loading, Text } from '@/components/Common'; +import { SearchInput, TagSearchResultList } from '@/components/Search'; +import { useSearch } from '@/hooks/search'; + +export const TagSearchResultPage = () => { + const { inputRef, searchQuery, isSubmitted, handleSearchQuery, handleSearchForm } = useSearch(); + + return ( +
      +
      + + +
      + + '{searchQuery}'가 포함된 상품 + + + }> + + + +
      +
      + ); +}; diff --git a/src/router/index.tsx b/src/router/index.tsx index f8c4e0261..4d037e417 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -158,6 +158,15 @@ const router = createBrowserRouter([ return { Component: SearchPage }; }, }, + { + path: `${PATH.SEARCH}/tags`, + async lazy() { + const { TagSearchResultPage } = await import( + /* webpackChunkName: "TagSearchResultPage" */ '@/pages/SearchPage/TagSearchResultPage' + ); + return { Component: TagSearchResultPage }; + }, + }, { path: `${PATH.SEARCH}/products`, async lazy() { From d0c4582edfddf1e58f1f8873f7e17556af417e2a Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 7 Apr 2024 14:43:37 +0200 Subject: [PATCH 16/22] =?UTF-8?q?feat:=20=EB=A7=90=ED=92=8D=EC=84=A0=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EC=BD=98=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .storybook/preview-body.html | 51 +++++++++++++++++++++--- src/components/Common/Svg/SvgSprite.tsx | 53 ++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/.storybook/preview-body.html b/.storybook/preview-body.html index 0e19cf713..36bc180e2 100644 --- a/.storybook/preview-body.html +++ b/.storybook/preview-body.html @@ -110,16 +110,55 @@ - - + + + + + + + + - - - + + diff --git a/src/components/Common/Svg/SvgSprite.tsx b/src/components/Common/Svg/SvgSprite.tsx index 24d11dfd1..db4472aeb 100644 --- a/src/components/Common/Svg/SvgSprite.tsx +++ b/src/components/Common/Svg/SvgSprite.tsx @@ -106,14 +106,55 @@ const SvgSprite = () => { - - - - + + + + + + + + + - - + + From fbdb3aad706828b7fe425f78dd7c419925b67cf9 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Sun, 7 Apr 2024 14:43:58 +0200 Subject: [PATCH 17/22] =?UTF-8?q?feat:=20ProductItem=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Product/ProductItem/ProductItem.tsx | 5 +++-- .../TagSearchResultList/TagSearchResultList.stories.tsx | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Product/ProductItem/ProductItem.tsx b/src/components/Product/ProductItem/ProductItem.tsx index 526b91980..ebcec3389 100644 --- a/src/components/Product/ProductItem/ProductItem.tsx +++ b/src/components/Product/ProductItem/ProductItem.tsx @@ -12,6 +12,7 @@ import { } from './productItem.css'; import { SvgIcon } from '@/components/Common'; +import { vars } from '@/styles/theme.css'; import type { Product } from '@/types/product'; interface ProductItemProps { @@ -44,13 +45,13 @@ const ProductItem = ({ product }: ProductItemProps) => {
      - + {averageRating.toFixed(1)}
      - + {reviewCount} diff --git a/src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx b/src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx index 1199821e3..dcf1385fe 100644 --- a/src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx +++ b/src/components/Search/TagSearchResultList/TagSearchResultList.stories.tsx @@ -10,4 +10,8 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = {}; +export const Default: Story = { + args: { + searchQuery: '꼬북칩', + }, +}; From ca2d24f473f4d9d6385b3c2b9bc732d52e3e298d Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Mon, 8 Apr 2024 21:06:38 +0200 Subject: [PATCH 18/22] =?UTF-8?q?feat:=20ProductSearchResultList=20->=20Pr?= =?UTF-8?q?oductSearchResultPreviewList=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductSearchResultList.stories.tsx | 16 ---------------- .../ProductSearchResultPreviewList.stories.tsx | 16 ++++++++++++++++ ...st.tsx => ProductSearchResultPreviewList.tsx} | 16 +++++++++++++--- src/components/Search/index.ts | 2 +- src/pages/SearchPage/SearchPage.tsx | 9 +++++++-- 5 files changed, 37 insertions(+), 22 deletions(-) delete mode 100644 src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx create mode 100644 src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.stories.tsx rename src/components/Search/ProductSearchResultList/{ProductSearchResultList.tsx => ProductSearchResultPreviewList.tsx} (62%) diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx deleted file mode 100644 index 58dc91809..000000000 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.stories.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import ProductSearchResultList from './ProductSearchResultList'; - -const meta: Meta = { - title: 'search/ProductSearchResultList', - component: ProductSearchResultList, - args: { - searchQuery: '꼬북칩', - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.stories.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.stories.tsx new file mode 100644 index 000000000..903bdc224 --- /dev/null +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.stories.tsx @@ -0,0 +1,16 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductSearchResultPreviewList from './ProductSearchResultPreviewList'; + +const meta: Meta = { + title: 'search/ProductSearchResultPreviewList', + component: ProductSearchResultPreviewList, + args: { + searchQuery: '꼬북칩', + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx similarity index 62% rename from src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx rename to src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx index 7707d5554..4707b9564 100644 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx @@ -1,17 +1,22 @@ import { useRef } from 'react'; +import { Link } from 'react-router-dom'; +import { showMoreLink } from './productSearchResultPreivewList.css'; import SearchNotFound from '../SearchNotFound/SearchNotFound'; +import { Text } from '@/components/Common'; import { ProductOverviewList } from '@/components/Product'; +import { PATH } from '@/constants/path'; import { useIntersectionObserver } from '@/hooks/common'; import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; import displaySlice from '@/utils/displaySlice'; -interface ProductSearchResultListProps { + +interface ProductSearchResultPreviewListProps { searchQuery: string; } -const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) => { +const ProductSearchResultPreviewList = ({ searchQuery }: ProductSearchResultPreviewListProps) => { const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery); const scrollRef = useRef(null); useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); @@ -30,9 +35,14 @@ const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) return ( <> + + + 더보기 + +
      ); }; -export default ProductSearchResultList; +export default ProductSearchResultPreviewList; diff --git a/src/components/Search/index.ts b/src/components/Search/index.ts index 9ec924cc6..2c312d80e 100644 --- a/src/components/Search/index.ts +++ b/src/components/Search/index.ts @@ -1,4 +1,4 @@ -export { default as ProductSearchResultList } from './ProductSearchResultList/ProductSearchResultList'; +export { default as ProductSearchResultPreviewList } from './ProductSearchResultList/ProductSearchResultPreviewList'; export { default as RecommendList } from './RecommendList/RecommendList'; export { default as RecipeSearchResultList } from './RecipeSearchResultList/RecipeSearchResultList'; export { default as TagSearchResultList } from './TagSearchResultList/TagSearchResultList'; diff --git a/src/pages/SearchPage/SearchPage.tsx b/src/pages/SearchPage/SearchPage.tsx index 254a83ba2..9f8562f7f 100644 --- a/src/pages/SearchPage/SearchPage.tsx +++ b/src/pages/SearchPage/SearchPage.tsx @@ -4,7 +4,12 @@ import { Suspense, useEffect, useState } from 'react'; import { badgeContainer, searchWrapper, searchResultTitle, searchSection, subTitle } from './searchPage.css'; import { Text, Badge, ErrorBoundary, ErrorComponent, Loading, PageHeader } from '@/components/Common'; -import { ProductSearchResultList, RecipeSearchResultList, RecommendList, SearchInput } from '@/components/Search'; +import { + ProductSearchResultPreviewList, + RecipeSearchResultList, + RecommendList, + SearchInput, +} from '@/components/Search'; import { RECOMMENDED_TAGS } from '@/constants'; import { useDebounce } from '@/hooks/common'; import { useSearch } from '@/hooks/search'; @@ -71,7 +76,7 @@ export const SearchPage = () => { }> - +
      From e40319b092f984c1b53173b35b2ec840f9e52fb0 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Mon, 8 Apr 2024 21:06:51 +0200 Subject: [PATCH 19/22] =?UTF-8?q?feat:=20=EB=B2=84=ED=8A=BC=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../productSearchResultPreivewList.css.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/components/Search/ProductSearchResultList/productSearchResultPreivewList.css.ts diff --git a/src/components/Search/ProductSearchResultList/productSearchResultPreivewList.css.ts b/src/components/Search/ProductSearchResultList/productSearchResultPreivewList.css.ts new file mode 100644 index 000000000..7ad896228 --- /dev/null +++ b/src/components/Search/ProductSearchResultList/productSearchResultPreivewList.css.ts @@ -0,0 +1,15 @@ +import { vars } from '@/styles/theme.css'; +import { style } from '@vanilla-extract/css'; + +export const showMoreLink = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + height: 44, + padding: '12px 0', + margin: '20px 0', + border: `1px solid ${vars.colors.border.default}`, + fontSize: 14, + borderRadius: 6, +}); From 5a0896969d18e47e3422cf80cb2ea69b5185a51b Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Mon, 8 Apr 2024 21:13:12 +0200 Subject: [PATCH 20/22] =?UTF-8?q?feat:=20ProductOverviewList=EB=A1=9C=20?= =?UTF-8?q?=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductOverviewList.tsx | 9 ++++++++- .../ProductSearchListPage.tsx | 20 +++++-------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/components/Product/ProductOverviewList/ProductOverviewList.tsx b/src/components/Product/ProductOverviewList/ProductOverviewList.tsx index d18ba0cd3..66d615cb7 100644 --- a/src/components/Product/ProductOverviewList/ProductOverviewList.tsx +++ b/src/components/Product/ProductOverviewList/ProductOverviewList.tsx @@ -8,9 +8,10 @@ import type { Product } from '@/types/product'; interface ProductOverviewListProps { products: Product[]; + isSearchPage?: boolean; } -const ProductOverviewList = ({ products }: ProductOverviewListProps) => { +const ProductOverviewList = ({ products, isSearchPage = false }: ProductOverviewListProps) => { return (
        {products.map(({ id, image, name, price, averageRating }) => ( @@ -18,6 +19,12 @@ const ProductOverviewList = ({ products }: ProductOverviewListProps) => { + {isSearchPage && ( + <> +
        +
        + + )} ))}
      diff --git a/src/pages/ProductSearchListPage/ProductSearchListPage.tsx b/src/pages/ProductSearchListPage/ProductSearchListPage.tsx index 768b5c310..9fe4b9f2e 100644 --- a/src/pages/ProductSearchListPage/ProductSearchListPage.tsx +++ b/src/pages/ProductSearchListPage/ProductSearchListPage.tsx @@ -1,11 +1,10 @@ import { useRef } from 'react'; -import { Link, useSearchParams } from 'react-router-dom'; +import { useSearchParams } from 'react-router-dom'; import { container } from './productSearchListPage.css'; import { PageHeader } from '@/components/Common'; -import { ProductOverviewItem } from '@/components/Product'; -import { PATH } from '@/constants/path'; +import { ProductOverviewList } from '@/components/Product'; import { useIntersectionObserver } from '@/hooks/common'; import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; @@ -22,18 +21,9 @@ export const ProductSearchListPage = () => { return ( <> -
        - {products.map(({ id, categoryType, image, name, price, averageRating }) => ( -
      • - - - -
        -
        -
        -
      • - ))} -
      +
      + +
      ); From e42355208e0559701df69a1c76bac8585fe1a3b8 Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Tue, 9 Apr 2024 20:47:25 +0200 Subject: [PATCH 21/22] =?UTF-8?q?fix:=20=EC=83=81=ED=92=88=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Search/TagSearchResultList/TagSearchResultList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/TagSearchResultList/TagSearchResultList.tsx b/src/components/Search/TagSearchResultList/TagSearchResultList.tsx index 907338a9b..c10206da4 100644 --- a/src/components/Search/TagSearchResultList/TagSearchResultList.tsx +++ b/src/components/Search/TagSearchResultList/TagSearchResultList.tsx @@ -34,7 +34,7 @@ const TagSearchResultList = ({ searchQuery }: TagSearchResultListProps) => {
        {products.map((product) => (
      • - +
      • From 12546c725f58afedbe757869356dfef7c465d26d Mon Sep 17 00:00:00 2001 From: TaeeunKim Date: Thu, 11 Apr 2024 16:23:41 +0200 Subject: [PATCH 22/22] =?UTF-8?q?refactor:=20=EC=9D=B8=EC=9E=90=EB=A1=9C?= =?UTF-8?q?=20endpoint=EB=A5=BC=20=EB=84=98=EA=B2=A8=EC=A3=BC=EA=B2=8C?= =?UTF-8?q?=EB=81=94=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductSearchResultPreviewList.tsx | 7 +++++-- .../Search/TagSearchResultList/TagSearchResultList.tsx | 6 +++++- .../search/useInfiniteProductSearchResultsQuery.ts | 8 ++++---- src/pages/ProductSearchListPage/ProductSearchListPage.tsx | 6 +++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx index 4707b9564..2dcc4c185 100644 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx @@ -11,13 +11,16 @@ import { useIntersectionObserver } from '@/hooks/common'; import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; import displaySlice from '@/utils/displaySlice'; - interface ProductSearchResultPreviewListProps { searchQuery: string; } const ProductSearchResultPreviewList = ({ searchQuery }: ProductSearchResultPreviewListProps) => { - const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery); + const { + data: searchResponse, + fetchNextPage, + hasNextPage, + } = useInfiniteProductSearchResultsQuery(searchQuery, 'products'); const scrollRef = useRef(null); useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); diff --git a/src/components/Search/TagSearchResultList/TagSearchResultList.tsx b/src/components/Search/TagSearchResultList/TagSearchResultList.tsx index c10206da4..37321011b 100644 --- a/src/components/Search/TagSearchResultList/TagSearchResultList.tsx +++ b/src/components/Search/TagSearchResultList/TagSearchResultList.tsx @@ -14,7 +14,11 @@ interface TagSearchResultListProps { } const TagSearchResultList = ({ searchQuery }: TagSearchResultListProps) => { - const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery, true); + const { + data: searchResponse, + fetchNextPage, + hasNextPage, + } = useInfiniteProductSearchResultsQuery(searchQuery, 'tags'); const scrollRef = useRef(null); useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); diff --git a/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts b/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts index 781f5131e..c7ae78f67 100644 --- a/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts +++ b/src/hooks/queries/search/useInfiniteProductSearchResultsQuery.ts @@ -3,7 +3,9 @@ import { useSuspendedInfiniteQuery } from '..'; import { searchApi } from '@/apis'; import type { ProductSearchResultResponse } from '@/types/response'; -const fetchProductSearchResults = async (query: string, endpoint: 'tags' | 'products', pageParam: number) => { +type SearchResultEndPoint = 'tags' | 'products'; + +const fetchProductSearchResults = async (query: string, endpoint: SearchResultEndPoint, pageParam: number) => { const response = await searchApi.get({ params: `/${endpoint}/results`, queries: `?query=${query}&lastProductId=${pageParam}`, @@ -13,9 +15,7 @@ const fetchProductSearchResults = async (query: string, endpoint: 'tags' | 'prod return data; }; -const useInfiniteProductSearchResultsQuery = (query: string, isTagSearch = false) => { - const endpoint = isTagSearch ? 'tags' : 'products'; - +const useInfiniteProductSearchResultsQuery = (query: string, endpoint: SearchResultEndPoint) => { return useSuspendedInfiniteQuery( ['search', endpoint, 'results', query], ({ pageParam = 0 }) => fetchProductSearchResults(query, endpoint, pageParam), diff --git a/src/pages/ProductSearchListPage/ProductSearchListPage.tsx b/src/pages/ProductSearchListPage/ProductSearchListPage.tsx index 9fe4b9f2e..7db784e5d 100644 --- a/src/pages/ProductSearchListPage/ProductSearchListPage.tsx +++ b/src/pages/ProductSearchListPage/ProductSearchListPage.tsx @@ -12,7 +12,11 @@ export const ProductSearchListPage = () => { const [searchParams, setSearchParams] = useSearchParams(); const searchQuery = searchParams.get('query') || ''; - const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery); + const { + data: searchResponse, + fetchNextPage, + hasNextPage, + } = useInfiniteProductSearchResultsQuery(searchQuery, 'products'); const scrollRef = useRef(null); useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage);