mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
🔀 merge: branch 'common' of https://github.com/KieIO/grocery-vercel-commerce into m4-tan
:%s
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
:global(.customArrow) {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: .8rem;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
12
src/components/common/CartDrawer/CartDrawer.module.scss
Normal file
12
src/components/common/CartDrawer/CartDrawer.module.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
@import '../../../styles/utilities';
|
||||
|
||||
|
||||
.cartDrawer {
|
||||
@apply flex flex-col h-full;
|
||||
.body {
|
||||
@apply overflow-y-auto overflow-x-hidden h-full custom-scroll;
|
||||
}
|
||||
.bottom {
|
||||
padding-top: 1.6rem;
|
||||
}
|
||||
}
|
35
src/components/common/CartDrawer/CartDrawer.tsx
Normal file
35
src/components/common/CartDrawer/CartDrawer.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data';
|
||||
import { DrawerCommon } from '..';
|
||||
import s from './CartDrawer.module.scss';
|
||||
import CartCheckoutButton from './components/CartCheckoutButton/CartCheckoutButton';
|
||||
import CartMessage from './components/CartMessage/CartMessage';
|
||||
import CartRecommendation from './components/CartRecommendation/CartRecommendation';
|
||||
import ProductsInCart from './components/ProductsInCart/ProductsInCart';
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const CartDrawer = ({ visible, onClose }: Props) => {
|
||||
return (
|
||||
<DrawerCommon
|
||||
title={`Your cart (${PRODUCT_CART_DATA_TEST.length})`}
|
||||
visible={visible}
|
||||
onClose={onClose}>
|
||||
<div className={s.cartDrawer}>
|
||||
<div className={s.body}>
|
||||
<ProductsInCart data={PRODUCT_CART_DATA_TEST}/>
|
||||
<CartRecommendation />
|
||||
</div>
|
||||
<div className={s.bottom}>
|
||||
<CartMessage />
|
||||
<CartCheckoutButton />
|
||||
</div>
|
||||
</div>
|
||||
</DrawerCommon>
|
||||
)
|
||||
}
|
||||
|
||||
export default CartDrawer;
|
@@ -0,0 +1,6 @@
|
||||
.cartCheckoutButton {
|
||||
padding: 1.6rem;
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
import React, { memo } from 'react';
|
||||
import { ButtonCommon } from 'src/components/common';
|
||||
import s from './CartCheckoutButton.module.scss';
|
||||
|
||||
const CartCheckoutButton = memo(() => {
|
||||
return (
|
||||
<div className={s.cartCheckoutButton}>
|
||||
<ButtonCommon size='large'>Check out - Rp 120.500</ButtonCommon>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default CartCheckoutButton;
|
@@ -0,0 +1,16 @@
|
||||
@import "../../../../../styles/utilities";
|
||||
|
||||
.cartMessage {
|
||||
@apply flex bg-info;
|
||||
padding: 1.2rem 1.6rem;
|
||||
.text {
|
||||
color: var(--white);
|
||||
font-weight: bold;
|
||||
margin-right: 0.8rem;
|
||||
}
|
||||
.icon {
|
||||
svg path {
|
||||
fill: var(--text-placeholder);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
import React, { memo } from 'react';
|
||||
import { IconInfo } from 'src/components/icons';
|
||||
import s from './CartMessage.module.scss';
|
||||
|
||||
const CartMessage = memo(() => {
|
||||
return (
|
||||
<div className={s.cartMessage}>
|
||||
<div className={s.text}>
|
||||
You save - Rp 150
|
||||
</div>
|
||||
<div className={s.icon}>
|
||||
<IconInfo />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default CartMessage;
|
@@ -0,0 +1,26 @@
|
||||
@import '../../../../../styles/utilities';
|
||||
|
||||
.cartRecommendation {
|
||||
@apply w-full bg-background-gray;
|
||||
.top {
|
||||
@apply flex justify-between items-center;
|
||||
padding: 1.6rem;
|
||||
.heading {
|
||||
@apply font-bold text-active sm-headline;
|
||||
}
|
||||
}
|
||||
.productCardWarpper {
|
||||
padding-left: 1.6rem;
|
||||
:global(.customArrow) {
|
||||
@apply bg-line;
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem - 2rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem - 2rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
import { TOptionsEvents } from 'keen-slider';
|
||||
import React from 'react';
|
||||
import { CarouselCommon, ViewAllItem } from 'src/components/common';
|
||||
import ProductCard, { ProductCardProps } from 'src/components/common/ProductCard/ProductCard';
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
import { PRODUCT_DATA_TEST } from 'src/utils/demo-data';
|
||||
import s from './CartRecommendation.module.scss';
|
||||
|
||||
const option: TOptionsEvents = {
|
||||
slidesPerView: 2,
|
||||
mode: 'free',
|
||||
breakpoints: {
|
||||
'(min-width: 640px)': {
|
||||
slidesPerView: 1,
|
||||
},
|
||||
'(min-width: 768px)': {
|
||||
slidesPerView: 2.5,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const CartRecommendation = () => {
|
||||
return (
|
||||
<div className={s.cartRecommendation}>
|
||||
<div className={s.top}>
|
||||
<div className={s.heading}>
|
||||
Recommendation
|
||||
</div>
|
||||
<ViewAllItem link={ROUTE.PRODUCTS} />
|
||||
</div>
|
||||
<div className={s.productCardWarpper}>
|
||||
<CarouselCommon<ProductCardProps>
|
||||
data={PRODUCT_DATA_TEST}
|
||||
Component={ProductCard}
|
||||
itemKey="cart-recommendation"
|
||||
option={option}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CartRecommendation;
|
@@ -0,0 +1,54 @@
|
||||
@import "../../../../../styles/utilities";
|
||||
|
||||
.productCartItem {
|
||||
@apply grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
padding-bottom: 1.6rem;
|
||||
padding-top: 1.6rem;
|
||||
.info {
|
||||
@apply flex;
|
||||
.imgWrap {
|
||||
width: 11rem;
|
||||
height: 7.5rem;
|
||||
margin-right: 1.6rem;
|
||||
img {
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
.detail {
|
||||
min-height: 9rem;
|
||||
.name {
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
.price {
|
||||
margin-top: 0.8rem;
|
||||
.old {
|
||||
margin-bottom: 0.8rem;
|
||||
.number {
|
||||
margin-right: 0.8rem;
|
||||
color: var(--text-label);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
.current {
|
||||
@apply text-active font-bold sm-headline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
@apply flex flex-col justify-between items-end;
|
||||
margin-left: 1.6rem;
|
||||
.iconDelete {
|
||||
@apply cursor-pointer;
|
||||
&:hover {
|
||||
svg path {
|
||||
fill: var(--negative);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link'
|
||||
import { QuanittyInput } from 'src/components/common';
|
||||
import { IconDelete } from 'src/components/icons';
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
import { ProductProps } from 'src/utils/types.utils';
|
||||
import ImgWithLink from '../../../ImgWithLink/ImgWithLink';
|
||||
import LabelCommon from '../../../LabelCommon/LabelCommon';
|
||||
import s from './ProductCartItem.module.scss';
|
||||
|
||||
export interface ProductCartItempProps extends ProductProps {
|
||||
quantity: number,
|
||||
}
|
||||
|
||||
const ProductCartItem = ({ name, slug, weight, price, oldPrice, discount, imageSrc, quantity }: ProductCartItempProps) => {
|
||||
return (
|
||||
<div className={s.productCartItem}>
|
||||
<div className={s.info}>
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a href="">
|
||||
<div className={s.imgWrap}>
|
||||
<ImgWithLink src={imageSrc} alt={name} />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.detail}>
|
||||
<Link href={`${ROUTE.PRODUCT_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.name}>
|
||||
{name} {weight ? `(${weight})` : ''}
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.price}>
|
||||
{
|
||||
oldPrice &&
|
||||
<div className={s.old}>
|
||||
<span className={s.number}>{oldPrice}</span>
|
||||
<LabelCommon type='discount'>{discount}</LabelCommon>
|
||||
</div>
|
||||
}
|
||||
<div className={s.current}>{price}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.actions}>
|
||||
<div className={s.iconDelete}>
|
||||
<IconDelete />
|
||||
</div>
|
||||
<QuanittyInput size='small' initValue={quantity} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCartItem;
|
@@ -0,0 +1,9 @@
|
||||
.productsInCart {
|
||||
padding: 1.6rem 1.6rem 0 1.6rem;
|
||||
list-style: none;
|
||||
li {
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid var(--border-line);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import ProductCartItem, { ProductCartItempProps } from '../ProductCartItem/ProductCartItem';
|
||||
import s from './ProductsInCart.module.scss';
|
||||
|
||||
interface Props {
|
||||
data: ProductCartItempProps[]
|
||||
}
|
||||
|
||||
const ProductsInCart = ({ data }: Props) => {
|
||||
return (
|
||||
<ul className={s.productsInCart}>
|
||||
{
|
||||
data.map(item => <li key={item.name}>
|
||||
<ProductCartItem
|
||||
name={item.name}
|
||||
slug={item.slug}
|
||||
weight={item.weight}
|
||||
price={item.price}
|
||||
oldPrice={item.oldPrice}
|
||||
discount={item.discount}
|
||||
imageSrc={item.imageSrc}
|
||||
quantity={item.quantity}
|
||||
/>
|
||||
</li>)
|
||||
}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductsInCart;
|
52
src/components/common/DrawerCommon/DrawerCommon.module.scss
Normal file
52
src/components/common/DrawerCommon/DrawerCommon.module.scss
Normal file
@@ -0,0 +1,52 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.drawerCommon {
|
||||
@apply fixed flex justify-end transition-all duration-200;
|
||||
overflow: hidden;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100vh;
|
||||
width: 90%;
|
||||
box-shadow: -3px 0 10px rgba(0, 0, 0, 0.15);
|
||||
z-index: 20000;
|
||||
|
||||
.inner {
|
||||
@apply flex flex-col bg-white;
|
||||
width: fit-content;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
@screen md {
|
||||
max-width: 55rem;
|
||||
}
|
||||
.top {
|
||||
@apply flex justify-between items-center;
|
||||
padding: 1.6rem;
|
||||
.heading {
|
||||
@apply sm-headline;
|
||||
}
|
||||
.iconClose {
|
||||
@apply cursor-pointer transition-all duration-200;
|
||||
&:hover {
|
||||
svg path {
|
||||
fill: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
&.hide {
|
||||
transform: translateX(110%);
|
||||
}
|
||||
|
||||
@screen md {
|
||||
width: unset;
|
||||
.inner {
|
||||
min-width: 48rem;
|
||||
}
|
||||
}
|
||||
}
|
36
src/components/common/DrawerCommon/DrawerCommon.tsx
Normal file
36
src/components/common/DrawerCommon/DrawerCommon.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React, { useRef } from 'react';
|
||||
import s from './DrawerCommon.module.scss';
|
||||
import classNames from 'classnames';
|
||||
import { IconClose } from 'src/components/icons';
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
title?: string
|
||||
children?: React.ReactNode
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const DrawerCommon = ({ title, visible, children, onClose }: Props) => {
|
||||
return (
|
||||
<div className={classNames({
|
||||
[s.drawerCommon]: true,
|
||||
[s.hide]: !visible
|
||||
})}>
|
||||
<div className={s.inner}>
|
||||
<div className={s.top}>
|
||||
<h4 className={s.heading}>
|
||||
{title}
|
||||
</h4>
|
||||
<div className={s.iconClose} onClick={onClose}>
|
||||
<IconClose />
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.content}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DrawerCommon;
|
@@ -42,7 +42,7 @@
|
||||
@apply hidden;
|
||||
@screen md {
|
||||
@apply flex items-center list-none;
|
||||
li {
|
||||
> li {
|
||||
@apply flex justify-center items-center w-full;
|
||||
&:not(:last-child) {
|
||||
margin-right: 4.8rem;
|
||||
@@ -52,13 +52,23 @@
|
||||
}
|
||||
a {
|
||||
@appy no-underline;
|
||||
&.iconFovourite {
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
&.iconFavourite {
|
||||
svg path {
|
||||
fill: var(--negative);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btnCart {
|
||||
&:hover {
|
||||
svg path {
|
||||
fill: var(--primary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ 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 { ACCOUNT_TAB, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
|
||||
import Logo from '../../../Logo/Logo'
|
||||
import s from './HeaderMenu.module.scss'
|
||||
|
||||
interface Props {
|
||||
@@ -39,7 +40,7 @@ const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo }: Props) => {
|
||||
<section className={classNames({ [s.headerMenu]: true, [s.full]: isFull })}>
|
||||
<div className={s.left}>
|
||||
<div className={s.top}>
|
||||
<div>Online Grocery</div>
|
||||
<Logo/>
|
||||
<button className={s.iconCart}>
|
||||
<IconBuy />
|
||||
</button>
|
||||
@@ -51,14 +52,14 @@ const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo }: Props) => {
|
||||
<ul className={s.menu}>
|
||||
<li>
|
||||
<Link href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.ORDER}`}>
|
||||
<a >
|
||||
<a>
|
||||
<IconHistory />
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.FAVOURITE}`}>
|
||||
<a className={s.iconFovourite}>
|
||||
<a className={s.iconFavourite}>
|
||||
<IconHeart />
|
||||
</a>
|
||||
</Link>
|
||||
@@ -67,7 +68,7 @@ const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo }: Props) => {
|
||||
<MenuDropdown options={optionMenu} isHasArrow={false}><IconUser /></MenuDropdown>
|
||||
</li>
|
||||
<li>
|
||||
<button>
|
||||
<button className={s.btnCart}>
|
||||
<IconBuy />
|
||||
</button>
|
||||
</li>
|
||||
|
@@ -1,4 +1,9 @@
|
||||
.imgWithLink {
|
||||
@apply w-full h-full;
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
min-width: 5rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import s from './ImgWithLink.module.scss'
|
||||
import Image from 'next/image'
|
||||
|
||||
export interface ImgWithLinkProps {
|
||||
src: string,
|
||||
@@ -8,8 +9,9 @@ export interface ImgWithLinkProps {
|
||||
|
||||
const ImgWithLink = ({ src, alt }: ImgWithLinkProps) => {
|
||||
return (
|
||||
<img className={s.imgWithLink} src={src} alt={alt} />
|
||||
|
||||
<div className={s.imgWithLink}>
|
||||
<Image src={src} alt={alt} layout="fill" className={s.imgWithLink} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { CommerceProvider } from '@framework'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC } from 'react'
|
||||
import { useModalCommon } from 'src/components/hooks'
|
||||
import { CartDrawer } from '..'
|
||||
import Footer from '../Footer/Footer'
|
||||
import Header from '../Header/Header'
|
||||
import s from './Layout.module.scss'
|
||||
@@ -13,13 +15,25 @@ interface Props {
|
||||
// note: demo code
|
||||
const Layout: FC<Props> = ({ children }) => {
|
||||
const { locale = 'en-US' } = useRouter()
|
||||
const { visible: visibleCartDrawer, openModal, closeModal: closeCartDrawer } = useModalCommon({ initialValue: false })
|
||||
|
||||
const toggle = () => {
|
||||
if (visibleCartDrawer) {
|
||||
closeCartDrawer()
|
||||
} else {
|
||||
openModal()
|
||||
}
|
||||
}
|
||||
return (
|
||||
<CommerceProvider locale={locale}>
|
||||
<div className={s.mainLayout}>
|
||||
{/* <Header /> */}
|
||||
<Header />
|
||||
<main >{children}</main>
|
||||
{/* <Footer /> */}
|
||||
<button onClick={toggle}>toggle card: {visibleCartDrawer.toString()}</button>
|
||||
<CartDrawer
|
||||
visible={visibleCartDrawer}
|
||||
onClose={closeCartDrawer} />
|
||||
<Footer />
|
||||
</div>
|
||||
</CommerceProvider>
|
||||
|
||||
|
@@ -11,7 +11,7 @@
|
||||
padding-top: 5.6rem;
|
||||
padding-bottom: 5.6rem;
|
||||
border: none;
|
||||
background-color: #f5f4f2;
|
||||
background-color: var(--background-gray);
|
||||
}
|
||||
.productsWrap {
|
||||
@apply spacing-horizontal-left;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
@import '../../../styles/utilities';
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
@apply flex justify-center items-center;
|
||||
.eclipse {
|
||||
width: 3.2rem;
|
||||
height: 3.2rem;
|
||||
@@ -10,9 +10,9 @@
|
||||
margin-right: 1.2rem;
|
||||
}
|
||||
.content {
|
||||
@apply font-logo;
|
||||
@apply font-logo font-bold;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,14 +1,18 @@
|
||||
import s from './Logo.module.scss'
|
||||
import Link from 'next/link'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
|
||||
const Logo = () => {
|
||||
return(
|
||||
<div className={s.logo}>
|
||||
<div className={s.eclipse}>
|
||||
</div>
|
||||
<div className={s.content}>
|
||||
ONLINE GROCERY
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<Link href={ROUTE.HOME}>
|
||||
<a className={s.logo}>
|
||||
<div className={s.eclipse}>
|
||||
</div>
|
||||
<div className={s.content}>
|
||||
ONLINE GROCERY
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -17,10 +17,21 @@
|
||||
}
|
||||
|
||||
.label {
|
||||
all: unset;
|
||||
@apply flex justify-end items-center transition-all duration-200;
|
||||
svg path {
|
||||
width: fit-content;
|
||||
}
|
||||
&:focus,
|
||||
&:active {
|
||||
color: var(--primary);
|
||||
svg path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: 2px solid #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.arrow {
|
||||
@@ -63,8 +74,9 @@
|
||||
@apply rounded list-none bg-white;
|
||||
border: 1px solid var(--text-active);
|
||||
margin-top: 0.4rem;
|
||||
li {
|
||||
> li {
|
||||
@apply block w-full transition-all duration-200 cursor-pointer text-active;
|
||||
white-space: nowrap;
|
||||
button {
|
||||
all: unset;
|
||||
color: currentColor;
|
||||
|
@@ -16,9 +16,9 @@ const MenuDropdown = ({ options, children, isHasArrow = true, align }: Props) =>
|
||||
[s.menuDropdown]: true,
|
||||
[s.arrow]: isHasArrow,
|
||||
})}>
|
||||
<span className={s.label}>
|
||||
<button className={s.label}>
|
||||
{children}
|
||||
</span>
|
||||
</button>
|
||||
<section className={classNames({
|
||||
[s.menu]: true,
|
||||
[s.left]: align === 'left',
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import Link from 'next/link'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { ButtonCommon, Inputcommon } from 'src/components/common'
|
||||
import InputPassword from 'src/components/common/InputPassword/InputPassword'
|
||||
import { ButtonCommon, Inputcommon, InputPassword } from 'src/components/common'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import { CustomInputCommon } from 'src/utils/type.utils'
|
||||
import s from '../FormAuthen.module.scss'
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { ButtonCommon, Inputcommon } from 'src/components/common'
|
||||
import { ButtonCommon, Inputcommon, InputPassword } from 'src/components/common'
|
||||
import s from '../FormAuthen.module.scss'
|
||||
import styles from './FormRegister.module.scss'
|
||||
import SocialAuthen from '../SocialAuthen/SocialAuthen'
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { ChangeEvent, useEffect, useState } from 'react'
|
||||
import s from './QuanittyInput.module.scss'
|
||||
import classNames from 'classnames'
|
||||
import { Minus, Plus } from '@components/icons'
|
||||
import { IconMinus, IconPlus } from '../../icons'
|
||||
interface QuanittyInputProps
|
||||
extends Omit<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
@@ -64,7 +64,7 @@ const QuanittyInput = ({
|
||||
return (
|
||||
<div className={classNames(s.quanittyInputWarper, { [s[size]]: size })}>
|
||||
<div className={s.minusIcon} onClick={onMinusClick}>
|
||||
<Minus />
|
||||
<IconMinus />
|
||||
</div>
|
||||
<input
|
||||
{...props}
|
||||
@@ -74,7 +74,7 @@ const QuanittyInput = ({
|
||||
className={s.quanittyInput}
|
||||
/>
|
||||
<div className={s.plusIcon} onClick={onPlusClick}>
|
||||
<Plus />
|
||||
<IconPlus />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@@ -1,69 +1,90 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.select{
|
||||
.select {
|
||||
background-color: var(--white);
|
||||
&.base{
|
||||
.selectTrigger {
|
||||
svg {
|
||||
@apply transition-all duration-200;
|
||||
}
|
||||
}
|
||||
&.base {
|
||||
width: 20.6rem;
|
||||
.selectTrigger{
|
||||
.selectTrigger {
|
||||
width: 20.6rem;
|
||||
padding: 1.2rem 1.6rem;
|
||||
}
|
||||
}
|
||||
&.large{
|
||||
&.large {
|
||||
width: 34.25rem;
|
||||
.selectTrigger{
|
||||
.selectTrigger {
|
||||
width: 34.25rem;
|
||||
padding: 1.6rem 1.6rem;
|
||||
}
|
||||
}
|
||||
&.default{
|
||||
.selectTrigger{
|
||||
&.default {
|
||||
.selectTrigger {
|
||||
@apply border-solid border border-current;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.custom{
|
||||
.selectTrigger{
|
||||
&.custom {
|
||||
.selectTrigger {
|
||||
@apply border-2;
|
||||
border-color: var(--border-line);
|
||||
color: var(--text-label);
|
||||
}
|
||||
}
|
||||
&.isActive{
|
||||
.selectOptionWrapper{
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
.hoverWrapper {
|
||||
@apply block;
|
||||
animation: SelectAnimation 0.2s ease-out;
|
||||
}
|
||||
.selectTrigger {
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.selectTrigger{
|
||||
.selectTrigger {
|
||||
@apply outline-none flex justify-between;
|
||||
color: var(--text-active);
|
||||
border-radius: 0.8rem;
|
||||
|
||||
}
|
||||
.selectOptionWrapper{
|
||||
@apply outline-none hidden z-10 absolute;
|
||||
border-radius: 0.8rem;
|
||||
background-color: var(--white);
|
||||
padding: 0.4rem 0rem 0.4rem 0rem;
|
||||
margin-top: 0.6rem;
|
||||
&.base{
|
||||
width: 20.6rem;
|
||||
.hoverWrapper {
|
||||
@apply hidden outline-none absolute z-10;
|
||||
padding-top: 0.6rem;
|
||||
.selectOptionWrapper {
|
||||
border-radius: 0.8rem;
|
||||
background-color: var(--white);
|
||||
padding: 0.4rem 0rem 0.4rem 0rem;
|
||||
&.base {
|
||||
width: 20.6rem;
|
||||
}
|
||||
&.large {
|
||||
width: 34.25rem;
|
||||
}
|
||||
&.default {
|
||||
@apply border-solid border border-current;
|
||||
}
|
||||
&.custom {
|
||||
@apply border-2;
|
||||
border-color: var(--border-line);
|
||||
color: var(--text-label);
|
||||
}
|
||||
}
|
||||
&.large{
|
||||
width: 34.25rem;
|
||||
}
|
||||
&.default{
|
||||
@apply border-solid border border-current;
|
||||
}
|
||||
&.custom{
|
||||
@apply border-2;
|
||||
border-color: var(--border-line);
|
||||
color: var(--text-label);
|
||||
}
|
||||
&.active{
|
||||
@apply hidden;
|
||||
&:hover {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@keyframes SelectAnimation {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(1.6rem);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +1,25 @@
|
||||
import s from './SelectCommon.module.scss'
|
||||
import classNames from 'classnames'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { IconVectorDown } from 'src/components/icons'
|
||||
import SelectOption from './SelectOption/SelectOption'
|
||||
|
||||
interface Props {
|
||||
children? : React.ReactNode,
|
||||
placeholder? : string,
|
||||
size?: 'base' | 'large',
|
||||
type?: 'default' | 'custom',
|
||||
option: {name: string}[],
|
||||
option: {name: string, value: string}[],
|
||||
onChange?: (value: string) => void,
|
||||
}
|
||||
|
||||
const SelectCommon = ({ type = 'default', size = 'base', option, children }: Props) => {
|
||||
const [isActive, setActive] = useState(false)
|
||||
const [selectedName, setSelectedName] = useState(children)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
const { target } = event;
|
||||
if (!ref?.current || ref?.current.contains(target as Node)) {
|
||||
return
|
||||
}
|
||||
else{
|
||||
setActive(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', handleClick)
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClick)
|
||||
}
|
||||
}, [ref])
|
||||
const SelectCommon = ({ type = 'default', size = 'base', option, placeholder, onChange}: Props) => {
|
||||
const [selectedName, setSelectedName] = useState(placeholder)
|
||||
const [selectedValue, setSelectedValue] = useState('')
|
||||
|
||||
const changeActiveStatus = () => {
|
||||
setActive(!isActive)
|
||||
}
|
||||
|
||||
const changeSelectedName = (item:string) => {
|
||||
const changeSelectedName = (item:string, value: string) => {
|
||||
setSelectedValue(value)
|
||||
setSelectedName(item)
|
||||
onChange && onChange(value)
|
||||
}
|
||||
return(
|
||||
<>
|
||||
@@ -45,29 +27,29 @@ const SelectCommon = ({ type = 'default', size = 'base', option, children }: Pro
|
||||
[s.select] : true,
|
||||
[s[size]] : !!size,
|
||||
[s[type]] : !!type,
|
||||
[s.isActive] : isActive,
|
||||
})}
|
||||
onClick = { changeActiveStatus }
|
||||
ref = {ref}
|
||||
>
|
||||
<div className={classNames({
|
||||
[s.selectTrigger] : true,
|
||||
|
||||
})}
|
||||
>{selectedName}<IconVectorDown /></div>
|
||||
|
||||
<div className={classNames({
|
||||
[s.selectOptionWrapper] : true,
|
||||
[s[type]] : !!type,
|
||||
[s[size]] : !!size,
|
||||
})}
|
||||
>
|
||||
{
|
||||
option.map(item =>
|
||||
<SelectOption itemName={item.name} onClick={changeSelectedName} size={size} />
|
||||
)
|
||||
}
|
||||
|
||||
<div className={s.hoverWrapper}>
|
||||
<div className={classNames({
|
||||
[s.selectOptionWrapper] : true,
|
||||
[s[type]] : !!type,
|
||||
[s[size]] : !!size,
|
||||
})}
|
||||
>
|
||||
{
|
||||
option.map(item =>
|
||||
<SelectOption key={item.value} itemName={item.name} value={item.value} onClick={changeSelectedName} size={size} selected={(selectedValue === item.value)} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@@ -13,5 +13,9 @@
|
||||
}
|
||||
&:hover{
|
||||
background-color: var(--gray);
|
||||
color: var(--primary);
|
||||
}
|
||||
&.isChoose{
|
||||
background-color: var(--gray);
|
||||
}
|
||||
}
|
@@ -2,20 +2,22 @@ import s from './SelectOption.module.scss'
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface Props{
|
||||
onClick: (value: string) => void,
|
||||
onClick: (name: string, value: string) => void,
|
||||
itemName: string,
|
||||
size: 'base' | 'large',
|
||||
value: string,
|
||||
selected?: boolean,
|
||||
}
|
||||
|
||||
const SelectOption = ({onClick, itemName, size}: Props) => {
|
||||
|
||||
const SelectOption = ({onClick, itemName, size, value, selected} : Props) => {
|
||||
const changeName = () => {
|
||||
onClick(itemName)
|
||||
onClick(itemName, value)
|
||||
}
|
||||
return(
|
||||
<div className={classNames({
|
||||
[s.selectOption] : true,
|
||||
[s[size]] : !!size,
|
||||
[s.isChoose] : selected ,
|
||||
})}
|
||||
onClick = {changeName}
|
||||
>{itemName}</div>
|
||||
|
@@ -36,3 +36,5 @@ export { default as RelevantBlogPosts} from './RelevantBlogPosts/RelevantBlogPos
|
||||
export { default as CollapseCommon} from './CollapseCommon/CollapseCommon'
|
||||
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'
|
||||
|
20
src/components/icons/IconClose.tsx
Normal file
20
src/components/icons/IconClose.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconClose = () => {
|
||||
return (
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.4099 9.00019L16.7099 2.71019C16.8982 2.52188 17.004 2.26649 17.004 2.00019C17.004 1.73388 16.8982 1.47849 16.7099 1.29019C16.5216 1.10188 16.2662 0.996094 15.9999 0.996094C15.7336 0.996094 15.4782 1.10188 15.2899 1.29019L8.99994 7.59019L2.70994 1.29019C2.52164 1.10188 2.26624 0.996094 1.99994 0.996094C1.73364 0.996094 1.47824 1.10188 1.28994 1.29019C1.10164 1.47849 0.995847 1.73388 0.995847 2.00019C0.995847 2.26649 1.10164 2.52188 1.28994 2.71019L7.58994 9.00019L1.28994 15.2902C1.19621 15.3831 1.12182 15.4937 1.07105 15.6156C1.02028 15.7375 0.994141 15.8682 0.994141 16.0002C0.994141 16.1322 1.02028 16.2629 1.07105 16.3848C1.12182 16.5066 1.19621 16.6172 1.28994 16.7102C1.3829 16.8039 1.4935 16.8783 1.61536 16.9291C1.73722 16.9798 1.86793 17.006 1.99994 17.006C2.13195 17.006 2.26266 16.9798 2.38452 16.9291C2.50638 16.8783 2.61698 16.8039 2.70994 16.7102L8.99994 10.4102L15.2899 16.7102C15.3829 16.8039 15.4935 16.8783 15.6154 16.9291C15.7372 16.9798 15.8679 17.006 15.9999 17.006C16.132 17.006 16.2627 16.9798 16.3845 16.9291C16.5064 16.8783 16.617 16.8039 16.7099 16.7102C16.8037 16.6172 16.8781 16.5066 16.9288 16.3848C16.9796 16.2629 17.0057 16.1322 17.0057 16.0002C17.0057 15.8682 16.9796 15.7375 16.9288 15.6156C16.8781 15.4937 16.8037 15.3831 16.7099 15.2902L10.4099 9.00019Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconClose
|
11
src/components/icons/IconDelete.tsx
Normal file
11
src/components/icons/IconDelete.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconDelete = () => {
|
||||
return (
|
||||
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 16C7.26522 16 7.51957 15.8946 7.70711 15.7071C7.89464 15.5196 8 15.2652 8 15V9C8 8.73478 7.89464 8.48043 7.70711 8.29289C7.51957 8.10536 7.26522 8 7 8C6.73478 8 6.48043 8.10536 6.29289 8.29289C6.10536 8.48043 6 8.73478 6 9V15C6 15.2652 6.10536 15.5196 6.29289 15.7071C6.48043 15.8946 6.73478 16 7 16ZM17 4H13V3C13 2.20435 12.6839 1.44129 12.1213 0.87868C11.5587 0.316071 10.7956 0 10 0H8C7.20435 0 6.44129 0.316071 5.87868 0.87868C5.31607 1.44129 5 2.20435 5 3V4H1C0.734784 4 0.48043 4.10536 0.292893 4.29289C0.105357 4.48043 0 4.73478 0 5C0 5.26522 0.105357 5.51957 0.292893 5.70711C0.48043 5.89464 0.734784 6 1 6H2V17C2 17.7956 2.31607 18.5587 2.87868 19.1213C3.44129 19.6839 4.20435 20 5 20H13C13.7956 20 14.5587 19.6839 15.1213 19.1213C15.6839 18.5587 16 17.7956 16 17V6H17C17.2652 6 17.5196 5.89464 17.7071 5.70711C17.8946 5.51957 18 5.26522 18 5C18 4.73478 17.8946 4.48043 17.7071 4.29289C17.5196 4.10536 17.2652 4 17 4ZM7 3C7 2.73478 7.10536 2.48043 7.29289 2.29289C7.48043 2.10536 7.73478 2 8 2H10C10.2652 2 10.5196 2.10536 10.7071 2.29289C10.8946 2.48043 11 2.73478 11 3V4H7V3ZM14 17C14 17.2652 13.8946 17.5196 13.7071 17.7071C13.5196 17.8946 13.2652 18 13 18H5C4.73478 18 4.48043 17.8946 4.29289 17.7071C4.10536 17.5196 4 17.2652 4 17V6H14V17ZM11 16C11.2652 16 11.5196 15.8946 11.7071 15.7071C11.8946 15.5196 12 15.2652 12 15V9C12 8.73478 11.8946 8.48043 11.7071 8.29289C11.5196 8.10536 11.2652 8 11 8C10.7348 8 10.4804 8.10536 10.2929 8.29289C10.1054 8.48043 10 8.73478 10 9V15C10 15.2652 10.1054 15.5196 10.2929 15.7071C10.4804 15.8946 10.7348 16 11 16Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconDelete
|
@@ -1,14 +1,14 @@
|
||||
const IconMinus = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 22 4"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.333 0.666748H1.66634C1.31272 0.666748 0.973581 0.807224 0.723532 1.05727C0.473484 1.30732 0.333008 1.64646 0.333008 2.00008C0.333008 2.3537 0.473484 2.69284 0.723532 2.94289C0.973581 3.19294 1.31272 3.33342 1.66634 3.33342H20.333C20.6866 3.33342 21.0258 3.19294 21.2758 2.94289C21.5259 2.69284 21.6663 2.3537 21.6663 2.00008C21.6663 1.64646 21.5259 1.30732 21.2758 1.05727C21.0258 0.807224 20.6866 0.666748 20.333 0.666748Z"
|
||||
d="M10.6667 5.33317H6.66669V1.33317C6.66669 1.15636 6.59645 0.98679 6.47142 0.861766C6.3464 0.736742 6.17683 0.666504 6.00002 0.666504C5.82321 0.666504 5.65364 0.736742 5.52862 0.861766C5.40359 0.98679 5.33335 1.15636 5.33335 1.33317V5.33317H1.33335C1.15654 5.33317 0.986974 5.40341 0.861949 5.52843C0.736925 5.65346 0.666687 5.82303 0.666687 5.99984C0.666687 6.17665 0.736925 6.34622 0.861949 6.47124C0.986974 6.59627 1.15654 6.6665 1.33335 6.6665H5.33335V10.6665C5.33335 10.8433 5.40359 11.0129 5.52862 11.1379C5.65364 11.2629 5.82321 11.3332 6.00002 11.3332C6.17683 11.3332 6.3464 11.2629 6.47142 11.1379C6.59645 11.0129 6.66669 10.8433 6.66669 10.6665V6.6665H10.6667C10.8435 6.6665 11.0131 6.59627 11.1381 6.47124C11.2631 6.34622 11.3334 6.17665 11.3334 5.99984C11.3334 5.82303 11.2631 5.65346 11.1381 5.52843C11.0131 5.40341 10.8435 5.33317 10.6667 5.33317Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
|
@@ -1,14 +1,14 @@
|
||||
const IconPlus = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="22"
|
||||
height="22"
|
||||
viewBox="0 0 22 22"
|
||||
width="12"
|
||||
height="2"
|
||||
viewBox="0 0 12 2"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.333 9.66658H12.333V1.66659C12.333 1.31296 12.1925 0.973825 11.9425 0.723776C11.6924 0.473728 11.3533 0.333252 10.9997 0.333252C10.6461 0.333252 10.3069 0.473728 10.0569 0.723776C9.80682 0.973825 9.66634 1.31296 9.66634 1.66659V9.66658H1.66634C1.31272 9.66658 0.973581 9.80706 0.723532 10.0571C0.473484 10.3072 0.333008 10.6463 0.333008 10.9999C0.333008 11.3535 0.473484 11.6927 0.723532 11.9427C0.973581 12.1928 1.31272 12.3333 1.66634 12.3333H9.66634V20.3333C9.66634 20.6869 9.80682 21.026 10.0569 21.2761C10.3069 21.5261 10.6461 21.6666 10.9997 21.6666C11.3533 21.6666 11.6924 21.5261 11.9425 21.2761C12.1925 21.026 12.333 20.6869 12.333 20.3333V12.3333H20.333C20.6866 12.3333 21.0258 12.1928 21.2758 11.9427C21.5259 11.6927 21.6663 11.3535 21.6663 10.9999C21.6663 10.6463 21.5259 10.3072 21.2758 10.0571C21.0258 9.80706 20.6866 9.66658 20.333 9.66658Z"
|
||||
d="M10.6667 0.333496H1.33335C1.15654 0.333496 0.986974 0.403734 0.861949 0.528758C0.736925 0.653783 0.666687 0.823352 0.666687 1.00016C0.666687 1.17697 0.736925 1.34654 0.861949 1.47157C0.986974 1.59659 1.15654 1.66683 1.33335 1.66683H10.6667C10.8435 1.66683 11.0131 1.59659 11.1381 1.47157C11.2631 1.34654 11.3334 1.17697 11.3334 1.00016C11.3334 0.823352 11.2631 0.653783 11.1381 0.528758C11.0131 0.403734 10.8435 0.333496 10.6667 0.333496Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
|
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
const IconVectorDown = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
|
@@ -23,3 +23,7 @@ export { default as IconCheck } from './IconCheck'
|
||||
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'
|
||||
|
@@ -11,11 +11,19 @@
|
||||
@apply grid;
|
||||
grid-template-columns: 1fr 1.8fr;
|
||||
.left {
|
||||
@apply flex items-end justify-center custom-border-radius-lg;
|
||||
@apply relative flex items-end justify-center custom-border-radius-lg;
|
||||
margin-right: 1.6rem;
|
||||
background-image: url('./assets/home_banner.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
.imgWrap {
|
||||
@apply absolute w-full h-full;
|
||||
top: 0;
|
||||
left: 0;
|
||||
> div {
|
||||
@apply w-full h-full custom-border-radius-lg;
|
||||
}
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
@apply relative font-heading text-center;
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import React from 'react'
|
||||
import { Banner } from 'src/components/common'
|
||||
import s from './HomeBanner.module.scss'
|
||||
import BannerImgRight from './assets/banner_full.png'
|
||||
import BannerImgRight2 from './assets/banner_product.png'
|
||||
import HomeBannerImg from './assets/home_banner.png'
|
||||
import s from './HomeBanner.module.scss'
|
||||
import Image from 'next/image'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -13,6 +14,9 @@ const HomeBanner = ({ }: Props) => {
|
||||
return (
|
||||
<div className={s.homeBanner}>
|
||||
<section className={s.left}>
|
||||
<div className={s.imgWrap}>
|
||||
<Image src={HomeBannerImg} placeholder='blur' />
|
||||
</div>
|
||||
<div className={s.text}>
|
||||
Freshness<br />guaranteed
|
||||
</div>
|
||||
@@ -28,10 +32,10 @@ const HomeBanner = ({ }: Props) => {
|
||||
{
|
||||
title: "Save 15% on your first order 2",
|
||||
subtitle: "Last call! Shop deep deals on 100+ bulk picks while you can.",
|
||||
imgLink: BannerImgRight2.src,
|
||||
imgLink: BannerImgRight.src,
|
||||
size: "small",
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div >
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 212 KiB |
@@ -6,7 +6,7 @@
|
||||
--primary-lightest: #effaf4;
|
||||
|
||||
--info-dark: #00317a;
|
||||
--info: #297fff;
|
||||
--info: #3468B7;
|
||||
--info-border-line: #d6e7ff;
|
||||
--info-light: #ebf3ff;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
--disabled: #cccccc;
|
||||
--border-line: #ebebeb;
|
||||
--background: #fff;
|
||||
--background-gray: #F5F4F2;
|
||||
--gray: #f8f8f8;
|
||||
--white: #fff;
|
||||
--background-arrow:rgba(20, 20, 20, 0.05);
|
||||
|
@@ -120,6 +120,25 @@
|
||||
font-family: var(--font-logo);
|
||||
}
|
||||
|
||||
.custom-scroll {
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: var(--background-gray);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
border-radius: 10px;
|
||||
width: 6px;
|
||||
background-color: var(--background-gray);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
background-color: var(--primary)
|
||||
}
|
||||
}
|
||||
|
||||
.u-form {
|
||||
.body {
|
||||
> div {
|
||||
@@ -130,6 +149,7 @@
|
||||
.line {
|
||||
@apply flex justify-between items-center;
|
||||
> div {
|
||||
flex: 1;
|
||||
&:not(:last-child) {
|
||||
margin-right: 1.6rem;
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ export const SOCIAL_LINKS = {
|
||||
export const ROUTE = {
|
||||
HOME: '/',
|
||||
PRODUCTS: '/products',
|
||||
PRODUCT_DETAIL: '/product',
|
||||
ABOUT: '/about',
|
||||
BLOG_DETAIL: '/blog',
|
||||
ACCOUNT: '/account',
|
||||
|
@@ -3,6 +3,7 @@ import { RecipeCardProps } from "src/components/common/RecipeCard/RecipeCard"
|
||||
export const PRODUCT_DATA_TEST = [
|
||||
{
|
||||
name: 'Tomato',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -10,6 +11,7 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -17,6 +19,7 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Carrot',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -24,6 +27,7 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Salad',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -31,6 +35,7 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -38,6 +43,7 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -45,6 +51,7 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -52,6 +59,7 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -59,9 +67,55 @@ export const PRODUCT_DATA_TEST = [
|
||||
},
|
||||
]
|
||||
|
||||
export const PRODUCT_CART_DATA_TEST = [
|
||||
{
|
||||
name: 'Tomato',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: "https://user-images.githubusercontent.com/76729908/131646211-d56b77ac-83f1-4dd2-b55c-e3f1e0ba4e49.png",
|
||||
oldPrice: 'Rp 32.000',
|
||||
discount: '15%',
|
||||
quantity: 1,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: "https://user-images.githubusercontent.com/76729908/131646211-d56b77ac-83f1-4dd2-b55c-e3f1e0ba4e49.png",
|
||||
oldPrice: 'Rp 32.000',
|
||||
discount: '15%',
|
||||
quantity: 2,
|
||||
},
|
||||
{
|
||||
name: 'Carrot',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: "https://user-images.githubusercontent.com/76729908/131646217-23b86160-45c9-4845-8dcc-b3e1a4483edd.png",
|
||||
oldPrice: 'Rp 32.000',
|
||||
discount: '20%',
|
||||
quantity: 3,
|
||||
},
|
||||
{
|
||||
name: 'Salad',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: "https://user-images.githubusercontent.com/76729908/131646221-aaa1d48d-bb80-470f-9400-ae2aa47285b6.png",
|
||||
quantity: 1,
|
||||
},
|
||||
]
|
||||
|
||||
export const INGREDIENT_DATA_TEST = [
|
||||
{
|
||||
name: 'Tomato',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -69,6 +123,7 @@ export const INGREDIENT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -77,6 +132,7 @@ export const INGREDIENT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Carrot',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -84,6 +140,7 @@ export const INGREDIENT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Salad',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -92,6 +149,7 @@ export const INGREDIENT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -99,6 +157,7 @@ export const INGREDIENT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -106,6 +165,7 @@ export const INGREDIENT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
@@ -113,6 +173,7 @@ export const INGREDIENT_DATA_TEST = [
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
slug: 'tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
|
@@ -1,8 +1,11 @@
|
||||
export interface ProductProps {
|
||||
category: string
|
||||
category?: string
|
||||
name: string
|
||||
slug: string
|
||||
weight: string
|
||||
price: string
|
||||
oldPrice?: string
|
||||
discount?: string
|
||||
imageSrc: string
|
||||
isNotSell?: boolean
|
||||
}
|
||||
@@ -17,7 +20,7 @@ export interface FeaturedProductProps {
|
||||
|
||||
export interface RecipeProps {
|
||||
title: string
|
||||
description:string
|
||||
description: string
|
||||
imageSrc: string
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user