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 |
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 |
11 |
14 | {brandingData &&
15 | brandingData.map((i, index) => (
16 |
17 | {i.icon}
18 |
19 |
{i.title}
20 |
{i.Description}
21 |
22 |
23 | ))
24 | }
25 |
26 |
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 |

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 |
9 |
10 |
11 |

16 |
17 |
18 |

23 |
24 |
25 |

30 |
31 |
32 |

37 |
38 |
39 |

44 |
45 |
46 |
47 | );
48 | };
49 |
50 | export default Sponsored;
--------------------------------------------------------------------------------