mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 12:24:18 +00:00
✨ feat: useGetActiveOrderForCheckout
:%s
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Shipping } from 'src/components/icons';
|
||||
import type { Discount, Measurement, Image } from './common'
|
||||
import { ShippingMethod } from '@framework/schema';
|
||||
import type { Discount, Image, Measurement } from './common';
|
||||
|
||||
export type SelectedOption = {
|
||||
// The option's id.
|
||||
@@ -60,6 +60,31 @@ export type ProductVariant = {
|
||||
|
||||
// Shopping cart, a.k.a Checkout
|
||||
export type Cart = {
|
||||
id: string
|
||||
// ID of the customer to which the cart belongs.
|
||||
customerId?: 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
|
||||
// Discounts that have been applied on the cart.
|
||||
discounts?: Discount[]
|
||||
}
|
||||
|
||||
export type CartCheckout = {
|
||||
id: string
|
||||
// ID of the customer to which the cart belongs.
|
||||
customerId?: string
|
||||
@@ -97,6 +122,10 @@ export type Cart = {
|
||||
// Discounts that have been applied on the cart.
|
||||
discounts?: Discount[]
|
||||
totalDiscount: number
|
||||
shippingLine: {
|
||||
priceWithTax: number
|
||||
shippingMethod: ShippingMethod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
5
framework/vendure/schema.d.ts
vendored
5
framework/vendure/schema.d.ts
vendored
@@ -3066,6 +3066,11 @@ export type CartFragment = { __typename?: 'Order' } & Pick<
|
||||
{ __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<
|
||||
{ __typename?: 'OrderLine' } & Pick<
|
||||
OrderLine,
|
||||
|
@@ -9,25 +9,8 @@ export const cartFragment = /* GraphQL */ `
|
||||
total
|
||||
totalWithTax
|
||||
currencyCode
|
||||
discounts {
|
||||
type
|
||||
description
|
||||
amount
|
||||
amountWithTax
|
||||
}
|
||||
customer {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
emailAddress
|
||||
}
|
||||
shippingAddress {
|
||||
streetLine1
|
||||
city
|
||||
province
|
||||
postalCode
|
||||
countryCode
|
||||
phoneNumber
|
||||
}
|
||||
lines {
|
||||
id
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { Cart } from '@commerce/types/cart'
|
||||
import { ProductCard, Product } from '@commerce/types/product'
|
||||
import { CartFragment, SearchResultFragment, Favorite } from '../schema'
|
||||
import { Cart, CartCheckout } from '@commerce/types/cart'
|
||||
import { Product, ProductCard } from '@commerce/types/product'
|
||||
import { CartFragment, Favorite, SearchResultFragment, ShippingMethod } from '../schema'
|
||||
|
||||
export function normalizeSearchResult(item: SearchResultFragment): ProductCard {
|
||||
return {
|
||||
@@ -36,6 +36,41 @@ export function normalizeFavoriteProductResult(item: Favorite) {
|
||||
|
||||
|
||||
export function normalizeCart(order: CartFragment): Cart {
|
||||
return {
|
||||
id: order.id.toString(),
|
||||
createdAt: order.createdAt,
|
||||
taxesIncluded: true,
|
||||
lineItemsSubtotalPrice: order.subTotalWithTax / 100,
|
||||
currency: { code: order.currencyCode },
|
||||
subtotalPrice: order.subTotalWithTax / 100,
|
||||
totalPrice: order.totalWithTax / 100,
|
||||
customerId: order.customer?.id,
|
||||
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 normalizeCartForCheckout(order: CartFragment): CartCheckout {
|
||||
return {
|
||||
id: order.id.toString(),
|
||||
createdAt: order.createdAt,
|
||||
@@ -59,6 +94,10 @@ export function normalizeCart(order: CartFragment): Cart {
|
||||
countryCode: order.shippingAddress?.countryCode || '',
|
||||
phoneNumber: order.shippingAddress?.phoneNumber || '',
|
||||
},
|
||||
shippingLine: {
|
||||
priceWithTax: order.shippingLines[0].priceWithTax,
|
||||
shippingMethod: order.shippingLines[0].shippingMethod as ShippingMethod
|
||||
},
|
||||
totalDiscount: order.discounts?.reduce((total, item) => total + item.amountWithTax, 0) / 100 || 0,
|
||||
discounts: order.discounts.map(item => {
|
||||
return { value: item.amountWithTax, description: item.description }
|
||||
|
@@ -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
|
||||
}
|
||||
`
|
||||
|
@@ -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 { 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 rawFetcher from 'src/utils/rawFetcher'
|
||||
import { useGetActiveOrder } from '.'
|
||||
import { adjustOrderLineMutation } from '@framework/utils/mutations/adjust-order-line-mutation'
|
||||
import { removeOrderLineMutation } from '@framework/utils/mutations/remove-order-line-mutation'
|
||||
|
||||
const useRemoveProductInCart = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
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
|
@@ -1,15 +1,15 @@
|
||||
import { Cart } from '@commerce/types/cart'
|
||||
import { CartCheckout } from '@commerce/types/cart'
|
||||
import Link from 'next/link'
|
||||
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 FormPromotionCode from './FormPromotionCode/FormPromotionCode'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface CheckoutBillProps {
|
||||
// data: CardItemCheckoutProps[]
|
||||
data: Cart | null
|
||||
data: CartCheckout | null
|
||||
}
|
||||
|
||||
const CheckoutBill = ({ data }: CheckoutBillProps) => {
|
||||
|
@@ -2,27 +2,30 @@ import React, { useEffect, useState } from 'react'
|
||||
import { Logo } from 'src/components/common'
|
||||
import CheckoutCollapse from 'src/components/common/CheckoutCollapse/CheckoutCollapse'
|
||||
import { useActiveCustomer } from 'src/components/hooks/auth'
|
||||
import { useGetActiveOrder } from 'src/components/hooks/cart'
|
||||
import useGetActiveOrderForCheckout from 'src/components/hooks/order/useGetActiveOrderForCheckout'
|
||||
import s from './CheckoutInfo.module.scss'
|
||||
import CustomerInfoForm from './components/CustomerInfoForm/CustomerInfoForm'
|
||||
import PaymentInfoForm from './components/PaymentInfoForm/PaymentInfoForm'
|
||||
import ShippingInfoForm from './components/ShippingInfoForm/ShippingInfoForm'
|
||||
import ShippingMethod from './components/ShippingMethod/ShippingMethod'
|
||||
interface CheckoutInfoProps {
|
||||
onViewCart: () => void
|
||||
currency?: string
|
||||
}
|
||||
|
||||
enum CheckoutStep {
|
||||
export enum CheckoutStep {
|
||||
CustomerInfo = 1,
|
||||
ShippingInfo = 2,
|
||||
PaymentInfo = 3,
|
||||
ShippingAddressInfo = 2,
|
||||
ShippingMethodInfo = 3,
|
||||
PaymentInfo = 4,
|
||||
}
|
||||
|
||||
const CheckoutInfo = ({ onViewCart, currency = "" }: CheckoutInfoProps) => {
|
||||
const [activeStep, setActiveStep] = useState(1)
|
||||
const [doneSteps, setDoneSteps] = useState<CheckoutStep[]>([])
|
||||
const { order } = useGetActiveOrder()
|
||||
const { order } = useGetActiveOrderForCheckout()
|
||||
const { customer } = useActiveCustomer()
|
||||
console.log("active order checkout: ", order)
|
||||
|
||||
useEffect(() => {
|
||||
if (customer) {
|
||||
@@ -82,7 +85,7 @@ const CheckoutInfo = ({ onViewCart, currency = "" }: CheckoutInfoProps) => {
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
case CheckoutStep.ShippingInfo:
|
||||
case CheckoutStep.ShippingAddressInfo:
|
||||
if (order?.shippingAddress) {
|
||||
const { streetLine1, city, province, postalCode, countryCode, phoneNumber } = order.shippingAddress
|
||||
return `${streetLine1}, ${city}, ${province}, ${postalCode}, ${countryCode}, ${phoneNumber}`
|
||||
@@ -100,9 +103,14 @@ const CheckoutInfo = ({ onViewCart, currency = "" }: CheckoutInfoProps) => {
|
||||
form: <CustomerInfoForm onConfirm={onConfirm} id={CheckoutStep.CustomerInfo} activeStep={activeStep} />,
|
||||
},
|
||||
{
|
||||
id: CheckoutStep.ShippingInfo,
|
||||
title: 'Shipping Information',
|
||||
form: <ShippingInfoForm onConfirm={onConfirm} id={CheckoutStep.ShippingInfo} activeStep={activeStep} currency={currency} />,
|
||||
id: CheckoutStep.ShippingAddressInfo,
|
||||
title: 'Shipping Address Information',
|
||||
form: <ShippingInfoForm onConfirm={onConfirm} id={CheckoutStep.ShippingAddressInfo} activeStep={activeStep}/>,
|
||||
},
|
||||
{
|
||||
id: CheckoutStep.ShippingMethodInfo,
|
||||
title: 'Shipping Method Information',
|
||||
form: <ShippingMethod onConfirm={onConfirm} currency={currency} />,
|
||||
},
|
||||
{
|
||||
id: CheckoutStep.PaymentInfo,
|
||||
|
@@ -7,15 +7,12 @@ import { LANGUAGE } from 'src/utils/language.utils'
|
||||
import { CustomInputCommon } from 'src/utils/type.utils'
|
||||
import * as Yup from 'yup'
|
||||
import ChekoutNotePolicy from '../ChekoutNotePolicy/ChekoutNotePolicy'
|
||||
import ShippingMethod from '../ShippingMethod/ShippingMethod'
|
||||
import s from './ShippingInfoForm.module.scss'
|
||||
|
||||
interface ShippingInfoFormProps {
|
||||
id: number
|
||||
activeStep: number
|
||||
onConfirm: (id: number) => void
|
||||
currency: string
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +46,7 @@ const provinceOptions = [
|
||||
},
|
||||
]
|
||||
|
||||
const ShippingInfoForm = ({ onConfirm, id, activeStep , currency}: ShippingInfoFormProps) => {
|
||||
const ShippingInfoForm = ({ onConfirm, id, activeStep }: ShippingInfoFormProps) => {
|
||||
const addressRef = useRef<CustomInputCommon>(null)
|
||||
const { setOrderShippingAddress, loading } = useSetOrderShippingAddress()
|
||||
const { showMessageError } = useMessage()
|
||||
@@ -176,7 +173,6 @@ const ShippingInfoForm = ({ onConfirm, id, activeStep , currency}: ShippingInfoF
|
||||
onEnter={isValid ? submitForm : undefined}
|
||||
/>
|
||||
</div>
|
||||
<ShippingMethod currency={currency}/>
|
||||
<div className={s.bottom}>
|
||||
<ChekoutNotePolicy />
|
||||
<ButtonCommon HTMLType='submit' loading={loading} size="large">
|
||||
|
@@ -4,6 +4,7 @@ import React, { memo, useState } from 'react'
|
||||
import { useMessage } from 'src/components/contexts'
|
||||
import { 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'
|
||||
|
||||
@@ -27,13 +28,15 @@ const MOCKUP_DATA = [
|
||||
]
|
||||
interface Props {
|
||||
currency: string
|
||||
onConfirm: (id: number) => void
|
||||
|
||||
}
|
||||
|
||||
const ShippingMethod = memo(({ currency }: Props) => {
|
||||
const ShippingMethod = memo(({ currency, onConfirm }: Props) => {
|
||||
const { setOrderShippingMethod } = useSetOrderShippingMethod()
|
||||
const [selectedValue, setSelectedValue] = useState<ShippingMethodQuote>(MOCKUP_DATA[0])
|
||||
const { showMessageError } = useMessage()
|
||||
const [isShowOptions, setIsShowOptions] = useState<boolean>(false)
|
||||
const [isShowOptions, setIsShowOptions] = useState<boolean>(true)
|
||||
|
||||
const onChange = (id: string) => {
|
||||
const newValue = MOCKUP_DATA.find(item => item.id === id)
|
||||
@@ -47,7 +50,9 @@ const ShippingMethod = memo(({ currency }: Props) => {
|
||||
}
|
||||
|
||||
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
|
||||
if (!isSuccess) {
|
||||
if (isSuccess) {
|
||||
onConfirm(CheckoutStep.ShippingMethodInfo)
|
||||
} else {
|
||||
showMessageError(msg)
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import classNames from 'classnames'
|
||||
import React, { useState } from 'react'
|
||||
import { MessageCommon } from 'src/components/common'
|
||||
import { useMessage } from 'src/components/contexts'
|
||||
import { useGetActiveOrder } from 'src/components/hooks/cart'
|
||||
import useGetActiveOrderForCheckout from 'src/components/hooks/order/useGetActiveOrderForCheckout'
|
||||
import IconHide from 'src/components/icons/IconHide'
|
||||
import { CHECKOUT_BILL_DATA } from 'src/utils/demo-data'
|
||||
import { CheckoutBill, CheckoutInfo } from '..'
|
||||
@@ -13,7 +13,7 @@ interface CheckoutPageProps {
|
||||
const CheckoutPage = ({ }: CheckoutPageProps) => {
|
||||
const { messages, removeMessage } = useMessage()
|
||||
const [isShow, setIsShow] = useState(false)
|
||||
const { order } = useGetActiveOrder()
|
||||
const { order } = useGetActiveOrderForCheckout()
|
||||
|
||||
const onClose = () => {
|
||||
setIsShow(false)
|
||||
|
Reference in New Issue
Block a user