Source: pages/customer/CustomerCheckout.jsx

import React, {useEffect, useState} from "react";
import {useNavigate} from "react-router-dom";
import CustomerCheckOutItem from "../../components/CustomerCheckOutItem";
import SuggestedMenuItem from "../../components/SuggestedMenuItem";
import {endpoints} from "../../utils/apiEndpoints";
import {HOST} from "../../utils/host";

import "../../styles/customer.scss";

import logo from "../../assets/logo.svg";
import beveragesImages from "../../utils/beverageImages";
import sauceImages from "../../utils/sauceImages";
import sidesImages from "../../utils/sidesImages";
import treatImages from "../../utils/treatImages";

/**
 * Array of objects containing recommended menu items
 * @typedef {Array<{name: string, src: string, key: number, id: number}>} PotentialRecs
 */
const potentialRecs = [
	{name: "Chick-fil-A Sauce", src: sauceImages.CFASauce, key: 2, id: 46},
	{name: "M. Soft Drink", src: beveragesImages.beverage9, key: 9, id: 29},
	{name: "M. Lemonade", src: beveragesImages.beverage5, key: 5, id: 25},
	{name: "M. Waffle Fries", src: sidesImages.side1, key: 1, id: 15},
	{name: "Vanilla Milkshake", src: treatImages.treat8, key: 8, id: 36},
	{name: "Icedream® Cone", src: treatImages.treat6, key: 6, id: 39},
];

/**
 * Component for rendering and managing customer checkout
 * @param {object} props - Props object
 * @param {string} props.exampleProp - An example prop
 */
function CustomerCheckout(props) {
	const navigate = useNavigate();

	// Get current order from local storage or create a new order
	const currentOrder = JSON.parse(localStorage.getItem("curOrder")) || {total: [0], items: []};
	const [showModal, setShowModal] = useState(false);
	const [cart, setCart] = useState(currentOrder ? currentOrder : {total: [0], items: []});
	const [items, setItems] = useState(Object.keys(cart.items));
	const [recs, setRecs] = useState([]);
	const [orderValue, setOrderValue] = useState(0);

	/**
	 * Clear current order from local storage
	 */
	function emptyCurrentOrder() {
		const defaultOrder = {total: [0], items: {}};
		localStorage.setItem("curOrder", JSON.stringify(defaultOrder));
		localStorage.setItem("numItems", "0");
	}

	/**
	 * Cancel current order and navigate back to order page
	 */
	function cancelOrder() {
		emptyCurrentOrder();
		navigate("/customer/order");
	}

	/**
	 * Handle payment and submit order to server
	 */
	function handlePayment() {
		let itemsArr = [];
		for (let cartID in cart.items) {
			const id = cart.items[cartID][3];
			const count = cart.items[cartID][1];
			const excluded = cart.items[cartID][4];

			let excludedIDs = [];
			for (let i = 0; i < excluded.length; ++i) {
				excludedIDs.push(excluded[i].id);
			}

			const curItem = {"id": id, "quantity": count, "excluded": excludedIDs};
			itemsArr.push(curItem);
		}

		if (itemsArr.length == 0)
			return;

		const order = {
			customerName: document.getElementById("customerName").value != "" ?
				document.getElementById("customerName").value : "noName",
			totalCost: cart.total[0].toString(),
			employeeID: -1,
			items: itemsArr
		};

		const url = HOST + endpoints.postOrder;

		if (order.items === undefined) {
			alert("Please add items to your order before checking out.");
			return;
		}
		fetch(url, {
			method: "POST",
			headers: {"Content-Type": "application/json"},
			body: JSON.stringify(order)
		})
			.then(response => {
				return response.text();
			});

		setShowModal(true);
		setTimeout(() => {
			setShowModal(false);
			navigate("/customer/order");
		}, 3000);

		const defaultOrder = {total: [0], items: []};
		setCart(defaultOrder);
		setItems([]);
		localStorage.setItem("curOrder", JSON.stringify(defaultOrder));
		localStorage.setItem("numItems", "0");
		setOrderValue(0);

	};

	/**
	 * Navigates back to the Order page
	 */
	function backToOrder() {
		navigate("/customer/order");
	};

	//get menu and update total
	useEffect(() => {
		//get menu
		const url = HOST + endpoints.getMenu;
		fetch(url, {
			method: "GET"
		})
			.then(response => {
				if (!response.ok) {
					throw new Error("Network Response Not OK");
				}
				return response.json();
			})
			.then(data => {
				localStorage.setItem("menu", JSON.stringify(data));
			});

		//update total
		const curOrder = JSON.parse(localStorage.getItem("curOrder"));
		if (curOrder && curOrder.total) {
			setOrderValue(curOrder.total[0]);
		}

	}, []);

	/**
	 * Removes an item from the shopping cart
	 * @param {Number} cartID - identification number to remove from the cart
	 */
	function removeFromCart(cartID) {
		let newCart = {...cart};
		newCart.total[0] -= newCart.items[cartID][2];
		if (newCart.items[cartID][1] > 1)
			newCart.items[cartID][1] -= 1;
		else
			delete newCart.items[cartID];

		// check if null
		for (let item in newCart.items) {
			if (item === null || item === undefined) {
				delete newCart.items[item];
			}
		}

		setCart(newCart);
		setItems(newCart.items ? Object.keys(newCart.items) : []);
		let newTotal = Math.abs(newCart.total[0]);

		setOrderValue(newTotal);
		localStorage.setItem("curOrder", JSON.stringify(newCart));
		localStorage.setItem("numItems", newCart.items ? Object.keys(newCart.items).length : 0);

		const checkOutBtn = document.getElementById("customerCheckoutBtn");
		checkOutBtn.disabled = newCart.items === undefined || Object.keys(newCart.items).length === 0;
	};

	useEffect(() => {
		// if cart doesn't contain fries, recommend fries
		let recs = [];

		let fries = false;
		let drink = false;
		let sauce = false;
		let treat = false;

		for (let id in cart.items) {
			if (cart.items[id][3] === 15 || cart.items[id][3] === 16)
				fries = true;
			if (cart.items[id][3] >= 20 && cart.items[id][3] <= 32)
				drink = true;
			if (cart.items[id][3] === 46)
				sauce = true;
			if (cart.items[id][3] >= 33 && cart.items[id][3] <= 41)
				treat = true;
		}

		if (!fries)
			recs.push(potentialRecs[3]);

		if (!drink) {
			recs.push(potentialRecs[1]);
			recs.push(potentialRecs[2]);
		}

		if (!sauce)
			recs.push(potentialRecs[0]);

		if (!treat) {
			recs.push(potentialRecs[4]);
			recs.push(potentialRecs[5]);
		}

		setRecs(recs);

	}, [cart]);

	/**
	 * Adds item(s) to the shopping cart and updates the total cost and number of items in the cart.
	 * @param {Object} item - The item(s) to be added to the cart.
	 * @param {number} item.id - The ID of the item to be added to the cart.
	 * @param {number[]} item.ids - An array of IDs of items to be added to the cart.
	 * @param {Object[]} excludeItems - An array of items to be excluded from the cart.
	 */
	function addToCartChkout(item, excludeItems) {
		const carts = JSON.parse(localStorage.getItem("curOrder")) || {total: [0], items: {}};
		let cartID = Object.keys(carts.items).length > 0 ? parseInt(Object.keys(carts.items)[Object.keys(carts.items).length - 1]) + 1 : 0;
		let newCart = {...cart};

		let numberOfItems = localStorage.getItem("numItems");
		numberOfItems = numberOfItems ? parseInt(numberOfItems) : 0;

		const menu = JSON.parse(localStorage.getItem("menu"));

		if (item.ids) {
			for (let i = 0; i < item.ids.length; ++i) {
				menu.forEach((arrItem) => {
					if (arrItem.id == item.ids[i]) {

						let found = false;
						for (let key in newCart.items) {
							if (newCart.items[key][0] === arrItem.name && (JSON.stringify(newCart.items[key][4]) === JSON.stringify(excludeItems) || i != 0)) {
								newCart.items[key][1] += 1;
								newCart.total[0] += Number(arrItem.cost);
								found = true;
								break;
							}
						}
						if (!found) {
							newCart.items[cartID] = [arrItem.name, 1, arrItem.cost, arrItem.id, i == 0 ? excludeItems : []];
							newCart.total[0] += Number(arrItem.cost);
							cartID++;
						}
						localStorage.setItem("numItems", JSON.stringify(numberOfItems + 1));
						window.dispatchEvent(new Event('storage'));
						return;
					}
				});
			}
		}

		if (item.id) {
			menu.forEach((arrItem) => {
				if (arrItem.id == item.id) {
					let found = false;
					for (let key in newCart.items) {
						if (newCart.items[key][0] === arrItem.name && JSON.stringify(newCart.items[key][4]) === JSON.stringify(excludeItems)) {
							newCart.items[key][1] += 1;
							newCart.total[0] += Number(arrItem.cost);
							found = true;
							break;
						}
					}
					if (!found) {
						newCart.items[cartID] = [arrItem.name, 1, arrItem.cost, arrItem.id, excludeItems];
						newCart.total[0] += Number(arrItem.cost);
					}
					localStorage.setItem("numItems", JSON.stringify(numberOfItems + 1));
					window.dispatchEvent(new Event('storage'));
					return;
				}
			});
		}
		setCart(newCart);
		setItems(Object.keys(newCart.items));
		let newTotal = Math.abs(newCart.total[0]);

		setOrderValue(newTotal);
		localStorage.setItem("curOrder", JSON.stringify(newCart));
		localStorage.setItem("numItems", newCart.items ? Object.keys(newCart.items).length : 0);
	}


	return (
		<div>
			<div className="customerCheckoutTop">
				<div className="backDiv" style={{width: "6em", margin: ".35em 0em 0em .35em"}}>
					<button title="Back to order list" data-cy="SubNavBack" className="backButton" onClick={backToOrder}>
						<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
							<path d="M14.09 22L5 12l9.09-10" stroke="#ffffff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
							</path>
						</svg>
						<div aria-hidden="true" className="backText">
							Back
						</div>
					</button>
				</div>
				<div className="customerCheckoutTitle">
					<h1>Cart</h1>
				</div>
				<div className="logo-icon">
					<img src={logo} alt="Chick-fil-A logo" className="logo-icon" />
				</div>
			</div>
			<div className="customerCheckout">
				<div className="customerCheckoutItems">
					{items && items.map(cartID => (
						cart.items[cartID] &&
						<CustomerCheckOutItem
							itemName={cart.items[cartID][0]}
							count={cart.items[cartID][1]}
							price={cart.items[cartID][2]}
							cartID={cartID}
							excluded={cart.items[cartID][4]}
							removeFromCart={removeFromCart}
						>
						</CustomerCheckOutItem>
					))}
				</div>

				<div className="customerCheckoutSuggs">
					{recs.length > 0 &&
						<h3>Recommended</h3>}
					<div className="customerCheckoutSuggsItems">
						{recs.map(item => (
							<SuggestedMenuItem key={item.id} item={item} addToCart={addToCartChkout} />
						))}
					</div>
				</div>
				<form className="customerNameForm">
					<label for="customerName">Please enter your name:</label>
					<input type="text" id="customerName" name="customerName" />
				</form>
				<div id="customerCheckoutButtons">
					<button className="cancelButton" onClick={cancelOrder}>Cancel Order</button>
					{showModal && (
						<h3 className="orderPopup">Order Successful</h3>
					)}
					<button onClick={e => handlePayment()} id="customerCheckoutBtn">Checkout ${orderValue.toFixed(2)}</button>
				</div>

			</div>
		</div>

	);
}

export default CustomerCheckout;