From 38bdf4c3c57e2fcb50a1a8741851278b3fc60568 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 16 Jan 2026 13:23:43 +0000 Subject: [PATCH] v2 --- frontend/src/components/NavBar.tsx | 9 +- frontend/src/components/NavBarItem.tsx | 7 +- frontend/src/components/NavBarMenuList.tsx | 7 +- frontend/src/menuNavBar.ts | 6 ++ frontend/src/pages/cart.tsx | 99 ++++++++++++++++++++++ frontend/src/pages/index.tsx | 39 +++++++++ 6 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 frontend/src/pages/cart.tsx diff --git a/frontend/src/components/NavBar.tsx b/frontend/src/components/NavBar.tsx index cedc7a7..3c09ec4 100644 --- a/frontend/src/components/NavBar.tsx +++ b/frontend/src/components/NavBar.tsx @@ -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`} > - + diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx index 89370d6..e80061c 100644 --- a/frontend/src/components/NavBarItem.tsx +++ b/frontend/src/components/NavBarItem.tsx @@ -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})`} {item.isCurrentUser && } {item.menu && ( diff --git a/frontend/src/components/NavBarMenuList.tsx b/frontend/src/components/NavBarMenuList.tsx index 0896428..162dedc 100644 --- a/frontend/src/components/NavBarMenuList.tsx +++ b/frontend/src/components/NavBarMenuList.tsx @@ -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) => (
- +
))} diff --git a/frontend/src/menuNavBar.ts b/frontend/src/menuNavBar.ts index a5dd956..be5b4b6 100644 --- a/frontend/src/menuNavBar.ts +++ b/frontend/src/menuNavBar.ts @@ -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: [ diff --git a/frontend/src/pages/cart.tsx b/frontend/src/pages/cart.tsx new file mode 100644 index 0000000..516af72 --- /dev/null +++ b/frontend/src/pages/cart.tsx @@ -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 ( + <> + + {getPageTitle('Shopping Cart')} + + + + + + <> +

Order Items

+ +
+ + + + + + + + + + + {pendingOrder && pendingOrder.order_items_order && Array.isArray(pendingOrder.order_items_order) && + pendingOrder.order_items_order.map((item: any) => ( + + + + + + + ))} + +
ProductQuantityPriceSubtotal
+ {item.product_name} + + {item.quantity} + + {item.unit_price} + + {item.subtotal} +
+
+ {!pendingOrder?.order_items_order?.length &&
Your cart is empty
} +
+ + router.push('/checkout')} + /> +
+
+ + ); +}; + +CartPage.getLayout = function getLayout(page: ReactElement) { + return ( + + {page} + + ) +} + +export default CartPage; diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index c1a1c4e..cfeca57 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -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 = () => {

{product.name}

${product.price}

+
))}