🔀 merge: branch 'common' of https://github.com/KieIO/grocery-vercel-commerce into m6-quangnhanimproveui

:%s
This commit is contained in:
lytrankieio123 2021-09-14 17:25:40 +07:00
commit a9f9330e8b
31 changed files with 354 additions and 58 deletions

3
next-env.d.ts vendored
View File

@ -1,3 +1,6 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

9
pages/_error.tsx Normal file
View File

@ -0,0 +1,9 @@
import { Layout } from 'src/components/common'
import { ErrorContent } from 'src/components/modules/error-page'
export default function NotFound() {
return (
<ErrorContent/>
)
}
NotFound.Layout = Layout

View File

@ -6,6 +6,7 @@ import MenuNavigationProductList from 'src/components/common/MenuNavigationProdu
// import { RecipeListPage } from 'src/components/modules/recipes';
import { OPTION_ALL, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils';
import { useModalCommon } from 'src/components/hooks';
const CATEGORY = [
{
name: 'All',
@ -33,7 +34,6 @@ const CATEGORY = [
},
]
const BRAND = [
{
name: 'Maggi',
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=veggie`,
@ -67,15 +67,14 @@ const FEATURED = [
link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=viewed`,
}
];
import CheckoutSuccess from 'src/components/modules/checkout/CheckoutSuccess/CheckoutSuccess'
import LoadingCommon from 'src/components/common/LoadingCommon/LoadingCommon'
import SkeletonParagraph from 'src/components/common/SkeletonCommon/SkeletonParagraph/SkeletonParagraph'
import SkeletonImage from 'src/components/common/SkeletonCommon/SkeletonImage/SkeletonImage'
export default function Test() {
const { visible: visibleMenuFilter, openModal, closeModal: closeMenuFilter } = useModalCommon({ initialValue: false })
const toggle = () => {
if (visibleMenuFilter) {
closeMenuFilter()
} else {
openModal()
}
}
return (
<>
<div className="shape-common-border">

View File

@ -8,7 +8,7 @@ interface Props {
const option = {
slidesPerView: 1,
breakpoints: {}
mode: 'free',
}
const Banner = memo(({ data }: Props) => {
if (data.length === 1) {

View File

@ -6,20 +6,28 @@
left: 0;
z-index: 9999;
margin-bottom: 3.2rem;
@screen md {
@apply relative;
}
&.full {
@apply shadow-none;
border: 1px solid var(--border-line);
}
.menu {
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
padding-left: 3.2rem;
padding-right: 3.2rem;
}
.logo {
@apply font-logo;
}
@screen md {
@apply relative;
&.full {
border: 1px solid var(--border-line);
}
.menu {
@apply shadow-none;
}
}
}
.headerSticky {

View File

@ -1,4 +1,3 @@
import classNames from 'classnames'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { memo, useMemo } from 'react'
@ -37,6 +36,10 @@ const HeaderMenu = memo(({ visibleFilter,openModalAuthen, openModalInfo, toggleF
link: '/account-not-login',
name: 'Account Not Login',
},
{
link: ROUTE.NOTIFICATION,
name: 'Notifications',
},
{
link: ROUTE.ACCOUNT,
name: 'Account',

View File

@ -25,7 +25,7 @@ const OPTION_MENU = [
isMarked: false,
},
{
link: `${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.NOTIFICATION}`,
link: ROUTE.NOTIFICATION,
name: 'Notifications',
icon: <IconNoti />,
isMarked: true,

View File

@ -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 } from '..'
import { CustomShapeSvg, ScrollToTop } from '..'
import Footer from '../Footer/Footer'
import Header from '../Header/Header'
import MenuNavigationProductList from '../MenuNavigationProductList/MenuNavigationProductList'
@ -34,6 +34,7 @@ const Layout: FC<Props> = ({ children }) => {
<CustomShapeSvg/>
<div className={s.filter}><MenuNavigationProductList categories={CATEGORY} brands={BRAND} featured={FEATURED} visible={visibleFilter} onClose={closeFilter}/> </div>
<ScrollToTop visibilityHeight={1500} />
<Footer />
</div>
</CommerceProvider>

View File

@ -0,0 +1,24 @@
@import '../../../styles/utilities';
.wrapper {
@apply text-center;
.loadingCommon {
@apply bg-white;
height: 7rem;
width: 7rem;
animation: spin 2s linear infinite;
margin: auto;
background: url('./assets/carrot.png') top 50% left 50% no-repeat;
}
.text {
@apply font-bold;
color: var(--primary);
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

View File

@ -0,0 +1,15 @@
import React from "react";
import s from './LoadingCommon.module.scss'
const LoadingCommon = () => {
return (
<div className={s.wrapper}>
<div className={s.loadingCommon}>
</div>
<p className={s.text}>Loading...</p>
</div>
)
}
export default LoadingCommon

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,20 +1,20 @@
@import '../../../styles/utilities';
@import "../../../styles/utilities";
.scrollToTop {
@apply hidden;
z-index: 9999;
@screen md {
&.show {
@apply block rounded-lg fixed cursor-pointer;
right: 11.2rem;
right: 6.4rem;
bottom: 21.6rem;
width: 6.4rem;
height: 6.4rem;
background-color: var(--border-line);
@screen lg {
right: 11.2rem;
}
&.hide {
@apply hidden;
}
}

View File

@ -1,44 +1,41 @@
import React, { useState, useEffect, MutableRefObject } from 'react'
import classNames from 'classnames'
import React, { useEffect, useState } from 'react'
import ArrowUp from '../../icons/IconArrowUp'
import s from './ScrollToTop.module.scss'
import ArrowUp from '../../icons/IconArrowUp'
interface ScrollToTopProps {
visibilityHeight?: number;
}
const ScrollToTop = ({ visibilityHeight = 450 }: ScrollToTopProps) => {
const [scrollPosition, setSrollPosition] = useState(0);
const [showScrollToTop, setShowScrollToTop] = useState("hide");
const [showScrollToTop, setShowScrollToTop] = useState<boolean>();
function handleVisibleButton() {
const position = window.pageYOffset;
setSrollPosition(position);
const scrollPosition = window.scrollY;
if (scrollPosition > visibilityHeight) {
return setShowScrollToTop("show")
} else if (scrollPosition < visibilityHeight) {
return setShowScrollToTop("hide");
setShowScrollToTop(true)
} else {
setShowScrollToTop(false)
}
};
useEffect(() => {
window.addEventListener("scroll", handleVisibleButton);
return () => {
window.removeEventListener("scroll", handleVisibleButton);
}
}, []);
function handleScrollUp() {
window.scrollTo(0, 0);
}
function addEventScroll() {
window.addEventListener("scroll", handleVisibleButton);
}
useEffect(() => {
addEventScroll();
});
return (
<div className={classNames(s.scrollToTop, {
[s[`${showScrollToTop}`]]: showScrollToTop
[s.show]: showScrollToTop
})}
onClick={handleScrollUp}
>

View File

@ -0,0 +1,53 @@
@import '../../../../styles/utilities';
.skeletonImage {
@apply relative;
background: #DDDBDD;
&.small {
width: 10rem;
height: 10rem;
}
&.default {
width: 15rem;
height: 15rem;
}
&.large {
width: 20rem;
height: 20rem;
}
&.left {
margin-left: 0;
}
&.center {
margin: auto;
}
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(#fff, 0) 0,
rgba(#fff, 0.2) 20%,
rgba(#fff, 0.5) 60%,
rgba(#fff, 0)
);
animation: shimmer 2s infinite;
content: '';
}
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}

View File

@ -0,0 +1,20 @@
import classNames from "classnames";
import React from "react";
import s from './SkeletonImage.module.scss'
interface SkeletonImageProps {
align?: "left" | "center"
size?: "small" | "default" | "large"
}
const SkeletonImage = ({ align="center", size="default" }: SkeletonImageProps) => {
return (
<div className={classNames(s.skeletonImage, {
[s[size]] : size,
[s[align]] : align
})}>
</div>
)
}
export default SkeletonImage

View File

@ -0,0 +1,65 @@
@import '../../../../styles/utilities';
.skeletonParagraph {
margin: 0 1.6rem;
.row {
display: inline-block;
height: 2rem;
width: 100%;
position: relative;
overflow: hidden;
background-color: #DDDBDD;
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(#fff, 0) 0,
rgba(#fff, 0.2) 20%,
rgba(#fff, 0.5) 60%,
rgba(#fff, 0)
);
animation: shimmer 2s infinite;
content: '';
}
}
.lastRow {
display: inline-block;
height: 2rem;
width: 80%;
position: relative;
overflow: hidden;
background-color: #DDDBDD;
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(#fff, 0) 0,
rgba(#fff, 0.2) 20%,
rgba(#fff, 0.5) 60%,
rgba(#fff, 0)
);
animation: shimmer 2s infinite;
content: '';
}
}
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}

View File

@ -0,0 +1,23 @@
import React from "react";
import s from './SkeletonParagraph.module.scss'
interface SkeletonParagraphProps {
rows?: number // number of rows in paragraph
}
const SkeletonParagraph = ({ rows=2 }: SkeletonParagraphProps) => {
return (
<div className={s.skeletonParagraph}>
{
[...Array(rows)].map((e, i) => {
if (i === rows-1) {
return <div key={i} className={s.lastRow}></div>
}
return <div key={i} className={s.row}></div>
})
}
</div>
)
}
export default SkeletonParagraph

View File

@ -6,7 +6,7 @@
background-color: white;
.inner{
height: 100vh;
height: 70vh;
.logo{
margin-top: 2rem;
}

View File

@ -0,0 +1,38 @@
@import '../../../../styles/utilities';
.checkoutSuccessWrapper {
@apply flex items-center justify-center;
margin-top: -3.2rem;
.checkoutSuccess {
border-radius: 80% 90% 18% 10% / 20% 10% 27% 20%;
max-width: 77.6rem;
height: fit-content;
background:
url('./assets/veget.png') left 0 top 0 no-repeat,
url('./assets/fish.png') right 0 top 0 no-repeat,
url('./assets/freezeShrimp.png') right 0 bottom 0 no-repeat,
url('./assets/coffeeBean.png') left 0 bottom 0 no-repeat;
background-color: #E3F2E9;
.checkoutContent {
@apply text-center;
margin: 7.2rem 4.8rem 6.4rem 4.8rem;
.checkoutMsg {
@apply heading-1 font-heading;
margin-top: 3.2rem;
margin-bottom: 1.6rem;
}
.checkoutSubMsg {
@apply sub-headline;
margin-bottom: 4rem;
}
.backToHomeBtn {
@apply flex justify-center;
}
}
}
}

View File

@ -0,0 +1,34 @@
import React from "react";
import s from './CheckoutSuccess.module.scss';
import Link from "next/link";
import checkIcon from './assets/checkIcon.png';
import { ButtonCommon, StaticImage } from "src/components/common";
import { IconArrowRight } from "src/components/icons";
const CheckoutSuccess = () => {
return (
<div className={s.checkoutSuccessWrapper}>
<div className={s.checkoutSuccess}>
<div className={s.checkoutContent}>
<StaticImage src={checkIcon} alt="check icon" />
<div className={s.checkoutMsg}>Your purchase has been successed!</div>
<div className={s.checkoutSubMsg}>Last call! Shop deep deals on 100+ bulk picks while you can.</div>
<div className={s.backToHomeBtn}>
<Link href="/">
<a>
<ButtonCommon size="large" icon={<IconArrowRight />} isIconSuffix={true}>Back to home</ButtonCommon>
</a>
</Link>
</div>
</div>
</div>
</div>
)
}
export default CheckoutSuccess

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -6,7 +6,7 @@
background-color: white;
.inner{
height: 100vh;
height: 70vh;
.logo{
margin-top: 2rem;
}

View File

@ -1,12 +1,12 @@
import Link from 'next/link';
import React from 'react';
import { ButtonCommon } from 'src/components/common';
import s from './ErrorPage.module.scss';
import s from './ErrorContent.module.scss';
interface Props {
}
const ErrorPage = ({ }: Props) => {
const ErrorContent = ({ }: Props) => {
return (
<div className={s.wrapper}>
@ -26,4 +26,4 @@ const ErrorPage = ({ }: Props) => {
)
}
export default ErrorPage
export default ErrorContent

View File

@ -0,0 +1 @@
export {default as ErrorContent} from './ErrorContent/ErrorContent';

View File

@ -1 +0,0 @@
export {default as ErrorPage} from './ErrorPage/ErrorPage';

View File

@ -31,11 +31,15 @@
width: min-content;
color: var(--white);
font-size: 8.8rem;
font-size: 7rem;
line-height: 8rem;
letter-spacing: -0.03em;
font-weight: bold;
text-transform: uppercase;
@screen 2xl {
line-height: 8rem;
}
&::after {
@apply absolute;
content: "";

View File

@ -21,6 +21,7 @@ export const ROUTE = {
RECIPES: '/recipes',
RECIPE_DETAIL: '/recipe',
NOTIFICATION: '/notifications',
BUSSINESS: '/bussiness',
CONTACT: '/contact',
CHECKOUT: '/checkout',
@ -35,7 +36,6 @@ export const ACCOUNT_TAB = {
CUSTOMER_INFO: '',
ORDER: 'orders',
FAVOURITE: 'wishlist',
NOTIFICATION: 'notification',
}
export const QUERY_KEY = {