From 2ccccc51deb5f990dd689401f04844b7f63d5315 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Tue, 5 Oct 2021 10:35:53 +0700 Subject: [PATCH 01/12] :recycle: enhan: not get fresh and featured product when don't have facet id :%s --- pages/index.tsx | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/pages/index.tsx b/pages/index.tsx index cac464a43..010094de4 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -62,27 +62,34 @@ export async function getStaticProps({ const freshFacetId = getFreshFacetId(facets) if (freshFacetId) { freshProductvariables.facetValueIds = [freshFacetId] + const freshProductsPromise = commerce.getAllProducts({ + variables: freshProductvariables, + config, + preview, + }) + promisesWithKey.push({ key: 'freshProducts', promise: freshProductsPromise, keyResult: 'products' }) + } else { + props.freshProducts = [] } - const freshProductsPromise = commerce.getAllProducts({ - variables: freshProductvariables, - config, - preview, - }) - promisesWithKey.push({ key: 'freshProducts', promise: freshProductsPromise, keyResult: 'products' }) // featured products const allFeaturedFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_FEATURED) const allDiscountFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_DISCOUNT) const facetValueIdsForFeaturedProducts = [...allFeaturedFacetIds, ...allDiscountFacetIds] - const featuredProductsPromise = commerce.getAllProducts({ - variables: { - facetValueIds: facetValueIdsForFeaturedProducts - }, - config, - preview, - }) - promisesWithKey.push({ key: 'featuredProducts', promise: featuredProductsPromise, keyResult: 'products' }) + + if (facetValueIdsForFeaturedProducts.length > 0) { + const featuredProductsPromise = commerce.getAllProducts({ + variables: { + facetValueIds: facetValueIdsForFeaturedProducts + }, + config, + preview, + }) + promisesWithKey.push({ key: 'featuredProducts', promise: featuredProductsPromise, keyResult: 'products' }) + } else { + props.featuredProducts = [] + } // collection const collectionsPromise = commerce.getAllCollections({ From c154541948a5a1d647b4f7bc4c638618049a331f Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Tue, 5 Oct 2021 18:10:22 +0700 Subject: [PATCH 02/12] :sparkles: feat: (product list) all products, brands, collection, featured :%s --- .../vendure/api/operations/get-all-facets.ts | 9 +- pages/products.tsx | 85 +++++++++++++++++-- .../common/MenuNavigation/MenuNavigation.tsx | 12 +-- .../common/ProductList/ProductList.tsx | 5 +- .../ProductListFilter/ProductListFilter.tsx | 32 ++++--- src/utils/constanst.utils.ts | 1 + src/utils/types.utils.ts | 19 +++-- 7 files changed, 128 insertions(+), 35 deletions(-) diff --git a/framework/vendure/api/operations/get-all-facets.ts b/framework/vendure/api/operations/get-all-facets.ts index c4b002744..0bde04090 100644 --- a/framework/vendure/api/operations/get-all-facets.ts +++ b/framework/vendure/api/operations/get-all-facets.ts @@ -1,10 +1,10 @@ import { OperationContext } from '@commerce/api/operations' import { Facet } from '@commerce/types/facet' import { Provider, VendureConfig } from '../' -import { GetAllFacetsQuery } from '../../schema' +import { FacetFilterParameter, FacetSortParameter, GetAllFacetsQuery } from '../../schema' import { getAllFacetsQuery } from '../../utils/queries/get-all-facets-query' -export type FacetVariables = { first?: number } +export type FacetVariables = { first?: number, filter?: FacetFilterParameter, sort?: FacetSortParameter } export default function getAllFacetsOperation({ commerce, @@ -27,9 +27,10 @@ export default function getAllFacetsOperation({ } = {}): Promise<{ facets: Facet[] | any[] }> { const config = commerce.getConfig(cfg) const variables = { - input: { + options: { take: vars.first, - groupByFacet: true, + filter: vars.filter, + sort: vars.sort, }, } const { data } = await config.fetch(query, { diff --git a/pages/products.tsx b/pages/products.tsx index 4f9c4eb66..93dda5cdb 100644 --- a/pages/products.tsx +++ b/pages/products.tsx @@ -1,19 +1,94 @@ +import { ProductCard } from '@commerce/types/product'; +import { Collection, Facet } from '@framework/schema'; +import commerce from '@lib/api/commerce'; +import { GetStaticPropsContext } from 'next'; import { Layout } from 'src/components/common'; import { ViewedProducts } from 'src/components/modules/product-detail'; import ProductListFilter from 'src/components/modules/product-list/ProductListFilter/ProductListFilter'; -import RecipeListBanner from 'src/components/modules/recipes-list/RecipeListBanner/RecipeListBanner'; -import RecipesList from 'src/components/modules/recipes-list/RecipesList/RecipesList'; +import { CODE_FACET_BRAND, CODE_FACET_FEATURED, DEFAULT_PAGE_SIZE } from 'src/utils/constanst.utils'; +import { getAllPromies } from 'src/utils/funtion.utils'; +import { PromiseWithKey, SortOrder } from 'src/utils/types.utils'; import ProductListBanner from '../src/components/modules/product-list/ProductListBanner/ProductListBanner'; +interface Props { + facets: Facet[], + collections: Collection[], + products: ProductCard[], -export default function Products() { +} + +export default function Products({ facets, collections, products }: Props) { + // console.log("facets: ", products) return ( <> - - + + ) } +export async function getStaticProps({ + preview, + locale, + locales, +}: GetStaticPropsContext) { + const config = { locale, locales } + let promisesWithKey = [] as PromiseWithKey[] + let props = {} as any + + const facetsPromise = commerce.getAllFacets({ + variables: { + sort: { + code: SortOrder.Asc + }, + filter: { + code: { + in: [CODE_FACET_FEATURED, CODE_FACET_BRAND] + } + } + }, + config, + preview, + }) + + promisesWithKey.push({ key: 'facets', promise: facetsPromise, keyResult: 'facets' }) + + // collection + const collectionsPromise = commerce.getAllCollections({ + variables: {}, + config, + preview, + }) + promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' }) + + // products + const productsPromise = commerce.getAllProducts({ + variables: { + first: DEFAULT_PAGE_SIZE, + }, + config, + preview, + }) + promisesWithKey.push({ key: 'products', promise: productsPromise, keyResult: 'products' }) + + + try { + const promises = getAllPromies(promisesWithKey) + const rs = await Promise.all(promises) + + promisesWithKey.map((item, index) => { + props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index] + return null + }) + + return { + props, + revalidate: 60, + } + } catch (err) { + + } +} + Products.Layout = Layout diff --git a/src/components/common/MenuNavigation/MenuNavigation.tsx b/src/components/common/MenuNavigation/MenuNavigation.tsx index 4a8943051..a2554451e 100644 --- a/src/components/common/MenuNavigation/MenuNavigation.tsx +++ b/src/components/common/MenuNavigation/MenuNavigation.tsx @@ -1,15 +1,17 @@ import classNames from 'classnames' import Link from 'next/link' import { useRouter } from 'next/router' +import { ROUTE } from 'src/utils/constanst.utils' import s from './MenuNavigation.module.scss' interface Props { children?: any, - heading:string, - categories:{name:string,link:string}[] + heading: string, + linkPrefix: string, + categories: { name: string, slug?: string, code?: string }[] } -const MenuNavigation = ({heading,categories}:Props)=> { +const MenuNavigation = ({ heading, linkPrefix, categories }: Props) => { const router = useRouter() return ( @@ -19,8 +21,8 @@ const MenuNavigation = ({heading,categories}:Props)=> { { categories.map(item =>
  • - - + + {item.name} diff --git a/src/components/common/ProductList/ProductList.tsx b/src/components/common/ProductList/ProductList.tsx index 7428e3a63..0c5deb91e 100644 --- a/src/components/common/ProductList/ProductList.tsx +++ b/src/components/common/ProductList/ProductList.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react' +import { DEFAULT_PAGE_SIZE } from 'src/utils/constanst.utils' import PaginationCommon from '../PaginationCommon/PaginationCommon' import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard' import s from "./ProductList.module.scss" @@ -15,13 +16,13 @@ const ProductList = ({data}: ProductListProps) => {
    { - data.slice(currentPage*20,(currentPage+1)*20).map((product,index)=>{ + data.slice(currentPage*DEFAULT_PAGE_SIZE,(currentPage+1)* DEFAULT_PAGE_SIZE).map((product,index)=>{ return }) }
    - +
    ) diff --git a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx index 5315283dc..736f3d239 100644 --- a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx +++ b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx @@ -1,12 +1,19 @@ +import { ProductCard } from '@commerce/types/product' +import { Collection, Facet } from '@framework/schema' import React from 'react' import { HeadingCommon, ProductList, SelectCommon } from 'src/components/common' import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbCommon' import MenuNavigation from 'src/components/common/MenuNavigation/MenuNavigation' -import { BRAND, CATEGORY, FEATURED} from 'src/utils/constanst.utils' -import { PRODUCT_DATA_TEST_PAGE } from 'src/utils/demo-data' +import { QUERY_KEY, ROUTE } from 'src/utils/constanst.utils' +import { PRODUCT_DATA_TEST_PAGE } from 'src/utils/demo-data' import s from './ProductListFilter.module.scss' -interface ProductListFilterProps {} +interface ProductListFilterProps { + facets: Facet[] + collections: Collection[] + products: ProductCard[] + +} const BREADCRUMB = [ { @@ -29,11 +36,7 @@ const OPTIONSLECT = [ }, ] -const onModalClose = () => { - -} - -const ProductListFilter = (props: ProductListFilterProps) => { +const ProductListFilter = ({facets, collections, products}: ProductListFilterProps) => { return (
    @@ -41,9 +44,14 @@ const ProductListFilter = (props: ProductListFilterProps) => {
    - - - + + { + facets.map(item => ) + }
    @@ -56,7 +64,7 @@ const ProductListFilter = (props: ProductListFilterProps) => {
    - + diff --git a/src/utils/constanst.utils.ts b/src/utils/constanst.utils.ts index 1db198178..432e9cc78 100644 --- a/src/utils/constanst.utils.ts +++ b/src/utils/constanst.utils.ts @@ -113,6 +113,7 @@ export const BRAND = [ export const CODE_FACET_FEATURED = 'featured' export const CODE_FACET_DISCOUNT = 'discount' +export const CODE_FACET_BRAND = 'brand' export const CODE_FACET_FEATURED_VARIANT = { FRESH: 'fresh', } diff --git a/src/utils/types.utils.ts b/src/utils/types.utils.ts index ca21b605f..59243b80f 100644 --- a/src/utils/types.utils.ts +++ b/src/utils/types.utils.ts @@ -34,18 +34,23 @@ export interface BlogProps { export interface CheckOutForm { name?: string - email?:string + email?: string address?: string - city?:string - state?:string - code?:number - phone?:number - method?:string - shipping_fee?:number + city?: string + state?: string + code?: number + phone?: number + method?: string + shipping_fee?: number } export type MouseAndTouchEvent = MouseEvent | TouchEvent +export enum SortOrder { + Asc = 'ASC', + Desc = 'DESC', +} + export type filterContextType = { visible: boolean; open: () => void; From ef7b490416b19aeace5baa00da68326f116da456 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 6 Oct 2021 11:14:10 +0700 Subject: [PATCH 03/12] :sparkles: feat: get collections, facets in menu filter :%s --- framework/vendure/schema.d.ts | 5 +- .../HeaderSubMenu/HeaderSubMenu.tsx | 2 +- .../Layout/LayoutContent/LayoutContent.tsx | 9 +- .../common/MenuFilter/MenuFilter.module.scss | 4 +- .../common/MenuFilter/MenuFilter.tsx | 36 +++---- .../MenuFilterItem/MenuFilterItem.module.scss | 16 +++ .../MenuFilterItem/MenuFilterItem.tsx | 28 +++++ .../MenuNavigationProductList.module.scss | 18 +--- .../MenuNavigationProductList.tsx | 100 +++++++++++------- .../MenuSort/MenuSort.module.scss | 7 +- .../hooks/collection/useGetAllCollection.tsx | 4 +- src/components/hooks/facets/useFacets.tsx | 2 +- 12 files changed, 136 insertions(+), 95 deletions(-) create mode 100644 src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss create mode 100644 src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts index ef667d9f3..5faa87b43 100644 --- a/framework/vendure/schema.d.ts +++ b/framework/vendure/schema.d.ts @@ -3228,8 +3228,9 @@ export type GetAllFacetsQuery = { __typename?: 'Query' } & { items: Array< { __typename?: 'Facet' } & Pick< Facet, - 'id' | 'name' | 'code' - > & { + 'id' | 'name' | 'code' | 'values' + > + & { parent?: Maybe<{ __typename?: 'Facet' } & Pick> children?: Maybe< Array<{ __typename?: 'Facet' } & Pick> diff --git a/src/components/common/Header/components/HeaderSubMenu/HeaderSubMenu.tsx b/src/components/common/Header/components/HeaderSubMenu/HeaderSubMenu.tsx index 2cd72e5f2..b119a1c8a 100644 --- a/src/components/common/Header/components/HeaderSubMenu/HeaderSubMenu.tsx +++ b/src/components/common/Header/components/HeaderSubMenu/HeaderSubMenu.tsx @@ -42,7 +42,7 @@ const HeaderSubMenu = memo(() => {
      {/* todo: handle active item */}
    • - Categories + Categories
    • { MENU.map(item =>
    • = ({ children }) => { - const { pathname } = useRouter() - const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: false }) const router = useRouter() + const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: true }) const {messages, removeMessage} = useMessage() const toggleFilter = () => { @@ -30,6 +29,7 @@ const LayoutContent: FC = ({ children }) => { return ( <>
      + {router.pathname}
      { router.pathname === ROUTE.ACCOUNT ? @@ -38,10 +38,9 @@ const LayoutContent: FC = ({ children }) => { :
      {children}
      } -
      { - FILTER_PAGE.includes(pathname) && (
      ) + FILTER_PAGE.includes(router.pathname) && (
      ) }
      diff --git a/src/components/common/MenuFilter/MenuFilter.module.scss b/src/components/common/MenuFilter/MenuFilter.module.scss index b8b6596ce..e887734a9 100644 --- a/src/components/common/MenuFilter/MenuFilter.module.scss +++ b/src/components/common/MenuFilter/MenuFilter.module.scss @@ -1,9 +1,7 @@ @import "../../../styles/utilities"; .menuFilterWrapper{ - @apply spacing-horizontal; - .menuFilterHeading{ - @apply sub-headline font-bold ; + @apply sub-headline font-bold; color: var(--text-active); font-feature-settings: 'salt' on; margin: 0.8rem 0; diff --git a/src/components/common/MenuFilter/MenuFilter.tsx b/src/components/common/MenuFilter/MenuFilter.tsx index 454942734..a0021da79 100644 --- a/src/components/common/MenuFilter/MenuFilter.tsx +++ b/src/components/common/MenuFilter/MenuFilter.tsx @@ -2,43 +2,31 @@ import classNames from 'classnames' import { useEffect, useState } from 'react'; import s from './MenuFilter.module.scss' +import MenuFilterItem from './MenuFilterItem/MenuFilterItem'; interface Props { children?: any, - heading?:string, - categories:{name:string,link:string}[], - type:string, + heading?: string, + categories: { name: string, slug?: string, code?: string }[], + type: string, onChangeValue?: (value: Object) => void } -const MenuFilter = ({heading,categories,type,onChangeValue}:Props)=> { - const [active, setActive] = useState(''); +const MenuFilter = ({ heading, categories, type, onChangeValue }: Props) => { + function handleClick(value: string) { - function handleClick(link:string){ - setActive(link); - - if(active === link){ - setActive(''); - } } - useEffect(()=>{ - - let href = active?.split("="); - const linkValue = href[1]; - - onChangeValue && onChangeValue({[type]:linkValue}); - },[active]) - return (

      {heading}

        { - categories.map(item =>
      • -
        handleClick(item.link)} className={classNames({ [s.active]: item.link === active? true: false })}> - {item.name} -
        -
      • ) + categories.map(item => ) }
      diff --git a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss new file mode 100644 index 000000000..f70a224f2 --- /dev/null +++ b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss @@ -0,0 +1,16 @@ + +.menuFilterItem { + margin: 1rem 0; + padding: 0; + div { + padding: 0.8rem 1.6rem; + margin-right: 0.8rem; + background-color: var(--gray); + border-radius: 0.8rem; + cursor: pointer; + &.active { + color: var(--white); + background-color: var(--primary); + } + } +} diff --git a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx new file mode 100644 index 000000000..d38d57208 --- /dev/null +++ b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx @@ -0,0 +1,28 @@ +import classNames from 'classnames'; +import { useState } from 'react'; +import s from './MenuFilterItem.module.scss'; + +interface Props { + name: string, + value: string, + onClick: (value: string) => void +} + +const MenuFilterItem = ({ name, value, onClick }: Props) => { + const [isSelected, setIsSelected] = useState(false) + + function handleClick() { + // todo + setIsSelected(!isSelected) + } + + return ( +
    • +
      + {name} +
      +
    • + ) +} + +export default MenuFilterItem diff --git a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.module.scss b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.module.scss index 6c7ee9c17..9fa431ca5 100644 --- a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.module.scss +++ b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.module.scss @@ -1,13 +1,5 @@ @import "../../../styles/utilities"; -.menuNavigationProductListDesktop{ - @screen sm { - @apply hidden; - } - @screen xl { - @apply block; - } -} .menuNavigationProductListMobile{ @apply relative transition-all duration-100; &.isShow{ @@ -37,7 +29,7 @@ transform: translateY(0%) } .content{ - @apply absolute w-full h-full; + @apply absolute w-full h-full spacing-horizontal custom-scroll; margin-top: 3rem; padding-top: 10rem ; padding-bottom: 10rem; @@ -46,6 +38,7 @@ height: 96%; bottom: 0; border-radius: 2.4rem 2.4rem 0 0; + .head{ @apply flex justify-between fixed; top:0; @@ -57,12 +50,11 @@ background-color: white; z-index: 10000; h3{ - @apply heading-3 font-bold; - color:var(--text-base); + @apply heading-3 font-heading; } } .foot{ - @apply fixed; + @apply fixed text-center; bottom: 0; left:0; width: 100%; @@ -70,7 +62,7 @@ padding: 0 1rem 3rem 1rem; } - button{ + button { margin-top: 2rem; width: 100%; } diff --git a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx index 026710bbf..ff4ab2b5a 100644 --- a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx +++ b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx @@ -1,56 +1,78 @@ +import { QueryFacetsArgs } from '@framework/schema'; +import classNames from 'classnames'; import React, { useState } from 'react'; -import {ButtonCommon} from 'src/components/common'; +import { ButtonCommon } from 'src/components/common'; +import { useGetAllCollection } from 'src/components/hooks/collection'; +import { useFacets } from 'src/components/hooks/facets'; +import IconHide from 'src/components/icons/IconHide'; +import { CODE_FACET_BRAND, CODE_FACET_FEATURED, QUERY_KEY } from 'src/utils/constanst.utils'; +import { LANGUAGE } from 'src/utils/language.utils'; +import { SortOrder } from 'src/utils/types.utils'; +import MenuFilter from '../MenuFilter/MenuFilter'; +import SkeletonParagraph from '../SkeletonCommon/SkeletonParagraph/SkeletonParagraph'; import s from './MenuNavigationProductList.module.scss'; import MenuSort from './MenuSort/MenuSort'; -import {LANGUAGE} from 'src/utils/language.utils'; -import classNames from 'classnames' -import MenuFilter from '../MenuFilter/MenuFilter'; -import MenuNavigation from '../MenuNavigation/MenuNavigation'; -import IconHide from 'src/components/icons/IconHide'; -interface Props{ - categories:{name:string,link:string}[], - brands:{name:string,link:string}[], - featured:{name:string,link:string}[], +interface Props { visible: boolean, onClose: () => void } -const MenuNavigationProductList = ({categories,brands,featured,visible,onClose}:Props)=>{ - - const [dataSort,setDataSort] = useState({}); - - function handleValue(value:Object){ - setDataSort({...dataSort,...value}); +const FACET_QUERY = { + options: { + sort: { + code: SortOrder.Asc + }, + filter: { + code: { + in: [CODE_FACET_FEATURED, CODE_FACET_BRAND] + } + } } - function filter(){ +} as QueryFacetsArgs + +const MenuNavigationProductList = ({ visible, onClose }: Props) => { + const { facets, loading: facetsLoading } = useFacets(FACET_QUERY) + const { collections, loading: collectionLoading } = useGetAllCollection() + + const [dataSort, setDataSort] = useState({}); + + function handleValue(value: Object) { + setDataSort({ ...dataSort, ...value }); + } + function filter() { // console.log(dataSort) } - return( - <> -
      - - - -
      -
      -
      -
      -
      -

      FILTER

      -
      -
      - - - - -
      - {LANGUAGE.BUTTON_LABEL.CONFIRM} -
      + + + return ( +
      +
      +
      +
      +

      FILTER

      +
      +
      + {collectionLoading && } + + {facetsLoading && <> + + + } + { + facets?.map(item => ) + } + +
      + {LANGUAGE.BUTTON_LABEL.CONFIRM}
      - +
      ) } diff --git a/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss b/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss index 732f0e6eb..8a466ff74 100644 --- a/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss +++ b/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss @@ -1,11 +1,8 @@ @import "../../../../styles/utilities"; -.menuSortWrapper{ - @apply spacing-horizontal; +.menuSortWrapper{ .menuSortHeading{ - @apply sub-headline font-bold ; - color: var(--text-active); - font-feature-settings: 'salt' on; + @apply heading-3 font-heading; margin: 0.8rem 0; } .menuSortList{ diff --git a/src/components/hooks/collection/useGetAllCollection.tsx b/src/components/hooks/collection/useGetAllCollection.tsx index 8e7232b2d..7e23cdf40 100644 --- a/src/components/hooks/collection/useGetAllCollection.tsx +++ b/src/components/hooks/collection/useGetAllCollection.tsx @@ -5,8 +5,8 @@ import useSWR from 'swr'; const useGetAllCollection = () => { - const { data, ...rest } = useSWR([getCollectionsNameQuery], gglFetcher) - return { collections: data?.collections, ...rest } + const { data, isValidating, ...rest } = useSWR([getCollectionsNameQuery], gglFetcher) + return { collections: data?.collections.items || [], loading: isValidating, ...rest } } export default useGetAllCollection; \ No newline at end of file diff --git a/src/components/hooks/facets/useFacets.tsx b/src/components/hooks/facets/useFacets.tsx index c9a4e85ab..8966bcedc 100644 --- a/src/components/hooks/facets/useFacets.tsx +++ b/src/components/hooks/facets/useFacets.tsx @@ -5,7 +5,7 @@ import useSWR from 'swr' const useFacets = (options?: QueryFacetsArgs) => { const { data, isValidating, ...rest } = useSWR([getAllFacetsQuery, options], gglFetcher) - return { items: data?.facets.items, totalItems: data?.facets.totalItems, loading: isValidating, ...rest } + return { facets: data?.facets.items, totalItems: data?.facets.totalItems, loading: isValidating, ...rest } } export default useFacets From c49ba5062a20417c8c8e82a4c8cd1ce21db6705c Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 6 Oct 2021 15:01:23 +0700 Subject: [PATCH 04/12] :sparkles: feat: initial active value for option filter products :%s --- .../utils/queries/get-collections-query.ts | 2 +- .../LayoutContent/LayoutContent.module.scss | 2 +- .../Layout/LayoutContent/LayoutContent.tsx | 3 +- .../common/MenuFilter/MenuFilter.module.scss | 17 +---- .../common/MenuFilter/MenuFilter.tsx | 17 ++--- .../MenuFilterItem/MenuFilterItem.module.scss | 1 - .../MenuFilterItem/MenuFilterItem.tsx | 18 +++-- .../MenuNavigation/MenuNavigation.module.scss | 32 ++------ .../common/MenuNavigation/MenuNavigation.tsx | 25 +++---- .../MenuNavigationItem.module.scss | 15 ++++ .../MenuNavigationItem/MenuNavigationItem.tsx | 57 ++++++++++++++ .../MenuNavigationProductList.tsx | 74 ++++++++++++++++--- .../MenuSort/MenuSort.module.scss | 25 ------- .../MenuSort/MenuSort.tsx | 61 ++++----------- .../MenuSortItem/MenuSortItem.module.scss | 26 +++++++ .../MenuSort/MenuSortItem/MenuSortItem.tsx | 24 ++++++ .../MenuSort/MenuSortItem/check.svg | 3 + .../ModalCreateUserInfo.tsx | 2 +- .../ProductList/ProductList.module.scss | 3 +- .../common/SelectCommon/SelectCommon.tsx | 35 ++++++--- .../SelectOption/SelectOption.tsx | 10 +-- .../EditInfoModal/EditInfoModal.tsx | 2 +- .../ShippingInfoForm/ShippingInfoForm.tsx | 2 +- .../ProductListFilter.module.scss | 26 +------ .../ProductListFilter/ProductListFilter.tsx | 42 ++--------- .../ProductSort/ProductSort.tsx | 40 ++++++++++ .../ProductsMenuNavigationTablet.module.scss | 12 +++ .../ProductsMenuNavigationTablet.tsx | 28 +++++++ .../recipes-list/RecipesList/RecipesList.tsx | 4 +- src/styles/_base.scss | 2 +- src/utils/constanst.utils.ts | 31 +++++++- 31 files changed, 400 insertions(+), 241 deletions(-) create mode 100644 src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.module.scss create mode 100644 src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx create mode 100644 src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.module.scss create mode 100644 src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.tsx create mode 100644 src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/check.svg create mode 100644 src/components/modules/product-list/ProductListFilter/ProductSort/ProductSort.tsx create mode 100644 src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.module.scss create mode 100644 src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx diff --git a/framework/vendure/utils/queries/get-collections-query.ts b/framework/vendure/utils/queries/get-collections-query.ts index 79e00a292..f07a85249 100644 --- a/framework/vendure/utils/queries/get-collections-query.ts +++ b/framework/vendure/utils/queries/get-collections-query.ts @@ -24,7 +24,7 @@ export const getCollectionsNameQuery = /* GraphQL */ ` collections{ items{ name - link:slug + slug } } } diff --git a/src/components/common/Layout/LayoutContent/LayoutContent.module.scss b/src/components/common/Layout/LayoutContent/LayoutContent.module.scss index 97ed29624..9e687217e 100644 --- a/src/components/common/Layout/LayoutContent/LayoutContent.module.scss +++ b/src/components/common/Layout/LayoutContent/LayoutContent.module.scss @@ -17,7 +17,7 @@ } } .filter { - @screen xl { + @screen md { display: none; } } diff --git a/src/components/common/Layout/LayoutContent/LayoutContent.tsx b/src/components/common/Layout/LayoutContent/LayoutContent.tsx index ce4022dd0..6e82f338a 100644 --- a/src/components/common/Layout/LayoutContent/LayoutContent.tsx +++ b/src/components/common/Layout/LayoutContent/LayoutContent.tsx @@ -15,7 +15,7 @@ interface Props { const LayoutContent: FC = ({ children }) => { const router = useRouter() - const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: true }) + const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: false }) const {messages, removeMessage} = useMessage() const toggleFilter = () => { @@ -29,7 +29,6 @@ const LayoutContent: FC = ({ children }) => { return ( <>
      - {router.pathname}
      { router.pathname === ROUTE.ACCOUNT ? diff --git a/src/components/common/MenuFilter/MenuFilter.module.scss b/src/components/common/MenuFilter/MenuFilter.module.scss index e887734a9..ef045ef02 100644 --- a/src/components/common/MenuFilter/MenuFilter.module.scss +++ b/src/components/common/MenuFilter/MenuFilter.module.scss @@ -1,4 +1,5 @@ @import "../../../styles/utilities"; + .menuFilterWrapper{ .menuFilterHeading{ @apply sub-headline font-bold; @@ -17,21 +18,5 @@ width: 100%; border-bottom: 1px solid var(--border-line); } - - li{ - margin: 1rem 0; - padding:0; - div{ - padding: 0.8rem 1.6rem; - margin-right: 0.8rem; - background-color: var(--gray); - border-radius: 0.8rem; - cursor: pointer; - &.active { - color:white; - background-color: var(--primary); - } - } - } } } diff --git a/src/components/common/MenuFilter/MenuFilter.tsx b/src/components/common/MenuFilter/MenuFilter.tsx index a0021da79..b23816aff 100644 --- a/src/components/common/MenuFilter/MenuFilter.tsx +++ b/src/components/common/MenuFilter/MenuFilter.tsx @@ -1,19 +1,17 @@ -import classNames from 'classnames' -import { useEffect, useState } from 'react'; - -import s from './MenuFilter.module.scss' +import s from './MenuFilter.module.scss'; import MenuFilterItem from './MenuFilterItem/MenuFilterItem'; + interface Props { children?: any, heading?: string, categories: { name: string, slug?: string, code?: string }[], type: string, - onChangeValue?: (value: Object) => void + onChange: (value: string, type: string, isSellect?: boolean) => void } -const MenuFilter = ({ heading, categories, type, onChangeValue }: Props) => { - function handleClick(value: string) { - +const MenuFilter = ({ heading, categories, type, onChange }: Props) => { + function handleChange(value: string, isSellect: boolean) { + onChange(value, type, isSellect) } return ( @@ -24,8 +22,9 @@ const MenuFilter = ({ heading, categories, type, onChangeValue }: Props) => { categories.map(item => ) }
    diff --git a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss index f70a224f2..3ecab2495 100644 --- a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss +++ b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.module.scss @@ -1,4 +1,3 @@ - .menuFilterItem { margin: 1rem 0; padding: 0; diff --git a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx index d38d57208..e247bfe93 100644 --- a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx +++ b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx @@ -1,18 +1,26 @@ import classNames from 'classnames'; -import { useState } from 'react'; +import { route } from 'next/dist/server/router'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; import s from './MenuFilterItem.module.scss'; interface Props { name: string, value: string, - onClick: (value: string) => void + type: string, + onChange: (value: string, isSellect: boolean) => void } -const MenuFilterItem = ({ name, value, onClick }: Props) => { - const [isSelected, setIsSelected] = useState(false) +const MenuFilterItem = ({ name, value, type, onChange }: Props) => { + const router = useRouter() + const [isSelected, setIsSelected] = useState() + useEffect(() => { + const rs = (router.query[type] || []).includes(value) + setIsSelected(rs) + }, [type, router.query, value]) function handleClick() { - // todo + onChange(value, !isSelected) setIsSelected(!isSelected) } diff --git a/src/components/common/MenuNavigation/MenuNavigation.module.scss b/src/components/common/MenuNavigation/MenuNavigation.module.scss index 6d0a06c3f..49639ce3c 100644 --- a/src/components/common/MenuNavigation/MenuNavigation.module.scss +++ b/src/components/common/MenuNavigation/MenuNavigation.module.scss @@ -3,31 +3,13 @@ @apply hidden; @screen md { @apply block; - } - .menuNavigationHeading{ - @screen md { - @apply sub-headline font-bold ; - color: var(--text-active); - font-feature-settings: 'salt' on; - margin: 1.6rem 0; - } - } - .menuNavigationList{ - @screen md { - li{ - margin: 0.8rem 0; - a{ - display:block; - width:100%; - color:var(--text-base); - &:hover { - @apply text-primary; - } - &.active { - @apply text-primary; - } - } + .menuNavigationHeading{ + @screen md { + @apply sub-headline font-bold ; + color: var(--text-active); + font-feature-settings: 'salt' on; + margin: 1.6rem 0; } } - } + } } diff --git a/src/components/common/MenuNavigation/MenuNavigation.tsx b/src/components/common/MenuNavigation/MenuNavigation.tsx index a2554451e..669eacf59 100644 --- a/src/components/common/MenuNavigation/MenuNavigation.tsx +++ b/src/components/common/MenuNavigation/MenuNavigation.tsx @@ -1,32 +1,25 @@ -import classNames from 'classnames' -import Link from 'next/link' -import { useRouter } from 'next/router' -import { ROUTE } from 'src/utils/constanst.utils' import s from './MenuNavigation.module.scss' +import MenuNavigationItem from './MenuNavigationItem/MenuNavigationItem' interface Props { children?: any, heading: string, - linkPrefix: string, + queryKey: string, categories: { name: string, slug?: string, code?: string }[] } -const MenuNavigation = ({ heading, linkPrefix, categories }: Props) => { - const router = useRouter() - +const MenuNavigation = ({ heading, queryKey, categories }: Props) => { return (

    {heading}({categories.length})

    diff --git a/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.module.scss b/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.module.scss new file mode 100644 index 000000000..97f336443 --- /dev/null +++ b/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.module.scss @@ -0,0 +1,15 @@ +.menuNavigationItem { + @screen md { + display: block; + width: 100%; + margin: 0.8rem 0; + color: var(--text-base); + cursor: pointer; + &:hover { + @apply text-active; + } + &.active { + @apply text-primary; + } + } +} diff --git a/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx b/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx new file mode 100644 index 000000000..c7aeba724 --- /dev/null +++ b/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx @@ -0,0 +1,57 @@ +import classNames from 'classnames' +import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' +import { QUERY_SPLIT_SEPERATOR, ROUTE } from 'src/utils/constanst.utils' +import s from './MenuNavigationItem.module.scss' + +interface Props { + name: string + value: string + queryKey: string, +} + +const MenuNavigationItem = ({ name, value, queryKey }: Props) => { + const router = useRouter() + const [isActive, setIsActive] = useState() + + useEffect(() => { + if (!value) { + setIsActive(false) + } + + const queryString = router.query[queryKey] as string || '' + setIsActive(queryString.split(QUERY_SPLIT_SEPERATOR).includes(value)) + }, [router.query, queryKey, value]) + + const handleClick = () => { + const queryString = router.query[queryKey] as string || '' + const prevQuery = queryString.split(QUERY_SPLIT_SEPERATOR) + + let newQuery = [] as string[] + if (isActive) { + newQuery = prevQuery.filter(item => item !== value) + } else { + newQuery = [...prevQuery, value] + } + // setIsActive(!isActive) + + router.push({ + pathname: ROUTE.PRODUCTS, + query: { + ...router.query, + [queryKey]: newQuery.join(QUERY_SPLIT_SEPERATOR) + } + }, + undefined, { shallow: true } + ) + } + + return (
  • + {name} +
  • ) + + +} + +export default MenuNavigationItem diff --git a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx index ff4ab2b5a..af3d4c9f9 100644 --- a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx +++ b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx @@ -1,11 +1,12 @@ import { QueryFacetsArgs } from '@framework/schema'; import classNames from 'classnames'; -import React, { useState } from 'react'; +import { useRouter } from 'next/router'; +import React, { useEffect, useState } from 'react'; import { ButtonCommon } from 'src/components/common'; import { useGetAllCollection } from 'src/components/hooks/collection'; import { useFacets } from 'src/components/hooks/facets'; import IconHide from 'src/components/icons/IconHide'; -import { CODE_FACET_BRAND, CODE_FACET_FEATURED, QUERY_KEY } from 'src/utils/constanst.utils'; +import { CODE_FACET_BRAND, CODE_FACET_FEATURED, OPTIONS_SORT_PRODUCT, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'; import { LANGUAGE } from 'src/utils/language.utils'; import { SortOrder } from 'src/utils/types.utils'; import MenuFilter from '../MenuFilter/MenuFilter'; @@ -32,16 +33,65 @@ const FACET_QUERY = { } as QueryFacetsArgs const MenuNavigationProductList = ({ visible, onClose }: Props) => { + const router = useRouter() const { facets, loading: facetsLoading } = useFacets(FACET_QUERY) const { collections, loading: collectionLoading } = useGetAllCollection() + const [brandQuery, setBrandQuery] = useState([]) + const [featuredQuery, setFeaturedQuery] = useState([]) + const [categoryQuery, setCategoryQuery] = useState([]) + const [sortValue, setSortValue] = useState(); - const [dataSort, setDataSort] = useState({}); + useEffect(() => { + const rs = router.query[QUERY_KEY.SORTBY] as string + if (rs) { + setSortValue(rs) + } + }, [router.query]) - function handleValue(value: Object) { - setDataSort({ ...dataSort, ...value }); + function onSubmit() { + let newURL = `${ROUTE.PRODUCTS}?` + + if (categoryQuery.length > 0) { + newURL += `&${QUERY_KEY.CATEGORY}=${categoryQuery.join(",")}` + } + + if (brandQuery.length > 0) { + newURL += `&${QUERY_KEY.BRAND}=${brandQuery.join(",")}` + } + + if (featuredQuery.length > 0) { + newURL += `&${QUERY_KEY.FEATURED}=${featuredQuery.join(",")}` + } + + if (sortValue) { + newURL += `&${QUERY_KEY.SORTBY}=${sortValue}` + } + router.push(newURL) + onClose() } - function filter() { - // console.log(dataSort) + + const onSortChange = (value: string) => { + setSortValue(value) + } + + const onFilterOptionChange = (value: string, type: string, isSelect: boolean = true) => { + let rs = [...categoryQuery] + let setDataFunction = setCategoryQuery + + if (type === CODE_FACET_BRAND) { + rs = [...brandQuery] + setDataFunction = setBrandQuery + } else if (type === CODE_FACET_FEATURED) { + rs = [...featuredQuery] + setDataFunction = setFeaturedQuery + } + + if (isSelect) { + rs.push(value) + } else { + rs = rs.filter(item => item !== value) + } + setDataFunction(rs) } @@ -54,7 +104,7 @@ const MenuNavigationProductList = ({ visible, onClose }: Props) => {
    {collectionLoading && } - + {facetsLoading && <> @@ -64,11 +114,13 @@ const MenuNavigationProductList = ({ visible, onClose }: Props) => { key={item.id} type={item.code} categories={item.values} - heading={item.name} />) + heading={item.name} + onChange={onFilterOptionChange} + />) } - +
    - {LANGUAGE.BUTTON_LABEL.CONFIRM} + {LANGUAGE.BUTTON_LABEL.CONFIRM}
    diff --git a/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss b/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss index 8a466ff74..ac3b0681a 100644 --- a/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss +++ b/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.module.scss @@ -8,30 +8,5 @@ .menuSortList{ padding-bottom: 1rem; box-sizing: border-box; - - li{ - div{ - height: 4.8rem; - line-height: 4.8rem; - padding: 0 1.6rem; - margin-right: 0.8rem; - border-radius: 0.8rem; - cursor: pointer; - &.active { - @apply font-bold relative; - color:var(--text-active); - background-color: var(--gray); - &::after{ - @apply absolute; - content:""; - background-image: url('/assets/svg/check.svg'); - right: 1.6rem; - top: calc(50% - 24px/2); - width: 2.4rem; - height: 2.4rem; - } - } - } - } } } diff --git a/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.tsx b/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.tsx index 2e66dfc83..cd771fe6e 100644 --- a/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.tsx +++ b/src/components/common/MenuNavigationProductList/MenuSort/MenuSort.tsx @@ -1,63 +1,30 @@ import classNames from 'classnames'; import { useEffect, useState } from 'react'; -import { QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'; +import { PRODUCT_SORT_OPTION_VALUE, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'; import s from './MenuSort.module.scss'; +import MenuSortItem from './MenuSortItem/MenuSortItem'; interface Props { children?: any, - heading:string, - type:string, - onChangeValue?: (value: Object) => void + heading: string, + options: {name: string, value: string}[] + value?: string, + onChange: (value: string) => void } -const SORT = [ - { - name: 'By Name', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=by-name`, - }, - { - name: 'Price(High to Low)', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=high-to-low`, - }, - { - name: 'Price (Low to High)', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=low-to-high`, - }, - { - name: 'On Sale', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=on-sale`, - }, - ]; - -const MenuSort = ({heading,type,onChangeValue}:Props)=> { - const [active, setActive] = useState(''); - - function handleClick(link:string){ - setActive(link); - - if(active === link){ - setActive(''); - } - } - - useEffect(()=>{ - - let href = active?.split("="); - const linkValue = href[1]; - - onChangeValue && onChangeValue({[type]:linkValue}); - },[active]) - +const MenuSort = ({ heading, value, onChange, options }: Props) => { return (

    {heading}

      { - SORT.map(item =>
    • -
      handleClick(item.link)} className={classNames({ [s.active]: item.link === active? true: false })}> - {item.name} -
      -
    • ) + options.map(item => ) }
    diff --git a/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.module.scss b/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.module.scss new file mode 100644 index 000000000..894cc191a --- /dev/null +++ b/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.module.scss @@ -0,0 +1,26 @@ +.menuSortItem { + div { + height: 4.8rem; + line-height: 4.8rem; + padding: 0 1.6rem; + margin-right: 0.8rem; + border-radius: 0.8rem; + cursor: pointer; + &.active { + @apply font-bold relative; + color: var(--text-active); + background-color: var(--primary-lightest); + &::after { + @apply absolute; + content: ""; + background-image: url("./check.svg"); + background-repeat: no-repeat; + background-position: center; + right: 1.6rem; + top: calc(50% - 24px / 2); + width: 2.4rem; + height: 2.4rem; + } + } + } +} diff --git a/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.tsx b/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.tsx new file mode 100644 index 000000000..f14e9cf24 --- /dev/null +++ b/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/MenuSortItem.tsx @@ -0,0 +1,24 @@ +import classNames from 'classnames'; +import s from './MenuSortItem.module.scss'; + +interface Props { + name: string, + value: string, + currentValue?: string, + onChange: (value: string) => void +} + +const MenuSortItem = ({ onChange, name, value, currentValue }: Props) => { + const handleChange = () => { + onChange(value) + } + return ( +
  • +
    + {name} +
    +
  • + ) +} + +export default MenuSortItem diff --git a/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/check.svg b/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/check.svg new file mode 100644 index 000000000..0962fae2c --- /dev/null +++ b/src/components/common/MenuNavigationProductList/MenuSort/MenuSortItem/check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/common/ModalCreateUserInfo/ModalCreateUserInfo.tsx b/src/components/common/ModalCreateUserInfo/ModalCreateUserInfo.tsx index c3794bf81..c5625c691 100644 --- a/src/components/common/ModalCreateUserInfo/ModalCreateUserInfo.tsx +++ b/src/components/common/ModalCreateUserInfo/ModalCreateUserInfo.tsx @@ -25,7 +25,7 @@ const ModalCreateUserInfo = ({ demoVisible: visible, demoCloseModal: closeModal
    - +
    diff --git a/src/components/common/ProductList/ProductList.module.scss b/src/components/common/ProductList/ProductList.module.scss index c49696ea5..e58b37f86 100644 --- a/src/components/common/ProductList/ProductList.module.scss +++ b/src/components/common/ProductList/ProductList.module.scss @@ -1,11 +1,10 @@ .wrapper{ + margin-top: 4rem; .list{ - // max-width: 109.4rem; @apply flex flex-wrap justify-around; } .pagination{ padding-top: 4.8rem; - // max-width: 109.4rem; @apply flex justify-center items-center ; } } \ No newline at end of file diff --git a/src/components/common/SelectCommon/SelectCommon.tsx b/src/components/common/SelectCommon/SelectCommon.tsx index 9b8c88e24..bf34ed3f9 100644 --- a/src/components/common/SelectCommon/SelectCommon.tsx +++ b/src/components/common/SelectCommon/SelectCommon.tsx @@ -1,24 +1,33 @@ -import s from './SelectCommon.module.scss' import classNames from 'classnames' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { IconVectorDown } from 'src/components/icons' +import s from './SelectCommon.module.scss' import SelectOption from './SelectOption/SelectOption' interface Props { placeholder? : string, + value?: string, size?: 'base' | 'large', type?: 'default' | 'custom', - option: {name: string, value: string}[], + options: {name: string, value: string}[], onChange?: (value: string) => void, } -const SelectCommon = ({ type = 'default', size = 'base', option, placeholder, onChange}: Props) => { - const [selectedName, setSelectedName] = useState(placeholder) - const [selectedValue, setSelectedValue] = useState('') +const SelectCommon = ({ value, type = 'default', size = 'base', options, placeholder, onChange}: Props) => { + const [selectedName, setSelectedName] = useState() + const [selectedValue, setSelectedValue] = useState('') - const changeSelectedName = (item:string, value: string) => { + useEffect(() => { + setSelectedValue(value || '') + + const name = options.find(item => item.value === value)?.name + setSelectedName(name) + }, [value, options]) + + const changeSelectedName = (value: string) => { setSelectedValue(value) - setSelectedName(item) + const name = options.find(item => item.value === value)?.name + setSelectedName(name) onChange && onChange(value) } return( @@ -33,7 +42,7 @@ const SelectCommon = ({ type = 'default', size = 'base', option, placeholder, on [s.selectTrigger] : true, })} - >{selectedName} + >{selectedName || placeholder}
    { - option.map(item => - + options.map(item => + ) }
    diff --git a/src/components/common/SelectCommon/SelectOption/SelectOption.tsx b/src/components/common/SelectCommon/SelectOption/SelectOption.tsx index 7e1968f9e..2968d6b33 100644 --- a/src/components/common/SelectCommon/SelectOption/SelectOption.tsx +++ b/src/components/common/SelectCommon/SelectOption/SelectOption.tsx @@ -2,16 +2,16 @@ import s from './SelectOption.module.scss' import classNames from 'classnames' interface Props{ - onClick: (name: string, value: string) => void, + onChange: (value: string) => void, itemName: string, size: 'base' | 'large', value: string, selected?: boolean, } -const SelectOption = ({onClick, itemName, size, value, selected} : Props) => { - const changeName = () => { - onClick(itemName, value) +const SelectOption = ({onChange, itemName, size, value, selected} : Props) => { + const handleChange = () => { + onChange(value) } return(
    { [s[size]] : !!size, [s.isChoose] : selected , })} - onClick = {changeName} + onClick = {handleChange} >{itemName}
    ) } diff --git a/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx b/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx index 06e6b2124..8289a3a93 100644 --- a/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx +++ b/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx @@ -43,7 +43,7 @@ const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoMod
    - +
    diff --git a/src/components/modules/checkout/CheckoutInfo/components/ShippingInfoForm/ShippingInfoForm.tsx b/src/components/modules/checkout/CheckoutInfo/components/ShippingInfoForm/ShippingInfoForm.tsx index 233e9d057..04217d706 100644 --- a/src/components/modules/checkout/CheckoutInfo/components/ShippingInfoForm/ShippingInfoForm.tsx +++ b/src/components/modules/checkout/CheckoutInfo/components/ShippingInfoForm/ShippingInfoForm.tsx @@ -46,7 +46,7 @@ const ShippingInfoForm = ({onConfirm,id}: ShippingInfoFormProps) => { />
    - State + State
    { +const ProductListFilter = ({ facets, collections, products }: ProductListFilterProps) => { + return (
    -
    - - { - facets.map(item => ) - } -
    - +
    SPECIAL RECIPES
    -
    -
    - -
    -
    +
    diff --git a/src/components/modules/product-list/ProductListFilter/ProductSort/ProductSort.tsx b/src/components/modules/product-list/ProductListFilter/ProductSort/ProductSort.tsx new file mode 100644 index 000000000..a10cec443 --- /dev/null +++ b/src/components/modules/product-list/ProductListFilter/ProductSort/ProductSort.tsx @@ -0,0 +1,40 @@ +import { useRouter } from 'next/router'; +import React, { useEffect, useState } from 'react'; +import { SelectCommon } from 'src/components/common'; +import { OPTIONS_SORT_PRODUCT, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'; + +const ProductSort = () => { + const router = useRouter() + const [sortValue, setSortValue] = useState(); + + useEffect(() => { + const rs = router.query[QUERY_KEY.SORTBY] as string + if (rs) { + setSortValue(rs) + } + }, [router.query]) + + const onSortChange = (value: string) => { + setSortValue(value) + router.push({ + pathname: ROUTE.PRODUCTS, + query: { + ...router.query, + [QUERY_KEY.SORTBY]: value + } + }, + undefined, { shallow: true } + ) + } + + return ( + + ); +}; + +export default ProductSort; \ No newline at end of file diff --git a/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.module.scss b/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.module.scss new file mode 100644 index 000000000..a27b395c3 --- /dev/null +++ b/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.module.scss @@ -0,0 +1,12 @@ + +.productsMenuNavigationTablet { + @apply hidden; + @screen md { + @apply block; + padding-right: 2.4rem; + } + @screen xl { + @apply block; + width: 25%; + } +} diff --git a/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx b/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx new file mode 100644 index 000000000..47127d53e --- /dev/null +++ b/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx @@ -0,0 +1,28 @@ +import { Collection, Facet } from '@framework/schema' +import React from 'react' +import MenuNavigation from 'src/components/common/MenuNavigation/MenuNavigation' +import { QUERY_KEY } from 'src/utils/constanst.utils' +import s from './ProductsMenuNavigationTablet.module.scss' + +interface Props { + facets: Facet[] + collections: Collection[] + +} + +const ProductsMenuNavigationTablet = ({ facets, collections }: Props) => { + return ( +
    + + { + facets.map(item => ) + } +
    + ) +} + +export default ProductsMenuNavigationTablet diff --git a/src/components/modules/recipes-list/RecipesList/RecipesList.tsx b/src/components/modules/recipes-list/RecipesList/RecipesList.tsx index f636157fa..e91599a2c 100644 --- a/src/components/modules/recipes-list/RecipesList/RecipesList.tsx +++ b/src/components/modules/recipes-list/RecipesList/RecipesList.tsx @@ -189,13 +189,13 @@ const RecipesList = ({ data =recipe}:Props) => {
    - +
    - +
    diff --git a/src/styles/_base.scss b/src/styles/_base.scss index 1eab49e38..e41b6763f 100644 --- a/src/styles/_base.scss +++ b/src/styles/_base.scss @@ -4,7 +4,7 @@ :root { --primary: #5b9a74; --primary-light: #e3f2e9; - --primary-lightest: #effaf4; + --primary-lightest: #F1F8F4; --info-dark: #00317a; --info: #3468B7; diff --git a/src/utils/constanst.utils.ts b/src/utils/constanst.utils.ts index 432e9cc78..630054959 100644 --- a/src/utils/constanst.utils.ts +++ b/src/utils/constanst.utils.ts @@ -45,15 +45,24 @@ export const LOCAL_STORAGE_KEY = { TOKEN: 'token' } +export const QUERY_SPLIT_SEPERATOR = ',' export const QUERY_KEY = { TAB: 'tab', CATEGORY: 'category', BRAND: 'brand', - FEATURED: 'feature', + FEATURED: 'featured', SORTBY: 'sortby', RECIPES: 'recipes' } +export const PRODUCT_SORT_OPTION_VALUE = { + NAME_ASC: 'name_asc', + NAME_DESC: 'name_desc', + PRICE_ASC: 'price_asc', + PRICE_DESC: 'price_desc', + +} + export enum ProductFeature { BestSellers = 'Best Sellers', Sales = 'Sales', @@ -118,6 +127,26 @@ export const CODE_FACET_FEATURED_VARIANT = { FRESH: 'fresh', } +export const OPTIONS_SORT_PRODUCT = [ + { + name: 'By Name (A-Z)', + value: PRODUCT_SORT_OPTION_VALUE.NAME_ASC, + }, + { + name: 'By Name (Z-A)', + value: PRODUCT_SORT_OPTION_VALUE.NAME_DESC, + }, + { + name: 'Price (Low to High)', + value: PRODUCT_SORT_OPTION_VALUE.PRICE_ASC, + }, + { + name: 'Price (High to Low)', + value: PRODUCT_SORT_OPTION_VALUE.PRICE_DESC, + }, +]; + + export const FEATURED = [ { name: 'Best Sellers', From a91417eca9614d3c9525f188f8f7e468086a05b7 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 6 Oct 2021 17:44:36 +0700 Subject: [PATCH 05/12] :sparkles: feat: filter product :%s --- .../api/operations/get-all-products.ts | 5 +- framework/vendure/schema.d.ts | 3 +- .../utils/queries/get-all-products-query.ts | 1 + pages/products.tsx | 13 +-- .../EmptyCommon/EmptyCommon.module.scss | 7 +- .../common/MenuNavigation/MenuNavigation.tsx | 4 +- .../MenuNavigationItem/MenuNavigationItem.tsx | 39 +++++---- .../ProductList/ProductList.module.scss | 19 +++-- .../common/ProductList/ProductList.tsx | 44 +++++++--- src/components/hooks/product/index.ts | 3 + .../hooks/product/useSearchProducts.tsx | 14 ++++ .../ProductListFilter.module.scss | 12 +-- .../ProductListFilter/ProductListFilter.tsx | 82 +++++++++++++++++-- .../ProductsMenuNavigationTablet.tsx | 6 +- src/utils/constanst.utils.ts | 3 +- src/utils/funtion.utils.ts | 25 +++++- 16 files changed, 222 insertions(+), 58 deletions(-) create mode 100644 src/components/hooks/product/index.ts create mode 100644 src/components/hooks/product/useSearchProducts.tsx diff --git a/framework/vendure/api/operations/get-all-products.ts b/framework/vendure/api/operations/get-all-products.ts index 1f558a7cb..5fb458f67 100644 --- a/framework/vendure/api/operations/get-all-products.ts +++ b/framework/vendure/api/operations/get-all-products.ts @@ -14,7 +14,7 @@ export default function getAllProductsOperation({ variables?: ProductVariables config?: Partial preview?: boolean - }): Promise<{ products: Product[] }> + }): Promise<{ products: Product[], totalItems: number }> async function getAllProducts({ query = getAllProductsQuery, @@ -25,7 +25,7 @@ export default function getAllProductsOperation({ variables?: ProductVariables config?: Partial preview?: boolean - } = {}): Promise<{ products: Product[] | any[] }> { + } = {}): Promise<{ products: Product[] | any[], totalItems: number }> { const config = commerce.getConfig(cfg) const variables = { input: { @@ -40,6 +40,7 @@ export default function getAllProductsOperation({ return { products: data.search.items.map((item) => normalizeSearchResult(item)), + totalItems: data.search.totalItems as number, } } diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts index 5faa87b43..8f49e2d2d 100644 --- a/framework/vendure/schema.d.ts +++ b/framework/vendure/schema.d.ts @@ -3219,7 +3219,8 @@ export type GetAllProductsQueryVariables = Exact<{ export type GetAllProductsQuery = { __typename?: 'Query' } & { search: { __typename?: 'SearchResponse' } & { - items: Array<{ __typename?: 'SearchResult' } & SearchResultFragment> + items: Array<{ __typename?: 'SearchResult' } & SearchResultFragment>, + 'totalItems' } } diff --git a/framework/vendure/utils/queries/get-all-products-query.ts b/framework/vendure/utils/queries/get-all-products-query.ts index 1b44b2017..007c6594d 100644 --- a/framework/vendure/utils/queries/get-all-products-query.ts +++ b/framework/vendure/utils/queries/get-all-products-query.ts @@ -3,6 +3,7 @@ import { searchResultFragment } from '../fragments/search-result-fragment' export const getAllProductsQuery = /* GraphQL */ ` query getAllProducts($input: SearchInput!) { search(input: $input) { + totalItems items { ...SearchResult } diff --git a/pages/products.tsx b/pages/products.tsx index 93dda5cdb..9559538df 100644 --- a/pages/products.tsx +++ b/pages/products.tsx @@ -13,16 +13,19 @@ import ProductListBanner from '../src/components/modules/product-list/ProductLis interface Props { facets: Facet[], collections: Collection[], - products: ProductCard[], + productsResult: { products: ProductCard[], totalItems: number }, } -export default function Products({ facets, collections, products }: Props) { - // console.log("facets: ", products) +export default function Products({ facets, collections, productsResult }: Props) { return ( <> - + ) @@ -70,7 +73,7 @@ export async function getStaticProps({ config, preview, }) - promisesWithKey.push({ key: 'products', promise: productsPromise, keyResult: 'products' }) + promisesWithKey.push({ key: 'productsResult', promise: productsPromise }) try { diff --git a/src/components/common/EmptyCommon/EmptyCommon.module.scss b/src/components/common/EmptyCommon/EmptyCommon.module.scss index a31ba4374..4014faeea 100644 --- a/src/components/common/EmptyCommon/EmptyCommon.module.scss +++ b/src/components/common/EmptyCommon/EmptyCommon.module.scss @@ -4,13 +4,16 @@ padding: 1.6rem; margin: auto; .imgWrap { - min-width: 10rem; + min-height: 10rem; text-align: center; + img { + min-height: 10rem; + } } .description { color: var(--disabled); text-align: center; - margin-top: .8rem; + margin-top: 0.8rem; } } diff --git a/src/components/common/MenuNavigation/MenuNavigation.tsx b/src/components/common/MenuNavigation/MenuNavigation.tsx index 669eacf59..a7cc9a3d7 100644 --- a/src/components/common/MenuNavigation/MenuNavigation.tsx +++ b/src/components/common/MenuNavigation/MenuNavigation.tsx @@ -6,9 +6,10 @@ interface Props { heading: string, queryKey: string, categories: { name: string, slug?: string, code?: string }[] + isSingleSelect?: boolean } -const MenuNavigation = ({ heading, queryKey, categories }: Props) => { +const MenuNavigation = ({ heading, queryKey, categories, isSingleSelect }: Props) => { return (

    {heading}({categories.length})

    @@ -19,6 +20,7 @@ const MenuNavigation = ({ heading, queryKey, categories }: Props) => { name={item.name} value={item.slug || item.code || ''} queryKey={queryKey} + isSingleSelect={isSingleSelect} />) } diff --git a/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx b/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx index c7aeba724..e1abee867 100644 --- a/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx +++ b/src/components/common/MenuNavigation/MenuNavigationItem/MenuNavigationItem.tsx @@ -1,16 +1,18 @@ import classNames from 'classnames' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' -import { QUERY_SPLIT_SEPERATOR, ROUTE } from 'src/utils/constanst.utils' +import { QUERY_KEY, QUERY_SPLIT_SEPERATOR, ROUTE } from 'src/utils/constanst.utils' import s from './MenuNavigationItem.module.scss' interface Props { name: string value: string - queryKey: string, + queryKey: string + isSingleSelect?: boolean + } -const MenuNavigationItem = ({ name, value, queryKey }: Props) => { +const MenuNavigationItem = ({ name, value, queryKey, isSingleSelect }: Props) => { const router = useRouter() const [isActive, setIsActive] = useState() @@ -26,21 +28,30 @@ const MenuNavigationItem = ({ name, value, queryKey }: Props) => { const handleClick = () => { const queryString = router.query[queryKey] as string || '' const prevQuery = queryString.split(QUERY_SPLIT_SEPERATOR) - - let newQuery = [] as string[] - if (isActive) { - newQuery = prevQuery.filter(item => item !== value) + + let newQuery = '' + if (isSingleSelect) { + newQuery = isActive ? '' : value } else { - newQuery = [...prevQuery, value] + if (isActive) { + newQuery = prevQuery.filter(item => item !== value).join(QUERY_SPLIT_SEPERATOR) + } else { + newQuery = [...prevQuery, value].join(QUERY_SPLIT_SEPERATOR) + } + } + + const query = { + ...router.query, + [queryKey]: newQuery + } + + if (queryKey === QUERY_KEY.CATEGORY) { + query[QUERY_KEY.PAGE] = "0" } - // setIsActive(!isActive) router.push({ pathname: ROUTE.PRODUCTS, - query: { - ...router.query, - [queryKey]: newQuery.join(QUERY_SPLIT_SEPERATOR) - } + query }, undefined, { shallow: true } ) @@ -50,8 +61,6 @@ const MenuNavigationItem = ({ name, value, queryKey }: Props) => { onClick={handleClick}> {name} ) - - } export default MenuNavigationItem diff --git a/src/components/common/ProductList/ProductList.module.scss b/src/components/common/ProductList/ProductList.module.scss index e58b37f86..b032f409d 100644 --- a/src/components/common/ProductList/ProductList.module.scss +++ b/src/components/common/ProductList/ProductList.module.scss @@ -1,10 +1,19 @@ -.wrapper{ +.wrapper { margin-top: 4rem; - .list{ + .list { @apply flex flex-wrap justify-around; + .empty { + button { + margin-top: 1.6rem; + width: 100%; + } + } } - .pagination{ + .pagination { padding-top: 4.8rem; - @apply flex justify-center items-center ; + @apply flex justify-center items-center; + &.hide { + @apply hidden; + } } -} \ No newline at end of file +} diff --git a/src/components/common/ProductList/ProductList.tsx b/src/components/common/ProductList/ProductList.tsx index 0c5deb91e..c901b4d46 100644 --- a/src/components/common/ProductList/ProductList.tsx +++ b/src/components/common/ProductList/ProductList.tsx @@ -1,28 +1,50 @@ -import React, { useState } from 'react' -import { DEFAULT_PAGE_SIZE } from 'src/utils/constanst.utils' +import classNames from 'classnames' +import { useRouter } from 'next/router' +import React from 'react' +import { DEFAULT_PAGE_SIZE, ROUTE } from 'src/utils/constanst.utils' +import { ButtonCommon, EmptyCommon } from '..' import PaginationCommon from '../PaginationCommon/PaginationCommon' import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard' import s from "./ProductList.module.scss" + interface ProductListProps { - data: ProductCardProps[] + data: ProductCardProps[], + total?: number, + defaultCurrentPage?: number + onPageChange?: (page: number) => void } -const ProductList = ({data}: ProductListProps) => { - const [currentPage, setCurrentPage] = useState(0) - const onPageChange = (page:number) => { - setCurrentPage(page) +const ProductList = ({ data, total = data.length, defaultCurrentPage, onPageChange }: ProductListProps) => { + const router = useRouter() + const handlePageChange = (page: number) => { + onPageChange && onPageChange(page) } + + const handleShowAllProduct = () => { + router.push({ + pathname: ROUTE.PRODUCTS, + }, + undefined, { shallow: true } + ) + } + return (
    { - data.slice(currentPage*DEFAULT_PAGE_SIZE,(currentPage+1)* DEFAULT_PAGE_SIZE).map((product,index)=>{ - return + data.map((product, index) => { + return }) } + { + data.length === 0 &&
    + + Show all products +
    + }
    -
    - +
    +
    ) diff --git a/src/components/hooks/product/index.ts b/src/components/hooks/product/index.ts new file mode 100644 index 000000000..ea2afe03a --- /dev/null +++ b/src/components/hooks/product/index.ts @@ -0,0 +1,3 @@ +export { default as useSearchProducts } from './useSearchProducts' + + diff --git a/src/components/hooks/product/useSearchProducts.tsx b/src/components/hooks/product/useSearchProducts.tsx new file mode 100644 index 000000000..e9d2ca127 --- /dev/null +++ b/src/components/hooks/product/useSearchProducts.tsx @@ -0,0 +1,14 @@ +import { GetAllProductsQuery, QuerySearchArgs } from '@framework/schema' +import { normalizeSearchResult } from '@framework/utils/normalize' +import { getAllProductsQuery } from '@framework/utils/queries/get-all-products-query' +import gglFetcher from 'src/utils/gglFetcher' +import useSWR from 'swr' + +const useSearchProducts = (options?: QuerySearchArgs) => { + const { data, isValidating, ...rest } = useSWR([getAllProductsQuery, options], gglFetcher) + console.log("on search ", data?.search.totalItems, options, data?.search.items) + + return { products: data?.search.items.map((item) => normalizeSearchResult(item)), totalItems: data?.search.totalItems, loading: isValidating, ...rest } +} + +export default useSearchProducts diff --git a/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss b/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss index fea3af574..84951b540 100644 --- a/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss +++ b/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss @@ -14,12 +14,12 @@ @apply flex; } .list{ - @screen md { - @apply flex justify-between flex-wrap w-full; - margin: 1rem 0; - } - @screen xl { - width:75%; + @apply w-full; + .top { + @screen md { + @apply flex justify-between flex-wrap w-full; + margin: 1rem 0; + } } .inner{ @screen md { diff --git a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx index 827ff966c..e35df74e1 100644 --- a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx +++ b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx @@ -1,8 +1,13 @@ import { ProductCard } from '@commerce/types/product' -import { Collection, Facet } from '@framework/schema' -import React from 'react' +import { Collection, Facet, FacetValue, QuerySearchArgs } from '@framework/schema' +import { useRouter } from 'next/router' +import React, { useEffect, useState } from 'react' import { HeadingCommon, ProductList } from 'src/components/common' import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbCommon' +import SkeletonImage from 'src/components/common/SkeletonCommon/SkeletonImage/SkeletonImage' +import { useSearchProducts } from 'src/components/hooks/product' +import { DEFAULT_PAGE_SIZE, QUERY_KEY, QUERY_SPLIT_SEPERATOR, ROUTE } from 'src/utils/constanst.utils' +import { getFacetIdsFromCodes, getPageFromQuery } from 'src/utils/funtion.utils' import s from './ProductListFilter.module.scss' import ProductsMenuNavigationTablet from './ProductsMenuNavigationTablet/ProductsMenuNavigationTablet' import ProductSort from './ProductSort/ProductSort' @@ -11,6 +16,7 @@ interface ProductListFilterProps { facets: Facet[] collections: Collection[] products: ProductCard[] + total: number } @@ -21,7 +27,64 @@ const BREADCRUMB = [ }, ] -const ProductListFilter = ({ facets, collections, products }: ProductListFilterProps) => { + +const DEFAULT_SEARCH_ARGS = { + groupByProduct: true, take: DEFAULT_PAGE_SIZE +} + +const ProductListFilter = ({ facets, collections, products, total }: ProductListFilterProps) => { + const router = useRouter() + const [initialQueryFlag, setInitialQueryFlag] = useState(true) + const [optionQueryProduct, setOptionQueryProduct] = useState({ input: DEFAULT_SEARCH_ARGS }) + const { products: productSearchResult, totalItems, loading } = useSearchProducts(optionQueryProduct) + const [currentPage, setCurrentPage] = useState(0) + + useEffect(() => { + const page = getPageFromQuery(router.query[QUERY_KEY.PAGE] as string) + setCurrentPage(page) + }, [router.query]) + + const onPageChange = (page: number) => { + setCurrentPage(page) + + router.push({ + pathname: ROUTE.PRODUCTS, + query: { + ...router.query, + [QUERY_KEY.PAGE]: page + } + }, + undefined, { shallow: true } + ) + } + + useEffect(() => { + const query = { input: { ...DEFAULT_SEARCH_ARGS } } as QuerySearchArgs + + const page = getPageFromQuery(router.query[QUERY_KEY.PAGE] as string) + query.input.skip = page * DEFAULT_PAGE_SIZE + + // collections + const categoryQuery = router.query[QUERY_KEY.CATEGORY] as string + if (categoryQuery) { + query.input.collectionSlug = categoryQuery + } + + // facets + const facetsQuery = [router.query[QUERY_KEY.FEATURED] as string, router.query[QUERY_KEY.BRAND] as string].join(QUERY_SPLIT_SEPERATOR) + if (facetsQuery) { + const facetsValue = [] as FacetValue[] + facets.map((item: Facet) => { + facetsValue.push(...item.values) + return null + }) + + query.input.facetValueIds = getFacetIdsFromCodes(facetsValue, facetsQuery.split(QUERY_SPLIT_SEPERATOR)) + } + + setOptionQueryProduct(query) + setInitialQueryFlag(false) + }, [router.query, facets]) return (
    @@ -31,12 +94,17 @@ const ProductListFilter = ({ facets, collections, products }: ProductListFilterP
    - SPECIAL RECIPES +
    + SPECIAL RECIPES -
    - +
    + +
    - + { + (!initialQueryFlag && loading && !productSearchResult) && + } +
    diff --git a/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx b/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx index 47127d53e..d31609639 100644 --- a/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx +++ b/src/components/modules/product-list/ProductListFilter/ProductsMenuNavigationTablet/ProductsMenuNavigationTablet.tsx @@ -13,7 +13,11 @@ interface Props { const ProductsMenuNavigationTablet = ({ facets, collections }: Props) => { return (
    - + { facets.map(item => (arr: Array, value: T): Array { const index = arr.indexOf(value); if (index > -1) { @@ -58,6 +71,16 @@ export function getFacetNamesFromIds(facets: FacetValue[], ids?: string[]): stri return names.join(", ") } -export function getAllPromies (promies: PromiseWithKey[]) { +export function getFacetIdsFromCodes(facets: FacetValue[], codes?: string[]): string[] { + if (!codes || codes?.length === 0) { + return [] + } + + const facetItems = facets.filter((item: FacetValue) => codes.includes(item.code)) + const ids = facetItems.map((item: FacetValue) => item.id) + return ids +} + +export function getAllPromies(promies: PromiseWithKey[]) { return promies.map(item => item.promise) } From fb375ed4e8ac1c66460c68f58b7bd9962a70d404 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 6 Oct 2021 17:49:41 +0700 Subject: [PATCH 06/12] :sparkles: feat: sort products list :%s --- .../ProductListFilter/ProductListFilter.tsx | 8 +++- src/utils/funtion.utils.ts | 41 +++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx index e35df74e1..17612866c 100644 --- a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx +++ b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx @@ -7,7 +7,7 @@ import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbC import SkeletonImage from 'src/components/common/SkeletonCommon/SkeletonImage/SkeletonImage' import { useSearchProducts } from 'src/components/hooks/product' import { DEFAULT_PAGE_SIZE, QUERY_KEY, QUERY_SPLIT_SEPERATOR, ROUTE } from 'src/utils/constanst.utils' -import { getFacetIdsFromCodes, getPageFromQuery } from 'src/utils/funtion.utils' +import { getFacetIdsFromCodes, getPageFromQuery, getProductSortParamFromQuery } from 'src/utils/funtion.utils' import s from './ProductListFilter.module.scss' import ProductsMenuNavigationTablet from './ProductsMenuNavigationTablet/ProductsMenuNavigationTablet' import ProductSort from './ProductSort/ProductSort' @@ -64,6 +64,12 @@ 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 sortQuery = router.query[QUERY_KEY.SORTBY] as string + if (sortQuery) { + query.input.sort = getProductSortParamFromQuery(sortQuery) + } + // collections const categoryQuery = router.query[QUERY_KEY.CATEGORY] as string if (categoryQuery) { diff --git a/src/utils/funtion.utils.ts b/src/utils/funtion.utils.ts index ee7f85c9d..9f712326d 100644 --- a/src/utils/funtion.utils.ts +++ b/src/utils/funtion.utils.ts @@ -1,7 +1,7 @@ import { Facet } from "@commerce/types/facet"; -import { FacetValue } from './../../framework/vendure/schema.d'; -import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT } from "./constanst.utils"; -import { PromiseWithKey } from "./types.utils"; +import { FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d'; +import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT, PRODUCT_SORT_OPTION_VALUE } from "./constanst.utils"; +import { PromiseWithKey, SortOrder } from "./types.utils"; export function isMobile() { return window.innerWidth < 768 @@ -20,6 +20,41 @@ export function getPageFromQuery(pageQuery: string) { return page } + +export function getProductSortParamFromQuery(query: string) { + let rs = {} as SearchResultSortParameter + switch (query) { + case PRODUCT_SORT_OPTION_VALUE.NAME_ASC: + rs = { + name: SortOrder.Asc + } + break; + + case PRODUCT_SORT_OPTION_VALUE.NAME_DESC: + rs = { + name: SortOrder.Desc + } + break; + + case PRODUCT_SORT_OPTION_VALUE.PRICE_ASC: + rs = { + price: SortOrder.Asc + } + break; + + case PRODUCT_SORT_OPTION_VALUE.PRICE_DESC: + rs = { + price: SortOrder.Desc + } + break; + + default: + break; + } + + return rs +} + export function removeItem(arr: Array, value: T): Array { const index = arr.indexOf(value); if (index > -1) { From 721ae9dedc4ccc4a4fd7c4677e89aa6bfe4cb932 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 6 Oct 2021 18:07:11 +0700 Subject: [PATCH 07/12] :sparkles: feat: filter products from menu filter (in header) :%s --- .../common/MenuFilter/MenuFilter.tsx | 5 +- .../MenuFilterItem/MenuFilterItem.tsx | 8 ++- .../MenuNavigationProductList.tsx | 64 +++++++++++++------ .../hooks/product/useSearchProducts.tsx | 1 - 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/components/common/MenuFilter/MenuFilter.tsx b/src/components/common/MenuFilter/MenuFilter.tsx index b23816aff..72f5a8302 100644 --- a/src/components/common/MenuFilter/MenuFilter.tsx +++ b/src/components/common/MenuFilter/MenuFilter.tsx @@ -7,9 +7,11 @@ interface Props { categories: { name: string, slug?: string, code?: string }[], type: string, onChange: (value: string, type: string, isSellect?: boolean) => void + isSingleSelect?: boolean + singleSelectedValue?: string } -const MenuFilter = ({ heading, categories, type, onChange }: Props) => { +const MenuFilter = ({ heading, categories, type, onChange, singleSelectedValue, isSingleSelect }: Props) => { function handleChange(value: string, isSellect: boolean) { onChange(value, type, isSellect) } @@ -25,6 +27,7 @@ const MenuFilter = ({ heading, categories, type, onChange }: Props) => { type={type} value={item.slug || item.code || ''} onChange={handleChange} + isActive={isSingleSelect && (item.slug || item.code) === singleSelectedValue} />) } diff --git a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx index e247bfe93..eeb96fae1 100644 --- a/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx +++ b/src/components/common/MenuFilter/MenuFilterItem/MenuFilterItem.tsx @@ -8,12 +8,18 @@ interface Props { name: string, value: string, type: string, + isActive?: boolean onChange: (value: string, isSellect: boolean) => void } -const MenuFilterItem = ({ name, value, type, onChange }: Props) => { +const MenuFilterItem = ({ name, value, type, isActive, onChange }: Props) => { const router = useRouter() const [isSelected, setIsSelected] = useState() + + useEffect(() => { + setIsSelected(isActive) + }, [isActive]) + useEffect(() => { const rs = (router.query[type] || []).includes(value) setIsSelected(rs) diff --git a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx index af3d4c9f9..122e8e3f8 100644 --- a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx +++ b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx @@ -38,7 +38,7 @@ const MenuNavigationProductList = ({ visible, onClose }: Props) => { const { collections, loading: collectionLoading } = useGetAllCollection() const [brandQuery, setBrandQuery] = useState([]) const [featuredQuery, setFeaturedQuery] = useState([]) - const [categoryQuery, setCategoryQuery] = useState([]) + const [categoryQuery, setCategoryQuery] = useState() const [sortValue, setSortValue] = useState(); useEffect(() => { @@ -48,11 +48,18 @@ const MenuNavigationProductList = ({ visible, onClose }: Props) => { } }, [router.query]) + useEffect(() => { + const rs = router.query[QUERY_KEY.CATEGORY] as string + if (rs) { + setCategoryQuery(rs) + } + }, [router.query]) + function onSubmit() { let newURL = `${ROUTE.PRODUCTS}?` - if (categoryQuery.length > 0) { - newURL += `&${QUERY_KEY.CATEGORY}=${categoryQuery.join(",")}` + if (categoryQuery) { + newURL += `&${QUERY_KEY.CATEGORY}=${categoryQuery}` } if (brandQuery.length > 0) { @@ -74,24 +81,33 @@ const MenuNavigationProductList = ({ visible, onClose }: Props) => { setSortValue(value) } - const onFilterOptionChange = (value: string, type: string, isSelect: boolean = true) => { - let rs = [...categoryQuery] - let setDataFunction = setCategoryQuery - - if (type === CODE_FACET_BRAND) { - rs = [...brandQuery] - setDataFunction = setBrandQuery - } else if (type === CODE_FACET_FEATURED) { - rs = [...featuredQuery] - setDataFunction = setFeaturedQuery - } - + const onCategoryChange = (value: string, isSelect: boolean) => { if (isSelect) { - rs.push(value) + setCategoryQuery(value) } else { - rs = rs.filter(item => item !== value) + setCategoryQuery('') + } + } + + const onFilterOptionChange = (value: string, type: string, isSelect: boolean = true) => { + if (type === QUERY_KEY.CATEGORY) { + onCategoryChange(value, isSelect) + } else { + let rs = [...featuredQuery] + let setDataFunction = setFeaturedQuery + + if (type === CODE_FACET_BRAND) { + rs = [...brandQuery] + setDataFunction = setBrandQuery + } + + if (isSelect) { + rs.push(value) + } else { + rs = rs.filter(item => item !== value) + } + setDataFunction(rs) } - setDataFunction(rs) } @@ -103,8 +119,16 @@ const MenuNavigationProductList = ({ visible, onClose }: Props) => {

    FILTER

    + {collectionLoading && } - + + {facetsLoading && <> @@ -118,7 +142,7 @@ const MenuNavigationProductList = ({ visible, onClose }: Props) => { onChange={onFilterOptionChange} />) } - +
    {LANGUAGE.BUTTON_LABEL.CONFIRM}
    diff --git a/src/components/hooks/product/useSearchProducts.tsx b/src/components/hooks/product/useSearchProducts.tsx index e9d2ca127..78acb7cf3 100644 --- a/src/components/hooks/product/useSearchProducts.tsx +++ b/src/components/hooks/product/useSearchProducts.tsx @@ -6,7 +6,6 @@ import useSWR from 'swr' const useSearchProducts = (options?: QuerySearchArgs) => { const { data, isValidating, ...rest } = useSWR([getAllProductsQuery, options], gglFetcher) - console.log("on search ", data?.search.totalItems, options, data?.search.items) return { products: data?.search.items.map((item) => normalizeSearchResult(item)), totalItems: data?.search.totalItems, loading: isValidating, ...rest } } From 97e3d10bfd9c626e7b391f6483fb8270ac12021f Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 6 Oct 2021 18:16:25 +0700 Subject: [PATCH 08/12] :art: styles: fix dot in icon filter from header :%s --- .../Header/components/HeaderMenu/HeaderMenu.module.scss | 4 ---- .../common/Header/components/HeaderMenu/HeaderMenu.tsx | 9 +-------- .../HeaderSubMenuMobile/HeaderSubMenuMobile.tsx | 1 + 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/components/common/Header/components/HeaderMenu/HeaderMenu.module.scss b/src/components/common/Header/components/HeaderMenu/HeaderMenu.module.scss index 6a16505bc..7baff45a6 100644 --- a/src/components/common/Header/components/HeaderMenu/HeaderMenu.module.scss +++ b/src/components/common/Header/components/HeaderMenu/HeaderMenu.module.scss @@ -50,10 +50,6 @@ width: 1.2rem; height: 1.2rem; border-radius: 1.2rem; - @apply hidden; - &.isShow { - @apply block; - } } @screen md { display: none; diff --git a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx index 4f7e5d21a..803e0b66b 100644 --- a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx +++ b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx @@ -27,7 +27,6 @@ interface Props { children?: any isFull?: boolean isStickyHeader?: boolean - visibleFilter?: boolean openModalLogin: () => void openModalRegister: () => void openModalInfo: () => void @@ -38,7 +37,6 @@ const HeaderMenu = memo( ({ isFull, isStickyHeader, - visibleFilter, openModalLogin, openModalRegister, openModalInfo, @@ -105,12 +103,7 @@ const HeaderMenu = memo( {FILTER_PAGE.includes(router.pathname) && ( )}
    From ee06aff83a92b14508c471a853286af538f9fae2 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Thu, 7 Oct 2021 10:27:07 +0700 Subject: [PATCH 12/12] :sparkles: feat: product filter provider :%s --- src/components/common/Header/Header.tsx | 6 ++- src/components/common/Layout/Layout.tsx | 10 +++-- .../Layout/LayoutContent/LayoutContent.tsx | 13 +----- .../MenuNavigationProductList.tsx | 7 +-- src/components/contexts/FilterContext.tsx | 43 ------------------ .../ProductFilter/ProductFilterContext.tsx | 20 +++++++++ .../ProductFilter/ProductFilterProvider.tsx | 30 +++++++++++++ src/components/contexts/index.ts | 3 ++ .../ProductListFilter.module.scss | 45 ++++++++++--------- .../ProductListFilter/ProductListFilter.tsx | 14 ++++-- 10 files changed, 105 insertions(+), 86 deletions(-) delete mode 100644 src/components/contexts/FilterContext.tsx create mode 100644 src/components/contexts/ProductFilter/ProductFilterContext.tsx create mode 100644 src/components/contexts/ProductFilter/ProductFilterProvider.tsx diff --git a/src/components/common/Header/Header.tsx b/src/components/common/Header/Header.tsx index 4bed36757..092bcc8e7 100644 --- a/src/components/common/Header/Header.tsx +++ b/src/components/common/Header/Header.tsx @@ -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(null) + const { toggleProductFilter: toggleFilter } = useProductFilter() const [isFullHeader, setIsFullHeader] = useState(true) const [isModeAuthenRegister, setIsModeAuthenRegister] = useState(false) const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false }) diff --git a/src/components/common/Layout/Layout.tsx b/src/components/common/Layout/Layout.tsx index ec5a1646d..82853117b 100644 --- a/src/components/common/Layout/Layout.tsx +++ b/src/components/common/Layout/Layout.tsx @@ -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 = ({ children }) => { return ( - - {children} - + + + {children} + + ) diff --git a/src/components/common/Layout/LayoutContent/LayoutContent.tsx b/src/components/common/Layout/LayoutContent/LayoutContent.tsx index 58ff83c95..7bfddafe7 100644 --- a/src/components/common/Layout/LayoutContent/LayoutContent.tsx +++ b/src/components/common/Layout/LayoutContent/LayoutContent.tsx @@ -15,21 +15,12 @@ interface Props { const LayoutContent: FC = ({ 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 ( <>
    -
    +
    { router.pathname === ROUTE.ACCOUNT ?
    @@ -39,7 +30,7 @@ const LayoutContent: FC = ({ children }) => { } { - FILTER_PAGE.includes(router.pathname) && (
    ) + FILTER_PAGE.includes(router.pathname) && (
    ) }
    diff --git a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx index 122e8e3f8..546fbf365 100644 --- a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx +++ b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx @@ -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([]) diff --git a/src/components/contexts/FilterContext.tsx b/src/components/contexts/FilterContext.tsx deleted file mode 100644 index 36a10ce9f..000000000 --- a/src/components/contexts/FilterContext.tsx +++ /dev/null @@ -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(contextDefaultValues); - -export function useAuth() { - return useContext(FilterContext); -} - -type FilterProviderProps = { - children: ReactNode; -}; - -export function FilterProvider({ children }: FilterProviderProps) { - const [visible, setVisible] = useState(false); - - const open = () => { - setVisible(true); - }; - - const close = () => { - setVisible(false); - }; - - const value = { - visible, - open, - close, - }; - return ( - <> - - {children} - - - ); -} \ No newline at end of file diff --git a/src/components/contexts/ProductFilter/ProductFilterContext.tsx b/src/components/contexts/ProductFilter/ProductFilterContext.tsx new file mode 100644 index 000000000..10417f528 --- /dev/null +++ b/src/components/contexts/ProductFilter/ProductFilterContext.tsx @@ -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(DEFAULT_VALUE) + +export function useProductFilter() { + return useContext(ProductFilterContext); +} diff --git a/src/components/contexts/ProductFilter/ProductFilterProvider.tsx b/src/components/contexts/ProductFilter/ProductFilterProvider.tsx new file mode 100644 index 000000000..068b9daab --- /dev/null +++ b/src/components/contexts/ProductFilter/ProductFilterProvider.tsx @@ -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(false); + + const closeProductFilter = () => { + setVisible(false); + }; + + const openProductFilter = () => { + setVisible(true); + }; + + const toggleProductFilter = () => { + setVisible(!visible); + }; + + return ( + <> + + {children} + + + ); +} \ No newline at end of file diff --git a/src/components/contexts/index.ts b/src/components/contexts/index.ts index 795c9161c..38e0c0b49 100644 --- a/src/components/contexts/index.ts +++ b/src/components/contexts/index.ts @@ -3,3 +3,6 @@ export * from './CartDrawer/CartDrawerProvider' export * from './Message/MessageContext' export * from './Message/MessageProvider' + +export * from './ProductFilter/ProductFilterContext' +export * from './ProductFilter/ProductFilterProvider' diff --git a/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss b/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss index 84951b540..d3223831a 100644 --- a/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss +++ b/src/components/modules/product-list/ProductListFilter/ProductListFilter.module.scss @@ -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; } } } diff --git a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx index f99e50c0d..558b9197a 100644 --- a/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx +++ b/src/components/modules/product-list/ProductListFilter/ProductListFilter.tsx @@ -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(true) const [optionQueryProduct, setOptionQueryProduct] = useState({ input: DEFAULT_SEARCH_ARGS }) const { products: productSearchResult, totalItems, loading } = useSearchProducts(optionQueryProduct) @@ -101,14 +103,20 @@ const ProductListFilter = ({ facets, collections, products, total }: ProductList
    - SPECIAL RECIPES +
    + SPECIAL RECIPES + +
    { - (!initialQueryFlag && loading && !productSearchResult) && + (!initialQueryFlag && loading && !productSearchResult) && }