├── socket ├── .env ├── package.json └── index.js ├── frontend ├── README.md ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── src │ ├── ShopRoutes.js │ ├── server.js │ ├── setupTests.js │ ├── App.test.js │ ├── index.js │ ├── reportWebVitals.js │ ├── pages │ │ ├── TrackOrderPage.jsx │ │ ├── OrderDetailsPage.jsx │ │ ├── Shop │ │ │ ├── ShopOrderDetails.jsx │ │ │ ├── ShopInboxPage.jsx │ │ │ ├── ShopSettingsPage.jsx │ │ │ ├── ShopWithDrawMoneyPage.jsx │ │ │ ├── ShopDashboardPage.jsx │ │ │ ├── ShopAllEvents.jsx │ │ │ ├── ShopAllOrders.jsx │ │ │ ├── ShopAllCoupouns.jsx │ │ │ ├── ShopAllProducts.jsx │ │ │ ├── ShopCreateEvents.jsx │ │ │ ├── ShopAllRefunds.jsx │ │ │ ├── ShopCreateProduct.jsx │ │ │ ├── ShopHomePage.jsx │ │ │ └── ShopPreviewPage.jsx │ │ ├── CheckoutPage.jsx │ │ ├── LoginPage.jsx │ │ ├── SignupPage.jsx │ │ ├── PaymentPage.jsx │ │ ├── ShopCreate.jsx │ │ ├── ShopLoginPage.jsx │ │ ├── AdminDashboardUsers.jsx │ │ ├── AdminDashboardEvents.jsx │ │ ├── AdminDashboardSellers.jsx │ │ ├── AdminDashboardProducts.jsx │ │ ├── AdminDashboardWithdraw.jsx │ │ ├── AdminDashboardPage.jsx │ │ ├── ProfilePage.jsx │ │ ├── EventsPage.jsx │ │ ├── HomePage.jsx │ │ ├── OrderSuccessPage.jsx │ │ ├── BestSellingPage.jsx │ │ ├── SellerActivationPage.jsx │ │ ├── ActivationPage.jsx │ │ ├── ProductsPage.jsx │ │ ├── ProductDetailsPage.jsx │ │ └── AdminDashboardOrders.jsx │ ├── routes │ │ ├── ProtectedRoute.js │ │ ├── ProtectedAdminRoute.js │ │ ├── SellerProtectedRoute.js │ │ ├── AdminRoutes.js │ │ ├── ShopRoutes.js │ │ └── Routes.js │ ├── redux │ │ ├── actions │ │ │ ├── sellers.js │ │ │ ├── wishlist.js │ │ │ ├── cart.js │ │ │ ├── order.js │ │ │ ├── event.js │ │ │ ├── product.js │ │ │ └── user.js │ │ ├── store.js │ │ └── reducers │ │ │ ├── wishlist.js │ │ │ ├── seller.js │ │ │ ├── order.js │ │ │ ├── cart.js │ │ │ ├── event.js │ │ │ ├── product.js │ │ │ └── user.js │ ├── components │ │ ├── Layout │ │ │ ├── Loader.jsx │ │ │ ├── Navbar.jsx │ │ │ ├── DropDown.jsx │ │ │ └── AdminHeader.jsx │ │ ├── Events │ │ │ ├── Events.jsx │ │ │ ├── CountDown.jsx │ │ │ └── EventCard.jsx │ │ ├── Route │ │ │ ├── FeaturedProduct │ │ │ │ └── FeaturedProduct.jsx │ │ │ ├── BestDeals │ │ │ │ └── BestDeals.jsx │ │ │ ├── Hero │ │ │ │ └── Hero.jsx │ │ │ ├── Categories │ │ │ │ └── Categories.jsx │ │ │ └── Sponsored.jsx │ │ ├── Products │ │ │ ├── Ratings.jsx │ │ │ └── SuggestedProduct.jsx │ │ ├── Checkout │ │ │ └── CheckoutSteps.jsx │ │ ├── Profile │ │ │ ├── TrackOrder.jsx │ │ │ └── ProfileSidebar.jsx │ │ ├── Admin │ │ │ ├── AllEvents.jsx │ │ │ ├── AllProducts.jsx │ │ │ ├── AllUsers.jsx │ │ │ ├── AllWithdraw.jsx │ │ │ ├── AllSellers.jsx │ │ │ └── Layout │ │ │ │ └── AdminSideBar.jsx │ │ ├── Shop │ │ │ ├── Layout │ │ │ │ └── DashboardHeader.jsx │ │ │ ├── AllOrders.jsx │ │ │ ├── AllProducts.jsx │ │ │ ├── AllRefundOrders.jsx │ │ │ ├── AllEvents.jsx │ │ │ └── ShopInfo.jsx │ │ └── Wishlist │ │ │ └── Wishlist.jsx │ ├── styles │ │ └── styles.js │ ├── App.css │ └── logo.svg ├── tailwind.config.js └── package.json ├── backend ├── middleware │ ├── catchAsyncErrors.js │ ├── error.js │ └── auth.js ├── utils │ ├── ErrorHandler.js │ ├── jwtToken.js │ ├── shopToken.js │ └── sendMail.js ├── db │ └── Database.js ├── model │ ├── messages.js │ ├── conversation.js │ ├── withdraw.js │ ├── coupounCode.js │ ├── order.js │ ├── product.js │ ├── event.js │ ├── user.js │ └── shop.js ├── multer.js ├── package.json ├── controller │ ├── payment.js │ ├── message.js │ ├── coupounCode.js │ ├── conversation.js │ ├── withdraw.js │ ├── event.js │ └── product.js └── server.js └── .gitignore /socket/.env: -------------------------------------------------------------------------------- 1 | PORT = 4000 -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Front_End Learning 2 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pattjoshi/Multi_vondor_E_shop/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pattjoshi/Multi_vondor_E_shop/HEAD/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pattjoshi/Multi_vondor_E_shop/HEAD/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/src/ShopRoutes.js: -------------------------------------------------------------------------------- 1 | 2 | import ShopHomePage from "../src/pages/Shop/ShopHomePage"; 3 | 4 | export { ShopHomePage }; 5 | -------------------------------------------------------------------------------- /frontend/src/server.js: -------------------------------------------------------------------------------- 1 | export const server = "http://localhost:8000/api/v2"; 2 | 3 | export const backend_url = "http://localhost:8000/"; 4 | -------------------------------------------------------------------------------- /backend/middleware/catchAsyncErrors.js: -------------------------------------------------------------------------------- 1 | module.exports = (theFunc) => (req, res, next) => { 2 | Promise.resolve(theFunc(req, res, next)).catch(next); 3 | }; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | backend/config/.env 3 | backend/uploads 4 | frontend/node_modules 5 | backend/node_modules 6 | socket/node_modules 7 | 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* -------------------------------------------------------------------------------- /frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /backend/utils/ErrorHandler.js: -------------------------------------------------------------------------------- 1 | class ErrorHandler extends Error { 2 | constructor(message, statusCode) { 3 | super(message); 4 | this.statusCode = statusCode; 5 | 6 | Error.captureStackTrace(this, this.constructor); 7 | } 8 | } 9 | module.exports = ErrorHandler; 10 | -------------------------------------------------------------------------------- /frontend/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /backend/db/Database.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const connectDatabase = () => { 4 | mongoose 5 | .connect(process.env.DB_URL, { 6 | useNewUrlParser: true, 7 | useUnifiedTopology: true, 8 | }) 9 | .then((data) => { 10 | console.log(`mongod connected with server: ${data.connection.host}`); 11 | }); 12 | }; 13 | 14 | module.exports = connectDatabase; 15 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import reportWebVitals from "./reportWebVitals"; 5 | 6 | import { Provider } from "react-redux"; 7 | import Store from "./redux/store"; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById("root") 14 | ); 15 | 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /frontend/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /backend/model/messages.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const messagesSchema = new mongoose.Schema( 4 | { 5 | conversationId: { 6 | type: String, 7 | }, 8 | text: { 9 | type: String, 10 | }, 11 | sender: { 12 | type: String, 13 | }, 14 | images: { 15 | type: String, 16 | }, 17 | }, 18 | { timestamps: true } 19 | ); 20 | 21 | module.exports = mongoose.model("Messages", messagesSchema); 22 | -------------------------------------------------------------------------------- /frontend/src/pages/TrackOrderPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Header from '../components/Layout/Header' 3 | import Footer from '../components/Layout/Footer' 4 | import TrackOrder from "../components/Profile/TrackOrder"; 5 | 6 | const TrackOrderPage = () => { 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 | ) 14 | } 15 | 16 | export default TrackOrderPage -------------------------------------------------------------------------------- /frontend/src/routes/ProtectedRoute.js: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { Navigate } from "react-router-dom"; 3 | 4 | const ProtectedRoute = ({ children }) => { 5 | const { loading, isAuthenticated } = useSelector((state) => state.user); 6 | if (loading === false) { 7 | if (!isAuthenticated) { 8 | return ; 9 | } 10 | return children; 11 | } 12 | }; 13 | 14 | export default ProtectedRoute; 15 | -------------------------------------------------------------------------------- /frontend/src/pages/OrderDetailsPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Header from '../components/Layout/Header' 3 | import Footer from '../components/Layout/Footer' 4 | import UserOrderDetails from "../components/UserOrderDetails"; 5 | 6 | const OrderDetailsPage = () => { 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 | ) 14 | } 15 | 16 | export default OrderDetailsPage -------------------------------------------------------------------------------- /backend/model/conversation.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const conversationSchema = new mongoose.Schema( 4 | { 5 | groupTitle: { 6 | type: String, 7 | }, 8 | members: { 9 | type: Array, 10 | }, 11 | lastMessage: { 12 | type: String, 13 | }, 14 | lastMessageId: { 15 | type: String, 16 | }, 17 | }, 18 | { timestamps: true } 19 | ); 20 | 21 | module.exports = mongoose.model("Conversation", conversationSchema); 22 | -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopOrderDetails.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import Footer from '../../components/Layout/Footer' 4 | import OrderDetails from "../../components/Shop/OrderDetails"; 5 | 6 | const ShopOrderDetails = () => { 7 | return ( 8 |
9 | 10 | 11 |
12 |
13 | ) 14 | } 15 | 16 | export default ShopOrderDetails -------------------------------------------------------------------------------- /socket/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js" 9 | }, 10 | "author": "omprakash", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "dotenv": "^16.0.3", 15 | "express": "^4.18.2", 16 | "nodemon": "^2.0.22", 17 | "socket.io": "^4.6.1" 18 | } 19 | } -------------------------------------------------------------------------------- /backend/model/withdraw.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const withdrawSchema = new mongoose.Schema({ 4 | seller: { 5 | type: Object, 6 | required: true, 7 | }, 8 | amount: { 9 | type: Number, 10 | required: true, 11 | }, 12 | status: { 13 | type: String, 14 | default: "Processing", 15 | }, 16 | createdAt: { 17 | type: Date, 18 | default: Date.now(), 19 | }, 20 | updatedAt: { 21 | type: Date, 22 | }, 23 | }); 24 | 25 | module.exports = mongoose.model("Withdraw", withdrawSchema); 26 | -------------------------------------------------------------------------------- /backend/multer.js: -------------------------------------------------------------------------------- 1 | const multer = require("multer"); 2 | const path = require("path"); 3 | 4 | const storage = multer.diskStorage({ 5 | destination: function (req, res, cb) { 6 | cb(null, path.join(__dirname, "./uploads")); 7 | }, 8 | filename: function (req, file, cb) { 9 | const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9); 10 | const filename = file.originalname.split(".")[0]; 11 | cb(null, filename + "-" + uniqueSuffix + ".png"); 12 | }, 13 | }); 14 | 15 | exports.upload = multer({ storage: storage }); 16 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{html,js,jsx}"], 4 | mode: "jit", 5 | theme: { 6 | fontFamily: { 7 | Roboto: ["Roboto", "sans-serif"], 8 | Poppins: ["Poppins", "sans-serif"], 9 | }, 10 | extend: { 11 | screens: { 12 | "1000px": "1050px", 13 | "1100px": "1110px", 14 | "800px": "800px", 15 | "1300px": "1300px", 16 | "400px": "400px", 17 | }, 18 | }, 19 | }, 20 | plugins: [], 21 | }; 22 | -------------------------------------------------------------------------------- /backend/utils/jwtToken.js: -------------------------------------------------------------------------------- 1 | // Create token and saving the in cookies and send response 2 | 3 | const sendToken = (user, statusCode, res) => { 4 | const token = user.getJwtToken(); 5 | 6 | // Options for cookies 7 | const options = { 8 | expires: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), 9 | httpOnly: true, 10 | sameSite: "none", 11 | secure: true, 12 | }; 13 | 14 | res.status(statusCode).cookie("token", token, options).json({ 15 | success: true, 16 | user, 17 | token, 18 | }); 19 | }; 20 | module.exports = sendToken; 21 | -------------------------------------------------------------------------------- /backend/utils/shopToken.js: -------------------------------------------------------------------------------- 1 | // create token and saving that in cookies 2 | const sendShopToken = (user, statusCode, res) => { 3 | const token = user.getJwtToken(); 4 | 5 | // Options for cookies 6 | const options = { 7 | expires: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), 8 | httpOnly: true, 9 | sameSite: "none", 10 | secure: true, 11 | }; 12 | 13 | res.status(statusCode).cookie("seller_token", token, options).json({ 14 | success: true, 15 | user, 16 | token, 17 | }); 18 | }; 19 | 20 | module.exports = sendShopToken; 21 | -------------------------------------------------------------------------------- /frontend/src/routes/ProtectedAdminRoute.js: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { Navigate } from "react-router-dom"; 3 | 4 | const ProtectedAdminRoute = ({ children }) => { 5 | const { loading, isAuthenticated, user } = useSelector((state) => state.user); 6 | if (loading === false) { 7 | if (!isAuthenticated) { 8 | return ; 9 | } else if (user.role !== "Admin") { 10 | return ; 11 | } 12 | return children; 13 | } 14 | }; 15 | 16 | export default ProtectedAdminRoute; 17 | -------------------------------------------------------------------------------- /frontend/src/routes/SellerProtectedRoute.js: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { Navigate } from "react-router-dom"; 3 | import Loader from "../components/Layout/Loader"; 4 | 5 | const SellerProtectedRoute = ({ children }) => { 6 | const { isLoading, isSeller } = useSelector((state) => state.seller); 7 | if (isLoading === true) { 8 | return ; 9 | } else { 10 | if (!isSeller) { 11 | return ; 12 | } 13 | return children; 14 | } 15 | }; 16 | 17 | export default SellerProtectedRoute; 18 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/pages/CheckoutPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Header from '../components/Layout/Header' 3 | import CheckoutSteps from "../components/Checkout/CheckoutSteps"; 4 | import Checkout from "../components/Checkout/Checkout"; 5 | import Footer from '../components/Layout/Footer'; 6 | 7 | const CheckoutPage = () => { 8 | return ( 9 |
10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default CheckoutPage -------------------------------------------------------------------------------- /frontend/src/pages/LoginPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import Login from '../components/Login/Login' 3 | import { useSelector } from 'react-redux'; 4 | import { useNavigate } from 'react-router-dom'; 5 | 6 | const LoginPage = () => { 7 | const navigate = useNavigate(); 8 | const { isAuthenticated } = useSelector((state) => state.user); 9 | // if user is login then redirect to home page 10 | useEffect(() => { 11 | if (isAuthenticated) { 12 | navigate("/"); 13 | } 14 | 15 | }) 16 | return ( 17 |
18 | 19 |
20 | ) 21 | } 22 | 23 | export default LoginPage -------------------------------------------------------------------------------- /frontend/src/pages/SignupPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import Signup from '../components/Signup/Signup' 3 | import { useNavigate } from 'react-router-dom'; 4 | import { useSelector } from 'react-redux'; 5 | 6 | const SignupPage = () => { 7 | 8 | const navigate = useNavigate(); 9 | const { isAuthenticated } = useSelector((state) => state.user); 10 | // if user is login then redirect to home page 11 | useEffect(() => { 12 | if (isAuthenticated) { 13 | navigate("/"); 14 | } 15 | }) 16 | 17 | return ( 18 |
19 | 20 |
21 | ) 22 | } 23 | 24 | export default SignupPage -------------------------------------------------------------------------------- /frontend/src/redux/actions/sellers.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { server } from "../../server"; 3 | 4 | // get all sellers --- admin 5 | export const getAllSellers = () => async (dispatch) => { 6 | try { 7 | dispatch({ 8 | type: "getAllSellersRequest", 9 | }); 10 | 11 | const { data } = await axios.get(`${server}/shop/admin-all-sellers`, { 12 | withCredentials: true, 13 | }); 14 | 15 | dispatch({ 16 | type: "getAllSellersSuccess", 17 | payload: data.sellers, 18 | }); 19 | } catch (error) { 20 | dispatch({ 21 | type: "getAllSellerFailed", 22 | // payload: error.response.data.message, 23 | }); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /frontend/src/pages/PaymentPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CheckoutSteps from '../components/Checkout/CheckoutSteps' 3 | import Footer from '../components/Layout/Footer' 4 | import Header from '../components/Layout/Header' 5 | import Payment from "../components/Payment/Payment.jsx"; 6 | 7 | const PaymentPage = () => { 8 | return ( 9 |
10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default PaymentPage -------------------------------------------------------------------------------- /frontend/src/components/Layout/Loader.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Lottie from "react-lottie"; 3 | import animationData from "../../Assests/animations/24151-ecommerce-animation.json"; 4 | 5 | const Loader = () => { 6 | const defaultOptions = { 7 | loop: true, 8 | autoplay: true, 9 | animationData: animationData, 10 | rendererSettings: { 11 | preserveAspectRatio: "xMidYMid slice", 12 | }, 13 | }; 14 | return ( 15 |
16 | 17 |
18 | ); 19 | }; 20 | 21 | export default Loader; -------------------------------------------------------------------------------- /frontend/src/redux/actions/wishlist.js: -------------------------------------------------------------------------------- 1 | // add to wishlist 2 | export const addToWishlist = (data) => async (dispatch, getState) => { 3 | dispatch({ 4 | type: "addToWishlist", 5 | payload: data, 6 | }); 7 | 8 | localStorage.setItem( 9 | "wishlistItems", 10 | JSON.stringify(getState().wishlist.wishlist) 11 | ); 12 | return data; 13 | }; 14 | 15 | // remove from wishlist 16 | export const removeFromWishlist = (data) => async (dispatch, getState) => { 17 | dispatch({ 18 | type: "removeFromWishlist", 19 | payload: data._id, 20 | }); 21 | localStorage.setItem( 22 | "wishlistItems", 23 | JSON.stringify(getState().wishlist.wishlist) 24 | ); 25 | return data; 26 | }; 27 | -------------------------------------------------------------------------------- /frontend/src/pages/ShopCreate.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import ShopCreate from "../components/Shop/ShopCreate"; 3 | import { useNavigate } from 'react-router-dom'; 4 | import { useSelector } from 'react-redux'; 5 | 6 | const ShopCreatePage = () => { 7 | const navigate = useNavigate(); 8 | const { isSeller, seller } = useSelector((state) => state.seller); 9 | // if user is login then redirect to home page 10 | useEffect(() => { 11 | if (isSeller === true) { 12 | navigate(`/shop/${seller._id}`); 13 | } 14 | }) 15 | return ( 16 |
17 | 18 |
19 | ) 20 | } 21 | 22 | export default ShopCreatePage -------------------------------------------------------------------------------- /backend/model/coupounCode.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const coupounCodeSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: [true, "Please enter your coupoun code name!"], 7 | unique: true, 8 | }, 9 | value: { 10 | type: Number, 11 | required: true, 12 | }, 13 | minAmount: { 14 | type: Number, 15 | }, 16 | maxAmount: { 17 | type: Number, 18 | }, 19 | shopId: { 20 | type: String, 21 | required: true, 22 | }, 23 | selectedProduct: { 24 | type: String, 25 | }, 26 | createdAt: { 27 | type: Date, 28 | default: Date.now(), 29 | }, 30 | }); 31 | 32 | module.exports = mongoose.model("CoupounCode", coupounCodeSchema); 33 | -------------------------------------------------------------------------------- /frontend/src/pages/ShopLoginPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import ShopLogin from "../components/Shop/ShopLogin"; 3 | import { useNavigate } from 'react-router-dom'; 4 | import { useSelector } from 'react-redux'; 5 | 6 | const ShopLoginPage = () => { 7 | const navigate = useNavigate(); 8 | const { isSeller, isLoading } = useSelector((state) => state.seller); 9 | // if user is login then redirect to home page 10 | useEffect(() => { 11 | if (isSeller === true) { 12 | navigate(`/dashboard`); 13 | } 14 | }, [isLoading, isSeller]) 15 | return ( 16 |
17 | 18 |
19 | ) 20 | } 21 | 22 | export default ShopLoginPage -------------------------------------------------------------------------------- /frontend/src/pages/AdminDashboardUsers.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AdminHeader from "../components/Layout/AdminHeader"; 3 | import AdminSideBar from "../components/Admin/Layout/AdminSideBar"; 4 | import AllUsers from "../components/Admin/AllUsers"; 5 | 6 | const AdminDashboardUsers = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | ); 20 | }; 21 | 22 | export default AdminDashboardUsers; 23 | -------------------------------------------------------------------------------- /backend/utils/sendMail.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | 3 | const sendMail = async (options) => { 4 | const transporter = nodemailer.createTransport({ 5 | host: process.env.SMPT_HOST, 6 | port: process.env.SMPT_PORT, 7 | service: process.env.SMPT_SERVICE, 8 | auth: { 9 | user: process.env.SMPT_MAIL, 10 | pass: process.env.SMPT_PASSWORD, 11 | }, 12 | tls: { 13 | rejectUnauthorized: false, 14 | }, 15 | }); 16 | 17 | const mailOptions = { 18 | from: process.env.SMPT_MAIL, 19 | to: options.email, 20 | subject: options.subject, 21 | text: options.message, 22 | }; 23 | 24 | await transporter.sendMail(mailOptions); 25 | }; 26 | 27 | module.exports = sendMail; 28 | -------------------------------------------------------------------------------- /frontend/src/pages/AdminDashboardEvents.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AdminHeader from "../components/Layout/AdminHeader"; 3 | import AdminSideBar from "../components/Admin/Layout/AdminSideBar"; 4 | import AllEvents from "../components/Admin/AllEvents"; 5 | 6 | const AdminDashboardEvents = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | ); 20 | }; 21 | 22 | export default AdminDashboardEvents; 23 | -------------------------------------------------------------------------------- /frontend/src/pages/AdminDashboardSellers.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AdminHeader from "../components/Layout/AdminHeader"; 3 | import AdminSideBar from "../components/Admin/Layout/AdminSideBar"; 4 | import AllSellers from "../components/Admin/AllSellers"; 5 | 6 | const AdminDashboardSellers = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | ); 20 | }; 21 | 22 | export default AdminDashboardSellers; 23 | -------------------------------------------------------------------------------- /frontend/src/routes/AdminRoutes.js: -------------------------------------------------------------------------------- 1 | import AdminDashboardPage from "../pages/AdminDashboardPage"; 2 | import AdminDashboardUsers from "../pages/AdminDashboardUsers"; 3 | import AdminDashboardSellers from "../pages/AdminDashboardSellers"; 4 | import AdminDashboardOrders from "../pages/AdminDashboardOrders"; 5 | import AdminDashboardProducts from "../pages/AdminDashboardProducts"; 6 | import AdminDashboardEvents from "../pages/AdminDashboardEvents"; 7 | import AdminDashboardWithdraw from "../pages/AdminDashboardWithdraw"; 8 | 9 | export { 10 | AdminDashboardPage, 11 | AdminDashboardUsers, 12 | AdminDashboardSellers, 13 | AdminDashboardOrders, 14 | AdminDashboardProducts, 15 | AdminDashboardEvents, 16 | AdminDashboardWithdraw, 17 | }; 18 | -------------------------------------------------------------------------------- /frontend/src/pages/AdminDashboardProducts.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AdminHeader from "../components/Layout/AdminHeader"; 3 | import AdminSideBar from "../components/Admin/Layout/AdminSideBar"; 4 | import AllProducts from "../components/Admin/AllProducts"; 5 | 6 | const AdminDashboardProducts = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | ); 20 | }; 21 | 22 | export default AdminDashboardProducts; 23 | -------------------------------------------------------------------------------- /frontend/src/pages/AdminDashboardWithdraw.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AdminHeader from "../components/Layout/AdminHeader"; 3 | import AdminSideBar from "../components/Admin/Layout/AdminSideBar"; 4 | import AllWithdraw from "../components/Admin/AllWithdraw"; 5 | 6 | const AdminDashboardWithdraw = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | ); 20 | }; 21 | 22 | export default AdminDashboardWithdraw; 23 | -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopInboxPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar' 4 | import DashboardMessages from "../../components/Shop/DashboardMessages"; 5 | 6 | const ShopInboxPage = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 | 15 |
16 |
17 | ) 18 | } 19 | 20 | export default ShopInboxPage -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopSettingsPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ShopSettings from "../../components/Shop/ShopSettings"; 3 | import DashboardHeader from "../../components/Shop/Layout/DashboardHeader"; 4 | import DashboardSideBar from "../../components/Shop/Layout/DashboardSideBar"; 5 | 6 | const ShopSettingsPage = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 | 15 |
16 |
17 | ); 18 | }; 19 | 20 | export default ShopSettingsPage; -------------------------------------------------------------------------------- /frontend/src/pages/AdminDashboardPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AdminHeader from "../components/Layout/AdminHeader"; 3 | import AdminSideBar from "../components/Admin/Layout/AdminSideBar.jsx"; 4 | import AdminDashboardMain from "../components/Admin/AdminDashboardMain.jsx"; 5 | 6 | const AdminDashboardPage = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | ); 20 | }; 21 | 22 | export default AdminDashboardPage; 23 | -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopWithDrawMoneyPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import WithdrawMoney from "../../components/Shop/WithdrawMoney"; 4 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar'; 5 | 6 | const ShopWithDrawMoneyPage = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 | 15 |
16 |
17 | ) 18 | } 19 | 20 | export default ShopWithDrawMoneyPage -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopDashboardPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DashboardHeader from "../../components/Shop/Layout/DashboardHeader"; 3 | import DashboardSideBar from "../../components/Shop/Layout/DashboardSideBar"; 4 | import DashboardHero from "../../components/Shop/DashboardHero"; 5 | 6 | 7 | 8 | const ShopDashboardPage = () => { 9 | return ( 10 |
11 | 12 |
13 |
14 | 15 |
16 | 17 |
18 |
19 | ); 20 | }; 21 | 22 | export default ShopDashboardPage; -------------------------------------------------------------------------------- /frontend/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import { userReducer } from "./reducers/user"; 3 | import { sellerReducer } from "./reducers/seller"; 4 | import { productReducer } from "./reducers/product"; 5 | import { eventReducer } from "./reducers/event"; 6 | import { cartReducer } from "./reducers/cart"; 7 | import { wishlistReducer } from "./reducers/wishlist"; 8 | import { orderReducer } from "./reducers/order"; 9 | 10 | const Store = configureStore({ 11 | reducer: { 12 | // Reducers 13 | user: userReducer, 14 | seller: sellerReducer, 15 | products: productReducer, 16 | events: eventReducer, 17 | cart: cartReducer, 18 | wishlist: wishlistReducer, 19 | order: orderReducer, 20 | }, 21 | }); 22 | 23 | export default Store; 24 | -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopAllEvents.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar' 4 | import AllEvents from "../../components/Shop/AllEvents"; 5 | 6 | const ShopAllEvents = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default ShopAllEvents -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopAllOrders.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar' 4 | import AllOrders from "../../components/Shop/AllOrders"; 5 | 6 | const ShopAllOrders = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default ShopAllOrders -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopAllCoupouns.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar' 4 | import AllCoupons from "../../components/Shop/AllCoupons"; 5 | 6 | const ShopAllCoupouns = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default ShopAllCoupouns -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopAllProducts.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar' 4 | import AllProducts from "../../components/Shop/AllProducts"; 5 | 6 | const ShopAllProducts = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default ShopAllProducts -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopCreateEvents.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import CreateEvent from "../../components/Shop/CreateEvent"; 4 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar'; 5 | 6 | const ShopCreateEvents = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default ShopCreateEvents -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopAllRefunds.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar' 4 | import AllRefundOrders from "../../components/Shop/AllRefundOrders"; 5 | 6 | const ShopAllRefunds = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default ShopAllRefunds -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-shop-multi-vendor", 3 | "version": "1.0.0", 4 | "main": "server.js", 5 | "engines": { 6 | "node": "18.16.0" 7 | }, 8 | "scripts": { 9 | "dev": "nodemon server.js", 10 | "start": "node server.js", 11 | "heroku-postbuild": "NPM_CONFIG_PRODUCTION=false" 12 | }, 13 | "author": "Om Prakash Pattjoshi", 14 | "license": "ISC", 15 | "dependencies": { 16 | "bcrypt": "^5.1.0", 17 | "bcryptjs": "^2.4.3", 18 | "cookie-parser": "^1.4.6", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.0.3", 21 | "express": "^4.18.2", 22 | "js-cookie": "^3.0.1", 23 | "jsonwebtoken": "^9.0.0", 24 | "mongoose": "^7.0.0", 25 | "multer": "^1.4.5-lts.1", 26 | "nodemailer": "^6.9.1", 27 | "nodemon": "^2.0.20", 28 | "stripe": "^12.0.0" 29 | }, 30 | "description": "" 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopCreateProduct.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DashboardHeader from '../../components/Shop/Layout/DashboardHeader' 3 | import DashboardSideBar from '../../components/Shop/Layout/DashboardSideBar' 4 | import CreateProduct from "../../components/Shop/CreateProduct"; 5 | 6 | const ShopCreateProduct = () => { 7 | return ( 8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default ShopCreateProduct -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopHomePage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "../../styles/styles"; 3 | import ShopInfo from "../../components/Shop/ShopInfo"; 4 | import ShopProfileData from "../../components/Shop/ShopProfileData"; 5 | 6 | const ShopHomePage = () => { 7 | return ( 8 |
9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 | ); 19 | }; 20 | 21 | export default ShopHomePage; 22 | -------------------------------------------------------------------------------- /frontend/src/pages/Shop/ShopPreviewPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "../../styles/styles"; 3 | import ShopInfo from "../../components/Shop/ShopInfo"; 4 | import ShopProfileData from "../../components/Shop/ShopProfileData"; 5 | 6 | const ShopPreviewPage = () => { 7 | return ( 8 |
9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 | ); 19 | }; 20 | 21 | export default ShopPreviewPage; 22 | -------------------------------------------------------------------------------- /frontend/src/pages/ProfilePage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import Header from '../components/Layout/Header' 3 | import styles from "../styles/styles"; 4 | import ProfileSideBar from "../components/Profile/ProfileSidebar"; 5 | import ProfileContent from "../components/Profile/ProfileContent"; 6 | 7 | 8 | const ProfilePage = () => { 9 | const [active, setActive] = useState(1); 10 | return ( 11 |
12 |
13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 | ) 21 | } 22 | 23 | export default ProfilePage -------------------------------------------------------------------------------- /frontend/src/pages/EventsPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector } from "react-redux"; 3 | import EventCard from "../components/Events/EventCard"; 4 | import Header from "../components/Layout/Header"; 5 | import Loader from "../components/Layout/Loader"; 6 | 7 | const EventsPage = () => { 8 | const { allEvents, isLoading } = useSelector((state) => state.events); 9 | return ( 10 | <> 11 | {isLoading ? ( 12 | 13 | ) : ( 14 |
15 |
16 | {allEvents.length !== 0 ? ( 17 | allEvents.map((event) => ( 18 | 19 | )) 20 | ) : ( 21 |

No events available.

22 | )} 23 |
24 | )} 25 | 26 | ); 27 | }; 28 | 29 | export default EventsPage; 30 | -------------------------------------------------------------------------------- /backend/controller/payment.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 4 | 5 | const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY); 6 | 7 | router.post( 8 | "/process", 9 | catchAsyncErrors(async (req, res, next) => { 10 | const myPayment = await stripe.paymentIntents.create({ 11 | amount: req.body.amount, 12 | currency: "inr", 13 | metadata: { 14 | company: "Omprakash", 15 | }, 16 | }); 17 | res.status(200).json({ 18 | success: true, 19 | client_secret: myPayment.client_secret, 20 | }); 21 | }) 22 | ); 23 | 24 | router.get( 25 | "/stripeapikey", 26 | catchAsyncErrors(async (req, res, next) => { 27 | res.status(200).json({ stripeApikey: process.env.STRIPE_API_KEY }); 28 | }) 29 | ); 30 | 31 | module.exports = router; 32 | -------------------------------------------------------------------------------- /frontend/src/components/Layout/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | import { navItems } from '../../static/data' 4 | import styles from '../../styles/styles' 5 | 6 | 7 | const Navbar = ({ active }) => { 8 | return ( 9 |
10 | { 11 | navItems.map((i, index) => ( 12 |
13 | 16 | {i.title} 17 | 18 |
19 | )) 20 | } 21 |
22 | ) 23 | } 24 | 25 | export default Navbar -------------------------------------------------------------------------------- /frontend/src/components/Events/Events.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import styles from "../../styles/styles"; 4 | import EventCard from "./EventCard"; 5 | 6 | const Events = () => { 7 | const { allEvents, isLoading } = useSelector((state) => state.events); 8 | 9 | return ( 10 |
11 | {!isLoading && ( 12 |
13 |
14 |

Popular Events

15 |
16 | 17 |
18 | {allEvents.length !== 0 && ( 19 | 20 | )} 21 |

{allEvents?.length === 0 && "No Events have!"}

22 |
23 |
24 | )} 25 |
26 | ); 27 | }; 28 | 29 | export default Events; 30 | -------------------------------------------------------------------------------- /frontend/src/redux/actions/cart.js: -------------------------------------------------------------------------------- 1 | // add To cart 2 | 3 | export const addTocart = (data) => async (dispatch, getState) => { 4 | dispatch({ 5 | type: "addToCart", 6 | payload: data, 7 | }); 8 | 9 | /* `localStorage.setItem("cartItems", JSON.stringify(getState().cart.cart));` is storing the cart 10 | items in the browser's local storage. */ 11 | localStorage.setItem("cartItems", JSON.stringify(getState().cart.cart)); 12 | return data; 13 | }; 14 | 15 | // Remove From cart 16 | export const removeFromCart = (data) => async (dispatch, getState) => { 17 | dispatch({ 18 | type: "removeFromCart", 19 | payload: data._id, 20 | }); 21 | localStorage.setItem("cartItems", JSON.stringify(getState().cart.cart)); 22 | return data; 23 | }; 24 | 25 | // Trigger an event , and call reducer 26 | // What is dispatch? 27 | // The dispatch function is typically used to send messages to objects that are part of a larger application 28 | -------------------------------------------------------------------------------- /frontend/src/pages/HomePage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Header from "../components/Layout/Header"; 3 | import Hero from '../components/Route/Hero/Hero'; 4 | import Categories from "../components/Route/Categories/Categories"; 5 | import BestDeals from "../components/Route/BestDeals/BestDeals"; 6 | import Events from "../components/Events/Events"; 7 | import FeaturedProduct from "../components/Route/FeaturedProduct/FeaturedProduct"; 8 | import Sponsored from "../components/Route/Sponsored"; 9 | import Footer from "../components/Layout/Footer"; 10 | 11 | 12 | const HomePage = () => { 13 | return ( 14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | ) 25 | } 26 | 27 | export default HomePage -------------------------------------------------------------------------------- /backend/model/order.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const orderSchema = new mongoose.Schema({ 4 | cart: { 5 | type: Array, 6 | required: true, 7 | }, 8 | shippingAddress: { 9 | type: Object, 10 | required: true, 11 | }, 12 | user: { 13 | type: Object, 14 | required: true, 15 | }, 16 | totalPrice: { 17 | type: Number, 18 | required: true, 19 | }, 20 | status: { 21 | type: String, 22 | default: "Processing", 23 | }, 24 | paymentInfo: { 25 | id: { 26 | type: String, 27 | }, 28 | status: { 29 | type: String, 30 | }, 31 | type: { 32 | type: String, 33 | }, 34 | }, 35 | paidAt: { 36 | type: Date, 37 | default: Date.now(), 38 | }, 39 | deliveredAt: { 40 | type: Date, 41 | }, 42 | createdAt: { 43 | type: Date, 44 | default: Date.now(), 45 | }, 46 | }); 47 | 48 | module.exports = mongoose.model("Order", orderSchema); 49 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/wishlist.js: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | wishlist: localStorage.getItem("wishlistItems") 5 | ? JSON.parse(localStorage.getItem("wishlistItems")) 6 | : [], 7 | }; 8 | 9 | export const wishlistReducer = createReducer(initialState, { 10 | addToWishlist: (state, action) => { 11 | const item = action.payload; 12 | const isItemExist = state.wishlist.find((i) => i._id === item._id); 13 | if (isItemExist) { 14 | return { 15 | ...state, 16 | wishlist: state.wishlist.map((i) => 17 | i._id === isItemExist._id ? item : i 18 | ), 19 | }; 20 | } else { 21 | return { 22 | ...state, 23 | wishlist: [...state.wishlist, item], 24 | }; 25 | } 26 | }, 27 | 28 | removeFromWishlist: (state, action) => { 29 | return { 30 | ...state, 31 | wishlist: state.wishlist.filter((i) => i._id !== action.payload), 32 | }; 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /frontend/src/components/Route/FeaturedProduct/FeaturedProduct.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import styles from "../../../styles/styles"; 4 | import ProductCard from "../ProductCard/ProductCard"; 5 | 6 | const FeaturedProduct = () => { 7 | const { allProducts } = useSelector((state) => state.products); 8 | 9 | return ( 10 |
11 |
12 |
13 |

Featured Products

14 |
15 |
16 | {allProducts && allProducts.length !== 0 && ( 17 | <> 18 | {allProducts && 19 | allProducts.map((i, index) => ( 20 | 21 | ))} 22 | 23 | )} 24 |
25 |
26 |
27 | ); 28 | }; 29 | 30 | export default FeaturedProduct; 31 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/seller.js: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | isLoading: true, 5 | }; 6 | 7 | export const sellerReducer = createReducer(initialState, { 8 | LoadSellerRequest: (state) => { 9 | state.isLoading = true; 10 | }, 11 | LoadSellerSuccess: (state, action) => { 12 | state.isSeller = true; 13 | state.isLoading = false; 14 | state.seller = action.payload; 15 | }, 16 | LoadSellerFail: (state, action) => { 17 | state.isLoading = false; 18 | state.error = action.payload; 19 | state.isSeller = false; 20 | }, 21 | // get all sellers ---admin 22 | getAllSellersRequest: (state) => { 23 | state.isLoading = true; 24 | }, 25 | getAllSellersSuccess: (state, action) => { 26 | state.isLoading = false; 27 | state.sellers = action.payload; 28 | }, 29 | getAllSellerFailed: (state, action) => { 30 | state.isLoading = false; 31 | state.error = action.payload; 32 | }, 33 | 34 | clearErrors: (state) => { 35 | state.error = null; 36 | }, 37 | }); 38 | 39 | // reducer -> logic (state change) 40 | -------------------------------------------------------------------------------- /frontend/src/styles/styles.js: -------------------------------------------------------------------------------- 1 | const styles = { 2 | custom_container: "w-11/12 hidden sm:block", 3 | heading: 4 | "text-[27px] text-center md:text-start font-[600] font-Roboto pb-[20px]", 5 | section: "w-11/12 mx-auto", 6 | productTitle: "text-[25px] font-[600] font-Roboto text-[#333]", 7 | productDiscountPrice: "font-bold text-[18px] text-[#333] font-Roboto", 8 | price: "font-[500] text-[16px] text-[#d55b45] pl-3 mt-[-4px] line-through", 9 | shop_name: "pt-3 text-[15px] text-blue-400 pb-3", 10 | active_indicator: "absolute bottom-[-27%] left-0 h-[3px] w-full bg-[crimson]", 11 | button: 12 | "w-[150px] bg-black h-[50px] my-3 flex items-center justify-center rounded-xl cursor-pointer", 13 | cart_button: 14 | "px-[20px] h-[38px] rounded-[20px] bg-[#f63b60] flex items-center justify-center cursor-pointer", 15 | cart_button_text: "text-[#fff] text-[16px] font-[600]", 16 | input: "w-full border p-1 rounded-[5px]", 17 | activeStatus: 18 | "w-[10px] h-[10px] rounded-full absolute top-0 right-1 bg-[#40d132]", 19 | noramlFlex: "flex items-center", 20 | }; 21 | 22 | export default styles; 23 | -------------------------------------------------------------------------------- /frontend/src/pages/OrderSuccessPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Footer from "../components/Layout/Footer"; 3 | import Header from "../components/Layout/Header"; 4 | import Lottie from "react-lottie"; 5 | import animationData from "../Assests/animations/107043-success.json"; 6 | 7 | const OrderSuccessPage = () => { 8 | return ( 9 |
10 |
11 | 12 |
13 |
14 | ); 15 | }; 16 | 17 | const Success = () => { 18 | const defaultOptions = { 19 | loop: false, 20 | autoplay: true, 21 | animationData: animationData, 22 | rendererSettings: { 23 | preserveAspectRatio: "xMidYMid slice", 24 | }, 25 | }; 26 | return ( 27 |
28 | 29 |
30 | Your order is successful 😍 31 |
32 |
33 |
34 |
35 | ); 36 | }; 37 | 38 | export default OrderSuccessPage; -------------------------------------------------------------------------------- /backend/middleware/error.js: -------------------------------------------------------------------------------- 1 | const ErrorHandler = require("../utils/ErrorHandler"); 2 | 3 | module.exports = (err, req, res, next) => { 4 | err.statusCode = err.statusCode || 500; 5 | err.message = err.message || "Internal server Error"; 6 | 7 | // wrong mongodb id error 8 | if (err.name === "CastError") { 9 | const message = `Resources not found with this id.. Invalid ${err.path}`; 10 | err = new ErrorHandler(message, 400); 11 | } 12 | 13 | // Duplicate key error 14 | if (err.code === 11000) { 15 | const message = `Duplicate key ${Object.keys(err.keyValue)} Entered`; 16 | err = new ErrorHandler(message, 400); 17 | } 18 | 19 | // wrong jwt error 20 | if (err.name === "JsonWebTokenError") { 21 | const message = `Your url is invalid please try again letter`; 22 | err = new ErrorHandler(message, 400); 23 | } 24 | 25 | // jwt expired 26 | if (err.name === "TokenExpiredError") { 27 | const message = `Your Url is expired please try again letter!`; 28 | err = new ErrorHandler(message, 400); 29 | } 30 | 31 | res.status(err.statusCode).json({ 32 | success: false, 33 | message: err.message, 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /frontend/src/routes/ShopRoutes.js: -------------------------------------------------------------------------------- 1 | import ShopDashboardPage from "../pages/Shop/ShopDashboardPage"; 2 | import ShopCreateProduct from "../pages/Shop/ShopCreateProduct"; 3 | import ShopAllProducts from "../pages/Shop/ShopAllProducts"; 4 | import ShopCreateEvents from "../pages/Shop/ShopCreateEvents"; 5 | import ShopAllEvents from "../pages/Shop/ShopAllEvents"; 6 | import ShopAllCoupouns from "../pages/Shop/ShopAllCoupouns"; 7 | import ShopPreviewPage from "../pages/Shop/ShopPreviewPage"; 8 | import ShopAllOrders from "../pages/Shop/ShopAllOrders"; 9 | import ShopOrderDetails from "../pages/Shop/ShopOrderDetails"; 10 | import ShopAllRefunds from "../pages/Shop/ShopAllRefunds"; 11 | import ShopSettingsPage from "../pages/Shop/ShopSettingsPage"; 12 | import ShopWithDrawMoneyPage from "../pages/Shop/ShopWithDrawMoneyPage"; 13 | import ShopInboxPage from "../pages/Shop/ShopInboxPage"; 14 | 15 | export { 16 | ShopDashboardPage, 17 | ShopCreateProduct, 18 | ShopAllProducts, 19 | ShopCreateEvents, 20 | ShopAllEvents, 21 | ShopAllCoupouns, 22 | ShopPreviewPage, 23 | ShopAllOrders, 24 | ShopOrderDetails, 25 | ShopAllRefunds, 26 | ShopSettingsPage, 27 | ShopWithDrawMoneyPage, 28 | ShopInboxPage, 29 | }; 30 | -------------------------------------------------------------------------------- /frontend/src/components/Products/Ratings.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { AiFillStar, AiOutlineStar } from "react-icons/ai"; 3 | import { BsStarHalf } from "react-icons/bs"; 4 | 5 | 6 | const Ratings = ({ rating }) => { 7 | const stars = []; 8 | 9 | for (let i = 1; i <= 5; i++) { 10 | if (i <= rating) { 11 | stars.push( 12 | 18 | ); 19 | } else if (i === Math.ceil(rating) && !Number.isInteger(rating)) { 20 | stars.push( 21 | 27 | ); 28 | } else { 29 | stars.push( 30 | 36 | ); 37 | } 38 | } 39 | 40 | return
{stars}
; 41 | 42 | } 43 | 44 | export default Ratings -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* fonts */ 6 | @import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"); 7 | @import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"); 8 | 9 | * { 10 | scroll-behavior: smooth; 11 | } 12 | body { 13 | font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 14 | Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 15 | background: #f6f6f5; 16 | scroll-behavior: smooth; 17 | } 18 | input, 19 | select { 20 | outline: none; 21 | } 22 | .batch_heart { 23 | background: tomato; 24 | width: 15px; 25 | height: 15px; 26 | display: flex; 27 | align-items: center; 28 | justify-content: center; 29 | border-radius: 100%; 30 | position: absolute; 31 | right: -24%; 32 | top: -9%; 33 | cursor: pointer; 34 | } 35 | /* Chrome, Safari, Edge, Opera */ 36 | input::-webkit-outer-spin-button, 37 | input::-webkit-inner-spin-button { 38 | -webkit-appearance: none; 39 | margin: 0; 40 | } 41 | 42 | /* Firefox */ 43 | input[type="number"] { 44 | -moz-appearance: textfield; 45 | } 46 | -------------------------------------------------------------------------------- /frontend/src/components/Route/BestDeals/BestDeals.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import styles from "../../../styles/styles"; 4 | import ProductCard from "../ProductCard/ProductCard"; 5 | 6 | const BestDeals = () => { 7 | const [data, setData] = useState([]); 8 | const { allProducts } = useSelector((state) => state.products); 9 | useEffect(() => { 10 | const allProductsData = allProducts ? [...allProducts] : []; 11 | const sortedData = allProductsData?.sort((a, b) => b.sold_out - a.sold_out); 12 | const firstFive = sortedData && sortedData.slice(0, 5); 13 | setData(firstFive); 14 | }, [allProducts]); 15 | 16 | return ( 17 |
18 |
19 |
20 |

Best Deals

21 |
22 |
23 | {data && data.length !== 0 && ( 24 | <> 25 | {data && 26 | data.map((i, index) => )} 27 | 28 | )} 29 |
30 |
31 |
32 | ); 33 | }; 34 | 35 | export default BestDeals; 36 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/order.js: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | isLoading: true, 5 | }; 6 | 7 | export const orderReducer = createReducer(initialState, { 8 | // get all orders of user 9 | getAllOrdersUserRequest: (state) => { 10 | state.isLoading = false; 11 | }, 12 | 13 | getAllOrdersUserSuccess: (state, action) => { 14 | state.isLoading = false; 15 | state.orders = action.payload; 16 | }, 17 | getAllOrdersUserFailed: (state, action) => { 18 | state.isLoading = false; 19 | state.error = action.payload; 20 | }, 21 | // get all orders of shop 22 | getAllOrdersShopRequest: (state) => { 23 | state.isLoading = true; 24 | }, 25 | getAllOrdersShopSuccess: (state, action) => { 26 | state.isLoading = false; 27 | state.orders = action.payload; 28 | }, 29 | getAllOrdersShopFailed: (state, action) => { 30 | state.isLoading = false; 31 | state.error = action.payload; 32 | }, 33 | // get all orders for admin 34 | adminAllOrdersRequest: (state) => { 35 | state.adminOrderLoading = true; 36 | }, 37 | adminAllOrdersSuccess: (state, action) => { 38 | state.adminOrderLoading = false; 39 | state.adminOrders = action.payload; 40 | }, 41 | adminAllOrdersFailed: (state, action) => { 42 | state.adminOrderLoading = false; 43 | state.error = action.payload; 44 | }, 45 | clearErrors: (state) => { 46 | state.error = null; 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /frontend/src/pages/BestSellingPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import Header from "../components/Layout/Header"; 3 | import { useSelector } from "react-redux"; 4 | import Loader from "../components/Layout/Loader"; 5 | import styles from "../styles/styles"; 6 | import ProductCard from "../components/Route/ProductCard/ProductCard"; 7 | 8 | const BestSellingPage = () => { 9 | const [data, setData] = useState([]); 10 | const { allProducts, isLoading } = useSelector((state) => state.products); 11 | 12 | useEffect(() => { 13 | if (Array.isArray(allProducts)) { 14 | const sortedProducts = [...allProducts].sort( 15 | (a, b) => b.sold_out - a.sold_out 16 | ); 17 | setData(sortedProducts); 18 | window.scrollTo(0, 0); 19 | } 20 | }, [allProducts]); 21 | 22 | return ( 23 | <> 24 | {isLoading ? ( 25 | 26 | ) : ( 27 |
28 |
29 |
30 |
31 |
32 |
33 | {data && 34 | data.map((i, index) => )} 35 |
36 |
37 |
38 | )} 39 | 40 | ); 41 | }; 42 | 43 | export default BestSellingPage; 44 | -------------------------------------------------------------------------------- /frontend/src/routes/Routes.js: -------------------------------------------------------------------------------- 1 | import LoginPage from "../pages/LoginPage"; 2 | import SignupPage from "../pages/SignupPage"; 3 | import ActivationPage from "../pages/ActivationPage"; 4 | import HomePage from "../pages/HomePage"; 5 | import ProductsPage from "../pages/ProductsPage"; 6 | import BestSellingPage from "../pages/BestSellingPage"; 7 | import EventsPage from "../pages/EventsPage"; 8 | import FAQPage from "../pages/FAQPage"; 9 | import CheckoutPage from "../pages/CheckoutPage"; 10 | import PaymentPage from "../pages/PaymentPage"; 11 | import OrderSuccessPage from "../pages/OrderSuccessPage"; 12 | import ProductDetailsPage from "../pages/ProductDetailsPage"; 13 | import ProfilePage from "../pages/ProfilePage"; 14 | import ShopCreatePage from "../pages/ShopCreate"; 15 | import SellerActivationPage from "../pages/SellerActivationPage"; 16 | import ShopLoginPage from "../pages/ShopLoginPage"; 17 | import OrderDetailsPage from "../pages/OrderDetailsPage"; 18 | import TrackOrderPage from "../pages/TrackOrderPage"; 19 | import UserInbox from "../pages/UserInbox"; 20 | 21 | export { 22 | LoginPage, 23 | SignupPage, 24 | ActivationPage, 25 | HomePage, 26 | ProductsPage, 27 | BestSellingPage, 28 | EventsPage, 29 | FAQPage, 30 | CheckoutPage, 31 | PaymentPage, 32 | OrderSuccessPage, 33 | ProductDetailsPage, 34 | ProfilePage, 35 | ShopCreatePage, 36 | SellerActivationPage, 37 | ShopLoginPage, 38 | OrderDetailsPage, 39 | TrackOrderPage, 40 | UserInbox, 41 | }; 42 | -------------------------------------------------------------------------------- /backend/middleware/auth.js: -------------------------------------------------------------------------------- 1 | const ErrorHandler = require("../utils/ErrorHandler"); 2 | const catchAsyncErrors = require("./catchAsyncErrors"); 3 | const jwt = require("jsonwebtoken"); 4 | const User = require("../model/user"); 5 | const Shop = require("../model/shop"); 6 | 7 | // Check if user is authenticated or not 8 | exports.isAuthenticated = catchAsyncErrors(async (req, res, next) => { 9 | const { token } = req.cookies; 10 | if (!token) { 11 | return next(new ErrorHandler("Please login to continue", 401)); 12 | } 13 | const decoded = jwt.verify(token, process.env.JWT_SECRET_KEY); 14 | 15 | req.user = await User.findById(decoded.id); 16 | next(); 17 | }); 18 | 19 | exports.isSeller = catchAsyncErrors(async (req, res, next) => { 20 | const { seller_token } = req.cookies; 21 | if (!seller_token) { 22 | return next(new ErrorHandler("Please login to continue", 401)); 23 | } 24 | 25 | const decoded = jwt.verify(seller_token, process.env.JWT_SECRET_KEY); 26 | 27 | req.seller = await Shop.findById(decoded.id); 28 | 29 | next(); 30 | }); 31 | 32 | exports.isAdmin = (...roles) => { 33 | return (req, res, next) => { 34 | if (!roles.includes(req.user.role)) { 35 | return next( 36 | new ErrorHandler(`${req.user.role} can not access this resources!`) 37 | ); 38 | } 39 | next(); 40 | }; 41 | }; 42 | 43 | // Why this auth? 44 | // This auth is for the user to login and get the token 45 | // This token will be used to access the protected routes like create, update, delete, etc. (autharization) 46 | -------------------------------------------------------------------------------- /frontend/src/components/Layout/DropDown.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | import styles from "../../styles/styles"; 4 | 5 | const DropDown = ({ categoriesData, setDropDown }) => { 6 | const navigate = useNavigate(); 7 | const submitHandle = (i) => { 8 | navigate(`/products?category=${i.title}`); 9 | setDropDown(false); 10 | window.location.reload(); 11 | }; 12 | return ( 13 |
14 | {categoriesData && 15 | categoriesData.map((i, index) => ( 16 |
submitHandle(i)} 20 | > 21 | Drop Down img 32 |

{i.title}

33 |
34 | ))} 35 |
36 | ); 37 | }; 38 | 39 | export default DropDown; -------------------------------------------------------------------------------- /frontend/src/pages/SellerActivationPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useParams } from 'react-router-dom'; 3 | import { server } from "../server"; 4 | import axios from 'axios'; 5 | 6 | 7 | const SellerActivationPage = () => { 8 | const { activation_token } = useParams(); 9 | const [error, setError] = useState(false); 10 | 11 | useEffect(() => { 12 | if (activation_token) { 13 | const activationEmail = async () => { 14 | try { 15 | const res = await axios 16 | .post(`${server}/shop/activation`, { 17 | activation_token 18 | }) 19 | 20 | } catch (err) { 21 | console.log(err.response.data.message); 22 | setError(true); 23 | } 24 | } 25 | activationEmail(); 26 | } 27 | 28 | }, []); 29 | 30 | return ( 31 |
39 | { 40 | error ? ( 41 |

Your toke is expair

42 | ) : ( 43 |

Your Account has been created sucess fully!

44 | ) 45 | } 46 | 47 |
48 | ) 49 | } 50 | 51 | export default SellerActivationPage 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /frontend/src/pages/ActivationPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useParams } from 'react-router-dom'; 3 | import { server } from "../server"; 4 | import axios from 'axios'; 5 | 6 | 7 | 8 | const ActivationPage = () => { 9 | const { activation_token } = useParams(); 10 | const [error, setError] = useState(false); 11 | 12 | useEffect(() => { 13 | if (activation_token) { 14 | const activationEmail = async () => { 15 | try { 16 | const res = await axios 17 | .post(`${server}/user/activation`, { 18 | activation_token 19 | }) 20 | console.log(res.data.message); 21 | } catch (err) { 22 | console.log(err.response.data.message); 23 | setError(true); 24 | } 25 | } 26 | activationEmail(); 27 | } 28 | 29 | }, []); 30 | 31 | return ( 32 |
40 | { 41 | error ? ( 42 |

Your toke is expair

43 | ) : ( 44 |

Your Account has been created sucess fully!

45 | ) 46 | } 47 | 48 |
49 | ) 50 | } 51 | 52 | export default ActivationPage -------------------------------------------------------------------------------- /backend/model/product.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const productSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: [true, "Please enter your product name!"], 7 | }, 8 | description: { 9 | type: String, 10 | required: [true, "Please enter your product description!"], 11 | }, 12 | category: { 13 | type: String, 14 | required: [true, "Please enter your product category!"], 15 | }, 16 | tags: { 17 | type: String, 18 | }, 19 | originalPrice: { 20 | type: Number, 21 | }, 22 | discountPrice: { 23 | type: Number, 24 | required: [true, "Please enter your product price!"], 25 | }, 26 | stock: { 27 | type: Number, 28 | required: [true, "Please enter your product stock!"], 29 | }, 30 | images: [ 31 | { 32 | type: String, 33 | }, 34 | ], 35 | 36 | reviews: [ 37 | { 38 | user: { 39 | type: Object, 40 | }, 41 | rating: { 42 | type: Number, 43 | }, 44 | comment: { 45 | type: String, 46 | }, 47 | productId: { 48 | type: String, 49 | }, 50 | createdAt: { 51 | type: Date, 52 | default: Date.now(), 53 | }, 54 | }, 55 | ], 56 | ratings: { 57 | type: Number, 58 | }, 59 | shopId: { 60 | type: String, 61 | required: true, 62 | }, 63 | shop: { 64 | type: Object, 65 | required: true, 66 | }, 67 | sold_out: { 68 | type: Number, 69 | default: 0, 70 | }, 71 | createdAt: { 72 | type: Date, 73 | default: Date.now(), 74 | }, 75 | }); 76 | 77 | module.exports = mongoose.model("Product", productSchema); 78 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 24 | Multi Vendor Marketplaces 25 | 26 | 27 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/cart.js: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | cart: localStorage.getItem("cartItems") 5 | ? JSON.parse(localStorage.getItem("cartItems")) 6 | : [], 7 | }; 8 | 9 | export const cartReducer = createReducer(initialState, { 10 | addToCart: (state, action) => { 11 | const item = action.payload; 12 | /* The line `const isItemExist = state.cart((i) => i._id == item._id);` is checking if an item with 13 | the same `_id` as the `item` being added already exists in the `cart` array. */ //[19.48] 14 | const isItemExist = state.cart.find((i) => i._id === item._id); 15 | if (isItemExist) { 16 | return { 17 | ...state, 18 | /* The line `cart: state.cart.map((i) => (i._id === isItemExist._id ? item : i))` is updating the 19 | `cart` array in the state. */ 20 | cart: state.cart.map((i) => (i._id === isItemExist._id ? item : i)), 21 | }; 22 | } else { 23 | return { 24 | ...state, 25 | /* The line `cart: [...state.cart, item],` is adding the `item` to the `cart` array in the 26 | state. It uses the spread operator (`...`) to create a new array that includes all the 27 | elements from the existing `state.cart` array, and then appends the `item` to the end of the 28 | new array. This ensures that the original `state.cart` array is not mutated, and a new array 29 | is created with the updated items. */ 30 | cart: [...state.cart, item], 31 | }; 32 | } 33 | }, 34 | 35 | // Remove from cart 36 | removeFromCart: (state, action) => { 37 | return { 38 | ...state, 39 | cart: state.cart.filter((i) => i._id !== action.payload), 40 | }; 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /frontend/src/components/Route/Hero/Hero.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from "react-router-dom"; 3 | import styles from "../../../styles/styles"; 4 | 5 | 6 | const Hero = () => { 7 | return ( 8 |
15 |
16 |

19 | Best Collection for
home Decoration 20 |

21 |

22 | Lorem ipsum dolor sit amet consectetur, adipisicing elit. Beatae, 23 | assumenda? Quisquam itaque
exercitationem labore vel, dolore 24 | quidem asperiores, laudantium temporibus soluta optio consequatur{" "} 25 |
aliquam deserunt officia. Dolorum saepe nulla provident. 26 |

27 | 28 |
29 | 30 | Shop Now 31 | 32 |
33 | 34 | 35 |
36 | 37 |
38 | ) 39 | } 40 | 41 | export default Hero -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.12.4", 7 | "@material-ui/data-grid": "^4.0.0-alpha.37", 8 | "@paypal/react-paypal-js": "^7.8.3", 9 | "@reduxjs/toolkit": "^1.9.3", 10 | "@stripe/react-stripe-js": "^2.1.0", 11 | "@stripe/stripe-js": "^1.52.1", 12 | "@testing-library/jest-dom": "^5.16.5", 13 | "@testing-library/react": "^13.4.0", 14 | "@testing-library/user-event": "^13.5.0", 15 | "axios": "^1.4.0", 16 | "country-state-city": "^3.1.2", 17 | "js-cookie": "^3.0.1", 18 | "react": "^18.2.0", 19 | "react-dom": "^18.2.0", 20 | "react-icons": "^4.7.1", 21 | "react-lottie": "^1.2.3", 22 | "react-redux": "^8.0.5", 23 | "react-router-dom": "^6.8.2", 24 | "react-scripts": "5.0.1", 25 | "react-toastify": "^9.1.1", 26 | "redux": "^4.2.1", 27 | "redux-thunk": "^2.4.2", 28 | "redux-toolkit": "^1.1.2", 29 | "socket.io-client": "^4.6.1", 30 | "timeago.js": "^4.0.2", 31 | "web-vitals": "^2.1.4" 32 | }, 33 | "scripts": { 34 | "start": "react-scripts start", 35 | "build": "react-scripts build", 36 | "test": "react-scripts test", 37 | "eject": "react-scripts eject" 38 | }, 39 | "eslintConfig": { 40 | "extends": [ 41 | "react-app", 42 | "react-app/jest" 43 | ] 44 | }, 45 | "browserslist": { 46 | "production": [ 47 | ">0.2%", 48 | "not dead", 49 | "not op_mini all" 50 | ], 51 | "development": [ 52 | "last 1 chrome version", 53 | "last 1 firefox version", 54 | "last 1 safari version" 55 | ] 56 | }, 57 | "devDependencies": { 58 | "autoprefixer": "^10.4.14", 59 | "postcss": "^8.4.24", 60 | "tailwindcss": "^3.3.2" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/event.js: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | isLoading: true, 5 | }; 6 | 7 | export const eventReducer = createReducer(initialState, { 8 | eventCreateRequest: (state) => { 9 | state.isLoading = true; 10 | }, 11 | eventCreateSuccess: (state, action) => { 12 | state.isLoading = false; 13 | state.event = action.payload; 14 | state.success = true; 15 | }, 16 | eventCreateFail: (state, action) => { 17 | state.isLoading = false; 18 | state.error = action.payload; 19 | state.success = false; 20 | }, 21 | 22 | // get all events of shop 23 | getAlleventsShopRequest: (state) => { 24 | state.isLoading = true; 25 | }, 26 | getAlleventsShopSuccess: (state, action) => { 27 | state.isLoading = false; 28 | state.events = action.payload; 29 | }, 30 | getAlleventsShopFailed: (state, action) => { 31 | state.isLoading = false; 32 | state.error = action.payload; 33 | }, 34 | 35 | // delete event of a shop 36 | deleteeventRequest: (state) => { 37 | state.isLoading = true; 38 | }, 39 | deleteeventSuccess: (state, action) => { 40 | state.isLoading = false; 41 | state.message = action.payload; 42 | }, 43 | deleteeventFailed: (state, action) => { 44 | state.isLoading = false; 45 | state.error = action.payload; 46 | }, 47 | 48 | // get all events 49 | getAlleventsRequest: (state) => { 50 | state.isLoading = true; 51 | }, 52 | getAlleventsSuccess: (state, action) => { 53 | state.isLoading = false; 54 | state.allEvents = action.payload; 55 | }, 56 | getAlleventsFailed: (state, action) => { 57 | state.isLoading = false; 58 | state.error = action.payload; 59 | }, 60 | 61 | clearErrors: (state) => { 62 | state.error = null; 63 | }, 64 | }); 65 | -------------------------------------------------------------------------------- /frontend/src/redux/actions/order.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { server } from "../../server"; 3 | 4 | // get all orders of user 5 | export const getAllOrdersOfUser = (userId) => async (dispatch) => { 6 | try { 7 | dispatch({ 8 | type: "getAllOrdersUserRequest", 9 | }); 10 | 11 | const { data } = await axios.get( 12 | `${server}/order/get-all-orders/${userId}` 13 | ); 14 | 15 | dispatch({ 16 | type: "getAllOrdersUserSuccess", 17 | payload: data.orders, 18 | }); 19 | } catch (error) { 20 | dispatch({ 21 | type: "getAllOrdersUserFailed", 22 | payload: error.response.data.message, 23 | }); 24 | } 25 | }; 26 | 27 | // Get all orders of seller 28 | export const getAllOrdersOfShop = (shopId) => async (dispatch) => { 29 | try { 30 | dispatch({ 31 | type: "getAllOrdersShopRequest", 32 | }); 33 | 34 | const { data } = await axios.get( 35 | `${server}/order/get-seller-all-orders/${shopId}` 36 | ); 37 | 38 | dispatch({ 39 | type: "getAllOrdersShopSuccess", 40 | payload: data.orders, 41 | }); 42 | } catch (error) { 43 | dispatch({ 44 | type: "getAllOrdersShopFailed", 45 | payload: error.response.data.message, 46 | }); 47 | } 48 | }; 49 | 50 | // get all orders of Admin 51 | export const getAllOrdersOfAdmin = () => async (dispatch) => { 52 | try { 53 | dispatch({ 54 | type: "adminAllOrdersRequest", 55 | }); 56 | 57 | const { data } = await axios.get(`${server}/order/admin-all-orders`, { 58 | withCredentials: true, 59 | }); 60 | 61 | dispatch({ 62 | type: "adminAllOrdersSuccess", 63 | payload: data.orders, 64 | }); 65 | } catch (error) { 66 | dispatch({ 67 | type: "adminAllOrdersFailed", 68 | payload: error.response.data.message, 69 | }); 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /frontend/src/components/Products/SuggestedProduct.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useSelector } from "react-redux"; 3 | import { productData } from '../../static/data' 4 | import styles from "../../styles/styles"; 5 | import ProductCard from "../Route/ProductCard/ProductCard" 6 | 7 | 8 | 9 | 10 | const SuggestedProduct = ({ data }) => { 11 | const [products, setProducts] = useState([]) 12 | const { allProducts } = useSelector((state) => state.products); 13 | const [productData, setProductData] = useState(); 14 | 15 | // Proudect is filter when the cataegory is same as the current product when page is loaded 16 | useEffect(() => { 17 | const d = allProducts && allProducts.filter((i) => i.category === data.category) 18 | setProductData(d) 19 | }, []) 20 | 21 | return ( 22 |
23 | { 24 | data ? ( 25 |
27 |

30 | Related Products 31 |

32 |
33 | { 34 | productData && productData.map((i, index) => ( 35 | 36 | )) 37 | } 38 |
39 |
40 | ) : null 41 | } 42 |
43 | ) 44 | } 45 | 46 | export default SuggestedProduct -------------------------------------------------------------------------------- /frontend/src/redux/reducers/product.js: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | isLoading: true, 5 | }; 6 | 7 | export const productReducer = createReducer(initialState, { 8 | productCreateRequest: (state) => { 9 | state.isLoading = true; 10 | }, 11 | productCreateSuccess: (state, action) => { 12 | state.isLoading = false; 13 | state.product = action.payload; 14 | state.success = true; 15 | }, 16 | productCreateFail: (state, action) => { 17 | state.isLoading = false; 18 | state.error = action.payload; 19 | state.success = false; 20 | }, 21 | 22 | // get all products of shop 23 | getAllProductsShopRequest: (state) => { 24 | state.isLoading = true; 25 | }, 26 | getAllProductsShopSuccess: (state, action) => { 27 | state.isLoading = false; 28 | state.products = action.payload; 29 | }, 30 | getAllProductsShopFailed: (state, action) => { 31 | state.isLoading = false; 32 | state.error = action.payload; 33 | }, 34 | 35 | // delete product of a shop 36 | deleteProductRequest: (state) => { 37 | state.isLoading = true; 38 | }, 39 | deleteProductSuccess: (state, action) => { 40 | state.isLoading = false; 41 | state.message = action.payload; 42 | }, 43 | deleteProductFailed: (state, action) => { 44 | state.isLoading = false; 45 | state.error = action.payload; 46 | }, 47 | 48 | // get all products 49 | getAllProductsRequest: (state) => { 50 | state.isLoading = true; 51 | }, 52 | getAllProductsSuccess: (state, action) => { 53 | state.isLoading = false; 54 | state.allProducts = action.payload; 55 | }, 56 | getAllProductsFailed: (state, action) => { 57 | state.isLoading = false; 58 | state.error = action.payload; 59 | }, 60 | 61 | clearErrors: (state) => { 62 | state.error = null; 63 | }, 64 | }); 65 | -------------------------------------------------------------------------------- /backend/model/event.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const eventSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: [true, "Please enter your event product name!"], 7 | }, 8 | description: { 9 | type: String, 10 | required: [true, "Please enter your event product description!"], 11 | }, 12 | category: { 13 | type: String, 14 | required: [true, "Please enter your event product category!"], 15 | }, 16 | start_Date: { 17 | type: Date, 18 | required: true, 19 | }, 20 | Finish_Date: { 21 | type: Date, 22 | required: true, 23 | }, 24 | status: { 25 | type: String, 26 | default: "Running", 27 | }, 28 | tags: { 29 | type: String, 30 | }, 31 | originalPrice: { 32 | type: Number, 33 | }, 34 | discountPrice: { 35 | type: Number, 36 | required: [true, "Please enter your event product price!"], 37 | }, 38 | stock: { 39 | type: Number, 40 | required: [true, "Please enter your event product stock!"], 41 | }, 42 | images: [ 43 | { 44 | type: String, 45 | }, 46 | ], 47 | reviews: [ 48 | { 49 | user: { 50 | type: Object, 51 | }, 52 | rating: { 53 | type: Number, 54 | }, 55 | comment: { 56 | type: String, 57 | }, 58 | productId: { 59 | type: String, 60 | }, 61 | createdAt: { 62 | type: Date, 63 | default: Date.now(), 64 | }, 65 | }, 66 | ], 67 | ratings: { 68 | type: Number, 69 | }, 70 | shopId: { 71 | type: String, 72 | required: true, 73 | }, 74 | shop: { 75 | type: Object, 76 | required: true, 77 | }, 78 | sold_out: { 79 | type: Number, 80 | default: 0, 81 | }, 82 | createdAt: { 83 | type: Date, 84 | default: Date.now(), 85 | }, 86 | }); 87 | 88 | module.exports = mongoose.model("Event", eventSchema); 89 | -------------------------------------------------------------------------------- /backend/controller/message.js: -------------------------------------------------------------------------------- 1 | const Messages = require("../model/messages"); 2 | const ErrorHandler = require("../utils/ErrorHandler"); 3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 4 | const express = require("express"); 5 | const { upload } = require("../multer"); 6 | const router = express.Router(); 7 | const path = require("path"); 8 | 9 | // create new message 10 | router.post( 11 | "/create-new-message", 12 | upload.single("images"), 13 | catchAsyncErrors(async (req, res, next) => { 14 | try { 15 | const messageData = req.body; 16 | 17 | if (req.file) { 18 | const filename = req.file.filename; 19 | const fileUrl = path.join(filename); 20 | messageData.images = fileUrl; 21 | } 22 | 23 | messageData.conversationId = req.body.conversationId; 24 | messageData.sender = req.body.sender; 25 | messageData.text = req.body.text; 26 | 27 | const message = new Messages({ 28 | conversationId: messageData.conversationId, 29 | text: messageData.text, 30 | sender: messageData.sender, 31 | images: messageData.images ? messageData.images : undefined, 32 | }); 33 | 34 | await message.save(); 35 | 36 | res.status(201).json({ 37 | success: true, 38 | message, 39 | }); 40 | } catch (error) { 41 | return next(new ErrorHandler(error.message), 500); 42 | } 43 | }) 44 | ); 45 | 46 | // get all messages with conversation id 47 | router.get( 48 | "/get-all-messages/:id", 49 | catchAsyncErrors(async (req, res, next) => { 50 | try { 51 | const messages = await Messages.find({ 52 | conversationId: req.params.id, 53 | }); 54 | 55 | res.status(201).json({ 56 | success: true, 57 | messages, 58 | }); 59 | } catch (error) { 60 | return next(new ErrorHandler(error.message), 500); 61 | } 62 | }) 63 | ); 64 | 65 | module.exports = router; 66 | -------------------------------------------------------------------------------- /frontend/src/pages/ProductsPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useSelector } from "react-redux"; 3 | import { useSearchParams } from "react-router-dom"; 4 | import Footer from "../components/Layout/Footer"; 5 | import Header from "../components/Layout/Header"; 6 | import Loader from "../components/Layout/Loader"; 7 | import ProductCard from "../components/Route/ProductCard/ProductCard"; 8 | import styles from "../styles/styles"; 9 | 10 | const ProductsPage = () => { 11 | const [searchParams] = useSearchParams(); 12 | const categoryData = searchParams.get("category"); 13 | const { allProducts, isLoading } = useSelector((state) => state.products); 14 | const [data, setData] = useState([]); 15 | 16 | useEffect(() => { 17 | if (categoryData === null) { 18 | const d = allProducts; 19 | setData(d); 20 | } else { 21 | const d = 22 | allProducts && allProducts.filter((i) => i.category === categoryData); 23 | setData(d); 24 | } 25 | // window.scrollTo(0,0); 26 | }, [allProducts]); 27 | 28 | return ( 29 | <> 30 | {isLoading ? ( 31 | 32 | ) : ( 33 |
34 |
35 |
36 |
37 |
38 |
39 | {data && 40 | data.map((i, index) => )} 41 |
42 | {data && data.length === 0 ? ( 43 |

44 | No products Found! 45 |

46 | ) : null} 47 |
48 |
49 |
50 | )} 51 | 52 | ); 53 | }; 54 | 55 | export default ProductsPage; 56 | -------------------------------------------------------------------------------- /frontend/src/components/Layout/AdminHeader.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MdOutlineLocalOffer } from "react-icons/md"; 3 | import { useSelector } from "react-redux"; 4 | import { Link } from "react-router-dom"; 5 | import { CiMoneyBill } from "react-icons/ci"; 6 | import { GrWorkshop } from "react-icons/gr"; 7 | import { backend_url } from "../../server"; 8 | 9 | const AdminHeader = () => { 10 | const { user } = useSelector((state) => state.user); 11 | 12 | return ( 13 |
14 |
15 | 16 | 20 | 21 |
22 |
23 |
24 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | 44 | 45 | 50 |
51 |
52 |
53 | ); 54 | }; 55 | 56 | export default AdminHeader; 57 | -------------------------------------------------------------------------------- /backend/model/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const bcrypt = require("bcryptjs"); 3 | const jwt = require("jsonwebtoken"); 4 | 5 | const userSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | required: [true, "Please enter your name!"], 9 | }, 10 | email: { 11 | type: String, 12 | required: [true, "Please enter your email!"], 13 | }, 14 | password: { 15 | type: String, 16 | required: [true, "Please enter your password"], 17 | minLength: [4, "Password should be greater than 4 characters"], 18 | select: false, 19 | }, 20 | phoneNumber: { 21 | type: Number, 22 | }, 23 | addresses: [ 24 | { 25 | country: { 26 | type: String, 27 | }, 28 | city: { 29 | type: String, 30 | }, 31 | address1: { 32 | type: String, 33 | }, 34 | address2: { 35 | type: String, 36 | }, 37 | zipCode: { 38 | type: Number, 39 | }, 40 | addressType: { 41 | type: String, 42 | }, 43 | }, 44 | ], 45 | role: { 46 | type: String, 47 | default: "user", 48 | }, 49 | avatar: { 50 | type: String, 51 | required: true, 52 | }, 53 | createdAt: { 54 | type: Date, 55 | default: Date.now(), 56 | }, 57 | resetPasswordToken: String, 58 | resetPasswordTime: Date, 59 | }); 60 | 61 | // Hash password 62 | userSchema.pre("save", async function (next) { 63 | if (!this.isModified("password")) { 64 | next(); 65 | } 66 | 67 | this.password = await bcrypt.hash(this.password, 10); 68 | }); 69 | 70 | // jwt token 71 | userSchema.methods.getJwtToken = function () { 72 | return jwt.sign({ id: this._id }, process.env.JWT_SECRET_KEY, { 73 | expiresIn: process.env.JWT_EXPIRES, 74 | }); 75 | }; 76 | 77 | // compare password 78 | userSchema.methods.comparePassword = async function (enteredPassword) { 79 | return await bcrypt.compare(enteredPassword, this.password); 80 | }; 81 | 82 | module.exports = mongoose.model("User", userSchema); 83 | -------------------------------------------------------------------------------- /frontend/src/components/Checkout/CheckoutSteps.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../../styles/styles' 3 | 4 | const CheckoutSteps = ({ active }) => { 5 | console.log(active); 6 | return ( 7 |
8 |
9 |
10 |
11 | 1.Shipping 12 |
13 |
1 ? "w-[30px] 800px:w-[70px] h-[4px] !bg-[#f63b60]" 14 | : "w-[30px] 800px:w-[70px] h-[4px] !bg-[#FDE1E6]" 15 | }`} /> 16 |
17 | 18 |
19 |
1 ? `${styles.cart_button}` : `${styles.cart_button} !bg-[#FDE1E6]`}`}> 20 | 1 ? `${styles.cart_button_text}` : `${styles.cart_button_text} !text-[#f63b60]`}`}> 21 | 2.Payment 22 | 23 |
24 |
25 | 26 |
27 |
3 ? "w-[30px] 800px:w-[70px] h-[4px] !bg-[#f63b60]" 28 | : "w-[30px] 800px:w-[70px] h-[4px] !bg-[#FDE1E6]" 29 | }`} /> 30 |
2 ? `${styles.cart_button}` : `${styles.cart_button} !bg-[#FDE1E6]`}`}> 31 | 2 ? `${styles.cart_button_text}` : `${styles.cart_button_text} !text-[#f63b60]`}`}> 32 | 3.Success 33 | 34 |
35 |
36 |
37 |
38 | ) 39 | } 40 | 41 | export default CheckoutSteps -------------------------------------------------------------------------------- /frontend/src/components/Events/CountDown.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { server } from "../../server"; 4 | 5 | const CountDown = ({ data }) => { 6 | const [timeLeft, setTimeLeft] = useState(calculateTimeLeft()); 7 | 8 | useEffect(() => { 9 | setTimeout(() => { 10 | setTimeLeft(calculateTimeLeft()); 11 | }, 1000); 12 | if ( 13 | typeof timeLeft.days === "undefined" && 14 | typeof timeLeft.hours === "undefined" && 15 | typeof timeLeft.minutes === "undefined" && 16 | typeof timeLeft.seconds === "undefined" 17 | ) { 18 | axios.delete(`${server}/event/delete-shop-event/${data._id}`); 19 | } 20 | return () => clearInterval(timeLeft); 21 | }); 22 | 23 | function calculateTimeLeft() { 24 | // today date + 3 days 25 | const evDate = new Date( 26 | new Date().getTime() + 3 * 24 * 60 * 60 * 1000 27 | ).toLocaleDateString(); 28 | 29 | // const difference = +new Date(evDate) - +new Date(); 30 | const difference = +new Date(data.Finish_Date) - +new Date(); 31 | let timeLeft = {}; 32 | 33 | if (difference > 0) { 34 | timeLeft = { 35 | days: Math.floor(difference / (1000 * 60 * 60 * 24)), 36 | hours: Math.floor((difference / (1000 * 60 * 60)) % 24), 37 | minutes: Math.floor((difference / 1000 / 60) % 60), 38 | seconds: Math.floor((difference / 1000) % 60), 39 | }; 40 | } 41 | return timeLeft; 42 | } 43 | 44 | const timerComponents = Object.keys(timeLeft).map((interval) => { 45 | if (!timeLeft[interval]) { 46 | return null; 47 | } 48 | 49 | return ( 50 | 51 | {timeLeft[interval]} {interval}{" "} 52 | 53 | ); 54 | }); 55 | 56 | return ( 57 |
58 | {timerComponents.length ? ( 59 | timerComponents 60 | ) : ( 61 | Time's Up 62 | )} 63 |
64 | ); 65 | }; 66 | 67 | export default CountDown; 68 | -------------------------------------------------------------------------------- /frontend/src/pages/ProductDetailsPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import Header from '../components/Layout/Header' 3 | import Footer from '../components/Layout/Footer' 4 | import ProductDetails from "../components/Products/ProductDetails"; 5 | import { useParams, useSearchParams } from 'react-router-dom'; 6 | import SuggestedProduct from "../components/Products/SuggestedProduct"; 7 | import { useSelector } from 'react-redux'; 8 | 9 | 10 | 11 | const ProductDetailsPage = () => { 12 | const { allProducts } = useSelector((state) => state.products); 13 | const { allEvents } = useSelector((state) => state.events); 14 | const { id } = useParams(); 15 | const [data, setData] = useState(null) 16 | const [searchParams] = useSearchParams(); 17 | const eventData = searchParams.get("isEvent"); 18 | 19 | 20 | 21 | 22 | // const productName = name.replace(/-/g, " "); 23 | 24 | useEffect(() => { 25 | if (eventData !== null) { 26 | const data = allEvents && allEvents.find((i) => i._id === id); 27 | setData(data); 28 | } else { 29 | const data = allProducts && allProducts.find((i) => i._id === id); 30 | setData(data); 31 | } 32 | /* `window.scrollTo(0, 0)` is a JavaScript method that scrolls the window to the top of the page. 33 | In this code, it is used inside the `useEffect` hook to scroll the window to the top whenever 34 | the component is rendered. This ensures that when the user navigates to the 35 | `ProductDetailsPage`, the page starts at the top rather than at the previous scroll position. */ 36 | window.scrollTo(0, 0) 37 | }, [allProducts, allEvents]); 38 | 39 | 40 | 41 | return ( 42 |
43 |
44 | 45 | { 46 | !eventData && ( 47 | <> 48 | {data && } 49 | 50 | ) 51 | } 52 |
53 |
54 | ) 55 | } 56 | 57 | export default ProductDetailsPage 58 | -------------------------------------------------------------------------------- /backend/model/shop.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const bcrypt = require("bcryptjs"); 3 | const jwt = require("jsonwebtoken"); 4 | 5 | const shopSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | required: [true, "Please enter your shop name!"], 9 | }, 10 | email: { 11 | type: String, 12 | required: [true, "Please enter your shop email address"], 13 | }, 14 | password: { 15 | type: String, 16 | required: [true, "Please enter your password"], 17 | minLength: [6, "Password should be greater than 6 characters"], 18 | select: false, 19 | }, 20 | description: { 21 | type: String, 22 | }, 23 | address: { 24 | type: String, 25 | required: true, 26 | }, 27 | phoneNumber: { 28 | type: Number, 29 | required: true, 30 | }, 31 | role: { 32 | type: String, 33 | default: "Seller", 34 | }, 35 | avatar: { 36 | type: String, 37 | required: true, 38 | }, 39 | zipCode: { 40 | type: Number, 41 | required: true, 42 | }, 43 | withdrawMethod: { 44 | type: Object, 45 | }, 46 | availableBalance: { 47 | type: Number, 48 | default: 0, 49 | }, 50 | transections: [ 51 | { 52 | amount: { 53 | type: Number, 54 | required: true, 55 | }, 56 | status: { 57 | type: String, 58 | default: "Processing", 59 | }, 60 | createdAt: { 61 | type: Date, 62 | default: Date.now(), 63 | }, 64 | updatedAt: { 65 | type: Date, 66 | }, 67 | }, 68 | ], 69 | createdAt: { 70 | type: Date, 71 | default: Date.now(), 72 | }, 73 | resetPasswordToken: String, 74 | resetPasswordTime: Date, 75 | }); 76 | 77 | // Hash password 78 | shopSchema.pre("save", async function (next) { 79 | if (!this.isModified("password")) { 80 | next(); 81 | } 82 | this.password = await bcrypt.hash(this.password, 10); 83 | }); 84 | 85 | // jwt token 86 | shopSchema.methods.getJwtToken = function () { 87 | return jwt.sign({ id: this._id }, process.env.JWT_SECRET_KEY, { 88 | expiresIn: process.env.JWT_EXPIRES, 89 | }); 90 | }; 91 | 92 | // comapre password 93 | shopSchema.methods.comparePassword = async function (enteredPassword) { 94 | return await bcrypt.compare(enteredPassword, this.password); 95 | }; 96 | 97 | module.exports = mongoose.model("Shop", shopSchema); 98 | -------------------------------------------------------------------------------- /frontend/src/components/Profile/TrackOrder.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { useParams } from "react-router-dom"; 4 | import { getAllOrdersOfUser } from "../../redux/actions/order"; 5 | 6 | 7 | const TrackOrder = () => { 8 | 9 | const { orders } = useSelector((state) => state.order); 10 | const { user } = useSelector((state) => state.user); 11 | const dispatch = useDispatch(); 12 | 13 | const { id } = useParams(); 14 | 15 | useEffect(() => { 16 | dispatch(getAllOrdersOfUser(user._id)); 17 | }, [dispatch]); 18 | 19 | const data = orders && orders.find((item) => item._id === id); 20 | 21 | return ( 22 |
23 | {" "} 24 | <> 25 | {data && data?.status === "Processing" ? ( 26 |

Your Order is processing in shop.

27 | ) : data?.status === "Transferred to delivery partner" ? ( 28 |

29 | Your Order is on the way for delivery partner. 30 |

31 | ) : data?.status === "Shipping" ? ( 32 |

33 | Your Order is on the way with our delivery partner. 34 |

35 | ) : data?.status === "Received" ? ( 36 |

37 | Your Order is in your city. Our Delivery man will deliver it. 38 |

39 | ) : data?.status === "On the way" ? ( 40 |

41 | Our Delivery man is going to deliver your order. 42 |

43 | ) : data?.status === "Delivered" ? ( 44 |

Your order is delivered!

45 | ) : data?.status === "Processing refund" ? ( 46 |

Your refund is processing!

47 | ) : data?.status === "Refund Success" ? ( 48 |

Your Refund is success!

49 | ) : null} 50 | 51 | 52 |
53 | ) 54 | } 55 | 56 | export default TrackOrder -------------------------------------------------------------------------------- /frontend/src/redux/actions/event.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { server } from "../../server"; 3 | 4 | // create event 5 | export const createevent = (newForm) => async (dispatch) => { 6 | try { 7 | dispatch({ 8 | type: "eventCreateRequest", 9 | }); 10 | 11 | const config = { headers: { "Content-Type": "multipart/form-data" } }; 12 | 13 | const { data } = await axios.post( 14 | `${server}/event/create-event`, 15 | newForm, 16 | config 17 | ); 18 | dispatch({ 19 | type: "eventCreateSuccess", 20 | payload: data.event, 21 | }); 22 | } catch (error) { 23 | dispatch({ 24 | type: "eventCreateFail", 25 | payload: error.response.data.message, 26 | }); 27 | } 28 | }; 29 | 30 | // get all events of a shop 31 | export const getAllEventsShop = (id) => async (dispatch) => { 32 | try { 33 | dispatch({ 34 | type: "getAlleventsShopRequest", 35 | }); 36 | 37 | const { data } = await axios.get(`${server}/event/get-all-events/${id}`); 38 | dispatch({ 39 | type: "getAlleventsShopSuccess", 40 | payload: data.events, 41 | }); 42 | } catch (error) { 43 | dispatch({ 44 | type: "getAlleventsShopFailed", 45 | payload: error.response.data.message, 46 | }); 47 | } 48 | }; 49 | 50 | // delete event of a shop 51 | export const deleteEvent = (id) => async (dispatch) => { 52 | try { 53 | dispatch({ 54 | type: "deleteeventRequest", 55 | }); 56 | 57 | const { data } = await axios.delete( 58 | `${server}/event/delete-shop-event/${id}`, 59 | { 60 | withCredentials: true, 61 | } 62 | ); 63 | 64 | dispatch({ 65 | type: "deleteeventSuccess", 66 | payload: data.message, 67 | }); 68 | } catch (error) { 69 | dispatch({ 70 | type: "deleteeventFailed", 71 | payload: error.response.data.message, 72 | }); 73 | } 74 | }; 75 | 76 | // get all events 77 | export const getAllEvents = () => async (dispatch) => { 78 | try { 79 | dispatch({ 80 | type: "getAlleventsRequest", 81 | }); 82 | 83 | const { data } = await axios.get(`${server}/event/get-all-events`); 84 | dispatch({ 85 | type: "getAlleventsSuccess", 86 | payload: data.events, 87 | }); 88 | } catch (error) { 89 | dispatch({ 90 | type: "getAlleventsFailed", 91 | payload: error.response.data.message, 92 | }); 93 | } 94 | }; 95 | -------------------------------------------------------------------------------- /frontend/src/components/Admin/AllEvents.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@material-ui/core"; 2 | import { DataGrid } from "@material-ui/data-grid"; 3 | import axios from "axios"; 4 | import React, { useEffect, useState } from "react"; 5 | import { AiOutlineEye } from "react-icons/ai"; 6 | import { Link } from "react-router-dom"; 7 | import { server } from "../../server"; 8 | 9 | const AllEvents = () => { 10 | const [events, setEvents] = useState([]); 11 | useEffect(() => { 12 | axios 13 | .get(`${server}/event/admin-all-events`, { withCredentials: true }) 14 | .then((res) => { 15 | setEvents(res.data.events); 16 | }); 17 | }, []); 18 | 19 | const columns = [ 20 | { field: "id", headerName: "Product Id", minWidth: 150, flex: 0.7 }, 21 | { 22 | field: "name", 23 | headerName: "Name", 24 | minWidth: 180, 25 | flex: 1.4, 26 | }, 27 | { 28 | field: "price", 29 | headerName: "Price", 30 | minWidth: 100, 31 | flex: 0.6, 32 | }, 33 | { 34 | field: "Stock", 35 | headerName: "Stock", 36 | type: "number", 37 | minWidth: 80, 38 | flex: 0.5, 39 | }, 40 | 41 | { 42 | field: "sold", 43 | headerName: "Sold out", 44 | type: "number", 45 | minWidth: 130, 46 | flex: 0.6, 47 | }, 48 | { 49 | field: "Preview", 50 | flex: 0.8, 51 | minWidth: 100, 52 | headerName: "", 53 | type: "number", 54 | sortable: false, 55 | renderCell: (params) => { 56 | return ( 57 | <> 58 | 59 | 62 | 63 | 64 | ); 65 | }, 66 | }, 67 | ]; 68 | 69 | const row = []; 70 | 71 | events && 72 | events.forEach((item) => { 73 | row.push({ 74 | id: item._id, 75 | name: item.name, 76 | price: "US$ " + item.discountPrice, 77 | Stock: item.stock, 78 | sold: item.sold_out, 79 | }); 80 | }); 81 | 82 | return ( 83 |
84 | 91 |
92 | ); 93 | }; 94 | 95 | export default AllEvents; 96 | -------------------------------------------------------------------------------- /frontend/src/redux/reducers/user.js: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | isAuthenticated: false, 5 | }; 6 | 7 | export const userReducer = createReducer(initialState, { 8 | LoadUserRequest: (state) => { 9 | state.loading = true; 10 | // state.loading meaning: if loading is true, then the user is not authenticated 11 | }, 12 | LoadUserSuccess: (state, action) => { 13 | state.isAuthenticated = true; 14 | state.loading = false; 15 | state.user = action.payload; 16 | }, 17 | LoadUserFail: (state, action) => { 18 | state.loading = false; 19 | state.error = action.payload; 20 | state.isAuthenticated = false; 21 | }, 22 | 23 | // update user information 24 | updateUserInfoRequest: (state) => { 25 | state.loading = true; 26 | }, 27 | updateUserInfoSuccess: (state, action) => { 28 | state.loading = false; 29 | state.user = action.payload; 30 | }, 31 | updateUserInfoFailed: (state, action) => { 32 | state.loading = false; 33 | state.error = action.payload; 34 | }, 35 | 36 | // Update User address 37 | updateUserAddressRequest: (state) => { 38 | state.addressloading = true; 39 | }, 40 | updateUserAddressSuccess: (state, action) => { 41 | state.addressloading = false; 42 | state.successMessage = action.payload.successMessage; 43 | state.user = action.payload.user; 44 | }, 45 | updateUserAddressFailed: (state, action) => { 46 | state.addressloading = false; 47 | state.error = action.payload; 48 | }, 49 | 50 | // delete user address 51 | deleteUserAddressRequest: (state) => { 52 | state.addressloading = true; 53 | }, 54 | deleteUserAddressSuccess: (state, action) => { 55 | state.addressloading = false; 56 | state.successMessage = action.payload.successMessage; 57 | state.user = action.payload.user; 58 | }, 59 | deleteUserAddressFailed: (state, action) => { 60 | state.addressloading = false; 61 | state.error = action.payload; 62 | }, 63 | // get all users --- admin 64 | getAllUsersRequest: (state) => { 65 | state.usersLoading = true; 66 | }, 67 | getAllUsersSuccess: (state, action) => { 68 | state.usersLoading = false; 69 | state.users = action.payload; 70 | }, 71 | getAllUsersFailed: (state, action) => { 72 | state.usersLoading = false; 73 | state.error = action.payload; 74 | }, 75 | 76 | clearErrors: (state) => { 77 | state.error = null; 78 | }, 79 | }); 80 | 81 | // reducer -> logic (state change) 82 | -------------------------------------------------------------------------------- /frontend/src/redux/actions/product.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { server } from "../../server"; 3 | 4 | // create product 5 | export const createProduct = (newForm) => async (dispatch) => { 6 | try { 7 | dispatch({ 8 | type: "productCreateRequest", 9 | }); 10 | 11 | const config = { headers: { "Content-Type": "multipart/form-data" } }; 12 | 13 | const { data } = await axios.post( 14 | `${server}/product/create-product`, 15 | newForm, 16 | config 17 | ); 18 | dispatch({ 19 | type: "productCreateSuccess", 20 | payload: data.product, 21 | }); 22 | } catch (error) { 23 | dispatch({ 24 | type: "productCreateFail", 25 | payload: error.response.data.message, 26 | }); 27 | } 28 | }; 29 | 30 | // get All Products of a shop 31 | export const getAllProductsShop = (id) => async (dispatch) => { 32 | try { 33 | dispatch({ 34 | type: "getAllProductsShopRequest", 35 | }); 36 | 37 | const { data } = await axios.get( 38 | `${server}/product/get-all-products-shop/${id}` 39 | ); 40 | dispatch({ 41 | type: "getAllProductsShopSuccess", 42 | payload: data.products, 43 | }); 44 | } catch (error) { 45 | dispatch({ 46 | type: "getAllProductsShopFailed", 47 | payload: error.response.data.message, 48 | }); 49 | } 50 | }; 51 | 52 | // delete product of a shop 53 | export const deleteProduct = (id) => async (dispatch) => { 54 | try { 55 | dispatch({ 56 | type: "deleteProductRequest", 57 | }); 58 | 59 | const { data } = await axios.delete( 60 | `${server}/product/delete-shop-product/${id}`, 61 | { 62 | withCredentials: true, 63 | } 64 | ); 65 | 66 | dispatch({ 67 | type: "deleteProductSuccess", 68 | payload: data.message, 69 | }); 70 | } catch (error) { 71 | dispatch({ 72 | type: "deleteProductFailed", 73 | payload: error.response.data.message, 74 | }); 75 | } 76 | }; 77 | 78 | // get all products 79 | export const getAllProducts = () => async (dispatch) => { 80 | try { 81 | dispatch({ 82 | type: "getAllProductsRequest", 83 | }); 84 | 85 | const { data } = await axios.get(`${server}/product/get-all-products`); 86 | dispatch({ 87 | type: "getAllProductsSuccess", 88 | payload: data.products, 89 | }); 90 | } catch (error) { 91 | dispatch({ 92 | type: "getAllProductsFailed", 93 | payload: error.response.data.message, 94 | }); 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /frontend/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/controller/coupounCode.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 3 | const Shop = require("../model/shop"); 4 | const ErrorHandler = require("../utils/ErrorHandler"); 5 | const { isSeller } = require("../middleware/auth"); 6 | const CoupounCode = require("../model/coupounCode"); 7 | const router = express.Router(); 8 | 9 | // create coupoun code 10 | router.post( 11 | "/create-coupon-code", 12 | isSeller, 13 | catchAsyncErrors(async (req, res, next) => { 14 | try { 15 | const isCoupounCodeExists = await CoupounCode.find({ 16 | name: req.body.name, 17 | }); 18 | 19 | if (isCoupounCodeExists.length !== 0) { 20 | return next(new ErrorHandler("Coupoun code already exists!", 400)); 21 | } 22 | 23 | const coupounCode = await CoupounCode.create(req.body); 24 | 25 | res.status(201).json({ 26 | success: true, 27 | coupounCode, 28 | }); 29 | } catch (error) { 30 | return next(new ErrorHandler(error, 400)); 31 | } 32 | }) 33 | ); 34 | 35 | // get all coupons of a shop 36 | router.get( 37 | "/get-coupon/:id", 38 | isSeller, 39 | catchAsyncErrors(async (req, res, next) => { 40 | try { 41 | const couponCodes = await CoupounCode.find({ shopId: req.seller.id }); 42 | res.status(201).json({ 43 | success: true, 44 | couponCodes, 45 | }); 46 | } catch (error) { 47 | return next(new ErrorHandler(error, 400)); 48 | } 49 | }) 50 | ); 51 | 52 | // delete coupoun code of a shop 53 | router.delete( 54 | "/delete-coupon/:id", 55 | isSeller, 56 | catchAsyncErrors(async (req, res, next) => { 57 | try { 58 | const couponCode = await CoupounCode.findByIdAndDelete(req.params.id); 59 | 60 | if (!couponCode) { 61 | return next(new ErrorHandler("Coupon code dosen't exists!", 400)); 62 | } 63 | res.status(201).json({ 64 | success: true, 65 | message: "Coupon code deleted successfully!", 66 | }); 67 | } catch (error) { 68 | return next(new ErrorHandler(error, 400)); 69 | } 70 | }) 71 | ); 72 | 73 | // get coupon code value by its name 74 | router.get( 75 | "/get-coupon-value/:name", 76 | catchAsyncErrors(async (req, res, next) => { 77 | try { 78 | const couponCode = await CoupounCode.findOne({ name: req.params.name }); 79 | 80 | res.status(200).json({ 81 | success: true, 82 | couponCode, 83 | }); 84 | } catch (error) { 85 | return next(new ErrorHandler(error, 400)); 86 | } 87 | }) 88 | ); 89 | 90 | module.exports = router; 91 | -------------------------------------------------------------------------------- /frontend/src/pages/AdminDashboardOrders.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import AdminHeader from "../components/Layout/AdminHeader"; 3 | import AdminSideBar from "../components/Admin/Layout/AdminSideBar"; 4 | import { DataGrid } from "@material-ui/data-grid"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import { getAllOrdersOfAdmin } from "../redux/actions/order"; 7 | 8 | const AdminDashboardOrders = () => { 9 | const dispatch = useDispatch(); 10 | 11 | const { adminOrders, adminOrderLoading } = useSelector( 12 | (state) => state.order 13 | ); 14 | 15 | useEffect(() => { 16 | dispatch(getAllOrdersOfAdmin()); 17 | }, []); 18 | 19 | const columns = [ 20 | { field: "id", headerName: "Order ID", minWidth: 150, flex: 0.7 }, 21 | 22 | { 23 | field: "status", 24 | headerName: "Status", 25 | minWidth: 130, 26 | flex: 0.7, 27 | cellClassName: (params) => { 28 | return params.getValue(params.id, "status") === "Delivered" 29 | ? "greenColor" 30 | : "redColor"; 31 | }, 32 | }, 33 | { 34 | field: "itemsQty", 35 | headerName: "Items Qty", 36 | type: "number", 37 | minWidth: 130, 38 | flex: 0.7, 39 | }, 40 | 41 | { 42 | field: "total", 43 | headerName: "Total", 44 | type: "number", 45 | minWidth: 130, 46 | flex: 0.8, 47 | }, 48 | { 49 | field: "createdAt", 50 | headerName: "Order Date", 51 | type: "number", 52 | minWidth: 130, 53 | flex: 0.8, 54 | }, 55 | ]; 56 | 57 | const row = []; 58 | adminOrders && 59 | adminOrders.forEach((item) => { 60 | row.push({ 61 | id: item._id, 62 | itemsQty: item?.cart?.reduce((acc, item) => acc + item.qty, 0), 63 | total: item?.totalPrice + " $", 64 | status: item?.status, 65 | createdAt: item?.createdAt.slice(0, 10), 66 | }); 67 | }); 68 | return ( 69 |
70 | 71 |
72 |
73 |
74 | 75 |
76 | 77 |
78 |
79 | 86 |
87 |
88 |
89 |
90 |
91 | ); 92 | }; 93 | 94 | export default AdminDashboardOrders; 95 | -------------------------------------------------------------------------------- /frontend/src/components/Events/EventCard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { backend_url } from "../../server"; 3 | import styles from "../../styles/styles"; 4 | import CountDown from "./CountDown"; 5 | import { Link } from "react-router-dom"; 6 | import { useDispatch, useSelector } from "react-redux"; 7 | import { addTocart } from "../../redux/actions/cart"; 8 | import { toast } from "react-toastify"; 9 | 10 | const EventCard = ({ active, data }) => { 11 | const { cart } = useSelector((state) => state.cart); 12 | const dispatch = useDispatch(); 13 | 14 | useEffect(() => { 15 | window.scrollTo(0, 0); 16 | }, []); 17 | 18 | const addToCartHandler = (data) => { 19 | const isItemExists = cart && cart.find((i) => i._id === data._id); 20 | if (isItemExists) { 21 | toast.error("Item alredy in cart!"); 22 | } else { 23 | if (data.stock < 1) { 24 | toast.error("Product stock limited!"); 25 | } else { 26 | const cartData = { ...data, qty: 1 }; 27 | dispatch(addTocart(cartData)); 28 | toast.success("item added to cart successfully!"); 29 | } 30 | } 31 | }; 32 | 33 | return ( 34 |
39 |
40 | 41 |
42 | 43 |
44 |

{data.name}

45 |

{data.description}

46 | 47 |
48 |
49 |
50 | {data.originalPrice}$ 51 |
52 |
53 | {data.discountPrice}$ 54 |
55 |
56 | 57 | {data.sold_out} sold 58 | 59 |
60 | 61 |
62 |
63 | 64 |
See Details
65 | 66 |
addToCartHandler(data)} 69 | > 70 | Add to cart 71 |
72 |
73 |
74 |
75 | ); 76 | }; 77 | 78 | export default EventCard; 79 | -------------------------------------------------------------------------------- /frontend/src/components/Admin/AllProducts.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@material-ui/core"; 2 | import { DataGrid } from "@material-ui/data-grid"; 3 | import React, { useEffect } from "react"; 4 | import { AiOutlineDelete, AiOutlineEye } from "react-icons/ai"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import { Link } from "react-router-dom"; 7 | import { getAllProductsShop } from "../../redux/actions/product"; 8 | import { deleteProduct } from "../../redux/actions/product"; 9 | import Loader from "../Layout/Loader"; 10 | import axios from "axios"; 11 | import { server } from "../../server"; 12 | import { useState } from "react"; 13 | 14 | const AllProducts = () => { 15 | const [data, setData] = useState([]); 16 | 17 | useEffect(() => { 18 | axios 19 | .get(`${server}/product/admin-all-products`, { withCredentials: true }) 20 | .then((res) => { 21 | setData(res.data.products); 22 | }); 23 | }, []); 24 | 25 | const columns = [ 26 | { field: "id", headerName: "Product Id", minWidth: 150, flex: 0.7 }, 27 | { 28 | field: "name", 29 | headerName: "Name", 30 | minWidth: 180, 31 | flex: 1.4, 32 | }, 33 | { 34 | field: "price", 35 | headerName: "Price", 36 | minWidth: 100, 37 | flex: 0.6, 38 | }, 39 | { 40 | field: "Stock", 41 | headerName: "Stock", 42 | type: "number", 43 | minWidth: 80, 44 | flex: 0.5, 45 | }, 46 | 47 | { 48 | field: "sold", 49 | headerName: "Sold out", 50 | type: "number", 51 | minWidth: 130, 52 | flex: 0.6, 53 | }, 54 | { 55 | field: "Preview", 56 | flex: 0.8, 57 | minWidth: 100, 58 | headerName: "", 59 | type: "number", 60 | sortable: false, 61 | renderCell: (params) => { 62 | return ( 63 | <> 64 | 65 | 68 | 69 | 70 | ); 71 | }, 72 | }, 73 | ]; 74 | 75 | const row = []; 76 | 77 | data && 78 | data.forEach((item) => { 79 | row.push({ 80 | id: item._id, 81 | name: item.name, 82 | price: "US$ " + item.discountPrice, 83 | Stock: item.stock, 84 | sold: item?.sold_out, 85 | }); 86 | }); 87 | 88 | return ( 89 | <> 90 |
91 | 98 |
99 | 100 | ); 101 | }; 102 | 103 | export default AllProducts; 104 | -------------------------------------------------------------------------------- /frontend/src/components/Route/Categories/Categories.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useNavigate } from "react-router-dom"; 3 | import { brandingData, categoriesData } from "../../../static/data"; 4 | import styles from '../../../styles/styles' 5 | 6 | const Categories = () => { 7 | const navigate = useNavigate(); 8 | return ( 9 | <> 10 | 27 | 28 | {/* categories */} 29 |
33 |
34 | { 35 | categoriesData && 36 | categoriesData.map((i) => { 37 | const handleSubmit = (i) => { 38 | navigate(`/products?category=${i.title}`); 39 | } 40 | return ( 41 |
handleSubmit(i)} 45 | > 46 |
{i.title}
47 | catagory 52 |
53 | ) 54 | }) 55 | } 56 |
57 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | ) 65 | } 66 | 67 | export default Categories -------------------------------------------------------------------------------- /frontend/src/components/Shop/Layout/DashboardHeader.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { AiOutlineGift } from "react-icons/ai"; 3 | import { MdOutlineLocalOffer } from "react-icons/md"; 4 | import { FiPackage, FiShoppingBag } from "react-icons/fi"; 5 | import { useSelector } from "react-redux"; 6 | import { Link } from "react-router-dom"; 7 | import { BiMessageSquareDetail } from "react-icons/bi"; 8 | import { backend_url } from "../../../server"; 9 | 10 | const DashboardHeader = () => { 11 | const { seller } = useSelector((state) => state.seller); 12 | return ( 13 |
14 |
15 | 16 | 20 | 21 |
22 |
23 |
24 | 25 | 30 | 31 | 32 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | 54 | 55 | 56 | 61 | 62 |
63 |
64 |
65 | ); 66 | }; 67 | 68 | export default DashboardHeader; -------------------------------------------------------------------------------- /backend/controller/conversation.js: -------------------------------------------------------------------------------- 1 | const Conversation = require("../model/conversation"); 2 | const ErrorHandler = require("../utils/ErrorHandler"); 3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 4 | const express = require("express"); 5 | const { isSeller, isAuthenticated } = require("../middleware/auth"); 6 | const router = express.Router(); 7 | 8 | // create a new conversation 9 | router.post( 10 | "/create-new-conversation", 11 | catchAsyncErrors(async (req, res, next) => { 12 | try { 13 | const { groupTitle, userId, sellerId } = req.body; 14 | 15 | const isConversationExist = await Conversation.findOne({ groupTitle }); 16 | 17 | if (isConversationExist) { 18 | const conversation = isConversationExist; 19 | res.status(201).json({ 20 | success: true, 21 | conversation, 22 | }); 23 | } else { 24 | const conversation = await Conversation.create({ 25 | members: [userId, sellerId], 26 | groupTitle: groupTitle, 27 | }); 28 | 29 | res.status(201).json({ 30 | success: true, 31 | conversation, 32 | }); 33 | } 34 | } catch (error) { 35 | return next(new ErrorHandler(error.response.message), 500); 36 | } 37 | }) 38 | ); 39 | 40 | // get seller conversations 41 | router.get( 42 | "/get-all-conversation-seller/:id", 43 | isSeller, 44 | catchAsyncErrors(async (req, res, next) => { 45 | try { 46 | const conversations = await Conversation.find({ 47 | members: { 48 | $in: [req.params.id], 49 | }, 50 | }).sort({ updatedAt: -1, createdAt: -1 }); 51 | 52 | res.status(201).json({ 53 | success: true, 54 | conversations, 55 | }); 56 | } catch (error) { 57 | return next(new ErrorHandler(error), 500); 58 | } 59 | }) 60 | ); 61 | 62 | // get user conversations 63 | router.get( 64 | "/get-all-conversation-user/:id", 65 | isAuthenticated, 66 | catchAsyncErrors(async (req, res, next) => { 67 | try { 68 | const conversations = await Conversation.find({ 69 | members: { 70 | $in: [req.params.id], 71 | }, 72 | }).sort({ updatedAt: -1, createdAt: -1 }); 73 | 74 | res.status(201).json({ 75 | success: true, 76 | conversations, 77 | }); 78 | } catch (error) { 79 | return next(new ErrorHandler(error), 500); 80 | } 81 | }) 82 | ); 83 | 84 | // update the last message 85 | router.put( 86 | "/update-last-message/:id", 87 | catchAsyncErrors(async (req, res, next) => { 88 | try { 89 | const { lastMessage, lastMessageId } = req.body; 90 | 91 | const conversation = await Conversation.findByIdAndUpdate(req.params.id, { 92 | lastMessage, 93 | lastMessageId, 94 | }); 95 | 96 | res.status(201).json({ 97 | success: true, 98 | conversation, 99 | }); 100 | } catch (error) { 101 | return next(new ErrorHandler(error), 500); 102 | } 103 | }) 104 | ); 105 | 106 | module.exports = router; 107 | -------------------------------------------------------------------------------- /backend/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const ErrorHandler = require("./middleware/error"); 3 | const connectDatabase = require("./db/Database"); 4 | const app = express(); 5 | 6 | const cookieParser = require("cookie-parser"); 7 | const bodyParser = require("body-parser"); 8 | const cors = require("cors"); 9 | const path = require("path"); 10 | 11 | // config 12 | if (process.env.NODE_ENV !== "PRODUCTION") { 13 | require("dotenv").config({ 14 | path: "config/.env", 15 | }); 16 | } 17 | // connect db 18 | connectDatabase(); 19 | 20 | // create server 21 | const server = app.listen(process.env.PORT, () => { 22 | console.log(`Server is running on http://localhost:${process.env.PORT}`); 23 | }); 24 | 25 | // middlewares 26 | app.use(express.json()); 27 | app.use(cookieParser()); 28 | // Enable CORS for all routes 29 | 30 | app.use( 31 | cors({ 32 | origin: "http://localhost:3000", 33 | credentials: true, 34 | }) 35 | ); 36 | 37 | app.use("/", express.static("uploads")); 38 | 39 | app.get("/test", (req, res) => { 40 | res.send("Hello World!"); 41 | }); 42 | 43 | app.use(bodyParser.urlencoded({ extended: true, limit: "50mb" })); 44 | 45 | // why bodyparser? 46 | // bodyparser is used to parse the data from the body of the request to the server (POST, PUT, DELETE, etc.) 47 | 48 | // config 49 | if (process.env.NODE_ENV !== "PRODUCTION") { 50 | require("dotenv").config({ 51 | path: "config/.env", 52 | }); 53 | } 54 | 55 | app.get("/", (req, res) => { 56 | res.send("Hello World!"); 57 | }); 58 | 59 | // routes 60 | const user = require("./controller/user"); 61 | const shop = require("./controller/shop"); 62 | const product = require("./controller/product"); 63 | const event = require("./controller/event"); 64 | const coupon = require("./controller/coupounCode"); 65 | const payment = require("./controller/payment"); 66 | const order = require("./controller/order"); 67 | const message = require("./controller/message"); 68 | const conversation = require("./controller/conversation"); 69 | const withdraw = require("./controller/withdraw"); 70 | app.use("/api/v2/withdraw", withdraw); 71 | 72 | // end points 73 | app.use("/api/v2/user", user); 74 | app.use("/api/v2/conversation", conversation); 75 | app.use("/api/v2/message", message); 76 | app.use("/api/v2/order", order); 77 | app.use("/api/v2/shop", shop); 78 | app.use("/api/v2/product", product); 79 | app.use("/api/v2/event", event); 80 | app.use("/api/v2/coupon", coupon); 81 | app.use("/api/v2/payment", payment); 82 | 83 | // it'for errhendel 84 | app.use(ErrorHandler); 85 | 86 | // Handling Uncaught Exceptions 87 | process.on("uncaughtException", (err) => { 88 | console.log(`Error: ${err.message}`); 89 | console.log(`shutting down the server for handling UNCAUGHT EXCEPTION! 💥`); 90 | }); 91 | 92 | // unhandled promise rejection 93 | process.on("unhandledRejection", (err) => { 94 | console.log(`Shutting down the server for ${err.message}`); 95 | console.log(`shutting down the server for unhandle promise rejection`); 96 | 97 | server.close(() => { 98 | process.exit(1); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /frontend/src/components/Shop/AllOrders.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@material-ui/core"; 2 | import { DataGrid } from "@material-ui/data-grid"; 3 | import React, { useEffect } from "react"; 4 | import { useDispatch, useSelector } from "react-redux"; 5 | import { Link } from "react-router-dom"; 6 | import Loader from "../Layout/Loader"; 7 | import { getAllOrdersOfShop } from "../../redux/actions/order"; 8 | import { AiOutlineArrowRight } from "react-icons/ai"; 9 | 10 | const AllOrders = () => { 11 | const { orders, isLoading } = useSelector((state) => state.order); 12 | const { seller } = useSelector((state) => state.seller); 13 | 14 | const dispatch = useDispatch(); 15 | 16 | useEffect(() => { 17 | dispatch(getAllOrdersOfShop(seller._id)); 18 | }, [dispatch]); 19 | 20 | const columns = [ 21 | { field: "id", headerName: "Order ID", minWidth: 150, flex: 0.7 }, 22 | 23 | { 24 | field: "status", 25 | headerName: "Status", 26 | minWidth: 130, 27 | flex: 0.7, 28 | cellClassName: (params) => { 29 | return params.getValue(params.id, "status") === "Delivered" 30 | ? "greenColor" 31 | : "redColor"; 32 | }, 33 | }, 34 | { 35 | field: "itemsQty", 36 | headerName: "Items Qty", 37 | type: "number", 38 | minWidth: 130, 39 | flex: 0.7, 40 | }, 41 | 42 | { 43 | field: "total", 44 | headerName: "Total", 45 | type: "number", 46 | minWidth: 130, 47 | flex: 0.8, 48 | }, 49 | 50 | { 51 | field: " ", 52 | flex: 1, 53 | minWidth: 150, 54 | headerName: "", 55 | type: "number", 56 | sortable: false, 57 | renderCell: (params) => { 58 | return ( 59 | <> 60 | 61 | 64 | 65 | 66 | ); 67 | }, 68 | }, 69 | ]; 70 | 71 | const row = []; 72 | 73 | orders && 74 | orders.forEach((item) => { 75 | row.push({ 76 | id: item._id, 77 | itemsQty: item.cart.length, 78 | total: "US$ " + item.totalPrice, 79 | status: item.status, 80 | }); 81 | }); 82 | 83 | return ( 84 | <> 85 | {isLoading ? ( 86 | 87 | ) : ( 88 |
89 | 96 |
97 | )} 98 | 99 | ); 100 | }; 101 | 102 | export default AllOrders; -------------------------------------------------------------------------------- /socket/index.js: -------------------------------------------------------------------------------- 1 | const socketIO = require("socket.io"); 2 | const http = require("http"); 3 | const express = require("express"); 4 | const cors = require("cors"); 5 | const app = express(); 6 | const server = http.createServer(app); 7 | const io = socketIO(server); 8 | 9 | require("dotenv").config({ 10 | path: "./.env", 11 | }); 12 | 13 | app.use(cors()); 14 | app.use(express.json()); 15 | 16 | app.get("/", (req, res) => { 17 | res.send("Hello world from socket server!"); 18 | }); 19 | 20 | let users = []; 21 | 22 | const addUser = (userId, socketId) => { 23 | !users.some((user) => user.userId === userId) && 24 | users.push({ userId, socketId }); 25 | }; 26 | 27 | const removeUser = (socketId) => { 28 | users = users.filter((user) => user.socketId !== socketId); 29 | }; 30 | 31 | const getUser = (receiverId) => { 32 | return users.find((user) => user.userId === receiverId); 33 | }; 34 | 35 | // Define a message object with a seen property 36 | const createMessage = ({ senderId, receiverId, text, images }) => ({ 37 | senderId, 38 | receiverId, 39 | text, 40 | images, 41 | seen: false, 42 | }); 43 | 44 | io.on("connection", (socket) => { 45 | // when connect 46 | // console.log(`a user is connected`); 47 | 48 | // take userId and socketId from user 49 | socket.on("addUser", (userId) => { 50 | addUser(userId, socket.id); 51 | io.emit("getUsers", users); 52 | }); 53 | 54 | // send and get message 55 | const messages = {}; // Object to track messages sent to each user 56 | 57 | socket.on("sendMessage", ({ senderId, receiverId, text, images }) => { 58 | const message = createMessage({ senderId, receiverId, text, images }); 59 | 60 | const user = getUser(receiverId); 61 | 62 | // Store the messages in the `messages` object 63 | if (!messages[receiverId]) { 64 | messages[receiverId] = [message]; 65 | } else { 66 | messages[receiverId].push(message); 67 | } 68 | 69 | // send the message to the recevier 70 | io.to(user?.socketId).emit("getMessage", message); 71 | }); 72 | 73 | socket.on("messageSeen", ({ senderId, receiverId, messageId }) => { 74 | const user = getUser(senderId); 75 | 76 | // update the seen flag for the message 77 | if (messages[senderId]) { 78 | const message = messages[senderId].find( 79 | (message) => 80 | message.receiverId === receiverId && message.id === messageId 81 | ); 82 | if (message) { 83 | message.seen = true; 84 | 85 | // send a message seen event to the sender 86 | io.to(user?.socketId).emit("messageSeen", { 87 | senderId, 88 | receiverId, 89 | messageId, 90 | }); 91 | } 92 | } 93 | }); 94 | 95 | // update and get last message 96 | socket.on("updateLastMessage", ({ lastMessage, lastMessagesId }) => { 97 | io.emit("getLastMessage", { 98 | lastMessage, 99 | lastMessagesId, 100 | }); 101 | }); 102 | 103 | //when disconnect 104 | socket.on("disconnect", () => { 105 | // console.log(`a user disconnected!`); 106 | removeUser(socket.id); 107 | io.emit("getUsers", users); 108 | }); 109 | }); 110 | 111 | server.listen(process.env.PORT || 4000, () => { 112 | console.log(`server is running on port ${process.env.PORT || 4000}`); 113 | }); 114 | -------------------------------------------------------------------------------- /frontend/src/components/Shop/AllProducts.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@material-ui/core"; 2 | import { DataGrid } from "@material-ui/data-grid"; 3 | import React, { useEffect } from "react"; 4 | import { AiOutlineDelete, AiOutlineEye } from "react-icons/ai"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import { Link } from "react-router-dom"; 7 | import { getAllProductsShop } from "../../redux/actions/product"; 8 | import { deleteProduct } from "../../redux/actions/product"; 9 | import Loader from "../Layout/Loader"; 10 | 11 | const AllProducts = () => { 12 | const { products, isLoading } = useSelector((state) => state.products); 13 | const { seller } = useSelector((state) => state.seller); 14 | 15 | const dispatch = useDispatch(); 16 | 17 | useEffect(() => { 18 | dispatch(getAllProductsShop(seller._id)); 19 | }, [dispatch]); 20 | 21 | const handleDelete = (id) => { 22 | dispatch(deleteProduct(id)); 23 | window.location.reload(); 24 | }; 25 | 26 | const columns = [ 27 | { field: "id", headerName: "Product Id", minWidth: 150, flex: 0.7 }, 28 | { 29 | field: "name", 30 | headerName: "Name", 31 | minWidth: 180, 32 | flex: 1.4, 33 | }, 34 | { 35 | field: "price", 36 | headerName: "Price", 37 | minWidth: 100, 38 | flex: 0.6, 39 | }, 40 | { 41 | field: "Stock", 42 | headerName: "Stock", 43 | type: "number", 44 | minWidth: 80, 45 | flex: 0.5, 46 | }, 47 | 48 | { 49 | field: "sold", 50 | headerName: "Sold out", 51 | type: "number", 52 | minWidth: 130, 53 | flex: 0.6, 54 | }, 55 | { 56 | field: "Preview", 57 | flex: 0.8, 58 | minWidth: 100, 59 | headerName: "", 60 | type: "number", 61 | sortable: false, 62 | renderCell: (params) => { 63 | return ( 64 | <> 65 | 66 | 69 | 70 | 71 | ); 72 | }, 73 | }, 74 | { 75 | field: "Delete", 76 | flex: 0.8, 77 | minWidth: 120, 78 | headerName: "", 79 | type: "number", 80 | sortable: false, 81 | renderCell: (params) => { 82 | return ( 83 | <> 84 | 87 | 88 | ); 89 | }, 90 | }, 91 | ]; 92 | 93 | const row = []; 94 | 95 | products && 96 | products.forEach((item) => { 97 | row.push({ 98 | id: item._id, 99 | name: item.name, 100 | price: "US$ " + item.discountPrice, 101 | Stock: item.stock, 102 | sold: item.sold_out, 103 | }); 104 | }); 105 | 106 | return ( 107 | <> 108 | {isLoading ? ( 109 | 110 | ) : ( 111 |
112 | 119 |
120 | )} 121 | 122 | ); 123 | }; 124 | 125 | export default AllProducts; 126 | -------------------------------------------------------------------------------- /frontend/src/components/Shop/AllRefundOrders.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@material-ui/core"; 2 | import { DataGrid } from "@material-ui/data-grid"; 3 | import React, { useEffect } from "react"; 4 | import { useDispatch, useSelector } from "react-redux"; 5 | import { Link } from "react-router-dom"; 6 | import Loader from "../Layout/Loader"; 7 | import { getAllOrdersOfShop } from "../../redux/actions/order"; 8 | import { AiOutlineArrowRight } from "react-icons/ai"; 9 | 10 | const AllRefundOrders = () => { 11 | const { orders, isLoading } = useSelector((state) => state.order); 12 | const { seller } = useSelector((state) => state.seller); 13 | 14 | const dispatch = useDispatch(); 15 | 16 | useEffect(() => { 17 | dispatch(getAllOrdersOfShop(seller._id)); 18 | }, [dispatch]); 19 | 20 | const refundOrders = orders && orders.filter((item) => item.status === "Processing refund" || item.status === "Refund Success"); 21 | 22 | const columns = [ 23 | { field: "id", headerName: "Order ID", minWidth: 150, flex: 0.7 }, 24 | 25 | { 26 | field: "status", 27 | headerName: "Status", 28 | minWidth: 130, 29 | flex: 0.7, 30 | cellClassName: (params) => { 31 | return params.getValue(params.id, "status") === "Delivered" 32 | ? "greenColor" 33 | : "redColor"; 34 | }, 35 | }, 36 | { 37 | field: "itemsQty", 38 | headerName: "Items Qty", 39 | type: "number", 40 | minWidth: 130, 41 | flex: 0.7, 42 | }, 43 | 44 | { 45 | field: "total", 46 | headerName: "Total", 47 | type: "number", 48 | minWidth: 130, 49 | flex: 0.8, 50 | }, 51 | 52 | { 53 | field: " ", 54 | flex: 1, 55 | minWidth: 150, 56 | headerName: "", 57 | type: "number", 58 | sortable: false, 59 | renderCell: (params) => { 60 | return ( 61 | <> 62 | 63 | 66 | 67 | 68 | ); 69 | }, 70 | }, 71 | ]; 72 | 73 | const row = []; 74 | 75 | refundOrders && 76 | refundOrders.forEach((item) => { 77 | row.push({ 78 | id: item._id, 79 | itemsQty: item.cart.length, 80 | total: "US$ " + item.totalPrice, 81 | status: item.status, 82 | }); 83 | }); 84 | 85 | return ( 86 | <> 87 | {isLoading ? ( 88 | 89 | ) : ( 90 |
91 | 98 |
99 | )} 100 | 101 | ); 102 | }; 103 | 104 | export default AllRefundOrders; -------------------------------------------------------------------------------- /frontend/src/components/Shop/AllEvents.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@material-ui/core"; 2 | import { DataGrid } from "@material-ui/data-grid"; 3 | import React, { useEffect } from "react"; 4 | import { AiOutlineDelete, AiOutlineEye } from "react-icons/ai"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import { Link } from "react-router-dom"; 7 | import { deleteEvent, getAllEventsShop } from "../../redux/actions/event"; 8 | import { getAllProductsShop } from "../../redux/actions/product"; 9 | import { deleteProduct } from "../../redux/actions/product"; 10 | import Loader from "../Layout/Loader"; 11 | 12 | const AllEvents = () => { 13 | const { events, isLoading } = useSelector((state) => state.events); 14 | const { seller } = useSelector((state) => state.seller); 15 | 16 | const dispatch = useDispatch(); 17 | 18 | useEffect(() => { 19 | dispatch(getAllEventsShop(seller._id)); 20 | }, [dispatch]); 21 | 22 | const handleDelete = (id) => { 23 | dispatch(deleteEvent(id)); 24 | window.location.reload(); 25 | }; 26 | 27 | const columns = [ 28 | { field: "id", headerName: "Product Id", minWidth: 150, flex: 0.7 }, 29 | { 30 | field: "name", 31 | headerName: "Name", 32 | minWidth: 180, 33 | flex: 1.4, 34 | }, 35 | { 36 | field: "price", 37 | headerName: "Price", 38 | minWidth: 100, 39 | flex: 0.6, 40 | }, 41 | { 42 | field: "Stock", 43 | headerName: "Stock", 44 | type: "number", 45 | minWidth: 80, 46 | flex: 0.5, 47 | }, 48 | 49 | { 50 | field: "sold", 51 | headerName: "Sold out", 52 | type: "number", 53 | minWidth: 130, 54 | flex: 0.6, 55 | }, 56 | { 57 | field: "Preview", 58 | flex: 0.8, 59 | minWidth: 100, 60 | headerName: "", 61 | type: "number", 62 | sortable: false, 63 | renderCell: (params) => { 64 | return ( 65 | <> 66 | 67 | 70 | 71 | 72 | ); 73 | }, 74 | }, 75 | { 76 | field: "Delete", 77 | flex: 0.8, 78 | minWidth: 120, 79 | headerName: "", 80 | type: "number", 81 | sortable: false, 82 | renderCell: (params) => { 83 | return ( 84 | <> 85 | 88 | 89 | ); 90 | }, 91 | }, 92 | ]; 93 | 94 | const row = []; 95 | 96 | events && 97 | events.forEach((item) => { 98 | row.push({ 99 | id: item._id, 100 | name: item.name, 101 | price: "US$ " + item.discountPrice, 102 | Stock: item.stock, 103 | sold: item.sold_out, 104 | }); 105 | }); 106 | 107 | return ( 108 | <> 109 | {isLoading ? ( 110 | 111 | ) : ( 112 |
113 | 120 |
121 | )} 122 | 123 | ); 124 | }; 125 | 126 | export default AllEvents; 127 | -------------------------------------------------------------------------------- /backend/controller/withdraw.js: -------------------------------------------------------------------------------- 1 | const Shop = require("../model/shop"); 2 | const ErrorHandler = require("../utils/ErrorHandler"); 3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 4 | const express = require("express"); 5 | const { isSeller, isAuthenticated, isAdmin } = require("../middleware/auth"); 6 | const Withdraw = require("../model/withdraw"); 7 | const sendMail = require("../utils/sendMail"); 8 | const router = express.Router(); 9 | 10 | // create withdraw request --- only for seller 11 | router.post( 12 | "/create-withdraw-request", 13 | isSeller, 14 | catchAsyncErrors(async (req, res, next) => { 15 | try { 16 | const { amount } = req.body; 17 | 18 | const data = { 19 | seller: req.seller, 20 | amount, 21 | }; 22 | 23 | try { 24 | await sendMail({ 25 | email: req.seller.email, 26 | subject: "Withdraw Request", 27 | message: `Hello ${req.seller.name}, Your withdraw request of ${amount}$ is processing. It will take 3days to 7days to processing! `, 28 | }); 29 | res.status(201).json({ 30 | success: true, 31 | }); 32 | } catch (error) { 33 | return next(new ErrorHandler(error.message, 500)); 34 | } 35 | 36 | const withdraw = await Withdraw.create(data); 37 | 38 | const shop = await Shop.findById(req.seller._id); 39 | 40 | shop.availableBalance = shop.availableBalance - amount; 41 | 42 | await shop.save(); 43 | 44 | res.status(201).json({ 45 | success: true, 46 | withdraw, 47 | }); 48 | } catch (error) { 49 | return next(new ErrorHandler(error.message, 500)); 50 | } 51 | }) 52 | ); 53 | 54 | // get all withdraws --- admnin 55 | 56 | router.get( 57 | "/get-all-withdraw-request", 58 | isAuthenticated, 59 | isAdmin("Admin"), 60 | catchAsyncErrors(async (req, res, next) => { 61 | try { 62 | const withdraws = await Withdraw.find().sort({ createdAt: -1 }); 63 | 64 | res.status(201).json({ 65 | success: true, 66 | withdraws, 67 | }); 68 | } catch (error) { 69 | return next(new ErrorHandler(error.message, 500)); 70 | } 71 | }) 72 | ); 73 | 74 | // update withdraw request ---- admin 75 | router.put( 76 | "/update-withdraw-request/:id", 77 | isAuthenticated, 78 | isAdmin("Admin"), 79 | catchAsyncErrors(async (req, res, next) => { 80 | try { 81 | const { sellerId } = req.body; 82 | 83 | const withdraw = await Withdraw.findByIdAndUpdate( 84 | req.params.id, 85 | { 86 | status: "succeed", 87 | updatedAt: Date.now(), 88 | }, 89 | { new: true } 90 | ); 91 | 92 | const seller = await Shop.findById(sellerId); 93 | 94 | const transection = { 95 | _id: withdraw._id, 96 | amount: withdraw.amount, 97 | updatedAt: withdraw.updatedAt, 98 | status: withdraw.status, 99 | }; 100 | 101 | seller.transections = [...seller.transections, transection]; 102 | 103 | await seller.save(); 104 | 105 | try { 106 | await sendMail({ 107 | email: seller.email, 108 | subject: "Payment confirmation", 109 | message: `Hello ${seller.name}, Your withdraw request of ${withdraw.amount}$ is on the way. Delivery time depends on your bank's rules it usually takes 3days to 7days.`, 110 | }); 111 | } catch (error) { 112 | return next(new ErrorHandler(error.message, 500)); 113 | } 114 | res.status(201).json({ 115 | success: true, 116 | withdraw, 117 | }); 118 | } catch (error) { 119 | return next(new ErrorHandler(error.message, 500)); 120 | } 121 | }) 122 | ); 123 | 124 | module.exports = router; 125 | -------------------------------------------------------------------------------- /frontend/src/components/Wishlist/Wishlist.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { RxCross1 } from "react-icons/rx"; 3 | import styles from "../../styles/styles"; 4 | import { Link } from "react-router-dom"; 5 | import { BsCartPlus } from "react-icons/bs"; 6 | import { AiOutlineHeart } from "react-icons/ai"; 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { removeFromWishlist } from "../../redux/actions/wishlist"; 9 | import { addTocart } from "../../redux/actions/cart"; 10 | import { backend_url } from "../../server"; 11 | 12 | const Wishlist = ({ setOpenWishlist }) => { 13 | const { wishlist } = useSelector((state) => state.wishlist); 14 | const dispatch = useDispatch(); 15 | 16 | const removeFromWishlistHandler = (data) => { 17 | dispatch(removeFromWishlist(data)); 18 | }; 19 | 20 | const addToCartHandler = (data) => { 21 | const newData = { ...data, qty: 1 }; 22 | dispatch(addTocart(newData)); 23 | setOpenWishlist(false); 24 | }; 25 | 26 | return ( 27 |
28 |
29 | {wishlist && wishlist.length === 0 ? ( 30 |
31 |
32 | setOpenWishlist(false)} 35 | /> 36 |
37 |
Wish items is empot!
38 |
39 | ) : ( 40 | <> 41 |
42 |
43 | setOpenWishlist(false)} 47 | /> 48 |
49 | {/* item length */} 50 |
51 | 52 |
53 | {wishlist && wishlist.length} items 54 |
55 |
56 | 57 | {/* Cart Single item */} 58 |
59 |
60 | {wishlist && 61 | wishlist.map((i, index) => { 62 | return ( 63 | 69 | ); 70 | })} 71 |
72 |
73 | 74 | )} 75 |
76 |
77 | ); 78 | }; 79 | 80 | const CartSingle = ({ data, removeFromWishlistHandler, addToCartHandler }) => { 81 | const [value, setValue] = useState(1); 82 | const totalPrice = data.discountPrice * value; 83 | 84 | return ( 85 | <> 86 |
87 |
88 | removeFromWishlistHandler(data)} 91 | /> 92 | 97 | 98 |
99 |

{data.name}

100 | 101 |

102 | US${totalPrice} 103 |

104 |
105 | 106 |
107 | addToCartHandler(data)} 112 | /> 113 |
114 |
115 |
116 | 117 | ); 118 | }; 119 | 120 | export default Wishlist; 121 | -------------------------------------------------------------------------------- /frontend/src/components/Admin/AllUsers.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { Link } from "react-router-dom"; 4 | import { getAllUsers } from "../../redux/actions/user"; 5 | import { DataGrid } from "@material-ui/data-grid"; 6 | import { AiOutlineDelete } from "react-icons/ai"; 7 | import { Button } from "@material-ui/core"; 8 | import styles from "../../styles/styles"; 9 | import { RxCross1 } from "react-icons/rx"; 10 | import axios from "axios"; 11 | import { server } from "../../server"; 12 | import { toast } from "react-toastify"; 13 | 14 | const AllUsers = () => { 15 | const dispatch = useDispatch(); 16 | const { users } = useSelector((state) => state.user); 17 | const [open, setOpen] = useState(false); 18 | const [userId, setUserId] = useState(""); 19 | 20 | useEffect(() => { 21 | dispatch(getAllUsers()); 22 | }, [dispatch]); 23 | 24 | const handleDelete = async (id) => { 25 | await axios 26 | .delete(`${server}/user/delete-user/${id}`, { withCredentials: true }) 27 | .then((res) => { 28 | toast.success(res.data.message); 29 | }); 30 | 31 | dispatch(getAllUsers()); 32 | }; 33 | 34 | const columns = [ 35 | { field: "id", headerName: "User ID", minWidth: 150, flex: 0.7 }, 36 | 37 | { 38 | field: "name", 39 | headerName: "name", 40 | minWidth: 130, 41 | flex: 0.7, 42 | }, 43 | { 44 | field: "email", 45 | headerName: "Email", 46 | type: "text", 47 | minWidth: 130, 48 | flex: 0.7, 49 | }, 50 | { 51 | field: "role", 52 | headerName: "User Role", 53 | type: "text", 54 | minWidth: 130, 55 | flex: 0.7, 56 | }, 57 | 58 | { 59 | field: "joinedAt", 60 | headerName: "joinedAt", 61 | type: "text", 62 | minWidth: 130, 63 | flex: 0.8, 64 | }, 65 | 66 | { 67 | field: " ", 68 | flex: 1, 69 | minWidth: 150, 70 | headerName: "Delete User", 71 | type: "number", 72 | sortable: false, 73 | renderCell: (params) => { 74 | return ( 75 | <> 76 | 79 | 80 | ); 81 | }, 82 | }, 83 | ]; 84 | 85 | const row = []; 86 | users && 87 | users.forEach((item) => { 88 | row.push({ 89 | id: item._id, 90 | name: item.name, 91 | email: item.email, 92 | role: item.role, 93 | joinedAt: item.createdAt.slice(0, 10), 94 | }); 95 | }); 96 | 97 | return ( 98 |
99 |
100 |

All Users

101 |
102 | 109 |
110 | {open && ( 111 |
112 |
113 |
114 | setOpen(false)} /> 115 |
116 |

117 | Are you sure you wanna delete this user? 118 |

119 |
120 |
setOpen(false)} 123 | > 124 | cancel 125 |
126 |
setOpen(false) || handleDelete(userId)} 129 | > 130 | confirm 131 |
132 |
133 |
134 |
135 | )} 136 |
137 |
138 | ); 139 | }; 140 | 141 | export default AllUsers; 142 | -------------------------------------------------------------------------------- /frontend/src/redux/actions/user.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { server } from "../../server"; 3 | 4 | // load user 5 | export const loadUser = () => async (dispatch) => { 6 | try { 7 | dispatch({ 8 | type: "LoadUserRequest", 9 | }); 10 | const { data } = await axios.get(`${server}/user/getuser`, { 11 | withCredentials: true, 12 | }); 13 | dispatch({ 14 | type: "LoadUserSuccess", 15 | payload: data.user, 16 | }); 17 | } catch (error) { 18 | dispatch({ 19 | type: "LoadUserFail", 20 | payload: error.response.data.message, 21 | }); 22 | } 23 | }; 24 | 25 | // load seller 26 | export const loadSeller = () => async (dispatch) => { 27 | try { 28 | dispatch({ 29 | type: "LoadSellerRequest", 30 | }); 31 | const { data } = await axios.get(`${server}/shop/getSeller`, { 32 | withCredentials: true, 33 | }); 34 | dispatch({ 35 | type: "LoadSellerSuccess", 36 | payload: data.seller, 37 | }); 38 | } catch (error) { 39 | dispatch({ 40 | type: "LoadSellerFail", 41 | payload: error.response.data.message, 42 | }); 43 | } 44 | }; 45 | 46 | // User update information 47 | export const updateUserInformation = 48 | (name, email, phoneNumber, password) => async (dispatch) => { 49 | try { 50 | dispatch({ 51 | type: "updateUserInfoRequest", 52 | }); 53 | 54 | const { data } = await axios.put( 55 | `${server}/user/update-user-info`, 56 | { 57 | name, 58 | email, 59 | phoneNumber, 60 | password, 61 | }, 62 | { 63 | withCredentials: true, 64 | } 65 | ); 66 | dispatch({ 67 | type: "updateUserInfoSuccess", 68 | payload: data.user, 69 | }); 70 | } catch (error) { 71 | dispatch({ 72 | type: "updateUserInfoFailed", 73 | payload: error.response.data.message, 74 | }); 75 | } 76 | }; 77 | 78 | // update user address 79 | export const updatUserAddress = 80 | (country, city, address1, address2, zipCode, addressType) => 81 | async (dispatch) => { 82 | try { 83 | dispatch({ 84 | type: "updateUserAddressRequest", 85 | }); 86 | 87 | const { data } = await axios.put( 88 | `${server}/user/update-user-addresses`, 89 | { 90 | country, 91 | city, 92 | address1, 93 | address2, 94 | zipCode, 95 | addressType, 96 | }, 97 | { withCredentials: true } 98 | ); 99 | 100 | dispatch({ 101 | type: "updateUserAddressSuccess", 102 | payload: { 103 | successMessage: "User address updated succesfully!", 104 | user: data.user, 105 | }, 106 | }); 107 | } catch (error) { 108 | dispatch({ 109 | type: "updateUserAddressFailed", 110 | payload: error.response.data.message, 111 | }); 112 | } 113 | }; 114 | 115 | // delete user address 116 | export const deleteUserAddress = (id) => async (dispatch) => { 117 | try { 118 | dispatch({ 119 | type: "deleteUserAddressRequest", 120 | }); 121 | 122 | const { data } = await axios.delete( 123 | `${server}/user/delete-user-address/${id}`, 124 | { withCredentials: true } 125 | ); 126 | 127 | dispatch({ 128 | type: "deleteUserAddressSuccess", 129 | payload: { 130 | successMessage: "Address deleted successfully!", 131 | user: data.user, 132 | }, 133 | }); 134 | } catch (error) { 135 | dispatch({ 136 | type: "deleteUserAddressFailed", 137 | payload: error.response.data.message, 138 | }); 139 | } 140 | }; 141 | 142 | // get all users --- admin 143 | export const getAllUsers = () => async (dispatch) => { 144 | try { 145 | dispatch({ 146 | type: "getAllUsersRequest", 147 | }); 148 | 149 | const { data } = await axios.get(`${server}/user/admin-all-users`, { 150 | withCredentials: true, 151 | }); 152 | 153 | dispatch({ 154 | type: "getAllUsersSuccess", 155 | payload: data.users, 156 | }); 157 | } catch (error) { 158 | dispatch({ 159 | type: "getAllUsersFailed", 160 | payload: error.response.data.message, 161 | }); 162 | } 163 | }; 164 | 165 | // what is action in redux ? 166 | // Trigger an event , and call reducer 167 | // action is a plain object that contains information about an event that has occurred 168 | // action is the only way to change the state in redux 169 | // action is the only way to send data from the application to the store 170 | 171 | // dispatch :- active action , (action trigger) 172 | -------------------------------------------------------------------------------- /frontend/src/components/Admin/AllWithdraw.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { server } from "../../server"; 4 | import { Link } from "react-router-dom"; 5 | import { DataGrid } from "@material-ui/data-grid"; 6 | import { BsPencil } from "react-icons/bs"; 7 | import { RxCross1 } from "react-icons/rx"; 8 | import styles from "../../styles/styles"; 9 | import { toast } from "react-toastify"; 10 | 11 | const AllWithdraw = () => { 12 | const [data, setData] = useState([]); 13 | const [open, setOpen] = useState(false); 14 | const [withdrawData, setWithdrawData] = useState(); 15 | const [withdrawStatus, setWithdrawStatus] = useState("Processing"); 16 | 17 | useEffect(() => { 18 | axios 19 | .get(`${server}/withdraw/get-all-withdraw-request`, { 20 | withCredentials: true, 21 | }) 22 | .then((res) => { 23 | setData(res.data.withdraws); 24 | }) 25 | .catch((error) => { 26 | console.log(error.response.data.message); 27 | }); 28 | }, []); 29 | 30 | const columns = [ 31 | { field: "id", headerName: "Withdraw Id", minWidth: 150, flex: 0.7 }, 32 | { 33 | field: "name", 34 | headerName: "Shop Name", 35 | minWidth: 180, 36 | flex: 1.4, 37 | }, 38 | { 39 | field: "shopId", 40 | headerName: "Shop Id", 41 | minWidth: 180, 42 | flex: 1.4, 43 | }, 44 | { 45 | field: "amount", 46 | headerName: "Amount", 47 | minWidth: 100, 48 | flex: 0.6, 49 | }, 50 | { 51 | field: "status", 52 | headerName: "status", 53 | type: "text", 54 | minWidth: 80, 55 | flex: 0.5, 56 | }, 57 | { 58 | field: "createdAt", 59 | headerName: "Request given at", 60 | type: "number", 61 | minWidth: 130, 62 | flex: 0.6, 63 | }, 64 | { 65 | field: " ", 66 | headerName: "Update Status", 67 | type: "number", 68 | minWidth: 130, 69 | flex: 0.6, 70 | renderCell: (params) => { 71 | return ( 72 | setOpen(true) || setWithdrawData(params.row)} 78 | /> 79 | ); 80 | }, 81 | }, 82 | ]; 83 | 84 | const handleSubmit = async () => { 85 | await axios 86 | .put( 87 | `${server}/withdraw/update-withdraw-request/${withdrawData.id}`, 88 | { 89 | sellerId: withdrawData.shopId, 90 | }, 91 | { withCredentials: true } 92 | ) 93 | .then((res) => { 94 | toast.success("Withdraw request updated successfully!"); 95 | setData(res.data.withdraws); 96 | setOpen(false); 97 | }); 98 | }; 99 | 100 | const row = []; 101 | 102 | data && 103 | data.forEach((item) => { 104 | row.push({ 105 | id: item._id, 106 | shopId: item.seller._id, 107 | name: item.seller.name, 108 | amount: "US$ " + item.amount, 109 | status: item.status, 110 | createdAt: item.createdAt.slice(0, 10), 111 | }); 112 | }); 113 | return ( 114 |
115 |
116 | 123 |
124 | {open && ( 125 |
126 |
127 |
128 | setOpen(false)} /> 129 |
130 |

131 | Update Withdraw status 132 |

133 |
134 | 143 | 150 |
151 |
152 | )} 153 |
154 | ); 155 | }; 156 | 157 | export default AllWithdraw; 158 | -------------------------------------------------------------------------------- /frontend/src/components/Shop/ShopInfo.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { useDispatch, useSelector } from "react-redux"; 4 | import { Link, useParams } from "react-router-dom"; 5 | import { getAllProductsShop } from "../../redux/actions/product"; 6 | import { backend_url, server } from "../../server"; 7 | import styles from "../../styles/styles"; 8 | import Loader from "../Layout/Loader"; 9 | 10 | 11 | 12 | const ShopInfo = ({ isOwner }) => { 13 | const [data, setData] = useState({}); 14 | const { products } = useSelector((state) => state.products); 15 | const [isLoading, setIsLoading] = useState(false); 16 | 17 | const { id } = useParams(); 18 | const dispatch = useDispatch(); 19 | 20 | 21 | useEffect(() => { 22 | dispatch(getAllProductsShop(id)); 23 | setIsLoading(true); 24 | axios.get(`${server}/shop/get-shop-info/${id}`).then((res) => { 25 | setData(res.data.shop); 26 | setIsLoading(false); 27 | }).catch((error) => { 28 | console.log(error); 29 | setIsLoading(false); 30 | }) 31 | }, []) 32 | 33 | 34 | const logoutHandler = async () => { 35 | axios.get(`${server}/shop/logout`, { 36 | withCredentials: true, 37 | }); 38 | window.location.reload(); 39 | }; 40 | 41 | 42 | const totalReviewsLength = 43 | products && 44 | products.reduce((acc, product) => acc + product.reviews.length, 0); 45 | 46 | const totalRatings = products && products.reduce((acc, product) => acc + product.reviews.reduce((sum, review) => sum + review.rating, 0), 0); 47 | 48 | const averageRating = totalRatings / totalReviewsLength || 0; 49 | 50 | 51 | 52 | return ( 53 | <> 54 | { 55 | isLoading ? ( 56 | 57 | ) : ( 58 |
59 |
60 |
61 | 66 |
67 |

{data.name}

68 |

69 | {data.description} 70 |

71 |
72 |
73 |
Address
74 |

{data.address}

75 |
76 |
77 |
Phone Number
78 |

{data.phoneNumber}

79 |
80 |
81 |
Total Products
82 |

{products && products.length}

83 |
84 |
85 |
Shop Ratings
86 |

{averageRating}/5

87 |
88 |
89 |
Joined On
90 |

{data?.createdAt?.slice(0, 10)}

91 |
92 | {isOwner && ( 93 |
94 | 95 |
96 | Edit Shop 97 |
98 | 99 | 100 |
103 | Log Out 104 |
105 |
106 | )} 107 |
108 | ) 109 | } 110 | 111 | ); 112 | }; 113 | 114 | export default ShopInfo; -------------------------------------------------------------------------------- /frontend/src/components/Admin/AllSellers.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { DataGrid } from "@material-ui/data-grid"; 4 | import { AiOutlineDelete, AiOutlineEye } from "react-icons/ai"; 5 | import { Button } from "@material-ui/core"; 6 | import styles from "../../styles/styles"; 7 | import { RxCross1 } from "react-icons/rx"; 8 | import axios from "axios"; 9 | import { server } from "../../server"; 10 | import { toast } from "react-toastify"; 11 | import { getAllSellers } from "../../redux/actions/sellers"; 12 | import { Link } from "react-router-dom"; 13 | 14 | const AllSellers = () => { 15 | const dispatch = useDispatch(); 16 | const { sellers } = useSelector((state) => state.seller); 17 | const [open, setOpen] = useState(false); 18 | const [userId, setUserId] = useState(""); 19 | 20 | useEffect(() => { 21 | dispatch(getAllSellers()); 22 | }, [dispatch]); 23 | 24 | const handleDelete = async (id) => { 25 | await axios 26 | .delete(`${server}/shop/delete-seller/${id}`, { withCredentials: true }) 27 | .then((res) => { 28 | toast.success(res.data.message); 29 | }); 30 | 31 | dispatch(getAllSellers()); 32 | }; 33 | 34 | const columns = [ 35 | { field: "id", headerName: "Seller ID", minWidth: 150, flex: 0.7 }, 36 | 37 | { 38 | field: "name", 39 | headerName: "name", 40 | minWidth: 130, 41 | flex: 0.7, 42 | }, 43 | { 44 | field: "email", 45 | headerName: "Email", 46 | type: "text", 47 | minWidth: 130, 48 | flex: 0.7, 49 | }, 50 | { 51 | field: "address", 52 | headerName: "Seller Address", 53 | type: "text", 54 | minWidth: 130, 55 | flex: 0.7, 56 | }, 57 | 58 | { 59 | field: "joinedAt", 60 | headerName: "joinedAt", 61 | type: "text", 62 | minWidth: 130, 63 | flex: 0.8, 64 | }, 65 | { 66 | field: " ", 67 | flex: 1, 68 | minWidth: 150, 69 | headerName: "Preview Shop", 70 | type: "number", 71 | sortable: false, 72 | renderCell: (params) => { 73 | return ( 74 | <> 75 | 76 | 79 | 80 | 81 | ); 82 | }, 83 | }, 84 | { 85 | field: " ", 86 | flex: 1, 87 | minWidth: 150, 88 | headerName: "Delete Seller", 89 | type: "number", 90 | sortable: false, 91 | renderCell: (params) => { 92 | return ( 93 | <> 94 | 97 | 98 | ); 99 | }, 100 | }, 101 | ]; 102 | 103 | const row = []; 104 | sellers && 105 | sellers.forEach((item) => { 106 | row.push({ 107 | id: item._id, 108 | name: item?.name, 109 | email: item?.email, 110 | joinedAt: item.createdAt.slice(0, 10), 111 | address: item.address, 112 | }); 113 | }); 114 | 115 | return ( 116 |
117 |
118 |

All Users

119 |
120 | 127 |
128 | {open && ( 129 |
130 |
131 |
132 | setOpen(false)} /> 133 |
134 |

135 | Are you sure you wanna delete this user? 136 |

137 |
138 |
setOpen(false)} 141 | > 142 | cancel 143 |
144 |
setOpen(false) || handleDelete(userId)} 147 | > 148 | confirm 149 |
150 |
151 |
152 |
153 | )} 154 |
155 |
156 | ); 157 | }; 158 | 159 | export default AllSellers; 160 | -------------------------------------------------------------------------------- /frontend/src/components/Admin/Layout/AdminSideBar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FiShoppingBag } from "react-icons/fi"; 3 | import { GrWorkshop } from "react-icons/gr"; 4 | import { RxDashboard } from "react-icons/rx"; 5 | import { CiMoneyBill, CiSettings } from "react-icons/ci"; 6 | import { Link } from "react-router-dom"; 7 | import { HiOutlineUserGroup } from "react-icons/hi"; 8 | import { BsHandbag } from "react-icons/bs"; 9 | import { MdOutlineLocalOffer } from "react-icons/md"; 10 | import { AiOutlineSetting } from "react-icons/ai"; 11 | 12 | const AdminSideBar = ({ active }) => { 13 | return ( 14 |
15 | {/* single item */} 16 |
17 | 18 | 22 |
27 | Dashboard 28 |
29 | 30 |
31 | 32 |
33 | 34 | 38 |
43 | All Orders 44 |
45 | 46 |
47 | 48 |
49 | 50 | 54 |
59 | All Sellers 60 |
61 | 62 |
63 | 64 |
65 | 66 | 70 |
75 | All Users 76 |
77 | 78 |
79 | 80 |
81 | 82 | 83 |
88 | All Products 89 |
90 | 91 |
92 | 93 |
94 | 95 | 99 |
104 | All Events 105 |
106 | 107 |
108 | 109 |
110 | 111 | 115 |
120 | Withdraw Request 121 |
122 | 123 |
124 | 125 |
126 | 127 | 131 |
136 | Settings 137 |
138 | 139 |
140 |
141 | ); 142 | }; 143 | 144 | export default AdminSideBar; 145 | -------------------------------------------------------------------------------- /backend/controller/event.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 3 | const { upload } = require("../multer"); 4 | const Shop = require("../model/shop"); 5 | const Event = require("../model/event"); 6 | const Order = require("../model/order"); 7 | const ErrorHandler = require("../utils/ErrorHandler"); 8 | const { isSeller, isAdmin, isAuthenticated } = require("../middleware/auth"); 9 | const router = express.Router(); 10 | const fs = require("fs"); 11 | 12 | // create event 13 | router.post( 14 | "/create-event", 15 | upload.array("images"), 16 | catchAsyncErrors(async (req, res, next) => { 17 | try { 18 | const shopId = req.body.shopId; 19 | const shop = await Shop.findById(shopId); 20 | if (!shop) { 21 | return next(new ErrorHandler("Shop Id is invalid!", 400)); 22 | } else { 23 | const files = req.files; 24 | const imageUrls = files.map((file) => `${file.filename}`); 25 | 26 | const eventData = req.body; 27 | eventData.images = imageUrls; 28 | eventData.shop = shop; 29 | 30 | const product = await Event.create(eventData); 31 | 32 | res.status(201).json({ 33 | success: true, 34 | product, 35 | }); 36 | } 37 | } catch (error) { 38 | return next(new ErrorHandler(error, 400)); 39 | } 40 | }) 41 | ); 42 | 43 | // get all events 44 | router.get("/get-all-events", async (req, res, next) => { 45 | try { 46 | const events = await Event.find(); 47 | res.status(201).json({ 48 | success: true, 49 | events, 50 | }); 51 | } catch (error) { 52 | return next(new ErrorHandler(error, 400)); 53 | } 54 | }); 55 | 56 | // get all events of a shop 57 | router.get( 58 | "/get-all-events/:id", 59 | catchAsyncErrors(async (req, res, next) => { 60 | try { 61 | const events = await Event.find({ shopId: req.params.id }); 62 | 63 | res.status(201).json({ 64 | success: true, 65 | events, 66 | }); 67 | } catch (error) { 68 | return next(new ErrorHandler(error, 400)); 69 | } 70 | }) 71 | ); 72 | 73 | // delete event of a shop 74 | router.delete( 75 | "/delete-shop-event/:id", 76 | isSeller, 77 | catchAsyncErrors(async (req, res, next) => { 78 | try { 79 | const productId = req.params.id; 80 | 81 | const eventData = await Event.findById(productId); 82 | 83 | eventData.images.forEach((imageUrl) => { 84 | const filename = imageUrl; 85 | const filePath = `uploads/${filename}`; 86 | 87 | fs.unlink(filePath, (err) => { 88 | if (err) { 89 | console.log(err); 90 | } 91 | }); 92 | }); 93 | 94 | const event = await Event.findByIdAndDelete(productId); 95 | 96 | if (!event) { 97 | return next(new ErrorHandler("Event not found with this id!", 500)); 98 | } 99 | 100 | res.status(201).json({ 101 | success: true, 102 | message: "Event Deleted successfully!", 103 | }); 104 | } catch (error) { 105 | return next(new ErrorHandler(error, 400)); 106 | } 107 | }) 108 | ); 109 | 110 | // all events --- for admin 111 | router.get( 112 | "/admin-all-events", 113 | isAuthenticated, 114 | isAdmin("Admin"), 115 | catchAsyncErrors(async (req, res, next) => { 116 | try { 117 | const events = await Event.find().sort({ 118 | createdAt: -1, 119 | }); 120 | res.status(201).json({ 121 | success: true, 122 | events, 123 | }); 124 | } catch (error) { 125 | return next(new ErrorHandler(error.message, 500)); 126 | } 127 | }) 128 | ); 129 | 130 | // review for a Event 131 | router.put( 132 | "/create-new-review-event", 133 | isAuthenticated, 134 | catchAsyncErrors(async (req, res, next) => { 135 | try { 136 | const { user, rating, comment, productId, orderId } = req.body; 137 | 138 | const event = await Event.findById(productId); 139 | 140 | const review = { 141 | user, 142 | rating, 143 | comment, 144 | productId, 145 | }; 146 | 147 | const isReviewed = event.reviews.find( 148 | (rev) => rev.user._id === req.user._id 149 | ); 150 | 151 | if (isReviewed) { 152 | event.reviews.forEach((rev) => { 153 | if (rev.user._id === req.user._id) { 154 | (rev.rating = rating), (rev.comment = comment), (rev.user = user); 155 | } 156 | }); 157 | } else { 158 | event.reviews.push(review); 159 | } 160 | 161 | let avg = 0; 162 | 163 | event.reviews.forEach((rev) => { 164 | avg += rev.rating; 165 | }); 166 | 167 | event.ratings = avg / event.reviews.length; 168 | 169 | await event.save({ validateBeforeSave: false }); 170 | 171 | await Order.findByIdAndUpdate( 172 | orderId, 173 | { $set: { "cart.$[elem].isReviewed": true } }, 174 | { arrayFilters: [{ "elem._id": productId }], new: true } 175 | ); 176 | 177 | res.status(200).json({ 178 | success: true, 179 | message: "Reviwed succesfully!", 180 | }); 181 | } catch (error) { 182 | return next(new ErrorHandler(error, 400)); 183 | } 184 | }) 185 | ); 186 | 187 | module.exports = router; 188 | -------------------------------------------------------------------------------- /backend/controller/product.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { isSeller, isAuthenticated, isAdmin } = require("../middleware/auth"); 3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors"); 4 | const router = express.Router(); 5 | const Product = require("../model/product"); 6 | const Order = require("../model/order"); 7 | const Shop = require("../model/shop"); 8 | const { upload } = require("../multer"); 9 | const ErrorHandler = require("../utils/ErrorHandler"); 10 | const fs = require("fs"); 11 | 12 | // create product 13 | router.post( 14 | "/create-product", 15 | upload.array("images"), 16 | catchAsyncErrors(async (req, res, next) => { 17 | try { 18 | const shopId = req.body.shopId; 19 | const shop = await Shop.findById(shopId); 20 | if (!shop) { 21 | return next(new ErrorHandler("Shop Id is invalid!", 400)); 22 | } else { 23 | const files = req.files; 24 | const imageUrls = files.map((file) => `${file.filename}`); 25 | 26 | const productData = req.body; 27 | productData.images = imageUrls; 28 | productData.shop = shop; 29 | 30 | const product = await Product.create(productData); 31 | 32 | res.status(201).json({ 33 | success: true, 34 | product, 35 | }); 36 | } 37 | } catch (error) { 38 | return next(new ErrorHandler(error, 400)); 39 | } 40 | }) 41 | ); 42 | 43 | // get all products of a shop 44 | router.get( 45 | "/get-all-products-shop/:id", 46 | catchAsyncErrors(async (req, res, next) => { 47 | try { 48 | const products = await Product.find({ shopId: req.params.id }); 49 | 50 | res.status(201).json({ 51 | success: true, 52 | products, 53 | }); 54 | } catch (error) { 55 | return next(new ErrorHandler(error, 400)); 56 | } 57 | }) 58 | ); 59 | 60 | // delete product of a shop 61 | router.delete( 62 | "/delete-shop-product/:id", 63 | isSeller, 64 | catchAsyncErrors(async (req, res, next) => { 65 | try { 66 | const productId = req.params.id; 67 | 68 | const productData = await Product.findById(productId); 69 | 70 | productData.images.forEach((imageUrl) => { 71 | const filename = imageUrl; 72 | const filePath = `uploads/${filename}`; 73 | 74 | fs.unlink(filePath, (err) => { 75 | if (err) { 76 | console.log(err); 77 | } 78 | }); 79 | }); 80 | 81 | const product = await Product.findByIdAndDelete(productId); 82 | 83 | if (!product) { 84 | return next(new ErrorHandler("Product not found with this id!", 500)); 85 | } 86 | 87 | res.status(201).json({ 88 | success: true, 89 | message: "Product Deleted successfully!", 90 | }); 91 | } catch (error) { 92 | return next(new ErrorHandler(error, 400)); 93 | } 94 | }) 95 | ); 96 | 97 | // get all products 98 | router.get( 99 | "/get-all-products", 100 | catchAsyncErrors(async (req, res, next) => { 101 | try { 102 | const products = await Product.find().sort({ createdAt: -1 }); 103 | 104 | res.status(201).json({ 105 | success: true, 106 | products, 107 | }); 108 | } catch (error) { 109 | return next(new ErrorHandler(error, 400)); 110 | } 111 | }) 112 | ); 113 | 114 | // review for a product 115 | router.put( 116 | "/create-new-review", 117 | isAuthenticated, 118 | catchAsyncErrors(async (req, res, next) => { 119 | try { 120 | const { user, rating, comment, productId, orderId } = req.body; 121 | 122 | const product = await Product.findById(productId); 123 | 124 | const review = { 125 | user, 126 | rating, 127 | comment, 128 | productId, 129 | }; 130 | 131 | const isReviewed = product.reviews.find( 132 | (rev) => rev.user._id === req.user._id 133 | ); 134 | 135 | if (isReviewed) { 136 | product.reviews.forEach((rev) => { 137 | if (rev.user._id === req.user._id) { 138 | (rev.rating = rating), (rev.comment = comment), (rev.user = user); 139 | } 140 | }); 141 | } else { 142 | product.reviews.push(review); 143 | } 144 | 145 | let avg = 0; 146 | 147 | product.reviews.forEach((rev) => { 148 | avg += rev.rating; 149 | }); 150 | 151 | product.ratings = avg / product.reviews.length; 152 | 153 | await product.save({ validateBeforeSave: false }); 154 | 155 | await Order.findByIdAndUpdate( 156 | orderId, 157 | { $set: { "cart.$[elem].isReviewed": true } }, 158 | { arrayFilters: [{ "elem._id": productId }], new: true } 159 | ); 160 | 161 | res.status(200).json({ 162 | success: true, 163 | message: "Reviwed succesfully!", 164 | }); 165 | } catch (error) { 166 | return next(new ErrorHandler(error, 400)); 167 | } 168 | }) 169 | ); 170 | 171 | // all products --- for admin 172 | router.get( 173 | "/admin-all-products", 174 | isAuthenticated, 175 | isAdmin("Admin"), 176 | catchAsyncErrors(async (req, res, next) => { 177 | try { 178 | const products = await Product.find().sort({ 179 | createdAt: -1, 180 | }); 181 | res.status(201).json({ 182 | success: true, 183 | products, 184 | }); 185 | } catch (error) { 186 | return next(new ErrorHandler(error.message, 500)); 187 | } 188 | }) 189 | ); 190 | 191 | module.exports = router; 192 | -------------------------------------------------------------------------------- /frontend/src/components/Profile/ProfileSidebar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { AiOutlineLogin, AiOutlineMessage } from "react-icons/ai"; 3 | import { RiLockPasswordLine } from "react-icons/ri"; 4 | import { HiOutlineReceiptRefund, HiOutlineShoppingBag } from "react-icons/hi"; 5 | import { RxPerson } from "react-icons/rx"; 6 | import { Link, useNavigate } from "react-router-dom"; 7 | import { 8 | MdOutlineAdminPanelSettings, 9 | MdOutlinePassword, 10 | MdOutlineTrackChanges, 11 | } from "react-icons/md"; 12 | import { TbAddressBook } from "react-icons/tb"; 13 | import axios from "axios"; 14 | import { server } from "../../server"; 15 | import { toast } from "react-toastify"; 16 | import { useSelector } from "react-redux"; 17 | 18 | const ProfileSidebar = ({ active, setActive }) => { 19 | const navigate = useNavigate(); 20 | 21 | const { user } = useSelector((state) => state.user); 22 | 23 | const logoutHandler = () => { 24 | axios 25 | .get(`${server}/user/logout`, { withCredentials: true }) 26 | .then((res) => { 27 | toast.success(res.data.message); 28 | window.location.reload(true); 29 | navigate("/login"); 30 | }) 31 | .catch((error) => { 32 | console.log(error.response.data.message); 33 | }); 34 | }; 35 | 36 | return ( 37 |
38 |
setActive(1)} 41 | > 42 | 43 | 48 | Profile 49 | 50 |
51 | 52 |
setActive(2)} 55 | > 56 | 57 | 62 | Orders 63 | 64 |
65 | 66 |
setActive(3)} 69 | > 70 | 71 | 76 | Refunds 77 | 78 |
79 | 80 |
setActive(4) || navigate("/inbox")} 83 | > 84 | 85 | 90 | inbox 91 | 92 |
93 | 94 |
setActive(5)} 97 | > 98 | 99 | 104 | Track Order 105 | 106 |
107 | 108 |
setActive(6)} 111 | > 112 | 113 | 114 | 119 | Change password 120 | 121 |
122 | 123 |
setActive(7)} 126 | > 127 | 128 | 133 | Address 134 | 135 |
136 | 137 | {user && user?.role === "Admin" && ( 138 | 139 |
setActive(8)} 142 | > 143 | 147 | 152 | Admin Dashboard 153 | 154 |
155 | 156 | )} 157 | 158 |
162 | 163 | 168 | loguot 169 | 170 |
171 |
172 | ); 173 | }; 174 | 175 | export default ProfileSidebar; 176 | -------------------------------------------------------------------------------- /frontend/src/components/Route/Sponsored.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "../../styles/styles"; 3 | 4 | const Sponsored = () => { 5 | return ( 6 | 47 | ); 48 | }; 49 | 50 | export default Sponsored; --------------------------------------------------------------------------------