mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
✨ feat: set shipping method
:%s
This commit is contained in:
7
framework/vendure/schema.d.ts
vendored
7
framework/vendure/schema.d.ts
vendored
@@ -377,6 +377,13 @@ export type SetShippingMethodMutation = {
|
|||||||
| Pick<NoActiveOrderError, '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'
|
||||||
id: Scalars['ID']
|
id: Scalars['ID']
|
||||||
|
@@ -0,0 +1,13 @@
|
|||||||
|
export const getEligibleShippingMethods = /* GraphQL */ `
|
||||||
|
query getEligibleShippingMethods {
|
||||||
|
eligibleShippingMethods {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
price
|
||||||
|
priceWithTax
|
||||||
|
metadata
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@@ -3,3 +3,6 @@ export { default as useSetOrderShippingAddress } from './useSetOrderShippingAddr
|
|||||||
export { default as useApplyCouponCode } from './useApplyCouponCode'
|
export { default as useApplyCouponCode } from './useApplyCouponCode'
|
||||||
export { default as useAvailableCountries } from './useAvailableCountries'
|
export { default as useAvailableCountries } from './useAvailableCountries'
|
||||||
export { default as useSetOrderShippingMethod } from './useSetOrderShippingMethod'
|
export { default as useSetOrderShippingMethod } from './useSetOrderShippingMethod'
|
||||||
|
export { default as useGetActiveOrderForCheckout } from './useGetActiveOrderForCheckout'
|
||||||
|
export { default as useEligibleShippingMethods } from './useEligibleShippingMethods'
|
||||||
|
|
||||||
|
@@ -3,13 +3,13 @@ import { applyCouponCodeMutation } from '@framework/utils/mutations/apply-coupon
|
|||||||
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 useApplyCouponCode = () => {
|
const useApplyCouponCode = () => {
|
||||||
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 applyCouponCode = (couponCode: string,
|
const applyCouponCode = (couponCode: string,
|
||||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||||
|
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
|
@@ -3,13 +3,13 @@ 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?: CommonError) => void
|
fCallBack: (isSuccess: boolean, message?: CommonError) => void
|
||||||
|
@@ -3,13 +3,13 @@ import { setOrderShippingAddressMutation } from '@framework/utils/mutations/set-
|
|||||||
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 useSetOrderShippingAddress = () => {
|
const useSetOrderShippingAddress = () => {
|
||||||
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 setOrderShippingAddress = (input: CreateAddressInput,
|
const setOrderShippingAddress = (input: CreateAddressInput,
|
||||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||||
@@ -21,7 +21,6 @@ const useSetOrderShippingAddress = () => {
|
|||||||
variables: { input },
|
variables: { input },
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
console.log("data: ", data)
|
|
||||||
if (data.setOrderShippingAddress.__typename === 'Order') {
|
if (data.setOrderShippingAddress.__typename === 'Order') {
|
||||||
fCallBack(true)
|
fCallBack(true)
|
||||||
mutate()
|
mutate()
|
||||||
|
@@ -3,13 +3,13 @@ import { setShippingMethodMutation } from '@framework/utils/mutations/set-order-
|
|||||||
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 useSetOrderShippingMethod = () => {
|
const useSetOrderShippingMethod = () => {
|
||||||
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 setOrderShippingMethod = (id: string,
|
const setOrderShippingMethod = (id: string,
|
||||||
fCallBack: (isSuccess: boolean, message?: string) => void
|
fCallBack: (isSuccess: boolean, message?: string) => void
|
||||||
|
@@ -8,7 +8,6 @@ import s from './CheckoutBill.module.scss'
|
|||||||
import FormPromotionCode from './FormPromotionCode/FormPromotionCode'
|
import FormPromotionCode from './FormPromotionCode/FormPromotionCode'
|
||||||
|
|
||||||
interface CheckoutBillProps {
|
interface CheckoutBillProps {
|
||||||
// data: CardItemCheckoutProps[]
|
|
||||||
data: CartCheckout | null
|
data: CartCheckout | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'
|
|||||||
import { 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 { useActiveCustomer } from 'src/components/hooks/auth'
|
import { useActiveCustomer } from 'src/components/hooks/auth'
|
||||||
import useGetActiveOrderForCheckout from 'src/components/hooks/order/useGetActiveOrderForCheckout'
|
import { useGetActiveOrderForCheckout } from 'src/components/hooks/order'
|
||||||
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'
|
||||||
@@ -25,7 +25,6 @@ const CheckoutInfo = ({ onViewCart, currency = "" }: CheckoutInfoProps) => {
|
|||||||
const [doneSteps, setDoneSteps] = useState<CheckoutStep[]>([])
|
const [doneSteps, setDoneSteps] = useState<CheckoutStep[]>([])
|
||||||
const { order } = useGetActiveOrderForCheckout()
|
const { order } = useGetActiveOrderForCheckout()
|
||||||
const { customer } = useActiveCustomer()
|
const { customer } = useActiveCustomer()
|
||||||
console.log("active order checkout: ", order)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (customer) {
|
if (customer) {
|
||||||
@@ -91,6 +90,9 @@ const CheckoutInfo = ({ onViewCart, currency = "" }: CheckoutInfoProps) => {
|
|||||||
return `${streetLine1}, ${city}, ${province}, ${postalCode}, ${countryCode}, ${phoneNumber}`
|
return `${streetLine1}, ${city}, ${province}, ${postalCode}, ${countryCode}, ${phoneNumber}`
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
|
case CheckoutStep.ShippingMethodInfo:
|
||||||
|
console.log('oder here: ', order?.shippingLine)
|
||||||
|
return `${order?.shippingLine.shippingMethod.name}, ${order?.shippingLine.priceWithTax ? `${order?.shippingLine.priceWithTax} ${currency}`: 'Free'}` || ''
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -105,7 +107,7 @@ const CheckoutInfo = ({ onViewCart, currency = "" }: CheckoutInfoProps) => {
|
|||||||
{
|
{
|
||||||
id: CheckoutStep.ShippingAddressInfo,
|
id: CheckoutStep.ShippingAddressInfo,
|
||||||
title: 'Shipping Address Information',
|
title: 'Shipping Address Information',
|
||||||
form: <ShippingInfoForm onConfirm={onConfirm} id={CheckoutStep.ShippingAddressInfo} activeStep={activeStep}/>,
|
form: <ShippingInfoForm onConfirm={onConfirm} id={CheckoutStep.ShippingAddressInfo} activeStep={activeStep} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: CheckoutStep.ShippingMethodInfo,
|
id: CheckoutStep.ShippingMethodInfo,
|
||||||
|
@@ -19,21 +19,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.options {
|
.options {
|
||||||
@apply absolute transition-all duration-200 overflow-hidden;
|
|
||||||
transform: translateX(-100%);
|
|
||||||
opacity: 0;
|
|
||||||
margin-top: 0.8rem;
|
margin-top: 0.8rem;
|
||||||
top: 6rem;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--white);
|
background: var(--white);
|
||||||
border: 1px solid var(--border-line);
|
border: 1px solid var(--border-line);
|
||||||
border-radius: 0.8rem;
|
border-radius: 0.8rem;
|
||||||
&.show {
|
|
||||||
z-index: 10;
|
|
||||||
opacity: 1;
|
|
||||||
transform: none;
|
|
||||||
position: initial;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,31 +1,12 @@
|
|||||||
import { ShippingMethodQuote } from '@framework/schema'
|
import { ShippingMethodQuote } from '@framework/schema'
|
||||||
import classNames from 'classnames'
|
|
||||||
import React, { memo, useState } from 'react'
|
import React, { memo, useState } from 'react'
|
||||||
import { useMessage } from 'src/components/contexts'
|
import { useMessage } from 'src/components/contexts'
|
||||||
import { useSetOrderShippingMethod } from 'src/components/hooks/order'
|
import { useEligibleShippingMethods, useSetOrderShippingMethod } from 'src/components/hooks/order'
|
||||||
import { Shipping } from 'src/components/icons'
|
import { Shipping } from 'src/components/icons'
|
||||||
import { CheckoutStep } from '../../CheckoutInfo'
|
import { CheckoutStep } from '../../CheckoutInfo'
|
||||||
import s from './ShippingMethod.module.scss'
|
import s from './ShippingMethod.module.scss'
|
||||||
import ShippingMethodItem from './ShippingMethodItem/ShippingMethodItem'
|
import ShippingMethodItem from './ShippingMethodItem/ShippingMethodItem'
|
||||||
|
|
||||||
const MOCKUP_DATA = [
|
|
||||||
{
|
|
||||||
"id": "1",
|
|
||||||
"name": "Standard Shipping",
|
|
||||||
"description": "",
|
|
||||||
"price": 0,
|
|
||||||
"priceWithTax": 0,
|
|
||||||
"code": "standard-shipping"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2",
|
|
||||||
"name": "Express Shipping",
|
|
||||||
"description": "",
|
|
||||||
"price": 1000,
|
|
||||||
"priceWithTax": 1000,
|
|
||||||
"code": "express-shipping"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
interface Props {
|
interface Props {
|
||||||
currency: string
|
currency: string
|
||||||
onConfirm: (id: number) => void
|
onConfirm: (id: number) => void
|
||||||
@@ -33,20 +14,19 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ShippingMethod = memo(({ currency, onConfirm }: Props) => {
|
const ShippingMethod = memo(({ currency, onConfirm }: Props) => {
|
||||||
|
const { eligibleShippingMethods } = useEligibleShippingMethods()
|
||||||
const { setOrderShippingMethod } = useSetOrderShippingMethod()
|
const { setOrderShippingMethod } = useSetOrderShippingMethod()
|
||||||
const [selectedValue, setSelectedValue] = useState<ShippingMethodQuote>(MOCKUP_DATA[0])
|
const [selectedValue, setSelectedValue] = useState<ShippingMethodQuote | undefined>(eligibleShippingMethods ? eligibleShippingMethods[0] : undefined)
|
||||||
const { showMessageError } = useMessage()
|
const { showMessageError } = useMessage()
|
||||||
const [isShowOptions, setIsShowOptions] = useState<boolean>(true)
|
|
||||||
|
|
||||||
const onChange = (id: string) => {
|
const onChange = (id: string) => {
|
||||||
const newValue = MOCKUP_DATA.find(item => item.id === id)
|
const newValue = eligibleShippingMethods?.find(item => item.id === id)
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
setSelectedValue(newValue)
|
setSelectedValue(newValue)
|
||||||
if (newValue?.id) {
|
if (newValue?.id) {
|
||||||
setOrderShippingMethod(newValue?.id, onSubmitCalBack)
|
setOrderShippingMethod(newValue?.id, onSubmitCalBack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setIsShowOptions(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
|
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
|
||||||
@@ -57,31 +37,26 @@ const ShippingMethod = memo(({ currency, onConfirm }: Props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCollapseOptions = () => {
|
|
||||||
setIsShowOptions(!isShowOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={s.shippingMethod}>
|
<div className={s.shippingMethod}>
|
||||||
<div className={s.method} onClick={onCollapseOptions}>
|
<div className={s.method}>
|
||||||
<div className={s.left}>
|
<div className={s.left}>
|
||||||
<div className={s.icon}>
|
<div className={s.icon}>
|
||||||
<Shipping />
|
<Shipping />
|
||||||
</div>
|
</div>
|
||||||
<div className={s.name}>
|
<div className={s.name}>
|
||||||
{selectedValue.name}
|
{selectedValue?.name}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={s.right}>
|
<div className={s.right}>
|
||||||
<div className={s.price}>
|
<div className={s.price}>
|
||||||
{selectedValue.price ? `${selectedValue.price / 100} ${currency}` : "Free"}
|
{selectedValue?.price ? `${selectedValue?.price / 100} ${currency}` : "Free"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classNames(s.options, { [s.show]: isShowOptions })}>
|
<div className={s.options}>
|
||||||
<ul>
|
<ul>
|
||||||
{MOCKUP_DATA.map(item => <ShippingMethodItem
|
{eligibleShippingMethods?.map(item => <ShippingMethodItem
|
||||||
key={item.id}
|
key={item.id}
|
||||||
id={item.id}
|
id={item.id}
|
||||||
name={item.name}
|
name={item.name}
|
||||||
|
@@ -2,7 +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/useGetActiveOrderForCheckout'
|
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 '..'
|
||||||
@@ -24,7 +24,7 @@ const CheckoutPage = ({ }: CheckoutPageProps) => {
|
|||||||
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} currency={order?.currency.code}/></div>
|
<div className={s.left}><CheckoutInfo onViewCart={onViewCart} currency={order?.currency.code} /></div>
|
||||||
<div className={s.right}><CheckoutBill data={order} /></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}>
|
||||||
|
Reference in New Issue
Block a user