├── 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 |
8 | About Us
9 | Return Policy
10 |
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 | removeFromCart(item._id)}>Delete
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 |
6 | Capstone React Node MongoDB MERN
7 |
8 | Home
9 | Products
10 | Carts
11 | Check Out
12 | Users
13 |
14 |
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 |
addToCart(product)}> Add to Cart
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 |
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 |
addToCart(element)}>
29 | Add to Cart
30 |
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 | Stripe Checkout Link ${totalPrice}
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 | removeFromCart(item._id)}
30 | >
31 | Delete
32 |
33 |
34 | ))}
35 |
36 |
37 |
38 | {" "}
39 | Total Price: ${totalPrice.toFixed(2)}
40 |
41 |
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 |
--------------------------------------------------------------------------------