feat: apply coupon code for order

:%s
This commit is contained in:
lytrankieio123
2021-10-19 18:48:10 +07:00
parent 9603f0dc6c
commit 693935a480
14 changed files with 229 additions and 23 deletions

View File

@@ -3053,6 +3053,7 @@ export type CartFragment = { __typename?: 'Order' } & Pick<
| 'currencyCode'
> & {
shippingAddress?: Maybe<{ __typename?: 'OrderAddress' } & Pick<OrderAddress, 'streetLine1' | 'fullName' | 'city' | 'province' | 'postalCode' |'countryCode' | 'phoneNumber'>>
discounts?: Maybe<{ __typename?: 'Discount' } & Pick<Discount, 'type' | 'amount' | 'amountWithTax'>>
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id' | 'firstName' | 'lastName' | 'emailAddress'>>
lines: Array<
{ __typename?: 'OrderLine' } & Pick<
@@ -3156,6 +3157,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<{
username: Scalars['String']
password: Scalars['String']

View File

@@ -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
}
}
}
`

View File

@@ -1,7 +1,7 @@
@import "../../../styles/utilities";
.buttonCommon {
@apply shape-common;
@apply shape-common h-full;
&:hover {
.inner {
@apply shadow-md;

View File

@@ -1,3 +1,4 @@
export { default as useSetCustomerForOrder } from './useSetCustomerForOrder'
export { default as useSetOrderShippingAddress } from './useSetOrderShippingAddress'
export { default as useApplyCouponCode } from './useApplyCouponCode'

View 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 { useGetActiveOrder } from '../cart'
const useApplyCouponCode = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<CommonError | null>(null)
const { mutate } = useGetActiveOrder()
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

View File

@@ -18,11 +18,6 @@
min-height: 52.8rem;
}
.bot {
.promo {
// padding: 3.2rem;
@apply bg-gray flex justify-between items-center;
min-height: 6.4rem;
}
.price {
margin-top: 3.2rem;
.line {

View File

@@ -1,8 +1,8 @@
import React from 'react'
import s from './CheckoutBill.module.scss'
import { CardItemCheckout } from '../../../common'
import { CardItemCheckoutProps } from '../../../common/CardItemCheckout/CardItemCheckout'
import { IconCirclePlus } from 'src/components/icons'
import s from './CheckoutBill.module.scss'
import FormPromotionCode from './FormPromotionCode/FormPromotionCode'
interface CheckoutBillProps {
data: CardItemCheckoutProps[]
@@ -20,10 +20,7 @@ const CheckoutBill = ({ data }: CheckoutBillProps) => {
})}
</div>
<div className={s.bot}>
<div className={s.promo}>
Apply Promotion Code
<IconCirclePlus />
</div>
<FormPromotionCode/>
<div className={s.price}>
<div className={s.line}>
Subtotal

View File

@@ -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;
}
}
}
}
}
}

View File

@@ -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="Coupon 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;

View File

@@ -132,7 +132,7 @@ const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
{/* TODO: remove */}
<ButtonCommon onClick={createOrder}>test create order</ButtonCommon>
<ButtonCommon onClick={createOrder}>test get activeStep order</ButtonCommon>
TOTAL: {order?.totalPrice}
<div className={s.title}>
<Logo />

View File

@@ -1,5 +1,5 @@
import { Form, Formik } from 'formik'
import React, { useRef, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { ButtonCommon, InputFiledInForm } from 'src/components/common'
import ModalAuthenticate from 'src/components/common/ModalAuthenticate/ModalAuthenticate'
import { useMessage } from 'src/components/contexts'
@@ -16,6 +16,8 @@ import ModalConfirmLogin from './ModalConfirmLogin/ModalConfirmLogin'
interface Props {
id: number
onConfirm: (id: number) => void
activeStep: number
}
const displayingErrorMessagesSchema = Yup.object().shape({
@@ -24,7 +26,7 @@ const displayingErrorMessagesSchema = Yup.object().shape({
emailAddress: Yup.string().email(LANGUAGE.MESSAGE.INVALID_EMAIL).required(LANGUAGE.MESSAGE.REQUIRED),
})
const CustomerInfoForm = ({ id, onConfirm }: Props) => {
const CustomerInfoForm = ({ id, onConfirm, activeStep }: Props) => {
const firstNameRef = useRef<CustomInputCommon>(null)
const { setCustomerForOrder, loading } = useSetCustomerForOrder()
const { showMessageError } = useMessage()
@@ -32,6 +34,11 @@ const CustomerInfoForm = ({ id, onConfirm }: Props) => {
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 { firstName, lastName, emailAddress } = values

View File

@@ -41,7 +41,7 @@ const provinceOptions = [
const ShippingInfoForm = ({ onConfirm, id, activeStep }: ShippingInfoFormProps) => {
const addressRef = useRef<CustomInputCommon>(null)
const { setOrderShippingAddress } = useSetOrderShippingAddress()
const { setOrderShippingAddress, loading } = useSetOrderShippingAddress()
const { showMessageError } = useMessage()
useEffect(() => {
@@ -171,8 +171,7 @@ const ShippingInfoForm = ({ onConfirm, id, activeStep }: ShippingInfoFormProps)
</div>
<div className={s.bottom}>
<ChekoutNotePolicy />
{/* <ButtonCommon HTMLType='submit' loading={loading} size="large"> */}
<ButtonCommon HTMLType='submit' size="large">
<ButtonCommon HTMLType='submit' loading={loading} size="large">
Continue to Payment
</ButtonCommon>
</div>

View File

@@ -46,10 +46,7 @@
color:var(--text-base);
}
}
button{
margin-top: 2rem;
width: 100%;
}
}
}
}

View File

@@ -5,7 +5,8 @@ export const LANGUAGE = {
CONFIRM:'Confirm',
ADD_TO_CARD: 'Add to Cart',
PREORDER: 'Pre-Order Now',
SIGNIN :'Sign In'
SIGNIN :'Sign In',
CANCEL: 'Cancel',
},
PLACE_HOLDER: {
SEARCH: 'Search',