mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
Merge branch 'common' of github.com:KieIO/grocery-vercel-commerce into m4-datnguyen
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
@import '../../../styles/utilities';
|
||||
|
||||
.breadcrumbCommon {
|
||||
color: var(--text-base);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
.currentItem {
|
||||
cursor: default;
|
||||
}
|
||||
>>>>>>> a9f9f06eb9dee2a1ddefe907ff804237a78c5210
|
||||
}
|
37
src/components/common/BreadcrumbCommon/BreadcrumbCommon.tsx
Normal file
37
src/components/common/BreadcrumbCommon/BreadcrumbCommon.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
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'
|
||||
|
||||
interface BreadcrumbCommonProps {
|
||||
crumbs: { link: string, name: string }[];
|
||||
showHomePage?: boolean;
|
||||
}
|
||||
|
||||
const BreadcrumbCommon = ({ crumbs, showHomePage = true }: BreadcrumbCommonProps) => {
|
||||
return (
|
||||
<section className={s.breadcrumbCommon}>
|
||||
{
|
||||
|
||||
showHomePage && <BreadcrumbItem key='Home' text='Home' href={ROUTE.HOME} />
|
||||
}
|
||||
{
|
||||
crumbs.length > 0 && <>
|
||||
|
||||
{
|
||||
crumbs.slice(0, crumbs.length - 1).map((crumb) => (
|
||||
< BreadcrumbSeparator key={crumb.name}>
|
||||
<BreadcrumbItem text={crumb.name} href={crumb.link} />
|
||||
</BreadcrumbSeparator>
|
||||
))}
|
||||
< BreadcrumbSeparator>
|
||||
<span className={s.currentItem}>{crumbs[crumbs.length - 1].name}</span>
|
||||
</BreadcrumbSeparator>
|
||||
</>
|
||||
}
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default BreadcrumbCommon
|
@@ -0,0 +1,5 @@
|
||||
.breadcrumbItem {
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import s from './BreadcrumbItem.module.scss'
|
||||
|
||||
interface BreadcrumbItemProps {
|
||||
text: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const BreadcrumbItem = ({ text, href }: BreadcrumbItemProps) => {
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a className={s.breadcrumbItem}>{text}</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default BreadcrumbItem
|
@@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
|
||||
interface BreadcrumbSeparatorProps {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const BreadcrumbSeparator = ({ children }: BreadcrumbSeparatorProps) => {
|
||||
return (
|
||||
<span>
|
||||
/ {children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default BreadcrumbSeparator
|
@@ -7,6 +7,9 @@
|
||||
align-items: center;
|
||||
padding: 1rem 2rem;
|
||||
@screen md {
|
||||
padding: 0.8rem 1.6rem;
|
||||
}
|
||||
@screen lg {
|
||||
padding: 0.8rem 3.2rem;
|
||||
}
|
||||
&:disabled {
|
||||
@@ -84,11 +87,14 @@
|
||||
padding: 1rem;
|
||||
}
|
||||
@screen md {
|
||||
padding: 1.6rem 4.8rem;
|
||||
padding: 1.6rem 3.2rem;
|
||||
&.onlyIcon {
|
||||
padding: 1.6rem;
|
||||
}
|
||||
}
|
||||
@screen lg {
|
||||
padding: 1.6rem 4.8rem;
|
||||
}
|
||||
&.loading {
|
||||
&::before {
|
||||
width: 2.4rem;
|
||||
|
34
src/components/common/CardBlog/CardBlog.module.scss
Normal file
34
src/components/common/CardBlog/CardBlog.module.scss
Normal file
@@ -0,0 +1,34 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.cardBlogWarpper {
|
||||
@apply inline-flex flex-col justify-start;
|
||||
max-width: 39.2rem;
|
||||
min-height: 34.4rem;
|
||||
.image {
|
||||
width: 100%;
|
||||
max-height: 22rem;
|
||||
border-radius: 2.4rem;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
padding: 1.6rem 0.8rem 0.4rem 0.8rem;
|
||||
@apply font-bold;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--text-active);
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.description {
|
||||
padding: 0 0.8rem;
|
||||
@apply overflow-hidden overflow-ellipsis ;
|
||||
color: var(--text-label);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
31
src/components/common/CardBlog/CardBlog.tsx
Normal file
31
src/components/common/CardBlog/CardBlog.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import { BlogProps } from 'src/utils/types.utils'
|
||||
import s from './CardBlog.module.scss'
|
||||
export interface BlogCardProps extends BlogProps {
|
||||
// todo: edit when intergrate API
|
||||
|
||||
}
|
||||
|
||||
const CardBlog = ({ imageSrc, title, description, slug }: BlogCardProps) => {
|
||||
return (
|
||||
<div className={s.cardBlogWarpper}>
|
||||
<Link href={`${ROUTE.BLOG_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.image}>
|
||||
<img src={imageSrc} alt="image cardblog" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href={`${ROUTE.BLOG_DETAIL}/${slug}`}>
|
||||
<a>
|
||||
<div className={s.title}>{title}</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className={s.description}>{description}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CardBlog
|
@@ -8,6 +8,7 @@
|
||||
:global(.customArrow) {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: .8rem;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
@@ -28,7 +28,6 @@ const CarouselCommon = <T,>({
|
||||
option: { slideChanged,slidesPerView, ...sliderOption },
|
||||
}: CarouselCommonProps<T>) => {
|
||||
const [currentSlide, setCurrentSlide] = React.useState(0)
|
||||
// const [dotActive, setDotActive] = React.useState<number>(0)
|
||||
const [dotArr, setDotArr] = React.useState<number[]>([])
|
||||
const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({
|
||||
...sliderOption,
|
||||
|
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;
|
@@ -0,0 +1,66 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.collapseWrapper {
|
||||
@apply border-t border-b;
|
||||
border-color: var(--border-line);
|
||||
max-width: 80.4rem;
|
||||
min-height: 4rem;
|
||||
&.isActive {
|
||||
.title {
|
||||
@apply pb-0;
|
||||
}
|
||||
.contentContainer {
|
||||
@apply block;
|
||||
animation: ContentAnimationIn 0.5s ease-out;
|
||||
}
|
||||
.toggle {
|
||||
&:before {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
&:after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
@apply outline-none flex justify-between font-heading items-center pt-16 pb-16;
|
||||
font-size: 3.2rem;
|
||||
line-height: 4rem;
|
||||
letter-spacing: -0.01em;
|
||||
.toggle {
|
||||
height: 2.2rem;
|
||||
width: 2.2rem;
|
||||
position: relative;
|
||||
&:before,
|
||||
&:after {
|
||||
@apply absolute h-2;
|
||||
content: "";
|
||||
border-radius: 0.8rem;
|
||||
background: var(--text-active);
|
||||
top: 40%;
|
||||
width: 2.2rem;
|
||||
transition: transform 500ms ease;
|
||||
}
|
||||
&:before {
|
||||
transform-origin: center;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
.contentContainer {
|
||||
@apply hidden pb-16;
|
||||
}
|
||||
@keyframes ContentAnimationIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-1.6rem);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
import s from './CollapseChild.module.scss'
|
||||
import { useState } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import CollapseContent from './CollapseContent/CollapseContent'
|
||||
|
||||
interface CollapseProps{
|
||||
title?: string,
|
||||
content: Array<string>,
|
||||
isToggle?: boolean,
|
||||
}
|
||||
const CollapseChild = ({title, content, isToggle=false}: CollapseProps) => {
|
||||
const [isActive, changeActive] = useState(isToggle)
|
||||
|
||||
const handleToggle = () => {
|
||||
changeActive(!isActive)
|
||||
}
|
||||
return(
|
||||
<div className={classNames({
|
||||
[s.collapseWrapper] : true,
|
||||
[s.isActive] : isActive
|
||||
})}
|
||||
onClick = { handleToggle }
|
||||
>
|
||||
<div className={s.title}>
|
||||
<h4>{title}</h4>
|
||||
<div className={s.toggle}></div>
|
||||
</div>
|
||||
<div className={s.contentContainer}>
|
||||
{
|
||||
content.map(item => <CollapseContent key={item} content={item} />)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CollapseChild
|
@@ -0,0 +1,3 @@
|
||||
.content {
|
||||
margin-top: 1.6rem;
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
import s from './CollapseContent.module.scss'
|
||||
|
||||
interface CollapseContentProps{
|
||||
content: string
|
||||
}
|
||||
|
||||
const CollapseContent = ({content}: CollapseContentProps) => {
|
||||
return (
|
||||
<div className={s.content}>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CollapseContent
|
19
src/components/common/CollapseCommon/CollapseCommon.tsx
Normal file
19
src/components/common/CollapseCommon/CollapseCommon.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import CollapseChild from './CollapseChild/CollapseChild'
|
||||
|
||||
interface CollapseCommonProps{
|
||||
data: {title: string, content: Array<string>}[],
|
||||
}
|
||||
|
||||
const CollapseCommon = ({data}: CollapseCommonProps) => {
|
||||
return (
|
||||
<section>
|
||||
{
|
||||
data.map(item =>
|
||||
<CollapseChild key={item.title} title={item.title} content={item.content}/>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default CollapseCommon
|
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;
|
@@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { memo, useEffect, useState } from 'react'
|
||||
import { useModalCommon } from 'src/components/hooks/useModalCommon'
|
||||
import { useModalCommon } from 'src/components/hooks'
|
||||
import { isMobile } from 'src/utils/funtion.utils'
|
||||
import ModalAuthenticate from '../ModalAuthenticate/ModalAuthenticate'
|
||||
import ModalCreateUserInfo from '../ModalCreateUserInfo/ModalCreateUserInfo'
|
||||
|
@@ -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>
|
||||
|
@@ -0,0 +1,9 @@
|
||||
.imgWithLink {
|
||||
position: relative;
|
||||
min-width: 5rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
18
src/components/common/ImgWithLink/ImgWithLink.tsx
Normal file
18
src/components/common/ImgWithLink/ImgWithLink.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
import s from './ImgWithLink.module.scss'
|
||||
import Image from 'next/image'
|
||||
|
||||
export interface ImgWithLinkProps {
|
||||
src: string,
|
||||
alt?: string,
|
||||
}
|
||||
|
||||
const ImgWithLink = ({ src, alt }: ImgWithLinkProps) => {
|
||||
return (
|
||||
<div className={s.imgWithLink}>
|
||||
<Image src={src} alt={alt} layout="fill" className={s.imgWithLink} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ImgWithLink
|
@@ -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,12 +15,24 @@ 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 />
|
||||
<main >{children}</main>
|
||||
<button onClick={toggle}>toggle card: {visibleCartDrawer.toString()}</button>
|
||||
<CartDrawer
|
||||
visible={visibleCartDrawer}
|
||||
onClose={closeCartDrawer} />
|
||||
<Footer />
|
||||
</div>
|
||||
</CommerceProvider>
|
||||
|
@@ -0,0 +1,23 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.infoProducts {
|
||||
@apply flex justify-between items-center spacing-horizontal;
|
||||
|
||||
.top {
|
||||
.sub {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@screen lg {
|
||||
@apply block;
|
||||
margin-right: 4rem;
|
||||
padding: 0;
|
||||
.top {
|
||||
margin-bottom: 3.2rem;
|
||||
.sub {
|
||||
display: block;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
import HeadingCommon from '../../HeadingCommon/HeadingCommon';
|
||||
import ViewAllItem from '../../ViewAllItem/ViewAllItem';
|
||||
import s from './InfoProducts.module.scss'
|
||||
interface Props {
|
||||
title: string,
|
||||
subtitle?: string,
|
||||
}
|
||||
|
||||
const InfoProducts = ({ title, subtitle }: Props) => {
|
||||
return (
|
||||
<div className={s.infoProducts}>
|
||||
<div className={s.top}>
|
||||
<HeadingCommon>{title}</HeadingCommon>
|
||||
<div className={s.sub}>
|
||||
{subtitle}
|
||||
</div>
|
||||
</div>
|
||||
<ViewAllItem link={ROUTE.PRODUCTS} />
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoProducts;
|
@@ -0,0 +1,48 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.listProductWithInfo {
|
||||
background-color: var(--background);
|
||||
border-top: 1rem solid var(--gray);
|
||||
border-bottom: 1rem solid var(--gray);
|
||||
padding-top: 6rem;
|
||||
padding-bottom: 6rem;
|
||||
@screen lg {
|
||||
@apply flex spacing-horizontal-left;
|
||||
padding-top: 5.6rem;
|
||||
padding-bottom: 5.6rem;
|
||||
border: none;
|
||||
background-color: var(--background-gray);
|
||||
}
|
||||
.productsWrap {
|
||||
@apply spacing-horizontal-left;
|
||||
@screen lg {
|
||||
max-width: 75%;
|
||||
@apply custom-border-radius-lg bg-white;
|
||||
padding: 4rem .8rem;
|
||||
:global(.customArrow) {
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem + 3rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem + 3rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@screen xl {
|
||||
padding: 4rem 2.4rem;
|
||||
max-width: 80%;
|
||||
:global(.customArrow) {
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem + 1rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem + 1rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
import { TOptionsEvents } from 'keen-slider';
|
||||
import React from 'react';
|
||||
import CarouselCommon from '../CarouselCommon/CarouselCommon';
|
||||
import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard';
|
||||
import InfoProducts from './InfoProducts/InfoProducts';
|
||||
import s from './ListProductWithInfo.module.scss';
|
||||
|
||||
interface Props {
|
||||
data: ProductCardProps[],
|
||||
title: string,
|
||||
subtitle?: string,
|
||||
}
|
||||
const OPTION_DEFAULT: TOptionsEvents = {
|
||||
slidesPerView: 2,
|
||||
mode: 'free',
|
||||
breakpoints: {
|
||||
'(min-width: 640px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
'(min-width: 768px)': {
|
||||
slidesPerView: 4,
|
||||
},
|
||||
'(min-width: 1024px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
'(min-width: 1280px)': {
|
||||
slidesPerView: 4.5,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const ListProductWithInfo = ({ data, title, subtitle }: Props) => {
|
||||
return (
|
||||
<div className={s.listProductWithInfo}>
|
||||
<InfoProducts
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
/>
|
||||
<div className={s.productsWrap}>
|
||||
<CarouselCommon<ProductCardProps>
|
||||
data={data}
|
||||
Component={ProductCard}
|
||||
itemKey={title}
|
||||
option={OPTION_DEFAULT}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListProductWithInfo;
|
@@ -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',
|
||||
|
40
src/components/common/MenuFilter/MenuFilter.module.scss
Normal file
40
src/components/common/MenuFilter/MenuFilter.module.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
@import "../../../styles/utilities";
|
||||
.menuFilterWrapper{
|
||||
|
||||
@screen md {
|
||||
@apply hidden;
|
||||
}
|
||||
.menuFilterHeading{
|
||||
@apply sub-headline font-bold ;
|
||||
color: var(--text-active);
|
||||
font-feature-settings: 'salt' on;
|
||||
margin: 0.8rem 0;
|
||||
}
|
||||
.menuFilterList{
|
||||
@apply flex flex-wrap justify-start relative;
|
||||
margin-bottom: 3rem;
|
||||
box-sizing: border-box;
|
||||
&::after{
|
||||
@apply absolute;
|
||||
top: 110%;
|
||||
content: "";
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--border-line);
|
||||
}
|
||||
|
||||
li{
|
||||
margin: 1rem 0;
|
||||
padding:0;
|
||||
div{
|
||||
padding: 0.8rem 1.6rem;
|
||||
margin-right: 0.8rem;
|
||||
background-color: var(--gray);
|
||||
border-radius: 0.8rem;
|
||||
&.active {
|
||||
color:white;
|
||||
background-color: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
src/components/common/MenuFilter/MenuFilter.tsx
Normal file
48
src/components/common/MenuFilter/MenuFilter.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import classNames from 'classnames'
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import s from './MenuFilter.module.scss'
|
||||
interface Props {
|
||||
children?: any,
|
||||
heading?:string,
|
||||
categories:{name:string,link:string}[],
|
||||
type:string,
|
||||
onChangeValue?: (value: Object) => void
|
||||
}
|
||||
|
||||
const MenuFilter = ({heading,categories,type,onChangeValue}:Props)=> {
|
||||
const [active, setActive] = useState<string>('');
|
||||
|
||||
function handleClick(link:string){
|
||||
setActive(link);
|
||||
|
||||
if(active === link){
|
||||
setActive('');
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
|
||||
let href = active?.split("=");
|
||||
const linkValue = href[1];
|
||||
|
||||
onChangeValue && onChangeValue({[type]:linkValue});
|
||||
},[active])
|
||||
|
||||
return (
|
||||
<section className={s.menuFilterWrapper}>
|
||||
<h2 className={s.menuFilterHeading}>{heading}</h2>
|
||||
<ul className={s.menuFilterList}>
|
||||
{
|
||||
categories.map(item => <li key={item.name}>
|
||||
<div onClick={()=> handleClick(item.link)} className={classNames({ [s.active]: item.link === active? true: false })}>
|
||||
{item.name}
|
||||
</div>
|
||||
</li>)
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default MenuFilter
|
@@ -0,0 +1,29 @@
|
||||
@import "../../../styles/utilities";
|
||||
.menuNavigationWrapper{
|
||||
.menuNavigationHeading{
|
||||
@screen md {
|
||||
@apply sub-headline font-bold ;
|
||||
color: var(--text-active);
|
||||
font-feature-settings: 'salt' on;
|
||||
margin: 1.6rem 0;
|
||||
}
|
||||
}
|
||||
.menuNavigationList{
|
||||
@screen md {
|
||||
li{
|
||||
margin: 0.8rem 0;
|
||||
a{
|
||||
display:block;
|
||||
width:100%;
|
||||
color:var(--text-base);
|
||||
&:hover {
|
||||
@apply text-primary;
|
||||
}
|
||||
&.active {
|
||||
@apply text-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
src/components/common/MenuNavigation/MenuNavigation.tsx
Normal file
34
src/components/common/MenuNavigation/MenuNavigation.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import classNames from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import s from './MenuNavigation.module.scss'
|
||||
|
||||
interface Props {
|
||||
children?: any,
|
||||
heading:string,
|
||||
categories:{name:string,link:string}[]
|
||||
}
|
||||
|
||||
const MenuNavigation = ({heading,categories}:Props)=> {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<section className={s.menuNavigationWrapper}>
|
||||
<h2 className={s.menuNavigationHeading}>{heading}({categories.length})</h2>
|
||||
<ul className={s.menuNavigationList}>
|
||||
{
|
||||
categories.map(item => <li key={item.name}
|
||||
>
|
||||
<Link href={item.link}>
|
||||
<a className={classNames({ [s.active]: router.asPath === item.link})}>
|
||||
{item.name}
|
||||
</a>
|
||||
</Link>
|
||||
</li>)
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default MenuNavigation
|
@@ -0,0 +1,45 @@
|
||||
@import "../../../styles/utilities";
|
||||
.menuNavigationProductListDesktop{
|
||||
@screen sm-only {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
.menuNavigationProductListMobile{
|
||||
@apply hidden;
|
||||
&.isShow{
|
||||
@apply block;
|
||||
@screen md {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
.menuNavigationProductModal{
|
||||
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: 2rem ;
|
||||
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,60 @@
|
||||
import React, { useState } from 'react';
|
||||
import {ButtonCommon} from 'src/components/common';
|
||||
import s from './MenuNavigationProductList.module.scss';
|
||||
import MenuSort from './MenuSort/MenuSort';
|
||||
import {LANGUAGE} from 'src/utils/language.utils';
|
||||
import classNames from 'classnames'
|
||||
import MenuFilter from '../MenuFilter/MenuFilter';
|
||||
import MenuNavigation from '../MenuNavigation/MenuNavigation';
|
||||
import IconHide from 'src/components/icons/IconHide';
|
||||
|
||||
interface Props{
|
||||
categories:{name:string,link:string}[],
|
||||
brands:{name:string,link:string}[],
|
||||
featured:{name:string,link:string}[],
|
||||
}
|
||||
|
||||
const MenuNavigationProductList = ({categories,brands,featured}:Props)=>{
|
||||
|
||||
const [dataSort,setDataSort] = useState({});
|
||||
const [isShow,setIsShow] = useState(true);
|
||||
|
||||
function handleValue(value:Object){
|
||||
setDataSort({...dataSort,...value});
|
||||
}
|
||||
function filter(){
|
||||
console.log(dataSort)
|
||||
}
|
||||
|
||||
function hideMenu(){
|
||||
if(isShow === true){
|
||||
setIsShow(false);
|
||||
}
|
||||
}
|
||||
return(
|
||||
<>
|
||||
<div className={s.menuNavigationProductListDesktop}>
|
||||
<MenuNavigation categories={categories} heading="Categories"/>
|
||||
<MenuNavigation categories={brands} heading="Brands"/>
|
||||
<MenuNavigation categories={featured} heading="Featured"/>
|
||||
</div>
|
||||
<div className={classNames({ [s.menuNavigationProductListMobile] :true,[s.isShow]: isShow})}>
|
||||
<div className={s.menuNavigationProductModal}>
|
||||
<div className={s.content}>
|
||||
<div className={s.head}>
|
||||
<h3>FILTER</h3>
|
||||
<div onClick={hideMenu}><IconHide/></div>
|
||||
</div>
|
||||
<MenuFilter categories={categories} heading="Categories" type="category" onChangeValue={handleValue}/>
|
||||
<MenuFilter categories={brands} heading="Brand" type="brand" onChangeValue={handleValue}/>
|
||||
<MenuFilter categories={featured} heading="Featured" type="featured" onChangeValue={handleValue}/>
|
||||
<MenuSort heading="SORT BY" type="sort" onChangeValue={handleValue}/>
|
||||
<ButtonCommon size="large" onClick={filter}>{LANGUAGE.BUTTON_LABEL.CONFIRM}</ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MenuNavigationProductList
|
@@ -0,0 +1,46 @@
|
||||
@import "../../../../styles/utilities";
|
||||
.menuSortWrapper{
|
||||
|
||||
@screen md {
|
||||
@apply hidden;
|
||||
}
|
||||
.menuSortHeading{
|
||||
@apply sub-headline font-bold ;
|
||||
color: var(--text-active);
|
||||
font-feature-settings: 'salt' on;
|
||||
margin: 0.8rem 0;
|
||||
}
|
||||
.menuSortList{
|
||||
box-sizing: border-box;
|
||||
&::after{
|
||||
@apply absolute;
|
||||
top: 110%;
|
||||
content: "";
|
||||
width: 100%;
|
||||
border-bottom: 1px solid var(--border-line);
|
||||
}
|
||||
li{
|
||||
div{
|
||||
height: 4.8rem;
|
||||
line-height: 4.8rem;
|
||||
padding: 0 1.6rem;
|
||||
margin-right: 0.8rem;
|
||||
border-radius: 0.8rem;
|
||||
&.active {
|
||||
@apply font-bold relative;
|
||||
color:var(--text-active);
|
||||
background-color: var(--primary-lightest);
|
||||
&::after{
|
||||
@apply absolute;
|
||||
content:"";
|
||||
background-image: url('/assets/svg/check.svg');
|
||||
right: 1.6rem;
|
||||
top: calc(50% - 24px/2);
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { QUERY_KEY, ROUTE } from 'src/utils/constanst.utils';
|
||||
import s from './MenuSort.module.scss';
|
||||
|
||||
interface Props {
|
||||
children?: any,
|
||||
heading:string,
|
||||
type:string,
|
||||
onChangeValue?: (value: Object) => void
|
||||
}
|
||||
const SORT = [
|
||||
{
|
||||
name: 'By Name',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=by-name`,
|
||||
},
|
||||
{
|
||||
name: 'Price(High to Low)',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=high-to-low`,
|
||||
},
|
||||
{
|
||||
name: 'Price (Low to High)',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=low-to-high`,
|
||||
},
|
||||
{
|
||||
name: 'On Sale',
|
||||
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.SORTBY}=on-sale`,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
const MenuSort = ({heading,type,onChangeValue}:Props)=> {
|
||||
const [active, setActive] = useState<string>('');
|
||||
|
||||
function handleClick(link:string){
|
||||
setActive(link);
|
||||
|
||||
if(active === link){
|
||||
setActive('');
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
|
||||
let href = active?.split("=");
|
||||
const linkValue = href[1];
|
||||
|
||||
onChangeValue && onChangeValue({[type]:linkValue});
|
||||
},[active])
|
||||
|
||||
return (
|
||||
<section className={classNames(s.menuSortWrapper)}>
|
||||
<h2 className={classNames(s.menuSortHeading)}>{heading}</h2>
|
||||
<ul className={s.menuSortList}>
|
||||
{
|
||||
SORT.map(item => <li key={item.name}>
|
||||
<div onClick={()=> handleClick(item.link)} className={classNames({ [s.active]: item.link === active? true: false })}>
|
||||
{item.name}
|
||||
</div>
|
||||
</li>)
|
||||
}
|
||||
</ul>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default MenuSort
|
@@ -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,18 +1,19 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { ArrowLeftSmall, ArrowRightSmall } from 'src/components/icons'
|
||||
import { DEFAULT_PAGE_SIZE } from 'src/utils/constanst.utils'
|
||||
import PaginationItem from './components/PaginationItem'
|
||||
import s from './PaginationCommon.module.scss'
|
||||
interface PaginationCommonProps {
|
||||
defaultCurrent?: number
|
||||
pageSize: number
|
||||
pageSize?: number
|
||||
total: number
|
||||
onChange?: (page: number, pageSize: number) => void
|
||||
}
|
||||
|
||||
const PaginationCommon = ({
|
||||
total,
|
||||
pageSize,
|
||||
pageSize=DEFAULT_PAGE_SIZE,
|
||||
defaultCurrent,
|
||||
onChange,
|
||||
}: PaginationCommonProps) => {
|
||||
|
@@ -2,8 +2,12 @@
|
||||
max-width: 20.8rem;
|
||||
min-height: 31.8rem;
|
||||
padding: 1.2rem 1.2rem 0 1.2rem;
|
||||
margin: auto;
|
||||
margin-bottom: 1px;
|
||||
@apply flex flex-col justify-between;
|
||||
&.notSell {
|
||||
@apply justify-center;
|
||||
}
|
||||
.cardTop{
|
||||
@apply relative;
|
||||
height: 13.8rem;
|
||||
@@ -29,8 +33,6 @@
|
||||
.cardMidTop{
|
||||
.productname{
|
||||
font-weight: bold;
|
||||
line-height: 2.4rem;
|
||||
font-size: 1.6rem;
|
||||
color: var(--text-active);
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
|
@@ -6,6 +6,7 @@ import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
|
||||
import ItemWishList from '../ItemWishList/ItemWishList'
|
||||
import LabelCommon from '../LabelCommon/LabelCommon'
|
||||
import s from './ProductCard.module.scss'
|
||||
import ProductNotSell from './ProductNotSell/ProductNotSell'
|
||||
|
||||
export interface ProductCardProps extends ProductProps {
|
||||
buttonText?: string
|
||||
@@ -18,7 +19,15 @@ const ProductCard = ({
|
||||
price,
|
||||
buttonText = 'Buy Now',
|
||||
imageSrc,
|
||||
isNotSell,
|
||||
}: ProductCardProps) => {
|
||||
if (isNotSell) {
|
||||
return <div className={`${s.productCardWarpper} ${s.notSell}`}>
|
||||
<ProductNotSell name={name} imageSrc={imageSrc} />
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={s.productCardWarpper}>
|
||||
<div className={s.cardTop}>
|
||||
|
@@ -0,0 +1,27 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.imgWrap {
|
||||
img {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
@apply text-label cursor-default font-bold;
|
||||
}
|
||||
|
||||
.info {
|
||||
@apply flex justify-center items-center custom-border-radius bg-info-light text-center;
|
||||
padding: .8rem 1.6rem;
|
||||
margin-top: 1.6rem;
|
||||
color: var(--info);
|
||||
svg {
|
||||
@apply u-icon;
|
||||
path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
margin-left: 0.8rem;
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { IconInfo } from 'src/components/icons';
|
||||
import ImgWithLink from '../../ImgWithLink/ImgWithLink';
|
||||
import s from './ProductNotSell.module.scss';
|
||||
|
||||
export interface Props {
|
||||
name: string,
|
||||
imageSrc: string,
|
||||
}
|
||||
|
||||
const ProductNotSell = ({ name, imageSrc }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<div className={s.imgWrap}>
|
||||
<ImgWithLink src={imageSrc} alt={name} />
|
||||
</div>
|
||||
<div className={s.name}>{name}</div>
|
||||
<div className={s.info}>
|
||||
<IconInfo />
|
||||
<div className={s.text}>
|
||||
Not Sell
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductNotSell;
|
@@ -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>
|
||||
)
|
||||
|
@@ -6,6 +6,9 @@
|
||||
width: 100%;
|
||||
max-height: 22rem;
|
||||
border-radius: 2.4rem;
|
||||
img {
|
||||
border-radius: 2.4rem;
|
||||
}
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
23
src/components/common/RecipeDetail/RecipeDetail.tsx
Normal file
23
src/components/common/RecipeDetail/RecipeDetail.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react'
|
||||
import { ProductCardProps } from '../ProductCard/ProductCard'
|
||||
import RecipeDetailInfo from './components/RecipeDetailInfo/RecipeDetailInfo'
|
||||
import RecipeIngredient from './components/RecipeIngredient/RecipeIngredient'
|
||||
import s from './RecipeDetail.module.scss'
|
||||
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any,
|
||||
ingredients: ProductCardProps[],
|
||||
}
|
||||
|
||||
const RecipeDetail = ({ ingredients }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeDetail}>
|
||||
<RecipeDetailInfo />
|
||||
<RecipeIngredient data={ingredients} />
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeDetail
|
@@ -0,0 +1,19 @@
|
||||
.recipeBriefInfo {
|
||||
@apply flex;
|
||||
.item {
|
||||
@apply flex;
|
||||
&:not(:last-child) {
|
||||
margin-right: 2.4rem;
|
||||
}
|
||||
svg {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
path {
|
||||
fill: var(--text-label);
|
||||
}
|
||||
}
|
||||
.content {
|
||||
margin-left: 0.8rem;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import { IconLocation, IconPeople, IconTime } from 'src/components/icons'
|
||||
import s from './RecipeBriefInfo.module.scss'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any,
|
||||
}
|
||||
|
||||
const RecipeBriefInfo = ({ }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeBriefInfo}>
|
||||
<div className={s.item}>
|
||||
<IconTime />
|
||||
<div className={s.content}>15 minutes</div>
|
||||
</div>
|
||||
<div className={s.item}>
|
||||
<IconPeople />
|
||||
<div className={s.content}>4 People</div>
|
||||
</div>
|
||||
<div className={s.item}>
|
||||
<IconLocation />
|
||||
<div className={s.content}>15 minutes</div>
|
||||
</div>
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeBriefInfo
|
@@ -0,0 +1,61 @@
|
||||
@import "../../../../../styles/utilities";
|
||||
|
||||
.recipeDetailInfo {
|
||||
@apply spacing-horizontal;
|
||||
margin: 5.6rem auto;
|
||||
@screen md {
|
||||
@apply flex;
|
||||
}
|
||||
.img {
|
||||
width: fit-content;
|
||||
margin-top: 0;
|
||||
|
||||
@screen sm-only {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
@screen lg {
|
||||
max-width: 60rem;
|
||||
}
|
||||
img {
|
||||
@apply w-full;
|
||||
object-fit: contain;
|
||||
max-height: 64rem;
|
||||
border-radius: 2.4rem;
|
||||
@screen md {
|
||||
max-height: 90rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recipeInfo {
|
||||
@screen md {
|
||||
max-width: 39rem;
|
||||
margin-left: 4.8rem;
|
||||
}
|
||||
@screen lg {
|
||||
margin-left: 11.2rem;
|
||||
}
|
||||
.top {
|
||||
margin-bottom: 4.8rem;
|
||||
.name {
|
||||
@apply heading-1 font-heading;
|
||||
margin-bottom: 1.6rem;
|
||||
}
|
||||
}
|
||||
.detail {
|
||||
.item {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 2.4rem;
|
||||
}
|
||||
.heading {
|
||||
@apply heading-3 font-heading;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
.content {
|
||||
list-style: disc;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
import React from 'react'
|
||||
import RecipeBriefInfo from '../RecipeBriefInfo/RecipeBriefInfo'
|
||||
import s from './RecipeDetailInfo.module.scss'
|
||||
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any
|
||||
}
|
||||
|
||||
const RecipeDetailInfo = ({ }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeDetailInfo}>
|
||||
<div className={s.img}>
|
||||
<img src="https://user-images.githubusercontent.com/76729908/131634880-8ae1437b-d3f8-421e-a546-d5a4f9a28e5f.png" alt="Recipe" />
|
||||
</div>
|
||||
<div className={s.recipeInfo}>
|
||||
<div className={s.top}>
|
||||
<h1 className={s.name}>
|
||||
Crispy Fried Calamari
|
||||
</h1>
|
||||
<RecipeBriefInfo />
|
||||
</div>
|
||||
<div className={s.detail}>
|
||||
<div className={s.item}>
|
||||
<h3 className={s.heading}>Ingredients</h3>
|
||||
<ul className={s.content}>
|
||||
<li>Canola oil for frying</li>
|
||||
<li>1 pound clean squid bodies cut in 1/4 inch rings and dried with a paper towel</li>
|
||||
<li>2 cups flour</li>
|
||||
<li>1/2 teaspoon kosher salt</li>
|
||||
<li>1/2 teaspoon garlic powder</li>
|
||||
<li>1/8 teaspoon coarse ground black pepper</li>
|
||||
<li>1 lemon cut into wedges</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={s.item}>
|
||||
<h3 className={s.heading}>Preparation</h3>
|
||||
<ul className={s.content}>
|
||||
<li>1In a large pot or dutch oven add three inches of oil and bring to 350 degrees.</li>
|
||||
<li>Add the flour, salt, garlic powder and pepper to a large bowl and stir to combine.</li>
|
||||
<li>Toss the squid pieces in the flour then into the hot oil.</li>
|
||||
<li>Fry the squid for 1-2 minutes. You want the color to stay pale like in the pictures.</li>
|
||||
<li>Remove to a cookie sheet to drain (do not add paper towels as it will steam the calamari and make it soft.)</li>
|
||||
<li>Serve with lemon wedges.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className={s.item}>
|
||||
<h3 className={s.heading}>Link</h3>
|
||||
<a href="https://dinnerthendessert.com/crispy-fried-calamari" target="_blank" rel="noopener noreferrer">https://dinnerthendessert.com/crispy-fried-calamari</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeDetailInfo
|
@@ -0,0 +1,21 @@
|
||||
@import "../../../../../styles/utilities";
|
||||
|
||||
.recipeIngredient {
|
||||
padding: 6rem 0;
|
||||
@screen md {
|
||||
padding: 5.6rem 0;
|
||||
}
|
||||
.top {
|
||||
@apply flex justify-between items-center spacing-horizontal;
|
||||
}
|
||||
.bottom {
|
||||
@apply flex justify-center items-center spacing-horizontal;
|
||||
margin-top: 4rem;
|
||||
button {
|
||||
width: 100%;
|
||||
@screen md {
|
||||
width: 39rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
import ButtonCommon from 'src/components/common/ButtonCommon/ButtonCommon'
|
||||
import HeadingCommon from 'src/components/common/HeadingCommon/HeadingCommon'
|
||||
import { ProductCardProps } from 'src/components/common/ProductCard/ProductCard'
|
||||
import ProductCarousel from 'src/components/common/ProductCarousel/ProductCarousel'
|
||||
import ViewAllItem from 'src/components/common/ViewAllItem/ViewAllItem'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import s from './RecipeIngredient.module.scss'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any,
|
||||
data: ProductCardProps[],
|
||||
}
|
||||
|
||||
const RecipeIngredient = ({ data }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeIngredient}>
|
||||
<div className={s.top}>
|
||||
<HeadingCommon>Ingredients</HeadingCommon>
|
||||
<div>
|
||||
<ViewAllItem link={ROUTE.PRODUCTS} />
|
||||
</div>
|
||||
</div>
|
||||
<ProductCarousel data={data} itemKey="recipe-ingredient" />
|
||||
<div className={s.bottom}>
|
||||
<ButtonCommon type='ghost' size='large'>Buy all</ButtonCommon>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeIngredient
|
@@ -0,0 +1,16 @@
|
||||
@import '../../../../styles/utilities';
|
||||
.blogCardWarpper {
|
||||
@apply spacing-horizontal;
|
||||
@screen xl {
|
||||
:global(.customArrow) {
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem - 2rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem - 2rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
import { TOptionsEvents } from 'keen-slider'
|
||||
import React from 'react'
|
||||
import CarouselCommon, {
|
||||
CarouselCommonProps,
|
||||
} from '../../CarouselCommon/CarouselCommon'
|
||||
import BlogCard, { BlogCardProps } from 'src/components/common/CardBlog/CardBlog'
|
||||
import s from "./BlogPostCarousel.module.scss"
|
||||
|
||||
interface BlogPostCarouselProps
|
||||
extends Omit<CarouselCommonProps<BlogCardProps>, 'Component'|"option"> {
|
||||
option?:TOptionsEvents
|
||||
}
|
||||
|
||||
const OPTION_DEFAULT: TOptionsEvents = {
|
||||
slidesPerView: 1.25,
|
||||
mode: 'free',
|
||||
spacing:24,
|
||||
breakpoints: {
|
||||
'(min-width: 640px)': {
|
||||
slidesPerView: 2,
|
||||
},
|
||||
'(min-width: 1024px)': {
|
||||
slidesPerView: 2.5,
|
||||
},
|
||||
'(min-width: 1440px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
'(min-width: 1536px)': {
|
||||
slidesPerView: 3.5,
|
||||
},
|
||||
},
|
||||
}
|
||||
const BlogPostCarousel = ({ option, data, ...props }: BlogPostCarouselProps) => {
|
||||
return (
|
||||
<div className={s.blogCardWarpper}>
|
||||
<CarouselCommon<BlogCardProps>
|
||||
data={data}
|
||||
Component={BlogCard}
|
||||
{...props}
|
||||
option={{ ...OPTION_DEFAULT, ...option }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BlogPostCarousel
|
@@ -0,0 +1,19 @@
|
||||
@import '../../../styles/utilities';
|
||||
|
||||
.blogPostWarpper {
|
||||
&.cream{
|
||||
background-color: #F5F4F2;
|
||||
}
|
||||
padding-top: 5.6rem;
|
||||
padding-bottom: 5.2rem;
|
||||
@apply flex flex-col;
|
||||
.top {
|
||||
@apply spacing-horizontal flex w-full justify-between;
|
||||
padding-bottom: 3.2rem;
|
||||
@screen xl {
|
||||
.right {
|
||||
margin-right: 2.476rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
import image15 from '../../../../public/assets/images/image15.png'
|
||||
import image16 from '../../../../public/assets/images/image16.png'
|
||||
import image17 from '../../../../public/assets/images/image17.png'
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
import { HeadingCommon, ViewAllItem } from 'src/components/common'
|
||||
import { BlogCardProps } from 'src/components/common/CardBlog/CardBlog'
|
||||
import BlogPostCarousel from './BlogPostCarousel/BlogPostCarousel'
|
||||
import s from './RelevantBlogPosts.module.scss'
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
|
||||
interface RelevantProps {
|
||||
data?: BlogCardProps[],
|
||||
itemKey?: string,
|
||||
title?: string,
|
||||
viewAllLink?: string,
|
||||
bgcolor?: "default" | "cream"
|
||||
}
|
||||
|
||||
const recipe:BlogCardProps[] = [
|
||||
{
|
||||
title: "Want to Lose Weight? Here are 10 DEBM Diet Guidelines for Beginners",
|
||||
slug: 'have-a-nice-lunch',
|
||||
description:"The DEBM diet stands for "+'"Delicious Happy Fun Diet"'+". This diet was popularized by Robert...",
|
||||
imageSrc: image15.src,
|
||||
},{
|
||||
title: "9 Ways to Make an Aloe Vera Mask at Home",
|
||||
slug: 'have-a-nice-lunch',
|
||||
description:"Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
|
||||
imageSrc: image16.src,
|
||||
},{
|
||||
title: "Don't Buy Wrong, Here Are 7 Ways to Choose a Ripe Dragon Fruit",
|
||||
slug: 'have-a-nice-lunch',
|
||||
description:"Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
|
||||
imageSrc: image17.src,
|
||||
},{
|
||||
title: "Want to Lose Weight? Here are 10 DEBM Diet Guidelines for Beginners",
|
||||
slug: 'have-a-nice-lunch',
|
||||
description:"The DEBM diet stands for "+'"Delicious Happy Fun Diet"'+". This diet was popularized by Robert...",
|
||||
imageSrc: image15.src,
|
||||
},{
|
||||
title: "9 Ways to Make an Aloe Vera Mask at Home",
|
||||
slug: 'have-a-nice-lunch',
|
||||
description:"Aloe vera or aloe vera is a green plant, has thorns on the side of the skin with yellowish patches and...",
|
||||
imageSrc: image16.src,
|
||||
},{
|
||||
title: "Don't Buy Wrong, Here Are 7 Ways to Choose a Ripe Dragon Fruit",
|
||||
slug: 'have-a-nice-lunch',
|
||||
description:"Dragon fruit is a type of fruit that is a favorite for many people because of its delicious and fresh...",
|
||||
imageSrc: image17.src,
|
||||
}]
|
||||
|
||||
const RelevantBlogPosts = ({ data = recipe, itemKey="detail-relevant", title="Relevant Blog Posts", bgcolor = "default" }: RelevantProps) => {
|
||||
return (
|
||||
<div className={classNames({
|
||||
[s.blogPostWarpper] : true,
|
||||
[s[bgcolor]] : bgcolor,
|
||||
})}
|
||||
>
|
||||
<div className={s.top}>
|
||||
<div className={s.left}>
|
||||
<HeadingCommon>{title}</HeadingCommon>
|
||||
</div>
|
||||
<div className={s.right}>
|
||||
<ViewAllItem link={ROUTE.BLOGS}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.bot}>
|
||||
<BlogPostCarousel data={data} itemKey={itemKey} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RelevantBlogPosts
|
@@ -1,15 +0,0 @@
|
||||
import React, { MutableRefObject } from 'react'
|
||||
|
||||
interface ScrollTargetProps {
|
||||
refScrollUp: MutableRefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const ScrollTarget = ({ refScrollUp } : ScrollTargetProps) => {
|
||||
|
||||
return (
|
||||
<div ref={refScrollUp}></div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default ScrollTarget
|
@@ -5,11 +5,10 @@ import s from './ScrollToTop.module.scss'
|
||||
import ArrowUp from '../../icons/IconArrowUp'
|
||||
|
||||
interface ScrollToTopProps {
|
||||
target: MutableRefObject<HTMLDivElement>;
|
||||
visibilityHeight?: number;
|
||||
}
|
||||
|
||||
const ScrollToTop = ({ target, visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
const ScrollToTop = ({ visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
|
||||
const [scrollPosition, setSrollPosition] = useState(0);
|
||||
const [showScrollToTop, setShowScrollToTop] = useState("hide");
|
||||
@@ -26,7 +25,7 @@ const ScrollToTop = ({ target, visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
};
|
||||
|
||||
function handleScrollUp() {
|
||||
target.current.scrollIntoView({ behavior: "smooth" });
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function addEventScroll() {
|
||||
@@ -34,7 +33,7 @@ const ScrollToTop = ({ target, visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
addEventScroll()
|
||||
addEventScroll();
|
||||
});
|
||||
|
||||
return (
|
||||
|
@@ -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>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
display: flex;
|
||||
.content {
|
||||
color: var(--primary);
|
||||
margin: 0.8rem 0.8rem 0.8rem 1.6rem;
|
||||
margin: 0.8rem 0.8rem 0.8rem 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.vector {
|
||||
|
@@ -19,7 +19,6 @@ export { default as DateTime} from './DateTime/DateTime'
|
||||
export { default as HeadingCommon } from './HeadingCommon/HeadingCommon'
|
||||
export { default as CollectionHeading } from './CollectionHeading/CollectionHeading'
|
||||
export { default as ScrollToTop } from './ScrollToTop/ScrollToTop'
|
||||
export { default as ScrollTarget } from './ScrollToTop/ScrollTarget'
|
||||
export { default as InputSearch} from './InputSearch/InputSearch'
|
||||
export { default as ButtonIconBuy} from './ButtonIconBuy/ButtonIconBuy'
|
||||
export { default as Banner} from './Banner/Banner'
|
||||
@@ -33,3 +32,10 @@ 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 CardBlog} from './CardBlog/CardBlog'
|
||||
export { default as RelevantBlogPosts} from './RelevantBlogPosts/RelevantBlogPosts'
|
||||
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'
|
||||
|
1
src/components/hooks/index.ts
Normal file
1
src/components/hooks/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as useModalCommon } from './useModalCommon'
|
@@ -4,7 +4,7 @@ interface Props {
|
||||
initialValue?: boolean,
|
||||
}
|
||||
|
||||
export const useModalCommon = ({ initialValue = false }: Props) => {
|
||||
const useModalCommon = ({ initialValue = false }: Props) => {
|
||||
const [visible, setVisible] = useState<boolean>(initialValue)
|
||||
|
||||
const openModal = (e?: any) => {
|
||||
@@ -21,3 +21,5 @@ export const useModalCommon = ({ initialValue = false }: Props) => {
|
||||
visible, openModal, closeModal
|
||||
}
|
||||
};
|
||||
|
||||
export default useModalCommon
|
@@ -2,7 +2,7 @@ const ArrowUp = () => {
|
||||
|
||||
return (
|
||||
<svg style={{margin:"auto", fontWeight: "bold"}} xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#141414" className="bi bi-chevron-up" viewBox="0 0 16 16">
|
||||
<path stroke="#141414" stroke-width="1" fillRule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/>
|
||||
<path stroke="#141414" strokeWidth="1" fillRule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
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
|
12
src/components/icons/IconHide.tsx
Normal file
12
src/components/icons/IconHide.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconHide = () => {
|
||||
return (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 6L6 18" stroke="#2F2F2F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M6 6L18 18" stroke="#2F2F2F" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconHide
|
11
src/components/icons/IconLocation.tsx
Normal file
11
src/components/icons/IconLocation.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconLocation = () => {
|
||||
return (
|
||||
<svg width="18" height="22" viewBox="0 0 18 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.9999 3.47991C13.4086 1.88861 11.2504 0.994629 8.99993 0.994629C6.74949 0.994629 4.59123 1.88861 2.99993 3.47991C1.40863 5.07121 0.514648 7.22947 0.514648 9.47991C0.514648 11.7303 1.40863 13.8886 2.99993 15.4799L8.26993 20.7599C8.36289 20.8536 8.47349 20.928 8.59535 20.9788C8.71721 21.0296 8.84792 21.0557 8.97993 21.0557C9.11194 21.0557 9.24265 21.0296 9.36451 20.9788C9.48637 20.928 9.59697 20.8536 9.68993 20.7599L14.9999 15.4299C16.5846 13.8452 17.4748 11.696 17.4748 9.45491C17.4748 7.21386 16.5846 5.06459 14.9999 3.47991ZM13.5699 13.9999L8.99993 18.5899L4.42993 13.9999C3.52707 13.0962 2.91241 11.9452 2.66362 10.6922C2.41484 9.43925 2.54312 8.14066 3.03223 6.96059C3.52135 5.78052 4.34935 4.77196 5.41156 4.06239C6.47377 3.35281 7.72251 2.97409 8.99993 2.97409C10.2773 2.97409 11.5261 3.35281 12.5883 4.06239C13.6505 4.77196 14.4785 5.78052 14.9676 6.96059C15.4567 8.14066 15.585 9.43925 15.3362 10.6922C15.0875 11.9452 14.4728 13.0962 13.5699 13.9999ZM5.99993 6.40991C5.19264 7.21968 4.73932 8.31648 4.73932 9.45991C4.73932 10.6033 5.19264 11.7001 5.99993 12.5099C6.59969 13.1107 7.36352 13.521 8.19558 13.6893C9.02764 13.8576 9.89088 13.7765 10.677 13.4561C11.4631 13.1356 12.1371 12.5902 12.6144 11.8882C13.0917 11.1861 13.3511 10.3588 13.3599 9.50991C13.3644 8.94311 13.2553 8.38117 13.0388 7.8573C12.8224 7.33343 12.5032 6.85827 12.0999 6.45991C11.7036 6.05449 11.231 5.73145 10.7094 5.50938C10.1877 5.2873 9.62727 5.17059 9.06033 5.16594C8.49339 5.16129 7.93113 5.26881 7.4059 5.4823C6.88067 5.69579 6.40285 6.01104 5.99993 6.40991ZM10.6899 11.0899C10.311 11.4747 9.81014 11.7158 9.27306 11.7722C8.73597 11.8285 8.19599 11.6966 7.74544 11.3988C7.29488 11.1011 6.96173 10.6562 6.80294 10.14C6.64415 9.62384 6.66958 9.06855 6.87489 8.56907C7.0802 8.06958 7.45263 7.65693 7.92851 7.40165C8.4044 7.14637 8.95418 7.06432 9.48387 7.16953C10.0135 7.27474 10.4902 7.56067 10.8324 7.97844C11.1746 8.39621 11.3611 8.91988 11.3599 9.45991C11.3454 10.0772 11.0864 10.6634 10.6399 11.0899H10.6899Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconLocation
|
@@ -1,4 +1,4 @@
|
||||
const ArrowRight = ({ ...props }) => {
|
||||
const IconMinus = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
@@ -15,4 +15,4 @@ const ArrowRight = ({ ...props }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowRight
|
||||
export default IconMinus
|
11
src/components/icons/IconPeople.tsx
Normal file
11
src/components/icons/IconPeople.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconPeople = () => {
|
||||
return (
|
||||
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3 9.22C11.8336 8.75813 12.2616 8.18688 12.5549 7.54502C12.8482 6.90316 13 6.20571 13 5.5C13 4.17392 12.4732 2.90215 11.5355 1.96447C10.5979 1.02678 9.32608 0.5 8 0.5C6.67392 0.5 5.40215 1.02678 4.46447 1.96447C3.52678 2.90215 3 4.17392 3 5.5C2.99999 6.20571 3.1518 6.90316 3.44513 7.54502C3.73845 8.18688 4.16642 8.75813 4.7 9.22C3.30014 9.85388 2.11247 10.8775 1.27898 12.1685C0.445495 13.4596 0.00147185 14.9633 0 16.5C0 16.7652 0.105357 17.0196 0.292893 17.2071C0.48043 17.3946 0.734784 17.5 1 17.5C1.26522 17.5 1.51957 17.3946 1.70711 17.2071C1.89464 17.0196 2 16.7652 2 16.5C2 14.9087 2.63214 13.3826 3.75736 12.2574C4.88258 11.1321 6.4087 10.5 8 10.5C9.5913 10.5 11.1174 11.1321 12.2426 12.2574C13.3679 13.3826 14 14.9087 14 16.5C14 16.7652 14.1054 17.0196 14.2929 17.2071C14.4804 17.3946 14.7348 17.5 15 17.5C15.2652 17.5 15.5196 17.3946 15.7071 17.2071C15.8946 17.0196 16 16.7652 16 16.5C15.9985 14.9633 15.5545 13.4596 14.721 12.1685C13.8875 10.8775 12.6999 9.85388 11.3 9.22ZM8 8.5C7.40666 8.5 6.82664 8.32405 6.33329 7.99441C5.83994 7.66476 5.45542 7.19623 5.22836 6.64805C5.0013 6.09987 4.94189 5.49667 5.05764 4.91473C5.1734 4.33279 5.45912 3.79824 5.87868 3.37868C6.29824 2.95912 6.83279 2.6734 7.41473 2.55764C7.99667 2.44189 8.59987 2.5013 9.14805 2.72836C9.69623 2.95542 10.1648 3.33994 10.4944 3.83329C10.8241 4.32664 11 4.90666 11 5.5C11 6.29565 10.6839 7.05871 10.1213 7.62132C9.55871 8.18393 8.79565 8.5 8 8.5ZM17.74 8.82C18.38 8.09933 18.798 7.20905 18.9438 6.25634C19.0896 5.30362 18.9569 4.32907 18.5618 3.45C18.1666 2.57093 17.5258 1.8248 16.7165 1.30142C15.9071 0.77805 14.9638 0.499742 14 0.5C13.7348 0.5 13.4804 0.605357 13.2929 0.792893C13.1054 0.98043 13 1.23478 13 1.5C13 1.76522 13.1054 2.01957 13.2929 2.20711C13.4804 2.39464 13.7348 2.5 14 2.5C14.7956 2.5 15.5587 2.81607 16.1213 3.37868C16.6839 3.94129 17 4.70435 17 5.5C16.9986 6.02524 16.8593 6.5409 16.5961 6.99542C16.3328 7.44994 15.9549 7.82738 15.5 8.09C15.3517 8.17552 15.2279 8.29766 15.1404 8.44474C15.0528 8.59182 15.0045 8.7589 15 8.93C14.9958 9.09976 15.0349 9.2678 15.1137 9.41826C15.1924 9.56872 15.3081 9.69665 15.45 9.79L15.84 10.05L15.97 10.12C17.1754 10.6917 18.1923 11.596 18.901 12.7263C19.6096 13.8566 19.9805 15.1659 19.97 16.5C19.97 16.7652 20.0754 17.0196 20.2629 17.2071C20.4504 17.3946 20.7048 17.5 20.97 17.5C21.2352 17.5 21.4896 17.3946 21.6771 17.2071C21.8646 17.0196 21.97 16.7652 21.97 16.5C21.9782 14.9654 21.5938 13.4543 20.8535 12.1101C20.1131 10.7659 19.0413 9.63331 17.74 8.82Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconPeople
|
@@ -1,4 +1,4 @@
|
||||
const ArrowLeft = ({ ...props }) => {
|
||||
const IconPlus = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
@@ -15,4 +15,4 @@ const ArrowLeft = ({ ...props }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowLeft
|
||||
export default IconPlus
|
11
src/components/icons/IconTime.tsx
Normal file
11
src/components/icons/IconTime.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconTime = () => {
|
||||
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 18ZM10 4C9.73479 4 9.48043 4.10536 9.2929 4.29289C9.10536 4.48043 9 4.73478 9 5V9.42L6.9 10.63C6.70736 10.7392 6.5564 10.9092 6.47078 11.1134C6.38517 11.3176 6.36975 11.5444 6.42695 11.7583C6.48414 11.9722 6.61072 12.1611 6.78682 12.2953C6.96292 12.4296 7.17859 12.5015 7.4 12.5C7.57518 12.5012 7.7476 12.4564 7.9 12.37L10.5 10.87L10.59 10.78L10.75 10.65C10.7891 10.6005 10.8226 10.5468 10.85 10.49C10.8826 10.4363 10.9094 10.3793 10.93 10.32C10.9572 10.2564 10.9741 10.1889 10.98 10.12L11 10V5C11 4.73478 10.8946 4.48043 10.7071 4.29289C10.5196 4.10536 10.2652 4 10 4Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconTime
|
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
const IconVectorDown = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
|
@@ -22,3 +22,10 @@ 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 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 |
@@ -1,19 +1,19 @@
|
||||
<svg width="240" height="268" viewBox="0 0 240 268" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.6">
|
||||
<rect x="170.045" y="167.357" width="32.2656" height="16.5938" rx="4" transform="rotate(-16 170.045 167.357)" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<path d="M170.168 201.23C167.732 192.736 172.644 183.875 181.138 181.439L199.117 176.284C207.611 173.848 216.471 178.76 218.907 187.254L234.907 243.052C236.125 247.299 233.669 251.73 229.422 252.948L196.063 262.513C191.816 263.731 187.386 261.275 186.168 257.028L170.168 201.23Z" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<rect x="170.045" y="167.357" width="32.2656" height="16.5938" rx="4" transform="rotate(-16 170.045 167.357)" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<path d="M170.168 201.23C167.732 192.736 172.644 183.875 181.138 181.439L199.117 176.284C207.611 173.848 216.471 178.76 218.907 187.254L234.907 243.052C236.125 247.299 233.669 251.73 229.422 252.948L196.063 262.513C191.816 263.731 187.386 261.275 186.168 257.028L170.168 201.23Z" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="177.519" y="170.009" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(-16 177.519 170.009)" fill="#CDF4DD"/>
|
||||
<rect x="185.494" y="167.723" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(-16 185.494 167.723)" fill="#CDF4DD"/>
|
||||
<rect x="193.47" y="165.436" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(-16 193.47 165.436)" fill="#CDF4DD"/>
|
||||
</g>
|
||||
<g opacity="0.6">
|
||||
<path d="M36.5973 106.436L39.9701 94.6735L64.4373 101.689L60.9881 113.718C59.4878 118.951 60.973 124.584 64.8578 128.397C69.0553 132.516 70.4233 138.726 68.3451 144.228L55.6959 177.716C54.5922 180.638 51.4315 182.224 48.4292 181.363L11.314 170.721C8.31623 169.861 6.47675 166.848 7.08188 163.789L13.7174 130.242C14.9118 124.203 19.3948 119.345 25.3188 117.671C30.7696 116.131 35.036 111.881 36.5973 106.436Z" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<rect x="42.3359" y="74.5464" width="32" height="19" rx="6" transform="rotate(16 42.3359 74.5464)" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<path d="M36.5973 106.436L39.9701 94.6735L64.4373 101.689L60.9881 113.718C59.4878 118.951 60.973 124.584 64.8578 128.397C69.0553 132.516 70.4233 138.726 68.3451 144.228L55.6959 177.716C54.5922 180.638 51.4315 182.224 48.4292 181.363L11.314 170.721C8.31623 169.861 6.47675 166.848 7.08188 163.789L13.7174 130.242C14.9118 124.203 19.3948 119.345 25.3188 117.671C30.7696 116.131 35.036 111.881 36.5973 106.436Z" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="42.3359" y="74.5464" width="32" height="19" rx="6" transform="rotate(16 42.3359 74.5464)" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="70.6162" y="92.0181" width="4" height="32" rx="2" transform="rotate(106 70.6162 92.0181)" fill="#CDF4DD"/>
|
||||
</g>
|
||||
<g opacity="0.6">
|
||||
<path d="M186.052 83.4148L179.934 94.0117L157.891 81.2851L164.148 70.448C166.869 65.7341 166.791 59.9085 163.944 55.2694C160.868 50.2567 161.043 43.9002 164.39 39.0645L184.765 9.63163C186.543 7.06354 189.994 6.28885 192.699 7.85054L226.137 27.156C228.837 28.7153 229.893 32.0836 228.566 34.9057L214.012 65.8511C211.392 71.4217 205.867 75.0504 199.714 75.2416C194.052 75.4176 188.885 78.5093 186.052 83.4148Z" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<rect x="172.769" y="112.969" width="32" height="19" rx="6" transform="rotate(-150 172.769 112.969)" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<path d="M186.052 83.4148L179.934 94.0117L157.891 81.2851L164.148 70.448C166.869 65.7341 166.791 59.9085 163.944 55.2694C160.868 50.2567 161.043 43.9002 164.39 39.0645L184.765 9.63163C186.543 7.06354 189.994 6.28885 192.699 7.85054L226.137 27.156C228.837 28.7153 229.893 32.0836 228.566 34.9057L214.012 65.8511C211.392 71.4217 205.867 75.0504 199.714 75.2416C194.052 75.4176 188.885 78.5093 186.052 83.4148Z" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="172.769" y="112.969" width="32" height="19" rx="6" transform="rotate(-150 172.769 112.969)" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="149.557" y="89.1743" width="4" height="32" rx="2" transform="rotate(-60 149.557 89.1743)" fill="#CDF4DD"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -1,21 +1,21 @@
|
||||
<svg width="237" height="252" viewBox="0 0 237 252" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.6">
|
||||
<rect x="196.607" y="142.667" width="32.2656" height="16.5938" rx="4" transform="rotate(16.8391 196.607 142.667)" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<path d="M178.342 171.193C180.901 162.736 189.833 157.955 198.291 160.514L216.192 165.932C224.649 168.492 229.431 177.424 226.871 185.881L210.055 241.439C208.776 245.668 204.31 248.059 200.081 246.779L166.866 236.726C162.637 235.446 160.246 230.98 161.526 226.751L178.342 171.193Z" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<rect x="196.607" y="142.667" width="32.2656" height="16.5938" rx="4" transform="rotate(16.8391 196.607 142.667)" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<path d="M178.342 171.193C180.901 162.736 189.833 157.955 198.291 160.514L216.192 165.932C224.649 168.492 229.431 177.424 226.871 185.881L210.055 241.439C208.776 245.668 204.31 248.059 200.081 246.779L166.866 236.726C162.637 235.446 160.246 230.98 161.526 226.751L178.342 171.193Z" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="201.447" y="148.949" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(16.8391 201.447 148.949)" fill="#CDF4DD"/>
|
||||
<rect x="209.389" y="151.352" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(16.8391 209.389 151.352)" fill="#CDF4DD"/>
|
||||
<rect x="217.33" y="153.756" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(16.8391 217.33 153.756)" fill="#CDF4DD"/>
|
||||
</g>
|
||||
<g opacity="0.6">
|
||||
<rect x="150.723" y="109.167" width="32.2656" height="16.5938" rx="4" transform="rotate(-150 150.723 109.167)" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<path d="M175.004 85.5494C170.586 93.202 160.8 95.824 153.147 91.4058L136.95 82.0542C129.297 77.6359 126.675 67.8505 131.094 60.1978L160.117 9.92772C162.326 6.10138 167.219 4.79038 171.045 6.99952L201.099 24.3511C204.925 26.5602 206.236 31.4529 204.027 35.2793L175.004 85.5494Z" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<rect x="150.723" y="109.167" width="32.2656" height="16.5938" rx="4" transform="rotate(-150 150.723 109.167)" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<path d="M175.004 85.5494C170.586 93.202 160.8 95.824 153.147 91.4058L136.95 82.0542C129.297 77.6359 126.675 67.8505 131.094 60.1978L160.117 9.92772C162.326 6.10138 167.219 4.79038 171.045 6.99952L201.099 24.3511C204.925 26.5602 206.236 31.4529 204.027 35.2793L175.004 85.5494Z" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="147.439" y="101.949" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(-150 147.439 101.949)" fill="#CDF4DD"/>
|
||||
<rect x="140.254" y="97.8003" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(-150 140.254 97.8003)" fill="#CDF4DD"/>
|
||||
<rect x="133.068" y="93.6519" width="3.6875" height="11.9844" rx="1.84375" transform="rotate(-150 133.068 93.6519)" fill="#CDF4DD"/>
|
||||
</g>
|
||||
<g opacity="0.6">
|
||||
<path d="M32.8015 122.764L35.7266 112.563L56.9458 118.648L53.9545 129.08C52.6533 133.618 53.9413 138.504 57.3104 141.81C60.9508 145.383 62.1371 150.768 60.3348 155.54L49.6241 183.896C48.5204 186.818 45.3597 188.404 42.3573 187.543L11.5784 178.717C8.5806 177.858 6.74113 174.845 7.34625 171.786L12.9588 143.41C13.9947 138.173 17.8826 133.96 23.0201 132.508C27.7474 131.172 31.4475 127.486 32.8015 122.764Z" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<rect x="37.7793" y="95.1074" width="27.7521" height="16.4778" rx="6" transform="rotate(16 37.7793 95.1074)" stroke="#CDF4DD" stroke-width="4"/>
|
||||
<path d="M32.8015 122.764L35.7266 112.563L56.9458 118.648L53.9545 129.08C52.6533 133.618 53.9413 138.504 57.3104 141.81C60.9508 145.383 62.1371 150.768 60.3348 155.54L49.6241 183.896C48.5204 186.818 45.3597 188.404 42.3573 187.543L11.5784 178.717C8.5806 177.858 6.74113 174.845 7.34625 171.786L12.9588 143.41C13.9947 138.173 17.8826 133.96 23.0201 132.508C27.7474 131.172 31.4475 127.486 32.8015 122.764Z" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="37.7793" y="95.1074" width="27.7521" height="16.4778" rx="6" transform="rotate(16 37.7793 95.1074)" stroke="#CDF4DD" strokeWidth="4"/>
|
||||
<rect x="62.3047" y="110.26" width="3.46901" height="27.7521" rx="1.7345" transform="rotate(106 62.3047 110.26)" fill="#CDF4DD"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,10 @@
|
||||
@import '../../../../styles/utilities';
|
||||
|
||||
.productInfoDetail {
|
||||
@apply spacing-horizontal;
|
||||
padding-bottom: 4rem;
|
||||
@screen md {
|
||||
@apply flex;
|
||||
padding-bottom: 5.6rem;
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import ProductImgs from './components/ProductImgs/ProductImgs'
|
||||
import ProductInfo from './components/ProductInfo/ProductInfo'
|
||||
import s from './ProductInfoDetail.module.scss'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any
|
||||
}
|
||||
|
||||
const ProductInfoDetail = ({ }: Props) => {
|
||||
return (
|
||||
<section className={s.productInfoDetail}>
|
||||
<ProductImgs/>
|
||||
<ProductInfo/>
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductInfoDetail
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user