mirror of
https://github.com/vercel/commerce.git
synced 2025-07-26 19:51:23 +00:00
Merge branch 'common' into m6-sonnguyen
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
.warpper {
|
||||
padding: 3.2rem;
|
||||
min-width: 100%;
|
||||
@screen lg {
|
||||
max-width: 56.3rem;
|
||||
@apply flex justify-between flex-col border-l-2 border-solid border-line;
|
||||
}
|
||||
.title {
|
||||
display: none;
|
||||
font-weight: bold;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
@screen md {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.list {
|
||||
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 {
|
||||
@apply flex justify-between items-center text-label;
|
||||
.total {
|
||||
font-weight: bold;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
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'
|
||||
|
||||
interface CheckoutBillProps {
|
||||
data: CardItemCheckoutProps[]
|
||||
}
|
||||
|
||||
const CheckoutBill = ({ data }: CheckoutBillProps) => {
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<div className = {s.title}>
|
||||
Your cart ({data.length})
|
||||
</div>
|
||||
<div className={s.list}>
|
||||
{data.map((item) => {
|
||||
return <CardItemCheckout {...item} key={item.slug} />
|
||||
})}
|
||||
</div>
|
||||
<div className={s.bot}>
|
||||
<div className={s.promo}>
|
||||
Apply Promotion Code
|
||||
<IconCirclePlus />
|
||||
</div>
|
||||
<div className={s.price}>
|
||||
<div className={s.line}>
|
||||
Subtotal
|
||||
<div className={s.subTotal}>RP 120.500</div>
|
||||
</div>
|
||||
<div className={s.line}>
|
||||
Shipping
|
||||
<div className={s.shipping}>Free</div>
|
||||
</div>
|
||||
<div className={s.line}>
|
||||
Estimated Total
|
||||
<div className={s.total}>RP 120.500</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CheckoutBill
|
@@ -0,0 +1,17 @@
|
||||
.warpper{
|
||||
@apply w-full;
|
||||
padding: 3.2rem;
|
||||
.title{
|
||||
margin-bottom: 3.2rem;
|
||||
@apply flex justify-between items-center;
|
||||
.viewCart{
|
||||
margin-right: 5.6rem;
|
||||
@apply text-primary font-bold;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
@screen lg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
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'
|
||||
import PaymentInfoForm from './components/PaymentInfoForm/PaymentInfoForm'
|
||||
import ShippingInfoForm from './components/ShippingInfoForm/ShippingInfoForm'
|
||||
interface CheckoutInfoProps {
|
||||
onViewCart:()=>void
|
||||
}
|
||||
|
||||
const CheckoutInfo = ({onViewCart}: CheckoutInfoProps) => {
|
||||
const [active, setActive] = useState(1)
|
||||
const [done, setDone] = useState<number[]>([])
|
||||
const [info, setInfo] = useState<CheckOutForm>({})
|
||||
|
||||
const onEdit = (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{
|
||||
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])
|
||||
}
|
||||
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,
|
||||
title: 'Customer Information',
|
||||
form: <CustomerInfoForm onConfirm={onConfirm} id={1}/>,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Shipping Information',
|
||||
form: <ShippingInfoForm onConfirm={onConfirm} id={2}/>,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Payment Information',
|
||||
form: <PaymentInfoForm onConfirm={onConfirm} id={3}/>,
|
||||
},
|
||||
]
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<div className={s.title}>
|
||||
<Logo />
|
||||
<div className={s.viewCart} onClick={onViewCart}>View cart</div>
|
||||
</div>
|
||||
{formList.map((item) => {
|
||||
let note = getNote(item.id)
|
||||
return <CheckoutCollapse
|
||||
key={item.title}
|
||||
id={item.id}
|
||||
visible={item.id === active}
|
||||
title={item.title}
|
||||
onEditClick={onEdit}
|
||||
isEdit={done.includes(item.id)}
|
||||
note={note}
|
||||
>
|
||||
{item.form}
|
||||
</CheckoutCollapse>
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CheckoutInfo
|
@@ -0,0 +1,15 @@
|
||||
.warpper{
|
||||
.info{
|
||||
.line{
|
||||
@apply flex justify-start items-center;
|
||||
.title{
|
||||
margin-right: 3.2rem;
|
||||
min-width: 19.4rem;
|
||||
@apply text-label;
|
||||
}
|
||||
.hightlight{
|
||||
@apply text-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
import s from './BankTransfer.module.scss'
|
||||
interface BankTransferProps {}
|
||||
|
||||
const BankTransfer = ({}: BankTransferProps) => {
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
export default BankTransfer
|
@@ -0,0 +1,12 @@
|
||||
@import "../../../../../../styles/utilities";
|
||||
.warpper{
|
||||
@apply u-form;
|
||||
.line{
|
||||
>div{
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
.checkbox{
|
||||
margin-top: 1.6rem;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -0,0 +1,15 @@
|
||||
@import "../../../../../../styles/utilities";
|
||||
.warpper{
|
||||
@apply u-form;
|
||||
@screen md {
|
||||
padding: 0 5.6rem;
|
||||
}
|
||||
.bottom{
|
||||
margin-top: 2.4rem;
|
||||
@apply flex justify-between items-center;
|
||||
.note{
|
||||
font-size: 1.2rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
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 { CheckOutForm } from 'src/utils/types.utils'
|
||||
import s from './CustomerInfoForm.module.scss'
|
||||
interface CustomerInfoFormProps {
|
||||
onConfirm?: (id: number, formInfo: CheckOutForm) => void
|
||||
id: number
|
||||
}
|
||||
|
||||
const CustomerInfoForm = ({ id, onConfirm }: CustomerInfoFormProps) => {
|
||||
const nameRef = useRef<React.ElementRef<typeof InputCommon>>(null)
|
||||
const emailRef = useRef<React.ElementRef<typeof InputCommon>>(null)
|
||||
|
||||
const handleConfirmClick = () => {
|
||||
onConfirm &&
|
||||
onConfirm(id, {
|
||||
name: nameRef?.current?.getValue().toString(),
|
||||
email: emailRef.current?.getValue().toString(),
|
||||
})
|
||||
}
|
||||
|
||||
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
|
@@ -0,0 +1,16 @@
|
||||
.wrapper{
|
||||
@screen md {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
import React from 'react'
|
||||
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 CreditCardForm from '../CreditCardForm/CreditCardForm'
|
||||
interface PaymentInfoFormProps {
|
||||
onConfirm?: (id: number, formInfo: CheckOutForm) => void
|
||||
id: number
|
||||
}
|
||||
|
||||
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
|
@@ -0,0 +1,39 @@
|
||||
@import "../../../../../../styles/utilities";
|
||||
|
||||
.warpper{
|
||||
@apply u-form;
|
||||
@screen md {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
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'
|
||||
import { CheckOutForm } from 'src/utils/types.utils'
|
||||
|
||||
interface ShippingInfoFormProps {
|
||||
onConfirm?: (id:number,formInfo:CheckOutForm)=>void
|
||||
id:number
|
||||
}
|
||||
|
||||
const option = [
|
||||
{
|
||||
name: 'Hồ Chí Minh',
|
||||
},
|
||||
{
|
||||
name: 'Hà Nội',
|
||||
},
|
||||
]
|
||||
|
||||
const ShippingInfoForm = ({onConfirm,id}: 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()),
|
||||
})
|
||||
}
|
||||
|
||||
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
|
@@ -0,0 +1,56 @@
|
||||
@import "../../../../styles/utilities";
|
||||
.warrper{
|
||||
@apply flex;
|
||||
.right{
|
||||
display: none;
|
||||
@screen lg {
|
||||
display: block;
|
||||
min-width: 45rem;
|
||||
}
|
||||
@screen xl {
|
||||
min-width: 56.3rem;
|
||||
}
|
||||
}
|
||||
.left{
|
||||
@apply w-full;
|
||||
}
|
||||
.mobile{
|
||||
@apply hidden;
|
||||
&.isShow{
|
||||
@apply block;
|
||||
@screen lg {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
.modal{
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10000;
|
||||
.content{
|
||||
@apply spacing-horizontal;
|
||||
margin-top: 3rem;
|
||||
padding-top: 6.4rem ;
|
||||
padding-bottom: 5rem;
|
||||
background-color: white;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
border-radius: 2.4rem 2.4rem 0 0;
|
||||
.head{
|
||||
@apply flex justify-between;
|
||||
h3{
|
||||
@apply heading-3 font-bold;
|
||||
color:var(--text-base);
|
||||
}
|
||||
}
|
||||
button{
|
||||
margin-top: 2rem;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { useState } from 'react'
|
||||
import IconHide from 'src/components/icons/IconHide'
|
||||
import { CHECKOUT_BILL_DATA } from 'src/utils/demo-data'
|
||||
import { CheckoutBill, CheckoutInfo } from '..'
|
||||
import s from "./CheckoutPage.module.scss"
|
||||
interface CheckoutPageProps {
|
||||
}
|
||||
|
||||
const CheckoutPage = ({}: CheckoutPageProps) => {
|
||||
const [isShow, setIsShow] = useState(false)
|
||||
const onClose = () => {
|
||||
setIsShow(false)
|
||||
}
|
||||
const onViewCart =() => {
|
||||
setIsShow(true)
|
||||
}
|
||||
return (
|
||||
<div className={s.warrper}>
|
||||
<div className={s.left}><CheckoutInfo onViewCart = {onViewCart}/></div>
|
||||
<div className={s.right}><CheckoutBill data={CHECKOUT_BILL_DATA}/></div>
|
||||
<div className={classNames({ [s.mobile] :true,[s.isShow]: isShow})}>
|
||||
<div className={s.modal}>
|
||||
<div className={s.content}>
|
||||
<div className={s.head}>
|
||||
<h3>Your Cart({CHECKOUT_BILL_DATA.length})</h3>
|
||||
<div onClick={onClose}><IconHide/></div>
|
||||
</div>
|
||||
<CheckoutBill data={CHECKOUT_BILL_DATA}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CheckoutPage
|
3
src/components/modules/checkout/index.ts
Normal file
3
src/components/modules/checkout/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as CheckoutInfo } from './CheckoutInfo/CheckoutInfo'
|
||||
export { default as CheckoutPage } from './CheckoutPage/CheckoutPage'
|
||||
export { default as CheckoutBill } from './CheckoutBill/CheckoutBill'
|
Reference in New Issue
Block a user