mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
Merge branch 'release-stable' of github.com:KieIO/grocery-vercel-commerce into feature/m3-viewed-product
This commit is contained in:
@@ -38,7 +38,6 @@ export default function getAllProductsOperation({
|
|||||||
const { data } = await config.fetch<GetAllProductsQuery>(query, {
|
const { data } = await config.fetch<GetAllProductsQuery>(query, {
|
||||||
variables,
|
variables,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
products: data.search.items.map((item) => normalizeSearchResult(item)),
|
products: data.search.items.map((item) => normalizeSearchResult(item)),
|
||||||
totalItems: data.search.totalItems as number,
|
totalItems: data.search.totalItems as number,
|
||||||
|
86
framework/vendure/schema.d.ts
vendored
86
framework/vendure/schema.d.ts
vendored
@@ -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';
|
import { FacetValue } from './schema.d';
|
||||||
export type Maybe<T> = T | null
|
export type Maybe<T> = T | null
|
||||||
export type Exact<T extends { [key: string]: unknown }> = {
|
export type Exact<T extends { [key: string]: unknown }> = {
|
||||||
@@ -304,6 +307,11 @@ export type MutationResetPasswordArgs = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Address = Node & {
|
export type Address = Node & {
|
||||||
|
updateCustomerAddress:
|
||||||
|
| {
|
||||||
|
__typename?: 'Address'
|
||||||
|
id: Scalars['ID']
|
||||||
|
}
|
||||||
__typename?: 'Address'
|
__typename?: 'Address'
|
||||||
id: Scalars['ID']
|
id: Scalars['ID']
|
||||||
createdAt: Scalars['DateTime']
|
createdAt: Scalars['DateTime']
|
||||||
@@ -322,6 +330,9 @@ export type Address = Node & {
|
|||||||
customFields?: Maybe<Scalars['JSON']>
|
customFields?: Maybe<Scalars['JSON']>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type Asset = Node & {
|
export type Asset = Node & {
|
||||||
__typename?: 'Asset'
|
__typename?: 'Asset'
|
||||||
id: Scalars['ID']
|
id: Scalars['ID']
|
||||||
@@ -1459,6 +1470,11 @@ export type CustomerListOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Customer = Node & {
|
export type Customer = Node & {
|
||||||
|
updateCustomer:
|
||||||
|
| {
|
||||||
|
__typename?: 'Customer'
|
||||||
|
id: Scalars['ID']
|
||||||
|
}
|
||||||
__typename?: 'Customer'
|
__typename?: 'Customer'
|
||||||
id: Scalars['ID']
|
id: Scalars['ID']
|
||||||
createdAt: Scalars['DateTime']
|
createdAt: Scalars['DateTime']
|
||||||
@@ -1466,7 +1482,7 @@ export type Customer = Node & {
|
|||||||
title?: Maybe<Scalars['String']>
|
title?: Maybe<Scalars['String']>
|
||||||
firstName: Scalars['String']
|
firstName: Scalars['String']
|
||||||
lastName: Scalars['String']
|
lastName: Scalars['String']
|
||||||
phoneNumber?: Maybe<Scalars['String']>
|
phoneNumber?: Maybe<Scalars['String']>
|
||||||
emailAddress: Scalars['String']
|
emailAddress: Scalars['String']
|
||||||
addresses?: Maybe<Array<Address>>
|
addresses?: Maybe<Array<Address>>
|
||||||
orders: OrderList
|
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<{
|
export type VerifyCustomerAccountVariables = Exact<{
|
||||||
token: Scalars['String']
|
token: Scalars['String']
|
||||||
password?: Maybe<Scalars['String']>
|
password?: Maybe<Scalars['String']>
|
||||||
@@ -3179,8 +3225,9 @@ export type SignupMutationVariables = Exact<{
|
|||||||
input: RegisterCustomerInput
|
input: RegisterCustomerInput
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export type SignupMutation = { __typename?: 'Mutation' } & {
|
|
||||||
registerCustomerAccount:
|
export type RequestPasswordReset = { __typename?: 'Mutation' } & {
|
||||||
|
requestPasswordReset:
|
||||||
| ({ __typename: 'Success' } & Pick<Success, 'success'>)
|
| ({ __typename: 'Success' } & Pick<Success, 'success'>)
|
||||||
| ({ __typename: 'MissingPasswordError' } & Pick<
|
| ({ __typename: 'MissingPasswordError' } & Pick<
|
||||||
MissingPasswordError,
|
MissingPasswordError,
|
||||||
@@ -3192,17 +3239,48 @@ export type SignupMutation = { __typename?: 'Mutation' } & {
|
|||||||
>)
|
>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type ActiveCustomerQueryVariables = Exact<{ [key: string]: never }>
|
export type ActiveCustomerQueryVariables = Exact<{ [key: string]: never }>
|
||||||
|
|
||||||
export type ActiveCustomerQuery = { __typename?: 'Query' } & {
|
export type ActiveCustomerQuery = { __typename?: 'Query' } & {
|
||||||
activeCustomer?: Maybe<
|
activeCustomer?: Maybe<
|
||||||
{ __typename?: 'Customer' } & Pick<
|
{ __typename?: 'Customer' } & Pick<
|
||||||
Customer,
|
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<{
|
export type GetAllProductPathsQueryVariables = Exact<{
|
||||||
first?: Maybe<Scalars['Int']>
|
first?: Maybe<Scalars['Int']>
|
||||||
}>
|
}>
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
export const requestPasswordReset = /* GraphQL */ `
|
||||||
|
mutation RequestPasswordReset($emailAddress: String!) {
|
||||||
|
requestPasswordReset(emailAddress: $emailAddress) {
|
||||||
|
__typename
|
||||||
|
...on Success{
|
||||||
|
success
|
||||||
|
}
|
||||||
|
...on ErrorResult{
|
||||||
|
errorCode
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
15
framework/vendure/utils/mutations/reset-password-mutation.ts
Normal file
15
framework/vendure/utils/mutations/reset-password-mutation.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -0,0 +1,9 @@
|
|||||||
|
export const toggleWishlistMutation = /* GraphQL */ `
|
||||||
|
mutation toggleFavorite($productId:ID!){
|
||||||
|
toggleFavorite(productId:$productId){
|
||||||
|
items{
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -0,0 +1,14 @@
|
|||||||
|
export const updateCustomerAddress = /* GraphQL */ `
|
||||||
|
mutation updateCustomerAddress($input: UpdateAddressInput!){
|
||||||
|
updateCustomerAddress(input: $input){
|
||||||
|
__typename
|
||||||
|
...on Address{
|
||||||
|
id
|
||||||
|
streetLine1
|
||||||
|
city
|
||||||
|
postalCode
|
||||||
|
province
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -0,0 +1,13 @@
|
|||||||
|
export const updateCustomer = /* GraphQL */ `
|
||||||
|
mutation updateCustomer($input: UpdateCustomerInput!){
|
||||||
|
updateCustomer(input:$input){
|
||||||
|
__typename
|
||||||
|
...on Customer{
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
phoneNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -1,6 +1,6 @@
|
|||||||
import { Cart } from '@commerce/types/cart'
|
import { Cart } from '@commerce/types/cart'
|
||||||
import { ProductCard, Product } from '@commerce/types/product'
|
import { ProductCard, Product } from '@commerce/types/product'
|
||||||
import { CartFragment, SearchResultFragment } from '../schema'
|
import { CartFragment, SearchResultFragment,Favorite } from '../schema'
|
||||||
|
|
||||||
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
||||||
return {
|
return {
|
||||||
@@ -23,6 +23,18 @@ export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeFavoriteProductResult(item: Favorite) {
|
||||||
|
return {
|
||||||
|
id: item.product.id,
|
||||||
|
name: item.product.name,
|
||||||
|
slug: item.product.slug,
|
||||||
|
imageSrc: item.product.assets[0].preview ? item.product.assets[0].preview + '?w=800&mode=crop' : '',
|
||||||
|
price: item.product.variants[0].priceWithTax as number / 100,
|
||||||
|
currencyCode: item.product.variants[0].currencyCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function normalizeCart(order: CartFragment): Cart {
|
export function normalizeCart(order: CartFragment): Cart {
|
||||||
return {
|
return {
|
||||||
id: order.id.toString(),
|
id: order.id.toString(),
|
||||||
|
@@ -1,10 +1,24 @@
|
|||||||
export const activeCustomerQuery = /* GraphQL */ `
|
export const activeCustomerQuery = /* GraphQL */ `
|
||||||
query activeCustomer {
|
query activeCustomer {
|
||||||
activeCustomer {
|
activeCustomer {
|
||||||
id
|
id
|
||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
emailAddress
|
emailAddress
|
||||||
|
favorites{
|
||||||
|
items{
|
||||||
|
product{
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
phoneNumber
|
||||||
|
addresses{
|
||||||
|
streetLine1
|
||||||
|
city
|
||||||
|
province
|
||||||
|
postalCode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
19
framework/vendure/utils/queries/get-user-order-query.ts
Normal file
19
framework/vendure/utils/queries/get-user-order-query.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export const getUserOrderQuery = /* GraphQL */ `
|
||||||
|
query activeCustomer {
|
||||||
|
activeCustomer {
|
||||||
|
orders{
|
||||||
|
items{
|
||||||
|
lines{
|
||||||
|
productVariant{
|
||||||
|
name
|
||||||
|
}
|
||||||
|
quantity
|
||||||
|
}
|
||||||
|
total
|
||||||
|
state
|
||||||
|
code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
16
framework/vendure/utils/queries/user-info-query.ts
Normal file
16
framework/vendure/utils/queries/user-info-query.ts
Normal 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
10
pages/forgot-password.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { FormForgot, Layout } from 'src/components/common'
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormForgot/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
NotFound.Layout = Layout
|
@@ -8,20 +8,23 @@ import { FeaturedProductsCarousel, FreshProducts, HomeBanner, HomeCategories, Ho
|
|||||||
import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice';
|
import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice';
|
||||||
import { FACET } from 'src/utils/constanst.utils';
|
import { FACET } from 'src/utils/constanst.utils';
|
||||||
import { FilterOneVatiant, getFacetIdByName } from 'src/utils/funtion.utils';
|
import { FilterOneVatiant, getFacetIdByName } from 'src/utils/funtion.utils';
|
||||||
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED } from 'src/utils/constanst.utils';
|
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED,COLLECTION_SLUG_SPICE } from 'src/utils/constanst.utils';
|
||||||
import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils';
|
import { getAllFacetValueIdsByParentCode, getAllFacetValuesForFeatuedProducts, getAllPromies, getFreshFacetId } from 'src/utils/funtion.utils';
|
||||||
import { PromiseWithKey } from 'src/utils/types.utils';
|
import { PromiseWithKey } from 'src/utils/types.utils';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
featuredAndDiscountFacetsValue: FacetValue[],
|
featuredAndDiscountFacetsValue: FacetValue[],
|
||||||
freshProducts: ProductCard[],
|
freshProducts: ProductCard[],
|
||||||
featuredProducts: ProductCard[],
|
featuredProducts: ProductCard[],
|
||||||
collections: Collection[]
|
collections: Collection[]
|
||||||
veggie: ProductCard[],
|
spiceProducts:ProductCard[]
|
||||||
|
veggie: ProductCard[],
|
||||||
|
|
||||||
}
|
}
|
||||||
export default function Home({ featuredAndDiscountFacetsValue,
|
export default function Home({ featuredAndDiscountFacetsValue, veggie,
|
||||||
freshProducts, featuredProducts, veggie,
|
freshProducts, featuredProducts,
|
||||||
collections }: Props) {
|
collections,spiceProducts }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HomeBanner />
|
<HomeBanner />
|
||||||
@@ -30,7 +33,7 @@ export default function Home({ featuredAndDiscountFacetsValue,
|
|||||||
<HomeCollection data = {veggie}/>
|
<HomeCollection data = {veggie}/>
|
||||||
<FreshProducts data={freshProducts} collections={collections} />
|
<FreshProducts data={freshProducts} collections={collections} />
|
||||||
<HomeVideo />
|
<HomeVideo />
|
||||||
<HomeSpice />
|
{spiceProducts.length>0 && <HomeSpice data={spiceProducts}/>}
|
||||||
<FeaturedProductsCarousel data={featuredProducts} featuredFacetsValue={featuredAndDiscountFacetsValue} />
|
<FeaturedProductsCarousel data={featuredProducts} featuredFacetsValue={featuredAndDiscountFacetsValue} />
|
||||||
<HomeCTA />
|
<HomeCTA />
|
||||||
<HomeRecipe />
|
<HomeRecipe />
|
||||||
@@ -76,6 +79,7 @@ export async function getStaticProps({
|
|||||||
props.freshProducts = []
|
props.freshProducts = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//veggie
|
||||||
const veggieProductvariables: ProductVariables = {
|
const veggieProductvariables: ProductVariables = {
|
||||||
groupByProduct:false
|
groupByProduct:false
|
||||||
}
|
}
|
||||||
@@ -115,16 +119,24 @@ export async function getStaticProps({
|
|||||||
})
|
})
|
||||||
promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' })
|
promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' })
|
||||||
|
|
||||||
|
// spiceProducts
|
||||||
|
const spiceProducts = commerce.getAllProducts({
|
||||||
|
variables: {
|
||||||
|
collectionSlug: COLLECTION_SLUG_SPICE,
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
preview,
|
||||||
|
})
|
||||||
|
promisesWithKey.push({ key: 'spiceProducts', promise: spiceProducts, keyResult: 'products' })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const promises = getAllPromies(promisesWithKey)
|
const promises = getAllPromies(promisesWithKey)
|
||||||
const rs = await Promise.all(promises)
|
const rs = await Promise.all(promises)
|
||||||
|
|
||||||
promisesWithKey.map((item, index) => {
|
promisesWithKey.map((item, index) => {
|
||||||
props[item.key] = item.keyResult ? FilterOneVatiant(rs[index][item.keyResult]) : rs[index]
|
props[item.key] = item.keyResult ? FilterOneVatiant(rs[index][item.keyResult]) : rs[index]
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props,
|
props,
|
||||||
revalidate: 60,
|
revalidate: 60,
|
||||||
|
@@ -3,13 +3,14 @@ import { Collection } from '@commerce/types/collection'
|
|||||||
import { Product, ProductCard } from '@commerce/types/product'
|
import { Product, ProductCard } from '@commerce/types/product'
|
||||||
import commerce from '@lib/api/commerce'
|
import commerce from '@lib/api/commerce'
|
||||||
import { GetStaticPathsContext, GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
import { GetStaticPathsContext, GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
||||||
import { useEffect } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Layout, RecipeDetail, RecommendedRecipes, RelevantBlogPosts } from 'src/components/common'
|
import { Layout, RecipeDetail, RecommendedRecipes, RelevantBlogPosts } from 'src/components/common'
|
||||||
import { useLocalStorage } from 'src/components/hooks/useLocalStorage'
|
import { useLocalStorage } from 'src/components/hooks/useLocalStorage'
|
||||||
import { ProductInfoDetail, ReleventProducts, ViewedProducts } from 'src/components/modules/product-detail'
|
import { ProductInfoDetail, ReleventProducts, ViewedProducts } from 'src/components/modules/product-detail'
|
||||||
import { LOCAL_STORAGE_KEY, MAX_PRODUCT_CAROUSEL, REVALIDATE_TIME } from 'src/utils/constanst.utils'
|
import { LOCAL_STORAGE_KEY, MAX_PRODUCT_CAROUSEL, REVALIDATE_TIME } from 'src/utils/constanst.utils'
|
||||||
import { BLOGS_DATA_TEST, INGREDIENT_DATA_TEST, RECIPE_DATA_TEST } from 'src/utils/demo-data'
|
import { BLOGS_DATA_TEST, INGREDIENT_DATA_TEST, RECIPE_DATA_TEST } from 'src/utils/demo-data'
|
||||||
import { getAllPromies } from 'src/utils/funtion.utils'
|
import { getAllPromies } from 'src/utils/funtion.utils'
|
||||||
|
import { normalizeProductCard } from '@framework/utils/normalize';
|
||||||
import { PromiseWithKey } from 'src/utils/types.utils'
|
import { PromiseWithKey } from 'src/utils/types.utils'
|
||||||
interface Props {
|
interface Props {
|
||||||
relevantProducts: ProductCard[],
|
relevantProducts: ProductCard[],
|
||||||
@@ -17,23 +18,17 @@ interface Props {
|
|||||||
collections: Collection[]
|
collections: Collection[]
|
||||||
}
|
}
|
||||||
export default function Slug({ product, relevantProducts, collections }: Props) {
|
export default function Slug({ product, relevantProducts, collections }: Props) {
|
||||||
const [viewedProduct,setViewedProduct] = useLocalStorage<Product[]>(LOCAL_STORAGE_KEY.VIEWEDPRODUCT, []);
|
const [local,setLocal] = useLocalStorage<Product[]>(LOCAL_STORAGE_KEY.VIEWEDPRODUCT, []);
|
||||||
|
const [viewed, setViewed] = useState<ProductCard[]>([])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// const local = localStorage.getItem(LOCAL_STORAGE_KEY.VIEWEDPRODUCT)
|
if(local){
|
||||||
// if(local){
|
if(!local.find(p => p.id === product.id)){
|
||||||
// const oldList:Product[] = JSON.parse(local)
|
setLocal([...local, product])
|
||||||
// if(!oldList.find(p => p.id === product.id)){
|
}else{
|
||||||
// localStorage.setItem(LOCAL_STORAGE_KEY.VIEWEDPRODUCT,JSON.stringify([...oldList,product]))
|
setViewed(local.filter((p)=>p.id !== product.id).map((p)=>normalizeProductCard(p)))
|
||||||
// }
|
|
||||||
// }else{
|
|
||||||
// localStorage.setItem(LOCAL_STORAGE_KEY.VIEWEDPRODUCT,JSON.stringify([product]))
|
|
||||||
// }
|
|
||||||
if(viewedProduct){
|
|
||||||
if(!viewedProduct.find(p => p.id === product.id)){
|
|
||||||
setViewedProduct([...viewedProduct, product])
|
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
setViewedProduct([product])
|
setLocal([product])
|
||||||
}
|
}
|
||||||
}, [product])
|
}, [product])
|
||||||
|
|
||||||
@@ -42,7 +37,7 @@ export default function Slug({ product, relevantProducts, collections }: Props)
|
|||||||
<RecipeDetail ingredients={INGREDIENT_DATA_TEST} />
|
<RecipeDetail ingredients={INGREDIENT_DATA_TEST} />
|
||||||
<RecommendedRecipes data={RECIPE_DATA_TEST} />
|
<RecommendedRecipes data={RECIPE_DATA_TEST} />
|
||||||
<ReleventProducts data={relevantProducts} collections={collections}/>
|
<ReleventProducts data={relevantProducts} collections={collections}/>
|
||||||
<ViewedProducts/>
|
<ViewedProducts data={viewed}/>
|
||||||
<RelevantBlogPosts data={BLOGS_DATA_TEST} title="relevent blog posts" />
|
<RelevantBlogPosts data={BLOGS_DATA_TEST} title="relevent blog posts" />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
10
pages/reset-password.tsx
Normal file
10
pages/reset-password.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { FormResetPassword, Layout } from 'src/components/common'
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormResetPassword/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
NotFound.Layout = Layout
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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;
|
@@ -58,6 +58,10 @@ const HeaderMenu = memo(
|
|||||||
onClick: openModalRegister,
|
onClick: openModalRegister,
|
||||||
name: 'Create account',
|
name: 'Create account',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
link: '/forgot-password',
|
||||||
|
name: 'Forgot Password',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
[openModalLogin, openModalRegister]
|
[openModalLogin, openModalRegister]
|
||||||
)
|
)
|
||||||
|
@@ -2,19 +2,38 @@ import classNames from 'classnames'
|
|||||||
import IconHeart from 'src/components/icons/IconHeart'
|
import IconHeart from 'src/components/icons/IconHeart'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
import s from './ItemWishList.module.scss'
|
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 {
|
interface Props {
|
||||||
|
id:string,
|
||||||
isActive?: boolean,
|
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(
|
return(
|
||||||
<div className={classNames({
|
<div className={classNames({
|
||||||
[s.heartToggle]: true,
|
[s.heartToggle]: true,
|
||||||
[s.isToggleOn]: isActive
|
[s.isToggleOn]: isActive
|
||||||
})}
|
})}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
onClick={toggleWishlist}
|
||||||
>
|
>
|
||||||
<IconHeart />
|
<IconHeart />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -16,6 +16,7 @@ import Router from 'next/router'
|
|||||||
export interface ProductCardProps extends ProductCard {
|
export interface ProductCardProps extends ProductCard {
|
||||||
buttonText?: string
|
buttonText?: string
|
||||||
isSingleButton?: boolean,
|
isSingleButton?: boolean,
|
||||||
|
activeWishlist?:boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductCardComponent = ({
|
const ProductCardComponent = ({
|
||||||
@@ -31,7 +32,8 @@ const ProductCardComponent = ({
|
|||||||
isNotSell,
|
isNotSell,
|
||||||
isSingleButton,
|
isSingleButton,
|
||||||
productVariantId,
|
productVariantId,
|
||||||
productVariantName
|
productVariantName,
|
||||||
|
activeWishlist
|
||||||
}: ProductCardProps) => {
|
}: ProductCardProps) => {
|
||||||
|
|
||||||
const {addProduct,loading} = useAddProductToCart()
|
const {addProduct,loading} = useAddProductToCart()
|
||||||
@@ -64,6 +66,7 @@ const ProductCardComponent = ({
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={s.productCardWarpper}>
|
<div className={s.productCardWarpper}>
|
||||||
<div className={s.cardTop}>
|
<div className={s.cardTop}>
|
||||||
@@ -93,7 +96,7 @@ const ProductCardComponent = ({
|
|||||||
<div className={s.cardMidBot}>
|
<div className={s.cardMidBot}>
|
||||||
<div className={s.productPrice}>{price} {currencyCode}</div>
|
<div className={s.productPrice}>{price} {currencyCode}</div>
|
||||||
<div className={s.wishList}>
|
<div className={s.wishList}>
|
||||||
<ItemWishList />
|
<ItemWishList isActive={activeWishlist} id={id}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { useActiveCustomer } from 'src/components/hooks/auth'
|
||||||
import { DEFAULT_PAGE_SIZE, ROUTE } from 'src/utils/constanst.utils'
|
import { DEFAULT_PAGE_SIZE, ROUTE } from 'src/utils/constanst.utils'
|
||||||
import { ButtonCommon, EmptyCommon } from '..'
|
import { ButtonCommon, EmptyCommon } from '..'
|
||||||
import PaginationCommon from '../PaginationCommon/PaginationCommon'
|
import PaginationCommon from '../PaginationCommon/PaginationCommon'
|
||||||
import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard'
|
import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard'
|
||||||
import s from "./ProductList.module.scss"
|
import s from "./ProductList.module.scss"
|
||||||
|
|
||||||
interface ProductListProps {
|
interface ProductListProps {
|
||||||
data: ProductCardProps[],
|
data: ProductCardProps[],
|
||||||
total?: number,
|
total?: number,
|
||||||
@@ -14,8 +14,10 @@ interface ProductListProps {
|
|||||||
onPageChange?: (page: number) => void
|
onPageChange?: (page: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductList = ({ data, total = data.length, defaultCurrentPage, onPageChange }: ProductListProps) => {
|
const ProductList = ({ data, total = data?.length, defaultCurrentPage, onPageChange }: ProductListProps) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const {wishlistId } = useActiveCustomer();
|
||||||
|
|
||||||
const handlePageChange = (page: number) => {
|
const handlePageChange = (page: number) => {
|
||||||
onPageChange && onPageChange(page)
|
onPageChange && onPageChange(page)
|
||||||
}
|
}
|
||||||
@@ -32,19 +34,20 @@ const ProductList = ({ data, total = data.length, defaultCurrentPage, onPageChan
|
|||||||
<div className={s.wrapper}>
|
<div className={s.wrapper}>
|
||||||
<div className={s.list}>
|
<div className={s.list}>
|
||||||
{
|
{
|
||||||
data.map((product, index) => {
|
data?.map((product, index) => {
|
||||||
return <ProductCard {...product} key={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 />
|
<EmptyCommon />
|
||||||
<ButtonCommon onClick={handleShowAllProduct}>Show all products</ButtonCommon>
|
<ButtonCommon onClick={handleShowAllProduct}>Show all products</ButtonCommon>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className={classNames(s.pagination, { [s.hide]: data.length === 0 })}>
|
<div className={classNames(s.pagination, { [s.hide]: data?.length === 0 })}>
|
||||||
<PaginationCommon defaultCurrent={defaultCurrentPage} total={total} pageSize={DEFAULT_PAGE_SIZE} onChange={handlePageChange} />
|
<PaginationCommon defaultCurrent={defaultCurrentPage} total={total ?? 0} pageSize={DEFAULT_PAGE_SIZE} onChange={handlePageChange} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
width: 20.6rem;
|
width: 20.6rem;
|
||||||
.selectTrigger {
|
.selectTrigger {
|
||||||
width: 20.6rem;
|
width: 20.6rem;
|
||||||
padding: 1.2rem 1.6rem;
|
padding: 1.6rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.large {
|
&.large {
|
||||||
|
@@ -5,6 +5,8 @@ import s from './SelectCommon.module.scss'
|
|||||||
import SelectOption from './SelectOption/SelectOption'
|
import SelectOption from './SelectOption/SelectOption'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
selected?:string|null,
|
||||||
|
initValue?:string|null,
|
||||||
placeholder? : string,
|
placeholder? : string,
|
||||||
value?: string,
|
value?: string,
|
||||||
size?: 'base' | 'large',
|
size?: 'base' | 'large',
|
||||||
@@ -13,16 +15,16 @@ interface Props {
|
|||||||
onChange?: (value: string) => void,
|
onChange?: (value: string) => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SelectCommon = ({ value, type = 'default', size = 'base', options, placeholder, onChange}: Props) => {
|
const SelectCommon = ({selected,initValue, type = 'default', size = 'base', options, placeholder, onChange}: Props) => {
|
||||||
const [selectedName, setSelectedName] = useState<string>()
|
const [selectedName, setSelectedName] = useState(placeholder)
|
||||||
const [selectedValue, setSelectedValue] = useState<string>('')
|
const [selectedValue, setSelectedValue] = useState('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(()=>{
|
||||||
setSelectedValue(value || '')
|
const nameSelect = options.find((val)=>val.value === selected);
|
||||||
|
setSelectedName(nameSelect?.name ?? 'State');
|
||||||
const name = options.find(item => item.value === value)?.name
|
setSelectedValue(initValue ?? '');
|
||||||
setSelectedName(name)
|
onChange && onChange(initValue ?? '');
|
||||||
}, [value, options])
|
},[])
|
||||||
|
|
||||||
const changeSelectedName = (value: string) => {
|
const changeSelectedName = (value: string) => {
|
||||||
setSelectedValue(value)
|
setSelectedValue(value)
|
||||||
|
@@ -51,6 +51,8 @@ export { default as LayoutCheckout} from './LayoutCheckout/LayoutCheckout'
|
|||||||
export { default as InputPasswordFiledInForm} from './InputPasswordFiledInForm/InputPasswordFiledInForm'
|
export { default as InputPasswordFiledInForm} from './InputPasswordFiledInForm/InputPasswordFiledInForm'
|
||||||
export { default as InputFiledInForm} from './InputFiledInForm/InputFiledInForm'
|
export { default as InputFiledInForm} from './InputFiledInForm/InputFiledInForm'
|
||||||
export { default as MessageCommon} from './MessageCommon/MessageCommon'
|
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 ProductCardSkeleton} from './ProductCardSkeleton/ProductCardSkeleton'
|
||||||
export { default as ListProductCardSkeleton} from './ListProductCardSkeleton/ListProductCardSkeleton'
|
export { default as ListProductCardSkeleton} from './ListProductCardSkeleton/ListProductCardSkeleton'
|
||||||
|
|
||||||
|
4
src/components/hooks/account/index.ts
Normal file
4
src/components/hooks/account/index.ts
Normal 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'
|
55
src/components/hooks/account/useEditCustomerAddress.tsx
Normal file
55
src/components/hooks/account/useEditCustomerAddress.tsx
Normal 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
|
51
src/components/hooks/account/useEditUserInfo.tsx
Normal file
51
src/components/hooks/account/useEditUserInfo.tsx
Normal 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
|
16
src/components/hooks/account/useGetFavoriteProduct.tsx
Normal file
16
src/components/hooks/account/useGetFavoriteProduct.tsx
Normal 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
|
20
src/components/hooks/account/useGetUserOrder.tsx
Normal file
20
src/components/hooks/account/useGetUserOrder.tsx
Normal 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
|
@@ -3,4 +3,6 @@ export { default as useLogin } from './useLogin'
|
|||||||
export { default as useLogout } from './useLogout'
|
export { default as useLogout } from './useLogout'
|
||||||
export { default as useVerifyCustomer } from './useVerifyCustomer'
|
export { default as useVerifyCustomer } from './useVerifyCustomer'
|
||||||
export { default as useActiveCustomer } from './useActiveCustomer'
|
export { default as useActiveCustomer } from './useActiveCustomer'
|
||||||
|
export { default as useRequestPasswordReset } from './useRequestPasswordReset'
|
||||||
|
export { default as useResetPassword } from './useResetPassword'
|
||||||
|
|
||||||
|
@@ -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 { activeCustomerQuery } from '@framework/utils/queries/active-customer-query'
|
||||||
import gglFetcher from 'src/utils/gglFetcher'
|
import gglFetcher from 'src/utils/gglFetcher'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
|
|
||||||
const useActiveCustomer = () => {
|
const useActiveCustomer = () => {
|
||||||
const { data, ...rest } = useSWR<ActiveCustomerQuery>([activeCustomerQuery], gglFetcher)
|
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
|
export default useActiveCustomer
|
||||||
|
50
src/components/hooks/auth/useRequestPasswordReset.tsx
Normal file
50
src/components/hooks/auth/useRequestPasswordReset.tsx
Normal 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
|
52
src/components/hooks/auth/useResetPassword.tsx
Normal file
52
src/components/hooks/auth/useResetPassword.tsx
Normal 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
|
@@ -4,7 +4,6 @@ import { SignupMutation } from '@framework/schema'
|
|||||||
import fetcher from 'src/utils/fetcher'
|
import fetcher from 'src/utils/fetcher'
|
||||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||||
import { signupMutation } from '@framework/utils/mutations/sign-up-mutation'
|
import { signupMutation } from '@framework/utils/mutations/sign-up-mutation'
|
||||||
|
|
||||||
interface SignupInput {
|
interface SignupInput {
|
||||||
email: string
|
email: string
|
||||||
firstName?: string
|
firstName?: string
|
||||||
|
@@ -1 +1,2 @@
|
|||||||
export { default as useModalCommon } from './useModalCommon'
|
export { default as useModalCommon } from './useModalCommon'
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
export { default as useSearchProducts } from './useSearchProducts'
|
export { default as useSearchProducts } from './useSearchProducts'
|
||||||
|
export { default as useToggleProductWishlist } from './useToggleProductWishlist'
|
||||||
export { default as useProductDetail } from './useProductDetail'
|
export { default as useProductDetail } from './useProductDetail'
|
||||||
|
|
||||||
|
|
||||||
|
44
src/components/hooks/product/useToggleProductWishlist.tsx
Normal file
44
src/components/hooks/product/useToggleProductWishlist.tsx
Normal 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
|
@@ -4,7 +4,17 @@
|
|||||||
@apply bg-background-gray;
|
@apply bg-background-gray;
|
||||||
padding: 3.2rem 2rem;
|
padding: 3.2rem 2rem;
|
||||||
min-height: 70rem;
|
min-height: 70rem;
|
||||||
|
@screen xl {
|
||||||
|
section{
|
||||||
|
div{
|
||||||
|
div{
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@screen md {
|
@screen md {
|
||||||
padding-left: 2.8rem;
|
padding-left: 2.8rem;
|
||||||
padding-right: 2.8rem;
|
padding-right: 2.8rem;
|
||||||
@@ -28,4 +38,5 @@
|
|||||||
margin-bottom: 3.8rem;
|
margin-bottom: 3.8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -1,17 +1,17 @@
|
|||||||
|
import { QueryFavorite } from "@framework/schema"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
import React, { useEffect, useState } from "react"
|
import React, { useEffect, useState } from "react"
|
||||||
import s from './AccountPage.module.scss'
|
|
||||||
|
|
||||||
import { HeadingCommon, TabPane } from "src/components/common"
|
import { HeadingCommon, TabPane } from "src/components/common"
|
||||||
|
import { useGetFavoriteProduct, useGetUserOrder } from 'src/components/hooks/account'
|
||||||
|
import { useActiveCustomer } from 'src/components/hooks/auth'
|
||||||
|
import { ACCOUNT_TAB, DEFAULT_PAGE_SIZE, QUERY_KEY } from "src/utils/constanst.utils"
|
||||||
|
import { getPageFromQuery } from 'src/utils/funtion.utils'
|
||||||
import AccountNavigation from '../AccountNavigation/AccountNavigation'
|
import AccountNavigation from '../AccountNavigation/AccountNavigation'
|
||||||
|
import s from './AccountPage.module.scss'
|
||||||
import AccountInfomation from "./components/AccountInfomation/AccountInfomation"
|
import AccountInfomation from "./components/AccountInfomation/AccountInfomation"
|
||||||
|
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
|
||||||
import FavouriteProducts from "./components/FavouriteProducts/FavouriteProducts"
|
import FavouriteProducts from "./components/FavouriteProducts/FavouriteProducts"
|
||||||
import OrderInfomation from './components/OrderInformation/OrderInformation'
|
import OrderInfomation from './components/OrderInformation/OrderInformation'
|
||||||
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
|
|
||||||
|
|
||||||
import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data';
|
|
||||||
import { ACCOUNT_TAB, QUERY_KEY } from "src/utils/constanst.utils"
|
|
||||||
import { useRouter } from "next/router"
|
|
||||||
|
|
||||||
const waiting = [
|
const waiting = [
|
||||||
{
|
{
|
||||||
@@ -26,6 +26,8 @@ const waiting = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const delivering = [
|
const delivering = [
|
||||||
{
|
{
|
||||||
id: "NO 123456",
|
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 {
|
interface AccountPageProps {
|
||||||
defaultActiveContent?: "info" | "orders" | "favorites"
|
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 AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
const {userInfo} = useActiveCustomer();
|
||||||
|
|
||||||
|
const {addingItem,arrangingPayment,cancelled} = useGetUserOrder();
|
||||||
|
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2)
|
const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2)
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
const [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(() => {
|
useEffect(() => {
|
||||||
const query = router.query[QUERY_KEY.TAB] as string
|
const query = router.query[QUERY_KEY.TAB] as string
|
||||||
const index = getTabIndex(query)
|
const index = getTabIndex(query)
|
||||||
@@ -106,19 +124,20 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
|
|||||||
|
|
||||||
<AccountNavigation defaultActiveIndex={activeTab}>
|
<AccountNavigation defaultActiveIndex={activeTab}>
|
||||||
<TabPane tabName="Customer Information">
|
<TabPane tabName="Customer Information">
|
||||||
<AccountInfomation account={account} onClick={showModal} />
|
<AccountInfomation account={userInfo} onClick={showModal} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tabName="Your Orders">
|
<TabPane tabName="Your Orders">
|
||||||
<OrderInfomation waiting={waiting} delivering={delivering} delivered={delivered} />
|
<OrderInfomation addingItem={addingItem} arrangingPayment={arrangingPayment} cancelled={cancelled} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tabName="Favourite">
|
<TabPane tabName="Favourite">
|
||||||
<FavouriteProducts products={PRODUCT_CART_DATA_TEST} />
|
<FavouriteProducts products={itemWishlist} totalItems={totalItems} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
</AccountNavigation>
|
</AccountNavigation>
|
||||||
</section>
|
</section>
|
||||||
<EditInfoModal accountInfo={account} closeModal={closeModal} visible={modalVisible} />
|
<EditInfoModal accountInfo={userInfo} closeModal={closeModal} visible={modalVisible} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AccountPage
|
export default AccountPage
|
||||||
|
|
||||||
|
@@ -6,17 +6,21 @@ import avatar from '../../assets/avatar.png'
|
|||||||
|
|
||||||
import { ButtonCommon } from 'src/components/common'
|
import { ButtonCommon } from 'src/components/common'
|
||||||
import { useActiveCustomer } from 'src/components/hooks/auth'
|
import { useActiveCustomer } from 'src/components/hooks/auth'
|
||||||
|
import { Address } from '@framework/schema'
|
||||||
|
|
||||||
interface AccountProps {
|
export interface AccountProps {
|
||||||
name: string
|
firstName?: string
|
||||||
email: string
|
lastName?: string
|
||||||
address: string
|
email?: string
|
||||||
state: string
|
phoneNumber?:string|null
|
||||||
city: string
|
address?: Address
|
||||||
postalCode: string
|
|
||||||
phoneNumber: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const states = [
|
||||||
|
{name: "District 1", value: "D1"},
|
||||||
|
{name: "District 2", value: "D2"},
|
||||||
|
{name: "District 3", value: "D3"}
|
||||||
|
]
|
||||||
interface AccountInfomationProps {
|
interface AccountInfomationProps {
|
||||||
account: AccountProps
|
account: AccountProps
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
@@ -24,11 +28,10 @@ interface AccountInfomationProps {
|
|||||||
|
|
||||||
const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => {
|
const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => {
|
||||||
const { customer } = useActiveCustomer()
|
const { customer } = useActiveCustomer()
|
||||||
|
|
||||||
// need to handle call back when edit account information
|
// need to handle call back when edit account information
|
||||||
|
|
||||||
const showEditForm = () => onClick()
|
const showEditForm = () => onClick()
|
||||||
|
const state = states.find((val)=>val.value == account.address?.province);
|
||||||
return (
|
return (
|
||||||
<section className={s.accountInfomation}>
|
<section className={s.accountInfomation}>
|
||||||
<div className={s.avatar}>
|
<div className={s.avatar}>
|
||||||
@@ -45,8 +48,8 @@ const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => {
|
|||||||
<div className={s.shippingInfo}>Shipping Infomation</div>
|
<div className={s.shippingInfo}>Shipping Infomation</div>
|
||||||
|
|
||||||
<div className={s.accountAddress}>
|
<div className={s.accountAddress}>
|
||||||
{account.address +
|
{account.address?.streetLine1 +
|
||||||
`, ${account.state}, ${account.city}, ${account.postalCode}`}
|
`, ${state?.name}, ${account.address?.city}, ${account.address?.postalCode}`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={s.accountPhoneNumber}>{account.phoneNumber}</div>
|
<div className={s.accountPhoneNumber}>{account.phoneNumber}</div>
|
||||||
|
@@ -1,6 +1,15 @@
|
|||||||
@import '../../../../../../styles/utilities';
|
@import '../../../../../../styles/utilities';
|
||||||
|
|
||||||
.editInfoModal {
|
.editInfoModal {
|
||||||
|
.u-form{
|
||||||
|
width: 60rem;
|
||||||
|
}
|
||||||
|
.inputName{
|
||||||
|
@apply flex justify-between;
|
||||||
|
.input{
|
||||||
|
width: 48.5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
.input {
|
.input {
|
||||||
@apply bg-white;
|
@apply bg-white;
|
||||||
margin-bottom: 1.6rem;
|
margin-bottom: 1.6rem;
|
||||||
@@ -23,6 +32,7 @@
|
|||||||
.inputPostalCode {
|
.inputPostalCode {
|
||||||
@apply bg-white;
|
@apply bg-white;
|
||||||
margin-left: 0.8rem;
|
margin-left: 0.8rem;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputPhoneNumber {
|
.inputPhoneNumber {
|
||||||
|
@@ -1,19 +1,34 @@
|
|||||||
import React from "react"
|
import React, { useState } from "react"
|
||||||
import s from './EditInfoModal.module.scss'
|
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 {
|
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;
|
visible: boolean;
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => {
|
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 = [
|
const states = [
|
||||||
{name: "District 1", value: "D1"},
|
{name: "District 1", value: "D1"},
|
||||||
@@ -21,44 +36,165 @@ const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoMod
|
|||||||
{name: "District 3", value: "D3"}
|
{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 (
|
return (
|
||||||
<ModalCommon onClose={closeModal} visible={visible} title="Edit Infomation">
|
<ModalCommon onClose={closeModal} visible={visible} title="Edit Infomation">
|
||||||
<section className={s.editInfoModal}>
|
<section className={s.editInfoModal}>
|
||||||
<div className={s.input}>
|
<Formik
|
||||||
<Inputcommon placeholder="Name" value={accountInfo.name} type="text" />
|
initialValues={
|
||||||
</div>
|
{
|
||||||
|
firstName:accountInfo.firstName,
|
||||||
<div className={s.inputDisable}>
|
lastName: accountInfo.lastName,
|
||||||
<Inputcommon placeholder="Email" value={accountInfo.email} type="email" />
|
address:accountInfo.address?.streetLine1,
|
||||||
</div>
|
city: accountInfo.address?.city,
|
||||||
|
postalCode: accountInfo.address?.postalCode,
|
||||||
<div className={s.input}>
|
phoneNumber:accountInfo.phoneNumber
|
||||||
<Inputcommon placeholder="Address" value={accountInfo.address} type="text" />
|
}}
|
||||||
</div>
|
validationSchema={DisplayingErrorMessagesSchema}
|
||||||
|
onSubmit={onEditUserInfo}
|
||||||
<div className={s.input}>
|
>
|
||||||
<Inputcommon placeholder="City" value={accountInfo.city} type="text" />
|
{({ errors, touched, isValid, submitForm }) => (
|
||||||
</div>
|
<Form className="u-form">
|
||||||
|
<div className={s.inputName}>
|
||||||
|
<div className={s.input}>
|
||||||
<div className="flex">
|
<InputFiledInForm
|
||||||
<div className={s.inputState}>
|
name="firstName"
|
||||||
<SelectCommon type="custom" placeholder="State" options={states} />
|
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>
|
||||||
|
|
||||||
<div className={s.inputPostalCode}>
|
<div className={s.input}>
|
||||||
<Inputcommon placeholder="Postal code" value={accountInfo.postalCode} type="text" />
|
<InputFiledInForm
|
||||||
|
name="address"
|
||||||
|
placeholder="Address"
|
||||||
|
error={
|
||||||
|
touched.address && errors.address
|
||||||
|
? errors.address.toString()
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
isShowIconSuccess={touched.address && !errors.address}
|
||||||
|
/>
|
||||||
</div>
|
</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}>
|
<div className="flex">
|
||||||
<Inputcommon placeholder="Phone number" value={accountInfo.phoneNumber} type="text" />
|
<div className={s.inputState}>
|
||||||
</div>
|
<SelectCommon initValue={accountInfo.address?.province} selected={accountInfo.address?.province} type="custom" onChange={state} placeholder="State" options={states} size="large"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={s.buttons}>
|
<div className={s.inputPostalCode}>
|
||||||
<ButtonCommon onClick={closeModal} type="light" size="large" >Cancel</ButtonCommon>
|
<InputFiledInForm
|
||||||
<ButtonCommon onClick={saveInfo} size="large" >Save</ButtonCommon>
|
name="postalCode"
|
||||||
</div>
|
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>
|
</section>
|
||||||
</ModalCommon>
|
</ModalCommon>
|
||||||
)
|
)
|
||||||
|
@@ -1,18 +1,34 @@
|
|||||||
import React from "react"
|
import { useRouter } from 'next/router'
|
||||||
import s from './FavouriteProducts.module.scss'
|
import React, { useState } from "react"
|
||||||
import {ProductList} from '../../../../../common'
|
import { QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
|
||||||
|
import { ProductList } from '../../../../../common'
|
||||||
import { ProductCardProps } from '../../../../../common/ProductCard/ProductCard'
|
import { ProductCardProps } from '../../../../../common/ProductCard/ProductCard'
|
||||||
|
import s from './FavouriteProducts.module.scss'
|
||||||
|
|
||||||
interface FavouriteProductsProps {
|
interface FavouriteProductsProps {
|
||||||
products: ProductCardProps[];
|
products: ProductCardProps[],
|
||||||
|
totalItems:number
|
||||||
}
|
}
|
||||||
|
|
||||||
const FavouriteProducts = ({ products } : FavouriteProductsProps) => {
|
const FavouriteProducts = ({ products,totalItems } : FavouriteProductsProps) => {
|
||||||
|
const router = useRouter()
|
||||||
|
const [currentPage, setCurrentPage] = useState(0);
|
||||||
|
|
||||||
|
function onPageChange(page:number){
|
||||||
|
setCurrentPage(page)
|
||||||
|
router.push({
|
||||||
|
pathname: ROUTE.ACCOUNT,
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
[QUERY_KEY.PAGE]: page
|
||||||
|
}
|
||||||
|
},
|
||||||
|
undefined, { shallow: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={s.favouriteProducts}>
|
<section className={s.favouriteProducts}>
|
||||||
<ProductList data={products} />
|
<ProductList data={products} total={totalItems} onPageChange={onPageChange}/>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -4,50 +4,50 @@ import s from './OrderInformation.module.scss'
|
|||||||
import { TabCommon } from '../../../../../common'
|
import { TabCommon } from '../../../../../common'
|
||||||
import TabPane from 'src/components/common/TabCommon/components/TabPane/TabPane'
|
import TabPane from 'src/components/common/TabCommon/components/TabPane/TabPane'
|
||||||
import DeliveryItem from '../../../DeliveryItem/DeliveryItem'
|
import DeliveryItem from '../../../DeliveryItem/DeliveryItem'
|
||||||
|
import { Order } from "@framework/schema"
|
||||||
|
|
||||||
|
|
||||||
interface OrderInformationProps {
|
interface OrderInformationProps {
|
||||||
waiting: {id: string, products: string[], totalPrice: number}[],
|
addingItem?: Order[],
|
||||||
delivering: {id: string, products: string[], totalPrice: number}[],
|
arrangingPayment?: Order[],
|
||||||
delivered: {id: string, products: string[], totalPrice: number}[],
|
cancelled?: Order[],
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderInformation = ({ waiting, delivering, delivered} : OrderInformationProps) => {
|
const OrderInformation = ({ addingItem, arrangingPayment, cancelled} : OrderInformationProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={s.orderInformation}>
|
<section className={s.orderInformation}>
|
||||||
<div className={s.title}>Order Information</div>
|
<div className={s.title}>Order Information</div>
|
||||||
|
|
||||||
<div className={s.tabs}>
|
<div className={s.tabs}>
|
||||||
<TabCommon>
|
<TabCommon>
|
||||||
<TabPane tabName={"Wait for Comfirmation"} >
|
<TabPane tabName={"Adding Item"} >
|
||||||
<div className={s.blank}></div>
|
<div className={s.blank}></div>
|
||||||
{
|
{
|
||||||
waiting.map((order, i) => {
|
addingItem?.map((order, i) => {
|
||||||
return (
|
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>
|
||||||
|
|
||||||
<TabPane tabName={"Delivering"}>
|
<TabPane tabName={"Arranging Payment"}>
|
||||||
<div className={s.blank}></div>
|
<div className={s.blank}></div>
|
||||||
{
|
{
|
||||||
delivering.map((order, i) => {
|
arrangingPayment?.map((order, i) => {
|
||||||
return (
|
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>
|
||||||
|
|
||||||
<TabPane tabName={"Delivered"}>
|
<TabPane tabName={"Cancelled"}>
|
||||||
<div className={s.blank}></div>
|
<div className={s.blank}></div>
|
||||||
{
|
{
|
||||||
delivered.map((order, i) => {
|
cancelled?.map((order, i) => {
|
||||||
return (
|
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} />
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -5,12 +5,13 @@ import IdAndStatus from './components/IdAndStatus/IdAndStatus'
|
|||||||
import Products from './components/Products/Products'
|
import Products from './components/Products/Products'
|
||||||
import TotalPrice from './components/TotalPrice/TotalPrice'
|
import TotalPrice from './components/TotalPrice/TotalPrice'
|
||||||
import ReOrder from './components/ReOrder/ReOrder'
|
import ReOrder from './components/ReOrder/ReOrder'
|
||||||
|
import { OrderLine } from "@framework/schema"
|
||||||
|
|
||||||
|
|
||||||
interface DeliveryItemProps {
|
interface DeliveryItemProps {
|
||||||
id: string;
|
id: string;
|
||||||
status: "waiting" | "delivering" | "delivered";
|
status: "waiting" | "delivering" | "delivered";
|
||||||
products: string[];
|
products?: OrderLine[];
|
||||||
totalPrice: number;
|
totalPrice: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
|
import { OrderLine } from "@framework/schema";
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import s from './Products.module.scss'
|
import s from './Products.module.scss'
|
||||||
|
|
||||||
interface ProductsProps {
|
interface ProductsProps {
|
||||||
products: string[];
|
products?: OrderLine[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Products = ({ products } : ProductsProps) => {
|
const Products = ({ products } : ProductsProps) => {
|
||||||
|
function toString(products?:OrderLine[]): string {
|
||||||
function toString(products:string[]): string {
|
|
||||||
let strProducts = "";
|
let strProducts = "";
|
||||||
products.map((prod, i) => {
|
products?.map((prod, i) => {
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
strProducts += prod;
|
strProducts += prod.productVariant?.name;
|
||||||
} else {
|
} else {
|
||||||
strProducts += `, ${prod}`
|
strProducts += `, ${prod.productVariant?.name}`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return strProducts;
|
return strProducts;
|
||||||
|
@@ -10,6 +10,7 @@ interface FreshProductsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FreshProducts = ({ data, collections }: FreshProductsProps) => {
|
const FreshProducts = ({ data, collections }: FreshProductsProps) => {
|
||||||
|
|
||||||
const dataWithCategory = useMemo(() => {
|
const dataWithCategory = useMemo(() => {
|
||||||
return data.map(item => {
|
return data.map(item => {
|
||||||
return {
|
return {
|
||||||
|
@@ -2,16 +2,16 @@ import React from 'react'
|
|||||||
import { ProductCarousel } from 'src/components/common'
|
import { ProductCarousel } from 'src/components/common'
|
||||||
import { SPICE_DATA_TEST } from "../../../../utils/demo-data"
|
import { SPICE_DATA_TEST } from "../../../../utils/demo-data"
|
||||||
import s from './HomeSpice.module.scss'
|
import s from './HomeSpice.module.scss'
|
||||||
|
import { ProductCard } from '@commerce/types/product'
|
||||||
interface HomeSpice {
|
interface HomeSpice {
|
||||||
|
data: ProductCard[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const HomeSpice = ({}: HomeSpice) => {
|
const HomeSpice = ({data}: HomeSpice) => {
|
||||||
return (
|
return (
|
||||||
<div className={s.homeSpiceWarpper}>
|
<div className={s.homeSpiceWarpper}>
|
||||||
<ProductCarousel data={SPICE_DATA_TEST} itemKey="product-7"/>
|
<ProductCarousel data={data} itemKey="product-7"/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -6,16 +6,10 @@ import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils'
|
|||||||
import { normalizeProductCard } from '@framework/utils/normalize';
|
import { normalizeProductCard } from '@framework/utils/normalize';
|
||||||
import { useLocalStorage } from 'src/components/hooks/useLocalStorage';
|
import { useLocalStorage } from 'src/components/hooks/useLocalStorage';
|
||||||
interface Props {
|
interface Props {
|
||||||
|
data: ProductCardProps[]
|
||||||
}
|
}
|
||||||
const ViewedProducts = ({}:Props) => {
|
const ViewedProducts = ({data}:Props) => {
|
||||||
const [data, setData] = useState<ProductCardProps[]>([])
|
if (data.length===0){
|
||||||
const [viewedProduct] = useLocalStorage<Product[]>(LOCAL_STORAGE_KEY.VIEWEDPRODUCT, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setData(viewedProduct.map((p)=>normalizeProductCard(p)))
|
|
||||||
}, [viewedProduct])
|
|
||||||
|
|
||||||
if (data.length>0){
|
|
||||||
return <div></div>
|
return <div></div>
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@@ -197,3 +197,4 @@ export const STATE_OPTIONS = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const COLLECTION_SLUG_SPICE ="spice";
|
||||||
|
Reference in New Issue
Block a user