diff --git a/design/scrum/step01_letsorder_planning_before_scrum.png b/design/scrum/step01_letsorder_planning_before_scrum.png new file mode 100644 index 0000000..9600dba Binary files /dev/null and b/design/scrum/step01_letsorder_planning_before_scrum.png differ diff --git a/design/scrum/step02_adding labels.png b/design/scrum/step02_adding labels.png new file mode 100644 index 0000000..db7bc78 Binary files /dev/null and b/design/scrum/step02_adding labels.png differ diff --git a/design/scrum/step03_epics categorized.png b/design/scrum/step03_epics categorized.png new file mode 100644 index 0000000..a92dbe0 Binary files /dev/null and b/design/scrum/step03_epics categorized.png differ diff --git a/dev/reactletsorder.ctb b/dev/reactletsorder.ctb new file mode 100644 index 0000000..38d6a86 Binary files /dev/null and b/dev/reactletsorder.ctb differ diff --git a/package.json b/package.json index a08e32d..84ff3a4 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "web-vitals": "^1.1.0" }, "scripts": { - "start": "react-scripts start", + "start": "PORT=3005 react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" diff --git a/src/components/FoodOrder/Cart/Cart.js b/src/components/FoodOrder/Cart/Cart.js index d74e70c..6cec969 100644 --- a/src/components/FoodOrder/Cart/Cart.js +++ b/src/components/FoodOrder/Cart/Cart.js @@ -1,28 +1,15 @@ import React, { useContext, useState } from "react"; -import Modal from "../UI/Modal"; +import Modal from "../UI/Modal/Modal"; import CartItem from "./CartItem"; import classes from "./Cart.module.css"; import CartContext from "../store/cart-context"; import OrderDetails from "./OrderDetails"; const Cart = (props) => { - - const userData = { - "clientName": "Marvin the Martian", - "clientId": "123" - }; - /* - const pruebaData = { - "ordersDate": "26-09-2021", - "ordersStatus": "ON KITCHEN", - "ordersDelivaddress": "Comunidad El Pino", - "idPayment": "1", - "idUser": "1" - }; - */ const [isCheckout, setIsCheckout] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [didSubmit, setDidSubmit] = useState(false); + const [isErrorOnSentOrder, setIsErrorOnSentOrder] = useState(false); const cartCtx = useContext(CartContext); const totalAmount = `$${cartCtx.totalAmount.toFixed(2)}`; @@ -33,37 +20,46 @@ const Cart = (props) => { }; const cartItemAddHandler = (item) => { - cartCtx.addItem({...item, amount: 1}); + cartCtx.addItem({ ...item, amount: 1 }); }; const orderHandler = () => { setIsCheckout(true); }; + const showCartHandler = () => { + setIsCheckout(false); + }; + + const errorOnSentOrderHandler = () => { + setIsErrorOnSentOrder(true); + }; + const submitOrderHandler = async (userData) => { - /* - await fetch("http://localhost:8080/api/orders", { - credentials: "include", + setIsSubmitting(true); + const rawData = cartCtx.items; + const cleanData = rawData.map(({id, ...restOfTheFields}) => restOfTheFields); + + const response = await fetch("http://localhost:3000/ordertemps", { + // method: "POST", - body: JSON.stringify(pruebaData), headers: { - 'Content-Type': 'application/json', -// 'Access-Control-Allow-Origin': '*', - 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0MUBmYWtlbWFpbC5jb20iLCJleHAiOjE2MzI3NTAzODYsImlhdCI6MTYzMjcxNDM4Nn0.2tvdnG9B0HdpUpV0xsOKKaATFkyuNVKMpzYE8sXBFtw', - } - */ - - setIsSubmitting(true); - await fetch("https://movieserp-default-rtdb.firebaseio.com/orders.json", { - method: 'POST', - BODY: JSON.stringify({ - user: userData, - orderedItems: cartCtx.items, - }), + "Content-Type": "application/json", + }, + body: JSON.stringify({ + ordertemp: cleanData, + }), //please include user: userData }); - setIsSubmitting(false); - setDidSubmit(true); - cartCtx.clearCart(); + + if (!response.ok){ + errorOnSentOrderHandler(); + } + else{ + setIsSubmitting(false); + setIsCheckout(false); + setDidSubmit(true); + cartCtx.clearCart(); + } }; const cartItems = ( @@ -81,16 +77,46 @@ const Cart = (props) => { ); - const modalActions = ( -
+ const cartEmptyButtons = ( + - {hasItems && ( - - )} + + ); + + const cartContentButtons = ( + + + + + ); + + const orderDetailsButtons = ( + + + + + + ); + + const modalActions = ( +
+ {!isCheckout && hasItems + ? cartContentButtons + : !isCheckout && !hasItems + ? cartEmptyButtons + : orderDetailsButtons}
); @@ -101,16 +127,36 @@ const Cart = (props) => { Total Amount {totalAmount}
+ {modalActions} + + ); + const OrderDetailsModalContent = ( + {isCheckout && ( - + )} - {!isCheckout && modalActions} + {modalActions} ); - const isSubmittingModalContent =

Sending order data...

; /* incluir transaccion para verificar si es exitoso o hubo algun error */ + const isSubmittingModalContent = ( + +

Sending order data...

+
+ ); + + const errorOnSentOrderModalContent = ( + +

Process failed. An error occurs sending the order!

+
+ +
+
+ ); const didSubmitModalContent = ( @@ -125,8 +171,10 @@ const Cart = (props) => { return ( - {!isSubmitting && !didSubmit && CartModalContent} - {isSubmitting && isSubmittingModalContent} + {!isSubmitting && !didSubmit && !isCheckout && CartModalContent} + {isCheckout && !isSubmitting && OrderDetailsModalContent} + {isSubmitting && !isErrorOnSentOrder && !didSubmit && isSubmittingModalContent} + {isErrorOnSentOrder && errorOnSentOrderModalContent} {!isSubmitting && didSubmit && didSubmitModalContent} ); diff --git a/src/components/FoodOrder/Cart/CartItem.js b/src/components/FoodOrder/Cart/CartItem.js index 0c534fc..c9291c7 100644 --- a/src/components/FoodOrder/Cart/CartItem.js +++ b/src/components/FoodOrder/Cart/CartItem.js @@ -1,7 +1,8 @@ import classes from './CartItem.module.css'; const CartItem = (props) => { - const price = `$${props.price.toFixed(2)}`; + //const price = `$${props.price.toFixed(2)}`; + const price = `$${parseFloat(props.price).toFixed(2)}`; return (
  • diff --git a/src/components/FoodOrder/Cart/OrderDetails.js b/src/components/FoodOrder/Cart/OrderDetails.js index d374032..b627ade 100644 --- a/src/components/FoodOrder/Cart/OrderDetails.js +++ b/src/components/FoodOrder/Cart/OrderDetails.js @@ -1,11 +1,17 @@ import { useRef, useState } from "react"; import TextField from "@material-ui/core/TextField"; -import Autocomplete from "@material-ui/lab/Autocomplete"; +import { + Select, + MenuItem, + FormControl, + InputLabel +} from "@material-ui/core"; import classes from "./Orderdetails.module.css"; const isEmpty = (value) => value.trim() === ""; const isNotNineChars = (value) => value.trim().length !== 9; -const paymentMethods = ["Cash", "Credit", "Crypto"]; +const notValidPayment = (value) => value === "0"; +//const paymentMethods = ["Cash", "Credit", "Crypto"]; const OrderDetails = (props) => { const [formInputsValidity, setFormInputsValidity] = useState({ @@ -15,8 +21,12 @@ const OrderDetails = (props) => { cityDeliveryAddress: true, postalCodeDeliveryAddress: true, ordersDeliveryAddress: true, + methodOfPayment: true, + }); + const [mopayment, setMopayment] = useState(0); + //estos objetos sirven para no capturar todos los keystrokes durante dataInput const clientNameRef = useRef(); const clientCellPhoneRef = useRef(); @@ -24,6 +34,9 @@ const OrderDetails = (props) => { const cityDeliveryAddressRef = useRef(); const postalCodeDeliveryAddressRef = useRef(); const ordersDeliveryAddressRef = useRef(); + const methodOfPaymentRef = useRef(); + + const handlerMopaymentChange = (e) => setMopayment(e.target.value); const ConfirmHandler = (event) => { event.preventDefault(); @@ -35,6 +48,7 @@ const OrderDetails = (props) => { const enteredCityDeliveryAddress = cityDeliveryAddressRef.current.value; const enteredPostalCodeDeliveryAddress = postalCodeDeliveryAddressRef.current.value; + const enteredMethodOfPayment = mopayment; const enteredNameIsValid = !isEmpty(enteredName); const enteredCellPhoneIsValid = !isNotNineChars(enteredCellPhone); @@ -51,6 +65,8 @@ const OrderDetails = (props) => { enteredPostalCodeDeliveryAddress ); + const enteredMethodOfPaymentIsValid = !notValidPayment(enteredMethodOfPayment); + setFormInputsValidity({ clientName: enteredNameIsValid, clientCellPhone: enteredCellPhoneIsValid, @@ -58,6 +74,7 @@ const OrderDetails = (props) => { streetDeliveryAddress: enteredStreetDeliveryAddressIsValid, cityDeliveryAddress: enteredCityDeliveryAddressIsValid, postalCodeDeliveryAddress: enteredPostalCodeDeliveryAddressIsValid, + methodOfPayment: enteredMethodOfPaymentIsValid, }); const formIsValid = @@ -66,7 +83,8 @@ const OrderDetails = (props) => { enteredOrdersDeliveryAddressIsValid && enteredStreetDeliveryAddressIsValid && enteredCityDeliveryAddressIsValid && - enteredPostalCodeDeliveryAddressIsValid; + enteredPostalCodeDeliveryAddressIsValid && + enteredMethodOfPaymentIsValid; if (!formIsValid) { return; @@ -79,6 +97,7 @@ const OrderDetails = (props) => { streetDeliveryAddress: enteredStreetDeliveryAddress, cityDeliveryAddress: enteredCityDeliveryAddress, postalCodeDeliveryAddress: enteredPostalCodeDeliveryAddress, + methodOfPayment: enteredMethodOfPayment, }); }; @@ -100,6 +119,9 @@ const OrderDetails = (props) => { const postalCodeDeliveryAddressControlClasses = `${classes.control} ${ formInputsValidity.postalCodeDeliveryAddress ? "" : classes.invalid }`; + const methodOfPaymentControlClasses = `${classes.control} ${ + formInputsValidity.methodOfPayment ? "" : classes.invalid + }`; return (
    @@ -107,6 +129,7 @@ const OrderDetails = (props) => { @@ -117,6 +140,7 @@ const OrderDetails = (props) => { {!formInputsValidity.clientCellPhone && ( @@ -128,6 +152,7 @@ const OrderDetails = (props) => { {!formInputsValidity.ordersDeliveryAddress && ( @@ -139,6 +164,7 @@ const OrderDetails = (props) => { {!formInputsValidity.cityDeliveryAddress && ( @@ -150,45 +176,47 @@ const OrderDetails = (props) => { {!formInputsValidity.streetDeliveryAddress && (

    Please Enter a valid street name

    )} - +
    - + {!formInputsValidity.postalCodeDeliveryAddress && (

    Please Enter a valid Postal Code

    )} -
    + +
    -
    - - Method of Payment * + + + {!formInputsValidity.methodOfPayment && ( +

    Please Choose a Method of Payment

    + )}
    + ); }; diff --git a/src/components/FoodOrder/FoodOrder.js b/src/components/FoodOrder/FoodOrder.js index 568797b..67fb894 100644 --- a/src/components/FoodOrder/FoodOrder.js +++ b/src/components/FoodOrder/FoodOrder.js @@ -1,31 +1,71 @@ -import React, { useState } from 'react'; -import Header from './Layout/Header'; -import Meals from './Meals/Meals'; -import Cart from './Cart/Cart'; -import CartProvider from './store/CartProvider'; - +import React, { useState } from "react"; +import Header from "./Layout/Header"; +import Meals from "./Meals/Meals"; +import Cart from "./Cart/Cart"; +import Login from "./Login/Login"; +import Signup from "./Login/Signup"; +import CartProvider from "./store/CartProvider"; const FoodOrder = () => { - const [cartIsShown, setCartIsShown] = useState(false); - - const showCartHandler = () => { - setCartIsShown(true); - } - - const hideCartHandler = () => { - setCartIsShown(false); - } - - return( - - {cartIsShown && } -
    -
    - -
    - - - ); -} - -export default FoodOrder; \ No newline at end of file + const [cartIsShown, setCartIsShown] = useState(false); + const [loginIsShown, setLoginIsShown] = useState(false); + const [SignupIsShown, setSignupIsShown] = useState(false); + + const showCartHandler = () => { + setCartIsShown(true); + }; + + const hideCartHandler = () => { + setCartIsShown(false); + }; + + const showLoginHandler = () => { + setLoginIsShown(true); + }; + + const hideLoginHandler = () => { + setLoginIsShown(false); + }; + + const showSignupHandler = () => { + setSignupIsShown(true); + }; + + const hideSignupHandler = () => { + setSignupIsShown(false); + }; + + const requestLogoutHandler = () => { + /* + steps to consider: + 1. products in the cart + 2. orders not place them + */ + + /* + source code example: + const authCtx = useContext(AuthContext); + + */ + } + + + return ( + + {cartIsShown && } + {loginIsShown && } + {SignupIsShown && } +
    +
    + +
    + + ); +}; + +export default FoodOrder; diff --git a/src/components/FoodOrder/Layout/Header.js b/src/components/FoodOrder/Layout/Header.js index 6b2f6c3..e11c0e4 100644 --- a/src/components/FoodOrder/Layout/Header.js +++ b/src/components/FoodOrder/Layout/Header.js @@ -1,20 +1,33 @@ -import React, { Fragment }from 'react'; -import HeaderCartButton from './HeaderCartButton'; -import mealsImage from '../assets/banner.jpg'; -import classes from './Header.module.css'; +import React, { useContext, Fragment } from "react"; +import AuthContext from "../store/auth-context"; +import HeaderCartButton from "../UI/Buttons/HeaderCartButton"; +import HeaderActionButton from "../UI/Buttons/HeaderActionButton"; +import mealsImage from "../assets/banner.jpg"; +import classes from "./Header.module.css"; -const Header = props => { - return ( - -
    -

    Lets Order!!!

    - -
    -
    - all you can eat brunch -
    -
    - ); +const Header = (props) => { + const authCtx = useContext(AuthContext); + return ( + +
    +

    Lets Order!!!

    + {authCtx.isLoggedIn ? ( +
    + + +
    + ) : ( +
    + + +
    + )} +
    +
    + all you can eat brunch +
    +
    + ); }; -export default Header; \ No newline at end of file +export default Header; diff --git a/src/components/FoodOrder/Layout/Header.module.css b/src/components/FoodOrder/Layout/Header.module.css index 8549499..bf00b1d 100644 --- a/src/components/FoodOrder/Layout/Header.module.css +++ b/src/components/FoodOrder/Layout/Header.module.css @@ -26,4 +26,9 @@ height: 100%; object-fit: cover; transform: rotateZ(-5deg) translateY(-4rem) translateX(-1rem); + } + + .btncontainer { + display: flex; + } \ No newline at end of file diff --git a/src/components/FoodOrder/Layout/Navigation.js b/src/components/FoodOrder/Layout/Navigation.js new file mode 100644 index 0000000..f110e0a --- /dev/null +++ b/src/components/FoodOrder/Layout/Navigation.js @@ -0,0 +1,32 @@ +import React, { useContext } from 'react'; + +import AuthContext from '../store/auth-context'; +import classes from './Navigation.module.css'; + +const Navigation = () => { + const ctx = useContext(AuthContext); + + return ( + + ); +}; + +export default Navigation; diff --git a/src/components/FoodOrder/Layout/Navigation.module.css b/src/components/FoodOrder/Layout/Navigation.module.css new file mode 100644 index 0000000..b09826f --- /dev/null +++ b/src/components/FoodOrder/Layout/Navigation.module.css @@ -0,0 +1,43 @@ +.nav ul { + list-style: none; + margin: 0; + padding: 0; + display: flex; + align-items: center; +} + +.nav li { + margin: 0; + margin-left: 2rem; +} + +.nav a { + text-decoration: none; + color: white; +} + +.nav a:hover, +.nav a:active { + color: #f3cafb; +} + +.nav button { + font: inherit; + background: #dd0db0; + border: 1px solid #dd0db0; + padding: 0.5rem 1.5rem; + color: white; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.26); + border-radius: 20px; +} + +.nav button:focus { + outline: none; +} + +.nav button:hover, +.nav button:active { + color: #f3cafb; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.26); +} diff --git a/src/components/FoodOrder/Login/Login.js b/src/components/FoodOrder/Login/Login.js new file mode 100644 index 0000000..0c85b41 --- /dev/null +++ b/src/components/FoodOrder/Login/Login.js @@ -0,0 +1,135 @@ +import React, { useContext, useState } from "react"; +import Modal from "../UI/Modal/Modal"; +import classes from "./Login.module.css"; +import Input from "../UI/Input/Input"; +import AuthContext from "../store/auth-context"; + +const Login = (props) => { + const [emailValue, setEmailValue] = useState(''); + const [passwordValue, setPasswordValue] = useState(''); + + const [isCanceling, setIsCanceling] = useState(false); + const [isValidating, setIsValidating] = useState(false); + const [didValidate, setDidValidate] = useState(false); + const [isErrorOnValidate, setIsErrorOnValidate] = useState(false); + const authCtx = useContext(AuthContext); + + const emailValueHandler = (event) => { + setEmailValue(event.target.value); + } + + const passwordValueHandler = (event) => { + setPasswordValue(event.target.value); + } + + const errorOnValidateHandler = () => { + setIsErrorOnValidate(true); + }; + + const validateCredentialsHandler = async () => { + setIsValidating(true); + const enteredEmail = emailValue; + const enteredPassword = passwordValue; + + const userCredentialsEntered = { + email: enteredEmail, + password: enteredPassword, + } + + const response = await fetch("http://localhost:3000/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(userCredentialsEntered), + }); + if (!response.ok) { + errorOnValidateHandler(); + } else { + setIsValidating(false); + setIsCanceling(false); + setDidValidate(true); + authCtx.onValidSession(); + //cartCtx.clearCart(); + } + }; + + const isValidatingModalContent =

    Validating Credentials...

    ; + /* incluir transaccion para verificar si es exitoso o hubo algun error */ + + const errorOnValidateModalContent = ( + +

    User or Password incorrect, please verify

    +
    + +
    +
    + ); + + const didValidateModalContent = ( + +

    Creditials verified, welcome!

    +
    + +
    +
    + ); + + const loginButtons = ( + + + + + ); + + const modalActions = ( +
    {!isCanceling ? loginButtons : ""}
    + ); + + const LoginModalContent = ( + + + + {modalActions} + + ); + + return ( + + {!isCanceling && !isValidating && !isErrorOnValidate && !didValidate && LoginModalContent} + {isValidating && isValidatingModalContent} + {isErrorOnValidate && errorOnValidateModalContent} + {!isValidating && didValidate && didValidateModalContent} + + ); +}; + +export default Login; diff --git a/src/components/FoodOrder/Login/Login.module.css b/src/components/FoodOrder/Login/Login.module.css new file mode 100644 index 0000000..c6ec895 --- /dev/null +++ b/src/components/FoodOrder/Login/Login.module.css @@ -0,0 +1,36 @@ +.login { + width: 90%; + max-width: 40rem; + margin: 2rem auto; + padding: 2rem; +} + +.actions { + text-align: center; +} + +.actions button { + font: inherit; + cursor: pointer; + background-color: transparent; + border: 1px solid #8a2b06; + padding: 0.5rem 2rem; + border-radius: 25px; + margin-left: 1rem; +} + +.actions button:hover, +.actions button:active { + background-color: #5a1a01; + border-color: #5a1a01; + color: white; +} + +.actions .button--alt { + color: #8a2b06; +} + +.actions .button { + background-color: #8a2b06; + color: white; +} diff --git a/src/components/FoodOrder/Login/Signup.js b/src/components/FoodOrder/Login/Signup.js new file mode 100644 index 0000000..080b50d --- /dev/null +++ b/src/components/FoodOrder/Login/Signup.js @@ -0,0 +1,183 @@ +import React, { useState, useRef } from "react"; +import Modal from "../UI/Modal/Modal"; +import classes from "./Login.module.css"; +import Input from "../UI/Input/Input"; + +const Signup = (props) => { + const [firstNameValue, setFirstNameValue] = useState(''); + const [lastNameValue, setLastNameValue] = useState(''); + const [emailClientValue, setEmailClientValue] = useState(''); + const [passwordClientValue, setPasswordClientValue] = useState(''); + + const [isCanceling, setIsCanceling] = useState(false); + const [isSaving, setIsSaving] = useState(false); + const [didSave, setDidSave] = useState(false); + const [isErrorOnSave, setIsErrorOnSave] = useState(false); + //const cartCtx = useContext(CartContext); + + const firstNameValueHandler = (event) => { + setFirstNameValue(event.target.value); + } + + const lastNameValueHandler = (event) => { + setLastNameValue(event.target.value); + } + + const emailClientValueHandler = (event) => { + setEmailClientValue(event.target.value); + } + + const passwordClientValueHandler = (event) => { + setPasswordClientValue(event.target.value); + } + + const errorOnSignupHandler = () => { + setIsErrorOnSave(true); + }; + + const signupHandler = async () => { + setIsSaving(true); + const enteredFirstname = firstNameValue; + const enteredLastname = lastNameValue; + const enteredEmail = emailClientValue; + const enteredPassword = passwordClientValue; + + const newClientData = { + firstname: enteredFirstname, + lastname: enteredLastname, + email: enteredEmail, + password: enteredPassword, + }; + + const response = await fetch("http://localhost:3000/clients", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(newClientData), + }); + + if (!response.ok) { + errorOnSignupHandler(); + } else { + setIsSaving(false); + setIsCanceling(false); + setDidSave(true); + //cartCtx.clearCart(); + } + }; + + const isSavingModalContent =

    Saving new user...

    ; + /* incluir transaccion para verificar si es exitoso o hubo algun error */ + + const errorOnSavingModalContent = ( + +

    The user account could not be created. Please try again later

    +
    + +
    +
    + ); + + const didSaveModalContent = ( + +

    User account created, welcome!

    +
    + +
    +
    + ); + + const SignupButtons = ( + + + + + ); + + const modalActions = ( +
    {!isCanceling ? SignupButtons : ""}
    + ); + + const SignupModalContent = ( + + + + + + + {modalActions} + + ); + + return ( + + {!isCanceling && !isSaving && !isErrorOnSave && !didSave && SignupModalContent} + {isSaving && isSavingModalContent} + {isErrorOnSave && errorOnSavingModalContent} + {!isSaving && didSave && didSaveModalContent} + + ); +}; + +export default Signup; diff --git a/src/components/FoodOrder/LoginUsers.js b/src/components/FoodOrder/LoginUsers.js new file mode 100644 index 0000000..ef5afec --- /dev/null +++ b/src/components/FoodOrder/LoginUsers.js @@ -0,0 +1,21 @@ +import React, { useContext } from "react"; + +import Login from "./Login/Login"; +import Home from "./Home/Home"; +import MainHeader from "./MainHeader/MainHeader"; +import AuthContext from "./store/auth-context"; + +function LoginUsers() { + const ctx = useContext(AuthContext); + return ( + + +
    + {!ctx.isLoggedIn && } + {ctx.isLoggedIn && } +
    +
    + ); +} + +export default LoginUsers; diff --git a/src/components/FoodOrder/Meals/AvailableMeals.js b/src/components/FoodOrder/Meals/AvailableMeals.js index eaab1c1..313cf07 100644 --- a/src/components/FoodOrder/Meals/AvailableMeals.js +++ b/src/components/FoodOrder/Meals/AvailableMeals.js @@ -1,10 +1,9 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState } from "react"; -import Card from "../UI/Card"; +import Card from "../UI/Card/Card"; import MealItem from "./MealItem/MealItem"; import classes from "./AvailableMeals.module.css"; - const AvailableMeals = () => { const [meals, setMeals] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -12,18 +11,20 @@ const AvailableMeals = () => { useEffect(() => { const fetchMeals = async () => { - const response = await fetch('https://movieserp-default-rtdb.firebaseio.com/meals.json'); - //const response = await fetch('http://localhost:8080/api/products'); + const response = await fetch("http://localhost:3000/meals", { + method: "GET", + }); - if(!response.ok){ - throw new Error('something went wrong'); + if (!response.ok) { + throw new Error("The data could not be shown"); } - const responseData = await response.json(); //este es un objeto + const responseData = await response.json(); + //el objeto se traduce a un array const loadedMeals = []; - for(const key in responseData){ + for (const key in responseData) { loadedMeals.push({ id: key, name: responseData[key].name, @@ -32,32 +33,41 @@ const AvailableMeals = () => { }); } - setMeals(loadedMeals); - setIsLoading(false); + //checking if the array is not null + if (!loadedMeals.length) { + throw new Error("No products found to display"); + } else { + setMeals(loadedMeals); + setIsLoading(false); + } }; - fetchMeals().catch(error => { + fetchMeals().catch((error) => { setIsLoading(false); setHttpError(error.message); }); }, []); - if(isLoading){ - return
    -

    Loading data...

    -
    + if (isLoading) { + return ( +
    +

    Loading data...

    +
    + ); } - if(httpError){ - return
    -

    {httpError}

    -
    + if (httpError) { + return ( +
    +

    {httpError}

    +
    + ); } const mealsList = meals.map((meal) => ( { const cartCtx = useContext(CartContext); - const price = `$${props.price.toFixed(2)}`; //content literal formatting + const authCtx = useContext(AuthContext); + //const price = `$${props.price.toFixed(2)}`; //content literal formatting + const price = `$ ${parseFloat(props.price).toFixed(2)}`; const addToCartHandler = amount => { cartCtx.addItem({ @@ -25,7 +28,7 @@ const MealItem = (props) => {
    {price}
    - + {authCtx.isLoggedIn && }
  • ); diff --git a/src/components/FoodOrder/Meals/MealItem/MealItemForm.js b/src/components/FoodOrder/Meals/MealItem/MealItemForm.js index cbea669..8958fbf 100644 --- a/src/components/FoodOrder/Meals/MealItem/MealItemForm.js +++ b/src/components/FoodOrder/Meals/MealItem/MealItemForm.js @@ -1,5 +1,5 @@ import { useRef, useState } from "react"; -import Input from "../../UI/Input"; +import Spinner from "../../UI/Spinner/Spinner"; import classes from "./MealItemForm.module.css"; const MealItemForm = (props) => { @@ -24,7 +24,7 @@ const MealItemForm = (props) => { return (
    - { + const userIcon = props.userIcon; + const requestedLabel = props.requestedLabel; + const [btnIsHighlighted, setBtnIsHighlighted] = useState(false); + + const btnClasses = `${classes.button} ${ + btnIsHighlighted ? classes.bump : "" + }`; + + useEffect(() => { + setBtnIsHighlighted(true); + const timer = setTimeout(() => { + setBtnIsHighlighted(false); + }, 300); + return () => { + clearTimeout(timer); + } + }, []); + + return ( + + ); +}; + +export default HeaderActionButton; diff --git a/src/components/FoodOrder/Layout/HeaderCartButton.js b/src/components/FoodOrder/UI/Buttons/HeaderCartButton.js similarity index 92% rename from src/components/FoodOrder/Layout/HeaderCartButton.js rename to src/components/FoodOrder/UI/Buttons/HeaderCartButton.js index 1125b42..92317c7 100644 --- a/src/components/FoodOrder/Layout/HeaderCartButton.js +++ b/src/components/FoodOrder/UI/Buttons/HeaderCartButton.js @@ -1,6 +1,6 @@ import { useContext, useEffect, useState } from "react"; -import CartIcon from "../Cart/CartIcon"; -import CartContext from "../store/cart-context"; +import CartIcon from "./CartIcon"; +import CartContext from "../../store/cart-context"; import classes from "./HeaderCartButton.module.css"; const HeaderCartButton = (props) => { diff --git a/src/components/FoodOrder/Layout/HeaderCartButton.module.css b/src/components/FoodOrder/UI/Buttons/HeaderCartButton.module.css similarity index 100% rename from src/components/FoodOrder/Layout/HeaderCartButton.module.css rename to src/components/FoodOrder/UI/Buttons/HeaderCartButton.module.css diff --git a/src/components/FoodOrder/UI/Buttons/SignupIcon.js b/src/components/FoodOrder/UI/Buttons/SignupIcon.js new file mode 100644 index 0000000..cf9e374 --- /dev/null +++ b/src/components/FoodOrder/UI/Buttons/SignupIcon.js @@ -0,0 +1,23 @@ +const SignupIcon = () => { + return ( + + + + + + + ); + }; + + export default SignupIcon; + \ No newline at end of file diff --git a/src/components/FoodOrder/UI/Buttons/UserIcon.js b/src/components/FoodOrder/UI/Buttons/UserIcon.js new file mode 100644 index 0000000..c591149 --- /dev/null +++ b/src/components/FoodOrder/UI/Buttons/UserIcon.js @@ -0,0 +1,22 @@ +const UserIcon = () => { + return ( + + + + + + ); + }; + + export default UserIcon; + \ No newline at end of file diff --git a/src/components/FoodOrder/UI/Card.js b/src/components/FoodOrder/UI/Card/Card.js similarity index 100% rename from src/components/FoodOrder/UI/Card.js rename to src/components/FoodOrder/UI/Card/Card.js diff --git a/src/components/FoodOrder/UI/Card.module.css b/src/components/FoodOrder/UI/Card/Card.module.css similarity index 100% rename from src/components/FoodOrder/UI/Card.module.css rename to src/components/FoodOrder/UI/Card/Card.module.css diff --git a/src/components/FoodOrder/UI/Input/Input.js b/src/components/FoodOrder/UI/Input/Input.js new file mode 100644 index 0000000..3d1dfd9 --- /dev/null +++ b/src/components/FoodOrder/UI/Input/Input.js @@ -0,0 +1,37 @@ +import React, { useRef, useImperativeHandle } from 'react'; +import classes from './Input.module.css'; + +const Input = React.forwardRef((props, ref) => { + const inputRef = useRef(); + + const activate = () => { + inputRef.current.focus(); + } + + useImperativeHandle(ref, () => { + return { + focus: activate + }; + }); + + return ( +
    + + +
    + ); +}); + +export default Input; \ No newline at end of file diff --git a/src/components/FoodOrder/UI/Input/Input.module.css b/src/components/FoodOrder/UI/Input/Input.module.css new file mode 100644 index 0000000..eb2fc7e --- /dev/null +++ b/src/components/FoodOrder/UI/Input/Input.module.css @@ -0,0 +1,44 @@ +.control { + margin: 1rem 0; + display: flex; + align-items: stretch; + flex-direction: column; + } + + .control label, + .control input { + display: block; + } + + .control label { + font-weight: bold; + flex: 1; + color: #464646; + margin-bottom: 0.5rem; + } + + .control input { + flex: 3; + font: inherit; + padding: 0.35rem 0.35rem; + border-radius: 6px; + border: 1px solid #ccc; + } + + .control input:focus { + outline: none; + border-color: #4f005f; + background: #f6dbfc; + } + + .control.invalid input { + border-color: red; + background: #fbdada; + } + + @media (min-width: 768px) { + .control { + align-items: center; + flex-direction: row; + } + } \ No newline at end of file diff --git a/src/components/FoodOrder/UI/Modal.js b/src/components/FoodOrder/UI/Modal/Modal.js similarity index 100% rename from src/components/FoodOrder/UI/Modal.js rename to src/components/FoodOrder/UI/Modal/Modal.js diff --git a/src/components/FoodOrder/UI/Modal.module.css b/src/components/FoodOrder/UI/Modal/Modal.module.css similarity index 100% rename from src/components/FoodOrder/UI/Modal.module.css rename to src/components/FoodOrder/UI/Modal/Modal.module.css diff --git a/src/components/FoodOrder/UI/Input.js b/src/components/FoodOrder/UI/Spinner/Spinner.js similarity index 77% rename from src/components/FoodOrder/UI/Input.js rename to src/components/FoodOrder/UI/Spinner/Spinner.js index 8034dfb..42ffe9f 100644 --- a/src/components/FoodOrder/UI/Input.js +++ b/src/components/FoodOrder/UI/Spinner/Spinner.js @@ -1,7 +1,7 @@ import React from 'react'; -import classes from './Input.module.css'; +import classes from './Spinner.module.css'; -const Input = React.forwardRef((props, ref) => { +const Spinner = React.forwardRef((props, ref) => { return(
    @@ -13,4 +13,4 @@ const Input = React.forwardRef((props, ref) => { ); }); -export default Input; \ No newline at end of file +export default Spinner; \ No newline at end of file diff --git a/src/components/FoodOrder/UI/Input.module.css b/src/components/FoodOrder/UI/Spinner/Spinner.module.css similarity index 100% rename from src/components/FoodOrder/UI/Input.module.css rename to src/components/FoodOrder/UI/Spinner/Spinner.module.css diff --git a/src/components/FoodOrder/store/AuthProvider.js b/src/components/FoodOrder/store/AuthProvider.js new file mode 100644 index 0000000..df8dd7d --- /dev/null +++ b/src/components/FoodOrder/store/AuthProvider.js @@ -0,0 +1,52 @@ +import { useReducer, useEffect, useState } from "react"; +import AuthContext from "./auth-context"; + +/* +const defaultAuthState = { + isLoggedIn: false, +}; +*/ + +export const AuthProvider = (props) => { + const [isLoggedIn, setIsLoggedIn] = useState(false); + + useEffect(() => { + //sin el uso de useEffect, el chequeo de esta flag serĂ­a infinito + //la funcion es ejecutada DESPUES que el componente ha sido re-evaluado + const storeUserLoggedInInfo = localStorage.getItem('isLoggedIn'); + if (storeUserLoggedInInfo === '1') { + setIsLoggedIn(true); + } + }, []); + + const logoutHandler = () => { + localStorage.removeItem('isLoggedIn'); + setIsLoggedIn(false); + }; + + const loginHandler = () => { + localStorage.setItem('isLoggedIn', '1'); + setIsLoggedIn(true); + }; + + //handler temporal + const initValidSessionHandler = () => { + setIsLoggedIn(true); + } + + return ( + + {props.children} + + ); + }; + +export default AuthProvider; + \ No newline at end of file diff --git a/src/components/FoodOrder/store/auth-context.js b/src/components/FoodOrder/store/auth-context.js new file mode 100644 index 0000000..a35bf63 --- /dev/null +++ b/src/components/FoodOrder/store/auth-context.js @@ -0,0 +1,11 @@ +import React from 'react'; + +const AuthContext = React.createContext({ + isLoggedIn: false, + onLogout: () => {}, + onLogin: (email, password) => {}, + onValidSession: () => {} + +}); + +export default AuthContext; diff --git a/src/index.js b/src/index.js index 9553039..9d7eefd 100644 --- a/src/index.js +++ b/src/index.js @@ -2,16 +2,15 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './containers/App'; -//import { AuthContextProvider } from './components/LoginUsers/store/auth-context'; +import { AuthProvider } from './components/FoodOrder/store/AuthProvider'; ReactDOM.render( - /* - + - , + , document.getElementById('root') - */ - , - document.getElementById('root') + + //, + //document.getElementById('root') );