Merge pull request #80 from KieIO/feature/m2-get-favorite-products

Feature/m2 get favorite products
This commit is contained in:
Quangnhankie
2021-10-17 09:29:40 +07:00
committed by GitHub
17 changed files with 189 additions and 60 deletions

View File

@@ -5,7 +5,7 @@ import { normalizeSearchResult } from '../../utils/normalize'
import { getAllProductsQuery } from '../../utils/queries/get-all-products-query' import { getAllProductsQuery } from '../../utils/queries/get-all-products-query'
import { OperationContext } from '@commerce/api/operations' import { OperationContext } from '@commerce/api/operations'
export type ProductVariables = { first?: number, facetValueIds?: string[] } export type ProductVariables = { first?: number, facetValueIds?: string[],collectionSlug?:string }
export default function getAllProductsOperation({ export default function getAllProductsOperation({
commerce, commerce,
@@ -31,13 +31,13 @@ export default function getAllProductsOperation({
input: { input: {
take: vars.first, take: vars.first,
facetValueIds: vars.facetValueIds, facetValueIds: vars.facetValueIds,
collectionSlug: vars.collectionSlug,
groupByProduct: true, groupByProduct: true,
}, },
} }
const { data } = await config.fetch<GetAllProductsQuery>(query, { const { data } = await config.fetch<GetAllProductsQuery>(query, {
variables, variables,
}) })
return { return {
products: data.search.items.map((item) => normalizeSearchResult(item)), products: data.search.items.map((item) => normalizeSearchResult(item)),
totalItems: data.search.totalItems as number, totalItems: data.search.totalItems as number,

View File

@@ -3247,10 +3247,21 @@ export type ActiveCustomerQuery = { __typename?: 'Query' } & {
activeCustomer?: Maybe< activeCustomer?: Maybe<
{ __typename?: 'Customer' } & Pick< { __typename?: 'Customer' } & Pick<
Customer, Customer,
'id' | 'firstName' | 'lastName' | 'emailAddress' | 'addresses' | 'phoneNumber'| 'favorites' | 'orders' Favorite,
'id' | 'firstName' | 'lastName' | 'emailAddress' | 'addresses' | 'phoneNumber'| 'orders'
> >
> >
} }
export type QueryFavorite = {
options: FavoriteListOptions
}
export type FavoriteListOptions = {
skip?: Maybe<Scalars['Int']>
take?: Maybe<Scalars['Int']>
}
export type FavoriteList = PaginatedList & { export type FavoriteList = PaginatedList & {
items: [Favorite!]! items: [Favorite!]!
totalItems: Int! totalItems: Int!
@@ -3264,6 +3275,12 @@ type Favorite = Node & {
customer: Customer! customer: Customer!
} }
type FavouriteOption = Customer & {
favorites(options: FavoriteListOptions): FavoriteList!
}
export type GetAllProductPathsQueryVariables = Exact<{ export type GetAllProductPathsQueryVariables = Exact<{
first?: Maybe<Scalars['Int']> first?: Maybe<Scalars['Int']>
}> }>

View File

@@ -1,6 +1,6 @@
import { Cart } from '@commerce/types/cart' import { Cart } from '@commerce/types/cart'
import { ProductCard } from '@commerce/types/product' import { ProductCard } from '@commerce/types/product'
import { CartFragment, SearchResultFragment } from '../schema' import { CartFragment, SearchResultFragment,Favorite,ActiveCustomerQuery } from '../schema'
export function normalizeSearchResult(item: SearchResultFragment): ProductCard { export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
return { return {
@@ -21,6 +21,18 @@ export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
} }
} }
export function normalizeFavoriteProductResult(item: Favorite) {
return {
id: item.product.id,
name: item.product.name,
slug: item.product.slug,
imageSrc: item.product.assets[0].preview ? item.product.assets[0].preview + '?w=800&mode=crop' : '',
price: item.product.variants[0].priceWithTax as number / 100,
currencyCode: item.product.variants[0].currencyCode,
}
}
export function normalizeCart(order: CartFragment): Cart { export function normalizeCart(order: CartFragment): Cart {
return { return {
id: order.id.toString(), id: order.id.toString(),

View File

@@ -1,5 +1,3 @@
import { searchResultFragment } from '../fragments/search-result-fragment'
export const activeCustomerQuery = /* GraphQL */ ` export const activeCustomerQuery = /* GraphQL */ `
query activeCustomer { query activeCustomer {
activeCustomer { activeCustomer {
@@ -11,15 +9,6 @@ query activeCustomer {
items{ items{
product{ product{
id id
name
slug
assets{
source
preview
}
variants{
price
}
} }
} }
} }
@@ -29,7 +18,7 @@ query activeCustomer {
city city
province province
postalCode postalCode
} }
} }
} }
` `

View File

@@ -0,0 +1,28 @@
export const getFavoriteProductQuery = /* GraphQL */ `
query activeCustomer($options: FavoriteListOptions) {
activeCustomer {
id
firstName
lastName
emailAddress
favorites(options: $options){
items{
product{
id
name
slug
assets{
source
preview
}
variants{
priceWithTax
currencyCode
}
}
}
totalItems
}
}
}
`

View File

@@ -6,7 +6,7 @@ import { GetStaticPropsContext } from 'next';
import { Layout } from 'src/components/common'; import { Layout } from 'src/components/common';
import { FeaturedProductsCarousel, FreshProducts, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home'; import { FeaturedProductsCarousel, FreshProducts, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home';
import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice'; import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice';
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED } from 'src/utils/constanst.utils'; import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED,COLLECTION_SLUG_SPICE } from 'src/utils/constanst.utils';
import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils'; import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils';
import { PromiseWithKey } from 'src/utils/types.utils'; import { PromiseWithKey } from 'src/utils/types.utils';
@@ -15,11 +15,12 @@ interface Props {
freshProducts: ProductCard[], freshProducts: ProductCard[],
featuredProducts: ProductCard[], featuredProducts: ProductCard[],
collections: Collection[] collections: Collection[]
spiceProducts:ProductCard[]
} }
export default function Home({ featuredAndDiscountFacetsValue, export default function Home({ featuredAndDiscountFacetsValue,
freshProducts, featuredProducts, freshProducts, featuredProducts,
collections }: Props) { collections,spiceProducts }: Props) {
return ( return (
<> <>
<HomeBanner /> <HomeBanner />
@@ -28,7 +29,7 @@ export default function Home({ featuredAndDiscountFacetsValue,
<FreshProducts data={freshProducts} collections={collections} /> <FreshProducts data={freshProducts} collections={collections} />
<HomeCollection /> <HomeCollection />
<HomeVideo /> <HomeVideo />
<HomeSpice /> {spiceProducts.length>0 && <HomeSpice data={spiceProducts}/>}
<FeaturedProductsCarousel data={featuredProducts} featuredFacetsValue={featuredAndDiscountFacetsValue} /> <FeaturedProductsCarousel data={featuredProducts} featuredFacetsValue={featuredAndDiscountFacetsValue} />
<HomeCTA /> <HomeCTA />
<HomeRecipe /> <HomeRecipe />
@@ -99,16 +100,24 @@ export async function getStaticProps({
}) })
promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' }) promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' })
// spiceProducts
const spiceProducts = commerce.getAllProducts({
variables: {
collectionSlug: COLLECTION_SLUG_SPICE,
},
config,
preview,
})
promisesWithKey.push({ key: 'spiceProducts', promise: spiceProducts, keyResult: 'products' })
try { try {
const promises = getAllPromies(promisesWithKey) const promises = getAllPromies(promisesWithKey)
const rs = await Promise.all(promises) const rs = await Promise.all(promises)
promisesWithKey.map((item, index) => { promisesWithKey.map((item, index) => {
props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index] props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index]
return null return null
}) })
return { return {
props, props,
revalidate: 60, revalidate: 60,

View File

@@ -14,10 +14,10 @@ interface ProductListProps {
onPageChange?: (page: number) => void onPageChange?: (page: number) => void
} }
const ProductList = ({ data, total = data.length, defaultCurrentPage, onPageChange }: ProductListProps) => { const ProductList = ({ data, total = data?.length, defaultCurrentPage, onPageChange }: ProductListProps) => {
const router = useRouter() const router = useRouter()
const {wishlistId } = useActiveCustomer(); const {wishlistId } = useActiveCustomer();
const handlePageChange = (page: number) => { const handlePageChange = (page: number) => {
onPageChange && onPageChange(page) onPageChange && onPageChange(page)
} }
@@ -34,20 +34,20 @@ const ProductList = ({ data, total = data.length, defaultCurrentPage, onPageChan
<div className={s.wrapper}> <div className={s.wrapper}>
<div className={s.list}> <div className={s.list}>
{ {
data.map((product, index) => { data?.map((product, index) => {
let activeWishlist = wishlistId?.findIndex((val:string) => val == product.id) !== -1; let activeWishlist = wishlistId?.findIndex((val:string) => val == product.id) !== -1;
return <ProductCard activeWishlist={activeWishlist} {...product} key={index} /> return <ProductCard activeWishlist={activeWishlist} {...product} key={index} />
}) })
} }
{ {
data.length === 0 && <div className={s.empty}> data?.length === 0 && <div className={s.empty}>
<EmptyCommon /> <EmptyCommon />
<ButtonCommon onClick={handleShowAllProduct}>Show all products</ButtonCommon> <ButtonCommon onClick={handleShowAllProduct}>Show all products</ButtonCommon>
</div> </div>
} }
</div> </div>
<div className={classNames(s.pagination, { [s.hide]: data.length === 0 })}> <div className={classNames(s.pagination, { [s.hide]: data?.length === 0 })}>
<PaginationCommon defaultCurrent={defaultCurrentPage} total={total} pageSize={DEFAULT_PAGE_SIZE} onChange={handlePageChange} /> <PaginationCommon defaultCurrent={defaultCurrentPage} total={total ?? 0} pageSize={DEFAULT_PAGE_SIZE} onChange={handlePageChange} />
</div> </div>
</div> </div>
) )

View File

@@ -1,3 +1,4 @@
export { default as useGetFavoriteProduct } from './useGetFavoriteProduct'
export { default as useGetUserOrder } from './useGetUserOrder'; export { default as useGetUserOrder } from './useGetUserOrder';
export { default as useEditUserInfo } from './useEditUserInfo' export { default as useEditUserInfo } from './useEditUserInfo'
export { default as useEditCustomerAddress } from './useEditCustomerAddress' export { default as useEditCustomerAddress } from './useEditCustomerAddress'

View File

@@ -0,0 +1,16 @@
import { ActiveCustomerQuery,QueryFavorite,Favorite } from '@framework/schema'
import { normalizeFavoriteProductResult } from '@framework/utils/normalize'
import { getFavoriteProductQuery } from '@framework/utils/queries/get-favorite-product-query'
import gglFetcher from 'src/utils/gglFetcher'
import useSWR from 'swr'
const useGetFavoriteProduct = (options?:QueryFavorite) => {
const { data, ...rest } = useSWR<ActiveCustomerQuery>([getFavoriteProductQuery, options], gglFetcher)
return {
itemWishlist: data?.activeCustomer?.favorites?.items?.map((item:Favorite) => normalizeFavoriteProductResult(item)),
totalItems: data?.activeCustomer?.favorites?.totalItems,
...rest
}
}
export default useGetFavoriteProduct

View File

@@ -5,7 +5,6 @@ import useSWR from 'swr'
const useActiveCustomer = () => { const useActiveCustomer = () => {
const { data, ...rest } = useSWR<ActiveCustomerQuery>([activeCustomerQuery], gglFetcher) const { data, ...rest } = useSWR<ActiveCustomerQuery>([activeCustomerQuery], gglFetcher)
return { return {
customer: data?.activeCustomer, customer: data?.activeCustomer,
userInfo:{ userInfo:{
@@ -15,7 +14,6 @@ const useActiveCustomer = () => {
phoneNumber: data?.activeCustomer?.phoneNumber, phoneNumber: data?.activeCustomer?.phoneNumber,
address: data?.activeCustomer?.addresses?.[0] address: data?.activeCustomer?.addresses?.[0]
}, },
itemWishlist:data?.activeCustomer?.favorites?.items,
wishlistId: data?.activeCustomer?.favorites?.items.map((val:Favorite)=>val.product.id), wishlistId: data?.activeCustomer?.favorites?.items.map((val:Favorite)=>val.product.id),
...rest ...rest
} }

View File

@@ -1,5 +1,5 @@
import { useState } from 'react' import { useState } from 'react'
import useActiveCustomer from '../auth/useActiveCustomer' import useGetFavoriteProduct from '../account/useGetFavoriteProduct'
import { FavoriteList } from '@framework/schema' import { FavoriteList } from '@framework/schema'
import fetcher from 'src/utils/fetcher' import fetcher from 'src/utils/fetcher'
import { CommonError } from 'src/domains/interfaces/CommonError' import { CommonError } from 'src/domains/interfaces/CommonError'
@@ -12,7 +12,7 @@ interface Props {
const useToggleProductWishlist = () => { const useToggleProductWishlist = () => {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null) const [error, setError] = useState<Error | null>(null)
const { mutate } = useActiveCustomer() const { mutate } = useGetFavoriteProduct();
const onToggleProductWishlist = ( const onToggleProductWishlist = (
{ productId }:Props , { productId }:Props ,
@@ -29,7 +29,7 @@ const useToggleProductWishlist = () => {
.then((data) => { .then((data) => {
mutate() mutate()
fCallBack(true) fCallBack(true)
return data return data
}) })
.catch((error) => { .catch((error) => {
setError(error) setError(error)

View File

@@ -4,7 +4,17 @@
@apply bg-background-gray; @apply bg-background-gray;
padding: 3.2rem 2rem; padding: 3.2rem 2rem;
min-height: 70rem; min-height: 70rem;
@screen xl {
section{
div{
div{
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
}
}
}
}
@screen md { @screen md {
padding-left: 2.8rem; padding-left: 2.8rem;
padding-right: 2.8rem; padding-right: 2.8rem;
@@ -28,4 +38,5 @@
margin-bottom: 3.8rem; margin-bottom: 3.8rem;
} }
} }
} }

View File

@@ -1,20 +1,31 @@
import { QueryFavorite } from "@framework/schema"
import { useRouter } from "next/router"
import React, { useEffect, useState } from "react" import React, { useEffect, useState } from "react"
import s from './AccountPage.module.scss'
import { HeadingCommon, TabPane } from "src/components/common" import { HeadingCommon, TabPane } from "src/components/common"
import { useGetFavoriteProduct, useGetUserOrder } from 'src/components/hooks/account'
import { useActiveCustomer } from 'src/components/hooks/auth'
import { ACCOUNT_TAB, DEFAULT_PAGE_SIZE, QUERY_KEY } from "src/utils/constanst.utils"
import { getPageFromQuery } from 'src/utils/funtion.utils'
import AccountNavigation from '../AccountNavigation/AccountNavigation' import AccountNavigation from '../AccountNavigation/AccountNavigation'
import s from './AccountPage.module.scss'
import AccountInfomation from "./components/AccountInfomation/AccountInfomation" import AccountInfomation from "./components/AccountInfomation/AccountInfomation"
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
import FavouriteProducts from "./components/FavouriteProducts/FavouriteProducts" import FavouriteProducts from "./components/FavouriteProducts/FavouriteProducts"
import OrderInfomation from './components/OrderInformation/OrderInformation' import OrderInfomation from './components/OrderInformation/OrderInformation'
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data'; const waiting = [
import { ACCOUNT_TAB, QUERY_KEY } from "src/utils/constanst.utils" {
import { useRouter } from "next/router" id: "NO 123456",
import { useActiveCustomer} from 'src/components/hooks/auth' products: ["Tomato", "Fish", "Pork", "Onion"],
import { useGetUserOrder} from 'src/components/hooks/account' totalPrice : 1000
import { AccountProps } from "./components/AccountInfomation/AccountInfomation" },
{
id: "NO 123457",
products: ["Tomato", "Fish", "Pork", "Onion"],
totalPrice : 1000
}
]
const delivering = [ const delivering = [
@@ -59,6 +70,13 @@ const getTabIndex = (tab?: string): number => {
} }
} }
const DEFAULT_FAVORITE_ARGS = {
options:{
skip:1, take:DEFAULT_PAGE_SIZE
}
}
const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => { const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
const router = useRouter() const router = useRouter()
@@ -69,9 +87,20 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2) const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2)
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
// const { itemWishlist } = useActiveCustomer(); const [optionQueryFavorite, setoptionQueryFavorite] = useState<QueryFavorite>(DEFAULT_FAVORITE_ARGS)
// console.log(itemWishlist)
const { itemWishlist,totalItems }= useGetFavoriteProduct(optionQueryFavorite);
// skip
useEffect(() => {
const query = { ...DEFAULT_FAVORITE_ARGS } as QueryFavorite;
const page = getPageFromQuery(router.query[QUERY_KEY.PAGE] as string);
query.options.skip = page * DEFAULT_PAGE_SIZE;
setoptionQueryFavorite(query);
},[router.query])
useEffect(() => { useEffect(() => {
const query = router.query[QUERY_KEY.TAB] as string const query = router.query[QUERY_KEY.TAB] as string
const index = getTabIndex(query) const index = getTabIndex(query)
@@ -101,7 +130,7 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
<OrderInfomation addingItem={addingItem} arrangingPayment={arrangingPayment} cancelled={cancelled} /> <OrderInfomation addingItem={addingItem} arrangingPayment={arrangingPayment} cancelled={cancelled} />
</TabPane> </TabPane>
<TabPane tabName="Favourite"> <TabPane tabName="Favourite">
<FavouriteProducts products={PRODUCT_CART_DATA_TEST} /> <FavouriteProducts products={itemWishlist} totalItems={totalItems} />
</TabPane> </TabPane>
</AccountNavigation> </AccountNavigation>
</section> </section>
@@ -110,4 +139,5 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
) )
} }
export default AccountPage export default AccountPage

View File

@@ -1,18 +1,34 @@
import React from "react" import { useRouter } from 'next/router'
import s from './FavouriteProducts.module.scss' import React, { useState } from "react"
import {ProductList} from '../../../../../common' import { QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import { ProductList } from '../../../../../common'
import { ProductCardProps } from '../../../../../common/ProductCard/ProductCard' import { ProductCardProps } from '../../../../../common/ProductCard/ProductCard'
import s from './FavouriteProducts.module.scss'
interface FavouriteProductsProps { interface FavouriteProductsProps {
products: ProductCardProps[]; products: ProductCardProps[],
totalItems:number
} }
const FavouriteProducts = ({ products } : FavouriteProductsProps) => { const FavouriteProducts = ({ products,totalItems } : FavouriteProductsProps) => {
const router = useRouter()
const [currentPage, setCurrentPage] = useState(0);
function onPageChange(page:number){
setCurrentPage(page)
router.push({
pathname: ROUTE.ACCOUNT,
query: {
...router.query,
[QUERY_KEY.PAGE]: page
}
},
undefined, { shallow: true }
)
}
return ( return (
<section className={s.favouriteProducts}> <section className={s.favouriteProducts}>
<ProductList data={products} /> <ProductList data={products} total={totalItems} onPageChange={onPageChange}/>
</section> </section>
) )
} }

View File

@@ -10,6 +10,7 @@ interface FreshProductsProps {
} }
const FreshProducts = ({ data, collections }: FreshProductsProps) => { const FreshProducts = ({ data, collections }: FreshProductsProps) => {
const dataWithCategory = useMemo(() => { const dataWithCategory = useMemo(() => {
return data.map(item => { return data.map(item => {
return { return {

View File

@@ -2,16 +2,16 @@ import React from 'react'
import { ProductCarousel } from 'src/components/common' import { ProductCarousel } from 'src/components/common'
import { SPICE_DATA_TEST } from "../../../../utils/demo-data" import { SPICE_DATA_TEST } from "../../../../utils/demo-data"
import s from './HomeSpice.module.scss' import s from './HomeSpice.module.scss'
import { ProductCard } from '@commerce/types/product'
interface HomeSpice { interface HomeSpice {
data: ProductCard[]
} }
const HomeSpice = ({}: HomeSpice) => { const HomeSpice = ({data}: HomeSpice) => {
return ( return (
<div className={s.homeSpiceWarpper}> <div className={s.homeSpiceWarpper}>
<ProductCarousel data={SPICE_DATA_TEST} itemKey="product-7"/> <ProductCarousel data={data} itemKey="product-7"/>
</div> </div>
) )
} }

View File

@@ -184,3 +184,4 @@ export const STATE_OPTIONS = [
}, },
] ]
export const COLLECTION_SLUG_SPICE ="spice";