mirror of
https://gitea.smigz.com/smiggiddy/odin-codeprojects.git
synced 2024-12-26 22:30:44 -05:00
added cart and store components
This commit is contained in:
parent
556a7f8fe2
commit
81d70ec032
13 changed files with 254 additions and 54 deletions
48
shopping-cart/package-lock.json
generated
48
shopping-cart/package-lock.json
generated
|
@ -8,8 +8,10 @@
|
||||||
"name": "shopping-cart",
|
"name": "shopping-cart",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1",
|
||||||
|
"react-router-dom": "^6.27.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.11.1",
|
"@eslint/js": "^9.11.1",
|
||||||
|
@ -980,6 +982,15 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@remix-run/router": {
|
||||||
|
"version": "1.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz",
|
||||||
|
"integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.24.0",
|
"version": "4.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
|
||||||
|
@ -3328,7 +3339,6 @@
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
@ -3578,7 +3588,6 @@
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
|
@ -3625,7 +3634,6 @@
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
|
@ -3638,6 +3646,38 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-router": {
|
||||||
|
"version": "6.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz",
|
||||||
|
"integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.20.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-router-dom": {
|
||||||
|
"version": "6.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz",
|
||||||
|
"integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@remix-run/router": "1.20.0",
|
||||||
|
"react-router": "6.27.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reflect.getprototypeof": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
|
||||||
|
|
|
@ -10,8 +10,10 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1",
|
||||||
|
"react-router-dom": "^6.27.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.11.1",
|
"@eslint/js": "^9.11.1",
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import "./App.css";
|
import { Outlet, useParams } from "react-router-dom";
|
||||||
import Main from "./components/main";
|
import Main from "./components/main";
|
||||||
|
import "./App.css";
|
||||||
import Navbar from "./components/navbar";
|
import Navbar from "./components/navbar";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [count, setCount] = useState(0);
|
const { path } = useParams();
|
||||||
|
const [cartItems, setCartItems] = useState(0);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
<Navbar cartItems={cartItems} />
|
||||||
<Main />
|
{path === "cart" ? <Outlet /> : path === "store" ? <Outlet /> : <Main />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
const Cart = (props) => {
|
const Cart = (props) => {
|
||||||
return (
|
return (
|
||||||
<section>
|
<>
|
||||||
|
<div>
|
||||||
{props.cart ? (
|
{props.cart ? (
|
||||||
<div>
|
<div>
|
||||||
<h2>Cart</h2>
|
<h2>Cart</h2>
|
||||||
{props.cart.map((item) =>
|
{props.cart.map((item) => (
|
||||||
<CartItem item={item} qty={item.qty} />
|
<CartItem item={item} key={item.id} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<h2>Your cart is empty</h2>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (<h2>Your cart is empty</h2>)}
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
</section>
|
function CartItem({ item }) {
|
||||||
)
|
const { name, price, img, qty } = item;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function CartItem({item, qty}) {
|
|
||||||
const {name, price, img} = item;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -26,5 +30,15 @@ function CartItem({item, qty}) {
|
||||||
<p>{price}</p>
|
<p>{price}</p>
|
||||||
<p>{price * qty}</p>
|
<p>{price * qty}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CartItem.propTypes = {
|
||||||
|
item: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
Cart.propTypes = {
|
||||||
|
cart: PropTypes.array,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Cart;
|
||||||
|
|
38
shopping-cart/src/Store.jsx
Normal file
38
shopping-cart/src/Store.jsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import ProductCollection from "./components/productCollection";
|
||||||
|
|
||||||
|
export default function Store(props) {
|
||||||
|
const { items, loading } = useFakeStoreAPI();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Smig.Tech Coaching Store</h1>
|
||||||
|
<ProductCollection loading={loading} items={items} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useFakeStoreAPI() {
|
||||||
|
const [items, setItems] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch("https://fakestoreapi.com/products?limit=5", { mode: "cors" })
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status >= 400) {
|
||||||
|
return { error: "unable to fetch items" };
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const arr = [];
|
||||||
|
response.forEach((item) => {
|
||||||
|
arr.push({ title: item.title, price: item.price, image: item.image });
|
||||||
|
});
|
||||||
|
setItems(arr);
|
||||||
|
})
|
||||||
|
.catch((error) => console.log(error))
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { items, loading };
|
||||||
|
}
|
|
@ -3,14 +3,20 @@ import styles from "./main.module.css";
|
||||||
export default function Main(props) {
|
export default function Main(props) {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<div>
|
<Default />
|
||||||
<h1 className={styles.mainHeading}>We help you skill up faster</h1>
|
|
||||||
<p>
|
|
||||||
Trying to pivot into tech? There's a lot to figure out. We can
|
|
||||||
help you navigate the path. Fast results and guaranteed growth.
|
|
||||||
</p>
|
|
||||||
<button>BOOK INTRO CALL</button>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Default() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className={styles.mainHeading}>We help you skill up faster</h1>
|
||||||
|
<p>
|
||||||
|
Trying to pivot into tech? There's a lot to figure out. We can help
|
||||||
|
you navigate the path. Fast results and guaranteed growth.
|
||||||
|
</p>
|
||||||
|
<button>BOOK INTRO CALL</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import styles from "./navbar.module.css";
|
import styles from "./navbar.module.css";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
export default function Navbar(props) {
|
export default function Navbar({ cartItems }) {
|
||||||
return (
|
return (
|
||||||
<nav className={styles.nav}>
|
<nav className={styles.nav}>
|
||||||
<h1>Smig.Tech</h1>
|
<h1>Smig.Tech</h1>
|
||||||
<Nav />
|
<Nav />
|
||||||
<button>I'm Ready</button>
|
{cartItems ? <h1>{cartItems}</h1> : <button>I'm Ready</button>}
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,8 +15,16 @@ export default function Navbar(props) {
|
||||||
function Nav() {
|
function Nav() {
|
||||||
return (
|
return (
|
||||||
<ul className={styles.nav}>
|
<ul className={styles.nav}>
|
||||||
<li>home</li>
|
<li>
|
||||||
<li>shop</li>
|
<Link to="/">home</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link to="store">shop</Link>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Navbar.propTypes = {
|
||||||
|
cartItems: PropTypes.number,
|
||||||
|
};
|
||||||
|
|
19
shopping-cart/src/components/productCollection.jsx
Normal file
19
shopping-cart/src/components/productCollection.jsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import Product from "./products";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
export default function ProductCollection({ loading, items }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{!loading
|
||||||
|
? items.map((item, index) => {
|
||||||
|
return <Product item={item} key={index} />;
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductCollection.propTypes = {
|
||||||
|
loading: PropTypes.bool,
|
||||||
|
items: PropTypes.array,
|
||||||
|
};
|
17
shopping-cart/src/components/products.jsx
Normal file
17
shopping-cart/src/components/products.jsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
import styles from "./products.module.css";
|
||||||
|
|
||||||
|
export default function Product({ item }) {
|
||||||
|
return (
|
||||||
|
<div className={styles.card}>
|
||||||
|
<img src={item.image} alt={item.title} className={styles.img} />
|
||||||
|
<p>{item.title}</p>
|
||||||
|
<p>${item.price}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Product.propTypes = {
|
||||||
|
item: PropTypes.object,
|
||||||
|
};
|
11
shopping-cart/src/components/products.module.css
Normal file
11
shopping-cart/src/components/products.module.css
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 350px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
14
shopping-cart/src/errorPage.jsx
Normal file
14
shopping-cart/src/errorPage.jsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
function ErrorPage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Oh no, this route doesn't exist!</h1>
|
||||||
|
<Link to="/">
|
||||||
|
You can go back to the home page by clicking here, though!
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorPage;
|
|
@ -1,10 +1,13 @@
|
||||||
import { StrictMode } from 'react'
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from "react-dom/client";
|
||||||
import App from './App.jsx'
|
import "./index.css";
|
||||||
import './index.css'
|
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||||
|
import routes from "./routes";
|
||||||
|
|
||||||
createRoot(document.getElementById('root')).render(
|
const router = createBrowserRouter(routes);
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<RouterProvider router={router} />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
);
|
||||||
|
|
25
shopping-cart/src/routes.jsx
Normal file
25
shopping-cart/src/routes.jsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import App from "./App";
|
||||||
|
import Cart from "./Cart";
|
||||||
|
import Store from "./Store";
|
||||||
|
import ErrorPage from "./errorPage";
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <App />,
|
||||||
|
errorElement: <ErrorPage />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: ":path",
|
||||||
|
element: <Store />,
|
||||||
|
index: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "store/:path",
|
||||||
|
element: <Cart />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
Loading…
Reference in a new issue