mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
Merge pull request #91 from KieIO/checkout
Checkout (without payment provider)
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import type { Discount, Measurement, Image } from './common'
|
import { ShippingMethod } from '@framework/schema';
|
||||||
|
import type { Discount, Image, Measurement } from './common';
|
||||||
|
|
||||||
export type SelectedOption = {
|
export type SelectedOption = {
|
||||||
// The option's id.
|
// The option's id.
|
||||||
@@ -83,6 +84,50 @@ export type Cart = {
|
|||||||
discounts?: Discount[]
|
discounts?: Discount[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CartCheckout = {
|
||||||
|
id: string
|
||||||
|
// ID of the customer to which the cart belongs.
|
||||||
|
customerId?: string
|
||||||
|
customer?: {
|
||||||
|
firstName: string,
|
||||||
|
lastName: string,
|
||||||
|
emailAddress: string,
|
||||||
|
}
|
||||||
|
shippingAddress?: {
|
||||||
|
streetLine1: string,
|
||||||
|
city: string,
|
||||||
|
province: string,
|
||||||
|
postalCode: string,
|
||||||
|
countryCode: string,
|
||||||
|
phoneNumber: string,
|
||||||
|
}
|
||||||
|
// The email assigned to this cart
|
||||||
|
email?: string
|
||||||
|
// The date and time when the cart was created.
|
||||||
|
createdAt: string
|
||||||
|
// The currency used for this cart
|
||||||
|
currency: { code: string }
|
||||||
|
// Specifies if taxes are included in the line items.
|
||||||
|
taxesIncluded: boolean
|
||||||
|
lineItems: LineItem[]
|
||||||
|
// The sum of all the prices of all the items in the cart.
|
||||||
|
// Duties, taxes, shipping and discounts excluded.
|
||||||
|
lineItemsSubtotalPrice: number
|
||||||
|
// Price of the cart before duties, shipping and taxes.
|
||||||
|
subtotalPrice: number
|
||||||
|
// The sum of all the prices of all the items in the cart.
|
||||||
|
// Duties, taxes and discounts included.
|
||||||
|
totalPrice: number
|
||||||
|
totalQuantity: number
|
||||||
|
// Discounts that have been applied on the cart.
|
||||||
|
discounts?: Discount[]
|
||||||
|
totalDiscount: number
|
||||||
|
shippingLine?: {
|
||||||
|
priceWithTax: number
|
||||||
|
shippingMethod: ShippingMethod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base cart item body used for cart mutations
|
* Base cart item body used for cart mutations
|
||||||
*/
|
*/
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
export type Discount = {
|
export type Discount = {
|
||||||
// The value of the discount, can be an amount or percentage
|
// The value of the discount, can be an amount or percentage
|
||||||
value: number
|
value: number
|
||||||
|
description?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Measurement = {
|
export type Measurement = {
|
||||||
|
@@ -2,7 +2,7 @@ import { Product } from '@commerce/types/product'
|
|||||||
import { OperationContext } from '@commerce/api/operations'
|
import { OperationContext } from '@commerce/api/operations'
|
||||||
import { Provider, VendureConfig } from '../'
|
import { Provider, VendureConfig } from '../'
|
||||||
import { GetProductQuery } from '../../schema'
|
import { GetProductQuery } from '../../schema'
|
||||||
import { getProductQuery, getProductDetailQuery } from '../../utils/queries/get-product-query'
|
import { getProductQuery } from '../../utils/queries/get-product-query'
|
||||||
|
|
||||||
export default function getProductOperation({
|
export default function getProductOperation({
|
||||||
commerce,
|
commerce,
|
||||||
|
90
framework/vendure/schema.d.ts
vendored
90
framework/vendure/schema.d.ts
vendored
@@ -332,17 +332,9 @@ export type SetCustomerForOrderMutation = { __typename?: 'Mutation' } & {
|
|||||||
>)
|
>)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SetCustomerForOrderMutation = { __typename?: 'Mutation' } & {
|
export type SetOrderShippingAddressMutation = { __typename?: 'Mutation' } & {
|
||||||
setCustomerForOrder:
|
setOrderShippingAddress:
|
||||||
| ({ __typename: 'ActiveOrderCustomerFragment' } & Pick<ActiveOrderCustomerFragment, 'customer', 'lines'>)
|
| ({ __typename: 'Order' } & Pick<Order, 'id' | 'total' | 'totalQuantity' | 'code' | 'shippingAddress'>)
|
||||||
| ({ __typename: 'AlreadyLoggedInError' } & Pick<
|
|
||||||
AlreadyLoggedInError,
|
|
||||||
'errorCode' | 'message'
|
|
||||||
>)
|
|
||||||
| ({ __typename: 'EmailAddressConflictError' } & Pick<
|
|
||||||
EmailAddressConflictError,
|
|
||||||
'errorCode' | 'message'
|
|
||||||
>)
|
|
||||||
| ({ __typename: 'NoActiveOrderError' } & Pick<
|
| ({ __typename: 'NoActiveOrderError' } & Pick<
|
||||||
NoActiveOrderError,
|
NoActiveOrderError,
|
||||||
'errorCode' | 'message'
|
'errorCode' | 'message'
|
||||||
@@ -373,8 +365,24 @@ export type Address = Node & {
|
|||||||
customFields?: Maybe<Scalars['JSON']>
|
customFields?: Maybe<Scalars['JSON']>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SetShippingMethodMutationVariables = Exact<{
|
||||||
|
id: Scalars['ID'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type SetShippingMethodMutation = {
|
||||||
|
setOrderShippingMethod:
|
||||||
|
| TestOrderFragmentFragment
|
||||||
|
| Pick<OrderModificationError, 'errorCode' | 'message'>
|
||||||
|
| Pick<IneligibleShippingMethodError, 'errorCode' | 'message'>
|
||||||
|
| Pick<NoActiveOrderError, 'errorCode' | 'message'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type GetEligibleMethodsQuery = {
|
||||||
|
eligibleShippingMethods: Array<
|
||||||
|
Pick<ShippingMethodQuote, 'id' | 'name' | 'description' | 'price' | 'priceWithTax' | 'metadata'>
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
export type Asset = Node & {
|
export type Asset = Node & {
|
||||||
__typename?: 'Asset'
|
__typename?: 'Asset'
|
||||||
@@ -3109,7 +3117,16 @@ export type CartFragment = { __typename?: 'Order' } & Pick<
|
|||||||
| 'totalWithTax'
|
| 'totalWithTax'
|
||||||
| 'currencyCode'
|
| 'currencyCode'
|
||||||
> & {
|
> & {
|
||||||
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>
|
shippingAddress?: Maybe<{ __typename?: 'OrderAddress' } & Pick<OrderAddress, 'streetLine1' | 'fullName' | 'city' | 'province' | 'postalCode' | 'countryCode' | 'phoneNumber'>>
|
||||||
|
discounts: Array<
|
||||||
|
{ __typename?: 'Discount' } & Pick<Discount, 'type' | 'description' | 'amount' | 'amountWithTax'>
|
||||||
|
>
|
||||||
|
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id' | 'firstName' | 'lastName' | 'emailAddress'>>
|
||||||
|
shippingLines: Array<
|
||||||
|
Pick<ShippingLine, 'priceWithTax'> & {
|
||||||
|
shippingMethod: Pick<ShippingMethod, 'id' | 'code' | 'name' | 'description'>;
|
||||||
|
}
|
||||||
|
>
|
||||||
lines: Array<
|
lines: Array<
|
||||||
{ __typename?: 'OrderLine' } & Pick<
|
{ __typename?: 'OrderLine' } & Pick<
|
||||||
OrderLine,
|
OrderLine,
|
||||||
@@ -3212,6 +3229,36 @@ export type AdjustOrderLineMutation = { __typename?: 'Mutation' } & {
|
|||||||
>)
|
>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type ApplyCouponCodeMutationVariables = Exact<{
|
||||||
|
couponCode: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type ApplyCouponCodeMutation = {
|
||||||
|
applyCouponCode:
|
||||||
|
| TestOrderFragmentFragment
|
||||||
|
| Pick<CouponCodeExpiredError, 'errorCode' | 'message'>
|
||||||
|
| Pick<CouponCodeInvalidError, 'errorCode' | 'message'>
|
||||||
|
| Pick<CouponCodeLimitError, 'errorCode' | 'message'>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApplyCouponCodeMutation = { __typename?: 'Mutation' } & {
|
||||||
|
applyCouponCode:
|
||||||
|
| ({ __typename: 'Order' } & CartFragment)
|
||||||
|
| ({ __typename: 'CouponCodeExpiredError' } & Pick<
|
||||||
|
CouponCodeExpiredError,
|
||||||
|
'errorCode' | 'message'
|
||||||
|
>)
|
||||||
|
| ({ __typename: 'CouponCodeInvalidError' } & Pick<
|
||||||
|
CouponCodeInvalidError,
|
||||||
|
'errorCode' | 'message'
|
||||||
|
>)
|
||||||
|
| ({ __typename: 'CouponCodeLimitError' } & Pick<
|
||||||
|
CouponCodeLimitError,
|
||||||
|
'errorCode' | 'message'
|
||||||
|
>)
|
||||||
|
}
|
||||||
|
|
||||||
export type LoginMutationVariables = Exact<{
|
export type LoginMutationVariables = Exact<{
|
||||||
username: Scalars['String']
|
username: Scalars['String']
|
||||||
password: Scalars['String']
|
password: Scalars['String']
|
||||||
@@ -3367,6 +3414,25 @@ type Favorite = Node & {
|
|||||||
customer: Customer!
|
customer: Customer!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GetAvailableCountriesQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
// export type GetAvailableCountriesQuery = { countries: (
|
||||||
|
// { __typename?: 'CountryList' }
|
||||||
|
// & { items: Array<(
|
||||||
|
// { __typename?: 'Country' }
|
||||||
|
// & Pick<Country, 'id' | 'code' | 'name' | 'enabled'>
|
||||||
|
// )> }
|
||||||
|
// ) };
|
||||||
|
|
||||||
|
export type GetAvailableCountriesQuery = {
|
||||||
|
availableCountries:
|
||||||
|
{ __typename?: 'CountryList' }
|
||||||
|
& Array<(
|
||||||
|
{ __typename?: 'Country' }
|
||||||
|
& Pick<Country, 'id' | 'code' | 'name' | 'enabled'>
|
||||||
|
)>
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
type FavouriteOption = Customer & {
|
type FavouriteOption = Customer & {
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
export const applyCouponCodeMutation = /* GraphQL */ `
|
||||||
|
mutation applyCouponCode($couponCode: String!) {
|
||||||
|
applyCouponCode(couponCode: $couponCode) {
|
||||||
|
__typename
|
||||||
|
... on Order {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
discounts {
|
||||||
|
type
|
||||||
|
amount
|
||||||
|
amountWithTax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on ErrorResult {
|
||||||
|
errorCode
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -0,0 +1,25 @@
|
|||||||
|
export const setOrderShippingAddressMutation = /* GraphQL */ `
|
||||||
|
mutation setOrderShippingAddress($input: CreateAddressInput!) {
|
||||||
|
setOrderShippingAddress(input: $input) {
|
||||||
|
__typename
|
||||||
|
... on Order {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
code
|
||||||
|
shippingAddress {
|
||||||
|
streetLine1
|
||||||
|
city
|
||||||
|
province
|
||||||
|
postalCode
|
||||||
|
countryCode
|
||||||
|
phoneNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on ErrorResult {
|
||||||
|
errorCode
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -0,0 +1,35 @@
|
|||||||
|
export const setShippingMethodMutation = /* GraphQL */ `
|
||||||
|
mutation SetShippingMethod($id: ID!) {
|
||||||
|
setOrderShippingMethod(shippingMethodId: $id) {
|
||||||
|
...Cart
|
||||||
|
...ErrorResult
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment Cart on Order {
|
||||||
|
id
|
||||||
|
code
|
||||||
|
state
|
||||||
|
active
|
||||||
|
shippingLines {
|
||||||
|
priceWithTax
|
||||||
|
shippingMethod {
|
||||||
|
id
|
||||||
|
code
|
||||||
|
name
|
||||||
|
description
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment ErrorResult on ErrorResult {
|
||||||
|
errorCode
|
||||||
|
message
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
`
|
@@ -1,6 +1,6 @@
|
|||||||
import { Cart } from '@commerce/types/cart'
|
import { Cart, CartCheckout } from '@commerce/types/cart'
|
||||||
import { ProductCard, Product } from '@commerce/types/product'
|
import { Product, ProductCard } from '@commerce/types/product'
|
||||||
import { CartFragment, SearchResultFragment,Favorite, BlogList } from '../schema'
|
import { CartFragment, Favorite, SearchResultFragment, ShippingMethod, BlogList } from '../schema'
|
||||||
|
|
||||||
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
||||||
return {
|
return {
|
||||||
@@ -11,10 +11,10 @@ export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
|||||||
price: (item.priceWithTax as any).min / 100,
|
price: (item.priceWithTax as any).min / 100,
|
||||||
currencyCode: item.currencyCode,
|
currencyCode: item.currencyCode,
|
||||||
productVariantId: item.productVariantId,
|
productVariantId: item.productVariantId,
|
||||||
productVariantName:item.productVariantName,
|
productVariantName: item.productVariantName,
|
||||||
facetValueIds: item.facetValueIds,
|
facetValueIds: item.facetValueIds,
|
||||||
collectionIds: item.collectionIds,
|
collectionIds: item.collectionIds,
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// oldPrice: item.price
|
// oldPrice: item.price
|
||||||
// discount
|
// discount
|
||||||
@@ -70,6 +70,63 @@ export function normalizeCart(order: CartFragment): Cart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeCartForCheckout(order: CartFragment): CartCheckout {
|
||||||
|
return {
|
||||||
|
id: order.id.toString(),
|
||||||
|
createdAt: order.createdAt,
|
||||||
|
taxesIncluded: true,
|
||||||
|
totalQuantity: order.totalQuantity,
|
||||||
|
lineItemsSubtotalPrice: order.subTotalWithTax / 100,
|
||||||
|
currency: { code: order.currencyCode },
|
||||||
|
subtotalPrice: order.subTotalWithTax / 100,
|
||||||
|
totalPrice: order.totalWithTax / 100,
|
||||||
|
customerId: order.customer?.id,
|
||||||
|
customer: {
|
||||||
|
firstName: order.customer?.firstName || '',
|
||||||
|
lastName: order.customer?.lastName || '',
|
||||||
|
emailAddress: order.customer?.emailAddress || '',
|
||||||
|
},
|
||||||
|
shippingAddress: {
|
||||||
|
streetLine1: order.shippingAddress?.streetLine1 || '',
|
||||||
|
city: order.shippingAddress?.city || '',
|
||||||
|
province: order.shippingAddress?.province || '',
|
||||||
|
postalCode: order.shippingAddress?.postalCode || '',
|
||||||
|
countryCode: order.shippingAddress?.countryCode || '',
|
||||||
|
phoneNumber: order.shippingAddress?.phoneNumber || '',
|
||||||
|
},
|
||||||
|
shippingLine: order.shippingLines[0] ? {
|
||||||
|
priceWithTax: order.shippingLines[0]?.priceWithTax / 100,
|
||||||
|
shippingMethod: order.shippingLines[0]?.shippingMethod as ShippingMethod
|
||||||
|
}: undefined,
|
||||||
|
totalDiscount: order.discounts?.reduce((total, item) => total + item.amountWithTax, 0) / 100 || 0,
|
||||||
|
discounts: order.discounts.map(item => {
|
||||||
|
return { value: item.amountWithTax, description: item.description }
|
||||||
|
}),
|
||||||
|
lineItems: order.lines?.map((l) => ({
|
||||||
|
id: l.id,
|
||||||
|
name: l.productVariant.name,
|
||||||
|
quantity: l.quantity,
|
||||||
|
slug: l.productVariant.product.slug,
|
||||||
|
variantId: l.productVariant.id,
|
||||||
|
productId: l.productVariant.productId,
|
||||||
|
images: [{ url: l.featuredAsset?.preview + '?preset=thumb' || '' }],
|
||||||
|
discounts: l.discounts.map((d) => ({ value: d.amount / 100 })),
|
||||||
|
path: '',
|
||||||
|
variant: {
|
||||||
|
id: l.productVariant.id,
|
||||||
|
name: l.productVariant.name,
|
||||||
|
sku: l.productVariant.sku,
|
||||||
|
price: l.discountedUnitPriceWithTax / 100,
|
||||||
|
listPrice: l.unitPriceWithTax / 100,
|
||||||
|
image: {
|
||||||
|
url: l.featuredAsset?.preview + '?preset=thumb' || '',
|
||||||
|
},
|
||||||
|
requiresShipping: true,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeProductCard(product: Product): ProductCard {
|
export function normalizeProductCard(product: Product): ProductCard {
|
||||||
return {
|
return {
|
||||||
id: product.id,
|
id: product.id,
|
||||||
@@ -79,7 +136,7 @@ export function normalizeProductCard(product: Product): ProductCard {
|
|||||||
price: product.price,
|
price: product.price,
|
||||||
currencyCode: product.currencyCode,
|
currencyCode: product.currencyCode,
|
||||||
productVariantId: product.variants?.[0].id.toString(),
|
productVariantId: product.variants?.[0].id.toString(),
|
||||||
productVariantName:product.variants?.[0].name,
|
productVariantName: product.variants?.[0].name,
|
||||||
facetValueIds: product.facetValueIds,
|
facetValueIds: product.facetValueIds,
|
||||||
collectionIds: product.collectionIds,
|
collectionIds: product.collectionIds,
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,116 @@
|
|||||||
|
export const getActiveOrderForCheckoutQuery = /* GraphQL */ `
|
||||||
|
query getActiveOrderForCheckout {
|
||||||
|
activeOrder {
|
||||||
|
...Cart
|
||||||
|
shippingAddress {
|
||||||
|
...OrderAddress
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment Cart on Order {
|
||||||
|
id
|
||||||
|
code
|
||||||
|
state
|
||||||
|
active
|
||||||
|
customer {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
emailAddress
|
||||||
|
}
|
||||||
|
lines {
|
||||||
|
id
|
||||||
|
featuredAsset {
|
||||||
|
...Asset
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
unitPrice
|
||||||
|
unitPriceWithTax
|
||||||
|
quantity
|
||||||
|
linePriceWithTax
|
||||||
|
discountedLinePriceWithTax
|
||||||
|
unitPriceWithTax
|
||||||
|
discountedUnitPriceWithTax
|
||||||
|
productVariant {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
priceWithTax
|
||||||
|
stockLevel
|
||||||
|
productId
|
||||||
|
product {
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
discounts {
|
||||||
|
amount
|
||||||
|
amountWithTax
|
||||||
|
description
|
||||||
|
adjustmentSource
|
||||||
|
type
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
totalQuantity
|
||||||
|
subTotal
|
||||||
|
subTotalWithTax
|
||||||
|
total
|
||||||
|
totalWithTax
|
||||||
|
shipping
|
||||||
|
shippingWithTax
|
||||||
|
currencyCode
|
||||||
|
shippingLines {
|
||||||
|
priceWithTax
|
||||||
|
shippingMethod {
|
||||||
|
id
|
||||||
|
code
|
||||||
|
name
|
||||||
|
description
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
discounts {
|
||||||
|
amount
|
||||||
|
amountWithTax
|
||||||
|
description
|
||||||
|
adjustmentSource
|
||||||
|
type
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment Asset on Asset {
|
||||||
|
id
|
||||||
|
width
|
||||||
|
height
|
||||||
|
name
|
||||||
|
preview
|
||||||
|
focalPoint {
|
||||||
|
x
|
||||||
|
y
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment OrderAddress on OrderAddress {
|
||||||
|
fullName
|
||||||
|
company
|
||||||
|
streetLine1
|
||||||
|
streetLine2
|
||||||
|
city
|
||||||
|
province
|
||||||
|
postalCode
|
||||||
|
country
|
||||||
|
phoneNumber
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
16
framework/vendure/utils/queries/available-countries-query.ts
Normal file
16
framework/vendure/utils/queries/available-countries-query.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const availableCountriesQuery = /* GraphQL */ `
|
||||||
|
query availableCountriesQuery {
|
||||||
|
availableCountries {
|
||||||
|
...Country
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment Country on Country {
|
||||||
|
id
|
||||||
|
code
|
||||||
|
name
|
||||||
|
enabled
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
`
|
@@ -0,0 +1,13 @@
|
|||||||
|
export const getEligibleShippingMethods = /* GraphQL */ `
|
||||||
|
query getEligibleShippingMethods {
|
||||||
|
eligibleShippingMethods {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
price
|
||||||
|
priceWithTax
|
||||||
|
metadata
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -1,7 +1,7 @@
|
|||||||
@import "../../../styles/utilities";
|
@import "../../../styles/utilities";
|
||||||
|
|
||||||
.buttonCommon {
|
.buttonCommon {
|
||||||
@apply shape-common;
|
@apply shape-common h-full;
|
||||||
&:hover {
|
&:hover {
|
||||||
.inner {
|
.inner {
|
||||||
@apply shadow-md;
|
@apply shadow-md;
|
||||||
|
@@ -1,24 +1,29 @@
|
|||||||
|
import { LineItem } from '@commerce/types/cart'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { ImgWithLink } from '..'
|
||||||
import s from "./CardItemCheckout.module.scss"
|
import s from "./CardItemCheckout.module.scss"
|
||||||
import { ProductProps } from 'src/utils/types.utils'
|
export interface CardItemCheckoutProps extends LineItem {
|
||||||
export interface CardItemCheckoutProps extends ProductProps {
|
currency: { code: string }
|
||||||
quantity:number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardItemCheckout = ({imageSrc,name,price,weight,quantity,category}: CardItemCheckoutProps) => {
|
const CardItemCheckout = ({
|
||||||
|
quantity,
|
||||||
|
variant,
|
||||||
|
name,
|
||||||
|
currency }: CardItemCheckoutProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={s.warpper}>
|
<div className={s.warpper}>
|
||||||
<div className={s.image}>
|
<div className={s.image}>
|
||||||
<img src={imageSrc} alt="image" />
|
<ImgWithLink src={variant?.image?.url ?? ''} alt={name} />
|
||||||
</div>
|
</div>
|
||||||
<div className={s.right}>
|
<div className={s.right}>
|
||||||
<div className={s.name}>
|
<div className={s.name}>
|
||||||
{`${name} (${weight})`}
|
{name} {variant?.weight ? `(${variant.weight})` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div className={s.quantity}>
|
<div className={s.quantity}>
|
||||||
Quantity:
|
Quantity:
|
||||||
<div className={s.price}>
|
<div className={s.price}>
|
||||||
{`${quantity} x ${price}`}
|
{`${quantity} x ${variant?.price} ${currency?.code}`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -74,7 +74,8 @@ const ProductCartItem = ({
|
|||||||
{discounts.length > 0 && (
|
{discounts.length > 0 && (
|
||||||
<div className={s.old}>
|
<div className={s.old}>
|
||||||
{/* <span className={s.number}>{oldPrice}</span> */}
|
{/* <span className={s.number}>{oldPrice}</span> */}
|
||||||
<LabelCommon type="discount">{discounts[0]}</LabelCommon>
|
{/* TODO: edit the value */}
|
||||||
|
<LabelCommon type="discount">{discounts[0]?.value}</LabelCommon>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={s.current}>{variant?.price} {currency?.code}</div>
|
<div className={s.current}>{variant?.price} {currency?.code}</div>
|
||||||
|
@@ -1,51 +1,52 @@
|
|||||||
.warpper{
|
.warpper {
|
||||||
padding: 2.4rem 0;
|
padding: 2.4rem 0;
|
||||||
@apply border-b border-solid border-line;
|
@apply border-b border-solid border-line;
|
||||||
.note{
|
.note {
|
||||||
|
@apply cursor-pointer;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
color: var(--text-label);
|
color: var(--text-label);
|
||||||
padding: 0 5.6rem;
|
padding: 0 5.6rem;
|
||||||
}
|
}
|
||||||
.header{
|
.header {
|
||||||
@apply flex justify-between;
|
@apply flex justify-between cursor-pointer;
|
||||||
.left{
|
.left {
|
||||||
@apply flex items-center;
|
@apply flex items-center;
|
||||||
.number{
|
.number {
|
||||||
width: 3.2rem;
|
width: 3.2rem;
|
||||||
height: 3.2rem;
|
height: 3.2rem;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
border: 1px solid var(--text-active);
|
border: 1px solid var(--text-active);
|
||||||
color: var(--text-active);
|
color: var(--text-active);
|
||||||
@apply flex justify-center items-center font-bold;
|
@apply flex justify-center items-center font-bold;
|
||||||
&.visible{
|
&.visible {
|
||||||
background-color: var(--text-active);
|
background-color: var(--text-active);
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
}
|
}
|
||||||
&.done{
|
&.done {
|
||||||
@apply border-2 border-solid border-primary;
|
@apply border-2 border-solid border-primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.title{
|
.title {
|
||||||
padding-left: 2.4rem;
|
padding-left: 2.4rem;
|
||||||
@apply font-bold select-none cursor-pointer;
|
@apply font-bold select-none;
|
||||||
color: var(--text-active);
|
color: var(--text-active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.edit{
|
.edit {
|
||||||
@apply font-bold cursor-pointer;
|
@apply font-bold cursor-pointer;
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
margin-right: 5.6rem;
|
margin-right: 5.6rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.body{
|
.body {
|
||||||
height: 0;
|
height: 0;
|
||||||
@apply overflow-hidden;
|
@apply overflow-hidden;
|
||||||
&.show{
|
&.show {
|
||||||
margin-top: 3.2rem;
|
margin-top: 3.2rem;
|
||||||
height: initial;
|
height: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { divide } from 'lodash'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { IconDoneCheckout } from 'src/components/icons'
|
import { IconDoneCheckout } from 'src/components/icons'
|
||||||
import { CheckOutForm } from 'src/utils/types.utils'
|
|
||||||
import s from './CheckoutCollapse.module.scss'
|
import s from './CheckoutCollapse.module.scss'
|
||||||
interface CheckoutCollapseProps {
|
interface CheckoutCollapseProps {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
@@ -10,10 +8,11 @@ interface CheckoutCollapseProps {
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
title: string
|
title: string
|
||||||
isEdit: boolean
|
isEdit: boolean
|
||||||
onClose?: (id:number) => void
|
onClose: (id: number) => void
|
||||||
onOpen?: (id:number) => void
|
onOpen?: (id: number) => void
|
||||||
onEditClick?:(id:number) => void
|
onEditClick?: (id: number) => void
|
||||||
note?:string
|
note?: string
|
||||||
|
disableEdit?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const CheckoutCollapse = ({
|
const CheckoutCollapse = ({
|
||||||
@@ -24,33 +23,34 @@ const CheckoutCollapse = ({
|
|||||||
visible,
|
visible,
|
||||||
note,
|
note,
|
||||||
onOpen,
|
onOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onEditClick
|
onEditClick,
|
||||||
|
disableEdit,
|
||||||
}: CheckoutCollapseProps) => {
|
}: CheckoutCollapseProps) => {
|
||||||
const handleTitleClick = () => {
|
const handleToggle = () => {
|
||||||
if(visible){
|
if (visible) {
|
||||||
onClose && onClose(id)
|
isEdit && onClose(id)
|
||||||
}else{
|
} else if (!disableEdit) {
|
||||||
onOpen && onOpen(id)
|
isEdit && onEditClick && onEditClick(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleEdit = () => {
|
const handleEdit = () => {
|
||||||
onEditClick && onEditClick(id)
|
onEditClick && onEditClick(id)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={s.warpper}>
|
<div className={s.warpper}>
|
||||||
<div className={s.header}>
|
<div className={s.header} onClick={handleToggle}>
|
||||||
<div className={s.left}>
|
<div className={s.left}>
|
||||||
<div className={classNames(s.number, { [s.visible]: visible, [s.done]:isEdit })}>
|
<div className={classNames(s.number, { [s.visible]: visible, [s.done]: isEdit })}>
|
||||||
{isEdit?<IconDoneCheckout/>:id}
|
{isEdit ? <IconDoneCheckout /> : id}
|
||||||
</div>
|
</div>
|
||||||
<div className={s.title} onClick={handleTitleClick}>
|
<div className={s.title}>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isEdit && <div className={s.edit} onClick={handleEdit}>{'Edit'}</div>}
|
{!disableEdit && isEdit && <div className={s.edit} onClick={handleEdit}>{'Edit'}</div>}
|
||||||
</div>
|
</div>
|
||||||
{(!visible && isEdit) && (<div className={s.note}>{note}</div>) }
|
{(!visible && isEdit) && (<div className={s.note} onClick={handleToggle}>{note}</div>)}
|
||||||
<div className={classNames(s.body, { [`${s.show}`]: visible })}>{children}</div>
|
<div className={classNames(s.body, { [`${s.show}`]: visible })}>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@@ -4,8 +4,11 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
> main {
|
> main {
|
||||||
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
max-width: min(100%, 1536px);
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
@apply spacing-horizontal;
|
@apply spacing-horizontal;
|
||||||
|
@@ -12,9 +12,11 @@ interface Props {
|
|||||||
visible: boolean
|
visible: boolean
|
||||||
closeModal: () => void
|
closeModal: () => void
|
||||||
mode?: '' | 'register'
|
mode?: '' | 'register'
|
||||||
|
initialEmail?: string
|
||||||
|
disableRedirect ?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModalAuthenticate = ({ visible, mode, closeModal }: Props) => {
|
const ModalAuthenticate = ({ visible, mode, closeModal, initialEmail, disableRedirect }: Props) => {
|
||||||
const [isLogin, setIsLogin] = useState<boolean>(true)
|
const [isLogin, setIsLogin] = useState<boolean>(true)
|
||||||
const { customer } = useActiveCustomer()
|
const { customer } = useActiveCustomer()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -30,9 +32,11 @@ const ModalAuthenticate = ({ visible, mode, closeModal }: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible && customer) {
|
if (visible && customer) {
|
||||||
closeModal()
|
closeModal()
|
||||||
router.push(ROUTE.ACCOUNT)
|
if (!disableRedirect) {
|
||||||
|
router.push(ROUTE.ACCOUNT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [customer, visible, closeModal, router])
|
}, [customer, visible, closeModal, router, disableRedirect])
|
||||||
|
|
||||||
const onSwitch = () => {
|
const onSwitch = () => {
|
||||||
setIsLogin(!isLogin)
|
setIsLogin(!isLogin)
|
||||||
@@ -51,7 +55,7 @@ const ModalAuthenticate = ({ visible, mode, closeModal }: Props) => {
|
|||||||
[s.register]: !isLogin,
|
[s.register]: !isLogin,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormLogin isHide={!isLogin} onSwitch={onSwitch} />
|
<FormLogin isHide={!isLogin} onSwitch={onSwitch} initialEmail={initialEmail} />
|
||||||
<FormRegister isHide={isLogin} onSwitch={onSwitch} />
|
<FormRegister isHide={isLogin} onSwitch={onSwitch} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@@ -15,6 +15,8 @@ import styles from './FormLogin.module.scss'
|
|||||||
interface Props {
|
interface Props {
|
||||||
isHide: boolean
|
isHide: boolean
|
||||||
onSwitch: () => void
|
onSwitch: () => void
|
||||||
|
initialEmail?: string
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayingErrorMessagesSchema = Yup.object().shape({
|
const displayingErrorMessagesSchema = Yup.object().shape({
|
||||||
@@ -24,7 +26,7 @@ const displayingErrorMessagesSchema = Yup.object().shape({
|
|||||||
.required(LANGUAGE.MESSAGE.REQUIRED),
|
.required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
})
|
})
|
||||||
|
|
||||||
const FormLogin = ({ onSwitch, isHide }: Props) => {
|
const FormLogin = ({ onSwitch, isHide, initialEmail = ''}: Props) => {
|
||||||
const emailRef = useRef<CustomInputCommon>(null)
|
const emailRef = useRef<CustomInputCommon>(null)
|
||||||
const { loading, login } = useLogin()
|
const { loading, login } = useLogin()
|
||||||
const { showMessageSuccess, showMessageError } = useMessage()
|
const { showMessageSuccess, showMessageError } = useMessage()
|
||||||
@@ -54,7 +56,7 @@ const FormLogin = ({ onSwitch, isHide }: Props) => {
|
|||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
password: '',
|
password: '',
|
||||||
email: '',
|
email: initialEmail,
|
||||||
}}
|
}}
|
||||||
validationSchema={displayingErrorMessagesSchema}
|
validationSchema={displayingErrorMessagesSchema}
|
||||||
onSubmit={onLogin}
|
onSubmit={onLogin}
|
||||||
|
@@ -5,7 +5,7 @@ import s from './ModalConfirm.module.scss'
|
|||||||
interface ModalConfirmProps extends ModalCommonProps {
|
interface ModalConfirmProps extends ModalCommonProps {
|
||||||
okText?: String
|
okText?: String
|
||||||
cancelText?: String
|
cancelText?: String
|
||||||
loading?:boolean
|
loading?: boolean
|
||||||
onOk?: () => void
|
onOk?: () => void
|
||||||
onCancel?: () => void
|
onCancel?: () => void
|
||||||
}
|
}
|
||||||
@@ -18,16 +18,17 @@ const ModalConfirm = ({
|
|||||||
children,
|
children,
|
||||||
title = 'Confirm',
|
title = 'Confirm',
|
||||||
loading,
|
loading,
|
||||||
|
onClose,
|
||||||
...props
|
...props
|
||||||
}: ModalConfirmProps) => {
|
}: ModalConfirmProps) => {
|
||||||
return (
|
return (
|
||||||
<ModalCommon {...props} title={title}>
|
<ModalCommon onClose={onClose} title={title} {...props}>
|
||||||
{children}
|
{children}
|
||||||
<div className={s.footer}>
|
<div className={s.footer}>
|
||||||
<div className="mr-4">
|
<div className="mr-4">
|
||||||
<ButtonCommon onClick={onCancel} type="light"> {cancelText}</ButtonCommon>
|
<ButtonCommon onClick={onCancel || onClose} type="light" size="small"> {cancelText}</ButtonCommon>
|
||||||
</div>
|
</div>
|
||||||
<ButtonCommon onClick={onOk} loading={loading}>{okText}</ButtonCommon>
|
<ButtonCommon onClick={onOk} loading={loading} size="small">{okText}</ButtonCommon>
|
||||||
</div>
|
</div>
|
||||||
</ModalCommon>
|
</ModalCommon>
|
||||||
)
|
)
|
||||||
|
@@ -0,0 +1,19 @@
|
|||||||
|
@import "../../../styles/form";
|
||||||
|
|
||||||
|
.inputWrap {
|
||||||
|
@extend .formInputWrap;
|
||||||
|
.inputInner {
|
||||||
|
select {
|
||||||
|
@apply w-full;
|
||||||
|
background-color: var(--white);
|
||||||
|
padding: 1.6rem 1.6rem;
|
||||||
|
border: 1px solid var(--border-line);
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
@apply shadow-md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
106
src/components/common/SelectFieldInForm/SelectFieldInForm.tsx
Normal file
106
src/components/common/SelectFieldInForm/SelectFieldInForm.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import classNames from "classnames"
|
||||||
|
import { Field } from "formik"
|
||||||
|
import { useMemo } from "react"
|
||||||
|
import { IconCheck, IconError } from "src/components/icons"
|
||||||
|
import s from './SelectFieldInForm.module.scss'
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
placeholder?: string
|
||||||
|
styleType?: 'default' | 'custom'
|
||||||
|
backgroundTransparent?: boolean
|
||||||
|
icon?: React.ReactNode
|
||||||
|
isIconSuffix?: boolean
|
||||||
|
isShowIconSuccess?: boolean
|
||||||
|
name: string
|
||||||
|
error?: string
|
||||||
|
options: any[]
|
||||||
|
keyNameOption?: string[]
|
||||||
|
keyValueOption?: string
|
||||||
|
nameSeperator?: string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelectFieldInForm = ({
|
||||||
|
name,
|
||||||
|
placeholder,
|
||||||
|
options,
|
||||||
|
styleType = 'default',
|
||||||
|
icon,
|
||||||
|
backgroundTransparent = false,
|
||||||
|
isIconSuffix = true,
|
||||||
|
isShowIconSuccess,
|
||||||
|
error,
|
||||||
|
keyNameOption = ['name'],
|
||||||
|
keyValueOption = 'value',
|
||||||
|
nameSeperator = " ",
|
||||||
|
|
||||||
|
}: Props) => {
|
||||||
|
const iconElement = useMemo(() => {
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<span className={s.icon}>
|
||||||
|
<IconError />{' '}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
} else if (isShowIconSuccess) {
|
||||||
|
return (
|
||||||
|
<span className={s.icon}>
|
||||||
|
<IconCheck />{' '}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
} else if (icon) {
|
||||||
|
return <span className={s.icon}>{icon} </span>
|
||||||
|
}
|
||||||
|
return <></>
|
||||||
|
}, [icon, error, isShowIconSuccess])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames({
|
||||||
|
[s.inputWrap]: true,
|
||||||
|
[s[styleType]]: true,
|
||||||
|
[s.bgTransparent]: backgroundTransparent,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classNames({
|
||||||
|
[s.inputInner]: true,
|
||||||
|
[s.preserve]: isIconSuffix,
|
||||||
|
[s.success]: isShowIconSuccess,
|
||||||
|
[s.error]: !!error,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{iconElement}
|
||||||
|
<Field
|
||||||
|
as="select"
|
||||||
|
name={name}
|
||||||
|
placeholder={placeholder}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
options.map((item) => {
|
||||||
|
let name = ''
|
||||||
|
keyNameOption.map((key) => {
|
||||||
|
if (name) {
|
||||||
|
name += nameSeperator
|
||||||
|
}
|
||||||
|
name += item[key]
|
||||||
|
})
|
||||||
|
name = name.trim()
|
||||||
|
return <option
|
||||||
|
key={item[keyValueOption]}
|
||||||
|
value={item[keyValueOption]}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</option>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{error && <div className={s.errorMessage}>{error}</div>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectFieldInForm
|
@@ -50,6 +50,7 @@ export { default as RecommendedRecipes} from './RecommendedRecipes/RecommendedRe
|
|||||||
export { default as LayoutCheckout} from './LayoutCheckout/LayoutCheckout'
|
export { default as LayoutCheckout} from './LayoutCheckout/LayoutCheckout'
|
||||||
export { default as InputPasswordFiledInForm} from './InputPasswordFiledInForm/InputPasswordFiledInForm'
|
export { default as InputPasswordFiledInForm} from './InputPasswordFiledInForm/InputPasswordFiledInForm'
|
||||||
export { default as InputFiledInForm} from './InputFiledInForm/InputFiledInForm'
|
export { default as InputFiledInForm} from './InputFiledInForm/InputFiledInForm'
|
||||||
|
export { default as SelectFieldInForm} from './SelectFieldInForm/SelectFieldInForm'
|
||||||
export { default as MessageCommon} from './MessageCommon/MessageCommon'
|
export { default as MessageCommon} from './MessageCommon/MessageCommon'
|
||||||
export { default as FormForgot} from './ForgotPassword/FormForgot/FormForgot'
|
export { default as FormForgot} from './ForgotPassword/FormForgot/FormForgot'
|
||||||
export { default as FormResetPassword} from './ForgotPassword/FormResetPassword/FormResetPassword'
|
export { default as FormResetPassword} from './ForgotPassword/FormResetPassword/FormResetPassword'
|
||||||
|
@@ -6,6 +6,7 @@ import { LoginMutation } from '@framework/schema'
|
|||||||
import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils'
|
import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils'
|
||||||
import { errorMapping } from 'src/utils/errrorMapping'
|
import { errorMapping } from 'src/utils/errrorMapping'
|
||||||
import { loginMutation } from '@framework/utils/mutations/log-in-mutation'
|
import { loginMutation } from '@framework/utils/mutations/log-in-mutation'
|
||||||
|
import { useGetActiveOrder } from '../cart'
|
||||||
|
|
||||||
interface LoginInput {
|
interface LoginInput {
|
||||||
username: string
|
username: string
|
||||||
@@ -16,6 +17,7 @@ const useLogin = () => {
|
|||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState<CommonError | null>(null)
|
const [error, setError] = useState<CommonError | null>(null)
|
||||||
const { mutate } = useActiveCustomer()
|
const { mutate } = useActiveCustomer()
|
||||||
|
const { mutate: mutateOrder } = useGetActiveOrder()
|
||||||
|
|
||||||
const login = (options: LoginInput,
|
const login = (options: LoginInput,
|
||||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||||
@@ -34,6 +36,7 @@ const useLogin = () => {
|
|||||||
if (authToken != null) {
|
if (authToken != null) {
|
||||||
localStorage.setItem(LOCAL_STORAGE_KEY.TOKEN, authToken)
|
localStorage.setItem(LOCAL_STORAGE_KEY.TOKEN, authToken)
|
||||||
mutate()
|
mutate()
|
||||||
|
mutateOrder()
|
||||||
}
|
}
|
||||||
fCallBack(true)
|
fCallBack(true)
|
||||||
})
|
})
|
||||||
|
@@ -15,8 +15,7 @@ const query = gql`
|
|||||||
|
|
||||||
const useGetActiveOrder = () => {
|
const useGetActiveOrder = () => {
|
||||||
const { data, ...rest } = useSWR<ActiveOrderQuery>([query], gglFetcher)
|
const { data, ...rest } = useSWR<ActiveOrderQuery>([query], gglFetcher)
|
||||||
return { order: data?.activeOrder ? normalizeCart(data!.activeOrder) : null, ...rest }
|
return { order: data?.activeOrder ? normalizeCart(data!.activeOrder) : null, ...rest }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useGetActiveOrder
|
export default useGetActiveOrder
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
|
import { RemoveOrderLineMutation, RemoveOrderLineMutationVariables } from '@framework/schema'
|
||||||
|
import { removeOrderLineMutation } from '@framework/utils/mutations/remove-order-line-mutation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
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 { errorMapping } from 'src/utils/errrorMapping'
|
||||||
|
import rawFetcher from 'src/utils/rawFetcher'
|
||||||
import { useGetActiveOrder } from '.'
|
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 useRemoveProductInCart = () => {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
@@ -1,2 +1,8 @@
|
|||||||
export { default as useSetCustomerForOrder } from './useSetCustomerForOrder'
|
export { default as useSetCustomerForOrder } from './useSetCustomerForOrder'
|
||||||
|
export { default as useSetOrderShippingAddress } from './useSetOrderShippingAddress'
|
||||||
|
export { default as useApplyCouponCode } from './useApplyCouponCode'
|
||||||
|
export { default as useAvailableCountries } from './useAvailableCountries'
|
||||||
|
export { default as useSetOrderShippingMethod } from './useSetOrderShippingMethod'
|
||||||
|
export { default as useGetActiveOrderForCheckout } from './useGetActiveOrderForCheckout'
|
||||||
|
export { default as useEligibleShippingMethods } from './useEligibleShippingMethods'
|
||||||
|
|
||||||
|
41
src/components/hooks/order/useApplyCouponCode.tsx
Normal file
41
src/components/hooks/order/useApplyCouponCode.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { ApplyCouponCodeMutation } from '@framework/schema'
|
||||||
|
import { applyCouponCodeMutation } from '@framework/utils/mutations/apply-coupon-code-mutation'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||||
|
import rawFetcher from 'src/utils/rawFetcher'
|
||||||
|
import { useGetActiveOrderForCheckout } from '.'
|
||||||
|
|
||||||
|
|
||||||
|
const useApplyCouponCode = () => {
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [error, setError] = useState<CommonError | null>(null)
|
||||||
|
const { mutate } = useGetActiveOrderForCheckout()
|
||||||
|
|
||||||
|
const applyCouponCode = (couponCode: string,
|
||||||
|
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||||
|
) => {
|
||||||
|
setError(null)
|
||||||
|
setLoading(true)
|
||||||
|
rawFetcher<ApplyCouponCodeMutation>({
|
||||||
|
query: applyCouponCodeMutation,
|
||||||
|
variables: { couponCode },
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
if (data.applyCouponCode.__typename === 'Order') {
|
||||||
|
fCallBack(true)
|
||||||
|
mutate()
|
||||||
|
} else {
|
||||||
|
fCallBack(false, data.applyCouponCode.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setError(error)
|
||||||
|
fCallBack(false, error.message)
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
return { loading, applyCouponCode, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useApplyCouponCode
|
14
src/components/hooks/order/useAvailableCountries.tsx
Normal file
14
src/components/hooks/order/useAvailableCountries.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { GetAvailableCountriesQuery } from '@framework/schema'
|
||||||
|
import { availableCountriesQuery } from '@framework/utils/queries/available-countries-query'
|
||||||
|
import gglFetcher from 'src/utils/gglFetcher'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
const useAvailableCountries = () => {
|
||||||
|
const { data, isValidating } = useSWR<GetAvailableCountriesQuery>([availableCountriesQuery], gglFetcher)
|
||||||
|
return {
|
||||||
|
countries: data?.availableCountries,
|
||||||
|
loading: isValidating,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useAvailableCountries
|
14
src/components/hooks/order/useEligibleShippingMethods.tsx
Normal file
14
src/components/hooks/order/useEligibleShippingMethods.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { GetEligibleMethodsQuery, ShippingMethodQuote } from '@framework/schema'
|
||||||
|
import { getEligibleShippingMethods } from '@framework/utils/queries/eligible-shipping-methods-query'
|
||||||
|
import gglFetcher from 'src/utils/gglFetcher'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
const useEligibleShippingMethods = () => {
|
||||||
|
const { data, isValidating } = useSWR<GetEligibleMethodsQuery>([getEligibleShippingMethods], gglFetcher)
|
||||||
|
return {
|
||||||
|
eligibleShippingMethods: data?.eligibleShippingMethods as ShippingMethodQuote[],
|
||||||
|
loading: isValidating,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useEligibleShippingMethods
|
13
src/components/hooks/order/useGetActiveOrderForCheckout.tsx
Normal file
13
src/components/hooks/order/useGetActiveOrderForCheckout.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { ActiveOrderQuery } from '@framework/schema'
|
||||||
|
import { normalizeCartForCheckout } from '@framework/utils/normalize'
|
||||||
|
import { getActiveOrderForCheckoutQuery } from '@framework/utils/queries/active-order-for-checkout-query'
|
||||||
|
import gglFetcher from 'src/utils/gglFetcher'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
|
||||||
|
const useGetActiveOrderForCheckout = () => {
|
||||||
|
const { data, ...rest } = useSWR<ActiveOrderQuery>([getActiveOrderForCheckoutQuery], gglFetcher)
|
||||||
|
return { order: data?.activeOrder ? normalizeCartForCheckout(data!.activeOrder) : null, ...rest }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useGetActiveOrderForCheckout
|
@@ -3,16 +3,16 @@ import { setCustomerForOrderMutation } from '@framework/utils/mutations/set-cust
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { CommonError } from 'src/domains/interfaces/CommonError'
|
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||||
import rawFetcher from 'src/utils/rawFetcher'
|
import rawFetcher from 'src/utils/rawFetcher'
|
||||||
import { useGetActiveOrder } from '../cart'
|
import { useGetActiveOrderForCheckout } from '.'
|
||||||
|
|
||||||
|
|
||||||
const useSetCustomerForOrder = () => {
|
const useSetCustomerForOrder = () => {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState<CommonError | null>(null)
|
const [error, setError] = useState<CommonError | null>(null)
|
||||||
const { mutate } = useGetActiveOrder()
|
const { mutate } = useGetActiveOrderForCheckout()
|
||||||
|
|
||||||
const setCustomerForOrder = (input: CreateCustomerInput,
|
const setCustomerForOrder = (input: CreateCustomerInput,
|
||||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
fCallBack: (isSuccess: boolean, message?: CommonError) => void
|
||||||
) => {
|
) => {
|
||||||
setError(null)
|
setError(null)
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -21,17 +21,17 @@ const useSetCustomerForOrder = () => {
|
|||||||
variables: { input },
|
variables: { input },
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
if (data.setCustomerForOrder.__typename === 'ActiveOrderCustomerFragment') {
|
if (data.setCustomerForOrder.__typename === 'Order') {
|
||||||
fCallBack(true)
|
fCallBack(true)
|
||||||
mutate()
|
mutate()
|
||||||
} else {
|
} else {
|
||||||
fCallBack(false, data.setCustomerForOrder.message)
|
fCallBack(false, data.setCustomerForOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setError(error)
|
setError(error)
|
||||||
fCallBack(false, error.message)
|
fCallBack(false, error)
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
}
|
}
|
||||||
|
42
src/components/hooks/order/useSetOrderShippingAddress.tsx
Normal file
42
src/components/hooks/order/useSetOrderShippingAddress.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { CreateAddressInput, SetOrderShippingAddressMutation } from '@framework/schema'
|
||||||
|
import { setOrderShippingAddressMutation } from '@framework/utils/mutations/set-order-shipping-address-mutation'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||||
|
import rawFetcher from 'src/utils/rawFetcher'
|
||||||
|
import { useGetActiveOrderForCheckout } from '.'
|
||||||
|
|
||||||
|
|
||||||
|
const useSetOrderShippingAddress = () => {
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [error, setError] = useState<CommonError | null>(null)
|
||||||
|
const { mutate } = useGetActiveOrderForCheckout()
|
||||||
|
|
||||||
|
const setOrderShippingAddress = (input: CreateAddressInput,
|
||||||
|
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||||
|
) => {
|
||||||
|
setError(null)
|
||||||
|
setLoading(true)
|
||||||
|
rawFetcher<SetOrderShippingAddressMutation>({
|
||||||
|
query: setOrderShippingAddressMutation,
|
||||||
|
variables: { input },
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
if (data.setOrderShippingAddress.__typename === 'Order') {
|
||||||
|
fCallBack(true)
|
||||||
|
mutate()
|
||||||
|
} else {
|
||||||
|
fCallBack(false, data.setOrderShippingAddress.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setError(error)
|
||||||
|
fCallBack(false, error.message)
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
return { loading, setOrderShippingAddress, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useSetOrderShippingAddress
|
41
src/components/hooks/order/useSetOrderShippingMethod.tsx
Normal file
41
src/components/hooks/order/useSetOrderShippingMethod.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { SetShippingMethodMutation } from '@framework/schema'
|
||||||
|
import { setShippingMethodMutation } from '@framework/utils/mutations/set-order-shipping-method-mutation'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||||
|
import rawFetcher from 'src/utils/rawFetcher'
|
||||||
|
import { useGetActiveOrderForCheckout } from '.'
|
||||||
|
|
||||||
|
|
||||||
|
const useSetOrderShippingMethod = () => {
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [error, setError] = useState<CommonError | null>(null)
|
||||||
|
const { mutate } = useGetActiveOrderForCheckout()
|
||||||
|
|
||||||
|
const setOrderShippingMethod = (id: string,
|
||||||
|
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||||
|
) => {
|
||||||
|
setError(null)
|
||||||
|
setLoading(true)
|
||||||
|
rawFetcher<SetShippingMethodMutation>({
|
||||||
|
query: setShippingMethodMutation,
|
||||||
|
variables: { id },
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
if (data.setOrderShippingMethod.__typename === 'Order') {
|
||||||
|
fCallBack(true)
|
||||||
|
mutate()
|
||||||
|
} else {
|
||||||
|
fCallBack(false, data.setOrderShippingMethod.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setError(error)
|
||||||
|
fCallBack(false, error.message)
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
return { loading, setOrderShippingMethod, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useSetOrderShippingMethod
|
@@ -1,6 +1,7 @@
|
|||||||
.warpper {
|
.warpper {
|
||||||
padding: 3.2rem;
|
padding: 3.2rem;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
@screen lg {
|
@screen lg {
|
||||||
max-width: 56.3rem;
|
max-width: 56.3rem;
|
||||||
@apply flex justify-between flex-col;
|
@apply flex justify-between flex-col;
|
||||||
@@ -14,15 +15,14 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.empty {
|
||||||
|
@apply flex flex-col justify-center items-center;
|
||||||
|
}
|
||||||
.list {
|
.list {
|
||||||
min-height: 52.8rem;
|
// min-height: 52.8rem;
|
||||||
}
|
}
|
||||||
.bot {
|
.bot {
|
||||||
.promo {
|
margin-top: auto;
|
||||||
// padding: 3.2rem;
|
|
||||||
@apply bg-gray flex justify-between items-center;
|
|
||||||
min-height: 6.4rem;
|
|
||||||
}
|
|
||||||
.price {
|
.price {
|
||||||
margin-top: 3.2rem;
|
margin-top: 3.2rem;
|
||||||
.line {
|
.line {
|
||||||
|
@@ -1,41 +1,59 @@
|
|||||||
|
import { CartCheckout } from '@commerce/types/cart'
|
||||||
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { ROUTE } from 'src/utils/constanst.utils'
|
||||||
|
import { LANGUAGE } from 'src/utils/language.utils'
|
||||||
|
import { ButtonCommon, CardItemCheckout, EmptyCommon } from '../../../common'
|
||||||
import s from './CheckoutBill.module.scss'
|
import s from './CheckoutBill.module.scss'
|
||||||
import { CardItemCheckout } from '../../../common'
|
import FormPromotionCode from './FormPromotionCode/FormPromotionCode'
|
||||||
import { CardItemCheckoutProps } from '../../../common/CardItemCheckout/CardItemCheckout'
|
|
||||||
import { IconCirclePlus } from 'src/components/icons'
|
|
||||||
|
|
||||||
interface CheckoutBillProps {
|
interface CheckoutBillProps {
|
||||||
data: CardItemCheckoutProps[]
|
data: CartCheckout | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const CheckoutBill = ({ data }: CheckoutBillProps) => {
|
const CheckoutBill = ({ data }: CheckoutBillProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={s.warpper}>
|
<div className={s.warpper}>
|
||||||
<div className = {s.title}>
|
<div className={s.title}>
|
||||||
Your cart ({data.length})
|
Your cart ({data?.totalQuantity || 0})
|
||||||
</div>
|
</div>
|
||||||
|
{
|
||||||
|
!data?.totalQuantity && <div className={s.empty}>
|
||||||
|
<EmptyCommon description="Your cart is empty" />
|
||||||
|
<Link href={ROUTE.HOME}>
|
||||||
|
<a>
|
||||||
|
<ButtonCommon>{LANGUAGE.BUTTON_LABEL.SHOP_NOW}</ButtonCommon>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div className={s.list}>
|
<div className={s.list}>
|
||||||
{data.map((item) => {
|
{data?.lineItems?.map((item) => {
|
||||||
return <CardItemCheckout {...item} key={item.slug} />
|
return <CardItemCheckout {...item} key={item.slug} currency={data?.currency} />
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className={s.bot}>
|
<div className={s.bot}>
|
||||||
<div className={s.promo}>
|
<FormPromotionCode />
|
||||||
Apply Promotion Code
|
|
||||||
<IconCirclePlus />
|
|
||||||
</div>
|
|
||||||
<div className={s.price}>
|
<div className={s.price}>
|
||||||
|
<div className={s.line}>
|
||||||
|
Discount {(data?.discounts?.length || 0) > 0 && `(${data?.discounts?.map(item => item.description).join(",")})`}
|
||||||
|
<div className={s.shipping}>
|
||||||
|
{data?.totalDiscount || 0} {data?.currency?.code}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className={s.line}>
|
<div className={s.line}>
|
||||||
Subtotal
|
Subtotal
|
||||||
<div className={s.subTotal}>RP 120.500</div>
|
<div className={s.subTotal}>
|
||||||
|
{data?.subtotalPrice || 0} {data?.currency?.code}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.line}>
|
<div className={s.line}>
|
||||||
Shipping
|
Shipping
|
||||||
<div className={s.shipping}>Free</div>
|
<div className={s.shipping}>{data?.shippingLine?.priceWithTax || 0} {data?.currency?.code}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.line}>
|
<div className={s.line}>
|
||||||
Estimated Total
|
Estimated Total
|
||||||
<div className={s.total}>RP 120.500</div>
|
<div className={s.total}>{data?.totalPrice || 0} {data?.currency?.code}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,19 @@
|
|||||||
|
.promo {
|
||||||
|
@apply bg-gray flex justify-between items-center;
|
||||||
|
min-height: 6.4rem;
|
||||||
|
.modalPromotion {
|
||||||
|
min-width: 40rem;
|
||||||
|
.bottom {
|
||||||
|
@apply flex justify-end items-center;
|
||||||
|
margin-top: 3.2rem;
|
||||||
|
button {
|
||||||
|
&:first-child {
|
||||||
|
margin-right: 0.8rem;
|
||||||
|
@screen md {
|
||||||
|
margin-right: 3.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,97 @@
|
|||||||
|
import { Form, Formik } from 'formik';
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { ButtonCommon, InputFiledInForm, ModalCommon } from 'src/components/common';
|
||||||
|
import { useMessage } from 'src/components/contexts';
|
||||||
|
import { useModalCommon } from 'src/components/hooks';
|
||||||
|
import { useApplyCouponCode } from 'src/components/hooks/order';
|
||||||
|
import { IconCirclePlus } from 'src/components/icons';
|
||||||
|
import { LANGUAGE } from 'src/utils/language.utils';
|
||||||
|
import { CustomInputCommon } from 'src/utils/type.utils';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import s from './FormPromotionCode.module.scss';
|
||||||
|
|
||||||
|
const displayingErrorMessagesSchema = Yup.object().shape({
|
||||||
|
couponCode: Yup.string().required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
|
})
|
||||||
|
|
||||||
|
const FormPromotionCode = () => {
|
||||||
|
const { visible, openModal, closeModal } = useModalCommon({ initialValue: false })
|
||||||
|
const { showMessageError, showMessageSuccess } = useMessage()
|
||||||
|
const { applyCouponCode, loading } = useApplyCouponCode()
|
||||||
|
const inputRef = useRef<CustomInputCommon>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (visible) {
|
||||||
|
inputRef.current?.focus()
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}, [visible])
|
||||||
|
|
||||||
|
const handleSubmit = (values: { couponCode: string }) => {
|
||||||
|
applyCouponCode(values.couponCode, onSubmitCalBack)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
|
||||||
|
// TODO:
|
||||||
|
if (isSuccess) {
|
||||||
|
showMessageSuccess("Applied coupon code successfully.", 5000)
|
||||||
|
closeModal()
|
||||||
|
} else {
|
||||||
|
showMessageError(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={s.promo}>
|
||||||
|
Apply Promotion Code
|
||||||
|
<button className={s.buttonAdd} onClick={openModal}>
|
||||||
|
<IconCirclePlus />
|
||||||
|
</button>
|
||||||
|
<ModalCommon
|
||||||
|
visible={visible}
|
||||||
|
onClose={closeModal}
|
||||||
|
>
|
||||||
|
<div className={s.modalPromotion}>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
couponCode: ''
|
||||||
|
}}
|
||||||
|
validationSchema={displayingErrorMessagesSchema}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
{({ errors, touched, isValid, submitForm }) => (
|
||||||
|
<Form className="u-form">
|
||||||
|
<div className="body">
|
||||||
|
|
||||||
|
<InputFiledInForm
|
||||||
|
name="couponCode"
|
||||||
|
placeholder="Promotion code"
|
||||||
|
error={
|
||||||
|
touched.couponCode && errors.couponCode
|
||||||
|
? errors.couponCode.toString()
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
isShowIconSuccess={touched.couponCode && !errors.couponCode}
|
||||||
|
onEnter={isValid ? submitForm : undefined}
|
||||||
|
ref={inputRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={s.bottom}>
|
||||||
|
<ButtonCommon disabled={loading} onClick={closeModal} type='light' size='small'>
|
||||||
|
{LANGUAGE.BUTTON_LABEL.CANCEL}
|
||||||
|
</ButtonCommon>
|
||||||
|
<ButtonCommon HTMLType='submit' loading={loading} size='small'>
|
||||||
|
Apply promotion code
|
||||||
|
</ButtonCommon>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</div>
|
||||||
|
</ModalCommon>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormPromotionCode;
|
@@ -1,51 +1,100 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { ButtonCommon, Logo } from 'src/components/common'
|
import { Logo } from 'src/components/common'
|
||||||
import CheckoutCollapse from 'src/components/common/CheckoutCollapse/CheckoutCollapse'
|
import CheckoutCollapse from 'src/components/common/CheckoutCollapse/CheckoutCollapse'
|
||||||
import { useAddProductToCart, useGetActiveOrder } from 'src/components/hooks/cart'
|
import { useActiveCustomer } from 'src/components/hooks/auth'
|
||||||
import { removeItem } from 'src/utils/funtion.utils'
|
import { useGetActiveOrderForCheckout } from 'src/components/hooks/order'
|
||||||
import { CheckOutForm } from 'src/utils/types.utils'
|
|
||||||
import s from './CheckoutInfo.module.scss'
|
import s from './CheckoutInfo.module.scss'
|
||||||
import CustomerInfoForm from './components/CustomerInfoForm/CustomerInfoForm'
|
import CustomerInfoForm from './components/CustomerInfoForm/CustomerInfoForm'
|
||||||
import PaymentInfoForm from './components/PaymentInfoForm/PaymentInfoForm'
|
import PaymentInfoForm from './components/PaymentInfoForm/PaymentInfoForm'
|
||||||
import ShippingInfoForm from './components/ShippingInfoForm/ShippingInfoForm'
|
import ShippingInfoForm from './components/ShippingInfoForm/ShippingInfoForm'
|
||||||
|
import ShippingMethod from './components/ShippingMethod/ShippingMethod'
|
||||||
interface CheckoutInfoProps {
|
interface CheckoutInfoProps {
|
||||||
onViewCart: () => void
|
onViewCart: () => void
|
||||||
|
currency?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
|
export enum CheckoutStep {
|
||||||
const [active, setActive] = useState(1)
|
CustomerInfo = 1,
|
||||||
const [done, setDone] = useState<number[]>([])
|
ShippingAddressInfo = 2,
|
||||||
const [info, setInfo] = useState<CheckOutForm>({})
|
ShippingMethodInfo = 3,
|
||||||
|
PaymentInfo = 4,
|
||||||
|
}
|
||||||
|
|
||||||
const onEdit = (id: number) => {
|
const CheckoutInfo = ({ onViewCart, currency = "" }: CheckoutInfoProps) => {
|
||||||
setActive(id)
|
const [activeStep, setActiveStep] = useState(1)
|
||||||
setDone(removeItem<number>(done, id))
|
const [doneSteps, setDoneSteps] = useState<CheckoutStep[]>([])
|
||||||
}
|
const { order } = useGetActiveOrderForCheckout()
|
||||||
|
const { customer } = useActiveCustomer()
|
||||||
|
|
||||||
const onConfirm = (id: number, formInfo: CheckOutForm) => {
|
useEffect(() => {
|
||||||
if (id + 1 > formList.length) {
|
if (customer) {
|
||||||
console.log({ ...info, ...formInfo })
|
if (!doneSteps.includes(CheckoutStep.CustomerInfo)) {
|
||||||
} else {
|
|
||||||
if (done.length > 0) {
|
if (doneSteps.length > 0) {
|
||||||
for (let i = id + 1; i <= formList.length; i++) {
|
for (let i = CheckoutStep.CustomerInfo + 1; i <= Object.keys(CheckoutStep).length; i++) {
|
||||||
if (!done.includes(i)) {
|
if (!doneSteps.includes(i)) {
|
||||||
setActive(i)
|
setActiveStep(i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setActiveStep(CheckoutStep.CustomerInfo + 1)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
setActive(id + 1)
|
setDoneSteps([...doneSteps, CheckoutStep.CustomerInfo])
|
||||||
}
|
}
|
||||||
setDone([...done, id])
|
|
||||||
}
|
}
|
||||||
setInfo({ ...info, ...formInfo })
|
}, [customer, doneSteps])
|
||||||
|
|
||||||
|
|
||||||
|
const onEdit = (id: CheckoutStep) => {
|
||||||
|
setActiveStep(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNote = (id: number) => {
|
const updateActiveStep = (step: CheckoutStep) => {
|
||||||
|
if (doneSteps.length > 0) {
|
||||||
|
for (let i = step + 1; i < Object.keys(CheckoutStep).length; i++) {
|
||||||
|
if (!doneSteps.includes(i)) {
|
||||||
|
setActiveStep(i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setActiveStep(step + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onConfirm = (step: CheckoutStep) => {
|
||||||
|
if (step + 1 > formList.length) {
|
||||||
|
// TODO: checkout
|
||||||
|
console.log("finish: ", order)
|
||||||
|
} else {
|
||||||
|
updateActiveStep(step)
|
||||||
|
setDoneSteps([...doneSteps, step])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const getNote = (id: CheckoutStep) => {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case 1:
|
case CheckoutStep.CustomerInfo:
|
||||||
return `${info.name}, ${info.email}`
|
if (order?.customer?.emailAddress) {
|
||||||
case 2:
|
return `${order?.customer?.firstName} ${order?.customer?.lastName}, ${order?.customer?.emailAddress}`
|
||||||
return `${info.address}, ${info.state}, ${info.city}, ${info.code}, ${info.phone}, `
|
} else if (customer) {
|
||||||
|
return `${customer.firstName} ${customer.lastName}, ${customer.emailAddress}`
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
case CheckoutStep.ShippingAddressInfo:
|
||||||
|
if (order?.shippingAddress) {
|
||||||
|
const { streetLine1, city, province, postalCode, countryCode, phoneNumber } = order.shippingAddress
|
||||||
|
return `${streetLine1}, ${city}, ${province}, ${postalCode}, ${countryCode}, ${phoneNumber}`
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
case CheckoutStep.ShippingMethodInfo:
|
||||||
|
if (order?.shippingLine) {
|
||||||
|
return `${order?.shippingLine.shippingMethod.name}, ${order?.shippingLine.priceWithTax ? `${order?.shippingLine.priceWithTax} ${currency}` : 'Free'}` || ''
|
||||||
|
}
|
||||||
|
return ''
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -53,39 +102,29 @@ const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
|
|||||||
|
|
||||||
const formList = [
|
const formList = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: CheckoutStep.CustomerInfo,
|
||||||
title: 'Customer Information',
|
title: 'Customer Information',
|
||||||
form: <CustomerInfoForm onConfirm={onConfirm} id={1} />,
|
form: <CustomerInfoForm onConfirm={onConfirm} id={CheckoutStep.CustomerInfo} activeStep={activeStep} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: CheckoutStep.ShippingAddressInfo,
|
||||||
title: 'Shipping Information',
|
title: 'Shipping Address Information',
|
||||||
form: <ShippingInfoForm onConfirm={onConfirm} id={2} />,
|
form: <ShippingInfoForm onConfirm={onConfirm} id={CheckoutStep.ShippingAddressInfo} activeStep={activeStep} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: CheckoutStep.ShippingMethodInfo,
|
||||||
|
title: 'Shipping Method Information',
|
||||||
|
form: <ShippingMethod onConfirm={onConfirm} currency={currency} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: CheckoutStep.PaymentInfo,
|
||||||
title: 'Payment Information',
|
title: 'Payment Information',
|
||||||
form: <PaymentInfoForm onConfirm={onConfirm} id={3} />,
|
form: <PaymentInfoForm onConfirm={onConfirm} id={CheckoutStep.PaymentInfo} />,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
const { addProduct } = useAddProductToCart()
|
|
||||||
|
|
||||||
const createOrder = () => {
|
|
||||||
addProduct({ variantId: "63", quantity: 1 }, handleAddToCartCallback)
|
|
||||||
}
|
|
||||||
const handleAddToCartCallback = (isSuccess: boolean, message?: string) => {
|
|
||||||
// console.log("after create order: ", isSuccess, message)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const {order} = useGetActiveOrder()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={s.warpper}>
|
<div className={s.warpper}>
|
||||||
<ButtonCommon onClick={createOrder}>test create order</ButtonCommon>
|
|
||||||
<ButtonCommon onClick={createOrder}>test get active order</ButtonCommon>
|
|
||||||
<div className={s.title}>
|
<div className={s.title}>
|
||||||
<Logo />
|
<Logo />
|
||||||
<div className={s.viewCart} onClick={onViewCart}>View cart</div>
|
<div className={s.viewCart} onClick={onViewCart}>View cart</div>
|
||||||
@@ -95,11 +134,13 @@ const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
|
|||||||
return <CheckoutCollapse
|
return <CheckoutCollapse
|
||||||
key={item.title}
|
key={item.title}
|
||||||
id={item.id}
|
id={item.id}
|
||||||
visible={item.id === active}
|
visible={item.id === activeStep}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
onEditClick={onEdit}
|
onEditClick={onEdit}
|
||||||
isEdit={done.includes(item.id)}
|
isEdit={doneSteps.includes(item.id)}
|
||||||
|
onClose={onConfirm}
|
||||||
note={note}
|
note={note}
|
||||||
|
disableEdit={customer && item.id === CheckoutStep.CustomerInfo}
|
||||||
>
|
>
|
||||||
{item.form}
|
{item.form}
|
||||||
</CheckoutCollapse>
|
</CheckoutCollapse>
|
||||||
|
@@ -18,7 +18,7 @@ const ChekoutNotePolicy = memo(() => {
|
|||||||
{
|
{
|
||||||
<Link href={ROUTE.PRIVACY_POLICY}>
|
<Link href={ROUTE.PRIVACY_POLICY}>
|
||||||
<a>
|
<a>
|
||||||
<strong>privacy policy </strong>
|
<strong>privacy policy</strong>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
|
@@ -8,11 +8,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.bottom{
|
.bottom{
|
||||||
|
@apply flex flex-col items-start;
|
||||||
margin-top: 2.4rem;
|
margin-top: 2.4rem;
|
||||||
@apply flex justify-between items-center;
|
button {
|
||||||
@screen sm-only {
|
margin-left: auto;
|
||||||
@apply flex-col items-start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,16 +1,23 @@
|
|||||||
import { Form, Formik } from 'formik'
|
import { Form, Formik } from 'formik'
|
||||||
import React, { useRef } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { ButtonCommon, InputFiledInForm } from 'src/components/common'
|
import { ButtonCommon, InputFiledInForm } from 'src/components/common'
|
||||||
|
import ModalAuthenticate from 'src/components/common/ModalAuthenticate/ModalAuthenticate'
|
||||||
import { useMessage } from 'src/components/contexts'
|
import { useMessage } from 'src/components/contexts'
|
||||||
|
import { useModalCommon } from 'src/components/hooks'
|
||||||
import { useSetCustomerForOrder } from 'src/components/hooks/order'
|
import { useSetCustomerForOrder } from 'src/components/hooks/order'
|
||||||
|
import { ErrorCode } from 'src/domains/enums/ErrorCode'
|
||||||
|
import { CommonError } from 'src/domains/interfaces/CommonError'
|
||||||
import { LANGUAGE } from 'src/utils/language.utils'
|
import { LANGUAGE } from 'src/utils/language.utils'
|
||||||
import { CustomInputCommon } from 'src/utils/type.utils'
|
import { CustomInputCommon } from 'src/utils/type.utils'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import ChekoutNotePolicy from '../ChekoutNotePolicy/ChekoutNotePolicy'
|
import ChekoutNotePolicy from '../ChekoutNotePolicy/ChekoutNotePolicy'
|
||||||
import s from './CustomerInfoForm.module.scss'
|
import s from './CustomerInfoForm.module.scss'
|
||||||
|
import ModalConfirmLogin from './ModalConfirmLogin/ModalConfirmLogin'
|
||||||
interface Props {
|
interface Props {
|
||||||
isHide: boolean
|
id: number
|
||||||
onSwitch: () => void
|
onConfirm: (id: number) => void
|
||||||
|
activeStep: number
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayingErrorMessagesSchema = Yup.object().shape({
|
const displayingErrorMessagesSchema = Yup.object().shape({
|
||||||
@@ -19,32 +26,49 @@ const displayingErrorMessagesSchema = Yup.object().shape({
|
|||||||
emailAddress: Yup.string().email(LANGUAGE.MESSAGE.INVALID_EMAIL).required(LANGUAGE.MESSAGE.REQUIRED),
|
emailAddress: Yup.string().email(LANGUAGE.MESSAGE.INVALID_EMAIL).required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
})
|
})
|
||||||
|
|
||||||
const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
|
const CustomerInfoForm = ({ id, onConfirm, activeStep }: Props) => {
|
||||||
const firstNameRef = useRef<CustomInputCommon>(null)
|
const firstNameRef = useRef<CustomInputCommon>(null)
|
||||||
const emailRef = useRef<CustomInputCommon>(null)
|
const emailRef = useRef<CustomInputCommon>(null)
|
||||||
const { setCustomerForOrder, loading } = useSetCustomerForOrder()
|
const { setCustomerForOrder, loading } = useSetCustomerForOrder()
|
||||||
const { showMessageError } = useMessage()
|
const { showMessageError } = useMessage()
|
||||||
|
const [emailAddress, setEmailAddress] = useState<string>('')
|
||||||
|
const { visible: visibleModalConfirmLogin, closeModal: closeModalConfirmLogin, openModal: openModalConfirmLogin } = useModalCommon({ initialValue: false })
|
||||||
|
const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
firstNameRef.current?.focus()
|
||||||
|
}, 500);
|
||||||
|
}, [activeStep])
|
||||||
|
|
||||||
const handleSubmit = (values: { firstName: string, lastName: string, emailAddress: string }) => {
|
const handleSubmit = (values: { firstName: string, lastName: string, emailAddress: string }) => {
|
||||||
console.log('on submit: ', values)
|
|
||||||
const { firstName, lastName, emailAddress } = values
|
const { firstName, lastName, emailAddress } = values
|
||||||
|
setEmailAddress(emailAddress)
|
||||||
setCustomerForOrder({ firstName, lastName, emailAddress }, onSubmitCalBack)
|
setCustomerForOrder({ firstName, lastName, emailAddress }, onSubmitCalBack)
|
||||||
// onConfirm &&
|
|
||||||
// onConfirm(id, {
|
|
||||||
// name: nameRef?.current?.getValue().toString(),
|
|
||||||
// email: emailRef.current?.getValue().toString(),
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
|
const onSubmitCalBack = (isSuccess: boolean, error?: CommonError) => {
|
||||||
// TODO:
|
// TODO:
|
||||||
console.log("result: ", isSuccess, msg)
|
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
|
onConfirm(id)
|
||||||
} else {
|
} else {
|
||||||
console.log("error here")
|
if (error?.errorCode === ErrorCode.EmailAddressConflictError) {
|
||||||
showMessageError(msg)
|
// show modal common
|
||||||
|
openModalConfirmLogin()
|
||||||
|
} else if (error?.errorCode === ErrorCode.NoActiveOrderError) {
|
||||||
|
showMessageError("Your cart is empty! Please add items to the cart!")
|
||||||
|
} else {
|
||||||
|
showMessageError(error?.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const handleOpenModalLogin = () => {
|
||||||
|
closeModalConfirmLogin()
|
||||||
|
openModalAuthen()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseModalConfirmLogin = () => {
|
||||||
|
closeModalConfirmLogin()
|
||||||
|
emailRef.current?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -58,7 +82,6 @@ const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
|
|||||||
}}
|
}}
|
||||||
validationSchema={displayingErrorMessagesSchema}
|
validationSchema={displayingErrorMessagesSchema}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
|
|
||||||
>
|
>
|
||||||
{({ errors, touched, isValid, submitForm }) => (
|
{({ errors, touched, isValid, submitForm }) => (
|
||||||
<Form className="u-form">
|
<Form className="u-form">
|
||||||
@@ -90,12 +113,12 @@ const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
|
|||||||
<InputFiledInForm
|
<InputFiledInForm
|
||||||
name="emailAddress"
|
name="emailAddress"
|
||||||
placeholder="Email Address"
|
placeholder="Email Address"
|
||||||
ref={emailRef}
|
|
||||||
error={
|
error={
|
||||||
touched.emailAddress && errors.emailAddress
|
touched.emailAddress && errors.emailAddress
|
||||||
? errors.emailAddress.toString()
|
? errors.emailAddress.toString()
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
ref={emailRef}
|
||||||
isShowIconSuccess={touched.emailAddress && !errors.emailAddress}
|
isShowIconSuccess={touched.emailAddress && !errors.emailAddress}
|
||||||
onEnter={isValid ? submitForm : undefined}
|
onEnter={isValid ? submitForm : undefined}
|
||||||
|
|
||||||
@@ -111,6 +134,8 @@ const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
|
<ModalConfirmLogin visible={visibleModalConfirmLogin} closeModal={handleCloseModalConfirmLogin} handleOk={handleOpenModalLogin} email={emailAddress} />
|
||||||
|
<ModalAuthenticate visible={visibleModalAuthen} closeModal={closeModalAuthen} initialEmail={emailAddress} disableRedirect={true} />
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
.modalConfirmLogin {
|
||||||
|
min-width: 40rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { ModalConfirm } from 'src/components/common';
|
||||||
|
import { LANGUAGE } from 'src/utils/language.utils';
|
||||||
|
import s from './ModalConfirmLogin.module.scss'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean
|
||||||
|
closeModal: () => void
|
||||||
|
handleOk: () => void
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModalConfirmLogin = ({ visible, closeModal, handleOk, email }: Props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ModalConfirm
|
||||||
|
visible={visible}
|
||||||
|
onClose={closeModal}
|
||||||
|
onOk={handleOk}
|
||||||
|
okText={LANGUAGE.BUTTON_LABEL.SIGNIN}
|
||||||
|
cancelText="Change email address"
|
||||||
|
>
|
||||||
|
<div className={s.modalConfirmLogin}>
|
||||||
|
<p> Account already exists for email {email} </p>
|
||||||
|
<p>Please signin to continue or use another email</p>
|
||||||
|
</div>
|
||||||
|
</ModalConfirm>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalConfirmLogin;
|
@@ -1,19 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { ButtonCommon, TabCommon, TabPane } from 'src/components/common'
|
import { ButtonCommon, TabCommon, TabPane } from 'src/components/common'
|
||||||
import { CheckOutForm } from 'src/utils/types.utils'
|
|
||||||
import BankTransfer from '../BankTransfer/BankTransfer'
|
import BankTransfer from '../BankTransfer/BankTransfer'
|
||||||
import Link from 'next/link'
|
import ChekoutNotePolicy from '../ChekoutNotePolicy/ChekoutNotePolicy'
|
||||||
|
|
||||||
import s from './PaymentInfoForm.module.scss'
|
|
||||||
import CreditCardForm from '../CreditCardForm/CreditCardForm'
|
import CreditCardForm from '../CreditCardForm/CreditCardForm'
|
||||||
|
import s from './PaymentInfoForm.module.scss'
|
||||||
|
|
||||||
interface PaymentInfoFormProps {
|
interface PaymentInfoFormProps {
|
||||||
onConfirm?: (id: number, formInfo: CheckOutForm) => void
|
onConfirm?: (id: number) => void
|
||||||
id: number
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const PaymentInfoForm = ({onConfirm,id}: PaymentInfoFormProps) => {
|
const PaymentInfoForm = ({onConfirm,id}: PaymentInfoFormProps) => {
|
||||||
const handleConfirmClick = () => {
|
const handleConfirmClick = () => {
|
||||||
onConfirm && onConfirm(id,{})
|
onConfirm && onConfirm(id)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={s.wrapper}>
|
<div className={s.wrapper}>
|
||||||
@@ -29,21 +28,7 @@ const PaymentInfoForm = ({onConfirm,id}: PaymentInfoFormProps) => {
|
|||||||
</TabPane>
|
</TabPane>
|
||||||
</TabCommon>
|
</TabCommon>
|
||||||
<div className={s.bottom}>
|
<div className={s.bottom}>
|
||||||
<div className={s.note}>
|
<ChekoutNotePolicy/>
|
||||||
By clicking continue you agree to Casper's{' '}
|
|
||||||
{
|
|
||||||
<Link href="#">
|
|
||||||
<strong>terms and conditions</strong>
|
|
||||||
</Link>
|
|
||||||
}{' '}
|
|
||||||
and{' '}
|
|
||||||
{
|
|
||||||
<Link href="#">
|
|
||||||
<strong>privacy policy </strong>
|
|
||||||
</Link>
|
|
||||||
}
|
|
||||||
.
|
|
||||||
</div>
|
|
||||||
<div className={s.button}>
|
<div className={s.button}>
|
||||||
<ButtonCommon onClick={handleConfirmClick}>
|
<ButtonCommon onClick={handleConfirmClick}>
|
||||||
Submit Order
|
Submit Order
|
||||||
|
@@ -1,45 +1,20 @@
|
|||||||
@import "../../../../../../styles/utilities";
|
@import "../../../../../../styles/utilities";
|
||||||
|
|
||||||
.warpper{
|
.warpper {
|
||||||
@apply u-form;
|
@apply u-form;
|
||||||
@screen md {
|
@screen md {
|
||||||
padding: 0 5.6rem;
|
padding: 0 5.6rem;
|
||||||
}
|
}
|
||||||
.bottom{
|
.bottom {
|
||||||
|
@apply flex flex-col items-start;
|
||||||
margin-top: 2.4rem;
|
margin-top: 2.4rem;
|
||||||
@apply flex justify-between items-center;
|
button {
|
||||||
.note{
|
margin-left: auto;
|
||||||
font-size: 1.2rem;
|
|
||||||
line-height: 2rem;
|
|
||||||
}
|
|
||||||
@screen sm-only {
|
|
||||||
@apply flex-col items-start;
|
|
||||||
.button {
|
|
||||||
padding-top: 2rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.line{
|
.line {
|
||||||
>div{
|
> div {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.method{
|
}
|
||||||
width: 100%;
|
|
||||||
height: 5.6rem;
|
|
||||||
padding: 1.6rem;
|
|
||||||
border-radius: 0.8rem;
|
|
||||||
@apply flex justify-between items-center border border-solid border-line bg-gray;
|
|
||||||
.left{
|
|
||||||
@apply flex;
|
|
||||||
.name{
|
|
||||||
margin-left: 1.6rem;
|
|
||||||
color: var(--text-active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.price{
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--text-active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,17 +1,37 @@
|
|||||||
import React, { useRef } from 'react'
|
import { Form, Formik } from 'formik'
|
||||||
import { ButtonCommon, Inputcommon, SelectCommon } from 'src/components/common'
|
import React, { useEffect, useRef } from 'react'
|
||||||
import s from './ShippingInfoForm.module.scss'
|
import { ButtonCommon, InputFiledInForm, SelectFieldInForm } from 'src/components/common'
|
||||||
import Link from 'next/link'
|
import { useMessage } from 'src/components/contexts'
|
||||||
|
import { useAvailableCountries, useSetOrderShippingAddress } from 'src/components/hooks/order'
|
||||||
|
import { LANGUAGE } from 'src/utils/language.utils'
|
||||||
import { CustomInputCommon } from 'src/utils/type.utils'
|
import { CustomInputCommon } from 'src/utils/type.utils'
|
||||||
import { Shipping } from 'src/components/icons'
|
import * as Yup from 'yup'
|
||||||
import { CheckOutForm } from 'src/utils/types.utils'
|
import ChekoutNotePolicy from '../ChekoutNotePolicy/ChekoutNotePolicy'
|
||||||
|
import s from './ShippingInfoForm.module.scss'
|
||||||
|
|
||||||
interface ShippingInfoFormProps {
|
interface ShippingInfoFormProps {
|
||||||
onConfirm?: (id:number,formInfo:CheckOutForm)=>void
|
id: number
|
||||||
id:number
|
activeStep: number
|
||||||
|
onConfirm: (id: number) => void
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const option = [
|
|
||||||
|
const displayingErrorMessagesSchema = Yup.object().shape({
|
||||||
|
streetLine1: Yup.string().required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
|
city: Yup.string().required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
|
province: Yup.string().required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
|
postalCode: Yup.number().required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
|
countryCode: Yup.string().required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
|
phoneNumber: Yup.string().required(LANGUAGE.MESSAGE.REQUIRED),
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const DEFAULT_COUNTRY_CODE = 'MY'
|
||||||
|
const DEFAULT_PROVINCE = 'Sabah'
|
||||||
|
|
||||||
|
// TODO: update data
|
||||||
|
const provinceOptions = [
|
||||||
{
|
{
|
||||||
name: 'Hồ Chí Minh',
|
name: 'Hồ Chí Minh',
|
||||||
value: 'Hồ Chí Minh',
|
value: 'Hồ Chí Minh',
|
||||||
@@ -20,79 +40,150 @@ const option = [
|
|||||||
name: 'Hà Nội',
|
name: 'Hà Nội',
|
||||||
value: 'Hà Nội',
|
value: 'Hà Nội',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Sabah',
|
||||||
|
value: 'Sabah',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const ShippingInfoForm = ({onConfirm,id}: ShippingInfoFormProps) => {
|
const ShippingInfoForm = ({ onConfirm, id, activeStep }: ShippingInfoFormProps) => {
|
||||||
const addressRef = useRef<CustomInputCommon>(null)
|
const addressRef = useRef<CustomInputCommon>(null)
|
||||||
const cityRef = useRef<CustomInputCommon>(null)
|
const { setOrderShippingAddress, loading } = useSetOrderShippingAddress()
|
||||||
const stateRef = useRef<CustomInputCommon>(null)
|
const { showMessageError } = useMessage()
|
||||||
const codeRef = useRef<CustomInputCommon>(null)
|
const { countries } = useAvailableCountries()
|
||||||
const phoneRef = useRef<CustomInputCommon>(null)
|
|
||||||
const handleConfirmClick = () => {
|
useEffect(() => {
|
||||||
onConfirm && onConfirm(id,{
|
setTimeout(() => {
|
||||||
address: addressRef?.current?.getValue().toString(),
|
addressRef.current?.focus()
|
||||||
city: cityRef.current?.getValue().toString(),
|
}, 500);
|
||||||
state: stateRef?.current?.getValue().toString(),
|
}, [activeStep])
|
||||||
code: Number(codeRef.current?.getValue()),
|
|
||||||
phone: Number(phoneRef?.current?.getValue()),
|
const handleSubmit = (values: any) => {
|
||||||
})
|
setOrderShippingAddress(values, onSubmitCalBack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
|
||||||
|
if (isSuccess) {
|
||||||
|
onConfirm(id)
|
||||||
|
} else {
|
||||||
|
showMessageError(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={s.warpper}>
|
<div className={s.warpper}>
|
||||||
<div className={s.body}>
|
<div className={s.body}>
|
||||||
<Inputcommon
|
<Formik
|
||||||
type="text"
|
initialValues={
|
||||||
placeholder="Street Address"
|
{
|
||||||
ref={addressRef}
|
streetLine1: '',
|
||||||
/>
|
city: '',
|
||||||
<Inputcommon type="text" placeholder="City" ref={cityRef} />
|
province: DEFAULT_PROVINCE,
|
||||||
<div className={s.line}>
|
postalCode: '',
|
||||||
<SelectCommon options={option} type="custom" size="large">State</SelectCommon>
|
countryCode: DEFAULT_COUNTRY_CODE,
|
||||||
<Inputcommon type="text" placeholder="Zip Code" ref={codeRef} />
|
phoneNumber: '',
|
||||||
</div>
|
}}
|
||||||
<Inputcommon
|
validationSchema={displayingErrorMessagesSchema}
|
||||||
type="text"
|
onSubmit={handleSubmit}
|
||||||
placeholder="Phone (delivery contact)"
|
>
|
||||||
ref={phoneRef}
|
{({ errors, touched, isValid, submitForm }) => (
|
||||||
/>
|
<Form className="u-form">
|
||||||
<div className={s.method}>
|
<div className="body">
|
||||||
<div className={s.left}>
|
<div className={s.input}>
|
||||||
<div className={s.icon}>
|
<InputFiledInForm
|
||||||
<Shipping/>
|
name="streetLine1"
|
||||||
</div>
|
placeholder="Address"
|
||||||
<div className={s.name}>
|
ref={addressRef}
|
||||||
Standard Delivery Method
|
error={
|
||||||
</div>
|
touched.streetLine1 && errors.streetLine1
|
||||||
</div>
|
? errors.streetLine1.toString()
|
||||||
<div className={s.right}>
|
: ''
|
||||||
<div className={s.price}>
|
}
|
||||||
Free
|
isShowIconSuccess={touched.streetLine1 && !errors.streetLine1}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="line">
|
||||||
</div>
|
<div className={s.input}>
|
||||||
<div className={s.bottom}>
|
<InputFiledInForm
|
||||||
<div className={s.note}>
|
name="city"
|
||||||
By clicking continue you agree to Casper's{' '}
|
placeholder="City"
|
||||||
{
|
error={
|
||||||
<Link href="#">
|
touched.city && errors.city
|
||||||
<strong>terms and conditions</strong>
|
? errors.city.toString()
|
||||||
</Link>
|
: ''
|
||||||
}{' '}
|
}
|
||||||
and{' '}
|
isShowIconSuccess={touched.city && !errors.city}
|
||||||
{
|
/>
|
||||||
<Link href="#">
|
</div>
|
||||||
<strong>privacy policy </strong>
|
|
||||||
</Link>
|
<div className={s.input}>
|
||||||
}
|
<SelectFieldInForm
|
||||||
.
|
options={provinceOptions}
|
||||||
</div>
|
name="province"
|
||||||
<div className={s.button}>
|
placeholder="Province"
|
||||||
<ButtonCommon onClick={handleConfirmClick}>
|
error={
|
||||||
Continue to Payment
|
touched.province && errors.province
|
||||||
</ButtonCommon>
|
? errors.province.toString()
|
||||||
</div>
|
: ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="line">
|
||||||
|
<div className={s.input}>
|
||||||
|
<InputFiledInForm
|
||||||
|
name="postalCode"
|
||||||
|
placeholder="Postal Code"
|
||||||
|
error={
|
||||||
|
touched.postalCode && errors.postalCode
|
||||||
|
? errors.postalCode.toString()
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
isShowIconSuccess={touched.postalCode && !errors.postalCode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={s.input}>
|
||||||
|
<SelectFieldInForm
|
||||||
|
options={countries || []}
|
||||||
|
keyNameOption={['name']}
|
||||||
|
keyValueOption="code"
|
||||||
|
name="countryCode"
|
||||||
|
placeholder="Country"
|
||||||
|
error={
|
||||||
|
touched.countryCode && errors.countryCode
|
||||||
|
? errors.countryCode.toString()
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</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.bottom}>
|
||||||
|
<ChekoutNotePolicy />
|
||||||
|
<ButtonCommon HTMLType='submit' loading={loading} size="large">
|
||||||
|
Continue to Shipping method
|
||||||
|
</ButtonCommon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
.shippingMethod {
|
||||||
|
@apply relative;
|
||||||
|
.method {
|
||||||
|
@apply w-full flex justify-between items-center border border-solid border-line bg-gray cursor-pointer;
|
||||||
|
height: 5.6rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
.left {
|
||||||
|
@apply flex;
|
||||||
|
.name {
|
||||||
|
margin-left: 1.6rem;
|
||||||
|
color: var(--text-active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.price {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--text-active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--white);
|
||||||
|
border: 1px solid var(--border-line);
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,75 @@
|
|||||||
|
import { ShippingMethodQuote } from '@framework/schema'
|
||||||
|
import React, { memo, useState } from 'react'
|
||||||
|
import { useMessage } from 'src/components/contexts'
|
||||||
|
import { useEligibleShippingMethods, useSetOrderShippingMethod } from 'src/components/hooks/order'
|
||||||
|
import { Shipping } from 'src/components/icons'
|
||||||
|
import { CheckoutStep } from '../../CheckoutInfo'
|
||||||
|
import s from './ShippingMethod.module.scss'
|
||||||
|
import ShippingMethodItem from './ShippingMethodItem/ShippingMethodItem'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
currency: string
|
||||||
|
onConfirm: (id: number) => void
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShippingMethod = memo(({ currency, onConfirm }: Props) => {
|
||||||
|
const { eligibleShippingMethods } = useEligibleShippingMethods()
|
||||||
|
const { setOrderShippingMethod } = useSetOrderShippingMethod()
|
||||||
|
const [selectedValue, setSelectedValue] = useState<ShippingMethodQuote | undefined>(eligibleShippingMethods ? eligibleShippingMethods[0] : undefined)
|
||||||
|
const { showMessageError } = useMessage()
|
||||||
|
|
||||||
|
const onChange = (id: string) => {
|
||||||
|
const newValue = eligibleShippingMethods?.find(item => item.id === id)
|
||||||
|
if (newValue) {
|
||||||
|
setSelectedValue(newValue)
|
||||||
|
if (newValue?.id) {
|
||||||
|
setOrderShippingMethod(newValue?.id, onSubmitCalBack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
|
||||||
|
if (isSuccess) {
|
||||||
|
onConfirm(CheckoutStep.ShippingMethodInfo)
|
||||||
|
} else {
|
||||||
|
showMessageError(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={s.shippingMethod}>
|
||||||
|
<div className={s.method}>
|
||||||
|
<div className={s.left}>
|
||||||
|
<div className={s.icon}>
|
||||||
|
<Shipping />
|
||||||
|
</div>
|
||||||
|
<div className={s.name}>
|
||||||
|
{selectedValue?.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={s.right}>
|
||||||
|
<div className={s.price}>
|
||||||
|
{selectedValue?.price ? `${selectedValue?.price / 100} ${currency}` : "Free"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={s.options}>
|
||||||
|
<ul>
|
||||||
|
{eligibleShippingMethods?.map(item => <ShippingMethodItem
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
name={item.name}
|
||||||
|
price={item.price}
|
||||||
|
currency={currency}
|
||||||
|
isActive={selectedValue?.id === item.id}
|
||||||
|
onSelect={onChange}
|
||||||
|
/>)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
ShippingMethod.displayName = 'ShippingMethod'
|
||||||
|
export default ShippingMethod
|
@@ -0,0 +1,25 @@
|
|||||||
|
.shippingMethodItem {
|
||||||
|
@apply w-full flex justify-between items-center cursor-pointer transition-all duration-200;
|
||||||
|
padding: 1.6rem;
|
||||||
|
&:hover {
|
||||||
|
@apply bg-gray;
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
@apply flex;
|
||||||
|
.icon {
|
||||||
|
@apply transition-all duration-200;
|
||||||
|
opacity: 0;
|
||||||
|
&.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
margin-left: 1.6rem;
|
||||||
|
color: var(--text-active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.price {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--text-active);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
import React, { memo } from 'react'
|
||||||
|
import { IconCheck } from 'src/components/icons'
|
||||||
|
import s from './ShippingMethodItem.module.scss'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
price: number
|
||||||
|
currency: string
|
||||||
|
onSelect: (id: string) => void
|
||||||
|
isActive: boolean
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShippingMethodItem = memo(({ id, name, price, currency, isActive, onSelect }: Props) => {
|
||||||
|
const handleSelect = () => {
|
||||||
|
onSelect(id)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<li className={s.shippingMethodItem} key={id} onClick={handleSelect}>
|
||||||
|
<div className={s.left}>
|
||||||
|
<div className={classNames(s.icon, { [s.show]: isActive })}>
|
||||||
|
<IconCheck />
|
||||||
|
</div>
|
||||||
|
<div className={s.name}>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={s.price}>
|
||||||
|
{price ? `${price / 100} ${currency}` : "Free"}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
ShippingMethodItem.displayName = 'ShippingMethodItem'
|
||||||
|
export default ShippingMethodItem
|
@@ -1,6 +1,7 @@
|
|||||||
@import "../../../../styles/utilities";
|
@import "../../../../styles/utilities";
|
||||||
.warrper{
|
.warrper{
|
||||||
@apply flex w-full h-full absolute;
|
@apply flex w-full;
|
||||||
|
min-height: 100%;
|
||||||
.right {
|
.right {
|
||||||
display: none;
|
display: none;
|
||||||
@screen lg {
|
@screen lg {
|
||||||
@@ -46,10 +47,7 @@
|
|||||||
color:var(--text-base);
|
color:var(--text-base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button{
|
|
||||||
margin-top: 2rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ import classNames from 'classnames'
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { MessageCommon } from 'src/components/common'
|
import { MessageCommon } from 'src/components/common'
|
||||||
import { useMessage } from 'src/components/contexts'
|
import { useMessage } from 'src/components/contexts'
|
||||||
|
import { useGetActiveOrderForCheckout } from 'src/components/hooks/order'
|
||||||
import IconHide from 'src/components/icons/IconHide'
|
import IconHide from 'src/components/icons/IconHide'
|
||||||
import { CHECKOUT_BILL_DATA } from 'src/utils/demo-data'
|
import { CHECKOUT_BILL_DATA } from 'src/utils/demo-data'
|
||||||
import { CheckoutBill, CheckoutInfo } from '..'
|
import { CheckoutBill, CheckoutInfo } from '..'
|
||||||
@@ -9,29 +10,30 @@ import s from "./CheckoutPage.module.scss"
|
|||||||
interface CheckoutPageProps {
|
interface CheckoutPageProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CheckoutPage = ({}: CheckoutPageProps) => {
|
const CheckoutPage = ({ }: CheckoutPageProps) => {
|
||||||
const { messages, removeMessage } = useMessage()
|
const { messages, removeMessage } = useMessage()
|
||||||
const [isShow, setIsShow] = useState(false)
|
const [isShow, setIsShow] = useState(false)
|
||||||
|
const { order } = useGetActiveOrderForCheckout()
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setIsShow(false)
|
setIsShow(false)
|
||||||
}
|
}
|
||||||
const onViewCart =() => {
|
const onViewCart = () => {
|
||||||
setIsShow(true)
|
setIsShow(true)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={s.warrper}>
|
<div className={s.warrper}>
|
||||||
<MessageCommon messages={messages} onRemove={removeMessage} />
|
<MessageCommon messages={messages} onRemove={removeMessage} />
|
||||||
<div className={s.left}><CheckoutInfo onViewCart = {onViewCart}/></div>
|
<div className={s.left}><CheckoutInfo onViewCart={onViewCart} currency={order?.currency.code} /></div>
|
||||||
<div className={s.right}><CheckoutBill data={CHECKOUT_BILL_DATA}/></div>
|
<div className={s.right}><CheckoutBill data={order} /></div>
|
||||||
<div className={classNames({ [s.mobile] :true,[s.isShow]: isShow})}>
|
<div className={classNames({ [s.mobile]: true, [s.isShow]: isShow })}>
|
||||||
<div className={s.modal}>
|
<div className={s.modal}>
|
||||||
<div className={s.content}>
|
<div className={s.content}>
|
||||||
<div className={s.head}>
|
<div className={s.head}>
|
||||||
<h3>Your Cart({CHECKOUT_BILL_DATA.length})</h3>
|
<h3>Your Cart({CHECKOUT_BILL_DATA.length})</h3>
|
||||||
<div onClick={onClose}><IconHide/></div>
|
<div onClick={onClose}><IconHide /></div>
|
||||||
</div>
|
</div>
|
||||||
<CheckoutBill data={CHECKOUT_BILL_DATA}/>
|
<CheckoutBill data={order} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Collection } from '@commerce/types/collection';
|
import { Collection } from '@commerce/types/collection';
|
||||||
import { Facet } from "@commerce/types/facet";
|
import { Facet } from "@commerce/types/facet";
|
||||||
import { Product, ProductCard, ProductOption, ProductOptionValues } from "@commerce/types/product";
|
import { Product, ProductCard, ProductOptionValues } from "@commerce/types/product";
|
||||||
import { FacetValue, SearchResultSortParameter } from './../../framework/vendure/schema.d';
|
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 { 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";
|
import { PromiseWithKey, SelectedOptions, SortOrder } from "./types.utils";
|
||||||
|
@@ -5,7 +5,8 @@ export const LANGUAGE = {
|
|||||||
CONFIRM:'Confirm',
|
CONFIRM:'Confirm',
|
||||||
ADD_TO_CARD: 'Add to Cart',
|
ADD_TO_CARD: 'Add to Cart',
|
||||||
PREORDER: 'Pre-Order Now',
|
PREORDER: 'Pre-Order Now',
|
||||||
SIGNIN :'Sign In'
|
SIGNIN :'Sign In',
|
||||||
|
CANCEL: 'Cancel',
|
||||||
},
|
},
|
||||||
PLACE_HOLDER: {
|
PLACE_HOLDER: {
|
||||||
SEARCH: 'Search',
|
SEARCH: 'Search',
|
||||||
|
@@ -33,18 +33,6 @@ export interface BlogProps {
|
|||||||
imageSrc: string | null,
|
imageSrc: string | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CheckOutForm {
|
|
||||||
name?: string
|
|
||||||
email?: string
|
|
||||||
address?: string
|
|
||||||
city?: string
|
|
||||||
state?: string
|
|
||||||
code?: number
|
|
||||||
phone?: number
|
|
||||||
method?: string
|
|
||||||
shipping_fee?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MouseAndTouchEvent = MouseEvent | TouchEvent
|
export type MouseAndTouchEvent = MouseEvent | TouchEvent
|
||||||
|
|
||||||
export enum SortOrder {
|
export enum SortOrder {
|
||||||
|
Reference in New Issue
Block a user