This commit is contained in:
unknown
2021-08-30 13:55:13 +07:00
181 changed files with 5664 additions and 11049 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

BIN
src/assets/imgs/gpay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

BIN
src/assets/imgs/visa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

View File

@@ -0,0 +1,17 @@
.authorWarper{
@apply flex flex-row items-center;
.authorImage{
width:3.2rem;
height:3.2rem;
border-radius:3.2rem;
}
.authorName{
margin-left:1rem;
color:var(--text-label);
font-family: var(--font-sans);
font-size: 1.2rem;
line-height: 2rem;
font-feature-settings: 'salt' on;
}
}

View File

@@ -0,0 +1,21 @@
import React from 'react';
import s from './Author.module.scss';
import classNames from 'classnames';
import Image from "next/image";
interface Props {
image:any,
name: string
}
const Author = ({image,name}:Props) =>{
return (
<div className={classNames(s.authorWarper)}>
<Image className={classNames(s.authorImage)} src={image} alt=""/>
<div className={classNames(s.authorName)}>{name}</div>
</div>
)
}
export default Author;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,52 @@
@import "../../../styles/utilities";
.banner {
@apply bg-primary-light custom-border-radius-lg overflow-hidden;
@screen md {
border: 1px solid var(--primary);
}
&.large {
margin-bottom: 2.8rem;
.inner {
@screen xl {
@apply bg-right-bottom;
background-size: unset;
}
}
}
.inner {
@apply bg-no-repeat;
background-size: 90%;
background-position: right -500% bottom 0%;
.content {
background-image: linear-gradient(
to right,
rgb(227, 242, 233, 0.9),
rgb(227, 242, 233, 0.5) 80%,
rgb(227, 242, 233, 0)
);
padding: 1.6rem;
max-width: 37rem;
@screen md {
max-width: 49.6rem;
padding: 4.8rem;
}
.top {
.heading {
@apply heading-1 font-heading;
margin-bottom: 1.6rem;
}
.subHeading {
@apply sub-headline;
@screen md {
@apply caption;
}
}
}
.bottom {
margin-top: 4rem;
}
}
}
}

View File

@@ -0,0 +1,48 @@
import classNames from 'classnames'
import Link from 'next/link'
import React, { memo } from 'react'
import { IconArrowRight } from 'src/components/icons'
import { ROUTE } from 'src/utils/constanst.utils'
import { LANGUAGE } from 'src/utils/language.utils'
import ButtonCommon from '../ButtonCommon/ButtonCommon'
import s from './Banner.module.scss'
interface Props {
imgLink: string,
title: string,
subtitle: string,
buttonLabel?: string,
linkButton?: string,
size?: 'small' | 'large',
}
const Banner = memo(({ imgLink, title, subtitle, buttonLabel = LANGUAGE.BUTTON_LABEL.SHOP_NOW, linkButton = ROUTE.HOME, size = 'large' }: Props) => {
return (
<div className={classNames({
[s.banner]: true,
[s[size]]: true,
})}>
<div className={s.inner} style={{ backgroundImage: `url(${imgLink})` }}>
<div className={s.content}>
<div className={s.top}>
<h1 className={s.heading}>
{title}
</h1>
<div className={s.subHeading}>
{subtitle}
</div>
</div>
<div className={s.bottom}>
<Link href={linkButton}>
<a>
<ButtonCommon icon={<IconArrowRight />} isIconSuffix={true}>{buttonLabel}</ButtonCommon>
</a>
</Link>
</div>
</div>
</div>
</div>
)
})
export default Banner

View File

@@ -5,12 +5,29 @@
display: flex;
justify-content: center;
align-items: center;
padding: 1.6rem 3.2rem;
padding: 1rem 2rem;
@screen md {
padding: 0.8rem 3.2rem;
}
&:disabled {
filter: brightness(0.9);
cursor: not-allowed;
color: var(--disabled);
}
&:hover {
@apply shadow-md;
&:not(:disabled) {
filter: brightness(1.05);
}
}
&:focus {
outline: none;
filter: brightness(1.05);
}
&:focus-visible {
outline: 2px solid var(--text-active);
}
&.loading {
&::before {
content: "";
@@ -24,20 +41,6 @@
margin-right: 0.8rem;
}
}
&:hover {
@apply shadow-md;
&:not(:disabled) {
filter: brightness(1.05);
}
}
&:focus {
outline: none;
filter: brightness(1.05);
}
&:focus-visible {
outline: 2px solid var(--text-active);
}
&.light {
@apply text-base bg-white;
@@ -48,8 +51,44 @@
}
}
}
&.lightBorderNone {
@apply bg-white text-primary;
&.loading {
&::before {
border-top-color: var(--primary);
}
}
}
&.ghost {
@apply bg-white text-primary;
border: 1px solid var(--primary);
&.loading {
&::before {
border-top-color: var(--text-active);
}
}
}
&.onlyIcon {
padding: 0.8rem;
.icon {
margin: 0;
}
}
&.large {
padding: 3.2rem 4.8rem;
padding: 1rem 1.5rem;
&.onlyIcon {
padding: 1rem;
}
@screen md {
padding: 1.6rem 4.8rem;
&.onlyIcon {
padding: 1.6rem;
}
}
&.loading {
&::before {
width: 2.4rem;

View File

@@ -1,21 +1,20 @@
import classNames from 'classnames'
import React, { memo } from 'react'
import { ButonType, ButtonSize } from 'src/utils/constanst.utils'
import s from './ButtonCommon.module.scss'
interface Props {
children?: React.ReactNode,
type?: ButonType,
size?: ButtonSize,
icon?: any,
type?: 'primary' | 'light' | 'ghost' | 'lightBorderNone',
size?: 'default' | 'large',
icon?: React.ReactNode,
isIconSuffix?: boolean,
loading?: boolean,
disabled?: boolean,
onClick?: () => void,
}
const ButtonCommon = memo(({ type = ButonType.primary, size = ButtonSize.default,
icon, loading, disabled, isIconSuffix, children, onClick }: Props) => {
const ButtonCommon = memo(({ type = 'primary', size = 'default', loading = false, isIconSuffix = false,
icon, disabled, children, onClick }: Props) => {
return (
<button className={classNames({
[s.buttonCommon]: true,
@@ -23,6 +22,7 @@ const ButtonCommon = memo(({ type = ButonType.primary, size = ButtonSize.default
[s[size]]: !!size,
[s.loading]: loading,
[s.preserve]: isIconSuffix,
[s.onlyIcon]: icon && !children,
})}
disabled={disabled}
onClick={onClick}

View File

@@ -0,0 +1,26 @@
import React, { memo } from 'react'
import { IconBuy } from 'src/components/icons'
import ButtonCommon from '../ButtonCommon/ButtonCommon'
interface Props {
type?: 'primary' | 'light' | 'ghost',
size?: 'default' | 'large',
loading?: boolean,
disabled?: boolean,
onClick?: () => void,
}
const ButtonIconBuy = memo(({ type = 'light', size = 'default', loading = false, disabled, onClick }: Props) => {
return (
<ButtonCommon
type={type}
size={size}
loading={loading}
disabled={disabled}
onClick={onClick}
icon={<IconBuy />}
/>
)
})
export default ButtonIconBuy

View File

@@ -0,0 +1,51 @@
@import '../../../styles/utilities';
.navigationWrapper {
@apply relative;
min-height: theme('caroucel.arrow-height');
.isPadding {
@apply spacing-horizontal;
}
:global(.customArrow) {
width: 64px;
height: 64px;
&:focus {
outline: none;
}
@apply absolute top-1/2 bg-background-arrow transform -translate-y-1/2 flex justify-center items-center transition duration-100;
&:global(.leftArrow) {
@apply left-0;
}
&:global(.rightArrow) {
@apply right-0;
}
&:global(.isDisabledArrow) {
@apply hidden;
}
}
:global {
.dots {
display: flex;
padding: 1rem 0;
justify-content: center;
}
.dot {
border: none;
width: 1rem;
height: 1rem;
background: #c5c5c5;
border-radius: 50%;
margin: 0 0.5rem;
padding: 0.5rem;
cursor: pointer;
}
.dot:focus {
outline: none;
}
.dot.active {
background: #000;
}
}
}

View File

@@ -0,0 +1,103 @@
import { useKeenSlider } from 'keen-slider/react'
import React, { useEffect } from 'react'
import 'keen-slider/keen-slider.min.css'
import { CustomCarouselArrow } from './CustomArrow/CustomCarouselArrow'
import s from './CarouselCommon.module.scss'
import { TOptionsEvents } from 'keen-slider'
import classNames from 'classnames'
import CustomDot from './CustomDot/CustomDot'
export interface CarouselCommonProps<T> {
data: T[]
Component: React.ComponentType<T>
isArrow?: Boolean
isDot?: Boolean
itemKey: String
option: TOptionsEvents
keenClassname?: string
isPadding?: boolean
}
const CarouselCommon = <T,>({
data,
Component,
itemKey,
keenClassname,
isPadding = false,
isArrow = true,
isDot = false,
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,
slidesPerView,
slideChanged(s) {
setCurrentSlide(s.details().relativeSlide)
},
})
useEffect(() => {
if(isDot && slider && data){
let array:number[]
let number = data.length - Math.floor(slider.details().slidesPerView - 1)
if(number<1){
number = 1
}
array = [...Array(number).keys()]
setDotArr(array)
}
}, [isDot,slider,data])
const handleRightArrowClick = () => {
slider.next()
}
const handleLeftArrowClick = () => {
slider.prev()
}
const onDotClick = (index:number) => {
slider.moveToSlideRelative(index)
}
return (
<div className={s.navigationWrapper}>
<div
ref={sliderRef}
className={classNames('keen-slider', keenClassname, {
[s.isPadding]: isPadding,
})}
>
{data?.map((props, index) => (
<div className="keen-slider__slide" key={`${itemKey}-${index}`}>
<Component {...props} />
</div>
))}
</div>
{slider && isArrow && (
<>
<CustomCarouselArrow
side="right"
onClick={handleRightArrowClick}
/>
<CustomCarouselArrow
side="left"
onClick={handleLeftArrowClick}
/>
</>
)}
{slider && isDot && (
<div className="dots">
{dotArr.map((index) => {
return (
<CustomDot key={`dot-${index}`} index={index} dotActive={currentSlide} onClick={onDotClick}/>
)
})}
</div>
)}
</div>
)
}
export default CarouselCommon

View File

@@ -0,0 +1,20 @@
.navigationWrapper{
:global(.customArrow) {
width: 64px;
height: 64px;
&:focus{
outline: none;
}
@apply absolute top-1/2 bg-background-arrow transform -translate-y-1/2 flex justify-center items-center transition duration-100;
&.leftArrow{
@apply left-0;
}
&.rightArrow{
@apply right-0;
}
&.isDisabled{
@apply hidden ;
}
}
}

View File

@@ -0,0 +1,25 @@
import classNames from 'classnames'
import React from 'react'
import ArrowLeft from 'src/components/icons/ArrowLeft'
import ArrowRight from 'src/components/icons/ArrowRight'
import "./CustomCarouselArrow.module.scss"
interface CustomCarouselArrowProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
side: 'left' | 'right'
isDisabled?:Boolean
}
export const CustomCarouselArrow = ({
side,isDisabled,
...props
}: CustomCarouselArrowProps) => {
return (
<button
{...props}
className={classNames("customArrow", { [`${side}Arrow`]: side,"isDisabledArrow":isDisabled})}
>
{side==='left'?(<ArrowLeft/>):(<ArrowRight/>)}
</button>
)
}

View File

@@ -0,0 +1,21 @@
import React from 'react'
interface Props {
index: number
dotActive:number
onClick: (index: number) => void
}
const CustomDot = ({ index, onClick, dotActive }: Props) => {
const handleOnClick = () => {
onClick && onClick(index)
}
return (
<button
onClick={handleOnClick}
className={'dot' + (dotActive === index ? ' active' : '')}
/>
)
}
export default CustomDot

View File

@@ -0,0 +1,71 @@
@import '../../../styles/utilities';
.checkboxCommonWarper{
@apply flex flex-col;
.checkboxItem{
display: block;
position: relative;
cursor: pointer;
border-radius: 0.4rem;
width:50%;
&:hover .checkboxInput ~ .checkMark {
background-color: #ccc;
}
.checkboxInput{
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
&:checked ~ .checkMark:after {
display: block;
}
}
.checkMark {
border-radius: 0.4rem;
position: absolute;
top: 0;
left: 0;
height: 2rem;
width: 2rem;
background-color:var(--positive);
&:after {
left: 25.74%;
bottom: 34.6%;
width: 0.878rem;
height: 0.7rem;
color:white;
content: "";
background-image:url('/assets/svg/checkmark.svg');
background-size:cover;
background-repeat: no-repeat;
position: absolute;
display: none;
}
}
&~ .checkMark{
background-color: #ccc;
}
&:checked ~ .checkMark {
background-color: #2196F3;
}
&:checked ~ .checkMark:after {
display: block;
}
&:hover .checkboxInput ~ .checkMark {
background-color: #ccc;
}
}
.checkboxText{
margin-left:3rem;
font-size:1.2rem;
line-height: 2rem;
font-family: var(--font-sans);
color:var(--text-base);
letter-spacing: 0.01em;
}
}

View File

@@ -0,0 +1,40 @@
import React,{ChangeEvent,useState,useEffect} from 'react';
import s from './CheckboxCommon.module.scss';
import classNames from 'classnames';
interface CheckboxProps extends Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'onChange'
>{
onChange?: (value: boolean) => void,
defaultChecked?: boolean
}
const CheckboxCommon = ({onChange,defaultChecked = true,...props}: CheckboxProps) =>{
const [value, setValue] = useState<boolean>(true);
useEffect(()=>{
onChange && onChange(value)
},[value])
const onValueChange = (e: ChangeEvent<HTMLInputElement>)=>{
let value =e.target.checked;
setValue(value);
}
return (
<div className={classNames(s.checkboxCommonWarper)}>
<label className={classNames(s.checkboxItem)}>
<input id="check" defaultChecked={defaultChecked} className={s.checkboxInput} type="checkbox" onChange={onValueChange}/>
<span className={s.checkMark}></span>
</label>
<div className={classNames(s.checkboxText)}>
<label htmlFor="check"> Billing address is same as shipping </label>
</div>
</div>
)
}
export default CheckboxCommon;

View File

@@ -0,0 +1,5 @@
.subtitle {
font-size: var(--font-size);
line-height: var(--line-height);
margin-top: .4rem;
}

View File

@@ -0,0 +1,22 @@
import React from 'react'
import s from './CollectionHeading.module.scss'
import HeadingCommon from '../HeadingCommon/HeadingCommon'
export interface CollectionHeadingProps {
type?: 'default' | 'highlight' | 'light';
title: string;
subtitle: string;
}
const CollectionHeading = ({ type = 'default', title, subtitle }: CollectionHeadingProps) => {
return (
<section>
<HeadingCommon type={type}>{title}</HeadingCommon>
<div className={s.subtitle}>{subtitle}</div>
</section>
)
}
export default CollectionHeading

View File

@@ -0,0 +1,9 @@
.dateTime{
color:var(--text-label);
text-transform: uppercase;
font-size: 1.2rem;
line-height: 2rem;
letter-spacing: 0.01em;
font-feature-settings: 'salt' on;
font-family: var(--font-sans);
}

View File

@@ -0,0 +1,15 @@
import React from 'react';
import s from './DateTime.module.scss';
import classNames from 'classnames';
interface Props {
date:string,
}
const DateTime = ({date}:Props) =>{
return (
<div className={classNames(s.dateTime)}>{date}</div>
)
}
export default DateTime;

View File

@@ -0,0 +1,61 @@
@import '../../../styles/utilities';
.featuredProductCardWarpper{
width: 59.8rem;
height: 28.8rem;
padding: 2.4rem;
@apply bg-primary-light inline-flex justify-start items-center custom-border-radius ;
.left{
width: 24rem;
height: 24rem;
}
.right{
padding-left: 2.4rem;
min-width: 27rem;
max-width: 28.6rem;
min-height: 16.8rem;
@apply flex justify-between flex-col;
.rightTop{
min-height: 9.6rem;
@apply flex justify-between flex-col;
.title{
@apply font-bold;
font-size: 2rem;
line-height: 2.8rem;
letter-spacing: -0.01em;
color: var(--text-active);
}
.subTitle{
color: var(--text-base);
font-size: 1.6rem;
line-height: 2.4rem;
}
.priceWrapper{
@apply flex justify-start;
.price{
@apply font-bold;
font-size: 2rem;
line-height: 2.8rem;
letter-spacing: -0.01em;
color: var(--text-active);
}
.originPrice{
margin-left: 0.8rem;
font-size: 2rem;
line-height: 2.8rem;
color: var(--text-label);
text-decoration-line: line-through;
}
}
}
.buttonWarpper{
@apply flex;
.icon{
width: 5.6rem;
}
.button{
margin-left: 0.8rem;
width: 20.6rem;
}
}
}
}

View File

@@ -0,0 +1,46 @@
import React from 'react'
import { FeaturedProductProps } from 'src/utils/types.utils'
import s from './FeaturedProductCard.module.scss'
import { LANGUAGE } from '../../../utils/language.utils'
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
import ButtonCommon from '../ButtonCommon/ButtonCommon'
interface FeaturedProductCardProps extends FeaturedProductProps {
buttonText?: string
}
const FeaturedProductCard = ({
imageSrc,
title,
subTitle,
price,
originPrice,
buttonText = LANGUAGE.BUTTON_LABEL.BUY_NOW,
}: FeaturedProductCardProps) => {
return (
<div className={s.featuredProductCardWarpper}>
<div className={s.left}>
<img src={imageSrc} alt="image" />
</div>
<div className={s.right}>
<div className={s.rightTop}>
<div className={s.title}>{title}</div>
<div className={s.subTitle}>{subTitle}</div>
<div className={s.priceWrapper}>
<div className={s.price}>{price} </div>
<div className={s.originPrice}>{originPrice} </div>
</div>
</div>
<div className={s.buttonWarpper}>
<div className={s.icon}>
<ButtonIconBuy />
</div>
<div className={s.button}>
<ButtonCommon>{buttonText}</ButtonCommon>
</div>
</div>
</div>
</div>
)
}
export default FeaturedProductCard

View File

@@ -0,0 +1,31 @@
@import "../../../styles/utilities";
.footer {
@apply spacing-horizontal;
padding-top: 4rem;
padding-bottom: 2rem;
margin-bottom: 10rem;
.footerMenu {
padding-bottom: 4rem;
}
.menu {
@apply flex flex-wrap;
}
@screen md {
margin-bottom: 0;
padding-bottom: 4rem;
padding-left: 3.2rem;
padding-right: 3.2rem;
.footerMenu {
@apply flex;
padding-bottom: 8rem;
.menu {
@apply flex-nowrap justify-between;
}
}
}
@screen lg {
@apply spacing-horizontal;
}
}

View File

@@ -0,0 +1,85 @@
import React from 'react'
import { ROUTE } from 'src/utils/constanst.utils'
import FooterColumn from './components/FooterColumn/FooterColumn'
import FooterSocial from './components/FooterSocial/FooterSocial'
import s from './Footer.module.scss'
const FOOTER_COLUMNS = [
{
title: 'Company',
items: [
{
name: 'All Product',
link: ROUTE.PRODUCTS,
},
{
name: 'About Us',
link: ROUTE.ABOUT,
},
{
name: 'Bussiness',
link: ROUTE.BUSSINESS,
}
]
},
{
title: 'Resources',
items: [
{
name: 'Contact Us',
link: ROUTE.CONTACT,
},
{
name: 'FAQ',
link: ROUTE.FAQ,
},
{
name: 'Customer Service',
link: ROUTE.CUSTOMER_SERVICE,
},
]
},
{
title: 'Quick Links',
items: [
{
name: 'Terms & Conditions',
link: ROUTE.TERM_CONDITION,
},
{
name: 'Privacy Policy',
link: ROUTE.TERM_CONDITION,
},
{
name: 'Blog',
link: ROUTE.TERM_CONDITION,
},
]
}
]
interface Props {
className?: string
children?: any
}
const Footer = ({ }: Props) => {
return (
<footer className={s.footer}>
<div className={s.footerMenu}>
<section className={s.menu}>
{FOOTER_COLUMNS.map(item => <FooterColumn
key={item.title}
title={item.title}
items={item.items} />)}
</section>
<FooterSocial />
</div>
<div>
© 2021 Online Grocery
</div>
</footer>
)
}
export default Footer

View File

@@ -0,0 +1,33 @@
@import "../../../../../styles/utilities";
.footerColumn {
width: 50%;
margin-bottom: 4rem;
@screen md {
padding-right: 6.4rem;
width: unset;
margin-bottom: 0;
}
@screen lg {
padding-right: 12.8rem;
}
.title {
@apply sm-headline text-active;
margin-bottom: 2.4rem;
}
ul {
list-style: none;
li {
&:not(:last-child) {
margin-bottom: 1.6rem;
}
a {
@apply transition-all duration-200 no-underline;
&:hover {
color: var(--primary);
}
}
}
}
}

View File

@@ -0,0 +1,38 @@
import Link from 'next/link';
import React from 'react';
import s from './FooterColumn.module.scss'
interface Props {
title: string,
items: { link: string, name: string, isOpenNewTab?: boolean }[],
}
const FooterColumn = ({ title, items }: Props) => {
return (
<section className={s.footerColumn}>
<h4 className={s.title}>
{title}
</h4>
<ul>
{
items.map(item => <li key={item.name}>
{
item.isOpenNewTab ?
<a href={item.link} target="_blank" rel="noopener noreferrer">
{item.name}
</a>
:
<Link href={item.link}>
<a >
{item.name}
</a>
</Link>
}
</li>)
}
</ul>
</section>
);
};
export default FooterColumn;

View File

@@ -0,0 +1,43 @@
@import "../../../../../styles/utilities";
.footerSocial {
.title {
@apply sm-headline text-active;
margin-bottom: 2.4rem;
}
.socialMedia,
.payment {
@apply list-none flex items-center;
}
.socialMedia {
li {
@apply transition-all duration-200;
margin-right: 1.6rem;
&:last-child {
margin-right: 0;
}
a {
@apply no-underline;
}
&:hover {
svg path {
fill: var(--primary);
}
}
}
}
.payment {
margin-top: 3.2rem;
li {
margin-right: 1.6rem;
width: 4rem;
img {
width: 100%;
object-fit: contain;
}
&:last-child {
margin-right: 0;
}
}
}
}

View File

@@ -0,0 +1,75 @@
import React from 'react';
import IconFacebook from 'src/components/icons/IconFacebook';
import IconInstagram from 'src/components/icons/IconInstagram';
import IconTwitter from 'src/components/icons/IconTwitter';
import IconYoutube from 'src/components/icons/IconYoutube';
import { SOCIAL_LINKS } from 'src/utils/constanst.utils';
import IconVisa from '../../../../../assets/imgs/visa.png';
import IconMasterCard from '../../../../../assets/imgs/mastercard.png';
import IconGooglePlay from '../../../../../assets/imgs/gpay.png';
import IconApplePay from '../../../../../assets/imgs/apple_pay.png';
import s from './FooterSocial.module.scss';
const SOCIAL_MENU = [
{
icon: <IconFacebook />,
link: SOCIAL_LINKS.FB,
},
{
icon: <IconTwitter />,
link: SOCIAL_LINKS.TWITTER,
},
{
icon: <IconYoutube />,
link: SOCIAL_LINKS.YOUTUBE,
},
{
icon: <IconInstagram />,
link: SOCIAL_LINKS.IG,
},
]
const PAYMENT_METHODS = [
{
icon: IconVisa.src,
name: 'Visa'
},
{
icon: IconMasterCard.src,
name: 'Master Card'
},
{
icon: IconGooglePlay.src,
name: 'GooglePay'
},
{
icon: IconApplePay.src,
name: 'Apple Pay'
},
]
const FooterSocial = () => {
return (
<section className={s.footerSocial}>
<div className={s.title}>Social</div>
<ul className={s.socialMedia}>
{
SOCIAL_MENU.map(item => <li key={item.link}>
<a href={item.link} target="_blank" rel="noopener noreferrer">
{item.icon}
</a>
</li>)
}
</ul>
<ul className={s.payment}>
{
PAYMENT_METHODS.map(item => <li key={item.name}>
<img src={item.icon} alt={item.name} />
</li>)
}
</ul>
</section>
);
};
export default FooterSocial;

View File

@@ -1,20 +1,17 @@
@import "../../../styles/utilities";
.header {
.btn {
// @apply font-bold py-2 px-4 rounded;
@apply sticky bg-white shadow-md;
top: 0;
z-index: 9999;
margin-bottom: 3.2rem;
&.full {
@apply shadow-none;
border: 1px solid var(--border-line);
}
.btnBlue {
@apply bg-primary hover:bg-warning text-label font-bold py-2 px-4 custom-border-radius;
}
.link {
color: theme("colors.warning");
}
.heading {
@apply text-base font-heading;
}
.paragraph {
@apply topline;
.menu {
padding-left: 3.2rem;
padding-right: 3.2rem;
}
.logo {
@apply font-logo;

View File

@@ -1,19 +1,48 @@
import { FC } from 'react'
import classNames from 'classnames'
import React, { memo, useEffect, useState } from 'react'
import { useModalCommon } from 'src/components/hooks/useModalCommon'
import { isMobile } from 'src/utils/funtion.utils'
import ModalAuthenticate from '../ModalAuthenticate/ModalAuthenticate'
import HeaderHighLight from './components/HeaderHighLight/HeaderHighLight'
import HeaderMenu from './components/HeaderMenu/HeaderMenu'
import HeaderSubMenu from './components/HeaderSubMenu/HeaderSubMenu'
import HeaderSubMenuMobile from './components/HeaderSubMenuMobile/HeaderSubMenuMobile'
import s from './Header.module.scss'
interface Props {
className?: string
children?: any
}
const Header: FC<Props> = ({ }: Props) => {
const Header = memo(() => {
const [isFullHeader, setIsFullHeader] = useState<boolean>(true)
const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false })
useEffect(() => {
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [])
const handleScroll = () => {
if (!isMobile()) {
if (window.scrollY === 0) {
setIsFullHeader(true)
} else {
setIsFullHeader(false)
}
}
}
return (
<div className={s.header}>
This is Header
<h1 className={s.heading}>This is heading</h1>
<div className={s.logo}>This is logo text</div>
</div>
<>
<header className={classNames({ [s.header]: true, [s.full]: isFullHeader })}>
<HeaderHighLight isShow={isFullHeader} />
<div className={s.menu}>
<HeaderMenu isFull={isFullHeader} openModalAuthen={openModalAuthen} />
<HeaderSubMenu isShow={isFullHeader} />
</div>
</header>
<HeaderSubMenuMobile />
<ModalAuthenticate visible={visibleModalAuthen} closeModal={closeModalAuthen} />
</>
)
}
})
export default Header

View File

@@ -0,0 +1,39 @@
@import "../../../../../styles/utilities";
.headerHighLight {
@apply hidden;
@screen md {
transform: translateY(-10rem);
height: 0;
&.show {
@apply flex justify-between items-center spacing-horizontal bg-primary caption;
animation: showHeaderHightlight 0.2s;
height: unset;
transform: none;
padding-top: 0.8rem;
padding-bottom: 0.8rem;
color: var(--white);
.menu {
@apply flex items-center list-none;
padding: 0.8rem 0;
li {
&:not(:last-child) {
margin-right: 3.2rem;
}
a {
@appy no-underline;
}
}
}
}
}
}
@keyframes showHeaderHightlight {
0% {
transform: translateY(-4rem);
}
100% {
transform: none;
}
}

View File

@@ -0,0 +1,49 @@
import classNames from 'classnames'
import Link from 'next/link'
import { memo, useEffect, useRef } from 'react'
import { ROUTE } from 'src/utils/constanst.utils'
import s from './HeaderHighLight.module.scss'
const MENU = [
{
name: 'Delivery & Policy',
link: ROUTE.PRIVACY_POLICY,
},
{
name: 'Blog',
link: ROUTE.BLOGS,
},
{
name: 'About Us',
link: ROUTE.ABOUT,
},
]
interface Props {
children?: any,
isShow: boolean,
}
const HeaderHighLight = memo(({ isShow }: Props) => {
return (
<section className={classNames({ [s.headerHighLight]: true, [s.show]: isShow })}>
<div>
Free Shipping on order $49+ / Express $99+
</div>
<ul className={s.menu}>
{
MENU.map(item => <li key={item.name}>
<Link href={item.link}>
<a >
{item.name}
</a>
</Link>
</li>)
}
</ul>
</section>
)
})
export default HeaderHighLight

View File

@@ -0,0 +1,65 @@
@import "../../../../../styles/utilities";
.headerMenu {
padding-top: 1.6rem;
padding-bottom: 0.8rem;
@screen md {
@apply flex justify-between items-center;
padding-top: 0.8rem;
padding-bottom: 0.8rem;
&.full {
padding-top: 2.4rem;
padding-bottom: 2.4rem;
}
}
.left {
.top {
@apply flex justify-between items-center;
.iconCart {
}
}
.inputSearch {
margin-top: 2.4rem;
@screen lg {
min-width: 51.2rem;
max-width: 50%;
}
}
@screen md {
@apply flex items-center;
.top {
.iconCart {
@apply hidden;
}
}
.inputSearch {
margin-left: 4.8rem;
margin-top: 0;
}
}
}
.menu {
@apply hidden;
@screen md {
@apply flex items-center list-none;
li {
@apply flex justify-center items-center w-full;
&:not(:last-child) {
margin-right: 4.8rem;
@screen lg {
margin-right: 6.4rem;
}
}
a {
@appy no-underline;
&.iconFovourite {
svg path {
fill: var(--negative);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,74 @@
import classNames from 'classnames'
import Link from 'next/link'
import { memo, useMemo } from 'react'
import InputSearch from 'src/components/common/InputSearch/InputSearch'
import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown'
import { IconBuy, IconHeart, IconHistory, IconUser } from 'src/components/icons'
import { ACCOUNT_TAB, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import s from './HeaderMenu.module.scss'
interface Props {
children?: any,
isFull: boolean,
openModalAuthen: () => void,
}
const HeaderMenu = memo(({ isFull, openModalAuthen }: Props) => {
const optionMenu = useMemo(() => [
{
onClick: openModalAuthen,
name: 'Login (Demo)',
},
{
link: ROUTE.ACCOUNT,
name: 'Account',
},
{
link: '/',
name: 'Logout',
},
], [openModalAuthen])
return (
<section className={classNames({ [s.headerMenu]: true, [s.full]: isFull })}>
<div className={s.left}>
<div className={s.top}>
<div>Online Grocery</div>
<button className={s.iconCart}>
<IconBuy />
</button>
</div>
<div className={s.inputSearch}>
<InputSearch />
</div>
</div>
<ul className={s.menu}>
<li>
<Link href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.ORDER}`}>
<a >
<IconHistory />
</a>
</Link>
</li>
<li>
<Link href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.FAVOURITE}`}>
<a className={s.iconFovourite}>
<IconHeart />
</a>
</Link>
</li>
<li>
<MenuDropdown options={optionMenu} isHasArrow={false}><IconUser /></MenuDropdown>
</li>
<li>
<button>
<IconBuy />
</button>
</li>
</ul>
</section>
)
})
export default HeaderMenu

View File

@@ -0,0 +1,3 @@
.headerNoti {
@apply flex items-center;
}

View File

@@ -0,0 +1,16 @@
import React from 'react';
import NotiMessage from 'src/components/common/NotiMessage/NotiMessage';
import { IconInfo } from 'src/components/icons';
import s from './HeaderNoti.module.scss';
const HeaderNoti = () => {
return (
<NotiMessage>
<div className={s.headerNoti}>
<IconInfo />&nbsp;<span>You can buy fresh products after <b>11pm</b> or <b>8am</b></span>
</div>
</NotiMessage>
);
};
export default HeaderNoti;

View File

@@ -0,0 +1,42 @@
@import "../../../../../styles/utilities";
.headerSubMenu {
@apply hidden;
@screen md {
transform: translateY(-10rem);
height: 0;
&.show {
@apply block;
padding-bottom: 2.4rem;
transform: none;
height: unset;
@screen lg {
@apply flex justify-between items-center;
}
.menu {
@apply flex items-center list-none;
margin-bottom: 2.4rem;
@screen lg {
margin-bottom: 0;
}
li {
&:not(:last-child) {
margin-right: 2.4rem;
@screen lg {
margin-right: 4rem;
}
}
a {
@appy no-underline;
}
&:hover {
@apply text-primary;
}
&.active {
@apply text-primary;
}
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
import classNames from 'classnames'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { memo } from 'react'
import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown'
import { ProductFeature, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import HeaderNoti from './HeaderNoti/HeaderNoti'
import s from './HeaderSubMenu.module.scss'
const MENU = [
{
name: 'New Items',
link: `${ROUTE.PRODUCTS}?${QUERY_KEY.FEATURED}=${ProductFeature.NewItem}`,
},
{
name: 'Sales',
link: `${ROUTE.PRODUCTS}?${QUERY_KEY.FEATURED}=${ProductFeature.Sales}`,
},
{
name: 'Best Sellers',
link: `${ROUTE.PRODUCTS}?${QUERY_KEY.FEATURED}=${ProductFeature.BestSellers}`,
},
{
name: 'About Us',
link: ROUTE.ABOUT,
},
{
name: 'Blog',
link: ROUTE.BLOGS,
},
]
// note: hard code, remove later
const CATEGORY = [
{
name: 'Veggie',
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=veggie`,
},
{
name: 'Seafood',
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=seafood`,
},
{
name: 'Frozen',
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=frozen`,
},
{
name: 'Coffee Bean',
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=coffee-bean`,
},
{
name: 'Sauce',
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=sauce`,
},
]
interface Props {
children?: any,
isShow: boolean,
}
const HeaderSubMenu = memo(({ isShow }: Props) => {
const router = useRouter()
return (
<section className={classNames({ [s.headerSubMenu]: true, [s.show]: isShow })}>
<ul className={s.menu}>
{/* todo: handle active item */}
<li>
<MenuDropdown options={CATEGORY} align="left">Categories</MenuDropdown>
</li>
{
MENU.map(item => <li key={item.name}
className={classNames({ [s.active]: router.asPath === item.link })}>
<Link href={item.link}>
<a >
{item.name}
</a>
</Link>
</li>)
}
</ul>
<HeaderNoti />
</section>
)
})
export default HeaderSubMenu

View File

@@ -0,0 +1,51 @@
@import "../../../../../styles/utilities";
.headerSubMenuMobile {
@apply fixed w-full bg-white;
bottom: 0;
left: 0;
padding: 2rem 1rem;
border-top: 1px solid var(--border-line);
box-shadow: -5px 6px 10px rgba(0, 0, 0, 0.2);
z-index: 9999;
.menu {
@apply grid grid-cols-4;
li {
a {
@apply transition-all duration-200 no-underline;
&:hover {
color: var(--primary);
}
}
.menuItem {
@apply flex flex-col justify-center items-center sm-label;
.icon {
position: relative;
margin-bottom: 0.5rem;
svg path {
fill: currentColor;
}
}
&.active {
@apply text-primary;
}
&.dot {
.icon {
&::after {
@apply absolute bg-negative rounded-full;
content: "";
top: 0;
right: 0;
$size: 1rem;
width: $size;
height: $size;
}
}
}
}
}
}
@screen md {
@apply hidden;
}
}

View File

@@ -0,0 +1,66 @@
import classNames from 'classnames'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { memo } from 'react'
import { IconHeart, IconHome, IconShopping, IconUser } from 'src/components/icons'
import { ACCOUNT_TAB, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import s from './HeaderSubMenuMobile.module.scss'
const OPTION_MENU = [
{
link: ROUTE.HOME,
name: 'Home',
icon: <IconHome />,
isMarked: true,
},
{
link: ROUTE.PRODUCTS,
name: 'Shopping',
icon: <IconShopping />,
isMarked: false,
},
{
link: `${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.FAVOURITE}`,
name: 'Favourites',
icon: <IconHeart />,
isMarked: false,
},
{
link: ROUTE.ACCOUNT,
name: 'Account',
icon: <IconUser />,
isMarked: false,
},
]
interface Props {
children?: any
}
const HeaderSubMenuMobile = memo(({ }: Props) => {
const router = useRouter()
return (
<header className={s.headerSubMenuMobile}>
<ul className={s.menu}>
{
OPTION_MENU.map(item => <li key={item.name}>
<Link href={item.link}>
<a >
<div className={classNames({
[s.menuItem]: true,
[s.dot]: item.isMarked,
[s.active]: router.pathname === item.link, // todo: handle active item
})}>
<span className={s.icon}>{item.icon}</span>
<span className={s.label}>{item.name}</span>
</div>
</a>
</Link>
</li>)
}
</ul>
</header>
)
})
export default HeaderSubMenuMobile

View File

@@ -0,0 +1,17 @@
@import '../../../styles/utilities';
.headingCommon {
@apply heading-2 font-heading text-left;
&.highlight {
color: var(--negative);
}
&.light {
color: var(--white);
}
&.center {
@apply text-center;
}
}

View File

@@ -0,0 +1,23 @@
import React from 'react'
import classNames from 'classnames'
import s from './HeadingCommon.module.scss'
interface HeadingCommonProps {
type?: 'highlight' | 'light' | 'default';
align?: 'center' | 'left';
children: string;
}
const HeadingCommon = ({ type='default', align='left', children }: HeadingCommonProps) => {
return (
<h2 className={classNames(s.headingCommon, {
[s[type]]: type,
[s[align]]: align
})}
>{children}</h2>
)
}
export default HeadingCommon

View File

@@ -0,0 +1,51 @@
@import "../../../styles/utilities";
.inputWrap {
@apply flex items-center relative;
.icon {
@apply absolute;
content: "";
left: 1.6rem;
margin-right: 1.6rem;
svg path {
fill: currentColor;
}
}
.icon + .inputCommon {
padding-left: 4.8rem;
}
.inputCommon {
@apply block w-full transition-all duration-200 rounded;
padding: 1.2rem 1.6rem;
border: 1px solid var(--border-line);
&:hover,
&:focus,
&:active {
outline: none;
border: 1px solid var(--primary);
@apply shadow-md;
}
&::placeholder {
@apply text-label;
}
&.custom {
@apply custom-border-radius;
border: 1px solid transparent;
background: var(--gray);
&:hover,
&:focus,
&:active {
border: 1px solid var(--primary);
}
}
&.bgTransparent {
background: rgb(227, 242, 233, 0.3);
color: var(--white);
&::placeholder {
color: var(--white);
}
}
}
}

View File

@@ -0,0 +1,69 @@
import classNames from 'classnames';
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import { KEY } from 'src/utils/constanst.utils';
import s from './InputCommon.module.scss';
type Ref = {
focus: () => void
} | null;
interface Props {
children?: React.ReactNode,
value?: string | number,
placeholder?: string,
type?: 'text' | 'number' | 'email' | 'password',
styleType?: 'default' | 'custom',
backgroundTransparent?: boolean,
icon?: React.ReactNode,
onChange?: (value: string | number) => void,
onEnter?: (value: string | number) => void,
}
const InputCommon = forwardRef<Ref, Props>(({ value, placeholder, type, styleType = 'default', icon, backgroundTransparent = false,
onChange, onEnter }: Props, ref) => {
const inputElementRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputElementRef.current?.focus();
},
getValue: () => {
const value = inputElementRef.current?.value || ''
return value
}
}));
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onChange && onChange(e.target.value)
}
const handleKeyDown = (e: any) => {
if (e.key === KEY.ENTER && onEnter) {
const value = inputElementRef.current?.value || ''
onEnter(value)
}
}
return (
<div className={s.inputWrap}>
{
icon && <span className={s.icon}>{icon}</span>
}
<input
ref={inputElementRef}
value={value}
type={type}
placeholder={placeholder}
onChange={handleChange}
onKeyDown={handleKeyDown}
className={classNames({
[s.inputCommon]: true,
[s[styleType]]: true,
[s.bgTransparent]: backgroundTransparent
})}
/>
</div>
)
})
export default InputCommon

View File

@@ -0,0 +1,22 @@
import React from 'react';
import { IconSearch } from 'src/components/icons';
import { LANGUAGE } from 'src/utils/language.utils';
import { Inputcommon } from '..';
interface Props {
onChange?: (value: string | number) => void,
onEnter?: (value: string | number) => void,
}
const InputSearch = ({ onChange, onEnter }: Props) => {
return (
<Inputcommon placeholder={LANGUAGE.PLACE_HOLDER.SEARCH}
styleType='custom'
icon={<IconSearch />}
onChange={onChange}
onEnter={onEnter}
/>
)
}
export default InputSearch

View File

@@ -0,0 +1,43 @@
.labelCommonWarper{
display: inline-flex;
align-items: flex-start;
font-weight: bold;
letter-spacing: 0.01em;
@apply text-white text-right;
&.defaultSize{
min-height: 2rem;
line-height: 2rem;
font-size: 1.2rem;
padding: 0 0.8rem;
}
&.largeSize{
max-height: 2.4rem;
line-height: 2.4rem;
font-size: 1.6rem;
padding: 0 1.8rem;
}
&.defaultType{
@apply bg-positive-dark;
}
&.discountType{
@apply bg-negative;
}
&.waitingType{
@apply bg-warning;
}
&.deliveringType{
@apply bg-info;
}
&.deliveredType{
@apply bg-positive;
}
&.defaultShape{
border-radius: 0.4rem;
}
&.halfShape{
border-radius: 0px 1.4rem 1.4rem 0px;
}
&.roundShape{
border-radius: 1.4rem;
}
}

View File

@@ -0,0 +1,30 @@
import classNames from 'classnames'
import React from 'react'
import s from './LabelCommon.module.scss'
interface LabelCommonProps extends React.HTMLAttributes<HTMLDivElement> {
size?: 'default' | 'large'
shape?: 'half' | 'round' | 'default'
type?: 'default' | 'discount' | 'waiting' | 'delivering' | 'delivered'
color?: string
}
const LabelCommon = ({
size = 'default',
type = 'default',
shape = "default",
children,
}: LabelCommonProps) => {
return (
<div
className={classNames(s.labelCommonWarper, {
[s[`${size}Size`]]: size,
[s[`${type}Type`]]: type,
[s[`${shape}Shape`]]: shape,
})}
>
{children}
</div>
)
}
export default LabelCommon

View File

@@ -0,0 +1,8 @@
.mainLayout {
display: flex;
flex-direction: column;
min-height: 100vh;
> main {
flex: 1;
}
}

View File

@@ -1,7 +1,9 @@
import { FC, useRef, useEffect } from 'react'
import Header from '../Header/Header'
import { CommerceProvider } from '@framework'
import { useRouter } from 'next/router'
import { FC } from 'react'
import Footer from '../Footer/Footer'
import Header from '../Header/Header'
import s from './Layout.module.scss'
interface Props {
className?: string
@@ -14,8 +16,11 @@ const Layout: FC<Props> = ({ children }) => {
return (
<CommerceProvider locale={locale}>
<Header />
<main>{children}</main>
<div className={s.mainLayout}>
<Header />
<main >{children}</main>
<Footer />
</div>
</CommerceProvider>
)

View File

@@ -1,17 +1,18 @@
@import '../../../styles/utilities';
.logo{
.logo {
display: flex;
.eclipse{
.eclipse {
width: 3.2rem;
height: 3.2rem;
background-color: var(--primary);
border-radius: 50%;
margin-right: 1.2rem;
}
.conTent{
.content {
@apply font-logo;
line-height: 3.2rem;
letter-spacing: -0.02rem;
font-size: 16px;
line-height: 32px;
letter-spacing: -0.02em;
}
}

View File

@@ -1,15 +1,11 @@
import s from './Logo.module.scss'
interface Props {
}
const Logo = ({}: Props) => {
const Logo = () => {
return(
<div className={s.logo}>
<div className={s.eclipse}>
</div>
<div className={s.conTent}>
<div className={s.content}>
ONLINE GROCERY
</div>
</div>

View File

@@ -0,0 +1,98 @@
@import "../../../styles/utilities";
.menuDropdown {
@apply relative cursor-pointer;
width: fit-content;
&:hover {
.label {
color: var(--primary);
svg path {
fill: currentColor;
}
}
.menu {
@apply block;
animation: menuDropdownAnimation 0.2s ease-out;
}
}
.label {
@apply flex justify-end items-center transition-all duration-200;
svg path {
width: fit-content;
}
}
&.arrow {
.label {
margin-right: 1.6rem;
}
&::after {
@apply inline-block absolute transition-all duration-100;
content: "";
top: 35%;
right: 0;
border: solid currentColor;
border-width: 0 2px 2px 0;
padding: 2px;
transform: rotate(45deg);
}
&:hover {
&::after {
@apply border-primary;
transform: rotate(-135deg);
}
}
}
.menu {
@apply hidden absolute;
content: "";
right: 0;
top: 2rem;
height: max-content;
min-width: 19.2rem;
z-index: 100;
&.left {
left: 0;
}
&:hover {
@apply block shadow-md;
}
.menuIner {
@apply rounded list-none bg-white;
border: 1px solid var(--text-active);
margin-top: 0.4rem;
li {
@apply block w-full transition-all duration-200 cursor-pointer text-active;
button {
all: unset;
color: currentColor;
@apply text-left w-full;
}
button,
a {
padding: 0.8rem 1.6rem;
}
a {
@apply block;
}
&:hover {
@apply bg-primary-lightest;
color: var(--primary);
}
}
}
}
}
@keyframes menuDropdownAnimation {
0% {
opacity: 0;
transform: translateY(1.6rem);
}
100% {
opacity: 1;
transform: none;
}
}

View File

@@ -0,0 +1,47 @@
import classNames from 'classnames';
import Link from 'next/link';
import React from 'react';
import s from './MenuDropdown.module.scss';
interface Props {
children?: React.ReactNode,
options: { link?: string, name: string, onClick?: () => void }[],
isHasArrow?: boolean,
align?: 'left'
}
const MenuDropdown = ({ options, children, isHasArrow = true, align }: Props) => {
return (
<div className={classNames({
[s.menuDropdown]: true,
[s.arrow]: isHasArrow,
})}>
<span className={s.label}>
{children}
</span>
<section className={classNames({
[s.menu]: true,
[s.left]: align === 'left',
})} >
<ul className={s.menuIner}>
{
options.map(item => <li key={item.name}>
{item.onClick ?
<button onClick={item.onClick}>
{item.name}
</button>
:
<Link href={item.link || ''}>
<a >
{item.name}
</a>
</Link>}
</li>)
}
</ul>
</section>
</div>
);
};
export default MenuDropdown;

View File

@@ -0,0 +1,10 @@
.formAuthenticate {
@apply overflow-hidden;
.inner {
@apply grid grid-cols-2 overflow-hidden transition-all duration-200;
width: 200%;
&.register {
transform: translateX(-50%);
}
}
}

View File

@@ -0,0 +1,36 @@
import classNames from 'classnames'
import React, { useState } from 'react'
import ModalCommon from '../ModalCommon/ModalCommon'
import FormLogin from './components/FormLogin/FormLogin'
import FormRegister from './components/FormRegister/FormRegister'
import s from './ModalAuthenticate.module.scss'
interface Props {
visible: boolean,
closeModal: () => void,
}
const ModalAuthenticate = ({ visible, closeModal }: Props) => {
const [isLogin, setIsLogin] = useState<boolean>(true)
const onSwitch = () => {
setIsLogin(!isLogin)
}
return (
<ModalCommon visible={visible} onClose={closeModal} title={isLogin ? 'Sign In' : 'Create Account'}>
<section className={s.formAuthenticate}>
<div className={classNames({
[s.inner]: true,
[s.register]: !isLogin,
})}>
<FormLogin isHide={!isLogin} onSwitch={onSwitch} />
<FormRegister isHide={isLogin} onSwitch={onSwitch} />
</div>
</section>
</ModalCommon>
)
}
export default ModalAuthenticate

View File

@@ -0,0 +1,35 @@
.formAuthen {
@apply bg-white w-full;
.inner {
@screen md {
max-width: 52rem;
margin: auto;
}
.body {
> div {
&:not(:last-child) {
margin-bottom: 1.6rem;
}
}
}
.others {
@apply font-bold text-center;
margin-top: 4rem;
span {
@apply text-active;
margin-right: 0.8rem;
}
button {
all: unset;
@apply text-primary cursor-pointer;
&:focus-visible {
outline: 2px solid #000;
}
&:focus {
outline: none;
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
.bottom {
@apply flex justify-between items-center;
margin: 4rem auto;
.forgotPassword {
@apply font-bold;
color: var(--primary);
}
}

View File

@@ -0,0 +1,53 @@
import Link from 'next/link'
import React, { useRef, useEffect } from 'react'
import { Inputcommon, ButtonCommon } from 'src/components/common'
import { ROUTE } from 'src/utils/constanst.utils'
import SocialAuthen from '../SocialAuthen/SocialAuthen'
import s from '../FormAuthen.module.scss'
import styles from './FormLogin.module.scss'
import classNames from 'classnames'
import { CustomInputCommon } from 'src/utils/type.utils'
interface Props {
isHide: boolean,
onSwitch: () => void
}
const FormLogin = ({ onSwitch, isHide }: Props) => {
const emailRef = useRef<CustomInputCommon>(null)
useEffect(() => {
if (!isHide) {
emailRef.current?.focus()
}
}, [isHide])
return (
<section className={classNames({
[s.formAuthen]: true,
// [styles.hide]: isHide
})}>
<div className={s.inner}>
<div className={s.body}>
<Inputcommon placeholder='Email Address' type='email' ref={emailRef} />
<Inputcommon placeholder='Password' type='password' />
</div>
<div className={styles.bottom}>
<Link href={ROUTE.FORGOT_PASSWORD}>
<a href="" className={styles.forgotPassword}>
Forgot Password?
</a>
</Link>
<ButtonCommon size='large'>Sign in</ButtonCommon>
</div>
<SocialAuthen />
<div className={s.others}>
<span>Don't have an account?</span>
<button onClick={onSwitch}>Create Account</button>
</div>
</div>
</section>
)
}
export default FormLogin

View File

@@ -0,0 +1,15 @@
@import "../../../../../styles/utilities";
.formRegister {
.passwordNote {
@apply text-center caption text-label;
margin-top: 0.8rem;
}
.bottom {
@apply flex justify-between items-center w-full;
margin: 4rem auto;
button {
@apply w-full;
}
}
}

View File

@@ -0,0 +1,50 @@
import React, { useEffect, useRef } from 'react'
import { ButtonCommon, Inputcommon } from 'src/components/common'
import s from '../FormAuthen.module.scss'
import styles from './FormRegister.module.scss'
import SocialAuthen from '../SocialAuthen/SocialAuthen'
import classNames from 'classnames'
import { CustomInputCommon } from 'src/utils/type.utils'
interface Props {
isHide: boolean,
onSwitch: () => void
}
const FormRegister = ({ onSwitch, isHide }: Props) => {
const emailRef = useRef<CustomInputCommon>(null)
useEffect(() => {
if (!isHide) {
emailRef.current?.focus()
}
}, [isHide])
return (
<section className={classNames({
[s.formAuthen]: true,
[styles.formRegister]: true,
// [styles.hide]: isHide
})}>
<div className={s.inner}>
<div className={s.body}>
<Inputcommon placeholder='Email Address' type='email' ref={emailRef}/>
<Inputcommon placeholder='Password' type='password' />
<div className={styles.passwordNote}>
Must contain 8 characters with at least 1 uppercase and 1 lowercase letter and either 1 number or 1 special character.
</div>
</div>
<div className={styles.bottom}>
<ButtonCommon size='large'>Create Account</ButtonCommon>
</div>
<SocialAuthen />
<div className={s.others}>
<span>Already an account?</span>
<button onClick={onSwitch}>Sign In</button>
</div>
</div>
</section>
)
}
export default FormRegister

View File

@@ -0,0 +1,36 @@
@import "../../../../../styles/utilities";
.socialAuthen {
.captionText {
@apply relative text-center;
margin-bottom: 4rem;
span {
@apply relative bg-white uppercase text-label caption;
padding: 0 0.8rem;
z-index: 10;
}
&::after {
@apply absolute bg-line;
content: "";
width: 100%;
height: 1px;
top: 50%;
left: 0;
transform: translateY(-50%);
}
}
.btns {
@apply grid grid-cols-3;
grid-gap: 1.6rem;
.buttonWithIcon {
@apply flex items-center;
.label {
@apply hidden;
@screen md {
@apply inline-block;
margin-left: 0.8rem;
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
import React from 'react'
import ButtonCommon from 'src/components/common/ButtonCommon/ButtonCommon'
import { IconApple, IconFacebookColor, IconGoogleColor } from 'src/components/icons'
import s from './SocialAuthen.module.scss'
const SocialAuthen = () => {
return (
<section className={s.socialAuthen}>
<div className={s.captionText}>
<span>
OR CONTINUE WITH
</span>
</div>
<div className={s.btns}>
<ButtonCommon type='light' size='large'>
<span className={s.buttonWithIcon}>
<IconFacebookColor /><span className={s.label}>Facebook</span>
</span>
</ButtonCommon>
<ButtonCommon type='light' size='large'>
<span className={s.buttonWithIcon}>
<IconApple />
<span className={s.label}>Apple</span>
</span>
</ButtonCommon>
<ButtonCommon type='light' size='large'>
<span className={s.buttonWithIcon}>
<IconGoogleColor />
<span className={s.label}>Google</span>
</span>
</ButtonCommon>
</div>
</section>
)
}
export default SocialAuthen

View File

@@ -0,0 +1,32 @@
@import '../../../styles/utilities';
.background{
@apply fixed inset-0 overflow-y-auto;
background: rgba(20, 20, 20, 0.65);
z-index: 10000;
.warpper{
@apply flex justify-center items-center min-h-screen;
.modal{
@apply inline-block align-bottom bg-white relative;
max-width: 60rem;
padding: 3.2rem;
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.24);
border-radius: 1.2rem;
.top {
margin-bottom: 4rem;
}
.title{
@apply font-heading heading-3;
padding: 0 0.8rem 0 0.8rem;
}
.close{
@apply absolute;
&:hover{
cursor: pointer;
}
top:4.4rem;
right: 4.4rem;
}
}
}
}

View File

@@ -0,0 +1,40 @@
import React, { useRef } from 'react'
import { Close } from 'src/components/icons'
import { useOnClickOutside } from 'src/utils/useClickOutSide'
import s from './ModalCommon.module.scss'
interface Props {
onClose: () => void
visible: boolean
children: React.ReactNode
title?: string
maxWidth?:string
}
const ModalCommon = ({ onClose, visible, children, title="Modal",maxWidth }: Props) => {
const modalRef = useRef<HTMLDivElement>(null)
const clickOutSide = () => {
onClose && onClose()
}
useOnClickOutside(modalRef, clickOutSide)
return (
<>
{visible && (
<div className={s.background}>
<div className={s.warpper}>
<div className={s.modal} ref={modalRef} style={{maxWidth}}>
<div className={s.top}>
<div className={s.title}>{title}</div>
<div className={s.close} onClick={clickOutSide}>
<Close />
</div>
</div>
{children}
</div>
</div>
</div>
)}
</>
)
}
export default ModalCommon

View File

@@ -0,0 +1,9 @@
@import "../../../styles/utilities";
.notiMessage {
@apply caption bg-info-light;
width: fit-content;
color: var(--info-dark);
padding: 0.4rem 1.6rem;
border-radius: 3rem;
}

View File

@@ -0,0 +1,16 @@
import React from 'react'
import s from './NotiMessage.module.scss'
interface Props {
children?: React.ReactNode
}
const NotiMessage = ({ children }: Props) => {
return (
<div className={s.notiMessage}>
{children}
</div>
)
}
export default NotiMessage

View File

@@ -0,0 +1,63 @@
.productCardWarpper{
max-width: 20.8rem;
min-height: 31.8rem;
padding: 1.2rem 1.2rem 0 1.2rem;
margin-bottom: 1px;
@apply flex flex-col justify-between;
.cardTop{
@apply relative;
height: 13.8rem;
width: 100%;
.productImage{
height: 100%;
width: 100%;
@apply flex justify-center items-center;
img{
@apply inline;
}
&:hover{
cursor: pointer;
}
}
.productLabel{
@apply absolute left-0 bottom-0;
}
}
.cardMid{
min-height: 10.4rem;
@apply flex flex-col justify-between;
.cardMidTop{
.productname{
font-weight: bold;
line-height: 2.4rem;
font-size: 1.6rem;
color: var(--text-active);
&:hover{
cursor: pointer;
}
}
.productWeight{
font-size: 1.2rem;
line-height: 2rem;
letter-spacing: 0.01em;
color: var(--text-base);
}
}
.cardMidBot{
padding-top: 0.8rem;
@apply flex justify-between items-center border-t border-solid border-line;
.productPrice{
@apply font-bold;
font-size: 2rem;
line-height: 2.8rem;
letter-spacing: -0.01em;
}
}
}
.cardBot{
min-height: 4rem;
@apply flex justify-between items-center;
.cardButton{
}
}
}

View File

@@ -0,0 +1,60 @@
import Link from 'next/link'
import React from 'react'
import { ProductProps } from 'src/utils/types.utils'
import ButtonCommon from '../ButtonCommon/ButtonCommon'
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
import ItemWishList from '../ItemWishList/ItemWishList'
import LabelCommon from '../LabelCommon/LabelCommon'
import s from './ProductCard.module.scss'
export interface ProductCardProps extends ProductProps {
buttonText?: string
}
const ProductCard = ({
category,
name,
weight,
price,
buttonText = 'Buy Now',
imageSrc,
}: ProductCardProps) => {
return (
<div className={s.productCardWarpper}>
<div className={s.cardTop}>
<Link href="#">
<div className={s.productImage}>
<img src={imageSrc} alt="image" />
</div>
</Link>
<div className={s.productLabel}>
<LabelCommon shape="half">{category}</LabelCommon>
</div>
</div>
<div className={s.cardMid}>
<div className={s.cardMidTop}>
<Link href="#">
<div className={s.productname}>{name} </div>
</Link>
<div className={s.productWeight}>{weight}</div>
</div>
<div className={s.cardMidBot}>
<div className={s.productPrice}>{price}</div>
<div className={s.wishList}>
<ItemWishList />
</div>
</div>
</div>
<div className={s.cardBot}>
<div className={s.cardIcon}>
<ButtonIconBuy />
</div>
<div className={s.cardButton}>
<ButtonCommon type="light">{buttonText}</ButtonCommon>
</div>
</div>
</div>
)
}
export default ProductCard

View File

@@ -0,0 +1,16 @@
@import '../../../styles/utilities';
.productCardWarpper {
@apply spacing-horizontal;
@screen xl {
:global(.customArrow) {
@screen lg {
&:global(.leftArrow) {
left: calc(-6.4rem - 2rem);
}
&:global(.rightArrow) {
right: calc(-6.4rem - 2rem);
}
}
}
}
}

View File

@@ -0,0 +1,44 @@
import { TOptionsEvents } from 'keen-slider'
import React from 'react'
import CarouselCommon, {
CarouselCommonProps,
} from '../CarouselCommon/CarouselCommon'
import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard'
import s from "./ProductCarousel.module.scss"
interface ProductCarouselProps
extends Omit<CarouselCommonProps<ProductCardProps>, 'Component'|"option"> {
option?:TOptionsEvents
}
const OPTION_DEFAULT: TOptionsEvents = {
slidesPerView: 2,
mode: 'free',
breakpoints: {
'(min-width: 640px)': {
slidesPerView: 3,
},
'(min-width: 768px)': {
slidesPerView: 4,
},
'(min-width: 1024px)': {
slidesPerView: 4.5,
},'(min-width: 1280px)': {
slidesPerView: 5.5,
},
},
}
const ProductCarousel = ({ option, data, ...props }: ProductCarouselProps) => {
return (
<div className={s.productCardWarpper}>
<CarouselCommon<ProductCardProps>
data={data}
Component={ProductCard}
{...props}
option={{ ...OPTION_DEFAULT, ...option }}
/>
</div>
)
}
export default ProductCarousel

View File

@@ -0,0 +1,39 @@
@import '../../../styles/utilities';
.quanittyInputWarper{
border-color: theme("textColor.active");
@apply border border-solid inline-flex justify-between items-center custom-border-radius;
.plusIcon, .minusIcon{
&:hover{
cursor: pointer;
}
}
&.default{
max-width: 18.4rem;
min-height: 4rem;
.plusIcon, .minusIcon{
margin: 0.8rem;
width: 2.4rem;
height: 2.4rem;
}
}
&.small{
max-width: 10rem;
min-height: 2.8rem;
.plusIcon, .minusIcon{
margin: 0 0.6rem;
// width: 1rem;
// height: 1rem;
}
}
.quanittyInput{
@apply bg-background outline-none w-1/2 text-center h-full font-bold;
font-size: 20px;
line-height: 28px;
color: theme("textColor.active");
&::-webkit-inner-spin-button, &::-webkit-inner-spin-button{
-webkit-appearance: none;
margin: 0;
}
}
}

View File

@@ -0,0 +1,83 @@
import React, { ChangeEvent, useEffect, useState } from 'react'
import s from './QuanittyInput.module.scss'
import classNames from 'classnames'
import { Minus, Plus } from '@components/icons'
interface QuanittyInputProps
extends Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'onChange' | 'min' | 'max' | 'step' | "type" | "size"
> {
size?: 'default' | 'small'
onChange?: (value: number) => void
initValue?: number
min?: number
max?: number
step?: number
}
const QuanittyInput = ({
size = 'default',
onChange,
initValue = 0,
min,
max,
step = 1,
...props
}: QuanittyInputProps) => {
const [value, setValue] = useState<number>(0)
useEffect(() => {
onChange && onChange(value)
}, [value])
useEffect(() => {
initValue && setValue(initValue)
}, [initValue])
const onPlusClick = () => {
if (max && value + step > max) {
setValue(max)
} else {
setValue(value + step)
}
}
const onMinusClick = () => {
if (min && value - step < min) {
setValue(min)
} else {
setValue(value - step)
}
}
const onValueChange = (e: ChangeEvent<HTMLInputElement>) => {
let value = Number(e.target.value) || 0
if (min && value < min) {
setValue(min)
} else if (max && value > max) {
setValue(max)
} else {
setValue(value)
}
}
return (
<div className={classNames(s.quanittyInputWarper, { [s[size]]: size })}>
<div className={s.minusIcon} onClick={onMinusClick}>
<Minus />
</div>
<input
{...props}
type="number"
value={value}
onChange={onValueChange}
className={s.quanittyInput}
/>
<div className={s.plusIcon} onClick={onPlusClick}>
<Plus />
</div>
</div>
)
}
export default QuanittyInput

View File

@@ -0,0 +1,31 @@
.recipeCardWarpper{
max-width: 39.2rem;
min-height: 34rem;
@apply inline-flex flex-col justify-start;
.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 ;
display: -webkit-box;
-webkit-line-clamp: 3; /* number of lines to show */
-webkit-box-orient: vertical;
}
}

View File

@@ -0,0 +1,23 @@
import Link from 'next/link'
import React from 'react'
import { RecipeProps } from 'src/utils/types.utils'
import s from './RecipeCard.module.scss'
export interface RecipeCardProps extends RecipeProps {}
const RecipeCard = ({ imageSrc, title, description }: RecipeCardProps) => {
return (
<div className={s.recipeCardWarpper}>
<Link href="#">
<div className={s.image}>
<img src={imageSrc} alt="image recipe" />
</div>
</Link>
<Link href="#">
<div className={s.title}>{title}</div>
</Link>
<div className={s.description}>{description}</div>
</div>
)
}
export default RecipeCard

View File

@@ -0,0 +1,16 @@
@import '../../../styles/utilities';
.recipeCardWarpper {
@apply spacing-horizontal;
@screen xl {
:global(.customArrow) {
@screen lg {
&:global(.leftArrow) {
left: calc(-6.4rem - 2rem);
}
&:global(.rightArrow) {
right: calc(-6.4rem - 2rem);
}
}
}
}
}

View File

@@ -0,0 +1,46 @@
import { TOptionsEvents } from 'keen-slider'
import React from 'react'
import CarouselCommon, {
CarouselCommonProps,
} from '../CarouselCommon/CarouselCommon'
import RecipeCard, { RecipeCardProps } from '../RecipeCard/RecipeCard'
import s from "./RecipeCarousel.module.scss"
interface RecipeCarouselProps
extends Omit<CarouselCommonProps<RecipeCardProps>, '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 RecipeCarousel = ({ option, data, ...props }: RecipeCarouselProps) => {
return (
<div className={s.recipeCardWarpper}>
<CarouselCommon<RecipeCardProps>
data={data}
Component={RecipeCard}
{...props}
option={{ ...OPTION_DEFAULT, ...option }}
/>
</div>
)
}
export default RecipeCarousel

View File

@@ -0,0 +1,15 @@
import React, { MutableRefObject } from 'react'
interface ScrollTargetProps {
refScrollUp: MutableRefObject<HTMLDivElement>;
}
const ScrollTarget = ({ refScrollUp } : ScrollTargetProps) => {
return (
<div ref={refScrollUp}></div>
)
}
export default ScrollTarget

View File

@@ -0,0 +1,24 @@
@import '../../../styles/utilities';
.scrollToTop {
@apply hidden;
@screen md {
&.show {
@apply block rounded-lg fixed cursor-pointer;
right: 11.2rem;
bottom: 21.6rem;
width: 6.4rem;
height: 6.4rem;
background-color: var(--border-line);
}
&.hide {
@apply hidden;
}
}
.scrollToTopBtn {
@apply outline-none w-full h-full;
}
}

View File

@@ -0,0 +1,54 @@
import React, { useState, useEffect, MutableRefObject } from 'react'
import classNames from 'classnames'
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 [scrollPosition, setSrollPosition] = useState(0);
const [showScrollToTop, setShowScrollToTop] = useState("hide");
function handleVisibleButton() {
const position = window.pageYOffset;
setSrollPosition(position);
if (scrollPosition > visibilityHeight) {
return setShowScrollToTop("show")
} else if (scrollPosition < visibilityHeight) {
return setShowScrollToTop("hide");
}
};
function handleScrollUp() {
target.current.scrollIntoView({ behavior: "smooth" });
}
function addEventScroll() {
window.addEventListener("scroll", handleVisibleButton);
}
useEffect(() => {
addEventScroll()
});
return (
<div className={classNames(s.scrollToTop, {
[s[`${showScrollToTop}`]]: showScrollToTop
})}
onClick={handleScrollUp}
>
<button className={s.scrollToTopBtn}>
<ArrowUp />
</button>
</div>
)
}
export default ScrollToTop

View File

@@ -0,0 +1,19 @@
import React from 'react';
import ReactPlayer from 'react-player/lazy'
interface Props {
url: string,
controls?: boolean,
muted?: boolean,
}
const VideoPlayer = ({ url, controls, muted }: Props) => {
return (
<ReactPlayer
url={url}
controls={controls}
muted={muted} />
);
};
export default VideoPlayer;

View File

@@ -1,18 +1,16 @@
@import '../../../styles/utilities';
@import "../../../styles/utilities";
.viewAll{
.viewAll {
display: flex;
color: theme("colors.primary");
.content{
.content {
color: var(--primary);
margin: 0.8rem 0.8rem 0.8rem 1.6rem;
font-weight: bold;
}
.vector{
.vector {
margin: 0.8rem 0rem 0.8rem 0rem;
svg path{
fill: theme("colors.primary");
svg path {
fill: var(--primary);
}
}
}

View File

@@ -1,7 +1,30 @@
export { default as ButtonCommon } from './ButtonCommon/ButtonCommon'
export { default as Layout } from './Layout/Layout'
export { default as CarouselCommon } from './CarouselCommon/CarouselCommon'
export { default as QuanittyInput } from './QuanittyInput/QuanittyInput'
export { default as LabelCommon } from './LabelCommon/LabelCommon'
export { default as ProductCard } from './ProductCard/ProductCard'
export { default as ProductCarousel } from './ProductCarousel/ProductCarousel'
export { default as FeaturedProductCard } from './FeaturedProductCard/FeaturedProductCard'
export { default as RecipeCard } from './RecipeCard/RecipeCard'
export { default as Head } from './Head/Head'
export { default as ViewAllItem} from './ViewAllItem/ViewAllItem'
export { default as ItemWishList} from './ItemWishList/ItemWishList'
export { default as Logo} from './Logo/Logo'
export { default as SelectCommon} from './SelectCommon/SelectCommon'
export { default as Inputcommon} from './InputCommon/InputCommon'
export { default as CheckboxCommon} from './CheckboxCommon/CheckboxCommon'
export { default as Author} from './Author/Author'
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'
export { default as Footer} from './Footer/Footer'
export { default as MenuDropdown} from './MenuDropdown/MenuDropdown'
export { default as NotiMessage} from './NotiMessage/NotiMessage'
export { default as VideoPlayer} from './VideoPlayer/VideoPlayer'
export { default as SelectCommon} from './SelectCommon/SelectCommon'
export { default as ModalCommon} from './ModalCommon/ModalCommon'

View File

@@ -0,0 +1,23 @@
import { useState } from 'react';
interface Props {
initialValue?: boolean,
}
export const useModalCommon = ({ initialValue = false }: Props) => {
const [visible, setVisible] = useState<boolean>(initialValue)
const openModal = (e?: any) => {
e && e.stopPropagation()
setVisible(true)
}
const closeModal = (e?: any) => {
e && e.stopPropagation()
setVisible(false)
}
return {
visible, openModal, closeModal
}
};

View File

@@ -0,0 +1,18 @@
const ArrowLeft = ({ ...props }) => {
return (
<svg
width="12"
height="18"
viewBox="0 0 12 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.81637 8.99983L10.7164 3.09983C11.0268 2.78756 11.201 2.36514 11.201 1.92483C11.201 1.48452 11.0268 1.0621 10.7164 0.749833C10.5614 0.593619 10.3771 0.469628 10.174 0.385014C9.9709 0.300399 9.75306 0.256836 9.53304 0.256836C9.31302 0.256836 9.09517 0.300399 8.89207 0.385014C8.68898 0.469628 8.50464 0.593619 8.3497 0.749833L1.28304 7.8165C1.12682 7.97144 1.00283 8.15577 0.918218 8.35887C0.833603 8.56197 0.790039 8.77981 0.790039 8.99983C0.790039 9.21985 0.833603 9.43769 0.918218 9.64079C1.00283 9.84389 1.12682 10.0282 1.28304 10.1832L8.3497 17.3332C8.50544 17.4876 8.69013 17.6098 8.89319 17.6928C9.09626 17.7757 9.31369 17.8178 9.53304 17.8165C9.75238 17.8178 9.96982 17.7757 10.1729 17.6928C10.3759 17.6098 10.5606 17.4876 10.7164 17.3332C11.0268 17.0209 11.201 16.5985 11.201 16.1582C11.201 15.7179 11.0268 15.2954 10.7164 14.9832L4.81637 8.99983Z"
fill="#141414"
/>
</svg>
)
}
export default ArrowLeft

View File

@@ -0,0 +1,18 @@
const ArrowRight = ({ ...props }) => {
return (
<svg
width="12"
height="18"
viewBox="0 0 12 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.7168 7.8165L3.65015 0.749833C3.49521 0.593619 3.31088 0.469628 3.10778 0.385014C2.90468 0.300399 2.68684 0.256836 2.46682 0.256836C2.2468 0.256836 2.02895 0.300399 1.82585 0.385014C1.62276 0.469628 1.43842 0.593619 1.28348 0.749833C0.973064 1.0621 0.798828 1.48452 0.798828 1.92483C0.798828 2.36514 0.973064 2.78756 1.28348 3.09983L7.18348 8.99983L1.28348 14.8998C0.973064 15.2121 0.798828 15.6345 0.798828 16.0748C0.798828 16.5151 0.973064 16.9376 1.28348 17.2498C1.43922 17.4043 1.62391 17.5265 1.82697 17.6095C2.03003 17.6924 2.24747 17.7344 2.46682 17.7332C2.68616 17.7344 2.9036 17.6924 3.10666 17.6095C3.30972 17.5265 3.49442 17.4043 3.65015 17.2498L10.7168 10.1832C10.873 10.0282 10.997 9.84389 11.0816 9.64079C11.1662 9.4377 11.2098 9.21985 11.2098 8.99983C11.2098 8.77981 11.1662 8.56197 11.0816 8.35887C10.997 8.15577 10.873 7.97144 10.7168 7.8165Z"
fill="#141414"
/>
</svg>
)
}
export default ArrowRight

View File

@@ -0,0 +1,22 @@
import React from 'react'
interface Props {}
const Close = (props: Props) => {
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 Close

View File

@@ -0,0 +1,20 @@
const Vector = ({ ...props }) => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M20.16 4.99992C19.1 3.93713 17.6948 3.28846 16.1984 3.17109C14.7019 3.05372 13.2128 3.47539 12 4.35992C10.7277 3.41356 9.14402 2.98443 7.56795 3.15896C5.99188 3.33348 4.54047 4.0987 3.506 5.30051C2.47154 6.50231 1.93085 8.05144 1.99283 9.63594C2.05481 11.2204 2.71485 12.7226 3.84003 13.8399L10.05 20.0599C10.57 20.5717 11.2704 20.8585 12 20.8585C12.7296 20.8585 13.43 20.5717 13.95 20.0599L20.16 13.8399C21.3276 12.6652 21.983 11.0762 21.983 9.41992C21.983 7.76365 21.3276 6.17465 20.16 4.99992ZM18.75 12.4599L12.54 18.6699C12.4694 18.7413 12.3853 18.7979 12.2926 18.8366C12.1999 18.8752 12.1005 18.8951 12 18.8951C11.8996 18.8951 11.8002 18.8752 11.7075 18.8366C11.6148 18.7979 11.5307 18.7413 11.46 18.6699L5.25003 12.4299C4.46579 11.6283 4.02664 10.5514 4.02664 9.42992C4.02664 8.30846 4.46579 7.23158 5.25003 6.42992C6.04919 5.64091 7.127 5.19849 8.25003 5.19849C9.37306 5.19849 10.4509 5.64091 11.25 6.42992C11.343 6.52365 11.4536 6.59804 11.5755 6.64881C11.6973 6.69958 11.828 6.72572 11.96 6.72572C12.092 6.72572 12.2227 6.69958 12.3446 6.64881C12.4665 6.59804 12.5771 6.52365 12.67 6.42992C13.4692 5.64091 14.547 5.19849 15.67 5.19849C16.7931 5.19849 17.8709 5.64091 18.67 6.42992C19.4651 7.22107 19.9186 8.29211 19.9336 9.41361C19.9485 10.5351 19.5237 11.6179 18.75 12.4299V12.4599Z"
fill={props.color}
/>
</svg>
)
}
export default Vector

View File

@@ -0,0 +1,18 @@
import React from 'react'
const IconApple = () => {
return (
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0)">
<path d="M22.292 18.7033C21.929 19.5418 21.4994 20.3136 21.0016 21.0232C20.3231 21.9906 19.7676 22.6602 19.3395 23.032C18.6758 23.6424 17.9647 23.955 17.2032 23.9728C16.6566 23.9728 15.9973 23.8172 15.23 23.5017C14.4601 23.1876 13.7525 23.032 13.1056 23.032C12.4271 23.032 11.6994 23.1876 10.9211 23.5017C10.1415 23.8172 9.51355 23.9817 9.03342 23.9979C8.30322 24.0291 7.57539 23.7076 6.8489 23.032C6.38521 22.6276 5.80523 21.9343 5.11043 20.9521C4.36498 19.9033 3.75211 18.687 3.27198 17.3004C2.75777 15.8026 2.5 14.3523 2.5 12.9482C2.5 11.3398 2.84754 9.95259 3.54367 8.79011C4.09076 7.85636 4.81859 7.11979 5.72953 6.57906C6.64046 6.03834 7.62473 5.76279 8.68469 5.74516C9.26467 5.74516 10.0252 5.92457 10.9704 6.27715C11.9129 6.63091 12.5181 6.81032 12.7834 6.81032C12.9817 6.81032 13.654 6.60054 14.7937 6.18233C15.8714 5.79449 16.781 5.63391 17.5262 5.69716C19.5454 5.86012 21.0624 6.6561 22.0712 8.09013C20.2654 9.18432 19.3721 10.7169 19.3898 12.6829C19.4061 14.2142 19.9617 15.4886 21.0535 16.5004C21.5483 16.97 22.1009 17.333 22.7156 17.5907C22.5823 17.9774 22.4416 18.3477 22.292 18.7033ZM17.661 0.480137C17.661 1.68041 17.2225 2.8011 16.3484 3.8384C15.2937 5.07155 14.0179 5.78412 12.6343 5.67168C12.6167 5.52769 12.6065 5.37614 12.6065 5.21688C12.6065 4.06462 13.1081 2.83147 13.9989 1.82321C14.4436 1.3127 15.0092 0.888228 15.6951 0.549615C16.3796 0.216055 17.0269 0.031589 17.6358 0C17.6536 0.160458 17.661 0.320926 17.661 0.480121V0.480137Z" fill="black" />
</g>
<defs>
<clipPath id="clip0">
<rect width="24" height="24" fill="white" transform="translate(0.5)" />
</clipPath>
</defs>
</svg>
)
}
export default IconApple

View File

@@ -0,0 +1,11 @@
import React from 'react'
const IconArrowRight = () => {
return (
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.92 6.62019C11.8724 6.49743 11.801 6.38529 11.71 6.29019L6.71 1.29019C6.61676 1.19695 6.50607 1.12299 6.38425 1.07253C6.26243 1.02207 6.13186 0.996094 6 0.996094C5.7337 0.996094 5.4783 1.10188 5.29 1.29019C5.19676 1.38342 5.1228 1.49411 5.07234 1.61594C5.02188 1.73776 4.99591 1.86833 4.99591 2.00019C4.99591 2.26649 5.1017 2.52188 5.29 2.71019L8.59 6.00019H1C0.734784 6.00019 0.48043 6.10554 0.292893 6.29308C0.105357 6.48062 0 6.73497 0 7.00019C0 7.2654 0.105357 7.51976 0.292893 7.70729C0.48043 7.89483 0.734784 8.00019 1 8.00019H8.59L5.29 11.2902C5.19627 11.3831 5.12188 11.4937 5.07111 11.6156C5.02034 11.7375 4.9942 11.8682 4.9942 12.0002C4.9942 12.1322 5.02034 12.2629 5.07111 12.3848C5.12188 12.5066 5.19627 12.6172 5.29 12.7102C5.38296 12.8039 5.49356 12.8783 5.61542 12.9291C5.73728 12.9798 5.86799 13.006 6 13.006C6.13201 13.006 6.26272 12.9798 6.38458 12.9291C6.50644 12.8783 6.61704 12.8039 6.71 12.7102L11.71 7.71019C11.801 7.61508 11.8724 7.50294 11.92 7.38019C12.02 7.13672 12.02 6.86365 11.92 6.62019Z" fill="white" />
</svg>
)
}
export default IconArrowRight

View File

@@ -0,0 +1,10 @@
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"/>
</svg>
)
}
export default ArrowUp

View File

@@ -0,0 +1,11 @@
import React from 'react'
const IconFacebook = () => {
return (
<svg width="11" height="20" viewBox="0 0 11 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.12035 3.32003H11.0004V0.14003C10.0901 0.045377 9.17551 -0.00135428 8.26035 2.98641e-05C5.54035 2.98641e-05 3.68035 1.66003 3.68035 4.70003V7.32003H0.610352V10.88H3.68035V20H7.36035V10.88H10.4204L10.8804 7.32003H7.36035V5.05003C7.36035 4.00003 7.64035 3.32003 9.12035 3.32003Z" fill="#141414" />
</svg>
)
}
export default IconFacebook

View File

@@ -0,0 +1,19 @@
import React from 'react'
const IconFacebookColor = () => {
return (
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0)">
<path d="M24.5 12C24.5 5.37258 19.1274 0 12.5 0C5.87258 0 0.5 5.37258 0.5 12C0.5 17.9895 4.8882 22.954 10.625 23.8542V15.4688H7.57812V12H10.625V9.35625C10.625 6.34875 12.4166 4.6875 15.1576 4.6875C16.4701 4.6875 17.8438 4.92188 17.8438 4.92188V7.875H16.3306C14.84 7.875 14.375 8.80008 14.375 9.75V12H17.7031L17.1711 15.4688H14.375V23.8542C20.1118 22.954 24.5 17.9895 24.5 12Z" fill="#1877F2" />
<path d="M17.1711 15.4688L17.7031 12H14.375V9.75C14.375 8.80102 14.84 7.875 16.3306 7.875H17.8438V4.92188C17.8438 4.92188 16.4705 4.6875 15.1576 4.6875C12.4166 4.6875 10.625 6.34875 10.625 9.35625V12H7.57812V15.4688H10.625V23.8542C11.8674 24.0486 13.1326 24.0486 14.375 23.8542V15.4688H17.1711Z" fill="white" />
</g>
<defs>
<clipPath id="clip0">
<rect width="24" height="24" fill="white" transform="translate(0.5)" />
</clipPath>
</defs>
</svg>
)
}
export default IconFacebookColor

View File

@@ -0,0 +1,21 @@
import React from 'react'
const IconGoogleColor = () => {
return (
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0)">
<path d="M24.2663 12.2767C24.2663 11.461 24.2001 10.6409 24.059 9.83838H12.7402V14.4594H19.222C18.953 15.9497 18.0888 17.2681 16.8233 18.1059V21.1042H20.6903C22.9611 19.0142 24.2663 15.9277 24.2663 12.2767Z" fill="#4285F4" />
<path d="M12.7391 24.0008C15.9756 24.0008 18.705 22.9382 20.6936 21.1039L16.8266 18.1055C15.7507 18.8375 14.3618 19.252 12.7435 19.252C9.61291 19.252 6.95849 17.1399 6.00607 14.3003H2.01562V17.3912C4.05274 21.4434 8.20192 24.0008 12.7391 24.0008Z" fill="#34A853" />
<path d="M6.00277 14.3002C5.50011 12.8099 5.50011 11.196 6.00277 9.70569V6.61475H2.01674C0.314734 10.0055 0.314734 14.0004 2.01674 17.3912L6.00277 14.3002Z" fill="#FBBC04" />
<path d="M12.7391 4.74966C14.4499 4.7232 16.1034 5.36697 17.3425 6.54867L20.7685 3.12262C18.5991 1.0855 15.7198 -0.034466 12.7391 0.000808666C8.20192 0.000808666 4.05274 2.55822 2.01562 6.61481L6.00166 9.70575C6.94967 6.86173 9.6085 4.74966 12.7391 4.74966Z" fill="#EA4335" />
</g>
<defs>
<clipPath id="clip0">
<rect width="24" height="24" fill="white" transform="translate(0.5)" />
</clipPath>
</defs>
</svg>
)
}
export default IconGoogleColor

View File

@@ -1,23 +1,11 @@
import React from 'react'
const IconHeart = ({ ...props }) => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M20.84 2.61C20.3292 2.099 19.7228 1.69365 19.0554 1.41708C18.3879 1.14052 17.6725 0.998175 16.95 0.998175C16.2275 0.998175 15.5121 1.14052 14.8446 1.41708C14.1772 1.69365 13.5708 2.099 13.06 2.61L12 3.67L10.94 2.61C9.9083 1.57831 8.50903 0.998709 7.05 0.998709C5.59096 0.998709 4.19169 1.57831 3.16 2.61C2.1283 3.64169 1.54871 5.04097 1.54871 6.5C1.54871 7.95903 2.1283 9.35831 3.16 10.39L4.22 11.45L12 19.23L19.78 11.45L20.84 10.39C21.351 9.87924 21.7563 9.27281 22.0329 8.60536C22.3095 7.9379 22.4518 7.22249 22.4518 6.5C22.4518 5.77751 22.3095 5.0621 22.0329 4.39464C21.7563 3.72719 21.351 3.12076 20.84 2.61V2.61Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
export default IconHeart
const IconHeart = () => {
return (
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.1598 2.61006C18.0981 1.54806 16.6905 0.902384 15.193 0.790439C13.6955 0.678494 12.2076 1.10772 10.9998 2.00006C9.72744 1.0537 8.14378 0.624569 6.56771 0.799095C4.99164 0.973621 3.54023 1.73884 2.50576 2.94064C1.47129 4.14245 0.93061 5.69158 0.992588 7.27607C1.05457 8.86057 1.71461 10.3627 2.83979 11.4801L10.2898 18.9301C10.3828 19.0238 10.4934 19.0982 10.6152 19.1489C10.7371 19.1997 10.8678 19.2259 10.9998 19.2259C11.1318 19.2259 11.2625 19.1997 11.3844 19.1489C11.5062 19.0982 11.6168 19.0238 11.7098 18.9301L19.1598 11.4801C19.7424 10.8978 20.2047 10.2064 20.52 9.44542C20.8354 8.68445 20.9977 7.86879 20.9977 7.04506C20.9977 6.22133 20.8354 5.40567 20.52 4.64469C20.2047 3.88371 19.7424 3.19233 19.1598 2.61006ZM17.7498 10.0701L10.9998 16.8101L4.24979 10.0701C3.65496 9.47276 3.24976 8.71311 3.08502 7.8864C2.92027 7.05969 3.00332 6.20274 3.32374 5.42305C3.64416 4.64336 4.18768 3.97565 4.88613 3.50368C5.58458 3.03171 6.40685 2.7765 7.24979 2.77006C8.3759 2.77282 9.45488 3.22239 10.2498 4.02006C10.3428 4.11379 10.4534 4.18818 10.5752 4.23895C10.6971 4.28972 10.8278 4.31586 10.9598 4.31586C11.0918 4.31586 11.2225 4.28972 11.3444 4.23895C11.4662 4.18818 11.5768 4.11379 11.6698 4.02006C12.4881 3.31094 13.5453 2.9392 14.6273 2.98008C15.7094 3.02096 16.7355 3.47141 17.498 4.24025C18.2605 5.00909 18.7024 6.0389 18.7343 7.12125C18.7662 8.2036 18.3857 9.25765 17.6698 10.0701H17.7498Z" fill="#141414" />
</svg>
)
}
export default IconHeart

View File

@@ -0,0 +1,11 @@
import React from 'react'
const IconHistory = () => {
return (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 0C7.43639 0.00731219 4.97349 0.998914 3.12 2.77V1C3.12 0.734784 3.01464 0.48043 2.82711 0.292893C2.63957 0.105357 2.38522 0 2.12 0C1.85478 0 1.60043 0.105357 1.41289 0.292893C1.22536 0.48043 1.12 0.734784 1.12 1V5.5C1.12 5.76522 1.22536 6.01957 1.41289 6.20711C1.60043 6.39464 1.85478 6.5 2.12 6.5H6.62C6.88522 6.5 7.13957 6.39464 7.32711 6.20711C7.51464 6.01957 7.62 5.76522 7.62 5.5C7.62 5.23478 7.51464 4.98043 7.32711 4.79289C7.13957 4.60536 6.88522 4.5 6.62 4.5H4.22C5.50578 3.15636 7.21951 2.30265 9.06653 2.08567C10.9136 1.86868 12.7785 2.30198 14.3407 3.31104C15.9028 4.32011 17.0646 5.84191 17.6263 7.61479C18.188 9.38767 18.1145 11.3009 17.4184 13.0254C16.7223 14.7499 15.4472 16.1781 13.8122 17.0643C12.1772 17.9505 10.2845 18.2394 8.45956 17.8813C6.63463 17.5232 4.99147 16.5405 3.81259 15.1022C2.63372 13.6638 1.99279 11.8597 2 10C2 9.73478 1.89464 9.48043 1.70711 9.29289C1.51957 9.10536 1.26522 9 1 9C0.734784 9 0.48043 9.10536 0.292893 9.29289C0.105357 9.48043 0 9.73478 0 10C0 11.9778 0.58649 13.9112 1.6853 15.5557C2.78412 17.2002 4.3459 18.4819 6.17317 19.2388C8.00043 19.9957 10.0111 20.1937 11.9509 19.8079C13.8907 19.422 15.6725 18.4696 17.0711 17.0711C18.4696 15.6725 19.422 13.8907 19.8079 11.9509C20.1937 10.0111 19.9957 8.00043 19.2388 6.17317C18.4819 4.3459 17.2002 2.78412 15.5557 1.6853C13.9112 0.58649 11.9778 0 10 0ZM10 6C9.73478 6 9.48043 6.10536 9.29289 6.29289C9.10536 6.48043 9 6.73478 9 7V10C9 10.2652 9.10536 10.5196 9.29289 10.7071C9.48043 10.8946 9.73478 11 10 11H12C12.2652 11 12.5196 10.8946 12.7071 10.7071C12.8946 10.5196 13 10.2652 13 10C13 9.73478 12.8946 9.48043 12.7071 9.29289C12.5196 9.10536 12.2652 9 12 9H11V7C11 6.73478 10.8946 6.48043 10.7071 6.29289C10.5196 6.10536 10.2652 6 10 6Z" fill="#5B9A74" />
</svg>
)
}
export default IconHistory

View File

@@ -0,0 +1,11 @@
import React from 'react'
const IconHome = () => {
return (
<svg width="18" height="21" viewBox="0 0 18 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.0001 7.00001L11.0001 1.74001C10.4501 1.24805 9.73803 0.976074 9.00009 0.976074C8.26216 0.976074 7.55012 1.24805 7.00009 1.74001L1.00009 7.00001C0.682463 7.28408 0.428995 7.63256 0.256567 8.02225C0.0841385 8.41194 -0.00329256 8.83389 9.47941e-05 9.26001V18C9.47941e-05 18.7957 0.316165 19.5587 0.878775 20.1213C1.44138 20.6839 2.20445 21 3.00009 21H15.0001C15.7957 21 16.5588 20.6839 17.1214 20.1213C17.684 19.5587 18.0001 18.7957 18.0001 18V9.25001C18.0021 8.82557 17.9139 8.40555 17.7416 8.01769C17.5692 7.62983 17.3165 7.28296 17.0001 7.00001ZM11.0001 19H7.00009V14C7.00009 13.7348 7.10545 13.4804 7.29299 13.2929C7.48052 13.1054 7.73488 13 8.00009 13H10.0001C10.2653 13 10.5197 13.1054 10.7072 13.2929C10.8947 13.4804 11.0001 13.7348 11.0001 14V19ZM16.0001 18C16.0001 18.2652 15.8947 18.5196 15.7072 18.7071C15.5197 18.8946 15.2653 19 15.0001 19H13.0001V14C13.0001 13.2044 12.684 12.4413 12.1214 11.8787C11.5588 11.3161 10.7957 11 10.0001 11H8.00009C7.20444 11 6.44138 11.3161 5.87877 11.8787C5.31616 12.4413 5.00009 13.2044 5.00009 14V19H3.00009C2.73488 19 2.48052 18.8946 2.29299 18.7071C2.10545 18.5196 2.00009 18.2652 2.00009 18V9.25001C2.00027 9.10802 2.03069 8.9677 2.08931 8.83839C2.14794 8.70907 2.23343 8.59372 2.3401 8.50001L8.34009 3.25001C8.52258 3.08969 8.75719 3.00127 9.00009 3.00127C9.243 3.00127 9.47761 3.08969 9.66009 3.25001L15.6601 8.50001C15.7668 8.59372 15.8523 8.70907 15.9109 8.83839C15.9695 8.9677 15.9999 9.10802 16.0001 9.25001V18Z" fill="#141414" />
</svg>
)
}
export default IconHome

View File

@@ -0,0 +1,11 @@
import React from 'react'
const IconInfo = () => {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.99968 6.3335C6.82287 6.3335 6.6533 6.40373 6.52827 6.52876C6.40325 6.65378 6.33301 6.82335 6.33301 7.00016V9.66683C6.33301 9.84364 6.40325 10.0132 6.52827 10.1382C6.6533 10.2633 6.82287 10.3335 6.99968 10.3335C7.17649 10.3335 7.34606 10.2633 7.47108 10.1382C7.59611 10.0132 7.66634 9.84364 7.66634 9.66683V7.00016C7.66634 6.82335 7.59611 6.65378 7.47108 6.52876C7.34606 6.40373 7.17649 6.3335 6.99968 6.3335ZM7.25301 3.72016C7.0907 3.65348 6.90865 3.65348 6.74634 3.72016C6.66451 3.75189 6.58975 3.79947 6.52634 3.86016C6.46746 3.92496 6.4201 3.99937 6.38634 4.08016C6.34902 4.15928 6.33076 4.24605 6.33301 4.3335C6.3325 4.42123 6.34932 4.50821 6.3825 4.58943C6.41567 4.67066 6.46456 4.74454 6.52634 4.80683C6.59114 4.86572 6.66555 4.91307 6.74634 4.94683C6.84734 4.98832 6.95699 5.00437 7.06564 4.99357C7.1743 4.98277 7.27863 4.94544 7.36949 4.88488C7.46034 4.82431 7.53492 4.74235 7.58668 4.64621C7.63845 4.55007 7.6658 4.44269 7.66634 4.3335C7.66389 4.15698 7.59483 3.98792 7.47301 3.86016C7.40961 3.79947 7.33485 3.75189 7.25301 3.72016ZM6.99968 0.333496C5.68114 0.333496 4.3922 0.724489 3.29588 1.45703C2.19955 2.18957 1.34506 3.23077 0.840481 4.44894C0.335896 5.66711 0.203874 7.00756 0.461109 8.30077C0.718344 9.59397 1.35328 10.7819 2.28563 11.7142C3.21798 12.6466 4.40587 13.2815 5.69908 13.5387C6.99228 13.796 8.33273 13.6639 9.5509 13.1594C10.7691 12.6548 11.8103 11.8003 12.5428 10.704C13.2753 9.60764 13.6663 8.31871 13.6663 7.00016C13.6663 6.12468 13.4939 5.25778 13.1589 4.44894C12.8238 3.6401 12.3328 2.90517 11.7137 2.28612C11.0947 1.66706 10.3597 1.176 9.5509 0.840966C8.74206 0.505935 7.87516 0.333496 6.99968 0.333496ZM6.99968 12.3335C5.94484 12.3335 4.9137 12.0207 4.03664 11.4347C3.15957 10.8486 2.47599 10.0157 2.07232 9.04114C1.66865 8.0666 1.56303 6.99425 1.76882 5.95968C1.97461 4.92512 2.48256 3.97481 3.22844 3.22893C3.97432 2.48305 4.92463 1.9751 5.9592 1.76931C6.99376 1.56352 8.06612 1.66914 9.04066 2.07281C10.0152 2.47647 10.8481 3.16006 11.4342 4.03712C12.0202 4.91418 12.333 5.94533 12.333 7.00016C12.333 8.41465 11.7711 9.77121 10.7709 10.7714C9.77072 11.7716 8.41417 12.3335 6.99968 12.3335Z" fill="#3468B7" />
</svg>
)
}
export default IconInfo

Some files were not shown because too many files have changed in this diff Show More