diff --git a/.storybook/preview-body.html b/.storybook/preview-body.html index ec34bccc0..23699d190 100644 --- a/.storybook/preview-body.html +++ b/.storybook/preview-body.html @@ -110,16 +110,55 @@ - - + + + + + + + + - - - + + diff --git a/src/assets/search-notfound.png b/src/assets/search-notfound.png new file mode 100644 index 000000000..a8bb7e024 Binary files /dev/null and b/src/assets/search-notfound.png differ diff --git a/src/components/Common/Svg/SvgSprite.tsx b/src/components/Common/Svg/SvgSprite.tsx index af9c52d72..922b6ec46 100644 --- a/src/components/Common/Svg/SvgSprite.tsx +++ b/src/components/Common/Svg/SvgSprite.tsx @@ -106,14 +106,55 @@ const SvgSprite = () => { - - - - + + + + + + + + + - - + + diff --git a/src/components/Common/Text/Text.tsx b/src/components/Common/Text/Text.tsx index 2297bfee4..e2ff5229d 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'; @@ -9,12 +11,13 @@ const Text = ({ weight = 'regular', color = 'default', as, + className, ...props }: TextProps) => { const Component = as || 'p'; return ( - + {children} ); 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/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/components/Product/ProductOverviewList/productOverviewList.css.ts b/src/components/Product/ProductOverviewList/productOverviewList.css.ts index d48c09e7b..1d89fd9d0 100644 --- a/src/components/Product/ProductOverviewList/productOverviewList.css.ts +++ b/src/components/Product/ProductOverviewList/productOverviewList.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, +}); diff --git a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx deleted file mode 100644 index f796afe23..000000000 --- a/src/components/Search/ProductSearchResultList/ProductSearchResultList.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useRef } from 'react'; - -import { ProductOverviewList } from '@/components/Product'; -import { useIntersectionObserver } from '@/hooks/common'; -import { useInfiniteProductSearchResultsQuery } from '@/hooks/queries/search'; - -interface ProductSearchResultListProps { - searchQuery: string; -} - -const ProductSearchResultList = ({ searchQuery }: ProductSearchResultListProps) => { - const { data: searchResponse, fetchNextPage, hasNextPage } = useInfiniteProductSearchResultsQuery(searchQuery); - 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 ( - <> - -
- - ); -}; - -export default ProductSearchResultList; 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/ProductSearchResultPreviewList.tsx b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx new file mode 100644 index 000000000..2dcc4c185 --- /dev/null +++ b/src/components/Search/ProductSearchResultList/ProductSearchResultPreviewList.tsx @@ -0,0 +1,51 @@ +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 ProductSearchResultPreviewListProps { + searchQuery: string; +} + +const ProductSearchResultPreviewList = ({ searchQuery }: ProductSearchResultPreviewListProps) => { + const { + data: searchResponse, + fetchNextPage, + hasNextPage, + } = useInfiniteProductSearchResultsQuery(searchQuery, 'products'); + const scrollRef = useRef(null); + useIntersectionObserver(fetchNextPage, scrollRef, hasNextPage); + + if (!searchResponse) { + return null; + } + + const products = searchResponse.pages.flatMap((page) => page.products); + const productToDisplay = displaySlice(true, products); + + if (products.length === 0) { + return ; + } + + return ( + <> + + + + 더보기 + + +
+ + ); +}; + +export default ProductSearchResultPreviewList; 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, +}); 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/SearchInput/SearchInput.tsx b/src/components/Search/SearchInput/SearchInput.tsx index 1a15dec38..092a878ce 100644 --- a/src/components/Search/SearchInput/SearchInput.tsx +++ b/src/components/Search/SearchInput/SearchInput.tsx @@ -1,16 +1,20 @@ 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 = false, ...props }: SearchInputProps, + ref: ForwardedRef + ) => { return (
    + {isTagSearch && isInputSubmitted && ( +
    + + {value} + + +
    + )} - {/* divider 컴포넌트 */} -
    -
    -

    꿀!조합 바로가기

    - - }> - - - -
    + <> +
    + + 상품 바로가기 + + + }> + + + +
    +
    +
    + + 꿀!조합 바로가기 + + + }> + + + +
    + ) : ( -
    -

    최근 검색어

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

    추천 태그

    + + 추천 태그 +
    {RECOMMENDED_TAGS.map(({ id, name }) => ( -