├── src ├── components │ ├── SearchBar.jsx │ ├── Loader.jsx │ ├── allComponents.js │ ├── FeaturedCategories.js │ ├── Footer.js │ ├── OrderSuccessCard.jsx │ ├── MobileMenu.jsx │ ├── AddressCard.jsx │ ├── HorizontalCard.js │ ├── ProductCard.js │ ├── Navbar.js │ ├── CartPriceCard.js │ ├── FiltersBar.js │ └── EditAddressModal.jsx ├── logo.png ├── App.css ├── styles │ ├── pages │ │ ├── errorPage.css │ │ ├── wishlist.css │ │ ├── forgotpassword.css │ │ ├── searchPage.css │ │ ├── profile.css │ │ ├── signup.css │ │ ├── login.css │ │ ├── singleProductPage.css │ │ ├── cart.css │ │ ├── productlist.css │ │ └── home.css │ ├── layouts │ │ ├── footer.css │ │ ├── scrollbar.css │ │ └── navbar.css │ ├── components │ │ ├── addressCard.css │ │ ├── orderCard.css │ │ ├── loader.css │ │ ├── editAddressModal.css │ │ └── mobileMenu.css │ └── utilities │ │ └── variables.css ├── utils │ ├── isItemInWishList.js │ ├── isItemInCart.js │ ├── filterReducer.js │ └── filterProducts.js ├── routes │ ├── RequiresAuth.js │ └── PageRoutes.js ├── App.js ├── reducers │ ├── wishListReducer.js │ ├── cartReducer.js │ └── addressReducer.js ├── backend │ ├── db │ │ ├── users.js │ │ ├── categories.js │ │ └── products.js │ ├── utils │ │ └── authUtils.js │ └── controllers │ │ ├── ProductController.js │ │ ├── CategoryController.js │ │ ├── AuthController.js │ │ ├── WishlistController.js │ │ └── CartController.js ├── context │ ├── cart-context.js │ ├── product-context.js │ ├── address-context.js │ ├── auth-context.js │ └── wishlist-context.js ├── services │ ├── getProducts.js │ ├── getFeaturedCategories.js │ ├── getProductById.js │ ├── wishlistServices.js │ └── cartServices.js ├── pages │ ├── ErrorPage.jsx │ ├── OrderSuccessPage.jsx │ ├── WishList.js │ ├── Checkout.jsx │ ├── Cart.js │ ├── ForgotPassword.js │ ├── Home.js │ ├── ProductListing.js │ ├── SearchPage.jsx │ ├── MyProfile.jsx │ ├── Login.js │ ├── SingleProductPage.jsx │ └── Signup.js ├── index.js └── server.js ├── public ├── _redirects ├── shop.ico ├── favicon.ico └── index.html ├── .gitattributes ├── .gitignore ├── .eslintrc.json ├── package.json └── README.md /src/components/SearchBar.jsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kedark152/AgroStores-react/HEAD/src/logo.png -------------------------------------------------------------------------------- /public/shop.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kedark152/AgroStores-react/HEAD/public/shop.ico -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kedark152/AgroStores-react/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @import url(./styles/layouts/scrollbar.css); 2 | .App { 3 | height: 100vh; 4 | } 5 | -------------------------------------------------------------------------------- /src/styles/pages/errorPage.css: -------------------------------------------------------------------------------- 1 | .error-img { 2 | height: 80vh; 3 | } 4 | 5 | .error-page-container { 6 | font-family: var(--lato-font); 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Loader.jsx: -------------------------------------------------------------------------------- 1 | import "../styles/components/loader.css"; 2 | 3 | export const Loader = () => { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |
11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /src/styles/layouts/footer.css: -------------------------------------------------------------------------------- 1 | footer{ 2 | background-color: var(--primary); 3 | color: var(--white); 4 | font-family: var(--acme-font); 5 | } 6 | 7 | .footer-links a{ 8 | color: var(--white); 9 | } 10 | 11 | .fab:hover{ 12 | color: var(--grey-dark); 13 | cursor: pointer; 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/isItemInWishList.js: -------------------------------------------------------------------------------- 1 | import {useWishList} from "../context/wishlist-context"; 2 | 3 | export const IsItemInWishList = (id)=>{ 4 | const {wishListState} = useWishList(); 5 | let itemStatus = false; 6 | wishListState.wishlistItems.map((item)=>{ 7 | item._id === id ? (itemStatus = true) : null 8 | } 9 | ) 10 | return itemStatus; 11 | } -------------------------------------------------------------------------------- /src/utils/isItemInCart.js: -------------------------------------------------------------------------------- 1 | import { useCart } from "../context/cart-context"; 2 | 3 | export const IsItemInCart = (id) => { 4 | const { cartState } = useCart(); 5 | let itemStatus = false; 6 | if (cartState.cartItems.length > 0) { 7 | cartState.cartItems.map((item) => { 8 | item._id === id ? (itemStatus = true) : null; 9 | }); 10 | } 11 | return itemStatus; 12 | }; 13 | -------------------------------------------------------------------------------- /src/routes/RequiresAuth.js: -------------------------------------------------------------------------------- 1 | import { Navigate, Outlet, useLocation } from "react-router-dom"; 2 | import { useAuth } from "../context/auth-context"; 3 | 4 | export const RequiresAuth = () => { 5 | const { auth } = useAuth(); 6 | const location = useLocation(); 7 | return auth.token ? ( 8 | 9 | ) : ( 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /.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 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | .eslintcache 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import "./styles/utilities/variables.css"; 3 | import { PageRoutes } from "./routes/PageRoutes"; 4 | import "react-toastify/dist/ReactToastify.css"; 5 | 6 | import { ToastContainer } from "react-toastify"; 7 | 8 | function App() { 9 | return ( 10 |
11 | 12 | 13 |
14 | ); 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /src/components/allComponents.js: -------------------------------------------------------------------------------- 1 | import {FeaturedCategories} from "./FeaturedCategories" 2 | import { FiltersBar } from "./FiltersBar" 3 | import { Navbar } from "./Navbar"; 4 | import { Footer } from "./Footer" 5 | import { ProductCard } from "./ProductCard"; 6 | import { HorizontalCard } from "./HorizontalCard"; 7 | import { CartPriceCard } from "./CartPriceCard"; 8 | 9 | 10 | export {FeaturedCategories,FiltersBar,Navbar,Footer,ProductCard, HorizontalCard, CartPriceCard}; 11 | -------------------------------------------------------------------------------- /src/reducers/wishListReducer.js: -------------------------------------------------------------------------------- 1 | export const wishListReducer = (state, { type, payload }) => { 2 | switch (type) { 3 | case "ADD-TO-WISHLIST": 4 | case "UPDATE-WISHLIST": 5 | return { ...state, wishlistItems: payload }; 6 | case "REMOVE-FROM-WISHLIST": 7 | return { ...state, wishlistItems: payload }; 8 | case "CLEAR-WISHLIST": 9 | return wishListInitialState; 10 | default: 11 | state; 12 | } 13 | }; 14 | 15 | export const wishListInitialState = { 16 | wishlistItems: [], 17 | }; 18 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended", 10 | "plugin:react/jsx-runtime", 11 | "plugin:react-hooks/recommended" 12 | ], 13 | "parserOptions": { 14 | "ecmaFeatures": { 15 | "jsx": true 16 | }, 17 | "ecmaVersion": 12, 18 | "sourceType": "module" 19 | }, 20 | "plugins": ["react"], 21 | "rules": { 22 | "react/prop-types": "off" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/styles/components/addressCard.css: -------------------------------------------------------------------------------- 1 | .address-box { 2 | padding: 2rem; 3 | background-color: var(--white); 4 | gap: 10px; 5 | box-shadow: var(--box-shadow-simple); 6 | } 7 | 8 | .single-address { 9 | gap: 8px; 10 | } 11 | 12 | .address-select-input { 13 | accent-color: var(--primary); 14 | } 15 | .btn-edit-address { 16 | margin-left: var(--size-md); 17 | } 18 | @media screen and (max-width: 800px) { 19 | .address-box { 20 | padding: 1rem; 21 | } 22 | .btn-edit-address { 23 | margin-left: var(--size-xsm); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/styles/layouts/scrollbar.css: -------------------------------------------------------------------------------- 1 | /* Scroll-bar styling */ 2 | /* Works on Firefox */ 3 | * { 4 | scrollbar-width: thin; 5 | scrollbar-color: var(--primary-transparent) var(--primary); 6 | } 7 | 8 | /* Works on Chrome, Edge, and Safari */ 9 | *::-webkit-scrollbar { 10 | width: 10px; 11 | } 12 | 13 | *::-webkit-scrollbar-track { 14 | background: var(--primary-transparent); 15 | } 16 | 17 | *::-webkit-scrollbar-thumb { 18 | background-color: var(--primary-hover); 19 | border-radius: 10px; 20 | border: 1px solid var(--primary-hover); 21 | } 22 | -------------------------------------------------------------------------------- /src/backend/db/users.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuid } from "uuid"; 2 | import { formatDate } from "../utils/authUtils"; 3 | /** 4 | * User Database can be added here. 5 | * You can add default users of your wish with different attributes 6 | * Every user will have cart (Quantity of all Products in Cart is set to 1 by default), wishList by default 7 | * */ 8 | 9 | export const users = [ 10 | { 11 | _id: uuid(), 12 | firstName: "Kedar", 13 | lastName: "Kulkarni", 14 | email: "kedar@gmail.com", 15 | password: "kedarInGoa@123", 16 | createdAt: formatDate(), 17 | updatedAt: formatDate(), 18 | }, 19 | ]; 20 | -------------------------------------------------------------------------------- /src/context/cart-context.js: -------------------------------------------------------------------------------- 1 | import {createContext,useContext,useReducer} from "react"; 2 | import { cartReducer, cartInitialState } from "../reducers/cartReducer"; 3 | 4 | const CartContext = createContext(null); 5 | 6 | // eslint-disable-next-line react/prop-types 7 | export const CartProvider = ({children}) =>{ 8 | const [cartState, dispatchCart] = useReducer(cartReducer, cartInitialState); 9 | 10 | return( 11 | 12 | {children} 13 | 14 | ) 15 | 16 | } 17 | 18 | export const useCart = () => useContext(CartContext); -------------------------------------------------------------------------------- /src/context/product-context.js: -------------------------------------------------------------------------------- 1 | import {createContext,useContext,useReducer} from "react"; 2 | import { filterReducer, filterInitialState } from "../utils/filterReducer"; 3 | 4 | const ProductContext = createContext(null); 5 | 6 | // eslint-disable-next-line react/prop-types 7 | export const ProductProvider = ({children}) =>{ 8 | const [filterState, dispatchFilters] = useReducer(filterReducer, filterInitialState); 9 | 10 | return( 11 | 12 | {children} 13 | 14 | ) 15 | 16 | } 17 | 18 | export const useProduct = ()=> useContext(ProductContext); 19 | -------------------------------------------------------------------------------- /src/services/getProducts.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import {useState,useEffect} from "react"; 3 | 4 | 5 | export const GetProducts = ()=>{ 6 | const [loader, setLoader] = useState(true); 7 | const [products, setProducts] = useState([{}]); 8 | 9 | 10 | useEffect(() => { 11 | (async function prodLoader() { 12 | try { 13 | const response = await axios.get("/api/products"); 14 | const getProducts = response.data.products; 15 | setLoader(false); 16 | setProducts(getProducts); 17 | } catch (error) { 18 | console.error(error); 19 | } 20 | })(); //IIFE - Immediately Invoked Function 21 | }, []); 22 | return {loader,products}; 23 | } 24 | -------------------------------------------------------------------------------- /src/styles/pages/wishlist.css: -------------------------------------------------------------------------------- 1 | .wishlist-body { 2 | font-family: var(--lato-font); 3 | background-color: var(--lightest-green); 4 | padding-bottom: 1rem; 5 | margin: 0; 6 | min-height: 100vh; 7 | max-height: max-content; 8 | } 9 | 10 | .wishlist-container-main { 11 | justify-content: center; 12 | width: 100%; 13 | gap: 1rem; 14 | flex-wrap: wrap; 15 | } 16 | .heart-icon-wishlist { 17 | position: absolute; 18 | z-index: 2; 19 | top: 0.1rem; 20 | right: 0.1rem; 21 | font-size: 1.6rem; 22 | cursor: pointer; 23 | background-color: var(--lightest-green); 24 | padding: 5px; 25 | border-radius: 50%; 26 | color: var(--danger); 27 | font-weight: bold; 28 | } 29 | -------------------------------------------------------------------------------- /src/context/address-context.js: -------------------------------------------------------------------------------- 1 | import { useContext, useReducer } from "react"; 2 | import { createContext } from "react"; 3 | import { 4 | addressInitialState, 5 | addressReducer, 6 | } from "../reducers/addressReducer"; 7 | 8 | const AddressContext = createContext(); 9 | 10 | export const AddressProvider = ({ children }) => { 11 | const [addressState, dispatchAddress] = useReducer( 12 | addressReducer, 13 | addressInitialState 14 | ); 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | export const useAddress = () => useContext(AddressContext); 24 | -------------------------------------------------------------------------------- /src/context/auth-context.js: -------------------------------------------------------------------------------- 1 | import { useState, useContext, createContext, useEffect } from "react"; 2 | 3 | const AuthContext = createContext(); 4 | 5 | const AuthProvider = ({ children }) => { 6 | const [auth, setAuth] = useState({}); 7 | useEffect(() => { 8 | const token = localStorage.getItem("token"); 9 | token 10 | ? setAuth({ token: token, isLoggedIn: true }) 11 | : setAuth({ token: "", isLoggedIn: false }); 12 | }, []); 13 | 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | const useAuth = () => useContext(AuthContext); 22 | 23 | export { AuthProvider, useAuth }; 24 | -------------------------------------------------------------------------------- /src/services/getFeaturedCategories.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useState, useEffect } from "react"; 3 | 4 | export const GetFeaturedCategories = ()=>{ 5 | const [categories,setCategories] = useState([]); 6 | useEffect(()=>{ 7 | (async function categoriesLoader(){ 8 | try{ 9 | const response = await axios.get("/api/categories"); 10 | const getCategories = (response.data.categories); 11 | setCategories(getCategories); 12 | } 13 | catch (error){ 14 | console.log("getFeaturedCategories.js Error:",error); 15 | } 16 | })(); 17 | },[]); 18 | return categories; 19 | } 20 | -------------------------------------------------------------------------------- /src/styles/components/orderCard.css: -------------------------------------------------------------------------------- 1 | .order-box { 2 | padding: 2rem; 3 | background-color: var(--white); 4 | gap: 10px; 5 | box-shadow: var(--box-shadow-simple); 6 | } 7 | .btn-continue-shopping { 8 | width: 14rem; 9 | margin-left: 10rem; 10 | padding: 1rem; 11 | } 12 | .success-tick-img { 13 | width: 8rem; 14 | margin-left: 14rem; 15 | } 16 | 17 | @media screen and (max-width: 800px) { 18 | .order-box { 19 | padding: 1rem; 20 | } 21 | .success-tick-img { 22 | width: 5rem; 23 | margin-left: 8rem; 24 | } 25 | .payment-text, 26 | .email-msg-text { 27 | font-size: 1rem; 28 | } 29 | .btn-continue-shopping { 30 | width: 12rem; 31 | margin-left: 5rem; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/context/wishlist-context.js: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useReducer } from "react"; 2 | import { 3 | wishListReducer, 4 | wishListInitialState, 5 | } from "../reducers/wishListReducer"; 6 | 7 | const WishListContext = createContext(null); 8 | 9 | // eslint-disable-next-line react/prop-types 10 | export const WishListProvider = ({ children }) => { 11 | const [wishListState, dispatchWishList] = useReducer( 12 | wishListReducer, 13 | wishListInitialState 14 | ); 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | export const useWishList = () => useContext(WishListContext); 24 | -------------------------------------------------------------------------------- /src/backend/utils/authUtils.js: -------------------------------------------------------------------------------- 1 | import { Response } from "miragejs"; 2 | import dayjs from "dayjs"; 3 | import jwt_decode from "jwt-decode"; 4 | 5 | export const requiresAuth = function (request) { 6 | const encodedToken = request.requestHeaders.authorization; 7 | const decodedToken = jwt_decode( 8 | encodedToken, 9 | process.env.REACT_APP_JWT_SECRET 10 | ); 11 | if (decodedToken) { 12 | const user = this.db.users.findBy({ email: decodedToken.email }); 13 | if (user) { 14 | return user._id; 15 | } 16 | } 17 | return new Response( 18 | 401, 19 | {}, 20 | { errors: ["The token is invalid. Unauthorized access error."] } 21 | ); 22 | }; 23 | 24 | export const formatDate = () => dayjs().format("YYYY-MM-DDTHH:mm:ssZ"); 25 | -------------------------------------------------------------------------------- /src/services/getProductById.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useState, useEffect } from "react"; 3 | 4 | export const GetProductById = (productId) => { 5 | const [loader, setLoader] = useState(true); 6 | const [product, setProduct] = useState([{}]); 7 | 8 | useEffect(() => { 9 | (async function productById() { 10 | try { 11 | const response = await axios.get(`/api/products/${productId}`); 12 | const getProduct = response.data.product; 13 | setLoader(false); 14 | setProduct(getProduct); 15 | } catch (error) { 16 | console.log("Error from getProductById.js ", error); 17 | } 18 | })(); //IIFE - Immediately Invoked Function 19 | }, [productId]); 20 | 21 | return { loader, product }; 22 | }; 23 | -------------------------------------------------------------------------------- /src/pages/ErrorPage.jsx: -------------------------------------------------------------------------------- 1 | import { Navbar } from "../components/Navbar"; 2 | import { Link } from "react-router-dom"; 3 | import "../styles/pages/errorPage.css"; 4 | export const ErrorPage = () => { 5 | return ( 6 | <> 7 |
8 | 9 | 10 |
11 | page-not-found 16 | 17 | Go to Home Page 18 | 19 |
20 |
21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/styles/utilities/variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --transparent-grey: rgba(255, 255, 255, 0.432); 3 | --tree-green: #74bb98; 4 | --box-shadow-1: rgba(0, 0, 0, 0.35) 0px 5px 15px; 5 | --box-shadow-2: rgba(0, 0, 0, 0.25) 0px 54px 55px, 6 | rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, 7 | rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px; 8 | --dark-blue: #00008b; 9 | --dark-brown: rgb(116, 30, 30); 10 | --box-shadow-right: 15px 0 15px -15px #333; 11 | --button-shadow-1: 0 0 20px #eee; 12 | --purple: rgb(170, 37, 170); 13 | } 14 | .white-color { 15 | color: #fff; 16 | } 17 | .red-color { 18 | color: red; 19 | } 20 | 21 | .purple-color { 22 | color: var(--purple-color); 23 | } 24 | .primary-color { 25 | color: var(--primary); 26 | } 27 | -------------------------------------------------------------------------------- /src/components/FeaturedCategories.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import {Link} from "react-router-dom"; 3 | import {useProduct} from "../context/product-context" 4 | 5 | function FeaturedCategories({imgSrc,categoryTitle}){ 6 | const {dispatchFilters} = useProduct(); 7 | return( 8 |
9 | 10 | dispatchFilters({type:"HOME-CATEGORIES-LINK",payload:categoryTitle})}> 11 |

{categoryTitle}

12 | {categoryTitle} 17 | 18 |
19 | ) 20 | } 21 | 22 | export {FeaturedCategories}; 23 | -------------------------------------------------------------------------------- /src/pages/OrderSuccessPage.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-hooks/exhaustive-deps */ 2 | import { 3 | Navbar, 4 | Footer, 5 | // HorizontalCard, 6 | } from "../components/allComponents"; 7 | import "../styles/pages/cart.css"; 8 | import { OrderSuccessCard } from "../components/OrderSuccessCard"; 9 | import { useParams } from "react-router-dom"; 10 | export const OrderSuccessPage = () => { 11 | let { paymentId } = useParams(); 12 | return ( 13 | <> 14 | 15 | {/* */} 16 |
17 |

18 | Order Confirmation Page 19 |

20 |
21 | {} 22 |
23 |
24 | 25 |