feat: add shipping address to order

:%s
This commit is contained in:
lytrankieio123
2021-10-19 17:30:00 +07:00
parent 461ee218fe
commit ae647003bc
24 changed files with 3767 additions and 159 deletions

View File

@@ -1,6 +1,7 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { ButtonCommon, Logo } from 'src/components/common'
import CheckoutCollapse from 'src/components/common/CheckoutCollapse/CheckoutCollapse'
import { useActiveCustomer } from 'src/components/hooks/auth'
import { useAddProductToCart, useGetActiveOrder } from 'src/components/hooks/cart'
import { removeItem } from 'src/utils/funtion.utils'
import { CheckOutForm } from 'src/utils/types.utils'
@@ -12,39 +13,79 @@ interface CheckoutInfoProps {
onViewCart: () => void
}
enum CheckoutStep {
CustomerInfo = 1,
ShippingInfo = 2,
PaymentInfo = 3,
}
const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
const [active, setActive] = useState(1)
const [done, setDone] = useState<number[]>([])
const [activeStep, setActiveStep] = useState(1)
const [doneSteps, setDoneSteps] = useState<CheckoutStep[]>([])
const [info, setInfo] = useState<CheckOutForm>({})
const { order } = useGetActiveOrder()
const { customer } = useActiveCustomer()
const onEdit = (id: number) => {
setActive(id)
setDone(removeItem<number>(done, id))
}
useEffect(() => {
if (customer) {
if (!doneSteps.includes(CheckoutStep.CustomerInfo)) {
const onConfirm = (id: number, formInfo: CheckOutForm) => {
if (id + 1 > formList.length) {
console.log({ ...info, ...formInfo })
} else {
if (done.length > 0) {
for (let i = id + 1; i <= formList.length; i++) {
if (!done.includes(i)) {
setActive(i)
if (doneSteps.length > 0) {
for (let i = CheckoutStep.CustomerInfo + 1; i <= Object.keys(CheckoutStep).length; i++) {
if (!doneSteps.includes(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)
setDoneSteps(removeItem<number>(doneSteps, 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)
}
}
} 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) {
case 1:
return `${info.name}, ${info.email}`
case 2:
case CheckoutStep.CustomerInfo:
// console.log("order info; ", order?.customer)
if (order?.customer) {
return `${order?.customer?.firstName} ${order?.customer?.lastName}, ${order?.customer?.emailAddress}`
} else if (customer) {
return `${customer.firstName} ${customer.lastName}, ${customer.emailAddress}`
} else {
return ''
}
case CheckoutStep.ShippingInfo:
return `${info.address}, ${info.state}, ${info.city}, ${info.code}, ${info.phone}, `
default:
return ""
@@ -53,19 +94,19 @@ const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
const formList = [
{
id: 1,
id: CheckoutStep.CustomerInfo,
title: 'Customer Information',
form: <CustomerInfoForm onConfirm={onConfirm} id={1} />,
form: <CustomerInfoForm onConfirm={onConfirm} id={CheckoutStep.CustomerInfo}/>,
},
{
id: 2,
id: CheckoutStep.ShippingInfo,
title: 'Shipping Information',
form: <ShippingInfoForm onConfirm={onConfirm} id={2} />,
form: <ShippingInfoForm onConfirm={onConfirm} id={CheckoutStep.ShippingInfo} activeStep={activeStep}/>,
},
{
id: 3,
id: CheckoutStep.PaymentInfo,
title: 'Payment Information',
form: <PaymentInfoForm onConfirm={onConfirm} id={3} />,
form: <PaymentInfoForm onConfirm={onConfirm} id={CheckoutStep.PaymentInfo} />,
},
]
@@ -77,15 +118,16 @@ const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
}
const handleAddToCartCallback = (isSuccess: boolean, message?: string) => {
// console.log("after create order: ", isSuccess, message)
}
const {order} = useGetActiveOrder()
return (
<div className={s.warpper}>
{/* TODO: remove */}
<ButtonCommon onClick={createOrder}>test create order</ButtonCommon>
<ButtonCommon onClick={createOrder}>test get active order</ButtonCommon>
<ButtonCommon onClick={createOrder}>test get activeStep order</ButtonCommon>
<div className={s.title}>
<Logo />
<div className={s.viewCart} onClick={onViewCart}>View cart</div>
@@ -95,11 +137,12 @@ const CheckoutInfo = ({ onViewCart }: CheckoutInfoProps) => {
return <CheckoutCollapse
key={item.title}
id={item.id}
visible={item.id === active}
visible={item.id === activeStep}
title={item.title}
onEditClick={onEdit}
isEdit={done.includes(item.id)}
isEdit={doneSteps.includes(item.id)}
note={note}
disableEdit={customer && item.id === CheckoutStep.CustomerInfo}
>
{item.form}
</CheckoutCollapse>

View File

@@ -1,16 +1,22 @@
import { Form, Formik } from 'formik'
import React, { useRef } from 'react'
import React, { 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'
import { useModalCommon } from 'src/components/hooks'
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 { CustomInputCommon } from 'src/utils/type.utils'
import { CheckOutForm } from 'src/utils/types.utils'
import * as Yup from 'yup'
import ChekoutNotePolicy from '../ChekoutNotePolicy/ChekoutNotePolicy'
import s from './CustomerInfoForm.module.scss'
import ModalConfirmLogin from './ModalConfirmLogin/ModalConfirmLogin'
interface Props {
isHide: boolean
onSwitch: () => void
id: number
onConfirm: (id: number) => void
}
const displayingErrorMessagesSchema = Yup.object().shape({
@@ -19,32 +25,36 @@ const displayingErrorMessagesSchema = Yup.object().shape({
emailAddress: Yup.string().email(LANGUAGE.MESSAGE.INVALID_EMAIL).required(LANGUAGE.MESSAGE.REQUIRED),
})
const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
const CustomerInfoForm = ({ id, onConfirm }: Props) => {
const firstNameRef = useRef<CustomInputCommon>(null)
const emailRef = useRef<CustomInputCommon>(null)
const { setCustomerForOrder, loading } = useSetCustomerForOrder()
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 })
const handleSubmit = (values: { firstName: string, lastName: string, emailAddress: string }) => {
console.log('on submit: ', values)
const { firstName, lastName, emailAddress } = values
setEmailAddress(emailAddress)
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:
console.log("result: ", isSuccess, msg)
if (isSuccess) {
onConfirm(id)
} else {
console.log("error here")
showMessageError(msg)
if (error?.errorCode === ErrorCode.EmailAddressConflictError) {
// show modal common
openModalConfirmLogin()
} else {
showMessageError(error?.message)
}
}
}
const handleCloseModalConfirmLogin = () => {
closeModalConfirmLogin()
openModalAuthen()
}
return (
@@ -58,7 +68,6 @@ const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
}}
validationSchema={displayingErrorMessagesSchema}
onSubmit={handleSubmit}
>
{({ errors, touched, isValid, submitForm }) => (
<Form className="u-form">
@@ -90,7 +99,6 @@ const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
<InputFiledInForm
name="emailAddress"
placeholder="Email Address"
ref={emailRef}
error={
touched.emailAddress && errors.emailAddress
? errors.emailAddress.toString()
@@ -111,6 +119,8 @@ const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
)}
</Formik>
</div>
<ModalConfirmLogin visible={visibleModalConfirmLogin} closeModal={closeModalConfirmLogin} handleOk={handleCloseModalConfirmLogin} email={emailAddress} />
<ModalAuthenticate visible={visibleModalAuthen} closeModal={closeModalAuthen} initialEmail={emailAddress} disableRedirect={true} />
</section>
)
}

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { ModalConfirm } from 'src/components/common';
import { LANGUAGE } from 'src/utils/language.utils';
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>
<p> Account already exists for email {email} </p>
<p>Please signin to continue or use another email</p>
</div>
</ModalConfirm>
</div>
);
};
export default ModalConfirmLogin;

View File

@@ -1,17 +1,37 @@
import React, { useRef } from 'react'
import { ButtonCommon, Inputcommon, SelectCommon } from 'src/components/common'
import React, { useEffect, useRef } from 'react'
import { ButtonCommon, Inputcommon, InputFiledInForm, SelectCommon, SelectFieldInForm } from 'src/components/common'
import s from './ShippingInfoForm.module.scss'
import Link from 'next/link'
import { CustomInputCommon } from 'src/utils/type.utils'
import { Shipping } from 'src/components/icons'
import { CheckOutForm } from 'src/utils/types.utils'
import { Form, Formik } from 'formik'
import { LANGUAGE } from 'src/utils/language.utils'
import * as Yup from 'yup'
import ChekoutNotePolicy from '../ChekoutNotePolicy/ChekoutNotePolicy'
import { useSetOrderShippingAddress } from 'src/components/hooks/order'
import { useMessage } from 'src/components/contexts'
import { COUNTRY_CODE } from 'src/domains/data/countryCode'
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 provinceOptions = [
{
name: 'Hồ Chí Minh',
value: 'Hồ Chí Minh',
@@ -22,77 +42,147 @@ const option = [
},
]
const ShippingInfoForm = ({onConfirm,id}: ShippingInfoFormProps) => {
const ShippingInfoForm = ({ onConfirm, id, activeStep }: ShippingInfoFormProps) => {
const addressRef = useRef<CustomInputCommon>(null)
const cityRef = useRef<CustomInputCommon>(null)
const stateRef = useRef<CustomInputCommon>(null)
const codeRef = useRef<CustomInputCommon>(null)
const phoneRef = useRef<CustomInputCommon>(null)
const handleConfirmClick = () => {
onConfirm && onConfirm(id,{
address: addressRef?.current?.getValue().toString(),
city: cityRef.current?.getValue().toString(),
state: stateRef?.current?.getValue().toString(),
code: Number(codeRef.current?.getValue()),
phone: Number(phoneRef?.current?.getValue()),
})
const { setOrderShippingAddress } = useSetOrderShippingAddress()
const { showMessageError } = useMessage()
useEffect(() => {
setTimeout(() => {
addressRef.current?.focus()
}, 500);
}, [activeStep])
const handleSubmit = (values: any) => {
console.log("values: ", values)
setOrderShippingAddress(values, onSubmitCalBack)
// onConfirm && onConfirm(id)
}
const onSubmitCalBack = (isSuccess: boolean, msg?: string) => {
if (isSuccess) {
onConfirm(id)
} else {
showMessageError(msg)
}
}
return (
<div className={s.warpper}>
<div className={s.body}>
<Inputcommon
type="text"
placeholder="Street Address"
ref={addressRef}
/>
<Inputcommon type="text" placeholder="City" ref={cityRef} />
<div className={s.line}>
<SelectCommon options={option} type="custom" size="large">State</SelectCommon>
<Inputcommon type="text" placeholder="Zip Code" ref={codeRef} />
</div>
<Inputcommon
type="text"
placeholder="Phone (delivery contact)"
ref={phoneRef}
/>
<div className={s.method}>
<div className={s.left}>
<div className={s.icon}>
<Shipping/>
</div>
<div className={s.name}>
Standard Delivery Method
</div>
</div>
<div className={s.right}>
<div className={s.price}>
Free
</div>
</div>
</div>
</div>
<div className={s.bottom}>
<div className={s.note}>
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}>
<ButtonCommon onClick={handleConfirmClick}>
Continue to Payment
</ButtonCommon>
</div>
<Formik
initialValues={
{
streetLine1: '',
city: '',
province: '',
postalCode: '',
countryCode: '',
phoneNumber: '',
}}
validationSchema={displayingErrorMessagesSchema}
onSubmit={handleSubmit}
>
{({ errors, touched, isValid, submitForm }) => (
<Form className="u-form">
<div className="body">
<div className={s.input}>
<InputFiledInForm
name="streetLine1"
placeholder="Address"
ref={addressRef}
error={
touched.streetLine1 && errors.streetLine1
? errors.streetLine1.toString()
: ''
}
isShowIconSuccess={touched.streetLine1 && !errors.streetLine1}
/>
</div>
<div className="line">
<div className={s.input}>
<InputFiledInForm
name="city"
placeholder="City"
error={
touched.city && errors.city
? errors.city.toString()
: ''
}
isShowIconSuccess={touched.city && !errors.city}
/>
</div>
<div className={s.input}>
<SelectFieldInForm
options={provinceOptions}
name="province"
placeholder="Province"
error={
touched.province && errors.province
? errors.province.toString()
: ''
}
/>
</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={COUNTRY_CODE}
keyNameOption={['name', 'alpha-2']}
keyValueOption="alpha-2"
name="countryCode"
placeholder="Country"
nameSeperator=" - "
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"> */}
<ButtonCommon HTMLType='submit' size="large">
Continue to Payment
</ButtonCommon>
</div>
</div>
</Form>
)}
</Formik>
</div>
</div>
)

View File

@@ -1,6 +1,6 @@
@import "../../../../styles/utilities";
.warrper{
@apply flex w-full h-full absolute;
@apply flex w-full;
.right {
display: none;
@screen lg {