mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 12:24:18 +00:00
✨ feat: search bar
:%s
This commit is contained in:
@@ -7,10 +7,10 @@ import { Layout } from 'src/components/common';
|
||||
import { FeaturedProductsCarousel, FreshProducts, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home';
|
||||
import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice';
|
||||
import { FACET } from 'src/utils/constanst.utils';
|
||||
import { FilterOneVatiant, getFacetIdByName } from 'src/utils/funtion.utils';
|
||||
import { FilterOneVatiant, getFacetIdByCode, getFacetIdByName } from 'src/utils/funtion.utils';
|
||||
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED,COLLECTION_SLUG_SPICE } from 'src/utils/constanst.utils';
|
||||
import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils';
|
||||
import { PromiseWithKey } from 'src/utils/types.utils';
|
||||
import { CollectionsWithData, PromiseWithKey } from 'src/utils/types.utils';
|
||||
|
||||
interface Props {
|
||||
featuredAndDiscountFacetsValue: FacetValue[],
|
||||
@@ -19,18 +19,17 @@ interface Props {
|
||||
collections: Collection[]
|
||||
spiceProducts:ProductCard[]
|
||||
veggie: ProductCard[],
|
||||
|
||||
collectionProps:CollectionsWithData[]
|
||||
}
|
||||
export default function Home({ featuredAndDiscountFacetsValue, veggie,
|
||||
export default function Home({ featuredAndDiscountFacetsValue, veggie,collectionProps,
|
||||
freshProducts, featuredProducts,
|
||||
collections,spiceProducts }: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<HomeBanner />
|
||||
<HomeFeature />
|
||||
<HomeCategories />
|
||||
<HomeCollection data = {veggie}/>
|
||||
<HomeCollection data = {collectionProps}/>
|
||||
<FreshProducts data={freshProducts} collections={collections} />
|
||||
<HomeVideo />
|
||||
{spiceProducts.length>0 && <HomeSpice data={spiceProducts}/>}
|
||||
@@ -60,10 +59,26 @@ export async function getStaticProps({
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
|
||||
props.featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets)
|
||||
|
||||
|
||||
// collection
|
||||
const { collections } = await commerce.getAllCollections({
|
||||
variables: {},
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
props.collections= collections
|
||||
let collectionsPromisesWithKey = [] as PromiseWithKey[]
|
||||
collections.map((collection)=>{
|
||||
const promise = commerce.getAllProducts({
|
||||
variables: {collectionSlug:collection.slug},
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
collectionsPromisesWithKey.push({ key: `${collection.slug}`, promise: promise, keyResult: 'products' })
|
||||
})
|
||||
// fresh products
|
||||
const freshProductvariables: ProductVariables = {}
|
||||
const freshFacetId = getFreshFacetId(facets)
|
||||
@@ -83,7 +98,7 @@ export async function getStaticProps({
|
||||
const veggieProductvariables: ProductVariables = {
|
||||
groupByProduct:false
|
||||
}
|
||||
const veggieId = getFacetIdByName(facets,FACET.CATEGORY.PARENT_NAME,FACET.CATEGORY.VEGGIE)
|
||||
const veggieId = getFacetIdByCode(facets,FACET.CATEGORY.PARENT_CODE,FACET.CATEGORY.VEGGIE)
|
||||
if (veggieId) {
|
||||
veggieProductvariables.facetValueIds = [veggieId]
|
||||
}
|
||||
@@ -111,13 +126,7 @@ export async function getStaticProps({
|
||||
props.featuredProducts = []
|
||||
}
|
||||
|
||||
// collection
|
||||
const collectionsPromise = commerce.getAllCollections({
|
||||
variables: {},
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' })
|
||||
|
||||
|
||||
// spiceProducts
|
||||
const spiceProducts = commerce.getAllProducts({
|
||||
@@ -130,6 +139,17 @@ export async function getStaticProps({
|
||||
promisesWithKey.push({ key: 'spiceProducts', promise: spiceProducts, keyResult: 'products' })
|
||||
|
||||
try {
|
||||
const collectionPromises = getAllPromies(collectionsPromisesWithKey)
|
||||
const collectionResult = await Promise.all(collectionPromises)
|
||||
let collectionProps:CollectionsWithData[] = []
|
||||
collectionsPromisesWithKey.map((item, index) => {
|
||||
collectionProps.push({
|
||||
...collections[index],
|
||||
items:item.keyResult ? FilterOneVatiant(collectionResult[index][item.keyResult]) : collectionResult[index]
|
||||
})
|
||||
return null
|
||||
})
|
||||
props.collectionProps=collectionProps
|
||||
const promises = getAllPromies(promisesWithKey)
|
||||
const rs = await Promise.all(promises)
|
||||
|
||||
|
@@ -26,7 +26,7 @@ export default function Products({ facets, collections, productsResult }: Props)
|
||||
facets={facets}
|
||||
products={productsResult.products}
|
||||
total={productsResult.totalItems} />
|
||||
<ViewedProducts />
|
||||
<ViewedProducts data={[]}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ const Header = memo(({ }: props) => {
|
||||
const [isModeAuthenRegister, setIsModeAuthenRegister] = useState<boolean>(false)
|
||||
const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false })
|
||||
const { visible: visibleModalInfo, closeModal: closeModalInfo, openModal: openModalInfo } = useModalCommon({ initialValue: false })
|
||||
const [searchValue, setSearchValue] = useState<string|number>("")
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
@@ -56,7 +57,9 @@ const Header = memo(({ }: props) => {
|
||||
toggleFilter={toggleFilter}
|
||||
openModalLogin={openModalLogin}
|
||||
openModalRegister={openModalRegister}
|
||||
openModalInfo={openModalInfo} />
|
||||
openModalInfo={openModalInfo}
|
||||
setSearchValue={setSearchValue}
|
||||
searchValue={searchValue}/>
|
||||
</div>
|
||||
|
||||
<header ref={headeFullRef} className={classNames({ [s.header]: true, [s.full]: isFullHeader })}>
|
||||
@@ -68,6 +71,8 @@ const Header = memo(({ }: props) => {
|
||||
openModalLogin={openModalLogin}
|
||||
openModalRegister = {openModalRegister}
|
||||
openModalInfo={openModalInfo}
|
||||
setSearchValue={setSearchValue}
|
||||
searchValue={searchValue}
|
||||
/>
|
||||
<HeaderSubMenu />
|
||||
</div>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { memo, useMemo } from 'react'
|
||||
import { memo, useMemo, useState } from 'react'
|
||||
import { ButtonCommon } from 'src/components/common'
|
||||
import InputSearch from 'src/components/common/InputSearch/InputSearch'
|
||||
import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown'
|
||||
@@ -31,6 +31,8 @@ interface Props {
|
||||
openModalRegister: () => void
|
||||
openModalInfo: () => void
|
||||
toggleFilter: () => void
|
||||
searchValue:string|number
|
||||
setSearchValue: (value: string | number) => void
|
||||
}
|
||||
|
||||
const HeaderMenu = memo(
|
||||
@@ -41,11 +43,13 @@ const HeaderMenu = memo(
|
||||
openModalRegister,
|
||||
openModalInfo,
|
||||
toggleFilter,
|
||||
searchValue,
|
||||
setSearchValue
|
||||
}: Props) => {
|
||||
const router = useRouter()
|
||||
const { toggleCartDrawer } = useCartDrawer()
|
||||
const { customer } = useActiveCustomer()
|
||||
|
||||
|
||||
const { logout } = useLogout()
|
||||
|
||||
const optionMenuNotAuthen = useMemo(
|
||||
@@ -92,6 +96,15 @@ const HeaderMenu = memo(
|
||||
],
|
||||
[logout]
|
||||
)
|
||||
|
||||
const onEnter = () => {
|
||||
console.log("enter")
|
||||
router.push(`${ROUTE.PRODUCTS}?${QUERY_KEY.SEARCH}=${searchValue}`)
|
||||
}
|
||||
|
||||
const onChange = (value:string|number) => {
|
||||
setSearchValue(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<section
|
||||
@@ -121,10 +134,10 @@ const HeaderMenu = memo(
|
||||
</div>
|
||||
<div className={s.searchWrap}>
|
||||
<div className={s.inputSearch}>
|
||||
<InputSearch />
|
||||
<InputSearch onChange={onChange} onEnter={onEnter} value={searchValue}/>
|
||||
</div>
|
||||
<div className={s.buttonSearch}>
|
||||
<ButtonCommon>Search</ButtonCommon>
|
||||
<ButtonCommon onClick={onEnter}>Search</ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -6,11 +6,15 @@ import { Inputcommon } from '..';
|
||||
interface Props {
|
||||
onChange?: (value: string | number) => void,
|
||||
onEnter?: (value: string | number) => void,
|
||||
value: string | number
|
||||
}
|
||||
|
||||
const InputSearch = ({ onChange, onEnter }: Props) => {
|
||||
const InputSearch = ({ onChange, onEnter, value }: Props) => {
|
||||
|
||||
|
||||
return (
|
||||
<Inputcommon placeholder={LANGUAGE.PLACE_HOLDER.SEARCH}
|
||||
value={value}
|
||||
styleType='custom'
|
||||
icon={<IconSearch />}
|
||||
onChange={onChange}
|
||||
|
@@ -5,134 +5,26 @@ import image5 from '../../../../../public/assets/images/image5.png'
|
||||
import image6 from '../../../../../public/assets/images/image6.png'
|
||||
import image7 from '../../../../../public/assets/images/image7.png'
|
||||
import image8 from '../../../../../public/assets/images/image8.png'
|
||||
import { CollectionsWithData } from 'src/utils/types.utils'
|
||||
interface HomeCollectionProps {
|
||||
data: ProductCard[]
|
||||
data: CollectionsWithData[]
|
||||
}
|
||||
const dataTest = [
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
{
|
||||
name: 'Carrot',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image7.src,
|
||||
},
|
||||
{
|
||||
name: 'Salad',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image8.src,
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
{
|
||||
name: 'Carrot',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image7.src,
|
||||
},
|
||||
{
|
||||
name: 'Salad',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image8.src,
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
]
|
||||
|
||||
const HomeCollection = ({data}: HomeCollectionProps) => {
|
||||
const HomeCollection = ({ data }: HomeCollectionProps) => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<CollectionCarcousel
|
||||
data={data}
|
||||
itemKey="product-2"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={data}
|
||||
itemKey="product-3"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={data}
|
||||
itemKey="product-4"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={data}
|
||||
itemKey="product-5"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={data}
|
||||
itemKey="product-6"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
{data.map((collection) => {
|
||||
return collection.items.length > 0 ? (
|
||||
<CollectionCarcousel
|
||||
key={collection.slug}
|
||||
data={collection.items}
|
||||
itemKey={collection.id}
|
||||
title={collection.name}
|
||||
subtitle={collection.description}
|
||||
category={collection.slug}
|
||||
/>
|
||||
) : null
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@@ -66,7 +66,11 @@ const ProductListFilter = ({ facets, collections, products, total }: ProductList
|
||||
const page = getPageFromQuery(router.query[QUERY_KEY.PAGE] as string)
|
||||
query.input.skip = page * DEFAULT_PAGE_SIZE
|
||||
|
||||
|
||||
const searchQuery = router.query[QUERY_KEY.SEARCH] as string
|
||||
if (searchQuery) {
|
||||
query.input.term = searchQuery
|
||||
}
|
||||
|
||||
const sortQuery = router.query[QUERY_KEY.SORTBY] as string
|
||||
if (sortQuery) {
|
||||
query.input.sort = getProductSortParamFromQuery(sortQuery)
|
||||
|
@@ -57,6 +57,7 @@ export const QUERY_KEY = {
|
||||
SORTBY: 'sortby',
|
||||
RECIPES: 'recipes',
|
||||
PAGE: 'page',
|
||||
SEARCH:"search"
|
||||
}
|
||||
|
||||
export const PRODUCT_SORT_OPTION_VALUE = {
|
||||
@@ -131,8 +132,11 @@ export const FACET = {
|
||||
BEST_SELLERS: 'Best seller'
|
||||
},
|
||||
CATEGORY: {
|
||||
PARENT_NAME:"category",
|
||||
VEGGIE:"veggie"
|
||||
PARENT_CODE:"category",
|
||||
VEGGIE:"veggie",
|
||||
FROZEN:"frozen",
|
||||
SEAFOOD:"seafood",
|
||||
COFFEE_BEAN:"coffee-bean"
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -88,6 +88,13 @@ export function getFacetIdByName(facets: Facet[], facetName: string, valueName:s
|
||||
}
|
||||
|
||||
|
||||
export function getFacetIdByCode(facets: Facet[], parentCode: string, valueCode:string) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.code === parentCode)
|
||||
const freshFacetValue = featuredFacet?.values.find((item: FacetValue) => item.code === valueCode)
|
||||
return freshFacetValue?.id
|
||||
}
|
||||
|
||||
|
||||
export function getAllFeaturedFacetId(facets: Facet[]) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.name === FACET.FEATURE.PARENT_NAME)
|
||||
const rs = featuredFacet?.values.map((item: FacetValue) => item.id)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { ProductCard } from './../../framework/commerce/types/product';
|
||||
import { Collection } from './../../framework/commerce/types/collection';
|
||||
|
||||
export interface ProductProps {
|
||||
category?: string
|
||||
@@ -72,4 +74,7 @@ export type PromiseWithKey = {
|
||||
keyResult?: string,
|
||||
}
|
||||
|
||||
export type SelectedOptions = Record<string, string | null>
|
||||
export type SelectedOptions = Record<string, string | null>
|
||||
export interface CollectionsWithData extends Collection {
|
||||
items: ProductCard[]
|
||||
}
|
||||
|
Reference in New Issue
Block a user