Merge branch 'common' of https://github.com/KieIO/grocery-vercel-commerce into m6-quangnhanfixui
@@ -1,7 +1,7 @@
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import type { AppProps } from 'next/app';
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { Head } from 'src/components/common';
|
||||
import { CustomShapeSvg, Head } from 'src/components/common';
|
||||
import 'keen-slider/keen-slider.min.css';
|
||||
import '../src/styles/main.scss';
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
<>
|
||||
<Head />
|
||||
<ThemeProvider>
|
||||
<CustomShapeSvg />
|
||||
<Layout pageProps={pageProps}>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
|
@@ -1,13 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Layout } from 'src/components/common';
|
||||
import { AccountPage, AccountSignIn } from 'src/components/modules/account';
|
||||
import { AccountPage } from 'src/components/modules/account';
|
||||
|
||||
const Account = () => {
|
||||
return (
|
||||
// <AccountPage/>
|
||||
<>
|
||||
<AccountSignIn/>
|
||||
</>
|
||||
<AccountPage/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { Layout } from 'src/components/common';
|
||||
|
||||
|
||||
|
||||
import { NotificationEmptyPage, NotificationHeading, NotificationBreadcrumb } from 'src/components/modules/Notification';
|
||||
export default function Demo() {
|
||||
return <>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias possimus tempore, nulla voluptate sed iste unde qui. Natus, amet minus, fugiat unde optio iste perferendis ea quae iusto asperiores voluptates enim sunt ducimus? Perferendis velit maxime sint pariatur beatae, veniam nulla sed, impedit, consectetur minus est libero enim? Quia reiciendis dolor, porro nisi harum fuga ullam pariatur facilis quas, praesentium quae, eveniet officiis officia animi aspernatur ut sunt commodi vero totam! Rerum, placeat perferendis laborum itaque blanditiis natus aperiam, eum delectus enim architecto eos, et voluptates! Illo at sed, pariatur ullam suscipit rerum recusandae doloremque natus nihil. Et temporibus quae necessitatibus quam alias, repellat laudantium a perspiciatis dolorum accusamus officiis pariatur ipsum facilis, nobis magni molestiae accusantium assumenda tempora consequuntur natus nostrum? Id, mollitia alias quidem hic aperiam error, blanditiis vero distinctio sit neque assumenda odio praesentium, perspiciatis aspernatur exercitationem. Eveniet nostrum tempore saepe cupiditate totam fuga doloremque placeat natus beatae quibusdam labore tempora delectus alias architecto vel, recusandae facilis nam rerum dolores magni? Eaque fugiat ut dicta. Aperiam, excepturi ad molestias non corrupti, officia dolore sequi, provident laborum officiis praesentium beatae quos? Totam et consequatur atque fugit voluptate. Aliquam neque, ab hic suscipit obcaecati ut aut quos. Expedita, ipsam.
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias possimus tempore, nulla voluptate sed iste unde qui. Natus, amet minus, fugiat unde optio iste perferendis ea quae iusto asperiores voluptates enim sunt ducimus? Perferendis velit maxime sint pariatur beatae, veniam nulla sed, impedit, consectetur minus est libero enim? Quia reiciendis dolor, porro nisi harum fuga ullam pariatur facilis quas, praesentium quae, eveniet officiis officia animi aspernatur ut sunt commodi vero totam! Rerum, placeat perferendis laborum itaque blanditiis natus aperiam, eum delectus enim architecto eos, et voluptates! Illo at sed, pariatur ullam suscipit rerum recusandae doloremque natus nihil. Et temporibus quae necessitatibus quam alias, repellat laudantium a perspiciatis dolorum accusamus officiis pariatur ipsum facilis, nobis magni molestiae accusantium assumenda tempora consequuntur natus nostrum? Id, mollitia alias quidem hic aperiam error, blanditiis vero distinctio sit neque assumenda odio praesentium, perspiciatis aspernatur exercitationem. Eveniet nostrum tempore saepe cupiditate totam fuga doloremque placeat natus beatae quibusdam labore tempora delectus alias architecto vel, recusandae facilis nam rerum dolores magni? Eaque fugiat ut dicta. Aperiam, excepturi ad molestias non corrupti, officia dolore sequi, provident laborum officiis praesentium beatae quos? Totam et consequatur atque fugit voluptate. Aliquam neque, ab hic suscipit obcaecati ut aut quos. Expedita, ipsam.
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestias possimus tempore, nulla voluptate sed iste unde qui. Natus, amet minus, fugiat unde optio iste perferendis ea quae iusto asperiores voluptates enim sunt ducimus? Perferendis velit maxime sint pariatur beatae, veniam nulla sed, impedit, consectetur minus est libero enim? Quia reiciendis dolor, porro nisi harum fuga ullam pariatur facilis quas, praesentium quae, eveniet officiis officia animi aspernatur ut sunt commodi vero totam! Rerum, placeat perferendis laborum itaque blanditiis natus aperiam, eum delectus enim architecto eos, et voluptates! Illo at sed, pariatur ullam suscipit rerum recusandae doloremque natus nihil. Et temporibus quae necessitatibus quam alias, repellat laudantium a perspiciatis dolorum accusamus officiis pariatur ipsum facilis, nobis magni molestiae accusantium assumenda tempora consequuntur natus nostrum? Id, mollitia alias quidem hic aperiam error, blanditiis vero distinctio sit neque assumenda odio praesentium, perspiciatis aspernatur exercitationem. Eveniet nostrum tempore saepe cupiditate totam fuga doloremque placeat natus beatae quibusdam labore tempora delectus alias architecto vel, recusandae facilis nam rerum dolores magni? Eaque fugiat ut dicta. Aperiam, excepturi ad molestias non corrupti, officia dolore sequi, provident laborum officiis praesentium beatae quos? Totam et consequatur atque fugit voluptate. Aliquam neque, ab hic suscipit obcaecati ut aut quos. Expedita, ipsam.
|
||||
return (
|
||||
<>
|
||||
<NotificationBreadcrumb />
|
||||
<NotificationHeading />
|
||||
<NotificationEmptyPage />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Demo.Layout = Layout
|
||||
|
17
pages/notifications.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { Layout } from 'src/components/common';
|
||||
import { NotificationBreadcrumb, NotificationHeading, NotificationPage } from 'src/components/modules/Notification';
|
||||
|
||||
const Notification = () => {
|
||||
return (
|
||||
<>
|
||||
<NotificationBreadcrumb />
|
||||
<NotificationHeading />
|
||||
<NotificationPage />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Notification.Layout = Layout
|
||||
|
||||
export default Notification;
|
BIN
public/fonts/Norquay-bold.otf
Normal file
BIN
public/fonts/Norquay-bold.woff
Normal file
4
public/fonts/style.css
Normal file
@@ -0,0 +1,4 @@
|
||||
@font-face {
|
||||
font-family: "Norquay-bold";
|
||||
src: url("./Norquay-bold.otf") format("opentype"), url("./Norquay-bold.woff") format("woff");
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
import { TOptionsEvents } from 'keen-slider'
|
||||
import React, { memo } from 'react'
|
||||
import CarouselCommon from '../CarouselCommon/CarouselCommon'
|
||||
import BannerItem, { BannerItemProps } from './BannerItem/BannerItem'
|
||||
@@ -6,7 +7,7 @@ interface Props {
|
||||
data: BannerItemProps[],
|
||||
}
|
||||
|
||||
const option = {
|
||||
const option: TOptionsEvents = {
|
||||
slidesPerView: 1,
|
||||
mode: 'free',
|
||||
}
|
||||
|
@@ -5,6 +5,9 @@
|
||||
padding: 0;
|
||||
&.large {
|
||||
margin-bottom: 2.8rem;
|
||||
background-image: url("./pattern.svg");
|
||||
background-repeat: repeat;
|
||||
background-size: auto;
|
||||
.inner {
|
||||
background-size: cover;
|
||||
background-position: center bottom;
|
||||
|
1
src/components/common/Banner/BannerItem/pattern.svg
Normal file
After Width: | Height: | Size: 10 KiB |
@@ -9,6 +9,7 @@
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: .8rem;
|
||||
backdrop-filter: saturate(180%) blur(10px);
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
@@ -4,6 +4,6 @@
|
||||
.cartDrawer {
|
||||
@apply flex flex-col h-full;
|
||||
.body {
|
||||
@apply flex flex-col justify-center overflow-y-auto overflow-x-hidden h-full custom-scroll;
|
||||
@apply flex flex-col overflow-y-auto overflow-x-hidden h-full custom-scroll;
|
||||
}
|
||||
}
|
@@ -25,7 +25,7 @@ const CartDrawer = ({ visible, onClose }: Props) => {
|
||||
</div>
|
||||
<div>
|
||||
<CartMessage />
|
||||
<CartCheckoutButton />
|
||||
<CartCheckoutButton onClose={onClose}/>
|
||||
</div>
|
||||
</div>
|
||||
</DrawerCommon>
|
||||
|
@@ -4,11 +4,16 @@ import s from './CartCheckoutButton.module.scss';
|
||||
import Link from 'next/link'
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
|
||||
const CartCheckoutButton = memo(() => {
|
||||
interface Props {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
|
||||
const CartCheckoutButton = memo(({ onClose }: Props) => {
|
||||
return (
|
||||
<Link href={ROUTE.CHECKOUT}>
|
||||
<a className={s.cartCheckoutButton}>
|
||||
<ButtonCommon size='large'>Check out - Rp 120.500</ButtonCommon>
|
||||
<ButtonCommon size='large' onClick={onClose}>Check out - Rp 120.500</ButtonCommon>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
|
@@ -1,21 +1,40 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.drawerCommon {
|
||||
@apply fixed flex justify-end transition-all duration-200;
|
||||
@apply fixed flex justify-end transition-all duration-500;
|
||||
overflow: hidden;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100vh;
|
||||
width: 90%;
|
||||
box-shadow: -3px 0 10px rgba(0, 0, 0, 0.15);
|
||||
width: 100%;
|
||||
z-index: 20000;
|
||||
transform: none;
|
||||
|
||||
&.hide {
|
||||
.innerWrap {
|
||||
background: none;
|
||||
}
|
||||
transform: translateX(110%);
|
||||
}
|
||||
&.show {
|
||||
.innerWrap {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
animation: animateBackground 0.8s;
|
||||
}
|
||||
}
|
||||
|
||||
.innerWrap {
|
||||
@apply w-full;
|
||||
}
|
||||
.inner {
|
||||
@apply flex flex-col bg-white;
|
||||
width: fit-content;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
box-shadow: -3px 0 10px rgba(0, 0, 0, 0.15);
|
||||
// transform: none;
|
||||
|
||||
@screen md {
|
||||
max-width: 52rem;
|
||||
}
|
||||
@@ -39,14 +58,21 @@
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
&.hide {
|
||||
transform: translateX(110%);
|
||||
}
|
||||
|
||||
@screen md {
|
||||
width: unset;
|
||||
.inner {
|
||||
min-width: 48rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animateBackground {
|
||||
0%,
|
||||
50% {
|
||||
background: none;
|
||||
}
|
||||
|
||||
100% {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
@@ -11,22 +11,33 @@ interface Props {
|
||||
}
|
||||
|
||||
const DrawerCommon = ({ title, visible, children, onClose }: Props) => {
|
||||
const refInner = useRef<HTMLDivElement>(null)
|
||||
|
||||
const handleClickOut = (e: any) => {
|
||||
if (e.target.contains(refInner.current)) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames({
|
||||
[s.drawerCommon]: true,
|
||||
[s.hide]: !visible
|
||||
[s.hide]: !visible,
|
||||
[s.show]: visible,
|
||||
})}>
|
||||
<div className={s.inner}>
|
||||
<div className={s.top}>
|
||||
<h4 className={s.heading}>
|
||||
{title}
|
||||
</h4>
|
||||
<div className={s.iconClose} onClick={onClose}>
|
||||
<IconClose />
|
||||
<div className={s.innerWrap} onClick={handleClickOut}>
|
||||
<div className={s.inner} ref={refInner}>
|
||||
<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 className={s.content}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -41,13 +41,13 @@
|
||||
transition: all 0.2s;
|
||||
&.show {
|
||||
display: block;
|
||||
animation: showHeaderSticky 0.2s;
|
||||
animation: showHeaderSticky 0.4s;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes showHeaderSticky {
|
||||
0% {
|
||||
transform: translateY(-7rem);
|
||||
transform: scale(.95);
|
||||
}
|
||||
100% {
|
||||
transform: none;
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { memo, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useModalCommon } from 'src/components/hooks'
|
||||
import { isMobile } from 'src/utils/funtion.utils'
|
||||
import { CartDrawer } from '..'
|
||||
import ModalAuthenticate from '../ModalAuthenticate/ModalAuthenticate'
|
||||
import ModalCreateUserInfo from '../ModalCreateUserInfo/ModalCreateUserInfo'
|
||||
@@ -12,10 +11,10 @@ import HeaderSubMenuMobile from './components/HeaderSubMenuMobile/HeaderSubMenuM
|
||||
import s from './Header.module.scss'
|
||||
interface props {
|
||||
toggleFilter: () => void,
|
||||
visibleFilter:boolean
|
||||
visibleFilter: boolean
|
||||
}
|
||||
|
||||
const Header = memo(({ toggleFilter,visibleFilter }: props) => {
|
||||
const Header = memo(({ toggleFilter, visibleFilter }: props) => {
|
||||
const headeFullRef = useRef<HTMLDivElement>(null)
|
||||
const [isFullHeader, setIsFullHeader] = useState<boolean>(true)
|
||||
const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false })
|
||||
@@ -30,27 +29,23 @@ const Header = memo(({ toggleFilter,visibleFilter }: props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const headerHeight = useMemo(() => {
|
||||
return headeFullRef.current?.offsetHeight
|
||||
}, [headeFullRef.current])
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (!isMobile()) {
|
||||
if (!headerHeight || window.scrollY > headerHeight) {
|
||||
setIsFullHeader(false)
|
||||
} else {
|
||||
setIsFullHeader(true)
|
||||
}
|
||||
if (!headeFullRef.current || window.scrollY > headeFullRef.current?.offsetHeight) {
|
||||
setIsFullHeader(false)
|
||||
} else {
|
||||
setIsFullHeader(true)
|
||||
}
|
||||
// if (!isMobile()) {
|
||||
// } else {
|
||||
// setIsFullHeader(true)
|
||||
// }
|
||||
}
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
}, [headerHeight])
|
||||
}, [headeFullRef.current])
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -59,6 +54,7 @@ const Header = memo(({ toggleFilter,visibleFilter }: props) => {
|
||||
[s.show]: !isFullHeader
|
||||
})}>
|
||||
<HeaderMenu
|
||||
isStickyHeader={true}
|
||||
toggleFilter={toggleFilter}
|
||||
toggleCart={toggleCart}
|
||||
openModalAuthen={openModalAuthen}
|
||||
@@ -69,6 +65,7 @@ const Header = memo(({ toggleFilter,visibleFilter }: props) => {
|
||||
<HeaderHighLight />
|
||||
<div className={s.menu}>
|
||||
<HeaderMenu
|
||||
isFull={isFullHeader}
|
||||
visibleFilter={visibleFilter}
|
||||
toggleFilter={toggleFilter}
|
||||
toggleCart={toggleCart}
|
||||
|
@@ -1,25 +1,48 @@
|
||||
@import "../../../../../styles/utilities";
|
||||
|
||||
.headerMenu {
|
||||
padding-top: 1.6rem;
|
||||
@apply hidden;
|
||||
padding-top: 0.8rem;
|
||||
padding-bottom: 0.8rem;
|
||||
&.full {
|
||||
@apply flex;
|
||||
padding-top: 1.6rem;
|
||||
@screen md {
|
||||
padding-top: 0.8rem;
|
||||
}
|
||||
}
|
||||
&.small {
|
||||
@apply flex;
|
||||
.left {
|
||||
.top {
|
||||
display: none;
|
||||
@screen md {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@screen md {
|
||||
@apply flex justify-between items-center;
|
||||
padding-top: 0.8rem;
|
||||
padding-bottom: 0.8rem;
|
||||
}
|
||||
.left {
|
||||
// margin-bottom: 3.2rem;
|
||||
flex: 1;
|
||||
.top {
|
||||
@apply flex justify-between items-center;
|
||||
.iconGroup{
|
||||
margin-bottom: 2rem;
|
||||
@screen md {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.iconGroup {
|
||||
@apply flex justify-between items-center;
|
||||
}
|
||||
.iconCart {
|
||||
margin-left: 1.6rem;
|
||||
}
|
||||
.iconFilter{
|
||||
.iconFilter {
|
||||
@apply relative;
|
||||
.dot{
|
||||
.dot {
|
||||
@apply absolute;
|
||||
top: -0.08rem;
|
||||
right: -0.2rem;
|
||||
@@ -28,7 +51,7 @@
|
||||
height: 1.2rem;
|
||||
border-radius: 1.2rem;
|
||||
@apply hidden;
|
||||
&.isShow{
|
||||
&.isShow {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
@@ -36,13 +59,24 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.inputSearch {
|
||||
margin-top: 2.4rem;
|
||||
.searchWrap {
|
||||
@apply flex items-center;
|
||||
.inputSearch {
|
||||
flex: 1;
|
||||
}
|
||||
.buttonSearch {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
@screen md {
|
||||
margin: 0 1.6rem 0 4.8rem;
|
||||
}
|
||||
@screen lg {
|
||||
min-width: 51.2rem;
|
||||
max-width: 50%;
|
||||
.buttonSearch {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
@screen md {
|
||||
@@ -52,10 +86,6 @@
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
.inputSearch {
|
||||
margin-left: 4.8rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btnCart {
|
||||
@@ -98,7 +128,7 @@
|
||||
}
|
||||
@screen xl {
|
||||
.iconFilterDesk {
|
||||
display:none;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import classNames from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { memo, useMemo } from 'react'
|
||||
import { ButtonCommon } from 'src/components/common'
|
||||
import InputSearch from 'src/components/common/InputSearch/InputSearch'
|
||||
import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown'
|
||||
import { IconBuy, IconFilter, IconHeart, IconHistory, IconUser } from 'src/components/icons'
|
||||
@@ -10,8 +12,9 @@ import s from './HeaderMenu.module.scss'
|
||||
import classNames from 'classnames'
|
||||
interface Props {
|
||||
children?: any,
|
||||
isFull: boolean,
|
||||
visibleFilter?:boolean,
|
||||
isFull?: boolean,
|
||||
isStickyHeader?: boolean,
|
||||
visibleFilter?: boolean,
|
||||
openModalAuthen: () => void,
|
||||
openModalInfo: () => void,
|
||||
toggleFilter: () => void,
|
||||
@@ -20,7 +23,7 @@ interface Props {
|
||||
|
||||
const FILTER_PAGE = [ROUTE.HOME, ROUTE.PRODUCTS]
|
||||
|
||||
const HeaderMenu = memo(({ visibleFilter,openModalAuthen, openModalInfo, toggleFilter, toggleCart}: Props) => {
|
||||
const HeaderMenu = memo(({ isFull, isStickyHeader, visibleFilter, openModalAuthen, openModalInfo, toggleFilter, toggleCart }: Props) => {
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
@@ -35,7 +38,11 @@ const HeaderMenu = memo(({ visibleFilter,openModalAuthen, openModalInfo, toggleF
|
||||
},
|
||||
{
|
||||
link: '/account-not-login',
|
||||
name: 'Account Not Login',
|
||||
name: 'Account Not Login (Demo)',
|
||||
},
|
||||
{
|
||||
link: '/demo',
|
||||
name: 'Notifications Empty (Demo)',
|
||||
},
|
||||
{
|
||||
link: ROUTE.NOTIFICATION,
|
||||
@@ -52,7 +59,11 @@ const HeaderMenu = memo(({ visibleFilter,openModalAuthen, openModalInfo, toggleF
|
||||
|
||||
], [openModalAuthen])
|
||||
return (
|
||||
<section className={s.headerMenu}>
|
||||
<section className={classNames({
|
||||
[s.headerMenu]: true,
|
||||
[s.small]: isStickyHeader,
|
||||
[s.full]: isFull,
|
||||
})}>
|
||||
<div className={s.left}>
|
||||
<div className={s.top}>
|
||||
<Logo />
|
||||
@@ -60,8 +71,8 @@ const HeaderMenu = memo(({ visibleFilter,openModalAuthen, openModalInfo, toggleF
|
||||
{
|
||||
FILTER_PAGE.includes(router.pathname) && (
|
||||
<button className={s.iconFilter} onClick={toggleFilter}>
|
||||
<IconFilter/>
|
||||
<div className={classNames({[s.dot]:true,[s.isShow]:visibleFilter})}></div>
|
||||
<IconFilter />
|
||||
<div className={classNames({ [s.dot]: true, [s.isShow]: visibleFilter })}></div>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -71,8 +82,13 @@ const HeaderMenu = memo(({ visibleFilter,openModalAuthen, openModalInfo, toggleF
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={s.inputSearch}>
|
||||
<InputSearch />
|
||||
<div className={s.searchWrap}>
|
||||
<div className={s.inputSearch}>
|
||||
<InputSearch />
|
||||
</div>
|
||||
<div className={s.buttonSearch}>
|
||||
<ButtonCommon>Search</ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul className={s.menu}>
|
||||
|
@@ -4,6 +4,9 @@
|
||||
min-height: 100vh;
|
||||
> main {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: min( 100%, 1536px);
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
.filter{
|
||||
|
@@ -3,7 +3,7 @@ import { useRouter } from 'next/router'
|
||||
import { FC } from 'react'
|
||||
import { useModalCommon } from 'src/components/hooks'
|
||||
import { BRAND, CATEGORY, FEATURED } from 'src/utils/constanst.utils'
|
||||
import { CustomShapeSvg, ScrollToTop } from '..'
|
||||
import { ScrollToTop } from '..'
|
||||
import Footer from '../Footer/Footer'
|
||||
import Header from '../Header/Header'
|
||||
import MenuNavigationProductList from '../MenuNavigationProductList/MenuNavigationProductList'
|
||||
@@ -28,15 +28,13 @@ const Layout: FC<Props> = ({ children }) => {
|
||||
}
|
||||
return (
|
||||
<CommerceProvider locale={locale}>
|
||||
<div className={s.mainLayout}>
|
||||
<Header toggleFilter={toggleFilter} visibleFilter={visibleFilter}/>
|
||||
<main >{children}</main>
|
||||
|
||||
<CustomShapeSvg/>
|
||||
<div className={s.filter}><MenuNavigationProductList categories={CATEGORY} brands={BRAND} featured={FEATURED} visible={visibleFilter} onClose={closeFilter}/> </div>
|
||||
<ScrollToTop visibilityHeight={1500} />
|
||||
<Footer />
|
||||
</div>
|
||||
<div className={s.mainLayout}>
|
||||
<Header toggleFilter={toggleFilter} visibleFilter={visibleFilter} />
|
||||
<main >{children}</main>
|
||||
<div className={s.filter}><MenuNavigationProductList categories={CATEGORY} brands={BRAND} featured={FEATURED} visible={visibleFilter} onClose={closeFilter} /> </div>
|
||||
<ScrollToTop visibilityHeight={1500} />
|
||||
<Footer />
|
||||
</div>
|
||||
</CommerceProvider>
|
||||
|
||||
)
|
||||
|
@@ -8,7 +8,7 @@
|
||||
&.show {
|
||||
@apply block rounded-lg fixed cursor-pointer;
|
||||
right: 6.4rem;
|
||||
bottom: 21.6rem;
|
||||
bottom: 10.6rem;
|
||||
width: 6.4rem;
|
||||
height: 6.4rem;
|
||||
background-color: var(--border-line);
|
||||
|
@@ -7,7 +7,7 @@
|
||||
.tabList {
|
||||
@apply flex;
|
||||
position: relative;
|
||||
border-bottom: 2px solid #FBFBFB;
|
||||
border-bottom: 2px solid #EBEBEB;
|
||||
padding: 0.8rem 0;
|
||||
&.center{
|
||||
margin: auto;
|
||||
|
19
src/components/icons/IconBell.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
const IconBell = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="129"
|
||||
height="128"
|
||||
viewBox="0 0 129 128"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M96.5 70.2933V53.3333C96.4923 45.7767 93.8107 38.4665 88.9299 32.6975C84.0492 26.9284 77.2843 23.0728 69.8333 21.8133V16C69.8333 14.5855 69.2714 13.229 68.2712 12.2288C67.271 11.2286 65.9145 10.6667 64.5 10.6667C63.0855 10.6667 61.729 11.2286 60.7288 12.2288C59.7286 13.229 59.1667 14.5855 59.1667 16V21.8133C51.7157 23.0728 44.9508 26.9284 40.0701 32.6975C35.1893 38.4665 32.5077 45.7767 32.5 53.3333V70.2933C29.3877 71.3937 26.692 73.4297 24.7823 76.1223C22.8727 78.8149 21.8426 82.0323 21.8333 85.3333V96C21.8333 97.4145 22.3952 98.7711 23.3954 99.7712C24.3956 100.771 25.7522 101.333 27.1667 101.333H43.9133C45.1415 105.853 47.8227 109.842 51.5432 112.687C55.2637 115.531 59.8168 117.072 64.5 117.072C69.1832 117.072 73.7363 115.531 77.4568 112.687C81.1773 109.842 83.8585 105.853 85.0867 101.333H101.833C103.248 101.333 104.604 100.771 105.605 99.7712C106.605 98.7711 107.167 97.4145 107.167 96V85.3333C107.157 82.0323 106.127 78.8149 104.218 76.1223C102.308 73.4297 99.6123 71.3937 96.5 70.2933ZM43.1667 53.3333C43.1667 47.6754 45.4143 42.2492 49.4151 38.2484C53.4158 34.2476 58.8421 32 64.5 32C70.158 32 75.5842 34.2476 79.585 38.2484C83.5857 42.2492 85.8333 47.6754 85.8333 53.3333V69.3333H43.1667V53.3333ZM64.5 106.667C62.6385 106.655 60.8124 106.157 59.2031 105.222C57.5938 104.286 56.2574 102.945 55.3267 101.333H73.6733C72.7426 102.945 71.4062 104.286 69.7969 105.222C68.1876 106.157 66.3615 106.655 64.5 106.667ZM96.5 90.6667H32.5V85.3333C32.5 83.9189 33.0619 82.5623 34.0621 81.5621C35.0623 80.5619 36.4189 80 37.8333 80H91.1667C92.5812 80 93.9377 80.5619 94.9379 81.5621C95.9381 82.5623 96.5 83.9189 96.5 85.3333V90.6667Z"
|
||||
fill="#CCCCCC"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconBell
|
||||
|
25
src/components/icons/IconBill.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
const IconBill = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
width="64"
|
||||
height="64"
|
||||
rx="32"
|
||||
fill="#F1F8F4"
|
||||
/>
|
||||
<path
|
||||
d="M29.0833 30.25H32C32.3094 30.25 32.6062 30.1271 32.825 29.9083C33.0437 29.6895 33.1667 29.3928 33.1667 29.0833C33.1667 28.7739 33.0437 28.4772 32.825 28.2584C32.6062 28.0396 32.3094 27.9167 32 27.9167H30.8333V27.3333C30.8333 27.0239 30.7104 26.7272 30.4916 26.5084C30.2728 26.2896 29.9761 26.1667 29.6667 26.1667C29.3572 26.1667 29.0605 26.2896 28.8417 26.5084C28.6229 26.7272 28.5 27.0239 28.5 27.3333V27.975C27.7912 28.1189 27.1611 28.5211 26.7321 29.1035C26.3031 29.6858 26.1058 30.4068 26.1785 31.1265C26.2512 31.8461 26.5887 32.5131 27.1255 32.9979C27.6623 33.4827 28.36 33.7507 29.0833 33.75H30.25C30.4047 33.75 30.5531 33.8115 30.6625 33.9209C30.7719 34.0303 30.8333 34.1786 30.8333 34.3333C30.8333 34.4881 30.7719 34.6364 30.6625 34.7458C30.5531 34.8552 30.4047 34.9167 30.25 34.9167H27.3333C27.0239 34.9167 26.7272 35.0396 26.5084 35.2584C26.2896 35.4772 26.1667 35.7739 26.1667 36.0833C26.1667 36.3928 26.2896 36.6895 26.5084 36.9083C26.7272 37.1271 27.0239 37.25 27.3333 37.25H28.5V37.8333C28.5 38.1428 28.6229 38.4395 28.8417 38.6583C29.0605 38.8771 29.3572 39 29.6667 39C29.9761 39 30.2728 38.8771 30.4916 38.6583C30.7104 38.4395 30.8333 38.1428 30.8333 37.8333V37.1917C31.5422 37.0477 32.1722 36.6456 32.6012 36.0632C33.0302 35.4808 33.2275 34.7599 33.1548 34.0402C33.0821 33.3206 32.7446 32.6536 32.2079 32.1688C31.6711 31.684 30.9733 31.4159 30.25 31.4167H29.0833C28.9286 31.4167 28.7803 31.3552 28.6709 31.2458C28.5615 31.1364 28.5 30.9881 28.5 30.8333C28.5 30.6786 28.5615 30.5303 28.6709 30.4209C28.7803 30.3115 28.9286 30.25 29.0833 30.25ZM42.5 32H39V21.5C39.0008 21.2944 38.9473 21.0923 38.8449 20.914C38.7424 20.7358 38.5947 20.5878 38.4167 20.485C38.2393 20.3826 38.0381 20.3287 37.8333 20.3287C37.6285 20.3287 37.4274 20.3826 37.25 20.485L33.75 22.4917L30.25 20.485C30.0726 20.3826 29.8715 20.3287 29.6667 20.3287C29.4619 20.3287 29.2607 20.3826 29.0833 20.485L25.5833 22.4917L22.0833 20.485C21.906 20.3826 21.7048 20.3287 21.5 20.3287C21.2952 20.3287 21.094 20.3826 20.9167 20.485C20.7386 20.5878 20.5909 20.7358 20.4885 20.914C20.386 21.0923 20.3325 21.2944 20.3333 21.5V40.1667C20.3333 41.0949 20.7021 41.9852 21.3585 42.6415C22.0148 43.2979 22.9051 43.6667 23.8333 43.6667H40.1667C41.0949 43.6667 41.9852 43.2979 42.6415 42.6415C43.2979 41.9852 43.6667 41.0949 43.6667 40.1667V33.1667C43.6667 32.8573 43.5438 32.5605 43.325 32.3417C43.1062 32.1229 42.8094 32 42.5 32ZM23.8333 41.3333C23.5239 41.3333 23.2272 41.2104 23.0084 40.9916C22.7896 40.7728 22.6667 40.4761 22.6667 40.1667V23.5183L25 24.8483C25.1801 24.9424 25.3802 24.9915 25.5833 24.9915C25.7865 24.9915 25.9866 24.9424 26.1667 24.8483L29.6667 22.8417L33.1667 24.8483C33.3467 24.9424 33.5469 24.9915 33.75 24.9915C33.9531 24.9915 34.1533 24.9424 34.3333 24.8483L36.6667 23.5183V40.1667C36.6698 40.5647 36.7408 40.9592 36.8767 41.3333H23.8333ZM41.3333 40.1667C41.3333 40.4761 41.2104 40.7728 40.9916 40.9916C40.7728 41.2104 40.4761 41.3333 40.1667 41.3333C39.8572 41.3333 39.5605 41.2104 39.3417 40.9916C39.1229 40.7728 39 40.4761 39 40.1667V34.3333H41.3333V40.1667Z"
|
||||
fill="#5B9A74"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconBill
|
||||
|
@@ -33,4 +33,6 @@ export { default as IconMinus } from './IconMinus'
|
||||
export { default as IconCirclePlus } from './IconCirclePlus'
|
||||
export { default as IconDoneCheckout } from './IconDoneCheckout'
|
||||
export { default as IconFilter } from './IconFilter'
|
||||
export { default as IconBell } from './IconBell'
|
||||
export { default as IconBill } from './IconBill'
|
||||
export { default as IconNoti } from './IconNoti'
|
@@ -0,0 +1,7 @@
|
||||
@import "../../../../styles/utilities";
|
||||
.breadCrumbWrapper {
|
||||
@apply py-12 spacing-horizontal;
|
||||
@screen lg {
|
||||
padding-left: 3.2rem;
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
import { BreadcrumbCommon } from "src/components/common"
|
||||
import s from './NotificationBreadcrumb.module.scss'
|
||||
|
||||
const NOTIFICATION_DATA = [
|
||||
{link: "/notifications", name: "Notifications"},
|
||||
];
|
||||
|
||||
const NotificationBreadcrumb = () => {
|
||||
return (
|
||||
<section className={s.breadCrumbWrapper}>
|
||||
<BreadcrumbCommon crumbs={NOTIFICATION_DATA} showHomePage={true}/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationBreadcrumb
|
@@ -0,0 +1,17 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.emptyPage {
|
||||
@apply flex justify-center items-center flex-col;
|
||||
margin-bottom: 5.4rem;
|
||||
@screen lg {
|
||||
margin-bottom: 12.8rem;
|
||||
}
|
||||
.emptyIcon {
|
||||
padding-bottom: 2.6rem;
|
||||
}
|
||||
.emptyContent {
|
||||
@apply sub-headline;
|
||||
align-content: center;
|
||||
color: var(--disabled);
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
import { IconBell } from 'src/components/icons'
|
||||
import s from '../NotificationEmptyPage/NotificationEmptyPage.module.scss'
|
||||
|
||||
|
||||
const NotificationEmptyPage = () => {
|
||||
return (
|
||||
<section className={s.emptyPage}>
|
||||
<div className={s.emptyIcon}>
|
||||
<IconBell />
|
||||
</div>
|
||||
<div className={s.emptyContent}>
|
||||
<p>Your Notification is empty</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
export default NotificationEmptyPage
|
@@ -0,0 +1,9 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.headingWrapper {
|
||||
@apply flex spacing-horizontal-left pb-16;
|
||||
.heading{
|
||||
max-width: 121.6rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
import { HeadingCommon } from "src/components/common"
|
||||
import s from './NotificationHeading.module.scss'
|
||||
|
||||
interface NotificationHeadingProps {
|
||||
children?: React.ReactNode,
|
||||
heading?: string,
|
||||
}
|
||||
|
||||
const NotificationHeading = ({heading = "NOTIFICATIONS"}: NotificationHeadingProps) => {
|
||||
return (
|
||||
<section className={s.headingWrapper}>
|
||||
<div className={s.heading}>
|
||||
<HeadingCommon>{heading}</HeadingCommon>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
export default NotificationHeading
|
@@ -0,0 +1,35 @@
|
||||
@import '../../../../styles/utilities';
|
||||
|
||||
.notificationItem {
|
||||
@apply flex flex-row spacing-horizontal;
|
||||
padding-top: 1.2rem;
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
.contentWrapper {
|
||||
padding-left: 1.6rem;
|
||||
padding-bottom: 1.4rem;
|
||||
max-width: 26.3rem;
|
||||
.title {
|
||||
font-weight: bold;
|
||||
color: var(--text-active);
|
||||
}
|
||||
.date {
|
||||
@apply caption;
|
||||
color: #828282;
|
||||
}
|
||||
}
|
||||
&.isChecked {
|
||||
.icon {
|
||||
rect {
|
||||
fill: var(--gray)
|
||||
}
|
||||
path {
|
||||
fill: var(--disabled)
|
||||
}
|
||||
}
|
||||
.title, .content {
|
||||
color: var(--text-label);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
import React, {useState} from 'react'
|
||||
import s from '../NotificationItem/NotificationItem.module.scss'
|
||||
import ClassNames from 'classnames'
|
||||
import { IconBill } from 'src/components/icons'
|
||||
import Link from 'next/link'
|
||||
import {ROUTE , QUERY_KEY, ACCOUNT_TAB } from 'src/utils/constanst.utils'
|
||||
|
||||
export interface NotificationItemProps {
|
||||
ID?: string,
|
||||
title?: string,
|
||||
content?: string,
|
||||
date?: string,
|
||||
checked?: boolean,
|
||||
}
|
||||
|
||||
const NotificationItem = ({ ID, title, content, date, checked}: NotificationItemProps) => {
|
||||
const [isChecked, setChecked] = useState(checked)
|
||||
const Check = () => {
|
||||
setChecked(true)
|
||||
}
|
||||
return (
|
||||
<section className={ClassNames({
|
||||
[s.notificationItem] : true,
|
||||
[s.isChecked] : isChecked,
|
||||
})}
|
||||
onClick = {Check}
|
||||
>
|
||||
<div className={s.icon}>
|
||||
<IconBill />
|
||||
</div>
|
||||
<Link href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.ORDER}`}>
|
||||
<a>
|
||||
<div className={s.contentWrapper}>
|
||||
<div className={s.title}>
|
||||
{title}
|
||||
</div>
|
||||
<div className={s.content}>
|
||||
{content}
|
||||
</div>
|
||||
<div className={s.date}>
|
||||
{date}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationItem
|
@@ -0,0 +1,6 @@
|
||||
.notificationPage {
|
||||
padding-bottom: 5.4rem;
|
||||
@screen md {
|
||||
padding-bottom: 12.8rem;
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
import React from 'react'
|
||||
import NotificationEmptyPage from '../NotificationEmptyPage/NotificationEmptyPage'
|
||||
import NotificationItem, { NotificationItemProps } from '../NotificationItem/NotificationItem'
|
||||
import s from './NotificationPage.module.scss'
|
||||
|
||||
interface NotificationPageProps {
|
||||
isEmpty?: boolean,
|
||||
data?: NotificationItemProps[],
|
||||
}
|
||||
const NOTIFICATION_DATA = [
|
||||
{
|
||||
ID: "ID33455",
|
||||
title: "Your order ID33455",
|
||||
content: "The order has been deliveried successfully!",
|
||||
date: "2 days ago",
|
||||
checked: false,
|
||||
},
|
||||
{
|
||||
ID: "ID33456",
|
||||
title: "Your order ID33456",
|
||||
content: "The order has been deliveried successfully!",
|
||||
date: "2 days ago",
|
||||
checked: false,
|
||||
},
|
||||
{
|
||||
ID: "ID33457",
|
||||
title: "Your order ID33457",
|
||||
content: "The order has been deliveried successfully!",
|
||||
date: "2 days ago",
|
||||
checked: true,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
const NotificationPage = ({ isEmpty=false, data = NOTIFICATION_DATA }: NotificationPageProps) => {
|
||||
return (
|
||||
<div className={s.notificationPage}>
|
||||
{
|
||||
isEmpty ?
|
||||
<NotificationEmptyPage />
|
||||
:
|
||||
<>
|
||||
{
|
||||
data.map(item => {
|
||||
return (
|
||||
<NotificationItem key={`${item.ID}-${item.title}`} title={item.title} content={item.content} date={item.date} checked={item.checked}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationPage
|
5
src/components/modules/Notification/index.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
export { default as NotificationPage } from './NotificationPage/NotificationPage'
|
||||
export { default as NotificationEmptyPage } from './NotificationEmptyPage/NotificationEmptyPage'
|
||||
export { default as NotificationItem } from './NotificationItem/NotificationItem'
|
||||
export { default as NotificationBreadcrumb } from './NotificationBreadcrum/NotificationBreadcrumb'
|
||||
export { default as NotificationHeading } from './NotificationHeading/NotificationHeading'
|
@@ -1,27 +1,54 @@
|
||||
@import '../../../../styles/utilities';
|
||||
|
||||
.accountNavigation {
|
||||
@apply flex;
|
||||
@apply relative;
|
||||
width: 100%;
|
||||
|
||||
@screen md {
|
||||
@apply flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.slider {
|
||||
@apply inline-block;
|
||||
width: 0.2rem;
|
||||
height: 4.8rem;
|
||||
border-radius: 3px;
|
||||
background-color: var(--primary);
|
||||
position: absolute;
|
||||
left: 11.2rem;
|
||||
transition: all .2s linear;
|
||||
@apply hidden;
|
||||
|
||||
@screen md {
|
||||
@apply inline-block;
|
||||
width: 0.2rem;
|
||||
height: 4.8rem;
|
||||
border-radius: 3px;
|
||||
background-color: var(--primary);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 450;
|
||||
transition: all .2s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.tabList {
|
||||
margin-top: 3.8rem;
|
||||
margin-right: 12.4rem;
|
||||
@apply flex;
|
||||
|
||||
@screen md {
|
||||
@apply block;
|
||||
margin-right: 0rem;
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
@apply block;
|
||||
margin-right: 4.8rem;
|
||||
}
|
||||
|
||||
@screen xl {
|
||||
@apply block;
|
||||
margin-right: 12.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tabBody {
|
||||
margin-top: -4.7rem;
|
||||
width: 100%;
|
||||
|
||||
@screen md {
|
||||
margin-top: -8.6rem;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,6 @@
|
||||
import React, { useRef, useEffect, Children, ReactElement, PropsWithChildren, useState, cloneElement } from "react"
|
||||
import s from './AccountNavigation.module.scss'
|
||||
|
||||
import AccountNavigationItem from './components/AccountNavigationItem'
|
||||
import AccountNavigationItem from './components/AccountNavigationItem/AccountNavigationItem'
|
||||
import {TabPaneProps} from '../../../common/TabCommon/components/TabPane/TabPane'
|
||||
|
||||
interface AccountNavigationProps {
|
||||
@@ -14,6 +13,12 @@ const AccountNavigation = ({ defaultActiveIndex, children } : AccountNavigationP
|
||||
const sliderRef = useRef<HTMLDivElement>(null);
|
||||
const headerRef = useRef<HTMLUListElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultActiveIndex !== undefined) {
|
||||
setActive(defaultActiveIndex)
|
||||
}
|
||||
}, [defaultActiveIndex])
|
||||
|
||||
const onTabClick = (index: number) => {
|
||||
setActive(index)
|
||||
}
|
||||
@@ -21,10 +26,12 @@ const AccountNavigation = ({ defaultActiveIndex, children } : AccountNavigationP
|
||||
function slide(index: number) {
|
||||
const active = headerRef.current?.children.item(index)?.getBoundingClientRect()
|
||||
const header = headerRef.current?.getBoundingClientRect()
|
||||
const firstEl = headerRef.current?.children.item(0)?.getBoundingClientRect()
|
||||
const current = sliderRef.current
|
||||
|
||||
if (current && active && header) {
|
||||
const top = active.top;
|
||||
if (current && active && header && firstEl) {
|
||||
const firstElTop = firstEl.top
|
||||
const top = active.top - firstElTop;
|
||||
current.style.top = top.toString()+"px";
|
||||
}
|
||||
}
|
||||
@@ -63,7 +70,6 @@ const AccountNavigation = ({ defaultActiveIndex, children } : AccountNavigationP
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div ref={slider} className={s.slider}></div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
@@ -1,17 +0,0 @@
|
||||
@import '../../../../../styles/utilities';
|
||||
|
||||
.accountNavigationItem {
|
||||
width: 28rem;
|
||||
padding: 1.2rem 0 1.2rem 1.6rem;
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
|
||||
&:hover {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #FBFBFB;
|
||||
border-radius: 0 1.6rem 1.6rem 0;
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
@import '../../../../../../styles/utilities';
|
||||
|
||||
.accountNavigationItem {
|
||||
width: fit-content;
|
||||
padding: 1.2rem 1.6rem 1.2rem 0rem;
|
||||
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
&:hover {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply shape-common font-bold;
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
padding: 1.2rem 1.6rem 1.2rem 1.6rem;
|
||||
margin-right: 1.6rem;
|
||||
|
||||
@screen md {
|
||||
@apply font-normal;
|
||||
background-color: #FBFBFB;
|
||||
border-radius: 0 1.6rem 1.6rem 0;
|
||||
color: #3D3D3D;
|
||||
clip-path: none;
|
||||
}
|
||||
}
|
||||
|
||||
@screen md {
|
||||
max-width: 24rem;
|
||||
min-width: 22rem;
|
||||
padding: 1.2rem 1.6rem 1.2rem 1.6rem;
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
max-width: 26rem;
|
||||
min-width: 23rem;
|
||||
padding: 1.2rem 1.6rem 1.2rem 1.6rem;
|
||||
}
|
||||
|
||||
@screen xl {
|
||||
max-width: 28rem;
|
||||
min-width: 24rem;
|
||||
padding: 1.2rem 1.6rem 1.2rem 1.6rem;
|
||||
}
|
||||
}
|
@@ -6,6 +6,8 @@
|
||||
margin-top: -3.2rem;
|
||||
padding-top: 3.2rem;
|
||||
padding-bottom: 3.2rem;
|
||||
min-height: 70rem;
|
||||
|
||||
|
||||
@screen md {
|
||||
padding-left: 3.2rem;
|
||||
|
@@ -1,18 +1,28 @@
|
||||
import React, { useState } from "react"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import s from './AccountPage.module.scss'
|
||||
|
||||
import { HeadingCommon, TabPane } from "src/components/common"
|
||||
|
||||
import AccountNavigation from '../AccountNavigation/AccountNavigation'
|
||||
import HeadingCommon from '../../../common/HeadingCommon/HeadingCommon'
|
||||
import AccountInfomation from "./components/AccountInfomation/AccountInfomation"
|
||||
import FavouriteProducts from "./components/FavouriteProducts/FavouriteProducts"
|
||||
import OrderInfomation from './components/OrderInformation/OrderInformation'
|
||||
import EditInfoModal from './components/EditInfoModal/EditInfoModal'
|
||||
import TabPane from "src/components/common/TabCommon/components/TabPane/TabPane"
|
||||
|
||||
import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data';
|
||||
import { ACCOUNT_TAB, QUERY_KEY } from "src/utils/constanst.utils"
|
||||
import { useRouter } from "next/router"
|
||||
|
||||
const waiting = [
|
||||
{
|
||||
id: "NO 123456",
|
||||
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||
totalPrice : 1000
|
||||
},
|
||||
{
|
||||
id: "NO 123457",
|
||||
products: ["Tomato", "Fish", "Pork", "Onion"],
|
||||
totalPrice : 1000
|
||||
}
|
||||
]
|
||||
|
||||
@@ -21,6 +31,11 @@ const delivering = [
|
||||
id: "NO 123456",
|
||||
products: ["Tomato", "Fish", "Pork", "Onion", "Tomato", "Fish", "Pork", "Onion"],
|
||||
totalPrice : 1000
|
||||
},
|
||||
{
|
||||
id: "NO 123457",
|
||||
products: ["Tomato", "Fish", "Pork", "Onion", "Tomato", "Fish", "Pork", "Onion"],
|
||||
totalPrice : 1000
|
||||
}
|
||||
]
|
||||
|
||||
@@ -29,6 +44,11 @@ const delivered = [
|
||||
id: "NO 123456",
|
||||
products: ["Tomato", "Fish", "Pork", "Onion", "Tomato", "Fish", "Pork", "Onion"],
|
||||
totalPrice : 1000
|
||||
},
|
||||
{
|
||||
id: "NO 123457",
|
||||
products: ["Tomato", "Fish", "Pork", "Onion", "Tomato", "Fish", "Pork", "Onion"],
|
||||
totalPrice : 1000
|
||||
}
|
||||
]
|
||||
|
||||
@@ -45,12 +65,30 @@ let account = {
|
||||
interface AccountPageProps {
|
||||
defaultActiveContent?: "info" | "orders" | "favorites"
|
||||
}
|
||||
const getTabIndex = (tab?: string): number => {
|
||||
switch (tab) {
|
||||
case ACCOUNT_TAB.CUSTOMER_INFO:
|
||||
return 0;
|
||||
case ACCOUNT_TAB.ORDER:
|
||||
return 1;
|
||||
case ACCOUNT_TAB.FAVOURITE:
|
||||
return 2;
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
const AccountPage = ({defaultActiveContent="orders"} : AccountPageProps) => {
|
||||
|
||||
const [activeTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2)
|
||||
const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
|
||||
const router = useRouter()
|
||||
const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2)
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const query = router.query[QUERY_KEY.TAB] as string
|
||||
const index = getTabIndex(query)
|
||||
setActiveTab(index)
|
||||
}, [router.query[QUERY_KEY.TAB]])
|
||||
|
||||
function showModal() {
|
||||
setModalVisible(true);
|
||||
}
|
||||
@@ -74,7 +112,7 @@ const AccountPage = ({defaultActiveContent="orders"} : AccountPageProps) => {
|
||||
<OrderInfomation waiting={waiting} delivering={delivering} delivered={delivered} />
|
||||
</TabPane>
|
||||
<TabPane tabName="Favourite">
|
||||
{/* <FavoriteProduct active={activeTab === 2} favProducts={favProducts} /> */}
|
||||
<FavouriteProducts products={PRODUCT_CART_DATA_TEST} />
|
||||
</TabPane>
|
||||
</AccountNavigation>
|
||||
</section>
|
||||
|
@@ -1,13 +1,11 @@
|
||||
@import '../../../../../../styles/utilities';
|
||||
|
||||
.accountInfomation {
|
||||
@apply flex justify-center items-center;
|
||||
text-align: center;
|
||||
@apply block text-center;
|
||||
margin-top: 1.6rem;
|
||||
|
||||
@screen md {
|
||||
@apply block;
|
||||
text-align: left;
|
||||
@apply text-left;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -32,17 +30,29 @@
|
||||
border: 1px solid var(--disabled);
|
||||
max-width: 39.2rem;
|
||||
min-width: 30rem;
|
||||
margin: auto;
|
||||
margin-top: 2.4rem;
|
||||
margin-bottom: 2.4rem;
|
||||
|
||||
@screen md {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.shippingInfo {
|
||||
@apply heading-3 font-heading;
|
||||
margin-bottom: .8rem;
|
||||
}
|
||||
|
||||
.accountAddress {
|
||||
max-width: 31rem;
|
||||
min-width: none;
|
||||
margin: auto;
|
||||
max-width: 28rem;
|
||||
|
||||
@screen md {
|
||||
max-width: 31rem;
|
||||
min-width: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.editInfoBtn {
|
||||
|
@@ -21,34 +21,30 @@ const AccountInfomation = ({ account, onClick } : AccountInfomationProps) => {
|
||||
|
||||
return (
|
||||
<section className={s.accountInfomation}>
|
||||
{
|
||||
<div>
|
||||
<div className={s.avatar}>
|
||||
<Image src={avatar} alt="avatar" />
|
||||
</div>
|
||||
<div className={s.avatar}>
|
||||
<Image src={avatar} alt="avatar" />
|
||||
</div>
|
||||
|
||||
<div className={s.accountName}>
|
||||
{account.name}
|
||||
</div>
|
||||
<div className={s.accountEmail}>
|
||||
{account.email}
|
||||
</div>
|
||||
<div className={s.accountName}>
|
||||
{account.name}
|
||||
</div>
|
||||
<div className={s.accountEmail}>
|
||||
{account.email}
|
||||
</div>
|
||||
|
||||
<div className={s.horizontalSeparator}></div>
|
||||
<div className={s.horizontalSeparator}></div>
|
||||
|
||||
<div className={s.shippingInfo}>Shipping Infomation</div>
|
||||
<div className={s.shippingInfo}>Shipping Infomation</div>
|
||||
|
||||
<div className={s.accountAddress}>
|
||||
{account.address + `, ${account.state}, ${account.city}, ${account.postalCode}`}
|
||||
</div>
|
||||
<div className={s.accountAddress}>
|
||||
{account.address + `, ${account.state}, ${account.city}, ${account.postalCode}`}
|
||||
</div>
|
||||
|
||||
<div className={s.accountPhoneNumber}>
|
||||
{account.phoneNumber}
|
||||
</div>
|
||||
<div className={s.accountPhoneNumber}>
|
||||
{account.phoneNumber}
|
||||
</div>
|
||||
|
||||
<div onClick={showEditForm} className={s.editInfoBtn}>Edit</div>
|
||||
</div>
|
||||
}
|
||||
<div onClick={showEditForm} className={s.editInfoBtn}>Edit</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
@@ -5,77 +5,35 @@
|
||||
@apply bg-white;
|
||||
margin-bottom: 1.6rem;
|
||||
width: 100%;
|
||||
border: 2px solid #EBEBEB;
|
||||
border-radius: .8rem;
|
||||
padding: 1.6rem;
|
||||
}
|
||||
|
||||
.inputDisable {
|
||||
margin-bottom: 1.6rem;
|
||||
width: 100%;
|
||||
border: 2px solid #EBEBEB;
|
||||
border-radius: .8rem;
|
||||
padding: 1.6rem;
|
||||
background-color: #EBEBEB;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.inputStateWrapper {
|
||||
@apply bg-white;
|
||||
margin-bottom: 1.6rem;
|
||||
margin-right: 1.6rem;
|
||||
border: 2px solid #EBEBEB;
|
||||
border-radius: .8rem;
|
||||
padding: 1.6rem;
|
||||
|
||||
.inputState {
|
||||
@apply bg-white cursor-pointer;
|
||||
border: white;
|
||||
}
|
||||
.inputState {
|
||||
@apply bg-white cursor-pointer;
|
||||
border: white;
|
||||
margin-right: 0.8rem;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.inputPostalCode {
|
||||
@apply bg-white;
|
||||
margin-bottom: 1.6rem;
|
||||
border: 2px solid #EBEBEB;
|
||||
border-radius: .8rem;
|
||||
padding: 1.6rem;
|
||||
margin-left: 0.8rem;
|
||||
}
|
||||
|
||||
.inputPhoneNumber {
|
||||
@apply bg-white;
|
||||
margin-bottom: 4rem;
|
||||
width: 100%;
|
||||
border: 2px solid #EBEBEB;
|
||||
border-radius: .8rem;
|
||||
padding: 1.6rem;
|
||||
margin-top: 1.6rem;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
@apply flex;
|
||||
|
||||
.buttonCancel {
|
||||
@apply bg-white text-center font-bold custom-border-radius-lg;
|
||||
color: #141414;
|
||||
border: 1px solid #141414;
|
||||
padding: 1.6rem;
|
||||
margin-right: 1.6rem;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonSave {
|
||||
@apply text-center font-bold custom-border-radius-lg;
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
padding: 1.6rem;
|
||||
width: 100%;
|
||||
&:hover {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
}
|
||||
@apply grid grid-cols-2;
|
||||
grid-gap: 1.6rem;
|
||||
}
|
||||
}
|
@@ -1,10 +1,7 @@
|
||||
import classNames from "classnames"
|
||||
import React, { useState } from "react"
|
||||
import React from "react"
|
||||
import s from './EditInfoModal.module.scss'
|
||||
|
||||
import {ModalCommon, MenuDropdown} from '../../../../../common'
|
||||
|
||||
import {ButtonCommon} from '../../../../../common'
|
||||
import { ModalCommon, Inputcommon, SelectCommon, ButtonCommon } from '../../../../../common'
|
||||
|
||||
interface EditInfoModalProps {
|
||||
accountInfo: {name: string, email: string, address: string, state: string, city: string, postalCode: string, phoneNumber: string};
|
||||
@@ -14,14 +11,6 @@ interface EditInfoModalProps {
|
||||
|
||||
const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => {
|
||||
|
||||
const [name, setName] = useState(accountInfo.name);
|
||||
const [email, setEmail] = useState(accountInfo.email);
|
||||
const [address, setAddress] = useState(accountInfo.address);
|
||||
const [state, setState] = useState(accountInfo.state);
|
||||
const [city, setCity] = useState(accountInfo.city);
|
||||
const [postalCode, setPostalCode] = useState(accountInfo.postalCode);
|
||||
const [phoneNumber, setPhoneNumber] = useState(accountInfo.phoneNumber);
|
||||
|
||||
function saveInfo() {
|
||||
console.log("saved !!!");
|
||||
|
||||
@@ -29,51 +18,48 @@ const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoMod
|
||||
}
|
||||
|
||||
const states = [
|
||||
{name: "D1", onClick: () => {setState("D1")}},
|
||||
{name: "D2", onClick: () => {setState("D2")}},
|
||||
{name: "D3", onClick: () => {setState("D3")}}
|
||||
{name: "District 1", value: "D1"},
|
||||
{name: "District 2", value: "D2"},
|
||||
{name: "District 3", value: "D3"}
|
||||
]
|
||||
|
||||
return (
|
||||
<ModalCommon onClose={closeModal} visible={visible} title="Edit Infomation">
|
||||
<section className={s.editInfoModal}>
|
||||
<div>
|
||||
<input className={s.input} type="text" name="name" placeholder="Name"
|
||||
value={name} onChange={e => {setName(e.target.value)}} />
|
||||
<div className={s.input}>
|
||||
<Inputcommon placeholder="Name" value={accountInfo.name} type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<input className={s.inputDisable} type="text" name="email" placeholder="Email"
|
||||
value={email} onChange={e => {setEmail(e.target.value)}} />
|
||||
|
||||
<div className={s.inputDisable}>
|
||||
<Inputcommon placeholder="Email" value={accountInfo.email} type="email" />
|
||||
</div>
|
||||
<div>
|
||||
<input className={s.input} type="text" name="address" placeholder="Address"
|
||||
value={address} onChange={e => {setAddress(e.target.value)}}/>
|
||||
|
||||
<div className={s.input}>
|
||||
<Inputcommon placeholder="Address" value={accountInfo.address} type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<input className={s.input} type="text" name="city" placeholder="City"
|
||||
value={city} onChange={e => {setCity(e.target.value)}} />
|
||||
</div>
|
||||
|
||||
<div className={s.input}>
|
||||
<Inputcommon placeholder="City" value={accountInfo.city} type="text" />
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex">
|
||||
<div className={s.inputStateWrapper}>
|
||||
<MenuDropdown options={states} isHasArrow={true} >
|
||||
<input className={s.inputState} type="text" name="state" placeholder="State"
|
||||
value={state} disabled />
|
||||
</MenuDropdown>
|
||||
<div className={s.inputState}>
|
||||
<SelectCommon type="custom" placeholder="State" option={states} />
|
||||
</div>
|
||||
|
||||
<input className={s.inputPostalCode} type="text" name="postalCode" placeholder="Postal code"
|
||||
value={postalCode} onChange={e => {setPostalCode(e.target.value)}} />
|
||||
<div className={s.inputPostalCode}>
|
||||
<Inputcommon placeholder="Postal code" value={accountInfo.postalCode} type="text" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input className={s.inputPhoneNumber} type="text" name="phoneNumber" placeholder="Phone number"
|
||||
value={phoneNumber} onChange={e => {setPhoneNumber(e.target.value)}} />
|
||||
<div className={s.inputPhoneNumber}>
|
||||
<Inputcommon placeholder="Phone number" value={accountInfo.phoneNumber} type="text" />
|
||||
</div>
|
||||
|
||||
<div className={s.buttons}>
|
||||
<div onClick={closeModal} className={s.buttonCancel}>Cancel</div>
|
||||
<div onClick={saveInfo} className={s.buttonSave}>Save</div>
|
||||
<ButtonCommon onClick={closeModal} type="light" size="large" >Cancel</ButtonCommon>
|
||||
<ButtonCommon onClick={saveInfo} size="large" >Save</ButtonCommon>
|
||||
</div>
|
||||
</section>
|
||||
</ModalCommon>
|
||||
|
@@ -0,0 +1,5 @@
|
||||
@import '../../../../../../styles/utilities';
|
||||
|
||||
.favouriteProducts {
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
import React from "react"
|
||||
import s from './FavouriteProducts.module.scss'
|
||||
import {ProductList} from '../../../../../common'
|
||||
import { ProductCardProps } from '../../../../../common/ProductCard/ProductCard'
|
||||
|
||||
|
||||
interface FavouriteProductsProps {
|
||||
products: ProductCardProps[];
|
||||
}
|
||||
|
||||
const FavouriteProducts = ({ products } : FavouriteProductsProps) => {
|
||||
|
||||
return (
|
||||
<section className={s.favouriteProducts}>
|
||||
<ProductList data={products} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default FavouriteProducts
|
@@ -8,6 +8,8 @@
|
||||
|
||||
.tabs {
|
||||
margin-top: 3.2rem;
|
||||
width: 100%;
|
||||
border-bottom: black;
|
||||
|
||||
.blank {
|
||||
margin-bottom: 2.4rem;
|
||||
|
@@ -10,55 +10,50 @@ interface OrderInformationProps {
|
||||
waiting: {id: string, products: string[], totalPrice: number}[],
|
||||
delivering: {id: string, products: string[], totalPrice: number}[],
|
||||
delivered: {id: string, products: string[], totalPrice: number}[],
|
||||
// active?: boolean
|
||||
}
|
||||
|
||||
const OrderInformation = ({ waiting, delivering, delivered} : OrderInformationProps) => {
|
||||
|
||||
return (
|
||||
<section className={s.orderInformation}>
|
||||
{
|
||||
<div>
|
||||
<div className={s.title}>Order Information</div>
|
||||
<div className={s.title}>Order Information</div>
|
||||
|
||||
<div className={s.tabs}>
|
||||
<TabCommon>
|
||||
<TabPane tabName={"Wait for Comfirmation"} >
|
||||
<div className={s.blank}></div>
|
||||
{
|
||||
waiting.map((order, i) => {
|
||||
return (
|
||||
<DeliveryItem key={order.id} id={order.id} status="waiting" products={order.products} totalPrice={order.totalPrice} />
|
||||
)
|
||||
})
|
||||
}
|
||||
</TabPane>
|
||||
<div className={s.tabs}>
|
||||
<TabCommon>
|
||||
<TabPane tabName={"Wait for Comfirmation"} >
|
||||
<div className={s.blank}></div>
|
||||
{
|
||||
waiting.map((order, i) => {
|
||||
return (
|
||||
<DeliveryItem key={order.id} id={order.id} status="Waiting" products={order.products} totalPrice={order.totalPrice} />
|
||||
)
|
||||
})
|
||||
}
|
||||
</TabPane>
|
||||
|
||||
<TabPane tabName={"Delivering"}>
|
||||
<div className={s.blank}></div>
|
||||
{
|
||||
delivering.map((order, i) => {
|
||||
return (
|
||||
<DeliveryItem key={order.id} id={order.id} status="delivering" products={order.products} totalPrice={order.totalPrice} />
|
||||
)
|
||||
})
|
||||
}
|
||||
</TabPane>
|
||||
<TabPane tabName={"Delivering"}>
|
||||
<div className={s.blank}></div>
|
||||
{
|
||||
delivering.map((order, i) => {
|
||||
return (
|
||||
<DeliveryItem key={order.id} id={order.id} status="Delivering" products={order.products} totalPrice={order.totalPrice} />
|
||||
)
|
||||
})
|
||||
}
|
||||
</TabPane>
|
||||
|
||||
<TabPane tabName={"Delivered"}>
|
||||
<div className={s.blank}></div>
|
||||
{
|
||||
delivered.map((order, i) => {
|
||||
return (
|
||||
<DeliveryItem key={order.id} id={order.id} status="delivered" products={order.products} totalPrice={order.totalPrice} />
|
||||
)
|
||||
})
|
||||
}
|
||||
</TabPane>
|
||||
</TabCommon>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<TabPane tabName={"Delivered"}>
|
||||
<div className={s.blank}></div>
|
||||
{
|
||||
delivered.map((order, i) => {
|
||||
return (
|
||||
<DeliveryItem key={order.id} id={order.id} status="Delivered" products={order.products} totalPrice={order.totalPrice} />
|
||||
)
|
||||
})
|
||||
}
|
||||
</TabPane>
|
||||
</TabCommon>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
@@ -1,9 +1,8 @@
|
||||
@import '../../../../styles/utilities';
|
||||
|
||||
.deliveryItem {
|
||||
@apply flex bg-white items-center custom-border-radius;
|
||||
@apply flex bg-white items-center shape-common;
|
||||
margin-bottom: 1.6rem;
|
||||
border: 1px solid var(--primary)
|
||||
}
|
||||
|
||||
.separator {
|
||||
|
@@ -9,20 +9,19 @@ import ReOrder from './components/ReOrder/ReOrder'
|
||||
|
||||
interface DeliveryItemProps {
|
||||
id: string;
|
||||
status: "waiting" | "delivering" | "delivered";
|
||||
status: "Waiting" | "Delivering" | "Delivered";
|
||||
products: string[];
|
||||
totalPrice: number;
|
||||
reOrderLink?: string;
|
||||
}
|
||||
|
||||
const DeliveryItem = ({ id, status, products, totalPrice, reOrderLink } : DeliveryItemProps) => {
|
||||
const DeliveryItem = ({ id, status, products, totalPrice } : DeliveryItemProps) => {
|
||||
return (
|
||||
<section className={s.deliveryItem}>
|
||||
<IdAndStatus id={id} status={status} />
|
||||
<div className={s.separator}></div>
|
||||
<Products products={products} />
|
||||
<TotalPrice totalPrice={totalPrice} />
|
||||
<ReOrder show={status==="delivered" ? true : false} href={reOrderLink} />
|
||||
<ReOrder show={status === "Delivered" ? true : false}/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
@@ -2,48 +2,7 @@
|
||||
|
||||
.idAndStatus {
|
||||
@apply items-center;
|
||||
padding: 2.4rem 0 2.4rem 1rem;
|
||||
|
||||
@screen md {
|
||||
padding: 2.4rem 0 2.4rem 1.2rem;
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
padding: 2.4rem 0 2.4rem 2.4rem;
|
||||
}
|
||||
|
||||
.id {
|
||||
@apply font-bold;
|
||||
margin-bottom: .8rem;
|
||||
}
|
||||
|
||||
.deliveryStatus {
|
||||
@apply font-bold text-white;
|
||||
font-size: 1.2rem;
|
||||
line-height: 2rem;
|
||||
padding: 0 .8rem;
|
||||
border-radius: 0.5rem;
|
||||
width: fit-content;
|
||||
|
||||
&.waiting {
|
||||
background-color: #D9A645;
|
||||
}
|
||||
&.delivering {
|
||||
background-color: var(--info-dark);
|
||||
}
|
||||
&.delivered {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
}
|
||||
}@import '../../../../../../styles/utilities';
|
||||
|
||||
.idAndStatus {
|
||||
@apply items-center;
|
||||
padding: 2.4rem 0 2.4rem 1rem;
|
||||
|
||||
@screen md {
|
||||
padding: 2.4rem 0 2.4rem 1.2rem;
|
||||
}
|
||||
padding: 2.4rem 0 2.4rem 1.2rem;
|
||||
|
||||
@screen lg {
|
||||
padding: 2.4rem 0 2.4rem 2.4rem;
|
||||
|
@@ -5,10 +5,10 @@ import s from './IdAndStatus.module.scss'
|
||||
|
||||
interface IdAndStatusProps {
|
||||
id?: string;
|
||||
status: "waiting" | "delivering" | "delivered";
|
||||
status: "Waiting" | "Delivering" | "Delivered";
|
||||
}
|
||||
|
||||
const IdAndStatus = ({ id, status="waiting" } : IdAndStatusProps) => {
|
||||
const IdAndStatus = ({ id, status="Waiting" } : IdAndStatusProps) => {
|
||||
return (
|
||||
<div className={s.idAndStatus}>
|
||||
<div className={s.id}>
|
||||
@@ -16,7 +16,7 @@ const IdAndStatus = ({ id, status="waiting" } : IdAndStatusProps) => {
|
||||
</div>
|
||||
<div className={classNames(s.deliveryStatus, {
|
||||
[s[status]]: status
|
||||
})}> {status}
|
||||
})}> {status.charAt(0).toUpperCase() + status.slice(1)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@@ -1,27 +1,9 @@
|
||||
@import '../../../../../../styles/utilities';
|
||||
|
||||
.reOrder {
|
||||
@apply text-white custom-border-radius hidden font-bold;
|
||||
padding: .4rem .6rem;
|
||||
margin-right: 1rem;
|
||||
background-color: var(--primary);
|
||||
|
||||
@screen md {
|
||||
padding: .4rem .6rem;
|
||||
margin-right: 1.2rem;
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
padding: .8rem 1.2rem;
|
||||
margin-right: 2.4rem;
|
||||
}
|
||||
|
||||
@apply hidden;
|
||||
margin-right: 1.2rem;
|
||||
&.show {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
}
|
@@ -1,21 +1,18 @@
|
||||
import classNames from "classnames"
|
||||
import React from "react"
|
||||
import { ButtonCommon } from "src/components/common"
|
||||
import s from './ReOrder.module.scss'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface ReOrderProps {
|
||||
show: boolean;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
const ReOrder = ({ show=false, href="#" } : ReOrderProps) => {
|
||||
const ReOrder = ({ show=false } : ReOrderProps) => {
|
||||
return (
|
||||
<div className={classNames(s.reOrder, {
|
||||
[s.show]: show
|
||||
})}>
|
||||
<Link href={href}>
|
||||
<a>Re-Order</a>
|
||||
</Link>
|
||||
<ButtonCommon>Re-Order</ButtonCommon>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@@ -2,11 +2,7 @@
|
||||
|
||||
.totalPrice {
|
||||
margin-left: auto;
|
||||
margin-right: 1rem;
|
||||
|
||||
@screen md {
|
||||
margin-right: 1.2rem;
|
||||
}
|
||||
margin-right: 1.2rem;
|
||||
|
||||
@screen lg {
|
||||
margin-right: 2.4rem;
|
||||
|
@@ -31,15 +31,12 @@
|
||||
width: min-content;
|
||||
|
||||
color: var(--white);
|
||||
font-size: 7rem;
|
||||
font-size: 8.8rem;
|
||||
line-height: 8rem;
|
||||
letter-spacing: -0.03em;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
|
||||
@screen 2xl {
|
||||
line-height: 8rem;
|
||||
}
|
||||
&::after {
|
||||
@apply absolute;
|
||||
content: "";
|
||||
|
@@ -1,16 +1,5 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.homeFeature {
|
||||
@apply spacing-horizontal;
|
||||
background-color: #ffffff;
|
||||
height: fit-content;
|
||||
margin: 3.2rem auto;
|
||||
@screen md {
|
||||
@apply grid grid-cols-3;
|
||||
grid-gap: 2.4rem;
|
||||
margin: 6.4rem auto;
|
||||
}
|
||||
@screen md {
|
||||
grid-gap: 4rem;
|
||||
}
|
||||
@apply spacing-horizontal-left bg-white;
|
||||
}
|
||||
|
@@ -1,23 +1,32 @@
|
||||
import React from 'react'
|
||||
import s from './HomeFeature.module.scss'
|
||||
|
||||
import HomeFeatureItem from './components/HomeFeatureItem/HomeFeatureItem'
|
||||
import FirstPic from './assets/10h30-11h.png'
|
||||
import SecondPic from './assets/8h.png'
|
||||
import ThirdPic from './assets/green.png'
|
||||
|
||||
const HomeFeature = () => {
|
||||
import HomeFeatureCarousel from './components/HomeFeatureCarousel/HomeFeatureCarousel'
|
||||
|
||||
const CAROUSEL_DATA = [
|
||||
{
|
||||
image: FirstPic,
|
||||
children: <span>Webshop owner will <b>upload products at 10:30pm </b>shoppers can buy <b>fresh products at 11pm.</b></span>,
|
||||
},
|
||||
{
|
||||
image: SecondPic,
|
||||
children: <span>Most fresh fish and seafood <b>will be listed at 8am </b>from inventory.</span>,
|
||||
},
|
||||
{
|
||||
image: ThirdPic,
|
||||
children: <span>Show that food will be shipped in <b>a greengrocery plastic bag</b>.</span>,
|
||||
},
|
||||
]
|
||||
|
||||
const HomeFeature = () => {
|
||||
return (
|
||||
<section className={s.homeFeature}>
|
||||
<HomeFeatureItem image="firstImg">
|
||||
<span> Webshop owner will <b>upload products at 10:30pm </b>shoppers can buy <b>fresh products at 11pm.</b></span>
|
||||
</HomeFeatureItem>
|
||||
|
||||
<HomeFeatureItem image="secondImg">
|
||||
<span>Most fresh fish and seafood <b>will be listed at 8am </b>from inventory.</span>
|
||||
</HomeFeatureItem>
|
||||
|
||||
<HomeFeatureItem image="thirdImg">
|
||||
<span>Show that food will be shipped in <b>a greengrocery plastic bag</b>.</span>
|
||||
</HomeFeatureItem>
|
||||
</section>
|
||||
<div className={s.homeFeature}>
|
||||
<HomeFeatureCarousel data={CAROUSEL_DATA} itemKey="Home Feature" isArrow={false} />
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,48 @@
|
||||
import React from 'react'
|
||||
import { TOptionsEvents } from 'keen-slider'
|
||||
|
||||
import HomeFeatureItem, {HomeFeatureItemProps} from '../HomeFeatureItem/HomeFeatureItem'
|
||||
import CarouselCommon, {CarouselCommonProps} from '../../../../../common/CarouselCommon/CarouselCommon'
|
||||
|
||||
interface HomeFeatureCarouselProps
|
||||
extends Omit<CarouselCommonProps<HomeFeatureItemProps>, 'Component' | "option"> {
|
||||
option?: TOptionsEvents
|
||||
}
|
||||
|
||||
const OPTION_DEFAULT: TOptionsEvents = {
|
||||
slidesPerView: 1.2,
|
||||
mode: 'free',
|
||||
breakpoints: {
|
||||
'(min-width: 640px)': {
|
||||
slidesPerView: 1.8,
|
||||
},
|
||||
'(min-width: 768px)': {
|
||||
slidesPerView: 2.1,
|
||||
},
|
||||
'(min-width: 1008px)': {
|
||||
slidesPerView: 2.3,
|
||||
},
|
||||
'(min-width: 1280px)': {
|
||||
slidesPerView: 2.8,
|
||||
},
|
||||
'(min-width: 1440px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const HomeFeatureCarousel = ({option, data, ...props} : HomeFeatureCarouselProps) => {
|
||||
return (
|
||||
<div>
|
||||
<CarouselCommon<HomeFeatureItemProps>
|
||||
data={data}
|
||||
Component={HomeFeatureItem}
|
||||
{...props}
|
||||
option={{ ...OPTION_DEFAULT, ...option }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default HomeFeatureCarousel
|
@@ -1,56 +1,18 @@
|
||||
@import "../../../../../../styles/utilities";
|
||||
|
||||
.homeFeatureItem {
|
||||
@apply flex items-center;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
|
||||
@screen md {
|
||||
@apply flex flex-col items-center justify-between;
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
@apply flex flex-row;
|
||||
}
|
||||
@apply flex items-center justify-center;
|
||||
margin-right: 0.8rem;
|
||||
height: 100%;
|
||||
|
||||
.itemImg {
|
||||
@apply flex float-left clear-both;
|
||||
margin-right: 2.4rem;
|
||||
align-items: center;
|
||||
&.firstImg {
|
||||
margin-top: 1rem;
|
||||
content: url("../../assets/10h30-11h-desktop.png");
|
||||
}
|
||||
&.secondImg {
|
||||
margin-top: 1rem;
|
||||
content: url("../../assets/8h-desktop.png");
|
||||
}
|
||||
&.thirdImg {
|
||||
margin-top: 1rem;
|
||||
content: url("../../assets/green-desktop.png");
|
||||
}
|
||||
|
||||
@screen md {
|
||||
@apply flex flex-col justify-center items-center;
|
||||
margin: auto;
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
|
||||
@screen lg {
|
||||
@apply flex float-left clear-both;
|
||||
margin-right: 2.4rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
max-width: 12rem;
|
||||
min-width: 8rem;
|
||||
}
|
||||
|
||||
.itemText {
|
||||
max-width: 28rem;
|
||||
min-width: 12rem;
|
||||
@screen md {
|
||||
@apply text-center;
|
||||
}
|
||||
@screen lg {
|
||||
@apply text-left;
|
||||
}
|
||||
min-width: none;
|
||||
}
|
||||
}
|
||||
|
@@ -1,21 +1,21 @@
|
||||
import React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import s from './HomeFeatureItem.module.scss'
|
||||
|
||||
import { StaticImage } from 'src/components/common'
|
||||
|
||||
interface HomeFeatureItemProps {
|
||||
image: string;
|
||||
children: any;
|
||||
export interface HomeFeatureItemProps {
|
||||
image: StaticImageData;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const HomeFeatureItem = ({ image, children }: HomeFeatureItemProps) => {
|
||||
|
||||
return (
|
||||
<div className={s.homeFeatureItem}>
|
||||
<img className={classNames(s.itemImg, {
|
||||
[s[image]]: image,
|
||||
})}
|
||||
alt="home feature item img" />
|
||||
<div className={s.itemImg}>
|
||||
<StaticImage src={image} alt="home feature item img" />
|
||||
</div>
|
||||
|
||||
<div className={s.itemText}>{children}</div>
|
||||
</div>
|
||||
)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Nunito&family=Poppins:wght@500&family=Righteous&display=swap");
|
||||
@import '../../public/fonts/style.css';
|
||||
|
||||
:root {
|
||||
--primary: #5b9a74;
|
||||
@@ -41,7 +42,7 @@
|
||||
--line-height: 2.4rem;
|
||||
|
||||
--font-sans: "Nunito", -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "Helvetica", sans-serif;
|
||||
--font-heading: "Righteous", -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "Helvetica", sans-serif;
|
||||
--font-heading: "Norquay-bold", "Righteous", -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "Helvetica", sans-serif;
|
||||
--font-logo: "Poppins", -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "Helvetica", sans-serif;
|
||||
}
|
||||
|
||||
@@ -79,6 +80,3 @@ html {
|
||||
font-feature-settings: "case" 1, "rlig" 1, "calt" 0;
|
||||
}
|
||||
|
||||
a {
|
||||
-webkit-tap-highlight-color: var(--primary);
|
||||
}
|
||||
|
@@ -122,7 +122,7 @@ module.exports = {
|
||||
'xl': '1280px',
|
||||
// => @media (min-width: 1280px) { ... }
|
||||
|
||||
'2xl': '1536px',
|
||||
'2xl': '1440px',
|
||||
// => @media (min-width: 1536px) { ... }
|
||||
},
|
||||
caroucel: {
|
||||
|