feat: Checkout Page

:%s
This commit is contained in:
unknown 2021-09-09 16:18:51 +07:00
parent 346a776bbd
commit 0eed5ab051
24 changed files with 287 additions and 112 deletions

13
pages/checkout.tsx Normal file
View File

@ -0,0 +1,13 @@
import { Layout } from 'src/components/common';
import { CheckoutPage } from 'src/components/modules/checkout';
import { HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home';
export default function Checkout() {
return (
<>
<CheckoutPage/>
</>
)
}
Checkout.Layout = Layout

View File

@ -1,6 +1,5 @@
import { useState } from 'react'
import {
CheckoutBill,
Layout,
} from 'src/components/common'
import { CardItemCheckoutProps } from 'src/components/common/CardItemCheckout/CardItemCheckout'
@ -9,43 +8,12 @@ import TabCommon from 'src/components/common/TabCommon/TabCommon'
import { CheckoutInfo } from 'src/components/modules/checkout'
import image7 from '../public/assets/images/image7.png'
import image8 from '../public/assets/images/image8.png'
const dataTest:CardItemCheckoutProps[] = [
{
name: 'Tomato',
slug: "tomato",
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
quantity:10
},
{
name: 'Carrot',
slug: "carrot",
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image7.src,
quantity:1
},
{
name: 'Salad',
slug:"salad",
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: image8.src,
quantity:2
},
]
export default function Test() {
return (
<>
<div className="w-full flex justify-between" style={{padding: "0 3.2rem"}} >
<CheckoutInfo/>
<CheckoutBill data={dataTest}/>
</div>
<TabCommon center={true}>
<TabPane
active={true}

View File

@ -4,13 +4,13 @@
padding:1.6rem 0;
@apply flex items-center;
.image{
min-width: 13.3rem;
max-width: 8.8rem;
width: 13.3rem;
height: 8.8rem;
margin: 0.8rem;
@apply flex justify-center items-center;
img{
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
}
}
.right{

View File

@ -8,9 +8,10 @@ interface CheckboxProps extends Omit<
>{
onChange?: (value: boolean) => void,
defaultChecked?: boolean
text?:string
}
const CheckboxCommon = ({onChange,defaultChecked = true,...props}: CheckboxProps) =>{
const CheckboxCommon = ({onChange,defaultChecked = true,text="Billing address is same as shipping", ...props}: CheckboxProps) =>{
const [value, setValue] = useState<boolean>(true);
@ -31,7 +32,7 @@ const CheckboxCommon = ({onChange,defaultChecked = true,...props}: CheckboxProps
<span className={s.checkMark}></span>
</label>
<div className={classNames(s.checkboxText)}>
<label htmlFor="check"> Billing address is same as shipping </label>
<label htmlFor="check"> {text} </label>
</div>
</div>
)

View File

@ -1,7 +1,15 @@
.warpper{
padding: 2.4rem 0;
@apply border-b border-solid border-line;
.note{
font-size: 1.2rem;
line-height: 2rem;
letter-spacing: 0.01em;
color: var(--text-label);
padding: 0 5.6rem;
}
.header{
@apply flex justify-between;
padding-bottom: 3.2rem;
.left{
@apply flex items-center;
.number{
@ -16,6 +24,9 @@
border: none;
color: var(--white);
}
&.done{
@apply border-2 border-solid border-primary;
}
}
.title{
padding-left: 2.4rem;
@ -33,6 +44,7 @@
height: 0;
@apply overflow-hidden;
&.show{
margin-top: 3.2rem;
height: initial;
}
}

View File

@ -1,5 +1,8 @@
import classNames from 'classnames'
import { divide } from 'lodash'
import React from 'react'
import { IconDoneCheckout } from 'src/components/icons'
import { CheckOutForm } from 'src/utils/types.utils'
import s from './CheckoutCollapse.module.scss'
interface CheckoutCollapseProps {
visible: boolean
@ -10,6 +13,7 @@ interface CheckoutCollapseProps {
onClose?: (id:number) => void
onOpen?: (id:number) => void
onEditClick?:(id:number) => void
note?:string
}
const CheckoutCollapse = ({
@ -18,6 +22,7 @@ const CheckoutCollapse = ({
title,
isEdit,
visible,
note,
onOpen,
onClose,
onEditClick
@ -36,8 +41,8 @@ const CheckoutCollapse = ({
<div className={s.warpper}>
<div className={s.header}>
<div className={s.left}>
<div className={classNames(s.number, { [`${s.visible}`]: visible })}>
{id}
<div className={classNames(s.number, { [s.visible]: visible, [s.done]:isEdit })}>
{isEdit?<IconDoneCheckout/>:id}
</div>
<div className={s.title} onClick={handleTitleClick}>
{title}
@ -45,6 +50,7 @@ const CheckoutCollapse = ({
</div>
{isEdit && <div className={s.edit} onClick={handleEdit}>{'Edit'}</div>}
</div>
{(!visible && isEdit) && (<div className={s.note}>{note}</div>) }
<div className={classNames(s.body, { [`${s.show}`]: visible })}>{children}</div>
</div>
)

View File

@ -36,7 +36,6 @@ export { default as CardItemCheckout} from './CardItemCheckout/CardItemCheckout'
export { default as CardBlog} from './CardBlog/CardBlog'
export { default as RelevantBlogPosts} from './RelevantBlogPosts/RelevantBlogPosts'
export { default as CollapseCommon} from './CollapseCommon/CollapseCommon'
export { default as CheckoutBill} from './CheckoutBill/CheckoutBill'
export { default as ImgWithLink} from './ImgWithLink/ImgWithLink'
export { default as RecipeDetail} from './RecipeDetail/RecipeDetail'
export { default as DrawerCommon} from './DrawerCommon/DrawerCommon'

View File

@ -0,0 +1,20 @@
import React from 'react'
const IconDoneCheckout = () => {
return (
<svg
width="16"
height="12"
viewBox="0 0 16 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.7099 1.20986C14.617 1.11613 14.5064 1.04174 14.3845 0.990969C14.2627 0.940201 14.132 0.914062 13.9999 0.914062C13.8679 0.914062 13.7372 0.940201 13.6154 0.990969C13.4935 1.04174 13.3829 1.11613 13.29 1.20986L5.83995 8.66986L2.70995 5.52986C2.61343 5.43662 2.49949 5.36331 2.37463 5.3141C2.24978 5.2649 2.11645 5.24077 1.98227 5.24309C1.84809 5.24541 1.71568 5.27414 1.5926 5.32763C1.46953 5.38113 1.35819 5.45834 1.26495 5.55486C1.17171 5.65138 1.0984 5.76532 1.04919 5.89018C0.999989 6.01503 0.975859 6.14836 0.97818 6.28254C0.980502 6.41672 1.00923 6.54913 1.06272 6.67221C1.11622 6.79529 1.19343 6.90662 1.28995 6.99986L5.12995 10.8399C5.22291 10.9336 5.33351 11.008 5.45537 11.0588C5.57723 11.1095 5.70794 11.1357 5.83995 11.1357C5.97196 11.1357 6.10267 11.1095 6.22453 11.0588C6.34639 11.008 6.45699 10.9336 6.54995 10.8399L14.7099 2.67986C14.8115 2.58622 14.8925 2.47257 14.9479 2.34607C15.0033 2.21957 15.0319 2.08296 15.0319 1.94486C15.0319 1.80676 15.0033 1.67015 14.9479 1.54365C14.8925 1.41715 14.8115 1.3035 14.7099 1.20986Z"
fill="#4EA674"
/>
</svg>
)
}
export default IconDoneCheckout

View File

@ -31,3 +31,4 @@ export { default as IconDelete } from './IconDelete'
export { default as IconPlus } from './IconPlus'
export { default as IconMinus } from './IconMinus'
export { default as IconCirclePlus } from './IconCirclePlus'
export { default as IconDoneCheckout } from './IconDoneCheckout'

View File

@ -17,6 +17,7 @@
min-height: 6.4rem;
}
.price {
margin-top: 3.2rem;
.line {
@apply flex justify-between items-center text-label;
.total {

View File

@ -1,7 +1,7 @@
import React from 'react'
import s from './CheckoutBill.module.scss'
import { CardItemCheckout } from '..'
import { CardItemCheckoutProps } from '../CardItemCheckout/CardItemCheckout'
import { CardItemCheckout } from '../../../common'
import { CardItemCheckoutProps } from '../../../common/CardItemCheckout/CardItemCheckout'
import { IconCirclePlus } from 'src/components/icons'
interface CheckoutBillProps {

View File

@ -1,7 +1,8 @@
.warpper{
@apply w-full;
padding: 3.2rem;
.title{
padding: 3.2rem 0;
margin-bottom: 3.2rem;
@apply flex justify-start items-center
}
}

View File

@ -1,6 +1,7 @@
import React, { useState } from 'react'
import { Logo } from 'src/components/common'
import CheckoutCollapse from 'src/components/common/CheckoutCollapse/CheckoutCollapse'
import { removeItem } from 'src/utils/funtion.utils'
import { CheckOutForm } from 'src/utils/types.utils'
import s from './CheckoutInfo.module.scss'
import CustomerInfoForm from './components/CustomerInfoForm/CustomerInfoForm'
@ -12,28 +13,41 @@ const CheckoutInfo = ({}: CheckoutInfoProps) => {
const [active, setActive] = useState(1)
const [done, setDone] = useState<number[]>([])
const [info, setInfo] = useState<CheckOutForm>({})
const onOpen = (id:number) => {
setActive(id)
}
const onEdit = (id:number) => {
setActive(id)
}
const onClose = (id:number) => {
setActive(id)
setDone(removeItem<number>(done,id))
}
const onConfirm = (id:number,formInfo:CheckOutForm) => {
if(id+1>formList.length){
console.log({...info,...formInfo})
}else{
setActive(id+1)
if(done.length>0){
for (let i = id+1; i <= formList.length; i++) {
if(!done.includes(i)){
setActive(i)
}
}
}else{
setActive(id+1)
}
setDone([...done,id])
}
setDone([...done,id])
setInfo({...info,...formInfo})
}
const getNote = (id:number) => {
switch (id) {
case 1:
return `${info.name}, ${info.email}`
case 2:
return `${info.address}, ${info.state}, ${info.city}, ${info.code}, ${info.phone}, `
default:
return ""
}
}
const formList = [
{
id: 1,
@ -57,6 +71,7 @@ const CheckoutInfo = ({}: CheckoutInfoProps) => {
<Logo />
</div>
{formList.map((item) => {
let note = getNote(item.id)
return <CheckoutCollapse
key={item.title}
id={item.id}
@ -64,30 +79,11 @@ const CheckoutInfo = ({}: CheckoutInfoProps) => {
title={item.title}
onEditClick={onEdit}
isEdit={done.includes(item.id)}
note={note}
>
{item.form}
</CheckoutCollapse>
})}
{/* <CheckoutCollapse
id={1}
visible={active === 1}
// onOpen={onOpen}
// onClose={onClose}
title="Customer Information"
isEdit={true}
>
<CustomerInfoForm />
</CheckoutCollapse>
<CheckoutCollapse
id={2}
visible={active === 2}
// onOpen={onOpen2}
// onClose={onClose2}
title="Shipping Information"
isEdit={true}
>
<ShippingInfoForm />
</CheckoutCollapse> */}
</div>
)
}

View File

@ -1,9 +1,15 @@
.warpper{
.title{
min-width: 19.4rem;
@apply text-label;
}
.hightlight{
@apply text-active;
.info{
.line{
@apply flex justify-start items-center;
.title{
margin-right: 3.2rem;
min-width: 19.4rem;
@apply text-label;
}
.hightlight{
@apply text-active;
}
}
}
}

View File

@ -5,17 +5,19 @@ interface BankTransferProps {}
const BankTransfer = ({}: BankTransferProps) => {
return (
<div className={s.warpper}>
<div className={s.line}>
<div className={s.title}>Account Name:</div>
<div className={s.hightlight}>Duong Dinh Vu</div>
</div>
<div className={s.line}>
<div className={s.title}>Account Number:</div>
<div className={s.hightlight}>1234 1234 1234 1234</div>
</div>
<div className={s.line}>
<div className={s.title}>Bank Name:</div>
<div className={s.hightlight}>Techcombank - HCMC</div>
<div className={s.info}>
<div className={s.line}>
<div className={s.title}>Account Name:</div>
<div className={s.hightlight}>Duong Dinh Vu</div>
</div>
<div className={s.line}>
<div className={s.title}>Account Number:</div>
<div className={s.hightlight}>1234 1234 1234 1234</div>
</div>
<div className={s.line}>
<div className={s.title}>Bank Name:</div>
<div className={s.hightlight}>Techcombank - HCMC</div>
</div>
</div>
</div>
)

View File

@ -0,0 +1,12 @@
@import "../../../../../../styles/utilities";
.warpper{
@apply u-form;
.line{
>div{
width: 50%;
}
}
.checkbox{
margin-top: 1.6rem;
}
}

View File

@ -0,0 +1,27 @@
import React, { useRef } from 'react'
import { CheckboxCommon, Inputcommon } from 'src/components/common'
import { CustomInputCommon } from 'src/utils/type.utils'
import s from "./CreditCardForm.module.scss"
interface CreditCardFormProps {
}
const CreditCardForm = ({}: CreditCardFormProps) => {
const cardNumberRef = useRef<CustomInputCommon>(null)
const dateRef = useRef<CustomInputCommon>(null)
const cvsRef = useRef<CustomInputCommon>(null)
return (
<div className={s.warpper}>
<div className={s.body}>
<Inputcommon type="text" placeholder="Cârd Number" ref={cardNumberRef} />
<div className={s.line}>
<Inputcommon type="text" placeholder="MM/YY" ref={dateRef} />
<Inputcommon type="text" placeholder="CVS" ref={cvsRef} />
</div>
</div>
<div className={s.checkbox}><CheckboxCommon/></div>
</div>
)
}
export default CreditCardForm

View File

@ -0,0 +1,14 @@
.wrapper{
padding: 0 5.6rem;
.inner{
padding: 4rem 0;
}
.bottom{
margin-top: 2.4rem;
@apply flex justify-between items-center;
.note{
font-size: 1.2rem;
line-height: 2rem;
}
}
}

View File

@ -1,28 +1,55 @@
import React from 'react'
import { TabCommon, TabPane } from 'src/components/common'
import { ButtonCommon, TabCommon, TabPane } from 'src/components/common'
import { CheckOutForm } from 'src/utils/types.utils'
import BankTransfer from '../BankTransfer/BankTransfer'
import Link from 'next/link'
import s from "./PaymentInfoForm.module.scss"
import s from './PaymentInfoForm.module.scss'
import CreditCardForm from '../CreditCardForm/CreditCardForm'
interface PaymentInfoFormProps {
onConfirm?: (id:number,formInfo:CheckOutForm)=>void
id:number
onConfirm?: (id: number, formInfo: CheckOutForm) => void
id: number
}
const PaymentInfoForm = ({}: PaymentInfoFormProps) => {
return (
<div className="">
<TabCommon>
<TabPane tabName = "Bank Transfer">
<BankTransfer/>
</TabPane>
<TabPane tabName = "Bank Transfer 2">
<BankTransfer/>
</TabPane>
</TabCommon>
const PaymentInfoForm = ({onConfirm,id}: PaymentInfoFormProps) => {
const handleConfirmClick = () => {
onConfirm && onConfirm(id,{})
}
return (
<div className={s.wrapper}>
<TabCommon>
<TabPane tabName="Bank Transfer">
<div className={s.inner}><BankTransfer /></div>
</TabPane>
<TabPane tabName="Ewallet">
<div className={s.inner}></div>
</TabPane>
<TabPane tabName="Credit Card">
<div className={s.inner}><CreditCardForm /></div>
</TabPane>
</TabCommon>
<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}>
Submit Order
</ButtonCommon>
</div>
</div>
)
}
export default PaymentInfoForm

View File

@ -0,0 +1,10 @@
.warrper{
@apply flex;
.right{
min-width: 56.3rem;
}
.left{
@apply w-full;
}
}

View File

@ -0,0 +1,18 @@
import React from 'react'
import { CHECKOUT_BILL_DATA } from 'src/utils/demo-data'
import { CheckoutBill, CheckoutInfo } from '..'
import s from "./CheckoutPage.module.scss"
interface CheckoutPageProps {
}
const CheckoutPage = ({}: CheckoutPageProps) => {
return (
<div className={s.warrper}>
<div className={s.left}><CheckoutInfo/></div>
<div className={s.right}><CheckoutBill data={CHECKOUT_BILL_DATA}/></div>
</div>
)
}
export default CheckoutPage

View File

@ -1 +1,3 @@
export { default as CheckoutInfo } from './CheckoutInfo/CheckoutInfo'
export { default as CheckoutPage } from './CheckoutPage/CheckoutPage'
export { default as CheckoutBill } from './CheckoutBill/CheckoutBill'

View File

@ -1,3 +1,4 @@
import { CardItemCheckoutProps } from "src/components/common/CardItemCheckout/CardItemCheckout"
import { RecipeCardProps } from "src/components/common/RecipeCard/RecipeCard"
export const PRODUCT_DATA_TEST = [
@ -212,4 +213,34 @@ export const RECIPE_DATA_TEST: RecipeCardProps[] = [
description: "The broth for Bun Bo Hue is prepared by slowly simmering various types of beef and pork bones (ox tail, beef shank, pork neck bones, pork feet,...",
imageSrc: 'https://user-images.githubusercontent.com/76729908/132159262-f28a9fb9-4852-47e6-80b5-d600521b548a.png'
},
]
]
export const CHECKOUT_BILL_DATA:CardItemCheckoutProps[] = [
{
name: 'Tomato',
slug: "tomato",
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: "https://user-images.githubusercontent.com/76729908/131646227-b5705e64-3b45-47a3-9433-9f4b5ee8d40c.png",
quantity:10
},
{
name: 'Carrot',
slug: "carrot",
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: "https://user-images.githubusercontent.com/76729908/131646217-23b86160-45c9-4845-8dcc-b3e1a4483edd.png",
quantity:1
},
{
name: 'Salad',
slug:"salad",
weight: '250g',
category: 'VEGGIE',
price: 'Rp 27.500',
imageSrc: "https://user-images.githubusercontent.com/76729908/131646221-aaa1d48d-bb80-470f-9400-ae2aa47285b6.png",
quantity:2
},
]

View File

@ -1,3 +1,11 @@
export function isMobile() {
return window.innerWidth <= 768
}
export function removeItem<T>(arr: Array<T>, value: T): Array<T> {
const index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
}
return [...arr];
}