├── src ├── App.css ├── index.css ├── assets │ ├── Hoodie1.jpg │ ├── Hoodie2.jpg │ ├── Hoodie3.jpg │ ├── Sweatpant1.jpg │ ├── Sweatpant2.jpg │ └── Sweatpant3.jpg ├── main.jsx └── App.jsx ├── .env.development ├── public ├── LLC.jpg ├── Hoodie1.jpg ├── Hoodie2.jpg ├── Hoodie3.jpg ├── Sweatpant1.jpg ├── Sweatpant2.jpg └── Sweatpant3.jpg ├── postcss.config.js ├── tailwind.config.js ├── vite.config.js ├── index.html ├── .gitignore ├── components ├── Footer.jsx ├── NavBar.jsx ├── Return.jsx └── About.jsx ├── README.md ├── pages ├── ProductsPage.jsx ├── DeleteCart.jsx ├── UsersCard.jsx ├── ProductDetail.jsx ├── Checkout.jsx ├── HomePage.jsx ├── CartContext.jsx ├── ProductInfo.jsx ├── Stripe.jsx └── CartCheckOut.jsx ├── package.json └── eslint.config.js /src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VITE_API_KEY=http://localhost:3000 -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /public/LLC.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/public/LLC.jpg -------------------------------------------------------------------------------- /public/Hoodie1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/public/Hoodie1.jpg -------------------------------------------------------------------------------- /public/Hoodie2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/public/Hoodie2.jpg -------------------------------------------------------------------------------- /public/Hoodie3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/public/Hoodie3.jpg -------------------------------------------------------------------------------- /public/Sweatpant1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/public/Sweatpant1.jpg -------------------------------------------------------------------------------- /public/Sweatpant2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/public/Sweatpant2.jpg -------------------------------------------------------------------------------- /public/Sweatpant3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/public/Sweatpant3.jpg -------------------------------------------------------------------------------- /src/assets/Hoodie1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/src/assets/Hoodie1.jpg -------------------------------------------------------------------------------- /src/assets/Hoodie2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/src/assets/Hoodie2.jpg -------------------------------------------------------------------------------- /src/assets/Hoodie3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/src/assets/Hoodie3.jpg -------------------------------------------------------------------------------- /src/assets/Sweatpant1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/src/assets/Sweatpant1.jpg -------------------------------------------------------------------------------- /src/assets/Sweatpant2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/src/assets/Sweatpant2.jpg -------------------------------------------------------------------------------- /src/assets/Sweatpant3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WENDYWEN20/CapstoneFront/HEAD/src/assets/Sweatpant3.jpg -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export default { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | } 8 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./src/**/*.{html,js,jsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vite.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import {Link} from 'react-router-dom' 2 | 3 | export default function FooterPage(){ 4 | 5 | return( 6 |
7 | 11 |
12 | ) 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Capstone Frontend React.js 2 | 3 | Home Page summarized each pages 4 | Products page showed all products together, if click on each product title, a single product page will be rendered. 5 | Both Products page and each single product page have an add to cart function where product is added to cart. 6 | “/cart" will lead to cart page that user can delete a product if user don't like it, total price is calculated. 7 | 8 | -------------------------------------------------------------------------------- /pages/ProductsPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import PropTypes from "prop-types"; 3 | import ProductDetails from '../pages/ProductDetail.jsx' 4 | 5 | 6 | function ProductsPage({ prod }) { 7 | 8 | return ( 9 | 10 |
11 | {prod.map((p) => ( 12 | 13 | ))} 14 | 15 |
16 | 17 | ); 18 | } 19 | 20 | export default ProductsPage; -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import './index.css' 4 | import App from './App.jsx' 5 | import {BrowserRouter as Router} from 'react-router-dom' 6 | import { CartProvider } from '../pages/CartContext.jsx'; 7 | createRoot(document.getElementById('root')).render( 8 | 9 | 10 | 11 | 12 | 13 | 14 | , 15 | ) 16 | -------------------------------------------------------------------------------- /pages/DeleteCart.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, createContext } from "react"; 2 | import {CartContext} from "./CartContext.jsx"; 3 | 4 | export default function DeleteCart(product) { 5 | 6 | const { cart, removeFromCart } = useContext(CartContext); 7 | // Function to remove an item from the cart by ID 8 | 9 | return( 10 | 11 | )} -------------------------------------------------------------------------------- /pages/UsersCard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { CartContext } from "./CartContext.jsx"; 3 | 4 | 5 | function UserCard() { 6 | const { cart } = useContext(CartContext); 7 | console.log(cart); 8 | const totalPrice = cart.reduce((total, item) => total + item.price, 0); 9 | const handleSubmit = async (event) => { 10 | event.preventDefault(); 11 | }; 12 | 13 | return ( 14 |
15 |

Total Price {totalPrice}

16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | 23 | export default UserCard; 24 | -------------------------------------------------------------------------------- /components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | export default function NavBar() { 4 | return ( 5 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /pages/ProductDetail.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { Link } from "react-router-dom"; 3 | 4 | import { CartContext } from '../pages/CartContext.jsx'; 5 | 6 | function Detail({ product}) { 7 | const { addToCart} = useContext(CartContext); 8 | 9 | return ( 10 |
11 | 12 | {product.name} 13 |

Price: ${product.price}

14 | 15 |
16 | 17 | ); 18 | } 19 | 20 | export default Detail; 21 | -------------------------------------------------------------------------------- /pages/Checkout.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext} from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import { CartContext } from "../pages/CartContext.jsx"; 5 | 6 | function CheckOut({product}) { 7 | const { cart } = useContext(CartContext); 8 | 9 | console.log(cart); 10 | const totalPrice = cart.reduce((total, item) => total + item.price, 0); 11 | 12 | 13 | 14 | return ( 15 |
16 |
17 | {product.name} 18 |

Price: ${product.price}

19 |
20 |
21 | ); 22 | } 23 | 24 | export default CheckOut; 25 | -------------------------------------------------------------------------------- /components/Return.jsx: -------------------------------------------------------------------------------- 1 | export default function Return() { 2 | return ( 3 |
4 |

This is the Return Component

5 | 6 |

7 | You can return an item for free within 30 days after receiving your 8 | order. It's also possible to return something to one of our stores if 9 | your order didn't have a delivery fee. 10 |

11 |

12 | Eligible online orders from October 24th, 2024 until January 13th, 2025 13 | can be returned or exchanged within 60 days of receipt with our holiday 14 | extended return period." 15 |

16 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /components/About.jsx: -------------------------------------------------------------------------------- 1 | 2 | export default function About(){ 3 | return ( 4 | 5 |
6 |

This is the About Contact US Component

7 |

Everything we do is rooted in sport. Sport plays an increasingly important role in more and more people’s lives, on and off the field of play. It is central to every culture and society, and is core to our health and happiness.

8 |
9 |
10 | 11 |

ADIDAS AG World of Sports

12 |

Adi-Dassler-Straße 1

13 |

91074 Herzogenaurach

14 |

Germany

15 |
16 | ) 17 | } -------------------------------------------------------------------------------- /pages/HomePage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from "prop-types"; 3 | 4 | 5 | function ProductsPage({ prod }) { 6 | const textStyle = { 7 | fontSize: "56px", // '16px' is a string value assigned to fontSize 8 | }; 9 | return ( 10 | 11 |
12 |

E commerce website

13 |

Products Page shows all products together

14 |

Click Product name will lead to Products/:id page with Add to Cart function

15 |

Products/:id shows each single product where backend product is rendered

16 | 17 |
18 | 19 | ); 20 | } 21 | 22 | export default ProductsPage; -------------------------------------------------------------------------------- /pages/CartContext.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, createContext , useContext} from "react"; 2 | 3 | //create a Context 4 | export const CartContext=createContext(); 5 | //CartProvider component to wrap the app 6 | export const CartProvider=({children})=>{ 7 | const [cart,setCart]=useState([]); 8 | const addToCart=(product)=>{ 9 | setCart((prevCart) => [product, ...prevCart]) 10 | } 11 | const removeFromCart = (id) => { 12 | setCart((prevCart) => prevCart.filter((item) => item._id !== id)); 13 | }; 14 | 15 | //CartContext: A context to manage cart state. 16 | //CartProvider: A wrapper component that holds the cart state and provides the addToCart function to the whole app. 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "capstonefront", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@stripe/stripe-js": "^1.54.2", 14 | "axios": "^1.7.7", 15 | "react": "^18.3.1", 16 | "react-dom": "^18.3.1", 17 | "react-router-dom": "^6.27.0", 18 | "react-stripe-js": "^1.1.5" 19 | }, 20 | "devDependencies": { 21 | "@eslint/js": "^9.13.0", 22 | "@types/react": "^18.3.11", 23 | "@types/react-dom": "^18.3.1", 24 | "@vitejs/plugin-react": "^4.3.3", 25 | "autoprefixer": "^10.4.20", 26 | "eslint": "^9.13.0", 27 | "eslint-plugin-react": "^7.37.1", 28 | "eslint-plugin-react-hooks": "^5.0.0", 29 | "eslint-plugin-react-refresh": "^0.4.13", 30 | "globals": "^15.11.0", 31 | "postcss": "^8.4.47", 32 | "tailwindcss": "^3.4.14", 33 | "vite": "^5.4.9" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /pages/ProductInfo.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState, useEffect } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import {Link} from "react-router-dom" 4 | 5 | import { CartContext } from "../pages/CartContext.jsx"; 6 | function Info() { 7 | const { addToCart, cart } = useContext(CartContext); 8 | const params = useParams(); 9 | const [element, setElement] = useState([]); 10 | //fetch each single product element when components first render 11 | useEffect(() => { 12 | console.log(params); 13 | const fetchElement = async () => { 14 | const res = await fetch(`http://localhost:3000/products/${params.id}`); 15 | const elementData = await res.json(); 16 | console.log(elementData); 17 | setElement(elementData.product); 18 | console.log(elementData.product); 19 | }; 20 | fetchElement(); 21 | }, [params.id]); 22 | 23 | return ( 24 |
25 | 26 |

{element.name}

27 |

Price: ${element.price}

28 | 31 |
32 | Show All Products 33 |
34 | ); 35 | } 36 | 37 | export default Info; 38 | -------------------------------------------------------------------------------- /pages/Stripe.jsx: -------------------------------------------------------------------------------- 1 | import {CartContext} from "./CartContext.jsx"; 2 | import React, { useContext } from "react"; 3 | 4 | 5 | // const stripe = useStripe(); // Initialize Stripe 6 | // const elements = useElements(); // Initialize Stripe Elements 7 | export default function CheckoutButton() { 8 | 9 | const { cart } = useContext(CartContext); 10 | const totalPrice = cart.reduce((total, item) => total + item.price, 0); 11 | // Function to handle the checkout session 12 | //set destructure here from cart in the req.body 13 | const {name, _id,price}=cart 14 | const handleCheckout = async() => { 15 | 16 | fetch('http://localhost:3000/create-checkout-session', { 17 | method: 'POST', 18 | headers: { 19 | 'Content-Type': 'application/json', 20 | }, 21 | body: JSON.stringify({name,_id, price} 22 | // cart 23 | // items:[ 24 | // {id:1, quantity:3}, 25 | // {id:2, quantity:1} 26 | // ] 27 | ), 28 | }) 29 | .then((res) => { 30 | console.log(res) 31 | console.log(res.body) 32 | console.log(cart) 33 | if (res.ok) return res.json(); 34 | return res.json().then(json=>Promise.reject(json)) 35 | 36 | }) 37 | .then(({ url }) => { 38 | // console.log(url) 39 | window.location = url; // Redirect to the checkout URL 40 | }) 41 | .catch((e) => { 42 | console.error(e.error); 43 | }); 44 | 45 | 46 | }; 47 | 48 | return ( 49 | 50 | ); 51 | } -------------------------------------------------------------------------------- /pages/CartCheckOut.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import CheckOut from "../pages/Checkout.jsx"; 4 | import { CartContext } from "../pages/CartContext.jsx"; 5 | import StripeButton from "../pages/Stripe.jsx"; 6 | 7 | export default function ProductCart() { 8 | const { cart, removeFromCart } = useContext(CartContext); // contextProvider can render what is needed to children, either a function or object 9 | console.log(cart); 10 | const totalPrice = cart.reduce((total, item) => total + item.price, 0); 11 | 12 | return ( 13 |
14 |

15 | Welcome to our Checkout Page 16 |

17 |

18 | We store your information securely 19 |

20 |

Current Shopping Cart:

21 | 22 |
23 |
24 | {cart.map((item) => ( 25 |
26 | 27 | 33 |
34 | ))} 35 |
36 |
37 |

38 | {" "} 39 | Total Price: ${totalPrice.toFixed(2)} 40 |

41 |
42 | 43 |
44 | Show All Products 45 |
46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import ProductCart from "../pages/CartCheckOut.jsx"; 3 | import StripeButton from "../pages/Stripe.jsx" 4 | import ProductInfo from "../pages/ProductInfo.jsx"; 5 | import UsersCard from "../pages/UsersCard.jsx"; 6 | import { Routes, Route } from "react-router-dom"; 7 | import NavBar from "../components/NavBar.jsx"; 8 | import FooterPage from "../components/Footer.jsx"; 9 | import About from "../components/About.jsx"; 10 | import Return from "../components/Return.jsx"; 11 | import HomePage from "../pages/HomePage.jsx"; 12 | import ProductsPage from "../pages/ProductsPage.jsx" 13 | 14 | 15 | console.log(import.meta.env.VITE_API_BASE_URL); 16 | 17 | function App() { 18 | const [productAPI, setproduct] = useState([]); 19 | //fetch all products added to cart when plan to check out 20 | useEffect(() => { 21 | const fetchProducts = async () => { 22 | const res = await fetch(`http://localhost:3000/products`); 23 | const productsData = await res.json(); 24 | console.log(productsData); 25 | setproduct(productsData.products); 26 | console.log(productAPI); 27 | }; 28 | fetchProducts(); 29 | }, []); 30 | 31 | return ( 32 |
33 | 34 |
35 | {/*
36 | {productAPI.map((product) => ( 37 | 38 | ))} 39 |
*/} 40 | 41 | 42 | } /> 43 | } /> 44 | } /> 45 | } /> 46 | } /> 47 | } /> 48 | } /> 49 | } /> 50 | 51 |
52 | 53 | 54 |
55 | ); 56 | } 57 | 58 | export default App; 59 | --------------------------------------------------------------------------------