├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.cjs
├── public
└── vite.svg
├── server.js
├── src
├── App.jsx
├── assets
│ ├── empty-cart.png
│ └── hero3.png
├── components
│ ├── Products
│ │ ├── GridView.jsx
│ │ ├── ListView.jsx
│ │ ├── ProductDetails.jsx
│ │ ├── ProductFilter.jsx
│ │ └── ProductList.jsx
│ ├── adminComponents
│ │ ├── AddProducts.jsx
│ │ ├── AdminHome.jsx
│ │ ├── AdminOrderDetails.jsx
│ │ ├── AdminSidebar.jsx
│ │ ├── Orders.jsx
│ │ └── ViewProducts.jsx
│ ├── adminRoute
│ │ └── AdminRoute.jsx
│ ├── breadcrumbs
│ │ └── Breadcrumbs.jsx
│ ├── changeOrderStatus
│ │ └── ChangeOrderStatus.jsx
│ ├── chart
│ │ └── chart.jsx
│ ├── checkoutForm
│ │ └── CheckoutForm.jsx
│ ├── checkoutSummary
│ │ └── CheckoutSummary.jsx
│ ├── countdown
│ │ └── Countdown.jsx
│ ├── header
│ │ └── Header.jsx
│ ├── hero
│ │ └── Hero.jsx
│ ├── index.js
│ ├── infoBox
│ │ └── InfoBox.jsx
│ ├── loader
│ │ └── Loader.jsx
│ ├── login
│ │ └── Login.jsx
│ ├── modal
│ │ └── Modal.jsx
│ ├── navbar
│ │ └── Navbar.jsx
│ ├── orderDetailsComponent
│ │ └── OrderDetailsComponent.jsx
│ ├── orderTable
│ │ └── OrderTable.jsx
│ ├── ordersComponent
│ │ └── OrdersComponent.jsx
│ ├── pagination
│ │ └── Pagination.jsx
│ ├── protectedRoute
│ │ └── ProtectedRoute.jsx
│ ├── register
│ │ └── Register.jsx
│ ├── review
│ │ └── ReviewComponent.jsx
│ ├── search
│ │ └── Search.jsx
│ └── steps
│ │ └── Steps.jsx
├── firebase
│ └── config.js
├── hooks
│ ├── useCountdown.jsx
│ ├── useFetchCollection.jsx
│ └── useFetchDocument.jsx
├── index.css
├── main.jsx
├── pages
│ ├── admin
│ │ └── Admin.jsx
│ ├── allProducts
│ │ └── Allproducts.jsx
│ ├── cart
│ │ └── Cart.jsx
│ ├── checkout
│ │ ├── Checkout.jsx
│ │ ├── CheckoutDetails.jsx
│ │ ├── CheckoutSuccess.jsx
│ │ └── stripe.css
│ ├── contact
│ │ └── Contact.jsx
│ ├── home
│ │ └── Home.jsx
│ ├── index.js
│ ├── notFound
│ │ └── NotFound.jsx
│ ├── orderDetails
│ │ └── OrderDetails.jsx
│ ├── orderHistory
│ │ └── OrderHistory.jsx
│ ├── resetPassword
│ │ └── ResetPassword.jsx
│ └── reviewProduct
│ │ └── Review.jsx
├── redux
│ ├── slice
│ │ ├── authSlice.js
│ │ ├── cartSlice.js
│ │ ├── checkoutSlice.js
│ │ ├── filterSlice.js
│ │ ├── orderSlice.js
│ │ └── productSlice.js
│ └── store.js
├── stripeStyles.css
└── utils
│ ├── adminAddProductDefaultValues.js
│ ├── adminProductCategories.js
│ ├── formatPrice.js
│ └── uniqueValues.js
├── tailwind.config.cjs
├── vite.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 | .env
15 | dotenv
16 | # Editor directories and files
17 | .vscode/*
18 | !.vscode/extensions.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fullstack-Ecommerce
2 |
3 | ## :moneybag: Eshop.com :moneybag:
4 |
5 | eShop.com is an e-Commerce website that enables users to shop through variety of products(electronic / household / fashion) , add a product to their cart, and checkout. A user can either register their own username and password or Sign in With Google, or they can simply use the "Guest Login" button to navigate the website without registering.
6 |
7 | 
8 |
9 | ## Summary
10 |
11 | - :star: [Website Link](#website-link)
12 | - :star: [Getting Started](#getting-started)
13 | - :star: [Prerequisites](#prerequisites)
14 | - :star: [Installing](#installing)
15 | - :star: [Built With](#built-with)
16 | - :star: [Software Developer](#software-developer)
17 |
18 | ## LIVE-Website-Link
19 |
20 | [EShop.com](https://eshop-firebase.vercel.app/)
21 |
22 | ## Getting-Started
23 |
24 | Feel free to fork the project and change it to your likings. Try it out by cloning the repo to your local machine or download the zip
25 |
26 | ## Prerequisites
27 |
28 | You need preferably the latest version of Chrome, and text editor.
29 |
30 | #### Go here for Chrome: https://www.google.com/chrome/
31 |
32 | #### VSCode is my go to: https://code.visualstudio.com/
33 |
34 | ## Installing
35 |
36 | To get started follow this guide:
37 |
38 | #### FOR DEVELOPMENT PURPOSES
39 |
40 | In your terminal clone repo to your local machine using git clone:
41 |
42 | ```
43 | git clone https://github.com/kartikpavan/Fullstack-Ecommerce.git
44 | ```
45 |
46 | Move to your newly cloned repo by entering the following in your terminal:
47 |
48 | ```
49 | $ cd Fullstack-Ecommerce && yarn or npm install
50 | ```
51 |
52 | To Run Project:-
53 |
54 | ```
55 | $ yarn dev or npm run dev
56 | ```
57 |
58 | To open all project files from terminal using VSCode just tpye and enter:
59 |
60 | ```
61 | $ code .
62 | ```
63 |
64 | ## Built With
65 |
66 | - React Js
67 | - Redux Toolkit
68 | - Firebase
69 | - Node.js
70 | - Express.js
71 | - Stripe
72 | - Chart Js
73 | - Email Js
74 | - Tailwind CSS
75 | - Daisy UI
76 |
77 | ## Software Developer
78 |
79 | - **Kartik Pavan**
80 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | E-Shop App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-firebase-ecom",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build",
8 | "preview": "vite preview",
9 | "server": "nodemon server.js",
10 | "start": "node server"
11 | },
12 | "dependencies": {
13 | "@emailjs/browser": "^3.7.0",
14 | "@reduxjs/toolkit": "^1.8.6",
15 | "@stripe/react-stripe-js": "^1.14.0",
16 | "@stripe/stripe-js": "^1.42.0",
17 | "chart.js": "^3.9.1",
18 | "cors": "^2.8.5",
19 | "daisyui": "^2.31.0",
20 | "dotenv": "^16.0.3",
21 | "express": "^4.18.2",
22 | "firebase": "^9.12.1",
23 | "react": "^18.2.0",
24 | "react-chartjs-2": "^4.3.1",
25 | "react-dom": "^18.2.0",
26 | "react-icons": "^4.6.0",
27 | "react-lazy-load-image-component": "^1.5.5",
28 | "react-redux": "^8.0.4",
29 | "react-router-dom": "^6.4.2",
30 | "react-star-rate": "^0.2.0",
31 | "react-toastify": "^9.0.8",
32 | "serve": "^14.0.1",
33 | "stripe": "^10.15.0"
34 | },
35 | "devDependencies": {
36 | "@types/react": "^18.0.17",
37 | "@types/react-dom": "^18.0.6",
38 | "@vitejs/plugin-react": "^2.1.0",
39 | "autoprefixer": "^10.4.12",
40 | "postcss": "^8.4.18",
41 | "tailwindcss": "^3.1.8",
42 | "vite": "^3.1.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config();
2 | const express = require("express");
3 | const cors = require("cors");
4 | const path = require("path");
5 | const app = express();
6 | if (process.env.NODE_ENV === "production") {
7 | app.use(express.static("build"));
8 | app.get("*", (req, res) => {
9 | res.sendFile(path.resolve(__dirname, "build", "index.html"));
10 | });
11 | }
12 | // This is your test secret API key.
13 | const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
14 |
15 | app.use(express.json());
16 | app.use(cors());
17 |
18 | app.get("/", (req, res) => {
19 | res.send("Welcome to Eshop");
20 | });
21 |
22 | const newArray = [];
23 | const calculateOrderAmount = (items) => {
24 | items.map((item) => {
25 | const { price, qty } = item;
26 | const totalItemAmount = price * qty;
27 | return newArray.push(totalItemAmount);
28 | });
29 | const totalCartAmount = newArray.reduce((total, curr) => total + curr, 0);
30 | return totalCartAmount * 100;
31 | };
32 |
33 | app.post("/create-payment-intent", async (req, res) => {
34 | const { items, shippingAddress, description } = req.body;
35 | console.log(shippingAddress);
36 | // Create a PaymentIntent with the order amount and currency
37 | const paymentIntent = await stripe.paymentIntents.create({
38 | amount: calculateOrderAmount(items),
39 | currency: "inr",
40 | automatic_payment_methods: {
41 | enabled: true,
42 | },
43 | description,
44 | shipping: {
45 | address: {
46 | line1: shippingAddress.line1,
47 | line2: shippingAddress.line2,
48 | city: shippingAddress.city,
49 | country: shippingAddress.country,
50 | // pin_code: shippingAddress.pin_code,
51 | },
52 | name: shippingAddress.name,
53 | phone: shippingAddress.phone,
54 | },
55 | });
56 |
57 | res.send({
58 | clientSecret: paymentIntent.client_secret,
59 | });
60 | });
61 |
62 | const PORT = process.env.PORT || 4242;
63 | app.listen(PORT, () => console.log(`Node server listening on port ${PORT}!`));
64 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import { ToastContainer } from "react-toastify";
3 | import { AdminRoute, Modal, Navbar, ProductDetails, ProtectedRoute } from "./components";
4 | import {
5 | Admin,
6 | AllProducts,
7 | Cart,
8 | Checkout,
9 | CheckoutDetails,
10 | CheckoutSuccess,
11 | Contact,
12 | Home,
13 | NotFound,
14 | OrderDetails,
15 | OrderHistory,
16 | ResetPassword,
17 | Review,
18 | } from "./pages";
19 |
20 | const App = () => {
21 | return (
22 | <>
23 |
24 |
25 |
26 | } />
27 |
31 |
32 |
33 | }
34 | />
35 | } />
36 |
40 |
41 |
42 | }
43 | />
44 | } />
45 | } />
46 | } />
47 | } />
48 | } />
49 | } />
50 | } />
51 | } />
52 | {/* ADMIN ROUTES */}
53 |
57 |
58 |
59 | }
60 | />
61 |
62 | {/* 404 routes */}
63 | } />
64 |
65 |
66 | >
67 | );
68 | };
69 |
70 | export default App;
71 |
--------------------------------------------------------------------------------
/src/assets/empty-cart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kartikpavan/Fullstack-Ecommerce/310237fc384d57ce03c83e69bd0f97c4b8b14e1b/src/assets/empty-cart.png
--------------------------------------------------------------------------------
/src/assets/hero3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kartikpavan/Fullstack-Ecommerce/310237fc384d57ce03c83e69bd0f97c4b8b14e1b/src/assets/hero3.png
--------------------------------------------------------------------------------
/src/components/Products/GridView.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import { formatPrice } from "../../utils/formatPrice";
4 | import { FcSearch } from "react-icons/fc";
5 | // lazy load
6 | import { LazyLoadImage } from "react-lazy-load-image-component";
7 | import "react-lazy-load-image-component/src/effects/blur.css";
8 | // redux
9 | import { useDispatch, useSelector } from "react-redux";
10 | import { addToCart } from "../../redux/slice/cartSlice";
11 |
12 | const GridView = ({ products }) => {
13 | const dispatch = useDispatch();
14 | if (!products.length) {
15 | return No Products Found
;
16 | }
17 |
18 | function add2CartFunction(product) {
19 | dispatch(addToCart(product));
20 | }
21 |
22 | return (
23 |
24 | {products.map((product) => {
25 | const { id, imageURL, name, price } = product;
26 | return (
27 |
28 |
29 |
30 |
37 |
38 | Free Delivery
39 |
40 |
41 |
{name}
42 |
{formatPrice(price)}
43 |
44 |
45 |
46 |
49 |
50 |
56 |
57 |
58 |
59 | );
60 | })}
61 |
62 | );
63 | };
64 |
65 | export default React.memo(GridView);
66 |
--------------------------------------------------------------------------------
/src/components/Products/ListView.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { formatPrice } from "../../utils/formatPrice";
3 | import { Link } from "react-router-dom";
4 | // lazy load
5 | import { LazyLoadImage } from "react-lazy-load-image-component";
6 | import "react-lazy-load-image-component/src/effects/blur.css";
7 | // redux
8 | import { useDispatch, useSelector } from "react-redux";
9 | import { addToCart } from "../../redux/slice/cartSlice";
10 |
11 | const ListView = ({ products }) => {
12 | const dispatch = useDispatch();
13 |
14 | if (!products.length) {
15 | return No Products Found
;
16 | }
17 |
18 | function add2CartFunction(product) {
19 | dispatch(addToCart(product));
20 | }
21 |
22 | return (
23 |
24 | {products.map((product) => {
25 | return (
26 |
27 |
28 |
35 |
36 | Free Delivery
37 |
38 |
39 |
{product.brand}
40 |
{product.name}
41 |
42 | {formatPrice(product.price)}
43 |
44 |
{product.description.slice(0, 150)}...
45 |
46 |
52 |
53 |
56 |
57 |
58 |
59 |
60 | );
61 | })}
62 |
63 | );
64 | };
65 |
66 | export default React.memo(ListView);
67 |
--------------------------------------------------------------------------------
/src/components/Products/ProductDetails.jsx:
--------------------------------------------------------------------------------
1 | import { doc, getDoc } from "firebase/firestore";
2 | import Breadcrumbs from "../breadcrumbs/Breadcrumbs";
3 | import { Link, useParams } from "react-router-dom";
4 | import { formatPrice } from "../../utils/formatPrice";
5 | import Loader from "../loader/Loader";
6 | import ReviewComponent from "../review/ReviewComponent";
7 | // Custom Hook
8 | import useFetchCollection from "../../hooks/useFetchCollection";
9 | //Lazy Load
10 | import { LazyLoadImage } from "react-lazy-load-image-component";
11 | import "react-lazy-load-image-component/src/effects/blur.css";
12 | // Firebase
13 | import { useEffect, useState } from "react";
14 | import { db } from "../../firebase/config";
15 | // Redux
16 | import { useDispatch, useSelector } from "react-redux";
17 | import { addToCart, decreaseCart, calculateTotalQuantity } from "../../redux/slice/cartSlice";
18 |
19 | const ProductDetails = () => {
20 | // get cart items from redux store
21 | const { cartItems } = useSelector((store) => store.cart);
22 | const [product, setProduct] = useState({});
23 | const [isLoading, setIsLoading] = useState(false);
24 | const { id } = useParams();
25 | const dispatch = useDispatch();
26 |
27 | //! fetch Review Collection
28 | const { data } = useFetchCollection("reviews");
29 |
30 | // find the review which matches the current product
31 | const filteredReview = data.filter((item) => item.productId === id);
32 |
33 | //! fetch single product Document from products collection
34 | async function getSingleDocument() {
35 | setIsLoading(true);
36 | const docRef = doc(db, "products", id);
37 | const docSnap = await getDoc(docRef);
38 | if (docSnap.exists()) {
39 | setProduct(docSnap.data());
40 | setIsLoading(false);
41 | } else {
42 | console.log("No such document!");
43 | setIsLoading(false);
44 | }
45 | }
46 | // Fetching single document from firestore on initial component mount
47 | useEffect(() => {
48 | getSingleDocument();
49 | }, []);
50 |
51 | // Add to cart
52 | function add2CartFunction(product) {
53 | dispatch(addToCart({ ...product, id }));
54 | dispatch(calculateTotalQuantity());
55 | }
56 | // Decrease Qty
57 | function decreaseQty(product) {
58 | dispatch(decreaseCart({ ...product, id }));
59 | dispatch(calculateTotalQuantity());
60 | }
61 | // Check if the item is already present in the cart or not
62 | let currentItem = cartItems.find((item) => item.id === id);
63 |
64 | return (
65 | <>
66 | {isLoading && }
67 |
68 |
69 | Product Details
70 |
71 | ← Back to All Products
72 |
73 |
74 |
75 |
82 |
83 |
84 |
{product.name}
85 |
86 | {formatPrice(product.price)}
87 |
88 |
{product.description}
89 |
90 | SKU : {id}
91 |
92 |
93 | Brand : {product.brand}
94 |
95 | {/* Button Group */}
96 | {cartItems.includes(currentItem) && (
97 |
98 |
105 |
108 |
114 |
115 | )}
116 |
117 |
118 |
124 |
125 |
126 |
127 |
128 |
129 |
Reviews
130 |
131 | {!filteredReview.length ? (
132 |
133 |
134 | Be the first one to review this product
135 |
136 |
137 | ) : (
138 |
139 | {filteredReview.map((review, index) => {
140 | return ;
141 | })}
142 |
143 | )}
144 |
145 |
146 | >
147 | );
148 | };
149 |
150 | export default ProductDetails;
151 |
--------------------------------------------------------------------------------
/src/components/Products/ProductFilter.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | // Utilities
3 | import { getUniqueValues } from "../../utils/uniqueValues";
4 | //Redux
5 | import { useDispatch, useSelector } from "react-redux";
6 | import { filterByCategory, filterByBrand, filterByprice } from "../../redux/slice/filterSlice";
7 | import { formatPrice } from "../../utils/formatPrice";
8 |
9 | const ProductFilter = () => {
10 | const { products } = useSelector((store) => store.product);
11 | const { minPrice, maxPrice } = useSelector((store) => store.product);
12 | const dispatch = useDispatch();
13 |
14 | const [category, setCategory] = useState("All");
15 | const [brand, setBrand] = useState("All");
16 | const [price, setPrice] = useState(maxPrice);
17 |
18 | // Getting new Category and brand Array
19 | const allCategories = getUniqueValues(products, "category");
20 | const allBrands = getUniqueValues(products, "brand");
21 | //! Categi
22 | const filterProducts = (c) => {
23 | setCategory(c);
24 | dispatch(filterByCategory({ products, category: c }));
25 | };
26 | //! Brand
27 | useEffect(() => {
28 | dispatch(filterByBrand({ products, brand }));
29 | }, [dispatch, products, brand]);
30 |
31 | //!Price
32 | useEffect(() => {
33 | dispatch(filterByprice({ products, price }));
34 | }, [dispatch, products, price]);
35 |
36 | function clearFilter() {
37 | setCategory("All");
38 | setBrand("All");
39 | setPrice(maxPrice);
40 | }
41 |
42 | return (
43 |
44 | {/* Categories */}
45 |
46 |
CATEGORIES
47 |
48 | {allCategories.map((c, index) => {
49 | return (
50 |
62 | );
63 | })}
64 |
65 |
66 | {/* Brand */}
67 |
68 |
BRAND
69 |
82 |
83 | {/* Price */}
84 |
85 |
PRICE
86 |
{formatPrice(price)}
87 |
setPrice(e.target.value)}
94 | />
95 |
96 |
97 |
100 |
101 |
102 | );
103 | };
104 |
105 | export default ProductFilter;
106 |
--------------------------------------------------------------------------------
/src/components/Products/ProductList.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { ListView, GridView, Search, ProductFilter, Pagination } from "../../components";
3 | import { BsFillGridFill, BsFilter } from "react-icons/bs";
4 | import { MdOutlineSubject } from "react-icons/md";
5 | // Redux
6 | import { filterBySearch, sortProducts } from "../../redux/slice/filterSlice";
7 | import { useDispatch, useSelector } from "react-redux";
8 |
9 | const ProductList = ({ products }) => {
10 | const { filteredProducts } = useSelector((store) => store.filter);
11 | const [grid, setGrid] = useState(true);
12 | const [search, setSearch] = useState("");
13 | const [sort, setSort] = useState("latest");
14 | // Pagination
15 | const [currentPage, setCurrentPage] = useState(1);
16 | const [productPerPage, setProductPerPage] = useState(9);
17 | // Scroll To Top
18 | const [bacToTop, setBackToTop] = useState(false);
19 | const dispatch = useDispatch();
20 |
21 | //! Search
22 | useEffect(() => {
23 | dispatch(filterBySearch({ products, search }));
24 | }, [dispatch, products, search]);
25 | //! Sort
26 | useEffect(() => {
27 | dispatch(sortProducts({ products, sort }));
28 | }, [dispatch, products, sort]);
29 |
30 | useEffect(() => {
31 | // Scroll back to top
32 | const event = window.addEventListener("scroll", () => {
33 | if (pageYOffset > 400) {
34 | setBackToTop(true);
35 | } else {
36 | setBackToTop(false);
37 | }
38 | () => removeEventListener(event);
39 | });
40 | }, []);
41 |
42 | function scrollToTop() {
43 | window.scrollTo({
44 | top: 0,
45 | behavior: "smooth",
46 | });
47 | }
48 |
49 | //get current product
50 | const indexOfLastProduct = currentPage * productPerPage;
51 | const indexOfFirstProduct = indexOfLastProduct - productPerPage;
52 | const currentProducts = filteredProducts.slice(indexOfFirstProduct, indexOfLastProduct);
53 |
54 | return (
55 |
56 |
106 |
107 | {grid ? (
108 |
109 | ) : (
110 |
111 | )}
112 |
113 | {bacToTop && (
114 |
115 |
121 |
122 | )}
123 |
129 |
130 | );
131 | };
132 |
133 | export default ProductList;
134 |
--------------------------------------------------------------------------------
/src/components/adminComponents/AddProducts.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { toast } from "react-toastify";
3 | import Loader from "../loader/Loader";
4 | import { useNavigate, useParams } from "react-router-dom";
5 | import { categories } from "../../utils/adminProductCategories";
6 | import { defaultValues } from "../../utils/adminAddProductDefaultValues";
7 | import { ref, uploadBytesResumable, getDownloadURL, deleteObject } from "firebase/storage";
8 | import { collection, addDoc, Timestamp, setDoc, doc } from "firebase/firestore";
9 | import { storage, db } from "../../firebase/config";
10 | import { useSelector } from "react-redux";
11 |
12 | //! Handle Input Changes
13 | const AddProducts = () => {
14 | const navigate = useNavigate();
15 | const { id: paramsId } = useParams();
16 | const { products: reduxProducts } = useSelector((store) => store.product);
17 | const productEdit = reduxProducts.find((item) => item.id === paramsId);
18 | const [product, setProduct] = useState(() => {
19 | return detectForm(paramsId, defaultValues, productEdit);
20 | });
21 | const [uploadProgress, setUploadProgress] = useState(0);
22 | const [isLoading, setIsLoading] = useState(false);
23 |
24 | //! Check for Add or Edit
25 | function detectForm(paramsId, func1, func2) {
26 | if (paramsId === "ADD") return func1;
27 | return func2;
28 | }
29 |
30 | function handleInputChange(e) {
31 | const { name, value } = e.target;
32 | setProduct({ ...product, [name]: value });
33 | }
34 | //! File Upload to FireStorage
35 | function handleImageChange(e) {
36 | const file = e.target.files[0];
37 | const storageRef = ref(storage, `images/${Date.now()}${file.name}`);
38 | const uploadTask = uploadBytesResumable(storageRef, file);
39 | uploadTask.on(
40 | "state_changed",
41 | (snapshot) => {
42 | const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
43 | setUploadProgress(progress);
44 | },
45 | (error) => {
46 | toast.error(error.code, error.message);
47 | },
48 | () => {
49 | // Handle successful uploads on complete
50 | getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
51 | setProduct({ ...product, imageURL: downloadURL });
52 | toast.success("File Uploaded Successfully");
53 | });
54 | }
55 | );
56 | }
57 | //! Add Product to Firebase
58 | async function addProduct(e) {
59 | e.preventDefault();
60 | setIsLoading(true);
61 | try {
62 | const docRef = await addDoc(collection(db, "products"), {
63 | name: product.name,
64 | imageURL: product.imageURL,
65 | price: Number(product.price),
66 | category: product.category,
67 | brand: product.brand,
68 | description: product.description,
69 | createdAt: Timestamp.now().toDate(),
70 | });
71 | setUploadProgress(0);
72 | setProduct(defaultValues);
73 | setIsLoading(false);
74 | toast.success("Product added to Database Successfully");
75 | navigate("/admin/all-products");
76 | } catch (error) {
77 | console.log(error.message);
78 | toast.error("Something Went Wrong , Check Console");
79 | setIsLoading(false);
80 | }
81 | }
82 | //! Edit Product
83 | async function editProduct(e) {
84 | e.preventDefault();
85 | setIsLoading(true);
86 | // Check if the image is updated
87 | if (product.imageURL !== productEdit.imageURL) {
88 | // deleting image from database storage
89 | const storageRef = ref(storage, productEdit.imageURL);
90 | await deleteObject(storageRef);
91 | }
92 | try {
93 | await setDoc(doc(db, "products", paramsId), {
94 | name: product.name,
95 | imageURL: product.imageURL,
96 | price: Number(product.price),
97 | category: product.category,
98 | brand: product.brand,
99 | description: product.description,
100 | // Preserving created at
101 | createdAt: productEdit.createdAt,
102 | editedAt: Timestamp.now().toDate(),
103 | });
104 | setUploadProgress(0);
105 | setProduct(defaultValues);
106 | setIsLoading(false);
107 | toast.success("Product Updated Successfully");
108 | navigate("/admin/all-products");
109 | } catch (error) {
110 | console.log(error.message);
111 | toast.error("Something Went Wrong , Check Console");
112 | setIsLoading(false);
113 | }
114 | }
115 |
116 | //! Disable button until everything added to input fields
117 | const AllFieldsRequired =
118 | Boolean(product.brand) &&
119 | Boolean(product.category) &&
120 | Boolean(product.description) &&
121 | Boolean(product.imageURL) &&
122 | Boolean(product.name) &&
123 | Boolean(product.name);
124 |
125 | return (
126 | <>
127 | {isLoading && }
128 |
129 |
130 |
131 | {detectForm(paramsId, "Add New Product", "Edit Product")}
132 |
133 |
243 |
244 | >
245 | );
246 | };
247 |
248 | export default AddProducts;
249 |
--------------------------------------------------------------------------------
/src/components/adminComponents/AdminHome.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { InfoBox, Chart } from "../../components";
3 | import { BiRupee } from "react-icons/bi";
4 | import { FaCartArrowDown } from "react-icons/fa";
5 | import { BsCart } from "react-icons/bs";
6 | import { formatPrice } from "../../utils/formatPrice";
7 | //redux
8 | import { useSelector, useDispatch } from "react-redux";
9 | import { totalOrderAmount, storeOrders } from "../../redux/slice/orderSlice";
10 | import useFetchCollection from "../../hooks/useFetchCollection";
11 |
12 | const earningIcon = ;
13 | const productIcon = ;
14 | const orderIcon = ;
15 |
16 | const AdminHome = () => {
17 | const { data } = useFetchCollection("orders");
18 | const { products } = useSelector((store) => store.product);
19 | const { orderHistory, totalAmount } = useSelector((store) => store.order);
20 | const dispatch = useDispatch();
21 |
22 | useEffect(() => {
23 | dispatch(storeOrders(data));
24 | dispatch(totalOrderAmount());
25 | }, [dispatch, data]);
26 |
27 | return (
28 |
29 | Admin Home
30 |
35 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default AdminHome;
43 |
--------------------------------------------------------------------------------
/src/components/adminComponents/AdminOrderDetails.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useParams, Link } from "react-router-dom";
3 | import Loader from "../../components/loader/Loader";
4 | //firebase
5 | import useFetchDocument from "../../hooks/useFetchDocument";
6 | import OrderDetailsComponent from "../../components/orderDetailsComponent/OrderDetailsComponent";
7 |
8 | const AdminOrderDetails = () => {
9 | const [order, setOrder] = useState(null);
10 | const { id } = useParams();
11 | const { document } = useFetchDocument("orders", id);
12 |
13 | useEffect(() => {
14 | setOrder(document);
15 | }, [document]);
16 |
17 | return (
18 | <>
19 | {order === null ? (
20 |
21 | ) : (
22 |
23 |
24 |
25 | )}
26 | >
27 | );
28 | };
29 |
30 | export default AdminOrderDetails;
31 |
--------------------------------------------------------------------------------
/src/components/adminComponents/AdminSidebar.jsx:
--------------------------------------------------------------------------------
1 | import { RiAdminLine } from "react-icons/ri";
2 | import { useSelector } from "react-redux";
3 | import { NavLink } from "react-router-dom";
4 |
5 | const AdminSidebar = () => {
6 | const { userName } = useSelector((store) => store.auth);
7 |
8 | // Active link class
9 | let activeStyle = {
10 | borderRight: "5px solid #181a2a",
11 | display: "block",
12 | // padding: "0.5rem 0",
13 | };
14 | return (
15 |
16 |
17 |
18 |
{userName}
19 |
20 |
21 | (isActive ? activeStyle : null)}>
22 | Home
23 |
24 |
25 |
26 | (isActive ? activeStyle : null)}
29 | >
30 | All Products
31 |
32 |
33 |
34 | (isActive ? activeStyle : null)}
37 | >
38 | Add Products
39 |
40 |
41 |
42 | (isActive ? activeStyle : null)}
45 | >
46 | Orders
47 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default AdminSidebar;
54 |
--------------------------------------------------------------------------------
/src/components/adminComponents/Orders.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import useFetchCollection from "../../hooks/useFetchCollection";
3 | import Loader from "../../components/loader/Loader";
4 | import { useNavigate } from "react-router-dom";
5 | // Redux
6 | import { useDispatch, useSelector } from "react-redux";
7 | import { storeOrders } from "../../redux/slice/orderSlice";
8 |
9 | import OrdersComponent from "../ordersComponent/OrdersComponent";
10 |
11 | const Orders = () => {
12 | const { data, isLoading } = useFetchCollection("orders");
13 | const { orderHistory } = useSelector((store) => store.order);
14 | const { userId } = useSelector((store) => store.auth);
15 | const dispatch = useDispatch();
16 | const navigate = useNavigate();
17 |
18 | useEffect(() => {
19 | dispatch(storeOrders(data));
20 | }, [dispatch, data]);
21 |
22 | return (
23 | <>
24 | {isLoading && }
25 | ALL ORDERS
26 |
27 |
28 |
29 | >
30 | );
31 | };
32 |
33 | export default Orders;
34 |
--------------------------------------------------------------------------------
/src/components/adminComponents/ViewProducts.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import Loader from "../loader/Loader";
3 | import { toast } from "react-toastify";
4 | import { formatPrice } from "../../utils/formatPrice";
5 | import { BiEdit, BiTrash } from "react-icons/bi";
6 | import { Link } from "react-router-dom";
7 | import { Search } from "../../components";
8 | import useFetchCollection from "../../hooks/useFetchCollection";
9 | import { doc, deleteDoc } from "firebase/firestore";
10 | import { ref, deleteObject } from "firebase/storage";
11 | import { db, storage } from "../../firebase/config";
12 | import { useDispatch, useSelector } from "react-redux";
13 | import { storeProducts } from "../../redux/slice/productSlice";
14 | import { filterBySearch } from "../../redux/slice/filterSlice";
15 | import { LazyLoadImage } from "react-lazy-load-image-component";
16 | import "react-lazy-load-image-component/src/effects/blur.css";
17 |
18 | const ViewProducts = () => {
19 | const [search, setSearch] = useState("");
20 | const dispatch = useDispatch();
21 |
22 | //! Fetching Products from collection using Custom Hook
23 | const { data, isLoading } = useFetchCollection("products");
24 | const { filteredProducts } = useSelector((store) => store.filter);
25 | const { products } = useSelector((store) => store.product);
26 |
27 | useEffect(() => {
28 | dispatch(storeProducts({ products: data }));
29 | }, [dispatch, data]);
30 |
31 | //! Search
32 | useEffect(() => {
33 | dispatch(filterBySearch({ products: data, search }));
34 | }, [dispatch, data, search]);
35 |
36 | //! Delete single product
37 | const deleteSingleProduct = async (id, imageURL) => {
38 | try {
39 | // deleting a document from product collection
40 | await deleteDoc(doc(db, "products", id));
41 | // deleting image from database storage
42 | const storageRef = ref(storage, imageURL);
43 | await deleteObject(storageRef);
44 | toast.info("Product deleted successfully");
45 | } catch (error) {
46 | toast.error(error.message);
47 | console.log(error.message);
48 | }
49 | };
50 | return (
51 | <>
52 | {isLoading && }
53 | All Products
54 | {products.length && (
55 |
56 |
57 | {filteredProducts.length} products found
58 |
59 |
60 | )}
61 | setSearch(e.target.value)} />
62 |
63 | {filteredProducts.length === 0 ? (
64 | NO PRODUCTS FOUND
65 | ) : (
66 |
67 |
68 | {/* TABLE HEAD */}
69 |
70 |
71 | |
72 | Image |
73 | Name |
74 | Category |
75 | Price |
76 | Options |
77 |
78 |
79 | {/* TABLE BODY */}
80 |
81 | {filteredProducts?.map((p, index) => {
82 | const { id, name, category, price, imageURL } = p;
83 | return (
84 |
85 | {index + 1} |
86 |
87 |
88 |
98 |
99 | |
100 | {name} |
101 | {category} |
102 | {formatPrice(price)} |
103 |
104 |
105 |
106 |
107 |
108 |
118 |
119 | |
120 |
121 | );
122 | })}
123 |
124 |
125 |
126 | )}
127 |
128 | >
129 | );
130 | };
131 |
132 | export default ViewProducts;
133 |
--------------------------------------------------------------------------------
/src/components/adminRoute/AdminRoute.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "react-router-dom";
3 | import { useSelector } from "react-redux";
4 |
5 | const AdminRoute = ({ children }) => {
6 | const { email } = useSelector((store) => store.auth);
7 | if (email === import.meta.env.VITE_ADMIN_KEY) return children;
8 | return (
9 |
10 | PERMISSION DENIED
11 | This page can only be viewed by admin.
12 |
13 | ← Take me back home
14 |
15 |
16 | );
17 | };
18 |
19 | export const AdminOnlyLink = ({ children }) => {
20 | const { email } = useSelector((store) => store.auth);
21 | if (email === import.meta.env.VITE_ADMIN_KEY) return children;
22 | return null;
23 | };
24 |
25 | export default AdminRoute;
26 |
--------------------------------------------------------------------------------
/src/components/breadcrumbs/Breadcrumbs.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link, NavLink } from "react-router-dom";
3 |
4 | const Breadcrumbs = ({ type, checkout, stripe }) => {
5 | const activeLink = ({ isActive }) => (isActive ? "text-secondary-content " : null);
6 |
7 | return (
8 |
9 |
10 |
11 | Home /
12 |
13 |
14 | Products
15 |
16 |
17 | {type && (
18 |
19 | / {type}
20 |
21 | )}
22 | {checkout && (
23 |
24 | / {checkout}
25 |
26 | )}
27 | {stripe && (
28 |
29 | / {stripe}
30 |
31 | )}
32 |
33 |
34 | );
35 | };
36 |
37 | export default Breadcrumbs;
38 |
--------------------------------------------------------------------------------
/src/components/changeOrderStatus/ChangeOrderStatus.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { toast } from "react-toastify";
3 | import Loader from "../loader/Loader";
4 | import { useNavigate } from "react-router-dom";
5 | // firebase
6 | import { doc, setDoc, Timestamp } from "firebase/firestore";
7 | import { db } from "../../firebase/config";
8 |
9 | const ChangeOrderStatus = ({ order, orderId }) => {
10 | const [status, setStatus] = useState("");
11 | const [isLoading, setIsloading] = useState(false);
12 | const navigate = useNavigate();
13 |
14 | const changeStatus = (e, orderId) => {
15 | e.preventDefault();
16 | setIsloading(true);
17 | const orderDetails = {
18 | userId: order.userId,
19 | email: order.email,
20 | orderDate: order.orderDate,
21 | orderTime: order.orderTime,
22 | orderAmount: order.orderAmount,
23 | orderStatus: status,
24 | cartItems: order.cartItems,
25 | shippingAddress: order.shippingAddress,
26 | createdAt: order.createdAt,
27 | editedAt: Timestamp.now().toDate(),
28 | };
29 | try {
30 | setDoc(doc(db, "orders", orderId), orderDetails);
31 | setIsloading(false);
32 | toast.success(`order status changed to ${status}`);
33 | navigate("/admin/orders");
34 | } catch (error) {
35 | toast.error(error.message);
36 | console.log(error);
37 | setIsloading(false);
38 | }
39 | };
40 |
41 | return (
42 | <>
43 | {isLoading && }
44 |
45 |
Update Order Status
46 |
62 |
63 | >
64 | );
65 | };
66 |
67 | export default ChangeOrderStatus;
68 |
--------------------------------------------------------------------------------
/src/components/chart/chart.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import useFetchCollection from "../../hooks/useFetchCollection";
3 |
4 | import {
5 | Chart as ChartJS,
6 | CategoryScale,
7 | LinearScale,
8 | BarElement,
9 | Title,
10 | Tooltip,
11 | Legend,
12 | } from "chart.js";
13 | import { Bar } from "react-chartjs-2";
14 | import { useSelector } from "react-redux";
15 |
16 | ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
17 |
18 | const options = {
19 | responsive: true,
20 | plugins: {
21 | legend: {
22 | position: "top",
23 | },
24 | title: {
25 | display: true,
26 | text: "Order Status",
27 | },
28 | },
29 | };
30 |
31 | const chart = () => {
32 | const { orderHistory } = useSelector((store) => store.order);
33 | // Create new Array of order status
34 | const filteredOrders = orderHistory.map((item) => item.orderStatus);
35 |
36 | // Count the occurances of order status(s)
37 | const getOrderCount = (arr, value) => {
38 | return arr.filter((item) => item === value).length;
39 | };
40 |
41 | const placed = getOrderCount(filteredOrders, "Order Placed");
42 | const processing = getOrderCount(filteredOrders, "Processing...");
43 | const shipped = getOrderCount(filteredOrders, "Item(s) Shipped");
44 | const delivered = getOrderCount(filteredOrders, "Item(s) Delivered");
45 |
46 | const data = {
47 | labels: ["Order Places", "Processing", "Shipped", "Delivered"],
48 | datasets: [
49 | {
50 | label: "Order Count",
51 | data: [placed, shipped, processing, delivered],
52 | backgroundColor: "#191a3ed6",
53 | },
54 | ],
55 | };
56 | return ;
57 | };
58 |
59 | export default chart;
60 |
--------------------------------------------------------------------------------
/src/components/checkoutForm/CheckoutForm.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import {
3 | PaymentElement,
4 | useStripe,
5 | useElements,
6 | } from "@stripe/react-stripe-js";
7 | import CheckoutSummary from "../checkoutSummary/CheckoutSummary";
8 | import Breadcrumbs from "../breadcrumbs/Breadcrumbs";
9 | import Header from "../header/Header";
10 | import { toast } from "react-toastify";
11 | import { useNavigate } from "react-router-dom";
12 | // firebase
13 | import { collection, addDoc, Timestamp } from "firebase/firestore";
14 | import { db } from "../../firebase/config";
15 | //redux
16 | import { useSelector, useDispatch } from "react-redux";
17 | import { clearCart } from "../../redux/slice/cartSlice";
18 | import Loader from "../loader/Loader";
19 |
20 | const CheckoutForm = () => {
21 | const stripe = useStripe();
22 | const elements = useElements();
23 |
24 | const [message, setMessage] = useState(null);
25 | const [isLoading, setIsLoading] = useState(false);
26 |
27 | const navigate = useNavigate();
28 | const dispatch = useDispatch();
29 | const { email, userId } = useSelector((store) => store.auth);
30 | const { cartItems, totalAmount } = useSelector((store) => store.cart);
31 | const { shippingAddress } = useSelector((store) => store.checkout);
32 |
33 | const saveOrder = () => {
34 | const date = new Date().toDateString();
35 | const time = new Date().toLocaleTimeString();
36 | const orderDetails = {
37 | userId,
38 | email,
39 | orderDate: date,
40 | orderTime: time,
41 | orderAmount: totalAmount,
42 | orderStatus: "Order Placed",
43 | cartItems,
44 | shippingAddress,
45 | createdAt: Timestamp.now().toDate(),
46 | };
47 | try {
48 | addDoc(collection(db, "orders"), orderDetails);
49 | dispatch(clearCart());
50 | } catch (error) {
51 | toast.error(error.message);
52 | }
53 | };
54 |
55 | const handleSubmit = async (e) => {
56 | e.preventDefault();
57 | setMessage(null);
58 | if (!stripe || !elements) {
59 | return;
60 | }
61 | setIsLoading(true);
62 | const confirmPayment = await stripe
63 | .confirmPayment({
64 | elements,
65 | confirmParams: {
66 | // Make sure to change this to your payment completion page
67 | return_url: "http://localhost:5173/checkout-success",
68 | },
69 | redirect: "if_required",
70 | })
71 | .then((res) => {
72 | if (res.error) {
73 | setMessage(res.error.message);
74 | toast.error(res.error.message);
75 | return;
76 | }
77 | if (res.paymentIntent) {
78 | if (res.paymentIntent.status === "succeeded") {
79 | setIsLoading(false);
80 | toast.success("Payment Successful");
81 | saveOrder();
82 | navigate("/checkout-success", { replace: true });
83 | }
84 | }
85 | });
86 | setIsLoading(false);
87 | };
88 |
89 | useEffect(() => {
90 | if (!stripe) {
91 | return;
92 | }
93 | const clientSecret = new URLSearchParams(window.location.search).get(
94 | "payment_intent_client_secret"
95 | );
96 | if (!clientSecret) {
97 | return;
98 | }
99 | }, [stripe]);
100 |
101 | return (
102 | <>
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
Stripe Checkout
111 |
130 |
131 |
132 |
133 | >
134 | );
135 | };
136 |
137 | export default CheckoutForm;
138 |
--------------------------------------------------------------------------------
/src/components/checkoutSummary/CheckoutSummary.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useSelector } from "react-redux";
3 | import { formatPrice } from "../../utils/formatPrice";
4 |
5 | const CheckoutSummary = () => {
6 | const { cartItems, totalQuantity, totalAmount } = useSelector(
7 | (store) => store.cart
8 | );
9 | return (
10 | <>
11 | Checkout Summary
12 |
13 |
14 | Cart Item(s): {totalQuantity}{" "}
15 |
16 |
17 |
Subtotal:
18 |
19 | {formatPrice(totalAmount)}
20 |
21 |
22 | {cartItems.map((item) => {
23 | const { id, name, price, qty } = item;
24 | return (
25 |
29 |
30 | {name}
31 |
32 | Quantity: {qty}
33 | Unit Price : {formatPrice(price)}
34 | Total Price: {formatPrice(price * qty)}
35 |
36 | );
37 | })}
38 |
39 | >
40 | );
41 | };
42 |
43 | export default CheckoutSummary;
44 |
--------------------------------------------------------------------------------
/src/components/countdown/Countdown.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Countdown = () => {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 | days
11 |
12 |
13 |
14 |
15 |
16 | hours
17 |
18 |
19 |
20 |
21 |
22 | min
23 |
24 |
25 |
26 |
27 |
28 | sec
29 |
30 |
31 | );
32 | };
33 |
34 | export default Countdown;
35 |
--------------------------------------------------------------------------------
/src/components/header/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Header = ({ text }) => {
4 | return (
5 |
10 | );
11 | };
12 |
13 | export default Header;
14 |
--------------------------------------------------------------------------------
/src/components/hero/Hero.jsx:
--------------------------------------------------------------------------------
1 | import hero from "../../assets/hero3.png";
2 | import { Link } from "react-router-dom";
3 | import { TbArrowNarrowRight } from "react-icons/tb";
4 | import { useEffect, useState } from "react";
5 | const tags = ["Mobiles", "Electronics", "Bags", "Clothes", "Jwellery"];
6 |
7 | let currentIndex = 0;
8 | const Hero = () => {
9 | const [tagName, setTagName] = useState("");
10 | function updateCountdown() {
11 | const currentItem = tags[currentIndex];
12 | setTagName(currentItem);
13 | currentIndex = (currentIndex + 1) % tags.length;
14 | setTimeout(updateCountdown, 2000);
15 | }
16 |
17 | useEffect(() => {
18 | updateCountdown();
19 | }, []);
20 |
21 | return (
22 | //
23 | //
24 | //

28 | //
29 | //
Limited Time Only
30 | //
Fashion
31 | //
32 | // Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi
33 | // exercitationem quasi.
34 | //
35 | //
39 | // Shop Now
40 | //
41 | //
42 | //
43 | //
44 | //
45 | <>
46 |
47 |
48 |
49 |
50 |
51 |
52 | Best place to choose
your{" "}
53 |
54 | {tagName}
55 |
56 |
57 |
58 |
59 | Lorem ipsum dolor sit amet, consectetur adipisicing
60 | elit. Porro beatae error laborum ab amet sunt
61 | recusandae? Reiciendis natus perspiciatis optio.
62 |
63 |
64 |
65 |
68 |
69 |
70 |
71 |
72 |
73 |

78 |
79 |
80 |
81 |
82 | >
83 | );
84 | };
85 |
86 | export default Hero;
87 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as GridView } from "./Products/GridView";
2 | export { default as ListView } from "./Products/ListView";
3 | export { default as ProductDetails } from "./Products/ProductDetails";
4 | export { default as ProductFilter } from "./Products/ProductFilter";
5 | export { default as ProductList } from "./Products/ProductList";
6 | export { default as AddProducts } from "./adminComponents/AddProducts";
7 | export { default as AdminHome } from "./adminComponents/AdminHome";
8 | export { default as AdminOrderDetails } from "./adminComponents/AdminOrderDetails";
9 | export { default as AdminSidebar } from "./adminComponents/AdminSidebar";
10 | export { default as Orders } from "./adminComponents/Orders";
11 | export { default as ViewProducts } from "./adminComponents/ViewProducts";
12 | export { default as AdminRoute } from "./adminRoute/AdminRoute";
13 | export { default as Breadcrumbs } from "./breadcrumbs/Breadcrumbs";
14 | export { default as Chart } from "./chart/chart";
15 | export { default as CheckoutForm } from "./checkoutForm/CheckoutForm";
16 | export { default as CheckoutSummary } from "./checkoutSummary/CheckoutSummary";
17 | export { default as Header } from "./header/Header";
18 | export { default as Hero } from "./hero/Hero";
19 | export { default as InfoBox } from "./infoBox/InfoBox";
20 | export { default as Login } from "./login/Login";
21 | export { default as Modal } from "./modal/Modal";
22 | export { default as Navbar } from "./navbar/Navbar";
23 | export { default as OrderDetailsComponent } from "./orderDetailsComponent/OrderDetailsComponent";
24 | export { default as OrderTable } from "./orderTable/OrderTable";
25 | export { default as OrdersComponent } from "./ordersComponent/OrdersComponent";
26 | export { default as Pagination } from "./pagination/Pagination";
27 | export { default as ProtectedRoute } from "./protectedRoute/ProtectedRoute";
28 | export { default as Register } from "./register/Register";
29 | export { default as ReviewComponent } from "./review/ReviewComponent";
30 | export { default as Search } from "./search/Search";
31 | export { default as Steps } from "./steps/Steps";
32 |
--------------------------------------------------------------------------------
/src/components/infoBox/InfoBox.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const InfoBox = ({ color, title, count, icon }) => {
4 | return (
5 |
6 |
{title}
7 |
8 |
{count}
9 | {icon}
10 |
11 |
12 | );
13 | };
14 |
15 | export default InfoBox;
16 |
--------------------------------------------------------------------------------
/src/components/loader/Loader.jsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from "react-dom";
2 |
3 | const Loader = () => {
4 | return ReactDOM.createPortal(
5 |
6 |
7 |
Please wait , This might take a while
8 |
9 |
10 |
,
11 | document.getElementById("loader")
12 | );
13 | // return (
14 | //
17 | // );
18 | };
19 |
20 | export default Loader;
21 |
--------------------------------------------------------------------------------
/src/components/login/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { AiFillEye, AiFillEyeInvisible } from "react-icons/ai";
3 | import { FcGoogle } from "react-icons/fc";
4 | import { Link, useNavigate } from "react-router-dom";
5 | import { toast } from "react-toastify";
6 | import "react-toastify/dist/ReactToastify.css";
7 | import Loader from "../loader/Loader";
8 | //Firebase
9 | import { GoogleAuthProvider, signInWithEmailAndPassword, signInWithPopup } from "firebase/auth";
10 | import { auth } from "../../firebase/config";
11 |
12 | const Login = () => {
13 | const [email, setEmail] = useState("");
14 | const [password, setPassword] = useState("");
15 | const [showPassword, setShowPassword] = useState(false);
16 | const [isLoading, setIsLoading] = useState(false);
17 | const navigate = useNavigate();
18 |
19 | //! Test / Guest Account Login
20 | //* Currently this feature is disabled due to spam messages reported by the ADMIN
21 | const testLogin = (e) => {
22 | e.preventDefault();
23 | // document.getElementById("my-modal-69").checked = true;
24 | // setInfoModalOpen(true);
25 | document.getElementById("my-modal-4").checked = false;
26 |
27 | let testEmail = import.meta.env.VITE_TEST_EMAIL;
28 | let testPass = import.meta.env.VITE_TEST_PASSWORD;
29 | setIsLoading(true);
30 | signInWithEmailAndPassword(auth, testEmail, testPass)
31 | .then((userCredential) => {
32 | const user = userCredential.user;
33 | toast.success("Login Successful");
34 | setIsLoading(false);
35 | // document.getElementById("my-modal-4").checked = false;
36 | navigate("/");
37 | })
38 | .catch((error) => {
39 | toast.error(error.code, error.message);
40 | setIsLoading(false);
41 | });
42 | };
43 |
44 | const handleSubmit = (e) => {
45 | e.preventDefault();
46 | // Close dialog box when Login Btn is clicked immediately
47 | document.getElementById("my-modal-4").checked = false;
48 |
49 | //* Custom User login
50 | setIsLoading(true);
51 | signInWithEmailAndPassword(auth, email, password)
52 | .then((userCredential) => {
53 | const user = userCredential.user;
54 | toast.success("Login Successful");
55 | setIsLoading(false);
56 | navigate("/");
57 | })
58 | .catch((error) => {
59 | toast.error(error.code, error.message);
60 | setIsLoading(false);
61 | });
62 |
63 | setEmail("");
64 | setPassword("");
65 | };
66 |
67 | //* Login with Google
68 | const provider = new GoogleAuthProvider();
69 | const googleSignIn = () => {
70 | setIsLoading(true);
71 | document.getElementById("my-modal-4").checked = false;
72 | signInWithPopup(auth, provider)
73 | .then((result) => {
74 | const user = result.user;
75 | toast.success("Login Successful");
76 | setIsLoading(false);
77 | navigate("/");
78 | })
79 | .catch((error) => {
80 | toast.error(error.code, error.message);
81 | setIsLoading(false);
82 | });
83 | };
84 |
85 | const AllFieldsRequired = Boolean(email) && Boolean(password);
86 |
87 | return (
88 | <>
89 | {isLoading && }
90 |
91 |
92 |
93 |
Welcome back
94 |
95 |
96 | Sign in with google
97 |
98 |
or login with email
99 |