v3
This commit is contained in:
parent
38bdf4c3c5
commit
0ad3ce27a4
@ -291,11 +291,20 @@ module.exports = class OrdersDBApi {
|
|||||||
const transaction = (options && options.transaction) || undefined;
|
const transaction = (options && options.transaction) || undefined;
|
||||||
|
|
||||||
let include = [
|
let include = [
|
||||||
|
{
|
||||||
|
model: db.order_items,
|
||||||
|
as: 'order_items_order',
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: db.products,
|
||||||
|
as: 'product',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
model: db.customers,
|
model: db.customers,
|
||||||
as: 'customer',
|
as: 'customer',
|
||||||
|
required: false,
|
||||||
where: filter.customer ? {
|
where: filter.customer ? {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ id: { [Op.in]: filter.customer.split('|').map(term => Utils.uuid(term)) } },
|
{ id: { [Op.in]: filter.customer.split('|').map(term => Utils.uuid(term)) } },
|
||||||
|
|||||||
84
frontend/src/components/LoginModal.tsx
Normal file
84
frontend/src/components/LoginModal.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Field, Form, Formik } from 'formik';
|
||||||
|
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||||
|
import { loginUser } from '../stores/authSlice';
|
||||||
|
import CardBoxModal from './CardBoxModal';
|
||||||
|
import FormField from './FormField';
|
||||||
|
import BaseButton from './BaseButton';
|
||||||
|
import BaseButtons from './BaseButtons';
|
||||||
|
import { mdiEye, mdiEyeOff } from '@mdi/js';
|
||||||
|
import BaseIcon from './BaseIcon';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isActive: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoginModal = ({ isActive, onClose }: Props) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { isFetching } = useAppSelector((state) => state.auth);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = (values) => {
|
||||||
|
dispatch(loginUser(values))
|
||||||
|
.unwrap()
|
||||||
|
.then(() => {
|
||||||
|
onClose();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// error is handled by slice
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
setShowPassword(!showPassword);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CardBoxModal
|
||||||
|
title="Login"
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
|
>
|
||||||
|
<Formik
|
||||||
|
initialValues={{ email: 'admin@flatlogic.com', password: 'eea84746' }}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<FormField label="Login" help="Please enter your login">
|
||||||
|
<Field name="email" />
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<FormField label="Password" help="Please enter your password">
|
||||||
|
<Field name="password" type={showPassword ? 'text' : 'password'} />
|
||||||
|
</FormField>
|
||||||
|
<div
|
||||||
|
className="absolute bottom-8 right-0 pr-3 flex items-center cursor-pointer"
|
||||||
|
onClick={togglePasswordVisibility}
|
||||||
|
>
|
||||||
|
<BaseIcon
|
||||||
|
className="text-gray-500 hover:text-gray-700"
|
||||||
|
size={20}
|
||||||
|
path={showPassword ? mdiEyeOff : mdiEye}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BaseButtons>
|
||||||
|
<BaseButton
|
||||||
|
className="w-full"
|
||||||
|
type="submit"
|
||||||
|
label={isFetching ? 'Loading...' : 'Login'}
|
||||||
|
color="info"
|
||||||
|
disabled={isFetching}
|
||||||
|
/>
|
||||||
|
</BaseButtons>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
</CardBoxModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoginModal;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { ReactNode, useState, useEffect } from 'react'
|
import React, { ReactNode, useState, useEffect, useContext } from 'react'
|
||||||
import { mdiClose, mdiDotsVertical } from '@mdi/js'
|
import { mdiClose, mdiDotsVertical } from '@mdi/js'
|
||||||
import { containerMaxW } from '../config'
|
import { containerMaxW } from '../config'
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
@ -6,6 +6,7 @@ import NavBarItemPlain from './NavBarItemPlain'
|
|||||||
import NavBarMenuList from './NavBarMenuList'
|
import NavBarMenuList from './NavBarMenuList'
|
||||||
import { MenuNavBarItem } from '../interfaces'
|
import { MenuNavBarItem } from '../interfaces'
|
||||||
import { useAppSelector } from '../stores/hooks';
|
import { useAppSelector } from '../stores/hooks';
|
||||||
|
import { ModalContext } from '../context/ModalContext';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuNavBarItem[]
|
menu: MenuNavBarItem[]
|
||||||
@ -19,6 +20,7 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
const { orders } = useAppSelector((state) => state.orders)
|
const { orders } = useAppSelector((state) => state.orders)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth)
|
const { currentUser } = useAppSelector((state) => state.auth)
|
||||||
|
const { openLoginModal } = useContext(ModalContext);
|
||||||
|
|
||||||
const pendingOrder = orders.find(order => order.status === 'pending' && order.customer?.id === currentUser?.id);
|
const pendingOrder = orders.find(order => order.status === 'pending' && order.customer?.id === currentUser?.id);
|
||||||
const cartItemsCount = pendingOrder?.order_items_order?.reduce((acc, item) => acc + item.quantity, 0) || 0;
|
const cartItemsCount = pendingOrder?.order_items_order?.reduce((acc, item) => acc + item.quantity, 0) || 0;
|
||||||
@ -38,6 +40,13 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
setIsMenuNavBarActive(!isMenuNavBarActive)
|
setIsMenuNavBarActive(!isMenuNavBarActive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const menuToShow = menu.filter((item) => {
|
||||||
|
if (currentUser) {
|
||||||
|
return !item.isLogin
|
||||||
|
}
|
||||||
|
return !item.isLogout && !item.isCurrentUser
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
className={`${className} top-0 inset-x-0 fixed ${bgColor} h-14 z-30 transition-position w-screen lg:w-auto dark:bg-dark-800`}
|
className={`${className} top-0 inset-x-0 fixed ${bgColor} h-14 z-30 transition-position w-screen lg:w-auto dark:bg-dark-800`}
|
||||||
@ -54,7 +63,7 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
isMenuNavBarActive ? 'block' : 'hidden'
|
isMenuNavBarActive ? 'block' : 'hidden'
|
||||||
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 ${bgColor} shadow-lg lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 ${bgColor} shadow-lg lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
||||||
>
|
>
|
||||||
<NavBarMenuList menu={menu} cartItemsCount={cartItemsCount} />
|
<NavBarMenuList menu={menuToShow} cartItemsCount={cartItemsCount} openLoginModal={openLoginModal} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@ -15,10 +15,11 @@ import ClickOutside from "./ClickOutside";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
item: MenuNavBarItem,
|
item: MenuNavBarItem,
|
||||||
cartItemsCount?: number
|
cartItemsCount?: number,
|
||||||
|
openLoginModal?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavBarItem({ item, cartItemsCount }: Props) {
|
export default function NavBarItem({ item, cartItemsCount, openLoginModal }: Props) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const excludedRef = useRef(null);
|
const excludedRef = useRef(null);
|
||||||
@ -59,9 +60,12 @@ export default function NavBarItem({ item, cartItemsCount }: Props) {
|
|||||||
dispatch(setDarkMode(null))
|
dispatch(setDarkMode(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.isLogin) {
|
||||||
|
openLoginModal();
|
||||||
|
}
|
||||||
|
|
||||||
if(item.isLogout) {
|
if(item.isLogout) {
|
||||||
dispatch(logoutUser())
|
dispatch(logoutUser())
|
||||||
router.push('/login')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +97,13 @@ export default function NavBarItem({ item, cartItemsCount }: Props) {
|
|||||||
item.isDesktopNoLabel && item.icon ? 'lg:hidden' : ''
|
item.isDesktopNoLabel && item.icon ? 'lg:hidden' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{itemLabel} {item.label === 'Cart' && cartItemsCount > 0 && `(${cartItemsCount})`}
|
{itemLabel}
|
||||||
</span>
|
</span>
|
||||||
|
{item.label === 'Cart' && cartItemsCount > 0 && (
|
||||||
|
<div className="bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">
|
||||||
|
{cartItemsCount}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{item.isCurrentUser && <UserAvatarCurrentUser className="w-6 h-6 mr-3 inline-flex" />}
|
{item.isCurrentUser && <UserAvatarCurrentUser className="w-6 h-6 mr-3 inline-flex" />}
|
||||||
{item.menu && (
|
{item.menu && (
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
|
|||||||
@ -4,15 +4,16 @@ import NavBarItem from './NavBarItem'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuNavBarItem[],
|
menu: MenuNavBarItem[],
|
||||||
cartItemsCount?: number
|
cartItemsCount?: number,
|
||||||
|
openLoginModal?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavBarMenuList({ menu, cartItemsCount }: Props) {
|
export default function NavBarMenuList({ menu, cartItemsCount, openLoginModal }: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{menu.map((item, index) => (
|
{menu.map((item, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<NavBarItem item={item} cartItemsCount={cartItemsCount} />
|
<NavBarItem item={item} cartItemsCount={cartItemsCount} openLoginModal={openLoginModal} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|||||||
30
frontend/src/context/ModalContext.tsx
Normal file
30
frontend/src/context/ModalContext.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import React, { createContext, useState, ReactNode } from 'react';
|
||||||
|
|
||||||
|
export const ModalContext = createContext({
|
||||||
|
isLoginModalActive: false,
|
||||||
|
openLoginModal: () => {},
|
||||||
|
closeLoginModal: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ModalProvider = ({ children }: Props) => {
|
||||||
|
const [isLoginModalActive, setIsLoginModalActive] = useState(false);
|
||||||
|
|
||||||
|
const openLoginModal = () => {
|
||||||
|
setIsLoginModalActive(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeLoginModal = () => {
|
||||||
|
setIsLoginModalActive(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContext.Provider value={{ isLoginModalActive, openLoginModal, closeLoginModal }}>
|
||||||
|
{children}
|
||||||
|
</ModalContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
27
frontend/src/layouts/LayoutShop.tsx
Normal file
27
frontend/src/layouts/LayoutShop.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
import React, { ReactNode, useContext } from 'react';
|
||||||
|
import { useAppSelector } from '../stores/hooks';
|
||||||
|
import NavBar from '../components/NavBar';
|
||||||
|
import menuNavBar from '../menuNavBar';
|
||||||
|
import LoginModal from '../components/LoginModal';
|
||||||
|
import { ModalContext } from '../context/ModalContext';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LayoutShop({ children }: Props) {
|
||||||
|
const darkMode = useAppSelector((state) => state.style.darkMode);
|
||||||
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
|
const { isLoginModalActive, closeLoginModal } = useContext(ModalContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={darkMode ? 'dark' : ''}>
|
||||||
|
<div className={`${bgColor} dark:bg-slate-800 dark:text-slate-100 pt-14`}>
|
||||||
|
<NavBar menu={menuNavBar} />
|
||||||
|
{children}
|
||||||
|
<LoginModal isActive={isLoginModalActive} onClose={closeLoginModal} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -10,7 +10,8 @@ import {
|
|||||||
mdiThemeLightDark,
|
mdiThemeLightDark,
|
||||||
mdiGithub,
|
mdiGithub,
|
||||||
mdiVuejs,
|
mdiVuejs,
|
||||||
mdiCart
|
mdiCart,
|
||||||
|
mdiLogin,
|
||||||
} from '@mdi/js'
|
} from '@mdi/js'
|
||||||
import { MenuNavBarItem } from './interfaces'
|
import { MenuNavBarItem } from './interfaces'
|
||||||
|
|
||||||
@ -44,6 +45,11 @@ const menuNavBar: MenuNavBarItem[] = [
|
|||||||
isDesktopNoLabel: true,
|
isDesktopNoLabel: true,
|
||||||
isToggleLightDark: true,
|
isToggleLightDark: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: mdiLogin,
|
||||||
|
label: 'Login',
|
||||||
|
isLogin: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: mdiLogout,
|
icon: mdiLogout,
|
||||||
label: 'Log out',
|
label: 'Log out',
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { appWithTranslation } from 'next-i18next';
|
|||||||
import '../i18n';
|
import '../i18n';
|
||||||
import IntroGuide from '../components/IntroGuide';
|
import IntroGuide from '../components/IntroGuide';
|
||||||
import { appSteps, loginSteps, usersSteps, rolesSteps } from '../stores/introSteps';
|
import { appSteps, loginSteps, usersSteps, rolesSteps } from '../stores/introSteps';
|
||||||
|
import { ModalProvider } from '../context/ModalContext';
|
||||||
|
|
||||||
// Initialize axios
|
// Initialize axios
|
||||||
axios.defaults.baseURL = process.env.NEXT_PUBLIC_BACK_API
|
axios.defaults.baseURL = process.env.NEXT_PUBLIC_BACK_API
|
||||||
@ -158,42 +159,44 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
{getLayout(
|
<ModalProvider>
|
||||||
<>
|
{getLayout(
|
||||||
<Head>
|
<>
|
||||||
<meta name="description" content={description} />
|
<Head>
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
|
||||||
<meta property="og:url" content={url} />
|
<meta property="og:url" content={url} />
|
||||||
<meta property="og:site_name" content="https://flatlogic.com/" />
|
<meta property="og:site_name" content="https://flatlogic.com/" />
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={title} />
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
<meta property="og:image" content={image} />
|
<meta property="og:image" content={image} />
|
||||||
<meta property="og:image:type" content="image/png" />
|
<meta property="og:image:type" content="image/png" />
|
||||||
<meta property="og:image:width" content={imageWidth} />
|
<meta property="og:image:width" content={imageWidth} />
|
||||||
<meta property="og:image:height" content={imageHeight} />
|
<meta property="og:image:height" content={imageHeight} />
|
||||||
|
|
||||||
<meta property="twitter:card" content="summary_large_image" />
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
<meta property="twitter:title" content={title} />
|
<meta property="twitter:title" content={title} />
|
||||||
<meta property="twitter:description" content={description} />
|
<meta property="twitter:description" content={description} />
|
||||||
<meta property="twitter:image:src" content={image} />
|
<meta property="twitter:image:src" content={image} />
|
||||||
<meta property="twitter:image:width" content={imageWidth} />
|
<meta property="twitter:image:width" content={imageWidth} />
|
||||||
<meta property="twitter:image:height" content={imageHeight} />
|
<meta property="twitter:image:height" content={imageHeight} />
|
||||||
|
|
||||||
<link rel="icon" href="/favicon.svg" />
|
<link rel="icon" href="/favicon.svg" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<IntroGuide
|
<IntroGuide
|
||||||
steps={steps}
|
steps={steps}
|
||||||
stepsName={stepName}
|
stepsName={stepName}
|
||||||
stepsEnabled={stepsEnabled}
|
stepsEnabled={stepsEnabled}
|
||||||
onExit={handleExit}
|
onExit={handleExit}
|
||||||
/>
|
/>
|
||||||
{(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev_stage') && <DevModeBadge />}
|
{(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev_stage') && <DevModeBadge />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</ModalProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,16 @@
|
|||||||
|
|
||||||
import React, { ReactElement, useEffect } from 'react';
|
import React, { ReactElement, useEffect } from 'react';
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import {
|
import {
|
||||||
useAppDispatch,
|
useAppDispatch,
|
||||||
useAppSelector
|
useAppSelector
|
||||||
} from "../../stores/hooks";
|
} from "../stores/hooks";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { fetch as fetchOrders } from '../../stores/orders/ordersSlice'
|
import { fetch as fetchOrders } from '../stores/orders/ordersSlice'
|
||||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
import LayoutShop from "../layouts/LayoutShop";
|
||||||
import { getPageTitle } from "../../config";
|
import { getPageTitle } from "../config";
|
||||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
import SectionTitleLineWithButton from "../components/SectionTitleLineWithButton";
|
||||||
import SectionMain from "../../components/SectionMain";
|
import SectionMain from "../components/SectionMain";
|
||||||
import CardBox from "../../components/CardBox";
|
import BaseButton from "../components/BaseButton";
|
||||||
import BaseButton from "../../components/BaseButton";
|
|
||||||
import { mdiCart } from "@mdi/js";
|
import { mdiCart } from "@mdi/js";
|
||||||
|
|
||||||
const CartPage = () => {
|
const CartPage = () => {
|
||||||
@ -21,11 +19,17 @@ const CartPage = () => {
|
|||||||
const { orders } = useAppSelector((state) => state.orders)
|
const { orders } = useAppSelector((state) => state.orders)
|
||||||
const { currentUser } = useAppSelector((state) => state.auth)
|
const { currentUser } = useAppSelector((state) => state.auth)
|
||||||
|
|
||||||
const pendingOrder = orders.find(order => order.status === 'pending' && order.customer?.id === currentUser?.id);
|
const pendingOrder =
|
||||||
|
orders &&
|
||||||
|
orders.find(
|
||||||
|
(order) => order.status === 'Pending' && order.customer?.id === currentUser?.user.id,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchOrders({ query: '?status=pending' }));
|
if (currentUser) {
|
||||||
}, [dispatch]);
|
dispatch(fetchOrders({ query: '?status=Pending' }));
|
||||||
|
}
|
||||||
|
}, [dispatch, currentUser]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -35,14 +39,12 @@ const CartPage = () => {
|
|||||||
<SectionMain>
|
<SectionMain>
|
||||||
<SectionTitleLineWithButton icon={mdiCart} title={'Shopping Cart'} main>
|
<SectionTitleLineWithButton icon={mdiCart} title={'Shopping Cart'} main>
|
||||||
</SectionTitleLineWithButton>
|
</SectionTitleLineWithButton>
|
||||||
<CardBox>
|
|
||||||
<>
|
<>
|
||||||
<p className={'block font-bold mb-2'}>Order Items</p>
|
<p className={'block font-bold mb-2'}>Order Items</p>
|
||||||
<CardBox
|
<div
|
||||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||||
hasTable
|
|
||||||
>
|
>
|
||||||
<div className='overflow-x-auto'>
|
<div className={'overflow-x-auto'}>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -57,7 +59,7 @@ const CartPage = () => {
|
|||||||
pendingOrder.order_items_order.map((item: any) => (
|
pendingOrder.order_items_order.map((item: any) => (
|
||||||
<tr key={item.id}>
|
<tr key={item.id}>
|
||||||
<td data-label="product_name">
|
<td data-label="product_name">
|
||||||
{item.product_name}
|
{item.product.name}
|
||||||
</td>
|
</td>
|
||||||
<td data-label="quantity">
|
<td data-label="quantity">
|
||||||
{item.quantity}
|
{item.quantity}
|
||||||
@ -74,14 +76,13 @@ const CartPage = () => {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{!pendingOrder?.order_items_order?.length && <div className={'text-center py-4'}>Your cart is empty</div>}
|
{!pendingOrder?.order_items_order?.length && <div className={'text-center py-4'}>Your cart is empty</div>}
|
||||||
</CardBox>
|
</div>
|
||||||
</>
|
</>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
color='info'
|
color='info'
|
||||||
label='Checkout'
|
label='Checkout'
|
||||||
onClick={() => router.push('/checkout')}
|
onClick={() => router.push('/checkout')}
|
||||||
/>
|
/>
|
||||||
</CardBox>
|
|
||||||
</SectionMain>
|
</SectionMain>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -89,10 +90,10 @@ const CartPage = () => {
|
|||||||
|
|
||||||
CartPage.getLayout = function getLayout(page: ReactElement) {
|
CartPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
<LayoutAuthenticated
|
<LayoutShop
|
||||||
>
|
>
|
||||||
{page}
|
{page}
|
||||||
</LayoutAuthenticated>
|
</LayoutShop>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,38 +1,41 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useContext } from 'react';
|
||||||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||||||
import { fetch as fetchProducts } from '../stores/products/productsSlice';
|
import { fetch as fetchProducts } from '../stores/products/productsSlice';
|
||||||
import LayoutGuest from '../layouts/Guest';
|
|
||||||
import CardBox from '../components/CardBox';
|
import CardBox from '../components/CardBox';
|
||||||
import { getPageTitle } from '../config';
|
import { getPageTitle } from '../config';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import SectionMain from '../components/SectionMain';
|
import SectionMain from '../components/SectionMain';
|
||||||
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
|
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
|
||||||
import { mdiStorefrontOutline } from '@mdi/js';
|
import { mdiStorefrontOutline } from '@mdi/js';
|
||||||
import { create as createOrder } from '../stores/orders/ordersSlice';
|
import { create as createOrder, fetch as fetchOrders } from '../stores/orders/ordersSlice';
|
||||||
import { create as createOrderItem } from '../stores/order_items/order_itemsSlice';
|
import { create as createOrderItem } from '../stores/order_items/order_itemsSlice';
|
||||||
|
import { ModalContext } from '../context/ModalContext';
|
||||||
|
import LayoutShop from '../layouts/LayoutShop';
|
||||||
|
|
||||||
const IndexPage = () => {
|
const IndexPage = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { products, loading } = useAppSelector((state) => state.products);
|
const { products, loading } = useAppSelector((state) => state.products);
|
||||||
const { orders } = useAppSelector((state) => state.orders);
|
const { orders } = useAppSelector((state) => state.orders);
|
||||||
const { currentUser } = useAppSelector((state) => state.auth);
|
const { currentUser } = useAppSelector((state) => state.auth);
|
||||||
|
const { openLoginModal } = useContext(ModalContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchProducts({}));
|
dispatch(fetchProducts({}));
|
||||||
|
dispatch(fetchOrders({}));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const handleAddToCart = async (product) => {
|
const handleAddToCart = async (product) => {
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
alert('Please login to add items to your cart.');
|
openLoginModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pendingOrder = orders.find(order => order.status === 'pending' && order.customer?.id === currentUser.id);
|
let pendingOrder = orders.find(order => order.status === 'Pending' && order.customer?.id === currentUser.id);
|
||||||
|
|
||||||
if (!pendingOrder) {
|
if (!pendingOrder) {
|
||||||
const orderData = {
|
const orderData = {
|
||||||
customer: currentUser.id,
|
customer: currentUser.id,
|
||||||
status: 'pending',
|
status: 'Pending',
|
||||||
};
|
};
|
||||||
const newOrderAction = await dispatch(createOrder(orderData));
|
const newOrderAction = await dispatch(createOrder(orderData));
|
||||||
if (newOrderAction.payload) {
|
if (newOrderAction.payload) {
|
||||||
@ -52,17 +55,13 @@ const IndexPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutGuest>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{getPageTitle('Shop')}</title>
|
<title>{getPageTitle('Shop')}</title>
|
||||||
</Head>
|
</Head>
|
||||||
<SectionMain>
|
<SectionMain>
|
||||||
<SectionTitleLineWithButton icon={mdiStorefrontOutline} title="Our Products" main>
|
<SectionTitleLineWithButton icon={mdiStorefrontOutline} title="Our Products" main />
|
||||||
</SectionTitleLineWithButton>
|
|
||||||
|
|
||||||
{loading && <div>Loading...</div>}
|
{loading && <div>Loading...</div>}
|
||||||
|
|
||||||
@ -89,8 +88,12 @@ const IndexPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</SectionMain>
|
</SectionMain>
|
||||||
</LayoutGuest>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IndexPage.getLayout = function getLayout(page) {
|
||||||
|
return <LayoutShop>{page}</LayoutShop>;
|
||||||
|
};
|
||||||
|
|
||||||
export default IndexPage;
|
export default IndexPage;
|
||||||
@ -86,8 +86,10 @@ export const authSlice = createSlice({
|
|||||||
const token = action.payload;
|
const token = action.payload;
|
||||||
const user = jwt.decode(token);
|
const user = jwt.decode(token);
|
||||||
|
|
||||||
|
state.isFetching = false;
|
||||||
state.errorMessage = '';
|
state.errorMessage = '';
|
||||||
state.token = token;
|
state.token = token;
|
||||||
|
state.currentUser = user;
|
||||||
localStorage.setItem('token', token);
|
localStorage.setItem('token', token);
|
||||||
localStorage.setItem('user', JSON.stringify(user));
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
|
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user