mirror of
https://gitea.smigz.com/smiggiddy/odin-codeprojects.git
synced 2024-12-25 22:10:43 -05:00
changes
This commit is contained in:
parent
0664755110
commit
aca34ffb10
18 changed files with 818 additions and 94 deletions
|
@ -10,7 +10,7 @@
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Smig.Tech Coaching</title>
|
<title>Hobby Tech</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
669
shopping-cart/package-lock.json
generated
669
shopping-cart/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,6 +10,10 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.13.5",
|
||||||
|
"@emotion/styled": "^11.13.5",
|
||||||
|
"@mui/icons-material": "^6.1.8",
|
||||||
|
"@mui/material": "^6.1.8",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
|
BIN
shopping-cart/public/hero.jpg
Normal file
BIN
shopping-cart/public/hero.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 481 KiB |
|
@ -1,3 +1,4 @@
|
||||||
|
import Button from "./components/button";
|
||||||
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";
|
||||||
|
@ -23,7 +24,6 @@ function Bag({ cartKeys, cart, setCart }) {
|
||||||
<>
|
<>
|
||||||
{cartKeys.length > 0 ? (
|
{cartKeys.length > 0 ? (
|
||||||
<div className={styles.fullwidth}>
|
<div className={styles.fullwidth}>
|
||||||
<h2 className={styles.header}>Cart</h2>
|
|
||||||
<div className={styles.cartItems}>
|
<div className={styles.cartItems}>
|
||||||
{cartKeys.map((key, index) => (
|
{cartKeys.map((key, index) => (
|
||||||
<CartItem
|
<CartItem
|
||||||
|
@ -36,7 +36,9 @@ function Bag({ cartKeys, cart, setCart }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
<div className={styles["empty-cart"]}>
|
||||||
<h2>Your cart is empty</h2>
|
<h2>Your cart is empty</h2>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -44,15 +46,17 @@ function Bag({ cartKeys, cart, setCart }) {
|
||||||
|
|
||||||
function OrderSummary({ cart }) {
|
function OrderSummary({ cart }) {
|
||||||
const cartItems = Object.keys(cart);
|
const cartItems = Object.keys(cart);
|
||||||
|
const shippingFee = 0.1;
|
||||||
let numItems = 0;
|
let numItems = 0;
|
||||||
let subTotal = 0;
|
let subTotal = 0;
|
||||||
|
|
||||||
cartItems.forEach((item) => {
|
cartItems.forEach((item) => {
|
||||||
subTotal += cart[item].price * cart[item].qty;
|
subTotal += Number(cart[item].price) * Number(cart[item].qty);
|
||||||
numItems += cart[item].qty;
|
numItems += Number(cart[item].qty);
|
||||||
});
|
});
|
||||||
const shippingFee = subTotal * 0.1;
|
|
||||||
const total = subTotal + shippingFee;
|
const shippingCosts = shippingFee * subTotal;
|
||||||
|
const total = shippingCosts + subTotal;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.summary}>
|
<div className={styles.summary}>
|
||||||
|
@ -60,9 +64,9 @@ function OrderSummary({ cart }) {
|
||||||
<p>
|
<p>
|
||||||
Subtotal ({numItems} items): ${currencyFormat(subTotal)}
|
Subtotal ({numItems} items): ${currencyFormat(subTotal)}
|
||||||
</p>
|
</p>
|
||||||
<p>Shipping (10%): ${currencyFormat(shippingFee)}</p>
|
<p>Shipping (10%): ${currencyFormat(shippingCosts)}</p>
|
||||||
<p>Total: ${currencyFormat(total)}</p>
|
<p>Total: ${currencyFormat(total)}</p>
|
||||||
<button>Checkout</button>
|
<Button text={"Checkout"} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -85,9 +89,10 @@ function CartItem({ item, cart, setCart }) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>total: ${currencyFormat(qty * price)} </p>
|
<p>total: ${currencyFormat(qty * price)} </p>
|
||||||
<button onClick={() => removeFromCart(item, cart, setCart)}>
|
<Button
|
||||||
Remove From Cart
|
onClick={() => removeFromCart(item, cart, setCart)}
|
||||||
</button>
|
text={"Remove From Cart"}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -96,13 +101,19 @@ function CartItem({ item, cart, setCart }) {
|
||||||
|
|
||||||
function QuantityInput({ item, cart, setCart }) {
|
function QuantityInput({ item, cart, setCart }) {
|
||||||
function handleChange(e) {
|
function handleChange(e) {
|
||||||
const newQty = e.target.value;
|
const newQty = Number(e.target.value);
|
||||||
let obj = { ...cart };
|
let obj = { ...cart };
|
||||||
|
if (newQty !== 0) {
|
||||||
obj[item.id].qty = newQty;
|
obj[item.id].qty = newQty;
|
||||||
|
} else {
|
||||||
|
obj[item.id].qty = 1;
|
||||||
|
}
|
||||||
setCart(obj);
|
setCart(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <input value={item.qty} onChange={handleChange} />;
|
return (
|
||||||
|
<input value={item.qty} onChange={handleChange} min="1" type="number" />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function increaseQty(item, cart, setCart) {
|
function increaseQty(item, cart, setCart) {
|
||||||
|
|
|
@ -10,7 +10,6 @@ export default function Store() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Smig.Tech Store</h1>
|
|
||||||
<ProductCollection
|
<ProductCollection
|
||||||
loading={loading}
|
loading={loading}
|
||||||
items={items}
|
items={items}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
.btn {
|
.btn {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action {
|
.action {
|
||||||
|
|
|
@ -12,18 +12,22 @@ export default function Main() {
|
||||||
|
|
||||||
function Default() {
|
function Default() {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<h1 className={styles.mainHeading}>
|
<h1 className={styles.mainHeading}>
|
||||||
Our products help you skill up faster
|
Our products help <span className={styles["heading-color"]}>you</span>{" "}
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p className={styles["sub-heading"]}>
|
||||||
Trying to pivot into tech? There's a lot to figure out. We can help
|
Keep engineers happy using working technology. Fast results and
|
||||||
you navigate the path with our products. Fast results and guaranteed
|
guaranteed value from day one.
|
||||||
growth.
|
|
||||||
</p>
|
</p>
|
||||||
<Link to="/store">
|
<Link to="/store">
|
||||||
<Button text={"Start shopping now!"} styles={"action"} />
|
<Button text={"Start shopping now!"} />
|
||||||
</Link>
|
</Link>
|
||||||
|
<div>
|
||||||
|
<img src="/hero.jpg" alt="" className={styles["hero-img"]} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,29 @@ main {
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: start;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainHeading {
|
||||||
|
font-size: 4rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-heading {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading-color {
|
||||||
|
color: #0e6c96;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-img {
|
||||||
|
margin-top: 100px;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 800px;
|
||||||
|
vertical-align: middle;
|
||||||
|
object-fit: contain;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,42 @@
|
||||||
import styles from "./navbar.module.css";
|
import styles from "./navbar.module.css";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
|
||||||
|
|
||||||
export default function Navbar({ cartItems }) {
|
export default function Navbar({ cartItems }) {
|
||||||
const sumCartItems = Object.keys(cartItems).length;
|
const sumCartItems = Object.keys(cartItems).length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={styles.container}>
|
<nav className={styles.container}>
|
||||||
<h1>Smig.Tech</h1>
|
<h1 className={styles["nav-heading"]}>Hobby Tech</h1>
|
||||||
<Nav />
|
<Nav sumCartItems={sumCartItems} />
|
||||||
{sumCartItems > 0 ? (
|
|
||||||
<Link to="bag">
|
|
||||||
{" "}
|
|
||||||
<h1>{sumCartItems}</h1>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<img src="/shopping_cart_icon.svg" />
|
|
||||||
)}
|
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Nav() {
|
function Nav({ sumCartItems }) {
|
||||||
return (
|
return (
|
||||||
<ul className={styles.nav}>
|
<ul className={styles.nav}>
|
||||||
<li>
|
<li>
|
||||||
<Link to="/">home</Link>
|
<Link to="/" className={styles.link}>
|
||||||
|
home
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link to="store">shop</Link>
|
<Link to="store" className={styles.link}>
|
||||||
|
shop
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
<Link to="bag" className={styles.link}>
|
||||||
|
{sumCartItems > 0 ? (
|
||||||
|
<div className={styles["cart-icon-container"]}>
|
||||||
|
<div className={styles["cart-icon-circle"]}></div>
|
||||||
|
<ShoppingCartIcon fontSize="large" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<ShoppingCartIcon fontSize="large" />
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -37,3 +44,7 @@ function Nav() {
|
||||||
Navbar.propTypes = {
|
Navbar.propTypes = {
|
||||||
cartItems: PropTypes.object,
|
cartItems: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Nav.propTypes = {
|
||||||
|
sumCartItems: PropTypes.number,
|
||||||
|
};
|
||||||
|
|
|
@ -15,3 +15,28 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #0e374e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-heading {
|
||||||
|
color: #0e374e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-icon-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-icon-circle {
|
||||||
|
background: #85d5f4;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
position: absolute;
|
||||||
|
top: 7px;
|
||||||
|
left: 10px;
|
||||||
|
opacity: 60%;
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,10 @@ export default function ProductCollection({ loading, items, cart, setCart }) {
|
||||||
return (
|
return (
|
||||||
<div key={index} className={styles.card}>
|
<div key={index} className={styles.card}>
|
||||||
<Products item={item} cart={cart} setCart={setCart} />
|
<Products item={item} cart={cart} setCart={setCart} />
|
||||||
<Link to={item.id}> More Info</Link>
|
<Link to={item.id} className={styles.link}>
|
||||||
|
{" "}
|
||||||
|
More Info
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,5 +12,18 @@
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
margin: 1.5rem;
|
margin: 1.5rem;
|
||||||
background: gray;
|
background: #e2f3fc;
|
||||||
|
border-radius: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #0e374e;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: font-weight 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover {
|
||||||
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { useParams, useOutletContext, Link } from "react-router-dom";
|
import { useParams, useOutletContext, Link } from "react-router-dom";
|
||||||
|
import Button from "./button";
|
||||||
import Products from "./products";
|
import Products from "./products";
|
||||||
|
|
||||||
import styles from "./productDetails.module.css";
|
import styles from "./productDetails.module.css";
|
||||||
|
@ -15,18 +16,23 @@ export default function ProductDetails() {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{item ? (
|
{item ? (
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<Link to="/store">Back</Link>
|
<Link to="/store" className={styles.link}>
|
||||||
|
Back
|
||||||
|
</Link>
|
||||||
<Products item={item} cart={cart} setCart={setCart} />
|
<Products item={item} cart={cart} setCart={setCart} />
|
||||||
<button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
cart[item.id]
|
cart[item.id]
|
||||||
? removeFromCart(item, cart, setCart)
|
? removeFromCart(item, cart, setCart)
|
||||||
: addToCart(item, cart, setCart);
|
: addToCart(item, cart, setCart);
|
||||||
}}
|
}}
|
||||||
>
|
text={cart[item.id] ? "Remove from Cart" : "Add to Cart"}
|
||||||
{cart[item.id] ? "Remove from Cart" : "Add to Cart"}
|
/>
|
||||||
</button>
|
{Object.keys(cart).length > 0 ? (
|
||||||
<Link to="/bag">View Cart</Link>
|
<Link to="/bag" className={styles.link}>
|
||||||
|
View Cart
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<HandleInvalidItem />
|
<HandleInvalidItem />
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.card {
|
.card {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -13,4 +14,16 @@
|
||||||
/*flex: 1;*/
|
/*flex: 1;*/
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
margin: 1.5rem;
|
margin: 1.5rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #0e374e;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: font-weight 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover {
|
||||||
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import styles from "./products.module.css";
|
||||||
|
|
||||||
import { currencyFormat } from "../utils/currency";
|
import { currencyFormat } from "../utils/currency";
|
||||||
|
|
||||||
export default function Products({ item, cart, setCart }) {
|
export default function Products({ item }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<img src={item.image} alt={item.title} className={styles.img} />
|
<img src={item.image} alt={item.title} className={styles.img} />
|
||||||
|
@ -16,6 +16,4 @@ export default function Products({ item, cart, setCart }) {
|
||||||
|
|
||||||
Products.propTypes = {
|
Products.propTypes = {
|
||||||
item: PropTypes.object,
|
item: PropTypes.object,
|
||||||
cart: PropTypes.object,
|
|
||||||
setCart: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,14 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-cart {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.summary {
|
.summary {
|
||||||
margin-right: 3rem;
|
margin-right: 3rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
font-family: Inter, system-ui, sans-serif;
|
font-family: Inter, system-ui, sans-serif;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
/*color-scheme: light dark;*/
|
/*color-scheme: light dark;*/
|
||||||
/*color: rgba(255, 255, 255, 0.87);*/
|
/*color: rgba(255, 255, 255, 0.87);*/
|
||||||
|
@ -18,6 +19,18 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Chrome, Safari, Edge, Opera */
|
||||||
|
input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
input[type="number"] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
.roboto-condensed-fnt {
|
.roboto-condensed-fnt {
|
||||||
font-family: "Roboto Condensed", sans-serif;
|
font-family: "Roboto Condensed", sans-serif;
|
||||||
font-optical-sizing: auto;
|
font-optical-sizing: auto;
|
||||||
|
|
Loading…
Reference in a new issue