├── README.md ├── package-lock.json ├── package.json ├── public ├── 1.png ├── 2.jpg ├── index.html ├── manifest.json └── robots.txt └── src ├── 2.png ├── App.css ├── App.js ├── App.scss ├── App.test.js ├── index.css ├── index.js ├── logo.svg ├── pages ├── Auth │ ├── Login.js │ └── Register.js ├── Category.js ├── Checkout.js ├── Home.js ├── ManageProducts.js ├── Navbar.js ├── Product.js ├── SingleProduct.js ├── Stocks.js └── cmjd-102.code-workspace ├── reportWebVitals.js ├── setupTests.js └── utils └── ProtectedRoutes.js /README.md: -------------------------------------------------------------------------------- 1 | # MyStore React Application 2 | 3 | A web application for managing products, categories, stocks, and checkout functionality. 4 | 5 | ## Description 6 | 7 | This project is a React-based web application designed to manage various functionalities related to an online store. It allows users to view and manage products, categories, stocks, and provides a checkout feature. 8 | BackEnd(Springboot) - https://github.com/Pawan-ML/MyStore-Springboot/ 9 | 10 | ## Technologies Used 11 | 12 | - React.js 13 | - JavaScript 14 | - HTML 15 | - CSS 16 | - Bootstrap 17 | - React Router DOM 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.17.0", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^1.6.2", 10 | "bootstrap": "^5.3.2", 11 | "cors": "^2.8.5", 12 | "react": "^18.2.0", 13 | "react-bootstrap": "^2.9.2", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.18.0", 16 | "react-scripts": "5.0.1", 17 | "sass": "^1.69.5", 18 | "web-vitals": "^2.1.4" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pawan-ML/MyStore-React/56a1948ad0c8e422ce72b4881a1ef57a07dc8a41/public/1.png -------------------------------------------------------------------------------- /public/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pawan-ML/MyStore-React/56a1948ad0c8e422ce72b4881a1ef57a07dc8a41/public/2.jpg -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | MyStore 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pawan-ML/MyStore-React/56a1948ad0c8e422ce72b4881a1ef57a07dc8a41/src/2.png -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import logo from './logo.svg'; 2 | import './App.scss'; 3 | import { useState } from 'react'; 4 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 5 | import Home from './pages/Home'; 6 | import Product from './pages/Product'; 7 | import SingleProduct from './pages/SingleProduct'; 8 | import Category from './pages/Category'; 9 | import Checkout from './pages/Checkout'; 10 | import Register from './pages/Auth/Register'; 11 | import Login from './pages/Auth/Login'; 12 | import Stocks from './pages/Stocks'; 13 | import ProtectedRoutes from './utils/ProtectedRoutes'; 14 | import ManageProducts from './pages/ManageProducts'; 15 | 16 | 17 | const App = () => { 18 | return ( 19 | 20 | 21 | }> 22 | } /> 23 | } /> 24 | } /> 25 | {/* } /> */} 26 | } /> 27 | } /> 28 | } /> 29 | } /> 30 | 31 | 32 | } /> 33 | } /> 34 | 35 | 36 | ); 37 | }; 38 | 39 | export default App; 40 | -------------------------------------------------------------------------------- /src/App.scss: -------------------------------------------------------------------------------- 1 | 2 | $primary: #f8bf02; 3 | 4 | $border-radius: 25px; 5 | 6 | .login-box { 7 | max-width: 600px; 8 | margin: 0 auto; 9 | } 10 | 11 | /* Importing Bootstrap Customizable SCSS */ 12 | @import '~bootstrap/scss/bootstrap'; -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/Auth/Login.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import { Link } from "react-router-dom"; 5 | 6 | const Login = () => { 7 | const [username, setUsername] = useState(""); 8 | const [password, setPassword] = useState(""); 9 | const navigate = useNavigate(); 10 | 11 | const handleUsername = (event) => { 12 | setUsername(event.target.value); 13 | } 14 | 15 | const handlePassword = (event) => { 16 | setPassword(event.target.value); 17 | } 18 | 19 | const handleLogin = async (event) => { 20 | event.preventDefault(); 21 | 22 | const data = { 23 | "username": username, 24 | "password": password, 25 | } 26 | 27 | try { 28 | console.log("Login data:", data); // Check if data is correct 29 | 30 | const response = await axios.post("http://localhost:8081/auth/login", data); 31 | 32 | console.log("Response:", response); // Log the response 33 | 34 | if (response.status === 200) { 35 | // Store the token in frontend 36 | localStorage.setItem("token", response.data); 37 | 38 | // Use this as the default token for axios 39 | axios.defaults.headers.common['Authorization'] = `Bearer ${response.data}`; 40 | 41 | navigate("/"); 42 | } else { 43 | console.log("Login error"); 44 | } 45 | } catch (error) { 46 | console.error("Login error:", error); 47 | } 48 | } 49 | 50 | return ( 51 |
52 |
53 |
54 |

User Login

55 |
56 |
57 |
58 | 59 |
60 |
61 | 62 |
63 | 64 |
65 |
66 |

Don't have an account? Register

67 |
68 |
69 |
70 | ) 71 | } 72 | 73 | export default Login; 74 | -------------------------------------------------------------------------------- /src/pages/Auth/Register.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import { Link } from "react-router-dom"; 5 | 6 | const Register = () => { 7 | const [username, setUsername] = useState(""); 8 | const [password, setPassword] = useState(""); 9 | const [email, setEmail] = useState(""); 10 | const navigate = useNavigate(); 11 | 12 | const handleUsername = (event) => { 13 | setUsername(event.target.value); 14 | } 15 | 16 | const handlePassword = (event) => { 17 | setPassword(event.target.value); 18 | } 19 | 20 | const handleEmail = (event) => { 21 | setEmail(event.target.value); 22 | } 23 | 24 | const handleRegister = async (event) => { 25 | event.preventDefault(); 26 | 27 | const data = { 28 | "username": username, 29 | "password": password, 30 | "email": email, 31 | } 32 | 33 | const response = await axios.post("http://localhost:8081/auth/register", data); 34 | 35 | if (response.status === 200) { 36 | navigate("/login"); 37 | } else { 38 | console.log("error"); 39 | } 40 | } 41 | 42 | return ( 43 |
44 |
45 |
46 |

User Register

47 |
48 |
49 |
50 | 51 |
52 |
53 | 54 |
55 |
56 | 57 |
58 | 59 |
60 |
61 |

Already have an account? Login

62 |
63 |
64 |
65 | ) 66 | } 67 | 68 | export default Register; 69 | -------------------------------------------------------------------------------- /src/pages/Category.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { Link, useParams } from "react-router-dom"; 3 | import axios from "axios"; 4 | import Navbar from './Navbar'; 5 | 6 | const Category = () => { 7 | const [categories, setCategories] = useState([]); 8 | const [newCategoryName, setNewCategoryName] = useState(''); 9 | const params = useParams(); 10 | 11 | const handleLogout = () => {}; 12 | 13 | const getCategories = async () => { 14 | try { 15 | const response = await axios.get("http://localhost:8081/categories"); 16 | setCategories(response.data); 17 | } catch (error) { 18 | console.error('Error fetching categories:', error); 19 | } 20 | }; 21 | 22 | useEffect(() => { 23 | getCategories(); 24 | }, []); 25 | 26 | const handleInputChange = (e) => { 27 | setNewCategoryName(e.target.value); 28 | }; 29 | 30 | const addCategory = async () => { 31 | try { 32 | const response = await axios.post("http://localhost:8081/categories", { name: newCategoryName }); 33 | 34 | if (response.status === 201) { 35 | console.log('New category added successfully:', response.data); 36 | 37 | setNewCategoryName(''); 38 | 39 | getCategories(); 40 | } else { 41 | console.error('Failed to add new category.'); 42 | } 43 | } catch (error) { 44 | console.error('Error adding category:', error); 45 | } 46 | }; 47 | 48 | 49 | 50 | return ( 51 | <> 52 | 53 |

Categories

54 |
55 |
56 | {categories.map((category) => ( 57 |
58 |
59 |
60 |
{category.name}
61 | 62 | View Details 63 | 64 |
65 |
66 |
67 | ))} 68 |
69 |
70 |
71 |
Add New Category
72 |
73 | 80 |
81 | 87 |
88 |
89 |
90 |
91 |
92 | 93 | ); 94 | }; 95 | 96 | export default Category; 97 | -------------------------------------------------------------------------------- /src/pages/Checkout.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useEffect, useState } from "react"; 3 | import Navbar from './Navbar'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | const Checkout = ({ categories, handleLogout }) => { 7 | const [products, setProducts] = useState([]); 8 | const [orderProducts, setOrderProducts] = useState([]); 9 | const [total, setTotal] = useState(0); 10 | const [tax, setTax] = useState(0); 11 | 12 | useEffect(() => { 13 | getProducts(); 14 | }, []); 15 | 16 | const getProducts = async () => { 17 | try { 18 | const response = await axios.get('http://localhost:8081/products'); 19 | setProducts(response.data); 20 | } catch (error) { 21 | console.error('Error fetching products:', error); 22 | } 23 | } 24 | 25 | const createReceipt = () => { 26 | const date = new Date().toLocaleString(); 27 | const receiptContent = ` 28 | Date: ${date}\n 29 | Total: $${total}\n 30 | Tax: $${tax}\n 31 | Thank you for your purchase!\n 32 | `; 33 | const printWindow = window.open('', '_blank'); 34 | printWindow.document.write(`
${receiptContent}
`); 35 | printWindow.document.close(); 36 | printWindow.print(); 37 | } 38 | 39 | const addToOrder = (product) => { 40 | setOrderProducts([...orderProducts, product]); 41 | setTotal(total + product.price); 42 | } 43 | 44 | const createOrder = async () => { 45 | try { 46 | const productIds = orderProducts.map(obj => obj.id); 47 | const data = { 48 | products: productIds 49 | }; 50 | const response = await axios.post("http://localhost:8081/orders", data); 51 | 52 | if (response.status === 201) { 53 | setOrderProducts([]); 54 | setTotal(0); 55 | setTax(0); 56 | createReceipt(); // Access tax here 57 | console.log('Order created successfully!'); 58 | } else { 59 | console.error('Failed to create order.'); 60 | } 61 | } catch (error) { 62 | console.error('Error creating order:', error); 63 | } 64 | } 65 | 66 | 67 | return ( 68 | <> 69 | 70 |

Checking Out

71 |
72 |
73 |
74 |

Available Products

75 | {products.map(product => ( 76 |
77 | {product.name} - {product.price} 78 | 81 |
82 | ))} 83 |
84 |
85 |

Order Summary

86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | {orderProducts.map(product => ( 96 | 97 | 98 | 99 | 100 | 101 | ))} 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
Product IDProduct NamePrice
{product.id}{product.name}{product.price}
Total{total}
Tax{tax}
114 | 115 |
116 |
117 |
118 | 119 | ) 120 | } 121 | 122 | export default Checkout; 123 | -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import axios from "axios"; 3 | import Navbar from './Navbar'; 4 | import { Link } from "react-router-dom"; 5 | //import './Home.css'; 6 | 7 | const Home = () => { 8 | const [products, setProducts] = useState([]); 9 | 10 | useEffect(() => { 11 | getProducts(); 12 | }, []); 13 | 14 | const getProducts = async () => { 15 | try { 16 | const response = await axios.get("http://localhost:8081/products"); 17 | setProducts(response.data); 18 | } catch (error) { 19 | console.error('Error fetching products:', error); 20 | } 21 | }; 22 | 23 | return ( 24 | <> 25 | 26 |
27 |

Welcome to MyStore

28 | 29 |
30 | {products.map((product) => ( 31 |
32 |
33 | {/* {product.name} */} 38 |
39 |
{product.name}
40 |

${product.price}

41 | 42 | View Details 43 | 44 |
45 |
46 |
47 | ))} 48 |
49 | 50 | {/* Additional UI elements */} 51 |
52 |

Discover New Arrivals

53 |

Check out our latest products and find something special!

54 | 55 | View 56 | 57 |
58 |
59 | 60 | ); 61 | }; 62 | 63 | export default Home; 64 | -------------------------------------------------------------------------------- /src/pages/ManageProducts.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import axios from "axios"; 3 | import Navbar from './Navbar'; 4 | import { Link } from "react-router-dom"; 5 | 6 | const ManageProducts = () => { 7 | const [products, setProducts] = useState(null); 8 | const [categories, setCategories] = useState(null); 9 | 10 | const [name, setName] = useState(null); 11 | const [price, setPrice] = useState(null); 12 | const [qty, setQty] = useState(0); 13 | const [categoryId, setCategoryId] = useState(null); 14 | 15 | useEffect(() => { 16 | getProducts(); 17 | getCategories(); 18 | }, []); 19 | 20 | const getProducts = async () => { 21 | try { 22 | const response = await axios.get("http://localhost:8081/products"); 23 | setProducts(response.data); 24 | } catch (error) { 25 | console.error('Error fetching products:', error); 26 | } 27 | }; 28 | 29 | const getCategories = async () => { 30 | try { 31 | const response = await axios.get("http://localhost:8081/categories"); 32 | setCategories(response.data); 33 | } catch (error) { 34 | console.error('Error fetching categories:', error); 35 | } 36 | }; 37 | 38 | const handleName = (event) => { 39 | setName(event.target.value); 40 | }; 41 | 42 | const handlePrice = (event) => { 43 | setPrice(event.target.value); 44 | }; 45 | 46 | const handleQty = (event) => { 47 | setQty(event.target.value); 48 | }; 49 | 50 | const handleCategory = (event) => { 51 | setCategoryId(event.target.value); 52 | }; 53 | 54 | const handleSubmit = async (event) => { 55 | event.preventDefault(); 56 | 57 | const data = { 58 | name: name, 59 | price: price, 60 | qty: qty, 61 | categoryId: categoryId, 62 | }; 63 | 64 | try { 65 | const response = await axios.post("http://localhost:8081/products", data); 66 | setProducts([...products, response.data]); 67 | setName(''); 68 | setPrice(''); 69 | setQty(0); 70 | setCategoryId(null); 71 | } catch (error) { 72 | console.error('Error saving product:', error); 73 | } 74 | }; 75 | 76 | return ( 77 | <> 78 | 79 |

Manage Products

80 | 81 |
82 |
83 |
84 | 85 | 93 |
94 |
95 | 96 | 103 |
104 |
105 | 106 | 113 |
114 |
115 | 116 | 125 |
126 | 129 |
130 | 131 | {/* 134 | 135 |
    136 | {products && 137 | products.map((product) => ( 138 |
  1. 139 | 140 | {product.name} 141 | 142 |
  2. 143 | ))} 144 |
*/} 145 | 146 |
147 | 148 | ); 149 | }; 150 | 151 | export default ManageProducts; 152 | -------------------------------------------------------------------------------- /src/pages/Navbar.js: -------------------------------------------------------------------------------- 1 | import { Link, useNavigate } from "react-router-dom"; 2 | import React, { useState } from 'react'; 3 | 4 | const Navbar = ({ categories }) => { 5 | const navigate = useNavigate(); 6 | const [collapsed, setCollapsed] = useState(true); 7 | 8 | const handleLogoutClick = () => { 9 | localStorage.removeItem("token"); 10 | navigate("/login"); 11 | } 12 | 13 | return ( 14 | 50 | ); 51 | }; 52 | 53 | export default Navbar; 54 | -------------------------------------------------------------------------------- /src/pages/Product.js: -------------------------------------------------------------------------------- 1 | import Navbar from './Navbar'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const Product = ({ categories, handleLogout }) => { 5 | return ( 6 | <> 7 | 8 |

Product

9 | 10 | ) 11 | } 12 | 13 | export default Product; 14 | -------------------------------------------------------------------------------- /src/pages/SingleProduct.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | 4 | const SingleProduct = () => { 5 | 6 | const params = useParams(); 7 | 8 | const [product, setProduct] = useState(null); 9 | 10 | useEffect(() => { 11 | getProductById(); 12 | },[]) 13 | 14 | const getProductById = () => { 15 | fetch(`http://localhost:8081/products/${params.id}`) 16 | .then((response) => { 17 | return response.json() 18 | }).then((data) => { 19 | setProduct(data); //setting product state 20 | }).catch((error) => { 21 | console.log(error); 22 | }) 23 | } 24 | 25 | return ( 26 | <> 27 | {product && 28 |
29 |

{product.name}

30 |
{product.price} LKR
31 |
Stock: {product.qty}
32 |
33 | } 34 | 35 | ) 36 | } 37 | 38 | export default SingleProduct; -------------------------------------------------------------------------------- /src/pages/Stocks.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import axios from 'axios'; 3 | import Navbar from './Navbar'; 4 | 5 | const Stocks = ({ categories, handleLogout }) => { 6 | const [stocks, setStocks] = useState([]); 7 | const [editedStock, setEditedStock] = useState({ id: null, name: '', qty: '', price: '' }); 8 | 9 | useEffect(() => { 10 | fetchStocks(); 11 | }, []); 12 | 13 | const fetchStocks = async () => { 14 | try { 15 | const response = await axios.get('http://localhost:8081/products'); 16 | setStocks(response.data); 17 | } catch (error) { 18 | console.error('Error fetching stocks:', error); 19 | } 20 | }; 21 | 22 | const handleEditClick = (id, name, qty, price) => { 23 | setEditedStock({ id, name, qty, price }); 24 | }; 25 | 26 | const handleUpdate = async (id) => { 27 | try { 28 | const { name, qty, price } = editedStock; 29 | const response = await axios.put(`http://localhost:8081/products/${id}`, { name, qty, price }); 30 | 31 | if (response.status === 200) { 32 | const updatedStocks = stocks.map(stock => { 33 | if (stock.id === id) { 34 | return { ...stock, qty: editedStock.qty, price: editedStock.price }; 35 | } 36 | return stock; 37 | }); 38 | setStocks(updatedStocks); 39 | setEditedStock({ id: null, name: '', qty: '', price: '' }); 40 | console.log('Stock updated successfully'); 41 | } else { 42 | console.error('Failed to update stock'); 43 | } 44 | } catch (error) { 45 | console.error('Error updating stock:', error.message || error); 46 | } 47 | }; 48 | 49 | const handleDelete = async (id) => { 50 | try { 51 | const response = await axios.delete(`http://localhost:8081/products/${id}`); 52 | if (response.status === 200) { 53 | const updatedStocks = stocks.filter(stock => stock.id !== id); 54 | setStocks(updatedStocks); 55 | console.log('Stock deleted successfully'); 56 | } else { 57 | console.error('Failed to delete stock'); 58 | } 59 | } catch (error) { 60 | console.error('Error deleting stock:', error.message || error); 61 | } 62 | }; 63 | 64 | const handleCancelEdit = () => { 65 | setEditedStock({ id: null, name: '', qty: '', price: '' }); 66 | }; 67 | 68 | return ( 69 |
70 | 71 |
72 |

Stocks

73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | {stocks.map((stock) => ( 85 | 86 | 87 | 99 | 111 | 124 | 125 | ))} 126 | 127 |
NameQtyPriceActions
{stock.name} 88 | {editedStock.id === stock.id ? ( 89 | setEditedStock({ ...editedStock, qty: e.target.value })} 94 | /> 95 | ) : ( 96 | stock.qty 97 | )} 98 | 100 | {editedStock.id === stock.id ? ( 101 | setEditedStock({ ...editedStock, price: e.target.value })} 106 | /> 107 | ) : ( 108 | stock.price 109 | )} 110 | 112 | {editedStock.id === stock.id ? ( 113 | <> 114 | 115 | 116 | 117 | ) : ( 118 | 121 | )} 122 | 123 |
128 |
129 |
130 |
131 | ); 132 | }; 133 | 134 | export default Stocks; 135 | -------------------------------------------------------------------------------- /src/pages/cmjd-102.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "../../../../SpringBoot/database" 5 | }, 6 | { 7 | "path": "../../../ProductApp" 8 | }, 9 | { 10 | "path": "../.." 11 | } 12 | ], 13 | "settings": {} 14 | } -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/utils/ProtectedRoutes.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Navigate, Outlet, useNavigate } from "react-router-dom"; 3 | 4 | const ProtectedRoutes = () => { 5 | const token = localStorage.getItem("token"); 6 | 7 | const navigate = useNavigate(); 8 | 9 | if(!token) { 10 | navigate("/login"); 11 | } 12 | 13 | axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; 14 | 15 | //show child elements 16 | return 17 | 18 | } 19 | 20 | export default ProtectedRoutes; --------------------------------------------------------------------------------