├── Ecommerce-Frontend ├── src │ ├── assets │ │ ├── unplugged.png │ │ └── react.svg │ ├── index.css │ ├── axios.jsx │ ├── main.jsx │ ├── components │ │ ├── CheckoutPopup.jsx │ │ ├── Product.jsx │ │ ├── Home.jsx │ │ ├── AddProduct.jsx │ │ ├── UpdateProduct.jsx │ │ ├── Navbar.jsx │ │ └── Cart.jsx │ ├── App.jsx │ ├── Context │ │ └── Context.jsx │ └── App.css ├── vite.config.js ├── README.md ├── index.html ├── package.json └── public │ └── vite.svg ├── Ecommerce-Backend ├── target │ ├── classes │ │ ├── com │ │ │ └── cart │ │ │ │ └── ecom_proj │ │ │ │ ├── model │ │ │ │ └── Product.class │ │ │ │ ├── repo │ │ │ │ └── ProductRepo.class │ │ │ │ ├── EcomProjApplication.class │ │ │ │ ├── service │ │ │ │ └── ProductService.class │ │ │ │ └── controller │ │ │ │ └── ProductController.class │ │ ├── application.properties │ │ └── data1.sql │ └── test-classes │ │ └── com │ │ └── cart │ │ └── ecom_proj │ │ └── EcomProjApplicationTests.class ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── cart │ │ │ └── ecom_proj │ │ │ └── EcomProjApplicationTests.java │ └── main │ │ ├── resources │ │ ├── application.properties │ │ └── data1.sql │ │ └── java │ │ └── com │ │ └── cart │ │ └── ecom_proj │ │ ├── EcomProjApplication.java │ │ ├── repo │ │ └── ProductRepo.java │ │ ├── service │ │ └── ProductService.java │ │ ├── model │ │ └── Product.java │ │ └── controller │ │ └── ProductController.java ├── HELP.md ├── pom.xml ├── mvnw.cmd └── mvnw └── README.md /Ecommerce-Frontend/src/assets/unplugged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GattiHarishKumar/SpringBoot-Reactjs-Ecommerce/HEAD/Ecommerce-Frontend/src/assets/unplugged.png -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/index.css: -------------------------------------------------------------------------------- 1 | .dark-theme{ 2 | --root_background: #1f1f1f;} 3 | .light-theme{ 4 | --root_background: white; 5 | } 6 | :root{ 7 | background-color: #1f1f1f; 8 | } -------------------------------------------------------------------------------- /Ecommerce-Backend/target/classes/com/cart/ecom_proj/model/Product.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GattiHarishKumar/SpringBoot-Reactjs-Ecommerce/HEAD/Ecommerce-Backend/target/classes/com/cart/ecom_proj/model/Product.class -------------------------------------------------------------------------------- /Ecommerce-Frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /Ecommerce-Backend/target/classes/com/cart/ecom_proj/repo/ProductRepo.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GattiHarishKumar/SpringBoot-Reactjs-Ecommerce/HEAD/Ecommerce-Backend/target/classes/com/cart/ecom_proj/repo/ProductRepo.class -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/axios.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const API = axios.create({ 4 | baseURL: "http://localhost:8080/api", 5 | }); 6 | delete API.defaults.headers.common["Authorization"]; 7 | export default API; 8 | -------------------------------------------------------------------------------- /Ecommerce-Backend/target/classes/com/cart/ecom_proj/EcomProjApplication.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GattiHarishKumar/SpringBoot-Reactjs-Ecommerce/HEAD/Ecommerce-Backend/target/classes/com/cart/ecom_proj/EcomProjApplication.class -------------------------------------------------------------------------------- /Ecommerce-Backend/target/classes/com/cart/ecom_proj/service/ProductService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GattiHarishKumar/SpringBoot-Reactjs-Ecommerce/HEAD/Ecommerce-Backend/target/classes/com/cart/ecom_proj/service/ProductService.class -------------------------------------------------------------------------------- /Ecommerce-Backend/target/classes/com/cart/ecom_proj/controller/ProductController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GattiHarishKumar/SpringBoot-Reactjs-Ecommerce/HEAD/Ecommerce-Backend/target/classes/com/cart/ecom_proj/controller/ProductController.class -------------------------------------------------------------------------------- /Ecommerce-Backend/target/test-classes/com/cart/ecom_proj/EcomProjApplicationTests.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GattiHarishKumar/SpringBoot-Reactjs-Ecommerce/HEAD/Ecommerce-Backend/target/test-classes/com/cart/ecom_proj/EcomProjApplicationTests.class -------------------------------------------------------------------------------- /Ecommerce-Backend/src/test/java/com/cart/ecom_proj/EcomProjApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.cart.ecom_proj; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class EcomProjApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Ecommerce-Backend/target/classes/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=ecom-proj 2 | 3 | spring.datasource.url=jdbc:h2:mem:Ecommerce 4 | spring.datasource.password=project1 5 | spring.datasource.driverClassName=org.h2.Driver 6 | 7 | spring.jpa.show-sql=true 8 | spring.jpa.hibernate.ddl-auto=update 9 | 10 | spring.jpa.defer-datasource-initialization=true -------------------------------------------------------------------------------- /Ecommerce-Backend/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=ecom-proj 2 | 3 | spring.datasource.url=jdbc:h2:mem:Ecommerce 4 | spring.datasource.password=project1 5 | spring.datasource.driverClassName=org.h2.Driver 6 | 7 | spring.jpa.show-sql=true 8 | spring.jpa.hibernate.ddl-auto=update 9 | 10 | spring.jpa.defer-datasource-initialization=true -------------------------------------------------------------------------------- /Ecommerce-Backend/src/main/java/com/cart/ecom_proj/EcomProjApplication.java: -------------------------------------------------------------------------------- 1 | package com.cart.ecom_proj; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class EcomProjApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(EcomProjApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "./index.css"; 5 | import { useContext } from "react"; 6 | import { AppProvider } from "./Context/Context.jsx"; 7 | // import { BrowserRouter as Router } from "react-router-dom"; 8 | ReactDOM.createRoot(document.getElementById("root")).render( 9 | 10 | {/* */} 11 | 12 | 13 | 14 | {/* */} 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Ecommerce-Backend/target/classes/data1.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO product (name, desc, brand, price, category, release_date, available, quantity) VALUES 2 | ('Tata Nexon', 'A compact SUV with excellent safety features and performance.', 'Tata Motors', 750000.00, 'Cars', '2024-01-15', true, 50), 3 | ('Maruti Suzuki Swift', 'A popular hatchback known for its fuel efficiency and reliability.', 'Maruti Suzuki', 550000.00, 'Cars', '2024-02-01', true, 100), 4 | ('Hyundai Creta', 'A stylish SUV with advanced features and comfortable interior.', 'Hyundai', 950000.00, 'Cars', '2024-03-01', true, 75), 5 | ('Mahindra Thar', 'A rugged off-road SUV with a powerful engine and modern amenities.', 'Mahindra', 1200000.00, 'Cars', '2024-04-01', true, 30), 6 | ('Honda City', 'A premium sedan with a sleek design and advanced safety features', 'Honda',1100000.00, 'Cars', '2024-05-01', true, 60); -------------------------------------------------------------------------------- /Ecommerce-Backend/src/main/resources/data1.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO product (name, desc, brand, price, category, release_date, available, quantity) VALUES 2 | ('Tata Nexon', 'A compact SUV with excellent safety features and performance.', 'Tata Motors', 750000.00, 'Cars', '2024-01-15', true, 50), 3 | ('Maruti Suzuki Swift', 'A popular hatchback known for its fuel efficiency and reliability.', 'Maruti Suzuki', 550000.00, 'Cars', '2024-02-01', true, 100), 4 | ('Hyundai Creta', 'A stylish SUV with advanced features and comfortable interior.', 'Hyundai', 950000.00, 'Cars', '2024-03-01', true, 75), 5 | ('Mahindra Thar', 'A rugged off-road SUV with a powerful engine and modern amenities.', 'Mahindra', 1200000.00, 'Cars', '2024-04-01', true, 30), 6 | ('Honda City', 'A premium sedan with a sleek design and advanced safety features', 'Honda',1100000.00, 'Cars', '2024-05-01', true, 60); -------------------------------------------------------------------------------- /Ecommerce-Backend/src/main/java/com/cart/ecom_proj/repo/ProductRepo.java: -------------------------------------------------------------------------------- 1 | package com.cart.ecom_proj.repo; 2 | 3 | import com.cart.ecom_proj.model.Product; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import java.util.List; 9 | 10 | @Repository 11 | public interface ProductRepo extends JpaRepository { 12 | 13 | @Query("SELECT p FROM Product p WHERE " + 14 | "LOWER(p.name) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " + 15 | "LOWER(p.description) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " + 16 | "LOWER(p.brand) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " + 17 | "LOWER(p.category) LIKE LOWER(CONCAT('%', :keyword, '%'))") 18 | List searchProducts(String keyword); 19 | } 20 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@popperjs/core": "^2.11.8", 14 | "axios": "^1.6.8", 15 | "bootstrap": "^5.3.3", 16 | "bootstrap-icons": "^1.11.3", 17 | "history": "^5.3.0", 18 | "react": "^18.2.0", 19 | "react-bootstrap": "^2.10.2", 20 | "react-dom": "^18.2.0", 21 | "react-icons": "^5.2.0", 22 | "react-router-dom": "^6.22.3" 23 | }, 24 | "devDependencies": { 25 | "@types/react": "^18.2.66", 26 | "@types/react-dom": "^18.2.22", 27 | "@vitejs/plugin-react-swc": "^3.5.0", 28 | "eslint": "^8.57.0", 29 | "eslint-plugin-react": "^7.34.1", 30 | "eslint-plugin-react-hooks": "^4.6.0", 31 | "eslint-plugin-react-refresh": "^0.4.6", 32 | "sass": "^1.74.1", 33 | "vite": "^5.2.8" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/components/CheckoutPopup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Modal, Button } from 'react-bootstrap'; 3 | 4 | const CheckoutPopup = ({ show, handleClose, cartItems, totalPrice, handleCheckout }) => { 5 | return ( 6 |
7 | 8 | 9 | 10 | Checkout 11 | 12 | 13 |
14 | {cartItems.map((item) => ( 15 |
16 | {item.name} 17 |
18 |

{item.name}

19 |

Quantity: {item.quantity}

20 |

Price: ${item.price * item.quantity}

21 |
22 |
23 | ))} 24 |
25 |
Total: ${totalPrice}
26 |
27 |
28 |
29 | 30 | 33 | 36 | 37 |
38 |
39 | ); 40 | }; 41 | 42 | export default CheckoutPopup; 43 | -------------------------------------------------------------------------------- /Ecommerce-Backend/src/main/java/com/cart/ecom_proj/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.cart.ecom_proj.service; 2 | 3 | import com.cart.ecom_proj.model.Product; 4 | import com.cart.ecom_proj.repo.ProductRepo; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.web.multipart.MultipartFile; 8 | 9 | import java.io.IOException; 10 | import java.util.List; 11 | 12 | @Service 13 | public class ProductService { 14 | 15 | @Autowired 16 | private ProductRepo repo; 17 | 18 | 19 | public List getAllProducts() { 20 | return repo.findAll(); 21 | } 22 | 23 | public Product getProductById(int id){ 24 | return repo.findById(id).orElse(null); 25 | } 26 | 27 | public Product addProduct(Product product, MultipartFile imageFile) throws IOException { 28 | product.setImageName(imageFile.getOriginalFilename()); 29 | product.setImageType(imageFile.getContentType()); 30 | product.setImageDate(imageFile.getBytes()); 31 | return repo.save(product); 32 | } 33 | 34 | public Product updateProduct(int id, Product product, MultipartFile imageFile) throws IOException { 35 | product.setImageDate(imageFile.getBytes()); 36 | product.setImageName(imageFile.getOriginalFilename()); 37 | product.getImageType(imageFile.getContentType()); 38 | return repo.save(product); 39 | } 40 | 41 | public void deleteProduct(int id) { 42 | repo.deleteById(id); 43 | } 44 | 45 | public List searchProducts(String keyword) { 46 | return repo.searchProducts(keyword); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Ecommerce-Backend/HELP.md: -------------------------------------------------------------------------------- 1 | # Read Me First 2 | The following was discovered as part of building this project: 3 | 4 | * The original package name 'com.cart.ecom-proj' is invalid and this project uses 'com.cart.ecom_proj' instead. 5 | 6 | # Getting Started 7 | 8 | ### Reference Documentation 9 | For further reference, please consider the following sections: 10 | 11 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 12 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/3.3.3/maven-plugin) 13 | * [Create an OCI image](https://docs.spring.io/spring-boot/3.3.3/maven-plugin/build-image.html) 14 | * [Spring Web](https://docs.spring.io/spring-boot/docs/3.3.3/reference/htmlsingle/index.html#web) 15 | * [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/3.3.3/reference/htmlsingle/index.html#using.devtools) 16 | * [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.3.3/reference/htmlsingle/index.html#data.sql.jpa-and-spring-data) 17 | 18 | ### Guides 19 | The following guides illustrate how to use some features concretely: 20 | 21 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 22 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 23 | * [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) 24 | * [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/) 25 | 26 | ### Maven Parent overrides 27 | 28 | Due to Maven's design, elements are inherited from the parent POM to the project POM. 29 | While most of the inheritance is fine, it also inherits unwanted elements like `` and `` from the parent. 30 | To prevent this, the project POM contains empty overrides for these elements. 31 | If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides. 32 | 33 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/App.jsx: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import React, { useState, useEffect } from "react"; 3 | import Home from "./components/Home"; 4 | import Navbar from "./components/Navbar"; 5 | import Cart from "./components/Cart"; 6 | import AddProduct from "./components/AddProduct"; 7 | import Product from "./components/Product"; 8 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 9 | import { AppProvider } from "./Context/Context"; 10 | import UpdateProduct from "./components/UpdateProduct"; 11 | import "bootstrap/dist/css/bootstrap.min.css"; 12 | import "bootstrap/dist/js/bootstrap.bundle.min.js"; 13 | import 'bootstrap/dist/css/bootstrap.min.css'; 14 | 15 | 16 | function App() { 17 | const [cart, setCart] = useState([]); 18 | const [selectedCategory, setSelectedCategory] = useState(""); 19 | 20 | const handleCategorySelect = (category) => { 21 | setSelectedCategory(category); 22 | console.log("Selected category:", category); 23 | }; 24 | const addToCart = (product) => { 25 | const existingProduct = cart.find((item) => item.id === product.id); 26 | if (existingProduct) { 27 | setCart( 28 | cart.map((item) => 29 | item.id === product.id 30 | ? { ...item, quantity: item.quantity + 1 } 31 | : item 32 | ) 33 | ); 34 | } else { 35 | setCart([...cart, { ...product, quantity: 1 }]); 36 | } 37 | }; 38 | 39 | return ( 40 | 41 | 42 | 44 | 45 | 50 | } 51 | /> 52 | } /> 53 | } /> 54 | } /> 55 | } /> 56 | } /> 57 | 58 | 59 | 60 | ); 61 | } 62 | 63 | export default App; 64 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/Context/Context.jsx: -------------------------------------------------------------------------------- 1 | import axios from "../axios"; 2 | import { useState, useEffect, createContext } from "react"; 3 | 4 | const AppContext = createContext({ 5 | data: [], 6 | isError: "", 7 | cart: [], 8 | addToCart: (product) => {}, 9 | removeFromCart: (productId) => {}, 10 | refreshData:() =>{}, 11 | updateStockQuantity: (productId, newQuantity) =>{} 12 | 13 | }); 14 | 15 | export const AppProvider = ({ children }) => { 16 | const [data, setData] = useState([]); 17 | const [isError, setIsError] = useState(""); 18 | const [cart, setCart] = useState(JSON.parse(localStorage.getItem('cart')) || []); 19 | 20 | 21 | const addToCart = (product) => { 22 | const existingProductIndex = cart.findIndex((item) => item.id === product.id); 23 | if (existingProductIndex !== -1) { 24 | const updatedCart = cart.map((item, index) => 25 | index === existingProductIndex 26 | ? { ...item, quantity: item.quantity + 1 } 27 | : item 28 | ); 29 | setCart(updatedCart); 30 | localStorage.setItem('cart', JSON.stringify(updatedCart)); 31 | } else { 32 | const updatedCart = [...cart, { ...product, quantity: 1 }]; 33 | setCart(updatedCart); 34 | localStorage.setItem('cart', JSON.stringify(updatedCart)); 35 | } 36 | }; 37 | 38 | const removeFromCart = (productId) => { 39 | console.log("productID",productId) 40 | const updatedCart = cart.filter((item) => item.id !== productId); 41 | setCart(updatedCart); 42 | localStorage.setItem('cart', JSON.stringify(updatedCart)); 43 | console.log("CART",cart) 44 | }; 45 | 46 | const refreshData = async () => { 47 | try { 48 | const response = await axios.get("/products"); 49 | setData(response.data); 50 | } catch (error) { 51 | setIsError(error.message); 52 | } 53 | }; 54 | 55 | const clearCart =() =>{ 56 | setCart([]); 57 | } 58 | 59 | useEffect(() => { 60 | refreshData(); 61 | }, []); 62 | 63 | useEffect(() => { 64 | localStorage.setItem('cart', JSON.stringify(cart)); 65 | }, [cart]); 66 | 67 | return ( 68 | 69 | {children} 70 | 71 | ); 72 | }; 73 | 74 | export default AppContext; -------------------------------------------------------------------------------- /Ecommerce-Backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.3.3 9 | 10 | 11 | com.cart 12 | ecom-proj 13 | 0.0.1-SNAPSHOT 14 | ecom-proj 15 | Demo project for Spring Boot 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 21 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-data-jpa 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-web 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-devtools 45 | runtime 46 | true 47 | 48 | 49 | com.h2database 50 | h2 51 | runtime 52 | 53 | 54 | org.projectlombok 55 | lombok 56 | true 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-test 61 | test 62 | 63 | 64 | org.jetbrains 65 | annotations 66 | RELEASE 67 | compile 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | 79 | org.projectlombok 80 | lombok 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Ecommerce-Backend/src/main/java/com/cart/ecom_proj/model/Product.java: -------------------------------------------------------------------------------- 1 | package com.cart.ecom_proj.model; 2 | 3 | import jakarta.persistence.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.math.BigDecimal; 9 | import java.util.Date; 10 | 11 | @Entity 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class Product { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private int id; 20 | private String name; 21 | private String description; 22 | private String brand; 23 | private BigDecimal price; 24 | private String category; 25 | 26 | private Date releaseDate; 27 | private boolean productAvailable; 28 | private int stockQuantity; 29 | 30 | private String imageName; 31 | private String imageType; 32 | @Lob 33 | private byte[] imageDate; 34 | 35 | public int getId() { 36 | return id; 37 | } 38 | 39 | public void setId(int id) { 40 | this.id = id; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | public String getDescription() { 52 | return description; 53 | } 54 | 55 | public void setDescription(String description) { 56 | this.description = description; 57 | } 58 | 59 | public String getBrand() { 60 | return brand; 61 | } 62 | 63 | public void setBrand(String brand) { 64 | this.brand = brand; 65 | } 66 | 67 | public BigDecimal getPrice() { 68 | return price; 69 | } 70 | 71 | public void setPrice(BigDecimal price) { 72 | this.price = price; 73 | } 74 | 75 | public String getCategory() { 76 | return category; 77 | } 78 | 79 | public void setCategory(String category) { 80 | this.category = category; 81 | } 82 | 83 | public Date getReleaseDate() { 84 | return releaseDate; 85 | } 86 | 87 | public void setReleaseDate(Date releaseDate) { 88 | this.releaseDate = releaseDate; 89 | } 90 | 91 | public boolean isProductAvailable() { 92 | return productAvailable; 93 | } 94 | 95 | public void setProductAvailable(boolean productAvailable) { 96 | this.productAvailable = productAvailable; 97 | } 98 | 99 | public int getStockQuantity() { 100 | return stockQuantity; 101 | } 102 | 103 | public void setStockQuantity(int stockQuantity) { 104 | this.stockQuantity = stockQuantity; 105 | } 106 | 107 | public String getImageName() { 108 | return imageName; 109 | } 110 | 111 | public void setImageName(String imageName) { 112 | this.imageName = imageName; 113 | } 114 | 115 | public String getImageType(String contentType) { 116 | return imageType; 117 | } 118 | 119 | public void setImageType(String imageType) { 120 | this.imageType = imageType; 121 | } 122 | 123 | public byte[] getImageDate() { 124 | return imageDate; 125 | } 126 | 127 | public void setImageDate(byte[] imageDate) { 128 | this.imageDate = imageDate; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 🛍️ Full Stack E-commerce Web Application 4 | 5 | A full-stack **E-commerce application** using **Spring Boot** (Java) for the backend and **ReactJS with Vite** for the frontend. This application demonstrates the integration of RESTful APIs with a modern frontend stack, ideal for learning and demonstration purposes. 6 | 7 | --- 8 | 9 | ## 📁 Project Structure 10 | 11 | ``` 12 | SpringBoot-Reactjs-Ecommerce-main/ 13 | ├── Ecommerce-Backend/ # Spring Boot REST API backend 14 | ├── Ecommerce-Frontend/ # React + Vite frontend application 15 | ``` 16 | 17 | --- 18 | 19 | ## 🧩 Backend - Spring Boot 20 | 21 | ### 🔧 Technologies Used 22 | 23 | * Java 17+ 24 | * Spring Boot 25 | * Spring Data JPA 26 | * MySQL (can be adapted) 27 | * Maven 28 | 29 | ### 📂 Backend Directory Structure 30 | 31 | ``` 32 | Ecommerce-Backend/ 33 | ├── controller/ # REST endpoints 34 | ├── model/ # JPA entity classes 35 | ├── repo/ # Spring Data JPA interfaces 36 | ├── service/ # Business logic 37 | ├── resources/ 38 | │ ├── application.properties 39 | │ └── data1.sql 40 | └── pom.xml # Maven build config 41 | ``` 42 | 43 | ### ⚙️ Setup Instructions 44 | 45 | 1. **Database Setup:** 46 | 47 | * Create a MySQL database, e.g., `ecomdb`. 48 | * Update `application.properties`: 49 | 50 | ```properties 51 | spring.datasource.url=jdbc:mysql://localhost:3306/ecomdb 52 | spring.datasource.username=root 53 | spring.datasource.password=yourpassword 54 | spring.jpa.hibernate.ddl-auto=update 55 | ``` 56 | 57 | 2. **Run the App:** 58 | 59 | ```bash 60 | cd Ecommerce-Backend 61 | mvn spring-boot:run 62 | ``` 63 | 64 | 3. **Data Initialization:** 65 | 66 | On first run, `data1.sql` inserts seed product data into your DB. 67 | 68 | ### 📡 REST API Endpoints 69 | 70 | | Method | Endpoint | Description | 71 | | ------ | ---------------- | ------------------ | 72 | | GET | `/products` | Fetch all products | 73 | | GET | `/products/{id}` | Get product by ID | 74 | | POST | `/products` | Add new product | 75 | | PUT | `/products/{id}` | Update product | 76 | | DELETE | `/products/{id}` | Delete product | 77 | 78 | --- 79 | 80 | ## 💻 Frontend - React + Vite 81 | 82 | ### 🔧 Technologies Used 83 | 84 | * ReactJS 85 | * Vite (bundler) 86 | * Axios (API calls) 87 | * Bootstrap (UI) 88 | * JavaScript (ES6+) 89 | 90 | ### 📂 Frontend Directory Structure 91 | 92 | ``` 93 | Ecommerce-Frontend/ 94 | ├── public/ 95 | ├── src/ 96 | │ ├── components/ # Reusable components 97 | │ ├── pages/ # Page-level components 98 | │ ├── App.jsx # App layout 99 | │ └── main.jsx # Entry point 100 | ├── package.json 101 | └── vite.config.js 102 | ``` 103 | 104 | ### ▶️ Getting Started 105 | 106 | 1. **Install dependencies:** 107 | 108 | ```bash 109 | cd Ecommerce-Frontend 110 | npm install 111 | ``` 112 | 113 | 2. **Run the app:** 114 | 115 | ```bash 116 | npm run dev 117 | ``` 118 | 119 | This will launch the frontend at `http://localhost:5173`. 120 | 121 | 3. **Connect to Backend:** 122 | 123 | Update the backend URL in API service files (usually inside `src/` or `src/services/`) if needed: 124 | 125 | ```js 126 | axios.get('http://localhost:8080/products') 127 | ``` 128 | 129 | ### 🧩 Features 130 | 131 | * Product List (from Spring Boot backend) 132 | * Dynamic rendering using React components 133 | * Fully responsive UI 134 | * Easy integration with further features (cart, checkout, login) 135 | 136 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Ecommerce-Backend/src/main/java/com/cart/ecom_proj/controller/ProductController.java: -------------------------------------------------------------------------------- 1 | package com.cart.ecom_proj.controller; 2 | 3 | import com.cart.ecom_proj.model.Product; 4 | import com.cart.ecom_proj.service.ProductService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.*; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | import java.io.IOException; 13 | import java.util.List; 14 | 15 | 16 | @RestController 17 | @CrossOrigin 18 | @RequestMapping("/api") 19 | public class ProductController { 20 | 21 | @Autowired 22 | private ProductService service; 23 | 24 | @GetMapping("/products") 25 | public ResponseEntity> getAllProducts(){ 26 | return new ResponseEntity<>(service.getAllProducts(), HttpStatus.OK); 27 | } 28 | 29 | @GetMapping("/product/{id}") 30 | public ResponseEntity getProduct(@PathVariable int id){ 31 | 32 | Product product = service.getProductById(id); 33 | 34 | if(product != null) 35 | return new ResponseEntity<>(product, HttpStatus.OK); 36 | else 37 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 38 | } 39 | 40 | @PostMapping("/product") 41 | public ResponseEntity addProduct(@RequestPart Product product, @RequestPart MultipartFile imageFile) { 42 | 43 | try { 44 | System.out.println(product); 45 | Product product1 = service.addProduct(product, imageFile); 46 | return new ResponseEntity<>(product1, HttpStatus.CREATED); 47 | } 48 | catch (Exception e) { 49 | return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); 50 | } 51 | } 52 | 53 | @GetMapping("product/{productId}/image") 54 | public ResponseEntity getImageByProductId(@PathVariable int productId){ 55 | 56 | Product product = service.getProductById(productId); 57 | byte[] imageFile = product.getImageDate(); 58 | 59 | return ResponseEntity.ok() 60 | .contentType(MediaType.valueOf(product.getImageType(""))) 61 | .body(imageFile); 62 | } 63 | 64 | @PutMapping("/product/{id}") 65 | public ResponseEntity updateProduct(@PathVariable int id, 66 | @RequestPart Product product, 67 | @RequestPart MultipartFile imageFile){ 68 | 69 | Product product1 = null; 70 | try { 71 | product1 = service.updateProduct(id, product, imageFile); 72 | } catch (IOException e) { 73 | throw new RuntimeException(e); 74 | } 75 | if (product1 != null) 76 | return new ResponseEntity<>("Updated", HttpStatus.OK); 77 | else 78 | return new ResponseEntity<>("Failed to update", HttpStatus.BAD_REQUEST); 79 | 80 | } 81 | 82 | @DeleteMapping("/product/{id}") 83 | public ResponseEntity deleteProduct(@PathVariable int id){ 84 | Product product = service.getProductById(id); 85 | if(product != null) { 86 | service.deleteProduct(id); 87 | return new ResponseEntity<>("Deleted", HttpStatus.OK); 88 | } 89 | else { 90 | return new ResponseEntity<>("Product not found", HttpStatus.NOT_FOUND); 91 | } 92 | } 93 | 94 | @GetMapping("/products/search") 95 | public ResponseEntity> searchProducts(@RequestParam String keyword){ 96 | System.out.println("searching with " + keyword); 97 | List products = service.searchProducts(keyword); 98 | return new ResponseEntity<>(products, HttpStatus.OK); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/components/Product.jsx: -------------------------------------------------------------------------------- 1 | import { useNavigate, useParams } from "react-router-dom"; 2 | import { useContext, useEffect } from "react"; 3 | import { useState } from "react"; 4 | import AppContext from "../Context/Context"; 5 | import axios from "../axios"; 6 | import UpdateProduct from "./UpdateProduct"; 7 | const Product = () => { 8 | const { id } = useParams(); 9 | const { data, addToCart, removeFromCart, cart, refreshData } = 10 | useContext(AppContext); 11 | const [product, setProduct] = useState(null); 12 | const [imageUrl, setImageUrl] = useState(""); 13 | const navigate = useNavigate(); 14 | 15 | useEffect(() => { 16 | const fetchProduct = async () => { 17 | try { 18 | const response = await axios.get( 19 | `http://localhost:8080/api/product/${id}` 20 | ); 21 | setProduct(response.data); 22 | if (response.data.imageName) { 23 | fetchImage(); 24 | } 25 | } catch (error) { 26 | console.error("Error fetching product:", error); 27 | } 28 | }; 29 | 30 | const fetchImage = async () => { 31 | const response = await axios.get( 32 | `http://localhost:8080/api/product/${id}/image`, 33 | { responseType: "blob" } 34 | ); 35 | setImageUrl(URL.createObjectURL(response.data)); 36 | }; 37 | 38 | fetchProduct(); 39 | }, [id]); 40 | 41 | const deleteProduct = async () => { 42 | try { 43 | await axios.delete(`http://localhost:8080/api/product/${id}`); 44 | removeFromCart(id); 45 | console.log("Product deleted successfully"); 46 | alert("Product deleted successfully"); 47 | refreshData(); 48 | navigate("/"); 49 | } catch (error) { 50 | console.error("Error deleting product:", error); 51 | } 52 | }; 53 | 54 | const handleEditClick = () => { 55 | navigate(`/product/update/${id}`); 56 | }; 57 | 58 | const handlAddToCart = () => { 59 | addToCart(product); 60 | alert("Product added to cart"); 61 | }; 62 | if (!product) { 63 | return ( 64 |

65 | Loading... 66 |

67 | ); 68 | } 69 | return ( 70 | <> 71 |
72 | {product.imageName} 78 | 79 |
80 |
81 |
82 | 83 | {product.category} 84 | 85 |

86 | 87 |

Listed : {new Date(product.releaseDate).toLocaleDateString()}
88 | {/* {new Date(product.releaseDate).toLocaleDateString()} */} 89 |

90 |
91 | 92 | 93 |

94 | {product.name} 95 |

96 | {product.brand} 97 |

PRODUCT DESCRIPTION :

98 |

{product.description}

99 |
100 | 101 |
102 | 103 | {"$" + product.price} 104 | 105 | 124 |
125 | Stock Available :{" "} 126 | 127 | {product.stockQuantity} 128 | 129 |
130 | 131 |
132 |
133 | 149 | {/* */} 150 | 166 |
167 |
168 |
169 | 170 | ); 171 | }; 172 | 173 | export default Product; -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/components/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import axios from "axios"; 4 | import AppContext from "../Context/Context"; 5 | import unplugged from "../assets/unplugged.png" 6 | 7 | const Home = ({ selectedCategory }) => { 8 | const { data, isError, addToCart, refreshData } = useContext(AppContext); 9 | const [products, setProducts] = useState([]); 10 | const [isDataFetched, setIsDataFetched] = useState(false); 11 | 12 | useEffect(() => { 13 | if (!isDataFetched) { 14 | refreshData(); 15 | setIsDataFetched(true); 16 | } 17 | }, [refreshData, isDataFetched]); 18 | 19 | useEffect(() => { 20 | if (data && data.length > 0) { 21 | const fetchImagesAndUpdateProducts = async () => { 22 | const updatedProducts = await Promise.all( 23 | data.map(async (product) => { 24 | try { 25 | const response = await axios.get( 26 | `http://localhost:8080/api/product/${product.id}/image`, 27 | { responseType: "blob" } 28 | ); 29 | const imageUrl = URL.createObjectURL(response.data); 30 | return { ...product, imageUrl }; 31 | } catch (error) { 32 | console.error( 33 | "Error fetching image for product ID:", 34 | product.id, 35 | error 36 | ); 37 | return { ...product, imageUrl: "placeholder-image-url" }; 38 | } 39 | }) 40 | ); 41 | setProducts(updatedProducts); 42 | }; 43 | 44 | fetchImagesAndUpdateProducts(); 45 | } 46 | }, [data]); 47 | 48 | const filteredProducts = selectedCategory 49 | ? products.filter((product) => product.category === selectedCategory) 50 | : products; 51 | 52 | if (isError) { 53 | return ( 54 |

55 | Error 56 |

57 | ); 58 | } 59 | return ( 60 | <> 61 |
71 | {filteredProducts.length === 0 ? ( 72 |

80 | No Products Available 81 |

82 | ) : ( 83 | filteredProducts.map((product) => { 84 | const { id, brand, name, price, productAvailable, imageUrl } = 85 | product; 86 | const cardStyle = { 87 | width: "18rem", 88 | height: "12rem", 89 | boxShadow: "rgba(0, 0, 0, 0.24) 0px 2px 3px", 90 | backgroundColor: productAvailable ? "#fff" : "#ccc", 91 | }; 92 | return ( 93 |
109 | 113 | {name} 125 |
135 |
136 |
140 | {name.toUpperCase()} 141 |
142 | 146 | {"~ " + brand} 147 | 148 |
149 |
150 |
151 |
155 | 156 | {price} 157 |
158 |
159 | 170 |
171 | 172 |
173 | ); 174 | }) 175 | )} 176 |
177 | 178 | ); 179 | }; 180 | 181 | export default Home; 182 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/components/AddProduct.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import axios from "axios"; 3 | 4 | const AddProduct = () => { 5 | const [product, setProduct] = useState({ 6 | name: "", 7 | brand: "", 8 | description: "", 9 | price: "", 10 | category: "", 11 | stockQuantity: "", 12 | releaseDate: "", 13 | productAvailable: false, 14 | }); 15 | const [image, setImage] = useState(null); 16 | 17 | const handleInputChange = (e) => { 18 | const { name, value } = e.target; 19 | setProduct({ ...product, [name]: value }); 20 | }; 21 | 22 | const handleImageChange = (e) => { 23 | setImage(e.target.files[0]); 24 | // setProduct({...product, image: e.target.files[0]}) 25 | }; 26 | 27 | const submitHandler = (event) => { 28 | event.preventDefault(); 29 | const formData = new FormData(); 30 | formData.append("imageFile", image); 31 | formData.append( 32 | "product", 33 | new Blob([JSON.stringify(product)], { type: "application/json" }) 34 | ); 35 | 36 | axios 37 | .post("http://localhost:8080/api/product", formData, { 38 | headers: { 39 | "Content-Type": "multipart/form-data", 40 | }, 41 | }) 42 | .then((response) => { 43 | console.log("Product added successfully:", response.data); 44 | alert("Product added successfully"); 45 | }) 46 | .catch((error) => { 47 | console.error("Error adding product:", error); 48 | alert("Error adding product"); 49 | }); 50 | }; 51 | 52 | return ( 53 |
54 |
55 |
56 |
57 | 60 | 68 |
69 |
70 | 73 | 82 |
83 |
84 | 87 | 96 |
97 |
98 | 101 | 110 |
111 | 112 |
113 | 116 | 131 |
132 | 133 |
134 | 137 | 147 |
148 |
149 | 152 | 160 |
161 | {/* setProduct({...product, image: e.target.files[0]})} /> 162 | */} 163 |
164 | 167 | 172 |
173 |
174 |
175 | 182 | setProduct({ ...product, productAvailable: e.target.checked }) 183 | } 184 | /> 185 | 186 |
187 |
188 |
189 | 196 |
197 |
198 |
199 |
200 | ); 201 | }; 202 | 203 | export default AddProduct; 204 | -------------------------------------------------------------------------------- /Ecommerce-Backend/mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM https://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" 82 | if ($env:MAVEN_USER_HOME) { 83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" 84 | } 85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 87 | 88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 91 | exit $? 92 | } 93 | 94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 96 | } 97 | 98 | # prepare tmp dir 99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 102 | trap { 103 | if ($TMP_DOWNLOAD_DIR.Exists) { 104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 106 | } 107 | } 108 | 109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 110 | 111 | # Download and Install Apache Maven 112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 113 | Write-Verbose "Downloading from: $distributionUrl" 114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 115 | 116 | $webclient = New-Object System.Net.WebClient 117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 119 | } 120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 122 | 123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 125 | if ($distributionSha256Sum) { 126 | if ($USE_MVND) { 127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 128 | } 129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 132 | } 133 | } 134 | 135 | # unzip and move 136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null 138 | try { 139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 140 | } catch { 141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 142 | Write-Error "fail to move MAVEN_HOME" 143 | } 144 | } finally { 145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 147 | } 148 | 149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 150 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/components/UpdateProduct.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import axios from "axios"; 4 | 5 | const UpdateProduct = () => { 6 | const { id } = useParams(); 7 | const [product, setProduct] = useState({}); 8 | const [image, setImage] = useState(); 9 | const [updateProduct, setUpdateProduct] = useState({ 10 | id: null, 11 | name: "", 12 | description: "", 13 | brand: "", 14 | price: "", 15 | category: "", 16 | releaseDate: "", 17 | productAvailable: false, 18 | stockQuantity: "", 19 | }); 20 | 21 | useEffect(() => { 22 | const fetchProduct = async () => { 23 | try { 24 | const response = await axios.get( 25 | `http://localhost:8080/api/product/${id}` 26 | ); 27 | 28 | setProduct(response.data); 29 | 30 | const responseImage = await axios.get( 31 | `http://localhost:8080/api/product/${id}/image`, 32 | { responseType: "blob" } 33 | ); 34 | const imageFile = await converUrlToFile(responseImage.data,response.data.imageName) 35 | setImage(imageFile); 36 | setUpdateProduct(response.data); 37 | } catch (error) { 38 | console.error("Error fetching product:", error); 39 | } 40 | }; 41 | 42 | fetchProduct(); 43 | }, [id]); 44 | 45 | useEffect(() => { 46 | console.log("image Updated", image); 47 | }, [image]); 48 | 49 | 50 | 51 | const converUrlToFile = async(blobData, fileName) => { 52 | const file = new File([blobData], fileName, { type: blobData.type }); 53 | return file; 54 | } 55 | 56 | const handleSubmit = async(e) => { 57 | e.preventDefault(); 58 | console.log("images", image) 59 | console.log("productsdfsfsf", updateProduct) 60 | const updatedProduct = new FormData(); 61 | updatedProduct.append("imageFile", image); 62 | updatedProduct.append( 63 | "product", 64 | new Blob([JSON.stringify(updateProduct)], { type: "application/json" }) 65 | ); 66 | 67 | 68 | console.log("formData : ", updatedProduct) 69 | axios 70 | .put(`http://localhost:8080/api/product/${id}`, updatedProduct, { 71 | headers: { 72 | "Content-Type": "multipart/form-data", 73 | }, 74 | }) 75 | .then((response) => { 76 | console.log("Product updated successfully:", updatedProduct); 77 | alert("Product updated successfully!"); 78 | }) 79 | .catch((error) => { 80 | console.error("Error updating product:", error); 81 | console.log("product unsuccessfull update",updateProduct) 82 | alert("Failed to update product. Please try again."); 83 | }); 84 | }; 85 | 86 | 87 | const handleChange = (e) => { 88 | const { name, value } = e.target; 89 | setUpdateProduct({ 90 | ...updateProduct, 91 | [name]: value, 92 | }); 93 | }; 94 | 95 | const handleImageChange = (e) => { 96 | setImage(e.target.files[0]); 97 | }; 98 | 99 | 100 | return ( 101 |
102 |
103 |

Update Product

104 |
105 |
106 | 109 | 117 |
118 |
119 | 122 | 131 |
132 |
133 | 136 | 145 |
146 |
147 | 150 | 159 |
160 |
161 | 164 | 179 |
180 | 181 |
182 | 185 | 194 |
195 |
196 | 199 | {product.imageName} 210 | 218 |
219 |
220 |
221 | 228 | setUpdateProduct({ ...updateProduct, productAvailable: e.target.checked }) 229 | } 230 | /> 231 | 232 |
233 |
234 | 235 |
236 | 239 |
240 |
241 |
242 |
243 | ); 244 | }; 245 | 246 | export default UpdateProduct; -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import Home from "./Home" 3 | import axios from "axios"; 4 | // import { json } from "react-router-dom"; 5 | // import { BiSunFill, BiMoon } from "react-icons/bi"; 6 | 7 | const Navbar = ({ onSelectCategory, onSearch }) => { 8 | const getInitialTheme = () => { 9 | const storedTheme = localStorage.getItem("theme"); 10 | return storedTheme ? storedTheme : "light-theme"; 11 | }; 12 | const [selectedCategory, setSelectedCategory] = useState(""); 13 | const [theme, setTheme] = useState(getInitialTheme()); 14 | const [input, setInput] = useState(""); 15 | const [searchResults, setSearchResults] = useState([]); 16 | const [noResults, setNoResults] = useState(false); 17 | const [searchFocused, setSearchFocused] = useState(false); 18 | const [showSearchResults,setShowSearchResults] = useState(false) 19 | useEffect(() => { 20 | fetchData(); 21 | }, []); 22 | 23 | const fetchData = async (value) => { 24 | try { 25 | const response = await axios.get("http://localhost:8080/api/products"); 26 | setSearchResults(response.data); 27 | console.log(response.data); 28 | } catch (error) { 29 | console.error("Error fetching data:", error); 30 | } 31 | }; 32 | 33 | const handleChange = async (value) => { 34 | setInput(value); 35 | if (value.length >= 1) { 36 | setShowSearchResults(true) 37 | try { 38 | const response = await axios.get( 39 | `http://localhost:8080/api/products/search?keyword=${value}` 40 | ); 41 | setSearchResults(response.data); 42 | setNoResults(response.data.length === 0); 43 | console.log(response.data); 44 | } catch (error) { 45 | console.error("Error searching:", error); 46 | } 47 | } else { 48 | setShowSearchResults(false); 49 | setSearchResults([]); 50 | setNoResults(false); 51 | } 52 | }; 53 | 54 | 55 | // const handleChange = async (value) => { 56 | // setInput(value); 57 | // if (value.length >= 1) { 58 | // setShowSearchResults(true); 59 | // try { 60 | // let response; 61 | // if (!isNaN(value)) { 62 | // // Input is a number, search by ID 63 | // response = await axios.get(`http://localhost:8080/api/products/search?id=${value}`); 64 | // } else { 65 | // // Input is not a number, search by keyword 66 | // response = await axios.get(`http://localhost:8080/api/products/search?keyword=${value}`); 67 | // } 68 | 69 | // const results = response.data; 70 | // setSearchResults(results); 71 | // setNoResults(results.length === 0); 72 | // console.log(results); 73 | // } catch (error) { 74 | // console.error("Error searching:", error.response ? error.response.data : error.message); 75 | // } 76 | // } else { 77 | // setShowSearchResults(false); 78 | // setSearchResults([]); 79 | // setNoResults(false); 80 | // } 81 | // }; 82 | 83 | const handleCategorySelect = (category) => { 84 | setSelectedCategory(category); 85 | onSelectCategory(category); 86 | }; 87 | const toggleTheme = () => { 88 | const newTheme = theme === "dark-theme" ? "light-theme" : "dark-theme"; 89 | setTheme(newTheme); 90 | localStorage.setItem("theme", newTheme); 91 | }; 92 | 93 | useEffect(() => { 94 | document.body.className = theme; 95 | }, [theme]); 96 | 97 | const categories = [ 98 | "Laptop", 99 | "Headphone", 100 | "Mobile", 101 | "Electronics", 102 | "Toys", 103 | "Fashion", 104 | ]; 105 | return ( 106 | <> 107 |
108 | 225 |
226 | 227 | ); 228 | }; 229 | 230 | export default Navbar; 231 | -------------------------------------------------------------------------------- /Ecommerce-Backend/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.3.2 23 | # 24 | # Optional ENV vars 25 | # ----------------- 26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source 27 | # MVNW_REPOURL - repo url base for downloading maven distribution 28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output 30 | # ---------------------------------------------------------------------------- 31 | 32 | set -euf 33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x 34 | 35 | # OS specific support. 36 | native_path() { printf %s\\n "$1"; } 37 | case "$(uname)" in 38 | CYGWIN* | MINGW*) 39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" 40 | native_path() { cygpath --path --windows "$1"; } 41 | ;; 42 | esac 43 | 44 | # set JAVACMD and JAVACCMD 45 | set_java_home() { 46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched 47 | if [ -n "${JAVA_HOME-}" ]; then 48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 49 | # IBM's JDK on AIX uses strange locations for the executables 50 | JAVACMD="$JAVA_HOME/jre/sh/java" 51 | JAVACCMD="$JAVA_HOME/jre/sh/javac" 52 | else 53 | JAVACMD="$JAVA_HOME/bin/java" 54 | JAVACCMD="$JAVA_HOME/bin/javac" 55 | 56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then 57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 59 | return 1 60 | fi 61 | fi 62 | else 63 | JAVACMD="$( 64 | 'set' +e 65 | 'unset' -f command 2>/dev/null 66 | 'command' -v java 67 | )" || : 68 | JAVACCMD="$( 69 | 'set' +e 70 | 'unset' -f command 2>/dev/null 71 | 'command' -v javac 72 | )" || : 73 | 74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then 75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 76 | return 1 77 | fi 78 | fi 79 | } 80 | 81 | # hash string like Java String::hashCode 82 | hash_string() { 83 | str="${1:-}" h=0 84 | while [ -n "$str" ]; do 85 | char="${str%"${str#?}"}" 86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) 87 | str="${str#?}" 88 | done 89 | printf %x\\n $h 90 | } 91 | 92 | verbose() { :; } 93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } 94 | 95 | die() { 96 | printf %s\\n "$1" >&2 97 | exit 1 98 | } 99 | 100 | trim() { 101 | # MWRAPPER-139: 102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. 103 | # Needed for removing poorly interpreted newline sequences when running in more 104 | # exotic environments such as mingw bash on Windows. 105 | printf "%s" "${1}" | tr -d '[:space:]' 106 | } 107 | 108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties 109 | while IFS="=" read -r key value; do 110 | case "${key-}" in 111 | distributionUrl) distributionUrl=$(trim "${value-}") ;; 112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; 113 | esac 114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" 115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" 116 | 117 | case "${distributionUrl##*/}" in 118 | maven-mvnd-*bin.*) 119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ 120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in 121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; 122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; 123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; 124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;; 125 | *) 126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 127 | distributionPlatform=linux-amd64 128 | ;; 129 | esac 130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" 131 | ;; 132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; 133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; 134 | esac 135 | 136 | # apply MVNW_REPOURL and calculate MAVEN_HOME 137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" 139 | distributionUrlName="${distributionUrl##*/}" 140 | distributionUrlNameMain="${distributionUrlName%.*}" 141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}" 142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" 143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" 144 | 145 | exec_maven() { 146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : 147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" 148 | } 149 | 150 | if [ -d "$MAVEN_HOME" ]; then 151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME" 152 | exec_maven "$@" 153 | fi 154 | 155 | case "${distributionUrl-}" in 156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; 157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; 158 | esac 159 | 160 | # prepare tmp dir 161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then 162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } 163 | trap clean HUP INT TERM EXIT 164 | else 165 | die "cannot create temp dir" 166 | fi 167 | 168 | mkdir -p -- "${MAVEN_HOME%/*}" 169 | 170 | # Download and Install Apache Maven 171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 172 | verbose "Downloading from: $distributionUrl" 173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 174 | 175 | # select .zip or .tar.gz 176 | if ! command -v unzip >/dev/null; then 177 | distributionUrl="${distributionUrl%.zip}.tar.gz" 178 | distributionUrlName="${distributionUrl##*/}" 179 | fi 180 | 181 | # verbose opt 182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' 183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v 184 | 185 | # normalize http auth 186 | case "${MVNW_PASSWORD:+has-password}" in 187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;; 188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; 189 | esac 190 | 191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then 192 | verbose "Found wget ... using wget" 193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" 194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then 195 | verbose "Found curl ... using curl" 196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" 197 | elif set_java_home; then 198 | verbose "Falling back to use Java to download" 199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" 200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" 201 | cat >"$javaSource" <<-END 202 | public class Downloader extends java.net.Authenticator 203 | { 204 | protected java.net.PasswordAuthentication getPasswordAuthentication() 205 | { 206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); 207 | } 208 | public static void main( String[] args ) throws Exception 209 | { 210 | setDefault( new Downloader() ); 211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); 212 | } 213 | } 214 | END 215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java 216 | verbose " - Compiling Downloader.java ..." 217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" 218 | verbose " - Running Downloader.java ..." 219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" 220 | fi 221 | 222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 223 | if [ -n "${distributionSha256Sum-}" ]; then 224 | distributionSha256Result=false 225 | if [ "$MVN_CMD" = mvnd.sh ]; then 226 | echo "Checksum validation is not supported for maven-mvnd." >&2 227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 228 | exit 1 229 | elif command -v sha256sum >/dev/null; then 230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then 231 | distributionSha256Result=true 232 | fi 233 | elif command -v shasum >/dev/null; then 234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then 235 | distributionSha256Result=true 236 | fi 237 | else 238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 240 | exit 1 241 | fi 242 | if [ $distributionSha256Result = false ]; then 243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 245 | exit 1 246 | fi 247 | fi 248 | 249 | # unzip and move 250 | if command -v unzip >/dev/null; then 251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" 252 | else 253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" 254 | fi 255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" 256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" 257 | 258 | clean || : 259 | exec_maven "$@" 260 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/components/Cart.jsx: -------------------------------------------------------------------------------- 1 | // import React, { useContext, useState, useEffect } from "react"; 2 | // // import axios from '../axios'; 3 | // import AppContext from "../Context/Context"; 4 | // import axios from "axios"; 5 | // import CheckoutPopup from "./CheckoutPopup"; 6 | // import { Button } from "react-bootstrap"; 7 | // const Cart = () => { 8 | // const { cart, removeFromCart } = useContext(AppContext); 9 | // const [cartItems, setCartItems] = useState([]); 10 | // const [totalPrice, setTotalPrice] = useState(0); 11 | // const [cartImage, setCartImage] =useState([]) 12 | // const [showModal, setShowModal] = useState(false); 13 | 14 | // // useEffect(() => { 15 | // // const fetchImagesAndUpdateCart = async () => { 16 | // // console.log("Cart", cart); 17 | // // const updatedCartItems = await Promise.all( 18 | // // cart.map(async (item) => { 19 | // // console.log("ITEM",item) 20 | // // try { 21 | // // const response = await axios.get( 22 | // // `http://localhost:8080/api/product/${item.id}/image`, 23 | // // { responseType: "blob" } 24 | // // ); 25 | // // const imageFile = await converUrlToFile(response.data,response.data.imageName) 26 | // // setCartImage(imageFile); 27 | // // const imageUrl = URL.createObjectURL(response.data); 28 | // // return { ...item, imageUrl, available: true }; 29 | // // } catch (error) { 30 | // // console.error("Error fetching image:", error); 31 | // // return { ...item, imageUrl: "placeholder-image-url", available: false }; 32 | // // } 33 | // // }) 34 | // // ); 35 | // // const filteredCartItems = updatedCartItems.filter((item) => item.available); 36 | // // setCartItems(updatedCartItems); 37 | 38 | // // }; 39 | 40 | // // if (cart.length) { 41 | // // fetchImagesAndUpdateCart(); 42 | // // } 43 | // // }, [cart]); 44 | 45 | // useEffect(() => { 46 | // const fetchImagesAndUpdateCart = async () => { 47 | // try { 48 | 49 | // const response = await axios.get("http://localhost:8080/api/products"); 50 | // const backendProductIds = response.data.map((product) => product.id); 51 | 52 | // const updatedCartItems = cart.filter((item) => backendProductIds.includes(item.id)); 53 | // const cartItemsWithImages = await Promise.all( 54 | // updatedCartItems.map(async (item) => { 55 | // try { 56 | // const response = await axios.get( 57 | // `http://localhost:8080/api/product/${item.id}/image`, 58 | // { responseType: "blob" } 59 | // ); 60 | // const imageFile = await converUrlToFile(response.data, response.data.imageName); 61 | // setCartImage(imageFile) 62 | // const imageUrl = URL.createObjectURL(response.data); 63 | // return { ...item, imageUrl }; 64 | // } catch (error) { 65 | // console.error("Error fetching image:", error); 66 | // return { ...item, imageUrl: "placeholder-image-url" }; 67 | // } 68 | // }) 69 | // ); 70 | 71 | // setCartItems(cartItemsWithImages); 72 | // } catch (error) { 73 | // console.error("Error fetching product data:", error); 74 | 75 | // } 76 | // }; 77 | 78 | // if (cart.length) { 79 | // fetchImagesAndUpdateCart(); 80 | // } 81 | // }, [cart]); 82 | 83 | 84 | 85 | // useEffect(() => { 86 | // console.log("CartItems", cartItems); 87 | // }, [cartItems]); 88 | // const converUrlToFile = async(blobData, fileName) => { 89 | // const file = new File([blobData], fileName, { type: blobData.type }); 90 | // return file; 91 | // } 92 | // useEffect(() => { 93 | // const total = cartItems.reduce( 94 | // (acc, item) => acc + item.price * item.quantity, 95 | // 0 96 | // ); 97 | // setTotalPrice(total); 98 | // }, [cartItems]); 99 | 100 | 101 | // const handleIncreaseQuantity = (itemId) => { 102 | // const newCartItems = cartItems.map((item) => 103 | // item.id === itemId ? { ...item, quantity: item.quantity + 1 } : item 104 | // ); 105 | // setCartItems(newCartItems); 106 | // }; 107 | // const handleDecreaseQuantity = (itemId) => { 108 | // const newCartItems = cartItems.map((item) => 109 | // item.id === itemId 110 | // ? { ...item, quantity: Math.max(item.quantity - 1, 1) } 111 | // : item 112 | // ); 113 | // setCartItems(newCartItems); 114 | // }; 115 | 116 | // const handleRemoveFromCart = (itemId) => { 117 | // removeFromCart(itemId); 118 | // const newCartItems = cartItems.filter((item) => item.id !== itemId); 119 | // setCartItems(newCartItems); 120 | // }; 121 | 122 | // const handleCheckout = async () => { 123 | // try { 124 | // for (const item of cartItems) { 125 | // const { imageUrl, imageName, imageData, imageType, quantity, ...rest } = item; 126 | // const updatedStockQuantity = item.stockQuantity - item.quantity; 127 | 128 | // const updatedProductData = { ...rest, stockQuantity: updatedStockQuantity }; 129 | // console.log("updated product data", updatedProductData) 130 | 131 | // const cartProduct = new FormData(); 132 | // cartProduct.append("imageFile", cartImage); 133 | // cartProduct.append( 134 | // "product", 135 | // new Blob([JSON.stringify(updatedProductData)], { type: "application/json" }) 136 | // ); 137 | 138 | // await axios 139 | // .put(`http://localhost:8080/api/product/${item.id}`, cartProduct, { 140 | // headers: { 141 | // "Content-Type": "multipart/form-data", 142 | // }, 143 | // }) 144 | // .then((response) => { 145 | // console.log("Product updated successfully:", (cartProduct)); 146 | 147 | // }) 148 | // .catch((error) => { 149 | // console.error("Error updating product:", error); 150 | // }); 151 | // } 152 | // setCartItems([]); 153 | // setShowModal(false); 154 | // } catch (error) { 155 | // console.log("error during checkout", error); 156 | // } 157 | // }; 158 | 159 | // return ( 160 | //
161 | //
162 | //
Shopping Bag
163 | // {cartItems.length === 0 ? ( 164 | //
165 | //

Your cart is empty

166 | //
167 | // ) : ( 168 | // <> 169 | // {cartItems.map((item) => ( 170 | //
  • 171 | //
    176 | //
    177 | //
    178 | // 179 | //
    180 | //
    181 | //
    182 | // {item.name} 188 | //
    189 | //
    190 | // {item.brand} 191 | // {item.name} 192 | //
    193 | 194 | //
    195 | // 203 | // 209 | // 218 | //
    219 | 220 | //
    221 | // ${item.price * item.quantity} 222 | //
    223 | // 229 | //
    230 | //
  • 231 | // ))} 232 | //
    Total: ${totalPrice}
    233 | // 240 | // 241 | // )} 242 | //
    243 | // setShowModal(false)} 246 | // cartItems={cartItems} 247 | // totalPrice={totalPrice} 248 | // handleCheckout={handleCheckout} 249 | // /> 250 | //
    251 | 252 | // ); 253 | // }; 254 | 255 | // export default Cart; 256 | 257 | 258 | 259 | 260 | 261 | import React, { useContext, useState, useEffect } from "react"; 262 | import AppContext from "../Context/Context"; 263 | import axios from "axios"; 264 | import CheckoutPopup from "./CheckoutPopup"; 265 | import { Button } from 'react-bootstrap'; 266 | 267 | const Cart = () => { 268 | const { cart, removeFromCart , clearCart } = useContext(AppContext); 269 | const [cartItems, setCartItems] = useState([]); 270 | const [totalPrice, setTotalPrice] = useState(0); 271 | const [cartImage, setCartImage] = useState([]); 272 | const [showModal, setShowModal] = useState(false); 273 | 274 | useEffect(() => { 275 | const fetchImagesAndUpdateCart = async () => { 276 | console.log("Cart", cart); 277 | try { 278 | const response = await axios.get("http://localhost:8080/api/products"); 279 | const backendProductIds = response.data.map((product) => product.id); 280 | 281 | const updatedCartItems = cart.filter((item) => backendProductIds.includes(item.id)); 282 | const cartItemsWithImages = await Promise.all( 283 | updatedCartItems.map(async (item) => { 284 | try { 285 | const response = await axios.get( 286 | `http://localhost:8080/api/product/${item.id}/image`, 287 | { responseType: "blob" } 288 | ); 289 | const imageFile = await converUrlToFile(response.data, response.data.imageName); 290 | setCartImage(imageFile) 291 | const imageUrl = URL.createObjectURL(response.data); 292 | return { ...item, imageUrl }; 293 | } catch (error) { 294 | console.error("Error fetching image:", error); 295 | return { ...item, imageUrl: "placeholder-image-url" }; 296 | } 297 | }) 298 | ); 299 | console.log("cart",cart) 300 | setCartItems(cartItemsWithImages); 301 | } catch (error) { 302 | console.error("Error fetching product data:", error); 303 | } 304 | }; 305 | 306 | if (cart.length) { 307 | fetchImagesAndUpdateCart(); 308 | } 309 | }, [cart]); 310 | 311 | useEffect(() => { 312 | const total = cartItems.reduce( 313 | (acc, item) => acc + item.price * item.quantity, 314 | 0 315 | ); 316 | setTotalPrice(total); 317 | }, [cartItems]); 318 | 319 | const converUrlToFile = async (blobData, fileName) => { 320 | const file = new File([blobData], fileName, { type: blobData.type }); 321 | return file; 322 | } 323 | 324 | const handleIncreaseQuantity = (itemId) => { 325 | const newCartItems = cartItems.map((item) => { 326 | if (item.id === itemId) { 327 | if (item.quantity < item.stockQuantity) { 328 | return { ...item, quantity: item.quantity + 1 }; 329 | } else { 330 | alert("Cannot add more than available stock"); 331 | } 332 | } 333 | return item; 334 | }); 335 | setCartItems(newCartItems); 336 | }; 337 | 338 | 339 | const handleDecreaseQuantity = (itemId) => { 340 | const newCartItems = cartItems.map((item) => 341 | item.id === itemId 342 | ? { ...item, quantity: Math.max(item.quantity - 1, 1) } 343 | : item 344 | ); 345 | setCartItems(newCartItems); 346 | }; 347 | 348 | const handleRemoveFromCart = (itemId) => { 349 | removeFromCart(itemId); 350 | const newCartItems = cartItems.filter((item) => item.id !== itemId); 351 | setCartItems(newCartItems); 352 | }; 353 | 354 | const handleCheckout = async () => { 355 | try { 356 | for (const item of cartItems) { 357 | const { imageUrl, imageName, imageData, imageType, quantity, ...rest } = item; 358 | const updatedStockQuantity = item.stockQuantity - item.quantity; 359 | 360 | const updatedProductData = { ...rest, stockQuantity: updatedStockQuantity }; 361 | console.log("updated product data", updatedProductData) 362 | 363 | const cartProduct = new FormData(); 364 | cartProduct.append("imageFile", cartImage); 365 | cartProduct.append( 366 | "product", 367 | new Blob([JSON.stringify(updatedProductData)], { type: "application/json" }) 368 | ); 369 | 370 | await axios 371 | .put(`http://localhost:8080/api/product/${item.id}`, cartProduct, { 372 | headers: { 373 | "Content-Type": "multipart/form-data", 374 | }, 375 | }) 376 | .then((response) => { 377 | console.log("Product updated successfully:", (cartProduct)); 378 | }) 379 | .catch((error) => { 380 | console.error("Error updating product:", error); 381 | }); 382 | } 383 | clearCart(); 384 | setCartItems([]); 385 | setShowModal(false); 386 | } catch (error) { 387 | console.log("error during checkout", error); 388 | } 389 | }; 390 | 391 | return ( 392 |
    393 |
    394 |
    Shopping Bag
    395 | {cartItems.length === 0 ? ( 396 |
    397 |

    Your cart is empty

    398 |
    399 | ) : ( 400 | <> 401 | {cartItems.map((item) => ( 402 |
  • 403 |
    408 | 409 |
    410 | {item.name} 415 |
    416 |
    417 | {item.brand} 418 | {item.name} 419 |
    420 | 421 |
    422 | 430 | 436 | 444 |
    445 | 446 |
    447 | ${item.price * item.quantity} 448 |
    449 | 455 |
    456 |
  • 457 | ))} 458 |
    Total: ${totalPrice}
    459 | 466 | 467 | )} 468 |
    469 | setShowModal(false)} 472 | cartItems={cartItems} 473 | totalPrice={totalPrice} 474 | handleCheckout={handleCheckout} 475 | /> 476 |
    477 | 478 | ); 479 | }; 480 | 481 | export default Cart; 482 | -------------------------------------------------------------------------------- /Ecommerce-Frontend/src/App.css: -------------------------------------------------------------------------------- 1 | 2 | /* Navbar Styles */ 3 | 4 | .light-theme{ 5 | --root_background: #1f1f1f; 6 | --body_background: white; 7 | --navbar_background: white; 8 | --navbar_text : black; 9 | /* --body_color : rgb(47, 45, 45); */ 10 | --body_color : white; 11 | --link_color : rgb(0, 0, 0); 12 | --link_hover_color : rgb(35, 35, 35); 13 | --link_visited_color : purple; 14 | --para-clr:black; 15 | --cart_body_color: white; 16 | --quantity-clr: rgb(0, 0, 0); 17 | --button-clr: black; 18 | --card-bg-clr: white; 19 | --btn-bg: var(--navbar_background); 20 | --btn-clr: black; 21 | --category-hvr: rgb(200, 200, 200); 22 | --search_result-bg: rgb(239, 239, 239); 23 | --hr_line_card: black 24 | } 25 | .dark-theme{ 26 | --root_background: white; 27 | --body_background: #1f1f1f; 28 | --navbar_background: black; 29 | --navbar_text : white; 30 | /* --body_color : white; */ 31 | --body_color : black; 32 | --link_color : rgb(255, 255, 255); 33 | --link_hover_color : rgb(137, 137, 137); 34 | --link_visited_color : purple; 35 | --para-clr:white; 36 | --cart_body_color: rgb(36, 35, 35); 37 | --quantity-clr: rgb(255, 255, 255); 38 | --button-clr: white; 39 | --card-bg-clr: #2c2b2b; 40 | --btn-bg: var(--navbar_background); 41 | --btn-clr: white; 42 | --category-hvr: rgb(115, 115, 115); 43 | --search_result-bg: rgb(49, 49, 49); 44 | --hr_line_card: rgb(255, 255, 255) 45 | } 46 | 47 | #root { 48 | max-width: 100%; 49 | max-height: 100%; 50 | margin: 0 auto; 51 | background-color: var(--root_background) !important; 52 | } 53 | 54 | /* <.............................................NAVBAR DROPDOWN......................................> */ 55 | .navbar-brand { 56 | font-size: 1.5rem; 57 | font-weight: bold; 58 | } 59 | 60 | .theme-btn { 61 | background-color: transparent; 62 | border: none; 63 | cursor: pointer; 64 | font-size: 1.5rem; 65 | color: inherit; 66 | border-radius: 50%; 67 | } 68 | 69 | .cart { 70 | position: relative; 71 | } 72 | 73 | /* .navbar-nav .nav-link { 74 | color: black; 75 | } */ 76 | 77 | /* .navbar-nav .nav-link:hover { 78 | color: #007bff; 79 | } */ 80 | 81 | .list-group { 82 | position: absolute; 83 | top: 100%; 84 | left: 0; 85 | width: calc(100% - 1rem); 86 | background-color: var(--body_color); 87 | border: 1px solid #ced4da; 88 | border-radius: 0.25rem; 89 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 90 | z-index: 1000; 91 | } 92 | 93 | .list-group-item { 94 | padding: 0.5rem 1rem; 95 | cursor: pointer; 96 | background-color: var(--search_result-bg) !important; 97 | } 98 | 99 | .list-group-item:hover { 100 | background-color: #f8f9fa; 101 | } 102 | 103 | 104 | .no-results-message { 105 | margin-top: 0.5rem; 106 | color: #dc3545; 107 | display: flex; 108 | justify-content: center; 109 | } 110 | .search-result-link { 111 | text-decoration: none; 112 | color: inherit; 113 | } 114 | 115 | .search-result-link:hover { 116 | text-decoration: underline; 117 | } 118 | 119 | /* <..........................................................................................................> */ 120 | h1{ 121 | color: var(--para-clr); 122 | } 123 | h2{ 124 | color: var(--para-clr); 125 | } 126 | h3{ 127 | color: var(--para-clr); 128 | } 129 | /* h4{ 130 | color: var(--para-clr); 131 | } */ 132 | h5{ 133 | color: var(--para-clr); 134 | } 135 | h6{ 136 | color: var(--para-clr); 137 | } 138 | p{ 139 | color: var(--para-clr); 140 | } 141 | 142 | a { 143 | color: var(--link_color); 144 | } 145 | .theme-btn{ 146 | background-color: var(--btn-bg); 147 | color: var(--btn-clr); 148 | border: 1px solid var(--button-clr); 149 | margin: 10px; 150 | } 151 | .navbar { 152 | position: fixed; 153 | top: 0; 154 | left: 0; 155 | width: 100%; 156 | background-color: var(--navbar_background); 157 | /* color: var(--navbar_text); */ 158 | padding: 10px 0; 159 | 160 | /* z-index: 1000; */ 161 | } 162 | .text-center{ 163 | color: var(--navbar_text) !important; 164 | } 165 | .navbar a,span,li,i,button { 166 | color: var(--navbar_text) !important; 167 | } 168 | .navbar a:hover { 169 | color: var(--link_hover_color); 170 | } 171 | .content { 172 | padding-top: 500px; 173 | } 174 | 175 | .card{ 176 | padding-top: 500px; 177 | width: 40px; 178 | padding: 1rem; 179 | height: 13rem; 180 | border-radius: 5%; 181 | background-color: var(--card-bg-clr) !important; 182 | 183 | 184 | } 185 | .card h5,h3,i{ 186 | color: var(--para-clr) !important; 187 | } 188 | .dropdown-item{ 189 | color: var(--para-clr) !important; 190 | } 191 | 192 | .dropdown-menu{ 193 | background-color: var(--card-bg-clr) !important; 194 | } 195 | .dropdown-item:hover { 196 | color: var(--bs-dropdown-link-hover-color); 197 | background-color: var(--category-hvr) !important; 198 | } 199 | .grid{ 200 | margin: 0 auto; 201 | max-width: 90%; 202 | width: 100%; 203 | height: 100%; 204 | display: grid; 205 | place-items: flex-start; 206 | padding: 3rem; 207 | grid-template-columns: 0.5fr 0.5fr 0.5fr; 208 | grid-template-rows: 1fr 1fr 1fr; 209 | /* grid-column-gap: 2rem; 210 | grid-row-gap: 2rem; */ 211 | background: var(--body_background); 212 | align-items: stretch; 213 | padding-top: 100px; 214 | /* box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; */ 215 | border-radius: .5rem; 216 | 217 | } 218 | .container{ 219 | margin-top: 11%; 220 | max-width: 120rem; 221 | display: grid; 222 | grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)); 223 | grid-template-rows: 1fr 1fr 1fr; 224 | margin: 0 auto; 225 | } 226 | /* grid-template-columns: 1fr 1fr 1fr; 227 | gap: 1.6rem; 228 | padding-top: 100px; 229 | } */ 230 | .card-brand{ 231 | font-size: 1.2rem; 232 | font-weight: 350; 233 | color: #524c4c; 234 | 235 | } 236 | .btn-primary{ 237 | border-radius: 5px; 238 | cursor: pointer; 239 | transition: all .3s ease; 240 | font-size: 1rem; 241 | } 242 | .card-button-container { 243 | position: absolute; 244 | bottom: 10px; 245 | left: 55%; 246 | /* transform: translateX(-50%); */ 247 | width: 100%; 248 | } 249 | 250 | .product-card{ 251 | padding-top: 500px; 252 | width: 40px; 253 | padding: 1rem; 254 | height: 13rem; 255 | border-radius: 5%; 256 | margin: 100px; 257 | } 258 | 259 | /* <---------------------------------------Product CSS--------------------------------------------------------------> */ 260 | 261 | 262 | .containers { 263 | width: 100%; 264 | height: 150vh; 265 | margin: 0 auto; 266 | padding: 15px; 267 | display: flex; 268 | justify-content: center; 269 | align-items: flex-start; 270 | background-color: var(--body_background); 271 | } 272 | .containers h1,h2,h3,h4,h5,h6,p,span{ 273 | color: var(--para-clr); 274 | } 275 | .left-column-img{ 276 | vertical-align: middle; 277 | max-width: 30rem; 278 | max-height: 30rem; 279 | padding-top: 6rem; 280 | margin: 20px; 281 | } 282 | .left-column { 283 | width: 40%; 284 | position: relative; 285 | } 286 | 287 | .right-column { 288 | width: 40%; 289 | margin-top: 60px; 290 | margin-left: 60px; 291 | } 292 | /* Left Column */ 293 | .left-column img { 294 | width: 100%; 295 | position: absolute; 296 | left: 0; 297 | top: 0; 298 | opacity: 0; 299 | transition: all 0.3s ease; 300 | } 301 | 302 | .left-column img.active { 303 | opacity: 1; 304 | } 305 | /* Product Description */ 306 | .product-description { 307 | border-bottom: 1px solid #E1E8EE; 308 | margin-bottom: 10px; 309 | margin-top: 60px; 310 | } 311 | .product-description h5{ 312 | color: var(--para-clr); 313 | } 314 | .product-description span { 315 | font-size: 12px; 316 | color: #358ED7 !important; 317 | letter-spacing: 1px; 318 | text-transform: uppercase; 319 | text-decoration: none; 320 | } 321 | 322 | 323 | .product-description h5 { 324 | font-weight: 400; 325 | font-size: 24px; 326 | /* color: #43484D; */ 327 | letter-spacing: -2px; 328 | } 329 | .product-description p { 330 | font-size: 16px; 331 | font-weight: 400; 332 | /* color: #86939E; */ 333 | line-height: 24px; 334 | } 335 | .release-date{ 336 | font-weight: 300; 337 | 338 | } 339 | /* Cable Configuration */ 340 | 341 | .cable-config a { 342 | color: #358ED7; 343 | text-decoration: none; 344 | font-size: 12px; 345 | position: relative; 346 | margin: 10px 0; 347 | display: inline-block; 348 | } 349 | 350 | .cable-config a:before { 351 | content: "?"; 352 | height: 15px; 353 | width: 15px; 354 | border-radius: 50%; 355 | border: 2px solid rgba(53, 142, 215, 0.5); 356 | display: inline-block; 357 | text-align: center; 358 | line-height: 16px; 359 | opacity: 0.5; 360 | margin-right: 5px; 361 | } 362 | /* Product Price */ 363 | .product-price { 364 | display: grid; 365 | justify-items: start; 366 | color: var(--para-clr); 367 | grid-gap: 10px 4px; 368 | 369 | } 370 | .update-button{ 371 | display: flex; 372 | width: 10rem; 373 | justify-content: space-around; 374 | } 375 | 376 | .product-price span { 377 | font-size: 26px; 378 | font-weight: 400; 379 | /* color: #43474D; */ 380 | margin-right: 20px; 381 | } 382 | 383 | .cart-btn { 384 | display: inline-block; 385 | background-color: #0d6efd; 386 | border-radius: 6px; 387 | font-size: 16px; 388 | color: #FFFFFF; 389 | text-decoration: none; 390 | padding: 12px 30px; 391 | transition: all .5s; 392 | } 393 | .cart-btn:hover { 394 | background-color: #64af3d; 395 | } 396 | .disabled-btn { 397 | background-color: #ccc; /* Grey background */ 398 | color: #666; /* Dark grey text color */ 399 | cursor: not-allowed; /* Show cursor as not-allowed */ 400 | } 401 | 402 | /* Responsive */ 403 | @media (max-width: 940px) { 404 | .container { 405 | flex-direction: column; 406 | margin-top: 60px; 407 | } 408 | 409 | .left-column, 410 | .right-column { 411 | width: 100%; 412 | } 413 | 414 | .left-column img { 415 | width: 300px; 416 | right: 0; 417 | top: -65px; 418 | left: initial; 419 | } 420 | } 421 | 422 | @media (max-width: 535px) { 423 | .left-column img { 424 | width: 220px; 425 | top: -85px; 426 | } 427 | } 428 | /* <....................................Add Product................................................> */ 429 | 430 | 431 | .container{ 432 | background-color: var(--body_background) !important; 433 | color: var(--body_color) !important; 434 | width: 100%; 435 | height: 100vh; 436 | display: flex; 437 | justify-content: center; 438 | align-items: center; 439 | } 440 | .center-container { 441 | position: absolute; 442 | top: 50%; 443 | left: 50%; 444 | color: var(--para-clr); 445 | transform: translate(-50%, -50%); 446 | background-color: var(--body_background) !important; 447 | /* padding-top: 12%; */ 448 | } 449 | 450 | .center-container .image-control { 451 | display: block; 452 | margin: 0 auto; 453 | padding-top: 2rem; 454 | background-color: var(--body_background); 455 | } 456 | 457 | /* <......................................CART....................................................> */ 458 | 459 | .shopping-cart { 460 | width: 70%; 461 | height: auto; 462 | padding: 80px; 463 | margin: 0 auto; 464 | font-family: Arial, Helvetica, sans-serif; 465 | background: var(--cart_body_color); 466 | box-shadow: 1px 2px 3px 2px rgba(0, 0, 0, 0.2); 467 | border-radius: 6px; 468 | display: flex; 469 | flex-direction: column; 470 | /* width: 100vh; 471 | height: 100vh; */ 472 | } 473 | 474 | .shopping-cart h1 { 475 | background-color: #f8f9fa; 476 | } 477 | 478 | .title { 479 | height: 60px; 480 | border-bottom: 1px solid #E1E8EE; 481 | padding: 20px 30px; 482 | color: var(--para-clr); 483 | font-size: 18px; 484 | font-weight: 400; 485 | } 486 | 487 | .empty h4 { 488 | color: var(--para-clr); 489 | } 490 | 491 | .item { 492 | padding: 10px 10px; 493 | width: 800px; 494 | display: flex; 495 | justify-content: space-between; 496 | align-items: center; 497 | /* margin-bottom: 10px; */ 498 | } 499 | 500 | .item:nth-child(3) { 501 | border-top: 1px solid #E1E8EE; 502 | border-bottom: 1px solid #E1E8EE; 503 | } 504 | 505 | .buttons { 506 | position: relative; 507 | display: flex; 508 | /* padding-top: 30px; */ 509 | margin-right: 60px; 510 | } 511 | 512 | .delete-btn, 513 | .like-btn { 514 | display: inline-block; 515 | Cursor: pointer; 516 | } 517 | 518 | .delete-btn { 519 | width: 18px; 520 | height: 17px; 521 | background: url("delete-icn.svg&quot;) no-repeat center; 522 | /* color: var(--body_color); */ 523 | } 524 | 525 | .like-btn { 526 | position: absolute; 527 | top: 9px; 528 | left: 15px; 529 | background: url("twitter-heart.png"); 530 | width: 60px; 531 | height: 60px; 532 | background-size: 2900%; 533 | background-repeat: no-repeat; 534 | } 535 | 536 | .is-active { 537 | animation-name: animate; 538 | animation-duration: 0.8s; 539 | animation-iteration-count: 1; 540 | animation-timing-function: steps(28); 541 | animation-fill-mode: forwards; 542 | } 543 | 544 | @keyframes animate { 545 | 0% { 546 | background-position: left; 547 | } 548 | 50% { 549 | background-position: right; 550 | } 551 | 100% { 552 | background-position: right; 553 | } 554 | } 555 | 556 | .image img { 557 | margin-right: 80px; 558 | height: 100px; 559 | } 560 | 561 | .description { 562 | color: var(--para-clr); 563 | padding-top: 10px; 564 | /* margin-right: 60px; */ 565 | padding-left: 10px; 566 | flex-grow: 1; 567 | } 568 | 569 | .description span { 570 | display: block; 571 | /* padding-left: 20px; */ 572 | font-size: 14px; 573 | color: var(--para-clr); 574 | font-weight: 400; 575 | } 576 | 577 | .description span:first-child { 578 | margin-bottom: 5px; 579 | } 580 | 581 | .description span:last-child { 582 | font-weight: 300; 583 | margin-top: 8px; 584 | color: var(--quantity-clr); 585 | } 586 | 587 | .quantity { 588 | /* padding-top: 20px; */ 589 | /* margin-right: 60px; */ 590 | display: flex; 591 | align-items: center; 592 | color: var(--cart_body_color); 593 | background-color: var(--body_background); 594 | } 595 | 596 | .quantity input { 597 | /* -webkit-appearance: none; */ 598 | border: none; 599 | text-align: center; 600 | width: 32px; 601 | font-size: 16px; 602 | /* color: var(--quantity-clr); */ 603 | font-weight: 400; 604 | } 605 | 606 | .buttons[class*=btn] { 607 | /* width: 30px; 608 | height: 30px; */ 609 | /* background-color: var(--body_color); */ 610 | border-radius: 6px; 611 | border: none; 612 | cursor: pointer; 613 | } 614 | 615 | .minus-btn { 616 | border: none; 617 | color: var(--button-clr); 618 | background-color: var(--cart_body_color); 619 | font-weight: 500; 620 | } 621 | 622 | .plus-btn { 623 | border: none; 624 | color: var(--button-clr); 625 | background-color: var(--cart_body_color); 626 | } 627 | 628 | .remove-btn { 629 | border: none; 630 | color: var(--button-clr); 631 | background-color: var(--cart_body_color); 632 | } 633 | 634 | .buttons:focus, 635 | input:focus { 636 | outline: 0; 637 | } 638 | 639 | .total-price { 640 | width: 83px; 641 | /* padding-top: 27px; */ 642 | text-align: center; 643 | font-size: 16px; 644 | color: var(--quantity-clr); 645 | font-weight: 300; 646 | } 647 | 648 | @media (max-width: 800px) { 649 | .shopping-cart { 650 | width: 100%; 651 | height: auto; 652 | overflow: hidden; 653 | } 654 | 655 | .item { 656 | height: auto; 657 | flex-wrap: wrap; 658 | justify-content: center; 659 | } 660 | 661 | .image img { 662 | width: 50%; 663 | } 664 | 665 | .image, 666 | .quantity, 667 | .description { 668 | width: 100%; 669 | text-align: center; 670 | margin: 6px 0; 671 | } 672 | 673 | .buttons { 674 | margin-right: 20px; 675 | } 676 | } 677 | 678 | .buttons-liked { 679 | padding-left: 10px; 680 | } 681 | 682 | .total { 683 | display: flex; 684 | justify-content: center; 685 | font-weight: 700; 686 | color: var(--quantity-clr); 687 | margin-top: 40px; } 688 | 689 | .cart-items { 690 | list-style: none; 691 | padding: 0; 692 | } 693 | 694 | .cart-item { 695 | display: flex; 696 | align-items: center; 697 | border-bottom: 1px solid #ccc; 698 | /* margin-bottom: 20px; 699 | padding-bottom: 20px; */ 700 | } 701 | 702 | .cart-item-image { 703 | width: 80px; 704 | height: 80px; 705 | margin: 10px; 706 | border-radius: 5px; 707 | object-fit: cover; 708 | } 709 | 710 | .cart-container { 711 | width: 100%; 712 | height: 100%; 713 | background-color: var(--body_background); 714 | } 715 | 716 | .checkout-button { 717 | /* width: 100%; 718 | height: 100%; */ 719 | display: flex; 720 | justify-content: center; 721 | align-items: center; 722 | } 723 | 724 | .checkout-button button { 725 | background-color: var(--button-clr); 726 | } 727 | /* <........................................Checkout Popup...........................................> */ 728 | .checkout-item p{ 729 | color: black; 730 | 731 | } 732 | .checkout-items{ 733 | display: column; 734 | } 735 | .checkoutPopup { 736 | background-color: var(--body_background) !important; 737 | } 738 | 739 | 740 | /* <........................................Update Product.......................................> */ 741 | 742 | .update-product-container { 743 | background-color: var(--body_background); 744 | width: 100%; 745 | min-height: 150vh; 746 | box-sizing: border-box; 747 | } 748 | /* <...............................Button...............................> */ 749 | .buttons { 750 | -webkit-box-sizing: border-box; 751 | -moz-box-sizing: border-box; 752 | box-sizing: border-box; 753 | } 754 | 755 | .buttons { 756 | margin: 10%; 757 | text-align: center; 758 | } 759 | 760 | .btn-hover { 761 | width: 150px; 762 | font-size: 16px; 763 | font-weight: 600; 764 | color: #fff; 765 | cursor: pointer; 766 | margin: 10px; 767 | height: 45px; 768 | text-align:center; 769 | border: none; 770 | background-size: 300% 100%; 771 | 772 | border-radius: 50px; 773 | -moz-transition: all .4s ease-in-out; 774 | -o-transition: all .4s ease-in-out; 775 | -webkit-transition: all .4s ease-in-out; 776 | transition: all .4s ease-in-out; 777 | } 778 | 779 | .btn-hover:hover { 780 | background-position: 100% 0; 781 | -moz-transition: all .4s ease-in-out; 782 | -o-transition: all .4s ease-in-out; 783 | -webkit-transition: all .4s ease-in-out; 784 | transition: all .4s ease-in-out; 785 | } 786 | 787 | .btn-hover:focus { 788 | outline: none; 789 | } 790 | 791 | .btn-hover.color-9 { 792 | background-image: linear-gradient(to right, #25aae1, #4481eb, #04befe, #3f86ed); 793 | box-shadow: 0 3px 5px 0 rgba(65, 132, 234, 0.75); 794 | } 795 | 796 | /* .home-cart-price{ 797 | display: flex; 798 | 799 | align-items: center; 800 | font-weight: 500; 801 | } */ 802 | /* <..........................................Product-Cart..................................> */ 803 | .bt { 804 | width: 100px; 805 | cursor: pointer; 806 | position: relative; 807 | font-family: "Roboto"; 808 | text-transform: uppercase; 809 | color: #503af6; 810 | letter-spacing: 0.5px; 811 | -webkit-user-select: none; 812 | -moz-user-select: none; 813 | -ms-user-select: none; 814 | user-select: none; 815 | outline: none; 816 | text-decoration: none; 817 | text-align: center; 818 | 819 | } 820 | 821 | .more-bt { 822 | border-right: 2px solid #503af6; 823 | border-bottom: 2px solid #503af6; 824 | padding: 17px 29px 15px 31px; 825 | border-color: #503af6; 826 | } 827 | 828 | .more-bt p { 829 | font-size: 14px; 830 | } 831 | 832 | #wrapper.smooth section.smoothy.show { 833 | visibility: visible; 834 | } 835 | 836 | .more-bt:before { 837 | left: 0; 838 | bottom: 0; 839 | height: -webkit-calc(100% - 17px); 840 | height: calc(100% - 17px); 841 | width: 2px; 842 | } 843 | 844 | .more-bt:after, .more-bt:before { 845 | content: " "; 846 | display: block; 847 | background: #503af6; 848 | position: absolute; 849 | -webkit-transition: .5s; 850 | transition: .5s; 851 | z-index: 10; 852 | } 853 | 854 | .more-bt:after { 855 | top: 0; 856 | right: 0; 857 | width: -webkit-calc(100% - 17px); 858 | width: calc(100% - 17px); 859 | height: 2px; 860 | } 861 | 862 | .more-bt:after, .more-bt:before { 863 | content: " "; 864 | display: block; 865 | background: #503af6; 866 | position: absolute; 867 | -webkit-transition: .5s; 868 | transition: .5s; 869 | z-index: 10; 870 | } 871 | 872 | ::selection { 873 | background: #503af6; 874 | color: #FFFFFF; 875 | text-shadow: none; 876 | } 877 | 878 | .more-bt:before { 879 | left: 0; 880 | bottom: 0; 881 | height: -webkit-calc(100% - 17px); 882 | height: calc(100% - 17px); 883 | width: 2px; 884 | } 885 | 886 | .more-bt:after, .more-bt:before { 887 | content: " "; 888 | display: block; 889 | background: #503af6; 890 | position: absolute; 891 | -webkit-transition: .5s; 892 | transition: .5s; 893 | z-index: 10; 894 | } 895 | 896 | .more-bt .fl, .more-bt .sfl { 897 | position: absolute; 898 | right: 0; 899 | height: 100%; 900 | width: 0; 901 | z-index: 2; 902 | background: #503af6; 903 | top: 0; 904 | -webkit-transition: .5s; 905 | transition: .5s; 906 | -webkit-transition-delay: .1s; 907 | transition-delay: .1s; 908 | } 909 | 910 | .more-bt .fl, .more-bt .sfl { 911 | position: absolute; 912 | right: 0; 913 | height: 100%; 914 | width: 0; 915 | z-index: 2; 916 | background: #503af6; 917 | top: 0; 918 | -webkit-transition: .5s; 919 | transition: .5s; 920 | -webkit-transition-delay: .1s; 921 | transition-delay: .1s; 922 | } 923 | 924 | .more-bt .sfl { 925 | z-index: 1; 926 | background: #4431D1; 927 | -webkit-transition: .7s; 928 | transition: .7s; 929 | } 930 | 931 | .more-bt .cross { 932 | position: absolute; 933 | z-index: 15; 934 | width: 18px; 935 | height: 18px; 936 | top: -webkit-calc(50% - 8px); 937 | top: calc(50% - 8px); 938 | left: -webkit-calc(50% - 8px); 939 | left: calc(50% - 8px); 940 | } 941 | 942 | #wrapper.smooth section.smoothy { 943 | visibility: hidden; 944 | } 945 | 946 | .more-bt .cross:before { 947 | width: 100%; 948 | height: 2px; 949 | top: 8px; 950 | left: 0px; 951 | -webkit-transform: translateX(50px) scaleX(0); 952 | -ms-transform: translateX(50px) scaleX(0); 953 | transform: translateX(50px) scaleX(0); 954 | } 955 | 956 | .more-bt .cross:before, .more-bt .cross:after { 957 | content: " "; 958 | background: #fff; 959 | display: block; 960 | position: absolute; 961 | opacity: 0; 962 | -webkit-transition-duration: .3s; 963 | transition-duration: .3s; 964 | -webkit-transition-delay: 0s; 965 | transition-delay: 0s; 966 | -webkit-transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1); 967 | transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1); 968 | } 969 | 970 | .more-bt .cross:after { 971 | width: 2px; 972 | height: 100%; 973 | left: 8px; 974 | top: 0; 975 | -webkit-transform: translateY(20px) scaleY(0); 976 | -ms-transform: translateY(20px) scaleY(0); 977 | transform: translateY(20px) scaleY(0); 978 | -webkit-transition-duration: .4s; 979 | transition-duration: .4s; 980 | } 981 | 982 | .more-bt .cross:before, .more-bt .cross:after { 983 | content: " "; 984 | background: #fff; 985 | display: block; 986 | position: absolute; 987 | opacity: 0; 988 | -webkit-transition-duration: .3s; 989 | transition-duration: .3s; 990 | -webkit-transition-delay: 0s; 991 | transition-delay: 0s; 992 | -webkit-transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1); 993 | transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1); 994 | } 995 | 996 | .more-bt i { 997 | position: absolute; 998 | display: block; 999 | top: 1px; 1000 | left: 1px; 1001 | -webkit-transition: .5s; 1002 | transition: .5s; 1003 | z-index: 10; 1004 | } 1005 | 1006 | .more-bt i:before { 1007 | -webkit-transform: rotate(90deg); 1008 | -ms-transform: rotate(90deg); 1009 | transform: rotate(90deg); 1010 | } 1011 | 1012 | .more-bt i:after, .more-bt i:before { 1013 | content: " "; 1014 | display: block; 1015 | width: 2px; 1016 | height: 20px; 1017 | background: #503af6; 1018 | position: absolute; 1019 | margin: -10px -1px; 1020 | left: 50%; 1021 | top: 50%; 1022 | transition: 0.3s; 1023 | } 1024 | 1025 | .more-bt:hover i:after { 1026 | content: " "; 1027 | display: block; 1028 | width: 2px; 1029 | height: 20px; 1030 | background: #503af6; 1031 | position: absolute; 1032 | margin: 0px -1px; 1033 | left: 50%; 1034 | top: 50%; 1035 | } 1036 | 1037 | .more-bt:hover i:before { 1038 | content: " "; 1039 | display: block; 1040 | width: 2px; 1041 | height: 20px; 1042 | background: #503af6; 1043 | position: absolute; 1044 | margin: -10px 0px -10px 8px; 1045 | left: 50%; 1046 | top: 50%; 1047 | } 1048 | 1049 | .more-bt p { 1050 | -webkit-transition: .5s; 1051 | transition: .5s; 1052 | position: relative; 1053 | z-index: 1; 1054 | } 1055 | 1056 | .more-bt:hover:before, .more-bt:before { 1057 | height: 100%; 1058 | } 1059 | 1060 | .more-bt:before { 1061 | left: 0; 1062 | bottom: 0; 1063 | height: -webkit-calc(100% - 17px); 1064 | height: calc(100% - 17px); 1065 | width: 2px; 1066 | } 1067 | 1068 | .more-bt:hover .fl, .more-bt .fl { 1069 | -webkit-transition: .7s; 1070 | transition: .7s; 1071 | } 1072 | 1073 | .more-bt:hover .fl, .more-bt:hover .sfl, .more-bt .fl, .more-bt .sfl { 1074 | -webkit-transition-delay: 0s; 1075 | transition-delay: 0s; 1076 | width: 100%; 1077 | } 1078 | 1079 | .more-bt .fl, .more-bt .sfl { 1080 | position: absolute; 1081 | right: 0; 1082 | height: 100%; 1083 | width: 0; 1084 | z-index: 2; 1085 | background: #503af6; 1086 | top: 0; 1087 | -webkit-transition: .5s; 1088 | transition: .5s; 1089 | -webkit-transition-delay: .1s; 1090 | transition-delay: .1s; 1091 | } 1092 | 1093 | .more-bt:hover .sfl, .more-bt.sfl { 1094 | -webkit-transition: .5s; 1095 | transition: .5s; 1096 | } 1097 | 1098 | .more-bt:hover .fl, .more-bt:hover .sfl, .more-bt.hvd .fl, .more-bt.hvd .sfl { 1099 | -webkit-transition-delay: 0s; 1100 | transition-delay: 0s; 1101 | width: 100%; 1102 | } 1103 | 1104 | .more-bt .sfl { 1105 | z-index: 1; 1106 | background: #4431D1; 1107 | -webkit-transition: .7s; 1108 | transition: .7s; 1109 | } 1110 | 1111 | .more-bt .cross { 1112 | position: absolute; 1113 | z-index: 15; 1114 | width: 18px; 1115 | height: 18px; 1116 | top: -webkit-calc(50% - 8px); 1117 | top: calc(50% - 8px); 1118 | left: -webkit-calc(50% - 8px); 1119 | left: calc(50% - 8px); 1120 | } 1121 | 1122 | .more-bt:hover .cross:before, .more-bt .cross:before { 1123 | -webkit-transition-duration: .5s; 1124 | transition-duration: .5s; 1125 | } 1126 | 1127 | .more-bt:hover .cross:after, .more-bt:hover .cross:before, .more-bt .cross:after, .more-bt .cross:before { 1128 | -webkit-transform: none; 1129 | -ms-transform: none; 1130 | transform: none; 1131 | opacity: 1; 1132 | -webkit-transition-delay: .2s; 1133 | transition-delay: .2s; 1134 | } 1135 | 1136 | .more-bt .cross:after { 1137 | width: 2px; 1138 | height: 100%; 1139 | left: 8px; 1140 | top: 0; 1141 | -webkit-transform: translateY(20px) scaleY(0); 1142 | -ms-transform: translateY(20px) scaleY(0); 1143 | transform: translateY(20px) scaleY(0); 1144 | -webkit-transition-duration: .4s; 1145 | transition-duration: .4s; 1146 | } 1147 | 1148 | .more-bt .cross:before, .more-bt .cross:after { 1149 | content: " "; 1150 | background: #fff; 1151 | display: block; 1152 | position: absolute; 1153 | opacity: 0; 1154 | -webkit-transition-duration: .3s; 1155 | transition-duration: .3s; 1156 | -webkit-transition-delay: 0s; 1157 | transition-delay: 0s; 1158 | -webkit-transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1); 1159 | transition-timing-function: cubic-bezier(0.86, 0, 0.07, 1); 1160 | } 1161 | 1162 | .more-bt:hover .cross:after, .more-bt:hover .cross:before, .more-bt.hvd .cross:after, .more-bt.hvd .cross:before { 1163 | -webkit-transform: none; 1164 | -ms-transform: none; 1165 | transform: none; 1166 | opacity: 1; 1167 | -webkit-transition-delay: .2s; 1168 | transition-delay: .2s; 1169 | } 1170 | 1171 | .more-bt:hover .cross:after, .more-bt.hvd .cross:after { 1172 | -webkit-transition-duration: .6s; 1173 | transition-duration: .6s; 1174 | } 1175 | /* <................................HR line..................> */ 1176 | 1177 | .hr-line { 1178 | width: 100%; 1179 | height: 1px; 1180 | background: var(--hr_line_card) 1181 | } 1182 | --------------------------------------------------------------------------------