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

@@ -62,6 +62,11 @@ export type Cart = {
id: string id: string
// ID of the customer to which the cart belongs. // ID of the customer to which the cart belongs.
customerId?: string customerId?: string
customer?: {
firstName: string,
lastName: string,
emailAddress: string,
}
// The email assigned to this cart // The email assigned to this cart
email?: string email?: string
// The date and time when the cart was created. // The date and time when the cart was created.

View File

@@ -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'
@@ -3060,7 +3052,7 @@ export type CartFragment = { __typename?: 'Order' } & Pick<
| 'totalWithTax' | 'totalWithTax'
| 'currencyCode' | 'currencyCode'
> & { > & {
customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>> customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id' | 'firstName' | 'lastName' | 'emailAddress'>>
lines: Array< lines: Array<
{ __typename?: 'OrderLine' } & Pick< { __typename?: 'OrderLine' } & Pick<
OrderLine, OrderLine,

View File

@@ -11,6 +11,9 @@ export const cartFragment = /* GraphQL */ `
currencyCode currencyCode
customer { customer {
id id
firstName
lastName
emailAddress
} }
lines { lines {
id id

View File

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

View File

@@ -45,6 +45,11 @@ export function normalizeCart(order: CartFragment): Cart {
subtotalPrice: order.subTotalWithTax / 100, subtotalPrice: order.subTotalWithTax / 100,
totalPrice: order.totalWithTax / 100, totalPrice: order.totalWithTax / 100,
customerId: order.customer?.id, customerId: order.customer?.id,
customer: {
firstName: order.customer?.firstName || '',
lastName: order.customer?.lastName || '',
emailAddress: order.customer?.emailAddress || '',
},
lineItems: order.lines?.map((l) => ({ lineItems: order.lines?.map((l) => ({
id: l.id, id: l.id,
name: l.productVariant.name, name: l.productVariant.name,

View File

@@ -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
@@ -14,6 +12,7 @@ interface CheckoutCollapseProps {
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 = ({
@@ -25,14 +24,15 @@ const CheckoutCollapse = ({
note, note,
onOpen, onOpen,
onClose, onClose,
onEditClick onEditClick,
disableEdit,
}: CheckoutCollapseProps) => { }: CheckoutCollapseProps) => {
const handleTitleClick = () => { const handleTitleClick = () => {
if(visible){ if(visible){
onClose && onClose(id) onClose && onClose(id)
}else{ } else if (!disableEdit) {
onOpen && onOpen(id) onOpen && onOpen(id)
} }
} }
const handleEdit = () => { const handleEdit = () => {
onEditClick && onEditClick(id) onEditClick && onEditClick(id)
@@ -48,7 +48,7 @@ const CheckoutCollapse = ({
{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}>{note}</div>) }
<div className={classNames(s.body, { [`${s.show}`]: visible })}>{children}</div> <div className={classNames(s.body, { [`${s.show}`]: visible })}>{children}</div>

View File

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

View File

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

View File

@@ -18,14 +18,15 @@ 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"> {cancelText}</ButtonCommon>
</div> </div>
<ButtonCommon onClick={onOk} loading={loading}>{okText}</ButtonCommon> <ButtonCommon onClick={onOk} loading={loading}>{okText}</ButtonCommon>
</div> </div>

View File

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

View 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

View File

@@ -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'

View File

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

View File

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

View File

@@ -12,7 +12,7 @@ const useSetCustomerForOrder = () => {
const { mutate } = useGetActiveOrder() const { mutate } = useGetActiveOrder()
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))
} }

View File

@@ -0,0 +1,43 @@
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 { useGetActiveOrder } from '../cart'
const useSetOrderShippingAddress = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<CommonError | null>(null)
const { mutate } = useGetActiveOrder()
const setOrderShippingAddress = (input: CreateAddressInput,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
rawFetcher<SetOrderShippingAddressMutation>({
query: setOrderShippingAddressMutation,
variables: { input },
})
.then(({ data }) => {
console.log("data: ", 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

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

View File

@@ -1,16 +1,22 @@
import { Form, Formik } from 'formik' import { Form, Formik } from 'formik'
import React, { useRef } from 'react' import React, { 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 { CheckOutForm } from 'src/utils/types.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
} }
const displayingErrorMessagesSchema = Yup.object().shape({ 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), 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 firstNameRef = 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 })
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 {
showMessageError(error?.message)
}
} }
}
const handleCloseModalConfirmLogin = () => {
closeModalConfirmLogin()
openModalAuthen()
} }
return ( return (
@@ -58,7 +68,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,7 +99,6 @@ 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()
@@ -111,6 +119,8 @@ const CustomerInfoForm = ({ onSwitch, isHide }: Props) => {
)} )}
</Formik> </Formik>
</div> </div>
<ModalConfirmLogin visible={visibleModalConfirmLogin} closeModal={closeModalConfirmLogin} handleOk={handleCloseModalConfirmLogin} email={emailAddress} />
<ModalAuthenticate visible={visibleModalAuthen} closeModal={closeModalAuthen} initialEmail={emailAddress} disableRedirect={true} />
</section> </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 React, { useEffect, useRef } from 'react'
import { ButtonCommon, Inputcommon, SelectCommon } from 'src/components/common' import { ButtonCommon, Inputcommon, InputFiledInForm, SelectCommon, SelectFieldInForm } from 'src/components/common'
import s from './ShippingInfoForm.module.scss' import s from './ShippingInfoForm.module.scss'
import Link from 'next/link' import Link from 'next/link'
import { CustomInputCommon } from 'src/utils/type.utils' import { CustomInputCommon } from 'src/utils/type.utils'
import { Shipping } from 'src/components/icons' import { Shipping } from 'src/components/icons'
import { CheckOutForm } from 'src/utils/types.utils' 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 { 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', name: 'Hồ Chí Minh',
value: '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 addressRef = useRef<CustomInputCommon>(null)
const cityRef = useRef<CustomInputCommon>(null) const { setOrderShippingAddress } = useSetOrderShippingAddress()
const stateRef = useRef<CustomInputCommon>(null) const { showMessageError } = useMessage()
const codeRef = useRef<CustomInputCommon>(null)
const phoneRef = useRef<CustomInputCommon>(null) useEffect(() => {
const handleConfirmClick = () => { setTimeout(() => {
onConfirm && onConfirm(id,{ addressRef.current?.focus()
address: addressRef?.current?.getValue().toString(), }, 500);
city: cityRef.current?.getValue().toString(), }, [activeStep])
state: stateRef?.current?.getValue().toString(),
code: Number(codeRef.current?.getValue()), const handleSubmit = (values: any) => {
phone: Number(phoneRef?.current?.getValue()), console.log("values: ", values)
}) setOrderShippingAddress(values, onSubmitCalBack)
// onConfirm && onConfirm(id)
} }
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: '',
<div className={s.line}> postalCode: '',
<SelectCommon options={option} type="custom" size="large">State</SelectCommon> countryCode: '',
<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={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>
</div> </div>
) )

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -34,8 +34,9 @@ export interface BlogProps {
} }
export interface CheckOutForm { export interface CheckOutForm {
name?: string firstName?: string
email?: string lastName?: string
emailAddress?: string
address?: string address?: string
city?: string city?: string
state?: string state?: string