mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
✨ feat: product filter provider
:%s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { memo, useEffect, useRef, useState } from 'react'
|
||||
import { useProductFilter } from 'src/components/contexts'
|
||||
import { useModalCommon } from 'src/components/hooks'
|
||||
import ModalAuthenticate from '../ModalAuthenticate/ModalAuthenticate'
|
||||
import ModalCreateUserInfo from '../ModalCreateUserInfo/ModalCreateUserInfo'
|
||||
@@ -9,11 +10,12 @@ import HeaderSubMenu from './components/HeaderSubMenu/HeaderSubMenu'
|
||||
import HeaderSubMenuMobile from './components/HeaderSubMenuMobile/HeaderSubMenuMobile'
|
||||
import s from './Header.module.scss'
|
||||
interface props {
|
||||
toggleFilter: () => void,
|
||||
|
||||
}
|
||||
|
||||
const Header = memo(({ toggleFilter }: props) => {
|
||||
const Header = memo(({ }: props) => {
|
||||
const headeFullRef = useRef<HTMLDivElement>(null)
|
||||
const { toggleProductFilter: toggleFilter } = useProductFilter()
|
||||
const [isFullHeader, setIsFullHeader] = useState<boolean>(true)
|
||||
const [isModeAuthenRegister, setIsModeAuthenRegister] = useState<boolean>(false)
|
||||
const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false })
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { CommerceProvider } from '@framework'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC } from 'react'
|
||||
import { CartDrawerProvider, MessageProvider } from 'src/components/contexts'
|
||||
import { CartDrawerProvider, MessageProvider, ProductFilterProvider } from 'src/components/contexts'
|
||||
import LayoutContent from './LayoutContent/LayoutContent'
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -13,9 +13,11 @@ const Layout: FC<Props> = ({ children }) => {
|
||||
return (
|
||||
<CommerceProvider locale={locale}>
|
||||
<CartDrawerProvider>
|
||||
<ProductFilterProvider>
|
||||
<MessageProvider>
|
||||
<LayoutContent>{children}</LayoutContent>
|
||||
</MessageProvider>
|
||||
</ProductFilterProvider>
|
||||
</CartDrawerProvider>
|
||||
</CommerceProvider>
|
||||
)
|
||||
|
@@ -15,21 +15,12 @@ interface Props {
|
||||
|
||||
const LayoutContent: FC<Props> = ({ children }) => {
|
||||
const router = useRouter()
|
||||
const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: false })
|
||||
const {messages, removeMessage} = useMessage()
|
||||
|
||||
const toggleFilter = () => {
|
||||
if (visibleFilter) {
|
||||
closeFilter()
|
||||
} else {
|
||||
openFilter()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={s.mainLayout}>
|
||||
<Header toggleFilter={toggleFilter}/>
|
||||
<Header/>
|
||||
{
|
||||
router.pathname === ROUTE.ACCOUNT ?
|
||||
<section className={s.wrapperWithBg}>
|
||||
@@ -39,7 +30,7 @@ const LayoutContent: FC<Props> = ({ children }) => {
|
||||
}
|
||||
<ScrollToTop visibilityHeight={1500} />
|
||||
{
|
||||
FILTER_PAGE.includes(router.pathname) && (<div className={s.filter}><MenuNavigationProductList visible={visibleFilter} onClose={closeFilter} /> </div>)
|
||||
FILTER_PAGE.includes(router.pathname) && (<div className={s.filter}><MenuNavigationProductList /> </div>)
|
||||
}
|
||||
<Footer />
|
||||
</div>
|
||||
|
@@ -3,6 +3,7 @@ import classNames from 'classnames';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ButtonCommon } from 'src/components/common';
|
||||
import { useProductFilter } from 'src/components/contexts';
|
||||
import { useGetAllCollection } from 'src/components/hooks/collection';
|
||||
import { useFacets } from 'src/components/hooks/facets';
|
||||
import IconHide from 'src/components/icons/IconHide';
|
||||
@@ -15,8 +16,7 @@ import s from './MenuNavigationProductList.module.scss';
|
||||
import MenuSort from './MenuSort/MenuSort';
|
||||
|
||||
interface Props {
|
||||
visible: boolean,
|
||||
onClose: () => void
|
||||
|
||||
}
|
||||
|
||||
const FACET_QUERY = {
|
||||
@@ -32,8 +32,9 @@ const FACET_QUERY = {
|
||||
}
|
||||
} as QueryFacetsArgs
|
||||
|
||||
const MenuNavigationProductList = ({ visible, onClose }: Props) => {
|
||||
const MenuNavigationProductList = ({}: Props) => {
|
||||
const router = useRouter()
|
||||
const { productFilterVisible: visible, closeProductFilter: onClose } = useProductFilter()
|
||||
const { facets, loading: facetsLoading } = useFacets(FACET_QUERY)
|
||||
const { collections, loading: collectionLoading } = useGetAllCollection()
|
||||
const [brandQuery, setBrandQuery] = useState<string[]>([])
|
||||
|
@@ -1,43 +0,0 @@
|
||||
import { createContext, ReactNode, useContext, useState } from "react";
|
||||
import { filterContextType } from "src/utils/types.utils";
|
||||
|
||||
const contextDefaultValues: filterContextType = {
|
||||
visible: false,
|
||||
open: () => {},
|
||||
close: () => {},
|
||||
};
|
||||
|
||||
const FilterContext = createContext<filterContextType>(contextDefaultValues);
|
||||
|
||||
export function useAuth() {
|
||||
return useContext(FilterContext);
|
||||
}
|
||||
|
||||
type FilterProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function FilterProvider({ children }: FilterProviderProps) {
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
|
||||
const open = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const value = {
|
||||
visible,
|
||||
open,
|
||||
close,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<FilterContext.Provider value={value}>
|
||||
{children}
|
||||
</FilterContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
export type ProductFilterContextType = {
|
||||
productFilterVisible: boolean;
|
||||
toggleProductFilter: (visible?: boolean) => void;
|
||||
openProductFilter: () => void;
|
||||
closeProductFilter: () => void;
|
||||
};
|
||||
const DEFAULT_VALUE: ProductFilterContextType = {
|
||||
productFilterVisible: false,
|
||||
toggleProductFilter: () => { },
|
||||
openProductFilter: () => { },
|
||||
closeProductFilter: () => { },
|
||||
};
|
||||
|
||||
export const ProductFilterContext = createContext<ProductFilterContextType>(DEFAULT_VALUE)
|
||||
|
||||
export function useProductFilter() {
|
||||
return useContext(ProductFilterContext);
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
import { ReactNode, useState } from "react";
|
||||
import { ProductFilterContext } from "./ProductFilterContext";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function ProductFilterProvider({ children }: Props) {
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
|
||||
const closeProductFilter = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const openProductFilter = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const toggleProductFilter = () => {
|
||||
setVisible(!visible);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProductFilterContext.Provider value={{productFilterVisible: visible, closeProductFilter, openProductFilter, toggleProductFilter}}>
|
||||
{children}
|
||||
</ProductFilterContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
@@ -3,3 +3,6 @@ export * from './CartDrawer/CartDrawerProvider'
|
||||
|
||||
export * from './Message/MessageContext'
|
||||
export * from './Message/MessageProvider'
|
||||
|
||||
export * from './ProductFilter/ProductFilterContext'
|
||||
export * from './ProductFilter/ProductFilterProvider'
|
||||
|
@@ -16,29 +16,34 @@
|
||||
.list{
|
||||
@apply w-full;
|
||||
.top {
|
||||
.left {
|
||||
@apply flex justify-between items-center;
|
||||
}
|
||||
|
||||
.iconFilter {
|
||||
@apply relative;
|
||||
&:focus {
|
||||
outline: none;
|
||||
filter: brightness(1.05);
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--text-active);
|
||||
}
|
||||
.dot {
|
||||
@apply absolute;
|
||||
top: -0.08rem;
|
||||
right: -0.2rem;
|
||||
background-color: var(--negative);
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
border-radius: 1.2rem;
|
||||
}
|
||||
}
|
||||
@screen md {
|
||||
@apply flex justify-between flex-wrap w-full;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
}
|
||||
.inner{
|
||||
@screen md {
|
||||
@apply flex flex-col items-center justify-center;
|
||||
}
|
||||
.boxItem {
|
||||
@screen md {
|
||||
@apply flex justify-between flex-wrap;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.item {
|
||||
@screen md {
|
||||
width: calc(97% / 2);
|
||||
margin-top:1rem;
|
||||
}
|
||||
@screen lg{
|
||||
width: calc(97% / 3);
|
||||
margin-top:1rem;
|
||||
}
|
||||
.iconFilter {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,9 @@ import { useRouter } from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { HeadingCommon, ListProductCardSkeleton, ProductList } from 'src/components/common'
|
||||
import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbCommon'
|
||||
import SkeletonImage from 'src/components/common/SkeletonCommon/SkeletonImage/SkeletonImage'
|
||||
import { useProductFilter } from 'src/components/contexts'
|
||||
import { useSearchProducts } from 'src/components/hooks/product'
|
||||
import { IconFilter } from 'src/components/icons'
|
||||
import { DEFAULT_PAGE_SIZE, QUERY_KEY, QUERY_SPLIT_SEPERATOR, ROUTE } from 'src/utils/constanst.utils'
|
||||
import { getFacetIdsFromCodes, getPageFromQuery, getProductSortParamFromQuery } from 'src/utils/funtion.utils'
|
||||
import s from './ProductListFilter.module.scss'
|
||||
@@ -34,6 +35,7 @@ const DEFAULT_SEARCH_ARGS = {
|
||||
|
||||
const ProductListFilter = ({ facets, collections, products, total }: ProductListFilterProps) => {
|
||||
const router = useRouter()
|
||||
const { openProductFilter } = useProductFilter()
|
||||
const [initialQueryFlag, setInitialQueryFlag] = useState<boolean>(true)
|
||||
const [optionQueryProduct, setOptionQueryProduct] = useState<QuerySearchArgs>({ input: DEFAULT_SEARCH_ARGS })
|
||||
const { products: productSearchResult, totalItems, loading } = useSearchProducts(optionQueryProduct)
|
||||
@@ -101,14 +103,20 @@ const ProductListFilter = ({ facets, collections, products, total }: ProductList
|
||||
<ProductsMenuNavigationTablet facets={facets} collections={collections} />
|
||||
<div className={s.list}>
|
||||
<div className={s.top}>
|
||||
<div className={s.left}>
|
||||
<HeadingCommon align="left">SPECIAL RECIPES</HeadingCommon>
|
||||
<button className={s.iconFilter} onClick={openProductFilter}>
|
||||
<IconFilter />
|
||||
<div className={s.dot}></div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={s.boxSelect}>
|
||||
<ProductSort />
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
(!initialQueryFlag && loading && !productSearchResult) && <ListProductCardSkeleton count={DEFAULT_PAGE_SIZE} isWrap/>
|
||||
(!initialQueryFlag && loading && !productSearchResult) && <ListProductCardSkeleton count={DEFAULT_PAGE_SIZE} isWrap />
|
||||
}
|
||||
<ProductList data={initialQueryFlag ? products : (productSearchResult || [])} total={totalItems !== undefined ? totalItems : total} onPageChange={onPageChange} defaultCurrentPage={currentPage} />
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user