v2
This commit is contained in:
parent
eee374359b
commit
38bdf4c3c5
@ -5,7 +5,7 @@ import BaseIcon from './BaseIcon'
|
||||
import NavBarItemPlain from './NavBarItemPlain'
|
||||
import NavBarMenuList from './NavBarMenuList'
|
||||
import { MenuNavBarItem } from '../interfaces'
|
||||
import { useAppSelector } from '../stores/hooks';
|
||||
import { useAppSelector } from '../stores/hooks';
|
||||
|
||||
type Props = {
|
||||
menu: MenuNavBarItem[]
|
||||
@ -17,6 +17,11 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
||||
const [isMenuNavBarActive, setIsMenuNavBarActive] = useState(false)
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||
const { orders } = useAppSelector((state) => state.orders)
|
||||
const { currentUser } = useAppSelector((state) => state.auth)
|
||||
|
||||
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;
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
@ -49,7 +54,7 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
||||
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`}
|
||||
>
|
||||
<NavBarMenuList menu={menu} />
|
||||
<NavBarMenuList menu={menu} cartItemsCount={cartItemsCount} />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@ -14,10 +14,11 @@ import { useRouter } from 'next/router';
|
||||
import ClickOutside from "./ClickOutside";
|
||||
|
||||
type Props = {
|
||||
item: MenuNavBarItem
|
||||
item: MenuNavBarItem,
|
||||
cartItemsCount?: number
|
||||
}
|
||||
|
||||
export default function NavBarItem({ item }: Props) {
|
||||
export default function NavBarItem({ item, cartItemsCount }: Props) {
|
||||
const router = useRouter();
|
||||
const dispatch = useAppDispatch();
|
||||
const excludedRef = useRef(null);
|
||||
@ -92,7 +93,7 @@ export default function NavBarItem({ item }: Props) {
|
||||
item.isDesktopNoLabel && item.icon ? 'lg:hidden' : ''
|
||||
}`}
|
||||
>
|
||||
{itemLabel}
|
||||
{itemLabel} {item.label === 'Cart' && cartItemsCount > 0 && `(${cartItemsCount})`}
|
||||
</span>
|
||||
{item.isCurrentUser && <UserAvatarCurrentUser className="w-6 h-6 mr-3 inline-flex" />}
|
||||
{item.menu && (
|
||||
|
||||
@ -3,15 +3,16 @@ import { MenuNavBarItem } from '../interfaces'
|
||||
import NavBarItem from './NavBarItem'
|
||||
|
||||
type Props = {
|
||||
menu: MenuNavBarItem[]
|
||||
menu: MenuNavBarItem[],
|
||||
cartItemsCount?: number
|
||||
}
|
||||
|
||||
export default function NavBarMenuList({ menu }: Props) {
|
||||
export default function NavBarMenuList({ menu, cartItemsCount }: Props) {
|
||||
return (
|
||||
<>
|
||||
{menu.map((item, index) => (
|
||||
<div key={index}>
|
||||
<NavBarItem item={item} />
|
||||
<NavBarItem item={item} cartItemsCount={cartItemsCount} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
|
||||
@ -10,10 +10,16 @@ import {
|
||||
mdiThemeLightDark,
|
||||
mdiGithub,
|
||||
mdiVuejs,
|
||||
mdiCart
|
||||
} from '@mdi/js'
|
||||
import { MenuNavBarItem } from './interfaces'
|
||||
|
||||
const menuNavBar: MenuNavBarItem[] = [
|
||||
{
|
||||
icon: mdiCart,
|
||||
label: 'Cart',
|
||||
href: '/cart',
|
||||
},
|
||||
{
|
||||
isCurrentUser: true,
|
||||
menu: [
|
||||
|
||||
99
frontend/src/pages/cart.tsx
Normal file
99
frontend/src/pages/cart.tsx
Normal file
@ -0,0 +1,99 @@
|
||||
|
||||
import React, { ReactElement, useEffect } from 'react';
|
||||
import Head from 'next/head'
|
||||
import {
|
||||
useAppDispatch,
|
||||
useAppSelector
|
||||
} from "../../stores/hooks";
|
||||
import { useRouter } from "next/router";
|
||||
import { fetch as fetchOrders } from '../../stores/orders/ordersSlice'
|
||||
import LayoutAuthenticated from "../../layouts/Authenticated";
|
||||
import { getPageTitle } from "../../config";
|
||||
import SectionTitleLineWithButton from "../../components/SectionTitleLineWithButton";
|
||||
import SectionMain from "../../components/SectionMain";
|
||||
import CardBox from "../../components/CardBox";
|
||||
import BaseButton from "../../components/BaseButton";
|
||||
import { mdiCart } from "@mdi/js";
|
||||
|
||||
const CartPage = () => {
|
||||
const router = useRouter()
|
||||
const dispatch = useAppDispatch()
|
||||
const { orders } = useAppSelector((state) => state.orders)
|
||||
const { currentUser } = useAppSelector((state) => state.auth)
|
||||
|
||||
const pendingOrder = orders.find(order => order.status === 'pending' && order.customer?.id === currentUser?.id);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchOrders({ query: '?status=pending' }));
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{getPageTitle('Shopping Cart')}</title>
|
||||
</Head>
|
||||
<SectionMain>
|
||||
<SectionTitleLineWithButton icon={mdiCart} title={'Shopping Cart'} main>
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Order Items</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Quantity</th>
|
||||
<th>Price</th>
|
||||
<th>Subtotal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pendingOrder && pendingOrder.order_items_order && Array.isArray(pendingOrder.order_items_order) &&
|
||||
pendingOrder.order_items_order.map((item: any) => (
|
||||
<tr key={item.id}>
|
||||
<td data-label="product_name">
|
||||
{item.product_name}
|
||||
</td>
|
||||
<td data-label="quantity">
|
||||
{item.quantity}
|
||||
</td>
|
||||
<td data-label="unit_price">
|
||||
{item.unit_price}
|
||||
</td>
|
||||
<td data-label="subtotal">
|
||||
{item.subtotal}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!pendingOrder?.order_items_order?.length && <div className={'text-center py-4'}>Your cart is empty</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
<BaseButton
|
||||
color='info'
|
||||
label='Checkout'
|
||||
onClick={() => router.push('/checkout')}
|
||||
/>
|
||||
</CardBox>
|
||||
</SectionMain>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
CartPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<LayoutAuthenticated
|
||||
>
|
||||
{page}
|
||||
</LayoutAuthenticated>
|
||||
)
|
||||
}
|
||||
|
||||
export default CartPage;
|
||||
@ -8,15 +8,51 @@ import Head from 'next/head';
|
||||
import SectionMain from '../components/SectionMain';
|
||||
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
|
||||
import { mdiStorefrontOutline } from '@mdi/js';
|
||||
import { create as createOrder } from '../stores/orders/ordersSlice';
|
||||
import { create as createOrderItem } from '../stores/order_items/order_itemsSlice';
|
||||
|
||||
const IndexPage = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { products, loading } = useAppSelector((state) => state.products);
|
||||
const { orders } = useAppSelector((state) => state.orders);
|
||||
const { currentUser } = useAppSelector((state) => state.auth);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchProducts({}));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleAddToCart = async (product) => {
|
||||
if (!currentUser) {
|
||||
alert('Please login to add items to your cart.');
|
||||
return;
|
||||
}
|
||||
|
||||
let pendingOrder = orders.find(order => order.status === 'pending' && order.customer?.id === currentUser.id);
|
||||
|
||||
if (!pendingOrder) {
|
||||
const orderData = {
|
||||
customer: currentUser.id,
|
||||
status: 'pending',
|
||||
};
|
||||
const newOrderAction = await dispatch(createOrder(orderData));
|
||||
if (newOrderAction.payload) {
|
||||
pendingOrder = newOrderAction.payload;
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingOrder) {
|
||||
const orderItemData = {
|
||||
order: pendingOrder.id,
|
||||
product: product.id,
|
||||
quantity: 1,
|
||||
unit_price: product.price,
|
||||
subtotal: product.price,
|
||||
};
|
||||
await dispatch(createOrderItem(orderItemData));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
@ -44,6 +80,9 @@ const IndexPage = () => {
|
||||
<div className="p-4">
|
||||
<h4 className="text-xl font-bold">{product.name}</h4>
|
||||
<p className="text-gray-500">${product.price}</p>
|
||||
<button onClick={() => handleAddToCart(product)} className="mt-4 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
Add to Cart
|
||||
</button>
|
||||
</div>
|
||||
</CardBox>
|
||||
))}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user