mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 12:24:18 +00:00
feat: get blog list
This commit is contained in:
@@ -17,6 +17,7 @@ export type LineItem = {
|
||||
quantity: number
|
||||
discounts: Discount[]
|
||||
// A human-friendly unique string automatically generated from the product’s name
|
||||
slug: string
|
||||
path: string
|
||||
variant: ProductVariant
|
||||
options?: SelectedOption[]
|
||||
|
@@ -30,6 +30,7 @@ export type ProductOptionValues = {
|
||||
|
||||
export type ProductVariant = {
|
||||
id: string | number
|
||||
name:string,
|
||||
options: ProductOption[]
|
||||
availableForSale?: boolean
|
||||
}
|
||||
@@ -48,7 +49,8 @@ export type Product = {
|
||||
options: ProductOption[]
|
||||
facetValueIds?: string[]
|
||||
collectionIds?: string[]
|
||||
collection?: string,
|
||||
collection?: string[],
|
||||
variants?: ProductVariant[]
|
||||
}
|
||||
|
||||
export type ProductCard = {
|
||||
@@ -65,6 +67,8 @@ export type ProductCard = {
|
||||
collectionIds?: string[],
|
||||
collection?: string,
|
||||
isNotSell?: boolean
|
||||
productVariantId?:string
|
||||
productVariantName?:string
|
||||
}
|
||||
|
||||
export type SearchProductsBody = {
|
||||
|
@@ -16,7 +16,7 @@ export default function getAllBlogsOperation({
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
}): Promise<{ blogs: GetAllBlogsQuery,totalItems:number }>
|
||||
}): Promise<{ blogs: GetAllBlogsQuery[],totalItems:number }>
|
||||
|
||||
async function getAllBlogs({
|
||||
query = getAllBlogsQuery,
|
||||
@@ -27,8 +27,8 @@ export default function getAllBlogsOperation({
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<{ blogs: GetAllBlogsQuery | any[] ,totalItems?:number }> {
|
||||
console.log(vars.excludeBlogIds)
|
||||
} = {}): Promise<{ blogs: GetAllBlogsQuery[] | any[] ,totalItems?:number }> {
|
||||
|
||||
const config = commerce.getConfig(cfg)
|
||||
const variables = {
|
||||
excludeBlogIds: vars.excludeBlogIds,
|
||||
|
@@ -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, groupByProduct?:boolean }
|
||||
|
||||
export default function getAllProductsOperation({
|
||||
commerce,
|
||||
@@ -31,13 +31,13 @@ export default function getAllProductsOperation({
|
||||
input: {
|
||||
take: vars.first,
|
||||
facetValueIds: vars.facetValueIds,
|
||||
groupByProduct: true,
|
||||
collectionSlug : vars.collectionSlug,
|
||||
groupByProduct: vars.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,
|
||||
|
@@ -15,7 +15,7 @@ export default function getFeaturedBlogOperation({
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
}): Promise<{ featuredBlogs: GetFeaturedBlogQuery,totalItems:number }>
|
||||
}): Promise<{ featuredBlogs: GetFeaturedBlogQuery[],totalItems:number }>
|
||||
|
||||
async function getFeaturedBlog({
|
||||
query = getFeatuedBlogQuery,
|
||||
@@ -26,7 +26,7 @@ export default function getFeaturedBlogOperation({
|
||||
variables?: BlogVariables
|
||||
config?: Partial<VendureConfig>
|
||||
preview?: boolean
|
||||
} = {}): Promise<{ featuredBlogs: GetFeaturedBlogQuery | any[] ,totalItems?:number }> {
|
||||
} = {}): Promise<{ featuredBlogs: GetFeaturedBlogQuery[] | any[] ,totalItems?:number }> {
|
||||
const config = commerce.getConfig(cfg)
|
||||
const variables = {
|
||||
options: {
|
||||
|
@@ -36,6 +36,7 @@ export default function getProductOperation({
|
||||
})),
|
||||
variants: product.variants.map((v) => ({
|
||||
id: v.id,
|
||||
name:v.name,
|
||||
options: v.options.map((o) => ({
|
||||
// This __typename property is required in order for the correct
|
||||
// variant selection to work, see `components/product/helpers.ts`
|
||||
@@ -54,7 +55,8 @@ export default function getProductOperation({
|
||||
values: og.options.map((o) => ({ label: o.name })),
|
||||
})),
|
||||
facetValueIds: product.facetValues.map(item=> item.id),
|
||||
collectionIds: product.collections.map(item => item.id)
|
||||
collectionIds: product.collections.map(item => item.id),
|
||||
collection:product.collections.map(item => item.name),
|
||||
} as Product
|
||||
}
|
||||
|
||||
|
93
framework/vendure/schema.d.ts
vendored
93
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';
|
||||
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
|
||||
@@ -2344,6 +2360,7 @@ export type GetAllBlogsQuery = PaginatedList & {
|
||||
}
|
||||
}
|
||||
export type GetFeaturedBlogQuery = PaginatedList & {
|
||||
id:string,
|
||||
featuredBlogs: { __typename?: 'BlogList' } & {
|
||||
items: Array<{ __typename?: 'Blog' } & BlogList!>,
|
||||
'totalItems'
|
||||
@@ -3087,7 +3104,7 @@ export type SearchResultFragment = { __typename?: 'SearchResult' } & Pick<
|
||||
SearchResult,
|
||||
'productId' | 'sku' | 'productName' | 'description' | 'slug' | 'sku' | 'currencyCode'
|
||||
| 'productAsset' | 'price' | 'priceWithTax' | 'currencyCode'
|
||||
| 'collectionIds' | 'facetValueIds' | 'collectionIds'
|
||||
| 'collectionIds' | 'productVariantId' | 'facetValueIds' | "productVariantName"
|
||||
> & {
|
||||
productAsset?: Maybe<
|
||||
{ __typename?: 'SearchResultAsset' } & Pick<
|
||||
@@ -3174,6 +3191,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']>
|
||||
@@ -3227,8 +3274,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,
|
||||
@@ -3240,17 +3288,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']>
|
||||
}>
|
||||
@@ -3351,7 +3430,7 @@ export type GetProductQuery = { __typename?: 'Query' } & {
|
||||
variants: Array<
|
||||
{ __typename?: 'ProductVariant' } & Pick<
|
||||
ProductVariant,
|
||||
'id' | 'priceWithTax' | 'currencyCode' | 'price'
|
||||
'id' | 'priceWithTax' | 'currencyCode' | 'price' | "name"
|
||||
> & {
|
||||
options: Array<
|
||||
{ __typename?: 'ProductOption' } & Pick<
|
||||
@@ -3395,7 +3474,7 @@ export type GetProductQuery = { __typename?: 'Query' } & {
|
||||
collections: Array<
|
||||
{ __typename?: 'Collection' } & Pick<
|
||||
Collection,
|
||||
'id'
|
||||
'id'|"name"
|
||||
>
|
||||
>
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ export const searchResultFragment = /* GraphQL */ `
|
||||
slug
|
||||
sku
|
||||
currencyCode
|
||||
productVariantId
|
||||
productVariantName
|
||||
productAsset {
|
||||
id
|
||||
preview
|
||||
|
@@ -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 { ProductCard } from '@commerce/types/product'
|
||||
import { CartFragment, SearchResultFragment } from '../schema'
|
||||
import { ProductCard, Product } from '@commerce/types/product'
|
||||
import { CartFragment, SearchResultFragment,Favorite } from '../schema'
|
||||
|
||||
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
||||
return {
|
||||
@@ -10,6 +10,8 @@ export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
||||
imageSrc: item.productAsset?.preview ? item.productAsset?.preview + '?w=800&mode=crop' : '',
|
||||
price: (item.priceWithTax as any).min / 100,
|
||||
currencyCode: item.currencyCode,
|
||||
productVariantId: item.productVariantId,
|
||||
productVariantName:item.productVariantName,
|
||||
facetValueIds: item.facetValueIds,
|
||||
collectionIds: item.collectionIds,
|
||||
|
||||
@@ -21,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 {
|
||||
return {
|
||||
id: order.id.toString(),
|
||||
@@ -35,7 +49,7 @@ export function normalizeCart(order: CartFragment): Cart {
|
||||
id: l.id,
|
||||
name: l.productVariant.name,
|
||||
quantity: l.quantity,
|
||||
url: l.productVariant.product.slug,
|
||||
slug: l.productVariant.product.slug,
|
||||
variantId: l.productVariant.id,
|
||||
productId: l.productVariant.productId,
|
||||
images: [{ url: l.featuredAsset?.preview + '?preset=thumb' || '' }],
|
||||
@@ -55,3 +69,18 @@ export function normalizeCart(order: CartFragment): Cart {
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeProductCard(product: Product): ProductCard {
|
||||
return {
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
slug: product.slug,
|
||||
imageSrc: product.images[0].url,
|
||||
price: product.price,
|
||||
currencyCode: product.currencyCode,
|
||||
productVariantId: product.variants?.[0].id.toString(),
|
||||
productVariantName:product.variants?.[0].name,
|
||||
facetValueIds: product.facetValueIds,
|
||||
collectionIds: product.collectionIds,
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
@@ -12,6 +12,7 @@ export const getProductQuery = /* GraphQL */ `
|
||||
}
|
||||
variants {
|
||||
id
|
||||
name
|
||||
priceWithTax
|
||||
currencyCode
|
||||
options {
|
||||
@@ -41,6 +42,7 @@ export const getProductQuery = /* GraphQL */ `
|
||||
}
|
||||
collections {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
@@ -3,34 +3,32 @@ import { GetStaticPropsContext } from 'next';
|
||||
import { Layout } from 'src/components/common';
|
||||
import { BlogCardProps } from 'src/components/common/CardBlog/CardBlog';
|
||||
import { BlogBreadCrumb, BlogHeading, BlogsList, FeaturedCardBlog } from 'src/components/modules/blogs';
|
||||
import { DEFAULT_BLOG_PAGE_SIZE,DEFAULT_FEATURED_BLOG_PAGE_SIZE } from "src/utils/constanst.utils";
|
||||
import { DEFAULT_BLOG_PAGE_SIZE } from "src/utils/constanst.utils";
|
||||
import { getAllPromies } from 'src/utils/funtion.utils';
|
||||
import { PromiseWithKey } from 'src/utils/types.utils';
|
||||
import { getIdFeaturedBlog } from 'src/utils/funtion.utils';
|
||||
|
||||
|
||||
interface Props {
|
||||
blogsResult: { blogs?: BlogCardProps[],featuredBlogs?: BlogCardProps[] },
|
||||
blogsResult: { blogs?: BlogCardProps[],featuredBlog?: BlogCardProps[] },
|
||||
totalItems: number
|
||||
}
|
||||
export default function BlogsPage({blogsResult,totalItems}:Props) {
|
||||
|
||||
// let date = new Date(blogsResult?.featuredBlogs[0]?.createdAt ?? '' );
|
||||
// let fullDate = date.toLocaleString('en-us', { month: 'long' }) + " " + date.getDate()+","+date.getFullYear();
|
||||
|
||||
let date = new Date(blogsResult.featuredBlog?.[0]?.createdAt ?? '' );
|
||||
let fullDate = date.toLocaleString('en-us', { month: 'long' }) + " " + date.getDate()+","+date.getFullYear();
|
||||
|
||||
return(
|
||||
<>
|
||||
<BlogBreadCrumb />
|
||||
<BlogHeading />
|
||||
{/* <FeaturedCardBlog
|
||||
title={blogsResult?.featuredBlogs[0]?.title}
|
||||
imgSrc={blogsResult?.featuredBlogs[0]?.imageSrc ?? ''}
|
||||
content={blogsResult?.featuredBlogs[0]?.description}
|
||||
imgAuthor={blogsResult?.featuredBlogs[0]?.authorAvatarAsset}
|
||||
authorName={blogsResult?.featuredBlogs[0]?.authorName}
|
||||
<FeaturedCardBlog
|
||||
title={blogsResult.featuredBlog?.[0]?.title}
|
||||
slug={blogsResult.featuredBlog?.[0]?.slug}
|
||||
imgSrc={blogsResult.featuredBlog?.[0]?.imageSrc ?? ''}
|
||||
content={blogsResult.featuredBlog?.[0]?.description}
|
||||
imgAuthor={blogsResult.featuredBlog?.[0]?.authorAvatarAsset}
|
||||
authorName={blogsResult.featuredBlog?.[0]?.authorName}
|
||||
date={fullDate}
|
||||
/> */}
|
||||
<BlogsList blogList={blogsResult.blogs} total={totalItems} />
|
||||
/>
|
||||
<BlogsList blogList={blogsResult.blogs} total={totalItems} idFeatured={blogsResult.featuredBlog?.[0]?.id} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -53,17 +51,12 @@ export async function getStaticProps({
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
|
||||
// console.log(featuredBlogs[0].id);
|
||||
// promisesWithKey.push({ key: 'blogsResult', promise: FeaturedBlogPromise })
|
||||
|
||||
|
||||
// Blogs
|
||||
const idBlog = featuredBlogs[0].id;
|
||||
const blogsPromise = commerce.getAllBlogs({
|
||||
const idFeaturedBlog = featuredBlogs[0].id;
|
||||
const blogsPromise = commerce.getAllBlogs({
|
||||
variables: {
|
||||
excludeBlogIds: [idBlog],
|
||||
excludeBlogIds: [idFeaturedBlog],
|
||||
take: DEFAULT_BLOG_PAGE_SIZE
|
||||
},
|
||||
config,
|
||||
@@ -71,7 +64,6 @@ export async function getStaticProps({
|
||||
})
|
||||
promisesWithKey.push({ key: 'blogsResult', promise: blogsPromise })
|
||||
|
||||
|
||||
|
||||
try {
|
||||
const promises = getAllPromies(promisesWithKey)
|
||||
@@ -81,8 +73,9 @@ export async function getStaticProps({
|
||||
props[item.key] = item.keyResult ? rs[index][item.keyResult] : rs[index]
|
||||
return null
|
||||
})
|
||||
props['blogsResult']['featuredBlog'] = featuredBlogs;
|
||||
|
||||
props['blogsResult']['featuredBlog'] = featuredBlogs;
|
||||
|
||||
return {
|
||||
props,
|
||||
revalidate: 60
|
||||
|
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
|
@@ -6,7 +6,9 @@ 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 { FACET } from 'src/utils/constanst.utils';
|
||||
import { FilterOneVatiant, getFacetIdByName } from 'src/utils/funtion.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';
|
||||
|
||||
@@ -16,19 +18,23 @@ interface Props {
|
||||
freshProducts: ProductCard[],
|
||||
featuredProducts: ProductCard[],
|
||||
collections: Collection[]
|
||||
spiceProducts:ProductCard[]
|
||||
veggie: ProductCard[],
|
||||
|
||||
}
|
||||
export default function Home({ featuredAndDiscountFacetsValue,
|
||||
export default function Home({ featuredAndDiscountFacetsValue, veggie,
|
||||
freshProducts, featuredProducts,
|
||||
collections }: Props) {
|
||||
collections,spiceProducts }: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<HomeBanner />
|
||||
<HomeFeature />
|
||||
<HomeCategories />
|
||||
<HomeCollection data = {veggie}/>
|
||||
<FreshProducts data={freshProducts} collections={collections} />
|
||||
<HomeCollection />
|
||||
<HomeVideo />
|
||||
<HomeSpice />
|
||||
{spiceProducts.length>0 && <HomeSpice data={spiceProducts}/>}
|
||||
<FeaturedProductsCarousel data={featuredProducts} featuredFacetsValue={featuredAndDiscountFacetsValue} />
|
||||
<HomeCTA />
|
||||
<HomeRecipe />
|
||||
@@ -55,6 +61,8 @@ export async function getStaticProps({
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
|
||||
props.featuredAndDiscountFacetsValue = getAllFacetValuesForFeatuedProducts(facets)
|
||||
|
||||
// fresh products
|
||||
@@ -72,7 +80,20 @@ export async function getStaticProps({
|
||||
props.freshProducts = []
|
||||
}
|
||||
|
||||
|
||||
//veggie
|
||||
const veggieProductvariables: ProductVariables = {
|
||||
groupByProduct:false
|
||||
}
|
||||
const veggieId = getFacetIdByName(facets,FACET.CATEGORY.PARENT_NAME,FACET.CATEGORY.VEGGIE)
|
||||
if (veggieId) {
|
||||
veggieProductvariables.facetValueIds = [veggieId]
|
||||
}
|
||||
const veggieProductsPromise = commerce.getAllProducts({
|
||||
variables: veggieProductvariables,
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
promisesWithKey.push({ key: 'veggie', promise: veggieProductsPromise, keyResult: 'products' })
|
||||
// featured products
|
||||
const allFeaturedFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_FEATURED)
|
||||
const allDiscountFacetIds = getAllFacetValueIdsByParentCode(facets, CODE_FACET_DISCOUNT)
|
||||
@@ -99,15 +120,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]
|
||||
props[item.key] = item.keyResult ? FilterOneVatiant(rs[index][item.keyResult]) : rs[index]
|
||||
return null
|
||||
})
|
||||
|
||||
return {
|
||||
props,
|
||||
revalidate: 60,
|
||||
|
@@ -1,22 +1,43 @@
|
||||
|
||||
import { Product } from '@framework/schema'
|
||||
import { Collection } from '@commerce/types/collection'
|
||||
import { Product, ProductCard } from '@commerce/types/product'
|
||||
import commerce from '@lib/api/commerce'
|
||||
import { GetStaticPathsContext, GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Layout, RecipeDetail, RecommendedRecipes, RelevantBlogPosts } from 'src/components/common'
|
||||
import { useLocalStorage } from 'src/components/hooks/useLocalStorage'
|
||||
import { ProductInfoDetail, ReleventProducts, ViewedProducts } from 'src/components/modules/product-detail'
|
||||
import { 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 { getAllPromies } from 'src/utils/funtion.utils'
|
||||
import { normalizeProductCard } from '@framework/utils/normalize';
|
||||
import { PromiseWithKey } from 'src/utils/types.utils'
|
||||
|
||||
export default function Slug({ product, relevantProducts, collections }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
|
||||
interface Props {
|
||||
relevantProducts: ProductCard[],
|
||||
product: Product,
|
||||
collections: Collection[]
|
||||
}
|
||||
export default function Slug({ product, relevantProducts, collections }: Props) {
|
||||
const [local,setLocal] = useLocalStorage<Product[]>(LOCAL_STORAGE_KEY.VIEWEDPRODUCT, []);
|
||||
const [viewed, setViewed] = useState<ProductCard[]>([])
|
||||
useEffect(() => {
|
||||
if(local){
|
||||
if(!local.find(p => p.id === product.id)){
|
||||
setLocal([...local, product])
|
||||
}else{
|
||||
setViewed(local.filter((p)=>p.id !== product.id).map((p)=>normalizeProductCard(p)))
|
||||
}
|
||||
}else{
|
||||
setLocal([product])
|
||||
}
|
||||
}, [product])
|
||||
|
||||
return <>
|
||||
<ProductInfoDetail productDetail={product} collections={collections}/>
|
||||
<ProductInfoDetail productDetail={product}/>
|
||||
<RecipeDetail ingredients={INGREDIENT_DATA_TEST} />
|
||||
<RecommendedRecipes data={RECIPE_DATA_TEST} />
|
||||
<ReleventProducts data={relevantProducts} collections={collections}/>
|
||||
<ViewedProducts />
|
||||
<ViewedProducts data={viewed}/>
|
||||
<RelevantBlogPosts data={BLOGS_DATA_TEST} title="relevent blog posts" />
|
||||
</>
|
||||
}
|
||||
@@ -68,7 +89,6 @@ export async function getStaticProps({
|
||||
})
|
||||
promisesWithKey.push({ key: 'collections', promise: collectionsPromise, keyResult: 'collections' })
|
||||
|
||||
|
||||
try {
|
||||
const promises = getAllPromies(promisesWithKey)
|
||||
const rs = await Promise.all(promises)
|
||||
|
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
|
@@ -1,5 +1,7 @@
|
||||
import { normalizeCart } from '@framework/utils/normalize';
|
||||
import React from 'react';
|
||||
import { useCartDrawer } from 'src/components/contexts';
|
||||
import useGetActiveOrder from 'src/components/hooks/cart/useGetActiveOrder';
|
||||
import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data';
|
||||
import { DrawerCommon } from '..';
|
||||
import s from './CartDrawer.module.scss';
|
||||
@@ -14,14 +16,15 @@ interface Props {
|
||||
|
||||
const CartDrawer = ({ }: Props) => {
|
||||
const { cartVisible, closeCartDrawer } = useCartDrawer()
|
||||
const {order} = useGetActiveOrder()
|
||||
return (
|
||||
<DrawerCommon
|
||||
title={`Your cart (${PRODUCT_CART_DATA_TEST.length})`}
|
||||
title={`Your cart (${order?.lineItems.length})`}
|
||||
visible={cartVisible}
|
||||
onClose={closeCartDrawer}>
|
||||
<div className={s.cartDrawer}>
|
||||
<div className={s.body}>
|
||||
<ProductsInCart data={PRODUCT_CART_DATA_TEST} />
|
||||
<ProductsInCart data={order?.lineItems||[]} currency={order?.currency||{code:"USA"}}/>
|
||||
<CartRecommendation />
|
||||
</div>
|
||||
<div>
|
||||
|
@@ -1,25 +1,64 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { QuanittyInput } from 'src/components/common';
|
||||
import { IconDelete } from 'src/components/icons';
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
import { ProductProps } from 'src/utils/types.utils';
|
||||
import ImgWithLink from '../../../ImgWithLink/ImgWithLink';
|
||||
import LabelCommon from '../../../LabelCommon/LabelCommon';
|
||||
import s from './ProductCartItem.module.scss';
|
||||
import { ModalConfirm, QuanittyInput } from 'src/components/common'
|
||||
import { IconDelete } from 'src/components/icons'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import ImgWithLink from '../../../ImgWithLink/ImgWithLink'
|
||||
import LabelCommon from '../../../LabelCommon/LabelCommon'
|
||||
import s from './ProductCartItem.module.scss'
|
||||
import { LineItem } from '@commerce/types/cart'
|
||||
import { useUpdateProductInCart } from 'src/components/hooks/cart'
|
||||
import { debounce } from 'lodash'
|
||||
import useRemoveProductInCart from 'src/components/hooks/cart/useRemoveProductInCart'
|
||||
|
||||
export interface ProductCartItempProps extends ProductProps {
|
||||
quantity: number,
|
||||
export interface ProductCartItempProps extends LineItem {
|
||||
currency: { code: string }
|
||||
}
|
||||
|
||||
const ProductCartItem = ({ name, slug, weight, price, oldPrice, discount, imageSrc, quantity }: ProductCartItempProps) => {
|
||||
const ProductCartItem = ({
|
||||
slug,
|
||||
discounts,
|
||||
quantity,
|
||||
variant,
|
||||
name,
|
||||
currency,
|
||||
id
|
||||
}: ProductCartItempProps) => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const {updateProduct} = useUpdateProductInCart()
|
||||
const {removeProduct, loading} = useRemoveProductInCart()
|
||||
const handleQuantityChangeCallback = (isSuccess:boolean,mess?:string) => {
|
||||
if(!isSuccess){
|
||||
console.log(mess)
|
||||
}
|
||||
}
|
||||
const handleRemoveCallback = (isSuccess:boolean,mess?:string) => {
|
||||
if(!isSuccess){
|
||||
console.log(mess)
|
||||
}else{
|
||||
setVisible(false)
|
||||
}
|
||||
}
|
||||
const handleQuantityChange = (value:number) => {
|
||||
updateProduct({orderLineId:id,quantity:value},handleQuantityChangeCallback)
|
||||
}
|
||||
const debounceFn = useCallback(debounce(handleQuantityChange, 500), []);
|
||||
const handleCancel = () => {
|
||||
setVisible(false)
|
||||
}
|
||||
const handleOpen = () => {
|
||||
setVisible(true)
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
removeProduct({orderLineId:id},handleRemoveCallback)
|
||||
}
|
||||
return (
|
||||
<div className={s.productCartItem}>
|
||||
<div className={s.info}>
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a href="">
|
||||
<div className={s.imgWrap}>
|
||||
<ImgWithLink src={imageSrc} alt={name} />
|
||||
<ImgWithLink src={variant?.image?.url ?? ''} alt={name} />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
@@ -27,30 +66,32 @@ const ProductCartItem = ({ name, slug, weight, price, oldPrice, discount, imageS
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.name}>
|
||||
{name} {weight ? `(${weight})` : ''}
|
||||
{name} {variant?.weight ? `(${variant.weight})` : ''}
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.price}>
|
||||
{
|
||||
oldPrice &&
|
||||
<div className={s.old}>
|
||||
<span className={s.number}>{oldPrice}</span>
|
||||
<LabelCommon type='discount'>{discount}</LabelCommon>
|
||||
</div>
|
||||
}
|
||||
<div className={s.current}>{price}</div>
|
||||
{discounts.length > 0 && (
|
||||
<div className={s.old}>
|
||||
{/* <span className={s.number}>{oldPrice}</span> */}
|
||||
<LabelCommon type="discount">{discounts[0]}</LabelCommon>
|
||||
</div>
|
||||
)}
|
||||
<div className={s.current}>{variant?.price} {currency?.code}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.actions}>
|
||||
<div className={s.iconDelete}>
|
||||
<div className={s.iconDelete} onClick={handleOpen}>
|
||||
<IconDelete />
|
||||
</div>
|
||||
<QuanittyInput size='small' initValue={quantity} />
|
||||
<QuanittyInput size="small" initValue={quantity} onChange={debounceFn}/>
|
||||
</div>
|
||||
<ModalConfirm visible={visible} onClose={handleCancel} onCancel={handleCancel} onOk={handleConfirm} loading={loading}>
|
||||
Are you sure want to remove {name} form your cart
|
||||
</ModalConfirm>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCartItem;
|
||||
export default ProductCartItem
|
||||
|
@@ -1,25 +1,21 @@
|
||||
import { LineItem } from '@commerce/types/cart';
|
||||
import React from 'react';
|
||||
import ProductCartItem, { ProductCartItempProps } from '../ProductCartItem/ProductCartItem';
|
||||
import s from './ProductsInCart.module.scss';
|
||||
|
||||
interface Props {
|
||||
data: ProductCartItempProps[]
|
||||
data: LineItem[]
|
||||
currency: { code: string }
|
||||
}
|
||||
|
||||
const ProductsInCart = ({ data }: Props) => {
|
||||
const ProductsInCart = ({ data, currency }: Props) => {
|
||||
return (
|
||||
<ul className={s.productsInCart}>
|
||||
{
|
||||
data.map(item => <li key={item.name}>
|
||||
<ProductCartItem
|
||||
name={item.name}
|
||||
slug={item.slug}
|
||||
weight={item.weight}
|
||||
price={item.price}
|
||||
oldPrice={item.oldPrice}
|
||||
discount={item.discount}
|
||||
imageSrc={item.imageSrc}
|
||||
quantity={item.quantity}
|
||||
currency = {currency}
|
||||
{...item}
|
||||
/>
|
||||
</li>)
|
||||
}
|
||||
|
@@ -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,
|
||||
name: 'Create account',
|
||||
},
|
||||
{
|
||||
link: '/forgot-password',
|
||||
name: 'Forgot Password',
|
||||
},
|
||||
],
|
||||
[openModalLogin, openModalRegister]
|
||||
)
|
||||
|
@@ -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>
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import classNames from 'classnames';
|
||||
import { route } from 'next/dist/server/router';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import s from './MenuFilterItem.module.scss';
|
||||
|
@@ -5,6 +5,7 @@ import s from './ModalConfirm.module.scss'
|
||||
interface ModalConfirmProps extends ModalCommonProps {
|
||||
okText?: String
|
||||
cancelText?: String
|
||||
loading?:boolean
|
||||
onOk?: () => void
|
||||
onCancel?: () => void
|
||||
}
|
||||
@@ -16,6 +17,7 @@ const ModalConfirm = ({
|
||||
onCancel,
|
||||
children,
|
||||
title = 'Confirm',
|
||||
loading,
|
||||
...props
|
||||
}: ModalConfirmProps) => {
|
||||
return (
|
||||
@@ -25,7 +27,7 @@ const ModalConfirm = ({
|
||||
<div className="mr-4">
|
||||
<ButtonCommon onClick={onCancel} type="light"> {cancelText}</ButtonCommon>
|
||||
</div>
|
||||
<ButtonCommon onClick={onOk}>{okText}</ButtonCommon>
|
||||
<ButtonCommon onClick={onOk} loading={loading}>{okText}</ButtonCommon>
|
||||
</div>
|
||||
</ModalCommon>
|
||||
)
|
||||
|
@@ -10,13 +10,17 @@ import ItemWishList from '../ItemWishList/ItemWishList'
|
||||
import LabelCommon from '../LabelCommon/LabelCommon'
|
||||
import s from './ProductCard.module.scss'
|
||||
import ProductNotSell from './ProductNotSell/ProductNotSell'
|
||||
|
||||
import {useAddProductToCart} from "../../hooks/cart"
|
||||
import { useCartDrawer } from 'src/components/contexts'
|
||||
import Router from 'next/router'
|
||||
export interface ProductCardProps extends ProductCard {
|
||||
buttonText?: string
|
||||
isSingleButton?: boolean,
|
||||
activeWishlist?:boolean
|
||||
}
|
||||
|
||||
const ProductCardComponent = ({
|
||||
id,
|
||||
collection,
|
||||
name,
|
||||
slug,
|
||||
@@ -27,13 +31,42 @@ const ProductCardComponent = ({
|
||||
imageSrc,
|
||||
isNotSell,
|
||||
isSingleButton,
|
||||
productVariantId,
|
||||
productVariantName,
|
||||
activeWishlist
|
||||
}: ProductCardProps) => {
|
||||
|
||||
const {addProduct,loading} = useAddProductToCart()
|
||||
const { openCartDrawer } = useCartDrawer()
|
||||
|
||||
const handleAddToCart = () => {
|
||||
if(productVariantId){
|
||||
addProduct({variantId:productVariantId,quantity:1},handleAddToCartCallback)
|
||||
}
|
||||
}
|
||||
const handleAddToCartCallback = () => {
|
||||
openCartDrawer && openCartDrawer()
|
||||
}
|
||||
|
||||
const handleBuyNowCallback = (success:boolean) => {
|
||||
if(success){
|
||||
Router.push(ROUTE.CHECKOUT)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBuyNow = () => {
|
||||
if(productVariantId){
|
||||
addProduct({variantId:productVariantId,quantity:1},handleBuyNowCallback)
|
||||
}
|
||||
}
|
||||
|
||||
if (isNotSell) {
|
||||
return <div className={`${s.productCardWarpper} ${s.notSell}`}>
|
||||
<ProductNotSell name={name} imageSrc={imageSrc} />
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className={s.productCardWarpper}>
|
||||
<div className={s.cardTop}>
|
||||
@@ -55,7 +88,7 @@ const ProductCardComponent = ({
|
||||
<div className={s.cardMidTop}>
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.productname}>{name} </div>
|
||||
<div className={s.productname}>{productVariantName} </div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.productWeight}>{weight}</div>
|
||||
@@ -63,7 +96,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>
|
||||
@@ -71,15 +104,15 @@ const ProductCardComponent = ({
|
||||
{
|
||||
isSingleButton ?
|
||||
<div className={s.cardButton}>
|
||||
<ButtonCommon type="light" icon={<IconBuy />} size='small'>Add to cart</ButtonCommon>
|
||||
<ButtonCommon type="light" icon={<IconBuy />} size='small' onClick={handleAddToCart}>Add to cart</ButtonCommon>
|
||||
</div>
|
||||
:
|
||||
<>
|
||||
<div className={s.cardIcon}>
|
||||
<ButtonIconBuy/>
|
||||
<div className={s.cardIcon} >
|
||||
<ButtonIconBuy onClick={handleAddToCart} loading={loading}/>
|
||||
</div>
|
||||
<div className={s.cardButton}>
|
||||
<ButtonCommon type="light" size='small'>{buttonText}</ButtonCommon>
|
||||
<ButtonCommon type="light" size='small' onClick={handleBuyNow}>{buttonText}</ButtonCommon>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
@@ -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>
|
||||
)
|
||||
|
@@ -26,10 +26,6 @@ const QuanittyInput = ({
|
||||
}: QuanittyInputProps) => {
|
||||
const [value, setValue] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
onChange && onChange(value)
|
||||
}, [value])
|
||||
|
||||
useEffect(() => {
|
||||
initValue && setValue(initValue)
|
||||
}, [initValue])
|
||||
@@ -37,16 +33,20 @@ const QuanittyInput = ({
|
||||
const onPlusClick = () => {
|
||||
if (max && value + step > max) {
|
||||
setValue(max)
|
||||
onChange && onChange(max)
|
||||
} else {
|
||||
setValue(value + step)
|
||||
onChange && onChange(value + step)
|
||||
}
|
||||
}
|
||||
|
||||
const onMinusClick = () => {
|
||||
if (min && value - step < min) {
|
||||
setValue(min)
|
||||
onChange && onChange(min)
|
||||
} else {
|
||||
setValue(value - step)
|
||||
onChange && onChange(value - step)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,13 @@ const QuanittyInput = ({
|
||||
let value = Number(e.target.value) || 0
|
||||
if (min && value < min) {
|
||||
setValue(min)
|
||||
onChange && onChange(min)
|
||||
} else if (max && value > max) {
|
||||
setValue(max)
|
||||
onChange && onChange(max)
|
||||
} else {
|
||||
setValue(value)
|
||||
onChange && onChange(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,7 @@
|
||||
width: 20.6rem;
|
||||
.selectTrigger {
|
||||
width: 20.6rem;
|
||||
padding: 1.2rem 1.6rem;
|
||||
padding: 1.6rem;
|
||||
}
|
||||
}
|
||||
&.large {
|
||||
|
@@ -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)
|
||||
|
@@ -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'
|
||||
|
||||
|
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 useVerifyCustomer } from './useVerifyCustomer'
|
||||
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 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
|
||||
|
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 { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import { signupMutation } from '@framework/utils/mutations/sign-up-mutation'
|
||||
|
||||
interface SignupInput {
|
||||
email: string
|
||||
firstName?: string
|
||||
|
3
src/components/hooks/cart/index.ts
Normal file
3
src/components/hooks/cart/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as useAddProductToCart } from './useAddProductToCart'
|
||||
export { default as useUpdateProductInCart } from './useUpdateProductInCart'
|
||||
export { default as useGetActiveOrder } from './useGetActiveOrder'
|
40
src/components/hooks/cart/useAddProductToCart.tsx
Normal file
40
src/components/hooks/cart/useAddProductToCart.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useState } from 'react'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import { AddItemToOrderMutation, AddItemToOrderMutationVariables } from '@framework/schema'
|
||||
import { errorMapping } from 'src/utils/errrorMapping'
|
||||
import { useGetActiveOrder } from '.'
|
||||
import { addItemToOrderMutation } from '@framework/utils/mutations/add-item-to-order-mutation'
|
||||
|
||||
const useAddProductToCart = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<CommonError | null>(null)
|
||||
const { mutate } = useGetActiveOrder()
|
||||
|
||||
const addProduct = (options:AddItemToOrderMutationVariables,
|
||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||
) => {
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
rawFetcher<AddItemToOrderMutation>({
|
||||
query: addItemToOrderMutation ,
|
||||
variables: options,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.addItemToOrder.__typename !== "Order") {
|
||||
throw CommonError.create(errorMapping(data.addItemToOrder.message), data.addItemToOrder.errorCode)
|
||||
}
|
||||
mutate()
|
||||
fCallBack(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(error)
|
||||
fCallBack(false, error.message)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return { loading, addProduct, error }
|
||||
}
|
||||
|
||||
export default useAddProductToCart
|
23
src/components/hooks/cart/useGetActiveOrder.tsx
Normal file
23
src/components/hooks/cart/useGetActiveOrder.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Cart } from '@commerce/types/cart'
|
||||
import { ActiveOrderQuery } from '@framework/schema'
|
||||
import { cartFragment } from '@framework/utils/fragments/cart-fragment'
|
||||
import { normalizeCart } from '@framework/utils/normalize'
|
||||
import { gql } from 'graphql-request'
|
||||
import gglFetcher from 'src/utils/gglFetcher'
|
||||
import useSWR from 'swr'
|
||||
const query = gql`
|
||||
query activeOrder {
|
||||
activeOrder {
|
||||
...Cart
|
||||
}
|
||||
}
|
||||
${ cartFragment }
|
||||
`
|
||||
|
||||
const useGetActiveOrder = () => {
|
||||
const { data, ...rest } = useSWR<ActiveOrderQuery>([query], gglFetcher)
|
||||
return { order: data?.activeOrder ? normalizeCart(data!.activeOrder) : null, ...rest }
|
||||
|
||||
}
|
||||
|
||||
export default useGetActiveOrder
|
41
src/components/hooks/cart/useRemoveProductInCart.tsx
Normal file
41
src/components/hooks/cart/useRemoveProductInCart.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useState } from 'react'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import { AdjustOrderLineMutationVariables,AdjustOrderLineMutation, RemoveOrderLineMutation, RemoveOrderLineMutationVariables } from '@framework/schema'
|
||||
import { errorMapping } from 'src/utils/errrorMapping'
|
||||
import { useGetActiveOrder } from '.'
|
||||
import { adjustOrderLineMutation } from '@framework/utils/mutations/adjust-order-line-mutation'
|
||||
import { removeOrderLineMutation } from '@framework/utils/mutations/remove-order-line-mutation'
|
||||
|
||||
const useRemoveProductInCart = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<CommonError | null>(null)
|
||||
const { mutate } = useGetActiveOrder()
|
||||
|
||||
const removeProduct = (options:RemoveOrderLineMutationVariables,
|
||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||
) => {
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
rawFetcher<RemoveOrderLineMutation>({
|
||||
query: removeOrderLineMutation ,
|
||||
variables: options,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.removeOrderLine.__typename !== "Order") {
|
||||
throw CommonError.create(errorMapping(data.removeOrderLine.message), data.removeOrderLine.errorCode)
|
||||
}
|
||||
mutate()
|
||||
fCallBack(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(error)
|
||||
fCallBack(false, error.message)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return { loading, removeProduct, error }
|
||||
}
|
||||
|
||||
export default useRemoveProductInCart
|
40
src/components/hooks/cart/useUpdateProductInCart.tsx
Normal file
40
src/components/hooks/cart/useUpdateProductInCart.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useState } from 'react'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import { AdjustOrderLineMutationVariables,AdjustOrderLineMutation } from '@framework/schema'
|
||||
import { errorMapping } from 'src/utils/errrorMapping'
|
||||
import { useGetActiveOrder } from '.'
|
||||
import { adjustOrderLineMutation } from '@framework/utils/mutations/adjust-order-line-mutation'
|
||||
|
||||
const useUpdateProductInCart = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<CommonError | null>(null)
|
||||
const { mutate } = useGetActiveOrder()
|
||||
|
||||
const updateProduct = (options:AdjustOrderLineMutationVariables,
|
||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||
) => {
|
||||
setError(null)
|
||||
setLoading(true)
|
||||
rawFetcher<AdjustOrderLineMutation>({
|
||||
query: adjustOrderLineMutation ,
|
||||
variables: options,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.adjustOrderLine.__typename !== "Order") {
|
||||
throw CommonError.create(errorMapping(data.adjustOrderLine.message), data.adjustOrderLine.errorCode)
|
||||
}
|
||||
mutate()
|
||||
fCallBack(true)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(error)
|
||||
fCallBack(false, error.message)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
return { loading, updateProduct, error }
|
||||
}
|
||||
|
||||
export default useUpdateProductInCart
|
@@ -1 +1,2 @@
|
||||
export { default as useModalCommon } from './useModalCommon'
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
export { default as useSearchProducts } from './useSearchProducts'
|
||||
export { default as useToggleProductWishlist } from './useToggleProductWishlist'
|
||||
export { default as useProductDetail } from './useProductDetail'
|
||||
|
||||
|
||||
|
@@ -1 +0,0 @@
|
||||
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
|
47
src/components/hooks/useGetProductListByCollection.tsx
Normal file
47
src/components/hooks/useGetProductListByCollection.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
// import { gql } from 'graphql-request'
|
||||
import { useMemo, useState } from 'react'
|
||||
// import useActiveCustomer from './useActiveCustomer'
|
||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||
import rawFetcher from 'src/utils/rawFetcher'
|
||||
import {
|
||||
CollectionList,
|
||||
CollectionListOptions,
|
||||
GetCollectionsQuery,
|
||||
GetCollectionsQueryVariables,
|
||||
LoginMutation,
|
||||
} from '@framework/schema'
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
import { getCollectionsQuery } from '@framework/utils/queries/get-collections-query'
|
||||
import useSWR from 'swr'
|
||||
import gglFetcher from 'src/utils/gglFetcher'
|
||||
|
||||
const query = gql`
|
||||
query getCollections($option: CollectionListOptions) {
|
||||
collections(options:$option) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
description
|
||||
slug
|
||||
productVariants {
|
||||
totalItems
|
||||
}
|
||||
parent {
|
||||
id
|
||||
}
|
||||
children {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const useGetProductListByCollection = (options: any) => {
|
||||
|
||||
const { data, ...rest } = useSWR<GetCollectionsQuery>([query, options], gglFetcher)
|
||||
return { collections: data?.collections, ...rest }
|
||||
}
|
||||
|
||||
export default useGetProductListByCollection
|
36
src/components/hooks/useLocalStorage.tsx
Normal file
36
src/components/hooks/useLocalStorage.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useState } from "react";
|
||||
|
||||
// Hook
|
||||
export function useLocalStorage<T>(key: string, initialValue: T) {
|
||||
// State to store our value
|
||||
// Pass initial state function to useState so logic is only executed once
|
||||
const [storedValue, setStoredValue] = useState<T>(() => {
|
||||
try {
|
||||
// Get from local storage by key
|
||||
const item = localStorage.getItem(key);
|
||||
// Parse stored json or if none return initialValue
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
} catch (error) {
|
||||
// If error also return initialValue
|
||||
// console.log(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
// Return a wrapped version of useState's setter function that ...
|
||||
// ... persists the new value to localStorage.
|
||||
const setValue = (value: T | ((val: T) => T)) => {
|
||||
try {
|
||||
// Allow value to be a function so we have same API as useState
|
||||
const valueToStore =
|
||||
value instanceof Function ? value(storedValue) : value;
|
||||
// Save state
|
||||
setStoredValue(valueToStore);
|
||||
// Save to local storage
|
||||
localStorage.setItem(key, JSON.stringify(valueToStore));
|
||||
} catch (error) {
|
||||
// A more advanced implementation would handle the error case
|
||||
// console.log(error);
|
||||
}
|
||||
};
|
||||
return [storedValue, setValue] as const;
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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 {
|
||||
|
@@ -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>
|
||||
)
|
||||
|
@@ -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>
|
||||
)
|
||||
}
|
||||
|
@@ -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} />
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect, useState,useRef } from 'react'
|
||||
import CardBlog, { BlogCardProps } from 'src/components/common/CardBlog/CardBlog'
|
||||
import PaginationCommon from 'src/components/common/PaginationCommon/PaginationCommon'
|
||||
import { DEFAULT_BLOG_PAGE_SIZE, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
|
||||
@@ -11,18 +11,21 @@ import { ListProductCardSkeleton } from 'src/components/common'
|
||||
|
||||
interface BlogsListProps {
|
||||
blogList?: BlogCardProps[],
|
||||
total?: number
|
||||
total?: number,
|
||||
idFeatured?:string
|
||||
}
|
||||
|
||||
const DEFAULT_BLOGS_ARGS = {
|
||||
excludeBlogIds: ["28"],
|
||||
options:{
|
||||
skip: 1, take: DEFAULT_BLOG_PAGE_SIZE
|
||||
|
||||
|
||||
const BlogsList = ({ blogList,total,idFeatured }:BlogsListProps) => {
|
||||
|
||||
const DEFAULT_BLOGS_ARGS = {
|
||||
excludeBlogIds: [idFeatured],
|
||||
options:{
|
||||
skip: 1, take: DEFAULT_BLOG_PAGE_SIZE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const BlogsList = ({ blogList,total }:BlogsListProps) => {
|
||||
|
||||
const router = useRouter();
|
||||
const [initialQueryFlag, setInitialQueryFlag] = useState<boolean>(true)
|
||||
|
||||
@@ -44,13 +47,14 @@ const BlogsList = ({ blogList,total }:BlogsListProps) => {
|
||||
}
|
||||
|
||||
// skip
|
||||
const firstRender = useRef(true);
|
||||
useEffect(() => {
|
||||
firstRender.current = false;
|
||||
const query = { ...DEFAULT_BLOGS_ARGS } as QueryBlogs;
|
||||
const page = getPageFromQuery(router.query[QUERY_KEY.PAGE] as string);
|
||||
query.options.skip = page * DEFAULT_BLOG_PAGE_SIZE;
|
||||
setOptionQueryBlog(query);
|
||||
setInitialQueryFlag(false);
|
||||
|
||||
},[router.query])
|
||||
|
||||
|
||||
@@ -60,7 +64,7 @@ const BlogsList = ({ blogList,total }:BlogsListProps) => {
|
||||
}else{
|
||||
data = blogs
|
||||
}
|
||||
console.log(blogList);
|
||||
|
||||
|
||||
return (
|
||||
<section>
|
||||
|
@@ -1,8 +1,10 @@
|
||||
import s from './FeaturedCardBlog.module.scss'
|
||||
import { Author, DateTime, ImgWithLink } from 'src/components/common'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
interface FeaturedCardBlogProps{
|
||||
title?: string,
|
||||
slug?:string,
|
||||
content?: string,
|
||||
imgSrc?: string,
|
||||
imgAuthor?: string,
|
||||
@@ -10,33 +12,31 @@ interface FeaturedCardBlogProps{
|
||||
authorName?: string,
|
||||
}
|
||||
|
||||
const FEATURED_DATA = {
|
||||
title: "Flammekueche with green asparagus",
|
||||
content: "Traditionally, the Flammekueche is made with rapeseed oil, which, contrary to popular belief, is indeed an oil that can be cooked hot and is not limited to seasoning. It is important to vary the oils in the kitchen to take advantage of the benefits of each. Rapeseed oil is an oil rich in omega 3 which participate in the proper functioning of the cardiovascular system as well as in vitamins E which contributes to the protection of cells against oxidative stress. In short, oils are your friends 😉",
|
||||
imgSrc: "https://user-images.githubusercontent.com/46085455/133186666-1ea8081f-4319-4617-8644-d20ed14b1825.png",
|
||||
imgAuthor: "https://user-images.githubusercontent.com/46085455/133186783-d0c71d43-b7bc-44b6-b560-818c71bd162f.png",
|
||||
date: "APRIL 30, 2021",
|
||||
author: "Alessandro Del Piero"
|
||||
}
|
||||
|
||||
const FeaturedCardBlog = ({
|
||||
title = FEATURED_DATA.title,
|
||||
content = FEATURED_DATA.content,
|
||||
imgSrc = FEATURED_DATA.imgSrc,
|
||||
imgAuthor = FEATURED_DATA.imgAuthor,
|
||||
date = FEATURED_DATA.date,
|
||||
authorName = FEATURED_DATA.author
|
||||
title,
|
||||
slug,
|
||||
content,
|
||||
imgSrc = '',
|
||||
imgAuthor = '',
|
||||
date = '',
|
||||
authorName = ''
|
||||
}: FeaturedCardBlogProps) => {
|
||||
return (
|
||||
<section className={s.featuredCard}>
|
||||
<div className={s.featuredCardWrapper}>
|
||||
<div className={s.left}>
|
||||
<ImgWithLink src={imgSrc} alt="image feature card"/>
|
||||
</div>
|
||||
<Link href={`${ROUTE.BLOG_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.left}>
|
||||
<ImgWithLink src={imgSrc} alt="image feature card"/>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.right}>
|
||||
<div className={s.titleWrapper}>
|
||||
<DateTime date={date}/>
|
||||
<a className={s.title}>{title}</a>
|
||||
<DateTime date={date }/>
|
||||
<Link href={`${ROUTE.BLOG_DETAIL}/${slug}`}>
|
||||
<a className={s.title}>{title}</a>
|
||||
</Link>
|
||||
</div>
|
||||
<Author name={authorName} image={imgAuthor}/>
|
||||
<div className={s.content}>{content}</div>
|
||||
|
@@ -10,6 +10,7 @@ interface FreshProductsProps {
|
||||
}
|
||||
|
||||
const FreshProducts = ({ data, collections }: FreshProductsProps) => {
|
||||
|
||||
const dataWithCategory = useMemo(() => {
|
||||
return data.map(item => {
|
||||
return {
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import React from 'react'
|
||||
import { CollectionListOptions, GetCollectionsQuery } from '@framework/schema'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { Banner, StaticImage } from 'src/components/common'
|
||||
import useGetProductListByCollection from 'src/components/hooks/useGetProductListByCollection'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import BannerImgRight from './assets/banner_full.png'
|
||||
import HomeBannerImg from './assets/home_banner.png'
|
||||
@@ -11,6 +13,10 @@ interface Props {
|
||||
}
|
||||
|
||||
const HomeBanner = ({ }: Props) => {
|
||||
// const variables = useMemo(() => {
|
||||
// return {option: {filter: {name: {eq: "Computers" }}}}
|
||||
// }, [])
|
||||
// const {collections} = useGetProductListByCollection(variables)
|
||||
return (
|
||||
<div className={s.homeBanner}>
|
||||
<section className={s.left}>
|
||||
|
@@ -1,10 +1,13 @@
|
||||
import React from 'react'
|
||||
import { ProductCard } from '@commerce/types/product'
|
||||
import { CollectionCarcousel } from '..'
|
||||
import image5 from '../../../../../public/assets/images/image5.png'
|
||||
import image6 from '../../../../../public/assets/images/image6.png'
|
||||
import image7 from '../../../../../public/assets/images/image7.png'
|
||||
import image8 from '../../../../../public/assets/images/image8.png'
|
||||
interface HomeCollectionProps {}
|
||||
interface HomeCollectionProps {
|
||||
data: ProductCard[]
|
||||
}
|
||||
const dataTest = [
|
||||
{
|
||||
name: 'Tomato',
|
||||
@@ -92,39 +95,39 @@ const dataTest = [
|
||||
},
|
||||
]
|
||||
|
||||
const HomeCollection = (props: HomeCollectionProps) => {
|
||||
const HomeCollection = ({data}: HomeCollectionProps) => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-2"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-3"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-4"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-5"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
data={data}
|
||||
itemKey="product-6"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
|
@@ -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>
|
||||
)
|
||||
}
|
||||
|
@@ -1,27 +1,18 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import ProductImgs from './components/ProductImgs/ProductImgs'
|
||||
import ProductInfo from './components/ProductInfo/ProductInfo'
|
||||
import s from './ProductInfoDetail.module.scss'
|
||||
import { Product } from '@commerce/types/product'
|
||||
import { Collection } from '@framework/schema'
|
||||
import { getCategoryNameFromCollectionId } from 'src/utils/funtion.utils';
|
||||
|
||||
interface Props {
|
||||
productDetail: Product,
|
||||
collections: Collection[]
|
||||
}
|
||||
|
||||
const ProductInfoDetail = ({ productDetail, collections }: Props) => {
|
||||
const dataWithCategoryName = useMemo(() => {
|
||||
return {
|
||||
...productDetail,
|
||||
collection: getCategoryNameFromCollectionId(collections, productDetail.collectionIds ? productDetail.collectionIds[0] : undefined)
|
||||
}
|
||||
}, [productDetail, collections])
|
||||
const ProductInfoDetail = ({ productDetail }: Props) => {
|
||||
return (
|
||||
<section className={s.productInfoDetail}>
|
||||
<ProductImgs productImage={productDetail.images}/>
|
||||
<ProductInfo productInfoDetail={dataWithCategoryName}/>
|
||||
<ProductInfo productInfoDetail={productDetail}/>
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
.warpper{
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
.name{
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.option{
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
// > button {
|
||||
// margin-right: 1rem;
|
||||
// }
|
||||
}
|
||||
.button {
|
||||
margin: 1rem 0;
|
||||
padding: 0;
|
||||
div {
|
||||
padding: 0.8rem 1.6rem;
|
||||
margin-right: 0.8rem;
|
||||
background-color: var(--gray);
|
||||
border-radius: 0.8rem;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
color: var(--white);
|
||||
background-color: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
import { ProductOption, ProductOptionValues } from '@commerce/types/product'
|
||||
import classNames from 'classnames'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { SelectedOptions } from 'src/utils/types.utils'
|
||||
import s from './ProductDetailOption.module.scss'
|
||||
interface Props {
|
||||
option: ProductOption
|
||||
onChane: (values: SelectedOptions) => void
|
||||
}
|
||||
|
||||
const ProductDetailOption = React.memo(({ option, onChane }: Props) => {
|
||||
const [selected, setSelected] = useState<string>('')
|
||||
useEffect(() => {
|
||||
if (option) {
|
||||
setSelected(option.values[0].label)
|
||||
}
|
||||
}, [option])
|
||||
const handleClick = (value:string) => {
|
||||
setSelected(value)
|
||||
onChane && onChane({[option.displayName]:value})
|
||||
}
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<div className={s.name}>{option.displayName}:</div>
|
||||
<div className={s.option}>
|
||||
{option.values.map((value) => {
|
||||
return <ProductDetailOptionButton value={value} selected={selected} onClick={handleClick} key={value.label}/>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
interface ProductDetailOptionButtonProps {
|
||||
value: ProductOptionValues
|
||||
selected: string
|
||||
onClick: (value: string) => void
|
||||
}
|
||||
const ProductDetailOptionButton = ({
|
||||
value,
|
||||
selected,
|
||||
onClick,
|
||||
}: ProductDetailOptionButtonProps) => {
|
||||
const handleClick = () => {
|
||||
onClick && onClick(value.label)
|
||||
}
|
||||
return (
|
||||
<div className={s.button}>
|
||||
<div onClick={handleClick} className={classNames({ [s.active]: selected === value.label })}>
|
||||
{value.label}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductDetailOption
|
@@ -1,8 +1,15 @@
|
||||
import { Product } from '@commerce/types/product'
|
||||
import React from 'react'
|
||||
import Router from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { ButtonCommon, LabelCommon, QuanittyInput } from 'src/components/common'
|
||||
import { useCartDrawer, useMessage } from 'src/components/contexts'
|
||||
import { useAddProductToCart } from 'src/components/hooks/cart'
|
||||
import { IconBuy } from 'src/components/icons'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import { getProductVariant } from 'src/utils/funtion.utils'
|
||||
import { LANGUAGE } from 'src/utils/language.utils'
|
||||
import { SelectedOptions } from 'src/utils/types.utils'
|
||||
import ProductDetailOption from '../ProductDetailOption/ProductDetailOption'
|
||||
import s from './ProductInfo.module.scss'
|
||||
|
||||
interface Props {
|
||||
@@ -10,11 +17,73 @@ interface Props {
|
||||
}
|
||||
|
||||
const ProductInfo = ({ productInfoDetail }: Props) => {
|
||||
console.log(productInfoDetail)
|
||||
const [option, setOption] = useState({})
|
||||
const [quanitty, setQuanitty] = useState(0)
|
||||
const [addToCartLoading, setAddToCartLoading] = useState(false)
|
||||
const [buyNowLoading, setBuyNowLoading] = useState(false)
|
||||
const { showMessageSuccess, showMessageError } = useMessage()
|
||||
useEffect(() => {
|
||||
let defaultOption:SelectedOptions = {}
|
||||
productInfoDetail.options.map((option)=>{
|
||||
defaultOption[option.displayName] = option.values[0].label
|
||||
return null
|
||||
})
|
||||
}, [productInfoDetail])
|
||||
|
||||
const {addProduct} = useAddProductToCart()
|
||||
const { openCartDrawer } = useCartDrawer()
|
||||
|
||||
function handleAddToCart() {
|
||||
setAddToCartLoading(true)
|
||||
const variant = getProductVariant(productInfoDetail, option)
|
||||
if (variant) {
|
||||
addProduct({ variantId: variant.id.toString(), quantity: quanitty }, handleAddToCartCallback)
|
||||
}else{
|
||||
setAddToCartLoading(false)
|
||||
}
|
||||
}
|
||||
const handleAddToCartCallback = (isSuccess:boolean,message?:string) => {
|
||||
setAddToCartLoading(false)
|
||||
if(isSuccess){
|
||||
showMessageSuccess("Add to cart successfully!", 4000)
|
||||
openCartDrawer && openCartDrawer()
|
||||
}else{
|
||||
showMessageError(message||"Error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const handleBuyNowCallback = (success:boolean,message?:string) => {
|
||||
setBuyNowLoading(false)
|
||||
if(success){
|
||||
Router.push(ROUTE.CHECKOUT)
|
||||
}else{
|
||||
showMessageError(message||"Error")
|
||||
}
|
||||
}
|
||||
|
||||
const handleBuyNow = () => {
|
||||
setBuyNowLoading(true)
|
||||
const variant = getProductVariant(productInfoDetail, option)
|
||||
if (variant) {
|
||||
addProduct({ variantId: variant.id.toString(), quantity: quanitty }, handleBuyNowCallback)
|
||||
}else{
|
||||
setBuyNowLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuanittyChange = (value:number) => {
|
||||
setQuanitty(value)
|
||||
}
|
||||
const onSelectOption = (value:SelectedOptions) => {
|
||||
setOption({...option,...value})
|
||||
// let variant = getProductVariant(productInfoDetail,value)
|
||||
// console.log(variant)
|
||||
}
|
||||
return (
|
||||
<section className={s.productInfo}>
|
||||
<div className={s.info}>
|
||||
<LabelCommon shape='half'>{productInfoDetail.collection}</LabelCommon>
|
||||
<LabelCommon shape='half'>{productInfoDetail.collection?.[0]}</LabelCommon>
|
||||
<h2 className={s.heading}>{productInfoDetail.name}</h2>
|
||||
<div className={s.price}>
|
||||
<div className={s.old}>
|
||||
@@ -26,14 +95,22 @@ const ProductInfo = ({ productInfoDetail }: Props) => {
|
||||
<div className={s.description}>
|
||||
{productInfoDetail.description}
|
||||
</div>
|
||||
<div className={s.options}>
|
||||
{
|
||||
productInfoDetail.options.map((option)=>{
|
||||
return <ProductDetailOption option={option} onChane={onSelectOption} key={option.displayName}/>
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.actions}>
|
||||
<QuanittyInput />
|
||||
<QuanittyInput value={quanitty} onChange={handleQuanittyChange}/>
|
||||
<div className={s.bottom}>
|
||||
{/* <ButtonCommon size='large'>{LANGUAGE.BUTTON_LABEL.PREORDER}</ButtonCommon> */}
|
||||
<ButtonCommon size='large'>{LANGUAGE.BUTTON_LABEL.BUY_NOW}</ButtonCommon>
|
||||
<ButtonCommon size='large' onClick={handleBuyNow} loading={buyNowLoading} disabled={addToCartLoading}>{LANGUAGE.BUTTON_LABEL.BUY_NOW}</ButtonCommon>
|
||||
|
||||
<ButtonCommon size='large' type='light'>
|
||||
<ButtonCommon size='large' type='light' onClick={handleAddToCart} loading={addToCartLoading} disabled={buyNowLoading}>
|
||||
<span className={s.buttonWithIcon}>
|
||||
<IconBuy /><span className={s.label}>{LANGUAGE.BUTTON_LABEL.ADD_TO_CARD}</span>
|
||||
</span>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Collection } from '@commerce/types/collection';
|
||||
import { ProductCard } from '@commerce/types/product';
|
||||
import { Collection } from '@framework/schema';
|
||||
import React, { useMemo } from 'react';
|
||||
import ListProductWithInfo from 'src/components/common/ListProductWithInfo/ListProductWithInfo';
|
||||
import { getCategoryNameFromCollectionId } from 'src/utils/funtion.utils';
|
||||
|
@@ -1,13 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Product } from '@commerce/types/product';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ListProductWithInfo from 'src/components/common/ListProductWithInfo/ListProductWithInfo';
|
||||
import { PRODUCT_DATA_TEST } from 'src/utils/demo-data';
|
||||
|
||||
const ViewedProducts = () => {
|
||||
import { ProductCardProps } from 'src/components/common/ProductCard/ProductCard';
|
||||
import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils'
|
||||
import { normalizeProductCard } from '@framework/utils/normalize';
|
||||
import { useLocalStorage } from 'src/components/hooks/useLocalStorage';
|
||||
interface Props {
|
||||
data: ProductCardProps[]
|
||||
}
|
||||
const ViewedProducts = ({data}:Props) => {
|
||||
if (data.length===0){
|
||||
return <div></div>
|
||||
}
|
||||
return (
|
||||
<ListProductWithInfo
|
||||
title="viewed Products"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
data={PRODUCT_DATA_TEST}
|
||||
data={data}
|
||||
hasBorderBottomMobile={true}
|
||||
/>
|
||||
);
|
||||
|
2
src/domains/enums/Message.ts
Normal file
2
src/domains/enums/Message.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
|
@@ -44,7 +44,8 @@ export const ACCOUNT_TAB = {
|
||||
}
|
||||
|
||||
export const LOCAL_STORAGE_KEY = {
|
||||
TOKEN: 'token'
|
||||
TOKEN: 'token',
|
||||
VIEWEDPRODUCT: "viewed-product"
|
||||
}
|
||||
|
||||
export const QUERY_SPLIT_SEPERATOR = ','
|
||||
@@ -82,46 +83,58 @@ export const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
|
||||
export const CATEGORY = [
|
||||
{
|
||||
name: 'All',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${OPTION_ALL}`,
|
||||
},
|
||||
{
|
||||
name: 'Veggie',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=veggie`,
|
||||
},
|
||||
{
|
||||
name: 'Seafood',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=seafood`,
|
||||
},
|
||||
{
|
||||
name: 'Frozen',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=frozen`,
|
||||
},
|
||||
{
|
||||
name: 'Coffee Bean',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=coffee_bean`,
|
||||
},
|
||||
{
|
||||
name: 'Sauce',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=sauce`,
|
||||
},
|
||||
]
|
||||
{
|
||||
name: 'All',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${OPTION_ALL}`,
|
||||
},
|
||||
{
|
||||
name: 'Veggie',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=veggie`,
|
||||
},
|
||||
{
|
||||
name: 'Seafood',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=seafood`,
|
||||
},
|
||||
{
|
||||
name: 'Frozen',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=frozen`,
|
||||
},
|
||||
{
|
||||
name: 'Coffee Bean',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=coffee_bean`,
|
||||
},
|
||||
{
|
||||
name: 'Sauce',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=sauce`,
|
||||
},
|
||||
]
|
||||
|
||||
export const BRAND = [
|
||||
{
|
||||
name: 'Maggi',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=maggi`,
|
||||
},
|
||||
{
|
||||
name: 'Chomilex',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chomilex`,
|
||||
},
|
||||
{
|
||||
name: 'Chinsu',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
|
||||
},
|
||||
]
|
||||
|
||||
export const BRAND = [
|
||||
{
|
||||
name: 'Maggi',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=maggi`,
|
||||
export const FACET = {
|
||||
FEATURE: {
|
||||
PARENT_NAME: 'Featured',
|
||||
FRESH: 'Fresh',
|
||||
BEST_SELLERS: 'Best seller'
|
||||
},
|
||||
{
|
||||
name: 'Chomilex',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chomilex`,
|
||||
},
|
||||
{
|
||||
name: 'Chinsu',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
|
||||
},
|
||||
]
|
||||
CATEGORY: {
|
||||
PARENT_NAME:"category",
|
||||
VEGGIE:"veggie"
|
||||
}
|
||||
}
|
||||
|
||||
export const CODE_FACET_FEATURED = 'featured'
|
||||
export const CODE_FACET_DISCOUNT = 'discount'
|
||||
@@ -186,3 +199,4 @@ export const STATE_OPTIONS = [
|
||||
},
|
||||
]
|
||||
|
||||
export const COLLECTION_SLUG_SPICE ="spice";
|
||||
|
0
src/utils/enum.ts
Normal file
0
src/utils/enum.ts
Normal file
@@ -1,8 +1,9 @@
|
||||
import { BlogList } from '@framework/schema';
|
||||
import { Collection } from '@commerce/types/collection';
|
||||
import { Facet } from "@commerce/types/facet";
|
||||
import { Collection, FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d';
|
||||
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT, PRODUCT_SORT_OPTION_VALUE } from "./constanst.utils";
|
||||
import { PromiseWithKey, SortOrder } from "./types.utils";
|
||||
import { Product, ProductCard, ProductOption, ProductOptionValues } from "@commerce/types/product";
|
||||
import { FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d';
|
||||
import { CODE_FACET_DISCOUNT, CODE_FACET_FEATURED, CODE_FACET_FEATURED_VARIANT, FACET, PRODUCT_SORT_OPTION_VALUE } from "./constanst.utils";
|
||||
import { PromiseWithKey, SelectedOptions, SortOrder } from "./types.utils";
|
||||
|
||||
export function isMobile() {
|
||||
return window.innerWidth < 768
|
||||
@@ -80,6 +81,18 @@ export function getFreshFacetId(facets: Facet[]) {
|
||||
return freshFacetValue?.id
|
||||
}
|
||||
|
||||
export function getFacetIdByName(facets: Facet[], facetName: string, valueName:string) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.name === facetName)
|
||||
const freshFacetValue = featuredFacet?.values.find((item: FacetValue) => item.name === valueName)
|
||||
return freshFacetValue?.id
|
||||
}
|
||||
|
||||
|
||||
export function getAllFeaturedFacetId(facets: Facet[]) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.name === FACET.FEATURE.PARENT_NAME)
|
||||
const rs = featuredFacet?.values.map((item: FacetValue) => item.id)
|
||||
return rs || []
|
||||
}
|
||||
export function getAllFacetValueIdsByParentCode(facets: Facet[], code: string) {
|
||||
const featuredFacet = facets.find((item: Facet) => item.code === code)
|
||||
const rs = featuredFacet?.values.map((item: FacetValue) => item.id)
|
||||
@@ -134,3 +147,34 @@ export function getIdFeaturedBlog(blog: BlogList) {
|
||||
return blog?.id
|
||||
}
|
||||
|
||||
export const FilterOneVatiant = (products:ProductCard[]) => {
|
||||
let idList:string[] = []
|
||||
let filtedProduct: ProductCard[]=[]
|
||||
products.map((product:ProductCard)=>{
|
||||
if(!idList.includes(product.id)){
|
||||
filtedProduct.push(product)
|
||||
idList.push(product.id)
|
||||
}
|
||||
})
|
||||
return filtedProduct
|
||||
}
|
||||
|
||||
export const convertOption = (values :ProductOptionValues[]) => {
|
||||
return values.map((value)=>{ return {name:value.label,value:value.label}})
|
||||
}
|
||||
|
||||
export function getProductVariant(product: Product, opts: SelectedOptions) {
|
||||
const variant = product.variants?.find((variant) => {
|
||||
return Object.entries(opts).every(([key, value]) =>
|
||||
variant.options.find((option) => {
|
||||
if (
|
||||
option.__typename === 'MultipleChoiceOption' &&
|
||||
option.displayName.toLowerCase() === key.toLowerCase()
|
||||
) {
|
||||
return option.values.find((v) => v.label.toLowerCase() === value)
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
return variant
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
|
||||
export interface ProductProps {
|
||||
category?: string
|
||||
name: string
|
||||
@@ -57,8 +58,18 @@ export type filterContextType = {
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
export interface StringMap { [key: string]: string; }
|
||||
|
||||
export interface FacetMap extends StringMap{
|
||||
PARENT_NAME: string
|
||||
}
|
||||
export interface FacetConstant{
|
||||
[key: string]: FacetMap;
|
||||
}
|
||||
export type PromiseWithKey = {
|
||||
key: string
|
||||
promise: PromiseLike<any>
|
||||
keyResult?: string,
|
||||
}
|
||||
|
||||
export type SelectedOptions = Record<string, string | null>
|
Reference in New Issue
Block a user