├── .gitignore ├── LICENSE ├── README.md ├── client ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public │ ├── favicon.png │ └── vite.svg ├── src │ ├── App.jsx │ ├── App.scss │ ├── assets │ │ ├── about.jpg │ │ ├── contact-bg.jpg │ │ ├── contact.jpg │ │ ├── hero.jpg │ │ ├── logo.png │ │ ├── pnf.jpg │ │ ├── pnf.png │ │ ├── policy.jpg │ │ ├── preview.png │ │ └── react.svg │ ├── components │ │ ├── Form │ │ │ ├── CategoryForm.jsx │ │ │ ├── CategoryForm.scss │ │ │ ├── SearchInput.jsx │ │ │ └── SearchInput.scss │ │ ├── Layout │ │ │ ├── AdminMenu │ │ │ │ ├── AdminMenu.jsx │ │ │ │ └── AdminMenu.scss │ │ │ ├── Footer │ │ │ │ ├── Footer.jsx │ │ │ │ └── Footer.scss │ │ │ ├── Header │ │ │ │ ├── Header.jsx │ │ │ │ └── Header.scss │ │ │ ├── Layout.jsx │ │ │ └── UserMenu │ │ │ │ ├── UserMenu.jsx │ │ │ │ └── UserMenu.scss │ │ ├── Prices │ │ │ └── Prices.jsx │ │ ├── Routes │ │ │ ├── AdminRoute.jsx │ │ │ └── Private.jsx │ │ └── Spinner │ │ │ ├── Spinner.jsx │ │ │ └── Spinner.scss │ ├── context │ │ ├── auth.jsx │ │ ├── cart.jsx │ │ └── search.jsx │ ├── hooks │ │ └── useCategory.jsx │ ├── index.scss │ ├── main.jsx │ └── pages │ │ ├── About │ │ ├── About.jsx │ │ └── About.scss │ │ ├── AdminDashboard │ │ ├── AdminDashboard.jsx │ │ └── AdminDashboard.scss │ │ ├── AdminOrders │ │ ├── AdminOrders.jsx │ │ └── AdminOrders.scss │ │ ├── Cart │ │ ├── CartPage.jsx │ │ └── CartPage.scss │ │ ├── Categories │ │ ├── Categories.jsx │ │ └── Categories.scss │ │ ├── CategoryProduct │ │ ├── CategoryProduct.jsx │ │ └── CategoryProduct.scss │ │ ├── Contact │ │ ├── Contact.jsx │ │ └── Contact.scss │ │ ├── CreateCategory │ │ ├── CreateCategory.jsx │ │ └── CreateCategory.scss │ │ ├── CreateProduct │ │ ├── CreateProduct.jsx │ │ └── CreateProduct.scss │ │ ├── Dashboard │ │ ├── Dashboard.jsx │ │ └── Dashboard.scss │ │ ├── ForgotPassword │ │ ├── ForgotPassword.jsx │ │ └── ForgotPassword.scss │ │ ├── Home │ │ ├── Home.jsx │ │ └── Home.scss │ │ ├── Login │ │ ├── Login.jsx │ │ └── Login.scss │ │ ├── Orders │ │ ├── Orders.jsx │ │ └── Orders.scss │ │ ├── PageNotFound │ │ ├── PageNotFound.jsx │ │ └── PageNotFound.scss │ │ ├── Policy │ │ ├── Policy.jsx │ │ └── Policy.scss │ │ ├── ProductDetails.jsx │ │ ├── ProductDetails.jsx │ │ └── ProductDetails.scss │ │ ├── Products │ │ ├── Products.jsx │ │ └── Products.scss │ │ ├── Profile │ │ ├── Profile.jsx │ │ └── Profile.scss │ │ ├── Register │ │ ├── Register.jsx │ │ └── Register.scss │ │ ├── Search │ │ ├── Search.jsx │ │ └── Search.scss │ │ ├── UpdateProduct │ │ ├── UpdateProduct.jsx │ │ └── UpdateProduct.scss │ │ └── Users │ │ ├── Users.jsx │ │ └── Users.scss └── vite.config.js ├── config └── db.js ├── controllers ├── authController.js ├── categoryController.js └── productController.js ├── helpers └── authHelper.js ├── middlewares └── authMiddleware.js ├── models ├── categoryModel.js ├── orderModel.js ├── productModel.js └── userModel.js ├── package-lock.json ├── package.json ├── routes ├── authRoute.js ├── categoryRoutes.js └── productRoutes.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore node_modules directory 2 | node_modules/ 3 | 4 | # Ignore .env file 5 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2023] [Muhammad Usama] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minimal E-Commerce Store (MERN Stack) 2 | 3 | ![Project Preview](./client/src/assets/preview.png) 4 | 5 | Welcome to our Minimal E-Commerce Store built using the MERN (MongoDB, Express, React, Node.js) stack. This application provides a user-friendly and efficient platform for browsing, searching, and purchasing products. It includes features such as user authentication, product categorization, shopping cart management, secure payment processing, and more. 6 | 7 | ## Features 8 | 9 | - User Registration and Authentication 10 | - Browse Products by Categories 11 | - Product Details and Descriptions 12 | - Add and Manage Products in the Shopping Cart 13 | - Secure Payment Processing through Braintree 14 | - User Profile Management 15 | - Admin Dashboard for Product and Category Management 16 | 17 | ## Deployment 18 | 19 | The application has been successfully deployed and can be accessed at [Live Demo](https://drab-gold-narwhal-gown.cyclic.cloud/). 20 | 21 | ## Technologies Used 22 | 23 | - Frontend: React, Vite 24 | - Backend: Node.js, Express.js 25 | - Database: MongoDB 26 | - Payment Integration: Braintree 27 | - Styling: SCSS 28 | - State Management: Context API 29 | - Routing: React Router 30 | - Authentication: JWT (JSON Web Tokens) 31 | - Deployment: Cyclic 32 | 33 | ## Main Repo 34 | 35 | ``` 36 | git clone https://github.com/alphadev97/ecommerce-mern-project 37 | ``` 38 | 39 | ## Deployed Repo 40 | 41 | ``` 42 | git clone https://github.com/alphadev97/production-ecommerce-mern.git 43 | ``` 44 | 45 | ## .env Variables 46 | 47 | ``` 48 | PORT = 49 | DEV_MODE = 50 | MONGO_URL = 51 | JWT_SECRET = 52 | BRAINTREE_MERCHANT_ID = 53 | BRAINTREE_PUBLIC_KEY = 54 | BRAINTREE_PRIVATE_KEY = 55 | ``` 56 | 57 | ## Contributing 58 | 59 | Contributions are welcome! Please create a pull request or open an issue if you encounter any bugs or have suggestions for improvements. 60 | 61 | ## License 62 | 63 | This project is licensed under the [MIT License](./LICENSE). 64 | -------------------------------------------------------------------------------- /client/.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-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /client/.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 | -------------------------------------------------------------------------------- /client/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 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Alpha97 - ECommerce 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "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 | "antd": "^5.8.2", 14 | "axios": "^1.4.0", 15 | "braintree-web-drop-in-react": "^1.2.1", 16 | "moment": "^2.29.4", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "react-helmet": "^6.1.0", 20 | "react-hot-toast": "^2.4.1", 21 | "react-icons": "^4.10.1", 22 | "react-router-dom": "^6.14.2", 23 | "react-toastify": "^9.1.3", 24 | "sass": "^1.64.1" 25 | }, 26 | "devDependencies": { 27 | "@types/react": "^18.2.15", 28 | "@types/react-dom": "^18.2.7", 29 | "@vitejs/plugin-react-swc": "^3.3.2", 30 | "eslint": "^8.45.0", 31 | "eslint-plugin-react": "^7.32.2", 32 | "eslint-plugin-react-hooks": "^4.6.0", 33 | "eslint-plugin-react-refresh": "^0.4.3", 34 | "vite": "^4.4.9" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/public/favicon.png -------------------------------------------------------------------------------- /client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Routes, Route } from "react-router-dom"; 2 | import Home from "./pages/Home/Home"; 3 | import About from "./pages/About/About"; 4 | import Contact from "./pages/Contact/Contact"; 5 | import Policy from "./pages/Policy/Policy"; 6 | import PageNotFound from "./pages/PageNotFound/PageNotFound"; 7 | import Register from "./pages/Register/Register"; 8 | import Login from "./pages/Login/Login"; 9 | import Dashboard from "./pages/Dashboard/Dashboard"; 10 | import { PrivateRoute } from "./components/Routes/Private"; 11 | import ForgotPassword from "./pages/ForgotPassword/ForgotPassword"; 12 | import { AdminRoute } from "./components/Routes/AdminRoute"; 13 | import AdminDashboard from "./pages/AdminDashboard/AdminDashboard"; 14 | import CreateCategory from "./pages/CreateCategory/CreateCategory"; 15 | import CreateProduct from "./pages/CreateProduct/CreateProduct"; 16 | import Users from "./pages/Users/Users"; 17 | import Orders from "./pages/Orders/Orders"; 18 | import Profile from "./pages/Profile/Profile"; 19 | import Products from "./pages/Products/Products"; 20 | import UpdateProduct from "./pages/UpdateProduct/UpdateProduct"; 21 | import Search from "./pages/Search/Search"; 22 | import ProductDetails from "./pages/ProductDetails.jsx/ProductDetails"; 23 | import Categories from "./pages/Categories/Categories"; 24 | import CategoryProduct from "./pages/CategoryProduct/CategoryProduct"; 25 | import CartPage from "./pages/Cart/CartPage"; 26 | import AdminOrders from "./pages/AdminOrders/AdminOrders"; 27 | 28 | function App() { 29 | return ( 30 | <> 31 | 32 | } /> 33 | } /> 34 | } /> 35 | } /> 36 | } /> 37 | } /> 38 | }> 39 | } /> 40 | } /> 41 | } /> 42 | 43 | 44 | }> 45 | } /> 46 | } /> 47 | } /> 48 | } /> 49 | } /> 50 | } /> 51 | } /> 52 | 53 | 54 | } /> 55 | } /> 56 | } /> 57 | } /> 58 | } /> 59 | } /> 60 | } /> 61 | 62 | 63 | ); 64 | } 65 | 66 | export default App; 67 | -------------------------------------------------------------------------------- /client/src/App.scss: -------------------------------------------------------------------------------- 1 | // Variables 2 | 3 | // Colors 4 | $polic-blue: #173f8c; 5 | $white: rgb(240, 240, 240); 6 | $green: rgb(61, 209, 2); 7 | $green-dark: rgb(20, 66, 2); 8 | $gargo-gas: #ffbc47; 9 | $orange: #f45050; 10 | 11 | // Fonts 12 | $font-assistant: "Assistant", sans-serif; 13 | $font-ubuntu: "Ubuntu", sans-serif; 14 | -------------------------------------------------------------------------------- /client/src/assets/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/about.jpg -------------------------------------------------------------------------------- /client/src/assets/contact-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/contact-bg.jpg -------------------------------------------------------------------------------- /client/src/assets/contact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/contact.jpg -------------------------------------------------------------------------------- /client/src/assets/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/hero.jpg -------------------------------------------------------------------------------- /client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/logo.png -------------------------------------------------------------------------------- /client/src/assets/pnf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/pnf.jpg -------------------------------------------------------------------------------- /client/src/assets/pnf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/pnf.png -------------------------------------------------------------------------------- /client/src/assets/policy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/policy.jpg -------------------------------------------------------------------------------- /client/src/assets/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alphadev97/ecommerce-mern-project/49614fc171dfd9be12f7ab3ae4a8496add7be461/client/src/assets/preview.png -------------------------------------------------------------------------------- /client/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/Form/CategoryForm.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./CategoryForm.scss"; 3 | 4 | const CategoryForm = ({ handleSubmit, value, setValue }) => { 5 | return ( 6 | <> 7 |
8 | setValue(e.target.value)} 13 | /> 14 | 15 |
16 | 17 | ); 18 | }; 19 | 20 | export default CategoryForm; 21 | -------------------------------------------------------------------------------- /client/src/components/Form/CategoryForm.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | form { 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | 9 | input { 10 | width: 50%; 11 | height: 2rem; 12 | padding: 1rem; 13 | margin: 1rem 0 1rem 1rem; 14 | border: 3px solid rgb(100, 99, 99); 15 | border-radius: 1rem; 16 | font-family: app.$font-assistant; 17 | font-size: 1rem; 18 | } 19 | 20 | button { 21 | height: 2rem; 22 | width: 50%; 23 | margin-left: 0.5rem; 24 | border-radius: 1rem; 25 | font-family: app.$font-assistant; 26 | font-size: 1rem; 27 | font-weight: 600; 28 | background-color: app.$gargo-gas; 29 | cursor: pointer; 30 | border: 3px solid app.$gargo-gas; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/src/components/Form/SearchInput.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./SearchInput.scss"; 3 | import { useSearch } from "../../context/search"; 4 | import axios from "axios"; 5 | import { useNavigate } from "react-router-dom"; 6 | 7 | const SearchInput = () => { 8 | const [values, setValues] = useSearch(); 9 | const navigate = useNavigate(); 10 | 11 | const handleSubmit = async (e) => { 12 | e.preventDefault(); 13 | try { 14 | const { data } = await axios.get( 15 | `http://localhost:8080/api/v1/product/search/${values.keyword}` 16 | ); 17 | setValues({ ...values, results: data }); 18 | navigate("/search"); 19 | } catch (error) { 20 | console.log(error); 21 | } 22 | }; 23 | 24 | return ( 25 |
26 |
27 | setValues({ ...values, keyword: e.target.value })} 32 | /> 33 | 34 |
35 |
36 | ); 37 | }; 38 | 39 | export default SearchInput; 40 | -------------------------------------------------------------------------------- /client/src/components/Form/SearchInput.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .search-form { 4 | display: flex; 5 | align-items: center; 6 | border-radius: 4px; 7 | padding: 8px; 8 | display: flex; 9 | flex-direction: row; 10 | margin-right: 2rem; 11 | 12 | input { 13 | border: none; 14 | outline: none; 15 | padding: 8px; 16 | font-size: 16px; 17 | width: 300px; 18 | 19 | &::placeholder { 20 | color: #999; 21 | } 22 | } 23 | 24 | button { 25 | border: none; 26 | outline: none; 27 | padding: 8px 16px; 28 | background-color: app.$gargo-gas; 29 | color: app.$polic-blue; 30 | border-radius: 4px; 31 | cursor: pointer; 32 | margin-left: 8px; 33 | font-size: 16px; 34 | width: 20%; 35 | 36 | &:hover { 37 | background-color: #0056b3; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/src/components/Layout/AdminMenu/AdminMenu.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./AdminMenu.scss"; 3 | import { NavLink } from "react-router-dom"; 4 | 5 | const AdminMenu = () => { 6 | return ( 7 | <> 8 |
9 |
10 |

Admin Panel

11 | 15 | Create Category 16 | 17 | 21 | Create Product 22 | 23 | 27 | All Products 28 | 29 | 33 | Orders 34 | 35 | 39 | Users 40 | 41 |
42 |
43 | 44 | ); 45 | }; 46 | 47 | export default AdminMenu; 48 | -------------------------------------------------------------------------------- /client/src/components/Layout/AdminMenu/AdminMenu.scss: -------------------------------------------------------------------------------- 1 | @use "../../../App.scss" as app; 2 | 3 | .text-center { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | text-align: center; 8 | font-family: app.$font-assistant; 9 | 10 | .list-group { 11 | display: flex; 12 | flex-direction: column; 13 | 14 | border: 1px solid rgb(136, 136, 136); 15 | margin-right: 1rem; 16 | 17 | > h4 { 18 | font-family: app.$font-ubuntu; 19 | font-size: 1.5rem; 20 | margin-bottom: 0.5rem; 21 | padding: 1rem 0; 22 | } 23 | 24 | .list-group-item { 25 | padding: 1rem 2rem; 26 | text-decoration: none; 27 | font-size: 1.2rem; 28 | border-top: 1px solid rgb(136, 136, 136); 29 | transition: all 0.5s ease; 30 | color: app.$polic-blue; 31 | 32 | &:hover { 33 | background-color: app.$polic-blue; 34 | color: white; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/src/components/Layout/Footer/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./Footer.scss"; 3 | import logo from "../../../assets/logo.png"; 4 | import { Link } from "react-router-dom"; 5 | 6 | const Footer = () => { 7 | return ( 8 |
9 |
10 |
11 | 12 | 13 | 14 |

We Provide Best Deals & Products

15 |
16 | 17 |
18 |

Important Links

19 |
    20 |
  • 21 | Home 22 |
  • 23 |
  • 24 | About 25 |
  • 26 |
  • 27 | Contact 28 |
  • 29 |
  • 30 | Policy 31 |
  • 32 |
33 |
34 | 35 |
36 |

Contact Us

37 |

Email

38 |

Github

39 |

Linkedin

40 |
41 |
42 |
43 |

© Copyright Protected 2023, Made With ❤️ By Muhammad Usama

44 |
45 |
46 | ); 47 | }; 48 | 49 | export default Footer; 50 | -------------------------------------------------------------------------------- /client/src/components/Layout/Footer/Footer.scss: -------------------------------------------------------------------------------- 1 | @use "../../../App.scss" as app; 2 | 3 | .footer { 4 | display: flex; 5 | align-items: top; 6 | justify-content: space-evenly; 7 | background-color: app.$polic-blue; 8 | padding: 3rem 0; 9 | color: app.$white; 10 | 11 | .footer-left { 12 | display: flex; 13 | flex-direction: column; 14 | 15 | > a { 16 | > img { 17 | width: 100%; 18 | height: 6rem; 19 | } 20 | } 21 | 22 | > p { 23 | font-family: app.$font-assistant; 24 | font-size: 1rem; 25 | font-weight: 500; 26 | } 27 | } 28 | 29 | .footer-center { 30 | > h2 { 31 | font-family: app.$font-ubuntu; 32 | font-size: 1.5rem; 33 | margin-bottom: 1rem; 34 | } 35 | 36 | > ul { 37 | list-style: none; 38 | 39 | > li { 40 | > a { 41 | text-decoration: none; 42 | color: app.$white; 43 | font-family: app.$font-assistant; 44 | font-size: 1.2rem; 45 | font-weight: 500; 46 | } 47 | } 48 | } 49 | } 50 | 51 | .footer-right { 52 | font-family: app.$font-assistant; 53 | } 54 | } 55 | 56 | .copyright { 57 | display: flex; 58 | padding: 1rem 0; 59 | align-items: center; 60 | justify-content: center; 61 | 62 | p { 63 | color: app.$polic-blue; 64 | font-family: app.$font-assistant; 65 | font-weight: 500; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /client/src/components/Layout/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { NavLink, Link } from "react-router-dom"; 3 | import logo from "../../../assets/logo.png"; 4 | import "./Header.scss"; 5 | import { useAuth } from "../../../context/auth"; 6 | import toast from "react-hot-toast"; 7 | import SearchInput from "../../Form/SearchInput"; 8 | import useCategory from "../../../hooks/useCategory"; 9 | import { useCart } from "../../../context/cart"; 10 | import { Badge } from "antd"; 11 | 12 | const Header = () => { 13 | const [auth, setAuth] = useAuth(); 14 | const [cart] = useCart(); 15 | const categories = useCategory(); 16 | 17 | const handleLogOut = () => { 18 | setAuth({ 19 | ...auth, 20 | user: null, 21 | token: "", 22 | }); 23 | localStorage.removeItem("auth"); 24 | toast.success("Logout Successfully"); 25 | }; 26 | 27 | return ( 28 | <> 29 |
30 |
31 | 32 | 33 | 34 |
35 | 36 |
37 | 105 |
106 |
107 | 108 | ); 109 | }; 110 | 111 | export default Header; 112 | -------------------------------------------------------------------------------- /client/src/components/Layout/Header/Header.scss: -------------------------------------------------------------------------------- 1 | @use "../../../App.scss" as app; 2 | 3 | .header { 4 | display: flex; 5 | background-color: app.$polic-blue; 6 | justify-content: space-between; 7 | align-items: center; 8 | padding: 0 1rem; 9 | font-family: app.$font-assistant; 10 | box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; 11 | 12 | .header-left { 13 | > a { 14 | > img { 15 | width: 100%; 16 | height: 5rem; 17 | } 18 | } 19 | } 20 | 21 | .header-center { 22 | width: 70%; 23 | .navbar { 24 | display: flex; 25 | align-items: center; 26 | 27 | .navbar-item { 28 | list-style: none; 29 | 30 | .link { 31 | text-decoration: none; 32 | color: app.$white; 33 | font-weight: 600; 34 | font-size: 1rem; 35 | transition: all 0.5s ease; 36 | text-transform: uppercase; 37 | font-family: app.$font-ubuntu; 38 | padding: 1rem 1.5rem; 39 | 40 | &:hover { 41 | background-color: app.$gargo-gas; 42 | color: app.$polic-blue; 43 | border-radius: 10px; 44 | box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 28px, 45 | rgba(0, 0, 0, 0.22) 0px 10px 10px; 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | .dropdown { 54 | position: relative; 55 | display: inline-block; 56 | .link { 57 | text-decoration: none; 58 | align-items: center; 59 | justify-content: center; 60 | color: app.$white; 61 | font-weight: 600; 62 | font-size: 1rem; 63 | transition: all 0.5s ease; 64 | text-transform: uppercase; 65 | font-family: app.$font-ubuntu; 66 | padding: 1rem 1.5rem; 67 | } 68 | } 69 | 70 | .dropdown-content { 71 | display: none; 72 | position: absolute; 73 | background-color: #f9f9f9; 74 | min-width: 160px; 75 | box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); 76 | padding: 1rem 1rem; 77 | z-index: 1; 78 | 79 | .d-link { 80 | text-decoration: none; 81 | font-family: app.$font-ubuntu; 82 | color: app.$polic-blue; 83 | } 84 | } 85 | 86 | .dropdown:hover .dropdown-content { 87 | display: grid; 88 | gap: 1rem; 89 | } 90 | -------------------------------------------------------------------------------- /client/src/components/Layout/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Header from "./Header/Header"; 3 | import Footer from "./Footer/Footer"; 4 | import { Helmet } from "react-helmet"; 5 | 6 | import { Toaster } from "react-hot-toast"; 7 | 8 | const Layout = ({ children, title, description, keywords, author }) => { 9 | return ( 10 |
11 | 12 | 13 | 14 | 15 | 16 | {title} 17 | 18 |
19 |
20 | 21 | {children} 22 |
23 |
25 | ); 26 | }; 27 | 28 | Layout.defaultProps = { 29 | title: "Alpha97 | E-Commerce", 30 | description: "Alpha97 | E-Commerce is a MERN Stack application", 31 | keywords: "mern, react, express, node, mongodb, alpha97, muhammad usama", 32 | author: "Muhammad Usama - Alpha97", 33 | }; 34 | 35 | export default Layout; 36 | -------------------------------------------------------------------------------- /client/src/components/Layout/UserMenu/UserMenu.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./UserMenu.scss"; 3 | import { NavLink } from "react-router-dom"; 4 | 5 | const UserMenu = () => { 6 | return ( 7 | <> 8 | <> 9 |
10 |
11 |

Dashboard

12 | 16 | Profile 17 | 18 | 22 | Orders 23 | 24 |
25 |
26 | 27 | 28 | ); 29 | }; 30 | 31 | export default UserMenu; 32 | -------------------------------------------------------------------------------- /client/src/components/Layout/UserMenu/UserMenu.scss: -------------------------------------------------------------------------------- 1 | @use "../../../App.scss" as app; 2 | 3 | .text-center { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | text-align: center; 8 | font-family: app.$font-assistant; 9 | 10 | .list-group { 11 | display: flex; 12 | flex-direction: column; 13 | 14 | border: 1px solid rgb(136, 136, 136); 15 | margin-right: 1rem; 16 | 17 | > h4 { 18 | font-family: app.$font-ubuntu; 19 | font-size: 1.5rem; 20 | margin-bottom: 0.5rem; 21 | padding: 1rem 0; 22 | } 23 | 24 | .list-group-item { 25 | padding: 1rem 2rem; 26 | text-decoration: none; 27 | font-size: 1.2rem; 28 | border-top: 1px solid rgb(136, 136, 136); 29 | transition: all 0.5s ease; 30 | color: app.$polic-blue; 31 | 32 | &:hover { 33 | background-color: app.$polic-blue; 34 | color: white; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/src/components/Prices/Prices.jsx: -------------------------------------------------------------------------------- 1 | export const Prices = [ 2 | { 3 | _id: 0, 4 | name: "$0 to 19", 5 | array: [0, 19], 6 | }, 7 | { 8 | _id: 1, 9 | name: "$20 to 39", 10 | array: [20, 39], 11 | }, 12 | { 13 | _id: 2, 14 | name: "$40 to 59", 15 | array: [40, 59], 16 | }, 17 | { 18 | _id: 3, 19 | name: "$60 to 79", 20 | array: [60, 79], 21 | }, 22 | { 23 | _id: 4, 24 | name: "$80 to 99", 25 | array: [80, 99], 26 | }, 27 | { 28 | _id: 4, 29 | name: "$100 or more", 30 | array: [100, 9999], 31 | }, 32 | ]; 33 | -------------------------------------------------------------------------------- /client/src/components/Routes/AdminRoute.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useAuth } from "../../context/auth"; 3 | import { Outlet } from "react-router-dom"; 4 | import axios from "axios"; 5 | import Spinner from "../Spinner/Spinner"; 6 | 7 | export const AdminRoute = () => { 8 | const [ok, setOk] = useState(false); 9 | const [auth, setAuth] = useAuth(); 10 | 11 | useEffect(() => { 12 | const authCheck = async () => { 13 | const res = await axios.get( 14 | "http://localhost:8080/api/v1/auth/admin-auth" 15 | ); 16 | if (res.data.ok) { 17 | setOk(true); 18 | } else { 19 | setOk(false); 20 | } 21 | }; 22 | if (auth?.token) authCheck(); 23 | }, [auth?.token]); 24 | 25 | return ok ? : ; 26 | }; 27 | -------------------------------------------------------------------------------- /client/src/components/Routes/Private.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useAuth } from "../../context/auth"; 3 | import { Outlet } from "react-router-dom"; 4 | import axios from "axios"; 5 | import Spinner from "../Spinner/Spinner"; 6 | 7 | export const PrivateRoute = () => { 8 | const [ok, setOk] = useState(false); 9 | const [auth, setAuth] = useAuth(); 10 | 11 | useEffect(() => { 12 | const authCheck = async () => { 13 | const res = await axios.get( 14 | "http://localhost:8080/api/v1/auth/user-auth" 15 | ); 16 | if (res.data.ok) { 17 | setOk(true); 18 | } else { 19 | setOk(false); 20 | } 21 | }; 22 | if (auth?.token) authCheck(); 23 | }, [auth?.token]); 24 | 25 | return ok ? : ; 26 | }; 27 | -------------------------------------------------------------------------------- /client/src/components/Spinner/Spinner.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useNavigate, useLocation } from "react-router-dom"; 3 | import "./Spinner.scss"; 4 | 5 | const Spinner = ({ path = "login" }) => { 6 | const [count, setCount] = useState(3); 7 | const navigate = useNavigate(); 8 | const location = useLocation(); 9 | 10 | useEffect(() => { 11 | const interval = setInterval(() => { 12 | setCount((prevValue) => --prevValue); 13 | }, 1000); 14 | count === 0 && 15 | navigate(`/${path}`, { 16 | state: location.pathname, 17 | }); 18 | return () => clearInterval(interval); 19 | }, [count, navigate, location, path]); 20 | 21 | return ( 22 | <> 23 |
24 |
Loading...
25 |

redirecting to you in {count} second!

26 |
27 | 28 | ); 29 | }; 30 | 31 | export default Spinner; 32 | -------------------------------------------------------------------------------- /client/src/components/Spinner/Spinner.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .body { 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | height: 100vh; 9 | margin: 0; 10 | background-color: app.$polic-blue; 11 | 12 | h1 { 13 | color: app.$gargo-gas; 14 | } 15 | } 16 | 17 | .loader { 18 | color: app.$gargo-gas; 19 | font-size: 90px; 20 | text-indent: -9999em; 21 | overflow: hidden; 22 | width: 1em; 23 | height: 1em; 24 | border-radius: 50%; 25 | margin: 72px auto; 26 | position: relative; 27 | -webkit-transform: translateZ(0); 28 | -ms-transform: translateZ(0); 29 | transform: translateZ(0); 30 | -webkit-animation: load6 1.7s infinite ease, round 1.7s infinite ease; 31 | animation: load6 1.7s infinite ease, round 1.7s infinite ease; 32 | } 33 | @-webkit-keyframes load6 { 34 | 0% { 35 | box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 36 | 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; 37 | } 38 | 5%, 39 | 95% { 40 | box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 41 | 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; 42 | } 43 | 10%, 44 | 59% { 45 | box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, 46 | -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, 47 | -0.297em -0.775em 0 -0.477em; 48 | } 49 | 20% { 50 | box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, 51 | -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, 52 | -0.749em -0.34em 0 -0.477em; 53 | } 54 | 38% { 55 | box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, 56 | -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, 57 | -0.82em -0.09em 0 -0.477em; 58 | } 59 | 100% { 60 | box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 61 | 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; 62 | } 63 | } 64 | @keyframes load6 { 65 | 0% { 66 | box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 67 | 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; 68 | } 69 | 5%, 70 | 95% { 71 | box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 72 | 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; 73 | } 74 | 10%, 75 | 59% { 76 | box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, 77 | -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, 78 | -0.297em -0.775em 0 -0.477em; 79 | } 80 | 20% { 81 | box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, 82 | -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, 83 | -0.749em -0.34em 0 -0.477em; 84 | } 85 | 38% { 86 | box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, 87 | -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, 88 | -0.82em -0.09em 0 -0.477em; 89 | } 90 | 100% { 91 | box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 92 | 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em; 93 | } 94 | } 95 | @-webkit-keyframes round { 96 | 0% { 97 | -webkit-transform: rotate(0deg); 98 | transform: rotate(0deg); 99 | } 100 | 100% { 101 | -webkit-transform: rotate(360deg); 102 | transform: rotate(360deg); 103 | } 104 | } 105 | @keyframes round { 106 | 0% { 107 | -webkit-transform: rotate(0deg); 108 | transform: rotate(0deg); 109 | } 110 | 100% { 111 | -webkit-transform: rotate(360deg); 112 | transform: rotate(360deg); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /client/src/context/auth.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useContext, createContext } from "react"; 2 | import axios from "axios"; 3 | 4 | const AuthContext = createContext(); 5 | 6 | const AuthProvider = ({ children }) => { 7 | const [auth, setAuth] = useState({ 8 | user: null, 9 | token: "", 10 | }); 11 | 12 | // Default axios 13 | axios.defaults.headers.common["Authorization"] = auth?.token; 14 | 15 | useEffect(() => { 16 | const data = localStorage.getItem("auth"); 17 | 18 | if (data) { 19 | const parseData = JSON.parse(data); 20 | setAuth({ 21 | ...auth, 22 | user: parseData.user, 23 | token: parseData.token, 24 | }); 25 | } 26 | // eslint-disable-next-line 27 | }, []); 28 | return ( 29 | 30 | {children} 31 | 32 | ); 33 | }; 34 | 35 | // Custom Hook 36 | const useAuth = () => useContext(AuthContext); 37 | 38 | export { useAuth, AuthProvider }; 39 | -------------------------------------------------------------------------------- /client/src/context/cart.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useContext, createContext, useEffect } from "react"; 2 | 3 | const CartContext = createContext(); 4 | 5 | const CartProvider = ({ children }) => { 6 | const [cart, setCart] = useState([]); 7 | 8 | useEffect(() => { 9 | let existingCartItems = localStorage.getItem("cart"); 10 | if (existingCartItems) setCart(JSON.parse(existingCartItems)); 11 | }, []); 12 | 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | }; 19 | 20 | // Custom Hook 21 | const useCart = () => useContext(CartContext); 22 | 23 | export { useCart, CartProvider }; 24 | -------------------------------------------------------------------------------- /client/src/context/search.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useContext, createContext } from "react"; 2 | 3 | const SearchContext = createContext(); 4 | 5 | const SearchProvider = ({ children }) => { 6 | const [auth, setAuth] = useState({ 7 | keyword: "", 8 | results: [], 9 | }); 10 | 11 | return ( 12 | 13 | {children} 14 | 15 | ); 16 | }; 17 | 18 | // Custom Hook 19 | const useSearch = () => useContext(SearchContext); 20 | 21 | export { useSearch, SearchProvider }; 22 | -------------------------------------------------------------------------------- /client/src/hooks/useCategory.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import axios from "axios"; 3 | 4 | export default function useCategory() { 5 | const [categories, setCategories] = useState([]); 6 | 7 | // get category 8 | const getCategories = async () => { 9 | try { 10 | const { data } = await axios.get( 11 | `http://localhost:8080/api/v1/category/get-category` 12 | ); 13 | setCategories(data?.category); 14 | } catch (error) { 15 | console.log(error); 16 | } 17 | }; 18 | 19 | useEffect(() => { 20 | getCategories(); 21 | }, []); 22 | 23 | return categories; 24 | } 25 | -------------------------------------------------------------------------------- /client/src/index.scss: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Assistant:wght@200;300;400;500;600;700;800&display=swap"); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | /* font-family: 'Assistant', sans-serif; 10 | font-family: 'Ubuntu', sans-serif; */ 11 | -------------------------------------------------------------------------------- /client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "./index.scss"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | import { AuthProvider } from "./context/auth.jsx"; 7 | import { SearchProvider } from "./context/search.jsx"; 8 | import { CartProvider } from "./context/cart.jsx"; 9 | 10 | ReactDOM.createRoot(document.getElementById("root")).render( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /client/src/pages/About/About.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Layout from "../../components/Layout/Layout"; 3 | import aboutImg from "../../assets/about.jpg"; 4 | import "./About.scss"; 5 | 6 | const About = () => { 7 | return ( 8 | 9 |

About Us

10 |
11 |
12 | 13 |
14 | 15 |
16 |

About Alpha97 | E-Commerce

17 |

18 | Lorem ipsum dolor sit amet consectetur adipiscing, elit sed
19 | venenatis varius sapien quam dignissim, leo tempus nostra velit{" "} 20 |
21 | facilisis. Ad mi eros dapibus eget sagittis per enim bibendum
22 | rhoncus vehicula, mus fringilla cras gravida sollicitudin lectus{" "} 23 |
24 | morbi convallis. Vivamus id nec tristique faucibus lacinia egestas{" "} 25 |
26 | curabitur class aliquet convallis, varius arcu semper aenean dis non{" "} 27 |
28 | nulla ultricies nullam, a sagittis sapien diam taciti nisl fringilla{" "} 29 |
30 | massa sodales. 31 |

32 |
33 |
34 |
35 | ); 36 | }; 37 | 38 | export default About; 39 | -------------------------------------------------------------------------------- /client/src/pages/About/About.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .ah1 { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | padding: 5rem 0; 8 | background-image: url(../../assets/contact-bg.jpg); 9 | background-position: center; 10 | background-size: cover; 11 | box-shadow: inset 0 0 0 2000px rgba(60, 72, 107, 0.507); 12 | font-size: 2rem; 13 | font-family: app.$font-ubuntu; 14 | color: white; 15 | } 16 | 17 | .about { 18 | display: flex; 19 | margin: 2rem; 20 | gap: 2rem; 21 | align-items: center; 22 | justify-content: center; 23 | 24 | .about-left { 25 | > img { 26 | width: 100%; 27 | height: 30rem; 28 | } 29 | } 30 | 31 | .about-right { 32 | > h2 { 33 | font-family: app.$font-ubuntu; 34 | font-size: 1.5rem; 35 | } 36 | 37 | > p { 38 | display: flex; 39 | margin: 1rem auto; 40 | padding: 1rem 0.3rem; 41 | font-family: app.$font-assistant; 42 | font-size: 1.2rem; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/src/pages/AdminDashboard/AdminDashboard.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./AdminDashboard.scss"; 3 | import Layout from "../../components/Layout/Layout"; 4 | import AdminMenu from "../../components/Layout/AdminMenu/AdminMenu"; 5 | import { useAuth } from "../../context/auth"; 6 | 7 | const AdminDashboard = () => { 8 | const [auth] = useAuth(); 9 | 10 | return ( 11 | 12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |

Admin Name: {auth?.user?.name}

20 |

Admin Email: {auth?.user?.email}

21 |

Admin Contact: {auth?.user?.phone}

22 |
23 |
24 |
25 |
26 |
27 | ); 28 | }; 29 | 30 | export default AdminDashboard; 31 | -------------------------------------------------------------------------------- /client/src/pages/AdminDashboard/AdminDashboard.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .admin-container { 4 | margin: 1rem; 5 | .row { 6 | display: flex; 7 | flex-direction: row; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/src/pages/AdminOrders/AdminOrders.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import axios from "axios"; 3 | import "./AdminOrders.scss"; 4 | import AdminMenu from "../../components/Layout/AdminMenu/AdminMenu"; 5 | import Layout from "../../components/Layout/Layout"; 6 | import toast from "react-hot-toast"; 7 | import { useAuth } from "../../context/auth"; 8 | import moment from "moment"; 9 | import { Select } from "antd"; 10 | const { Option } = Select; 11 | 12 | const AdminOrders = () => { 13 | const [status, setStatus] = useState([ 14 | "Not Process", 15 | "Processing", 16 | "Shipped", 17 | "Delievered", 18 | "Cancel", 19 | ]); 20 | const [changeStatus, setChangeStatus] = useState(""); 21 | const [orders, setOrders] = useState([]); 22 | const [auth, setAuth] = useAuth(); 23 | 24 | const getOrders = async () => { 25 | try { 26 | const { data } = await axios( 27 | "http://localhost:8080/api/v1/auth/all-orders" 28 | ); 29 | setOrders(data); 30 | } catch (error) { 31 | console.log(error); 32 | } 33 | }; 34 | 35 | useEffect(() => { 36 | if (auth?.token) getOrders(); 37 | }, [auth?.token]); 38 | 39 | const handleChange = async (orderId, value) => { 40 | try { 41 | const { data } = await axios.put( 42 | `http://localhost:8080/api/v1/auth/order-status/${orderId}`, 43 | { status: value } 44 | ); 45 | getOrders(); 46 | } catch (error) { 47 | console.log(error); 48 | } 49 | }; 50 | 51 | return ( 52 | 53 |
54 |
55 | 56 |
57 |
58 |

All Orders

59 | {orders?.map((o, i) => { 60 | return ( 61 |
62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
#StatusBuyerDatePaymentQuantity
{i + 1} 77 | 88 | {o?.buyer?.name}{moment(o?.createdAt).fromNow()}{o?.payment.success ? "Success" : "Faild"}{o?.products?.length}
96 |
97 | {o?.products?.map((p, i) => ( 98 |
99 |
100 | {p.name} 106 |
107 |
108 |

{p.name}

109 |

{p.description.substring(0, 30)}

110 |

${p.price}

111 |
112 |
113 | ))} 114 |
115 |
116 | ); 117 | })} 118 |
119 |
120 |
121 | ); 122 | }; 123 | 124 | export default AdminOrders; 125 | -------------------------------------------------------------------------------- /client/src/pages/AdminOrders/AdminOrders.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .admin-orders { 4 | display: flex; 5 | flex-direction: row; 6 | margin: 1rem; 7 | 8 | .left { 9 | display: flex; 10 | } 11 | 12 | .right { 13 | width: 80%; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/pages/Cart/CartPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import "./CartPage.scss"; 3 | import Layout from "../../components/Layout/Layout"; 4 | import { useCart } from "../../context/cart"; 5 | import { useAuth } from "../../context/auth"; 6 | import { useNavigate } from "react-router-dom"; 7 | import DropIn from "braintree-web-drop-in-react"; 8 | import axios from "axios"; 9 | import { toast } from "react-hot-toast"; 10 | 11 | const CartPage = () => { 12 | const [auth, setAuth] = useAuth(); 13 | const [cart, setCart] = useCart(); 14 | const [clientToken, setClientToken] = useState(""); 15 | const [instance, setInstance] = useState(""); 16 | const [loading, setLoading] = useState(false); 17 | const navigate = useNavigate(); 18 | 19 | // total price 20 | const totalPrice = () => { 21 | try { 22 | let total = 0; 23 | cart?.map((item) => { 24 | total = total + item.price; 25 | }); 26 | return total.toLocaleString("en-US", { 27 | style: "currency", 28 | currency: "USD", 29 | }); 30 | } catch (error) { 31 | console.log(error); 32 | } 33 | }; 34 | 35 | // delete item 36 | const removeCartItem = (pid) => { 37 | try { 38 | let myCart = [...cart]; 39 | let index = myCart.findIndex((item) => item._id === pid); 40 | myCart.splice(index, 1); 41 | setCart(myCart); 42 | localStorage.setItem("cart", JSON.stringify(myCart)); 43 | } catch (error) { 44 | console.log(error); 45 | } 46 | }; 47 | 48 | // get payment gateway token 49 | const getToken = async () => { 50 | try { 51 | const { data } = await axios.get( 52 | "http://localhost:8080/api/v1/product/braintree/token" 53 | ); 54 | setClientToken(data?.clientToken); 55 | } catch (error) { 56 | console.log(error); 57 | } 58 | }; 59 | useEffect(() => { 60 | getToken(); 61 | }, [auth?.token]); 62 | 63 | // handle payment 64 | 65 | const handlePayment = async () => { 66 | try { 67 | setLoading(true); 68 | const { nonce } = await instance.requestPaymentMethod(); 69 | const { data } = await axios.post( 70 | "http://localhost:8080/api/v1/product/braintree/payment", 71 | { 72 | nonce, 73 | cart, 74 | } 75 | ); 76 | setLoading(false); 77 | localStorage.removeItem("cart"); 78 | setCart([]); 79 | navigate("/dashboard/user/orders"); 80 | toast.success("Payment Completed Successfully "); 81 | } catch (error) { 82 | console.log(error); 83 | setLoading(false); 84 | } 85 | }; 86 | 87 | return ( 88 | 89 |
90 |
91 |
92 |

{`Hello ${auth?.token && auth?.user?.name}`}

93 |

94 | {cart?.length 95 | ? `You have ${cart.length} items in your cart ${ 96 | auth?.token ? "" : "please login to checkout" 97 | }` 98 | : "Your cart is empty"} 99 |

100 |
101 |
102 |
103 |
104 | {cart?.map((p) => ( 105 |
106 |
107 | {p.name} 113 |
114 |
115 |

{p.name}

116 |

{p.description.substring(0, 30)}

117 |

${p.price}

118 | 119 |
120 |
121 | ))} 122 |
123 |
124 |

Cart Summary

125 |

Total | Checkout | Payment

126 |
127 |

Total: {totalPrice()}

128 | {auth?.user?.address ? ( 129 | <> 130 |
131 |

Current Address

132 |
{auth?.user?.address}
133 | 136 |
137 | 138 | ) : ( 139 |
140 | {auth?.token ? ( 141 | 144 | ) : ( 145 | 150 | )} 151 |
152 | )} 153 |
154 | {!clientToken || !auth?.token || !cart?.length ? ( 155 | "" 156 | ) : ( 157 | <> 158 | setInstance(instance)} 166 | /> 167 | 173 | 174 | )} 175 |
176 |
177 |
178 |
179 |
180 | ); 181 | }; 182 | 183 | export default CartPage; 184 | -------------------------------------------------------------------------------- /client/src/pages/Cart/CartPage.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .cart-container { 4 | display: flex; 5 | flex-direction: column; 6 | 7 | .cart-details { 8 | display: flex; 9 | justify-content: center; 10 | padding: 1rem 0; 11 | } 12 | 13 | .cart { 14 | padding: 2rem 0; 15 | width: 100%; 16 | display: flex; 17 | flex-direction: row; 18 | justify-content: space-evenly; 19 | 20 | .cart-item { 21 | width: 50%; 22 | display: flex; 23 | flex-direction: column; 24 | 25 | .items { 26 | display: flex; 27 | flex-direction: row; 28 | gap: 1rem; 29 | margin: 1rem 1rem; 30 | // border: 1px solid app.$polic-blue; 31 | padding: 1rem; 32 | border-radius: 1rem; 33 | box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; 34 | 35 | .item-det { 36 | display: flex; 37 | flex-direction: column; 38 | justify-content: center; 39 | 40 | > h4 { 41 | font-family: app.$font-ubuntu; 42 | font-size: 2rem; 43 | padding: 1rem 0; 44 | } 45 | 46 | > p { 47 | font-family: app.$font-assistant; 48 | font-size: 1.5rem; 49 | font-weight: 500; 50 | } 51 | 52 | > button { 53 | width: 40%; 54 | padding: 0.5rem 0.6rem; 55 | background-color: app.$polic-blue; 56 | color: white; 57 | cursor: pointer; 58 | font-family: app.$font-ubuntu; 59 | border: none; 60 | } 61 | } 62 | } 63 | } 64 | 65 | .checkout { 66 | width: 50%; 67 | 68 | // buttons 69 | button { 70 | font-family: app.$font-assistant; 71 | font-size: 1rem; 72 | margin-top: 1rem; 73 | padding: 0.5rem 1rem; 74 | cursor: pointer; 75 | border: none; 76 | background-color: app.$polic-blue; 77 | color: white; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /client/src/pages/Categories/Categories.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import "./Categories.scss"; 4 | import Layout from "../../components/Layout/Layout"; 5 | import useCategory from "../../hooks/useCategory"; 6 | 7 | const Categories = () => { 8 | const categories = useCategory(); 9 | return ( 10 | 11 |
12 |
13 | {categories.map((c) => ( 14 |
15 | 16 | {c.name} 17 | 18 |
19 | ))} 20 |
21 |
22 |
23 | ); 24 | }; 25 | 26 | export default Categories; 27 | -------------------------------------------------------------------------------- /client/src/pages/Categories/Categories.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .container { 4 | display: flex; 5 | font-family: app.$font-assistant; 6 | justify-content: center; 7 | margin: 2rem 0; 8 | 9 | .category { 10 | display: flex; 11 | gap: 1rem; 12 | justify-content: space-evenly; 13 | 14 | .cat-btn { 15 | .cat-link { 16 | padding: 0.5rem 1rem; 17 | border: none; 18 | background-color: app.$gargo-gas; 19 | border-radius: 0.2rem; 20 | text-decoration: none; 21 | font-size: 1rem; 22 | color: black; 23 | font-weight: 500; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/pages/CategoryProduct/CategoryProduct.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import "./CategoryProduct.scss"; 3 | import Layout from "../../components/Layout/Layout"; 4 | import axios from "axios"; 5 | import { useParams, useNavigate } from "react-router-dom"; 6 | 7 | const CategoryProduct = () => { 8 | const params = useParams(); 9 | const navigate = useNavigate(); 10 | const [products, setProducts] = useState([]); 11 | const [category, setCategory] = useState([]); 12 | 13 | useEffect(() => { 14 | if (params?.slug) getProductByCategory(); 15 | }, [params?.slug]); 16 | 17 | const getProductByCategory = async () => { 18 | try { 19 | const { data } = await axios.get( 20 | `http://localhost:8080/api/v1/product/product-category/${params.slug}` 21 | ); 22 | setProducts(data?.products); 23 | setCategory(data?.category); 24 | } catch (error) { 25 | console.log(error); 26 | } 27 | }; 28 | 29 | return ( 30 | 31 |
32 |

Category - {category?.name}

33 |
{products.length} results found
34 |
35 | {products?.map((p) => ( 36 |
37 | {p.name} 41 |
42 |
{p.name}
43 |

{p.description.substring(0, 30)}

44 |

${p.price}

45 |
46 |
47 | 50 | 51 |
52 |
53 | ))} 54 |
55 |
56 |
57 | ); 58 | }; 59 | 60 | export default CategoryProduct; 61 | -------------------------------------------------------------------------------- /client/src/pages/CategoryProduct/CategoryProduct.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .container { 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | 9 | > h4 { 10 | font-family: app.$font-ubuntu; 11 | font-size: 1rem; 12 | } 13 | 14 | > h6 { 15 | font-family: app.$font-assistant; 16 | font-size: 1rem; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/pages/Contact/Contact.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Layout from "../../components/Layout/Layout"; 3 | import "./Contact.scss"; 4 | import contactImg from "../../assets/contact.jpg"; 5 | import { BiMap, BiPhoneCall, BiLink } from "react-icons/bi"; 6 | import { MdEmail } from "react-icons/md"; 7 | 8 | const Contact = () => { 9 | return ( 10 | 11 |

Contact Us

12 |
13 |
14 | 15 |
16 | 17 |
18 |

Feel Free To Contact Us!

19 |

20 | 123 Any Street, Anywhere City, ST, 12345. 21 |

22 |

23 | +123 456 789 24 |

25 |

26 | hello@anywebsite.com 27 |

28 |
29 |
30 |
31 | ); 32 | }; 33 | 34 | export default Contact; 35 | -------------------------------------------------------------------------------- /client/src/pages/Contact/Contact.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .ch1 { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | padding: 5rem 0; 8 | background-image: url(../../assets/contact-bg.jpg); 9 | background-position: center; 10 | background-size: cover; 11 | box-shadow: inset 0 0 0 2000px rgba(60, 72, 107, 0.507); 12 | font-size: 2rem; 13 | font-family: app.$font-ubuntu; 14 | color: white; 15 | } 16 | 17 | .contact { 18 | display: flex; 19 | margin: 2rem auto; 20 | gap: 2rem; 21 | align-items: center; 22 | justify-content: center; 23 | 24 | .contact-left { 25 | > img { 26 | width: 100%; 27 | height: 30rem; 28 | } 29 | } 30 | 31 | .contact-right { 32 | > h2 { 33 | font-family: app.$font-ubuntu; 34 | font-size: 1.5rem; 35 | } 36 | 37 | .cList { 38 | display: flex; 39 | margin: 1rem auto; 40 | padding: 1rem 0.3rem; 41 | font-family: app.$font-assistant; 42 | font-size: 1.2rem; 43 | align-items: center; 44 | background-color: app.$polic-blue; 45 | border-radius: 1rem; 46 | color: white; 47 | 48 | > svg { 49 | color: app.$gargo-gas; 50 | margin-right: 1rem; 51 | font-size: 1.5rem; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client/src/pages/CreateCategory/CreateCategory.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import "./CreateCategory.scss"; 3 | import Layout from "../../components/Layout/Layout"; 4 | import AdminMenu from "../../components/Layout/AdminMenu/AdminMenu"; 5 | import toast from "react-hot-toast"; 6 | import axios from "axios"; 7 | import CategoryForm from "../../components/Form/CategoryForm"; 8 | import { Modal } from "antd"; 9 | 10 | const CreateCategory = () => { 11 | const [categories, setCategories] = useState([]); 12 | const [name, setName] = useState(""); 13 | const [visible, setVisible] = useState(false); 14 | const [selected, setSelected] = useState(null); 15 | const [updatedName, setUpdatedName] = useState(""); 16 | 17 | // handle form 18 | const handleSubmit = async (e) => { 19 | e.preventDefault(); 20 | try { 21 | const { data } = await axios.post( 22 | "http://localhost:8080/api/v1/category/create-category", 23 | { name } 24 | ); 25 | if (data.success) { 26 | toast.success(`${name} is created!`); 27 | getAllCategory(); 28 | } else { 29 | toast.error(data.message); 30 | } 31 | } catch (error) { 32 | console.log(error); 33 | toast.error("Something went wrong un input form"); 34 | } 35 | }; 36 | 37 | // get all categories 38 | const getAllCategory = async () => { 39 | try { 40 | const { data } = await axios.get( 41 | "http://localhost:8080/api/v1/category/get-category" 42 | ); 43 | if (data?.success) { 44 | setCategories(data?.category); 45 | } 46 | } catch (error) { 47 | console.log(error); 48 | toast.error("Something went wrong in getting category"); 49 | } 50 | }; 51 | 52 | useEffect(() => { 53 | getAllCategory(); 54 | }, []); 55 | 56 | // update category 57 | 58 | const handleUpdate = async (e) => { 59 | e.preventDefault(); 60 | try { 61 | const { data } = await axios.put( 62 | `http://localhost:8080/api/v1/category/update-category/${selected._id}`, 63 | { name: updatedName } 64 | ); 65 | if (data.success) { 66 | toast.success(`${updatedName} is updated!`); 67 | setSelected(null); 68 | setUpdatedName(""); 69 | setVisible(false); 70 | getAllCategory(); 71 | } else { 72 | toast.error(data.message); 73 | } 74 | } catch (error) { 75 | toast.error("Something went wrong"); 76 | } 77 | }; 78 | 79 | // delete category 80 | 81 | const handleDelete = async (pId) => { 82 | try { 83 | const { data } = await axios.delete( 84 | `http://localhost:8080/api/v1/category/delete-category/${pId}`, 85 | { name: updatedName } 86 | ); 87 | if (data.success) { 88 | toast.success(`Category is Deleted!`); 89 | getAllCategory(); 90 | } else { 91 | toast.error(data.message); 92 | } 93 | } catch (error) { 94 | toast.error("Something went wrong"); 95 | } 96 | }; 97 | 98 | return ( 99 | 100 |
101 |
102 |
103 | 104 |
105 |
106 |
107 |

Create Category

108 |
109 | 114 |
115 |

Manage Category

116 |
117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | {categories?.map((c) => ( 127 | <> 128 | 129 | 130 | 150 | 151 | 152 | ))} 153 | 154 |
NameAction
{c.name} 131 | 141 | 149 |
155 |
156 |
157 | setVisible(false)} 159 | footer={null} 160 | visible={visible} 161 | > 162 | 167 | 168 |
169 |
170 |
171 |
172 |
173 | ); 174 | }; 175 | 176 | export default CreateCategory; 177 | -------------------------------------------------------------------------------- /client/src/pages/CreateCategory/CreateCategory.scss: -------------------------------------------------------------------------------- 1 | @use "../../App.scss" as app; 2 | 3 | .container { 4 | margin: 1rem; 5 | .row { 6 | display: flex; 7 | flex-direction: row; 8 | 9 | .row-right { 10 | font-family: app.$font-assistant; 11 | width: 100%; 12 | .card { 13 | > h1 { 14 | font-family: app.$font-ubuntu; 15 | padding: 1rem; 16 | text-align: center; 17 | } 18 | } 19 | } 20 | } 21 | } 22 | 23 | .table { 24 | // sets 25 | 26 | $gl-ms: "screen and (max-width: 23.5em)"; // up to 360px 27 | $gl-xs: "screen and (max-width: 35.5em)"; // up to 568px 28 | $gl-sm: "screen and (max-width: 48em)"; // max 768px 29 | $gl-md: "screen and (max-width: 64em)"; // max 1024px 30 | $gl-lg: "screen and (max-width: 80em)"; // max 1280px 31 | 32 | // table style 33 | 34 | table { 35 | border-spacing: 1; 36 | border-collapse: collapse; 37 | background: white; 38 | justify-content: center; 39 | align-items: center; 40 | overflow: hidden; 41 | max-width: 800px; 42 | width: 100%; 43 | margin: 0 auto; 44 | position: relative; 45 | 46 | * { 47 | position: relative; 48 | } 49 | 50 | td, 51 | th { 52 | padding-left: 8px; 53 | } 54 | 55 | thead tr { 56 | height: 60px; 57 | background: app.$polic-blue; 58 | font-size: 16px; 59 | color: white; 60 | } 61 | 62 | tbody tr { 63 | height: 48px; 64 | } 65 | 66 | td, 67 | th { 68 | font-family: app.$font-assistant; 69 | text-align: left; 70 | font-size: 1.5rem; 71 | border: 1px solid black; 72 | } 73 | } 74 | 75 | .form { 76 | margin: 1rem; 77 | } 78 | 79 | .btn { 80 | font-size: 1rem; 81 | padding: 0.5rem 2rem; 82 | background-color: app.$gargo-gas; 83 | border: 1px solid black; 84 | cursor: pointer; 85 | margin-right: 1rem; 86 | } 87 | 88 | .btn-danger { 89 | font-size: 1rem; 90 | padding: 0.5rem 2rem; 91 | background-color: red; 92 | border: 1px solid black; 93 | color: white; 94 | cursor: pointer; 95 | } 96 | 97 | @media #{$gl-xs} { 98 | table { 99 | display: block; 100 | > *, 101 | tr, 102 | td, 103 | th { 104 | display: block; 105 | } 106 | 107 | thead { 108 | display: none; 109 | } 110 | tbody tr { 111 | height: auto; 112 | padding: 8px 0; 113 | td { 114 | padding-left: 45%; 115 | margin-bottom: 12px; 116 | &:last-child { 117 | margin-bottom: 0; 118 | } 119 | &:before { 120 | position: absolute; 121 | font-weight: 700; 122 | width: 40%; 123 | left: 10px; 124 | top: 0; 125 | } 126 | 127 | &:nth-child(1):before { 128 | content: "Code"; 129 | } 130 | &:nth-child(2):before { 131 | content: "Stock"; 132 | } 133 | &:nth-child(3):before { 134 | content: "Cap"; 135 | } 136 | &:nth-child(4):before { 137 | content: "Inch"; 138 | } 139 | &:nth-child(5):before { 140 | content: "Box Type"; 141 | } 142 | } 143 | } 144 | } 145 | } 146 | 147 | // body style 148 | 149 | body { 150 | background: #9bc86a; 151 | font: 400 14px "Calibri", "Arial"; 152 | padding: 20px; 153 | } 154 | 155 | blockquote { 156 | color: white; 157 | text-align: center; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /client/src/pages/CreateProduct/CreateProduct.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import "./CreateProduct.scss"; 3 | import Layout from "../../components/Layout/Layout"; 4 | import AdminMenu from "../../components/Layout/AdminMenu/AdminMenu"; 5 | import toast from "react-hot-toast"; 6 | import axios from "axios"; 7 | import { Select } from "antd"; 8 | import { useNavigate } from "react-router-dom"; 9 | 10 | const { Option } = Select; 11 | 12 | const CreateProduct = () => { 13 | const navigate = useNavigate(); 14 | const [categories, setCategories] = useState([]); 15 | const [photo, setPhoto] = useState(""); 16 | const [name, setName] = useState(""); 17 | const [description, setDescription] = useState(""); 18 | const [price, setPrice] = useState(""); 19 | const [category, setCategory] = useState(""); 20 | const [quantity, setQuantity] = useState(""); 21 | const [shipping, setShipping] = useState(""); 22 | 23 | // Get all category 24 | 25 | const getAllCategory = async () => { 26 | try { 27 | const { data } = await axios.get( 28 | "http://localhost:8080/api/v1/category/get-category" 29 | ); 30 | if (data?.success) { 31 | setCategories(data?.category); 32 | } 33 | } catch (error) { 34 | console.log(error); 35 | toast.error("Something went wrong in getting category"); 36 | } 37 | }; 38 | 39 | // create product function 40 | 41 | const handleCreate = async (e) => { 42 | e.preventDefault(); 43 | try { 44 | const productData = new FormData(); 45 | productData.append("name", name); 46 | productData.append("description", description); 47 | productData.append("price", price); 48 | productData.append("quantity", quantity); 49 | productData.append("category", category); 50 | productData.append("photo", photo); 51 | productData.append("shipping", shipping); 52 | const { data } = axios.post( 53 | "http://localhost:8080/api/v1/product/create-product", 54 | productData 55 | ); 56 | if (data?.success) { 57 | toast.error(data?.message); 58 | } else { 59 | toast.success("Product Created Successfully"); 60 | navigate("/dashboard/admin/products"); 61 | } 62 | } catch (error) { 63 | console.log(error); 64 | toast.error("Something went wrong"); 65 | } 66 | }; 67 | 68 | useEffect(() => { 69 | getAllCategory(); 70 | }, []); 71 | 72 | return ( 73 | 74 |
75 |
76 |
77 | 78 |
79 |
80 |
81 |

Create Product

82 |
83 | 99 | 100 |
101 | 111 |
112 |
113 | {photo && ( 114 |
115 | product photo 120 |
121 | )} 122 |
123 |
124 | setName(e.target.value)} 129 | /> 130 |
131 |
132 |