Compare commits

...

6 Commits

7 changed files with 239 additions and 13 deletions

View File

@ -1,11 +1,37 @@
import { useContext } from "react";
import classes from "./Cart.module.css";
import Modal from "../UI/Modal";
import CartContext from "../../store/cart-context";
import CartItem from "./CartItem";
const Cart = (props) => {
const cartCtx = useContext(CartContext);
const totalAmount = `$${cartCtx.totalAmount.toFixed(2)}`;
const hasItems = cartCtx.items.length > 0;
const cartItemAddHandler = (item) => {
cartCtx.addItem({ ...item, amount: 1 });
};
const cartItemRemoveHandler = (id) => {
cartCtx.removeItem(id);
};
const cartItems = (
<ul className={classes["cart-items"]}>
{[{ id: "c1", name: "sushi", amount: 2, price: 12.99 }].map((item) => (
<li>{item.name}</li>
{cartCtx.items.map((item) => (
<CartItem
key={item.id}
name={item.name}
amount={item.amount}
price={item.price}
onRemove={cartItemRemoveHandler.bind(null, item.id)}
onAdd={cartItemAddHandler.bind(null, item)}
>
{item.name}
</CartItem>
))}
</ul>
);
@ -15,13 +41,13 @@ const Cart = (props) => {
{cartItems}
<div className={classes.total}>
<span>Total Amount</span>
<span>35.63</span>
<span>{totalAmount}</span>
</div>
<div className={classes.actions}>
<button className={classes["button--alt"]} onClick={props.onHideCart}>
Close
</button>
<button className={classes.button}>Order</button>
{hasItems && <button className={classes.button}>Order</button>}
</div>
</Modal>
);

View File

@ -0,0 +1,23 @@
import classes from './CartItem.module.css';
const CartItem = (props) => {
const price = `$${props.price.toFixed(2)}`;
return (
<li className={classes['cart-item']}>
<div>
<h2>{props.name}</h2>
<div className={classes.summary}>
<span className={classes.price}>{price}</span>
<span className={classes.amount}>x {props.amount}</span>
</div>
</div>
<div className={classes.actions}>
<button onClick={props.onRemove}></button>
<button onClick={props.onAdd}>+</button>
</div>
</li>
);
};
export default CartItem;

View File

@ -0,0 +1,65 @@
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid #8a2b06;
padding: 1rem 0;
margin: 1rem 0;
}
.cart-item h2 {
margin: 0 0 0.5rem 0;
color: #363636;
}
.summary {
width: 10rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-weight: bold;
color: #8a2b06;
}
.amount {
font-weight: bold;
border: 1px solid #ccc;
padding: 0.25rem 0.75rem;
border-radius: 6px;
color: #363636;
}
.actions {
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.actions {
flex-direction: row;
}
}
.cart-item button {
font: inherit;
font-weight: bold;
font-size: 1.25rem;
color: #8a2b06;
border: 1px solid #8a2b06;
width: 3rem;
text-align: center;
border-radius: 6px;
background-color: transparent;
cursor: pointer;
margin-left: 1rem;
margin: 0.25rem;
}
.cart-item button:hover,
.cart-item button:active {
background-color: #8a2b06;
color: white;
}

View File

@ -1,9 +1,22 @@
import { useContext } from "react";
import classes from "./MealItem.module.css";
import MealItemForm from "./MealItemForm";
import CartContext from "../../../store/cart-context";
const MealItem = (props) => {
const price = `$${props.price.toFixed(2)}`;
const cartCtx = useContext(CartContext);
const addToCartHandler = (amount) => {
cartCtx.addItem({
id: props.id,
name: props.name,
amount: amount,
price: props.price,
});
};
return (
<li className={classes.meal}>
<div>
@ -12,7 +25,7 @@ const MealItem = (props) => {
<div className={classes.price}>{price}</div>
</div>
<div>
<MealItemForm id={props.id} />
<MealItemForm id={props.id} onAddToCart={addToCartHandler} />
</div>
</li>
);

View File

@ -1,10 +1,33 @@
import classes from "./MealItemForm.module.css";
import Input from "../../UI/Input";
import { useRef, useState } from "react";
const MealItemForm = (props) => {
const amountInputRef = useRef();
const [amountIsValid, setAmountIsValid] = useState(true);
const submitHandler = (event) => {
event.preventDefault();
const enteredAmount = amountInputRef.current.value;
const enteredAmountNumber = +enteredAmount;
if (
enteredAmount.trim().length === 0 ||
enteredAmountNumber < 1 ||
enteredAmountNumber > 5
) {
setAmountIsValid(false);
return;
}
props.onAddToCart(enteredAmountNumber);
};
return (
<form className={classes.form}>
<form className={classes.form} onSubmit={submitHandler}>
<Input
ref={amountInputRef}
label="Amount"
input={{
id: "amount_" + props.id,
@ -16,6 +39,7 @@ const MealItemForm = (props) => {
}}
/>
<button>+ Add</button>
{!amountIsValid && <p>Please enter a valid amount (1-5).</p>}
</form>
);
};

View File

@ -1,12 +1,13 @@
import React from "react";
import classes from "./Input.module.css";
const Input = (props) => {
const Input = React.forwardRef((props, ref) => {
return (
<div className={classes.input}>
<label htmlFor={props.input.id}>{props.label}</label>
<input id={props.input.id} {...props.input} />
<input ref={ref} id={props.input.id} {...props.input} />
</div>
);
};
});
export default Input;

View File

@ -1,12 +1,86 @@
import { useReducer } from "react";
import CartContext from "./cart-context";
const defaultCartState = {
items: [],
totalAmount: 0,
};
const cartReducer = (state, action) => {
if (action.type === "ADD_ITEM") {
const updatedTotalAmount =
state.totalAmount + action.item.price * action.item.amount;
const existingCartItemIndex = state.items.findIndex(
(item) => item.id === action.item.id
);
const existingCartItem = state.items[existingCartItemIndex];
let updatedItem;
let updatedItems;
if (existingCartItem) {
updatedItem = {
...existingCartItem,
amount: existingCartItem.amount + action.item.amount,
};
updatedItems = [...state.items];
updatedItems[existingCartItemIndex] = updatedItem;
} else {
updatedItem = { ...action.item };
updatedItems = state.items.concat(action.item);
}
return { items: updatedItems, totalAmount: updatedTotalAmount };
}
if (action.type === "REMOVE_ITEM") {
const existingCartItemIndex = state.items.findIndex(
(item) => item.id === action.id
);
const existingCartItem = state.items[existingCartItemIndex];
let updatedTotalAmount = state.totalAmount;
if (existingCartItem) {
updatedTotalAmount = state.totalAmount - existingCartItem.price;
}
let updatedItems;
if (existingCartItem.amount === 1) {
// remove the item from array because this is the last item
updatedItems = state.items.filter((item) => item.id !== action.id);
} else {
const updatedItem = {
...existingCartItem,
amount: existingCartItem.amount - 1,
};
updatedItems = [...state.items];
updatedItems[existingCartItemIndex] = updatedItem;
}
return { items: updatedItems, totalAmount: updatedTotalAmount };
}
return defaultCartState;
};
const CartProvider = (props) => {
const addItemToCartHandler = (item) => {};
const removeItemFromCartHandler = (id) => {};
const [cartState, dispatchCartAction] = useReducer(
cartReducer,
defaultCartState
);
const addItemToCartHandler = (item) => {
dispatchCartAction({ type: "ADD_ITEM", item: item });
};
const removeItemFromCartHandler = (id) => {
dispatchCartAction({ type: "REMOVE_ITEM", id: id });
};
const cartContext = {
items: [],
totalAmount: 0,
items: cartState.items,
totalAmount: cartState.totalAmount,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
};