Source: cashier/CashierView.jsx

import { useEffect, useState } from 'react';
import { useAuth } from '../lib/AuthContext';

const VITE_BACKEND_URL = import.meta.env.VITE_BACKEND_URL;

/**
 * Component to render an individual menu item with increment and decrement buttons.
 * @param {string} name - Name of the menu item.
 * @param {number} price - Price of the menu item.
 * @param {Map} countMap - State map holding item quantities.
 * @param {function} setCountMap - Function to update the item quantities.
 */
function ListPanelItems({ name, price, countMap, setCountMap }) {
    const incrementCounter = () => {
        setCountMap((prevMap) => new Map(prevMap.set(name, (prevMap.get(name) || 0) + 1)));
    };

    const decrementCounter = () => {
        setCountMap((prevMap) => {
            const currentCount = prevMap.get(name) || 0;
            if (currentCount > 0)
                currentCount === 1 ? prevMap.delete(name) : prevMap.set(name, currentCount - 1);
            return new Map(prevMap);
        });
    };

    return (
        <li className='flex flex-col bg-white text-black dark:bg-slate-700 dark:border-white dark:text-white border-black shadow-black border-2 m-2 p-4 flex justify-between items-center rounded-lg'>
            <span className='flex-1 truncate m-2'>{name} ${price.toFixed(2)}</span>
            <div className='flex items-center space-x-2'>
                <button
                    className='bg-green-600 text-white border-black border-2 rounded px-3 py-1 shadow-sm hover:bg-green-700 transition-all'
                    onClick={incrementCounter}
                > +
                </button>
                <button
                    className='bg-red-600 text-white border-black border-2 rounded px-3.5 py-1 shadow-sm hover:bg-red-700 transition-all'
                    onClick={decrementCounter}
                > -
                </button>
                <span className='text-black dark:text-white m-2'> x{countMap.get(name) || 0}</span>
            </div>
        </li>
    );
}

/**
 * Modal dialog to confirm order details.
 * @param {boolean} isOpen - Controls visibility of the dialog.
 * @param {function} onClose - Function to close the dialog.
 * @param {number} totalPrice - Total price of the order.
 * @param {function} onConfirmOrder - Function to handle order confirmation.
 * @param {Map} itemMap - Map of selected items with their quantities.
 */
function OrderDialog({ isOpen, onClose, totalPrice, onConfirmOrder, itemMap }) {
    const [customerName, setCustomerName] = useState('');
    // const [employeeName, setEmployeeName] = useState('');
    const [customerAccount, setCustomerAccount] = useState('');

    const { user } = useAuth();

    if (!isOpen) {
        return null;
    }

    const handleConfirm = () => {
        onConfirmOrder(customerName, customerAccount);
        onClose();
    };

    // Component to render an individual order summary item
    const OrderSummary = ({ name, quantity }) => {
        return (
            <li>{name} ... x{quantity}</li>
        );
    };

    return (
        <div className="fixed inset-0 bg-gray-800 bg-opacity-50 flex items-center justify-center">
            <div className='flex flex-col text-center bg-white dark:bg-slate-500 text-black dark:text-white border-black dark:border-white shadow-white border-2 rounded-md'>
                <h2>Confirm Order</h2>
                <p> Total: ${totalPrice.toFixed(2)}</p>
                <label>
                    Customer Name:
                    <input
                        className='border-black border-2 rounded-md m-2'
                        type='text'
                        value={customerName}
                        onChange={(e) => setCustomerName(e.target.value)}
                    />
                </label>
                <label>
                    Employee Name:
                    <span className='border-black border-2 rounded-md m-2'>
                        {user.name}
                    </span>
                </label>
                <label>
                    Customer Account:
                    <input
                        className='border-black border-2 rounded-md m-2'
                        type='number'
                        value={customerAccount}
                        onChange={(e) => { setCustomerAccount(e.target.value) }}
                    />
                </label>
                <ul>
                    {Array.from(itemMap.entries()).map(([name, quantity]) => (
                        <OrderSummary name={name} quantity={quantity} key={name} />
                    ))}
                </ul>
                <div>
                    <button
                        className='bg-red-600 text-black border-black border-2 rounded p-1 shadow-black m-4'
                        onClick={onClose}
                    >Cancel
                    </button>
                    <button
                        className='bg-green-600 text-white border-black border-2 rounded p-1 shadow-black m-4'
                        onClick={handleConfirm}
                    >Confirm
                    </button>
                </div>
            </div>
        </div>
    );
}

/**
 * Main component to render the cashier panel.
 * It displays categorized menu items, calculates total price, and allows order placement.
 */
function CashierPanel() {
    const [menuItems, setMenuItems] = useState([]);
    const [error, setError] = useState(null);
    const [isDialogOpen, setIsDialogOpen] = useState(false);
    const [countMap, setCountMap] = useState(new Map());
    const [totalPrice, setTotalPrice] = useState(0);

    const { user } = useAuth();

    // Fetch menu items from backend on component mount
    useEffect(() => {
        const fetchMenuItems = async () => {
            try {
                const response = await fetch(`${VITE_BACKEND_URL}/api/menuitems/`);
                if (!response.ok) {
                    throw new Error(`Error: ${response.status}`);
                }

                const data = await response.json();
                setMenuItems(data);
            } catch (err) {
                setError(err.message);
                console.error("Error fetching menu items:", error)
            }
        };

        fetchMenuItems();
    }, [error]);

    // Categorize menu items by category
    const categorizedItems = {
        Entrees: [],
        Sides: [],
        Drinks: [],
        Appetizers: []
    };

    menuItems.forEach((item) => {
        switch (item.category) {
            case 'Entree':
                categorizedItems.Entrees.push(item);
                break;
            case 'Side':
                categorizedItems.Sides.push(item);
                break;
            case 'Drink':
                categorizedItems.Drinks.push(item);
                break;
            case 'Appetizer':
                categorizedItems.Appetizers.push(item);
                break;
            default:
                break;
        }
    });

    // Calculate total price whenever countMap changes
    const priceMap = new Map(menuItems.map((item) => [item.menu_item_name, item.price]));
    useEffect(() => {
        let newTotal = 0;
        countMap.forEach((quantity, name) => {
            const itemPrice = priceMap.get(name) || 0;
            newTotal += quantity * itemPrice;
        });
        setTotalPrice(newTotal);
    }, [countMap, priceMap]);

    const handlePlaceOrder = () => {
        setIsDialogOpen(true);
    };

    const handleCloseDialog = () => {
        setIsDialogOpen(false);
    };

    const handleConfirmOrder = (customerName, customerAccount) => {
        console.log(`Total Order is $${totalPrice.toFixed(2)} for ${customerName}`);
        console.log(`The map count is ${JSON.stringify(Object.fromEntries(countMap))}`);

        const transactionData = {
            items: Object.fromEntries(countMap),
            customer: customerName,
            customer_id: customerAccount,
            employee: user.name,
            total_price: Number(totalPrice.toFixed(2)),
            discount_points: 0
        };

        fetch(`${VITE_BACKEND_URL}/api/transactions/create`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(transactionData),
        }).then((response) => {
            if (!response.ok) {
                throw new Error(`Server error: ${response.status}`);
            }
            return response.json();
        }).then((data) => {
            console.log('Transaction successful:', data);
        }).catch((error) => {
            console.error('Error:', error);
        });

        setTotalPrice(0);
        setCountMap(new Map());
    };

    // Component to render category cards
    const CategoryCard = ({ title, items, countMap, setCountMap }) => {
        return (
            <section className='h-96 overflow-x-hidden m-4 bg-white dark:bg-slate-800 border-black dark:border-white border-2 rounded-md text-red-600 shadow-black flex-1'>
                <h2 className='border-b-black dark:border-b-white border-2 m-2 text-center'>{title}</h2>
                <ul className='overflow-y-auto '>
                    {items.map((item) => (
                        <ListPanelItems
                            name={item.menu_item_name}
                            price={item.price}
                            countMap={countMap}
                            setCountMap={setCountMap}
                            key={item.menu_item_id}
                        />
                    ))}
                </ul>
            </section>
        );
    };

    return (
        <div>
            <h1 className='flex m-4 justify-center text-red-600 font-bold text-4xl'>Cashier</h1>
            <div className='flex flex-row space-x-4'>
                <CategoryCard
                    title="Entrees"
                    items={categorizedItems.Entrees}
                    countMap={countMap}
                    setCountMap={setCountMap}
                />
                <CategoryCard
                    title="Sides"
                    items={categorizedItems.Sides}
                    countMap={countMap}
                    setCountMap={setCountMap}
                />
                <CategoryCard
                    title="Drinks"
                    items={categorizedItems.Drinks}
                    countMap={countMap}
                    setCountMap={setCountMap}
                />
                <CategoryCard
                    title="Appetizers"
                    items={categorizedItems.Appetizers}
                    countMap={countMap}
                    setCountMap={setCountMap}
                />
            </div>
            <div className='flex justify-center'>
                <button
                    className='bg-green-600 text-white border-black border-2 rounded p-1 shadow-black m-4'
                    onClick={handlePlaceOrder}
                >
                    Place Order
                </button>
            </div>
            <OrderDialog
                isOpen={isDialogOpen}
                onClose={handleCloseDialog}
                totalPrice={totalPrice}
                onConfirmOrder={handleConfirmOrder}
                itemMap={countMap}
            />
        </div>
    );
}

export default CashierPanel;