diff --git a/src/Components/LayoutFooter/LayoutFooter.js b/src/Components/LayoutFooter/LayoutFooter.js index c109ed0..cf41b81 100644 --- a/src/Components/LayoutFooter/LayoutFooter.js +++ b/src/Components/LayoutFooter/LayoutFooter.js @@ -1,24 +1,32 @@ -import React, { useState } from 'react'; -import { useNavigate } from "react-router-dom"; - -import ButtonSet from '../FooterButton/FooterButton'; -import PropTypes from 'prop-types'; -import ModalNewOrder from './ModalNewOrder'; +import PropTypes from "prop-types"; +import React, { useState } from "react"; import { IoMdArrowDropleft } from "react-icons/io"; +import { useNavigate } from "react-router-dom"; +import ButtonSet from "../FooterButton/FooterButton"; +import ModalNewOrder from "./ModalNewOrder"; const NewTicket = () => ( -
- - - - - - - - -
-) - +
+ + + + + + + + +
+); /** * Footer component for the POS Front-End application. @@ -35,82 +43,160 @@ const NewTicket = () => ( * @param {Function} props.setDrawerOpen - Function to open the drawer (typically for navigation or options). * @returns {JSX.Element} The rendered Footer component. */ -function Footer({ buttons, setConfig, setOrders, activeTab, updateActiveTab, setSelectedOrder, setDrawerOpen }) { - const [isModalOpen, setModalOpen] = useState(false); - - return ( -
-
-
- -
-
-
{setModalOpen(!isModalOpen)}}> - -
-
{setDrawerOpen(true)}}> - -
-
-
- {isModalOpen && ()} -
- ); +function Footer({ + buttons, + setConfig, + setOrders, + activeTab, + updateActiveTab, + setSelectedOrder, + setDrawerOpen, +}) { + const [isModalOpen, setModalOpen] = useState(false); + + return ( +
+
+
+ +
+
+
{ + setModalOpen(!isModalOpen); + }} + > + +
+
{ + setDrawerOpen(true); + }} + > + +
+
+
+ {isModalOpen && ( + + )} +
+ ); } -export function FooterMainButton({ price, config, setConfig, priceLess, setOrders, payDetail, setPriceLess, setPayList }) { - const navigate = useNavigate(); - - const handlePayement = async () => { - const body = { value: payDetail, user: Number(JSON.parse(localStorage.getItem("userInfo")).id), discount: 0 }; - await fetch(`${process.env.REACT_APP_BACKEND_URL}:${process.env.REACT_APP_BACKEND_PORT}/api/${localStorage.getItem("restaurantID")}/orders/payment/${config.id_order}`, { - method: 'PUT', - body: JSON.stringify(body), - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${localStorage.getItem("token")}` - } - }); - }; +export function FooterMainButton({ + price, + config, + setConfig, + priceLess, + setOrders, + payDetail, + setPriceLess, + setPayList, +}) { + const navigate = useNavigate(); + + const handlePayement = async () => { + const body = { + value: payDetail, + user: Number(JSON.parse(localStorage.getItem("userInfo")).id), + discount: 0, + }; + await fetch( + `${process.env.REACT_APP_BACKEND_URL}:${process.env.REACT_APP_BACKEND_PORT}/api/${localStorage.getItem("restaurantID")}/orders/payment/${config.id_order}`, + { + method: "PUT", + body: JSON.stringify(body), + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + }, + ); + }; - return ( -
- {(priceLess > 0) || (!config.payement) ? ( -
-
{ setConfig(prevConfig => ({ ...prevConfig, payement: !prevConfig.payement })); navigate(!config.payement ? '/dashboard/pay' : '/dashboard'); }}> - {!config.payement ? `Encaisser ${Number(price).toFixed(2).toString()}€` : 'Retour'} -
-
- ) : ( -
-
{ handlePayement(); setPriceLess(0); setPayList([]); setConfig(prevConfig => ({ ...prevConfig, payement: !prevConfig.payement })); setOrders({number: "42", channel: "Sur place", orderId: null, food: [], tmp: {}}); navigate(!config.payement ? '/dashboard/pay' : '/dashboard'); }}> - Terminée -
-
- )} -
- ) + return ( +
+ {priceLess > 0 || !config.payement ? ( +
+
{ + setConfig((prevConfig) => ({ + ...prevConfig, + payement: !prevConfig.payement, + })); + navigate(!config.payement ? "/dashboard/pay" : "/dashboard"); + }} + > + {!config.payement + ? `Encaisser ${Number(price).toFixed(2).toString()}€` + : "Retour"} +
+
+ ) : ( +
+
{ + handlePayement(); + setPriceLess(0); + setPayList([]); + setConfig((prevConfig) => ({ + ...prevConfig, + payement: !prevConfig.payement, + firstSend: true, + })); + setOrders({ + number: "DIRECT", + channel: "Sur place", + orderId: null, + food: [], + tmp: {}, + }); + navigate(!config.payement ? "/dashboard/pay" : "/dashboard"); + }} + > + Terminée +
+
+ )} +
+ ); } Footer.propTypes = { - buttons: PropTypes.array.isRequired, - setConfig: PropTypes.func.isRequired, - setOrders: PropTypes.func.isRequired, - activeTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, - updateActiveTab: PropTypes.func.isRequired, - setSelectedOrder: PropTypes.func.isRequired, - setDrawerOpen: PropTypes.func.isRequired, -} + buttons: PropTypes.array.isRequired, + setConfig: PropTypes.func.isRequired, + setOrders: PropTypes.func.isRequired, + activeTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) + .isRequired, + updateActiveTab: PropTypes.func.isRequired, + setSelectedOrder: PropTypes.func.isRequired, + setDrawerOpen: PropTypes.func.isRequired, +}; FooterMainButton.propTypes = { - price: PropTypes.string.isRequired, - config: PropTypes.object.isRequired, - setConfig: PropTypes.func.isRequired, - priceLess: PropTypes.number.isRequired, - setOrders: PropTypes.func.isRequired, - payDetail: PropTypes.array.isRequired, - setPriceLess: PropTypes.func.isRequired, - setPayList: PropTypes.func.isRequired, -} + price: PropTypes.string.isRequired, + config: PropTypes.object.isRequired, + setConfig: PropTypes.func.isRequired, + priceLess: PropTypes.number.isRequired, + setOrders: PropTypes.func.isRequired, + payDetail: PropTypes.array.isRequired, + setPriceLess: PropTypes.func.isRequired, + setPayList: PropTypes.func.isRequired, +}; export default Footer; diff --git a/src/Components/LayoutFooter/ModalNewOrder.js b/src/Components/LayoutFooter/ModalNewOrder.js index a53be89..a432f88 100644 --- a/src/Components/LayoutFooter/ModalNewOrder.js +++ b/src/Components/LayoutFooter/ModalNewOrder.js @@ -1,88 +1,106 @@ -import React from 'react'; - -import PropTypes from 'prop-types'; +import PropTypes from "prop-types"; +import React from "react"; import { useNavigate } from "react-router-dom"; -function ModalNewOrder({setModalOpen, setOrders, setConfig, setSelectedOrder}) { - - const navigate = useNavigate(); +function ModalNewOrder({ + setModalOpen, + setOrders, + setConfig, + setSelectedOrder, +}) { + const navigate = useNavigate(); - const clearOrder = (orderType) => { - if (orderType === "direct") { - setOrders({ - number: "Direct", - channel: "Sur place", - orderId: null, - food: [], - tmp: {} - }); - } else { - fetch(`${process.env.REACT_APP_BACKEND_URL}:${process.env.REACT_APP_BACKEND_PORT}/api/${localStorage.getItem("restaurantID")}/orders/number?channel=${orderType}`, - {headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }}) - .then((response) => { - if (response.status === 401) { - navigate("/", {state: {error: "Unauthorized access. Please log in."}}); - throw new Error("Unauthorized access. Please log in."); - } - return response.json(); - }) - .then(data => { - if (orderType === "eatin") { - setOrders({ - number: `${data}`, - channel: "Sur place", - orderId: null, - food: [], - tmp: {} - }); - } - if (orderType === "togo") { - setOrders({ - number: `${data}`, - channel: "A emporter", - orderId: null, - food: [], - tmp: {} - }); - } - }) - } - // } else if (orderType === "OnSite") { - // setOrders([{nb: ""}, [], {channel: "Sur place"}, {orderId: null}]); - // } else { - // setOrders([{nb: ""}, [], {channel: "A emporter"}, {orderId: null}]); - // } - setConfig({payement: false, firstSend: true, id_order: null}); - setSelectedOrder(""); - setModalOpen(false); - } + const clearOrder = (orderType) => { + if (orderType === "direct") { + setOrders({ + number: "DIRECT", + channel: "Sur place", + orderId: null, + food: [], + tmp: {}, + }); + } else { + fetch( + `${process.env.REACT_APP_BACKEND_URL}:${process.env.REACT_APP_BACKEND_PORT}/api/${localStorage.getItem("restaurantID")}/orders/number?channel=${orderType}`, + { + headers: { + Authorization: `Bearer ${localStorage.getItem("token")}`, + }, + }, + ) + .then((response) => { + if (response.status === 401) { + navigate("/", { + state: { error: "Unauthorized access. Please log in." }, + }); + throw new Error("Unauthorized access. Please log in."); + } + return response.json(); + }) + .then((data) => { + if (orderType === "eatin") { + setOrders({ + number: `${data}`, + channel: "Sur place", + orderId: null, + food: [], + tmp: {}, + }); + } + if (orderType === "togo") { + setOrders({ + number: `${data}`, + channel: "A emporter", + orderId: null, + food: [], + tmp: {}, + }); + } + }); + } + // } else if (orderType === "OnSite") { + // setOrders([{nb: ""}, [], {channel: "Sur place"}, {orderId: null}]); + // } else { + // setOrders([{nb: ""}, [], {channel: "A emporter"}, {orderId: null}]); + // } + setConfig({ payement: false, firstSend: true, id_order: null }); + setSelectedOrder(""); + setModalOpen(false); + }; - return ( -
-
-
-
clearOrder("direct")}> - DIRECT -
-
clearOrder("eatin")}> - Sur place -
-
clearOrder("togo")}> - A emporter -
-
-
-
- ); + return ( +
+
+
+
clearOrder("direct")} + > + DIRECT +
+
clearOrder("eatin")} + > + Sur place +
+
clearOrder("togo")} + > + A emporter +
+
+
+
+ ); } ModalNewOrder.propTypes = { - setModalOpen: PropTypes.func.isRequired, - setConfig: PropTypes.func.isRequired, - setOrders: PropTypes.func.isRequired, - setSelectedOrder: PropTypes.func.isRequired, -} + setModalOpen: PropTypes.func.isRequired, + setConfig: PropTypes.func.isRequired, + setOrders: PropTypes.func.isRequired, + setSelectedOrder: PropTypes.func.isRequired, +}; -export default ModalNewOrder; \ No newline at end of file +export default ModalNewOrder; diff --git a/src/Pages/PosRouter.js b/src/Pages/PosRouter.js index 5548cd0..50b75ad 100644 --- a/src/Pages/PosRouter.js +++ b/src/Pages/PosRouter.js @@ -1,86 +1,124 @@ -import React, { useState, useEffect } from 'react'; -import { BrowserRouter, Routes, Route } from "react-router-dom"; - -import Login from './Login/Login'; -import Loading from './Loading/Loading'; -import Dashboard from './Dashboard/Dashboard'; -import Pay from './Pay/Pay'; -import Layout from './Layout'; - -import { DndProvider } from 'react-dnd' -import { HTML5Backend } from 'react-dnd-html5-backend' +import React, { useEffect, useState } from "react"; +import { DndProvider } from "react-dnd"; +import { HTML5Backend } from "react-dnd-html5-backend"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import Dashboard from "./Dashboard/Dashboard"; +import Layout from "./Layout"; +import Loading from "./Loading/Loading"; +import Login from "./Login/Login"; +import Pay from "./Pay/Pay"; // Initial data for the orders // TMP : To be replaced when the 'New order' button is implemented -let data = - { - number: "42", - channel: "Sur place", - orderId: null, - food: [], - tmp: {} - }; +const data = { + number: "DIRECT", + channel: "Sur place", + orderId: null, + food: [], + tmp: {}, +}; /** * Component : Main Component of the POS Application, used as the main Router, handles all initialisation of variables, states and routes needed for the POS. - * + * * @component PosRouter */ function PosRouter() { + const [config, setConfig] = useState({ + payement: false, + firstSend: true, + id_order: null, + }); + const [price, setPrice] = useState(null); + const [orders, setOrders] = useState(data); + const [priceLess, setPriceLess] = useState(price); + const [payList, setPayList] = useState([]); + const [payDetail, setPayDetail] = useState([]); + const [ready, setReady] = useState(false); + const [orderDetails, setOrderDetails] = useState({ details: [], sups: [] }); + const [tableBoard, setTableBoard] = useState( + localStorage.getItem("tables") + ? JSON.parse(localStorage.getItem("tables")) + : [], + ); - const [config, setConfig] = useState({ payement: false, firstSend: true, id_order: null}); - const [price, setPrice] = useState(null); - const [orders, setOrders] = useState(data); - const [priceLess, setPriceLess] = useState(price); - const [payList, setPayList] = useState([]); - const [payDetail, setPayDetail] = useState([]); - const [ready, setReady] = useState(false); - const [orderDetails, setOrderDetails] = useState({details: [], sups: []}); - const [tableBoard, setTableBoard] = useState(localStorage.getItem("tables") ? JSON.parse(localStorage.getItem("tables")) : []); - - useEffect(() => { - localStorage.setItem("tables", JSON.stringify(tableBoard)); - }, [tableBoard]); + useEffect(() => { + localStorage.setItem("tables", JSON.stringify(tableBoard)); + }, [tableBoard]); - //update the price of the current order every time the order is updated - useEffect(() => { - let tmp = 0; - for (let i = 0; i < orders.food.length; i++) { - if (orders.food[i].price) { - for (let j = 0; j < orders.food[i].quantity; j++) { - let priceSup = 0; - if (orders.food[i].mods_ingredients) { - orders.food[i].mods_ingredients.forEach((ingredient) => { - if (ingredient.suppPrice) { - priceSup += Number(ingredient.suppPrice); - } - }); - } - tmp += Number(orders.food[i].price) + priceSup; - } - } - } - setPrice(tmp); - setPriceLess(tmp); - setReady(true) - }, [orders]); + //update the price of the current order every time the order is updated + useEffect(() => { + let tmp = 0; + for (let i = 0; i < orders.food.length; i++) { + if (orders.food[i].price) { + for (let j = 0; j < orders.food[i].quantity; j++) { + let priceSup = 0; + if (orders.food[i].mods_ingredients) { + orders.food[i].mods_ingredients.forEach((ingredient) => { + if (ingredient.suppPrice) { + priceSup += Number(ingredient.suppPrice); + } + }); + } + tmp += Number(orders.food[i].price) + priceSup; + } + } + } + setPrice(tmp); + setPriceLess(tmp); + setReady(true); + }, [orders]); - if (ready) { - return ( - - - - } /> - } /> - }> - } /> - } /> - - - - - ); - } + if (ready) { + return ( + + + + } /> + } + /> + + } + > + + } + /> + } /> + + + + + ); + } } export default PosRouter; diff --git a/src/__tests__/Components/LayoutFooter.test.js b/src/__tests__/Components/LayoutFooter.test.js index e71b2e2..c949d77 100644 --- a/src/__tests__/Components/LayoutFooter.test.js +++ b/src/__tests__/Components/LayoutFooter.test.js @@ -1,372 +1,389 @@ -import React from 'react'; -import { render, screen, fireEvent, act } from '@testing-library/react'; -import Footer from '../../Components/LayoutFooter/LayoutFooter'; -import { useNavigate } from 'react-router-dom'; -import { FooterMainButton } from '../../Components/LayoutFooter/LayoutFooter'; - -jest.mock('react-router-dom', () => ({ - useNavigate: jest.fn(() => jest.fn()), +import { act, fireEvent, render, screen } from "@testing-library/react"; +import React from "react"; +import { useNavigate } from "react-router-dom"; +import Footer, { + FooterMainButton, +} from "../../Components/LayoutFooter/LayoutFooter"; + +jest.mock("react-router-dom", () => ({ + useNavigate: jest.fn(() => jest.fn()), })); -describe('Footer Component', () => { - let setConfig, setOrders, navigate, setPayDetail, setSelectedOrder, setPayList, updateActiveTab; - - beforeEach(() => { - setConfig = jest.fn(); - setOrders = jest.fn(); - updateActiveTab = jest.fn(); - setPayDetail = jest.fn(); - setSelectedOrder = jest.fn(); - setPayList = jest.fn(); - navigate = useNavigate(); - navigate.mockClear(); - }); - - test('renders buttons based on buttons prop', () => { - let buttons = ['tables','commandes']; - - const { rerender } = render( -