Merge branch 'release-stable' of https://github.com/KieIO/grocery-vercel-commerce into release-stable

This commit is contained in:
Quangnhankie
2021-10-17 10:05:07 +07:00
52 changed files with 1195 additions and 150 deletions

View File

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

View File

@@ -1,3 +1,6 @@
import { FacetValue, UpdateAddressInput } from './schema.d';
import { ResetPassword } from './schema.d';
import { requestPasswordReset } from '@framework/schema';
import { FacetValue } from './schema.d';
export type Maybe<T> = T | null
export type Exact<T extends { [key: string]: unknown }> = {
@@ -304,6 +307,11 @@ export type MutationResetPasswordArgs = {
}
export type Address = Node & {
updateCustomerAddress:
| {
__typename?: 'Address'
id: Scalars['ID']
}
__typename?: 'Address'
id: Scalars['ID']
createdAt: Scalars['DateTime']
@@ -322,6 +330,9 @@ export type Address = Node & {
customFields?: Maybe<Scalars['JSON']>
}
export type Asset = Node & {
__typename?: 'Asset'
id: Scalars['ID']
@@ -1459,6 +1470,11 @@ export type CustomerListOptions = {
}
export type Customer = Node & {
updateCustomer:
| {
__typename?: 'Customer'
id: Scalars['ID']
}
__typename?: 'Customer'
id: Scalars['ID']
createdAt: Scalars['DateTime']
@@ -1466,7 +1482,7 @@ export type Customer = Node & {
title?: Maybe<Scalars['String']>
firstName: Scalars['String']
lastName: Scalars['String']
phoneNumber?: Maybe<Scalars['String']>
phoneNumber?: Maybe<Scalars['String']>
emailAddress: Scalars['String']
addresses?: Maybe<Array<Address>>
orders: OrderList
@@ -3126,6 +3142,36 @@ export type LoginMutation = { __typename?: 'Mutation' } & {
>)
}
export type ResetPasswordMutation = { __typename?: 'Mutation' } & {
resetPassword:
| ({ __typename: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| ({ __typename: 'PasswordResetTokenInvalidError' } & Pick<
PasswordResetTokenInvalidError,
'errorCode' | 'message'
>)
| ({ __typename: 'PasswordResetTokenExpiredError' } & Pick<
PasswordResetTokenExpiredError,
'errorCode' | 'message'
>)
| ({ __typename: 'NativeAuthStrategyError' } & Pick<
NativeAuthStrategyError,
'errorCode' | 'message'
>)
}
export type SignupMutation = { __typename?: 'Mutation' } & {
registerCustomerAccount:
| ({ __typename: 'Success' } & Pick<Success, 'success'>)
| ({ __typename: 'MissingPasswordError' } & Pick<
MissingPasswordError,
'errorCode' | 'message'
>)
| ({ __typename: 'NativeAuthStrategyError' } & Pick<
NativeAuthStrategyError,
'errorCode' | 'message'
>)
}
export type VerifyCustomerAccountVariables = Exact<{
token: Scalars['String']
password?: Maybe<Scalars['String']>
@@ -3179,8 +3225,9 @@ export type SignupMutationVariables = Exact<{
input: RegisterCustomerInput
}>
export type SignupMutation = { __typename?: 'Mutation' } & {
registerCustomerAccount:
export type RequestPasswordReset = { __typename?: 'Mutation' } & {
requestPasswordReset:
| ({ __typename: 'Success' } & Pick<Success, 'success'>)
| ({ __typename: 'MissingPasswordError' } & Pick<
MissingPasswordError,
@@ -3192,17 +3239,48 @@ export type SignupMutation = { __typename?: 'Mutation' } & {
>)
}
export type ActiveCustomerQueryVariables = Exact<{ [key: string]: never }>
export type ActiveCustomerQuery = { __typename?: 'Query' } & {
activeCustomer?: Maybe<
{ __typename?: 'Customer' } & Pick<
Customer,
'id' | 'firstName' | 'lastName' | 'emailAddress'
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 & {
items: [Favorite!]!
totalItems: Int!
}
type Favorite = Node & {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
product: Product
customer: Customer!
}
type FavouriteOption = Customer & {
favorites(options: FavoriteListOptions): FavoriteList!
}
export type GetAllProductPathsQueryVariables = Exact<{
first?: Maybe<Scalars['Int']>
}>

View File

@@ -0,0 +1,14 @@
export const requestPasswordReset = /* GraphQL */ `
mutation RequestPasswordReset($emailAddress: String!) {
requestPasswordReset(emailAddress: $emailAddress) {
__typename
...on Success{
success
}
...on ErrorResult{
errorCode
message
}
}
}
`

View File

@@ -0,0 +1,15 @@
export const resetPasswordMutation = /* GraphQL */ `
mutation resetPassword($token: String!,$password: String!){
resetPassword(token: $token,password: $password){
__typename
...on CurrentUser{
id
identifier
}
...on ErrorResult{
errorCode
message
}
}
}
`

View File

@@ -0,0 +1,9 @@
export const toggleWishlistMutation = /* GraphQL */ `
mutation toggleFavorite($productId:ID!){
toggleFavorite(productId:$productId){
items{
id
}
}
}
`

View File

@@ -0,0 +1,14 @@
export const updateCustomerAddress = /* GraphQL */ `
mutation updateCustomerAddress($input: UpdateAddressInput!){
updateCustomerAddress(input: $input){
__typename
...on Address{
id
streetLine1
city
postalCode
province
}
}
}
`

View File

@@ -0,0 +1,13 @@
export const updateCustomer = /* GraphQL */ `
mutation updateCustomer($input: UpdateCustomerInput!){
updateCustomer(input:$input){
__typename
...on Customer{
id
firstName
lastName
phoneNumber
}
}
}
`

View File

@@ -1,6 +1,6 @@
import { Cart } from '@commerce/types/cart'
import { ProductCard } from '@commerce/types/product'
import { CartFragment, SearchResultFragment } from '../schema'
import { CartFragment, SearchResultFragment,Favorite,ActiveCustomerQuery } from '../schema'
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
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 {
return {
id: order.id.toString(),

View File

@@ -1,10 +1,24 @@
export const activeCustomerQuery = /* GraphQL */ `
query activeCustomer {
activeCustomer {
id
firstName
lastName
emailAddress
query activeCustomer {
activeCustomer {
id
firstName
lastName
emailAddress
favorites{
items{
product{
id
}
}
}
phoneNumber
addresses{
streetLine1
city
province
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

@@ -0,0 +1,19 @@
export const getUserOrderQuery = /* GraphQL */ `
query activeCustomer {
activeCustomer {
orders{
items{
lines{
productVariant{
name
}
quantity
}
total
state
code
}
}
}
}
`

View File

@@ -0,0 +1,16 @@
export const userInfoQuery = /* GraphQL */ `
query activeCustomer{
activeCustomer{
lastName
firstName
emailAddress
phoneNumber
addresses{
streetLine1
city
province
postalCode
}
}
}
`

10
pages/forgot-password.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { FormForgot, Layout } from 'src/components/common'
export default function NotFound() {
return (
<div>
<FormForgot/>
</div>
)
}
NotFound.Layout = Layout

View File

@@ -6,7 +6,7 @@ import { GetStaticPropsContext } from 'next';
import { Layout } from 'src/components/common';
import { FeaturedProductsCarousel, FreshProducts, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home';
import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice';
import { 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 { PromiseWithKey } from 'src/utils/types.utils';
@@ -15,11 +15,12 @@ interface Props {
freshProducts: ProductCard[],
featuredProducts: ProductCard[],
collections: Collection[]
spiceProducts:ProductCard[]
}
export default function Home({ featuredAndDiscountFacetsValue,
freshProducts, featuredProducts,
collections }: Props) {
collections,spiceProducts }: Props) {
return (
<>
<HomeBanner />
@@ -28,7 +29,7 @@ export default function Home({ featuredAndDiscountFacetsValue,
<FreshProducts data={freshProducts} collections={collections} />
<HomeCollection />
<HomeVideo />
<HomeSpice />
{spiceProducts.length>0 && <HomeSpice data={spiceProducts}/>}
<FeaturedProductsCarousel data={featuredProducts} featuredFacetsValue={featuredAndDiscountFacetsValue} />
<HomeCTA />
<HomeRecipe />
@@ -99,16 +100,24 @@ export async function getStaticProps({
})
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 {
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,

10
pages/reset-password.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { FormResetPassword, Layout } from 'src/components/common'
export default function NotFound() {
return (
<div>
<FormResetPassword/>
</div>
)
}
NotFound.Layout = Layout

View File

@@ -0,0 +1,22 @@
@import '../../../../styles/utilities';
.formAuthen{
width: 50%;
margin: 0 auto;
padding: 4rem 0 ;
.title{
@apply font-heading heading-3;
padding: 0 1.6rem 0 0.8rem;
margin-bottom: 2rem;
}
.bottom {
@apply flex justify-between items-center;
margin: 4rem auto;
.remembered {
@apply font-bold cursor-pointer;
color: var(--primary);
}
}
.socialAuthen{
margin-bottom: 3rem;
}
}

View File

@@ -0,0 +1,89 @@
import { Form, Formik } from 'formik';
import React, { useRef } from 'react';
import { ButtonCommon, InputFiledInForm } from 'src/components/common';
import { useModalCommon } from 'src/components/hooks';
import useRequestPasswordReset from 'src/components/hooks/auth/useRequestPasswordReset';
import { CustomInputCommon } from 'src/utils/type.utils';
import * as Yup from 'yup';
import ModalAuthenticate from '../../ModalAuthenticate/ModalAuthenticate';
import { default as s, default as styles } from './FormForgot.module.scss';
import { useMessage } from 'src/components/contexts'
import { LANGUAGE } from 'src/utils/language.utils'
interface Props {
}
const DisplayingErrorMessagesSchema = Yup.object().shape({
email: Yup.string().email('Your email was wrong').required('Required')
})
const FormForgot = ({ }: Props) => {
const {requestPassword} = useRequestPasswordReset();
const { showMessageSuccess, showMessageError } = useMessage();
const emailRef = useRef<CustomInputCommon>(null);
const { visible: visibleModalAuthen,closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false });
const onForgot = (values: { email: string }) => {
requestPassword({email: values.email},onForgotPasswordCallBack);
}
const onForgotPasswordCallBack = (isSuccess: boolean, message?: string) => {
if (isSuccess) {
showMessageSuccess("Request forgot password successfully. Please verify your email to login.")
} else {
showMessageError(message || LANGUAGE.MESSAGE.ERROR)
}
}
return (
<section className={s.formAuthen}>
<div className={s.inner}>
<div className={s.body}>
<div className={s.title}>Forgot Password</div>
<Formik
initialValues={{
email: '',
}}
validationSchema={DisplayingErrorMessagesSchema}
onSubmit={onForgot}
>
{({ errors, touched, isValid, submitForm }) => (
<Form className="u-form">
<div className="body">
<InputFiledInForm
name="email"
placeholder="Email Address"
ref={emailRef}
error={
touched.email && errors.email
? errors.email.toString()
: ''
}
isShowIconSuccess={touched.email && !errors.email}
onEnter={isValid ? submitForm : undefined}
/>
</div>
<div className={styles.bottom}>
<div className={styles.remembered} onClick={openModalAuthen}>
I Remembered My Password?
</div>
<ButtonCommon HTMLType='submit' size="large">
Reset Password
</ButtonCommon>
</div>
</Form>
)}
</Formik>
</div>
<ModalAuthenticate visible={visibleModalAuthen} closeModal={closeModalAuthen} />
</div>
</section>
)
}
export default FormForgot;

View File

@@ -0,0 +1,27 @@
@import '../../../../styles/utilities';
.formAuthen{
width: 50%;
margin: 0 auto;
padding: 4rem 0 ;
.title{
@apply font-heading heading-3;
padding: 0 1.6rem 0 0.8rem;
margin-bottom: 2rem;
}
.passwordNote {
@apply text-center caption text-label;
margin-top: 0.8rem;
}
.bottom {
@apply flex justify-center items-center;
margin: 4rem auto;
.remembered {
@apply font-bold cursor-pointer;
color: var(--primary);
}
}
.confirmPassword{
margin-top: 2rem;
}
}

View File

@@ -0,0 +1,108 @@
import { Form, Formik } from 'formik';
import React, { useRef } from 'react';
import { ButtonCommon, InputPasswordFiledInForm } from 'src/components/common';
import { useMessage } from 'src/components/contexts';
import useRequestPasswordReset from 'src/components/hooks/auth/useRequestPasswordReset';
import { LANGUAGE } from 'src/utils/language.utils';
import { CustomInputCommon } from 'src/utils/type.utils';
import * as Yup from 'yup';
import { useRouter } from 'next/router'
import { default as s, default as styles } from './FormResetPassword.module.scss';
import { useResetPassword } from 'src/components/hooks/auth';
interface Props {
}
const DisplayingErrorMessagesSchema = Yup.object().shape({
password: Yup.string()
.matches(
/^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])((?=.*[0-9!@#$%^&*()\-_=+{};:,<.>]){1}).*$/,
'Must contain 8 characters with at least 1 uppercase and 1 lowercase letter and either 1 number or 1 special character.'
)
.max(30, 'Password is too long')
.required('Required'),
confirmPassword: Yup.string()
.label('Password Confirm')
.required()
.oneOf([Yup.ref('password')], 'Passwords does not match'),
})
const FormResetPassword = ({ }: Props) => {
const router = useRouter();
const {resetPassword} = useResetPassword();
const { showMessageSuccess, showMessageError } = useMessage();
const onReset = (values: {password: string }) => {
const { token } = router.query;
resetPassword({token:token,password: values.password},onResetPasswordCallBack);
}
const onResetPasswordCallBack = (isSuccess: boolean, message?: string) => {
if (isSuccess) {
showMessageSuccess("Reset password successfully. Please to login.")
} else {
showMessageError(message || LANGUAGE.MESSAGE.ERROR)
}
}
return (
<section className={s.formAuthen}>
<div className={s.inner}>
<div className={s.body}>
<div className={s.title}>Reset Password</div>
<Formik
initialValues={{
password: '',
confirmPassword: '',
}}
validationSchema={DisplayingErrorMessagesSchema}
onSubmit={onReset}
>
{({ errors, touched, isValid, submitForm }) => (
<Form className="u-form">
<div>
<InputPasswordFiledInForm
name="password"
placeholder="Password"
error={
touched.password && errors.password
? errors.password.toString()
: ''
}
/>
</div>
<div className={s.confirmPassword}>
<InputPasswordFiledInForm
name="confirmPassword"
placeholder="Password confirm"
error={
touched.confirmPassword && errors.confirmPassword
? errors.confirmPassword.toString()
: ''
}
onEnter={isValid ? submitForm : undefined}
/>
</div>
<div className={styles.passwordNote}>
Must contain 8 characters with at least 1 uppercase and 1
lowercase letter and either 1 number or 1 special character.
</div>
<div className={styles.bottom}>
<ButtonCommon HTMLType='submit' size="large">
Change Password
</ButtonCommon>
</div>
</Form>
)}
</Formik>
</div>
</div>
</section>
)
}
export default FormResetPassword;

View File

@@ -58,6 +58,10 @@ const HeaderMenu = memo(
onClick: openModalRegister,
name: 'Create account',
},
{
link: '/forgot-password',
name: 'Forgot Password',
},
],
[openModalLogin, openModalRegister]
)

View File

@@ -2,19 +2,38 @@ import classNames from 'classnames'
import IconHeart from 'src/components/icons/IconHeart'
import React, { memo } from 'react'
import s from './ItemWishList.module.scss'
import { useToggleProductWishlist } from '../../../../src/components/hooks/product'
import { useMessage } from 'src/components/contexts'
import { LANGUAGE } from 'src/utils/language.utils'
interface Props {
id:string,
isActive?: boolean,
onChange?: () => void
onChange?: () => string
}
const ItemWishList = memo(({isActive=false, onChange}:Props) => {
const ItemWishList = memo(({id,isActive=false, onChange}:Props) => {
const {onToggleProductWishlist} = useToggleProductWishlist();
const { showMessageSuccess, showMessageError } = useMessage();
function toggleWishlist(){
onToggleProductWishlist({productId:id},onSignupCallBack)
}
const onSignupCallBack = (isSuccess: boolean, message?: string) => {
if (isSuccess) {
// showMessageSuccess("Create account successfully. Please verify your email to login.", 15000)
} else {
showMessageError(message || LANGUAGE.MESSAGE.ERROR)
}
}
return(
<div className={classNames({
[s.heartToggle]: true,
[s.isToggleOn]: isActive
})}
onChange={onChange}
onClick={toggleWishlist}
>
<IconHeart />
</div>

View File

@@ -14,9 +14,11 @@ import ProductNotSell from './ProductNotSell/ProductNotSell'
export interface ProductCardProps extends ProductCard {
buttonText?: string
isSingleButton?: boolean,
activeWishlist?:boolean
}
const ProductCardComponent = ({
id,
collection,
name,
slug,
@@ -27,6 +29,7 @@ const ProductCardComponent = ({
imageSrc,
isNotSell,
isSingleButton,
activeWishlist
}: ProductCardProps) => {
if (isNotSell) {
return <div className={`${s.productCardWarpper} ${s.notSell}`}>
@@ -34,6 +37,7 @@ const ProductCardComponent = ({
</div>
}
return (
<div className={s.productCardWarpper}>
<div className={s.cardTop}>
@@ -63,7 +67,7 @@ const ProductCardComponent = ({
<div className={s.cardMidBot}>
<div className={s.productPrice}>{price} {currencyCode}</div>
<div className={s.wishList}>
<ItemWishList />
<ItemWishList isActive={activeWishlist} id={id}/>
</div>
</div>
</div>

View File

@@ -1,12 +1,12 @@
import classNames from 'classnames'
import { useRouter } from 'next/router'
import React from 'react'
import { useActiveCustomer } from 'src/components/hooks/auth'
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[],
total?: number,
@@ -14,8 +14,10 @@ interface ProductListProps {
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 {wishlistId } = useActiveCustomer();
const handlePageChange = (page: number) => {
onPageChange && onPageChange(page)
}
@@ -32,19 +34,20 @@ const ProductList = ({ data, total = data.length, defaultCurrentPage, onPageChan
<div className={s.wrapper}>
<div className={s.list}>
{
data.map((product, index) => {
return <ProductCard {...product} key={index} />
data?.map((product, index) => {
let activeWishlist = wishlistId?.findIndex((val:string) => val == product.id) !== -1;
return <ProductCard activeWishlist={activeWishlist} {...product} key={index} />
})
}
{
data.length === 0 && <div className={s.empty}>
data?.length === 0 && <div className={s.empty}>
<EmptyCommon />
<ButtonCommon onClick={handleShowAllProduct}>Show all products</ButtonCommon>
</div>
}
</div>
<div className={classNames(s.pagination, { [s.hide]: data.length === 0 })}>
<PaginationCommon defaultCurrent={defaultCurrentPage} total={total} pageSize={DEFAULT_PAGE_SIZE} onChange={handlePageChange} />
<div className={classNames(s.pagination, { [s.hide]: data?.length === 0 })}>
<PaginationCommon defaultCurrent={defaultCurrentPage} total={total ?? 0} pageSize={DEFAULT_PAGE_SIZE} onChange={handlePageChange} />
</div>
</div>
)

View File

@@ -11,7 +11,7 @@
width: 20.6rem;
.selectTrigger {
width: 20.6rem;
padding: 1.2rem 1.6rem;
padding: 1.6rem;
}
}
&.large {

View File

@@ -5,6 +5,8 @@ import s from './SelectCommon.module.scss'
import SelectOption from './SelectOption/SelectOption'
interface Props {
selected?:string|null,
initValue?:string|null,
placeholder? : string,
value?: string,
size?: 'base' | 'large',
@@ -13,16 +15,16 @@ interface Props {
onChange?: (value: string) => void,
}
const SelectCommon = ({ value, type = 'default', size = 'base', options, placeholder, onChange}: Props) => {
const [selectedName, setSelectedName] = useState<string>()
const [selectedValue, setSelectedValue] = useState<string>('')
const SelectCommon = ({selected,initValue, type = 'default', size = 'base', options, placeholder, onChange}: Props) => {
const [selectedName, setSelectedName] = useState(placeholder)
const [selectedValue, setSelectedValue] = useState('')
useEffect(() => {
setSelectedValue(value || '')
const name = options.find(item => item.value === value)?.name
setSelectedName(name)
}, [value, options])
useEffect(()=>{
const nameSelect = options.find((val)=>val.value === selected);
setSelectedName(nameSelect?.name ?? 'State');
setSelectedValue(initValue ?? '');
onChange && onChange(initValue ?? '');
},[])
const changeSelectedName = (value: string) => {
setSelectedValue(value)

View File

@@ -51,6 +51,8 @@ export { default as LayoutCheckout} from './LayoutCheckout/LayoutCheckout'
export { default as InputPasswordFiledInForm} from './InputPasswordFiledInForm/InputPasswordFiledInForm'
export { default as InputFiledInForm} from './InputFiledInForm/InputFiledInForm'
export { default as MessageCommon} from './MessageCommon/MessageCommon'
export { default as FormForgot} from './ForgotPassword/FormForgot/FormForgot'
export { default as FormResetPassword} from './ForgotPassword/FormResetPassword/FormResetPassword'
export { default as ProductCardSkeleton} from './ProductCardSkeleton/ProductCardSkeleton'
export { default as ListProductCardSkeleton} from './ListProductCardSkeleton/ListProductCardSkeleton'

View File

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

View File

@@ -0,0 +1,55 @@
import { Address } from '@framework/schema'
import { updateCustomerAddress } from '@framework/utils/mutations/update-customer-address-mutation'
import { useState } from 'react'
import fetcher from 'src/utils/fetcher'
import { useActiveCustomer } from '../auth'
interface Props {
address?:string,
city?:string|null,
postalCode?:string|null,
state?:string
}
const useEditCustomerAddress = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const {customer,mutate} = useActiveCustomer();
const editCustomerAddress = (
{ address,city,postalCode,state}: Props,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
fetcher<Address>({
query: updateCustomerAddress,
variables: {
input: {
id:customer?.id,
streetLine1:address,
city,
postalCode,
province:state
},
},
}) .then((data) => {
if(data.updateCustomerAddress.__typename == 'Address'){
mutate();
fCallBack(true)
return data
}
}) .catch((error) => {
setError(error)
fCallBack(false, error.message)
})
.finally(() => setLoading(false))
}
return { loading, editCustomerAddress, error }
}
export default useEditCustomerAddress

View File

@@ -0,0 +1,51 @@
import { useState } from 'react'
import { Customer } from '@framework/schema'
import fetcher from 'src/utils/fetcher'
import { updateCustomer } from '@framework/utils/mutations/update-customer-mutation'
import { useActiveCustomer } from '../auth'
interface Props {
firstName?: string;
lastName?: string,
phoneNumber?:string,
}
const useEditUserInfo = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const {mutate} = useActiveCustomer();
const editUserInfo = (
{ firstName,lastName,phoneNumber}: Props,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
fetcher<Customer>({
query: updateCustomer,
variables: {
input: {
firstName,
lastName,
phoneNumber
},
},
})
.then((data) => {
if (data.updateCustomer.__typename == 'Customer') {
mutate();
return data
}
})
.catch((error) => {
setError(error)
fCallBack(false, error.message)
})
.finally(() => setLoading(false))
}
return { loading, editUserInfo, error }
}
export default useEditUserInfo

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

@@ -0,0 +1,20 @@
import { ActiveCustomerQuery, Order } from '@framework/schema'
import { getUserOrderQuery } from '@framework/utils/queries/get-user-order-query'
import gglFetcher from 'src/utils/gglFetcher'
import useSWR from 'swr'
const useGetUserOrder = () => {
const { data, ...rest } = useSWR<ActiveCustomerQuery>([getUserOrderQuery], gglFetcher)
const addingItem = data?.activeCustomer?.orders.items.filter((val:Order) =>val.state == 'AddingItems');
const arrangingPayment = data?.activeCustomer?.orders.items.filter((val:Order) =>val.state == 'ArrangingPayment');
const cancelled = data?.activeCustomer?.orders.items.filter((val:Order) =>val.state == "Cancelled");
return {
addingItem: addingItem,
arrangingPayment: arrangingPayment,
cancelled: cancelled,
...rest
}
}
export default useGetUserOrder

View File

@@ -3,4 +3,6 @@ export { default as useLogin } from './useLogin'
export { default as useLogout } from './useLogout'
export { default as useVerifyCustomer } from './useVerifyCustomer'
export { default as useActiveCustomer } from './useActiveCustomer'
export { default as useRequestPasswordReset } from './useRequestPasswordReset'
export { default as useResetPassword } from './useResetPassword'

View File

@@ -1,11 +1,22 @@
import { ActiveCustomerQuery } from '@framework/schema'
import { ActiveCustomerQuery,Favorite } from '@framework/schema'
import { activeCustomerQuery } from '@framework/utils/queries/active-customer-query'
import gglFetcher from 'src/utils/gglFetcher'
import useSWR from 'swr'
const useActiveCustomer = () => {
const { data, ...rest } = useSWR<ActiveCustomerQuery>([activeCustomerQuery], gglFetcher)
return { customer: data?.activeCustomer, ...rest }
return {
customer: data?.activeCustomer,
userInfo:{
firstName: data?.activeCustomer?.firstName,
lastName:data?.activeCustomer?.lastName,
email:data?.activeCustomer?.emailAddress,
phoneNumber: data?.activeCustomer?.phoneNumber,
address: data?.activeCustomer?.addresses?.[0]
},
wishlistId: data?.activeCustomer?.favorites?.items.map((val:Favorite)=>val.product.id),
...rest
}
}
export default useActiveCustomer

View File

@@ -0,0 +1,50 @@
import { useState } from 'react'
import useActiveCustomer from './useActiveCustomer'
import fetcher from 'src/utils/fetcher'
import { CommonError } from 'src/domains/interfaces/CommonError'
import { requestPasswordReset } from '@framework/utils/mutations/request-password-reset-mutation'
import { RequestPasswordReset } from '@framework/schema'
interface ForgotPassword {
email: string
}
const useRequestPasswordReset = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
// const { mutate } = useActiveCustomer()
const requestPassword = (
{email}: ForgotPassword,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
fetcher<RequestPasswordReset>({
query: requestPasswordReset,
variables: {
emailAddress: email
},
})
.then((data) => {
if (data.requestPasswordReset.__typename !== 'Success') {
throw CommonError.create(
data.requestPasswordReset.message,
data.requestPasswordReset.errorCode
)
}
// mutate()
fCallBack(true)
return data
})
.catch((error) => {
setError(error)
fCallBack(false, error.message)
})
.finally(() => setLoading(false))
}
return { loading, requestPassword, error }
}
export default useRequestPasswordReset

View File

@@ -0,0 +1,52 @@
import { useState } from 'react'
import useActiveCustomer from './useActiveCustomer'
import fetcher from 'src/utils/fetcher'
import { CommonError } from 'src/domains/interfaces/CommonError'
import { resetPasswordMutation } from '@framework/utils/mutations/reset-password-mutation'
import { ResetPasswordMutation } from '@framework/schema'
interface Props {
token?: string| string[] ,
password:string
}
const useResetPassword = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
// const { mutate } = useActiveCustomer()
const resetPassword = (
{token,password}: Props,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
fetcher<ResetPasswordMutation>({
query: resetPasswordMutation,
variables: {
token: token,
password:password
},
})
.then((data) => {
if (data.resetPassword.__typename !== 'CurrentUser') {
throw CommonError.create(
data.resetPassword.message,
data.resetPassword.errorCode
)
}
// mutate()
fCallBack(true)
return data
})
.catch((error) => {
setError(error)
fCallBack(false, error.message)
})
.finally(() => setLoading(false))
}
return { loading, resetPassword, error }
}
export default useResetPassword

View File

@@ -4,7 +4,6 @@ import { SignupMutation } from '@framework/schema'
import fetcher from 'src/utils/fetcher'
import { CommonError } from 'src/domains/interfaces/CommonError'
import { signupMutation } from '@framework/utils/mutations/sign-up-mutation'
interface SignupInput {
email: string
firstName?: string

View File

@@ -1 +1,2 @@
export { default as useModalCommon } from './useModalCommon'

View File

@@ -1,3 +1,5 @@
export { default as useSearchProducts } from './useSearchProducts'
export { default as useToggleProductWishlist } from './useToggleProductWishlist'
export { default as useProductDetail } from './useProductDetail'

View File

@@ -1 +0,0 @@
export { default as useProductDetail } from './useProductDetail'

View File

@@ -0,0 +1,44 @@
import { useState } from 'react'
import useGetFavoriteProduct from '../account/useGetFavoriteProduct'
import { FavoriteList } from '@framework/schema'
import fetcher from 'src/utils/fetcher'
import { CommonError } from 'src/domains/interfaces/CommonError'
import { toggleWishlistMutation } from '@framework/utils/mutations/toggle-wishlist-mutation'
interface Props {
productId?:string
}
const useToggleProductWishlist = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const { mutate } = useGetFavoriteProduct();
const onToggleProductWishlist = (
{ productId }:Props ,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
fetcher<FavoriteList>({
query: toggleWishlistMutation,
variables: {
productId
},
})
.then((data) => {
mutate()
fCallBack(true)
return data
})
.catch((error) => {
setError(error)
fCallBack(false, error.message)
})
.finally(() => setLoading(false))
}
return { loading, onToggleProductWishlist, error }
}
export default useToggleProductWishlist

View File

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

View File

@@ -1,17 +1,17 @@
import { QueryFavorite } from "@framework/schema"
import { useRouter } from "next/router"
import React, { useEffect, useState } from "react"
import s from './AccountPage.module.scss'
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 s from './AccountPage.module.scss'
import AccountInfomation from "./components/AccountInfomation/AccountInfomation"
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
import FavouriteProducts from "./components/FavouriteProducts/FavouriteProducts"
import OrderInfomation from './components/OrderInformation/OrderInformation'
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data';
import { ACCOUNT_TAB, QUERY_KEY } from "src/utils/constanst.utils"
import { useRouter } from "next/router"
const waiting = [
{
@@ -26,6 +26,8 @@ const waiting = [
}
]
const delivering = [
{
id: "NO 123456",
@@ -52,16 +54,6 @@ const delivered = [
}
]
let account = {
name: "vu duong",
email: "vuduong@gmail.com",
address: "234 Dien Bien Phu Bis, Dakao ward",
state: "District 1",
city: "HCMC",
postalCode: "700000",
phoneNumber: "(+84) 937 937 195"
}
interface AccountPageProps {
defaultActiveContent?: "info" | "orders" | "favorites"
}
@@ -78,11 +70,37 @@ const getTabIndex = (tab?: string): number => {
}
}
const DEFAULT_FAVORITE_ARGS = {
options:{
skip:1, take:DEFAULT_PAGE_SIZE
}
}
const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
const router = useRouter()
const {userInfo} = useActiveCustomer();
const {addingItem,arrangingPayment,cancelled} = useGetUserOrder();
const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2)
const [modalVisible, setModalVisible] = useState(false);
const [optionQueryFavorite, setoptionQueryFavorite] = useState<QueryFavorite>(DEFAULT_FAVORITE_ARGS)
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(() => {
const query = router.query[QUERY_KEY.TAB] as string
const index = getTabIndex(query)
@@ -106,19 +124,20 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
<AccountNavigation defaultActiveIndex={activeTab}>
<TabPane tabName="Customer Information">
<AccountInfomation account={account} onClick={showModal} />
<AccountInfomation account={userInfo} onClick={showModal} />
</TabPane>
<TabPane tabName="Your Orders">
<OrderInfomation waiting={waiting} delivering={delivering} delivered={delivered} />
<OrderInfomation addingItem={addingItem} arrangingPayment={arrangingPayment} cancelled={cancelled} />
</TabPane>
<TabPane tabName="Favourite">
<FavouriteProducts products={PRODUCT_CART_DATA_TEST} />
<FavouriteProducts products={itemWishlist} totalItems={totalItems} />
</TabPane>
</AccountNavigation>
</section>
<EditInfoModal accountInfo={account} closeModal={closeModal} visible={modalVisible} />
<EditInfoModal accountInfo={userInfo} closeModal={closeModal} visible={modalVisible} />
</>
)
}
export default AccountPage
export default AccountPage

View File

@@ -6,17 +6,21 @@ import avatar from '../../assets/avatar.png'
import { ButtonCommon } from 'src/components/common'
import { useActiveCustomer } from 'src/components/hooks/auth'
import { Address } from '@framework/schema'
interface AccountProps {
name: string
email: string
address: string
state: string
city: string
postalCode: string
phoneNumber: string
export interface AccountProps {
firstName?: string
lastName?: string
email?: string
phoneNumber?:string|null
address?: Address
}
const states = [
{name: "District 1", value: "D1"},
{name: "District 2", value: "D2"},
{name: "District 3", value: "D3"}
]
interface AccountInfomationProps {
account: AccountProps
onClick: () => void
@@ -24,11 +28,10 @@ interface AccountInfomationProps {
const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => {
const { customer } = useActiveCustomer()
// need to handle call back when edit account information
const showEditForm = () => onClick()
const state = states.find((val)=>val.value == account.address?.province);
return (
<section className={s.accountInfomation}>
<div className={s.avatar}>
@@ -45,8 +48,8 @@ const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => {
<div className={s.shippingInfo}>Shipping Infomation</div>
<div className={s.accountAddress}>
{account.address +
`, ${account.state}, ${account.city}, ${account.postalCode}`}
{account.address?.streetLine1 +
`, ${state?.name}, ${account.address?.city}, ${account.address?.postalCode}`}
</div>
<div className={s.accountPhoneNumber}>{account.phoneNumber}</div>

View File

@@ -1,6 +1,15 @@
@import '../../../../../../styles/utilities';
.editInfoModal {
.u-form{
width: 60rem;
}
.inputName{
@apply flex justify-between;
.input{
width: 48.5%;
}
}
.input {
@apply bg-white;
margin-bottom: 1.6rem;
@@ -23,6 +32,7 @@
.inputPostalCode {
@apply bg-white;
margin-left: 0.8rem;
width: 100%;
}
.inputPhoneNumber {

View File

@@ -1,19 +1,34 @@
import React from "react"
import React, { useState } from "react"
import s from './EditInfoModal.module.scss'
import { ModalCommon, Inputcommon, SelectCommon, ButtonCommon } from '../../../../../common'
import { ModalCommon, SelectCommon, ButtonCommon } from '../../../../../common'
import { Address } from "@framework/schema";
import {
InputFiledInForm,
} from 'src/components/common'
import * as Yup from 'yup'
import { Form, Formik } from 'formik'
import { useEditCustomerAddress, useEditUserInfo } from "src/components/hooks/account";
import { LANGUAGE } from 'src/utils/language.utils'
import { useMessage } from 'src/components/contexts'
interface EditInfoModalProps {
accountInfo: {name: string, email: string, address: string, state: string, city: string, postalCode: string, phoneNumber: string};
accountInfo: {
firstName?: string
lastName?: string
email?: string
phoneNumber?:string|null
address?: Address
};
visible: boolean;
closeModal: () => void;
}
const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => {
const [stateValue,setStateValue] = useState('');
const { loading, editUserInfo } = useEditUserInfo();
const {editCustomerAddress} = useEditCustomerAddress();
const { showMessageSuccess, showMessageError } = useMessage()
function saveInfo() {
closeModal();
}
const states = [
{name: "District 1", value: "D1"},
@@ -21,44 +36,165 @@ const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoMod
{name: "District 3", value: "D3"}
]
const DisplayingErrorMessagesSchema = Yup.object().shape({
firstName: Yup.string().required('Required'),
lastName: Yup.string().required('Required'),
address: Yup.string().required('Required'),
city: Yup.string().required('Required'),
postalCode: Yup.string(),
phoneNumber: Yup.string(),
})
function onEditUserInfo (
values: {
firstName: string|undefined;
lastName: string|undefined,
address:string|undefined,
city?:string|null,
postalCode?:string|null,
phoneNumber?:string|null
}) {
editUserInfo(
{
firstName: values.firstName,
lastName: values.lastName,
phoneNumber:values.phoneNumber ?? '',
},onChangUserInfoCallBack);
editCustomerAddress(
{
address: values.address ,
city:values.city,
postalCode:values.postalCode,
state:stateValue
},
onChangUserInfoCallBack);
}
function onChangUserInfoCallBack(isSuccess: boolean, message?: string){
if (isSuccess) {
closeModal();
showMessageSuccess("Change Your Information Successfully.", 15000)
} else {
showMessageError(LANGUAGE.MESSAGE.ERROR)
}
}
function state(state:string){
setStateValue(state);
}
return (
<ModalCommon onClose={closeModal} visible={visible} title="Edit Infomation">
<section className={s.editInfoModal}>
<div className={s.input}>
<Inputcommon placeholder="Name" value={accountInfo.name} type="text" />
</div>
<div className={s.inputDisable}>
<Inputcommon placeholder="Email" value={accountInfo.email} type="email" />
</div>
<div className={s.input}>
<Inputcommon placeholder="Address" value={accountInfo.address} type="text" />
</div>
<div className={s.input}>
<Inputcommon placeholder="City" value={accountInfo.city} type="text" />
</div>
<div className="flex">
<div className={s.inputState}>
<SelectCommon type="custom" placeholder="State" options={states} />
<Formik
initialValues={
{
firstName:accountInfo.firstName,
lastName: accountInfo.lastName,
address:accountInfo.address?.streetLine1,
city: accountInfo.address?.city,
postalCode: accountInfo.address?.postalCode,
phoneNumber:accountInfo.phoneNumber
}}
validationSchema={DisplayingErrorMessagesSchema}
onSubmit={onEditUserInfo}
>
{({ errors, touched, isValid, submitForm }) => (
<Form className="u-form">
<div className={s.inputName}>
<div className={s.input}>
<InputFiledInForm
name="firstName"
placeholder="First Name"
error={
touched.firstName && errors.firstName
? errors.firstName.toString()
: ''
}
isShowIconSuccess={touched.firstName && !errors.firstName}
/>
</div>
<div className={s.input}>
<InputFiledInForm
name="lastName"
placeholder="Last Name"
error={
touched.lastName && errors.lastName
? errors.lastName.toString()
: ''
}
isShowIconSuccess={touched.lastName && !errors.lastName}
/>
</div>
</div>
<div className={s.inputPostalCode}>
<Inputcommon placeholder="Postal code" value={accountInfo.postalCode} type="text" />
<div className={s.input}>
<InputFiledInForm
name="address"
placeholder="Address"
error={
touched.address && errors.address
? errors.address.toString()
: ''
}
isShowIconSuccess={touched.address && !errors.address}
/>
</div>
</div>
<div className={s.input}>
<InputFiledInForm
name="city"
placeholder="City"
error={
touched.city && errors.city
? errors.city.toString()
: ''
}
isShowIconSuccess={touched.city && !errors.city}
/>
</div>
<div className={s.inputPhoneNumber}>
<Inputcommon placeholder="Phone number" value={accountInfo.phoneNumber} type="text" />
</div>
<div className="flex">
<div className={s.inputState}>
<SelectCommon initValue={accountInfo.address?.province} selected={accountInfo.address?.province} type="custom" onChange={state} placeholder="State" options={states} size="large"/>
</div>
<div className={s.buttons}>
<ButtonCommon onClick={closeModal} type="light" size="large" >Cancel</ButtonCommon>
<ButtonCommon onClick={saveInfo} size="large" >Save</ButtonCommon>
</div>
<div className={s.inputPostalCode}>
<InputFiledInForm
name="postalCode"
placeholder="Postal code"
error={
touched.postalCode && errors.postalCode
? errors.postalCode.toString()
: ''
}
isShowIconSuccess={touched.postalCode && !errors.postalCode}
/>
</div>
</div>
<div className={s.inputPhoneNumber}>
<InputFiledInForm
name="phoneNumber"
placeholder="Phone number"
error={
touched.phoneNumber && errors.phoneNumber
? errors.phoneNumber.toString()
: ''
}
isShowIconSuccess={touched.phoneNumber && !errors.phoneNumber}
onEnter={isValid ? submitForm : undefined}
/>
</div>
<div className={s.buttons}>
<ButtonCommon onClick={closeModal} type="light" size="large" >Cancel</ButtonCommon>
<ButtonCommon HTMLType="submit" loading={loading} size="large" >Save</ButtonCommon>
</div>
</Form>
)}
</Formik>
</section>
</ModalCommon>
)

View File

@@ -1,18 +1,34 @@
import React from "react"
import s from './FavouriteProducts.module.scss'
import {ProductList} from '../../../../../common'
import { useRouter } from 'next/router'
import React, { useState } from "react"
import { QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import { ProductList } from '../../../../../common'
import { ProductCardProps } from '../../../../../common/ProductCard/ProductCard'
import s from './FavouriteProducts.module.scss'
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 (
<section className={s.favouriteProducts}>
<ProductList data={products} />
<ProductList data={products} total={totalItems} onPageChange={onPageChange}/>
</section>
)
}

View File

@@ -4,50 +4,50 @@ import s from './OrderInformation.module.scss'
import { TabCommon } from '../../../../../common'
import TabPane from 'src/components/common/TabCommon/components/TabPane/TabPane'
import DeliveryItem from '../../../DeliveryItem/DeliveryItem'
import { Order } from "@framework/schema"
interface OrderInformationProps {
waiting: {id: string, products: string[], totalPrice: number}[],
delivering: {id: string, products: string[], totalPrice: number}[],
delivered: {id: string, products: string[], totalPrice: number}[],
addingItem?: Order[],
arrangingPayment?: Order[],
cancelled?: Order[],
}
const OrderInformation = ({ waiting, delivering, delivered} : OrderInformationProps) => {
const OrderInformation = ({ addingItem, arrangingPayment, cancelled} : OrderInformationProps) => {
return (
<section className={s.orderInformation}>
<div className={s.title}>Order Information</div>
<div className={s.tabs}>
<TabCommon>
<TabPane tabName={"Wait for Comfirmation"} >
<TabPane tabName={"Adding Item"} >
<div className={s.blank}></div>
{
waiting.map((order, i) => {
addingItem?.map((order, i) => {
return (
<DeliveryItem key={order.id} id={order.id} status="waiting" products={order.products} totalPrice={order.totalPrice} />
<DeliveryItem key={order.code} id={order.code} status="waiting" products={order.lines} totalPrice={order.total} />
)
})
}
</TabPane>
<TabPane tabName={"Delivering"}>
<TabPane tabName={"Arranging Payment"}>
<div className={s.blank}></div>
{
delivering.map((order, i) => {
arrangingPayment?.map((order, i) => {
return (
<DeliveryItem key={order.id} id={order.id} status="delivering" products={order.products} totalPrice={order.totalPrice} />
<DeliveryItem key={order.id} id={order.id} status="delivering" products={order.lines} totalPrice={order.total} />
)
})
}
</TabPane>
<TabPane tabName={"Delivered"}>
<TabPane tabName={"Cancelled"}>
<div className={s.blank}></div>
{
delivered.map((order, i) => {
cancelled?.map((order, i) => {
return (
<DeliveryItem key={order.id} id={order.id} status="delivered" products={order.products} totalPrice={order.totalPrice} />
<DeliveryItem key={order.id} id={order.id} status="delivered" products={order.lines} totalPrice={order.total} />
)
})
}

View File

@@ -5,12 +5,13 @@ import IdAndStatus from './components/IdAndStatus/IdAndStatus'
import Products from './components/Products/Products'
import TotalPrice from './components/TotalPrice/TotalPrice'
import ReOrder from './components/ReOrder/ReOrder'
import { OrderLine } from "@framework/schema"
interface DeliveryItemProps {
id: string;
status: "waiting" | "delivering" | "delivered";
products: string[];
products?: OrderLine[];
totalPrice: number;
}

View File

@@ -1,19 +1,19 @@
import { OrderLine } from "@framework/schema";
import React from "react"
import s from './Products.module.scss'
interface ProductsProps {
products: string[];
products?: OrderLine[];
}
const Products = ({ products } : ProductsProps) => {
function toString(products:string[]): string {
function toString(products?:OrderLine[]): string {
let strProducts = "";
products.map((prod, i) => {
products?.map((prod, i) => {
if (i === 0) {
strProducts += prod;
strProducts += prod.productVariant?.name;
} else {
strProducts += `, ${prod}`
strProducts += `, ${prod.productVariant?.name}`
}
});
return strProducts;

View File

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

View File

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

View File

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