├── .gitIgnore
├── Procfile
├── README.md
├── backend
├── app.js
├── controller
│ ├── CartController.js
│ ├── OrderController.js
│ ├── PaymentController.js
│ ├── ProductController.js
│ └── UserController.js
├── db
│ └── Database.js
├── index.html
├── middleware
│ ├── auth.js
│ ├── catchAsyncErrors.js
│ └── error.js
├── models
│ ├── CartModel.js
│ ├── OrderModel.js
│ ├── ProductModel.js
│ ├── UserModel.js
│ └── WishListModel.js
├── routes
│ ├── OrderRoute.js
│ ├── PaymentRoute.js
│ ├── ProductRoute.js
│ ├── UserRoute.js
│ └── WishListRoute.js
├── server.js
└── utils
│ ├── ErrorHandler.js
│ ├── Features.js
│ ├── jwtToken.js
│ └── sendMail.js
├── frontend
├── .gitignore
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ ├── profile.png
│ └── robots.txt
└── src
│ ├── App.css
│ ├── App.js
│ ├── Assets
│ ├── background.jpg
│ ├── background2.jpg
│ ├── bg.jpg
│ ├── icons
│ │ ├── bag.png
│ │ ├── home.png
│ │ ├── homeActive.png
│ │ ├── logout.png
│ │ ├── logoutActive.png
│ │ ├── search.png
│ │ ├── searchActive.png
│ │ ├── shopping-bagActive.png
│ │ ├── shopping-cart.png
│ │ ├── shopping-cartActive.png
│ │ ├── user.png
│ │ └── userActive.png
│ └── slider-2.png
│ ├── Footer.jsx
│ ├── actions
│ ├── CartAction.js
│ ├── FavouriteAction.js
│ ├── OrderAction.js
│ ├── ProductActions.js
│ └── userAction.js
│ ├── component
│ ├── Admin
│ │ ├── AllOrder.jsx
│ │ ├── AllProducts.css
│ │ ├── AllProducts.jsx
│ │ ├── AllReviews.jsx
│ │ ├── AllUsers.jsx
│ │ ├── CreateProduct.jsx
│ │ ├── Dashboard.jsx
│ │ ├── EditProduct.jsx
│ │ ├── Sidebar.css
│ │ ├── Sidebar.js
│ │ ├── UpdateOrder.css
│ │ ├── UpdateOrder.jsx
│ │ ├── UpdateUser.jsx
│ │ ├── dashboard.css
│ │ ├── newProduct.css
│ │ └── productReviews.css
│ ├── Authentication
│ │ ├── LoginSignup.css
│ │ └── LoginSignup.jsx
│ ├── Home
│ │ ├── Header.css
│ │ ├── Header.jsx
│ │ ├── Home.css
│ │ └── Home.jsx
│ ├── Products
│ │ ├── Product.css
│ │ ├── ProductCard.jsx
│ │ ├── ProductDetails.jsx
│ │ ├── Productdetails.css
│ │ ├── Products.jsx
│ │ ├── ReviewCard.jsx
│ │ ├── Search.css
│ │ └── Search.jsx
│ ├── about
│ │ ├── About.css
│ │ └── About.jsx
│ ├── cart
│ │ ├── Cart.css
│ │ ├── Cart.jsx
│ │ ├── CartItemCard.css
│ │ ├── CartItemCard.js
│ │ ├── CheckoutSteps.css
│ │ ├── CheckoutSteps.jsx
│ │ ├── ConfirmOrder.css
│ │ ├── ConfirmOrder.jsx
│ │ ├── Favourite.css
│ │ ├── FavouriteItemsCard
│ │ ├── FavouriteItemsCard.css
│ │ ├── FavouriteItemsCard.jsx
│ │ ├── Favourites.jsx
│ │ ├── Payment.jsx
│ │ ├── Shipping.css
│ │ ├── Shipping.jsx
│ │ ├── Success.jsx
│ │ ├── orderSuccess.css
│ │ └── payment.css
│ └── user
│ │ ├── EditProfile.css
│ │ ├── EditProfile.jsx
│ │ ├── ForgotPassword.css
│ │ ├── ForgotPassword.jsx
│ │ ├── MoreOption.jsx
│ │ ├── MyOrder.jsx
│ │ ├── MyOrderDetails.jsx
│ │ ├── Profile.css
│ │ ├── Profile.jsx
│ │ ├── ResetPassword.css
│ │ ├── ResetPassword.jsx
│ │ ├── UpdatePassword.css
│ │ ├── UpdatePassword.jsx
│ │ ├── myOrders.css
│ │ └── orderDetails.css
│ ├── constans
│ ├── CartConstans.js
│ ├── FavouriteConstans.js
│ ├── OrderConstans.js
│ ├── ProductConstans.js
│ └── userContans.js
│ ├── index.css
│ ├── index.js
│ ├── more
│ ├── BottomTab.jsx
│ ├── BottomTabs.css
│ ├── CommingSoon.css
│ ├── CommingSoon.jsx
│ ├── Contact.jsx
│ ├── Loader.js
│ ├── Loading.css
│ ├── Metadata.js
│ ├── Notfound.jsx
│ ├── Rules.css
│ ├── Rules.jsx
│ ├── Support.css
│ ├── Support.jsx
│ ├── UserData.jsx
│ └── UserOption.css
│ ├── reducers
│ ├── CartReducer.js
│ ├── FavouriteReducer.js
│ ├── OrderReducer.js
│ ├── ProductReducer.js
│ └── userReducer.js
│ ├── reportWebVitals.js
│ ├── route
│ └── ProtectedRoute.js
│ └── store.js
└── package.json
/.gitIgnore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /frontend/node_modules
4 | /frontend/.pnp
5 | /frontend/.pnp.js
6 |
7 | # testing
8 | /frontend/coverage
9 |
10 | # production
11 | /frontend/build
12 |
13 | # misc
14 | /backend/config/.env
15 | /frontend/.DS_Store
16 | /frontend/.env.local
17 | /frontend/.env.development.local
18 | /frontend/.env.test.local
19 | /frontend/.env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node backend/server.js
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a MERN Stack Ecommerce website. I made it for Youtube tutorials. I think everyone will like it.
2 | In this project I am using:
3 |
4 | M = MONGODB FOR DATABASE
5 | E = EXPRESS JS FOR CREATING SERVER
6 | R = REACT JS FOR FRONTEND
7 | R = REDUX FOR CREATE STATE AND USE THAT IN COMPONENT
8 | N = NODE JS FOR BACKEND
9 |
10 | Some important website links:
11 | ** NODE JS
12 | https://nodejs.org/en/
13 | ** REACT JS
14 | https://reactjs.org/
15 | ** NODEMAILER
16 | https://nodemailer.com/about/
17 | ** EMAIL JS
18 | https://www.emailjs.com/
19 |
20 | ============================== CONNECTS WITH ME ========================
21 | INSTAGRAM: https://www.instagram.com/dev_shahriar/
22 | YOUTUBE: https://www.youtube.com/
23 | FACEBOOK: https://www.facebook.com/shahriar.sajeeb.16
24 |
25 |
26 |
--------------------------------------------------------------------------------
/backend/app.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const app = express();
3 | const ErrorHandler = require("./middleware/error");
4 | const cookieParser = require("cookie-parser");
5 | const fileUpload = require("express-fileupload");
6 | const bodyParser = require("body-parser");
7 | const path = require("path");
8 |
9 | app.use(express.json());
10 | app.use(cookieParser());
11 | app.use(bodyParser.urlencoded({extended:true,limit:"50mb"}));
12 | app.use(express.urlencoded({ limit: "50mb", extended: true }));
13 | app.use(fileUpload({useTempFiles: true}));
14 |
15 | // config
16 | if(process.env.NODE_ENV!=="PRODUCTION"){
17 | require("dotenv").config({
18 | path:"backend/config/.env"
19 | })}
20 |
21 | // Route imports
22 | const product = require("./routes/ProductRoute");
23 | const user = require("./routes/UserRoute");
24 | const order = require("./routes/OrderRoute");
25 | const payment = require("./routes/PaymentRoute");
26 | const cart = require("./routes/WishListRoute");
27 |
28 | app.use("/api/v2",product);
29 |
30 | app.use("/api/v2",user);
31 |
32 | app.use("/api/v2",order);
33 |
34 | app.use("/api/v2",payment);
35 |
36 | app.use("/api/v2",cart);
37 |
38 | app.use(express.static(path.join(__dirname,"../frontend/build")));
39 |
40 | app.get("*",(req,res) =>{
41 | res.sendFile(path.resolve(__dirname,"../frontend/build/index.html"));
42 | })
43 |
44 | // it's for errorHandeling
45 | app.use(ErrorHandler);
46 |
47 | module.exports = app
--------------------------------------------------------------------------------
/backend/controller/CartController.js:
--------------------------------------------------------------------------------
1 | const Cart = require("../models/CartModel");
2 | const Wishlist = require("../models/WishListModel");
3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors");
4 | const ErrorHandler = require("../utils/ErrorHandler");
5 |
6 | // Add to wishlist
7 | exports.addToWishlist = catchAsyncErrors(async (req, res, next) => {
8 | const {
9 | productName,
10 | quantity,
11 | productImage,
12 | productPrice,
13 | userId,
14 | productId,
15 | Stock,
16 | } = req.body;
17 | const wishList = await Wishlist.create({
18 | productName,
19 | quantity,
20 | productImage,
21 | productPrice,
22 | userId,
23 | productId,
24 | Stock,
25 | });
26 |
27 | res.status(200).json({
28 | success: true,
29 | wishList,
30 | });
31 | });
32 |
33 | // get wishlistData Data
34 | exports.getWishlistData = catchAsyncErrors(async (req, res, next) => {
35 | const wishlistData = await Wishlist.find({ userId: req.user.id });
36 |
37 | res.status(200).json({
38 | success: true,
39 | wishlistData,
40 | });
41 | });
42 |
43 | // remove wishlistData
44 | exports.removeWishlistData = catchAsyncErrors(async (req, res, next) => {
45 | const wishlistData = await Wishlist.findById(req.params.id);
46 |
47 | if (!wishlistData) {
48 | return next(new ErrorHandler("No wishlistData found with this id", 404));
49 | }
50 |
51 | await wishlistData.remove();
52 |
53 | res.status(200).json({
54 | success: true,
55 | message: "Item removed from wishlist",
56 | });
57 | });
58 |
59 | // add To Cart
60 | exports.addToCart = catchAsyncErrors(async (req, res, next) => {
61 | const {
62 | productName,
63 | quantity,
64 | productImage,
65 | productPrice,
66 | userId,
67 | productId,
68 | Stock,
69 | } = req.body;
70 | const cart = await Cart.create({
71 | productName,
72 | quantity,
73 | productImage,
74 | productPrice,
75 | userId,
76 | productId,
77 | Stock,
78 | });
79 |
80 | res.status(200).json({
81 | success: true,
82 | cart,
83 | });
84 | });
85 |
86 | // update Cart
87 | exports.updateCart = catchAsyncErrors(async (req, res, next) => {
88 | const {
89 | quantity,
90 | } = req.body;
91 | const cart = await Cart.findByIdAndUpdate(req.params.id);
92 |
93 | if (!cart) {
94 | return next(new ErrorHandler("No cart found with this id", 404));
95 | }
96 |
97 | await cart.update({
98 | quantity,
99 | });
100 | });
101 |
102 | // get Cart Data
103 | exports.getCartData = catchAsyncErrors(async (req, res, next) => {
104 | const cartData = await Cart.find({ userId: req.user.id });
105 | res.status(200).json({
106 | success: true,
107 | cartData,
108 | });
109 | });
110 |
111 | // remove Cart Data
112 | exports.removeCartData = catchAsyncErrors(async (req, res, next) => {
113 | const cartData = await Cart.findById(req.params.id);
114 |
115 | if (!cartData) {
116 | return next(new ErrorHandler("Items is not found with this id", 404));
117 | }
118 |
119 | await cartData.remove();
120 |
121 | res.status(200).json({
122 | success: true,
123 | message: "Item removed from cart",
124 | });
125 | });
126 |
--------------------------------------------------------------------------------
/backend/controller/OrderController.js:
--------------------------------------------------------------------------------
1 | const Order = require("../models/OrderModel");
2 | const ErrorHandler = require("../utils/ErrorHandler.js");
3 | const catchAsyncErrors = require("../middleware/catchAsyncErrors");
4 | const Product = require("../models/ProductModel");
5 |
6 | // Create Order
7 | exports.createOrder = catchAsyncErrors(async (req,res,next) =>{
8 |
9 | const {
10 | shippingInfo,
11 | orderItems,
12 | paymentInfo,
13 | itemsPrice,
14 | taxPrice,
15 | shippingPrice,
16 | totalPrice,
17 | } = req.body;
18 |
19 | const order = await Order.create({
20 | shippingInfo,
21 | orderItems,
22 | paymentInfo,
23 | itemsPrice,
24 | taxPrice,
25 | shippingPrice,
26 | totalPrice,
27 | paidAt:Date.now(),
28 | user: req.user._id,
29 | });
30 |
31 | res.status(201).json({
32 | success: true,
33 | order
34 | });
35 | });
36 |
37 | // Get Single order
38 | exports.getSingleOrder = catchAsyncErrors(async (req,res,next) =>{
39 | const order = await Order.findById(req.params.id).populate(
40 | "user",
41 | "name email"
42 | );
43 |
44 | if(!order){
45 | return next(new ErrorHandler("Order not found with this id",404));
46 | }
47 |
48 | res.status(200).json({
49 | success: true,
50 | order
51 | });
52 | });
53 |
54 | // Get all orders
55 | exports.getAllOrders = catchAsyncErrors(async (req,res,next) =>{
56 | const orders = await Order.find({user: req.user._id});
57 | res.status(200).json({
58 | success: true,
59 | orders
60 | });
61 | });
62 |
63 | // Get All orders ---Admin
64 | exports.getAdminAllOrders = catchAsyncErrors(async (req,res,next) =>{
65 | const orders = await Order.find();
66 |
67 | let totalAmount = 0;
68 |
69 | orders.forEach((order) =>{
70 | totalAmount += order.totalPrice;
71 | });
72 |
73 | res.status(200).json({
74 | success: true,
75 | totalAmount,
76 | orders
77 | });
78 | });
79 |
80 | // update Order Status ---Admin
81 | exports.updateAdminOrder = catchAsyncErrors(async (req, res, next) => {
82 |
83 | const order = await Order.findById(req.params.id);
84 |
85 | if (!order) {
86 | return next(new ErrorHandler("Order not found with this Id", 404));
87 | }
88 |
89 | if (order.orderStatus === "Delivered") {
90 | return next(new ErrorHandler("You have already delivered this order", 400));
91 | }
92 |
93 | if (req.body.status === "Shipped") {
94 | order.orderItems.forEach(async (o) => {
95 | await updateStock(o.product, o.quantity);
96 | });
97 | }
98 | order.orderStatus = req.body.status;
99 |
100 | if (req.body.status === "Delivered") {
101 | order.deliveredAt = Date.now();
102 | }
103 |
104 | await order.save({ validateBeforeSave: false });
105 | res.status(200).json({
106 | success: true,
107 | });
108 | });
109 |
110 | async function updateStock(id, quantity) {
111 |
112 | const product = await Product.findById(id);
113 |
114 | product.Stock -= quantity;
115 |
116 | await product.save({ validateBeforeSave: false });
117 | }
118 |
119 |
120 | // delete Order ---Admin
121 | exports.deleteOrder = catchAsyncErrors(async (req,res,next) =>{
122 |
123 | const order = await Order.findById(req.params.id);
124 |
125 | if(!order){
126 | return next(new ErrorHandler("Order not found with this Id", 404));
127 | }
128 |
129 | await order.remove();
130 |
131 | res.status(200).json({
132 | success: true,
133 | });
134 | });
135 |
--------------------------------------------------------------------------------
/backend/controller/PaymentController.js:
--------------------------------------------------------------------------------
1 | const catchAsyncErrors = require("../middleware/catchAsyncErrors");
2 |
3 | const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
4 |
5 | exports.Payment = catchAsyncErrors(async (req, res, next) => {
6 | const myPayment = await stripe.paymentIntents.create({
7 | amount: req.body.amount,
8 | currency: "usd",
9 | metadata: {
10 | company: "MERN",
11 | },
12 | });
13 |
14 | res
15 | .status(200)
16 | .json({ success: true, client_secret: myPayment.client_secret });
17 | });
18 |
19 | exports.sendStripeApiKey = catchAsyncErrors(async (req, res, next) => {
20 | res.status(200).json({ stripeApiKey: process.env.STRIPE_API_KEY });
21 | });
22 |
--------------------------------------------------------------------------------
/backend/db/Database.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 |
4 | const connectDatabase = () =>{
5 | mongoose.connect(process.env.DB_URL,{
6 | useNewUrlParser: true,
7 | useUnifiedTopology: true,
8 | }).then((data) =>{
9 | console.log(`mongodb is connected with server: ${data.connection.host}`);
10 | })
11 | }
12 |
13 | module.exports = connectDatabase
--------------------------------------------------------------------------------
/backend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
26 |
--------------------------------------------------------------------------------
/backend/middleware/auth.js:
--------------------------------------------------------------------------------
1 | const ErrorHandler = require("../utils/ErrorHandler");
2 | const catchAsyncErrors = require("./catchAsyncErrors");
3 | const jwt = require("jsonwebtoken");
4 | const User = require("../models/UserModel");
5 |
6 |
7 | exports.isAuthenticatedUser = catchAsyncErrors(async (req,res,next) =>{
8 | const { token } = req.cookies;
9 |
10 | if (!token) {
11 | return next(new ErrorHandler("Please Login for access this resource", 401));
12 | }
13 |
14 | const decodedData = jwt.verify(token, process.env.JWT_SECRET_KEY);
15 |
16 | req.user = await User.findById(decodedData.id);
17 |
18 | next();
19 | });
20 |
21 | // Admin Roles
22 | exports.authorizeRoles = (...roles) =>{
23 | return (req,res,next) =>{
24 | if(!roles.includes(req.user.role)){
25 | return next(new ErrorHandler(`${req.user.role} can not access this resources`));
26 | };
27 | next();
28 | }
29 | }
--------------------------------------------------------------------------------
/backend/middleware/catchAsyncErrors.js:
--------------------------------------------------------------------------------
1 | module.exports = (theFunc) => (req,res,next) =>{
2 | Promise.resolve(theFunc(req,res,next)).catch(next);
3 | };
--------------------------------------------------------------------------------
/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 || "Interval 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 |
14 | // Duplicate key error
15 | if (err.code === 11000) {
16 | const message = `Duplicate ${Object.keys(err.keyValue)} Entered`;
17 | err = new ErrorHandler(message, 400);
18 | }
19 |
20 | // Wrong Jwt error
21 | if (err.name === "JsonWebTokenError") {
22 | const message = `Your url is invalid please try again`;
23 | err = new ErrorHandler(message, 400);
24 | }
25 |
26 | //Jwt expired error
27 | if (err.name === "TokenExpiredError") {
28 | const message = `Your url is expired please try again`;
29 | err = new ErrorHandler(message, 400);
30 | }
31 |
32 | res.status(err.statusCode).json({
33 | success: false,
34 | message: err.message
35 | })
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/backend/models/CartModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const cartSchema = new mongoose.Schema({
4 | productName: {
5 | type: String,
6 | required: [true, "Please enter your product name"],
7 | },
8 | productPrice: {
9 | type: Number,
10 | required: [true, "Please enter your product price"],
11 | },
12 | productImage: {
13 | type: String,
14 | required: [true, "Please enter your product image"],
15 | },
16 | quantity: {
17 | type: Number,
18 | required: [true, "Please enter your product quantity"],
19 | },
20 | userId: {
21 | type: String,
22 | required: [true, "Please enter your user id"],
23 | },
24 | productId: {
25 | type: String,
26 | required: [true, "Please enter your user id"],
27 | },
28 | Stock: {
29 | type: Number,
30 | required: [true, "Please enter your product stock"],
31 | }
32 | });
33 |
34 | module.exports = mongoose.model("Cart", cartSchema);
35 |
--------------------------------------------------------------------------------
/backend/models/OrderModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const orderSchema = new mongoose.Schema({
4 | shippingInfo: {
5 | address: {
6 | type: String,
7 | // required: true,
8 | },
9 | city: {
10 | type: String,
11 | // required: true,
12 | },
13 | state: {
14 | type: String,
15 | // required: true,
16 | },
17 | country: {
18 | type: String,
19 | // required: true,
20 | },
21 | pinCode: {
22 | type: Number,
23 | // required: true,
24 | },
25 | phoneNo: {
26 | type: Number,
27 | // required: true,
28 | },
29 | },
30 | orderItems: [
31 | {
32 | productName: {
33 | type: String,
34 | required: true,
35 | },
36 | productPrice: {
37 | type: Number,
38 | required: true,
39 | },
40 | quantity: {
41 | type: Number,
42 | required: true,
43 | },
44 | productImage: {
45 | type: String,
46 | required: true,
47 | },
48 | productId: {
49 | type: mongoose.Schema.ObjectId,
50 | ref: "Product",
51 | required: true,
52 | },
53 | },
54 | ],
55 | user: {
56 | type: mongoose.Schema.ObjectId,
57 | ref: "User",
58 | required: true,
59 | },
60 | paymentInfo: {
61 | id: {
62 | type: String,
63 | required: true,
64 | },
65 | status: {
66 | type: String,
67 | required: true,
68 | },
69 | },
70 | paidAt: {
71 | type: Date,
72 | required: true,
73 | },
74 | itemsPrice: {
75 | type: Number,
76 | required: true,
77 | default: 0,
78 | },
79 | taxPrice: {
80 | type: Number,
81 | default: 0,
82 | },
83 | shippingPrice: {
84 | type: Number,
85 | required: true,
86 | default: 0,
87 | },
88 | totalPrice: {
89 | type: Number,
90 | required: true,
91 | default: 0,
92 | },
93 | orderStatus: {
94 | type: String,
95 | required: true,
96 | default: "Processing",
97 | },
98 | deliveredAt: Date,
99 | createdAt: {
100 | type: Date,
101 | default: Date.now,
102 | },
103 | });
104 |
105 | module.exports = mongoose.model("Order", orderSchema);
--------------------------------------------------------------------------------
/backend/models/ProductModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const productSchema = new mongoose.Schema({
4 | name:{
5 | type:String,
6 | required:[true, "Please enter a name of a product"],
7 | trim: true,
8 | maxLength:[20, "Product name not exceed than 20 characters"]
9 | },
10 | description:{
11 | type:String,
12 | required:[true, "Please add a description of your product"],
13 | maxlength:[4000,"Description is can not exceed than 4000 characters"]
14 | },
15 | price:{
16 | type:Number,
17 | required: [true, "Please add a price for your product"],
18 | maxLength:[8, "Price can not exceed than 8 characters"],
19 | },
20 | offerPrice:{
21 | type:String,
22 | maxLength: [4, "Discount price can not exceed than 4 characters"],
23 | },
24 | color:{
25 | type: String,
26 | },
27 | size:{
28 | type: String,
29 | },
30 | ratings:{
31 | type: Number,
32 | default: 0,
33 | },
34 | images:[
35 | {
36 | public_id:{
37 | type:String,
38 | required:true,
39 | },
40 | url:{
41 | type:String,
42 | required:true,
43 | },
44 | }
45 | ],
46 | category:{
47 | type: String,
48 | required:[true,"Please add a category of your product"],
49 | },
50 | Stock:{
51 | type: Number,
52 | required:[true,"Please add some stoke for your product"],
53 | maxLength: [3, "Stock can not exceed than 3 characters"],
54 | },
55 | numOfReviews:{
56 | type: Number,
57 | default: 0
58 | },
59 | reviews:[
60 | {
61 | user: {
62 | type:mongoose.Schema.ObjectId,
63 | ref:"User",
64 | required: true,
65 | },
66 | name:{
67 | type: String,
68 | required: true,
69 | },
70 | rating:{
71 | type: Number,
72 | required: true,
73 | },
74 | comment:{
75 | type:String,
76 | },
77 | time:{
78 | type: Date,
79 | default: Date.now()
80 | },
81 | },
82 | ],
83 | user:{
84 | type: mongoose.Schema.ObjectId,
85 | ref:"User",
86 | // required: true
87 | },
88 | createAt:{
89 | type:Date,
90 | default: Date.now()
91 | }
92 | })
93 |
94 | module.exports = mongoose.model("Product",productSchema);
--------------------------------------------------------------------------------
/backend/models/UserModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const validator = require("validator");
3 | const bcrypt = require("bcryptjs");
4 | const jwt = require("jsonwebtoken");
5 | const crypto = require("crypto");
6 |
7 | const userSchema = new mongoose.Schema({
8 | name: {
9 | type: String,
10 | required: [true, "Please your Name"],
11 | minlength: [3, "Please enter a name atleast 3 characters"],
12 | maxlength: [15, "Name can not big than 15 characters"],
13 | },
14 | email: {
15 | type: String,
16 | required: [true, "Please enter your email"],
17 | validate: [validator.isEmail, "Please enter a valid email"],
18 | unique: true,
19 | },
20 | password: {
21 | type: String,
22 | required: [true, "Please enter your password!"],
23 | minlength: [8, "Password should be greater than 8 characters"],
24 | select: false,
25 | },
26 | avatar: {
27 | public_id: {
28 | type: String,
29 | required: true,
30 | },
31 | url: {
32 | type: String,
33 | required: true,
34 | },
35 | },
36 | role: {
37 | type: String,
38 | default: "user",
39 | },
40 | createdAt: {
41 | type: Date,
42 | default: Date.now(),
43 | },
44 | resetPasswordToken: String,
45 | resetPasswordTime: Date,
46 | });
47 |
48 | // Hash password
49 | userSchema.pre("save", async function (next) {
50 | if (!this.isModified("password")) {
51 | next();
52 | }
53 | this.password = await bcrypt.hash(this.password, 10);
54 | });
55 |
56 | // jwt token
57 | userSchema.methods.getJwtToken = function () {
58 | return jwt.sign({ id: this._id }, process.env.JWT_SECRET_KEY, {
59 | expiresIn: process.env.JWT_EXPIRES,
60 | });
61 | };
62 |
63 | // compare password
64 | userSchema.methods.comparePassword = async function (enteredPassword) {
65 | return await bcrypt.compare(enteredPassword, this.password);
66 | };
67 |
68 | // Forgot password
69 | userSchema.methods.getResetToken = function () {
70 | // Generating token
71 | const resetToken = crypto.randomBytes(20).toString("hex");
72 |
73 | // hashing and adding resetPasswordToken to userSchema
74 | this.resetPasswordToken = crypto
75 | .createHash("sha256")
76 | .update(resetToken)
77 | .digest("hex");
78 |
79 | this.resetPasswordTime = Date.now() + 15 * 60 * 1000;
80 |
81 | return resetToken;
82 | };
83 |
84 | module.exports = mongoose.model("User", userSchema);
85 |
--------------------------------------------------------------------------------
/backend/models/WishListModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const wishListSchema = new mongoose.Schema({
4 | productName: {
5 | type: String,
6 | required: [true, "Please enter your product name"],
7 | },
8 | productPrice: {
9 | type: Number,
10 | required: [true, "Please enter your product price"],
11 | },
12 | productImage: {
13 | type: String,
14 | required: [true, "Please enter your product image"],
15 | },
16 | quantity: {
17 | type: Number,
18 | required: [true, "Please enter your product quantity"],
19 | },
20 | userId: {
21 | type: String,
22 | required: [true, "Please enter your user id"],
23 | },
24 | productId:{
25 | type: String,
26 | required: [true, "Please enter your user id"],
27 | },
28 | Stock: {
29 | type: Number,
30 | required: [true, "Please enter your product stock"],
31 | }
32 | });
33 |
34 | module.exports = mongoose.model("Wishlist", wishListSchema);
35 |
--------------------------------------------------------------------------------
/backend/routes/OrderRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const {
3 | createOrder,
4 | getSingleOrder,
5 | getAllOrders,
6 | getAdminAllOrders,
7 | updateAdminOrder,
8 | deleteOrder,
9 | } = require("../controller/OrderController");
10 | const { isAuthenticatedUser, authorizeRoles } = require("../middleware/auth");
11 | const router = express.Router();
12 |
13 | router.route("/order/new").post(isAuthenticatedUser, createOrder);
14 |
15 | router.route("/order/:id").get(isAuthenticatedUser, getSingleOrder);
16 |
17 | router.route("/orders/me").get(isAuthenticatedUser, getAllOrders);
18 |
19 | router
20 | .route("/admin/orders")
21 | .get(isAuthenticatedUser, authorizeRoles("admin"), getAdminAllOrders);
22 |
23 | router
24 | .route("/admin/order/:id")
25 | .put(isAuthenticatedUser, authorizeRoles("admin"), updateAdminOrder)
26 | .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteOrder);
27 |
28 | module.exports = router;
29 |
--------------------------------------------------------------------------------
/backend/routes/PaymentRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const { Payment, sendStripeApiKey } = require("../controller/PaymentController");
3 | const router = express.Router();
4 | const {isAuthenticatedUser} = require("../middleware/auth");
5 |
6 | router.route("/payment/process").post(isAuthenticatedUser, Payment);
7 |
8 | router.route("/stripeapikey").get(isAuthenticatedUser, sendStripeApiKey);
9 |
10 |
11 | module.exports = router;
--------------------------------------------------------------------------------
/backend/routes/ProductRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const {
3 | getAllProducts,
4 | createProduct,
5 | updateProduct,
6 | deleteProduct,
7 | getSingleProduct,
8 | createProductReview,
9 | getSingleProductReviews,
10 | deleteReview,
11 | getAdminProducts,
12 | } = require("../controller/ProductController");
13 | const { isAuthenticatedUser, authorizeRoles } = require("../middleware/auth");
14 | const router = express.Router();
15 |
16 | router.route("/products").get(getAllProducts);
17 |
18 | router
19 | .route("/admin/products")
20 | .get(isAuthenticatedUser, authorizeRoles("admin"), getAdminProducts);
21 |
22 | router
23 | .route("/product/new")
24 | .post(isAuthenticatedUser, authorizeRoles("admin"), createProduct);
25 |
26 | router
27 | .route("/product/:id")
28 | .put(isAuthenticatedUser, authorizeRoles("admin"), updateProduct)
29 | .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteProduct)
30 | .get(getSingleProduct);
31 |
32 | router.route("/product/review").post(isAuthenticatedUser, createProductReview);
33 |
34 | router
35 | .route("/reviews")
36 | .get(getSingleProductReviews)
37 | .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteReview);
38 |
39 | module.exports = router;
40 |
--------------------------------------------------------------------------------
/backend/routes/UserRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const {
3 | createUser,
4 | loginUser,
5 | logoutUser,
6 | forgotPassword,
7 | resetPassword,
8 | userDetails,
9 | updatePassword,
10 | updateProfile,
11 | getAllUsers,
12 | getSingleUser,
13 | updateUserRole,
14 | deleteUser,
15 | } = require("../controller/UserController");
16 | const { isAuthenticatedUser, authorizeRoles } = require("../middleware/auth");
17 | const router = express.Router();
18 |
19 | router.route("/registration").post(createUser);
20 |
21 | router.route("/login").post(loginUser);
22 |
23 | router.route("/logout").get(logoutUser);
24 |
25 | router.route("/password/forgot").post(forgotPassword);
26 |
27 | router.route("/password/reset/:token").put(resetPassword);
28 |
29 | router.route("/me/update").put(isAuthenticatedUser, updatePassword);
30 |
31 | router.route("/me/update/info").put(isAuthenticatedUser, updateProfile);
32 |
33 | router.route("/me").get(isAuthenticatedUser, userDetails);
34 |
35 | router
36 | .route("/admin/users")
37 | .get(isAuthenticatedUser, authorizeRoles("admin"), getAllUsers);
38 |
39 | router
40 | .route("/admin/user/:id")
41 | .get(isAuthenticatedUser, authorizeRoles("admin"), getSingleUser)
42 | .put(isAuthenticatedUser, authorizeRoles("admin"), updateUserRole)
43 | .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteUser);
44 |
45 | module.exports = router;
46 |
--------------------------------------------------------------------------------
/backend/routes/WishListRoute.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const {
3 | addToWishlist,
4 | getWishlistData,
5 | removeWishlistData,
6 | addToCart,
7 | getCartData,
8 | updateCart,
9 | removeCartData,
10 | } = require("../controller/CartController");
11 | const { isAuthenticatedUser } = require("../middleware/auth");
12 | const router = express.Router();
13 |
14 | router.route("/wishlist").get(isAuthenticatedUser, getWishlistData);
15 |
16 | router.route("/addToWishlist").post(isAuthenticatedUser, addToWishlist);
17 |
18 | router
19 | .route("/removeWishlist/:id")
20 | .delete(isAuthenticatedUser, removeWishlistData);
21 |
22 | router.route("/addToCart").post(isAuthenticatedUser, addToCart);
23 |
24 | router.route("/cart").get(isAuthenticatedUser, getCartData);
25 |
26 | router.route("/cart/update/:id").put(isAuthenticatedUser, updateCart);
27 |
28 | router.route("/removeCart/:id").delete(isAuthenticatedUser, removeCartData);
29 |
30 | module.exports = router;
31 |
--------------------------------------------------------------------------------
/backend/server.js:
--------------------------------------------------------------------------------
1 | const app = require("./app");
2 | const connectDatabase = require("./db/Database.js");
3 | const cloudinary = require("cloudinary");
4 |
5 | // Handling uncaught Exception
6 | process.on("uncaughtException",(err) =>{
7 | console.log(`Error: ${err.message}`);
8 | console.log(`Shutting down the server for Handling uncaught Exception`);
9 | })
10 |
11 | // config
12 | if(process.env.NODE_ENV!=="PRODUCTION"){
13 | require("dotenv").config({
14 | path:"backend/config/.env"
15 | })}
16 | // connect database
17 | connectDatabase();
18 |
19 | cloudinary.config({
20 | cloud_name: process.env.CLOUDINARY_NAME,
21 | api_key: process.env.CLOUDINARY_API_KEY,
22 | api_secret: process.env.CLOUDINARY_API_SECRET
23 | })
24 |
25 | // create server
26 | const server = app.listen(process.env.PORT,() =>{
27 | console.log(`Server is working on http://localhost:${process.env.PORT}`)
28 | })
29 |
30 |
31 | // Unhandled promise rejection
32 | process.on("unhandledRejection", (err) =>{
33 | console.log(`Shutting down server for ${err.message}`);
34 | console.log(`Shutting down the server due to Unhandled promise rejection`);
35 | server.close(() =>{
36 | process.exit(1);
37 | });
38 | });
--------------------------------------------------------------------------------
/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 |
10 | }
11 | module.exports = ErrorHandler
--------------------------------------------------------------------------------
/backend/utils/Features.js:
--------------------------------------------------------------------------------
1 | class Features {
2 | constructor(query,queryStr){
3 | this.query = query;
4 | this.queryStr = queryStr;
5 | }
6 |
7 | search() {
8 | const keyword = this.queryStr.keyword ? {
9 | name:{
10 | $regex: this.queryStr.keyword,
11 | $options: "i"
12 | }
13 | }
14 | :{
15 |
16 | }
17 | this.query = this.query.find({...keyword});
18 | return this;
19 | }
20 |
21 | filter(){
22 | const queryCopy = { ...this.queryStr };
23 |
24 | // Removing some field for category
25 | const removeFields = ["keyword","page","limit"];
26 |
27 | removeFields.forEach((key) => delete queryCopy[key]);
28 |
29 | this.query = this.query.find(queryCopy);
30 | return this;
31 | }
32 |
33 | pagination(resultPerPage){
34 | const currentPage = Number(this.queryStr.page) || 1;
35 | const skip = resultPerPage *(currentPage - 1);
36 |
37 | this.query= this.query.limit(resultPerPage).skip(skip);
38 |
39 | return this;
40 | }
41 |
42 | }
43 |
44 | module.exports = Features;
--------------------------------------------------------------------------------
/backend/utils/jwtToken.js:
--------------------------------------------------------------------------------
1 | // create token and saving that in cookies
2 | const sendToken = (user,statusCode,res) =>{
3 |
4 | const token = user.getJwtToken();
5 |
6 | // Options for cookies
7 | const options = {
8 | expires: new Date(
9 | Date.now() + process.env.COOKIE_EXPIRE * 24 * 60 * 60 * 1000
10 | ),
11 | httpOnly: true
12 | };
13 |
14 | res.status(statusCode).cookie("token",token,options).json({
15 | success: true,
16 | user,
17 | token
18 | });
19 | }
20 |
21 | module.exports = sendToken;
--------------------------------------------------------------------------------
/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 | });
13 |
14 | const mailOptions = {
15 | from: process.env.SMPT_MAIL,
16 | to: options.email,
17 | subject: options.subject,
18 | text: options.message,
19 | };
20 |
21 | await transporter.sendMail(mailOptions);
22 | };
23 |
24 | module.exports = sendMail;
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@emailjs/browser": "^3.5.0",
7 | "@material-ui/core": "^4.12.3",
8 | "@material-ui/data-grid": "^4.0.0-alpha.37",
9 | "@material-ui/icons": "^4.11.2",
10 | "@material-ui/lab": "^4.0.0-alpha.60",
11 | "@mui/icons-material": "^5.5.0",
12 | "@stripe/react-stripe-js": "^1.7.0",
13 | "@stripe/stripe-js": "^1.26.0",
14 | "@testing-library/jest-dom": "^5.16.2",
15 | "@testing-library/react": "^12.1.2",
16 | "@testing-library/user-event": "^13.5.0",
17 | "axios": "0.21.1",
18 | "chart.js": "^3.6.2",
19 | "country-state-city": "^3.0.1",
20 | "npm": "^8.7.0",
21 | "react": "^17.0.2",
22 | "react-alert": "^7.0.3",
23 | "react-chartjs-2": "^4.0.0",
24 | "react-dom": "^17.0.2",
25 | "react-helmet": "^6.1.0",
26 | "react-js-pagination": "^3.0.3",
27 | "react-material-ui-carousel": "^2.3.1",
28 | "react-rating-stars-component": "^2.2.0",
29 | "react-redux": "^7.2.6",
30 | "react-router-dom": "5.2.0",
31 | "react-scripts": "5.0.0",
32 | "react-toastify": "^8.2.0",
33 | "redux": "^4.1.2",
34 | "redux-devtools-extension": "^2.13.9",
35 | "redux-thunk": "^2.4.1",
36 | "web-vitals": "^2.1.4",
37 | "webfontloader": "^1.6.28"
38 | },
39 | "scripts": {
40 | "start": "react-scripts start",
41 | "build": "react-scripts build",
42 | "test": "react-scripts test",
43 | "eject": "react-scripts eject"
44 | },
45 | "eslintConfig": {
46 | "extends": [
47 | "react-app",
48 | "react-app/jest"
49 | ]
50 | },
51 | "browserslist": {
52 | "production": [
53 | ">0.2%",
54 | "not dead",
55 | "not op_mini all"
56 | ],
57 | "development": [
58 | "last 1 chrome version",
59 | "last 1 firefox version",
60 | "last 1 safari version"
61 | ]
62 | },
63 | "proxy": "http://localhost:4000"
64 | }
65 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 | React App
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/public/logo192.png
--------------------------------------------------------------------------------
/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/public/logo512.png
--------------------------------------------------------------------------------
/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/public/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/public/profile.png
--------------------------------------------------------------------------------
/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/frontend/src/Assets/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/background.jpg
--------------------------------------------------------------------------------
/frontend/src/Assets/background2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/background2.jpg
--------------------------------------------------------------------------------
/frontend/src/Assets/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/bg.jpg
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/bag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/bag.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/home.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/homeActive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/homeActive.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/logout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/logout.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/logoutActive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/logoutActive.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/search.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/searchActive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/searchActive.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/shopping-bagActive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/shopping-bagActive.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/shopping-cart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/shopping-cart.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/shopping-cartActive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/shopping-cartActive.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/user.png
--------------------------------------------------------------------------------
/frontend/src/Assets/icons/userActive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/icons/userActive.png
--------------------------------------------------------------------------------
/frontend/src/Assets/slider-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/Assets/slider-2.png
--------------------------------------------------------------------------------
/frontend/src/actions/CartAction.js:
--------------------------------------------------------------------------------
1 | import {
2 | ADD_TO_CART,
3 | REMOVE_CART_ITEM,
4 | SAVE_SHIPPING_INFO,
5 | } from "../constans/CartConstans";
6 | import axios from "axios";
7 |
8 | // Add to Cart ---Product
9 | export const addItemsToCart = (id, quantity) => async (dispatch, getState) => {
10 | const { data } = await axios.get(`/api/v2/product/${id}`);
11 |
12 | dispatch({
13 | type: ADD_TO_CART,
14 | payload: {
15 | product: data.product._id,
16 | name: data.product.name,
17 | price: data.product.price,
18 | image: data.product.images[0].url,
19 | stock: data.product.Stock,
20 | quantity,
21 | },
22 | });
23 |
24 | localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems));
25 | };
26 |
27 | // REMOVE FROM CART ---Product
28 | export const removeItemsFromCart = (id) => async (dispatch, getState) => {
29 | dispatch({
30 | type: REMOVE_CART_ITEM,
31 | payload: id,
32 | });
33 |
34 | localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems));
35 | };
36 |
37 |
38 | // SAVE SHIPPING INFO
39 | export const saveShippingInfo = (data) => async (dispatch) => {
40 | dispatch({
41 | type: SAVE_SHIPPING_INFO,
42 | payload: data,
43 | });
44 |
45 | localStorage.setItem("shippingInfo", JSON.stringify(data));
46 | };
--------------------------------------------------------------------------------
/frontend/src/actions/FavouriteAction.js:
--------------------------------------------------------------------------------
1 | import { ADD_TO_FAVOURITE, ADD_TO_FAVOURITE_OFFER, REMOVE_FROM_FAVOURITE, REMOVE_FROM_FAVOURITE_OFFER}
2 | from "../constans/FavouriteConstans";
3 | import axios from "axios";
4 |
5 | // Add to favourites
6 | export const addFavouriteItemsToCart = (id,quantity) => async (dispatch, getState) =>{
7 | const {data} = await axios.get(`/api/v2/product/${id}`);
8 |
9 | dispatch({
10 | type: ADD_TO_FAVOURITE,
11 | payload: {
12 | product: data.product._id,
13 | name: data.product.name,
14 | price: data.product.price,
15 | image: data.product.images[0].url,
16 | stock: data.product.Stock,
17 | quantity,
18 | }
19 | })
20 |
21 | localStorage.setItem("favouriteItems", JSON.stringify(getState().favourite.favouriteItems));
22 | }
23 |
24 | // Delete from favourites
25 | export const deleteFavouriteItemsToCart = (id) => async (dispatch, getState) => {
26 | dispatch({
27 | type: REMOVE_FROM_FAVOURITE,
28 | payload: id,
29 | });
30 |
31 | localStorage.setItem("favouriteItems", JSON.stringify(getState().favourite.favouriteItems));
32 | };
33 |
--------------------------------------------------------------------------------
/frontend/src/actions/OrderAction.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import {
3 | ALL_ORDERS_FAIL,
4 | ALL_ORDERS_REQUEST,
5 | ALL_ORDERS_SUCCESS,
6 | CREATE_ORDER_FAIL,
7 | CREATE_ORDER_REQUEST,
8 | CREATE_ORDER_SUCCESS,
9 | DELETE_ORDER_FAIL,
10 | DELETE_ORDER_REQUEST,
11 | DELETE_ORDER_SUCCESS,
12 | MY_ORDERS_FAIL,
13 | MY_ORDERS_REQUEST,
14 | MY_ORDERS_SUCCESS,
15 | ORDER_DETAILS_FAIL,
16 | ORDER_DETAILS_REQUEST,
17 | ORDER_DETAILS_SUCCESS,
18 | UPDATE_ORDER_FAIL,
19 | UPDATE_ORDER_REQUEST,
20 | UPDATE_ORDER_SUCCESS,
21 | } from "../constans/OrderConstans";
22 | import { CLEAR_ERRORS } from "../constans/userContans";
23 |
24 | // Create Order
25 | export const createOrder = (order) => async (dispatch) => {
26 | try {
27 | dispatch({ type: CREATE_ORDER_REQUEST });
28 |
29 | const config = {
30 | headers: {
31 | "Content-Type": "application/json",
32 | },
33 | };
34 | const { data } = await axios.post("/api/v2/order/new", order, config);
35 |
36 | dispatch({ type: CREATE_ORDER_SUCCESS, payload: data });
37 | } catch (error) {
38 | dispatch({
39 | type: CREATE_ORDER_FAIL,
40 | payload: error.response.data.message,
41 | });
42 | }
43 | };
44 |
45 |
46 | // My Orders
47 | export const myOrders = () => async (dispatch) => {
48 | try {
49 | dispatch({ type: MY_ORDERS_REQUEST });
50 |
51 | const { data } = await axios.get("/api/v2/orders/me");
52 |
53 | dispatch({ type: MY_ORDERS_SUCCESS, payload: data.orders });
54 | } catch (error) {
55 | dispatch({
56 | type: MY_ORDERS_FAIL,
57 | payload: error.response.data.message,
58 | });
59 | }
60 | };
61 |
62 | // Get Order Details
63 | export const getOrderDetails = (id) => async (dispatch) => {
64 | try {
65 | dispatch({ type: ORDER_DETAILS_REQUEST });
66 |
67 | const { data } = await axios.get(`/api/v2/order/${id}`);
68 |
69 | dispatch({ type: ORDER_DETAILS_SUCCESS, payload: data.order });
70 | } catch (error) {
71 | dispatch({
72 | type: ORDER_DETAILS_FAIL,
73 | payload: error.response.data.message,
74 | });
75 | }
76 | };
77 |
78 |
79 | // All order -----Admin
80 | export const getAllOrders = () => async (dispatch) => {
81 | try {
82 | dispatch({ type: ALL_ORDERS_REQUEST });
83 |
84 | const { data } = await axios.get("/api/v2/admin/orders");
85 |
86 | dispatch({ type: ALL_ORDERS_SUCCESS, payload: data.orders });
87 | } catch (error) {
88 | dispatch({
89 | type: ALL_ORDERS_FAIL,
90 | payload: error.response.data.message,
91 | });
92 | }
93 | };
94 |
95 | // Update Order
96 | export const updateOrder = (id, order) => async (dispatch) => {
97 | try {
98 | dispatch({ type: UPDATE_ORDER_REQUEST });
99 |
100 | const config = {
101 | headers: {
102 | "Content-Type": "application/json",
103 | },
104 | };
105 | const { data } = await axios.put(
106 | `/api/v2/admin/order/${id}`,
107 | order,
108 | config
109 | );
110 |
111 | dispatch({ type: UPDATE_ORDER_SUCCESS, payload: data.success });
112 | } catch (error) {
113 | dispatch({
114 | type: UPDATE_ORDER_FAIL,
115 | payload: error.response.data.message,
116 | });
117 | }
118 | };
119 |
120 | // Delete Order
121 | export const deleteOrder = (id) => async (dispatch) => {
122 | try {
123 | dispatch({ type: DELETE_ORDER_REQUEST });
124 |
125 | const { data } = await axios.delete(`/api/v2/admin/order/${id}`);
126 |
127 | dispatch({ type: DELETE_ORDER_SUCCESS, payload: data.success });
128 | } catch (error) {
129 | dispatch({
130 | type: DELETE_ORDER_FAIL,
131 | payload: error.response.data.message,
132 | });
133 | }
134 | };
135 |
136 |
137 |
138 |
139 | // Clearing Errors
140 | export const clearErrors = () => async (dispatch) => {
141 | dispatch({ type: CLEAR_ERRORS });
142 | };
143 |
--------------------------------------------------------------------------------
/frontend/src/component/Admin/AllOrder.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect } from "react";
2 | import { DataGrid } from "@material-ui/data-grid";
3 | import "./newProduct.css";
4 | import { useSelector, useDispatch } from "react-redux";
5 | import { Link } from "react-router-dom";
6 | import { Button } from "@material-ui/core";
7 | import MetaData from "../../more/Metadata";
8 | import EditIcon from "@material-ui/icons/Edit";
9 | import DeleteIcon from "@material-ui/icons/Delete";
10 | import SideBar from "./Sidebar";
11 | import {
12 | getAllOrders,
13 | clearErrors,
14 | deleteOrder,
15 | } from "../../actions/OrderAction";
16 | import { DELETE_ORDER_RESET } from "../../constans/OrderConstans";
17 | import { ToastContainer, toast } from 'react-toastify';
18 |
19 |
20 | const AllOrder = ({ history }) => {
21 | const dispatch = useDispatch();
22 |
23 | const { error, orders } = useSelector((state) => state.AllOrders);
24 |
25 | const { error: deleteError, isDeleted } = useSelector((state) => state.deleteOrder);
26 |
27 | const deleteOrderHandler = (id) => {
28 | dispatch(deleteOrder(id));
29 | };
30 |
31 | useEffect(() => {
32 | if (error) {
33 | toast.error(error);
34 | dispatch(clearErrors());
35 | }
36 |
37 | if (deleteError) {
38 | toast.error(deleteError);
39 | dispatch(clearErrors());
40 | }
41 |
42 | if (isDeleted) {
43 | toast.success("Order Deleted Successfully");
44 | history.push("/admin/orders");
45 | dispatch({ type: DELETE_ORDER_RESET });
46 | }
47 |
48 | dispatch(getAllOrders());
49 | }, [dispatch, error, deleteError, history, isDeleted]);
50 |
51 | const columns = [
52 | { field: "id", headerName: "Order ID", minWidth: 300, flex: 1 },
53 |
54 | {
55 | field: "status",
56 | headerName: "Status",
57 | minWidth: 150,
58 | flex: 0.5,
59 | cellClassName: (params) => {
60 | return params.getValue(params.id, "status") === "Delivered"
61 | ? "greenColor"
62 | : "redColor";
63 | },
64 | },
65 | {
66 | field: "itemsQty",
67 | headerName: "Items Qty",
68 | type: "number",
69 | minWidth: 150,
70 | flex: 0.4,
71 | },
72 |
73 | {
74 | field: "amount",
75 | headerName: "Amount",
76 | type: "number",
77 | minWidth: 270,
78 | flex: 0.5,
79 | },
80 |
81 | {
82 | field: "actions",
83 | flex: 0.3,
84 | headerName: "Actions",
85 | minWidth: 150,
86 | type: "number",
87 | sortable: false,
88 | renderCell: (params) => {
89 | return (
90 |
91 |
92 |
93 |
94 |
95 |
102 |
103 | );
104 | },
105 | },
106 | ];
107 |
108 | const rows = [];
109 |
110 | orders &&
111 | orders.forEach((item) => {
112 | rows.push({
113 | id: item._id,
114 | itemsQty: item.orderItems.length,
115 | amount: item.totalPrice,
116 | status: item.orderStatus,
117 | });
118 | });
119 |
120 | return (
121 |
122 |
123 |
124 |
125 |
126 |
127 |
ALL ORDERS
128 |
129 |
137 |
138 |
139 |
150 |
151 | );
152 | };
153 |
154 | export default AllOrder;
--------------------------------------------------------------------------------
/frontend/src/component/Admin/AllProducts.css:
--------------------------------------------------------------------------------
1 | .productListContainer {
2 | width: 100%;
3 | box-sizing: border-box;
4 | background-color: rgb(255, 255, 255);
5 | border-left: 1px solid rgba(0, 0, 0, 0.158);
6 | display: flex;
7 | flex-direction: column;
8 | height: 100vh;
9 | }
10 | .MuiDataGrid-columnHeader{
11 | background: #3BB77E;
12 | }
13 |
14 | #productListHeading {
15 | font: 400 2rem "Roboto";
16 | padding: 0.5vmax;
17 | box-sizing: border-box;
18 | color: rgba(0, 0, 0, 0.637);
19 | transition: all 0.5s;
20 | margin: 2rem;
21 | text-align: center;
22 | }
23 |
24 | .productListTable {
25 | background-color: white;
26 | border: none !important;
27 | }
28 |
29 | .productListTable div {
30 | font: 300 1vmax "Roboto";
31 | color: rgba(0, 0, 0, 0.678);
32 | border: none !important;
33 | }
34 |
35 | .productListTable a,
36 | .productListTable button {
37 | color: rgba(0, 0, 0, 0.527);
38 | transition: all 0.5s;
39 | }
40 |
41 | .productListTable a:hover {
42 | color: tomato;
43 | }
44 |
45 | .productListTable button:hover {
46 | color: rgb(236, 30, 30);
47 | }
48 |
49 | .MuiDataGrid-columnHeader div {
50 | color: rgb(255, 255, 255);
51 | }
52 |
53 | @media screen and (max-width: 600px) {
54 | .productListTable div {
55 | font: 300 4vw "Roboto";
56 | }
57 | }
--------------------------------------------------------------------------------
/frontend/src/component/Admin/AllProducts.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect } from "react";
2 | import { DataGrid } from "@material-ui/data-grid";
3 | import "./AllProducts.css";
4 | import { useSelector, useDispatch } from "react-redux";
5 | import {
6 | clearErrors,
7 | deleteProduct,
8 | getAdminProduct,
9 | } from "../../actions/ProductActions";
10 | import { Link } from "react-router-dom";
11 | import { Button } from "@material-ui/core";
12 | import MetaData from "../../more/Metadata";
13 | import EditIcon from "@material-ui/icons/Edit";
14 | import DeleteIcon from "@material-ui/icons/Delete";
15 | import SideBar from "./Sidebar";
16 | import { ToastContainer, toast } from 'react-toastify';
17 | import { DELETE_PRODUCT_RESET } from "../../constans/ProductConstans";
18 |
19 |
20 | const AllProducts = ({history}) => {
21 |
22 | const dispatch = useDispatch();
23 |
24 | const { error, products } = useSelector((state) => state.products);
25 |
26 | const { error: deleteError, isDeleted } = useSelector(
27 | (state) => state.deleteProduct
28 | );
29 |
30 | const deleteProductHandler = (id) => {
31 | dispatch(deleteProduct(id));
32 | };
33 |
34 | useEffect(() => {
35 | if (error) {
36 | alert(error);
37 | dispatch(clearErrors());
38 | }
39 | if (deleteError) {
40 | toast.error(deleteError);
41 | dispatch(clearErrors());
42 | }
43 |
44 | if (isDeleted) {
45 | toast.success("Product Deleted Successfully");
46 | history.push("/dashboard");
47 | dispatch({ type: DELETE_PRODUCT_RESET });
48 | }
49 | dispatch(getAdminProduct());
50 | }, [dispatch, alert, error, history]);
51 |
52 | const columns = [
53 | { field: "id", headerName: "Product ID", minWidth: 200, flex: 0.5 },
54 |
55 | {
56 | field: "name",
57 | headerName: "Name",
58 | minWidth: 350,
59 | flex: 1,
60 | },
61 | {
62 | field: "stock",
63 | headerName: "Stock",
64 | type: "number",
65 | minWidth: 150,
66 | flex: 0.3,
67 | },
68 |
69 | {
70 | field: "price",
71 | headerName: "Price",
72 | type: "number",
73 | minWidth: 270,
74 | flex: 0.5,
75 | },
76 |
77 | {
78 | field: "actions",
79 | flex: 0.3,
80 | headerName: "Actions",
81 | minWidth: 150,
82 | type: "number",
83 | sortable: false,
84 | renderCell: (params) => {
85 | return (
86 |
87 |
88 |
89 |
90 |
91 |
98 |
99 | );
100 | },
101 | },
102 | ];
103 |
104 | const rows = [];
105 |
106 | products &&
107 | products.forEach((item) => {
108 | rows.push({
109 | id: item._id,
110 | stock: item.Stock,
111 | price: item.price,
112 | name: item.name,
113 | });
114 | });
115 |
116 | return (
117 |
118 |
119 |
120 |
121 |
122 |
123 |
ALL PRODUCTS
124 |
125 |
133 |
134 |
135 |
146 |
147 | )
148 | }
149 |
150 | export default AllProducts
151 |
--------------------------------------------------------------------------------
/frontend/src/component/Admin/AllUsers.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect } from "react";
2 | import { DataGrid } from "@material-ui/data-grid";
3 | import "./newProduct.css";
4 | import { useSelector, useDispatch } from "react-redux";
5 | import { Link } from "react-router-dom";
6 | import { Button } from "@material-ui/core";
7 | import MetaData from "../../more/Metadata";
8 | import EditIcon from "@material-ui/icons/Edit";
9 | import DeleteIcon from "@material-ui/icons/Delete";
10 | import SideBar from "./Sidebar";
11 | import { getAllUsers, clearErrors, deleteUser } from "../../actions/userAction";
12 | import { DELETE_USER_RESET } from "../../constans/userContans";
13 | import { ToastContainer, toast } from 'react-toastify';
14 |
15 | const AllUsers = ({ history }) => {
16 |
17 | const dispatch = useDispatch();
18 |
19 | const { error, users } = useSelector((state) => state.allUsers);
20 |
21 | const {
22 | error: deleteError,
23 | isDeleted,
24 | message,
25 | } = useSelector((state) => state.profile);
26 |
27 | const deleteUserHandler = (id) => {
28 | dispatch(deleteUser(id));
29 | };
30 |
31 | useEffect(() => {
32 | if (error) {
33 | toast.error(error);
34 | dispatch(clearErrors());
35 | }
36 |
37 | if (deleteError) {
38 | toast.error(deleteError);
39 | dispatch(clearErrors());
40 | }
41 |
42 | if (isDeleted) {
43 | toast.success(message);
44 | history.push("/admin/users");
45 | dispatch({ type: DELETE_USER_RESET });
46 | }
47 |
48 | dispatch(getAllUsers());
49 | }, [dispatch, alert, error, deleteError, history, isDeleted, message]);
50 |
51 | const columns = [
52 | { field: "id", headerName: "User ID", minWidth: 180, flex: 0.8 },
53 |
54 | {
55 | field: "email",
56 | headerName: "Email",
57 | minWidth: 200,
58 | flex: 1,
59 | },
60 | {
61 | field: "name",
62 | headerName: "Name",
63 | minWidth: 150,
64 | flex: 0.5,
65 | },
66 |
67 | {
68 | field: "role",
69 | headerName: "Role",
70 | type: "number",
71 | minWidth: 150,
72 | flex: 0.3,
73 | cellClassName: (params) => {
74 | return params.getValue(params.id, "role") === ("admin")
75 | ? "greenColor"
76 | : "redColor";
77 | },
78 | },
79 |
80 | {
81 | field: "actions",
82 | flex: 0.3,
83 | headerName: "Actions",
84 | minWidth: 150,
85 | type: "number",
86 | sortable: false,
87 | renderCell: (params) => {
88 | return (
89 |
90 |
91 |
92 |
93 |
94 |
101 |
102 | );
103 | },
104 | },
105 | ];
106 |
107 | const rows = [];
108 |
109 | users &&
110 | users.forEach((item) => {
111 | rows.push({
112 | id: item._id,
113 | role: item.role,
114 | email: item.email,
115 | name: item.name,
116 | });
117 | });
118 |
119 | return (
120 |
121 |
122 |
123 |
124 |
125 |
126 |
ALL USERS
127 |
128 |
136 |
137 |
138 |
149 |
150 | );
151 | };
152 |
153 | export default AllUsers;
--------------------------------------------------------------------------------
/frontend/src/component/Admin/Dashboard.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import Sidebar from "./Sidebar.js";
3 | import "./dashboard.css";
4 | import { Typography } from "@material-ui/core";
5 | import {Link} from "react-router-dom";
6 | import { Doughnut, Line } from "react-chartjs-2";
7 | // eslint-disable-next-line
8 | import Chart from 'chart.js/auto';
9 | import { useSelector, useDispatch } from "react-redux";
10 | import MetaData from "../../more/Metadata.js";
11 | import Loading from "../../more/Loader.js";
12 | import { getAdminProduct } from "../../actions/ProductActions.js";
13 | import { getAllOrders } from "../../actions/OrderAction.js";
14 | import { getAllUsers } from "../../actions/userAction.js";
15 |
16 | const Dashboard = () => {
17 |
18 | const dispatch = useDispatch();
19 |
20 | const { products,loading } = useSelector((state) => state.products);
21 |
22 | const { orders } = useSelector((state) => state.AllOrders);
23 |
24 | const { users } = useSelector((state) => state.allUsers);
25 |
26 | let outOfStock = 0;
27 |
28 | products &&
29 | products.forEach((item) => {
30 | if (item.Stock === 0) {
31 | outOfStock += 1;
32 | }
33 | });
34 |
35 | useEffect(() => {
36 | dispatch(getAdminProduct());
37 | dispatch(getAllOrders());
38 | dispatch(getAllUsers());
39 | }, [dispatch]);
40 |
41 | let totalAmount = 0;
42 | orders &&
43 | orders.forEach((item) => {
44 | totalAmount += item.totalPrice;
45 | });
46 |
47 | const lineState = {
48 | labels: ["Initial Amount", "Amount Earned"],
49 | datasets: [
50 | {
51 | label: "TOTAL AMOUNT",
52 | backgroundColor: ["#3BB77E"],
53 | hoverBackgroundColor: ["#3BB77E"],
54 | data: [0, totalAmount],
55 | },
56 | ],
57 | };
58 |
59 | const doughnutState = {
60 | labels: ["Out of Stock", "InStock"],
61 | datasets: [
62 | {
63 | backgroundColor: ["#00A6B4", "#6800B4"],
64 | hoverBackgroundColor: ["#4B5000", "#35014F"],
65 | data: [outOfStock, products.length - outOfStock],
66 | },
67 | ],
68 | };
69 |
70 | return (
71 | <>
72 | {loading ?
73 |
74 | :(
75 |
76 |
77 |
78 |
79 |
80 |
Dashboard
81 |
82 |
83 |
84 |
85 | Total Amount
${totalAmount}
86 |
87 |
88 |
89 |
90 |
Product
91 |
{products && products.length}
92 |
93 |
94 |
Orders
95 |
{orders && orders.length}
96 |
97 |
98 |
Users
99 |
{users && users.length}
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | )
114 | }
115 | >
116 | );
117 | };
118 | export default Dashboard
119 |
--------------------------------------------------------------------------------
/frontend/src/component/Admin/Sidebar.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | background-color: rgb(255, 255, 255);
3 | position: sticky;
4 | top: 0;
5 | left: 0;
6 | display: flex;
7 | flex-direction: column;
8 | overflow: hidden;
9 | height: 100vh;
10 | }
11 |
12 | .sidebar > a:first-child {
13 | padding: 0;
14 | }
15 | .sidebar > a > img {
16 | width: 100%;
17 | transition: all 0.5s;
18 | padding: 1vmax 0;
19 | }
20 |
21 | .sidebar a {
22 | text-decoration: none;
23 | color: rgba(0, 0, 0, 0.493);
24 | font: 200 1rem "Roboto";
25 | padding: 1.8rem 1.2rem;
26 | transition: all 0.5s;
27 | }
28 | .sidebar a > P {
29 | display: flex;
30 | align-items: center;
31 | }
32 | .sidebar a > p > svg {
33 | margin-right: 0.5rem;
34 | }
35 |
36 | .MuiTypography-root {
37 | background-color: #fff !important;
38 | }
39 | .MuiTypography-root.MuiTreeItem-label.MuiTypography-body1 {
40 | color: #999;
41 | }
--------------------------------------------------------------------------------
/frontend/src/component/Admin/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Sidebar.css";
3 | import { Link } from "react-router-dom";
4 | import PostAddIcon from "@material-ui/icons/PostAdd";
5 | import AddIcon from "@material-ui/icons/Add";
6 | import LocalOffer from "@material-ui/icons/LocalOffer";
7 | import ListAltIcon from "@material-ui/icons/ListAlt";
8 | import DashboardIcon from "@material-ui/icons/Dashboard";
9 | import PeopleIcon from "@material-ui/icons/People";
10 | import RateReviewIcon from "@material-ui/icons/RateReview";
11 |
12 | const Sidebar = () => {
13 |
14 | const button = () =>{
15 | let items = document.querySelectorAll(".Dashboard__item");
16 |
17 | }
18 |
19 | return (
20 |
21 |
22 |

24 |
25 |
26 |
27 | Dashboard
28 |
29 |
30 |
31 |
All Products
32 |
33 |
34 |
35 |
Create Product
36 |
37 |
38 |
39 |
40 |
41 |
42 | Orders
43 |
44 |
45 |
46 |
47 | Users
48 |
49 |
50 |
51 |
52 |
53 | Reviews
54 |
55 |
56 |
57 | );
58 | };
59 |
60 | export default Sidebar;
--------------------------------------------------------------------------------
/frontend/src/component/Admin/UpdateOrder.css:
--------------------------------------------------------------------------------
1 | .updateOrderForm {
2 | margin: 5vmax 0;
3 | padding: 3vmax;
4 | background-color: white;
5 | }
6 |
7 | .updateOrderForm > div {
8 | display: flex;
9 | width: 100%;
10 | align-items: center;
11 | }
12 | .updateOrderForm > div > select {
13 | padding: 1vmax 4vmax;
14 | margin: 2rem 0;
15 | width: 100%;
16 | box-sizing: border-box;
17 | border: 1px solid rgba(0, 0, 0, 0.267);
18 | border-radius: 4px;
19 | font: 300 0.9vmax cursive;
20 | outline: none;
21 | }
22 |
23 | .updateOrderForm > div > svg {
24 | position: absolute;
25 | transform: translateX(1vmax);
26 | font-size: 1.6vmax;
27 | color: rgba(0, 0, 0, 0.623);
28 | }
29 |
30 | @media screen and (max-width: 600px) {
31 | .updateOrderForm {
32 | padding: 5vmax;
33 | }
34 |
35 | .updateOrderForm > div > select {
36 | padding: 2.5vmax 5vmax;
37 | font: 300 1.7vmax cursive;
38 | }
39 |
40 | .updateOrderForm > div > svg {
41 | font-size: 2.8vmax;
42 | }
43 | }
--------------------------------------------------------------------------------
/frontend/src/component/Admin/UpdateUser.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect, useState } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import { Button } from "@material-ui/core";
4 | import MetaData from "../../more/Metadata";
5 | import MailOutlineIcon from "@material-ui/icons/MailOutline";
6 | import PersonIcon from "@material-ui/icons/Person";
7 | import VerifiedUserIcon from "@material-ui/icons/VerifiedUser";
8 | import SideBar from "./Sidebar";
9 | import { UPDATE_USER_RESET } from "../../constans/userContans";
10 | import {
11 | getUserDetails,
12 | updateUser,
13 | clearErrors,
14 | } from "../../actions/userAction";
15 | import Loading from "../../more/Loader";
16 | import { ToastContainer, toast } from 'react-toastify';
17 |
18 |
19 | const UpdateUser = ({ history, match }) => {
20 | const dispatch = useDispatch();
21 |
22 | const { loading, error, user } = useSelector((state) => state.userDetails);
23 |
24 | const {
25 | loading: updateLoading,
26 | error: updateError,
27 | isUpdated,
28 | } = useSelector((state) => state.profile);
29 |
30 | const [name, setName] = useState("");
31 | const [email, setEmail] = useState("");
32 | const [role, setRole] = useState("");
33 |
34 | const userId = match.params.id;
35 |
36 | useEffect(() => {
37 | if (user && user._id !== userId) {
38 | dispatch(getUserDetails(userId));
39 | } else {
40 | setName(user.name);
41 | setEmail(user.email);
42 | setRole(user.role);
43 | }
44 | if (error) {
45 | toast.error(error);
46 | dispatch(clearErrors());
47 | }
48 |
49 | if (updateError) {
50 | toast.error(updateError);
51 | dispatch(clearErrors());
52 | }
53 |
54 | if (isUpdated) {
55 | toast.success("User Updated Successfully");
56 | history.push("/admin/users");
57 | dispatch({ type: UPDATE_USER_RESET });
58 | }
59 | }, [dispatch, alert, error, history, isUpdated, updateError, user, userId]);
60 |
61 | const updateUserSubmitHandler = (e) => {
62 | e.preventDefault();
63 |
64 | const myForm = new FormData();
65 |
66 | myForm.set("name", name);
67 | myForm.set("email", email);
68 | myForm.set("role", role);
69 |
70 | dispatch(updateUser(userId, myForm));
71 | };
72 |
73 | return (
74 |
75 |
76 |
77 |
78 |
79 | {loading ? (
80 |
81 | ) : (
82 |
128 | )}
129 |
130 |
131 |
142 |
143 | );
144 | };
145 |
146 | export default UpdateUser;
--------------------------------------------------------------------------------
/frontend/src/component/Admin/dashboard.css:
--------------------------------------------------------------------------------
1 | .dashboard {
2 | width: 100vw;
3 | max-width: 100%;
4 | display: grid;
5 | grid-template-columns: 1fr 5fr;
6 | position: absolute;
7 | }
8 |
9 | .dashboardContainer {
10 | border-left: 1px solid rgba(0, 0, 0, 0.13);
11 | background-color: rgb(255, 255, 255);
12 | padding: 1rem 0;
13 | }
14 |
15 | .dashboardContainer > h1 {
16 | color: rgba(0, 0, 0, 0.733);
17 | font: 300 2rem "Roboto";
18 | text-align: center;
19 | width: 50%;
20 | padding: 1.5rem;
21 | margin: auto;
22 | }
23 |
24 | .dashboardSummary {
25 | margin: 2rem 0;
26 | }
27 |
28 | .dashboardSummary > div {
29 | display: flex;
30 | background-color: white;
31 | justify-content: center;
32 | }
33 | .dashboardSummary > div > p {
34 | background-color: #3BB77E;
35 | color: white;
36 | font: 300 1.3rem "Roboto";
37 | text-align: center;
38 | padding: 1.5rem;
39 | width: 100%;
40 | margin: 0 2rem;
41 | }
42 | .dashboardSummaryBox2 > a {
43 | color: rgb(0, 0, 0);
44 | font: 300 2rem "Roboto";
45 | text-align: center;
46 | background-color: rgb(255, 233, 174);
47 | text-decoration: none;
48 | padding: 1.5rem;
49 | width: 10vmax;
50 | height: 10vmax;
51 | margin: 2rem;
52 | border-radius: 100%;
53 | display: flex;
54 | justify-content: center;
55 | align-items: center;
56 | flex-direction: column;
57 | }
58 |
59 | .dashboardSummaryBox2 > a:first-child {
60 | background-color: rgb(255, 110, 110);
61 | color: rgb(255, 255, 255);
62 | }
63 |
64 | .dashboardSummaryBox2 > a:last-child {
65 | background-color: rgb(51, 51, 51);
66 | color: rgb(255, 255, 255);
67 | }
68 |
69 | .lineChart {
70 | width: 80%;
71 | margin: auto;
72 | }
73 |
74 | .doughnutChart {
75 | width: 30vmax;
76 | margin: auto;
77 | }
78 |
79 | @media screen and (max-width: 600px) {
80 | .dashboard {
81 | grid-template-columns: 1fr;
82 | }
83 |
84 | .dashboardContainer {
85 | border-left: none;
86 | }
87 |
88 | .dashboardSummary > div > p {
89 | margin: 0;
90 | }
91 |
92 | .dashboardSummaryBox2 > a {
93 | padding: 0.5rem;
94 | margin: 1rem;
95 | font: 300 0.9rem "Roboto";
96 | }
97 | }
--------------------------------------------------------------------------------
/frontend/src/component/Admin/newProduct.css:
--------------------------------------------------------------------------------
1 | .newProductContainer {
2 | width: 100%;
3 | box-sizing: border-box;
4 | background-color: rgb(221, 221, 221);
5 | border-left: 1px solid rgba(0, 0, 0, 0.158);
6 | display: flex;
7 | flex-direction: column;
8 | height: 100vh;
9 | }
10 | .newProductContainer h1 {
11 | color: rgba(0, 0, 0, 0.733);
12 | font: 300 2rem "Roboto";
13 | text-align: center;
14 | }
15 |
16 | .createProductForm {
17 | display: flex;
18 | flex-direction: column;
19 | align-items: center;
20 | margin: auto;
21 | padding: 3vmax;
22 | justify-content: space-evenly;
23 | height: 70%;
24 | width: 40vh;
25 | background-color: white;
26 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.267);
27 | }
28 |
29 | .createProductForm > div {
30 | display: flex;
31 | width: 100%;
32 | align-items: center;
33 | }
34 | .createProductForm > div > input,
35 | .createProductForm > div > select,
36 | .createProductForm > div > textarea {
37 | padding: 1vmax 4vmax;
38 | text-align: start;
39 | padding-right: 1vmax;
40 | width: 100%;
41 | box-sizing: border-box;
42 | border: 1px solid rgba(0, 0, 0, 0.267);
43 | border-radius: 4px;
44 | font: 300 0.9vmax cursive;
45 | outline: none;
46 | }
47 |
48 | .createProductForm > div > svg {
49 | position: absolute;
50 | transform: translateX(1vmax);
51 | font-size: 1.6vmax;
52 | color: rgba(0, 0, 0, 0.623);
53 | }
54 |
55 | #createProductFormFile > input {
56 | display: flex;
57 | padding: 0%;
58 | }
59 |
60 | #createProductFormFile > input::file-selector-button {
61 | cursor: pointer;
62 | width: 100%;
63 | z-index: 2;
64 | height: 5vh;
65 | border: none;
66 | margin: 0%;
67 | font: 400 0.8vmax cursive;
68 | transition: all 0.5s;
69 | padding: 0 1vmax;
70 | color: rgba(0, 0, 0, 0.623);
71 | background-color: rgb(255, 255, 255);
72 | }
73 |
74 | #createProductFormFile > input::file-selector-button:hover {
75 | background-color: rgb(235, 235, 235);
76 | }
77 |
78 | #createProductFormImage {
79 | width: 100%;
80 | overflow: auto;
81 | }
82 |
83 | #createProductFormImage > img {
84 | width: 3vmax;
85 | margin: 0 0.5vmax;
86 | }
87 | #createProductBtn {
88 | border: none;
89 | background-color: #3BB77E;;
90 | color: white;
91 | font: 300 0.9vmax "Roboto";
92 | width: 100%;
93 | padding: 0.8vmax;
94 | cursor: pointer;
95 | transition: all 0.5s;
96 | border-radius: 4px;
97 | outline: none;
98 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.219);
99 | }
100 |
101 | #createProductBtn:hover {
102 | background-color: #3BB77E;
103 | }
104 |
105 | @media screen and (max-width: 600px) {
106 | .newProductContainer {
107 | background-color: rgb(255, 255, 255);
108 | }
109 | .createProductForm {
110 | padding: 5vmax;
111 | }
112 |
113 | .createProductForm > div > input,
114 | .createProductForm > div > select,
115 | .createProductForm > div > textarea {
116 | padding: 2.5vmax 5vmax;
117 | font: 300 1.7vmax cursive;
118 | }
119 |
120 | .createProductForm > div > svg {
121 | font-size: 2.8vmax;
122 | }
123 |
124 | #createProductFormFile > img {
125 | width: 8vmax;
126 | border-radius: 100%;
127 | }
128 |
129 | #createProductFormFile > input::file-selector-button {
130 | height: 7vh;
131 | font: 400 1.8vmax cursive;
132 | }
133 |
134 | #createProductBtn {
135 | font: 300 1.9vmax "Roboto";
136 | padding: 1.8vmax;
137 | }
138 | }
--------------------------------------------------------------------------------
/frontend/src/component/Admin/productReviews.css:
--------------------------------------------------------------------------------
1 | .productReviewsContainer {
2 | width: 100%;
3 | box-sizing: border-box;
4 | background-color: rgb(255, 255, 255);
5 | border-left: 1px solid rgba(0, 0, 0, 0.158);
6 | height: 100vh;
7 | }
8 |
9 | .productReviewsForm {
10 | width: 20rem;
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | margin: auto;
15 | padding: 3vmax;
16 | background-color: white;
17 | }
18 |
19 | .productReviewsFormHeading {
20 | color: rgba(0, 0, 0, 0.733);
21 | font: 300 2rem "Roboto";
22 | text-align: center;
23 | }
24 |
25 | .productReviewsForm > div {
26 | display: flex;
27 | width: 100%;
28 | align-items: center;
29 | margin: 2rem;
30 | }
31 | .productReviewsForm > div > input {
32 | padding: 1vmax 4vmax;
33 | padding-right: 1vmax;
34 | width: 100%;
35 | box-sizing: border-box;
36 | border: 1px solid rgba(0, 0, 0, 0.267);
37 | border-radius: 4px;
38 | font: 300 0.9vmax cursive;
39 | outline: none;
40 | }
41 |
42 | .productReviewsForm > div > svg {
43 | position: absolute;
44 | transform: translateX(1vmax);
45 | font-size: 1.6vmax;
46 | color: rgba(0, 0, 0, 0.623);
47 | }
48 |
49 | @media screen and (max-width: 600px) {
50 | .productReviewsContainer {
51 | border-left: none;
52 | border-top: 1px solid rgba(0, 0, 0, 0.158);
53 | }
54 | .productReviewsForm > div > input {
55 | padding: 2.5vmax 5vmax;
56 | font: 300 1.7vmax cursive;
57 | }
58 |
59 | .productReviewsForm > div > svg {
60 | font-size: 2.8vmax;
61 | }
62 | }
--------------------------------------------------------------------------------
/frontend/src/component/Home/Header.css:
--------------------------------------------------------------------------------
1 | .inputBox > span{
2 | font-family: "Roboto";
3 | animation: sliding 14s linear infinite;
4 | display: block;
5 | }
6 | @keyframes sliding{
7 | 0%{
8 | transform: translateX(-70%);
9 | }
10 | 100%{
11 | transform: translateX(130%);
12 | }
13 | }
--------------------------------------------------------------------------------
/frontend/src/component/Products/Product.css:
--------------------------------------------------------------------------------
1 | ul.pagination{
2 | display: flex;
3 | align-items: center;
4 | padding: 0;
5 | }
6 | .page-item{
7 | background-color: #fff;
8 | list-style: none;
9 | border: 1px solid rgba(0,0,0,0.178);
10 | padding: 1vmax 1.5vmax;
11 | transition: all 0.3s;
12 | cursor: pointer;
13 | }
14 | .page-item:first-child{
15 | border-radius: 5px 0 0 5px;
16 | }
17 | .page-item:last-child{
18 | border-top-right-radius: 5px;
19 | border-bottom-right-radius: 5px;
20 |
21 | }
22 | .page-link{
23 | font: 300 0.7vmax "Roboto";
24 | color: rgb(80,80,80);
25 | transition: all 0.3s;
26 | }
27 | .page-item:hover{
28 | background-color: rgb(230,230,230);
29 | }
30 | .page-item:hover .page-link{
31 | color: rgb(37, 37, 37);
32 | }
33 | .pageItemActive{
34 | background-color: #197EF3;
35 | }
36 | .pageLinkActive{
37 | color: white;
38 | }
39 | li.category-link{
40 | list-style: none;
41 | font-family: Roboto;
42 | cursor: pointer;
43 | border-top: 1px solid #999;
44 | padding: 10px 10px;
45 | }
46 |
--------------------------------------------------------------------------------
/frontend/src/component/Products/ProductCard.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import { Rating } from "@material-ui/lab";
4 | const ProductCard = ({ product }) => {
5 | const options = {
6 | value: product.ratings,
7 | readOnly: true,
8 | precision: 0.5,
9 | };
10 |
11 | return (
12 | <>
13 |
14 |
19 | {product.name}
20 |
21 |
22 | ({product.numOfReviews} Reviews)
23 |
24 |
30 |
31 |
39 | {product.offerPrice > 0 ? `$${product.offerPrice}` : ""}
40 |
41 | {`$${product.price}`}
42 |
43 |
44 |
45 | >
46 | );
47 | };
48 |
49 | export default ProductCard;
50 |
--------------------------------------------------------------------------------
/frontend/src/component/Products/Productdetails.css:
--------------------------------------------------------------------------------
1 | .ProductDetails{
2 | display: flex;
3 | width: 100%;
4 | margin: 20px 0;
5 | }
6 | input:checked ~ label{
7 | display: none;
8 | }
9 | input{
10 | outline: none;
11 | }
12 | img.CarouselImage{
13 | width: 350px;
14 | height: 350px;
15 | object-fit: contain;
16 | }
17 | .first__varse {margin: auto;}
18 | .varse__2{
19 | width: 50%;
20 | overflow: hidden;
21 | }
22 | .css-14j5k7k{
23 | justify-content: center!important;
24 | align-items: center!important;
25 | display: flex!important;
26 | }
27 | .detailsBlock-1 > h2 {
28 | padding: 5px 0px;
29 | }
30 | .detailsBlock-2 > span {
31 | padding: 5px 0px;
32 | }
33 | .detailsBlock-3-1 {
34 | display: flex;
35 | align-items: center;
36 | }
37 |
38 | .detailsBlock-3-1-1 {
39 | width: 60px;
40 | overflow: hidden;
41 | display: flex;
42 | margin: 10px;
43 | }
44 | p.noReviews {
45 | text-align: center;
46 | }
47 | .detailsBlock-3-1-1 > button {
48 | width: 100%;
49 | background: #3BB77E;
50 | border: none;
51 | color: #fff;
52 | height: 20px;
53 | cursor: pointer;
54 | }
55 | input[type="number"] {
56 | border: none;
57 | width: 100%;
58 | text-align: center;
59 | }
60 |
61 | @media screen and (max-width: 600px) {
62 | .varse__2 {
63 | width: 100%;
64 | overflow: hidden;
65 | text-align: center;
66 | }
67 | .detailsBlock-3-1{
68 | flex-direction: column;
69 | }
70 | .Description{
71 | padding: 0 3vmax;
72 | }
73 | .Description p{
74 | padding-left: 0!important;
75 | }
76 | .detailsBlock > div > h1 {
77 | font-size: 3vmax;
78 | color: #000!important;
79 | opacity: 1;
80 | padding: 1vmax 0;
81 | }
82 | .home__content{
83 | width: 100%!important;
84 | left: 0!important;
85 | }
86 | .home__content > div > h2 {font-size: 3vmax!important;
87 | line-height: 1.9;}
88 | .home__content > div > span{
89 | height: 30px!important;
90 | width: 135px!important;
91 | }
92 | button.Home__button {font-size: 2.1vmax!important;}
93 | .CarouselItem {
94 | width: 100%;
95 | height: 100%;
96 | display: flex!important;
97 | align-items: center!important;
98 | justify-content: center!important;
99 | }
100 | .first__varse{
101 | margin: unset!important;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/frontend/src/component/Products/ReviewCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useSelector } from "react-redux";
3 | import { Rating } from "@material-ui/lab";
4 | import Loading from "../../more/Loader"
5 |
6 | const ReviewCard = ({review}) => {
7 | // eslint-disable-next-line
8 |
9 | const { product,loading } = useSelector(
10 | (state) => state.productDetails
11 | );
12 |
13 | const options = {
14 | value: review.rating,
15 | readOnly: true,
16 | precision: 0.5,
17 | color:"#3BB77E"
18 | };
19 |
20 | return (
21 | <>
22 | {loading ? (
23 |
24 | ) :(
25 | <>
26 |
31 |
{review.name}
32 |
{String(review.time).substr(0,10)}
33 |
34 |
35 |
{review.comment}
36 |
37 |
38 | >
39 | )}
40 | >
41 | )
42 | }
43 |
44 | export default ReviewCard
45 |
--------------------------------------------------------------------------------
/frontend/src/component/Products/Search.css:
--------------------------------------------------------------------------------
1 | .searchBox {
2 | width: 100vw;
3 | height: 100vh;
4 | max-width: 100%;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | background-color: rgb(231, 231, 231);
9 | position: fixed;
10 | top: 0%;
11 | left: 0;
12 | }
13 |
14 | .searchBox > input[type="text"] {
15 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.274);
16 | background-color: white;
17 | color: rgba(0, 0, 0, 0.637);
18 | padding: 1vmax 2vmax;
19 | width: 50%;
20 | outline: none;
21 | border: 2px solid #197EF3;
22 | border-radius: 8px;
23 | font: 300 1.1vmax cursive;
24 | box-sizing: border-box;
25 | height: 8%;
26 | }
27 | @media screen and (max-width: 600px) {
28 | .searchBox > input[type="text"] {
29 | width: 100%;
30 | font: 300 4vw cursive;
31 | height: 10%;
32 | }
33 |
34 | .searchBox > input[type="submit"] {
35 | height: 10%;
36 | width: 30%;
37 | font: 300 4vw "Roboto";
38 | }
39 | }
40 | @media screen and (max-width: 600px) {
41 | .searchBox > input[type="text"]{
42 | margin: 0 5%;
43 | }
44 | svg.bi.bi-search.pointer{
45 | right: 9%!important;
46 | }
47 | }
--------------------------------------------------------------------------------
/frontend/src/component/Products/Search.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, Fragment } from "react";
2 | import BottomTab from "../../more/BottomTab";
3 | import MetaData from "../../more/Metadata";
4 | import "./Search.css";
5 |
6 | const Search = ({ history }) => {
7 | const [keyword, setKeyword] = useState("");
8 |
9 | const searchSubmitHandler = (e) => {
10 | e.preventDefault();
11 | if (keyword.trim()) {
12 | history.push(`/products/${keyword}`);
13 | } else {
14 | history.push("/products");
15 | }
16 | };
17 |
18 | return (
19 |
20 |
21 |
43 |
44 |
45 | );
46 | };
47 |
48 | export default Search;
49 |
--------------------------------------------------------------------------------
/frontend/src/component/cart/Cart.css:
--------------------------------------------------------------------------------
1 | .emptyCart {
2 | margin: auto;
3 | text-align: center;
4 | padding: 10vmax;
5 | height: 50vh;
6 | display: flex;
7 | flex-direction: column;
8 | justify-content: center;
9 | align-items: center;
10 | }
11 | .emptyCart > svg {
12 | font-size: 5vmax;
13 | color: #3BB77E;
14 | }
15 | .emptyCart > p {
16 | font-size: 2vmax;
17 | }
18 | .emptyCart > a {
19 | background-color: #3BB77E;
20 | color: white;
21 | border: none;
22 | padding: 1vmax 3vmax;
23 | cursor: pointer;
24 | font: 400 1vmax "Roboto";
25 | margin-top: 1%;
26 | text-decoration: none;
27 | }
28 |
29 | .cartPage {
30 | padding: 5vmax;
31 | }
32 |
33 | .cartHeader {
34 | background-color: #3BB77E;
35 | width: 90%;
36 | box-sizing: border-box;
37 | margin: auto;
38 | color: white;
39 | display: grid;
40 | grid-template-columns: 4fr 1fr 1fr;
41 | font: 300 0.7vmax "Roboto";
42 | }
43 | .cartHeader > p {
44 | margin: 10px;
45 | }
46 | .cartHeader > p:last-child {
47 | text-align: end;
48 | }
49 |
50 | .cartContainer {
51 | width: 90%;
52 | margin: auto;
53 | border-bottom: 1px solid #99999942;
54 | display: grid;
55 | grid-template-columns: 4fr 1fr 1fr;
56 | }
57 |
58 | .cartInput {
59 | display: flex;
60 | align-items: center;
61 | height: 8vmax;
62 | }
63 |
64 | .cartInput > button {
65 | border: none;
66 | background-color: #3BB77E;
67 | padding: 0.5vmax;
68 | cursor: pointer;
69 | color: white;
70 | transition: all 0.5s;
71 | }
72 | .cartInput > button:hover {
73 | background-color:#3BB77E;
74 | }
75 |
76 | .cartInput > input {
77 | border: none;
78 | padding: 0.5vmax;
79 | width: 1vmax;
80 | text-align: center;
81 | outline: none;
82 | font: 400 0.8vmax "Roboto";
83 | color: rgba(0, 0, 0, 0.74);
84 | }
85 |
86 | .cartSubtotal {
87 | display: flex;
88 | padding: 0.5vmax;
89 | height: 8vmax;
90 | align-items: center;
91 | box-sizing: border-box;
92 | font: 300 1vmax cursive;
93 | justify-content: flex-end;
94 | color: rgba(0, 0, 0, 0.753);
95 | }
96 |
97 | .cartGrossProfit {
98 | display: grid;
99 | grid-template-columns: 2fr 1.2fr;
100 | }
101 |
102 | .cartGrossProfitBox {
103 | border-top: 3px solid #3BB77E;
104 | margin: 1vmax 4vmax;
105 | box-sizing: border-box;
106 | padding: 2vmax 0;
107 | font: 300 1vmax "Roboto";
108 | display: flex;
109 | justify-content: space-between;
110 | }
111 |
112 | .checkOutBtn {
113 | display: flex;
114 | justify-content: flex-end;
115 | }
116 | .checkOutBtn > button {
117 | background-color: #3BB77E;
118 | color: white;
119 | border: none;
120 | padding: 0.8vmax 3vmax;
121 | font: 300 0.8vmax "Roboto";
122 | margin: 1vmax 4vmax;
123 | cursor: pointer;
124 | }
125 |
126 | @media screen and (max-width: 600px) {
127 | .cartPage {
128 | padding: 0;
129 | min-height: 60vh;
130 | }
131 |
132 | .cartHeader {
133 | width: 100%;
134 | font: 300 1.7vmax "Roboto";
135 | grid-template-columns: 3fr 1fr 1fr;
136 | }
137 |
138 | .cartContainer {
139 | width: 100%;
140 | grid-template-columns: 3fr 1fr 1fr;
141 | }
142 |
143 | .cartInput {
144 | height: 20vmax;
145 | }
146 |
147 | .cartInput > button {
148 | padding: 1.5vmax;
149 | }
150 |
151 | .cartInput > input {
152 | width: 2vmax;
153 | padding: 1.5vmax;
154 | font: 400 1.8vmax "Roboto";
155 | }
156 |
157 | .cartSubtotal {
158 | padding: 1.5vmax;
159 | height: 20vmax;
160 | font: 300 2vmax "Roboto";
161 | }
162 |
163 | .cartGrossProfit {
164 | display: grid;
165 | grid-template-columns: 0fr 2fr;
166 | }
167 |
168 | .cartGrossProfitBox {
169 | padding: 2vmax;
170 | font: 300 2vmax "Roboto";
171 | }
172 |
173 | .checkOutBtn > button {
174 | padding: 2vmax 4vmax;
175 | width: 100%;
176 | font: 300 2vmax "Roboto";
177 | }
178 | }
--------------------------------------------------------------------------------
/frontend/src/component/cart/Cart.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Cart.css";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import { addItemsToCart, removeItemsFromCart } from "../../actions/CartAction";
5 | import { Typography } from "@material-ui/core";
6 | import RemoveShoppingCartIcon from "@material-ui/icons/RemoveShoppingCart";
7 | import { Link } from "react-router-dom";
8 | import CartItemCard from "./CartItemCard.js";
9 | import BottomTab from "../../more/BottomTab";
10 | import { ToastContainer, toast } from 'react-toastify';
11 | import 'react-toastify/dist/ReactToastify.css';
12 |
13 | const Cart = ({ history }) => {
14 | const dispatch = useDispatch();
15 |
16 | const { cartItems } = useSelector((state) => state.cart);
17 |
18 | let Price = cartItems.reduce(
19 | (acc, item) => acc + item.quantity * item.price,
20 | 0
21 | );
22 |
23 | let totalPrice = Price;
24 |
25 | const increaseQuantity = (id, quantity, stock) => {
26 | const newQty = quantity + 1;
27 | if (stock <= quantity) {
28 | return toast.error("Product Stock Limited");
29 | }
30 | dispatch(addItemsToCart(id, newQty));
31 | };
32 |
33 | const decreaseQuantity = (id, quantity) => {
34 | const newQty = quantity - 1;
35 | if (1 >= quantity) {
36 | return;
37 | }
38 | dispatch(addItemsToCart(id, newQty));
39 | };
40 |
41 | const deleteCartItems = (id) => {
42 | dispatch(removeItemsFromCart(id));
43 | };
44 |
45 | const checkoutHandler = () => {
46 | history.push("/login?redirect=shipping");
47 | };
48 |
49 | return (
50 | <>
51 | {cartItems.length === 0 ? (
52 |
53 |
54 | No Items In Cart
55 | View Products
56 |
57 |
58 | ) : (
59 | <>
60 |
61 |
62 |
Product
63 |
Quantity
64 |
Subtotal
65 |
66 |
67 | {cartItems &&
68 | cartItems.map((item) => (
69 |
70 |
71 |
72 |
79 |
80 |
91 |
92 |
{`$${
93 | item.price * item.quantity
94 | }`}
95 |
96 | ))}
97 |
98 |
99 |
100 |
101 |
Price Total
102 |
$ {totalPrice}
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
121 |
122 | >
123 | )}
124 | >
125 | );
126 | };
127 |
128 | export default Cart;
129 |
--------------------------------------------------------------------------------
/frontend/src/component/cart/CartItemCard.css:
--------------------------------------------------------------------------------
1 | .CartItemCard {
2 | display: flex;
3 | padding: 1vmax;
4 | height: 8vmax;
5 | align-items: flex-start;
6 | box-sizing: border-box;
7 | }
8 | .CartItemCard > img {
9 | width: 5vmax;
10 | }
11 |
12 | .CartItemCard > div {
13 | display: flex;
14 | margin: 0.3vmax 1vmax;
15 | flex-direction: column;
16 | }
17 |
18 | .CartItemCard > div > a {
19 | font: 300 0.9vmax cursive;
20 | color: rgba(24, 24, 24, 0.815);
21 | text-decoration: none;
22 | }
23 |
24 | .CartItemCard > div > span {
25 | font: 300 0.9vmax "Roboto";
26 | color: rgba(24, 24, 24, 0.815);
27 | }
28 |
29 | .CartItemCard > div > p {
30 | color: #3BB77E;
31 | font: 100 0.8vmax "Roboto";
32 | cursor: pointer;
33 | }
34 |
35 | @media screen and (max-width: 600px) {
36 | .CartItemCard {
37 | padding: 3vmax;
38 | height: 25vmax;
39 | }
40 | .CartItemCard > img {
41 | width: 10vmax;
42 | }
43 |
44 | .CartItemCard > div {
45 | margin: 1vmax 2vmax;
46 | }
47 |
48 | .CartItemCard > div > a {
49 | font: 300 2vmax cursive;
50 | }
51 |
52 | .CartItemCard > div > span {
53 | font: 300 1.9vmax "Roboto";
54 | }
55 |
56 | .CartItemCard > div > p {
57 | font: 100 1.8vmax "Roboto";
58 | }
59 | }
--------------------------------------------------------------------------------
/frontend/src/component/cart/CartItemCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import "./CartItemCard.css";
4 |
5 | const CartItemCard = ({item, deleteCartItems}) => {
6 | return (
7 |
8 |

9 |
10 |
{item.name}
11 |
{`Price: $ ${item.price}`}
12 |
deleteCartItems(item.product)}>Remove
13 |
14 |
15 | )
16 | }
17 |
18 | export default CartItemCard
19 |
--------------------------------------------------------------------------------
/frontend/src/component/cart/CheckoutSteps.css:
--------------------------------------------------------------------------------
1 | .confirmOrderPage {
2 | height: 100vh;
3 | background-color: white;
4 | display: grid;
5 | grid-template-columns: 6fr 3fr;
6 | }
7 |
8 | .confirmOrderPage > div:last-child {
9 | border-left: 1px solid rgba(0, 0, 0, 0.247);
10 | }
11 |
12 | .confirmshippingArea {
13 | padding: 5vmax;
14 | padding-bottom: 0%;
15 | }
16 |
17 | .confirmshippingArea > p {
18 | font: 400 1.8vmax "Roboto";
19 | }
20 |
21 | .confirmshippingAreaBox,
22 | .confirmCartItemsContainer {
23 | margin: 2vmax;
24 | }
25 |
26 | .confirmshippingAreaBox > div {
27 | display: flex;
28 | margin: 1vmax 0;
29 | }
30 |
31 | .confirmshippingAreaBox > div > p {
32 | font: 400 1vmax "Roboto";
33 | color: black;
34 | }
35 | .confirmshippingAreaBox > div > span {
36 | margin: 0 1vmax;
37 | font: 100 1vmax "Roboto";
38 | color: #575757;
39 | }
40 |
41 | .confirmCartItems > p {
42 | font: 400 1.8vmax "Roboto";
43 | }
44 |
45 | .confirmCartItems {
46 | padding: 5vmax;
47 | padding-top: 2vmax;
48 | }
49 |
50 | .confirmCartItemsContainer {
51 | max-height: 20vmax;
52 | overflow-y: auto;
53 | }
54 |
55 | .confirmCartItemsContainer > div {
56 | display: flex;
57 | font: 400 1vmax "Roboto";
58 | align-items: center;
59 | justify-content: space-between;
60 | margin: 2vmax 0;
61 | }
62 |
63 | .confirmCartItemsContainer > div > img {
64 | width: 3vmax;
65 | }
66 |
67 | .confirmCartItemsContainer > div > a {
68 | color: #575757;
69 | margin: 0 2vmax;
70 | width: 60%;
71 | text-decoration: none;
72 | }
73 |
74 | .confirmCartItemsContainer > div > span {
75 | font: 100 1vmax "Roboto";
76 | color: #5e5e5e;
77 | }
78 |
79 | .orderSummary {
80 | padding: 7vmax;
81 | }
82 |
83 | .orderSummary > p {
84 | text-align: center;
85 | font: 400 1.8vmax "Roboto";
86 | border-bottom: 1px solid rgba(0, 0, 0, 0.267);
87 | padding: 1vmax;
88 | width: 100%;
89 | margin: auto;
90 | box-sizing: border-box;
91 | }
92 |
93 | .orderSummary > div > div {
94 | display: flex;
95 | font: 300 1vmax "Roboto";
96 | justify-content: space-between;
97 | margin: 2vmax 0;
98 | }
99 | .orderSummary > div > div > span {
100 | color: rgba(0, 0, 0, 0.692);
101 | }
102 |
103 | .orderSummaryTotal {
104 | display: flex;
105 | font: 300 1vmax "Roboto";
106 | justify-content: space-between;
107 | border-top: 1px solid rgba(0, 0, 0, 0.363);
108 | padding: 2vmax 0;
109 | }
110 |
111 | .orderSummary > button {
112 | background-color: #3BB77E;
113 | color: white;
114 | width: 100%;
115 | padding: 1vmax;
116 | border: none;
117 | margin: auto;
118 | cursor: pointer;
119 | transition: 0.5s;
120 | font: 400 1vmax "Roboto";
121 | }
122 |
123 | .orderSummary > button:hover {
124 | background-color: #3BB77E;
125 | }
126 |
127 | @media screen and (max-width: 600px) {
128 | .confirmOrderPage {
129 | grid-template-columns: 1fr;
130 | height: unset;
131 | }
132 |
133 | .confirmOrderPage > div:last-child {
134 | border-left: 0;
135 | border-top: 1px solid rgba(0, 0, 0, 0.247);
136 | }
137 |
138 | .confirmshippingArea > p {
139 | font: 400 6vw "Roboto";
140 | }
141 |
142 | .confirmshippingAreaBox > div {
143 | display: flex;
144 | margin: 6vw 0;
145 | }
146 |
147 | .confirmshippingAreaBox > div > p {
148 | font: 400 4vw "Roboto";
149 | }
150 | .confirmshippingAreaBox > div > span {
151 | font: 100 4vw "Roboto";
152 | }
153 |
154 | .confirmCartItems > p {
155 | font: 400 6vw "Roboto";
156 | }
157 |
158 | .confirmCartItemsContainer {
159 | max-height: 50vw;
160 | }
161 |
162 | .confirmCartItemsContainer > div {
163 | font: 400 4vw "Roboto";
164 | margin: 4vw 0;
165 | }
166 |
167 | .confirmCartItemsContainer > div > img {
168 | width: 10vw;
169 | }
170 |
171 | .confirmCartItemsContainer > div > a {
172 | margin: 0;
173 | width: 30%;
174 | }
175 |
176 | .confirmCartItemsContainer > div > span {
177 | font: 100 4vw "Roboto";
178 | }
179 |
180 | .orderSummary {
181 | padding: 12vw;
182 | }
183 |
184 | .orderSummary > p {
185 | font: 400 6vw "Roboto";
186 | padding: 4vw;
187 | }
188 |
189 | .orderSummary > div > div {
190 | font: 300 4vw "Roboto";
191 | }
192 |
193 | .orderSummaryTotal {
194 | font: 300 4vw "Roboto";
195 | padding: 5vw 0;
196 | }
197 |
198 | .orderSummary > button {
199 | padding: 4vw;
200 | margin: 4vw auto;
201 | font: 400 4vw "Roboto";
202 | }
203 | }
--------------------------------------------------------------------------------
/frontend/src/component/cart/CheckoutSteps.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Typography, Stepper, StepLabel, Step } from "@material-ui/core";
3 | import LocalShippingIcon from "@material-ui/icons/LocalShipping";
4 | import LibraryAddCheckIcon from "@material-ui/icons/LibraryAddCheck";
5 | import AccountBalanceIcon from "@material-ui/icons/AccountBalance";
6 | import "./CheckoutSteps.css";
7 | import BottomTab from "../../more/BottomTab";
8 |
9 | const CheckoutSteps = ({ activeStep }) => {
10 | const steps = [
11 | {
12 | label: Shipping Details,
13 | icon: ,
14 | },
15 | {
16 | label: Confirm Order,
17 | icon: ,
18 | },
19 | {
20 | label: Payment,
21 | icon: ,
22 | },
23 | ];
24 |
25 | const stepStyles = {
26 | boxSizing: "border-box",
27 | };
28 |
29 | return (
30 | <>
31 |
32 | {steps.map((item, index) => (
33 | = index ? true : false}
37 | >
38 | = index ? "#3BB77E" : "rgba(0, 0, 0, 0.649)",
41 | }}
42 | icon={item.icon}
43 | >
44 | {item.label}
45 |
46 |
47 | ))}
48 |
49 |
50 | >
51 | );
52 | };
53 |
54 | export default CheckoutSteps;
--------------------------------------------------------------------------------
/frontend/src/component/cart/ConfirmOrder.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/component/cart/ConfirmOrder.css
--------------------------------------------------------------------------------
/frontend/src/component/cart/ConfirmOrder.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./ConfirmOrder.css";
3 | import { useSelector } from "react-redux";
4 | import CheckoutSteps from "./CheckoutSteps";
5 | import MetaData from "../../more/Metadata";
6 | import { Link } from "react-router-dom";
7 | import { Typography } from "@material-ui/core";
8 | import BottomTab from "../../more/BottomTab";
9 |
10 |
11 |
12 | const ConfirmOrder = ({ history }) => {
13 | const { shippingInfo, cartItems } = useSelector((state) => state.cart);
14 |
15 | const { user } = useSelector((state) => state.user);
16 |
17 | let productPrice = cartItems.reduce(
18 | (acc, item) => acc + item.quantity * item.price,
19 | 0
20 | );
21 |
22 | const subtotal = productPrice
23 | // eslint-disable-next-line
24 | const shippingCharges = productPrice > 99 ? 0 : 50;
25 |
26 | const totalPrice = subtotal + shippingCharges;
27 |
28 | const address = `${shippingInfo.address}, ${shippingInfo.state}, ${shippingInfo.country}`;
29 |
30 | const proceedToPayment = () => {
31 | const data = {
32 | subtotal,
33 | shippingCharges,
34 | totalPrice,
35 | };
36 |
37 | sessionStorage.setItem("orderInfo", JSON.stringify(data));
38 |
39 | history.push("/process/payment");
40 | };
41 |
42 | return (
43 | <>
44 |
45 |
46 |
47 |
48 |
49 |
Shipping Info
50 |
51 |
52 |
Name:
53 |
{user.name}
54 |
55 |
56 |
Phone:
57 |
{shippingInfo.phoneNo}
58 |
59 |
60 |
Address:
61 |
{address}
62 |
63 |
64 |
65 |
66 |
Your Cart Items:
67 |
68 |
69 | {cartItems.length === 0 ?
70 |
71 | ""
72 |
73 | :
74 |
75 | {cartItems.map((item) => (
76 |
77 |

78 |
79 | {item.name}
80 | {" "}
81 |
82 | {item.quantity} X ${item.price} ={" "}
83 | ${item.price * item.quantity}
84 |
85 |
86 | ))
87 | }
88 |
89 | }
90 |
91 |
92 |
93 | {/* */}
94 |
95 |
96 |
Order Summery
97 |
98 |
99 |
Subtotal:
100 |
${subtotal}
101 |
102 |
103 |
Shipping Charges:
104 |
${shippingCharges}
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | Total:
113 |
114 |
${totalPrice}
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | >
123 | );
124 | };
125 |
126 | export default ConfirmOrder;
127 |
--------------------------------------------------------------------------------
/frontend/src/component/cart/Favourite.css:
--------------------------------------------------------------------------------
1 | .emptyfavourites {
2 | margin: auto;
3 | text-align: center;
4 | padding: 10vmax;
5 | height: 50vh;
6 | display: flex;
7 | flex-direction: column;
8 | justify-content: center;
9 | align-items: center;
10 | }
11 | .emptyfavourites > svg {
12 | font-size: 5vmax;
13 | color: #3BB77E;
14 | }
15 | .emptyfavourites > p {
16 | font-size: 2vmax;
17 | }
18 | .emptyfavourites > a {
19 | background-color: rgb(51, 51, 51);
20 | color: white;
21 | border: none;
22 | padding: 1vmax 3vmax;
23 | cursor: pointer;
24 | font: 400 1vmax "Roboto";
25 | text-decoration: none;
26 | }
27 |
28 | .favouritesPage {
29 | padding: 5vmax;
30 | }
31 |
32 | .favouritesHeader {
33 | background-color: #3BB77E;
34 | width: 100%;
35 | box-sizing: border-box;
36 | margin: auto;
37 | color: white;
38 | justify-content: space-between;
39 | display: flex;
40 | font: 300 0.7vmax "Roboto";
41 | }
42 | .favouritesHeader > p {
43 | margin: 10px;
44 | }
45 | .favouritesHeader > p:last-child {
46 | text-align: end;
47 | }
48 |
49 | .favouritesContainer {
50 | width: 100%;
51 | margin: auto;
52 | border-bottom: 1px solid #99999942;
53 | display: flex;
54 | justify-content: space-between;
55 | }
56 |
57 | .favouritesInput {
58 | display: flex;
59 | align-items: center;
60 | height: 8vmax;
61 | }
62 |
63 | .favouritesInput > button {
64 | border: none;
65 | background-color: #3BB77E;
66 | padding: 0.5vmax;
67 | cursor: pointer;
68 | color: white;
69 | transition: all 0.5s;
70 | }
71 | .favouritesInput > button:hover {
72 | background-color: #3BB77E;
73 | }
74 |
75 | .favouritesInput > input {
76 | border: none;
77 | padding: 0.5vmax;
78 | width: 1vmax;
79 | text-align: center;
80 | outline: none;
81 | font: 400 0.8vmax "Roboto";
82 | color: rgba(0, 0, 0, 0.74);
83 | }
84 |
85 | .favouritesSubtotal {
86 | display: flex;
87 | padding: 0.5vmax;
88 | height: 8vmax;
89 | align-items: center;
90 | box-sizing: border-box;
91 | font: 300 1vmax cursive;
92 | justify-content: flex-end;
93 | color: rgba(0, 0, 0, 0.753);
94 | }
95 |
96 | .favouritesGrossProfit {
97 | display: grid;
98 | grid-template-columns: 2fr 1.2fr;
99 | }
100 |
101 | .favouritesGrossProfitBox {
102 | border-top: 3px solid #3BB77E;
103 | margin: 1vmax 4vmax;
104 | box-sizing: border-box;
105 | padding: 2vmax 0;
106 | font: 300 1vmax "Roboto";
107 | display: flex;
108 | justify-content: space-between;
109 | }
110 |
111 | .checkOutBtn {
112 | display: flex;
113 | justify-content: flex-end;
114 | }
115 | .checkOutBtn > button {
116 | background-color: #3BB77E;
117 | color: white;
118 | border: none;
119 | padding: 0.8vmax 3vmax;
120 | font: 300 0.8vmax "Roboto";
121 | margin: 1vmax 4vmax;
122 | cursor: pointer;
123 | }
124 |
125 | @media screen and (max-width: 600px) {
126 | .favouritesPage {
127 | padding: 0;
128 | min-height: 60vh;
129 | }
130 |
131 | .favouritesHeader {
132 | width: 100%;
133 | font: 300 1.7vmax "Roboto";
134 | grid-template-columns: 3fr 1fr 1fr;
135 | }
136 |
137 | .favouritesContainer {
138 | width: 100%;
139 | grid-template-columns: 3fr 1fr 1fr;
140 | }
141 |
142 | .favouritesInput {
143 | height: 20vmax;
144 | }
145 |
146 | .favouritesInput > button {
147 | padding: 1.5vmax;
148 | }
149 |
150 | .favouritesInput > input {
151 | width: 2vmax;
152 | padding: 1.5vmax;
153 | font: 400 1.8vmax "Roboto";
154 | }
155 |
156 | .favouritesSubtotal {
157 | padding: 1.5vmax;
158 | height: 20vmax;
159 | font: 300 2vmax "Roboto";
160 | }
161 |
162 | .favouritesGrossProfit {
163 | display: grid;
164 | grid-template-columns: 0fr 2fr;
165 | }
166 |
167 | .favouritesGrossProfitBox {
168 | padding: 2vmax;
169 | font: 300 2vmax "Roboto";
170 | }
171 |
172 | .checkOutBtn > button {
173 | padding: 2vmax 4vmax;
174 | width: 100%;
175 | font: 300 2vmax "Roboto";
176 | }
177 | }
--------------------------------------------------------------------------------
/frontend/src/component/cart/FavouriteItemsCard:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shahriarsajeeb/MERN-Ecommerce-store/a5ffa1e3215ee6592fed2f8392140ce938226391/frontend/src/component/cart/FavouriteItemsCard
--------------------------------------------------------------------------------
/frontend/src/component/cart/FavouriteItemsCard.css:
--------------------------------------------------------------------------------
1 | .FavouriteItemsCard {
2 | display: flex;
3 | padding: 1vmax;
4 | width: 100%;
5 | justify-content: space-between;
6 | box-sizing: border-box;
7 | }
8 | .FavouriteItemsCard > div > img {
9 | width: 5vmax;
10 | }
11 | .favouritesButton{
12 | padding: 12px 25px;
13 | border: none;
14 | background-color: #3BB77E;
15 | color: #fff;
16 | cursor: pointer;
17 | }
18 |
19 | .FavouriteItemsCard > div {
20 | display: flex;
21 | flex-direction: column;
22 | }
23 |
24 | .FavouriteItemsCard > div > a {
25 | font: 300 0.9vmax cursive;
26 | color: rgba(24, 24, 24, 0.815);
27 | text-decoration: none;
28 | }
29 |
30 | .FavouriteItemsCard > div > span {
31 | font: 300 0.9vmax "Roboto";
32 | color: rgba(24, 24, 24, 0.815);
33 | }
34 |
35 | .FavouriteItemsCard > div > p {
36 | color: #3BB77E;
37 | font: 100 0.8vmax "Roboto";
38 | cursor: pointer;
39 | }
40 |
41 | @media screen and (max-width: 600px) {
42 | .FavouriteItemsCard {
43 | padding: 3vmax;
44 | height: 25vmax;
45 | }
46 | .FavouriteItemsCard > img {
47 | width: 10vmax;
48 | }
49 |
50 | .FavouriteItemsCard > div {
51 | margin: 1vmax 2vmax;
52 | }
53 |
54 | .FavouriteItemsCard > div > a {
55 | font: 300 2vmax cursive;
56 | }
57 |
58 | .FavouriteItemsCard > div > span {
59 | font: 300 1.9vmax "Roboto";
60 | }
61 |
62 | .FavouriteItemsCard > div > p {
63 | font: 100 1.8vmax "Roboto";
64 | }
65 | }
--------------------------------------------------------------------------------
/frontend/src/component/cart/FavouriteItemsCard.jsx:
--------------------------------------------------------------------------------
1 | import React,{useState,useEffect} from 'react';
2 | import { Link} from 'react-router-dom';
3 | import "./FavouriteItemsCard.css";
4 | import { useSelector,useDispatch } from "react-redux";
5 |
6 | const FavouriteItemsCard = ({item, deleteFavouriteItems}) => {
7 | const dispatch = useDispatch();
8 | const { product} = useSelector(
9 | (state) => state.productDetails
10 | );
11 |
12 | return (
13 |
14 |
15 |

16 |
deleteFavouriteItems(item.product)}>Remove
17 |
{item.name}
21 |
22 |
23 |
24 | {`$ ${item.price}`}
25 |
26 |
27 |
28 |
29 |
30 | {product.Stock < 1 ? "OutOfStock" : "InStock"}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 | }
44 |
45 | export default FavouriteItemsCard
46 |
--------------------------------------------------------------------------------
/frontend/src/component/cart/Favourites.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import "./Favourite.css";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import {deleteFavouriteItemsToCart, deleteOfferFavouriteItemsToCart} from "../../actions/FavouriteAction"
5 | import { Typography } from "@material-ui/core";
6 | import RemoveShoppingCartIcon from "@material-ui/icons/FavoriteBorder";
7 | import { Link } from "react-router-dom";
8 | import FavouriteItemsCard from './FavouriteItemsCard.jsx';
9 | import MetaData from '../../more/Metadata';
10 | import Loading from '../../more/Loader';
11 | import { useState } from "react";
12 | import BottomTab from '../../more/BottomTab';
13 |
14 | const Favourite = ({history}) => {
15 | const dispatch = useDispatch();
16 |
17 | const {loading} = useSelector(
18 | (state) => state.productDetails
19 | );
20 | const { favouriteItems } = useSelector((state) => state.favourite);
21 |
22 | const deleteFavouriteItems = (id) => {
23 | dispatch(deleteFavouriteItemsToCart(id));
24 | };
25 |
26 | return (
27 | <>
28 | {loading ? (
29 |
30 | ) : (
31 | <>
32 |
33 | {favouriteItems.length === 0 ? (
34 |
35 |
36 | No Items In Favourites
37 | View Products
38 |
39 |
40 | ): (
41 | <>
42 |
43 |
44 |
Product
45 |
Price
46 |
Stock Status
47 |
Action
48 |
49 | {favouriteItems &&
50 | favouriteItems.map((item) => (
51 |
52 |
53 |
54 | ))
55 | }
56 |
57 |
58 | >
59 | )}
60 | >
61 | )}
62 | >
63 | )
64 | }
65 |
66 | export default Favourite
67 |
--------------------------------------------------------------------------------
/frontend/src/component/cart/Shipping.css:
--------------------------------------------------------------------------------
1 | .shippingContainer {
2 | width: 100vw;
3 | max-width: 100%;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | flex-direction: column;
8 | }
9 |
10 | .shippingBox {
11 | background-color: white;
12 | width: 25vw;
13 | height: 90vh;
14 | box-sizing: border-box;
15 | overflow: hidden;
16 | }
17 |
18 | .shippingHeading {
19 | text-align: center;
20 | color: rgba(0, 0, 0, 0.664);
21 | font: 400 1.3vmax "Roboto";
22 | padding: 1.3vmax;
23 | border-bottom: 1px solid rgba(0, 0, 0, 0.205);
24 | width: 50%;
25 | margin: auto;
26 | }
27 |
28 | .shippingForm {
29 | display: flex;
30 | flex-direction: column;
31 | align-items: center;
32 | margin: auto;
33 | padding: 2vmax;
34 | justify-content: space-evenly;
35 | height: 80%;
36 | transition: all 0.5s;
37 | }
38 |
39 | .shippingForm > div {
40 | display: flex;
41 | width: 100%;
42 | align-items: center;
43 | }
44 |
45 | .shippingForm > div > input,
46 | .shippingForm > div > select {
47 | padding: 1vmax 4vmax;
48 | padding-right: 1vmax;
49 | text-align: start;
50 | width: 100%;
51 | box-sizing: border-box;
52 | border: 1px solid rgba(0, 0, 0, 0.267);
53 | border-radius: 4px;
54 | font: 300 0.9vmax cursive;
55 | outline: none;
56 | }
57 |
58 | .shippingForm > div > svg {
59 | position: absolute;
60 | transform: translateX(1vmax);
61 | font-size: 1.6vmax;
62 | color: rgba(0, 0, 0, 0.623);
63 | }
64 |
65 | .shippingBtn {
66 | border: none;
67 | background-color: #3BB77E;
68 | color: white;
69 | font: 300 1vmax "Roboto";
70 | width: 100%;
71 | padding: 1vmax;
72 | cursor: pointer;
73 | transition: all 0.5s;
74 | outline: none;
75 | margin: 2vmax;
76 | }
77 |
78 | .shippingBtn:hover {
79 | background-color: #3BB77E;
80 | }
81 |
82 | @media screen and (max-width: 600px) {
83 | .shippingBox {
84 | width: 100vw;
85 | height: 95vh;
86 | }
87 |
88 | .shippingHeading {
89 | font: 400 6vw "Roboto";
90 | padding: 5vw;
91 | }
92 |
93 | .shippingForm {
94 | padding: 11vw;
95 | }
96 |
97 | .shippingForm > div > input,
98 | .shippingForm > div > select {
99 | padding: 5vw 10vw;
100 | font: 300 4vw cursive;
101 | }
102 |
103 | .shippingForm > div > svg {
104 | font-size: 6vw;
105 | transform: translateX(3vw);
106 | }
107 |
108 | .shippingBtn {
109 | font: 300 4vw "Roboto";
110 | padding: 4vw;
111 | }
112 | }
--------------------------------------------------------------------------------
/frontend/src/component/cart/Shipping.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import "./Shipping.css";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import CheckoutSteps from "../cart/CheckoutSteps.jsx";
5 | import MetaData from "../../more/Metadata";
6 | import HomeIcon from "@material-ui/icons/Home";
7 | import PublicIcon from "@material-ui/icons/Public";
8 | import PhoneIcon from "@material-ui/icons/Phone";
9 | import TransferWithinAStationIcon from "@material-ui/icons/TransferWithinAStation";
10 | import { Country, State } from "country-state-city";
11 | import { saveShippingInfo } from "../../actions/CartAction";
12 | import BottomTab from "../../more/BottomTab";
13 | import { ToastContainer, toast } from "react-toastify";
14 | import "react-toastify/dist/ReactToastify.css";
15 |
16 | const Shipping = ({ history }) => {
17 | const dispatch = useDispatch();
18 |
19 | const { shippingInfo } = useSelector((state) => state.cart);
20 |
21 | const [address, setAddress] = useState(shippingInfo.address);
22 | // eslint-disable-next-line
23 | const [state, setState] = useState(shippingInfo.state);
24 | const [country, setCountry] = useState(shippingInfo.country);
25 | // eslint-disable-next-line
26 | const [phoneNo, setPhoneNo] = useState(shippingInfo.phoneNo);
27 |
28 | const shippingSubmit = (e) => {
29 | e.preventDefault();
30 |
31 | if (phoneNo.length < 11 || phoneNo.length > 11) {
32 | toast.error("Phone Number should be 11digits");
33 | return;
34 | }
35 | dispatch(saveShippingInfo({ address, state, country, phoneNo }));
36 | history.push("/order/confirm");
37 | };
38 |
39 | return (
40 | <>
41 |
42 |
43 |
44 |
45 |
46 |
47 |
Shipping Details
48 |
49 |
122 |
123 |
124 |
135 |
136 | >
137 | );
138 | };
139 |
140 | export default Shipping;
141 |
--------------------------------------------------------------------------------
/frontend/src/component/cart/Success.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CheckCircleIcon from "@material-ui/icons/CheckCircle";
3 | import "./orderSuccess.css";
4 | import { Typography } from "@material-ui/core";
5 | import { Link } from "react-router-dom";
6 |
7 | const Success = () => {
8 | return (
9 |
10 |
11 |
12 | Your Order has been Placed successfully
13 | View Orders
14 |
15 | );
16 | };
17 |
18 | export default Success;
--------------------------------------------------------------------------------
/frontend/src/component/cart/orderSuccess.css:
--------------------------------------------------------------------------------
1 | .orderSuccess {
2 | margin: auto;
3 | text-align: center;
4 | padding: 10vmax;
5 | height: 50vh;
6 | display: flex;
7 | flex-direction: column;
8 | justify-content: center;
9 | align-items: center;
10 | }
11 | .orderSuccess > svg {
12 | font-size: 7vmax;
13 | color: #3BB77E;
14 | }
15 | .orderSuccess > p {
16 | font-size: 2vmax;
17 | }
18 | .orderSuccess > a {
19 | background-color: #3BB77E;
20 | color: white;
21 | border: none;
22 | padding: 1vmax 3vmax;
23 | cursor: pointer;
24 | font: 400 1vmax "Roboto";
25 | text-decoration: none;
26 | margin: 2vmax;
27 | }
28 |
29 | @media screen and (max-width: 600px) {
30 | .orderSuccess > a {
31 | padding: 3vw 6vw;
32 | font: 400 4vw "Roboto";
33 | margin: 2vmax;
34 | }
35 |
36 | .orderSuccess > svg {
37 | font-size: 20vw;
38 | }
39 | .orderSuccess > p {
40 | margin: 2vmax;
41 | font-size: 5vw;
42 | }
43 | }
--------------------------------------------------------------------------------
/frontend/src/component/cart/payment.css:
--------------------------------------------------------------------------------
1 | .paymentContainer {
2 | display: grid;
3 | place-items: center;
4 | background-color: rgb(255, 255, 255);
5 | height: 65vh;
6 | margin: 2vmax;
7 | }
8 |
9 | .paymentForm {
10 | width: 22%;
11 | height: 100%;
12 | }
13 |
14 | .paymentForm > p {
15 | font: 400 2vmax "Roboto";
16 | color: rgba(0, 0, 0, 0.753);
17 | border-bottom: 1px solid rgba(0, 0, 0, 0.13);
18 | padding: 1vmax 0;
19 | text-align: center;
20 | width: 50%;
21 | margin: auto;
22 | }
23 |
24 | .paymentForm > div {
25 | display: flex;
26 | align-items: center;
27 | margin: 2vmax 0;
28 | }
29 |
30 | .paymentInput {
31 | padding: 1vmax 4vmax;
32 | padding-right: 1vmax;
33 | width: 100%;
34 | box-sizing: border-box;
35 | border: 1px solid rgba(0, 0, 0, 0.267);
36 | border-radius: 4px;
37 | outline: none;
38 | }
39 |
40 | .paymentForm > div > svg {
41 | position: absolute;
42 | transform: translateX(1vmax);
43 | font-size: 1.6vmax;
44 | color: rgba(0, 0, 0, 0.623);
45 | }
46 |
47 | .paymentFormBtn {
48 | border: none;
49 | background-color: #3BB77E;
50 | color: white;
51 | font: 300 0.9vmax "Roboto";
52 | width: 100%;
53 | padding: 0.8vmax;
54 | cursor: pointer;
55 | transition: all 0.5s;
56 | outline: none;
57 | }
58 |
59 | .paymentFormBtn:hover {
60 | background-color: #3BB77E;
61 | }
62 |
63 | @media screen and (max-width: 600px) {
64 | .paymentForm {
65 | width: 90%;
66 | }
67 |
68 | .paymentForm > p {
69 | font: 400 8vw "Roboto";
70 | padding: 4vw 0;
71 | width: 60%;
72 | }
73 |
74 | .paymentForm > div {
75 | margin: 10vw 0;
76 | }
77 |
78 | .paymentInput {
79 | padding: 4vw 10vw;
80 | }
81 |
82 | .paymentForm > div > svg {
83 | font-size: 6vw;
84 | }
85 |
86 | .paymentFormBtn {
87 | font: 300 4vw "Roboto";
88 | padding: 4vw;
89 | }
90 | }
--------------------------------------------------------------------------------
/frontend/src/component/user/EditProfile.jsx:
--------------------------------------------------------------------------------
1 | import React, {useState, useEffect,Fragment } from "react";
2 | import "./EditProfile.css";
3 | import MailOutlineIcon from "@material-ui/icons/MailOutline";
4 | import FaceIcon from "@material-ui/icons/Face";
5 | import { useDispatch, useSelector } from "react-redux";
6 | import { clearErrors, loadUser, updateProfile } from "../../actions/userAction";
7 | import Loading from "../../more/Loader";
8 | import MetaData from "../../more/Metadata";
9 | import { UPDATE_PROFILE_RESET } from "../../constans/userContans";
10 | import { ToastContainer, toast } from 'react-toastify';
11 |
12 | const EditProfile = ({history}) => {
13 | const dispatch = useDispatch();
14 |
15 | const { user } = useSelector(
16 | (state) => state.user
17 | );
18 |
19 | const {error, isUpdated, loading } = useSelector((state) => state.profile);
20 |
21 | const [name, setName] = useState("");
22 | const [email, setEmail] = useState("");
23 | const [avatar, setAvatar] = useState();
24 | const [avatarPreview, setAvatarPreview] = useState("/profile.png");
25 |
26 |
27 | const updateProfileSubmit = (e) => {
28 | e.preventDefault();
29 |
30 | const myForm = new FormData();
31 |
32 | myForm.set("name", name);
33 | myForm.set("email", email);
34 | myForm.set("avatar", avatar);
35 | dispatch(updateProfile(myForm));
36 | };
37 |
38 | console.log(avatar);
39 |
40 | const updateProfileDataChange = (e) => {
41 | const reader = new FileReader();
42 |
43 | reader.onload = () => {
44 | if (reader.readyState === 2) {
45 | setAvatarPreview(reader.result);
46 | setAvatar(reader.result);
47 | }
48 | }
49 | reader.readAsDataURL(e.target.files[0]);
50 | };
51 |
52 | useEffect(() => {
53 | if(user){
54 | setName(user.name);
55 | setEmail(user.email);
56 | setAvatarPreview(user.avatar.url)
57 | }
58 |
59 | if (error) {
60 | toast.error(error);
61 | dispatch(clearErrors());
62 | }
63 |
64 | if (isUpdated) {
65 | toast.success("Profile updated successfully");
66 | dispatch(loadUser());
67 |
68 | history.push("/me");
69 |
70 | dispatch({
71 | type: UPDATE_PROFILE_RESET,
72 | })
73 | }
74 | }, [dispatch, error, alert, history, isUpdated,user]);
75 |
76 |
77 | return (
78 | <>
79 | {loading ? () : (
80 | <>
81 |
82 |
83 |
84 |
Update Profile
85 |
86 |
129 |
130 |
131 | >
132 | )}
133 |
144 | >
145 | )
146 | }
147 |
148 | export default EditProfile
149 |
--------------------------------------------------------------------------------
/frontend/src/component/user/ForgotPassword.css:
--------------------------------------------------------------------------------
1 | .forgotPasswordContainer {
2 | width: 100vw;
3 | height: 100vh;
4 | max-width: 100%;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | background-color: rgb(231, 231, 231);
9 | position: fixed;
10 | top: 0%;
11 | left: 0;
12 | }
13 |
14 | .forgotPasswordBox {
15 | background-color: white;
16 | width: 25vw;
17 | height: 40vh;
18 | box-sizing: border-box;
19 | overflow: hidden;
20 | }
21 |
22 | .forgotPasswordHeading {
23 | text-align: center;
24 | color: rgba(0, 0, 0, 0.664);
25 | font: 400 1.3vmax "Roboto";
26 | padding: 1.3vmax;
27 | border-bottom: 1px solid rgba(0, 0, 0, 0.205);
28 | width: 50%;
29 | margin: auto;
30 | }
31 |
32 | .forgotPasswordForm {
33 | display: flex;
34 | flex-direction: column;
35 | align-items: center;
36 | margin: auto;
37 | padding: 2vmax;
38 | justify-content: space-evenly;
39 | height: 70%;
40 | transition: all 0.5s;
41 | }
42 |
43 | .forgotPasswordForm > div {
44 | display: flex;
45 | width: 100%;
46 | align-items: center;
47 | }
48 |
49 | .forgotPasswordForm > div > input {
50 | padding: 1vmax 4vmax;
51 | padding-right: 1vmax;
52 | width: 100%;
53 | box-sizing: border-box;
54 | border: 1px solid rgba(0, 0, 0, 0.267);
55 | border-radius: 4px;
56 | font: 300 0.9vmax cursive;
57 | outline: none;
58 | }
59 |
60 | .forgotPasswordForm > div > svg {
61 | position: absolute;
62 | transform: translateX(1vmax);
63 | font-size: 1.6vmax;
64 | color: rgba(0, 0, 0, 0.623);
65 | }
66 |
67 | .forgotPasswordBtn {
68 | border: none;
69 | background-color: #4CABDB;
70 | color: white;
71 | font: 300 0.9vmax "Roboto";
72 | width: 100%;
73 | padding: 0.8vmax;
74 | cursor: pointer;
75 | transition: all 0.5s;
76 | border-radius: 4px;
77 | outline: none;
78 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.219);
79 | }
80 |
81 | .forgotPasswordBtn:hover {
82 | background-color: #4CABDB;
83 | }
84 |
85 | @media screen and (max-width: 600px) {
86 | .forgotPasswordContainer {
87 | background-color: white;
88 | }
89 | .forgotPasswordBox {
90 | width: 100vw;
91 | height: 95vh;
92 | }
93 |
94 | .forgotPasswordForm {
95 | padding: 5vmax;
96 | }
97 |
98 | .forgotPasswordForm > div > input {
99 | padding: 2.5vmax 5vmax;
100 | font: 300 1.7vmax cursive;
101 | }
102 |
103 | .forgotPasswordForm > div > svg {
104 | font-size: 2.8vmax;
105 | }
106 |
107 | .forgotPasswordBtn {
108 | font: 300 1.9vmax "Roboto";
109 | padding: 1.8vmax;
110 | }
111 | }
--------------------------------------------------------------------------------
/frontend/src/component/user/ForgotPassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useEffect } from "react";
2 | import "./ForgotPassword.css";
3 | import Loading from "../../more/Loader";
4 | import MailOutlineIcon from "@material-ui/icons/MailOutline";
5 | import { useDispatch, useSelector } from "react-redux";
6 | import { clearErrors, forgotPassword } from "../../actions/userAction";
7 | import { ToastContainer, toast } from 'react-toastify';
8 | import MetaData from "../../more/Metadata";
9 |
10 | const ForgotPassword = () => {
11 | const dispatch = useDispatch();
12 |
13 | const { error, message, loading } = useSelector(
14 | (state) => state.forgotPassword
15 | );
16 |
17 | const [email, setEmail] = useState("");
18 |
19 | const forgotPasswordSubmit = (e) => {
20 | e.preventDefault();
21 |
22 | const myForm = new FormData();
23 |
24 | myForm.set("email", email);
25 | dispatch(forgotPassword(myForm));
26 | };
27 |
28 | useEffect(() => {
29 | if (error) {
30 | toast.error(error);
31 | dispatch(clearErrors());
32 | }
33 |
34 | if (message) {
35 | toast.success(message);
36 | }
37 | }, [dispatch, error, message]);
38 |
39 | return (
40 |
41 | {loading ? (
42 |
43 | ) : (
44 |
45 |
46 |
47 |
48 |
Forgot Password
49 |
50 |
72 |
73 |
74 |
75 | )}
76 |
87 |
88 | );
89 | };
90 |
91 | export default ForgotPassword
92 |
--------------------------------------------------------------------------------
/frontend/src/component/user/MyOrder.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect } from "react";
2 | import { DataGrid } from "@material-ui/data-grid";
3 | import "./myOrders.css";
4 | import { useSelector, useDispatch } from "react-redux";
5 | import { clearErrors, myOrders } from "../../actions/OrderAction";
6 | import { Link } from "react-router-dom";
7 | import MetaData from "../../more/Metadata";
8 | import LaunchIcon from "@material-ui/icons/Launch";
9 | import Loading from "../../more/Loader";
10 | import BottomTab from "../../more/BottomTab";
11 | import { ToastContainer, toast } from "react-toastify";
12 | import "react-toastify/dist/ReactToastify.css";
13 |
14 | const MyOrder = () => {
15 | const dispatch = useDispatch();
16 |
17 | const { loading, error, orders } = useSelector((state) => state.myOrder);
18 |
19 | const { user } = useSelector((state) => state.user);
20 |
21 | const columns = [
22 | { field: "id", headerName: "Order ID", minWidth: 300, flex: 1 },
23 |
24 | {
25 | field: "status",
26 | headerName: "Status",
27 | minWidth: 150,
28 | flex: 0.5,
29 | cellClassName: (params) => {
30 | return params.getValue(params.id, "status") === "Delivered"
31 | ? "greenColor"
32 | : "redColor";
33 | },
34 | },
35 | {
36 | field: "itemsQty",
37 | headerName: "Items Qty",
38 | type: "number",
39 | minWidth: 150,
40 | flex: 0.3,
41 | },
42 |
43 | {
44 | field: "amount",
45 | headerName: "Amount",
46 | type: "number",
47 | minWidth: 270,
48 | flex: 0.5,
49 | },
50 |
51 | {
52 | field: "actions",
53 | flex: 0.3,
54 | headerName: "Actions",
55 | minWidth: 150,
56 | type: "number",
57 | sortable: false,
58 | renderCell: (params) => {
59 | return (
60 |
61 |
62 |
63 | );
64 | },
65 | },
66 | ];
67 | const rows = [];
68 |
69 | orders &&
70 | orders.forEach((item, index) => {
71 | rows.push({
72 | itemsQty: item.orderItems.length === 0 ? 1 : item.orderItems.length,
73 | id: item._id,
74 | status: item.orderStatus,
75 | amount: item.totalPrice,
76 | });
77 | });
78 |
79 | useEffect(() => {
80 | if (error) {
81 | toast.error(error);
82 | dispatch(clearErrors());
83 | }
84 |
85 | dispatch(myOrders());
86 | }, [dispatch, alert, error]);
87 |
88 | return (
89 |
90 |
91 |
92 | {loading ? (
93 |
94 | ) : (
95 |
96 |
104 |
105 | )}
106 |
107 |
118 |
119 | );
120 | };
121 |
122 | export default MyOrder;
123 |
--------------------------------------------------------------------------------
/frontend/src/component/user/MyOrderDetails.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import "./orderDetails.css";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import MetaData from "../../more/Metadata";
5 | import { Link } from "react-router-dom";
6 | import { Typography } from "@material-ui/core";
7 | import { getOrderDetails, clearErrors } from "../../actions/OrderAction";
8 | import { useAlert } from "react-alert";
9 | import Loading from "../../more/Loader";
10 | import BottomTab from "../../more/BottomTab";
11 |
12 | const MyOrderDetails = ({ match }) => {
13 | const { order, error, loading } = useSelector((state) => state.myOrderDetails);
14 |
15 | const dispatch = useDispatch();
16 |
17 | useEffect(() => {
18 | if (error) {
19 | alert.error(error);
20 | dispatch(clearErrors());
21 | }
22 |
23 | dispatch(getOrderDetails(match.params.id));
24 | }, [dispatch, alert, error, match.params.id]);
25 | return (
26 | <>
27 | {loading ? (
28 |
29 | ) : (
30 | <>
31 |
32 |
33 |
34 |
35 | Order #{order && order._id}
36 |
37 |
Shipping Info
38 |
39 |
40 |
Name:
41 |
{order.user && order.user.name}
42 |
43 |
44 |
Phone:
45 |
46 | {order.shippingInfo && order.shippingInfo.phoneNo}
47 |
48 |
49 |
50 |
Address:
51 |
52 | {order.shippingInfo &&
53 | `${order.shippingInfo.address}, ${order.shippingInfo.state}`}
54 |
55 |
56 |
57 |
Payment
58 |
59 |
60 |
67 |
68 |
71 | PAID
72 |
73 |
74 |
75 |
76 |
Amount:
77 |
$ {order.totalPrice && order.totalPrice}
78 |
79 |
80 |
81 |
Order Status
82 |
83 |
84 |
91 | {order.orderStatus && order.orderStatus}
92 |
93 |
94 |
95 |
96 |
97 |
98 |
Order Items:
99 |
100 |
101 | {order.orderItems &&
102 | order.orderItems.map((item) => (
103 |
104 |

105 |
106 | {item.name}
107 | {" "}
108 |
109 | {item.quantity} X ${item.price} ={" "}
110 | ${item.price * item.quantity}
111 |
112 |
113 | ))}
114 |
115 |
116 |
117 |
118 |
119 | >
120 | )}
121 |
122 | >
123 | );
124 | };
125 |
126 | export default MyOrderDetails;
--------------------------------------------------------------------------------
/frontend/src/component/user/Profile.css:
--------------------------------------------------------------------------------
1 | .profileContainer {
2 | width: 100%;
3 | display: flex;
4 | align-items: center;
5 | justify-content: center;
6 | padding: 1vmax 0px;
7 | }
8 |
9 | img.profile__img {
10 | border-radius: 100%;
11 | height: 150px;
12 | width: 150px;
13 | object-fit: cover;
14 | border: 4px solid #2f6ddf;
15 | }
16 | .edit__profile{
17 | color: #0a8ef7;
18 | font-family: "Poppins";
19 | width: 100%;
20 | text-align: end;
21 | padding: .5vmax 0px;
22 | }
23 | .information {
24 | display: flex;
25 | flex-direction: column;
26 | align-items: center;
27 | justify-content: center;
28 | }
29 | .info {
30 | display: flex;
31 | padding: 5px 0px;
32 | }
33 | .change__info {
34 | display: flex;
35 | flex-direction: column;
36 | padding: 2vmax 0;
37 | }
38 | a.settings {
39 | color: #1c6068;
40 | font-family: 'Poppins';
41 | font-weight: 600;
42 | }
--------------------------------------------------------------------------------
/frontend/src/component/user/Profile.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { useSelector } from "react-redux";
3 | import { Link } from 'react-router-dom'
4 | import Footer from "../../Footer";
5 | import Header from "../Home/Header";
6 | import MetaData from "../../more/Metadata";
7 | import Loading from "../../more/Loader";
8 | import "./Profile.css";
9 | import BottomTab from "../../more/BottomTab";
10 |
11 | const Profile = ({history }) => {
12 |
13 | const { user, loading, isAuthenticated } = useSelector((state) => state.user);
14 |
15 | useEffect(() => {
16 | if (isAuthenticated === false) {
17 | history.push("/login");
18 | }
19 | }, [history, isAuthenticated]);
20 |
21 | return (
22 | <>
23 | {loading ? ():(
24 | <>
25 |
26 |
27 |
28 |
29 |
35 |
My Profile
39 |

40 |
Edit Profile
41 |
42 |
43 |
44 |
45 |
46 |
Full Name:
49 |
{user.name}
50 |
51 |
52 |
Email:
55 |
{user.email}
56 |
57 |
58 |
Joined On:
61 |
{String(user.createdAt).substr(0,10)}
62 |
63 |
64 |
65 | My Orders
66 | Change Password
67 |
68 |
69 |
70 |
71 |
72 |
73 | >
74 | )}
75 | >
76 | )
77 | }
78 |
79 | export default Profile
80 |
--------------------------------------------------------------------------------
/frontend/src/component/user/ResetPassword.css:
--------------------------------------------------------------------------------
1 | .resetPasswordContainer {
2 | width: 100vw;
3 | height: 100vh;
4 | max-width: 100%;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | background-color: rgb(231, 231, 231);
9 | position: fixed;
10 | top: 0%;
11 | left: 0;
12 | }
13 |
14 | .resetPasswordBox {
15 | background-color: white;
16 | width: 25vw;
17 | height: 70vh;
18 | box-sizing: border-box;
19 | overflow: hidden;
20 | }
21 |
22 | .resetPasswordHeading {
23 | text-align: center;
24 | color: rgba(0, 0, 0, 0.664);
25 | font: 400 1.3vmax "Roboto";
26 | padding: 1.3vmax;
27 | border-bottom: 1px solid rgba(0, 0, 0, 0.205);
28 | width: 50%;
29 | margin: auto;
30 | }
31 |
32 | .resetPasswordForm {
33 | display: flex;
34 | flex-direction: column;
35 | align-items: center;
36 | margin: auto;
37 | padding: 2vmax;
38 | justify-content: space-evenly;
39 | height: 70%;
40 | transition: all 0.5s;
41 | }
42 |
43 | .resetPasswordForm > div {
44 | display: flex;
45 | width: 100%;
46 | align-items: center;
47 | }
48 |
49 | .resetPasswordForm > div > input {
50 | padding: 1vmax 4vmax;
51 | padding-right: 1vmax;
52 | width: 100%;
53 | box-sizing: border-box;
54 | border: 1px solid rgba(0, 0, 0, 0.267);
55 | border-radius: 4px;
56 | font: 300 0.9vmax cursive;
57 | outline: none;
58 | }
59 |
60 | .resetPasswordForm > div > svg {
61 | position: absolute;
62 | transform: translateX(1vmax);
63 | font-size: 1.6vmax;
64 | color: rgba(0, 0, 0, 0.623);
65 | }
66 |
67 | .resetPasswordBtn {
68 | border: none;
69 | background-color: #4CABDB;
70 | color: white;
71 | font: 300 0.9vmax "Roboto";
72 | width: 100%;
73 | padding: 0.8vmax;
74 | cursor: pointer;
75 | transition: all 0.5s;
76 | border-radius: 4px;
77 | outline: none;
78 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.219);
79 | }
80 |
81 | .resetPasswordBtn:hover {
82 | background-color: #4CABDB;
83 | }
84 |
85 | @media screen and (max-width: 600px) {
86 | .resetPasswordContainer {
87 | background-color: white;
88 | }
89 | .resetPasswordBox {
90 | width: 100vw;
91 | height: 95vh;
92 | }
93 |
94 | .resetPasswordForm {
95 | padding: 5vmax;
96 | }
97 |
98 | .resetPasswordForm > div > input {
99 | padding: 2.5vmax 5vmax;
100 | font: 300 1.7vmax cursive;
101 | }
102 |
103 | .resetPasswordForm > div > svg {
104 | font-size: 2.8vmax;
105 | }
106 |
107 | .resetPasswordBtn {
108 | font: 300 1.9vmax "Roboto";
109 | padding: 1.8vmax;
110 | }
111 | }
--------------------------------------------------------------------------------
/frontend/src/component/user/ResetPassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useEffect } from "react";
2 | import "./ResetPassword.css";
3 | import Loading from "../../more/Loader";
4 | import { useDispatch, useSelector } from "react-redux";
5 | import { clearErrors, resetPassword } from "../../actions/userAction";
6 | import MetaData from "../../more/Metadata";
7 | import LockOpenIcon from "@material-ui/icons/LockOpen";
8 | import LockIcon from "@material-ui/icons/Lock";
9 | import { ToastContainer, toast } from 'react-toastify';
10 |
11 | const ResetPassword = ({ history, match }) => {
12 | const dispatch = useDispatch();
13 |
14 | const { isAuthenticated } = useSelector((state) => state.user);
15 |
16 | const { error, success, loading } = useSelector(
17 | (state) => state.forgotPassword
18 | );
19 |
20 | const [password, setPassword] = useState("");
21 | const [confirmPassword, setConfirmPassword] = useState("");
22 |
23 | const resetPasswordSubmit = (e) => {
24 | e.preventDefault();
25 |
26 | const myForm = new FormData();
27 |
28 | myForm.set("password", password);
29 | myForm.set("confirmPassword", confirmPassword);
30 |
31 | dispatch(resetPassword(match.params.token, myForm));
32 | };
33 |
34 | useEffect(() => {
35 | if (error) {
36 | toast.error(error);
37 | dispatch(clearErrors());
38 | }
39 |
40 | if (success) {
41 | toast.success("Password Updated Successfully");
42 |
43 | history.push("/login");
44 | }
45 | }, [dispatch, error, history, success]);
46 |
47 | return (
48 |
49 | {loading ? (
50 |
51 | ) : (
52 |
53 |
54 |
55 |
56 |
Update Profile
57 |
58 |
88 |
89 |
90 |
91 | )}
92 |
103 |
104 | );
105 | };
106 |
107 | export default ResetPassword;
--------------------------------------------------------------------------------
/frontend/src/component/user/UpdatePassword.css:
--------------------------------------------------------------------------------
1 | .updatePasswordContainer {
2 | width: 100vw;
3 | height: 100vh;
4 | max-width: 100%;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | background-color: rgb(231, 231, 231);
9 | position: fixed;
10 | top: 0%;
11 | left: 0;
12 | }
13 |
14 | .updatePasswordBox {
15 | background-color: white;
16 | width: 25vw;
17 | height: 70vh;
18 | box-sizing: border-box;
19 | overflow: hidden;
20 | }
21 |
22 | .updatePasswordHeading {
23 | text-align: center;
24 | color: rgba(0, 0, 0, 0.664);
25 | font: 400 1.3vmax "Roboto";
26 | padding: 1.3vmax;
27 | border-bottom: 1px solid rgba(0, 0, 0, 0.205);
28 | width: 50%;
29 | margin: auto;
30 | }
31 |
32 | .updatePasswordForm {
33 | display: flex;
34 | flex-direction: column;
35 | align-items: center;
36 | margin: auto;
37 | padding: 2vmax;
38 | justify-content: space-evenly;
39 | height: 70%;
40 | transition: all 0.5s;
41 | }
42 |
43 | .updatePasswordForm > div {
44 | display: flex;
45 | width: 100%;
46 | align-items: center;
47 | }
48 |
49 | .updatePasswordForm > div > input {
50 | padding: 1vmax 4vmax;
51 | padding-right: 1vmax;
52 | width: 100%;
53 | box-sizing: border-box;
54 | border: 1px solid rgba(0, 0, 0, 0.267);
55 | border-radius: 4px;
56 | font: 300 0.9vmax cursive;
57 | outline: none;
58 | }
59 |
60 | .updatePasswordForm > div > svg {
61 | position: absolute;
62 | transform: translateX(1vmax);
63 | font-size: 1.6vmax;
64 | color: rgba(0, 0, 0, 0.623);
65 | }
66 |
67 | .updatePasswordBtn {
68 | border: none;
69 | background-color: #3BB77E;
70 | color: white;
71 | font: 300 0.9vmax "Roboto";
72 | width: 100%;
73 | padding: 0.8vmax;
74 | cursor: pointer;
75 | transition: all 0.5s;
76 | border-radius: 4px;
77 | outline: none;
78 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.219);
79 | }
80 |
81 | .updatePasswordBtn:hover {
82 | background-color: #3BB77E;
83 | }
84 |
85 | @media screen and (max-width: 600px) {
86 | .updatePasswordContainer {
87 | background-color: white;
88 | }
89 | .updatePasswordBox {
90 | width: 100vw;
91 | height: 95vh;
92 | }
93 |
94 | .updatePasswordForm {
95 | padding: 5vmax;
96 | }
97 |
98 | .updatePasswordForm > div > input {
99 | padding: 2.5vmax 5vmax;
100 | font: 300 1.7vmax cursive;
101 | }
102 |
103 | .updatePasswordForm > div > svg {
104 | font-size: 2.8vmax;
105 | }
106 |
107 | .updatePasswordBtn {
108 | font: 300 1.9vmax "Roboto";
109 | padding: 1.8vmax;
110 | }
111 | }
--------------------------------------------------------------------------------
/frontend/src/component/user/UpdatePassword.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useEffect } from "react";
2 | import "./UpdatePassword.css";
3 | import Loading from "../../more/Loader";
4 | import { useDispatch, useSelector } from "react-redux";
5 | // import { useAlert } from "react-alert";
6 | import MetaData from "../../more/Metadata";
7 | import LockOpenIcon from "@material-ui/icons/LockOpen";
8 | import LockIcon from "@material-ui/icons/Lock";
9 | import VpnKeyIcon from "@material-ui/icons/VpnKey";
10 | import { clearErrors, updatePassword } from "../../actions/userAction";
11 | import { UPDATE_PASSWORD_RESET } from "../../constans/userContans";
12 |
13 | const UpdatePassword = ({ history }) => {
14 |
15 | const dispatch = useDispatch();
16 | // const alert = useAlert();
17 |
18 | const { error, isUpdated, loading } = useSelector((state) => state.profile);
19 |
20 | const [oldPassword, setOldPassword] = useState("");
21 | const [newPassword, setNewPassword] = useState("");
22 | const [confirmPassword, setConfirmPassword] = useState("");
23 |
24 | const updatePasswordSubmit = (e) => {
25 | e.preventDefault();
26 |
27 | const myForm = new FormData();
28 |
29 | myForm.set("oldPassword", oldPassword);
30 | myForm.set("newPassword", newPassword);
31 | myForm.set("confirmPassword", confirmPassword);
32 |
33 | dispatch(updatePassword(myForm));
34 | };
35 |
36 | useEffect(() => {
37 | if (error) {
38 | alert(error);
39 | dispatch(clearErrors());
40 | }
41 |
42 | if (isUpdated) {
43 | alert("Profile Updated Successfully");
44 | history.push("/me");
45 | dispatch({
46 | type: UPDATE_PASSWORD_RESET,
47 | });
48 | }
49 | }, [dispatch, error, alert, history, isUpdated]);
50 |
51 | return (
52 | <>
53 | {loading ? (
54 |
55 | ) : (
56 | <>
57 |
58 |
59 |
60 |
Update Profile
61 |
62 |
103 |
104 |
105 | >
106 | )}
107 | {/* */}
108 | >
109 | );
110 | };
111 |
112 | export default UpdatePassword
113 |
--------------------------------------------------------------------------------
/frontend/src/component/user/myOrders.css:
--------------------------------------------------------------------------------
1 | .myOrdersPage {
2 | width: 100vw;
3 | max-width: 100%;
4 | padding: 0 7vmax;
5 | box-sizing: border-box;
6 | background-color: rgb(235, 235, 235);
7 | position: fixed;
8 | top: 0;
9 | left: 0;
10 | height: 100vh;
11 | display: flex;
12 | flex-direction: column;
13 | }
14 |
15 | #myOrdersHeading {
16 | text-align: center;
17 | font: 400 1.2vmax "Roboto";
18 | padding: 0.5vmax;
19 | box-sizing: border-box;
20 | color: rgb(255, 255, 255);
21 | transition: all 0.5s;
22 | background-color: rgb(44, 44, 44);
23 | }
24 |
25 | .myOrdersTable {
26 | background-color: white;
27 | }
28 |
29 | .myOrdersTable div {
30 | font: 300 1vmax "Roboto";
31 | color: rgba(0, 0, 0, 0.678);
32 | border: none;
33 | }
34 |
35 | .myOrdersTable a {
36 | color: rgba(0, 0, 0, 0.527);
37 | transition: all 0.5s;
38 | }
39 |
40 | .myOrdersTable a:hover {
41 | color: #3BB77E;
42 | }
43 |
44 | .MuiDataGrid-columnHeader {
45 | background-color: #3BB77E;
46 | padding: 1vmax !important;
47 | }
48 |
49 | .MuiDataGrid-columnHeader div {
50 | color: rgb(255, 255, 255);
51 | font: 500 1.1vmax "Roboto" !important;
52 | }
53 |
54 | .MuiDataGrid-iconSeparator {
55 | display: none !important;
56 | }
57 |
58 | @media screen and (max-width: 600px) {
59 | .myOrdersPage {
60 | padding: 0;
61 | height: 93vh;
62 | }
63 |
64 | #myOrdersHeading {
65 | font: 400 2.2vmax "Roboto";
66 | padding: 4vw;
67 | }
68 |
69 | .myOrdersTable div {
70 | font: 300 4vw "Roboto";
71 | }
72 |
73 | .MuiDataGrid-columnHeader {
74 | padding: 20px !important;
75 | }
76 |
77 | .MuiDataGrid-columnHeader div {
78 | font: 500 5vw "Roboto" !important;
79 | }
80 | }
--------------------------------------------------------------------------------
/frontend/src/component/user/orderDetails.css:
--------------------------------------------------------------------------------
1 | .orderDetailsPage {
2 | background-color: white;
3 | }
4 |
5 | .orderDetailsContainer > h1 {
6 | font: 300 3vmax "Roboto";
7 | margin: 4vmax 0;
8 | color: #3BB77E;
9 | }
10 |
11 | .orderDetailsContainer {
12 | padding: 5vmax;
13 | padding-bottom: 0%;
14 | }
15 |
16 | .orderDetailsContainer > p {
17 | font: 400 1.8vmax "Roboto";
18 | }
19 |
20 | .orderDetailsContainerBox,
21 | .orderDetailsCartItemsContainer {
22 | margin: 2vmax;
23 | }
24 |
25 | .orderDetailsContainerBox > div {
26 | display: flex;
27 | margin: 1vmax 0;
28 | }
29 |
30 | .orderDetailsContainerBox > div > p {
31 | font: 400 1vmax "Roboto";
32 | color: black;
33 | }
34 | .orderDetailsContainerBox > div > span {
35 | margin: 0 1vmax;
36 | font: 100 1vmax "Roboto";
37 | color: #575757;
38 | }
39 |
40 | .orderDetailsCartItems > p {
41 | font: 400 1.8vmax "Roboto";
42 | }
43 |
44 | .orderDetailsCartItems {
45 | padding: 2vmax 5vmax;
46 | border-top: 1px solid rgba(0, 0, 0, 0.164);
47 | }
48 |
49 | .orderDetailsCartItemsContainer > div {
50 | display: flex;
51 | font: 400 1vmax "Roboto";
52 | align-items: center;
53 | margin: 2vmax 0;
54 | }
55 |
56 | .orderDetailsCartItemsContainer > div > img {
57 | width: 3vmax;
58 | }
59 |
60 | .orderDetailsCartItemsContainer > div > a {
61 | color: #575757;
62 | margin: 0 2vmax;
63 | width: 60%;
64 | text-decoration: none;
65 | }
66 |
67 | .orderDetailsCartItemsContainer > div > span {
68 | font: 100 1vmax "Roboto";
69 | color: #5e5e5e;
70 | }
71 |
72 | @media screen and (max-width: 600px) {
73 | .orderDetailsContainer > p {
74 | font: 400 6vw "Roboto";
75 | }
76 |
77 | .orderDetailsContainerBox > div {
78 | margin: 6vw 0;
79 | }
80 |
81 | .orderDetailsContainerBox > div > p {
82 | font: 400 4vw "Roboto";
83 | }
84 | .orderDetailsContainerBox > div > span {
85 | font: 100 4vw "Roboto";
86 | }
87 |
88 | .orderDetailsCartItems > p {
89 | font: 400 6vw "Roboto";
90 | }
91 |
92 | .orderDetailsCartItemsContainer > div {
93 | font: 400 4vw "Roboto";
94 | margin: 4vw 0;
95 | }
96 |
97 | .orderDetailsCartItemsContainer > div > img {
98 | width: 10vw;
99 | }
100 |
101 | .orderDetailsCartItemsContainer > div > a {
102 | margin: 2vw;
103 | width: 30%;
104 | }
105 |
106 | .orderDetailsCartItemsContainer > div > span {
107 | font: 100 4vw "Roboto";
108 | }
109 | }
--------------------------------------------------------------------------------
/frontend/src/constans/CartConstans.js:
--------------------------------------------------------------------------------
1 | export const ADD_TO_CART = "ADD_TO_CART";
2 |
3 | export const REMOVE_CART_ITEM = "REMOVE_CART_ITEM";
4 |
5 | export const SAVE_SHIPPING_INFO = "SAVE_SHIPPING_INFO";
--------------------------------------------------------------------------------
/frontend/src/constans/FavouriteConstans.js:
--------------------------------------------------------------------------------
1 | export const ADD_TO_FAVOURITE = "ADD_TO_FAVOURITE";
2 |
3 | export const REMOVE_FROM_FAVOURITE = "REMOVE_FROM_FAVOURITE";
4 |
5 |
--------------------------------------------------------------------------------
/frontend/src/constans/OrderConstans.js:
--------------------------------------------------------------------------------
1 | export const CREATE_ORDER_REQUEST = "CREATE_ORDER_REQUEST";
2 | export const CREATE_ORDER_SUCCESS = "CREATE_ORDER_SUCCESS";
3 | export const CREATE_ORDER_FAIL = "CREATE_ORDER_FAIL";
4 |
5 | export const MY_ORDERS_REQUEST = "MY_ORDERS_REQUEST";
6 | export const MY_ORDERS_SUCCESS = "MY_ORDERS_SUCCESS";
7 | export const MY_ORDERS_FAIL = "MY_ORDERS_FAIL";
8 |
9 | export const ALL_ORDERS_REQUEST = "ALL_ORDERS_REQUEST";
10 | export const ALL_ORDERS_SUCCESS = "ALL_ORDERS_SUCCESS";
11 | export const ALL_ORDERS_FAIL = "ALL_ORDERS_FAIL";
12 |
13 | export const UPDATE_ORDER_REQUEST = "UPDATE_ORDER_REQUEST";
14 | export const UPDATE_ORDER_SUCCESS = "UPDATE_ORDER_SUCCESS";
15 | export const UPDATE_ORDER_RESET = "UPDATE_ORDER_RESET";
16 | export const UPDATE_ORDER_FAIL = "UPDATE_ORDER_FAIL";
17 |
18 | export const DELETE_ORDER_REQUEST = "DELETE_ORDER_REQUEST";
19 | export const DELETE_ORDER_SUCCESS = "DELETE_ORDER_SUCCESS";
20 | export const DELETE_ORDER_RESET = "DELETE_ORDER_RESET";
21 | export const DELETE_ORDER_FAIL = "DELETE_ORDER_FAIL";
22 |
23 | export const ORDER_DETAILS_REQUEST = "ORDER_DETAILS_REQUEST";
24 | export const ORDER_DETAILS_SUCCESS = "ORDER_DETAILS_SUCCESS";
25 | export const ORDER_DETAILS_FAIL = "ORDER_DETAILS_FAIL";
26 |
27 | export const CLEAR_ERRORS = "CLEAR_ERRORS";
--------------------------------------------------------------------------------
/frontend/src/constans/ProductConstans.js:
--------------------------------------------------------------------------------
1 | export const ALL_PRODUCT_REQUEST = "ALL_PRODUCT_REQUEST";
2 | export const ALL_PRODUCT_SUCCESS = "ALL_PRODUCT_SUCCESS";
3 | export const ALL_PRODUCT_FAIL = "ALL_PRODUCT_FAIL";
4 |
5 | export const ADMIN_PRODUCT_REQUEST = "ADMIN_PRODUCT_REQUEST";
6 | export const ADMIN_PRODUCT_SUCCESS = "ADMIN_PRODUCT_SUCCESS";
7 | export const ADMIN_PRODUCT_FAIL = "ADMIN_PRODUCT_FAIL";
8 |
9 | export const NEW_PRODUCT_REQUEST = "NEW_PRODUCT_REQUEST";
10 | export const NEW_PRODUCT_SUCCESS = "NEW_PRODUCT_SUCCESS";
11 | export const NEW_PRODUCT_RESET = "NEW_PRODUCT_RESET";
12 | export const NEW_PRODUCT_FAIL = "NEW_PRODUCT_FAIL";
13 |
14 | export const UPDATE_PRODUCT_REQUEST = "UPDATE_PRODUCT_REQUEST";
15 | export const UPDATE_PRODUCT_SUCCESS = "UPDATE_PRODUCT_SUCCESS";
16 | export const UPDATE_PRODUCT_RESET = "UPDATE_PRODUCT_RESET";
17 | export const UPDATE_PRODUCT_FAIL = "UPDATE_PRODUCT_FAIL";
18 |
19 | export const DELETE_PRODUCT_REQUEST = "DELETE_PRODUCT_REQUEST";
20 | export const DELETE_PRODUCT_SUCCESS = "DELETE_PRODUCT_SUCCESS";
21 | export const DELETE_PRODUCT_RESET = "DELETE_PRODUCT_RESET";
22 | export const DELETE_PRODUCT_FAIL = "DELETE_PRODUCT_FAIL";
23 |
24 | export const PRODUCT_DETAILS_REQUEST = "PRODUCT_DETAILS_REQUEST";
25 | export const PRODUCT_DETAILS_SUCCESS = "PRODUCT_DETAILS_SUCCESS";
26 | export const PRODUCT_DETAILS_FAIL = "PRODUCT_DETAILS_FAIL";
27 |
28 | export const NEW_REVIEW_REQUEST = "NEW_REVIEW_REQUEST";
29 | export const NEW_REVIEW_SUCCESS = "NEW_REVIEW_SUCCESS";
30 | export const NEW_REVIEW_RESET = "NEW_REVIEW_RESET";
31 | export const NEW_REVIEW_FAIL = "NEW_REVIEW_FAIL";
32 |
33 | export const ALL_REVIEW_REQUEST = "ALL_REVIEW_REQUEST";
34 | export const ALL_REVIEW_SUCCESS = "ALL_REVIEW_SUCCESS";
35 | export const ALL_REVIEW_FAIL = "ALL_REVIEW_FAIL";
36 |
37 | export const DELETE_REVIEW_REQUEST = "DELETE_REVIEW_REQUEST";
38 | export const DELETE_REVIEW_SUCCESS = "DELETE_REVIEW_SUCCESS";
39 | export const DELETE_REVIEW_RESET = "DELETE_REVIEW_RESET";
40 | export const DELETE_REVIEW_FAIL = "DELETE_REVIEW_FAIL";
41 |
42 | export const CLEAR_ERRORS = "CLEAR_ERRORS";
--------------------------------------------------------------------------------
/frontend/src/constans/userContans.js:
--------------------------------------------------------------------------------
1 | export const LOGIN_REQUEST="LOGIN_REQUEST";
2 |
3 | export const LOGIN_SUCCESS="LOGIN_SUCCESS";
4 |
5 | export const LOGIN_FAIL="LOGIN_FAIL";
6 |
7 |
8 | export const REGISTER_USER_REQUEST="REGISTER_USER_REQUEST";
9 |
10 | export const REGISTER_USER_SUCCESS="REGISTER_USER_SUCCESS";
11 |
12 | export const REGISTER_USER_FAIL="REGISTER_USER_FAIL";
13 |
14 |
15 | export const LOAD_USER_REQUEST="LOAD_USER_REQUEST";
16 |
17 | export const LOAD_USER_SUCCESS="LOAD_USER_SUCCESS";
18 |
19 | export const LOAD_USER_FAIL="LOAD_USER_FAIL";
20 |
21 |
22 | export const LOGOUT_SUCCESS="LOGOUT_SUCCESS";
23 |
24 | export const LOGOUT_FAIL="LOGOUT_FAIL";
25 |
26 |
27 | export const UPDATE_PROFILE_REQUEST="UPDATE_PROFILE_REQUEST";
28 |
29 | export const UPDATE_PROFILE_SUCCESS="UPDATE_PROFILE_SUCCESS";
30 |
31 | export const UPDATE_PROFILE_RESET="UPDATE_PROFILE_RESET";
32 |
33 | export const UPDATE_PROFILE_FAIL="UPDATE_PROFILE_FAIL";
34 |
35 |
36 | export const UPDATE_PASSWORD_REQUEST="UPDATE_PASSWORD_REQUEST";
37 |
38 | export const UPDATE_PASSWORD_SUCCESS="UPDATE_PASSWORD_SUCCESS";
39 |
40 | export const UPDATE_PASSWORD_RESET="UPDATE_PASSWORD_RESET";
41 |
42 | export const UPDATE_PASSWORD_FAIL="UPDATE_PASSWORD_FAIL";
43 |
44 |
45 | export const FORGOT_PASSWORD_REQUEST = "FORGOT_PASSWORD_REQUEST";
46 | export const FORGOT_PASSWORD_SUCCESS = "FORGOT_PASSWORD_SUCCESS";
47 | export const FORGOT_PASSWORD_FAIL = "FORGOT_PASSWORD_FAIL";
48 |
49 | export const RESET_PASSWORD_REQUEST = "RESET_PASSWORD_REQUEST";
50 | export const RESET_PASSWORD_SUCCESS = "RESET_PASSWORD_SUCCESS";
51 | export const RESET_PASSWORD_FAIL = "RESET_PASSWORD_FAIL";
52 |
53 | export const ALL_USERS_REQUEST = "ALL_USERS_REQUEST";
54 | export const ALL_USERS_SUCCESS = "ALL_USERS_SUCCESS";
55 | export const ALL_USERS_FAIL = "ALL_USERS_FAIL";
56 |
57 | export const USER_DETAILS_REQUEST = "USER_DETAILS_REQUEST";
58 | export const USER_DETAILS_SUCCESS = "USER_DETAILS_SUCCESS";
59 | export const USER_DETAILS_FAIL = "USER_DETAILS_FAIL";
60 |
61 | export const UPDATE_USER_REQUEST = "UPDATE_USER_REQUEST";
62 | export const UPDATE_USER_SUCCESS = "UPDATE_USER_SUCCESS";
63 | export const UPDATE_USER_RESET = "UPDATE_USER_RESET";
64 | export const UPDATE_USER_FAIL = "UPDATE_USER_FAIL";
65 |
66 | export const DELETE_USER_REQUEST = "DELETE_USER_REQUEST";
67 | export const DELETE_USER_SUCCESS = "DELETE_USER_SUCCESS";
68 | export const DELETE_USER_FAIL = "DELETE_USER_FAIL";
69 | export const DELETE_USER_RESET = "DELETE_USER_RESET";
70 |
71 |
72 | export const CLEAR_ERRORS="CLEAR_ERRORS";
73 |
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 | import {Provider} from "react-redux";
7 | import store from './store';
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('root')
14 | );
15 |
16 | reportWebVitals();
17 |
--------------------------------------------------------------------------------
/frontend/src/more/BottomTab.jsx:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | import React from "react";
3 | import "./BottomTabs.css";
4 | import { Link } from "react-router-dom";
5 | import { useSelector } from "react-redux";
6 | import HomeIcon from "@material-ui/icons/Home";
7 | import SearchIcon from '@material-ui/icons/Search';
8 | import LocalMallIcon from '@material-ui/icons/LocalMall';
9 | import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder';
10 | import PersonIcon from '@material-ui/icons/Person';
11 | import DehazeIcon from '@material-ui/icons/Dehaze';
12 |
13 | const BottomTab = () => {
14 |
15 | const { cartItems } = useSelector((state) => state.cart);
16 | const { favouriteItems } = useSelector((state) => state.favourite);
17 |
18 | return (
19 | <>
20 |
21 |
22 |
30 |
31 |
32 |
39 |
40 |
41 |
44 |
52 |
53 | {cartItems.length}
68 |
69 |
70 |
71 |
74 |
82 | {favouriteItems.length}
97 |
98 |
99 |
100 |
108 |
109 |
110 |
116 |
117 |
118 | >
119 | );
120 | };
121 |
122 | export default BottomTab;
123 |
--------------------------------------------------------------------------------
/frontend/src/more/BottomTabs.css:
--------------------------------------------------------------------------------
1 |
2 | .bottomOption{
3 | position: fixed;
4 | z-index: 10000;
5 | width: 100%;
6 | height: 60px;
7 | bottom: 0;
8 | display: flex;
9 | align-items: center;
10 | justify-content: space-around;
11 | box-shadow: 0 2px 5px rgb(0 0 0 / 65%);
12 | background-color: #fff;
13 | left: 0;
14 | }
15 | .icon{
16 | display: flex;
17 | }
--------------------------------------------------------------------------------
/frontend/src/more/CommingSoon.css:
--------------------------------------------------------------------------------
1 | .bg{
2 | background-color: #252839;
3 | width: 100%;
4 | height: 100vh;
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | }
9 | .bg span{
10 | font-size: 10vh;
11 | color: #252839;
12 | position: relative;
13 | letter-spacing: 5px;
14 | -webkit-text-stroke: 0.3vw #383d52;
15 | text-transform: uppercase;
16 | padding: 0px 10px;
17 | }
18 | .bg span::before{
19 | content: attr(dataText);
20 | position: absolute;
21 | top: 0;
22 | letter-spacing: 5px;
23 | left: 0;
24 | width: 0%;
25 | height: 100%;
26 | color: #01fe87;
27 | -webkit-text-stroke: 0vw #383d52;
28 | padding: 0px 10px;
29 | overflow: hidden;
30 | animation: animate 6s linear infinite;
31 | }
32 | @keyframes animate {
33 | 0%,10%,100%{
34 | width: 0%;
35 | }
36 | 70%,90%{
37 | width: 100%;
38 | }
39 | }
40 | .circle{
41 | width:35px;
42 | height: 35px;
43 | border-radius: 50%;
44 | background-color: yellow;
45 | position: fixed;
46 | top: 10%;
47 | right: 10%;
48 | border: none;
49 | animation: zoom 3s linear infinite;
50 | }
51 | @keyframes zoom {
52 | 0%,100%{
53 | transform: scale(1.5);
54 | }
55 | 50%{
56 | transform: scale(.7);
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/frontend/src/more/CommingSoon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useSelector } from 'react-redux';
3 | import "./CommingSoon.css";
4 | import BottomTab from './BottomTab';
5 | import Loading from './Loader';
6 | import MetaData from './Metadata';
7 |
8 | const CommingSoon = () => {
9 |
10 | const {loading} = useSelector(
11 | (state) => state.cart
12 | );
13 |
14 | return (
15 | <>
16 | {loading ? () : (
17 | <>
18 |
19 |
20 |
21 |
CommingSoon....
22 |
27 |
28 |
29 |
30 | >
31 | )}
32 | >
33 | )
34 | }
35 |
36 | export default CommingSoon
37 |
--------------------------------------------------------------------------------
/frontend/src/more/Loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import "./Loading.css";
3 |
4 | const Loading = () => {
5 | return (
6 |
12 | )
13 | }
14 |
15 | export default Loading
16 |
--------------------------------------------------------------------------------
/frontend/src/more/Loading.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap');
2 | .loading{
3 | background-color: #000;
4 | height: 100vh;
5 | width: 100%;
6 | display: flex;
7 | align-items: center;
8 | justify-content: center;
9 | flex-direction: column;
10 | }
11 |
12 | label{
13 | position: relative;
14 | height: 125px;
15 | width: 125px;
16 | display: inline-block;
17 | border: 2px solid rgba(255,255,255,0.2);
18 | border-radius: 50%;
19 | border-left-color: #5cb85c;
20 | animation: rotate 1.2s linear infinite;
21 | }
22 | @keyframes rotate {
23 | 50%{
24 | border-left-color: #9b59b6;
25 | }
26 | 75%{
27 | border-left-color: #e67e22;
28 | }
29 | 100%{
30 | transform: rotate(360deg);
31 | }
32 | }
33 | label .check-icon{
34 | display: none;
35 | }
36 | label .check-icon:after{
37 | position: absolute;
38 | content: "";
39 | top: 50%;
40 | left: 28px;
41 | transform: scaleX(-1) rotate(135deg);
42 | height: 56px;
43 | width: 28px;
44 | border-top: 4px solid #5cb85c;
45 | border-right: 4px solid #5cb85c;
46 | transform-origin: left top;
47 | animation: check-icon 0.8s ease;
48 | }
49 | @keyframes check-icon {
50 | 0%{
51 | height: 0;
52 | width: 0;
53 | opacity: 1;
54 | }
55 | 20%{
56 | height: 0;
57 | width: 28px;
58 | opacity: 1;
59 | }
60 | 40%{
61 | height: 56px;
62 | width: 28px;
63 | opacity: 1;
64 | }
65 | 100%{
66 | height: 56px;
67 | width: 28px;
68 | opacity: 1;
69 | }
70 | }
71 | input#check{
72 | display: none;
73 | }
74 | input:checked ~ label .check-icon{
75 | display: block;
76 | }
77 | input:checked ~ label{
78 | animation: none;
79 | border-color: #5cb85c;
80 | transition: border 0.5s ease-out;
81 | }
82 |
--------------------------------------------------------------------------------
/frontend/src/more/Metadata.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from "react-helmet";
3 |
4 | const MetaData = ({title}) => {
5 | return (
6 |
7 | {title}
8 |
9 | )
10 | }
11 |
12 | export default MetaData
13 |
--------------------------------------------------------------------------------
/frontend/src/more/Notfound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 |
4 | const Notfound = () => {
5 | return (
6 |
7 |
17 |
404 Not Found anything in this url
18 | Go Back to Home
26 |
27 |
28 | )
29 | }
30 |
31 | export default Notfound
--------------------------------------------------------------------------------
/frontend/src/more/Rules.css:
--------------------------------------------------------------------------------
1 | ul.rules > li{
2 | font-family: sans-serif;
3 | padding: 10px 0;
4 | list-style: none;
5 | line-height: 1.3;
6 | font-weight: 500;
7 | font-size: 1.2rem;
8 | }
--------------------------------------------------------------------------------
/frontend/src/more/Rules.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import "./Rules.css";
3 | import Header from '../component/Home/Header';
4 | import BottomTab from './BottomTab';
5 | import MetaData from './Metadata';
6 | import Footer from '../Footer';
7 |
8 | const Rules = () => {
9 | return (
10 | <>
11 |
12 |
13 |
19 |
20 | Some Rules:
26 | - 1. You can easily return your product..But you have to give us the delivery charge...
27 | - 2. You have to give delivery charge first for cash on Delivery..In Los Angeles City you have to give 70tk and outside jashore charge will be 120 tk
28 | - 3. You can not buy the outofstock products...
29 | - 4. You can buy any products from us...we are trying to our best for give the best quality of products...
30 | - 5. You can find more new features in our buiseness in very soon...Our developers team always work for your good services...
31 | - 6. At last thanks for visit our website...Have a good day !
32 |
33 |
34 |
35 |
36 | >
37 | )
38 | }
39 |
40 | export default Rules
41 |
--------------------------------------------------------------------------------
/frontend/src/more/Support.css:
--------------------------------------------------------------------------------
1 | .support{
2 | background-image: url(https://image.freepik.com/free-vector/flat-customer-support-illustration_23-2148899114.jpg);
3 | background-position: left;
4 | background-repeat: no-repeat;
5 | background-size: 30%;
6 | }
7 | .animation{
8 | width:35px;
9 | height: 35px;
10 | border-radius: 50%;
11 | background-color: pink;
12 | position: fixed;
13 | top: 10%;
14 | right: 10%;
15 | border: none;
16 | animation: zoom 3s linear infinite;
17 | }
18 | @keyframes zoom {
19 | 0%,100%{
20 | transform: scale(1.5);
21 | }
22 | 50%{
23 | transform: scale(.7);
24 | }
25 | }
--------------------------------------------------------------------------------
/frontend/src/more/Support.jsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState } from 'react'
2 | import MetaData from './Metadata';
3 | import "./Support.css";
4 | import emailjs from "@emailjs/browser";
5 | import BottomTab from './BottomTab.jsx';
6 | import { ToastContainer, toast } from 'react-toastify';
7 | import 'react-toastify/dist/ReactToastify.css';
8 |
9 | const Support = ({history}) => {
10 |
11 | const [done, setDone] = useState(false);
12 |
13 | const formRef = useRef(null)
14 |
15 | const handleSubmit = (e) =>{
16 | e.preventDefault()
17 | emailjs.sendForm('service_hc4y6hp', 'template_q6oankt', formRef.current, 'user_XiIxNsDzs1ebEgXJcyD1U')
18 | .then((result) => {
19 | console.log(result.text);
20 | setDone(true)
21 | }, (error) => {
22 | console.log(error.text);
23 | });
24 |
25 | }
26 |
27 | return (
28 | <>
29 |
30 |
38 |
Hey How can we improve our services
41 |
Report us for something...
44 |
45 |
108 |
109 |
110 |
111 |
112 |
113 |
124 |
125 | >
126 | )
127 | }
128 |
129 | export default Support
130 |
--------------------------------------------------------------------------------
/frontend/src/more/UserOption.css:
--------------------------------------------------------------------------------
1 | .speedDialIcon{
2 | width: 56px;
3 | height: 56px;
4 | border-radius: 100%;
5 | object-fit: cover;
6 | }
7 | .speedDial{
8 | position: fixed;
9 | right: 1vmax;
10 | top: 13vmax;
11 | }
12 | .speedDial.active{
13 | top: 5vmax;
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/src/reducers/CartReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | ADD_TO_CART,
3 | REMOVE_CART_ITEM,
4 | SAVE_SHIPPING_INFO,
5 | } from "../constans/CartConstans";
6 |
7 | export const cartReducer = (
8 | state = { cartItems: [], shippingInfo: {} },
9 | action
10 | ) => {
11 | switch (action.type) {
12 | case ADD_TO_CART:
13 | const item = action.payload;
14 |
15 | const isItemExist = state.cartItems.find(
16 | (i) => i.product === item.product
17 | );
18 |
19 | if (isItemExist) {
20 | return {
21 | ...state,
22 | cartItems: state.cartItems.map((i) =>
23 | i.product === isItemExist.product ? item : i
24 | ),
25 | };
26 | } else {
27 | return {
28 | ...state,
29 | cartItems: [...state.cartItems, item],
30 | };
31 | }
32 |
33 | case REMOVE_CART_ITEM:
34 | return {
35 | ...state,
36 | cartItems: state.cartItems.filter((i) => i.product !== action.payload),
37 | };
38 |
39 | case SAVE_SHIPPING_INFO:
40 | return {
41 | ...state,
42 | shippingInfo: action.payload,
43 | };
44 |
45 | default:
46 | return state;
47 |
48 | }
49 | };
50 |
51 |
--------------------------------------------------------------------------------
/frontend/src/reducers/FavouriteReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | ADD_TO_FAVOURITE,
3 | REMOVE_FROM_FAVOURITE,
4 | } from "../constans/FavouriteConstans";
5 |
6 | export const favouriteReducer = (
7 | state = {favouriteItems: []},
8 | action
9 | ) => {
10 | switch (action.type){
11 | case ADD_TO_FAVOURITE:
12 | const item = action.payload;
13 |
14 | const isItemExist = state.favouriteItems.find(
15 | (i) => i.product === item.product
16 | );
17 |
18 | if (isItemExist) {
19 | return {
20 | ...state,
21 | favouriteItems: state.favouriteItems.map((i) =>
22 | i.product === isItemExist.product ? item : i
23 | ),
24 | };
25 | } else {
26 | return {
27 | ...state,
28 | favouriteItems: [...state.favouriteItems, item],
29 | };
30 | }
31 |
32 | case REMOVE_FROM_FAVOURITE:
33 | return{
34 | ...state,
35 | favouriteItems: state.favouriteItems.filter((i) => i.product !== action.payload),
36 | };
37 |
38 | default:
39 | return state;
40 | }
41 | };
42 |
43 |
--------------------------------------------------------------------------------
/frontend/src/reducers/OrderReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | ALL_ORDERS_FAIL,
3 | ALL_ORDERS_REQUEST,
4 | ALL_ORDERS_SUCCESS,
5 | CLEAR_ERRORS,
6 | CREATE_ORDER_FAIL,
7 | CREATE_ORDER_REQUEST,
8 | CREATE_ORDER_SUCCESS,
9 | DELETE_ORDER_FAIL,
10 | DELETE_ORDER_REQUEST,
11 | DELETE_ORDER_RESET,
12 | DELETE_ORDER_SUCCESS,
13 | MY_ORDERS_FAIL,
14 | MY_ORDERS_REQUEST,
15 | MY_ORDERS_SUCCESS,
16 | ORDER_DETAILS_FAIL,
17 | ORDER_DETAILS_REQUEST,
18 | ORDER_DETAILS_SUCCESS,
19 | UPDATE_ORDER_FAIL,
20 | UPDATE_ORDER_REQUEST,
21 | UPDATE_ORDER_RESET,
22 | UPDATE_ORDER_SUCCESS,
23 | } from "../constans/OrderConstans";
24 |
25 | export const newOrderReducer = (state = {}, action) => {
26 | switch (action.type) {
27 | case CREATE_ORDER_REQUEST:
28 | return {
29 | ...state,
30 | loading: true,
31 | };
32 |
33 | case CREATE_ORDER_SUCCESS:
34 | return {
35 | loading: false,
36 | order: action.payload,
37 | };
38 |
39 | case CREATE_ORDER_FAIL:
40 | return {
41 | loading: false,
42 | error: action.payload,
43 | };
44 | case CLEAR_ERRORS:
45 | return {
46 | ...state,
47 | error: null,
48 | };
49 |
50 | default:
51 | return state;
52 | }
53 | };
54 |
55 | export const myOrdersReducer = (state = { orders: [] }, action) => {
56 | switch (action.type) {
57 | case MY_ORDERS_REQUEST:
58 | return {
59 | loading: true,
60 | };
61 |
62 | case MY_ORDERS_SUCCESS:
63 | return {
64 | loading: false,
65 | orders: action.payload,
66 | };
67 |
68 | case MY_ORDERS_FAIL:
69 | return {
70 | loading: false,
71 | error: action.payload,
72 | };
73 | case CLEAR_ERRORS:
74 | return {
75 | ...state,
76 | error: null,
77 | };
78 |
79 | default:
80 | return state;
81 | }
82 | };
83 |
84 | export const orderDetailsReducer = (state = { order: {} }, action) => {
85 | switch (action.type) {
86 | case ORDER_DETAILS_REQUEST:
87 | return {
88 | loading: true,
89 | };
90 |
91 | case ORDER_DETAILS_SUCCESS:
92 | return {
93 | loading: false,
94 | order: action.payload,
95 | };
96 |
97 | case ORDER_DETAILS_FAIL:
98 | return {
99 | loading: false,
100 | error: action.payload,
101 | };
102 | case CLEAR_ERRORS:
103 | return {
104 | ...state,
105 | error: null,
106 | };
107 |
108 | default:
109 | return state;
110 | }
111 | };
112 |
113 | // All Orders ------ Admin
114 | export const allOrdersReducer = (state = { orders: [] }, action) => {
115 | switch (action.type) {
116 | case ALL_ORDERS_REQUEST:
117 | return {
118 | loading: true,
119 | };
120 |
121 | case ALL_ORDERS_SUCCESS:
122 | return {
123 | loading: false,
124 | orders: action.payload,
125 | };
126 |
127 | case ALL_ORDERS_FAIL:
128 | return {
129 | loading: false,
130 | error: action.payload,
131 | };
132 | case CLEAR_ERRORS:
133 | return {
134 | ...state,
135 | error: null,
136 | };
137 |
138 | default:
139 | return state;
140 | }
141 | };
142 |
143 | export const orderReducer = (state = {}, action) => {
144 | switch (action.type) {
145 | case UPDATE_ORDER_REQUEST:
146 | case DELETE_ORDER_REQUEST:
147 | return {
148 | ...state,
149 | loading: true,
150 | };
151 |
152 | case UPDATE_ORDER_SUCCESS:
153 | return {
154 | ...state,
155 | loading: false,
156 | isUpdated: action.payload,
157 | };
158 |
159 | case DELETE_ORDER_SUCCESS:
160 | return {
161 | ...state,
162 | loading: false,
163 | isDeleted: action.payload,
164 | };
165 |
166 | case UPDATE_ORDER_FAIL:
167 | case DELETE_ORDER_FAIL:
168 | return {
169 | ...state,
170 | loading: false,
171 | error: action.payload,
172 | };
173 | case UPDATE_ORDER_RESET:
174 | return {
175 | ...state,
176 | isUpdated: false,
177 | };
178 |
179 | case DELETE_ORDER_RESET:
180 | return {
181 | ...state,
182 | isDeleted: false,
183 | };
184 | case CLEAR_ERRORS:
185 | return {
186 | ...state,
187 | error: null,
188 | };
189 |
190 | default:
191 | return state;
192 | }
193 | };
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/frontend/src/route/ProtectedRoute.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useSelector } from 'react-redux'
3 | import { Redirect,Route } from 'react-router-dom';
4 |
5 | const ProtectedRoute = ({isAdmin, component: Component, ...rest}) => {
6 |
7 | const {loading, isAuthenticated, user} = useSelector((state) => state.user);
8 |
9 | return (
10 | <>
11 | {loading === false && (
12 | {
16 | if(isAuthenticated === false){
17 | return
18 | }
19 | if(isAdmin === true && user.role !=="admin"){
20 | return
21 | }
22 | return
23 | }
24 | }
25 | />
26 | )}
27 | >
28 | )
29 | }
30 |
31 | export default ProtectedRoute
32 |
--------------------------------------------------------------------------------
/frontend/src/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, combineReducers, applyMiddleware } from "redux";
2 | import thunk from "redux-thunk";
3 | import { composeWithDevTools } from "redux-devtools-extension";
4 | import {
5 | deleteProductReducer,
6 | deleteReviewReducer,
7 | newProductReducer,
8 | newReviewReducer,
9 | productDetailsReducer,
10 | productReviewsReducer,
11 | productsReducer,
12 | } from "./reducers/ProductReducer";
13 | import { allUsersReducer, forgotPasswordReducer, profileReducer, userDetailsReducer, userReducer } from "./reducers/userReducer";
14 | import { cartReducer } from "./reducers/CartReducer";
15 | import { favouriteReducer } from "./reducers/FavouriteReducer";
16 | import {
17 | allOrdersReducer,
18 | myOrdersReducer,
19 | newOrderReducer,
20 | orderDetailsReducer,
21 | orderReducer,
22 | } from "./reducers/OrderReducer";
23 |
24 | const reducer = combineReducers({
25 | products: productsReducer,
26 | productDetails: productDetailsReducer,
27 | user: userReducer,
28 | profile: profileReducer,
29 | cart: cartReducer,
30 | favourite: favouriteReducer,
31 | order: newOrderReducer,
32 | myOrder: myOrdersReducer,
33 | myOrderDetails: orderDetailsReducer,
34 | newReview: newReviewReducer,
35 | createProduct: newProductReducer,
36 | deleteProduct: deleteProductReducer,
37 | AllOrders: allOrdersReducer,
38 | deleteOrder: orderReducer,
39 | allUsers: allUsersReducer,
40 | userDetails: userDetailsReducer,
41 | deleteReview: deleteReviewReducer,
42 | productReviews: productReviewsReducer,
43 | forgotPassword:forgotPasswordReducer,
44 | });
45 |
46 | let initialState = {
47 | cart: {
48 | cartItems: localStorage.getItem("cartItems")
49 | ? JSON.parse(localStorage.getItem("cartItems"))
50 | : [],
51 |
52 | shippingInfo: localStorage.getItem("shippingInfo")
53 | ? JSON.parse(localStorage.getItem("shippingInfo"))
54 | : {},
55 | },
56 | favourite: {
57 | favouriteItems: localStorage.getItem("favouriteItems")
58 | ? JSON.parse(localStorage.getItem("favouriteItems"))
59 | : [],
60 | },
61 | };
62 |
63 | const middleWare = [thunk];
64 |
65 | const store = createStore(
66 | reducer,
67 | initialState,
68 | composeWithDevTools(applyMiddleware(...middleWare))
69 | );
70 |
71 | export default store;
72 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ecommerce",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "backend/server.js",
6 | "engines": {
7 | "node": "16.14.1"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "start": "node backend/server.js",
12 | "dev": "nodemon backend/server.js",
13 | "heroku-postbuild":"NPM_CONFIG_PRODUCTION=false && npm install --prefix frontend && npm run build --prefix frontend"
14 | },
15 | "author": "shahriar sajeeb",
16 | "license": "ISC",
17 | "dependencies": {
18 | "@material-ui/core": "^4.12.3",
19 | "bcryptjs": "^2.4.3",
20 | "body-parser": "^1.19.1",
21 | "cloudinary": "^1.29.0",
22 | "cookie-parser": "^1.4.6",
23 | "dotenv": "^10.0.0",
24 | "express": "^4.17.2",
25 | "express-fileupload": "^1.3.1",
26 | "jsonwebtoken": "^8.5.1",
27 | "mongoose": "^6.1.4",
28 | "nodemailer": "^6.7.2",
29 | "nodemon": "^2.0.15",
30 | "npm": "^8.7.0",
31 | "stripe": "^8.215.0",
32 | "validator": "^13.7.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------