mirror of
https://github.com/vercel/commerce.git
synced 2025-07-26 19:51:23 +00:00
🔀 merge: branch 'release-stable' of https://github.com/KieIO/grocery-vercel-commerce into checkout
:%s
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { normalizeCart } from '@framework/utils/normalize';
|
||||
import React from 'react';
|
||||
import { useCartDrawer } from 'src/components/contexts';
|
||||
import useGetActiveOrder from 'src/components/hooks/cart/useGetActiveOrder';
|
||||
import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data';
|
||||
import { DrawerCommon } from '..';
|
||||
import s from './CartDrawer.module.scss';
|
||||
@@ -14,14 +16,15 @@ interface Props {
|
||||
|
||||
const CartDrawer = ({ }: Props) => {
|
||||
const { cartVisible, closeCartDrawer } = useCartDrawer()
|
||||
const {order} = useGetActiveOrder()
|
||||
return (
|
||||
<DrawerCommon
|
||||
title={`Your cart (${PRODUCT_CART_DATA_TEST.length})`}
|
||||
title={`Your cart (${order?.lineItems.length})`}
|
||||
visible={cartVisible}
|
||||
onClose={closeCartDrawer}>
|
||||
<div className={s.cartDrawer}>
|
||||
<div className={s.body}>
|
||||
<ProductsInCart data={PRODUCT_CART_DATA_TEST} />
|
||||
<ProductsInCart data={order?.lineItems||[]} currency={order?.currency||{code:"USA"}}/>
|
||||
<CartRecommendation />
|
||||
</div>
|
||||
<div>
|
||||
|
@@ -1,25 +1,64 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { QuanittyInput } from 'src/components/common';
|
||||
import { IconDelete } from 'src/components/icons';
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
import { ProductProps } from 'src/utils/types.utils';
|
||||
import ImgWithLink from '../../../ImgWithLink/ImgWithLink';
|
||||
import LabelCommon from '../../../LabelCommon/LabelCommon';
|
||||
import s from './ProductCartItem.module.scss';
|
||||
import { ModalConfirm, QuanittyInput } from 'src/components/common'
|
||||
import { IconDelete } from 'src/components/icons'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import ImgWithLink from '../../../ImgWithLink/ImgWithLink'
|
||||
import LabelCommon from '../../../LabelCommon/LabelCommon'
|
||||
import s from './ProductCartItem.module.scss'
|
||||
import { LineItem } from '@commerce/types/cart'
|
||||
import { useUpdateProductInCart } from 'src/components/hooks/cart'
|
||||
import { debounce } from 'lodash'
|
||||
import useRemoveProductInCart from 'src/components/hooks/cart/useRemoveProductInCart'
|
||||
|
||||
export interface ProductCartItempProps extends ProductProps {
|
||||
quantity: number,
|
||||
export interface ProductCartItempProps extends LineItem {
|
||||
currency: { code: string }
|
||||
}
|
||||
|
||||
const ProductCartItem = ({ name, slug, weight, price, oldPrice, discount, imageSrc, quantity }: ProductCartItempProps) => {
|
||||
const ProductCartItem = ({
|
||||
slug,
|
||||
discounts,
|
||||
quantity,
|
||||
variant,
|
||||
name,
|
||||
currency,
|
||||
id
|
||||
}: ProductCartItempProps) => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const {updateProduct} = useUpdateProductInCart()
|
||||
const {removeProduct, loading} = useRemoveProductInCart()
|
||||
const handleQuantityChangeCallback = (isSuccess:boolean,mess?:string) => {
|
||||
if(!isSuccess){
|
||||
console.log(mess)
|
||||
}
|
||||
}
|
||||
const handleRemoveCallback = (isSuccess:boolean,mess?:string) => {
|
||||
if(!isSuccess){
|
||||
console.log(mess)
|
||||
}else{
|
||||
setVisible(false)
|
||||
}
|
||||
}
|
||||
const handleQuantityChange = (value:number) => {
|
||||
updateProduct({orderLineId:id,quantity:value},handleQuantityChangeCallback)
|
||||
}
|
||||
const debounceFn = useCallback(debounce(handleQuantityChange, 500), []);
|
||||
const handleCancel = () => {
|
||||
setVisible(false)
|
||||
}
|
||||
const handleOpen = () => {
|
||||
setVisible(true)
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
removeProduct({orderLineId:id},handleRemoveCallback)
|
||||
}
|
||||
return (
|
||||
<div className={s.productCartItem}>
|
||||
<div className={s.info}>
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a href="">
|
||||
<div className={s.imgWrap}>
|
||||
<ImgWithLink src={imageSrc} alt={name} />
|
||||
<ImgWithLink src={variant?.image?.url ?? ''} alt={name} />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
@@ -27,30 +66,32 @@ const ProductCartItem = ({ name, slug, weight, price, oldPrice, discount, imageS
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.name}>
|
||||
{name} {weight ? `(${weight})` : ''}
|
||||
{name} {variant?.weight ? `(${variant.weight})` : ''}
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.price}>
|
||||
{
|
||||
oldPrice &&
|
||||
<div className={s.old}>
|
||||
<span className={s.number}>{oldPrice}</span>
|
||||
<LabelCommon type='discount'>{discount}</LabelCommon>
|
||||
</div>
|
||||
}
|
||||
<div className={s.current}>{price}</div>
|
||||
{discounts.length > 0 && (
|
||||
<div className={s.old}>
|
||||
{/* <span className={s.number}>{oldPrice}</span> */}
|
||||
<LabelCommon type="discount">{discounts[0]}</LabelCommon>
|
||||
</div>
|
||||
)}
|
||||
<div className={s.current}>{variant?.price} {currency?.code}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.actions}>
|
||||
<div className={s.iconDelete}>
|
||||
<div className={s.iconDelete} onClick={handleOpen}>
|
||||
<IconDelete />
|
||||
</div>
|
||||
<QuanittyInput size='small' initValue={quantity} />
|
||||
<QuanittyInput size="small" initValue={quantity} onChange={debounceFn}/>
|
||||
</div>
|
||||
<ModalConfirm visible={visible} onClose={handleCancel} onCancel={handleCancel} onOk={handleConfirm} loading={loading}>
|
||||
Are you sure want to remove {name} form your cart
|
||||
</ModalConfirm>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCartItem;
|
||||
export default ProductCartItem
|
||||
|
@@ -1,25 +1,21 @@
|
||||
import { LineItem } from '@commerce/types/cart';
|
||||
import React from 'react';
|
||||
import ProductCartItem, { ProductCartItempProps } from '../ProductCartItem/ProductCartItem';
|
||||
import s from './ProductsInCart.module.scss';
|
||||
|
||||
interface Props {
|
||||
data: ProductCartItempProps[]
|
||||
data: LineItem[]
|
||||
currency: { code: string }
|
||||
}
|
||||
|
||||
const ProductsInCart = ({ data }: Props) => {
|
||||
const ProductsInCart = ({ data, currency }: Props) => {
|
||||
return (
|
||||
<ul className={s.productsInCart}>
|
||||
{
|
||||
data.map(item => <li key={item.name}>
|
||||
<ProductCartItem
|
||||
name={item.name}
|
||||
slug={item.slug}
|
||||
weight={item.weight}
|
||||
price={item.price}
|
||||
oldPrice={item.oldPrice}
|
||||
discount={item.discount}
|
||||
imageSrc={item.imageSrc}
|
||||
quantity={item.quantity}
|
||||
currency = {currency}
|
||||
{...item}
|
||||
/>
|
||||
</li>)
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import classNames from 'classnames';
|
||||
import { route } from 'next/dist/server/router';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import s from './MenuFilterItem.module.scss';
|
||||
|
@@ -5,6 +5,7 @@ import s from './ModalConfirm.module.scss'
|
||||
interface ModalConfirmProps extends ModalCommonProps {
|
||||
okText?: String
|
||||
cancelText?: String
|
||||
loading?:boolean
|
||||
onOk?: () => void
|
||||
onCancel?: () => void
|
||||
}
|
||||
@@ -16,6 +17,7 @@ const ModalConfirm = ({
|
||||
onCancel,
|
||||
children,
|
||||
title = 'Confirm',
|
||||
loading,
|
||||
...props
|
||||
}: ModalConfirmProps) => {
|
||||
return (
|
||||
@@ -25,7 +27,7 @@ const ModalConfirm = ({
|
||||
<div className="mr-4">
|
||||
<ButtonCommon onClick={onCancel} type="light"> {cancelText}</ButtonCommon>
|
||||
</div>
|
||||
<ButtonCommon onClick={onOk}>{okText}</ButtonCommon>
|
||||
<ButtonCommon onClick={onOk} loading={loading}>{okText}</ButtonCommon>
|
||||
</div>
|
||||
</ModalCommon>
|
||||
)
|
||||
|
@@ -10,7 +10,9 @@ import ItemWishList from '../ItemWishList/ItemWishList'
|
||||
import LabelCommon from '../LabelCommon/LabelCommon'
|
||||
import s from './ProductCard.module.scss'
|
||||
import ProductNotSell from './ProductNotSell/ProductNotSell'
|
||||
|
||||
import {useAddProductToCart} from "../../hooks/cart"
|
||||
import { useCartDrawer } from 'src/components/contexts'
|
||||
import Router from 'next/router'
|
||||
export interface ProductCardProps extends ProductCard {
|
||||
buttonText?: string
|
||||
isSingleButton?: boolean,
|
||||
@@ -29,14 +31,41 @@ const ProductCardComponent = ({
|
||||
imageSrc,
|
||||
isNotSell,
|
||||
isSingleButton,
|
||||
productVariantId,
|
||||
productVariantName,
|
||||
activeWishlist
|
||||
}: ProductCardProps) => {
|
||||
|
||||
const {addProduct,loading} = useAddProductToCart()
|
||||
const { openCartDrawer } = useCartDrawer()
|
||||
|
||||
const handleAddToCart = () => {
|
||||
if(productVariantId){
|
||||
addProduct({variantId:productVariantId,quantity:1},handleAddToCartCallback)
|
||||
}
|
||||
}
|
||||
const handleAddToCartCallback = () => {
|
||||
openCartDrawer && openCartDrawer()
|
||||
}
|
||||
|
||||
const handleBuyNowCallback = (success:boolean) => {
|
||||
if(success){
|
||||
Router.push(ROUTE.CHECKOUT)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBuyNow = () => {
|
||||
if(productVariantId){
|
||||
addProduct({variantId:productVariantId,quantity:1},handleBuyNowCallback)
|
||||
}
|
||||
}
|
||||
|
||||
if (isNotSell) {
|
||||
return <div className={`${s.productCardWarpper} ${s.notSell}`}>
|
||||
<ProductNotSell name={name} imageSrc={imageSrc} />
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={s.productCardWarpper}>
|
||||
@@ -59,7 +88,7 @@ const ProductCardComponent = ({
|
||||
<div className={s.cardMidTop}>
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.productname}>{name} </div>
|
||||
<div className={s.productname}>{productVariantName} </div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.productWeight}>{weight}</div>
|
||||
@@ -75,15 +104,15 @@ const ProductCardComponent = ({
|
||||
{
|
||||
isSingleButton ?
|
||||
<div className={s.cardButton}>
|
||||
<ButtonCommon type="light" icon={<IconBuy />} size='small'>Add to cart</ButtonCommon>
|
||||
<ButtonCommon type="light" icon={<IconBuy />} size='small' onClick={handleAddToCart}>Add to cart</ButtonCommon>
|
||||
</div>
|
||||
:
|
||||
<>
|
||||
<div className={s.cardIcon}>
|
||||
<ButtonIconBuy/>
|
||||
<div className={s.cardIcon} >
|
||||
<ButtonIconBuy onClick={handleAddToCart} loading={loading}/>
|
||||
</div>
|
||||
<div className={s.cardButton}>
|
||||
<ButtonCommon type="light" size='small'>{buttonText}</ButtonCommon>
|
||||
<ButtonCommon type="light" size='small' onClick={handleBuyNow}>{buttonText}</ButtonCommon>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
@@ -26,10 +26,6 @@ const QuanittyInput = ({
|
||||
}: QuanittyInputProps) => {
|
||||
const [value, setValue] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
onChange && onChange(value)
|
||||
}, [value])
|
||||
|
||||
useEffect(() => {
|
||||
initValue && setValue(initValue)
|
||||
}, [initValue])
|
||||
@@ -37,16 +33,20 @@ const QuanittyInput = ({
|
||||
const onPlusClick = () => {
|
||||
if (max && value + step > max) {
|
||||
setValue(max)
|
||||
onChange && onChange(max)
|
||||
} else {
|
||||
setValue(value + step)
|
||||
onChange && onChange(value + step)
|
||||
}
|
||||
}
|
||||
|
||||
const onMinusClick = () => {
|
||||
if (min && value - step < min) {
|
||||
setValue(min)
|
||||
onChange && onChange(min)
|
||||
} else {
|
||||
setValue(value - step)
|
||||
onChange && onChange(value - step)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,13 @@ const QuanittyInput = ({
|
||||
let value = Number(e.target.value) || 0
|
||||
if (min && value < min) {
|
||||
setValue(min)
|
||||
onChange && onChange(min)
|
||||
} else if (max && value > max) {
|
||||
setValue(max)
|
||||
onChange && onChange(max)
|
||||
} else {
|
||||
setValue(value)
|
||||
onChange && onChange(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
3
src/components/hooks/cart/index.ts
Normal file
3
src/components/hooks/cart/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as useAddProductToCart } from './useAddProductToCart'
|
||||
export { default as useUpdateProductInCart } from './useUpdateProductInCart'
|
||||
export { default as useGetActiveOrder } from './useGetActiveOrder'
|
40
src/components/hooks/cart/useAddProductToCart.tsx
Normal file
40
src/components/hooks/cart/useAddProductToCart.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useState } from 'react'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import { AddItemToOrderMutation, AddItemToOrderMutationVariables } from '@framework/schema'
|
||||
import { errorMapping } from 'src/utils/errrorMapping'
|
||||
import { useGetActiveOrder } from '.'
|
||||
import { addItemToOrderMutation } from '@framework/utils/mutations/add-item-to-order-mutation'
|
||||
|
||||
const useAddProductToCart = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<CommonError | null>(null)
|
||||
const { mutate } = useGetActiveOrder()
|
||||
|
||||
const addProduct = (options:AddItemToOrderMutationVariables,
|
||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||
) => {
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
rawFetcher<AddItemToOrderMutation>({
|
||||
query: addItemToOrderMutation ,
|
||||
variables: options,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.addItemToOrder.__typename !== "Order") {
|
||||
throw CommonError.create(errorMapping(data.addItemToOrder.message), data.addItemToOrder.errorCode)
|
||||
}
|
||||
mutate()
|
||||
fCallBack(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(error)
|
||||
fCallBack(false, error.message)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return { loading, addProduct, error }
|
||||
}
|
||||
|
||||
export default useAddProductToCart
|
23
src/components/hooks/cart/useGetActiveOrder.tsx
Normal file
23
src/components/hooks/cart/useGetActiveOrder.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Cart } from '@commerce/types/cart'
|
||||
import { ActiveOrderQuery } from '@framework/schema'
|
||||
import { cartFragment } from '@framework/utils/fragments/cart-fragment'
|
||||
import { normalizeCart } from '@framework/utils/normalize'
|
||||
import { gql } from 'graphql-request'
|
||||
import gglFetcher from 'src/utils/gglFetcher'
|
||||
import useSWR from 'swr'
|
||||
const query = gql`
|
||||
query activeOrder {
|
||||
activeOrder {
|
||||
...Cart
|
||||
}
|
||||
}
|
||||
${ cartFragment }
|
||||
`
|
||||
|
||||
const useGetActiveOrder = () => {
|
||||
const { data, ...rest } = useSWR<ActiveOrderQuery>([query], gglFetcher)
|
||||
return { order: data?.activeOrder ? normalizeCart(data!.activeOrder) : null, ...rest }
|
||||
|
||||
}
|
||||
|
||||
export default useGetActiveOrder
|
41
src/components/hooks/cart/useRemoveProductInCart.tsx
Normal file
41
src/components/hooks/cart/useRemoveProductInCart.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useState } from 'react'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import { AdjustOrderLineMutationVariables,AdjustOrderLineMutation, RemoveOrderLineMutation, RemoveOrderLineMutationVariables } from '@framework/schema'
|
||||
import { errorMapping } from 'src/utils/errrorMapping'
|
||||
import { useGetActiveOrder } from '.'
|
||||
import { adjustOrderLineMutation } from '@framework/utils/mutations/adjust-order-line-mutation'
|
||||
import { removeOrderLineMutation } from '@framework/utils/mutations/remove-order-line-mutation'
|
||||
|
||||
const useRemoveProductInCart = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<CommonError | null>(null)
|
||||
const { mutate } = useGetActiveOrder()
|
||||
|
||||
const removeProduct = (options:RemoveOrderLineMutationVariables,
|
||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||
) => {
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
rawFetcher<RemoveOrderLineMutation>({
|
||||
query: removeOrderLineMutation ,
|
||||
variables: options,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.removeOrderLine.__typename !== "Order") {
|
||||
throw CommonError.create(errorMapping(data.removeOrderLine.message), data.removeOrderLine.errorCode)
|
||||
}
|
||||
mutate()
|
||||
fCallBack(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(error)
|
||||
fCallBack(false, error.message)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return { loading, removeProduct, error }
|
||||
}
|
||||
|
||||
export default useRemoveProductInCart
|
40
src/components/hooks/cart/useUpdateProductInCart.tsx
Normal file
40
src/components/hooks/cart/useUpdateProductInCart.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useState } from 'react'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import { AdjustOrderLineMutationVariables,AdjustOrderLineMutation } from '@framework/schema'
|
||||
import { errorMapping } from 'src/utils/errrorMapping'
|
||||
import { useGetActiveOrder } from '.'
|
||||
import { adjustOrderLineMutation } from '@framework/utils/mutations/adjust-order-line-mutation'
|
||||
|
||||
const useUpdateProductInCart = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<CommonError | null>(null)
|
||||
const { mutate } = useGetActiveOrder()
|
||||
|
||||
const updateProduct = (options:AdjustOrderLineMutationVariables,
|
||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||
) => {
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
rawFetcher<AdjustOrderLineMutation>({
|
||||
query: adjustOrderLineMutation ,
|
||||
variables: options,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.adjustOrderLine.__typename !== "Order") {
|
||||
throw CommonError.create(errorMapping(data.adjustOrderLine.message), data.adjustOrderLine.errorCode)
|
||||
}
|
||||
mutate()
|
||||
fCallBack(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(error)
|
||||
fCallBack(false, error.message)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return { loading, updateProduct, error }
|
||||
}
|
||||
|
||||
export default useUpdateProductInCart
|
47
src/components/hooks/useGetProductListByCollection.tsx
Normal file
47
src/components/hooks/useGetProductListByCollection.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
// import { gql } from 'graphql-request'
|
||||
import { useMemo, useState } from 'react'
|
||||
// import useActiveCustomer from './useActiveCustomer'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import {
|
||||
CollectionList,
|
||||
CollectionListOptions,
|
||||
GetCollectionsQuery,
|
||||
GetCollectionsQueryVariables,
|
||||
LoginMutation,
|
||||
} from '@framework/schema'
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
import { getCollectionsQuery } from '@framework/utils/queries/get-collections-query'
|
||||
import useSWR from 'swr'
|
||||
import gglFetcher from 'src/utils/gglFetcher'
|
||||
|
||||
const query = gql`
|
||||
query getCollections($option: CollectionListOptions) {
|
||||
collections(options:$option) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
description
|
||||
slug
|
||||
productVariants {
|
||||
totalItems
|
||||
}
|
||||
parent {
|
||||
id
|
||||
}
|
||||
children {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const useGetProductListByCollection = (options: any) => {
|
||||
|
||||
const { data, ...rest } = useSWR<GetCollectionsQuery>([query, options], gglFetcher)
|
||||
return { collections: data?.collections, ...rest }
|
||||
}
|
||||
|
||||
export default useGetProductListByCollection
|
36
src/components/hooks/useLocalStorage.tsx
Normal file
36
src/components/hooks/useLocalStorage.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useState } from "react";
|
||||
|
||||
// Hook
|
||||
export function useLocalStorage<T>(key: string, initialValue: T) {
|
||||
// State to store our value
|
||||
// Pass initial state function to useState so logic is only executed once
|
||||
const [storedValue, setStoredValue] = useState<T>(() => {
|
||||
try {
|
||||
// Get from local storage by key
|
||||
const item = localStorage.getItem(key);
|
||||
// Parse stored json or if none return initialValue
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
} catch (error) {
|
||||
// If error also return initialValue
|
||||
// console.log(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
// Return a wrapped version of useState's setter function that ...
|
||||
// ... persists the new value to localStorage.
|
||||
const setValue = (value: T | ((val: T) => T)) => {
|
||||
try {
|
||||
// Allow value to be a function so we have same API as useState
|
||||
const valueToStore =
|
||||
value instanceof Function ? value(storedValue) : value;
|
||||
// Save state
|
||||
setStoredValue(valueToStore);
|
||||
// Save to local storage
|
||||
localStorage.setItem(key, JSON.stringify(valueToStore));
|
||||
} catch (error) {
|
||||
// A more advanced implementation would handle the error case
|
||||
// console.log(error);
|
||||
}
|
||||
};
|
||||
return [storedValue, setValue] as const;
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
import React from 'react'
|
||||
import { CollectionListOptions, GetCollectionsQuery } from '@framework/schema'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { Banner, StaticImage } from 'src/components/common'
|
||||
import useGetProductListByCollection from 'src/components/hooks/useGetProductListByCollection'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import BannerImgRight from './assets/banner_full.png'
|
||||
import HomeBannerImg from './assets/home_banner.png'
|
||||
@@ -11,6 +13,10 @@ interface Props {
|
||||
}
|
||||
|
||||
const HomeBanner = ({ }: Props) => {
|
||||
// const variables = useMemo(() => {
|
||||
// return {option: {filter: {name: {eq: "Computers" }}}}
|
||||
// }, [])
|
||||
// const {collections} = useGetProductListByCollection(variables)
|
||||
return (
|
||||
<div className={s.homeBanner}>
|
||||
<section className={s.left}>
|
||||
|
@@ -1,10 +1,13 @@
|
||||
import React from 'react'
|
||||
import { ProductCard } from '@commerce/types/product'
|
||||
import { CollectionCarcousel } from '..'
|
||||
import image5 from '../../../../../public/assets/images/image5.png'
|
||||
import image6 from '../../../../../public/assets/images/image6.png'
|
||||
import image7 from '../../../../../public/assets/images/image7.png'
|
||||
import image8 from '../../../../../public/assets/images/image8.png'
|
||||
interface HomeCollectionProps {}
|
||||
interface HomeCollectionProps {
|
||||
data: ProductCard[]
|
||||
}
|
||||
const dataTest = [
|
||||
{
|
||||
name: 'Tomato',
|
||||
@@ -92,39 +95,39 @@ const dataTest = [
|
||||
},
|
||||
]
|
||||
|
||||
const HomeCollection = (props: HomeCollectionProps) => {
|
||||
const HomeCollection = ({data}: HomeCollectionProps) => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-2"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-3"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-4"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-5"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-6"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
|
@@ -1,27 +1,18 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import ProductImgs from './components/ProductImgs/ProductImgs'
|
||||
import ProductInfo from './components/ProductInfo/ProductInfo'
|
||||
import s from './ProductInfoDetail.module.scss'
|
||||
import { Product } from '@commerce/types/product'
|
||||
import { Collection } from '@framework/schema'
|
||||
import { getCategoryNameFromCollectionId } from 'src/utils/funtion.utils';
|
||||
|
||||
interface Props {
|
||||
productDetail: Product,
|
||||
collections: Collection[]
|
||||
}
|
||||
|
||||
const ProductInfoDetail = ({ productDetail, collections }: Props) => {
|
||||
const dataWithCategoryName = useMemo(() => {
|
||||
return {
|
||||
...productDetail,
|
||||
collection: getCategoryNameFromCollectionId(collections, productDetail.collectionIds ? productDetail.collectionIds[0] : undefined)
|
||||
}
|
||||
}, [productDetail, collections])
|
||||
const ProductInfoDetail = ({ productDetail }: Props) => {
|
||||
return (
|
||||
<section className={s.productInfoDetail}>
|
||||
<ProductImgs productImage={productDetail.images}/>
|
||||
<ProductInfo productInfoDetail={dataWithCategoryName}/>
|
||||
<ProductInfo productInfoDetail={productDetail}/>
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
.warpper{
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
.name{
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.option{
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
// > button {
|
||||
// margin-right: 1rem;
|
||||
// }
|
||||
}
|
||||
.button {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
import { ProductOption, ProductOptionValues } from '@commerce/types/product'
|
||||
import classNames from 'classnames'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { SelectedOptions } from 'src/utils/types.utils'
|
||||
import s from './ProductDetailOption.module.scss'
|
||||
interface Props {
|
||||
option: ProductOption
|
||||
onChane: (values: SelectedOptions) => void
|
||||
}
|
||||
|
||||
const ProductDetailOption = React.memo(({ option, onChane }: Props) => {
|
||||
const [selected, setSelected] = useState<string>('')
|
||||
useEffect(() => {
|
||||
if (option) {
|
||||
setSelected(option.values[0].label)
|
||||
}
|
||||
}, [option])
|
||||
const handleClick = (value:string) => {
|
||||
setSelected(value)
|
||||
onChane && onChane({[option.displayName]:value})
|
||||
}
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<div className={s.name}>{option.displayName}:</div>
|
||||
<div className={s.option}>
|
||||
{option.values.map((value) => {
|
||||
return <ProductDetailOptionButton value={value} selected={selected} onClick={handleClick} key={value.label}/>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
interface ProductDetailOptionButtonProps {
|
||||
value: ProductOptionValues
|
||||
selected: string
|
||||
onClick: (value: string) => void
|
||||
}
|
||||
const ProductDetailOptionButton = ({
|
||||
value,
|
||||
selected,
|
||||
onClick,
|
||||
}: ProductDetailOptionButtonProps) => {
|
||||
const handleClick = () => {
|
||||
onClick && onClick(value.label)
|
||||
}
|
||||
return (
|
||||
<div className={s.button}>
|
||||
<div onClick={handleClick} className={classNames({ [s.active]: selected === value.label })}>
|
||||
{value.label}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductDetailOption
|
@@ -1,8 +1,15 @@
|
||||
import { Product } from '@commerce/types/product'
|
||||
import React from 'react'
|
||||
import Router from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { ButtonCommon, LabelCommon, QuanittyInput } from 'src/components/common'
|
||||
import { useCartDrawer, useMessage } from 'src/components/contexts'
|
||||
import { useAddProductToCart } from 'src/components/hooks/cart'
|
||||
import { IconBuy } from 'src/components/icons'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import { getProductVariant } from 'src/utils/funtion.utils'
|
||||
import { LANGUAGE } from 'src/utils/language.utils'
|
||||
import { SelectedOptions } from 'src/utils/types.utils'
|
||||
import ProductDetailOption from '../ProductDetailOption/ProductDetailOption'
|
||||
import s from './ProductInfo.module.scss'
|
||||
|
||||
interface Props {
|
||||
@@ -10,11 +17,73 @@ interface Props {
|
||||
}
|
||||
|
||||
const ProductInfo = ({ productInfoDetail }: Props) => {
|
||||
console.log(productInfoDetail)
|
||||
const [option, setOption] = useState({})
|
||||
const [quanitty, setQuanitty] = useState(0)
|
||||
const [addToCartLoading, setAddToCartLoading] = useState(false)
|
||||
const [buyNowLoading, setBuyNowLoading] = useState(false)
|
||||
const { showMessageSuccess, showMessageError } = useMessage()
|
||||
useEffect(() => {
|
||||
let defaultOption:SelectedOptions = {}
|
||||
productInfoDetail.options.map((option)=>{
|
||||
defaultOption[option.displayName] = option.values[0].label
|
||||
return null
|
||||
})
|
||||
}, [productInfoDetail])
|
||||
|
||||
const {addProduct} = useAddProductToCart()
|
||||
const { openCartDrawer } = useCartDrawer()
|
||||
|
||||
function handleAddToCart() {
|
||||
setAddToCartLoading(true)
|
||||
const variant = getProductVariant(productInfoDetail, option)
|
||||
if (variant) {
|
||||
addProduct({ variantId: variant.id.toString(), quantity: quanitty }, handleAddToCartCallback)
|
||||
}else{
|
||||
setAddToCartLoading(false)
|
||||
}
|
||||
}
|
||||
const handleAddToCartCallback = (isSuccess:boolean,message?:string) => {
|
||||
setAddToCartLoading(false)
|
||||
if(isSuccess){
|
||||
showMessageSuccess("Add to cart successfully!", 4000)
|
||||
openCartDrawer && openCartDrawer()
|
||||
}else{
|
||||
showMessageError(message||"Error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const handleBuyNowCallback = (success:boolean,message?:string) => {
|
||||
setBuyNowLoading(false)
|
||||
if(success){
|
||||
Router.push(ROUTE.CHECKOUT)
|
||||
}else{
|
||||
showMessageError(message||"Error")
|
||||
}
|
||||
}
|
||||
|
||||
const handleBuyNow = () => {
|
||||
setBuyNowLoading(true)
|
||||
const variant = getProductVariant(productInfoDetail, option)
|
||||
if (variant) {
|
||||
addProduct({ variantId: variant.id.toString(), quantity: quanitty }, handleBuyNowCallback)
|
||||
}else{
|
||||
setBuyNowLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuanittyChange = (value:number) => {
|
||||
setQuanitty(value)
|
||||
}
|
||||
const onSelectOption = (value:SelectedOptions) => {
|
||||
setOption({...option,...value})
|
||||
// let variant = getProductVariant(productInfoDetail,value)
|
||||
// console.log(variant)
|
||||
}
|
||||
return (
|
||||
<section className={s.productInfo}>
|
||||
<div className={s.info}>
|
||||
<LabelCommon shape='half'>{productInfoDetail.collection}</LabelCommon>
|
||||
<LabelCommon shape='half'>{productInfoDetail.collection?.[0]}</LabelCommon>
|
||||
<h2 className={s.heading}>{productInfoDetail.name}</h2>
|
||||
<div className={s.price}>
|
||||
<div className={s.old}>
|
||||
@@ -26,14 +95,22 @@ const ProductInfo = ({ productInfoDetail }: Props) => {
|
||||
<div className={s.description}>
|
||||
{productInfoDetail.description}
|
||||
</div>
|
||||
<div className={s.options}>
|
||||
{
|
||||
productInfoDetail.options.map((option)=>{
|
||||
return <ProductDetailOption option={option} onChane={onSelectOption} key={option.displayName}/>
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.actions}>
|
||||
<QuanittyInput />
|
||||
<QuanittyInput value={quanitty} onChange={handleQuanittyChange}/>
|
||||
<div className={s.bottom}>
|
||||
{/* <ButtonCommon size='large'>{LANGUAGE.BUTTON_LABEL.PREORDER}</ButtonCommon> */}
|
||||
<ButtonCommon size='large'>{LANGUAGE.BUTTON_LABEL.BUY_NOW}</ButtonCommon>
|
||||
<ButtonCommon size='large' onClick={handleBuyNow} loading={buyNowLoading} disabled={addToCartLoading}>{LANGUAGE.BUTTON_LABEL.BUY_NOW}</ButtonCommon>
|
||||
|
||||
<ButtonCommon size='large' type='light'>
|
||||
<ButtonCommon size='large' type='light' onClick={handleAddToCart} loading={addToCartLoading} disabled={buyNowLoading}>
|
||||
<span className={s.buttonWithIcon}>
|
||||
<IconBuy /><span className={s.label}>{LANGUAGE.BUTTON_LABEL.ADD_TO_CARD}</span>
|
||||
</span>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Collection } from '@commerce/types/collection';
|
||||
import { ProductCard } from '@commerce/types/product';
|
||||
import { Collection } from '@framework/schema';
|
||||
import React, { useMemo } from 'react';
|
||||
import ListProductWithInfo from 'src/components/common/ListProductWithInfo/ListProductWithInfo';
|
||||
import { getCategoryNameFromCollectionId } from 'src/utils/funtion.utils';
|
||||
|
@@ -1,13 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Product } from '@commerce/types/product';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ListProductWithInfo from 'src/components/common/ListProductWithInfo/ListProductWithInfo';
|
||||
import { PRODUCT_DATA_TEST } from 'src/utils/demo-data';
|
||||
|
||||
const ViewedProducts = () => {
|
||||
import { ProductCardProps } from 'src/components/common/ProductCard/ProductCard';
|
||||
import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils'
|
||||
import { normalizeProductCard } from '@framework/utils/normalize';
|
||||
import { useLocalStorage } from 'src/components/hooks/useLocalStorage';
|
||||
interface Props {
|
||||
data: ProductCardProps[]
|
||||
}
|
||||
const ViewedProducts = ({data}:Props) => {
|
||||
if (data.length===0){
|
||||
return <div></div>
|
||||
}
|
||||
return (
|
||||
<ListProductWithInfo
|
||||
title="viewed Products"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
data={PRODUCT_DATA_TEST}
|
||||
data={data}
|
||||
hasBorderBottomMobile={true}
|
||||
/>
|
||||
);
|
||||
|
2
src/domains/enums/Message.ts
Normal file
2
src/domains/enums/Message.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
|
@@ -44,7 +44,8 @@ export const ACCOUNT_TAB = {
|
||||
}
|
||||
|
||||
export const LOCAL_STORAGE_KEY = {
|
||||
TOKEN: 'token'
|
||||
TOKEN: 'token',
|
||||
VIEWEDPRODUCT: "viewed-product"
|
||||
}
|
||||
|
||||
export const QUERY_SPLIT_SEPERATOR = ','
|
||||
@@ -82,46 +83,58 @@ export const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
|
||||
export const CATEGORY = [
|
||||
{
|
||||
name: 'All',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${OPTION_ALL}`,
|
||||
},
|
||||
{
|
||||
name: 'Veggie',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=veggie`,
|
||||
},
|
||||
{
|
||||
name: 'Seafood',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=seafood`,
|
||||
},
|
||||
{
|
||||
name: 'Frozen',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=frozen`,
|
||||
},
|
||||
{
|
||||
name: 'Coffee Bean',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=coffee_bean`,
|
||||
},
|
||||
{
|
||||
name: 'Sauce',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=sauce`,
|
||||
},
|
||||
]
|
||||
{
|
||||
name: 'All',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${OPTION_ALL}`,
|
||||
},
|
||||
{
|
||||
name: 'Veggie',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=veggie`,
|
||||
},
|
||||
{
|
||||
name: 'Seafood',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=seafood`,
|
||||
},
|
||||
{
|
||||
name: 'Frozen',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=frozen`,
|
||||
},
|
||||
{
|
||||
name: 'Coffee Bean',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=coffee_bean`,
|
||||
},
|
||||
{
|
||||
name: 'Sauce',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=sauce`,
|
||||
},
|
||||
]
|
||||
|
||||
export const BRAND = [
|
||||
{
|
||||
name: 'Maggi',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=maggi`,
|
||||
},
|
||||
{
|
||||
name: 'Chomilex',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chomilex`,
|
||||
},
|
||||
{
|
||||
name: 'Chinsu',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
|
||||
},
|
||||
]
|
||||
|
||||
export const BRAND = [
|
||||
{
|
||||
name: 'Maggi',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=maggi`,
|
||||
export const FACET = {
|
||||
FEATURE: {
|
||||
PARENT_NAME: 'Featured',
|
||||
FRESH: 'Fresh',
|
||||
BEST_SELLERS: 'Best seller'
|
||||
},
|
||||
{
|
||||
name: 'Chomilex',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chomilex`,
|
||||
},
|
||||
{
|
||||
name: 'Chinsu',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
|
||||
},
|
||||
]
|
||||
CATEGORY: {
|
||||
PARENT_NAME:"category",
|
||||
VEGGIE:"veggie"
|
||||
}
|
||||
}
|
||||
|
||||
export const CODE_FACET_FEATURED = 'featured'
|
||||
export const CODE_FACET_DISCOUNT = 'discount'
|
||||
|
0
src/utils/enum.ts
Normal file
0
src/utils/enum.ts
Normal file
@@ -1,7 +1,9 @@
|
||||
import { Collection } from '@commerce/types/collection';
|
||||
import { Facet } from "@commerce/types/facet";
|
||||
import { Collection, 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";
|
||||
import { Product, ProductCard, ProductOption, ProductOptionValues } from "@commerce/types/product";
|
||||
import { FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d';
|
||||
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT, FACET, PRODUCT_SORT_OPTION_VALUE } from "./constanst.utils";
|
||||
import { PromiseWithKey, SelectedOptions, SortOrder } from "./types.utils";
|
||||
|
||||
export function isMobile() {
|
||||
return window.innerWidth < 768
|
||||
@@ -79,6 +81,18 @@ export function getFreshFacetId(facets: Facet[]) {
|
||||
return freshFacetValue?.id
|
||||
}
|
||||
|
||||
export function getFacetIdByName(facets: Facet[], facetName: string, valueName:string) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.name === facetName)
|
||||
const freshFacetValue = featuredFacet?.values.find((item: FacetValue) => item.name === valueName)
|
||||
return freshFacetValue?.id
|
||||
}
|
||||
|
||||
|
||||
export function getAllFeaturedFacetId(facets: Facet[]) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.name === FACET.FEATURE.PARENT_NAME)
|
||||
const rs = featuredFacet?.values.map((item: FacetValue) => item.id)
|
||||
return rs || []
|
||||
}
|
||||
export function getAllFacetValueIdsByParentCode(facets: Facet[], code: string) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.code === code)
|
||||
const rs = featuredFacet?.values.map((item: FacetValue) => item.id)
|
||||
@@ -127,4 +141,36 @@ export const getCategoryNameFromCollectionId = (colelctions: Collection[], colle
|
||||
|
||||
export function getAllPromies(promies: PromiseWithKey[]) {
|
||||
return promies.map(item => item.promise)
|
||||
}
|
||||
|
||||
export const FilterOneVatiant = (products:ProductCard[]) => {
|
||||
let idList:string[] = []
|
||||
let filtedProduct: ProductCard[]=[]
|
||||
products.map((product:ProductCard)=>{
|
||||
if(!idList.includes(product.id)){
|
||||
filtedProduct.push(product)
|
||||
idList.push(product.id)
|
||||
}
|
||||
})
|
||||
return filtedProduct
|
||||
}
|
||||
|
||||
export const convertOption = (values :ProductOptionValues[]) => {
|
||||
return values.map((value)=>{ return {name:value.label,value:value.label}})
|
||||
}
|
||||
|
||||
export function getProductVariant(product: Product, opts: SelectedOptions) {
|
||||
const variant = product.variants?.find((variant) => {
|
||||
return Object.entries(opts).every(([key, value]) =>
|
||||
variant.options.find((option) => {
|
||||
if (
|
||||
option.__typename === 'MultipleChoiceOption' &&
|
||||
option.displayName.toLowerCase() === key.toLowerCase()
|
||||
) {
|
||||
return option.values.find((v) => v.label.toLowerCase() === value)
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
return variant
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
|
||||
export interface ProductProps {
|
||||
category?: string
|
||||
name: string
|
||||
@@ -57,6 +58,14 @@ export type filterContextType = {
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
export interface StringMap { [key: string]: string; }
|
||||
|
||||
export interface FacetMap extends StringMap{
|
||||
PARENT_NAME: string
|
||||
}
|
||||
export interface FacetConstant{
|
||||
[key: string]: FacetMap;
|
||||
}
|
||||
export type PromiseWithKey = {
|
||||
key: string
|
||||
promise: PromiseLike<any>
|
||||
@@ -77,3 +86,4 @@ export type OrderState = | 'Created'
|
||||
| 'ArrangingAdditionalPayment'
|
||||
| 'Cancelled'
|
||||
|
||||
export type SelectedOptions = Record<string, string | null>
|
||||
|
Reference in New Issue
Block a user