mirror of
https://gitea.smigz.com/smiggiddy/odin-codeprojects.git
synced 2024-12-26 14:20:43 -05:00
basic features
This commit is contained in:
parent
da6d76e598
commit
6babd2bee0
8 changed files with 146 additions and 51 deletions
|
@ -1,51 +1,106 @@
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import styles from "./css/cart.module.css";
|
import styles from "./css/cart.module.css";
|
||||||
import { useOutletContext } from "react-router-dom";
|
import { useOutletContext } from "react-router-dom";
|
||||||
|
import { currencyFormat } from "./utils/currency";
|
||||||
|
|
||||||
const Cart = () => {
|
const Cart = () => {
|
||||||
const [cart, setCart, items, SetItems] = useOutletContext();
|
const [cart, setCart] = useOutletContext();
|
||||||
|
|
||||||
const cartItems = Object.keys(cart);
|
const cartKeys = Object.keys(cart);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<div>
|
<h2>Cart</h2>
|
||||||
{cart ? (
|
<Bag cartKeys={cartKeys} cart={cart} setCart={setCart} />
|
||||||
<div>
|
</div>
|
||||||
<h2>Cart</h2>
|
|
||||||
{cartItems.map((item, index) => (
|
|
||||||
<CartItem item={cart[item]} key={index} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<h2>Your cart is empty</h2>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function CartItem({ item }) {
|
function Bag({ cartKeys, cart, setCart }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{cartKeys.length > 0 ? (
|
||||||
|
<div>
|
||||||
|
{cartKeys.map((key, index) => (
|
||||||
|
<CartItem
|
||||||
|
item={cart[key]}
|
||||||
|
cart={cart}
|
||||||
|
setCart={setCart}
|
||||||
|
key={index}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<h2>Your cart is empty</h2>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CartItem({ item, cart, setCart }) {
|
||||||
const { title, price, image, qty } = item;
|
const { title, price, image, qty } = item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<img src={image} alt={title} />
|
<img src={image} alt={title} />
|
||||||
<h3>{title}</h3>
|
<div>
|
||||||
<p>Qty: {qty}</p>
|
<h3>{title}</h3>
|
||||||
<p>Price: {price}</p>
|
<p>Price: ${currencyFormat(price)}</p>
|
||||||
<p>Total: {price * qty}</p>
|
<p>Qty: {qty}</p>
|
||||||
<p>{item.id}</p>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={() => decreaseQty(item, cart, setCart)}>
|
||||||
|
Decrease
|
||||||
|
</button>
|
||||||
|
<p>{qty}</p>
|
||||||
|
<button onClick={() => increaseQty(item, cart, setCart)}>
|
||||||
|
Increase
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>Total: {currencyFormat(qty * price)}</p>
|
||||||
|
<button onClick={() => removeFromCart(item, cart, setCart)}>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function increaseQty(item, cart, setCart) {
|
||||||
|
if (cart[item.id]) {
|
||||||
|
let obj = { ...cart };
|
||||||
|
obj[item.id].qty += 1;
|
||||||
|
setCart(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decreaseQty(item, cart, setCart) {
|
||||||
|
if (cart[item.id]) {
|
||||||
|
let obj = { ...cart };
|
||||||
|
if (obj[item.id].qty > 1) obj[item.id].qty -= 1;
|
||||||
|
setCart(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFromCart(item, cart, setCart) {
|
||||||
|
if (cart[item.id]) {
|
||||||
|
let obj = { ...cart };
|
||||||
|
delete obj[item.id];
|
||||||
|
setCart(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CartItem.propTypes = {
|
CartItem.propTypes = {
|
||||||
item: PropTypes.object,
|
item: PropTypes.object,
|
||||||
|
cart: PropTypes.object,
|
||||||
|
setCart: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
Cart.propTypes = {
|
Bag.propTypes = {
|
||||||
cart: PropTypes.array,
|
cartKeys: PropTypes.array,
|
||||||
|
cart: PropTypes.object,
|
||||||
|
setCart: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Cart;
|
export default Cart;
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default function Store() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Smig.Tech Coaching Store</h1>
|
<h1>Smig.Tech Store</h1>
|
||||||
<ProductCollection
|
<ProductCollection
|
||||||
loading={loading}
|
loading={loading}
|
||||||
items={items}
|
items={items}
|
||||||
|
@ -28,7 +28,9 @@ function useFakeStoreAPIData(items, setItems, loading, setLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch("https://fakestoreapi.com/products?limit=5", { mode: "cors" })
|
fetch("https://fakestoreapi.com/products/category/electronics", {
|
||||||
|
mode: "cors",
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status >= 400) {
|
if (response.status >= 400) {
|
||||||
throw new Error("unable to fetch items");
|
throw new Error("unable to fetch items");
|
||||||
|
@ -40,7 +42,7 @@ function useFakeStoreAPIData(items, setItems, loading, setLoading) {
|
||||||
response.forEach((item) => {
|
response.forEach((item) => {
|
||||||
arr.push({
|
arr.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
price: item.price,
|
price: item.price * 100,
|
||||||
image: item.image,
|
image: item.image,
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
.card {
|
.card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: calc(100% / 4);
|
max-width: calc(100% / 5);
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
/*flex: 1;*/
|
/*flex: 1;*/
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
|
|
|
@ -1,47 +1,62 @@
|
||||||
import { useParams, useOutletContext } from "react-router-dom";
|
import { useParams, useOutletContext, Link } from "react-router-dom";
|
||||||
import Products from "./products";
|
import Products from "./products";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import styles from "./productCollection.module.css";
|
import styles from "./productDetails.module.css";
|
||||||
|
|
||||||
export default function ProductDetails() {
|
export default function ProductDetails() {
|
||||||
const [cart, setCart, items, setItems] = useOutletContext();
|
const [cart, setCart, items] = useOutletContext();
|
||||||
|
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const item = items.filter((item) => item.id === id)[0];
|
|
||||||
|
if (!items) return <HandleInvalidItem />;
|
||||||
|
const item = items.find((item) => item.id === id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{item ? (
|
{item ? (
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
|
<Link to="/store">Back</Link>
|
||||||
<Products item={item} cart={cart} setCart={setCart} />
|
<Products item={item} cart={cart} setCart={setCart} />
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
addToCart(item, cart, setCart);
|
cart[item.id]
|
||||||
|
? removeFromCart(item, cart, setCart)
|
||||||
|
: addToCart(item, cart, setCart);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add to Cart
|
{cart[item.id] ? "Remove from Cart" : "Add to Cart"}
|
||||||
</button>
|
</button>
|
||||||
|
<Link to="/bag">View Cart</Link>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : (
|
||||||
|
<HandleInvalidItem />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function HandleInvalidItem() {
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.card}>
|
||||||
|
<h1>Product Does Not Exist!</h1>
|
||||||
|
<Link to="/store">Return to Store</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToCart(item, cart, setCart) {
|
function addToCart(item, cart, setCart) {
|
||||||
let obj = { ...cart };
|
let obj = { ...cart };
|
||||||
if (obj[item.id]) {
|
obj[item.id] = item;
|
||||||
obj[item.id].qty += 1;
|
obj[item.id].qty = 1;
|
||||||
} else {
|
|
||||||
obj[item.id] = item;
|
|
||||||
obj[item.id].qty = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCart(obj);
|
setCart(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProductDetails.propTypes = {
|
function removeFromCart(item, cart, setCart) {
|
||||||
item: PropTypes.object,
|
if (cart[item.id]) {
|
||||||
cart: PropTypes.object,
|
let obj = { ...cart };
|
||||||
setCart: PropTypes.func,
|
delete obj[item.id];
|
||||||
};
|
setCart(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
16
shopping-cart/src/components/productDetails.module.css
Normal file
16
shopping-cart/src/components/productDetails.module.css
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: calc(100% / 5);
|
||||||
|
justify-content: end;
|
||||||
|
/*flex: 1;*/
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1.5rem;
|
||||||
|
}
|
|
@ -2,13 +2,15 @@ import PropTypes from "prop-types";
|
||||||
|
|
||||||
import styles from "./products.module.css";
|
import styles from "./products.module.css";
|
||||||
|
|
||||||
|
import { currencyFormat } from "../utils/currency";
|
||||||
|
|
||||||
export default function Products({ item, cart, setCart }) {
|
export default function Products({ item, cart, setCart }) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.card}>
|
<>
|
||||||
<img src={item.image} alt={item.title} className={styles.img} />
|
<img src={item.image} alt={item.title} className={styles.img} />
|
||||||
<p>{item.title}</p>
|
<p>{item.title}</p>
|
||||||
<p>${item.price}</p>
|
<p>${currencyFormat(item.price)}</p>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
object-fit: cover;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
3
shopping-cart/src/utils/currency.js
Normal file
3
shopping-cart/src/utils/currency.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export function currencyFormat(str) {
|
||||||
|
return (str / 100).toFixed(2);
|
||||||
|
}
|
Loading…
Reference in a new issue