├── .gitattributes
├── src
├── axois.jsx
├── main.jsx
├── Context
│ └── Context.jsx
├── App.jsx
├── index.css
├── components
│ ├── Home.jsx
│ ├── Navbar.jsx
│ ├── Product.jsx
│ ├── AddProduct.jsx
│ └── UpdateProduct.jsx
├── assets
│ └── react.svg
└── App.css
├── vite.config.js
├── .gitignore
├── README.md
├── .eslintrc.cjs
├── index.html
├── package.json
└── public
└── vite.svg
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/src/axois.jsx:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const API = axios.create({
4 | baseURL: "http://localhost:8080/api",
5 | });
6 |
7 | export default API;
8 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/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 { AppProvider } from './Context/Context.jsx'
6 |
7 | ReactDOM.createRoot(document.getElementById('root')).render(
8 |
9 |
10 |
11 |
12 | ,
13 | )
14 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:react/recommended',
7 | 'plugin:react/jsx-runtime',
8 | 'plugin:react-hooks/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12 | settings: { react: { version: '18.2' } },
13 | plugins: ['react-refresh'],
14 | rules: {
15 | 'react/jsx-no-target-blank': 'off',
16 | 'react-refresh/only-export-components': [
17 | 'warn',
18 | { allowConstantExport: true },
19 | ],
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Context/Context.jsx:
--------------------------------------------------------------------------------
1 | import axios from "../axois";
2 | import { useState, useEffect, createContext } from "react";
3 |
4 | const AppContext = createContext();
5 |
6 | export const AppProvider = ({ children }) => {
7 | const [data, setData] = useState([]);
8 | const [isError, setIsError] = useState("");
9 |
10 | const getApiData = async () => {
11 | try {
12 | const response = await axios.get("/products");
13 | setData(response.data);
14 | } catch (error) {
15 | setIsError(error.message);
16 | }
17 | };
18 |
19 | useEffect(() => {
20 | getApiData();
21 | }, []);
22 |
23 | return (
24 |
25 | {children}
26 |
27 | );
28 | };
29 |
30 | export default AppContext;
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "telusko-ecommerce-part-3",
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 | "axios": "^1.6.8",
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0",
16 | "react-router-dom": "^6.23.0"
17 | },
18 | "devDependencies": {
19 | "@types/react": "^18.2.66",
20 | "@types/react-dom": "^18.2.22",
21 | "@vitejs/plugin-react": "^4.2.1",
22 | "eslint": "^8.57.0",
23 | "eslint-plugin-react": "^7.34.1",
24 | "eslint-plugin-react-hooks": "^4.6.0",
25 | "eslint-plugin-react-refresh": "^0.4.6",
26 | "vite": "^5.2.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import React from "react";
3 | import Home from "./components/Home";
4 | import Navbar from "./components/Navbar";
5 | import AddProduct from "./components/AddProduct";
6 | import Product from "./components/Product";
7 | import { BrowserRouter, Routes, Route } from "react-router-dom";
8 | import { AppProvider } from "./Context/Context";
9 | import UpdateProduct from "./components/UpdateProduct";
10 | function App() {
11 | return (
12 |
13 |
14 |
15 |
16 | } />
17 | } />
18 | } />
19 | } />
20 | } />
21 |
22 |
23 |
24 | );
25 | }
26 |
27 | export default App;
28 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | /* :root {
2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | color-scheme: light dark;
7 | color: rgba(255, 255, 255, 0.87);
8 | background-color: #242424;
9 |
10 | font-synthesis: none;
11 | text-rendering: optimizeLegibility;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | }
15 |
16 | a {
17 | font-weight: 500;
18 | color: #646cff;
19 | text-decoration: inherit;
20 | }
21 | a:hover {
22 | color: #535bf2;
23 | }
24 |
25 | body {
26 | margin: 0;
27 | display: flex;
28 | place-items: center;
29 | min-width: 320px;
30 | min-height: 100vh;
31 | }
32 |
33 | h1 {
34 | font-size: 3.2em;
35 | line-height: 1.1;
36 | }
37 |
38 | button {
39 | border-radius: 8px;
40 | border: 1px solid transparent;
41 | padding: 0.6em 1.2em;
42 | font-size: 1em;
43 | font-weight: 500;
44 | font-family: inherit;
45 | background-color: #1a1a1a;
46 | cursor: pointer;
47 | transition: border-color 0.25s;
48 | }
49 | button:hover {
50 | border-color: #646cff;
51 | }
52 | button:focus,
53 | button:focus-visible {
54 | outline: 4px auto -webkit-focus-ring-color;
55 | }
56 |
57 | @media (prefers-color-scheme: light) {
58 | :root {
59 | color: #213547;
60 | background-color: #ffffff;
61 | }
62 | a:hover {
63 | color: #747bff;
64 | }
65 | button {
66 | background-color: #f9f9f9;
67 | }
68 | } */
69 |
--------------------------------------------------------------------------------
/src/components/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { Link } from "react-router-dom";
3 | import AppContext from "../Context/Context";
4 |
5 | const Home = () => {
6 | const { data, isError } = useContext(AppContext);
7 | console.log(data);
8 | if (isError) {
9 | return Loading...
;
10 | }
11 |
12 |
13 | return (
14 | <>
15 | {isError && {isError}
}
16 |
17 |
18 |
19 | {data&&data.map((curProduct) => {
20 | const { id, brand, name, price, productAvailable } = curProduct;
21 | const cardStyle = {
22 | width: "18rem",
23 | height: "12rem",
24 | boxShadow: "rgba(0, 0, 0, 0.24) 0px 2px 3px",
25 | backgroundColor: productAvailable ? "#fff" : "#ccc",
26 | };
27 | return (
28 |
37 |
38 |
42 |
43 | {curProduct.name.toUpperCase()}
44 |
45 |
53 | {"~ " + curProduct.brand}
54 |
55 | {"$" + curProduct.price}
56 |
57 |
58 |
67 |
68 |
69 |
70 | );
71 | })}
72 |
73 | >
74 | );
75 | };
76 |
77 | export default Home;
78 |
--------------------------------------------------------------------------------
/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React,{useEffect} from "react";
2 |
3 |
4 | const Navbar = () => {
5 |
6 |
7 | return (
8 | <>
9 |
90 | >
91 | );
92 | };
93 |
94 | export default Navbar;
95 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 "../axois";
6 | import UpdateProduct from "./UpdateProduct";
7 | const Product = () => {
8 | const { id } = useParams();
9 | const { data } = useContext(AppContext);
10 | const [product, setProduct] = useState(null);
11 | const [imageUrl, setImageUrl] = useState("");
12 | const navigate = useNavigate();
13 |
14 | useEffect(() => {
15 | const fetchProduct = async () => {
16 | try {
17 | const response = await axios.get(
18 | `http://localhost:8080/api/product/${id}`
19 | );
20 | setProduct(response.data);
21 | if (response.data.imageName) {
22 | fetchImage();
23 | console.log(response.data.imageName);
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 | console.log(response.data);
37 | };
38 |
39 | fetchProduct();
40 | }, [id]);
41 |
42 | console.log("URL Parameter ID:", id);
43 | // console.log("Product Data:", data);
44 |
45 |
46 |
47 | const deleteProduct = () => {
48 | try {
49 | axios.delete(`http://localhost:8080/api/product/${id}`);
50 | navigate("/"); // Redirect to a different route after successful deletion
51 | console.log("Product deleted successfully");
52 | alert("Product deleted successfully");
53 | } catch (error) {
54 | console.error("Error deleting product:", error);
55 | if (error.response) {
56 | // Error if i am getting it from backend
57 | console.error("Backend error:", error.response.data);
58 | alert("Failed to delete product. Backend error.");
59 | } else if (error.request) {
60 | // error if network errors
61 | console.error("Network error:", error.request);
62 | alert("Failed to delete product. Network error.");
63 | } else {
64 | // error if Handle other errors
65 | console.error("Other error:", error.message);
66 | alert("Failed to delete product. Please try again.");
67 | }
68 | }
69 | };
70 |
71 | const handleEditClick = () => {
72 | navigate(`/product/update/${id}`);
73 | };
74 |
75 | if (!product) {
76 | return (
77 |
78 | Loading...
79 |
80 | );
81 | }
82 | return (
83 | <>
84 |
85 | {/*
*/}
86 |

91 |
92 | {/*
*/}
93 |
94 |
95 |
{product.category}
96 |
{product.name}
97 |
{product.brand}
98 |
{product.description}
99 |
100 |
101 |
102 |
{"$" + product.price}
103 |
104 | Add to cart
105 |
106 |
107 | Stock Available :{" "}
108 |
109 | {product.stockQuantity}
110 |
111 |
112 |
113 |
Product listed on:
114 |
{new Date(product.releaseDate).toLocaleDateString()}
115 |
116 |
117 |
118 |
121 | {/* */}
122 |
129 |
130 |
131 |
132 | >
133 | );
134 | };
135 |
136 | export default Product;
137 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | /* #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | /* text-align: center;
6 | }
7 | /* Navbar Styles */
8 | .navbar {
9 | position: fixed;
10 | top: 0;
11 | left: 0;
12 | width: 100%;
13 | background-color: #333;
14 | color: #fff;
15 | padding: 10px 0;
16 | z-index: 1000;
17 | }
18 |
19 | .content {
20 | padding-top: 500px;
21 | }
22 |
23 | .card{
24 | padding-top: 500px;
25 | width: 40px;
26 | padding: 1rem;
27 | height: 13rem;
28 | border-radius: 5%;
29 |
30 | }
31 |
32 |
33 | .grid{
34 | margin: 0 auto;
35 | max-width: 90%;
36 | display: grid;
37 | place-items: center;
38 | padding: 3rem;
39 | grid-template-columns: 0.5fr 0.5fr 0.5fr;
40 | grid-template-rows: 1fr 1fr 1fr;
41 | grid-column-gap: 2rem;
42 | grid-row-gap: 2rem;
43 | background:#fafafa;
44 | align-items: stretch;
45 | padding-top: 100px;
46 | /* box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; */
47 | border-radius: .5rem;
48 | }
49 | .container{
50 | max-width: 120rem;
51 | display: grid;
52 | grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
53 | grid-template-rows: 1fr 1fr 1fr;
54 | margin: 0 auto;
55 | }
56 | /* grid-template-columns: 1fr 1fr 1fr;
57 | gap: 1.6rem;
58 | padding-top: 100px;
59 | } */
60 | .card-brand{
61 | font-size: 1.2rem;
62 | font-weight: 350;
63 | color: #524c4c;
64 |
65 | }
66 | .btn-primary{
67 | border-radius: 5px;
68 | cursor: pointer;
69 | transition: all .3s ease;
70 | font-size: 1rem;
71 | }
72 | .card-button-container {
73 | position: absolute;
74 | bottom: 10px;
75 | left: 55%;
76 | /* transform: translateX(-50%); */
77 | width: 100%;
78 | }
79 |
80 | .product-card{
81 | padding-top: 500px;
82 | width: 40px;
83 | padding: 1rem;
84 | height: 13rem;
85 | border-radius: 5%;
86 | margin: 100px;
87 | }
88 |
89 | /* <---------------------------------------Product CSS--------------------------------------------------------------> */
90 |
91 |
92 | .containers {
93 | max-width: 1200px;
94 | margin: 0 auto;
95 | padding: 15px;
96 | display: flex;
97 | }
98 | .left-column-img{
99 | vertical-align: middle;
100 | width: 40rem;
101 | padding-top: 6rem;
102 | margin: 20px;
103 | }
104 | .left-column {
105 | width: 40%;
106 | position: relative;
107 | }
108 |
109 | .right-column {
110 | width: 60%;
111 | margin-top: 60px;
112 | }
113 | /* Left Column */
114 | .left-column img {
115 | width: 100%;
116 | position: absolute;
117 | left: 0;
118 | top: 0;
119 | opacity: 0;
120 | transition: all 0.3s ease;
121 | }
122 |
123 | .left-column img.active {
124 | opacity: 1;
125 | }
126 | /* Product Description */
127 | .product-description {
128 | border-bottom: 1px solid #E1E8EE;
129 | margin-bottom: 20px;
130 | margin-top: 60px;
131 | }
132 | .product-description span {
133 | font-size: 12px;
134 | color: #358ED7;
135 | letter-spacing: 1px;
136 | text-transform: uppercase;
137 | text-decoration: none;
138 | }
139 | .product-description h1 {
140 | font-weight: 300;
141 | font-size: 52px;
142 | color: #43484D;
143 | letter-spacing: -2px;
144 | }
145 |
146 | .product-description h5 {
147 | font-weight: 400;
148 | font-size: 24px;
149 | color: #43484D;
150 | letter-spacing: -2px;
151 | }
152 | .product-description p {
153 | font-size: 16px;
154 | font-weight: 400;
155 | color: #86939E;
156 | line-height: 24px;
157 | }
158 | .release-date{
159 | font-weight: 300;
160 |
161 | }
162 | /* Cable Configuration */
163 |
164 | .cable-config a {
165 | color: #358ED7;
166 | text-decoration: none;
167 | font-size: 12px;
168 | position: relative;
169 | margin: 10px 0;
170 | display: inline-block;
171 | }
172 |
173 | .cable-config a:before {
174 | content: "?";
175 | height: 15px;
176 | width: 15px;
177 | border-radius: 50%;
178 | border: 2px solid rgba(53, 142, 215, 0.5);
179 | display: inline-block;
180 | text-align: center;
181 | line-height: 16px;
182 | opacity: 0.5;
183 | margin-right: 5px;
184 | }
185 | /* Product Price */
186 | .product-price {
187 | /* display: flex;
188 | align-items: center;
189 | flex-direction: column;
190 | align-items: flex-start; */
191 | display: grid;
192 | justify-items: start;
193 | /* grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); */
194 | grid-gap: 10px 4px;
195 |
196 | }
197 | .update-button{
198 | display: flex;
199 | width: 10rem;
200 | justify-content: space-around;
201 | }
202 |
203 | .product-price span {
204 | font-size: 26px;
205 | font-weight: 400;
206 | color: #43474D;
207 | margin-right: 20px;
208 | }
209 |
210 | .cart-btn {
211 | display: inline-block;
212 | background-color: #0d6efd;
213 | border-radius: 6px;
214 | font-size: 16px;
215 | color: #FFFFFF;
216 | text-decoration: none;
217 | padding: 12px 30px;
218 | transition: all .5s;
219 | }
220 | .cart-btn:hover {
221 | background-color: #64af3d;
222 | }
223 | /* Responsive */
224 | @media (max-width: 940px) {
225 | .container {
226 | flex-direction: column;
227 | margin-top: 60px;
228 | }
229 |
230 | .left-column,
231 | .right-column {
232 | width: 100%;
233 | }
234 |
235 | .left-column img {
236 | width: 300px;
237 | right: 0;
238 | top: -65px;
239 | left: initial;
240 | }
241 | }
242 |
243 | @media (max-width: 535px) {
244 | .left-column img {
245 | width: 220px;
246 | top: -85px;
247 | }
248 | }
249 | /* <....................................Add Product................................................> */
250 |
251 | .center-container {
252 | position: absolute;
253 | top: 50%;
254 | left: 50%;
255 | transform: translate(-50%, -50%);
256 | }
257 |
258 | .center-container .image-control {
259 | display: block;
260 | margin: 0 auto;
261 | padding-top: 2rem;
262 | }
--------------------------------------------------------------------------------
/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 |
191 | );
192 | };
193 |
194 | export default AddProduct;
195 |
--------------------------------------------------------------------------------
/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(null);
9 | const [updateProduct, setUpdateProduct] = useState({
10 | name: "",
11 | brand: "",
12 | description: "",
13 | price: "",
14 | category: "",
15 | stockQuantity: "",
16 | releaseDate: "",
17 | productAvailable: false,
18 | });
19 |
20 | useEffect(() => {
21 | const fetchProduct = async() => {
22 | try {
23 | const response = await axios.get(
24 | `http://localhost:8080/api/product/${id}`
25 | );
26 | setProduct(response.data);
27 | // console.log(setProduct);
28 | setUpdateProduct(response.data);
29 | } catch (error) {
30 | console.error("Error fetching product:", error);
31 | }
32 | };
33 |
34 | fetchProduct();
35 | }, [id]);
36 |
37 |
38 | const handleSubmit = (e) => {
39 | e.preventDefault();
40 | const updatedProduct = new FormData();
41 | updatedProduct.append("imageFile", image);
42 | updatedProduct.append(
43 | "product",
44 | new Blob([JSON.stringify(updateProduct)], { type: "application/json" })
45 | );
46 |
47 | axios.put(`http://localhost:8080/api/product/${id}`,updatedProduct, {
48 | headers: {
49 | "Content-Type": "multipart/form-data",
50 | },
51 | })
52 | .then((response) => {
53 | console.log("Product updated successfully:", response.data);
54 | alert("Product updated successfully!");
55 | })
56 | .catch((error) => {
57 | console.error("Error updating product:", error);
58 | alert("Failed to update product. Please try again."); // Informative error message
59 | });
60 | };
61 |
62 |
63 | const formatDate = (dateString) => {
64 | const date = new Date(dateString);
65 | const year = date.getFullYear();
66 | const month = String(date.getMonth() + 1).padStart(2, "0"); // Adding 1 to month since January is 0
67 | const day = String(date.getDate()).padStart(2, "0");
68 | return `${year}-${month}-${day}`;
69 | };
70 | const handleChange = (e) => {
71 | const{name,value} =e.target;
72 | setUpdateProduct({
73 | ...updateProduct,
74 | [name]: value
75 | });
76 | };
77 | const handleImageChange = (e) => {
78 | setImage(e.target.files[0]);
79 | };
80 |
81 | return (
82 |
83 |
Update Product
84 |
214 |
215 | );
216 | };
217 |
218 | export default UpdateProduct;
219 |
--------------------------------------------------------------------------------