877 lines
47 KiB
JavaScript
877 lines
47 KiB
JavaScript
"use client"
|
|
import React, { Suspense, useEffect, useState } from "react";
|
|
import { useParams, useSearchParams } from "next/navigation";
|
|
import Link from "next/link";
|
|
import MasterLayout from "@/masterLayout/MasterLayout";
|
|
import client from "../../../../Auth";
|
|
import { gradientClasses } from "../../../../utils/constant.utils";
|
|
import { Icon } from "@iconify/react";
|
|
import PageLoader from "@/components/common-component/PageLoader";
|
|
import PageNoData from "@/components/common-component/PageNoData";
|
|
import Breadcrumb from "@/components/Breadcrumb";
|
|
|
|
const MenuItemsCategoryInner = () => {
|
|
const params = useParams();
|
|
// const menuName = params.name;
|
|
|
|
const searchParams = useSearchParams();
|
|
const tableName = searchParams.get('table');
|
|
const seats = searchParams.get('seats');
|
|
const time = searchParams.get('time');
|
|
const menuName = searchParams.get('menuname');
|
|
|
|
|
|
const [menuData, setMenuData] = useState(null);
|
|
const [menuItems, setMenuItems] = useState(null);
|
|
// const [sections, setSections] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
const [activeCategory, setActiveCategory] = useState(null);
|
|
|
|
const [cart, setCart] = useState([]);
|
|
const [showOrderModal, setShowOrderModal] = useState(false);
|
|
const [orderStatus, setOrderStatus] = useState('pending');
|
|
const HST_TAX_RATE = 0.13; // 13% HST tax rate
|
|
|
|
const [catMenu, setCatMenu] = useState(null);
|
|
const [catMenuActive, setCatMenuActive] = useState(null)
|
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
const [showItemModal, setShowItemModal] = useState(false);
|
|
const [selectedExtras, setSelectedExtras] = useState([]);
|
|
const [selectedDrink, setSelectedDrink] = useState(null);
|
|
const [orderItems, setOrderItems] = useState([])
|
|
|
|
useEffect(() => {
|
|
getMenuItem();
|
|
}, []);
|
|
|
|
const getMenuItem = async () => {
|
|
try {
|
|
const res = await client?.get(`/Dine360 Menu?fields=["*"]&limit_page_length=100`);
|
|
setCatMenu(res?.data?.data || []);
|
|
setCatMenuActive(res?.data?.data[0]?.name || [])
|
|
} catch (error) {
|
|
console.error("Error fetching menu list:", error);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (catMenu?.length > 0) {
|
|
getMenuItems(catMenu[0]?.menuname); // auto-load first menu's categories
|
|
}
|
|
}, [catMenu]);
|
|
|
|
|
|
useEffect(() => {
|
|
if (menuData?.length > 0) {
|
|
getMenuFoodItems(menuData[0]);
|
|
setActiveCategory(menuData[0]?.name);
|
|
}
|
|
}, [menuData]);
|
|
|
|
const getMenuItems = async (menuname) => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const menuRes = await client.get(
|
|
`/Dine360%20Menu%20Category%20Link?fields=["*"]&limit_page_length=100&filters=[["menu","=","${menuname}"]]`
|
|
);
|
|
|
|
const menuLinks = menuRes?.data?.data || [];
|
|
|
|
const menuCategoryPromises = menuLinks.map(async (menuItem) => {
|
|
const res = await client.get(
|
|
`/Dine360%20Menu%20Category?fields=["*"]&limit_page_length=100&filters=[["name","=","${menuItem.menucategory}"]]`
|
|
);
|
|
return res?.data?.data?.[0] || null;
|
|
});
|
|
|
|
const categories = await Promise.all(menuCategoryPromises);
|
|
const validCategories = categories.filter(Boolean);
|
|
|
|
setMenuData(validCategories);
|
|
} catch (error) {
|
|
console.error("Error fetching menu categories:", error);
|
|
setError(error?.message || "Failed to fetch menu categories");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const getMenuFoodItems = async (category) => {
|
|
if (!category?.name) return;
|
|
try {
|
|
const res = await client.get(
|
|
`/Dine360%20Menu%20Category/${category.name}?fields=["*"]&limit_page_length=100`
|
|
);
|
|
setMenuItems(res?.data?.data || []);
|
|
} catch (error) {
|
|
console.error("Error fetching menu items:", error);
|
|
}
|
|
};
|
|
|
|
|
|
// Fetch sides linked to a specific food item
|
|
const getLinkedSides = async (foodItemName) => {
|
|
try {
|
|
const res = await client.get(
|
|
`/Dine360 FoodItem Sides Link?fields=["*"]&filters=[["menuitemname","=","${foodItemName}"]]`
|
|
);
|
|
return res.data.data; // Sides linked to this menu item
|
|
} catch (error) {
|
|
console.error(`Error fetching linked sides for item: ${foodItemName}`, error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
// Fetch and group all sides by their category
|
|
const getAllSidesGroupedByCategory = async (foodItemName) => {
|
|
try {
|
|
const res = await client.get(`/Dine360 FoodItem Sides Link?fields=["*"]&filters=[["menuitemname","=","${foodItemName}"]]`);
|
|
console.log("Grouped Sides:", res.data.data);
|
|
|
|
const SliderLink = res?.data?.data || [];
|
|
|
|
const SidesList = SliderLink.map(async (sideItem) => {
|
|
const res = await client.get(
|
|
`/Dine360 Food Sides?fields=["*"]&filters=[["name","=","${sideItem.menucategory}"]]`
|
|
);
|
|
return res?.data?.data?.[0] || null;
|
|
});
|
|
const categories = await Promise.all(SidesList);
|
|
const validSides = categories.filter(Boolean);
|
|
console.log("validSides", validSides)
|
|
|
|
} catch (err) {
|
|
console.error("Error fetching grouped sides:", err);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
const handleAddToOrder = () => {
|
|
const cartItem = cart.find(item => item.name === selectedItem.name);
|
|
|
|
if (!cartItem) {
|
|
alert("Please add the item to cart before adding to order.");
|
|
return;
|
|
}
|
|
|
|
setOrderItems(cart);
|
|
setShowItemModal(false);
|
|
};
|
|
|
|
// For user clicking category from catMenu
|
|
const handleCatMenuClick = async (menuname) => {
|
|
await getMenuItems(menuname);
|
|
setCatMenuActive(menuname)
|
|
};
|
|
|
|
// For user clicking individual menu category
|
|
const handleMenuClick = async (menu) => {
|
|
try {
|
|
const res = await client.get(
|
|
`/Dine360%20Menu%20Category/${menu.name}?fields=["*"]&limit_page_length=100`
|
|
);
|
|
setMenuItems(res?.data?.data || []);
|
|
setActiveCategory(menu?.name);
|
|
} catch (error) {
|
|
console.error("Error fetching menu data:", error);
|
|
setError(error?.message || "Failed to fetch menu data");
|
|
}
|
|
};
|
|
|
|
|
|
console.log("menuItems", menuItems);
|
|
const addToCart = (item) => {
|
|
const existingItem = cart.find(cartItem => cartItem.name === item.name);
|
|
if (existingItem) {
|
|
setCart(cart.map(cartItem =>
|
|
cartItem.name === item.name
|
|
? { ...cartItem, quantity: cartItem.quantity + 1 }
|
|
: cartItem
|
|
));
|
|
} else {
|
|
setCart([...cart, { ...item, quantity: 1 }]);
|
|
}
|
|
|
|
};
|
|
|
|
const removeFromCart = (itemName) => {
|
|
setCart(prev => prev.filter(item => item.name !== itemName));
|
|
setOrderItems(prev => prev.filter(item => item.name !== itemName));
|
|
};
|
|
|
|
const updateQuantity = (itemName, newQuantity) => {
|
|
if (newQuantity < 1) {
|
|
removeItem(itemName);
|
|
return;
|
|
}
|
|
|
|
// Update cart
|
|
setCart(prev =>
|
|
prev.map(item =>
|
|
item.name === itemName
|
|
? { ...item, quantity: newQuantity }
|
|
: item
|
|
)
|
|
);
|
|
|
|
// Update orderItems
|
|
setOrderItems(prev =>
|
|
prev.map(item =>
|
|
item.name === itemName
|
|
? { ...item, quantity: newQuantity }
|
|
: item
|
|
)
|
|
);
|
|
};
|
|
|
|
|
|
const calculateSubtotal = () => {
|
|
return cart.reduce((total, item) => total + (item.price * item.quantity), 0);
|
|
};
|
|
|
|
const calculateTax = () => {
|
|
return calculateSubtotal() * HST_TAX_RATE;
|
|
};
|
|
|
|
const calculateTotal = () => {
|
|
return calculateSubtotal() + calculateTax();
|
|
};
|
|
|
|
const handlePlaceOrder = () => {
|
|
setShowOrderModal(true);
|
|
};
|
|
|
|
// Toggle extras like checkbox buttons
|
|
const toggleExtra = (extra) => {
|
|
setSelectedExtras(prev =>
|
|
prev.includes(extra)
|
|
? prev.filter(item => item !== extra)
|
|
: [...prev, extra]
|
|
);
|
|
};
|
|
|
|
// Select one drink at a time
|
|
const selectDrink = (drink) => {
|
|
setSelectedDrink(drink === selectedDrink ? null : drink);
|
|
};
|
|
|
|
const confirmOrder = async () => {
|
|
// Get current time in YYYY-MM-DD HH:mm:ss format (Canada Eastern Time)
|
|
const now = new Date();
|
|
|
|
const year = now.getFullYear();
|
|
const month = String(now.getMonth() + 1).padStart(2, '0'); // month is 0-based
|
|
const day = String(now.getDate()).padStart(2, '0');
|
|
const hour = String(now.getHours()).padStart(2, '0');
|
|
const minute = String(now.getMinutes()).padStart(2, '0');
|
|
const second = String(now.getSeconds()).padStart(2, '0');
|
|
|
|
const orderStartTime = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
|
|
|
console.log(orderStartTime);
|
|
|
|
// Prepare formatted order data
|
|
const formattedOrder = orderItems.map((item) => ({
|
|
menuitem: item.name,
|
|
quantity: item.quantity,
|
|
rate: item.price,
|
|
amount: item.price * item.quantity,
|
|
menu: menuName,
|
|
menucategory: item.parent,
|
|
status: "Avaialble"
|
|
}));
|
|
|
|
const Body = {
|
|
customer: "kadeeja",
|
|
ordertaker: "surya",
|
|
phone: "+91-8220348218",
|
|
table: tableName,
|
|
partysize: seats,
|
|
orderstatus: "Cooking",
|
|
tableoccupiedtime: time,
|
|
orderstarttime: orderStartTime, // dynamically set here
|
|
orderendtime: null, // optional: update later
|
|
durationintable: null,
|
|
orderamount: calculateSubtotal().toFixed(2),
|
|
hstpercentage: 13,
|
|
hstamount: calculateTax().toFixed(2),
|
|
totalamount: calculateTotal().toFixed(2),
|
|
orderinstructions: null,
|
|
order_items: formattedOrder
|
|
};
|
|
console.log("Body", Body);
|
|
try {
|
|
const res = await client.post('/Dine360 Order', Body, {
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.log("error", error)
|
|
}
|
|
|
|
// Clear and reset
|
|
alert('Order placed successfully!');
|
|
setCart([]);
|
|
setShowOrderModal(false);
|
|
setOrderStatus('pending');
|
|
};
|
|
|
|
|
|
const renderMenuItem = (menu) => {
|
|
console.log("menu", menu)
|
|
const cartItem = cart.find(item => item.name === menu.name);
|
|
const isInCart = !!cartItem;
|
|
|
|
return (
|
|
<div key={menu.name} className="col-xxl-3 col-md-6 user-grid-card">
|
|
<div className="position-relative border radius-16 overflow-hidden">
|
|
<div className="card cursor-pointer" onClick={() => {
|
|
setSelectedItem(menu);
|
|
getAllSidesGroupedByCategory(menu?.name)
|
|
setShowItemModal(true);
|
|
}}
|
|
>
|
|
<div className="card-body text-center p-3">
|
|
<img
|
|
src={menu.profileImg}
|
|
alt=""
|
|
className="border br-white border-width-2-px w-100-px h-100-px rounded-circle object-fit-cover"
|
|
/>
|
|
{/* <div className="w-100 max-h-150-px radius-0 overflow-hidden" >
|
|
<img alt="" className="w-100 h-150-px object-fit-cover" src="/assets/images/blog/blog5.png" style={{ height: "150px" }} />
|
|
</div> */}
|
|
<div className="p-0">
|
|
<h6 className="text-lg mb-1 mt-1">{menu.menuitemname}</h6>
|
|
<span className="text-secondary-light text-sm lh-sm mb-1">{menu.parent}</span>
|
|
<h6 className="text-md mb-0 mt-1">${menu.price.toFixed(2)}</h6>
|
|
</div>
|
|
|
|
|
|
{/* {isInCart ? (
|
|
<div className="d-flex align-items-center justify-content-center gap-2">
|
|
<button
|
|
className="btn btn-sm btn-outline-danger-500"
|
|
onClick={() => updateQuantity(menu.name, cartItem.quantity - 1)}
|
|
>
|
|
-
|
|
</button>
|
|
<span className="mx-2 border border-white px-3 py-1 radius-6">{cartItem.quantity}</span>
|
|
<button
|
|
className="btn btn-sm btn-outline-success-500"
|
|
onClick={() => updateQuantity(menu.name, cartItem.quantity + 1)}
|
|
>
|
|
+
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<button
|
|
onClick={() => addToCart(menu)}
|
|
className="bg-danger-500 text-white p-10 text-sm btn-sm px-12 py-12 radius-8 d-flex align-items-center justify-content-center mt-2 fw-medium gap-2 w-100"
|
|
>
|
|
Add to Cart
|
|
</button>
|
|
)} */}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
|
|
const cartItem = cart.find(item => item.name === selectedItem.name);
|
|
const isInCart = !!cartItem;
|
|
const quantity = cartItem?.quantity || 1;
|
|
|
|
console.log("catMenuActive", catMenuActive)
|
|
console.log("cartItem?.quantity", cartItem)
|
|
|
|
return (
|
|
<>
|
|
<div className="container-fluid" style={{ marginBottom: "100px" }}>
|
|
|
|
{
|
|
loading ? (
|
|
<PageLoader />
|
|
) : menuData?.length == 0 ? (
|
|
<PageNoData />
|
|
) : (
|
|
<div className="row gy-4">
|
|
{/* Menu Category - Always col-xxl-2 */}
|
|
<div className="col-xxl-2">
|
|
<div className="d-inline-block w-100">
|
|
|
|
<div className="card p-0 overflow-hidden position-relative radius-12 h-100">
|
|
<div className="card-body p-24">
|
|
<ul className="d-flex flex-column gap-2">
|
|
{catMenu.map((menu) => (
|
|
<li
|
|
className={`nav-item border rounded-2 px-3 py-3 bg-border-theme d-flex align-items-center gap-3 ${catMenuActive === menu.menuname ? "bg-theme " : ""
|
|
}`}
|
|
role="presentation"
|
|
key={menu?.name}
|
|
onClick={() => handleCatMenuClick(menu?.name)}
|
|
style={{ cursor: "pointer" }}
|
|
>
|
|
<img
|
|
src="/assets/images/menu/menu-icons/all-menu.png"
|
|
alt="all menu"
|
|
className="w-28-px h-28-px"
|
|
/>
|
|
<span className="line-height-1">{menu?.menuname}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Menu Items - col-xxl-10 if cart is empty, else col-xxl-7 */}
|
|
<div className="col-xxl-7">
|
|
<div className="card p-0 overflow-hidden position-relative radius-12 mb-3">
|
|
<div className="card-body p-24">
|
|
<ul className="d-flex gap-2 mb-0">
|
|
{menuData.map((menu) => (
|
|
<li
|
|
className={`nav-item border rounded-2 px-3 py-1 bg-outline-theme d-flex align-items-center gap-3 ${activeCategory === menu.name ? "bg-theme" : ""
|
|
}`}
|
|
role="presentation"
|
|
key={menu?.name}
|
|
onClick={() => handleMenuClick(menu)}
|
|
style={{ cursor: "pointer" }}
|
|
>
|
|
<img
|
|
src="/assets/images/menu/menu-icons/all-menu.png"
|
|
alt="all menu"
|
|
className="w-28-px h-28-px"
|
|
/>
|
|
<span className="line-height-1">{menu?.menucategoryname}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div className="row gy-4">
|
|
{menuItems?.menuitems_child?.map(renderMenuItem)}
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/* Cart - Show only if cart has items */}
|
|
{/* {cart.length > 0 && ( */}
|
|
<div className="col-xxl-3">
|
|
<div className="card p-0 overflow-hidden position-relative radius-12 h-100">
|
|
<div className="card-body p-24">
|
|
<h6 className="text-lg mb-3">POS Dine-in</h6>
|
|
<div className="d-flex flex-wrap align-items-center gap-4 mb-3">
|
|
<div className="d-flex flex-wrap align-items-center gap-3">
|
|
<button
|
|
type="button"
|
|
className="btn btn-primary-600 position-relative px-20 py-8 text-sm line-height-1 d-flex align-items-center"
|
|
>
|
|
Items
|
|
<span className="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-primary-600 border border-white">
|
|
{orderItems.length}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<div className="d-flex flex-wrap align-items-center gap-3">
|
|
<button
|
|
type="button"
|
|
className="btn btn-warning-600 position-relative px-20 py-8 text-sm line-height-1 d-flex align-items-center"
|
|
>
|
|
Table Name
|
|
{/* <span className="position-absolute top-0 end-0 translate-middle-y badge rounded-pill bg-danger-600">
|
|
99+
|
|
</span> */}
|
|
</button>
|
|
</div>
|
|
|
|
<div className="d-flex flex-wrap align-items-center gap-3">
|
|
<button
|
|
type="button"
|
|
className="btn btn-success-600 position-relative px-20 py-8 text-sm line-height-1 d-flex align-items-center"
|
|
>
|
|
Members
|
|
<span className="position-absolute top-0 end-0 translate-middle-y badge rounded-pill bg-success-600 border border-white ">
|
|
3
|
|
</span>
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
<div className="mb-1 border-bottom pb-3 mb-3">
|
|
<ul className="list-group radius-8">
|
|
<li className="list-group-item d-flex align-items-center justify-content-between border text-secondary-light p-6 bg-base border-bottom-0">
|
|
<div className="d-flex text-sm align-items-center gap-2">
|
|
<img className="w-20-px h-20-px rounded-circle" alt="" src="/assets/images/lists/list-img1.png" />
|
|
Contact Number
|
|
</div>
|
|
<span className="text-xs bg-success-100 text-success-600 radius-4 px-10 py-2 fw-semibold cursor-pointer" data-bs-toggle="tooltip" title="This is the contact number">Show</span>
|
|
</li>
|
|
|
|
<li className="list-group-item d-flex align-items-center justify-content-between border text-secondary-light p-6 bg-base">
|
|
<div className="d-flex align-items-center text-sm gap-2">
|
|
<img className="w-20-px h-20-px rounded-circle" alt="" src="/assets/images/lists/list-img2.png" />
|
|
Customer Info
|
|
</div>
|
|
<span className="text-xs bg-success-100 text-success-600 radius-4 px-10 py-2 fw-semibold cursor-pointer" data-bs-toggle="tooltip" title="Customer details here">Show</span>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</div>
|
|
|
|
{orderItems.length === 0 ? (
|
|
<p className="text-muted">Your cart is empty.</p>
|
|
) : (
|
|
<>
|
|
{orderItems.map((item) => (
|
|
<div key={item.name} className="card bg-theme mb-3 border">
|
|
<div className="card-body p-3">
|
|
<div className="d-flex align-items-center gap-3">
|
|
<div className="flex-shrink-0">
|
|
<img
|
|
src={item.profileImg}
|
|
alt={item.menuitemname}
|
|
className="rounded-circle"
|
|
style={{
|
|
width: "60px",
|
|
height: "60px",
|
|
objectFit: "cover",
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="flex-grow-1">
|
|
<div className="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 className="mb-0 text-md text-truncate">
|
|
{item.menuitemname}
|
|
</h6>
|
|
<p className="mb-0 text-sm">${item.price.toFixed(2)}</p>
|
|
</div>
|
|
<div>
|
|
<div className="text-end mb-2">
|
|
<button
|
|
className="btn btn-sm btn-link text-danger-500 p-0 ms-2"
|
|
onClick={() => removeFromCart(item.name)}
|
|
>
|
|
<Icon icon="mdi:delete" className="fs-5" />
|
|
</button>
|
|
</div>
|
|
<div className="d-flex align-items-center gap-1 mt-2">
|
|
<button
|
|
className="px-1 py-0 text-sm text-danger-500 border border-danger-500 radius-4"
|
|
onClick={() => updateQuantity(item.name, item.quantity - 1)}
|
|
>
|
|
-
|
|
</button>
|
|
<span className="mx-1 text-sm border border-white px-1 radius-4">{item.quantity}</span>
|
|
<button
|
|
className="px-1 py-0 text-sm text-success-500 radius-4 border border-success-500"
|
|
onClick={() => updateQuantity(item.name, item.quantity + 1)}
|
|
>
|
|
+
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
<div className="order-summary border-top pt-3">
|
|
<button
|
|
className="btn btn-bg-theme w-100"
|
|
onClick={handlePlaceOrder}
|
|
>
|
|
Place Order
|
|
</button>
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* )} */}
|
|
</div>
|
|
)
|
|
}
|
|
</div>
|
|
|
|
|
|
{showItemModal && selectedItem && (
|
|
|
|
<>
|
|
<div
|
|
className="modal-backdrop fade show"
|
|
style={{
|
|
position: 'fixed',
|
|
top: 0, left: 0, right: 0, bottom: 0,
|
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
zIndex: 1040
|
|
}}
|
|
onClick={() => setShowItemModal(false)}
|
|
/>
|
|
<div className="modal fade show" style={{ display: 'block', zIndex: 1050 }}>
|
|
<div className="modal-dialog modal-xl modal-dialog-centered">
|
|
<div className="modal-content radius-12 overflow-hidden">
|
|
<div className="modal-header border-0">
|
|
<h6 className="modal-title text-lg">{selectedItem.menuitemname}</h6>
|
|
<button type="button" className="btn-close" onClick={() => setShowItemModal(false)} />
|
|
</div>
|
|
<div className="modal-body">
|
|
<div className="row gy-3">
|
|
{/* Col 1 - Image */}
|
|
<div className="col-md-4">
|
|
<img
|
|
src="https://images.immediate.co.uk/production/volatile/sites/30/2020/08/chorizo-mozarella-gnocchi-bake-cropped-9ab73a3.jpg?quality=90&resize=700,636"
|
|
alt={selectedItem.menuitemname}
|
|
className="w-100 rounded"
|
|
style={{ objectFit: 'cover' }}
|
|
/>
|
|
</div>
|
|
|
|
{/* Col 2 - Size + Quantity */}
|
|
<div className="col-md-4">
|
|
<h6 className="mb-2 text-lg">Choose Size</h6>
|
|
<select className="form-select mb-3">
|
|
<option>Regular</option>
|
|
<option>Large</option>
|
|
<option>Family</option>
|
|
</select>
|
|
|
|
<h6 className="mb-1 text-lg">Quantity</h6>
|
|
{isInCart ? (
|
|
<div className="d-flex align-items-center gap-2">
|
|
<button
|
|
className="btn btn-sm btn-outline-danger-500"
|
|
onClick={() => updateQuantity(selectedItem.name, cartItem.quantity - 1)}
|
|
>
|
|
-
|
|
</button>
|
|
<span className="mx-2 border border-white px-3 py-1 radius-6">{cartItem.quantity}</span>
|
|
<button
|
|
className="btn btn-sm btn-outline-success-500"
|
|
onClick={() => updateQuantity(selectedItem.name, cartItem.quantity + 1)}
|
|
>
|
|
+
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<button
|
|
onClick={() => addToCart(selectedItem)}
|
|
className="btn-bg-theme text-white p-10 text-sm btn-sm px-14 py-12 radius-8 d-flex align-items-center justify-content-center mt-2 fw-medium gap-2 w-100"
|
|
>
|
|
Add to Cart
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Col 3 - Extras */}
|
|
{/* Col 3 - Extras + Order Button + Total */}
|
|
<div className="col-md-4">
|
|
|
|
<h6 className="mb-2 text-lg">Add Extras</h6>
|
|
<div className="d-flex flex-wrap gap-3 mb-3">
|
|
<button
|
|
type="button"
|
|
className={`btn text-sm btn-sm px-14 py-6 radius-8 d-flex justify-content-between align-items-center gap-2 border ${selectedExtras.includes('cheese') ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
|
onClick={() => toggleExtra('cheese')}
|
|
>
|
|
<span>Extra Cheese</span>
|
|
<span>$1.50</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`btn text-sm btn-sm px-14 py-6 d-flex justify-content-between align-items-center gap-2 border ${selectedExtras.includes('sauce') ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
|
onClick={() => toggleExtra('sauce')}
|
|
>
|
|
<span>Spicy Sauce</span>
|
|
<span>$0.75</span>
|
|
</button>
|
|
</div>
|
|
|
|
<h6 className="mb-1 text-lg">Drinks</h6>
|
|
<div className="d-flex flex-column gap-2 mb-2">
|
|
<button
|
|
type="button"
|
|
className={`btn text-sm btn-sm px-14 py-6 d-flex justify-content-between align-items-center border ${selectedDrink === 'coke' ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
|
onClick={() => selectDrink('coke')}
|
|
>
|
|
<span>Coke</span>
|
|
<span>$2.00</span>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`btn text-sm btn-sm px-14 py-6 d-flex justify-content-between align-items-center border ${selectedDrink === 'pepsi' ? 'btn-bg-theme text-white' : 'btn-outline-theme'}`}
|
|
onClick={() => selectDrink('pepsi')}
|
|
>
|
|
<span>Pepsi</span>
|
|
<span>$2.00</span>
|
|
</button>
|
|
</div>
|
|
|
|
|
|
<div className="bg-success-100 p-3 mb-3 mt-3 rounded">
|
|
<h6 className="mb-2">Total: <span className="text-success-500">$12.99</span></h6>
|
|
</div>
|
|
|
|
<button className="btn btn-bg-theme w-100 mb-3 text-white" onClick={handleAddToOrder}>
|
|
Add to Order
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
{/*
|
|
<div className="modal-footer border-0">
|
|
<button className="btn btn-danger-500 w-100 text-white">Add to Order</button>
|
|
</div> */}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
|
|
{/* Order Confirmation Modal */}
|
|
{
|
|
showOrderModal && (
|
|
<>
|
|
<div
|
|
className="modal-backdrop fade show"
|
|
style={{
|
|
position: 'fixed',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
zIndex: 1040
|
|
}}
|
|
onClick={() => setShowOrderModal(false)}
|
|
/>
|
|
<div
|
|
className="modal fade show"
|
|
style={{
|
|
display: 'block',
|
|
zIndex: 1050
|
|
}}
|
|
>
|
|
<div className="modal-dialog modal-dialog-centered modal-lg">
|
|
<div className="modal-content shadow-lg">
|
|
<div className="modal-header border-0">
|
|
<h6 className="modal-title text-lg">Confirm Your Order</h6>
|
|
<button
|
|
type="button"
|
|
className="btn-close"
|
|
onClick={() => setShowOrderModal(false)}
|
|
></button>
|
|
</div>
|
|
<div className="modal-body">
|
|
<div className="order-items mb-4">
|
|
<div className="d-flex justify-content-between align-items-center mb-3">
|
|
<p className="fw-bold mb-0">Order Items:</p>
|
|
<select
|
|
className="form-select form-select-sm w-auto"
|
|
value={orderStatus}
|
|
onChange={(e) => setOrderStatus(e.target.value)}
|
|
>
|
|
<option value="pending">Pending</option>
|
|
<option value="preparing">Preparing</option>
|
|
<option value="ready">Ready for Pickup</option>
|
|
<option value="completed">Completed</option>
|
|
<option value="cancelled">Cancelled</option>
|
|
</select>
|
|
</div>
|
|
{cart.map((item) => (
|
|
<div key={item.name} className="card bg-tb-lilac mb-2 border">
|
|
<div className="card-body p-3">
|
|
<div className="d-flex align-items-center gap-3">
|
|
<div className="flex-shrink-0">
|
|
<img
|
|
src={item.profileImg}
|
|
alt={item.name}
|
|
className="rounded-circle"
|
|
style={{ width: '50px', height: '50px', objectFit: 'cover' }}
|
|
/>
|
|
</div>
|
|
<div className="flex-grow-1">
|
|
<div className="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 className="mb-0 text-md fw-semibold">{item.menuitemname}</h6>
|
|
<p className="mb-0 text-sm text-secondary">Quantity: {item.quantity}</p>
|
|
</div>
|
|
<div className="text-end">
|
|
<h6 className="mb-0 text-md fw-semibold">${(item.price * item.quantity).toFixed(2)}</h6>
|
|
<p className="mb-0 text-sm text-secondary">${item.price.toFixed(2)} each</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="order-summary border-top pt-3">
|
|
<div className="d-flex justify-content-between mb-2">
|
|
<span>Subtotal:</span>
|
|
<span>${calculateSubtotal().toFixed(2)}</span>
|
|
</div>
|
|
<div className="d-flex justify-content-between mb-2">
|
|
<span>HST Tax (13%):</span>
|
|
<span>${calculateTax().toFixed(2)}</span>
|
|
</div>
|
|
<div className="d-flex justify-content-between mb-3 fw-bold">
|
|
<span>Total:</span>
|
|
<span>${calculateTotal().toFixed(2)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="modal-footer border-0">
|
|
<button
|
|
type="button"
|
|
className="btn border border-danger-500 text-danger-500 text-sm"
|
|
onClick={() => setShowOrderModal(false)}
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="btn btn-danger-500 text-sm"
|
|
onClick={confirmOrder}
|
|
>
|
|
Confirm Order
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
</>
|
|
);
|
|
};
|
|
|
|
const MenuItemsCategory = (() => {
|
|
return (
|
|
<MasterLayout>
|
|
<Breadcrumb title="Menu Items" />
|
|
<Suspense fallback={<PageLoader />}>
|
|
<MenuItemsCategoryInner />
|
|
</Suspense>
|
|
</MasterLayout>
|
|
)
|
|
})
|
|
|
|
export default MenuItemsCategory;
|