This commit is contained in:
Flatlogic Bot 2026-01-16 13:23:43 +00:00
parent eee374359b
commit 38bdf4c3c5
6 changed files with 159 additions and 8 deletions

View File

@ -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>

View File

@ -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 && (

View File

@ -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>
))}
</>

View File

@ -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: [

View 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;

View File

@ -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>
))}