feat: collapse checkout custom form shipping form

:%s
This commit is contained in:
unknown 2021-09-07 09:17:59 +07:00
parent d1f22b1c44
commit 757cb68319
18 changed files with 375 additions and 358 deletions

View File

@ -1,15 +1,12 @@
import { useState } from 'react'
import {
ButtonCommon,
CardItemCheckout,
Layout,
ModalCommon,
ModalConfirm,
ModalInfo,
ProductCarousel,
ProductList,
} from 'src/components/common'
import PaginationCommon from 'src/components/common/PaginationCommon/PaginationCommon'
import CheckoutCollapse from 'src/components/modules/checkout/components/CheckoutCollapse/CheckoutCollapse'
import CustomerInfoForm from 'src/components/modules/checkout/components/CustomerInfoForm/CustomerInfoForm'
import ShippingInfoForm from 'src/components/modules/checkout/components/ShippingInfoForm/ShippingInfoForm'
import image5 from '../public/assets/images/image5.png'
import image6 from '../public/assets/images/image6.png'
import image7 from '../public/assets/images/image7.png'
@ -22,146 +19,6 @@ const dataTest = [
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Cucumber',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image6.src,
},
{
name: 'Carrot',
weight: '250g',
@ -197,209 +54,6 @@ const dataTest = [
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Cucumber',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image6.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Cucumber',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image6.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Carrot',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
},
{
name: 'Salad',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
},
{
name: 'Tomato',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image5.src,
},
{
name: 'Cucumber',
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image6.src,
},
]
export default function Test() {
const [visible, setVisible] = useState(false)
@ -409,13 +63,23 @@ export default function Test() {
const onOpen = () => {
setVisible(true)
}
const [visible2, setVisible2] = useState(false)
const onClose2 = () => {
setVisible2(false)
}
const onOpen2 = () => {
setVisible2(true)
}
return (
<>
<CardItemCheckout {...dataTest[0]} quantity={2}/>
<div className="w-full" >
<ButtonCommon>
test
</ButtonCommon>
<div className="w-full" style={{padding: "0 3.2rem"}} >
<CheckoutCollapse id={1} visible={visible} onOpen={onOpen} onClose={onClose} title="Customer Information" isEdit={true}>
<CustomerInfoForm/>
</CheckoutCollapse>
<CheckoutCollapse id={2} visible={visible2} onOpen={onOpen2} onClose={onClose2} title="Shipping Information" isEdit={true}>
<ShippingInfoForm/>
</CheckoutCollapse>
</div>
</>
)

View File

@ -15,9 +15,9 @@
.icon + .inputCommon {
padding-left: 4.8rem;
}
.inputCommon {
@apply block w-full transition-all duration-200 rounded;
@apply block w-full transition-all duration-200 rounded bg-white;
padding: 1.2rem 1.6rem;
border: 1px solid var(--border-line);
&:hover,

View File

@ -6,6 +6,7 @@ import s from './InputCommon.module.scss';
type Ref = {
focus: () => void
getValue: () => string | number
} | null;
interface Props {
children?: React.ReactNode,

View File

@ -1,6 +1,6 @@
import React, { useRef } from 'react'
import { Close } from 'src/components/icons'
import { useOnClickOutside } from 'src/utils/useClickOutSide'
import { useOnClickOutside } from 'src/components/hooks/useClickOutSide'
import s from './ModalCommon.module.scss'
export interface ModalCommonProps {
onClose: () => void

View File

@ -1,5 +1,5 @@
import { RefObject, useEffect } from 'react'
import { MouseAndTouchEvent } from './types.utils'
import { MouseAndTouchEvent } from '../../utils/types.utils'
export function useOnClickOutside<T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,

View File

@ -0,0 +1,20 @@
import React from 'react'
const Shipping = () => {
return (
<svg
width="22"
height="20"
viewBox="0 0 22 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 10.5V15.5C22 15.7652 21.8946 16.0196 21.7071 16.2071C21.5196 16.3946 21.2652 16.5 21 16.5H20C20 17.2956 19.6839 18.0587 19.1213 18.6213C18.5587 19.1839 17.7956 19.5 17 19.5C16.2044 19.5 15.4413 19.1839 14.8787 18.6213C14.3161 18.0587 14 17.2956 14 16.5H8C8 17.2956 7.68393 18.0587 7.12132 18.6213C6.55871 19.1839 5.79565 19.5 5 19.5C4.20435 19.5 3.44129 19.1839 2.87868 18.6213C2.31607 18.0587 2 17.2956 2 16.5H1C0.734783 16.5 0.480429 16.3946 0.292892 16.2071C0.105356 16.0196 0 15.7652 0 15.5V3.5C0 2.70435 0.316071 1.94129 0.878679 1.37868C1.44129 0.816071 2.20435 0.5 3 0.5H12C12.7956 0.5 13.5587 0.816071 14.1213 1.37868C14.6839 1.94129 15 2.70435 15 3.5V5.5H17C17.4657 5.5 17.9251 5.60844 18.3416 5.81672C18.7582 6.025 19.1206 6.32741 19.4 6.7L21.8 9.9C21.8292 9.94347 21.8528 9.99052 21.87 10.04L21.93 10.15C21.9741 10.2615 21.9978 10.3801 22 10.5ZM6 16.5C6 16.3022 5.94135 16.1089 5.83147 15.9444C5.72159 15.78 5.56541 15.6518 5.38268 15.5761C5.19996 15.5004 4.99889 15.4806 4.80491 15.5192C4.61093 15.5578 4.43274 15.653 4.29289 15.7929C4.15304 15.9327 4.0578 16.1109 4.01921 16.3049C3.98063 16.4989 4.00043 16.7 4.07612 16.8827C4.15181 17.0654 4.27998 17.2216 4.44443 17.3315C4.60888 17.4414 4.80222 17.5 5 17.5C5.26522 17.5 5.51957 17.3946 5.70711 17.2071C5.89464 17.0196 6 16.7652 6 16.5ZM13 3.5C13 3.23478 12.8946 2.98043 12.7071 2.79289C12.5196 2.60536 12.2652 2.5 12 2.5H3C2.73478 2.5 2.48043 2.60536 2.29289 2.79289C2.10536 2.98043 2 3.23478 2 3.5V14.5H2.78C3.06118 14.1906 3.40391 13.9435 3.78622 13.7743C4.16852 13.6052 4.58195 13.5178 5 13.5178C5.41805 13.5178 5.83148 13.6052 6.21378 13.7743C6.59609 13.9435 6.93882 14.1906 7.22 14.5H13V3.5ZM15 9.5H19L17.8 7.9C17.7069 7.7758 17.5861 7.675 17.4472 7.60557C17.3084 7.53615 17.1552 7.5 17 7.5H15V9.5ZM18 16.5C18 16.3022 17.9414 16.1089 17.8315 15.9444C17.7216 15.78 17.5654 15.6518 17.3827 15.5761C17.2 15.5004 16.9989 15.4806 16.8049 15.5192C16.6109 15.5578 16.4327 15.653 16.2929 15.7929C16.153 15.9327 16.0578 16.1109 16.0192 16.3049C15.9806 16.4989 16.0004 16.7 16.0761 16.8827C16.1518 17.0654 16.28 17.2216 16.4444 17.3315C16.6089 17.4414 16.8022 17.5 17 17.5C17.2652 17.5 17.5196 17.3946 17.7071 17.2071C17.8946 17.0196 18 16.7652 18 16.5ZM20 11.5H15V14.28C15.5902 13.7526 16.3649 13.4797 17.1553 13.5209C17.9457 13.5621 18.6879 13.914 19.22 14.5H20V11.5Z"
fill="#141414"
/>
</svg>
)
}
export default Shipping

View File

@ -23,4 +23,5 @@ export { default as IconPasswordCross } from './IconPasswordCross'
export { default as IconError } from './IconError'
export { default as IconCheck } from './IconCheck'
export { default as IconPlus } from './IconPlus'
export { default as IconMinus } from './IconMinus'
export { default as IconMinus } from './IconMinus'
export { default as Shipping} from './Shipping'

View File

@ -0,0 +1,23 @@
import React from 'react'
import s from "CheckoutBill.module.scss"
interface CheckoutBillProps {
}
const CheckoutBill = ({}: CheckoutBillProps) => {
return (
<div className={s.warpper}>
<div className={s.list}>
</div>
<div className={s.promo}>
</div>
<div className={s.price}>
</div>
</div>
)
}
export default CheckoutBill

View File

@ -0,0 +1,39 @@
.warpper{
.header{
@apply flex justify-between;
padding-bottom: 3.2rem;
.left{
@apply flex items-center;
.number{
width: 3.2rem;
height: 3.2rem;
border-radius: 100%;
border: 1px solid var(--text-active);
color: var(--text-active);
@apply flex justify-center items-center font-bold;
&.visible{
background-color: var(--text-active);
border: none;
color: var(--white);
}
}
.title{
padding-left: 2.4rem;
@apply font-bold select-none cursor-pointer;
color: var(--text-active);
}
}
.edit{
@apply font-bold cursor-pointer;
text-decoration-line: underline;
margin-right: 5.6rem;
}
}
.body{
height: 0;
@apply overflow-hidden;
&.show{
height: initial;
}
}
}

View File

@ -0,0 +1,48 @@
import classNames from 'classnames'
import React from 'react'
import s from './CheckoutCollapse.module.scss'
interface CheckoutCollapseProps {
visible: boolean
id: number
children: React.ReactNode
title: string
isEdit: boolean
onClose: () => void
onOpen: () => void
}
const CheckoutCollapse = ({
children,
id,
title,
isEdit,
visible,
onOpen,
onClose,
}: CheckoutCollapseProps) => {
const handleTitleClick = () => {
if(visible){
onClose && onClose()
}else{
onOpen && onOpen()
}
}
return (
<div className={s.warpper}>
<div className={s.header}>
<div className={s.left}>
<div className={classNames(s.number, { [`${s.visible}`]: visible })}>
{id}
</div>
<div className={s.title} onClick={handleTitleClick}>
{title}
</div>
</div>
{isEdit && <div className={s.edit}>{'Edit'}</div>}
</div>
<div className={classNames(s.body, { [`${s.show}`]: visible })}>{children}</div>
</div>
)
}
export default CheckoutCollapse

View File

@ -0,0 +1,13 @@
@import "../../../../../styles/utilities";
.warpper{
@apply u-form;
padding: 0 5.6rem;
.bottom{
margin-top: 2.4rem;
@apply flex justify-between items-center;
.note{
font-size: 1.2rem;
line-height: 2rem;
}
}
}

View File

@ -0,0 +1,51 @@
import Link from 'next/link'
import React, { useRef } from 'react'
import { ButtonCommon, Inputcommon } from 'src/components/common'
import InputCommon from 'src/components/common/InputCommon/InputCommon'
import s from './CustomerInfoForm.module.scss'
interface CustomerInfoFormProps {
onConfirm?: ()=>void
}
const CustomerInfoForm = ({}: CustomerInfoFormProps) => {
const nameRef = useRef<React.ElementRef<typeof InputCommon>>(null);
const emailRef = useRef<React.ElementRef<typeof InputCommon>>(null);
const handleConfirmClick = () => {
return {
name:nameRef?.current?.getValue(),
email:emailRef.current?.getValue()
}
}
return (
<div className={s.warpper}>
<div className={s.body}>
<Inputcommon type="text" placeholder="Full Name" ref={nameRef}/>
<Inputcommon type="text" placeholder="Email Address" ref={emailRef}/>
</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>
<ButtonCommon onClick={handleConfirmClick}>Continue to Shipping</ButtonCommon>
</div>
</div>
)
}
export default CustomerInfoForm

View File

@ -0,0 +1,15 @@
import React from 'react'
interface PaymentInfoFormProps {
}
const PaymentInfoForm = (props: PaymentInfoFormProps) => {
return (
<div>
</div>
)
}
export default PaymentInfoForm

View File

@ -0,0 +1,36 @@
@import "../../../../../styles/utilities";
.warpper{
@apply u-form;
padding: 0 5.6rem;
.bottom{
margin-top: 2.4rem;
@apply flex justify-between items-center;
.note{
font-size: 1.2rem;
line-height: 2rem;
}
}
.line{
>div{
width: 50%;
}
}
.method{
width: 100%;
height: 5.6rem;
padding: 1.6rem;
border-radius: 0.8rem;
@apply flex justify-between items-center border border-solid border-line bg-gray;
.left{
@apply flex;
.name{
margin-left: 1.6rem;
color: var(--text-active);
}
}
.price{
font-weight: bold;
color: var(--text-active);
}
}
}

View File

@ -0,0 +1,93 @@
import React, { useRef } from 'react'
import { ButtonCommon, Inputcommon, SelectCommon } 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'
interface ShippingInfoFormProps {}
const option = [
{
name: 'Hồ Chí Minh',
},
{
name: 'Hà Nội',
},
]
const ShippingInfoForm = ({}: 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 = () => {
return {
address: addressRef?.current?.getValue(),
city: cityRef.current?.getValue(),
state: stateRef?.current?.getValue(),
code: codeRef.current?.getValue(),
phone: phoneRef?.current?.getValue(),
}
}
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 option={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>
<ButtonCommon onClick={handleConfirmClick}>
Continue to Payment
</ButtonCommon>
</div>
</div>
)
}
export default ShippingInfoForm

View File

@ -20,4 +20,17 @@ export interface RecipeProps {
imageSrc: string
}
export interface CheckOutForm {
name: string
email:string
address: string
city:string
state:string
code:number
phone:number
method:string
shipping_fee:number
}
export type MouseAndTouchEvent = MouseEvent | TouchEvent