mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 12:24:18 +00:00
13
pages/checkout.tsx
Normal file
13
pages/checkout.tsx
Normal 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
|
17
pages/products.tsx
Normal file
17
pages/products.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Layout } from 'src/components/common';
|
||||
import ProductListFilter from 'src/components/modules/product-list/ProductListFilter/ProductListFilter';
|
||||
import RecipeListBanner from 'src/components/modules/recipes-list/RecipeListBanner/RecipeListBanner';
|
||||
import RecipesList from 'src/components/modules/recipes-list/RecipesList/RecipesList';
|
||||
import ProductListBanner from '../src/components/modules/product-list/ProductListBanner/ProductListBanner';
|
||||
|
||||
|
||||
export default function Products() {
|
||||
return (
|
||||
<>
|
||||
<ProductListBanner />
|
||||
<ProductListFilter/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Products.Layout = Layout
|
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import s from './BreadcrumbCommon.module.scss'
|
||||
|
||||
import BreadcrumbItem from './components/BreadcrumbItem/BreadcrumbItem'
|
||||
import BreadcrumbSeparator from './components/BreadcrumbSeparator/BreadcrumbSeparator'
|
||||
|
||||
|
@@ -0,0 +1,31 @@
|
||||
.warpper{
|
||||
max-height: 12rem;
|
||||
padding:1.6rem 0;
|
||||
@apply flex items-center;
|
||||
@screen lg {
|
||||
max-width: 49.9rem;
|
||||
}
|
||||
.image{
|
||||
width: 13.3rem;
|
||||
height: 8.8rem;
|
||||
margin: 0.8rem;
|
||||
@apply flex justify-center items-center;
|
||||
img{
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
.right{
|
||||
width: 100%;
|
||||
.name{
|
||||
color: var(--text-active);
|
||||
margin-bottom: 0.5erm;
|
||||
}
|
||||
.quantity {
|
||||
@apply flex justify-between text-label;
|
||||
.price{
|
||||
color: var(--text-base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
src/components/common/CardItemCheckout/CardItemCheckout.tsx
Normal file
29
src/components/common/CardItemCheckout/CardItemCheckout.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import s from "./CardItemCheckout.module.scss"
|
||||
import { ProductProps } from 'src/utils/types.utils'
|
||||
export interface CardItemCheckoutProps extends ProductProps {
|
||||
quantity:number
|
||||
}
|
||||
|
||||
const CardItemCheckout = ({imageSrc,name,price,weight,quantity,category}: CardItemCheckoutProps) => {
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<div className={s.image}>
|
||||
<img src={imageSrc} alt="image" />
|
||||
</div>
|
||||
<div className={s.right}>
|
||||
<div className={s.name}>
|
||||
{`${name} (${weight})`}
|
||||
</div>
|
||||
<div className={s.quantity}>
|
||||
Quantity:
|
||||
<div className={s.price}>
|
||||
{`${quantity} x ${price}`}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CardItemCheckout
|
@@ -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>
|
||||
)
|
||||
|
@@ -0,0 +1,51 @@
|
||||
.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;
|
||||
.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);
|
||||
}
|
||||
&.done{
|
||||
@apply border-2 border-solid border-primary;
|
||||
}
|
||||
}
|
||||
.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{
|
||||
margin-top: 3.2rem;
|
||||
height: initial;
|
||||
}
|
||||
}
|
||||
}
|
59
src/components/common/CheckoutCollapse/CheckoutCollapse.tsx
Normal file
59
src/components/common/CheckoutCollapse/CheckoutCollapse.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
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
|
||||
id: number
|
||||
children: React.ReactNode
|
||||
title: string
|
||||
isEdit: boolean
|
||||
onClose?: (id:number) => void
|
||||
onOpen?: (id:number) => void
|
||||
onEditClick?:(id:number) => void
|
||||
note?:string
|
||||
}
|
||||
|
||||
const CheckoutCollapse = ({
|
||||
children,
|
||||
id,
|
||||
title,
|
||||
isEdit,
|
||||
visible,
|
||||
note,
|
||||
onOpen,
|
||||
onClose,
|
||||
onEditClick
|
||||
}: CheckoutCollapseProps) => {
|
||||
const handleTitleClick = () => {
|
||||
if(visible){
|
||||
onClose && onClose(id)
|
||||
}else{
|
||||
onOpen && onOpen(id)
|
||||
}
|
||||
}
|
||||
const handleEdit = () => {
|
||||
onEditClick && onEditClick(id)
|
||||
}
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<div className={s.header}>
|
||||
<div className={s.left}>
|
||||
<div className={classNames(s.number, { [s.visible]: visible, [s.done]:isEdit })}>
|
||||
{isEdit?<IconDoneCheckout/>:id}
|
||||
</div>
|
||||
<div className={s.title} onClick={handleTitleClick}>
|
||||
{title}
|
||||
</div>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
export default CheckoutCollapse
|
@@ -9,9 +9,11 @@ import HeaderMenu from './components/HeaderMenu/HeaderMenu'
|
||||
import HeaderSubMenu from './components/HeaderSubMenu/HeaderSubMenu'
|
||||
import HeaderSubMenuMobile from './components/HeaderSubMenuMobile/HeaderSubMenuMobile'
|
||||
import s from './Header.module.scss'
|
||||
interface props {
|
||||
toggleFilter:()=>void
|
||||
}
|
||||
|
||||
|
||||
const Header = memo(() => {
|
||||
const Header = memo(({toggleFilter}:props) => {
|
||||
const headeFullRef = useRef<HTMLDivElement>(null)
|
||||
const [isFullHeader, setIsFullHeader] = useState<boolean>(true)
|
||||
const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false })
|
||||
@@ -43,6 +45,7 @@ const Header = memo(() => {
|
||||
<HeaderHighLight />
|
||||
<div className={s.menu}>
|
||||
<HeaderMenu
|
||||
toggleFilter={toggleFilter}
|
||||
isFull={isFullHeader}
|
||||
openModalAuthen={openModalAuthen}
|
||||
openModalInfo={openModalInfo} />
|
||||
@@ -56,6 +59,7 @@ const Header = memo(() => {
|
||||
[s.show]: !isFullHeader
|
||||
})}>
|
||||
<HeaderMenu isFull={isFullHeader}
|
||||
toggleFilter={toggleFilter}
|
||||
openModalAuthen={openModalAuthen}
|
||||
openModalInfo={openModalInfo} />
|
||||
</div>
|
||||
|
@@ -15,7 +15,16 @@
|
||||
.left {
|
||||
.top {
|
||||
@apply flex justify-between items-center;
|
||||
.iconGroup{
|
||||
@apply flex justify-between items-center;
|
||||
}
|
||||
.iconCart {
|
||||
margin-left: 1.6rem;
|
||||
}
|
||||
.iconFilter{
|
||||
@screen md {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.inputSearch {
|
||||
@@ -76,5 +85,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@screen xl {
|
||||
.iconFilterDesk {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,19 +3,23 @@ import Link from 'next/link'
|
||||
import { memo, useMemo } from 'react'
|
||||
import InputSearch from 'src/components/common/InputSearch/InputSearch'
|
||||
import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown'
|
||||
import { IconBuy, IconHeart, IconHistory, IconUser } from 'src/components/icons'
|
||||
import { IconBuy, IconFilter, IconHeart, IconHistory, IconUser } from 'src/components/icons'
|
||||
import { ACCOUNT_TAB, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
|
||||
import Logo from '../../../Logo/Logo'
|
||||
import s from './HeaderMenu.module.scss'
|
||||
|
||||
import { useRouter } from 'next/router'
|
||||
interface Props {
|
||||
children?: any,
|
||||
isFull: boolean,
|
||||
openModalAuthen: () => void,
|
||||
openModalInfo: () => void,
|
||||
toggleFilter:() => void,
|
||||
}
|
||||
|
||||
const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo }: Props) => {
|
||||
const FILTER_PAGE = [ROUTE.HOME,ROUTE.PRODUCTS]
|
||||
|
||||
const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo, toggleFilter }: Props) => {
|
||||
const router = useRouter()
|
||||
const optionMenu = useMemo(() => [
|
||||
{
|
||||
onClick: openModalAuthen,
|
||||
@@ -35,15 +39,24 @@ const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo }: Props) => {
|
||||
},
|
||||
|
||||
], [openModalAuthen])
|
||||
|
||||
return (
|
||||
<section className={classNames({ [s.headerMenu]: true, [s.full]: isFull })}>
|
||||
<div className={s.left}>
|
||||
<div className={s.top}>
|
||||
<Logo/>
|
||||
<button className={s.iconCart}>
|
||||
<IconBuy />
|
||||
</button>
|
||||
<div className={s.iconGroup}>
|
||||
{
|
||||
FILTER_PAGE.includes(router.pathname) && (
|
||||
<button className={s.iconFilter} onClick={toggleFilter}>
|
||||
<IconFilter/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
<button className={s.iconCart}>
|
||||
<IconBuy />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={s.inputSearch}>
|
||||
<InputSearch />
|
||||
@@ -72,6 +85,16 @@ const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo }: Props) => {
|
||||
<IconBuy />
|
||||
</button>
|
||||
</li>
|
||||
|
||||
{
|
||||
FILTER_PAGE.includes(router.pathname) && (
|
||||
<li className={s.iconFilterDesk}>
|
||||
<button className={s.iconFilter} onClick={toggleFilter}>
|
||||
<IconFilter/>
|
||||
</button>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
)
|
||||
|
@@ -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,
|
||||
|
@@ -6,6 +6,7 @@ import s from './InputCommon.module.scss';
|
||||
|
||||
type Ref = {
|
||||
focus: () => void
|
||||
getValue: () => string | number
|
||||
} | null;
|
||||
interface Props {
|
||||
children?: React.ReactNode,
|
||||
|
@@ -6,3 +6,8 @@
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.filter{
|
||||
@screen xl {
|
||||
display: none;
|
||||
}
|
||||
}
|
@@ -1,10 +1,13 @@
|
||||
import { CommerceProvider } from '@framework'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC } from 'react'
|
||||
import { FilterProvider } from 'src/components/contexts/FilterContext'
|
||||
import { useModalCommon } from 'src/components/hooks'
|
||||
import { BRAND, CATEGORY, FEATURED } from 'src/utils/constanst.utils'
|
||||
import { CartDrawer, CustomShapeSvg } from '..'
|
||||
import Footer from '../Footer/Footer'
|
||||
import Header from '../Header/Header'
|
||||
import MenuNavigationProductList from '../MenuNavigationProductList/MenuNavigationProductList'
|
||||
import s from './Layout.module.scss'
|
||||
|
||||
interface Props {
|
||||
@@ -16,6 +19,7 @@ interface Props {
|
||||
const Layout: FC<Props> = ({ children }) => {
|
||||
const { locale = 'en-US' } = useRouter()
|
||||
const { visible: visibleCartDrawer, openModal, closeModal: closeCartDrawer } = useModalCommon({ initialValue: false })
|
||||
const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: false })
|
||||
|
||||
const toggle = () => {
|
||||
if (visibleCartDrawer) {
|
||||
@@ -24,18 +28,27 @@ const Layout: FC<Props> = ({ children }) => {
|
||||
openModal()
|
||||
}
|
||||
}
|
||||
const toggleFilter = () => {
|
||||
console.log("click")
|
||||
if (visibleFilter) {
|
||||
closeFilter()
|
||||
} else {
|
||||
openFilter()
|
||||
}
|
||||
}
|
||||
return (
|
||||
<CommerceProvider locale={locale}>
|
||||
<div className={s.mainLayout}>
|
||||
<Header />
|
||||
<main >{children}</main>
|
||||
<button onClick={toggle}>toggle card: {visibleCartDrawer.toString()}</button>
|
||||
<CustomShapeSvg/>
|
||||
<CartDrawer
|
||||
visible={visibleCartDrawer}
|
||||
onClose={closeCartDrawer} />
|
||||
<Footer />
|
||||
</div>
|
||||
<div className={s.mainLayout}>
|
||||
<Header toggleFilter={toggleFilter}/>
|
||||
<main >{children}</main>
|
||||
<button onClick={toggle}>toggle card: {visibleCartDrawer.toString()}</button>
|
||||
<CustomShapeSvg/>
|
||||
<CartDrawer
|
||||
visible={visibleCartDrawer}
|
||||
onClose={closeCartDrawer} />
|
||||
<div className={s.filter}><MenuNavigationProductList categories={CATEGORY} brands={BRAND} featured={FEATURED} visible={visibleFilter} onClose={closeFilter}/> </div>
|
||||
<Footer />
|
||||
</div>
|
||||
</CommerceProvider>
|
||||
|
||||
)
|
||||
|
@@ -1,9 +1,7 @@
|
||||
@import "../../../styles/utilities";
|
||||
.menuFilterWrapper{
|
||||
@apply spacing-horizontal;
|
||||
@screen md {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.menuFilterHeading{
|
||||
@apply sub-headline font-bold ;
|
||||
color: var(--text-active);
|
||||
|
@@ -1,18 +1,17 @@
|
||||
@import "../../../styles/utilities";
|
||||
.menuNavigationProductListDesktop{
|
||||
@screen sm-only {
|
||||
@screen sm {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
@screen xl {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
.menuNavigationProductListMobile{
|
||||
@apply relative transition-all duration-100;
|
||||
|
||||
@screen md{
|
||||
@apply hidden;
|
||||
}
|
||||
@screen xl{
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
&.isShow{
|
||||
&::after{
|
||||
content: "";
|
||||
|
@@ -1,9 +1,7 @@
|
||||
@import "../../../../styles/utilities";
|
||||
.menuSortWrapper{
|
||||
@apply spacing-horizontal;
|
||||
@screen md {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.menuSortHeading{
|
||||
@apply sub-headline font-bold ;
|
||||
color: var(--text-active);
|
||||
|
@@ -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
|
||||
|
@@ -25,4 +25,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -82,4 +82,4 @@ import React, {
|
||||
)
|
||||
}
|
||||
|
||||
export default TabCommon
|
||||
export default TabCommon
|
||||
|
@@ -10,4 +10,4 @@
|
||||
&.tabItemActive {
|
||||
@apply font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,9 @@
|
||||
import classNames from 'classnames'
|
||||
<<<<<<< HEAD
|
||||
import React, { RefObject, useRef } from 'react'
|
||||
=======
|
||||
import React from 'react'
|
||||
>>>>>>> 88f90912429447f6ae7bafa77484465965e0ee13
|
||||
import s from './TabItem.module.scss'
|
||||
|
||||
interface TabItemProps {
|
||||
@@ -28,4 +32,8 @@ const TabItem = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default TabItem
|
||||
<<<<<<< HEAD
|
||||
export default TabItem
|
||||
=======
|
||||
export default TabItem
|
||||
>>>>>>> 88f90912429447f6ae7bafa77484465965e0ee13
|
||||
|
@@ -32,6 +32,7 @@ export { default as ModalConfirm} from "./ModalConfirm/ModalConfirm"
|
||||
export { default as ModalInfo} from "./ModalInfo/ModalInfo"
|
||||
export { default as ProductList} from "./ProductList/ProductList"
|
||||
export { default as ModalCreateUserInfo} from './ModalCreateUserInfo/ModalCreateUserInfo'
|
||||
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'
|
||||
@@ -40,6 +41,8 @@ export { default as ImgWithLink} from './ImgWithLink/ImgWithLink'
|
||||
export { default as RecipeDetail} from './RecipeDetail/RecipeDetail'
|
||||
export { default as DrawerCommon} from './DrawerCommon/DrawerCommon'
|
||||
export { default as CartDrawer} from './CartDrawer/CartDrawer'
|
||||
export { default as TabPane} from './TabCommon/components/TabPane/TabPane'
|
||||
export { default as TabCommon} from './TabCommon/TabCommon'
|
||||
export { default as StaticImage} from './StaticImage/StaticImage'
|
||||
export { default as EmptyCommon} from './EmptyCommon/EmptyCommon'
|
||||
export { default as CustomShapeSvg} from './CustomShapeSvg/CustomShapeSvg'
|
||||
|
43
src/components/contexts/FilterContext.tsx
Normal file
43
src/components/contexts/FilterContext.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { createContext, ReactNode, useContext, useState } from "react";
|
||||
import { filterContextType } from "src/utils/types.utils";
|
||||
|
||||
const contextDefaultValues: filterContextType = {
|
||||
visible: false,
|
||||
open: () => {},
|
||||
close: () => {},
|
||||
};
|
||||
|
||||
const FilterContext = createContext<filterContextType>(contextDefaultValues);
|
||||
|
||||
export function useAuth() {
|
||||
return useContext(FilterContext);
|
||||
}
|
||||
|
||||
type FilterProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function FilterProvider({ children }: FilterProviderProps) {
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
|
||||
const open = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const value = {
|
||||
visible,
|
||||
open,
|
||||
close,
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<FilterContext.Provider value={value}>
|
||||
{children}
|
||||
</FilterContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
@@ -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>,
|
20
src/components/icons/IconCirclePlus.tsx
Normal file
20
src/components/icons/IconCirclePlus.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconCirclePlus = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 0C8.02219 0 6.08879 0.58649 4.4443 1.6853C2.79981 2.78412 1.51809 4.3459 0.761209 6.17317C0.00433284 8.00043 -0.193701 10.0111 0.192152 11.9509C0.578004 13.8907 1.53041 15.6725 2.92894 17.0711C4.32746 18.4696 6.10929 19.422 8.0491 19.8079C9.98891 20.1937 11.9996 19.9957 13.8268 19.2388C15.6541 18.4819 17.2159 17.2002 18.3147 15.5557C19.4135 13.9112 20 11.9778 20 10C20 8.68678 19.7413 7.38642 19.2388 6.17317C18.7363 4.95991 17.9997 3.85752 17.0711 2.92893C16.1425 2.00035 15.0401 1.26375 13.8268 0.761205C12.6136 0.258658 11.3132 0 10 0ZM10 18C8.41775 18 6.87104 17.5308 5.55544 16.6518C4.23985 15.7727 3.21447 14.5233 2.60897 13.0615C2.00347 11.5997 1.84504 9.99113 2.15372 8.43928C2.4624 6.88743 3.22433 5.46197 4.34315 4.34315C5.46197 3.22433 6.88743 2.4624 8.43928 2.15372C9.99113 1.84504 11.5997 2.00346 13.0615 2.60896C14.5233 3.21447 15.7727 4.23984 16.6518 5.55544C17.5308 6.87103 18 8.41775 18 10C18 12.1217 17.1572 14.1566 15.6569 15.6569C14.1566 17.1571 12.1217 18 10 18ZM14 9H11V6C11 5.73478 10.8946 5.48043 10.7071 5.29289C10.5196 5.10536 10.2652 5 10 5C9.73479 5 9.48043 5.10536 9.2929 5.29289C9.10536 5.48043 9 5.73478 9 6V9H6C5.73479 9 5.48043 9.10536 5.2929 9.29289C5.10536 9.48043 5 9.73478 5 10C5 10.2652 5.10536 10.5196 5.2929 10.7071C5.48043 10.8946 5.73479 11 6 11H9V14C9 14.2652 9.10536 14.5196 9.2929 14.7071C9.48043 14.8946 9.73479 15 10 15C10.2652 15 10.5196 14.8946 10.7071 14.7071C10.8946 14.5196 11 14.2652 11 14V11H14C14.2652 11 14.5196 10.8946 14.7071 10.7071C14.8946 10.5196 15 10.2652 15 10C15 9.73478 14.8946 9.48043 14.7071 9.29289C14.5196 9.10536 14.2652 9 14 9Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconCirclePlus
|
20
src/components/icons/IconDoneCheckout.tsx
Normal file
20
src/components/icons/IconDoneCheckout.tsx
Normal 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
|
22
src/components/icons/IconFilter.tsx
Normal file
22
src/components/icons/IconFilter.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const IconFilter = (props: Props) => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M17 0H3C2.20435 0 1.44129 0.316071 0.87868 0.87868C0.316071 1.44129 1.80878e-07 2.20435 1.80878e-07 3V4.17C-0.000143207 4.58294 0.0849664 4.99147 0.25 5.37V5.43C0.39128 5.75097 0.591392 6.04266 0.84 6.29L7 12.41V19C6.99966 19.1699 7.04264 19.3372 7.12487 19.4859C7.20711 19.6346 7.32589 19.7599 7.47 19.85C7.62914 19.9486 7.81277 20.0006 8 20C8.15654 19.9991 8.31068 19.9614 8.45 19.89L12.45 17.89C12.6149 17.8069 12.7536 17.6798 12.8507 17.5227C12.9478 17.3656 12.9994 17.1847 13 17V12.41L19.12 6.29C19.3686 6.04266 19.5687 5.75097 19.71 5.43V5.37C19.8888 4.99443 19.9876 4.58578 20 4.17V3C20 2.20435 19.6839 1.44129 19.1213 0.87868C18.5587 0.316071 17.7956 0 17 0ZM11.29 11.29C11.1973 11.3834 11.124 11.4943 11.0742 11.6161C11.0245 11.7379 10.9992 11.8684 11 12V16.38L9 17.38V12C9.00076 11.8684 8.97554 11.7379 8.92577 11.6161C8.87601 11.4943 8.80268 11.3834 8.71 11.29L3.41 6H16.59L11.29 11.29ZM18 4H2V3C2 2.73478 2.10536 2.48043 2.29289 2.29289C2.48043 2.10536 2.73478 2 3 2H17C17.2652 2 17.5196 2.10536 17.7071 2.29289C17.8946 2.48043 18 2.73478 18 3V4Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconFilter
|
20
src/components/icons/Shipping.tsx
Normal file
20
src/components/icons/Shipping.tsx
Normal 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
|
@@ -22,10 +22,14 @@ export { default as IconPassword } from './IconPassword'
|
||||
export { default as IconPasswordCross } from './IconPasswordCross'
|
||||
export { default as IconError } from './IconError'
|
||||
export { default as IconCheck } from './IconCheck'
|
||||
export { default as Shipping} from './Shipping'
|
||||
export { default as IconTime } from './IconTime'
|
||||
export { default as IconPeople } from './IconPeople'
|
||||
export { default as IconLocation } from './IconLocation'
|
||||
export { default as IconClose } from './IconClose'
|
||||
export { default as IconDelete } from './IconDelete'
|
||||
export { default as IconPlus } from './IconPlus'
|
||||
export { default as IconMinus } from './IconMinus'
|
||||
export { default as IconMinus } from './IconMinus'
|
||||
export { default as IconCirclePlus } from './IconCirclePlus'
|
||||
export { default as IconDoneCheckout } from './IconDoneCheckout'
|
||||
export { default as IconFilter } from './IconFilter'
|
||||
|
@@ -63,6 +63,7 @@ const AccountNavigation = ({ defaultActiveIndex, children } : AccountNavigationP
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div ref={slider} className={s.slider}></div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { RefObject } from "react";
|
||||
import classNames from "classnames";
|
||||
import s from './AccountNavigationItem.module.scss'
|
||||
|
||||
|
@@ -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'
|
@@ -0,0 +1,8 @@
|
||||
@import "../../../../styles/_utilities";
|
||||
|
||||
.productListBanner{
|
||||
@apply spacing-horizontal;
|
||||
@screen md {
|
||||
padding:0 3.2rem;
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import { Banner } from 'src/components/common'
|
||||
import BannerRight from './assets/bannerrecipes.png'
|
||||
import s from './ProductListBanner.module.scss'
|
||||
|
||||
interface Props {
|
||||
}
|
||||
|
||||
const ProductListBanner = ({ }: Props) => {
|
||||
return (
|
||||
<div className={s.productListBanner}>
|
||||
<Banner
|
||||
data={
|
||||
[{
|
||||
title: "Save 15% on your first order",
|
||||
subtitle: "Last call! Shop deep deals on 100+ bulk picks while you can.",
|
||||
imgLink: BannerRight.src,
|
||||
size: "large",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductListBanner
|
Binary file not shown.
After Width: | Height: | Size: 780 KiB |
@@ -0,0 +1,88 @@
|
||||
@import "../../../../styles/_utilities";
|
||||
|
||||
.warpper {
|
||||
@apply spacing-horizontal;
|
||||
@screen md{
|
||||
padding:0 3.2rem;
|
||||
padding-bottom:5.6rem;
|
||||
}
|
||||
.breadcrumb{
|
||||
padding:1rem 0;
|
||||
}
|
||||
.main{
|
||||
@screen md {
|
||||
@apply flex;
|
||||
}
|
||||
.categories{
|
||||
@apply hidden;
|
||||
@screen md {
|
||||
@apply hidden;
|
||||
}
|
||||
@screen xl{
|
||||
@apply block;
|
||||
width:25%;
|
||||
}
|
||||
}
|
||||
.list{
|
||||
@screen md {
|
||||
@apply flex justify-between flex-wrap w-full;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
@screen xl {
|
||||
width:75%;
|
||||
}
|
||||
.inner{
|
||||
@screen md {
|
||||
@apply flex flex-col items-center justify-center;
|
||||
}
|
||||
.boxItem {
|
||||
@screen md {
|
||||
@apply flex justify-between flex-wrap;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.item {
|
||||
@screen md {
|
||||
width: calc(97% / 2);
|
||||
margin-top:1rem;
|
||||
}
|
||||
@screen lg{
|
||||
width: calc(97% / 3);
|
||||
margin-top:1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.boxSelect{
|
||||
@apply w-auto;
|
||||
// padding: 2.5rem 0;
|
||||
display: none;
|
||||
|
||||
@screen xl {
|
||||
@apply block;
|
||||
width: auto;
|
||||
padding:0;
|
||||
}
|
||||
.categorySelectCate{
|
||||
@screen xl {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
label{
|
||||
@apply font-bold topline ;
|
||||
color:var(--text-active);
|
||||
@screen xl {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
.select{
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
import React from 'react'
|
||||
import { HeadingCommon, ProductList, SelectCommon } from 'src/components/common'
|
||||
import BreadcrumbCommon from 'src/components/common/BreadcrumbCommon/BreadcrumbCommon'
|
||||
import MenuNavigation from 'src/components/common/MenuNavigation/MenuNavigation'
|
||||
import { BRAND, CATEGORY, FEATURED} from 'src/utils/constanst.utils'
|
||||
import { PRODUCT_DATA_TEST_PAGE } from 'src/utils/demo-data'
|
||||
import s from './ProductListFilter.module.scss'
|
||||
|
||||
interface ProductListFilterProps {}
|
||||
|
||||
const BREADCRUMB = [
|
||||
{
|
||||
name: 'Products',
|
||||
link: `#`,
|
||||
},
|
||||
]
|
||||
const OPTIONSLECT = [
|
||||
{
|
||||
name: 'Most Viewed',
|
||||
value: 'most-viewed',
|
||||
},
|
||||
{
|
||||
name: 'Lastest Products',
|
||||
value: 'lastest-products',
|
||||
},
|
||||
{
|
||||
name: 'Recent Products',
|
||||
value: 'recent-products',
|
||||
},
|
||||
]
|
||||
|
||||
const onModalClose = () => {
|
||||
|
||||
}
|
||||
|
||||
const ProductListFilter = (props: ProductListFilterProps) => {
|
||||
return (
|
||||
<div className={s.warpper}>
|
||||
<div className={s.breadcrumb}>
|
||||
<BreadcrumbCommon crumbs={BREADCRUMB} />
|
||||
</div>
|
||||
<div className={s.main}>
|
||||
<div className={s.categories}>
|
||||
<MenuNavigation categories={CATEGORY} heading="Categories" />
|
||||
<MenuNavigation categories={BRAND} heading="Brands" />
|
||||
<MenuNavigation categories={FEATURED} heading="featured" />
|
||||
</div>
|
||||
|
||||
<div className={s.list}>
|
||||
<HeadingCommon align="left">SPECIAL RECIPES</HeadingCommon>
|
||||
|
||||
<div className={s.boxSelect}>
|
||||
<div className={s.categorySelectSort}>
|
||||
<div className={s.select}>
|
||||
<SelectCommon option={OPTIONSLECT} placeholder="Sort By" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ProductList data={PRODUCT_DATA_TEST_PAGE} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductListFilter
|
@@ -53,4 +53,67 @@ export const KEY = {
|
||||
|
||||
export const OPTION_ALL = 'all';
|
||||
export const DEFAULT_PAGE_SIZE=20;
|
||||
|
||||
|
||||
export const CATEGORY = [
|
||||
{
|
||||
name: 'All',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=${OPTION_ALL}`,
|
||||
},
|
||||
{
|
||||
name: 'Veggie',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=veggie`,
|
||||
},
|
||||
{
|
||||
name: 'Seafood',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=seafood`,
|
||||
},
|
||||
{
|
||||
name: 'Frozen',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=frozen`,
|
||||
},
|
||||
{
|
||||
name: 'Coffee Bean',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=coffee_bean`,
|
||||
},
|
||||
{
|
||||
name: 'Sauce',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.CATEGORY}=sauce`,
|
||||
},
|
||||
]
|
||||
|
||||
export const BRAND = [
|
||||
{
|
||||
name: 'Maggi',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=maggi`,
|
||||
},
|
||||
{
|
||||
name: 'Chomilex',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chomilex`,
|
||||
},
|
||||
{
|
||||
name: 'Chinsu',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=chinsu`,
|
||||
},
|
||||
]
|
||||
|
||||
export const FEATURED = [
|
||||
{
|
||||
name: 'Best Sellers',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=best_sellers`,
|
||||
},
|
||||
{
|
||||
name: 'Sales',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=sales`,
|
||||
},
|
||||
{
|
||||
name: 'New Item',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=new_item`,
|
||||
},
|
||||
{
|
||||
name: 'Viewed',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.FEATURED}=viewed`,
|
||||
},
|
||||
]
|
||||
|
||||
export const DEFAULT_BLOG_PAGE_SIZE=6;
|
||||
|
@@ -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 = [
|
||||
@@ -218,4 +219,35 @@ export const RECIPE_DATA_TEST: RecipeCardProps[] = [
|
||||
imageSrc: 'https://user-images.githubusercontent.com/76729908/132159262-f28a9fb9-4852-47e6-80b5-d600521b548a.png',
|
||||
slug:"the-best-recipe-of-beef-noodle-soup"
|
||||
},
|
||||
]
|
||||
]
|
||||
|
||||
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
|
||||
},
|
||||
]
|
||||
export const PRODUCT_DATA_TEST_PAGE = [...PRODUCT_DATA_TEST, ...PRODUCT_DATA_TEST, ...PRODUCT_DATA_TEST, ...PRODUCT_DATA_TEST, ...PRODUCT_DATA_TEST]
|
@@ -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];
|
||||
}
|
@@ -32,4 +32,22 @@ export interface BlogProps {
|
||||
imageSrc: string
|
||||
}
|
||||
|
||||
export type MouseAndTouchEvent = MouseEvent | TouchEvent
|
||||
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
|
||||
|
||||
export type filterContextType = {
|
||||
visible: boolean;
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
};
|
Reference in New Issue
Block a user